You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.0 KiB
140 lines
3.0 KiB
2 years ago
|
'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;
|
||
|
};
|