Skip to content

Commit 1610728

Browse files
iansuBethGriggs
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 f552e5c commit 1610728

File tree

10 files changed

+518
-37
lines changed

10 files changed

+518
-37
lines changed

doc/api/errors.md

+5
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,11 @@ added: v14.0.0
928928
Used when a feature that is not available
929929
to the current platform which is running Node.js is used.
930930

931+
<a id="ERR_FS_EISDIR"></a>
932+
### `ERR_FS_EISDIR`
933+
934+
Path is a directory.
935+
931936
<a id="ERR_FS_FILE_TOO_LARGE"></a>
932937
### `ERR_FS_FILE_TOO_LARGE`
933938

doc/api/fs.md

+78-9
Original file line numberDiff line numberDiff line change
@@ -3509,9 +3509,9 @@ changes:
35093509
* `options` {Object}
35103510
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
35113511
`EPERM` error is encountered, Node.js will retry the operation with a linear
3512-
backoff wait of `retryDelay` ms longer on each try. This option represents
3513-
the number of retries. This option is ignored if the `recursive` option is
3514-
not `true`. **Default:** `0`.
3512+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3513+
represents the number of retries. This option is ignored if the `recursive`
3514+
option is not `true`. **Default:** `0`.
35153515
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
35163516
recursive mode, errors are not reported if `path` does not exist, and
35173517
operations are retried on failure. **Default:** `false`.
@@ -3560,9 +3560,9 @@ changes:
35603560
* `options` {Object}
35613561
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
35623562
`EPERM` error is encountered, Node.js will retry the operation with a linear
3563-
backoff wait of `retryDelay` ms longer on each try. This option represents
3564-
the number of retries. This option is ignored if the `recursive` option is
3565-
not `true`. **Default:** `0`.
3563+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3564+
represents the number of retries. This option is ignored if the `recursive`
3565+
option is not `true`. **Default:** `0`.
35663566
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
35673567
recursive mode, errors are not reported if `path` does not exist, and
35683568
operations are retried on failure. **Default:** `false`.
@@ -3581,6 +3581,53 @@ that represent files will be deleted. The permissive behavior of the
35813581
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
35823582
the future.
35833583

3584+
## `fs.rm(path[, options], callback)`
3585+
<!-- YAML
3586+
added: REPLACEME
3587+
-->
3588+
3589+
* `path` {string|Buffer|URL}
3590+
* `options` {Object}
3591+
* `force` don't error on nonexistent path
3592+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
3593+
`EPERM` error is encountered, Node.js will retry the operation with a linear
3594+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3595+
represents the number of retries. This option is ignored if the `recursive`
3596+
option is not `true`. **Default:** `0`.
3597+
* `recursive` {boolean} If `true`, perform a recursive removal. In
3598+
recursive mode operations are retried on failure. **Default:** `false`.
3599+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
3600+
retries. This option is ignored if the `recursive` option is not `true`.
3601+
**Default:** `100`.
3602+
* `callback` {Function}
3603+
* `err` {Error}
3604+
3605+
Asynchronously removes files and directories (modeled on the standard POSIX `rm`
3606+
utility). No arguments other than a possible exception are given to the
3607+
completion callback.
3608+
3609+
## `fs.rmSync(path[, options])`
3610+
<!-- YAML
3611+
added: REPLACEME
3612+
-->
3613+
3614+
* `path` {string|Buffer|URL}
3615+
* `options` {Object}
3616+
* `force` Ignore errors
3617+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
3618+
`EPERM` error is encountered, Node.js will retry the operation with a linear
3619+
backoff wait of `retryDelay` milliseconds longer on each try. This option
3620+
represents the number of retries. This option is ignored if the `recursive`
3621+
option is not `true`. **Default:** `0`.
3622+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
3623+
recursive mode operations are retried on failure. **Default:** `false`.
3624+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
3625+
retries. This option is ignored if the `recursive` option is not `true`.
3626+
**Default:** `100`.
3627+
3628+
Synchronously removes files and directories (modeled on the standard POSIX `rm`
3629+
utility). Returns `undefined`.
3630+
35843631
## `fs.stat(path[, options], callback)`
35853632
<!-- YAML
35863633
added: v0.0.2
@@ -5447,9 +5494,9 @@ changes:
54475494
* `options` {Object}
54485495
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
54495496
`EPERM` error is encountered, Node.js will retry the operation with a linear
5450-
backoff wait of `retryDelay` ms longer on each try. This option represents
5451-
the number of retries. This option is ignored if the `recursive` option is
5452-
not `true`. **Default:** `0`.
5497+
backoff wait of `retryDelay` milliseconds longer on each try. This option
5498+
represents the number of retries. This option is ignored if the `recursive`
5499+
option is not `true`. **Default:** `0`.
54535500
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
54545501
recursive mode, errors are not reported if `path` does not exist, and
54555502
operations are retried on failure. **Default:** `false`.
@@ -5471,6 +5518,28 @@ that represent files will be deleted. The permissive behavior of the
54715518
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
54725519
the future.
54735520

5521+
## `fsPromises.rm(path[, options])`
5522+
<!-- YAML
5523+
added: REPLACEME
5524+
-->
5525+
5526+
* `path` {string|Buffer|URL}
5527+
* `options` {Object}
5528+
* `force` Ignore errors
5529+
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
5530+
`EPERM` error is encountered, Node.js will retry the operation with a linear
5531+
backoff wait of `retryDelay` milliseconds longer on each try. This option
5532+
represents the number of retries. This option is ignored if the `recursive`
5533+
option is not `true`. **Default:** `0`.
5534+
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
5535+
recursive mode operations are retried on failure. **Default:** `false`.
5536+
* `retryDelay` {integer} The amount of time in milliseconds to wait between
5537+
retries. This option is ignored if the `recursive` option is not `true`.
5538+
**Default:** `100`.
5539+
5540+
Synchronously removes files and directories (modeled on the standard POSIX `rm`
5541+
utility). Resolves the `Promise` with no arguments on success.
5542+
54745543
### `fsPromises.stat(path[, options])`
54755544
<!-- YAML
54765545
added: v10.0.0

lib/fs.js

+48-11
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

+1
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM',
839839
'The feature %s is unavailable on the current platform' +
840840
', which is being used to run Node.js',
841841
TypeError);
842+
E('ERR_FS_EISDIR', 'Path is a directory', SystemError);
842843
E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError);
843844
E('ERR_FS_INVALID_SYMLINK_TYPE',
844845
'Symlink type must be one of "dir", "file", or "junction". Received "%s"',

lib/internal/fs/promises.js

+9
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)