'use strict'; const {promisify} = require('util'); const path = require('path'); const globby = require('globby'); const isGlob = require('is-glob'); const slash = require('slash'); const gracefulFs = require('graceful-fs'); const isPathCwd = require('is-path-cwd'); const isPathInside = require('is-path-inside'); const rimraf = require('rimraf'); const pMap = require('p-map'); const rimrafP = promisify(rimraf); const rimrafOptions = { glob: false, unlink: gracefulFs.unlink, unlinkSync: gracefulFs.unlinkSync, chmod: gracefulFs.chmod, chmodSync: gracefulFs.chmodSync, stat: gracefulFs.stat, statSync: gracefulFs.statSync, lstat: gracefulFs.lstat, lstatSync: gracefulFs.lstatSync, rmdir: gracefulFs.rmdir, rmdirSync: gracefulFs.rmdirSync, readdir: gracefulFs.readdir, readdirSync: gracefulFs.readdirSync }; function safeCheck(file, cwd) { if (isPathCwd(file)) { throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.'); } if (!isPathInside(file, cwd)) { throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'); } } function normalizePatterns(patterns) { patterns = Array.isArray(patterns) ? patterns : [patterns]; patterns = patterns.map(pattern => { if (process.platform === 'win32' && isGlob(pattern) === false) { return slash(pattern); } return pattern; }); return patterns; } module.exports = async (patterns, {force, dryRun, cwd = process.cwd(), onProgress = () => {}, ...options} = {}) => { options = { expandDirectories: false, onlyFiles: false, followSymbolicLinks: false, cwd, ...options }; patterns = normalizePatterns(patterns); const files = (await globby(patterns, options)) .sort((a, b) => b.localeCompare(a)); if (files.length === 0) { onProgress({ totalCount: 0, deletedCount: 0, percent: 1 }); } let deletedCount = 0; const mapper = async file => { file = path.resolve(cwd, file); if (!force) { safeCheck(file, cwd); } if (!dryRun) { await rimrafP(file, rimrafOptions); } deletedCount += 1; onProgress({ totalCount: files.length, deletedCount, percent: deletedCount / files.length }); return file; }; const removedFiles = await pMap(files, mapper, options); removedFiles.sort((a, b) => a.localeCompare(b)); return removedFiles; }; module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => { options = { expandDirectories: false, onlyFiles: false, followSymbolicLinks: false, cwd, ...options }; patterns = normalizePatterns(patterns); const files = globby.sync(patterns, options) .sort((a, b) => b.localeCompare(a)); const removedFiles = files.map(file => { file = path.resolve(cwd, file); if (!force) { safeCheck(file, cwd); } if (!dryRun) { rimraf.sync(file, rimrafOptions); } return file; }); removedFiles.sort((a, b) => a.localeCompare(b)); return removedFiles; };