From 9845d05844288b7d66f4bd2e6835ed9fd04059fc Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Sat, 3 Oct 2020 20:20:28 -0600 Subject: [PATCH 1/9] fs: add rm method --- doc/api/deprecations.md | 10 + doc/api/errors.md | 5 + doc/api/fs.md | 45 +++ lib/fs.js | 60 +++- lib/internal/errors.js | 1 + lib/internal/fs/promises.js | 9 + lib/internal/fs/utils.js | 124 ++++++++- test/common/tmpdir.js | 8 +- test/parallel/test-fs-rm.js | 273 +++++++++++++++++++ test/parallel/test-fs-rmdir-recursive.js | 14 +- test/parallel/test-policy-parse-integrity.js | 2 +- test/pummel/test-policy-integrity.js | 4 +- 12 files changed, 525 insertions(+), 30 deletions(-) create mode 100644 test/parallel/test-fs-rm.js diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 8c4ed95069c346..53d27931b69b24 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2650,6 +2650,16 @@ Type: Documentation-only The [`crypto.Certificate()` constructor][] is deprecated. Use [static methods of `crypto.Certificate()`][] instead. +### DEP0147: `Permissive rmdir recursive is deprecated, use rm recursive` + + +Type: Runtime + [Legacy URL API]: url.md#url_legacy_url_api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 diff --git a/doc/api/errors.md b/doc/api/errors.md index 814a6e5b16a8f2..4d51de0bf541a2 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -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. + +### `ERR_FS_EISDIR` + +Path is a directory. + ### `ERR_FS_FILE_TOO_LARGE` diff --git a/doc/api/fs.md b/doc/api/fs.md index 291e920f0e1b4f..4b12783cd043c9 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3598,6 +3598,51 @@ 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)` + + +* `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` 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`. + * `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} + +Asynchronous rm(2). No arguments other than a possible exception are given +to the completion callback. + +## `fs.rmSync(path[, options])` + + +* `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` 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`. + * `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`. + +Synchronous rm(2). Returns `undefined`. + ## `fs.stat(path[, options], callback)` diff --git a/doc/api/fs.md b/doc/api/fs.md index 4b12783cd043c9..73bd230c570f0c 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3600,7 +3600,7 @@ the future. ## `fs.rm(path[, options], callback)` * `path` {string|Buffer|URL} @@ -3624,7 +3624,7 @@ to the completion callback. ## `fs.rmSync(path[, options])` * `path` {string|Buffer|URL} diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 60f8441f5d3d6e..62b8b39e993625 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -52,7 +52,7 @@ const { validateBufferArray, validateOffsetLengthRead, validateOffsetLengthWrite, - validateRmOptionsSync, + validateRmOptions, validateRmdirOptions, validateStringAfterArrayBufferView, warnOnNonPortableTemplate @@ -420,8 +420,12 @@ async function ftruncate(handle, len = 0) { async function rm(path, options) { path = pathModule.toNamespacedPath(getValidatedPath(path)); - options = validateRmOptionsSync(path, options); - + options = await new Promise((resolve, reject) => { + validateRmOptions(path, options, false, (err, options) => { + if (err) return reject(err); + return resolve(options); + }); + }); return rimrafPromises(path, options); } diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 5f43e862e81157..2e0fc46a578170 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -670,13 +670,14 @@ function emitPermissiveRmdirWarning() { if (!permissiveRmdirWarned) { process.emitWarning( 'Permissive rmdir recursive is deprecated, use rm recursive instead', + 'DeprecationWarning', 'DEP0147' ); permissiveRmdirWarned = true; } } -const validateRmOptions = (path, options, warn, callback) => { +const validateRmOptions = hideStackFrames((path, options, warn, callback) => { try { options = validateRmdirOptions(options, defaultRmOptions); } catch (err) { @@ -718,9 +719,9 @@ const validateRmOptions = (path, options, warn, callback) => { } return callback(null, options); }); -}; +}); -const validateRmOptionsSync = (path, options, warn) => { +const validateRmOptionsSync = hideStackFrames((path, options, warn) => { options = validateRmdirOptions(options, defaultRmOptions); if (typeof options.force !== 'boolean') @@ -753,7 +754,7 @@ const validateRmOptionsSync = (path, options, warn) => { } return options; -}; +}); const validateRmdirOptions = hideStackFrames( (options, defaults = defaultRmdirOptions) => { From f0f37b1fa8ff9d630e0e94ce720fd50d5a617009 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Tue, 6 Oct 2020 19:05:22 -0700 Subject: [PATCH 3/9] Update lib/fs.js --- lib/fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fs.js b/lib/fs.js index 8f5257d4445f5b..9d194949f1f832 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -906,7 +906,7 @@ function rmSync(path, options) { options = validateRmOptionsSync(path, options, false); lazyLoadRimraf(); - return rimrafSync(path, options); + return rimrafSync(pathModule.toNamespacedPath(path), options); } function fdatasync(fd, callback) { From b1fa541547a85ba6b4cbdd32303032ee939e0dc9 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Tue, 6 Oct 2020 19:05:33 -0700 Subject: [PATCH 4/9] Update lib/fs.js --- lib/fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fs.js b/lib/fs.js index 9d194949f1f832..f20dad23ef975e 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -898,7 +898,7 @@ function rm(path, options, callback) { return callback(err); } lazyLoadRimraf(); - return rimraf(path, options, callback); + return rimraf(pathModule.toNamespacedPath(path), options, callback); }); } From 641bc3bbdae51da715fc0073c7ce2b5194bc5b7c Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Wed, 7 Oct 2020 13:10:18 -0600 Subject: [PATCH 5/9] Remove deprecation warning for recursive rmdir --- doc/api/deprecations.md | 10 -------- doc/api/fs.md | 10 ++++---- lib/fs.js | 7 +++--- lib/internal/errors.js | 2 +- lib/internal/fs/promises.js | 2 +- lib/internal/fs/utils.js | 30 ++---------------------- test/parallel/test-fs-rm.js | 10 ++++++++ test/parallel/test-fs-rmdir-recursive.js | 14 +---------- 8 files changed, 24 insertions(+), 61 deletions(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index a6acc3b412f592..8c4ed95069c346 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2650,16 +2650,6 @@ Type: Documentation-only The [`crypto.Certificate()` constructor][] is deprecated. Use [static methods of `crypto.Certificate()`][] instead. -### DEP0147: `Permissive recursive rmdir is deprecated; use recursive rm` - - -Type: Runtime - [Legacy URL API]: url.md#url_legacy_url_api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 diff --git a/doc/api/fs.md b/doc/api/fs.md index 73bd230c570f0c..99fe0a3e4aa3ca 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3605,7 +3605,7 @@ added: REPLACEME * `path` {string|Buffer|URL} * `options` {Object} - * `force` Ignore errors + * `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` ms longer on each try. This option represents @@ -3619,8 +3619,9 @@ added: REPLACEME * `callback` {Function} * `err` {Error} -Asynchronous rm(2). No arguments other than a possible exception are given -to the completion callback. +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])` + +* `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])`