UNPKG

fs-extender

Version:
489 lines 19 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.rmSync = exports.emptyDirSync = exports.promises = exports.emptyDir = exports.rm = void 0; const path_extender_1 = __importDefault(require("path-extender")); const fs = __importStar(require("../patch")); const util = __importStar(require("../util")); const mkdirp = __importStar(require("../mkdirp")); const os_1 = require("os"); const list = __importStar(require("../list")); /** @hidden */ const IsWindows = /^win/.test((0, os_1.platform)()); /**@internal */ function getOptions(opt) { return { noPreserveRoot: util.getObjectOption(opt, "noPreserveRoot", false), force: util.getObjectOption(opt, "force", false), maxRetries: util.getObjectOption(opt, "maxRetries", 0), recursive: util.getObjectOption(opt, "recursive", false), retryDelay: util.getObjectOption(opt, "retryDelay", 100), stream: util.getObjectOption(opt, "stream", undefined), }; } /**@internal */ function getOptionsEmptyDir(opt) { return { force: util.getObjectOption(opt, "force", false), maxRetries: util.getObjectOption(opt, "maxRetries", 0), recursive: true, retryDelay: util.getObjectOption(opt, "retryDelay", 100), stream: util.getObjectOption(opt, "stream", undefined), }; } /**@internal */ function writeToStream(options, path, stats, err) { if (options.stream) { const obj = { type: util.getItemTypeName(util.getItemType(stats)), item: path, }; if (err) { obj.error = err; } options.stream.push(JSON.stringify(obj)); } } /**@internal */ function isRootPath(path, options) { const root = path_extender_1.default.parse(path).root; if (util.equal(path, root) && !options.noPreserveRoot) { const e = new Error(`To remove '${root}' the 'noPreserveRoot' must be specified as true in options.`); e.code = "EPERM"; return e; } } function rm(path, options, callback) { const opt = getOptions(options); const cb = util.getCallback(options, callback); path = Buffer.isBuffer(path) ? path : util.fileURLToPath(path); const isRoot = isRootPath(path, opt); if (isRoot) { return cb(isRoot); } _rm(path, opt) .then(() => cb(null)) .catch((err) => cb(err)); } exports.rm = rm; /**@internal */ function _rm(path, options, ignoreFirst = false) { return __awaiter(this, void 0, void 0, function* () { try { const stat = yield fs.promises.lstat(path); /* istanbul ignore next */ if (stat.isDirectory() && !options.recursive) { const err = new Error(`Path is a directory: rm returned EISDIR (is a directory) '${path}'`); err.code = "EISDIR"; throw err; } if (stat.isFile()) { writeToStream(options, path, stat); yield fs.promises.unlink(path); return; } } catch (errStat) { if (errStat.code === "ENOENT" && options.force) { return; } throw errStat; } const items = yield list.promises.list(path); if (ignoreFirst) { items.shift(); } let stackRetry = []; if (items.length === 0) { return; } const itemsNotDir = items.filter((i) => !i.stats.isDirectory()); const itemsIsDir = items.filter((i) => i.stats.isDirectory()); while (itemsNotDir.length) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const item = itemsNotDir.pop(); writeToStream(options, item.path, item.stats); yield fs.promises.unlink(item.path); } /* istanbul ignore next */ while (itemsIsDir.length) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const item = itemsIsDir.pop(); try { writeToStream(options, item.path, item.stats); yield fs.promises.rmdir(item.path); } catch (er) { const err = er; writeToStream(options, item.path, item.stats, err); if (err.code === "ENOENT") { const idx = stackRetry.indexOf(item); if (idx !== -1) { stackRetry.splice(idx, 1); } continue; } if (item.stats.isDirectory()) { if (err.code === "EPERM" && IsWindows) { yield fs.promises.chmod(item.path, 0o666); //re-list for removal itemsIsDir.push(item); } else if (err.code === "ENOTEMPTY" || err.code === "EEXIST" || err.code === "EPERM") { if (stackRetry.indexOf(item) === -1) { stackRetry.push(item); } } else { throw err; } continue; } throw err; } } stackRetry = stackRetry.reverse(); /* istanbul ignore next: only happens when file is blocked by the os system */ if (stackRetry.length > 0) { let retries = 0; function retry(item) { return __awaiter(this, void 0, void 0, function* () { try { writeToStream(options, item.path, item.stats); yield fs.promises.rmdir(item.path); } catch (err) { writeToStream(options, item.path, item.stats, err); if (err.code === "ENOENT") { return; } else if (["EBUSY", "EMFILE", "ENFILE", "ENOTEMPTY", "EPERM"].indexOf(err.code) && retries < options.maxRetries) { retries++; setTimeout(retry, retries * options.retryDelay, item); } else if (retries >= options.maxRetries) { throw err; } } }); } const ps = Array.from(stackRetry).map((p) => retry(p)); yield Promise.all(ps); } if (options.stream /* && !options.stickyStream*/) { options.stream.push(null); } }); } function emptyDir(path, options, callback) { const opt = getOptionsEmptyDir(options); const cb = util.getCallback(options, callback); _emptyDir(path, opt) .then(() => cb(null)) .catch((err) => cb(err)); } exports.emptyDir = emptyDir; /**@internal */ function _emptyDir(path, options) { return __awaiter(this, void 0, void 0, function* () { try { yield fs.promises.readdir(path); } catch (err) { /* istanbul ignore next */ if (err.code !== "EEXIST") { yield mkdirp.promises.mkdirp(path); return; } else { throw err; } } path = Buffer.isBuffer(path) ? path : util.fileURLToPath(path); return _rm(path, getOptionsEmptyDir(options), true); }); } // eslint-disable-next-line @typescript-eslint/no-namespace var promises; (function (promises) { /** * Emulate rm -rf command in node * * ```js * import * as fs from "fs-extender"; * await fs.promises.rm(path); * console.log("item removed with success."); * ``` * * @param path - path to remove * @param options - options * - `noPreserveRoot` - This options prevents accidentally removing the disc root item, default to `false` * If `true` will allow to remove all data in the drive, if no error occur * - `force` - When `true`, exceptions will be ignored if path does not exist. Default: `false`. * - `maxRetries` - If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or `EPERM` error is encountered, Node.js will retry * the operation with a linear backoff wait of retryDelay milliseconds longer on each try. * This option represents the number of retries. This option is ignored if the recursive option is not true. * Default: 0. * - `recursive` - If `true`, perform a recursive directory removal. * In recursive mode, operations are retried on failure. Default: `false`. * - `retryDelay` - The amount of time in milliseconds to wait between retries. * This option is ignored if the recursive option is not true. Default: `100`. * - `stream` - if a stream is passed then it's possible to check the rm process with * ```js * stream.on("data",(chunk:string)=>{ * const obj:StreamOutType = JSON.parse(chunk); * }); * ``` * Note: this doesn't work with `rmSync` or `emptyDirSync` * */ function rm(path, options) { return __awaiter(this, void 0, void 0, function* () { const opt = getOptions(options); path = Buffer.isBuffer(path) ? path : util.fileURLToPath(path); const isRoot = isRootPath(path, opt); if (isRoot) { throw isRoot; } return _rm(path, opt); }); } promises.rm = rm; /** * Delete all items inside a directory * * ```js * import * as fs from "fs-extender"; * await fs.promises.emptyDir(path); * console.log("dir is empty"); * ``` * * @param path - path to remove * @param options - options * - `force` - When `true`, exceptions will be ignored if path does not exist. Default: `false`. * - `maxRetries` - If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or `EPERM` error is encountered, Node.js will retry * the operation with a linear backoff wait of retryDelay milliseconds longer on each try. * This option represents the number of retries. Default: 0. * - `retryDelay` - The amount of time in milliseconds to wait between retries. Default: `100`. * - `stream` - if a stream is passed then it's possible to check the rm process with * ```js * stream.on("data",(chunk:string)=>{ * const obj:StreamOutType = JSON.parse(chunk); * }); * ``` * Note: this doesn't work with `rmSync` or `emptyDirSync` */ function emptyDir(path, options) { return __awaiter(this, void 0, void 0, function* () { const opt = getOptionsEmptyDir(options); return _emptyDir(path, opt); }); } promises.emptyDir = emptyDir; })(promises = exports.promises || (exports.promises = {})); /** * Delete all items inside a directory * * ```js * import * as fs from "fs-extender"; * fs.emptyDirSync(path); * console.log("dir is empty"); * ``` * * @param path - path to remove * @param options - options * - `force` - When `true`, exceptions will be ignored if path does not exist. Default: `false`. * - `maxRetries` - If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or `EPERM` error is encountered, Node.js will retry * the operation with a linear backoff wait of retryDelay milliseconds longer on each try. * This option represents the number of retries. Default: 0. * - `retryDelay` - The amount of time in milliseconds to wait between retries. Default: `100`. */ function emptyDirSync(path, options) { const opt = getOptionsEmptyDir(options); path = Buffer.isBuffer(path) ? path : util.fileURLToPath(path); try { fs.readdirSync(path); } catch (err) { /* istanbul ignore next */ if (err.code !== "EEXIST") { mkdirp.mkdirpSync(path); return; } else { throw err; } } _rmSync(path, opt, true); } exports.emptyDirSync = emptyDirSync; /** * Emulate rm -rf command in node * * ```js * import * as fs from "fs-extender"; * fs.rmSync(path); * console.log("item removed with success."); * ``` * * @param path - path to remove * @param options - options * - `noPreserveRoot` - This options prevents accidentally removing the disc root item, default to `false` * If `true` will allow to remove all data in the drive, if no error occur * - `force` - When `true`, exceptions will be ignored if path does not exist. Default: `false`. * - `maxRetries` - If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or `EPERM` error is encountered, Node.js will retry * the operation with a linear backoff wait of retryDelay milliseconds longer on each try. * This option represents the number of retries. This option is ignored if the recursive option is not true. * Default: 0. * - `recursive` - If `true`, perform a recursive directory removal. * In recursive mode, operations are retried on failure. Default: `false`. * - `retryDelay` - The amount of time in milliseconds to wait between retries. * This option is ignored if the recursive option is not true. Default: `100`. */ function rmSync(path, options) { const opt = getOptions(options); path = Buffer.isBuffer(path) ? path : util.fileURLToPath(path); const isRoot = isRootPath(path, opt); if (isRoot) { throw isRoot; } _rmSync(path, opt); } exports.rmSync = rmSync; /**@internal */ function _rmSync(path, options, ignoreFirst = false) { try { const stat = fs.lstatSync(path); /* istanbul ignore next */ if (stat.isDirectory() && !options.recursive) { const err = new Error(`Path is a directory: rm returned EISDIR (is a directory) '${path}'`); err.code = "EISDIR"; throw err; } /* istanbul ignore next */ if (stat.isFile()) { fs.unlinkSync(path); return; } } catch (errStat) { if (errStat.code === "ENOENT" && options.force) { return; } throw errStat; } const items = list.listSync(path); if (ignoreFirst) { items.shift(); } let stackRetry = []; if (items.length === 0) { return; } const itemsNotDir = items.filter((i) => !i.stats.isDirectory()); const itemsIsDir = items.filter((i) => i.stats.isDirectory()); while (itemsNotDir.length) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const item = itemsNotDir.pop(); fs.unlinkSync(item.path); } /* istanbul ignore next */ while (itemsIsDir.length) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const item = itemsIsDir.pop(); try { fs.rmdirSync(item.path); } catch (er) { const err = er; if (err.code === "ENOENT") { const idx = stackRetry.indexOf(item); if (idx !== -1) { stackRetry.splice(idx, 1); } continue; } if (item.stats.isDirectory()) { if (err.code === "EPERM" && IsWindows) { fs.chmodSync(item.path, 0o666); //re-list for removal itemsIsDir.push(item); } else if (err.code === "ENOTEMPTY" || err.code === "EEXIST" || err.code === "EPERM") { if (stackRetry.indexOf(item) === -1) { stackRetry.push(item); } } else { throw err; } continue; } throw err; } } stackRetry = stackRetry.reverse(); /* istanbul ignore next: only happens when file is blocked by the os system */ while (stackRetry.length) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const p = stackRetry.pop(); //if we enter here we have sticky bastards const tries = options.maxRetries + 1; for (let i = 1; i <= tries; i++) { try { fs.rmdirSync(p.path); } catch (err) { // Only sleep if this is not the last try, and the delay is greater // than zero, and an error was encountered that warrants a retry. if (["EBUSY", "EMFILE", "ENFILE", "ENOTEMPTY", "EPERM"].indexOf(err.code) && i < tries && options.retryDelay > 0) { //poor sleeping const stop = Date.now() + i * options.retryDelay; while (stop < Date.now()) { } //sleep(i * options.retryDelay); } else if (err.code === "ENOENT") { // The file is already gone. continue; } else if (i === tries) { throw err; } } } } } //# sourceMappingURL=index.js.map