| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 | const assert = require("assert")const path = require("path")const fs = require("fs")let glob = undefinedtry {  glob = require("glob")} catch (_err) {  // treat glob as optional.}const defaultGlobOpts = {  nosort: true,  silent: true}// for EMFILE handlinglet timeout = 0const isWindows = (process.platform === "win32")const 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  options.emfileWait = options.emfileWait || 1000  if (options.glob === false) {    options.disableGlob = true  }  if (options.disableGlob !== true && glob === undefined) {    throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')  }  options.disableGlob = options.disableGlob || false  options.glob = options.glob || defaultGlobOpts}const rimraf = (p, options, cb) => {  if (typeof options === 'function') {    cb = options    options = {}  }  assert(p, 'rimraf: missing path')  assert.equal(typeof p, 'string', 'rimraf: path should be a string')  assert.equal(typeof cb, 'function', 'rimraf: callback function required')  assert(options, 'rimraf: invalid options argument provided')  assert.equal(typeof options, 'object', 'rimraf: options should be object')  defaults(options)  let busyTries = 0  let errState = null  let n = 0  const next = (er) => {    errState = errState || er    if (--n === 0)      cb(errState)  }  const afterGlob = (er, results) => {    if (er)      return cb(er)    n = results.length    if (n === 0)      return cb()    results.forEach(p => {      const CB = (er) => {        if (er) {          if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&              busyTries < options.maxBusyTries) {            busyTries ++            // try again, with the same exact callback as this one.            return setTimeout(() => rimraf_(p, options, CB), busyTries * 100)          }          // this one won't happen if graceful-fs is used.          if (er.code === "EMFILE" && timeout < options.emfileWait) {            return setTimeout(() => rimraf_(p, options, CB), timeout ++)          }          // already gone          if (er.code === "ENOENT") er = null        }        timeout = 0        next(er)      }      rimraf_(p, options, CB)    })  }  if (options.disableGlob || !glob.hasMagic(p))    return afterGlob(null, [p])  options.lstat(p, (er, stat) => {    if (!er)      return afterGlob(null, [p])    glob(p, options.glob, afterGlob)  })}// 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.const 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)      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)    })  })}const 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)      })  })}const fixWinEPERMSync = (p, options, er) => {  assert(p)  assert(options)  try {    options.chmodSync(p, 0o666)  } catch (er2) {    if (er2.code === "ENOENT")      return    else      throw er  }  let stats  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)}const 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)  })}const 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    if (n === 0)      return options.rmdir(p, cb)    let errState    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.const rimrafSync = (p, options) => {  options = options || {}  defaults(options)  assert(p, 'rimraf: missing path')  assert.equal(typeof p, 'string', 'rimraf: path should be a string')  assert(options, 'rimraf: missing options')  assert.equal(typeof options, 'object', 'rimraf: options should be object')  let results  if (options.disableGlob || !glob.hasMagic(p)) {    results = [p]  } else {    try {      options.lstatSync(p)      results = [p]    } catch (er) {      results = glob.sync(p, options.glob)    }  }  if (!results.length)    return  for (let i = 0; i < results.length; i++) {    const p = results[i]    let st    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      if (er.code === "EPERM")        return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)      if (er.code !== "EISDIR")        throw er      rmdirSync(p, options, er)    }  }}const rmdirSync = (p, options, originalEr) => {  assert(p)  assert(options)  try {    options.rmdirSync(p)  } catch (er) {    if (er.code === "ENOENT")      return    if (er.code === "ENOTDIR")      throw originalEr    if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")      rmkidsSync(p, options)  }}const rmkidsSync = (p, options) => {  assert(p)  assert(options)  options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))  // 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 retries = isWindows ? 100 : 1  let i = 0  do {    let threw = true    try {      const ret = options.rmdirSync(p, options)      threw = false      return ret    } finally {      if (++i < retries && threw)        continue    }  } while (true)}module.exports = rimrafrimraf.sync = rimrafSync
 |