diff --git a/lib/remove/rimraf.js b/lib/remove/rimraf.js index 28d4aeb..15924c3 100644 --- a/lib/remove/rimraf.js +++ b/lib/remove/rimraf.js @@ -42,7 +42,7 @@ function rimraf (p, options, cb) { rimraf_(p, options, function CB (er) { if (er) { - if (isWindows && (er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') && + if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') && busyTries < options.maxBusyTries) { busyTries++ let time = busyTries * 100 @@ -289,7 +289,25 @@ function rmkidsSync (p, options) { assert(p) assert(options) options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) - options.rmdirSync(p, options) + + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + const retries = isWindows ? 100 : 1 + let i = 0 + do { + let threw = true + try { + const ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) continue // eslint-disable-line + } + } while (true) } module.exports = rimraf