source: node_modules/rimraf/rimraf.js@ 65b6638

main
Last change on this file since 65b6638 was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 9.0 KB
Line 
1module.exports = rimraf
2rimraf.sync = rimrafSync
3
4var assert = require("assert")
5var path = require("path")
6var fs = require("fs")
7var glob = undefined
8try {
9 glob = require("glob")
10} catch (_err) {
11 // treat glob as optional.
12}
13var _0666 = parseInt('666', 8)
14
15var defaultGlobOpts = {
16 nosort: true,
17 silent: true
18}
19
20// for EMFILE handling
21var timeout = 0
22
23var isWindows = (process.platform === "win32")
24
25function defaults (options) {
26 var methods = [
27 'unlink',
28 'chmod',
29 'stat',
30 'lstat',
31 'rmdir',
32 'readdir'
33 ]
34 methods.forEach(function(m) {
35 options[m] = options[m] || fs[m]
36 m = m + 'Sync'
37 options[m] = options[m] || fs[m]
38 })
39
40 options.maxBusyTries = options.maxBusyTries || 3
41 options.emfileWait = options.emfileWait || 1000
42 if (options.glob === false) {
43 options.disableGlob = true
44 }
45 if (options.disableGlob !== true && glob === undefined) {
46 throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
47 }
48 options.disableGlob = options.disableGlob || false
49 options.glob = options.glob || defaultGlobOpts
50}
51
52function rimraf (p, options, cb) {
53 if (typeof options === 'function') {
54 cb = options
55 options = {}
56 }
57
58 assert(p, 'rimraf: missing path')
59 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
60 assert.equal(typeof cb, 'function', 'rimraf: callback function required')
61 assert(options, 'rimraf: invalid options argument provided')
62 assert.equal(typeof options, 'object', 'rimraf: options should be object')
63
64 defaults(options)
65
66 var busyTries = 0
67 var errState = null
68 var n = 0
69
70 if (options.disableGlob || !glob.hasMagic(p))
71 return afterGlob(null, [p])
72
73 options.lstat(p, function (er, stat) {
74 if (!er)
75 return afterGlob(null, [p])
76
77 glob(p, options.glob, afterGlob)
78 })
79
80 function next (er) {
81 errState = errState || er
82 if (--n === 0)
83 cb(errState)
84 }
85
86 function afterGlob (er, results) {
87 if (er)
88 return cb(er)
89
90 n = results.length
91 if (n === 0)
92 return cb()
93
94 results.forEach(function (p) {
95 rimraf_(p, options, function CB (er) {
96 if (er) {
97 if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
98 busyTries < options.maxBusyTries) {
99 busyTries ++
100 var time = busyTries * 100
101 // try again, with the same exact callback as this one.
102 return setTimeout(function () {
103 rimraf_(p, options, CB)
104 }, time)
105 }
106
107 // this one won't happen if graceful-fs is used.
108 if (er.code === "EMFILE" && timeout < options.emfileWait) {
109 return setTimeout(function () {
110 rimraf_(p, options, CB)
111 }, timeout ++)
112 }
113
114 // already gone
115 if (er.code === "ENOENT") er = null
116 }
117
118 timeout = 0
119 next(er)
120 })
121 })
122 }
123}
124
125// Two possible strategies.
126// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
127// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
128//
129// Both result in an extra syscall when you guess wrong. However, there
130// are likely far more normal files in the world than directories. This
131// is based on the assumption that a the average number of files per
132// directory is >= 1.
133//
134// If anyone ever complains about this, then I guess the strategy could
135// be made configurable somehow. But until then, YAGNI.
136function rimraf_ (p, options, cb) {
137 assert(p)
138 assert(options)
139 assert(typeof cb === 'function')
140
141 // sunos lets the root user unlink directories, which is... weird.
142 // so we have to lstat here and make sure it's not a dir.
143 options.lstat(p, function (er, st) {
144 if (er && er.code === "ENOENT")
145 return cb(null)
146
147 // Windows can EPERM on stat. Life is suffering.
148 if (er && er.code === "EPERM" && isWindows)
149 fixWinEPERM(p, options, er, cb)
150
151 if (st && st.isDirectory())
152 return rmdir(p, options, er, cb)
153
154 options.unlink(p, function (er) {
155 if (er) {
156 if (er.code === "ENOENT")
157 return cb(null)
158 if (er.code === "EPERM")
159 return (isWindows)
160 ? fixWinEPERM(p, options, er, cb)
161 : rmdir(p, options, er, cb)
162 if (er.code === "EISDIR")
163 return rmdir(p, options, er, cb)
164 }
165 return cb(er)
166 })
167 })
168}
169
170function fixWinEPERM (p, options, er, cb) {
171 assert(p)
172 assert(options)
173 assert(typeof cb === 'function')
174 if (er)
175 assert(er instanceof Error)
176
177 options.chmod(p, _0666, function (er2) {
178 if (er2)
179 cb(er2.code === "ENOENT" ? null : er)
180 else
181 options.stat(p, function(er3, stats) {
182 if (er3)
183 cb(er3.code === "ENOENT" ? null : er)
184 else if (stats.isDirectory())
185 rmdir(p, options, er, cb)
186 else
187 options.unlink(p, cb)
188 })
189 })
190}
191
192function fixWinEPERMSync (p, options, er) {
193 assert(p)
194 assert(options)
195 if (er)
196 assert(er instanceof Error)
197
198 try {
199 options.chmodSync(p, _0666)
200 } catch (er2) {
201 if (er2.code === "ENOENT")
202 return
203 else
204 throw er
205 }
206
207 try {
208 var stats = options.statSync(p)
209 } catch (er3) {
210 if (er3.code === "ENOENT")
211 return
212 else
213 throw er
214 }
215
216 if (stats.isDirectory())
217 rmdirSync(p, options, er)
218 else
219 options.unlinkSync(p)
220}
221
222function rmdir (p, options, originalEr, cb) {
223 assert(p)
224 assert(options)
225 if (originalEr)
226 assert(originalEr instanceof Error)
227 assert(typeof cb === 'function')
228
229 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
230 // if we guessed wrong, and it's not a directory, then
231 // raise the original error.
232 options.rmdir(p, function (er) {
233 if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
234 rmkids(p, options, cb)
235 else if (er && er.code === "ENOTDIR")
236 cb(originalEr)
237 else
238 cb(er)
239 })
240}
241
242function rmkids(p, options, cb) {
243 assert(p)
244 assert(options)
245 assert(typeof cb === 'function')
246
247 options.readdir(p, function (er, files) {
248 if (er)
249 return cb(er)
250 var n = files.length
251 if (n === 0)
252 return options.rmdir(p, cb)
253 var errState
254 files.forEach(function (f) {
255 rimraf(path.join(p, f), options, function (er) {
256 if (errState)
257 return
258 if (er)
259 return cb(errState = er)
260 if (--n === 0)
261 options.rmdir(p, cb)
262 })
263 })
264 })
265}
266
267// this looks simpler, and is strictly *faster*, but will
268// tie up the JavaScript thread and fail on excessively
269// deep directory trees.
270function rimrafSync (p, options) {
271 options = options || {}
272 defaults(options)
273
274 assert(p, 'rimraf: missing path')
275 assert.equal(typeof p, 'string', 'rimraf: path should be a string')
276 assert(options, 'rimraf: missing options')
277 assert.equal(typeof options, 'object', 'rimraf: options should be object')
278
279 var results
280
281 if (options.disableGlob || !glob.hasMagic(p)) {
282 results = [p]
283 } else {
284 try {
285 options.lstatSync(p)
286 results = [p]
287 } catch (er) {
288 results = glob.sync(p, options.glob)
289 }
290 }
291
292 if (!results.length)
293 return
294
295 for (var i = 0; i < results.length; i++) {
296 var p = results[i]
297
298 try {
299 var st = options.lstatSync(p)
300 } catch (er) {
301 if (er.code === "ENOENT")
302 return
303
304 // Windows can EPERM on stat. Life is suffering.
305 if (er.code === "EPERM" && isWindows)
306 fixWinEPERMSync(p, options, er)
307 }
308
309 try {
310 // sunos lets the root user unlink directories, which is... weird.
311 if (st && st.isDirectory())
312 rmdirSync(p, options, null)
313 else
314 options.unlinkSync(p)
315 } catch (er) {
316 if (er.code === "ENOENT")
317 return
318 if (er.code === "EPERM")
319 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
320 if (er.code !== "EISDIR")
321 throw er
322
323 rmdirSync(p, options, er)
324 }
325 }
326}
327
328function rmdirSync (p, options, originalEr) {
329 assert(p)
330 assert(options)
331 if (originalEr)
332 assert(originalEr instanceof Error)
333
334 try {
335 options.rmdirSync(p)
336 } catch (er) {
337 if (er.code === "ENOENT")
338 return
339 if (er.code === "ENOTDIR")
340 throw originalEr
341 if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
342 rmkidsSync(p, options)
343 }
344}
345
346function rmkidsSync (p, options) {
347 assert(p)
348 assert(options)
349 options.readdirSync(p).forEach(function (f) {
350 rimrafSync(path.join(p, f), options)
351 })
352
353 // We only end up here once we got ENOTEMPTY at least once, and
354 // at this point, we are guaranteed to have removed all the kids.
355 // So, we know that it won't be ENOENT or ENOTDIR or anything else.
356 // try really hard to delete stuff on windows, because it has a
357 // PROFOUNDLY annoying habit of not closing handles promptly when
358 // files are deleted, resulting in spurious ENOTEMPTY errors.
359 var retries = isWindows ? 100 : 1
360 var i = 0
361 do {
362 var threw = true
363 try {
364 var ret = options.rmdirSync(p, options)
365 threw = false
366 return ret
367 } finally {
368 if (++i < retries && threw)
369 continue
370 }
371 } while (true)
372}
Note: See TracBrowser for help on using the repository browser.