| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 | 'use strict'const fs = require('graceful-fs')const path = require('path')const assert = require('assert')const isWindows = (process.platform === 'win32')function defaults (options) {  const methods = [    'unlink',    'chmod',    'stat',    'lstat',    'rmdir',    'readdir'  ]  methods.forEach(m => {    options[m] = options[m] || fs[m]    m = m + 'Sync'    options[m] = options[m] || fs[m]  })  options.maxBusyTries = options.maxBusyTries || 3}function rimraf (p, options, cb) {  let busyTries = 0  if (typeof options === 'function') {    cb = options    options = {}  }  assert(p, 'rimraf: missing path')  assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')  assert.strictEqual(typeof cb, 'function', 'rimraf: callback function required')  assert(options, 'rimraf: invalid options argument provided')  assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')  defaults(options)  rimraf_(p, options, function CB (er) {    if (er) {      if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&          busyTries < options.maxBusyTries) {        busyTries++        const time = busyTries * 100        // try again, with the same exact callback as this one.        return setTimeout(() => rimraf_(p, options, CB), time)      }      // already gone      if (er.code === 'ENOENT') er = null    }    cb(er)  })}// Two possible strategies.// 1. Assume it's a file.  unlink it, then do the dir stuff on EPERM or EISDIR// 2. Assume it's a directory.  readdir, then do the file stuff on ENOTDIR//// Both result in an extra syscall when you guess wrong.  However, there// are likely far more normal files in the world than directories.  This// is based on the assumption that a the average number of files per// directory is >= 1.//// If anyone ever complains about this, then I guess the strategy could// be made configurable somehow.  But until then, YAGNI.function rimraf_ (p, options, cb) {  assert(p)  assert(options)  assert(typeof cb === 'function')  // sunos lets the root user unlink directories, which is... weird.  // so we have to lstat here and make sure it's not a dir.  options.lstat(p, (er, st) => {    if (er && er.code === 'ENOENT') {      return cb(null)    }    // Windows can EPERM on stat.  Life is suffering.    if (er && er.code === 'EPERM' && isWindows) {      return fixWinEPERM(p, options, er, cb)    }    if (st && st.isDirectory()) {      return rmdir(p, options, er, cb)    }    options.unlink(p, er => {      if (er) {        if (er.code === 'ENOENT') {          return cb(null)        }        if (er.code === 'EPERM') {          return (isWindows)            ? fixWinEPERM(p, options, er, cb)            : rmdir(p, options, er, cb)        }        if (er.code === 'EISDIR') {          return rmdir(p, options, er, cb)        }      }      return cb(er)    })  })}function fixWinEPERM (p, options, er, cb) {  assert(p)  assert(options)  assert(typeof cb === 'function')  options.chmod(p, 0o666, er2 => {    if (er2) {      cb(er2.code === 'ENOENT' ? null : er)    } else {      options.stat(p, (er3, stats) => {        if (er3) {          cb(er3.code === 'ENOENT' ? null : er)        } else if (stats.isDirectory()) {          rmdir(p, options, er, cb)        } else {          options.unlink(p, cb)        }      })    }  })}function fixWinEPERMSync (p, options, er) {  let stats  assert(p)  assert(options)  try {    options.chmodSync(p, 0o666)  } catch (er2) {    if (er2.code === 'ENOENT') {      return    } else {      throw er    }  }  try {    stats = options.statSync(p)  } catch (er3) {    if (er3.code === 'ENOENT') {      return    } else {      throw er    }  }  if (stats.isDirectory()) {    rmdirSync(p, options, er)  } else {    options.unlinkSync(p)  }}function rmdir (p, options, originalEr, cb) {  assert(p)  assert(options)  assert(typeof cb === 'function')  // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)  // if we guessed wrong, and it's not a directory, then  // raise the original error.  options.rmdir(p, er => {    if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {      rmkids(p, options, cb)    } else if (er && er.code === 'ENOTDIR') {      cb(originalEr)    } else {      cb(er)    }  })}function rmkids (p, options, cb) {  assert(p)  assert(options)  assert(typeof cb === 'function')  options.readdir(p, (er, files) => {    if (er) return cb(er)    let n = files.length    let errState    if (n === 0) return options.rmdir(p, cb)    files.forEach(f => {      rimraf(path.join(p, f), options, er => {        if (errState) {          return        }        if (er) return cb(errState = er)        if (--n === 0) {          options.rmdir(p, cb)        }      })    })  })}// this looks simpler, and is strictly *faster*, but will// tie up the JavaScript thread and fail on excessively// deep directory trees.function rimrafSync (p, options) {  let st  options = options || {}  defaults(options)  assert(p, 'rimraf: missing path')  assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')  assert(options, 'rimraf: missing options')  assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')  try {    st = options.lstatSync(p)  } catch (er) {    if (er.code === 'ENOENT') {      return    }    // Windows can EPERM on stat.  Life is suffering.    if (er.code === 'EPERM' && isWindows) {      fixWinEPERMSync(p, options, er)    }  }  try {    // sunos lets the root user unlink directories, which is... weird.    if (st && st.isDirectory()) {      rmdirSync(p, options, null)    } else {      options.unlinkSync(p)    }  } catch (er) {    if (er.code === 'ENOENT') {      return    } else if (er.code === 'EPERM') {      return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)    } else if (er.code !== 'EISDIR') {      throw er    }    rmdirSync(p, options, er)  }}function rmdirSync (p, options, originalEr) {  assert(p)  assert(options)  try {    options.rmdirSync(p)  } catch (er) {    if (er.code === 'ENOTDIR') {      throw originalEr    } else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {      rmkidsSync(p, options)    } else if (er.code !== 'ENOENT') {      throw er    }  }}function rmkidsSync (p, options) {  assert(p)  assert(options)  options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))  if (isWindows) {    // We only end up here once we got ENOTEMPTY at least once, and    // at this point, we are guaranteed to have removed all the kids.    // So, we know that it won't be ENOENT or ENOTDIR or anything else.    // try really hard to delete stuff on windows, because it has a    // PROFOUNDLY annoying habit of not closing handles promptly when    // files are deleted, resulting in spurious ENOTEMPTY errors.    const startTime = Date.now()    do {      try {        const ret = options.rmdirSync(p, options)        return ret      } catch {}    } while (Date.now() - startTime < 500) // give up after 500ms  } else {    const ret = options.rmdirSync(p, options)    return ret  }}module.exports = rimrafrimraf.sync = rimrafSync
 |