Skip to content

Commit 4e9f3cc

Browse files
iansuBenjamin Coe
authored andcommitted
fs: add rm method
This PR introduces a new method fs.rm that provides the behaviour of rimraf when used with the recursive: true and force: true options. PR-URL: #35494 Reviewed-By: Ben Coe <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Ruy Adorno <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 6141ca3 commit 4e9f3cc

File tree

10 files changed

+518
-37
lines changed

10 files changed

+518
-37
lines changed

doc/api/errors.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,11 @@ added: v14.0.0
10481048
Used when a feature that is not available
10491049
to the current platform which is running Node.js is used.
10501050

1051+
<a id="ERR_FS_EISDIR"></a>
1052+
### `ERR_FS_EISDIR`
1053+
1054+
Path is a directory.
1055+
10511056
<a id="ERR_FS_FILE_TOO_LARGE"></a>
10521057
### `ERR_FS_FILE_TOO_LARGE`
10531058

doc/api/fs.md

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,9 +3533,9 @@ changes:
35333533
* `options` {Object}
35343534
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
35353535
`EPERM` error is encountered, Node.js will retry the operation with a linear
3536-
backoff wait of `retryDelay` ms longer on each try. This option represents
3537-
the number of retries. This option is ignored if the `recursive` option is
3538-
not `true`. **Default:** `0`.
3536+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3537+
represents the number of retries. This option is ignored if the `recursive`
3538+
option is not `true`. **Default:** `0`.
35393539
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
35403540
recursive mode, errors are not reported if `path` does not exist, and
35413541
operations are retried on failure. **Default:** `false`.
@@ -3584,9 +3584,9 @@ changes:
35843584
* `options` {Object}
35853585
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
35863586
`EPERM` error is encountered, Node.js will retry the operation with a linear
3587-
backoff wait of `retryDelay` ms longer on each try. This option represents
3588-
the number of retries. This option is ignored if the `recursive` option is
3589-
not `true`. **Default:** `0`.
3587+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3588+
represents the number of retries. This option is ignored if the `recursive`
3589+
option is not `true`. **Default:** `0`.
35903590
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
35913591
recursive mode, errors are not reported if `path` does not exist, and
35923592
operations are retried on failure. **Default:** `false`.
@@ -3605,6 +3605,53 @@ that represent files will be deleted. The permissive behavior of the
36053605
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
36063606
the future.
36073607

3608+
## `fs.rm(path[, options], callback)`
3609+
<!-- YAML
3610+
added: REPLACEME
3611+
-->
3612+
3613+
* `path` {string|Buffer|URL}
3614+
* `options` {Object}
3615+
* `force` don't error on nonexistent path
3616+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
3617+
`EPERM` error is encountered, Node.js will retry the operation with a linear
3618+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3619+
represents the number of retries. This option is ignored if the `recursive`
3620+
option is not `true`. **Default:** `0`.
3621+
* `recursive` {boolean} If `true`, perform a recursive removal. In
3622+
recursive mode operations are retried on failure. **Default:** `false`.
3623+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
3624+
retries. This option is ignored if the `recursive` option is not `true`.
3625+
**Default:** `100`.
3626+
* `callback` {Function}
3627+
* `err` {Error}
3628+
3629+
Asynchronously removes files and directories (modeled on the standard POSIX `rm`
3630+
utility). No arguments other than a possible exception are given to the
3631+
completion callback.
3632+
3633+
## `fs.rmSync(path[, options])`
3634+
<!-- YAML
3635+
added: REPLACEME
3636+
-->
3637+
3638+
* `path` {string|Buffer|URL}
3639+
* `options` {Object}
3640+
* `force` Ignore errors
3641+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
3642+
`EPERM` error is encountered, Node.js will retry the operation with a linear
3643+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3644+
represents the number of retries. This option is ignored if the `recursive`
3645+
option is not `true`. **Default:** `0`.
3646+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
3647+
recursive mode operations are retried on failure. **Default:** `false`.
3648+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
3649+
retries. This option is ignored if the `recursive` option is not `true`.
3650+
**Default:** `100`.
3651+
3652+
Synchronously removes files and directories (modeled on the standard POSIX `rm`
3653+
utility). Returns `undefined`.
3654+
36083655
## `fs.stat(path[, options], callback)`
36093656
<!-- YAML
36103657
added: v0.0.2
@@ -5477,9 +5524,9 @@ changes:
54775524
* `options` {Object}
54785525
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
54795526
`EPERM` error is encountered, Node.js will retry the operation with a linear
5480-
backoff wait of `retryDelay` ms longer on each try. This option represents
5481-
the number of retries. This option is ignored if the `recursive` option is
5482-
not `true`. **Default:** `0`.
5527+
backoff wait of `retryDelay` milliseconds longer on each try. This option
5528+
represents the number of retries. This option is ignored if the `recursive`
5529+
option is not `true`. **Default:** `0`.
54835530
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
54845531
recursive mode, errors are not reported if `path` does not exist, and
54855532
operations are retried on failure. **Default:** `false`.
@@ -5501,6 +5548,28 @@ that represent files will be deleted. The permissive behavior of the
55015548
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
55025549
the future.
55035550

5551+
## `fsPromises.rm(path[, options])`
5552+
<!-- YAML
5553+
added: REPLACEME
5554+
-->
5555+
5556+
* `path` {string|Buffer|URL}
5557+
* `options` {Object}
5558+
* `force` Ignore errors
5559+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
5560+
`EPERM` error is encountered, Node.js will retry the operation with a linear
5561+
backoff wait of `retryDelay` milliseconds longer on each try. This option
5562+
represents the number of retries. This option is ignored if the `recursive`
5563+
option is not `true`. **Default:** `0`.
5564+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
5565+
recursive mode operations are retried on failure. **Default:** `false`.
5566+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
5567+
retries. This option is ignored if the `recursive` option is not `true`.
5568+
**Default:** `100`.
5569+
5570+
Synchronously removes files and directories (modeled on the standard POSIX `rm`
5571+
utility). Resolves the `Promise` with no arguments on success.
5572+
55045573
### `fsPromises.stat(path[, options])`
55055574
<!-- YAML
55065575
added: v10.0.0

lib/fs.js

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ const {
9797
validateOffsetLengthRead,
9898
validateOffsetLengthWrite,
9999
validatePath,
100+
validateRmOptions,
101+
validateRmOptionsSync,
100102
validateRmdirOptions,
101103
validateStringAfterArrayBufferView,
102104
warnOnNonPortableTemplate
@@ -855,30 +857,63 @@ function rmdir(path, options, callback) {
855857

856858
callback = makeCallback(callback);
857859
path = pathModule.toNamespacedPath(getValidatedPath(path));
858-
options = validateRmdirOptions(options);
859860

860-
if (options.recursive) {
861-
lazyLoadRimraf();
862-
return rimraf(path, options, callback);
863-
}
861+
if (options && options.recursive) {
862+
options = validateRmOptions(
863+
path,
864+
{ ...options, force: true },
865+
(err, options) => {
866+
if (err) {
867+
return callback(err);
868+
}
864869

865-
const req = new FSReqCallback();
866-
req.oncomplete = callback;
867-
binding.rmdir(path, req);
870+
lazyLoadRimraf();
871+
return rimraf(path, options, callback);
872+
});
873+
874+
} else {
875+
options = validateRmdirOptions(options);
876+
const req = new FSReqCallback();
877+
req.oncomplete = callback;
878+
return binding.rmdir(path, req);
879+
}
868880
}
869881

870882
function rmdirSync(path, options) {
871883
path = getValidatedPath(path);
872-
options = validateRmdirOptions(options);
873884

874-
if (options.recursive) {
885+
if (options && options.recursive) {
886+
options = validateRmOptionsSync(path, { ...options, force: true });
875887
lazyLoadRimraf();
876888
return rimrafSync(pathModule.toNamespacedPath(path), options);
877889
}
878890

891+
options = validateRmdirOptions(options);
879892
const ctx = { path };
880893
binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
881-
handleErrorFromBinding(ctx);
894+
return handleErrorFromBinding(ctx);
895+
}
896+
897+
function rm(path, options, callback) {
898+
if (typeof options === 'function') {
899+
callback = options;
900+
options = undefined;
901+
}
902+
903+
validateRmOptions(path, options, (err, options) => {
904+
if (err) {
905+
return callback(err);
906+
}
907+
lazyLoadRimraf();
908+
return rimraf(pathModule.toNamespacedPath(path), options, callback);
909+
});
910+
}
911+
912+
function rmSync(path, options) {
913+
options = validateRmOptionsSync(path, options);
914+
915+
lazyLoadRimraf();
916+
return rimrafSync(pathModule.toNamespacedPath(path), options);
882917
}
883918

884919
function fdatasync(fd, callback) {
@@ -2040,6 +2075,8 @@ module.exports = fs = {
20402075
realpathSync,
20412076
rename,
20422077
renameSync,
2078+
rm,
2079+
rmSync,
20432080
rmdir,
20442081
rmdirSync,
20452082
stat,

lib/internal/errors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,7 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM',
843843
'The feature %s is unavailable on the current platform' +
844844
', which is being used to run Node.js',
845845
TypeError);
846+
E('ERR_FS_EISDIR', 'Path is a directory', SystemError);
846847
E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError);
847848
E('ERR_FS_INVALID_SYMLINK_TYPE',
848849
'Symlink type must be one of "dir", "file", or "junction". Received "%s"',

lib/internal/fs/promises.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const {
5252
validateBufferArray,
5353
validateOffsetLengthRead,
5454
validateOffsetLengthWrite,
55+
validateRmOptions,
5556
validateRmdirOptions,
5657
validateStringAfterArrayBufferView,
5758
warnOnNonPortableTemplate
@@ -79,6 +80,7 @@ const {
7980
} = require('internal/worker/js_transferable');
8081

8182
const getDirectoryEntriesPromise = promisify(getDirents);
83+
const validateRmOptionsPromise = promisify(validateRmOptions);
8284

8385
class FileHandle extends JSTransferable {
8486
constructor(filehandle) {
@@ -417,6 +419,12 @@ async function ftruncate(handle, len = 0) {
417419
return binding.ftruncate(handle.fd, len, kUsePromises);
418420
}
419421

422+
async function rm(path, options) {
423+
path = pathModule.toNamespacedPath(getValidatedPath(path));
424+
options = await validateRmOptionsPromise(path, options);
425+
return rimrafPromises(path, options);
426+
}
427+
420428
async function rmdir(path, options) {
421429
path = pathModule.toNamespacedPath(getValidatedPath(path));
422430
options = validateRmdirOptions(options);
@@ -635,6 +643,7 @@ module.exports = {
635643
opendir: promisify(opendir),
636644
rename,
637645
truncate,
646+
rm,
638647
rmdir,
639648
mkdir,
640649
readdir,

0 commit comments

Comments
 (0)