1 const assert = require("assert")
2 const path = require("path")
3 const fs = require("fs")
8 // treat glob as optional.
11 const defaultGlobOpts = {
16 // for EMFILE handling
19 const isWindows = (process.platform === "win32")
21 const defaults = options => {
30 methods.forEach(m => {
31 options[m] = options[m] || fs[m]
33 options[m] = options[m] || fs[m]
36 options.maxBusyTries = options.maxBusyTries || 3
37 options.emfileWait = options.emfileWait || 1000
38 if (options.glob === false) {
39 options.disableGlob = true
41 if (options.disableGlob !== true && glob === undefined) {
42 throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
44 options.disableGlob = options.disableGlob || false
45 options.glob = options.glob || defaultGlobOpts
48 const rimraf = (p, options, cb) => {
49 if (typeof options === 'function') {
54 assert(p, 'rimraf: missing path')
55 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
56 assert.equal(typeof cb, 'function', 'rimraf: callback function required')
57 assert(options, 'rimraf: invalid options argument provided')
58 assert.equal(typeof options, 'object', 'rimraf: options should be object')
66 const next = (er) => {
67 errState = errState || er
72 const afterGlob = (er, results) => {
80 results.forEach(p => {
83 if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
84 busyTries < options.maxBusyTries) {
86 // try again, with the same exact callback as this one.
87 return setTimeout(() => rimraf_(p, options, CB), busyTries * 100)
90 // this one won't happen if graceful-fs is used.
91 if (er.code === "EMFILE" && timeout < options.emfileWait) {
92 return setTimeout(() => rimraf_(p, options, CB), timeout ++)
96 if (er.code === "ENOENT") er = null
102 rimraf_(p, options, CB)
106 if (options.disableGlob || !glob.hasMagic(p))
107 return afterGlob(null, [p])
109 options.lstat(p, (er, stat) => {
111 return afterGlob(null, [p])
113 glob(p, options.glob, afterGlob)
118 // Two possible strategies.
119 // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
120 // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
122 // Both result in an extra syscall when you guess wrong. However, there
123 // are likely far more normal files in the world than directories. This
124 // is based on the assumption that a the average number of files per
125 // directory is >= 1.
127 // If anyone ever complains about this, then I guess the strategy could
128 // be made configurable somehow. But until then, YAGNI.
129 const rimraf_ = (p, options, cb) => {
132 assert(typeof cb === 'function')
134 // sunos lets the root user unlink directories, which is... weird.
135 // so we have to lstat here and make sure it's not a dir.
136 options.lstat(p, (er, st) => {
137 if (er && er.code === "ENOENT")
140 // Windows can EPERM on stat. Life is suffering.
141 if (er && er.code === "EPERM" && isWindows)
142 fixWinEPERM(p, options, er, cb)
144 if (st && st.isDirectory())
145 return rmdir(p, options, er, cb)
147 options.unlink(p, er => {
149 if (er.code === "ENOENT")
151 if (er.code === "EPERM")
153 ? fixWinEPERM(p, options, er, cb)
154 : rmdir(p, options, er, cb)
155 if (er.code === "EISDIR")
156 return rmdir(p, options, er, cb)
163 const fixWinEPERM = (p, options, er, cb) => {
166 assert(typeof cb === 'function')
168 options.chmod(p, 0o666, er2 => {
170 cb(er2.code === "ENOENT" ? null : er)
172 options.stat(p, (er3, stats) => {
174 cb(er3.code === "ENOENT" ? null : er)
175 else if (stats.isDirectory())
176 rmdir(p, options, er, cb)
178 options.unlink(p, cb)
183 const fixWinEPERMSync = (p, options, er) => {
188 options.chmodSync(p, 0o666)
190 if (er2.code === "ENOENT")
198 stats = options.statSync(p)
200 if (er3.code === "ENOENT")
206 if (stats.isDirectory())
207 rmdirSync(p, options, er)
209 options.unlinkSync(p)
212 const rmdir = (p, options, originalEr, cb) => {
215 assert(typeof cb === 'function')
217 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
218 // if we guessed wrong, and it's not a directory, then
219 // raise the original error.
220 options.rmdir(p, er => {
221 if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
222 rmkids(p, options, cb)
223 else if (er && er.code === "ENOTDIR")
230 const rmkids = (p, options, cb) => {
233 assert(typeof cb === 'function')
235 options.readdir(p, (er, files) => {
240 return options.rmdir(p, cb)
243 rimraf(path.join(p, f), options, er => {
247 return cb(errState = er)
255 // this looks simpler, and is strictly *faster*, but will
256 // tie up the JavaScript thread and fail on excessively
257 // deep directory trees.
258 const rimrafSync = (p, options) => {
259 options = options || {}
262 assert(p, 'rimraf: missing path')
263 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
264 assert(options, 'rimraf: missing options')
265 assert.equal(typeof options, 'object', 'rimraf: options should be object')
269 if (options.disableGlob || !glob.hasMagic(p)) {
276 results = glob.sync(p, options.glob)
283 for (let i = 0; i < results.length; i++) {
288 st = options.lstatSync(p)
290 if (er.code === "ENOENT")
293 // Windows can EPERM on stat. Life is suffering.
294 if (er.code === "EPERM" && isWindows)
295 fixWinEPERMSync(p, options, er)
299 // sunos lets the root user unlink directories, which is... weird.
300 if (st && st.isDirectory())
301 rmdirSync(p, options, null)
303 options.unlinkSync(p)
305 if (er.code === "ENOENT")
307 if (er.code === "EPERM")
308 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
309 if (er.code !== "EISDIR")
312 rmdirSync(p, options, er)
317 const rmdirSync = (p, options, originalEr) => {
324 if (er.code === "ENOENT")
326 if (er.code === "ENOTDIR")
328 if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
329 rmkidsSync(p, options)
333 const rmkidsSync = (p, options) => {
336 options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
338 // We only end up here once we got ENOTEMPTY at least once, and
339 // at this point, we are guaranteed to have removed all the kids.
340 // So, we know that it won't be ENOENT or ENOTDIR or anything else.
341 // try really hard to delete stuff on windows, because it has a
342 // PROFOUNDLY annoying habit of not closing handles promptly when
343 // files are deleted, resulting in spurious ENOTEMPTY errors.
344 const retries = isWindows ? 100 : 1
349 const ret = options.rmdirSync(p, options)
353 if (++i < retries && threw)
359 module.exports = rimraf
360 rimraf.sync = rimrafSync