root / HServer / 00.Server / 00.Program / node_modules / rimraf / rimraf.js
이력 | 보기 | 이력해설 | 다운로드 (7.75 KB)
| 1 | 39 | HKM | module.exports = rimraf |
|---|---|---|---|
| 2 | rimraf.sync = rimrafSync |
||
| 3 | |||
| 4 | var assert = require("assert") |
||
| 5 | var path = require("path") |
||
| 6 | var fs = require("fs") |
||
| 7 | var glob = require("glob") |
||
| 8 | |||
| 9 | var globOpts = {
|
||
| 10 | nosort: true, |
||
| 11 | nocomment: true, |
||
| 12 | nonegate: true, |
||
| 13 | silent: true |
||
| 14 | } |
||
| 15 | |||
| 16 | // for EMFILE handling
|
||
| 17 | var timeout = 0 |
||
| 18 | |||
| 19 | var isWindows = (process.platform === "win32") |
||
| 20 | |||
| 21 | function defaults (options) { |
||
| 22 | var methods = [
|
||
| 23 | 'unlink',
|
||
| 24 | 'chmod',
|
||
| 25 | 'stat',
|
||
| 26 | 'lstat',
|
||
| 27 | 'rmdir',
|
||
| 28 | 'readdir'
|
||
| 29 | ] |
||
| 30 | methods.forEach(function(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 | options.disableGlob = options.disableGlob || false
|
||
| 39 | } |
||
| 40 | |||
| 41 | function rimraf (p, options, cb) { |
||
| 42 | if (typeof options === 'function') { |
||
| 43 | cb = options |
||
| 44 | options = {}
|
||
| 45 | } |
||
| 46 | |||
| 47 | assert(p, 'rimraf: missing path')
|
||
| 48 | assert.equal(typeof p, 'string', 'rimraf: path should be a string') |
||
| 49 | assert(options, 'rimraf: missing options')
|
||
| 50 | assert.equal(typeof options, 'object', 'rimraf: options should be object') |
||
| 51 | assert.equal(typeof cb, 'function', 'rimraf: callback function required') |
||
| 52 | |||
| 53 | defaults(options) |
||
| 54 | |||
| 55 | var busyTries = 0 |
||
| 56 | var errState = null |
||
| 57 | var n = 0 |
||
| 58 | |||
| 59 | if (options.disableGlob || !glob.hasMagic(p))
|
||
| 60 | return afterGlob(null, [p]) |
||
| 61 | |||
| 62 | fs.lstat(p, function (er, stat) {
|
||
| 63 | if (!er)
|
||
| 64 | return afterGlob(null, [p]) |
||
| 65 | |||
| 66 | glob(p, globOpts, afterGlob) |
||
| 67 | }) |
||
| 68 | |||
| 69 | function next (er) { |
||
| 70 | errState = errState || er |
||
| 71 | if (--n === 0) |
||
| 72 | cb(errState) |
||
| 73 | } |
||
| 74 | |||
| 75 | function afterGlob (er, results) { |
||
| 76 | if (er)
|
||
| 77 | return cb(er)
|
||
| 78 | |||
| 79 | n = results.length |
||
| 80 | if (n === 0) |
||
| 81 | return cb()
|
||
| 82 | |||
| 83 | results.forEach(function (p) {
|
||
| 84 | rimraf_(p, options, function CB (er) { |
||
| 85 | if (er) {
|
||
| 86 | if (isWindows && (er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && |
||
| 87 | busyTries < options.maxBusyTries) {
|
||
| 88 | busyTries ++ |
||
| 89 | var time = busyTries * 100 |
||
| 90 | // try again, with the same exact callback as this one.
|
||
| 91 | return setTimeout(function () { |
||
| 92 | rimraf_(p, options, CB) |
||
| 93 | }, time) |
||
| 94 | } |
||
| 95 | |||
| 96 | // this one won't happen if graceful-fs is used.
|
||
| 97 | if (er.code === "EMFILE" && timeout < options.emfileWait) { |
||
| 98 | return setTimeout(function () { |
||
| 99 | rimraf_(p, options, CB) |
||
| 100 | }, timeout ++) |
||
| 101 | } |
||
| 102 | |||
| 103 | // already gone
|
||
| 104 | if (er.code === "ENOENT") er = null |
||
| 105 | } |
||
| 106 | |||
| 107 | timeout = 0
|
||
| 108 | next(er) |
||
| 109 | }) |
||
| 110 | }) |
||
| 111 | } |
||
| 112 | } |
||
| 113 | |||
| 114 | // Two possible strategies.
|
||
| 115 | // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
|
||
| 116 | // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
|
||
| 117 | //
|
||
| 118 | // Both result in an extra syscall when you guess wrong. However, there
|
||
| 119 | // are likely far more normal files in the world than directories. This
|
||
| 120 | // is based on the assumption that a the average number of files per
|
||
| 121 | // directory is >= 1.
|
||
| 122 | //
|
||
| 123 | // If anyone ever complains about this, then I guess the strategy could
|
||
| 124 | // be made configurable somehow. But until then, YAGNI.
|
||
| 125 | function rimraf_ (p, options, cb) { |
||
| 126 | assert(p) |
||
| 127 | assert(options) |
||
| 128 | assert(typeof cb === 'function') |
||
| 129 | |||
| 130 | // sunos lets the root user unlink directories, which is... weird.
|
||
| 131 | // so we have to lstat here and make sure it's not a dir.
|
||
| 132 | options.lstat(p, function (er, st) {
|
||
| 133 | if (er && er.code === "ENOENT") |
||
| 134 | return cb(null) |
||
| 135 | |||
| 136 | if (st && st.isDirectory())
|
||
| 137 | return rmdir(p, options, er, cb)
|
||
| 138 | |||
| 139 | options.unlink(p, function (er) {
|
||
| 140 | if (er) {
|
||
| 141 | if (er.code === "ENOENT") |
||
| 142 | return cb(null) |
||
| 143 | if (er.code === "EPERM") |
||
| 144 | return (isWindows)
|
||
| 145 | ? fixWinEPERM(p, options, er, cb) |
||
| 146 | : rmdir(p, options, er, cb) |
||
| 147 | if (er.code === "EISDIR") |
||
| 148 | return rmdir(p, options, er, cb)
|
||
| 149 | } |
||
| 150 | return cb(er)
|
||
| 151 | }) |
||
| 152 | }) |
||
| 153 | } |
||
| 154 | |||
| 155 | function fixWinEPERM (p, options, er, cb) { |
||
| 156 | assert(p) |
||
| 157 | assert(options) |
||
| 158 | assert(typeof cb === 'function') |
||
| 159 | if (er)
|
||
| 160 | assert(er instanceof Error)
|
||
| 161 | |||
| 162 | options.chmod(p, 666, function (er2) { |
||
| 163 | if (er2)
|
||
| 164 | cb(er2.code === "ENOENT" ? null : er) |
||
| 165 | else
|
||
| 166 | options.stat(p, function(er3, stats) {
|
||
| 167 | if (er3)
|
||
| 168 | cb(er3.code === "ENOENT" ? null : er) |
||
| 169 | else if (stats.isDirectory()) |
||
| 170 | rmdir(p, options, er, cb) |
||
| 171 | else
|
||
| 172 | options.unlink(p, cb) |
||
| 173 | }) |
||
| 174 | }) |
||
| 175 | } |
||
| 176 | |||
| 177 | function fixWinEPERMSync (p, options, er) { |
||
| 178 | assert(p) |
||
| 179 | assert(options) |
||
| 180 | if (er)
|
||
| 181 | assert(er instanceof Error)
|
||
| 182 | |||
| 183 | try {
|
||
| 184 | options.chmodSync(p, 666)
|
||
| 185 | } catch (er2) {
|
||
| 186 | if (er2.code === "ENOENT") |
||
| 187 | return
|
||
| 188 | else
|
||
| 189 | throw er
|
||
| 190 | } |
||
| 191 | |||
| 192 | try {
|
||
| 193 | var stats = options.statSync(p)
|
||
| 194 | } catch (er3) {
|
||
| 195 | if (er3.code === "ENOENT") |
||
| 196 | return
|
||
| 197 | else
|
||
| 198 | throw er
|
||
| 199 | } |
||
| 200 | |||
| 201 | if (stats.isDirectory())
|
||
| 202 | rmdirSync(p, options, er) |
||
| 203 | else
|
||
| 204 | options.unlinkSync(p) |
||
| 205 | } |
||
| 206 | |||
| 207 | function rmdir (p, options, originalEr, cb) { |
||
| 208 | assert(p) |
||
| 209 | assert(options) |
||
| 210 | if (originalEr)
|
||
| 211 | assert(originalEr instanceof Error)
|
||
| 212 | assert(typeof cb === 'function') |
||
| 213 | |||
| 214 | // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
|
||
| 215 | // if we guessed wrong, and it's not a directory, then
|
||
| 216 | // raise the original error.
|
||
| 217 | options.rmdir(p, function (er) {
|
||
| 218 | if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) |
||
| 219 | rmkids(p, options, cb) |
||
| 220 | else if (er && er.code === "ENOTDIR") |
||
| 221 | cb(originalEr) |
||
| 222 | else
|
||
| 223 | cb(er) |
||
| 224 | }) |
||
| 225 | } |
||
| 226 | |||
| 227 | function rmkids(p, options, cb) { |
||
| 228 | assert(p) |
||
| 229 | assert(options) |
||
| 230 | assert(typeof cb === 'function') |
||
| 231 | |||
| 232 | options.readdir(p, function (er, files) {
|
||
| 233 | if (er)
|
||
| 234 | return cb(er)
|
||
| 235 | var n = files.length
|
||
| 236 | if (n === 0) |
||
| 237 | return options.rmdir(p, cb)
|
||
| 238 | var errState
|
||
| 239 | files.forEach(function (f) {
|
||
| 240 | rimraf(path.join(p, f), options, function (er) {
|
||
| 241 | if (errState)
|
||
| 242 | return
|
||
| 243 | if (er)
|
||
| 244 | return cb(errState = er)
|
||
| 245 | if (--n === 0) |
||
| 246 | options.rmdir(p, cb) |
||
| 247 | }) |
||
| 248 | }) |
||
| 249 | }) |
||
| 250 | } |
||
| 251 | |||
| 252 | // this looks simpler, and is strictly *faster*, but will
|
||
| 253 | // tie up the JavaScript thread and fail on excessively
|
||
| 254 | // deep directory trees.
|
||
| 255 | function rimrafSync (p, options) { |
||
| 256 | options = options || {}
|
||
| 257 | defaults(options) |
||
| 258 | |||
| 259 | assert(p, 'rimraf: missing path')
|
||
| 260 | assert.equal(typeof p, 'string', 'rimraf: path should be a string') |
||
| 261 | assert(options, 'rimraf: missing options')
|
||
| 262 | assert.equal(typeof options, 'object', 'rimraf: options should be object') |
||
| 263 | |||
| 264 | var results
|
||
| 265 | |||
| 266 | if (options.disableGlob || !glob.hasMagic(p)) {
|
||
| 267 | results = [p] |
||
| 268 | } else {
|
||
| 269 | try {
|
||
| 270 | fs.lstatSync(p) |
||
| 271 | results = [p] |
||
| 272 | } catch (er) {
|
||
| 273 | results = glob.sync(p, globOpts) |
||
| 274 | } |
||
| 275 | } |
||
| 276 | |||
| 277 | if (!results.length)
|
||
| 278 | return
|
||
| 279 | |||
| 280 | for (var i = 0; i < results.length; i++) { |
||
| 281 | var p = results[i]
|
||
| 282 | |||
| 283 | try {
|
||
| 284 | var st = options.lstatSync(p)
|
||
| 285 | } catch (er) {
|
||
| 286 | if (er.code === "ENOENT") |
||
| 287 | return
|
||
| 288 | } |
||
| 289 | |||
| 290 | try {
|
||
| 291 | // sunos lets the root user unlink directories, which is... weird.
|
||
| 292 | if (st && st.isDirectory())
|
||
| 293 | rmdirSync(p, options, null)
|
||
| 294 | else
|
||
| 295 | options.unlinkSync(p) |
||
| 296 | } catch (er) {
|
||
| 297 | if (er.code === "ENOENT") |
||
| 298 | return
|
||
| 299 | if (er.code === "EPERM") |
||
| 300 | return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
|
||
| 301 | if (er.code !== "EISDIR") |
||
| 302 | throw er
|
||
| 303 | rmdirSync(p, options, er) |
||
| 304 | } |
||
| 305 | } |
||
| 306 | } |
||
| 307 | |||
| 308 | function rmdirSync (p, options, originalEr) { |
||
| 309 | assert(p) |
||
| 310 | assert(options) |
||
| 311 | if (originalEr)
|
||
| 312 | assert(originalEr instanceof Error)
|
||
| 313 | |||
| 314 | try {
|
||
| 315 | options.rmdirSync(p) |
||
| 316 | } catch (er) {
|
||
| 317 | if (er.code === "ENOENT") |
||
| 318 | return
|
||
| 319 | if (er.code === "ENOTDIR") |
||
| 320 | throw originalEr
|
||
| 321 | if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") |
||
| 322 | rmkidsSync(p, options) |
||
| 323 | } |
||
| 324 | } |
||
| 325 | |||
| 326 | function rmkidsSync (p, options) { |
||
| 327 | assert(p) |
||
| 328 | assert(options) |
||
| 329 | options.readdirSync(p).forEach(function (f) {
|
||
| 330 | rimrafSync(path.join(p, f), options) |
||
| 331 | }) |
||
| 332 | options.rmdirSync(p, options) |
||
| 333 | } |