From ca56f60675be1da436275a816d0d5b599b2fa81d Mon Sep 17 00:00:00 2001 From: CxRes Date: Tue, 31 Mar 2020 15:32:03 +0530 Subject: [PATCH] Remove: Option for Error on Missing File or Directory Added a `shoutMissing` flag to remove() options that when set to `true` will emit an error when the specified file or directory is missing. The default is `false`, existing users are not affected. Fixes #246 Partially --- docs/remove.md | 9 +++++++-- lib/remove/__tests__/remove-dir.test.js | 8 ++++++++ lib/remove/__tests__/remove.test.js | 16 +++++++++++++++ lib/remove/rimraf.js | 27 ++++++++++++++++++------- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/docs/remove.md b/docs/remove.md index be319fa3..34c18fa0 100644 --- a/docs/remove.md +++ b/docs/remove.md @@ -1,8 +1,13 @@ -# remove(path[, callback]) +# remove(path[, options][, callback]) -Removes a file or directory. The directory can have contents. If the path does not exist, silently does nothing. Like `rm -rf`. +Removes a file or directory. The directory can have contents. + +If `options.shoutMissing` is not set and the path does not exist, silently does nothing. Like `rm -rf`. - `path` `` +- `options` `` + - `shoutMissing` ``: Throw an error if path does not exist, default is `false`. + - `maxBusyTries` ``: Maximum number of attempts at removal if file or directory is busy or locked, default is `3`. - `callback` `` ## Example: diff --git a/lib/remove/__tests__/remove-dir.test.js b/lib/remove/__tests__/remove-dir.test.js index 344311d8..2ebf0e5b 100644 --- a/lib/remove/__tests__/remove-dir.test.js +++ b/lib/remove/__tests__/remove-dir.test.js @@ -25,5 +25,13 @@ describe('remove / async / dir', () => { done() }) }) + it('should throw an error if shoutMissing is set', done => { + const someDir = path.join(TEST_DIR, 'some-dir/') + assert.strictEqual(fs.existsSync(someDir), false) + fse.remove(someDir, { shoutMissing: true }, err => { + assert.strictEqual(err.code, 'ENOENT') + done() + }) + }) }) }) diff --git a/lib/remove/__tests__/remove.test.js b/lib/remove/__tests__/remove.test.js index ff14ae69..7781fc80 100644 --- a/lib/remove/__tests__/remove.test.js +++ b/lib/remove/__tests__/remove.test.js @@ -120,5 +120,21 @@ describe('remove', () => { done() }) }) + + it('should shout while not deleting glob matches when file doesn’t exist and shoutMissing is set', done => { + const nonexistentFile = path.join(TEST_DIR, 'file?') + + const wrongFile = path.join(TEST_DIR, 'file1') + fs.writeFileSync(wrongFile, 'yo') + + assert(!fs.existsSync(nonexistentFile)) + assert(fs.existsSync(wrongFile)) + fse.remove(nonexistentFile, { shoutMissing: true }, err => { + assert.strictEqual(err.code, 'ENOENT') + assert(!fs.existsSync(nonexistentFile)) + assert(fs.existsSync(wrongFile)) + done() + }) + }) }) }) diff --git a/lib/remove/rimraf.js b/lib/remove/rimraf.js index 1e44c128..e115e4b3 100644 --- a/lib/remove/rimraf.js +++ b/lib/remove/rimraf.js @@ -22,6 +22,7 @@ function defaults (options) { }) options.maxBusyTries = options.maxBusyTries || 3 + options.shoutMissing = options.shoutMissing || false } function rimraf (p, options, cb) { @@ -50,8 +51,12 @@ function rimraf (p, options, cb) { return setTimeout(() => rimraf_(p, options, CB), time) } - // already gone - if (er.code === 'ENOENT') er = null + if (er.code === 'ENOENT') { + // was never there + if (busyTries === 0) er = options.shoutMissing ? er : null + // already gone + else er = null + } } cb(er) @@ -78,7 +83,7 @@ function rimraf_ (p, options, cb) { // so we have to lstat here and make sure it's not a dir. options.lstat(p, (er, st) => { if (er && er.code === 'ENOENT') { - return cb(null) + return options.shoutMissing ? cb(er) : cb(null) // just return cb(er); cb checks ENOENT } // Windows can EPERM on stat. Life is suffering. @@ -92,8 +97,8 @@ function rimraf_ (p, options, cb) { options.unlink(p, er => { if (er) { - if (er.code === 'ENOENT') { - return cb(null) + if (er.code === 'ENOENT') { // this "if" can be removed; cb checks ENOENT + return options.shoutMissing ? cb(er) : cb(null) } if (er.code === 'EPERM') { return (isWindows) @@ -119,11 +124,19 @@ function fixWinEPERM (p, options, er, cb) { options.chmod(p, 0o666, er2 => { if (er2) { - cb(er2.code === 'ENOENT' ? null : er) + if (er2.code === 'ENOENT') { // replace with cb(er2.code === 'ENOENT' ? er2 : er) + options.shoutMissing ? cb(er2) : cb(null) + } else { + cb(er) + } } else { options.stat(p, (er3, stats) => { if (er3) { - cb(er3.code === 'ENOENT' ? null : er) + if (er3.code === 'ENOENT') { // replace with cb(er3.code === 'ENOENT' ? er3 : er) + options.shoutMissing ? cb(er3) : cb(null) + } else { + cb(er) + } } else if (stats.isDirectory()) { rmdir(p, options, er, cb) } else {