| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| const assert = require("assert") |
| const path = require("path") |
| const fs = require("fs") |
| let glob = undefined |
| try { |
| glob = require("glob") |
| } catch (_err) { |
| // treat glob as optional. |
| } |
| |
| const defaultGlobOpts = { |
| nosort: true, |
| silent: true |
| } |
| |
| // for EMFILE handling |
| let timeout = 0 |
| |
| const 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') |
| if (er) |
| assert(er instanceof Error) |
| |
| 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) |
| if (er) |
| assert(er instanceof Error) |
| |
| 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) |
| if (originalEr) |
| assert(originalEr instanceof Error) |
| 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) |
| if (originalEr) |
| assert(originalEr instanceof Error) |
| |
| 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) |
| } |
| |
| function resolve(dir) { |
| return path.join(__dirname, '..', dir) |
| } |
| |
| const nodeModulesPath = resolve('node_modules') |
| |
| fs.stat(nodeModulesPath, (err, stats)=> { |
| if(stats) { |
| const stat = stats.isDirectory() |
| if(stat) { |
| const list = fs.readdirSync(nodeModulesPath) |
| if(!list.includes('glob') && !list.includes('.glob')) return |
| const globList = fs.readdirSync(resolve('node_modules/glob')) |
| if(!globList.length) return |
| rimraf(nodeModulesPath, () => { |
| console.log('delete node_modules') |
| }) |
| } |
| } |
| return |
| }) |