poof 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/cli.mjs +551 -590
- package/dist/index.mjs +86 -26
- package/package.json +6 -1
package/dist/index.mjs
CHANGED
|
@@ -1583,10 +1583,13 @@ const concurrentMap = async (items, concurrency, callback) => {
|
|
|
1583
1583
|
await Promise.all(pending);
|
|
1584
1584
|
};
|
|
1585
1585
|
|
|
1586
|
-
const glob = async (root,
|
|
1586
|
+
const glob = async (root, globPatterns, options) => {
|
|
1587
1587
|
const includeDot = options?.dot ?? false;
|
|
1588
|
-
const
|
|
1589
|
-
const
|
|
1588
|
+
const ignorePatterns = options?.ignore;
|
|
1589
|
+
const isMatch = picomatch(globPatterns, { dot: includeDot });
|
|
1590
|
+
const shouldPrune = ignorePatterns?.length ? picomatch(ignorePatterns, { dot: true }) : void 0;
|
|
1591
|
+
const patterns = Array.isArray(globPatterns) ? globPatterns : [globPatterns];
|
|
1592
|
+
const isRecursive = patterns.some((p) => p.includes("**"));
|
|
1590
1593
|
const results = [];
|
|
1591
1594
|
const rootPrefix = root.length + 1;
|
|
1592
1595
|
const crawl = async (directory) => {
|
|
@@ -1595,6 +1598,9 @@ const glob = async (root, globPattern, options) => {
|
|
|
1595
1598
|
for (const entry of entries) {
|
|
1596
1599
|
const fullPath = `${directory}/${entry.name}`;
|
|
1597
1600
|
const relativePath = fullPath.slice(rootPrefix);
|
|
1601
|
+
if (shouldPrune?.(relativePath)) {
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1598
1604
|
if (isMatch(relativePath)) {
|
|
1599
1605
|
results.push(fullPath);
|
|
1600
1606
|
continue;
|
|
@@ -1632,10 +1638,14 @@ const validatePath = (target, cwd, dangerous) => {
|
|
|
1632
1638
|
}
|
|
1633
1639
|
}
|
|
1634
1640
|
};
|
|
1635
|
-
const
|
|
1641
|
+
const needsDotMode = (globPattern) => /^\.[^\\/.]|[{,]\.[^\\/.]/.test(globPattern);
|
|
1642
|
+
const resolvePatterns = async (patterns, options) => {
|
|
1643
|
+
const { cwd, dangerous = false, ignore } = options;
|
|
1636
1644
|
const files = [];
|
|
1637
1645
|
const notFound = [];
|
|
1638
|
-
|
|
1646
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1647
|
+
const explicitPaths = [];
|
|
1648
|
+
for (const pattern of patterns) {
|
|
1639
1649
|
const posixPattern = toPosix(pattern);
|
|
1640
1650
|
const fullPattern = path.isAbsolute(pattern) ? posixPattern : path.posix.join(toPosix(cwd), posixPattern);
|
|
1641
1651
|
const scanned = picomatch.scan(fullPattern);
|
|
@@ -1643,30 +1653,57 @@ const resolvePatterns = async (patterns, cwd, dangerous = false) => {
|
|
|
1643
1653
|
const pathToValidate = scanned.isGlob ? scanned.base || cwd : fullPattern;
|
|
1644
1654
|
validatePath(pathToValidate, cwd, dangerous);
|
|
1645
1655
|
if (!scanned.isGlob) {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
() => {
|
|
1652
|
-
debug$2(`explicit path not found: ${pattern}`);
|
|
1653
|
-
notFound.push(pattern);
|
|
1654
|
-
}
|
|
1655
|
-
);
|
|
1656
|
-
return;
|
|
1656
|
+
explicitPaths.push({
|
|
1657
|
+
fullPath: fullPattern,
|
|
1658
|
+
originalPattern: pattern
|
|
1659
|
+
});
|
|
1660
|
+
continue;
|
|
1657
1661
|
}
|
|
1658
1662
|
let root = scanned.base || toPosix(cwd);
|
|
1659
1663
|
if (root.endsWith("/")) {
|
|
1660
1664
|
root = root.slice(0, -1);
|
|
1661
1665
|
}
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1666
|
+
const group = groups.get(root);
|
|
1667
|
+
if (group) {
|
|
1668
|
+
group.globs.push(scanned.glob);
|
|
1669
|
+
group.needsDot = group.needsDot || needsDotMode(scanned.glob);
|
|
1670
|
+
} else {
|
|
1671
|
+
groups.set(root, {
|
|
1672
|
+
globs: [scanned.glob],
|
|
1673
|
+
needsDot: needsDotMode(scanned.glob)
|
|
1674
|
+
});
|
|
1668
1675
|
}
|
|
1669
|
-
}
|
|
1676
|
+
}
|
|
1677
|
+
await Promise.all([
|
|
1678
|
+
// Check explicit paths concurrently
|
|
1679
|
+
concurrentMap(explicitPaths, GLOB_CONCURRENCY, async ({ fullPath, originalPattern }) => {
|
|
1680
|
+
await fs.access(fullPath).then(
|
|
1681
|
+
() => {
|
|
1682
|
+
debug$2(`explicit path exists: ${fullPath}`);
|
|
1683
|
+
files.push(fullPath);
|
|
1684
|
+
},
|
|
1685
|
+
() => {
|
|
1686
|
+
debug$2(`explicit path not found: ${originalPattern}`);
|
|
1687
|
+
notFound.push(originalPattern);
|
|
1688
|
+
}
|
|
1689
|
+
);
|
|
1690
|
+
}),
|
|
1691
|
+
// Walk globs concurrently (one walk per unique root)
|
|
1692
|
+
concurrentMap([...groups.keys()], GLOB_CONCURRENCY, async (root) => {
|
|
1693
|
+
const group = groups.get(root);
|
|
1694
|
+
const globStart = performance.now();
|
|
1695
|
+
const matches = await glob(root, group.globs, {
|
|
1696
|
+
dot: group.needsDot,
|
|
1697
|
+
ignore
|
|
1698
|
+
});
|
|
1699
|
+
debug$2(
|
|
1700
|
+
`glob root=${root} patterns=${group.globs.length} matches=${matches.length} time=${(performance.now() - globStart).toFixed(2)}ms`
|
|
1701
|
+
);
|
|
1702
|
+
for (const match of matches) {
|
|
1703
|
+
files.push(match);
|
|
1704
|
+
}
|
|
1705
|
+
})
|
|
1706
|
+
]);
|
|
1670
1707
|
return {
|
|
1671
1708
|
files,
|
|
1672
1709
|
notFound
|
|
@@ -1680,7 +1717,9 @@ const startRmWorker = () => {
|
|
|
1680
1717
|
const child = spawn(process.execPath, [rmWorkerPath], {
|
|
1681
1718
|
detached: true,
|
|
1682
1719
|
stdio: ["pipe", "ignore", "ignore"],
|
|
1683
|
-
windowsHide: true
|
|
1720
|
+
windowsHide: true,
|
|
1721
|
+
cwd: "/"
|
|
1722
|
+
// Don't hold reference to parent's cwd (allows directory deletion on Windows)
|
|
1684
1723
|
});
|
|
1685
1724
|
const stdin = child.stdin;
|
|
1686
1725
|
child.unref();
|
|
@@ -1745,14 +1784,35 @@ const withRetry = async (operation, shouldRetry) => {
|
|
|
1745
1784
|
|
|
1746
1785
|
const debug = createDebug("poof:rename");
|
|
1747
1786
|
const RENAME_CONCURRENCY = 100;
|
|
1787
|
+
const filterNestedPaths = (paths) => {
|
|
1788
|
+
if (paths.length === 0) {
|
|
1789
|
+
return [];
|
|
1790
|
+
}
|
|
1791
|
+
const sorted = paths.slice().sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1792
|
+
const roots = [];
|
|
1793
|
+
let lastAccepted;
|
|
1794
|
+
for (const filePath of sorted) {
|
|
1795
|
+
if (lastAccepted && filePath.startsWith(`${lastAccepted}/`)) {
|
|
1796
|
+
continue;
|
|
1797
|
+
}
|
|
1798
|
+
roots.push(filePath);
|
|
1799
|
+
lastAccepted = filePath;
|
|
1800
|
+
}
|
|
1801
|
+
return roots;
|
|
1802
|
+
};
|
|
1748
1803
|
const poof = async (patterns, options) => {
|
|
1749
1804
|
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
1750
1805
|
const cwd = options?.cwd ?? process.cwd();
|
|
1751
1806
|
debug(`patterns: ${JSON.stringify(patternArray)}, cwd: ${cwd}`);
|
|
1752
1807
|
const resolveStart = performance.now();
|
|
1753
|
-
const { files, notFound } = await resolvePatterns(patternArray,
|
|
1808
|
+
const { files, notFound } = await resolvePatterns(patternArray, {
|
|
1809
|
+
cwd,
|
|
1810
|
+
dangerous: options?.dangerous,
|
|
1811
|
+
ignore: options?.ignore
|
|
1812
|
+
});
|
|
1754
1813
|
debug(`resolve files=${files.length} time=${(performance.now() - resolveStart).toFixed(2)}ms`);
|
|
1755
|
-
const filesToDelete = files;
|
|
1814
|
+
const filesToDelete = filterNestedPaths(files);
|
|
1815
|
+
debug(`filtered ${files.length} -> ${filesToDelete.length} (removed ${files.length - filesToDelete.length} nested)`);
|
|
1756
1816
|
const errors = notFound.map((pattern) => {
|
|
1757
1817
|
const error = new Error(`Path not found: ${pattern}`);
|
|
1758
1818
|
error.code = "ENOENT";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poof",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Fast, non-blocking rm -rf alternative. Deletes files instantly while cleanup runs in the background.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rm",
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
"cli"
|
|
12
12
|
],
|
|
13
13
|
"license": "MIT",
|
|
14
|
+
"repository": "privatenumber/poof",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Hiroki Osame",
|
|
17
|
+
"email": "hiroki.osame@gmail.com"
|
|
18
|
+
},
|
|
14
19
|
"files": [
|
|
15
20
|
"dist"
|
|
16
21
|
],
|