]> git.r.bdr.sh - rbdr/dotfiles/blob
eb96c46afde6d782568cd709578f790827aa1991
[rbdr/dotfiles] /
1 module.exports = rimraf
2 rimraf.sync = rimrafSync
3
4 var assert = require("assert")
5 var path = require("path")
6 var fs = require("fs")
7
8 // for EMFILE handling
9 var timeout = 0
10 exports.EMFILE_MAX = 1000
11 exports.BUSYTRIES_MAX = 3
12
13 var isWindows = (process.platform === "win32")
14
15 function defaults (options) {
16 var methods = [
17 'unlink',
18 'chmod',
19 'stat',
20 'rmdir',
21 'readdir'
22 ]
23 methods.forEach(function(m) {
24 options[m] = options[m] || fs[m]
25 m = m + 'Sync'
26 options[m] = options[m] || fs[m]
27 })
28 }
29
30 function rimraf (p, options, cb) {
31 if (typeof options === 'function') {
32 cb = options
33 options = {}
34 }
35 assert(p)
36 assert(options)
37 assert(typeof cb === 'function')
38
39 defaults(options)
40
41 if (!cb) throw new Error("No callback passed to rimraf()")
42
43 var busyTries = 0
44 rimraf_(p, options, function CB (er) {
45 if (er) {
46 if (isWindows && (er.code === "EBUSY" || er.code === "ENOTEMPTY") &&
47 busyTries < exports.BUSYTRIES_MAX) {
48 busyTries ++
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)
53 }, time)
54 }
55
56 // this one won't happen if graceful-fs is used.
57 if (er.code === "EMFILE" && timeout < exports.EMFILE_MAX) {
58 return setTimeout(function () {
59 rimraf_(p, options, CB)
60 }, timeout ++)
61 }
62
63 // already gone
64 if (er.code === "ENOENT") er = null
65 }
66
67 timeout = 0
68 cb(er)
69 })
70 }
71
72 // Two possible strategies.
73 // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
74 // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
75 //
76 // Both result in an extra syscall when you guess wrong. However, there
77 // are likely far more normal files in the world than directories. This
78 // is based on the assumption that a the average number of files per
79 // directory is >= 1.
80 //
81 // If anyone ever complains about this, then I guess the strategy could
82 // be made configurable somehow. But until then, YAGNI.
83 function rimraf_ (p, options, cb) {
84 assert(p)
85 assert(options)
86 assert(typeof cb === 'function')
87
88 options.unlink(p, function (er) {
89 if (er) {
90 if (er.code === "ENOENT")
91 return cb(null)
92 if (er.code === "EPERM")
93 return (isWindows)
94 ? fixWinEPERM(p, options, er, cb)
95 : rmdir(p, options, er, cb)
96 if (er.code === "EISDIR")
97 return rmdir(p, options, er, cb)
98 }
99 return cb(er)
100 })
101 }
102
103 function fixWinEPERM (p, options, er, cb) {
104 assert(p)
105 assert(options)
106 assert(typeof cb === 'function')
107 if (er)
108 assert(er instanceof Error)
109
110 options.chmod(p, 666, function (er2) {
111 if (er2)
112 cb(er2.code === "ENOENT" ? null : er)
113 else
114 options.stat(p, function(er3, stats) {
115 if (er3)
116 cb(er3.code === "ENOENT" ? null : er)
117 else if (stats.isDirectory())
118 rmdir(p, options, er, cb)
119 else
120 options.unlink(p, cb)
121 })
122 })
123 }
124
125 function fixWinEPERMSync (p, options, er) {
126 assert(p)
127 assert(options)
128 if (er)
129 assert(er instanceof Error)
130
131 try {
132 options.chmodSync(p, 666)
133 } catch (er2) {
134 if (er2.code === "ENOENT")
135 return
136 else
137 throw er
138 }
139
140 try {
141 var stats = options.statSync(p)
142 } catch (er3) {
143 if (er3.code === "ENOENT")
144 return
145 else
146 throw er
147 }
148
149 if (stats.isDirectory())
150 rmdirSync(p, options, er)
151 else
152 options.unlinkSync(p)
153 }
154
155 function rmdir (p, options, originalEr, cb) {
156 assert(p)
157 assert(options)
158 if (originalEr)
159 assert(originalEr instanceof Error)
160 assert(typeof cb === 'function')
161
162 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
163 // if we guessed wrong, and it's not a directory, then
164 // raise the original error.
165 options.rmdir(p, function (er) {
166 if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
167 rmkids(p, options, cb)
168 else if (er && er.code === "ENOTDIR")
169 cb(originalEr)
170 else
171 cb(er)
172 })
173 }
174
175 function rmkids(p, options, cb) {
176 assert(p)
177 assert(options)
178 assert(typeof cb === 'function')
179
180 options.readdir(p, function (er, files) {
181 if (er)
182 return cb(er)
183 var n = files.length
184 if (n === 0)
185 return options.rmdir(p, cb)
186 var errState
187 files.forEach(function (f) {
188 rimraf(path.join(p, f), options, function (er) {
189 if (errState)
190 return
191 if (er)
192 return cb(errState = er)
193 if (--n === 0)
194 options.rmdir(p, cb)
195 })
196 })
197 })
198 }
199
200 // this looks simpler, and is strictly *faster*, but will
201 // tie up the JavaScript thread and fail on excessively
202 // deep directory trees.
203 function rimrafSync (p, options) {
204 options = options || {}
205 defaults(options)
206
207 assert(p)
208 assert(options)
209
210 try {
211 options.unlinkSync(p)
212 } catch (er) {
213 if (er.code === "ENOENT")
214 return
215 if (er.code === "EPERM")
216 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
217 if (er.code !== "EISDIR")
218 throw er
219 rmdirSync(p, options, er)
220 }
221 }
222
223 function rmdirSync (p, options, originalEr) {
224 assert(p)
225 assert(options)
226 if (originalEr)
227 assert(originalEr instanceof Error)
228
229 try {
230 options.rmdirSync(p)
231 } catch (er) {
232 if (er.code === "ENOENT")
233 return
234 if (er.code === "ENOTDIR")
235 throw originalEr
236 if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
237 rmkidsSync(p, options)
238 }
239 }
240
241 function rmkidsSync (p, options) {
242 assert(p)
243 assert(options)
244 options.readdirSync(p).forEach(function (f) {
245 rimrafSync(path.join(p, f), options)
246 })
247 options.rmdirSync(p, options)
248 }