1 | import fs from 'node:fs';
|
2 | import fsPromises from 'node:fs/promises';
|
3 | import path from 'node:path';
|
4 | import process from 'node:process';
|
5 | import {globby, globbySync} from 'globby';
|
6 | import isGlob from 'is-glob';
|
7 | import isPathCwd from 'is-path-cwd';
|
8 | import isPathInside from 'is-path-inside';
|
9 | import pMap from 'p-map';
|
10 | import slash from 'slash';
|
11 |
|
12 | function safeCheck(file, cwd) {
|
13 | if (isPathCwd(file)) {
|
14 | throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.');
|
15 | }
|
16 |
|
17 | if (!isPathInside(file, cwd)) {
|
18 | throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.');
|
19 | }
|
20 | }
|
21 |
|
22 | function normalizePatterns(patterns) {
|
23 | patterns = Array.isArray(patterns) ? patterns : [patterns];
|
24 |
|
25 | patterns = patterns.map(pattern => {
|
26 | if (process.platform === 'win32' && isGlob(pattern) === false) {
|
27 | return slash(pattern);
|
28 | }
|
29 |
|
30 | return pattern;
|
31 | });
|
32 |
|
33 | return patterns;
|
34 | }
|
35 |
|
36 | export async function deleteAsync(patterns, {force, dryRun, cwd = process.cwd(), onProgress = () => {}, ...options} = {}) {
|
37 | options = {
|
38 | expandDirectories: false,
|
39 | onlyFiles: false,
|
40 | followSymbolicLinks: false,
|
41 | cwd,
|
42 | ...options,
|
43 | };
|
44 |
|
45 | patterns = normalizePatterns(patterns);
|
46 |
|
47 | const paths = await globby(patterns, options);
|
48 | const files = paths.sort((a, b) => b.localeCompare(a));
|
49 |
|
50 | if (files.length === 0) {
|
51 | onProgress({
|
52 | totalCount: 0,
|
53 | deletedCount: 0,
|
54 | percent: 1,
|
55 | });
|
56 | }
|
57 |
|
58 | let deletedCount = 0;
|
59 |
|
60 | const mapper = async file => {
|
61 | file = path.resolve(cwd, file);
|
62 |
|
63 | if (!force) {
|
64 | safeCheck(file, cwd);
|
65 | }
|
66 |
|
67 | if (!dryRun) {
|
68 | await fsPromises.rm(file, {recursive: true, force: true});
|
69 | }
|
70 |
|
71 | deletedCount += 1;
|
72 |
|
73 | onProgress({
|
74 | totalCount: files.length,
|
75 | deletedCount,
|
76 | percent: deletedCount / files.length,
|
77 | path: file,
|
78 | });
|
79 |
|
80 | return file;
|
81 | };
|
82 |
|
83 | const removedFiles = await pMap(files, mapper, options);
|
84 |
|
85 | removedFiles.sort((a, b) => a.localeCompare(b));
|
86 |
|
87 | return removedFiles;
|
88 | }
|
89 |
|
90 | export function deleteSync(patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) {
|
91 | options = {
|
92 | expandDirectories: false,
|
93 | onlyFiles: false,
|
94 | followSymbolicLinks: false,
|
95 | cwd,
|
96 | ...options,
|
97 | };
|
98 |
|
99 | patterns = normalizePatterns(patterns);
|
100 |
|
101 | const files = globbySync(patterns, options)
|
102 | .sort((a, b) => b.localeCompare(a));
|
103 |
|
104 | const removedFiles = files.map(file => {
|
105 | file = path.resolve(cwd, file);
|
106 |
|
107 | if (!force) {
|
108 | safeCheck(file, cwd);
|
109 | }
|
110 |
|
111 | if (!dryRun) {
|
112 | fs.rmSync(file, {recursive: true, force: true});
|
113 | }
|
114 |
|
115 | return file;
|
116 | });
|
117 |
|
118 | removedFiles.sort((a, b) => a.localeCompare(b));
|
119 |
|
120 | return removedFiles;
|
121 | }
|