remnem 0.1.7 → 0.1.9
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 +63 -65
- package/bin/remnem.js +77 -194
- package/package.json +14 -39
package/README.md
CHANGED
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
# remnem
|
|
2
2
|
|
|
3
|
-
**r**e**m**ove **n**ode_**m**odules — find every nested `node_modules` in a project (root + all workspaces + any nested ones) and delete them all,
|
|
3
|
+
**r**e**m**ove **n**ode_**m**odules — find every nested `node_modules` in a project (root + all workspaces + any nested ones) and delete them all, **instantly**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A single self-contained CLI written in **Rust**: a lean, parallel, directory-only walker finds every `node_modules`, then each one is disposed of by an **O(1) rename** rather than a slow recursive unlink — so clearing a 1000-package monorepo takes a couple hundred milliseconds instead of tens of seconds.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
$ remnem
|
|
9
9
|
root: /Users/you/dev/my-monorepo
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
found 1947 node_modules:
|
|
11
|
+
node_modules
|
|
12
|
+
apps/web/node_modules
|
|
13
|
+
...
|
|
14
|
+
permanently delete these 1947 directories? [y/N] y
|
|
15
|
+
|
|
16
|
+
deleted: 1947/1947 node_modules in 130ms
|
|
17
|
+
(space is being reclaimed in the background)
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## How it's instant
|
|
21
|
+
|
|
22
|
+
Physically deleting `node_modules` means unlinking hundreds of thousands of
|
|
23
|
+
files — that is I/O-bound and unavoidably slow (tens of seconds on a big
|
|
24
|
+
monorepo). remnem sidesteps that on the critical path:
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
1. **Find** — a parallel, directory-only walk. It reads directory entries via
|
|
27
|
+
`readdir`'s `d_type` (no per-file `stat`), never looks at regular files, and
|
|
28
|
+
never descends *into* a `node_modules` (the whole subtree is going anyway).
|
|
29
|
+
So the walk is proportional to your *source* tree, not the installed
|
|
30
|
+
dependency tree.
|
|
31
|
+
2. **Rename aside** — each `node_modules` is `rename`d to a hidden sibling in the
|
|
32
|
+
same directory. On one filesystem that is an O(1) metadata operation no matter
|
|
33
|
+
how large the tree is. The instant it returns, `node_modules` is gone from its
|
|
34
|
+
location — **a clean reinstall can start immediately**.
|
|
35
|
+
3. **Reclaim in the background** — a detached background process `rm -rf`s the
|
|
36
|
+
renamed directories, so the disk-freeing I/O never blocks you. Space comes
|
|
37
|
+
back within a few seconds, hands-free.
|
|
24
38
|
|
|
25
|
-
Pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
Pass **`--sync`** if you'd rather block until the space is actually reclaimed
|
|
40
|
+
(e.g. a script that measures free disk right after).
|
|
41
|
+
|
|
42
|
+
## Delete vs. Trash
|
|
43
|
+
|
|
44
|
+
By default `remnem` **permanently deletes** (via the instant rename-then-reap
|
|
45
|
+
above). Pass **`-t` / `--trash`** to move each `node_modules` to the OS trash
|
|
46
|
+
instead (Finder Trash on macOS, the freedesktop trash on Linux, the Recycle Bin
|
|
47
|
+
on Windows) — recoverable from the trash, with the space reclaimed when you empty
|
|
48
|
+
it. A `node_modules` on a *different* volume (rare) can't be renamed instantly, so
|
|
49
|
+
those fall back to a direct delete.
|
|
31
50
|
|
|
32
51
|
## Install
|
|
33
52
|
|
|
@@ -36,36 +55,29 @@ npm install -g remnem
|
|
|
36
55
|
# or: bun install -g remnem
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
The right prebuilt
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
The right prebuilt binary is pulled in automatically for your platform via
|
|
59
|
+
`optionalDependencies` — the main `remnem` package is a tiny launcher that execs
|
|
60
|
+
it. Supported: **macOS** (arm64, x64), **Linux** (arm64 & x64, glibc & musl),
|
|
61
|
+
**Windows** (arm64, x64).
|
|
42
62
|
|
|
43
63
|
Then from any repo root:
|
|
44
64
|
|
|
45
65
|
```sh
|
|
46
|
-
remnem
|
|
66
|
+
remnem # or: npx remnem / bunx remnem
|
|
47
67
|
```
|
|
48
68
|
|
|
49
69
|
### From source
|
|
50
70
|
|
|
51
71
|
```sh
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
bun link # makes `remnem` available on your PATH
|
|
72
|
+
cargo build --release # produces target/release/remnem
|
|
73
|
+
./target/release/remnem --help
|
|
55
74
|
```
|
|
56
75
|
|
|
57
76
|
## What it clears
|
|
58
77
|
|
|
59
78
|
**Every `node_modules` directory** under the given root — the root's own, every
|
|
60
79
|
workspace package's, and any stray nested ones — leaving all your source and
|
|
61
|
-
`package.json` files untouched.
|
|
62
|
-
`node_modules` (the whole subtree is going to be removed anyway), so it stays
|
|
63
|
-
fast even on trees with hundreds of thousands of files.
|
|
64
|
-
|
|
65
|
-
Workspace resolution (reading `package.json#workspaces` for bun/npm/yarn, or
|
|
66
|
-
`pnpm-workspace.yaml#packages` for pnpm) is used to **report** the workspace
|
|
67
|
-
layout; clearing always targets every nested `node_modules`, not only workspace
|
|
68
|
-
packages.
|
|
80
|
+
`package.json` files untouched.
|
|
69
81
|
|
|
70
82
|
## Usage
|
|
71
83
|
|
|
@@ -76,22 +88,27 @@ Arguments:
|
|
|
76
88
|
path Project root to clean (default: current directory)
|
|
77
89
|
|
|
78
90
|
Options:
|
|
79
|
-
-t, --trash Move to the Trash instead of deleting (
|
|
91
|
+
-t, --trash Move to the Trash instead of deleting (recoverable)
|
|
80
92
|
-l, --list List what would be cleared; touch nothing
|
|
81
|
-
|
|
93
|
+
-m, --measure Size each node_modules (slow: walks every dependency tree)
|
|
94
|
+
-w, --workspace Also resolve & report the bun/pnpm workspace layout (slow)
|
|
95
|
+
--sync Wait for the disk space to actually free before returning
|
|
82
96
|
--json Print the raw result as JSON
|
|
83
97
|
-y, --yes Skip the confirmation prompt
|
|
84
98
|
-h, --help Show this help
|
|
85
99
|
```
|
|
86
100
|
|
|
87
|
-
By default `remnem`
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
By default `remnem` deletes each `node_modules` after printing what it found and
|
|
102
|
+
asking for confirmation (skipped with `-y`, or when stdin isn't a TTY, e.g. in
|
|
103
|
+
CI). Use `-l` to list without touching anything, `-t` to move to the Trash, or
|
|
104
|
+
`--sync` to wait for the space to be reclaimed.
|
|
105
|
+
|
|
106
|
+
Sizing (`-m`) and workspace-layout resolution (`-w`) each require an extra tree
|
|
107
|
+
walk, so they are **off by default** — the fast path does neither.
|
|
91
108
|
|
|
92
|
-
## Workspace resolution
|
|
109
|
+
## Workspace resolution (`-w`)
|
|
93
110
|
|
|
94
|
-
`remnem`
|
|
111
|
+
With `-w`, `remnem` reports the workspace layout the way bun and pnpm resolve it:
|
|
95
112
|
|
|
96
113
|
| Source | Field | Example |
|
|
97
114
|
| --- | --- | --- |
|
|
@@ -108,34 +125,15 @@ matcher bun/npm/yarn use):
|
|
|
108
125
|
- `!pattern` excludes previously-matched directories (`!**/test/**` drops a
|
|
109
126
|
directory named `test` and its contents)
|
|
110
127
|
|
|
111
|
-
|
|
112
|
-
`
|
|
113
|
-
|
|
114
|
-
## API
|
|
115
|
-
|
|
116
|
-
The napi-rs core is also usable directly from JavaScript:
|
|
117
|
-
|
|
118
|
-
```js
|
|
119
|
-
const { clean, resolveWorkspace } = require("remnem");
|
|
120
|
-
|
|
121
|
-
// Clear every nested node_modules under a root.
|
|
122
|
-
// trash: false (default) → permanent parallel delete; true → move to Trash.
|
|
123
|
-
const result = clean({ root: "/path/to/repo", dryRun: false, measure: true, trash: false });
|
|
124
|
-
// → { root, workspaceKind, workspacePackages, cleaned: [{ path, bytes, deleted, trashed, error }], totalBytes, count, failed }
|
|
125
|
-
|
|
126
|
-
// Just inspect how bun/pnpm would see the workspace (no deletion).
|
|
127
|
-
const ws = resolveWorkspace("/path/to/repo");
|
|
128
|
-
// → { workspaceKind: "pnpm" | "package.json" | "none", workspacePackages: [...] }
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
See `index.d.ts` for the full typed surface.
|
|
128
|
+
This is purely informational: clearing always targets every nested
|
|
129
|
+
`node_modules`, not only workspace packages.
|
|
132
130
|
|
|
133
131
|
## Development
|
|
134
132
|
|
|
135
133
|
```sh
|
|
136
134
|
cargo test # Rust unit tests (workspace resolution + glob semantics)
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
cargo build --release # release build (LTO)
|
|
136
|
+
node __test__/smoke.mjs ./target/release/remnem # end-to-end smoke test
|
|
139
137
|
```
|
|
140
138
|
|
|
141
139
|
## License
|
package/bin/remnem.js
CHANGED
|
@@ -1,213 +1,96 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
json: false,
|
|
38
|
-
yes: false,
|
|
39
|
-
help: false,
|
|
40
|
-
};
|
|
41
|
-
for (let i = 0; i < argv.length; i++) {
|
|
42
|
-
const arg = argv[i];
|
|
43
|
-
switch (arg) {
|
|
44
|
-
case "-h":
|
|
45
|
-
case "--help":
|
|
46
|
-
opts.help = true;
|
|
47
|
-
break;
|
|
48
|
-
case "-l":
|
|
49
|
-
case "--list":
|
|
50
|
-
opts.list = true;
|
|
51
|
-
break;
|
|
52
|
-
case "-t":
|
|
53
|
-
case "--trash":
|
|
54
|
-
opts.trash = true;
|
|
55
|
-
break;
|
|
56
|
-
case "--no-measure":
|
|
57
|
-
opts.measure = false;
|
|
58
|
-
break;
|
|
59
|
-
case "--measure":
|
|
60
|
-
opts.measure = true;
|
|
61
|
-
break;
|
|
62
|
-
case "--json":
|
|
63
|
-
opts.json = true;
|
|
64
|
-
break;
|
|
65
|
-
case "-y":
|
|
66
|
-
case "--yes":
|
|
67
|
-
opts.yes = true;
|
|
68
|
-
break;
|
|
69
|
-
default:
|
|
70
|
-
if (arg.startsWith("-")) {
|
|
71
|
-
process.stderr.write(`remnem: unknown option ${arg}\n\n${HELP}`);
|
|
72
|
-
process.exit(2);
|
|
73
|
-
}
|
|
74
|
-
if (opts.root !== undefined) {
|
|
75
|
-
process.stderr.write(`remnem: unexpected extra argument ${arg}\n`);
|
|
76
|
-
process.exit(2);
|
|
77
|
-
}
|
|
78
|
-
opts.root = arg;
|
|
4
|
+
// Thin launcher. remnem ships as a compiled Rust binary, one per platform, in an
|
|
5
|
+
// optional dependency package (@leonsilicon/remnem-<platform>). This shim
|
|
6
|
+
// resolves the binary for the current platform and hands off to it with the same
|
|
7
|
+
// argv, stdio, and exit code — so `remnem` behaves exactly like the native
|
|
8
|
+
// executable while still installing through npm's platform-package mechanism.
|
|
9
|
+
|
|
10
|
+
const { spawnSync } = require("child_process");
|
|
11
|
+
const { existsSync } = require("fs");
|
|
12
|
+
const { join } = require("path");
|
|
13
|
+
|
|
14
|
+
// Map Node's platform/arch to the npm sub-package that carries the binary. Keep
|
|
15
|
+
// this in sync with the `optionalDependencies` in package.json and the `npm/*`
|
|
16
|
+
// package directories.
|
|
17
|
+
const PACKAGES = {
|
|
18
|
+
"darwin-arm64": "@leonsilicon/remnem-darwin-arm64",
|
|
19
|
+
"darwin-x64": "@leonsilicon/remnem-darwin-x64",
|
|
20
|
+
"linux-arm64-gnu": "@leonsilicon/remnem-linux-arm64-gnu",
|
|
21
|
+
"linux-arm64-musl": "@leonsilicon/remnem-linux-arm64-musl",
|
|
22
|
+
"linux-x64-gnu": "@leonsilicon/remnem-linux-x64-gnu",
|
|
23
|
+
"linux-x64-musl": "@leonsilicon/remnem-linux-x64-musl",
|
|
24
|
+
"win32-arm64-msvc": "@leonsilicon/remnem-win32-arm64-msvc",
|
|
25
|
+
"win32-x64-msvc": "@leonsilicon/remnem-win32-x64-msvc",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// On Linux, tell glibc from musl so we pick the matching binary package.
|
|
29
|
+
function isMusl() {
|
|
30
|
+
if (process.platform !== "linux") return false;
|
|
31
|
+
try {
|
|
32
|
+
// `process.report` exposes the runtime's libc in its header on modern Node.
|
|
33
|
+
if (process.report && typeof process.report.getReport === "function") {
|
|
34
|
+
const report = process.report.getReport();
|
|
35
|
+
const header = report.header || {};
|
|
36
|
+
if (typeof header.glibcVersionRuntime === "string") return false;
|
|
79
37
|
}
|
|
80
|
-
}
|
|
81
|
-
return opts;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function formatBytes(n) {
|
|
85
|
-
const bytes = typeof n === "bigint" ? Number(n) : n;
|
|
86
|
-
if (!bytes) return "0 B";
|
|
87
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
88
|
-
const exp = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
89
|
-
const value = bytes / 1024 ** exp;
|
|
90
|
-
return `${value.toFixed(value >= 10 || exp === 0 ? 0 : 1)} ${units[exp]}`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function relativizePath(root, p) {
|
|
94
|
-
if (p === root) return ".";
|
|
95
|
-
if (p.startsWith(root + "/")) return p.slice(root.length + 1);
|
|
96
|
-
return p;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Confirmation prompt (synchronous) so a bare `remnem` in a real repo can't nuke
|
|
100
|
-
// node_modules by a stray keystroke. Skipped with -y, with --list, or when not
|
|
101
|
-
// attached to a TTY (CI / piped).
|
|
102
|
-
function confirm(question) {
|
|
103
|
-
if (!process.stdin.isTTY) return true;
|
|
104
|
-
const fs = require("fs");
|
|
105
|
-
process.stdout.write(question);
|
|
106
|
-
const buf = Buffer.alloc(64);
|
|
107
|
-
let bytesRead = 0;
|
|
38
|
+
} catch {}
|
|
108
39
|
try {
|
|
109
|
-
|
|
40
|
+
// Fallback: the ldd on musl systems mentions musl.
|
|
41
|
+
return require("fs").readFileSync("/usr/bin/ldd", "utf8").includes("musl");
|
|
110
42
|
} catch {
|
|
43
|
+
// Default to gnu; if wrong the resolution below fails loudly with guidance.
|
|
111
44
|
return false;
|
|
112
45
|
}
|
|
113
|
-
const answer = buf.toString("utf8", 0, bytesRead).trim().toLowerCase();
|
|
114
|
-
return answer === "y" || answer === "yes";
|
|
115
46
|
}
|
|
116
47
|
|
|
117
|
-
function
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Phase 1: dry-run scan so we can show the user exactly what will go, then
|
|
125
|
-
// (unless -y / non-TTY) confirm before the real deletion.
|
|
126
|
-
const scan = clean({ root: opts.root, dryRun: true, measure: opts.measure });
|
|
127
|
-
|
|
128
|
-
const kindLabel =
|
|
129
|
-
scan.workspaceKind === "none"
|
|
130
|
-
? "no workspace config"
|
|
131
|
-
: `${scan.workspaceKind} workspace (${scan.workspacePackages.length} package${
|
|
132
|
-
scan.workspacePackages.length === 1 ? "" : "s"
|
|
133
|
-
})`;
|
|
134
|
-
|
|
135
|
-
if (opts.json && opts.list) {
|
|
136
|
-
process.stdout.write(JSON.stringify(scan, replacer, 2) + "\n");
|
|
137
|
-
return;
|
|
48
|
+
function platformKey() {
|
|
49
|
+
const { platform, arch } = process;
|
|
50
|
+
if (platform === "linux") {
|
|
51
|
+
return `linux-${arch}-${isMusl() ? "musl" : "gnu"}`;
|
|
138
52
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
process.stdout.write(`remnem: no node_modules found under ${scan.root} (${kindLabel})\n`);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
process.stdout.write(`root: ${scan.root}\n`);
|
|
146
|
-
process.stdout.write(` ${kindLabel}\n`);
|
|
147
|
-
process.stdout.write(
|
|
148
|
-
`found ${scan.count} node_modules${
|
|
149
|
-
opts.measure ? ` totalling ${formatBytes(scan.totalBytes)}` : ""
|
|
150
|
-
}:\n`,
|
|
151
|
-
);
|
|
152
|
-
for (const dir of scan.cleaned) {
|
|
153
|
-
const size = opts.measure ? ` ${formatBytes(dir.bytes).padStart(8)}` : "";
|
|
154
|
-
process.stdout.write(`${size} ${relativizePath(scan.root, dir.path)}\n`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (opts.list) {
|
|
158
|
-
process.stdout.write(`\n(list only — nothing ${opts.trash ? "trashed" : "deleted"})\n`);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const verb = opts.trash ? "move to Trash" : "permanently delete";
|
|
163
|
-
if (!opts.yes && !confirm(`\n${verb} these ${scan.count} directories? [y/N] `)) {
|
|
164
|
-
process.stdout.write("aborted.\n");
|
|
165
|
-
process.exit(1);
|
|
53
|
+
if (platform === "win32") {
|
|
54
|
+
return `win32-${arch}-msvc`;
|
|
166
55
|
}
|
|
56
|
+
return `${platform}-${arch}`;
|
|
57
|
+
}
|
|
167
58
|
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (opts.json) {
|
|
174
|
-
process.stdout.write(JSON.stringify(result, replacer, 2) + "\n");
|
|
175
|
-
return;
|
|
59
|
+
function resolveBinary() {
|
|
60
|
+
const key = platformKey();
|
|
61
|
+
const pkg = PACKAGES[key];
|
|
62
|
+
if (!pkg) {
|
|
63
|
+
return { error: `remnem: unsupported platform ${process.platform}-${process.arch}` };
|
|
176
64
|
}
|
|
65
|
+
const exe = process.platform === "win32" ? "remnem.exe" : "remnem";
|
|
177
66
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
`\n${action}: ${done}/${result.count} node_modules${
|
|
193
|
-
opts.measure ? ` (${formatBytes(scan.totalBytes)})` : ""
|
|
194
|
-
} in ${elapsedMs.toFixed(0)}ms\n`,
|
|
195
|
-
);
|
|
196
|
-
if (opts.trash && trashedCount > 0) {
|
|
197
|
-
process.stdout.write(`empty the Trash to reclaim the space (or re-run without -t to delete).\n`);
|
|
198
|
-
}
|
|
199
|
-
if (result.failed > 0) {
|
|
200
|
-
for (const dir of result.cleaned) {
|
|
201
|
-
if (dir.error) {
|
|
202
|
-
process.stderr.write(` failed: ${relativizePath(result.root, dir.path)} — ${dir.error}\n`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
67
|
+
// Prefer resolving through the package's own manifest so we find it wherever
|
|
68
|
+
// the package manager placed it (hoisted, nested, pnpm store, etc.).
|
|
69
|
+
try {
|
|
70
|
+
const manifest = require.resolve(`${pkg}/package.json`);
|
|
71
|
+
const binPath = join(manifest, "..", exe);
|
|
72
|
+
if (existsSync(binPath)) return { binPath };
|
|
73
|
+
} catch {}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
error:
|
|
77
|
+
`remnem: could not find the ${pkg} package for your platform.\n` +
|
|
78
|
+
`Install it with your package manager, or reinstall remnem so npm can pull the\n` +
|
|
79
|
+
`matching optional dependency (${pkg}).`,
|
|
80
|
+
};
|
|
207
81
|
}
|
|
208
82
|
|
|
209
|
-
|
|
210
|
-
|
|
83
|
+
const { binPath, error } = resolveBinary();
|
|
84
|
+
if (error) {
|
|
85
|
+
process.stderr.write(error + "\n");
|
|
86
|
+
process.exit(1);
|
|
211
87
|
}
|
|
212
88
|
|
|
213
|
-
|
|
89
|
+
// Hand off: inherit stdio so the interactive confirmation prompt and all output
|
|
90
|
+
// pass straight through, and propagate the binary's exit code.
|
|
91
|
+
const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
92
|
+
if (result.error) {
|
|
93
|
+
process.stderr.write(`remnem: failed to run ${binPath}: ${result.error.message}\n`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
process.exit(result.status === null ? 1 : result.status);
|
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remnem",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Find and delete every nested node_modules in a project as fast as possible (
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Find and delete every nested node_modules in a project as fast as possible (Rust binary)",
|
|
5
5
|
"type": "commonjs",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"types": "index.d.ts",
|
|
8
6
|
"license": "MIT",
|
|
9
7
|
"author": "Leon Si",
|
|
10
8
|
"homepage": "https://github.com/leonsilicon/remnem#readme",
|
|
@@ -22,7 +20,6 @@
|
|
|
22
20
|
"clean",
|
|
23
21
|
"cleaner",
|
|
24
22
|
"delete",
|
|
25
|
-
"napi-rs",
|
|
26
23
|
"rust",
|
|
27
24
|
"bun",
|
|
28
25
|
"pnpm"
|
|
@@ -31,52 +28,30 @@
|
|
|
31
28
|
"remnem": "bin/remnem.js"
|
|
32
29
|
},
|
|
33
30
|
"files": [
|
|
34
|
-
"index.js",
|
|
35
|
-
"index.d.ts",
|
|
36
31
|
"bin/remnem.js",
|
|
37
32
|
"README.md",
|
|
38
33
|
"LICENSE"
|
|
39
34
|
],
|
|
40
|
-
"napi": {
|
|
41
|
-
"binaryName": "remnem",
|
|
42
|
-
"targets": [
|
|
43
|
-
"x86_64-apple-darwin",
|
|
44
|
-
"aarch64-apple-darwin",
|
|
45
|
-
"x86_64-unknown-linux-gnu",
|
|
46
|
-
"aarch64-unknown-linux-gnu",
|
|
47
|
-
"x86_64-unknown-linux-musl",
|
|
48
|
-
"aarch64-unknown-linux-musl",
|
|
49
|
-
"x86_64-pc-windows-msvc",
|
|
50
|
-
"aarch64-pc-windows-msvc"
|
|
51
|
-
],
|
|
52
|
-
"packageName": "@leonsilicon/remnem"
|
|
53
|
-
},
|
|
54
35
|
"engines": {
|
|
55
36
|
"node": ">= 18"
|
|
56
37
|
},
|
|
57
38
|
"optionalDependencies": {
|
|
58
|
-
"@leonsilicon/remnem-darwin-x64": "0.1.
|
|
59
|
-
"@leonsilicon/remnem-darwin-arm64": "0.1.
|
|
60
|
-
"@leonsilicon/remnem-linux-x64-gnu": "0.1.
|
|
61
|
-
"@leonsilicon/remnem-linux-arm64-gnu": "0.1.
|
|
62
|
-
"@leonsilicon/remnem-linux-x64-musl": "0.1.
|
|
63
|
-
"@leonsilicon/remnem-linux-arm64-musl": "0.1.
|
|
64
|
-
"@leonsilicon/remnem-win32-x64-msvc": "0.1.
|
|
65
|
-
"@leonsilicon/remnem-win32-arm64-msvc": "0.1.
|
|
39
|
+
"@leonsilicon/remnem-darwin-x64": "0.1.9",
|
|
40
|
+
"@leonsilicon/remnem-darwin-arm64": "0.1.9",
|
|
41
|
+
"@leonsilicon/remnem-linux-x64-gnu": "0.1.9",
|
|
42
|
+
"@leonsilicon/remnem-linux-arm64-gnu": "0.1.9",
|
|
43
|
+
"@leonsilicon/remnem-linux-x64-musl": "0.1.9",
|
|
44
|
+
"@leonsilicon/remnem-linux-arm64-musl": "0.1.9",
|
|
45
|
+
"@leonsilicon/remnem-win32-x64-msvc": "0.1.9",
|
|
46
|
+
"@leonsilicon/remnem-win32-arm64-msvc": "0.1.9"
|
|
66
47
|
},
|
|
67
48
|
"publishConfig": {
|
|
68
49
|
"access": "public",
|
|
69
50
|
"provenance": true
|
|
70
51
|
},
|
|
71
52
|
"scripts": {
|
|
72
|
-
"build": "
|
|
73
|
-
"
|
|
74
|
-
"artifacts": "napi artifacts",
|
|
75
|
-
"prepublishOnly": "napi prepublish -t npm",
|
|
76
|
-
"test": "cargo test",
|
|
77
|
-
"version": "napi version"
|
|
53
|
+
"build": "cargo build --release",
|
|
54
|
+
"test": "cargo test"
|
|
78
55
|
},
|
|
79
|
-
"devDependencies": {
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
}
|
|
56
|
+
"devDependencies": {}
|
|
57
|
+
}
|