Skip to content

Commit

Permalink
fs: add rm method
Browse files Browse the repository at this point in the history
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 <bencoe@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ruy Adorno <ruyadorno@github.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
  • Loading branch information
iansu authored and BethGriggs committed Oct 13, 2020
1 parent f552e5c commit 1610728
Show file tree
Hide file tree
Showing 10 changed files with 518 additions and 37 deletions.
5 changes: 5 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,11 @@ added: v14.0.0
Used when a feature that is not available
to the current platform which is running Node.js is used.

<a id="ERR_FS_EISDIR"></a>
### `ERR_FS_EISDIR`

Path is a directory.

<a id="ERR_FS_FILE_TOO_LARGE"></a>
### `ERR_FS_FILE_TOO_LARGE`

Expand Down
87 changes: 78 additions & 9 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3509,9 +3509,9 @@ changes:
* `options` {Object}
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` ms longer on each try. This option represents
the number of retries. This option is ignored if the `recursive` option is
not `true`. **Default:** `0`.
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
recursive mode, errors are not reported if `path` does not exist, and
operations are retried on failure. **Default:** `false`.
Expand Down Expand Up @@ -3560,9 +3560,9 @@ changes:
* `options` {Object}
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` ms longer on each try. This option represents
the number of retries. This option is ignored if the `recursive` option is
not `true`. **Default:** `0`.
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
recursive mode, errors are not reported if `path` does not exist, and
operations are retried on failure. **Default:** `false`.
Expand All @@ -3581,6 +3581,53 @@ that represent files will be deleted. The permissive behavior of the
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
the future.

## `fs.rm(path[, options], callback)`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL}
* `options` {Object}
* `force` don't error on nonexistent path
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive removal. In
recursive mode operations are retried on failure. **Default:** `false`.
* `retryDelay` {integer} The amount of time in milliseconds to wait between
retries. This option is ignored if the `recursive` option is not `true`.
**Default:** `100`.
* `callback` {Function}
* `err` {Error}

Asynchronously removes files and directories (modeled on the standard POSIX `rm`
utility). No arguments other than a possible exception are given to the
completion callback.

## `fs.rmSync(path[, options])`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL}
* `options` {Object}
* `force` Ignore errors
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
recursive mode operations are retried on failure. **Default:** `false`.
* `retryDelay` {integer} The amount of time in milliseconds to wait between
retries. This option is ignored if the `recursive` option is not `true`.
**Default:** `100`.

Synchronously removes files and directories (modeled on the standard POSIX `rm`
utility). Returns `undefined`.

## `fs.stat(path[, options], callback)`
<!-- YAML
added: v0.0.2
Expand Down Expand Up @@ -5447,9 +5494,9 @@ changes:
* `options` {Object}
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` ms longer on each try. This option represents
the number of retries. This option is ignored if the `recursive` option is
not `true`. **Default:** `0`.
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
recursive mode, errors are not reported if `path` does not exist, and
operations are retried on failure. **Default:** `false`.
Expand All @@ -5471,6 +5518,28 @@ that represent files will be deleted. The permissive behavior of the
`recursive` option is deprecated, `ENOTDIR` and `ENOENT` will be thrown in
the future.

## `fsPromises.rm(path[, options])`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL}
* `options` {Object}
* `force` Ignore errors
* `maxRetries` {integer} If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or
`EPERM` error is encountered, Node.js will retry the operation with a linear
backoff wait of `retryDelay` milliseconds longer on each try. This option
represents the number of retries. This option is ignored if the `recursive`
option is not `true`. **Default:** `0`.
* `recursive` {boolean} If `true`, perform a recursive directory removal. In
recursive mode operations are retried on failure. **Default:** `false`.
* `retryDelay` {integer} The amount of time in milliseconds to wait between
retries. This option is ignored if the `recursive` option is not `true`.
**Default:** `100`.

Synchronously removes files and directories (modeled on the standard POSIX `rm`
utility). Resolves the `Promise` with no arguments on success.

### `fsPromises.stat(path[, options])`
<!-- YAML
added: v10.0.0
Expand Down
59 changes: 48 additions & 11 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ const {
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePath,
validateRmOptions,
validateRmOptionsSync,
validateRmdirOptions,
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate
Expand Down Expand Up @@ -855,30 +857,63 @@ function rmdir(path, options, callback) {

callback = makeCallback(callback);
path = pathModule.toNamespacedPath(getValidatedPath(path));
options = validateRmdirOptions(options);

if (options.recursive) {
lazyLoadRimraf();
return rimraf(path, options, callback);
}
if (options && options.recursive) {
options = validateRmOptions(
path,
{ ...options, force: true },
(err, options) => {
if (err) {
return callback(err);
}

const req = new FSReqCallback();
req.oncomplete = callback;
binding.rmdir(path, req);
lazyLoadRimraf();
return rimraf(path, options, callback);
});

} else {
options = validateRmdirOptions(options);
const req = new FSReqCallback();
req.oncomplete = callback;
return binding.rmdir(path, req);
}
}

function rmdirSync(path, options) {
path = getValidatedPath(path);
options = validateRmdirOptions(options);

if (options.recursive) {
if (options && options.recursive) {
options = validateRmOptionsSync(path, { ...options, force: true });
lazyLoadRimraf();
return rimrafSync(pathModule.toNamespacedPath(path), options);
}

options = validateRmdirOptions(options);
const ctx = { path };
binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
handleErrorFromBinding(ctx);
return handleErrorFromBinding(ctx);
}

function rm(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}

validateRmOptions(path, options, (err, options) => {
if (err) {
return callback(err);
}
lazyLoadRimraf();
return rimraf(pathModule.toNamespacedPath(path), options, callback);
});
}

function rmSync(path, options) {
options = validateRmOptionsSync(path, options);

lazyLoadRimraf();
return rimrafSync(pathModule.toNamespacedPath(path), options);
}

function fdatasync(fd, callback) {
Expand Down Expand Up @@ -2040,6 +2075,8 @@ module.exports = fs = {
realpathSync,
rename,
renameSync,
rm,
rmSync,
rmdir,
rmdirSync,
stat,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM',
'The feature %s is unavailable on the current platform' +
', which is being used to run Node.js',
TypeError);
E('ERR_FS_EISDIR', 'Path is a directory', SystemError);
E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError);
E('ERR_FS_INVALID_SYMLINK_TYPE',
'Symlink type must be one of "dir", "file", or "junction". Received "%s"',
Expand Down
9 changes: 9 additions & 0 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const {
validateBufferArray,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validateRmOptions,
validateRmdirOptions,
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate
Expand Down Expand Up @@ -79,6 +80,7 @@ const {
} = require('internal/worker/js_transferable');

const getDirectoryEntriesPromise = promisify(getDirents);
const validateRmOptionsPromise = promisify(validateRmOptions);

class FileHandle extends JSTransferable {
constructor(filehandle) {
Expand Down Expand Up @@ -417,6 +419,12 @@ async function ftruncate(handle, len = 0) {
return binding.ftruncate(handle.fd, len, kUsePromises);
}

async function rm(path, options) {
path = pathModule.toNamespacedPath(getValidatedPath(path));
options = await validateRmOptionsPromise(path, options);
return rimrafPromises(path, options);
}

async function rmdir(path, options) {
path = pathModule.toNamespacedPath(getValidatedPath(path));
options = validateRmdirOptions(options);
Expand Down Expand Up @@ -635,6 +643,7 @@ module.exports = {
opendir: promisify(opendir),
rename,
truncate,
rm,
rmdir,
mkdir,
readdir,
Expand Down
Loading

0 comments on commit 1610728

Please sign in to comment.