Skip to content

Commit

Permalink
Remove: Option for Error on Missing File or Directory
Browse files Browse the repository at this point in the history
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 jprichardson#246 Partially
  • Loading branch information
CxRes committed Mar 31, 2020
1 parent 934ea75 commit ca56f60
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 9 deletions.
9 changes: 7 additions & 2 deletions docs/remove.md
Original file line number Diff line number Diff line change
@@ -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` `<String>`
- `options` `<Object>`
- `shoutMissing` `<boolean>`: Throw an error if path does not exist, default is `false`.
- `maxBusyTries` `<number>`: Maximum number of attempts at removal if file or directory is busy or locked, default is `3`.
- `callback` `<Function>`

## Example:
Expand Down
8 changes: 8 additions & 0 deletions lib/remove/__tests__/remove-dir.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
})
})
16 changes: 16 additions & 0 deletions lib/remove/__tests__/remove.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
})
})
27 changes: 20 additions & 7 deletions lib/remove/rimraf.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function defaults (options) {
})

options.maxBusyTries = options.maxBusyTries || 3
options.shoutMissing = options.shoutMissing || false
}

function rimraf (p, options, cb) {
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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 {
Expand Down

0 comments on commit ca56f60

Please sign in to comment.