Skip to content

Commit ed2293e

Browse files
cjihrigBridgeAR
authored andcommitted
fs: add recursive option to rmdir()
This commit adds a recursive option to fs.rmdir(), fs.rmdirSync(), and fs.promises.rmdir(). The implementation is a port of the npm module rimraf. PR-URL: #29168 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Roman Reiss <[email protected]> Reviewed-By: Ben Coe <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Jiawen Geng <[email protected]>
1 parent 6a4f156 commit ed2293e

File tree

9 files changed

+550
-11
lines changed

9 files changed

+550
-11
lines changed

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,3 +1507,22 @@ The externally maintained libraries used by Node.js are:
15071507
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15081508
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15091509
"""
1510+
1511+
- rimraf, located at lib/internal/fs/rimraf.js, is licensed as follows:
1512+
"""
1513+
The ISC License
1514+
1515+
Copyright (c) Isaac Z. Schlueter and Contributors
1516+
1517+
Permission to use, copy, modify, and/or distribute this software for any
1518+
purpose with or without fee is hereby granted, provided that the above
1519+
copyright notice and this permission notice appear in all copies.
1520+
1521+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1522+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1523+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1524+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1525+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1526+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
1527+
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1528+
"""

doc/api/fs.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3017,10 +3017,14 @@ changes:
30173017

30183018
Synchronous rename(2). Returns `undefined`.
30193019

3020-
## fs.rmdir(path, callback)
3020+
## fs.rmdir(path[, options], callback)
30213021
<!-- YAML
30223022
added: v0.0.2
30233023
changes:
3024+
- version: REPLACEME
3025+
pr-url: https://github.com/nodejs/node/pull/29168
3026+
description: The `recursive`, `maxBusyTries`, and `emfileWait` options are
3027+
now supported.
30243028
- version: v10.0.0
30253029
pr-url: https://github.com/nodejs/node/pull/12562
30263030
description: The `callback` parameter is no longer optional. Not passing
@@ -3035,7 +3039,21 @@ changes:
30353039
it will emit a deprecation warning with id DEP0013.
30363040
-->
30373041

3042+
> Stability: 1 - Recursive removal is experimental.
3043+
30383044
* `path` {string|Buffer|URL}
3045+
* `options` {Object}
3046+
* `emfileWait` {integer} If an `EMFILE` error is encountered, Node.js will
3047+
retry the operation with a linear backoff of 1ms longer on each try until the
3048+
timeout duration passes this limit. This option is ignored if the `recursive`
3049+
option is not `true`. **Default:** `1000`.
3050+
* `maxBusyTries` {integer} If an `EBUSY`, `ENOTEMPTY`, or `EPERM` error is
3051+
encountered, Node.js will retry the operation with a linear backoff wait of
3052+
100ms longer on each try. This option represents the number of retries. This
3053+
option is ignored if the `recursive` option is not `true`. **Default:** `3`.
3054+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
3055+
recursive mode, errors are not reported if `path` does not exist, and
3056+
operations are retried on failure. **Default:** `false`.
30393057
* `callback` {Function}
30403058
* `err` {Error}
30413059

@@ -3045,17 +3063,27 @@ to the completion callback.
30453063
Using `fs.rmdir()` on a file (not a directory) results in an `ENOENT` error on
30463064
Windows and an `ENOTDIR` error on POSIX.
30473065

3048-
## fs.rmdirSync(path)
3066+
## fs.rmdirSync(path[, options])
30493067
<!-- YAML
30503068
added: v0.1.21
30513069
changes:
3070+
- version: REPLACEME
3071+
pr-url: https://github.com/nodejs/node/pull/29168
3072+
description: The `recursive`, `maxBusyTries`, and `emfileWait` options are
3073+
now supported.
30523074
- version: v7.6.0
30533075
pr-url: https://github.com/nodejs/node/pull/10739
30543076
description: The `path` parameters can be a WHATWG `URL` object using
30553077
`file:` protocol. Support is currently still *experimental*.
30563078
-->
30573079

3080+
> Stability: 1 - Recursive removal is experimental.
3081+
30583082
* `path` {string|Buffer|URL}
3083+
* `options` {Object}
3084+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
3085+
recursive mode, errors are not reported if `path` does not exist, and
3086+
operations are retried on failure. **Default:** `false`.
30593087

30603088
Synchronous rmdir(2). Returns `undefined`.
30613089

@@ -4694,12 +4722,31 @@ added: v10.0.0
46944722
Renames `oldPath` to `newPath` and resolves the `Promise` with no arguments
46954723
upon success.
46964724

4697-
### fsPromises.rmdir(path)
4725+
### fsPromises.rmdir(path[, options])
46984726
<!-- YAML
46994727
added: v10.0.0
4728+
changes:
4729+
- version: REPLACEME
4730+
pr-url: https://github.com/nodejs/node/pull/29168
4731+
description: The `recursive`, `maxBusyTries`, and `emfileWait` options are
4732+
now supported.
47004733
-->
47014734

4735+
> Stability: 1 - Recursive removal is experimental.
4736+
47024737
* `path` {string|Buffer|URL}
4738+
* `options` {Object}
4739+
* `emfileWait` {integer} If an `EMFILE` error is encountered, Node.js will
4740+
retry the operation with a linear backoff of 1ms longer on each try until the
4741+
timeout duration passes this limit. This option is ignored if the `recursive`
4742+
option is not `true`. **Default:** `1000`.
4743+
* `maxBusyTries` {integer} If an `EBUSY`, `ENOTEMPTY`, or `EPERM` error is
4744+
encountered, Node.js will retry the operation with a linear backoff wait of
4745+
100ms longer on each try. This option represents the number of retries. This
4746+
option is ignored if the `recursive` option is not `true`. **Default:** `3`.
4747+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
4748+
recursive mode, errors are not reported if `path` does not exist, and
4749+
operations are retried on failure. **Default:** `false`.
47034750
* Returns: {Promise}
47044751

47054752
Removes the directory identified by `path` then resolves the `Promise` with
@@ -5193,7 +5240,7 @@ the file contents.
51935240
[`fs.readdir()`]: #fs_fs_readdir_path_options_callback
51945241
[`fs.readdirSync()`]: #fs_fs_readdirsync_path_options
51955242
[`fs.realpath()`]: #fs_fs_realpath_path_options_callback
5196-
[`fs.rmdir()`]: #fs_fs_rmdir_path_callback
5243+
[`fs.rmdir()`]: #fs_fs_rmdir_path_options_callback
51975244
[`fs.stat()`]: #fs_fs_stat_path_options_callback
51985245
[`fs.symlink()`]: #fs_fs_symlink_target_path_type_callback
51995246
[`fs.utimes()`]: #fs_fs_utimes_path_atime_mtime_callback

lib/fs.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const {
7676
validateOffsetLengthRead,
7777
validateOffsetLengthWrite,
7878
validatePath,
79+
validateRmdirOptions,
7980
warnOnNonPortableTemplate
8081
} = require('internal/fs/utils');
8182
const {
@@ -100,6 +101,8 @@ let watchers;
100101
let ReadFileContext;
101102
let ReadStream;
102103
let WriteStream;
104+
let rimraf;
105+
let rimrafSync;
103106

104107
// These have to be separate because of how graceful-fs happens to do it's
105108
// monkeypatching.
@@ -720,16 +723,41 @@ function ftruncateSync(fd, len = 0) {
720723
handleErrorFromBinding(ctx);
721724
}
722725

723-
function rmdir(path, callback) {
726+
727+
function lazyLoadRimraf() {
728+
if (rimraf === undefined)
729+
({ rimraf, rimrafSync } = require('internal/fs/rimraf'));
730+
}
731+
732+
function rmdir(path, options, callback) {
733+
if (typeof options === 'function') {
734+
callback = options;
735+
options = undefined;
736+
}
737+
724738
callback = makeCallback(callback);
725-
path = getValidatedPath(path);
739+
path = pathModule.toNamespacedPath(getValidatedPath(path));
740+
options = validateRmdirOptions(options);
741+
742+
if (options.recursive) {
743+
lazyLoadRimraf();
744+
return rimraf(path, options, callback);
745+
}
746+
726747
const req = new FSReqCallback();
727748
req.oncomplete = callback;
728-
binding.rmdir(pathModule.toNamespacedPath(path), req);
749+
binding.rmdir(path, req);
729750
}
730751

731-
function rmdirSync(path) {
752+
function rmdirSync(path, options) {
732753
path = getValidatedPath(path);
754+
options = validateRmdirOptions(options);
755+
756+
if (options.recursive) {
757+
lazyLoadRimraf();
758+
return rimrafSync(pathModule.toNamespacedPath(path), options);
759+
}
760+
733761
const ctx = { path };
734762
binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
735763
handleErrorFromBinding(ctx);

lib/internal/fs/promises.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {
1818
ERR_METHOD_NOT_IMPLEMENTED
1919
} = require('internal/errors').codes;
2020
const { isUint8Array } = require('internal/util/types');
21+
const { rimrafPromises } = require('internal/fs/rimraf');
2122
const {
2223
copyObject,
2324
getDirents,
@@ -32,6 +33,7 @@ const {
3233
validateBufferArray,
3334
validateOffsetLengthRead,
3435
validateOffsetLengthWrite,
36+
validateRmdirOptions,
3537
warnOnNonPortableTemplate
3638
} = require('internal/fs/utils');
3739
const {
@@ -291,9 +293,15 @@ async function ftruncate(handle, len = 0) {
291293
return binding.ftruncate(handle.fd, len, kUsePromises);
292294
}
293295

294-
async function rmdir(path) {
295-
path = getValidatedPath(path);
296-
return binding.rmdir(pathModule.toNamespacedPath(path), kUsePromises);
296+
async function rmdir(path, options) {
297+
path = pathModule.toNamespacedPath(getValidatedPath(path));
298+
options = validateRmdirOptions(options);
299+
300+
if (options.recursive) {
301+
return rimrafPromises(path, options);
302+
}
303+
304+
return binding.rmdir(path, kUsePromises);
297305
}
298306

299307
async function fdatasync(handle) {

0 commit comments

Comments
 (0)