.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / rimraf / rimraf.js
1 const assert = require("assert")
2 const path = require("path")
3 const fs = require("fs")
4 let glob = undefined
5 try {
6   glob = require("glob")
7 } catch (_err) {
8   // treat glob as optional.
9 }
10
11 const defaultGlobOpts = {
12   nosort: true,
13   silent: true
14 }
15
16 // for EMFILE handling
17 let timeout = 0
18
19 const isWindows = (process.platform === "win32")
20
21 const 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
48 const 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.
129 const 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
163 const 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
183 const 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
212 const 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
230 const 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.
258 const 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
317 const 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
333 const 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
359 module.exports = rimraf
360 rimraf.sync = rimrafSync