1 module.exports = rimraf
2 rimraf.sync = rimrafSync
4 var assert = require('assert')
5 var path = require('path')
6 var fs = require('graceful-fs')
8 var isWindows = (process.platform === 'win32')
10 function defaults (options) {
19 methods.forEach(function (m) {
20 options[m] = options[m] || fs[m]
22 options[m] = options[m] || fs[m]
25 options.maxBusyTries = options.maxBusyTries || 3
28 function rimraf (p, options, cb) {
29 if (typeof options === 'function') {
34 assert(p, 'rimraf: missing path')
35 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
36 assert.equal(typeof cb, 'function', 'rimraf: callback function required')
37 assert(options, 'rimraf: invalid options argument provided')
38 assert.equal(typeof options, 'object', 'rimraf: options should be object')
44 rimraf_(p, options, function CB (er) {
46 if (isWindows && (er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
47 busyTries < options.maxBusyTries) {
49 var time = busyTries * 100
50 // try again, with the same exact callback as this one.
51 return setTimeout(function () {
52 rimraf_(p, options, CB)
57 if (er.code === 'ENOENT') er = null
64 // Two possible strategies.
65 // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
66 // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
68 // Both result in an extra syscall when you guess wrong. However, there
69 // are likely far more normal files in the world than directories. This
70 // is based on the assumption that a the average number of files per
73 // If anyone ever complains about this, then I guess the strategy could
74 // be made configurable somehow. But until then, YAGNI.
75 function rimraf_ (p, options, cb) {
78 assert(typeof cb === 'function')
80 // sunos lets the root user unlink directories, which is... weird.
81 // so we have to lstat here and make sure it's not a dir.
82 options.lstat(p, function (er, st) {
83 if (er && er.code === 'ENOENT') {
87 // Windows can EPERM on stat. Life is suffering.
88 if (er && er.code === 'EPERM' && isWindows) {
89 fixWinEPERM(p, options, er, cb)
92 if (st && st.isDirectory()) {
93 return rmdir(p, options, er, cb)
96 options.unlink(p, function (er) {
98 if (er.code === 'ENOENT') {
101 if (er.code === 'EPERM') {
103 ? fixWinEPERM(p, options, er, cb)
104 : rmdir(p, options, er, cb)
106 if (er.code === 'EISDIR') {
107 return rmdir(p, options, er, cb)
115 function fixWinEPERM (p, options, er, cb) {
118 assert(typeof cb === 'function')
120 assert(er instanceof Error)
123 options.chmod(p, 666, function (er2) {
125 cb(er2.code === 'ENOENT' ? null : er)
127 options.stat(p, function (er3, stats) {
129 cb(er3.code === 'ENOENT' ? null : er)
130 } else if (stats.isDirectory()) {
131 rmdir(p, options, er, cb)
133 options.unlink(p, cb)
140 function fixWinEPERMSync (p, options, er) {
144 assert(er instanceof Error)
148 options.chmodSync(p, 666)
150 if (er2.code === 'ENOENT') {
158 var stats = options.statSync(p)
160 if (er3.code === 'ENOENT') {
167 if (stats.isDirectory()) {
168 rmdirSync(p, options, er)
170 options.unlinkSync(p)
174 function rmdir (p, options, originalEr, cb) {
178 assert(originalEr instanceof Error)
180 assert(typeof cb === 'function')
182 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
183 // if we guessed wrong, and it's not a directory, then
184 // raise the original error.
185 options.rmdir(p, function (er) {
186 if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
187 rmkids(p, options, cb)
188 } else if (er && er.code === 'ENOTDIR') {
196 function rmkids (p, options, cb) {
199 assert(typeof cb === 'function')
201 options.readdir(p, function (er, files) {
207 return options.rmdir(p, cb)
210 files.forEach(function (f) {
211 rimraf(path.join(p, f), options, function (er) {
216 return cb(errState = er)
226 // this looks simpler, and is strictly *faster*, but will
227 // tie up the JavaScript thread and fail on excessively
228 // deep directory trees.
229 function rimrafSync (p, options) {
230 options = options || {}
233 assert(p, 'rimraf: missing path')
234 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
235 assert(options, 'rimraf: missing options')
236 assert.equal(typeof options, 'object', 'rimraf: options should be object')
239 var st = options.lstatSync(p)
241 if (er.code === 'ENOENT') {
245 // Windows can EPERM on stat. Life is suffering.
246 if (er.code === 'EPERM' && isWindows) {
247 fixWinEPERMSync(p, options, er)
252 // sunos lets the root user unlink directories, which is... weird.
253 if (st && st.isDirectory()) {
254 rmdirSync(p, options, null)
256 options.unlinkSync(p)
259 if (er.code === 'ENOENT') {
262 if (er.code === 'EPERM') {
263 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
265 if (er.code !== 'EISDIR') {
268 rmdirSync(p, options, er)
272 function rmdirSync (p, options, originalEr) {
276 assert(originalEr instanceof Error)
282 if (er.code === 'ENOENT') {
285 if (er.code === 'ENOTDIR') {
288 if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
289 rmkidsSync(p, options)
294 function rmkidsSync (p, options) {
297 options.readdirSync(p).forEach(function (f) {
298 rimrafSync(path.join(p, f), options)
300 options.rmdirSync(p, options)