source: imaps-frontend/node_modules/rimraf/rimraf.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.7 KB
RevLine 
[d565449]1const assert = require("assert")
2const path = require("path")
3const fs = require("fs")
4let glob = undefined
5try {
6 glob = require("glob")
7} catch (_err) {
8 // treat glob as optional.
9}
10
11const defaultGlobOpts = {
12 nosort: true,
13 silent: true
14}
15
16// for EMFILE handling
17let timeout = 0
18
19const isWindows = (process.platform === "win32")
20
21const defaults = options => {
22 const methods = [
23 'unlink',
24 'chmod',
25 'stat',
26 'lstat',
27 'rmdir',
28 'readdir'
29 ]
30 methods.forEach(m => {
31 options[m] = options[m] || fs[m]
32 m = m + 'Sync'
33 options[m] = options[m] || fs[m]
34 })
35
36 options.maxBusyTries = options.maxBusyTries || 3
37 options.emfileWait = options.emfileWait || 1000
38 if (options.glob === false) {
39 options.disableGlob = true
40 }
41 if (options.disableGlob !== true && glob === undefined) {
42 throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
43 }
44 options.disableGlob = options.disableGlob || false
45 options.glob = options.glob || defaultGlobOpts
46}
47
48const rimraf = (p, options, cb) => {
49 if (typeof options === 'function') {
50 cb = options
51 options = {}
52 }
53
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')
59
60 defaults(options)
61
62 let busyTries = 0
63 let errState = null
64 let n = 0
65
66 const next = (er) => {
67 errState = errState || er
68 if (--n === 0)
69 cb(errState)
70 }
71
72 const afterGlob = (er, results) => {
73 if (er)
74 return cb(er)
75
76 n = results.length
77 if (n === 0)
78 return cb()
79
80 results.forEach(p => {
81 const CB = (er) => {
82 if (er) {
83 if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
84 busyTries < options.maxBusyTries) {
85 busyTries ++
86 // try again, with the same exact callback as this one.
87 return setTimeout(() => rimraf_(p, options, CB), busyTries * 100)
88 }
89
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 ++)
93 }
94
95 // already gone
96 if (er.code === "ENOENT") er = null
97 }
98
99 timeout = 0
100 next(er)
101 }
102 rimraf_(p, options, CB)
103 })
104 }
105
106 if (options.disableGlob || !glob.hasMagic(p))
107 return afterGlob(null, [p])
108
109 options.lstat(p, (er, stat) => {
110 if (!er)
111 return afterGlob(null, [p])
112
113 glob(p, options.glob, afterGlob)
114 })
115
116}
117
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
121//
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.
126//
127// If anyone ever complains about this, then I guess the strategy could
128// be made configurable somehow. But until then, YAGNI.
129const rimraf_ = (p, options, cb) => {
130 assert(p)
131 assert(options)
132 assert(typeof cb === 'function')
133
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")
138 return cb(null)
139
140 // Windows can EPERM on stat. Life is suffering.
141 if (er && er.code === "EPERM" && isWindows)
142 fixWinEPERM(p, options, er, cb)
143
144 if (st && st.isDirectory())
145 return rmdir(p, options, er, cb)
146
147 options.unlink(p, er => {
148 if (er) {
149 if (er.code === "ENOENT")
150 return cb(null)
151 if (er.code === "EPERM")
152 return (isWindows)
153 ? fixWinEPERM(p, options, er, cb)
154 : rmdir(p, options, er, cb)
155 if (er.code === "EISDIR")
156 return rmdir(p, options, er, cb)
157 }
158 return cb(er)
159 })
160 })
161}
162
163const fixWinEPERM = (p, options, er, cb) => {
164 assert(p)
165 assert(options)
166 assert(typeof cb === 'function')
167
168 options.chmod(p, 0o666, er2 => {
169 if (er2)
170 cb(er2.code === "ENOENT" ? null : er)
171 else
172 options.stat(p, (er3, stats) => {
173 if (er3)
174 cb(er3.code === "ENOENT" ? null : er)
175 else if (stats.isDirectory())
176 rmdir(p, options, er, cb)
177 else
178 options.unlink(p, cb)
179 })
180 })
181}
182
183const fixWinEPERMSync = (p, options, er) => {
184 assert(p)
185 assert(options)
186
187 try {
188 options.chmodSync(p, 0o666)
189 } catch (er2) {
190 if (er2.code === "ENOENT")
191 return
192 else
193 throw er
194 }
195
196 let stats
197 try {
198 stats = options.statSync(p)
199 } catch (er3) {
200 if (er3.code === "ENOENT")
201 return
202 else
203 throw er
204 }
205
206 if (stats.isDirectory())
207 rmdirSync(p, options, er)
208 else
209 options.unlinkSync(p)
210}
211
212const rmdir = (p, options, originalEr, cb) => {
213 assert(p)
214 assert(options)
215 assert(typeof cb === 'function')
216
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")
224 cb(originalEr)
225 else
226 cb(er)
227 })
228}
229
230const rmkids = (p, options, cb) => {
231 assert(p)
232 assert(options)
233 assert(typeof cb === 'function')
234
235 options.readdir(p, (er, files) => {
236 if (er)
237 return cb(er)
238 let n = files.length
239 if (n === 0)
240 return options.rmdir(p, cb)
241 let errState
242 files.forEach(f => {
243 rimraf(path.join(p, f), options, er => {
244 if (errState)
245 return
246 if (er)
247 return cb(errState = er)
248 if (--n === 0)
249 options.rmdir(p, cb)
250 })
251 })
252 })
253}
254
255// this looks simpler, and is strictly *faster*, but will
256// tie up the JavaScript thread and fail on excessively
257// deep directory trees.
258const rimrafSync = (p, options) => {
259 options = options || {}
260 defaults(options)
261
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')
266
267 let results
268
269 if (options.disableGlob || !glob.hasMagic(p)) {
270 results = [p]
271 } else {
272 try {
273 options.lstatSync(p)
274 results = [p]
275 } catch (er) {
276 results = glob.sync(p, options.glob)
277 }
278 }
279
280 if (!results.length)
281 return
282
283 for (let i = 0; i < results.length; i++) {
284 const p = results[i]
285
286 let st
287 try {
288 st = options.lstatSync(p)
289 } catch (er) {
290 if (er.code === "ENOENT")
291 return
292
293 // Windows can EPERM on stat. Life is suffering.
294 if (er.code === "EPERM" && isWindows)
295 fixWinEPERMSync(p, options, er)
296 }
297
298 try {
299 // sunos lets the root user unlink directories, which is... weird.
300 if (st && st.isDirectory())
301 rmdirSync(p, options, null)
302 else
303 options.unlinkSync(p)
304 } catch (er) {
305 if (er.code === "ENOENT")
306 return
307 if (er.code === "EPERM")
308 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
309 if (er.code !== "EISDIR")
310 throw er
311
312 rmdirSync(p, options, er)
313 }
314 }
315}
316
317const rmdirSync = (p, options, originalEr) => {
318 assert(p)
319 assert(options)
320
321 try {
322 options.rmdirSync(p)
323 } catch (er) {
324 if (er.code === "ENOENT")
325 return
326 if (er.code === "ENOTDIR")
327 throw originalEr
328 if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
329 rmkidsSync(p, options)
330 }
331}
332
333const rmkidsSync = (p, options) => {
334 assert(p)
335 assert(options)
336 options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
337
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
345 let i = 0
346 do {
347 let threw = true
348 try {
349 const ret = options.rmdirSync(p, options)
350 threw = false
351 return ret
352 } finally {
353 if (++i < retries && threw)
354 continue
355 }
356 } while (true)
357}
358
359module.exports = rimraf
360rimraf.sync = rimrafSync
Note: See TracBrowser for help on using the repository browser.