repo-clean 1.0.2 → 1.2.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 +18 -1
- package/bin/repo-clean.js +46 -130
- package/lib/args.js +135 -0
- package/lib/cleanup.js +85 -0
- package/lib/confirm.js +37 -0
- package/lib/targets.js +74 -0
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -22,10 +22,14 @@ repo-clean
|
|
|
22
22
|
Use the command patterns below with either `npx repo-clean` or `repo-clean` (after global install).
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
repo-clean [--dry-run] [--node-modules] [--pm-cache] [--logs] [--editor] [--tmp]
|
|
25
|
+
repo-clean [--dry-run] [--node-modules] [--pm-cache] [--logs] [--editor] [--tmp] [--force]
|
|
26
26
|
repo-clean --all [--keep-nm] [--dry-run]
|
|
27
|
+
repo-clean --help | -h
|
|
28
|
+
repo-clean --version | -v
|
|
27
29
|
```
|
|
28
30
|
|
|
31
|
+
`repo-clean` accepts flags only. Positional arguments are rejected with an error.
|
|
32
|
+
|
|
29
33
|
### Default behavior (no flags)
|
|
30
34
|
|
|
31
35
|
Removes:
|
|
@@ -43,6 +47,7 @@ Removes:
|
|
|
43
47
|
|
|
44
48
|
- `--dry-run` Print what would be removed without deleting anything.
|
|
45
49
|
- `--node-modules` Also remove `node_modules`.
|
|
50
|
+
- `--force` Skip confirmation prompt for `node_modules` removal.
|
|
46
51
|
- `--pm-cache` Also remove package manager caches/stores:
|
|
47
52
|
- `.yarn/cache`
|
|
48
53
|
- `.yarn/unplugged`
|
|
@@ -59,6 +64,8 @@ Removes:
|
|
|
59
64
|
- `--tmp` Also remove `tmp` and `temp`.
|
|
60
65
|
- `--all` Include all optional cleanup categories above.
|
|
61
66
|
- `--keep-nm` With `--all`, keep `node_modules`.
|
|
67
|
+
- `--help`, `-h` Print usage and flags.
|
|
68
|
+
- `--version`, `-v` Print the CLI version.
|
|
62
69
|
|
|
63
70
|
## Examples
|
|
64
71
|
|
|
@@ -67,10 +74,19 @@ Removes:
|
|
|
67
74
|
npx repo-clean --dry-run
|
|
68
75
|
|
|
69
76
|
# Remove default targets + node_modules
|
|
77
|
+
npx repo-clean --node-modules --force
|
|
78
|
+
|
|
79
|
+
# Prompted node_modules removal (confirm with y/yes)
|
|
70
80
|
npx repo-clean --node-modules
|
|
71
81
|
|
|
72
82
|
# Full cleanup except node_modules
|
|
73
83
|
npx repo-clean --all --keep-nm
|
|
84
|
+
|
|
85
|
+
# Print installed version
|
|
86
|
+
npx repo-clean --version
|
|
87
|
+
|
|
88
|
+
# Invalid (positional argument): exits with code 2
|
|
89
|
+
npx repo-clean my-folder
|
|
74
90
|
```
|
|
75
91
|
|
|
76
92
|
## Issues and Support
|
|
@@ -101,6 +117,7 @@ Contributions are welcome.
|
|
|
101
117
|
```bash
|
|
102
118
|
git clone https://github.com/arbenkryemadhi/repo-clean.git
|
|
103
119
|
cd repo-clean
|
|
120
|
+
npm test
|
|
104
121
|
node ./bin/repo-clean.js --help
|
|
105
122
|
node ./bin/repo-clean.js --dry-run
|
|
106
123
|
```
|
package/bin/repo-clean.js
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/* repo-clean: remove common repo build outputs and caches */
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const { parseCliArgs } = require("../lib/args");
|
|
5
|
+
const { buildCleanupPlan } = require("../lib/targets");
|
|
6
|
+
const { removeTargets, removeRootLogs } = require("../lib/cleanup");
|
|
7
|
+
const { confirmNodeModulesRemoval } = require("../lib/confirm");
|
|
8
|
+
const { version: packageVersion } = require("../package.json");
|
|
6
9
|
|
|
7
10
|
const argv = process.argv.slice(2);
|
|
8
|
-
const
|
|
11
|
+
const parsed = parseCliArgs(argv);
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
if (!parsed.ok) {
|
|
14
|
+
console.error(parsed.error);
|
|
15
|
+
process.exit(parsed.exitCode);
|
|
16
|
+
}
|
|
12
17
|
|
|
13
|
-
const flagAll =
|
|
14
|
-
const keepNm = args.has("--keep-nm");
|
|
18
|
+
const { dryRun, help, version, force, flagAll, keepNm } = parsed.options;
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const flagTmp = args.has("--tmp");
|
|
20
|
+
if (version) {
|
|
21
|
+
console.log(packageVersion);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
if (help) {
|
|
23
26
|
console.log(`repo-clean
|
|
24
27
|
|
|
25
28
|
Usage:
|
|
26
|
-
repo-clean [--dry-run] [--node-modules] [--pm-cache] [--logs] [--editor] [--tmp]
|
|
29
|
+
repo-clean [--dry-run] [--node-modules] [--pm-cache] [--logs] [--editor] [--tmp] [--force]
|
|
27
30
|
repo-clean --all [--keep-nm] [--dry-run]
|
|
31
|
+
repo-clean --version | -v
|
|
28
32
|
|
|
29
33
|
Defaults (no flags):
|
|
30
34
|
Removes build outputs + framework caches:
|
|
@@ -39,134 +43,46 @@ Flags:
|
|
|
39
43
|
--tmp Also remove tmp/temp folders
|
|
40
44
|
--all Default + all flags above
|
|
41
45
|
--keep-nm With --all, keep node_modules (do not remove it)
|
|
46
|
+
--force Skip confirmation prompt for node_modules removal
|
|
47
|
+
--version, -v Print CLI version
|
|
42
48
|
`);
|
|
43
49
|
process.exit(0);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
const cwd = process.cwd();
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"coverage",
|
|
52
|
-
".next",
|
|
53
|
-
".turbo",
|
|
54
|
-
".vite",
|
|
55
|
-
".parcel-cache",
|
|
56
|
-
".cache",
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
const editorTargets = [".vscode", ".idea"];
|
|
60
|
-
const tmpTargets = ["tmp", "temp"];
|
|
61
|
-
|
|
62
|
-
// Mix of directories + files that can exist in a repo for Yarn/Pnpm setups.
|
|
63
|
-
// (No globs; exact known paths only.)
|
|
64
|
-
const pmCacheTargets = [
|
|
65
|
-
".yarn/cache",
|
|
66
|
-
".yarn/unplugged",
|
|
67
|
-
".yarn/install-state.gz",
|
|
68
|
-
".pnp.cjs",
|
|
69
|
-
".pnp.loader.mjs",
|
|
70
|
-
".pnpm-store",
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
// Log file prefixes to delete in repo root (covers e.g. npm-debug.log.123)
|
|
74
|
-
const logPrefixes = [
|
|
75
|
-
"npm-debug.log",
|
|
76
|
-
"yarn-error.log",
|
|
77
|
-
"pnpm-debug.log",
|
|
78
|
-
"lerna-debug.log",
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
function exists(p) {
|
|
82
|
-
try {
|
|
83
|
-
fs.accessSync(p);
|
|
84
|
-
return true;
|
|
85
|
-
} catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function removePath(absPath) {
|
|
91
|
-
if (!exists(absPath)) return false;
|
|
92
|
-
|
|
93
|
-
if (dryRun) {
|
|
94
|
-
console.log(`[dry-run] would remove: ${absPath}`);
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fs.rmSync(absPath, {
|
|
99
|
-
recursive: true,
|
|
100
|
-
force: true,
|
|
101
|
-
maxRetries: 2,
|
|
102
|
-
retryDelay: 50,
|
|
103
|
-
});
|
|
104
|
-
console.log(`removed: ${absPath}`);
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function gatherRootLogs() {
|
|
109
|
-
const found = [];
|
|
110
|
-
let entries;
|
|
111
|
-
try {
|
|
112
|
-
entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
113
|
-
} catch {
|
|
114
|
-
return found;
|
|
115
|
-
}
|
|
54
|
+
async function main() {
|
|
55
|
+
const cleanupPlan = buildCleanupPlan(parsed.options);
|
|
56
|
+
const { targets, includeLogs, includeNodeModules, logPrefixes } = cleanupPlan;
|
|
116
57
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
58
|
+
if (includeNodeModules && !dryRun && !force) {
|
|
59
|
+
const confirmed = await confirmNodeModulesRemoval();
|
|
60
|
+
if (!confirmed) {
|
|
61
|
+
console.log("Aborted. No files were removed.");
|
|
62
|
+
process.exit(1);
|
|
122
63
|
}
|
|
123
64
|
}
|
|
124
|
-
return found;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Determine what to remove
|
|
128
|
-
const targets = new Set(defaultTargets);
|
|
129
|
-
|
|
130
|
-
const includePmCache = flagAll || flagPmCache;
|
|
131
|
-
const includeLogs = flagAll || flagLogs;
|
|
132
|
-
const includeEditor = flagAll || flagEditor;
|
|
133
|
-
const includeTmp = flagAll || flagTmp;
|
|
134
65
|
|
|
135
|
-
|
|
136
|
-
// - default: off
|
|
137
|
-
// - --node-modules: on
|
|
138
|
-
// - --all: on, unless --keep-nm is also present
|
|
139
|
-
const includeNodeModules = (flagAll || flagNodeModules) && !(flagAll && keepNm);
|
|
66
|
+
let removedCount = removeTargets(cwd, targets, dryRun);
|
|
140
67
|
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
if (includeTmp) for (const t of tmpTargets) targets.add(t);
|
|
144
|
-
if (includeNodeModules) targets.add("node_modules");
|
|
145
|
-
|
|
146
|
-
// Remove fixed targets
|
|
147
|
-
let removedCount = 0;
|
|
148
|
-
|
|
149
|
-
for (const rel of Array.from(targets).sort()) {
|
|
150
|
-
const abs = path.join(cwd, rel);
|
|
151
|
-
if (removePath(abs)) removedCount += 1;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Remove root log files (if enabled)
|
|
155
|
-
if (includeLogs) {
|
|
156
|
-
const logs = gatherRootLogs();
|
|
157
|
-
for (const name of logs.sort()) {
|
|
158
|
-
const abs = path.join(cwd, name);
|
|
159
|
-
if (removePath(abs)) removedCount += 1;
|
|
68
|
+
if (includeLogs) {
|
|
69
|
+
removedCount += removeRootLogs(cwd, logPrefixes, dryRun);
|
|
160
70
|
}
|
|
71
|
+
|
|
72
|
+
const nmNote =
|
|
73
|
+
flagAll && keepNm
|
|
74
|
+
? " (kept node_modules)"
|
|
75
|
+
: includeNodeModules
|
|
76
|
+
? " (includes node_modules)"
|
|
77
|
+
: "";
|
|
78
|
+
const mode = dryRun ? "done (dry-run)" : "done";
|
|
79
|
+
console.log(
|
|
80
|
+
`${mode}. ${dryRun ? "would remove" : "removed"} ${removedCount} item(s).${nmNote}`,
|
|
81
|
+
);
|
|
161
82
|
}
|
|
162
83
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
: "";
|
|
169
|
-
const mode = dryRun ? "done (dry-run)" : "done";
|
|
170
|
-
console.log(
|
|
171
|
-
`${mode}. ${dryRun ? "would remove" : "removed"} ${removedCount} item(s).${nmNote}`,
|
|
172
|
-
);
|
|
84
|
+
main().catch((err) => {
|
|
85
|
+
const msg = err && err.message ? err.message : String(err);
|
|
86
|
+
console.error(`Error: ${msg}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
package/lib/args.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const allowedFlags = new Set([
|
|
2
|
+
"--dry-run",
|
|
3
|
+
"--help",
|
|
4
|
+
"-h",
|
|
5
|
+
"--version",
|
|
6
|
+
"-v",
|
|
7
|
+
"--force",
|
|
8
|
+
"--all",
|
|
9
|
+
"--keep-nm",
|
|
10
|
+
"--node-modules",
|
|
11
|
+
"--pm-cache",
|
|
12
|
+
"--logs",
|
|
13
|
+
"--editor",
|
|
14
|
+
"--tmp",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const EXIT_INVALID_ARGS = 2;
|
|
18
|
+
|
|
19
|
+
function isFlagToken(arg) {
|
|
20
|
+
return arg.startsWith("-");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function editDistance(a, b) {
|
|
24
|
+
const m = a.length;
|
|
25
|
+
const n = b.length;
|
|
26
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i <= m; i += 1) dp[i][0] = i;
|
|
29
|
+
for (let j = 0; j <= n; j += 1) dp[0][j] = j;
|
|
30
|
+
|
|
31
|
+
for (let i = 1; i <= m; i += 1) {
|
|
32
|
+
for (let j = 1; j <= n; j += 1) {
|
|
33
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
34
|
+
dp[i][j] = Math.min(
|
|
35
|
+
dp[i - 1][j] + 1,
|
|
36
|
+
dp[i][j - 1] + 1,
|
|
37
|
+
dp[i - 1][j - 1] + cost,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return dp[m][n];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getClosestFlag(input) {
|
|
46
|
+
let best = null;
|
|
47
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
48
|
+
|
|
49
|
+
for (const flag of allowedFlags) {
|
|
50
|
+
const dist = editDistance(input, flag);
|
|
51
|
+
if (dist < bestDistance) {
|
|
52
|
+
best = flag;
|
|
53
|
+
bestDistance = dist;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!best) return null;
|
|
58
|
+
if (bestDistance > 3) return null;
|
|
59
|
+
return best;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatUnknownFlagError(unknownFlags) {
|
|
63
|
+
const unknownList = unknownFlags.join(", ");
|
|
64
|
+
const flagLabel = unknownFlags.length === 1 ? "flag" : "flags";
|
|
65
|
+
const suggestions = unknownFlags
|
|
66
|
+
.map((flag) => {
|
|
67
|
+
const suggestion = getClosestFlag(flag);
|
|
68
|
+
return suggestion ? { flag, suggestion } : null;
|
|
69
|
+
})
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
|
|
72
|
+
let suggestionText = "";
|
|
73
|
+
if (suggestions.length === 1 && unknownFlags.length === 1) {
|
|
74
|
+
suggestionText = ` Did you mean ${suggestions[0].suggestion}?`;
|
|
75
|
+
} else if (suggestions.length > 0) {
|
|
76
|
+
const pairs = suggestions.map(
|
|
77
|
+
(item) => `${item.flag} -> ${item.suggestion}`,
|
|
78
|
+
);
|
|
79
|
+
suggestionText = ` Did you mean: ${pairs.join(", ")}?`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return `Unknown ${flagLabel}: ${unknownList}. This looks like a typo.${suggestionText} Run repo-clean --help for valid options.`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function formatPositionalArgError(positionalArgs) {
|
|
86
|
+
const argLabel = positionalArgs.length === 1 ? "argument" : "arguments";
|
|
87
|
+
const list = positionalArgs.join(", ");
|
|
88
|
+
return `Unexpected positional ${argLabel}: ${list}. repo-clean accepts flags only. Run repo-clean --help for usage.`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseCliArgs(argv) {
|
|
92
|
+
const args = new Set(argv);
|
|
93
|
+
|
|
94
|
+
const unknownFlags = argv.filter(
|
|
95
|
+
(arg) => isFlagToken(arg) && !allowedFlags.has(arg),
|
|
96
|
+
);
|
|
97
|
+
if (unknownFlags.length > 0) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
exitCode: EXIT_INVALID_ARGS,
|
|
101
|
+
error: formatUnknownFlagError(unknownFlags),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const positionalArgs = argv.filter((arg) => !isFlagToken(arg));
|
|
106
|
+
if (positionalArgs.length > 0) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
exitCode: EXIT_INVALID_ARGS,
|
|
110
|
+
error: formatPositionalArgError(positionalArgs),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
options: {
|
|
117
|
+
dryRun: args.has("--dry-run"),
|
|
118
|
+
help: args.has("--help") || args.has("-h"),
|
|
119
|
+
version: args.has("--version") || args.has("-v"),
|
|
120
|
+
force: args.has("--force"),
|
|
121
|
+
flagAll: args.has("--all"),
|
|
122
|
+
keepNm: args.has("--keep-nm"),
|
|
123
|
+
flagNodeModules: args.has("--node-modules"),
|
|
124
|
+
flagPmCache: args.has("--pm-cache"),
|
|
125
|
+
flagLogs: args.has("--logs"),
|
|
126
|
+
flagEditor: args.has("--editor"),
|
|
127
|
+
flagTmp: args.has("--tmp"),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
allowedFlags,
|
|
134
|
+
parseCliArgs,
|
|
135
|
+
};
|
package/lib/cleanup.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
function exists(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
fs.accessSync(filePath);
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function removePath(absPath, dryRun) {
|
|
14
|
+
if (!exists(absPath)) return false;
|
|
15
|
+
|
|
16
|
+
if (dryRun) {
|
|
17
|
+
console.log(`[dry-run] would remove: ${absPath}`);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fs.rmSync(absPath, {
|
|
22
|
+
recursive: true,
|
|
23
|
+
force: true,
|
|
24
|
+
maxRetries: 2,
|
|
25
|
+
retryDelay: 50,
|
|
26
|
+
});
|
|
27
|
+
console.log(`removed: ${absPath}`);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function gatherRootLogs(cwd, logPrefixes) {
|
|
32
|
+
const found = [];
|
|
33
|
+
let entries;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
37
|
+
} catch {
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (!entry.isFile()) continue;
|
|
43
|
+
if (
|
|
44
|
+
logPrefixes.some(
|
|
45
|
+
(prefix) => entry.name === prefix || entry.name.startsWith(prefix),
|
|
46
|
+
)
|
|
47
|
+
) {
|
|
48
|
+
found.push(entry.name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return found;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function removeTargets(cwd, targets, dryRun) {
|
|
56
|
+
let removedCount = 0;
|
|
57
|
+
|
|
58
|
+
for (const rel of Array.from(targets).sort()) {
|
|
59
|
+
const absPath = path.join(cwd, rel);
|
|
60
|
+
if (removePath(absPath, dryRun)) {
|
|
61
|
+
removedCount += 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return removedCount;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function removeRootLogs(cwd, logPrefixes, dryRun) {
|
|
69
|
+
let removedCount = 0;
|
|
70
|
+
const logs = gatherRootLogs(cwd, logPrefixes);
|
|
71
|
+
|
|
72
|
+
for (const name of logs.sort()) {
|
|
73
|
+
const absPath = path.join(cwd, name);
|
|
74
|
+
if (removePath(absPath, dryRun)) {
|
|
75
|
+
removedCount += 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return removedCount;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
removeTargets,
|
|
84
|
+
removeRootLogs,
|
|
85
|
+
};
|
package/lib/confirm.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const readline = require("node:readline/promises");
|
|
3
|
+
|
|
4
|
+
async function confirmNodeModulesRemoval() {
|
|
5
|
+
if (!process.stdin.isTTY) {
|
|
6
|
+
let input = "";
|
|
7
|
+
try {
|
|
8
|
+
input = fs.readFileSync(0, "utf8");
|
|
9
|
+
} catch {
|
|
10
|
+
input = "";
|
|
11
|
+
}
|
|
12
|
+
const answer = input.trim().toLowerCase();
|
|
13
|
+
return answer === "y" || answer === "yes";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const rl = readline.createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const answer = (
|
|
23
|
+
await rl.question(
|
|
24
|
+
"Safety check: this will remove node_modules. Are you sure? [y/N]: ",
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
.trim()
|
|
28
|
+
.toLowerCase();
|
|
29
|
+
return answer === "y" || answer === "yes";
|
|
30
|
+
} finally {
|
|
31
|
+
rl.close();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
confirmNodeModulesRemoval,
|
|
37
|
+
};
|
package/lib/targets.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const defaultTargets = [
|
|
2
|
+
"dist",
|
|
3
|
+
"build",
|
|
4
|
+
"coverage",
|
|
5
|
+
".next",
|
|
6
|
+
".turbo",
|
|
7
|
+
".vite",
|
|
8
|
+
".parcel-cache",
|
|
9
|
+
".cache",
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const editorTargets = [".vscode", ".idea"];
|
|
13
|
+
const tmpTargets = ["tmp", "temp"];
|
|
14
|
+
|
|
15
|
+
const pmCacheTargets = [
|
|
16
|
+
".yarn/cache",
|
|
17
|
+
".yarn/unplugged",
|
|
18
|
+
".yarn/install-state.gz",
|
|
19
|
+
".pnp.cjs",
|
|
20
|
+
".pnp.loader.mjs",
|
|
21
|
+
".pnpm-store",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const logPrefixes = [
|
|
25
|
+
"npm-debug.log",
|
|
26
|
+
"yarn-error.log",
|
|
27
|
+
"pnpm-debug.log",
|
|
28
|
+
"lerna-debug.log",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function buildCleanupPlan(options) {
|
|
32
|
+
const targets = new Set(defaultTargets);
|
|
33
|
+
|
|
34
|
+
const includePmCache = options.flagAll || options.flagPmCache;
|
|
35
|
+
const includeLogs = options.flagAll || options.flagLogs;
|
|
36
|
+
const includeEditor = options.flagAll || options.flagEditor;
|
|
37
|
+
const includeTmp = options.flagAll || options.flagTmp;
|
|
38
|
+
const includeNodeModules =
|
|
39
|
+
(options.flagAll || options.flagNodeModules) &&
|
|
40
|
+
!(options.flagAll && options.keepNm);
|
|
41
|
+
|
|
42
|
+
if (includePmCache) {
|
|
43
|
+
for (const target of pmCacheTargets) {
|
|
44
|
+
targets.add(target);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (includeEditor) {
|
|
49
|
+
for (const target of editorTargets) {
|
|
50
|
+
targets.add(target);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (includeTmp) {
|
|
55
|
+
for (const target of tmpTargets) {
|
|
56
|
+
targets.add(target);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (includeNodeModules) {
|
|
61
|
+
targets.add("node_modules");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
targets,
|
|
66
|
+
includeLogs,
|
|
67
|
+
includeNodeModules,
|
|
68
|
+
logPrefixes,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
buildCleanupPlan,
|
|
74
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repo-clean",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Clean common build and cache folders from a repository.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -10,15 +10,26 @@
|
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/arbenkryemadhi/repo-clean.git"
|
|
12
12
|
},
|
|
13
|
-
"keywords": [
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"repo",
|
|
16
|
+
"workspace",
|
|
17
|
+
"cleanup",
|
|
18
|
+
"clean",
|
|
19
|
+
"cache"
|
|
20
|
+
],
|
|
14
21
|
"author": "Arben Kryemadhi",
|
|
15
22
|
"type": "commonjs",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node --test test/*.test.js"
|
|
25
|
+
},
|
|
16
26
|
"bugs": {
|
|
17
27
|
"url": "https://github.com/arbenkryemadhi/repo-clean/issues"
|
|
18
28
|
},
|
|
19
29
|
"homepage": "https://github.com/arbenkryemadhi/repo-clean#readme",
|
|
20
30
|
"files": [
|
|
21
31
|
"bin",
|
|
32
|
+
"lib",
|
|
22
33
|
"README.md",
|
|
23
34
|
"LICENSE"
|
|
24
35
|
]
|