Skip to content

Commit eec0302

Browse files
authored
fs: move rmdir recursive option to end-of-life
Has been runtime deprecated for ~ 5 years now. It's time. PR-URL: #58616 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Dario Piotrowicz <dario.piotrowicz@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent b04c4a4 commit eec0302

13 files changed

+105
-434
lines changed

doc/api/deprecations.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3057,6 +3057,9 @@ The [`crypto.Certificate()` constructor][] is deprecated. Use
30573057

30583058
<!-- YAML
30593059
changes:
3060+
- version: REPLACEME
3061+
pr-url: https://github.com/nodejs/node/pull/58616
3062+
description: End-of-Life.
30603063
- version: v16.0.0
30613064
pr-url: https://github.com/nodejs/node/pull/37302
30623065
description: Runtime deprecation.
@@ -3068,10 +3071,10 @@ changes:
30683071
description: Documentation-only deprecation.
30693072
-->
30703073

3071-
Type: Runtime
3074+
Type: End-of-Life
30723075

3073-
In future versions of Node.js, `recursive` option will be ignored for
3074-
`fs.rmdir`, `fs.rmdirSync`, and `fs.promises.rmdir`.
3076+
The `fs.rmdir`, `fs.rmdirSync`, and `fs.promises.rmdir` methods used
3077+
to support a `recursive` option. That option has been removed.
30753078

30763079
Use `fs.rm(path, { recursive: true, force: true })`,
30773080
`fs.rmSync(path, { recursive: true, force: true })` or

doc/api/fs.md

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,9 @@ Renames `oldPath` to `newPath`.
15891589
<!-- YAML
15901590
added: v10.0.0
15911591
changes:
1592+
- version: REPLACEME
1593+
pr-url: https://github.com/nodejs/node/pull/58616
1594+
description: Remove `recursive` option.
15921595
- version: v16.0.0
15931596
pr-url: https://github.com/nodejs/node/pull/37216
15941597
description: "Using `fsPromises.rmdir(path, { recursive: true })` on a `path`
@@ -1622,18 +1625,10 @@ changes:
16221625
-->
16231626
16241627
* `path` {string|Buffer|URL}
1625-
* `options` {Object}
1626-
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
1627-
`EPERM` error is encountered, Node.js retries the operation with a linear
1628-
backoff wait of `retryDelay` milliseconds longer on each try. This option
1629-
represents the number of retries. This option is ignored if the `recursive`
1630-
option is not `true`. **Default:** `0`.
1631-
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
1632-
recursive mode, operations are retried on failure. **Default:** `false`.
1633-
**Deprecated.**
1634-
* `retryDelay` {integer} The amount of time in milliseconds to wait between
1635-
retries. This option is ignored if the `recursive` option is not `true`.
1636-
**Default:** `100`.
1628+
* `options` {Object} There are currently no options exposed. There used to
1629+
be options for `recursive`, `maxBusyTries`, and `emfileWait` but they were
1630+
deprecated and removed. The `options` argument is still accepted for
1631+
backwards compatibility but it is not used.
16371632
* Returns: {Promise} Fulfills with `undefined` upon success.
16381633
16391634
Removes the directory identified by `path`.
@@ -4255,6 +4250,9 @@ rename('oldFile.txt', 'newFile.txt', (err) => {
42554250
<!-- YAML
42564251
added: v0.0.2
42574252
changes:
4253+
- version: REPLACEME
4254+
pr-url: https://github.com/nodejs/node/pull/58616
4255+
description: Remove `recursive` option.
42584256
- version: v18.0.0
42594257
pr-url: https://github.com/nodejs/node/pull/41678
42604258
description: Passing an invalid callback to the `callback` argument
@@ -4305,18 +4303,10 @@ changes:
43054303
-->
43064304
43074305
* `path` {string|Buffer|URL}
4308-
* `options` {Object}
4309-
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
4310-
`EPERM` error is encountered, Node.js retries the operation with a linear
4311-
backoff wait of `retryDelay` milliseconds longer on each try. This option
4312-
represents the number of retries. This option is ignored if the `recursive`
4313-
option is not `true`. **Default:** `0`.
4314-
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
4315-
recursive mode, operations are retried on failure. **Default:** `false`.
4316-
**Deprecated.**
4317-
* `retryDelay` {integer} The amount of time in milliseconds to wait between
4318-
retries. This option is ignored if the `recursive` option is not `true`.
4319-
**Default:** `100`.
4306+
* `options` {Object} There are currently no options exposed. There used to
4307+
be options for `recursive`, `maxBusyTries`, and `emfileWait` but they were
4308+
deprecated and removed. The `options` argument is still accepted for
4309+
backwards compatibility but it is not used.
43204310
* `callback` {Function}
43214311
* `err` {Error}
43224312
@@ -6234,6 +6224,9 @@ See the POSIX rename(2) documentation for more details.
62346224
<!-- YAML
62356225
added: v0.1.21
62366226
changes:
6227+
- version: REPLACEME
6228+
pr-url: https://github.com/nodejs/node/pull/58616
6229+
description: Remove `recursive` option.
62376230
- version: v16.0.0
62386231
pr-url: https://github.com/nodejs/node/pull/37216
62396232
description: "Using `fs.rmdirSync(path, { recursive: true })` on a `path`
@@ -6271,18 +6264,10 @@ changes:
62716264
-->
62726265
62736266
* `path` {string|Buffer|URL}
6274-
* `options` {Object}
6275-
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
6276-
`EPERM` error is encountered, Node.js retries the operation with a linear
6277-
backoff wait of `retryDelay` milliseconds longer on each try. This option
6278-
represents the number of retries. This option is ignored if the `recursive`
6279-
option is not `true`. **Default:** `0`.
6280-
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
6281-
recursive mode, operations are retried on failure. **Default:** `false`.
6282-
**Deprecated.**
6283-
* `retryDelay` {integer} The amount of time in milliseconds to wait between
6284-
retries. This option is ignored if the `recursive` option is not `true`.
6285-
**Default:** `100`.
6267+
* `options` {Object} There are currently no options exposed. There used to
6268+
be options for `recursive`, `maxBusyTries`, and `emfileWait` but they were
6269+
deprecated and removed. The `options` argument is still accepted for
6270+
backwards compatibility but it is not used.
62866271
62876272
Synchronous rmdir(2). Returns `undefined`.
62886273

lib/fs.js

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ const {
102102
},
103103
copyObject,
104104
Dirent,
105-
emitRecursiveRmdirWarning,
106105
getDirent,
107106
getDirents,
108107
getOptions,
@@ -1109,11 +1108,7 @@ function lazyLoadRimraf() {
11091108
/**
11101109
* Asynchronously removes a directory.
11111110
* @param {string | Buffer | URL} path
1112-
* @param {{
1113-
* maxRetries?: number;
1114-
* recursive?: boolean;
1115-
* retryDelay?: number;
1116-
* }} [options]
1111+
* @param {{}} [options]
11171112
* @param {(err?: Error) => any} callback
11181113
* @returns {void}
11191114
*/
@@ -1123,60 +1118,45 @@ function rmdir(path, options, callback) {
11231118
options = undefined;
11241119
}
11251120

1121+
if (options?.recursive !== undefined) {
1122+
// This API previously accepted a `recursive` option that was deprecated
1123+
// and removed. However, in order to make the change more visible, we
1124+
// opted to throw an error if recursive is specified rather than removing it
1125+
// entirely.
1126+
throw new ERR_INVALID_ARG_VALUE(
1127+
'options.recursive',
1128+
options.recursive,
1129+
'is no longer supported',
1130+
);
1131+
}
1132+
11261133
callback = makeCallback(callback);
11271134
path = getValidatedPath(path);
11281135

1129-
if (options?.recursive) {
1130-
emitRecursiveRmdirWarning();
1131-
validateRmOptions(
1132-
path,
1133-
{ ...options, force: false },
1134-
true,
1135-
(err, options) => {
1136-
if (err === false) {
1137-
const req = new FSReqCallback();
1138-
req.oncomplete = callback;
1139-
binding.rmdir(path, req);
1140-
return;
1141-
}
1142-
if (err) {
1143-
return callback(err);
1144-
}
1145-
1146-
lazyLoadRimraf();
1147-
rimraf(path, options, callback);
1148-
});
1149-
} else {
1150-
validateRmdirOptions(options);
1151-
const req = new FSReqCallback();
1152-
req.oncomplete = callback;
1153-
binding.rmdir(path, req);
1154-
}
1136+
validateRmdirOptions(options);
1137+
const req = new FSReqCallback();
1138+
req.oncomplete = callback;
1139+
binding.rmdir(path, req);
11551140
}
11561141

11571142
/**
11581143
* Synchronously removes a directory.
11591144
* @param {string | Buffer | URL} path
1160-
* @param {{
1161-
* maxRetries?: number;
1162-
* recursive?: boolean;
1163-
* retryDelay?: number;
1164-
* }} [options]
1145+
* @param {{}} [options]
11651146
* @returns {void}
11661147
*/
11671148
function rmdirSync(path, options) {
11681149
path = getValidatedPath(path);
11691150

1170-
if (options?.recursive) {
1171-
emitRecursiveRmdirWarning();
1172-
options = validateRmOptionsSync(path, { ...options, force: false }, true);
1173-
if (options !== false) {
1174-
return binding.rmSync(path, options.maxRetries, options.recursive, options.retryDelay);
1175-
}
1176-
} else {
1177-
validateRmdirOptions(options);
1151+
if (options?.recursive !== undefined) {
1152+
throw new ERR_INVALID_ARG_VALUE(
1153+
'options.recursive',
1154+
options.recursive,
1155+
'is no longer supported',
1156+
);
11781157
}
11791158

1159+
validateRmdirOptions(options);
11801160
binding.rmdir(path);
11811161
}
11821162

lib/internal/fs/promises.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ const {
5656
kWriteFileMaxChunkSize,
5757
},
5858
copyObject,
59-
emitRecursiveRmdirWarning,
6059
getDirents,
6160
getOptions,
6261
getStatFsFromBinding,
@@ -812,16 +811,17 @@ async function rm(path, options) {
812811

813812
async function rmdir(path, options) {
814813
path = getValidatedPath(path);
815-
options = validateRmdirOptions(options);
816814

817-
if (options.recursive) {
818-
emitRecursiveRmdirWarning();
819-
const stats = await stat(path);
820-
if (stats.isDirectory()) {
821-
return lazyRimRaf()(path, options);
822-
}
815+
if (options?.recursive !== undefined) {
816+
throw new ERR_INVALID_ARG_VALUE(
817+
'options.recursive',
818+
options.recursive,
819+
'is no longer supported',
820+
);
823821
}
824822

823+
options = validateRmdirOptions(options);
824+
825825
return await PromisePrototypeThen(
826826
binding.rmdir(path, kUsePromises),
827827
undefined,

lib/internal/fs/utils.js

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -778,12 +778,6 @@ const defaultRmOptions = {
778778
maxRetries: 0,
779779
};
780780

781-
const defaultRmdirOptions = {
782-
retryDelay: 100,
783-
maxRetries: 0,
784-
recursive: false,
785-
};
786-
787781
const validateCpOptions = hideStackFrames((options) => {
788782
if (options === undefined)
789783
return { ...defaultCpOptions };
@@ -807,7 +801,10 @@ const validateCpOptions = hideStackFrames((options) => {
807801

808802
const validateRmOptions = hideStackFrames((path, options, expectDir, cb) => {
809803
options = validateRmdirOptions(options, defaultRmOptions);
810-
validateBoolean(options.force, 'options.force');
804+
validateBoolean.withoutStackTrace(options.force, 'options.force');
805+
validateBoolean.withoutStackTrace(options.recursive, 'options.recursive');
806+
validateInt32.withoutStackTrace(options.retryDelay, 'options.retryDelay', 0);
807+
validateUint32.withoutStackTrace(options.maxRetries, 'options.maxRetries');
811808

812809
lazyLoadFs().lstat(path, (err, stats) => {
813810
if (err) {
@@ -839,6 +836,10 @@ const validateRmOptions = hideStackFrames((path, options, expectDir, cb) => {
839836
const validateRmOptionsSync = hideStackFrames((path, options, expectDir) => {
840837
options = validateRmdirOptions.withoutStackTrace(options, defaultRmOptions);
841838
validateBoolean.withoutStackTrace(options.force, 'options.force');
839+
validateBoolean.withoutStackTrace(options.recursive, 'options.recursive');
840+
validateInt32.withoutStackTrace(options.retryDelay, 'options.retryDelay', 0);
841+
validateUint32.withoutStackTrace(options.maxRetries, 'options.maxRetries');
842+
842843

843844
if (!options.force || expectDir || !options.recursive) {
844845
const isDirectory = lazyLoadFs()
@@ -862,35 +863,14 @@ const validateRmOptionsSync = hideStackFrames((path, options, expectDir) => {
862863
return options;
863864
});
864865

865-
let recursiveRmdirWarned;
866-
function emitRecursiveRmdirWarning() {
867-
if (recursiveRmdirWarned === undefined) {
868-
// TODO(joyeecheung): use getOptionValue('--no-deprecation') instead.
869-
recursiveRmdirWarned = process.noDeprecation;
870-
}
871-
if (!recursiveRmdirWarned) {
872-
process.emitWarning(
873-
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
874-
'will be removed. Use fs.rm(path, { recursive: true }) instead',
875-
'DeprecationWarning',
876-
'DEP0147',
877-
);
878-
recursiveRmdirWarned = true;
879-
}
880-
}
881-
882866
const validateRmdirOptions = hideStackFrames(
883-
(options, defaults = defaultRmdirOptions) => {
867+
(options, defaults = { __proto__: null }) => {
884868
if (options === undefined)
885869
return defaults;
886870
validateObject.withoutStackTrace(options, 'options');
887871

888872
options = { ...defaults, ...options };
889873

890-
validateBoolean.withoutStackTrace(options.recursive, 'options.recursive');
891-
validateInt32.withoutStackTrace(options.retryDelay, 'options.retryDelay', 0);
892-
validateUint32.withoutStackTrace(options.maxRetries, 'options.maxRetries');
893-
894874
return options;
895875
});
896876

@@ -950,7 +930,6 @@ module.exports = {
950930
copyObject,
951931
Dirent,
952932
DirentFromStats,
953-
emitRecursiveRmdirWarning,
954933
getDirent,
955934
getDirents,
956935
getOptions,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const {
6+
rmdir,
7+
rmdirSync,
8+
promises: { rmdir: rmdirPromise }
9+
} = require('fs');
10+
11+
assert.throws(() => {
12+
rmdir('nonexistent', {
13+
recursive: true,
14+
}, common.mustNotCall());
15+
}, {
16+
code: 'ERR_INVALID_ARG_VALUE',
17+
});
18+
19+
assert.throws(() => {
20+
rmdirSync('nonexistent', {
21+
recursive: true,
22+
});
23+
}, {
24+
code: 'ERR_INVALID_ARG_VALUE',
25+
});
26+
27+
rmdirPromise('nonexistent', {
28+
recursive: true,
29+
}).then(common.mustNotCall(), common.mustCall((err) => {
30+
assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE');
31+
}));

test/parallel/test-fs-rmdir-recursive-sync-warns-not-found.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)