source: trip-planner-front/node_modules/fs-extra/lib/remove/rimraf.js@ 188ee53

Last change on this file since 188ee53 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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