pruneguard 0.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 +6 -0
- package/bin/pruneguard +3 -0
- package/configuration_schema.json +693 -0
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +21 -0
- package/dist/index.d.mts +228 -0
- package/dist/index.mjs +144 -0
- package/dist/runtime-BxlGT_W-.mjs +132 -0
- package/package.json +57 -0
- package/report_schema.json +697 -0
package/dist/bin.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as binaryPath, t as PruneguardExecutionError } from "./runtime-BxlGT_W-.mjs";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
//#region src-js/bin.ts
|
|
5
|
+
try {
|
|
6
|
+
const child = spawn(binaryPath({ allowPathFallback: true }), process.argv.slice(2), { stdio: "inherit" });
|
|
7
|
+
for (const sig of [
|
|
8
|
+
"SIGINT",
|
|
9
|
+
"SIGTERM",
|
|
10
|
+
"SIGHUP"
|
|
11
|
+
]) process.on(sig, () => child.kill(sig));
|
|
12
|
+
child.on("close", (code, signal) => {
|
|
13
|
+
if (signal) process.kill(process.pid, signal);
|
|
14
|
+
else process.exitCode = code ?? 1;
|
|
15
|
+
});
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(err instanceof PruneguardExecutionError ? err.message : String(err));
|
|
18
|
+
process.exitCode = 2;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export {};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
//#region src-js/runtime.d.ts
|
|
2
|
+
type CommandResult = {
|
|
3
|
+
args: string[];
|
|
4
|
+
cwd?: string;
|
|
5
|
+
exitCode: number;
|
|
6
|
+
stdout: string;
|
|
7
|
+
stderr: string;
|
|
8
|
+
durationMs: number;
|
|
9
|
+
};
|
|
10
|
+
declare class PruneguardExecutionError extends Error {
|
|
11
|
+
code: "PRUNEGUARD_BINARY_NOT_FOUND" | "PRUNEGUARD_EXECUTION_FAILED" | "PRUNEGUARD_JSON_PARSE_FAILED";
|
|
12
|
+
exitCode?: number;
|
|
13
|
+
stdout?: string;
|
|
14
|
+
stderr?: string;
|
|
15
|
+
binaryPath?: string;
|
|
16
|
+
args?: string[];
|
|
17
|
+
constructor(code: PruneguardExecutionError["code"], message: string, details?: {
|
|
18
|
+
exitCode?: number;
|
|
19
|
+
stdout?: string;
|
|
20
|
+
stderr?: string;
|
|
21
|
+
binaryPath?: string;
|
|
22
|
+
args?: string[];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src-js/index.d.ts
|
|
27
|
+
type Profile = "production" | "development" | "all";
|
|
28
|
+
type ScanOptions = {
|
|
29
|
+
cwd?: string;
|
|
30
|
+
config?: string;
|
|
31
|
+
paths?: string[];
|
|
32
|
+
profile?: Profile;
|
|
33
|
+
changedSince?: string;
|
|
34
|
+
focus?: string;
|
|
35
|
+
noCache?: boolean;
|
|
36
|
+
noBaseline?: boolean;
|
|
37
|
+
requireFullScope?: boolean;
|
|
38
|
+
};
|
|
39
|
+
type ImpactOptions = {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
config?: string;
|
|
42
|
+
target: string;
|
|
43
|
+
profile?: Profile;
|
|
44
|
+
focus?: string;
|
|
45
|
+
};
|
|
46
|
+
type ExplainOptions = {
|
|
47
|
+
cwd?: string;
|
|
48
|
+
config?: string;
|
|
49
|
+
query: string;
|
|
50
|
+
profile?: Profile;
|
|
51
|
+
focus?: string;
|
|
52
|
+
};
|
|
53
|
+
type DebugResolveOptions = {
|
|
54
|
+
cwd?: string;
|
|
55
|
+
config?: string;
|
|
56
|
+
specifier: string;
|
|
57
|
+
from: string;
|
|
58
|
+
};
|
|
59
|
+
type DebugEntrypointsOptions = {
|
|
60
|
+
cwd?: string;
|
|
61
|
+
config?: string;
|
|
62
|
+
profile?: Profile;
|
|
63
|
+
};
|
|
64
|
+
type AnalysisReport = {
|
|
65
|
+
version: number;
|
|
66
|
+
toolVersion: string;
|
|
67
|
+
cwd: string;
|
|
68
|
+
profile: string;
|
|
69
|
+
summary: {
|
|
70
|
+
totalFiles: number;
|
|
71
|
+
totalPackages: number;
|
|
72
|
+
totalWorkspaces: number;
|
|
73
|
+
totalExports: number;
|
|
74
|
+
totalFindings: number;
|
|
75
|
+
errors: number;
|
|
76
|
+
warnings: number;
|
|
77
|
+
infos: number;
|
|
78
|
+
};
|
|
79
|
+
inventories: {
|
|
80
|
+
files: Array<{
|
|
81
|
+
path: string;
|
|
82
|
+
workspace?: string;
|
|
83
|
+
kind: string;
|
|
84
|
+
role?: "source" | "test" | "story" | "fixture" | "example" | "template" | "benchmark" | "config" | "generated" | "buildOutput";
|
|
85
|
+
}>;
|
|
86
|
+
packages: Array<{
|
|
87
|
+
name: string;
|
|
88
|
+
version?: string;
|
|
89
|
+
workspace: string;
|
|
90
|
+
path: string;
|
|
91
|
+
}>;
|
|
92
|
+
workspaces: Array<{
|
|
93
|
+
name: string;
|
|
94
|
+
path: string;
|
|
95
|
+
packageCount: number;
|
|
96
|
+
}>;
|
|
97
|
+
};
|
|
98
|
+
findings: Array<{
|
|
99
|
+
id: string;
|
|
100
|
+
code: string;
|
|
101
|
+
severity: "error" | "warn" | "info";
|
|
102
|
+
confidence: "high" | "medium" | "low";
|
|
103
|
+
category: string;
|
|
104
|
+
subject: string;
|
|
105
|
+
workspace?: string;
|
|
106
|
+
package?: string;
|
|
107
|
+
message: string;
|
|
108
|
+
evidence: Array<{
|
|
109
|
+
kind: string;
|
|
110
|
+
file?: string;
|
|
111
|
+
line?: number;
|
|
112
|
+
description: string;
|
|
113
|
+
}>;
|
|
114
|
+
suggestion?: string;
|
|
115
|
+
ruleName?: string;
|
|
116
|
+
}>;
|
|
117
|
+
entrypoints: Array<{
|
|
118
|
+
path: string;
|
|
119
|
+
kind: string;
|
|
120
|
+
profile: string;
|
|
121
|
+
workspace?: string;
|
|
122
|
+
source: string;
|
|
123
|
+
}>;
|
|
124
|
+
stats: {
|
|
125
|
+
durationMs: number;
|
|
126
|
+
filesParsed: number;
|
|
127
|
+
filesCached: number;
|
|
128
|
+
filesDiscovered: number;
|
|
129
|
+
filesResolved: number;
|
|
130
|
+
unresolvedSpecifiers: number;
|
|
131
|
+
unresolvedByReason: {
|
|
132
|
+
missingFile: number;
|
|
133
|
+
unsupportedSpecifier: number;
|
|
134
|
+
tsconfigPathMiss: number;
|
|
135
|
+
exportsConditionMiss: number;
|
|
136
|
+
externalized: number;
|
|
137
|
+
};
|
|
138
|
+
resolvedViaExports: number;
|
|
139
|
+
entrypointsDetected: number;
|
|
140
|
+
graphNodes: number;
|
|
141
|
+
graphEdges: number;
|
|
142
|
+
changedFiles: number;
|
|
143
|
+
affectedFiles: number;
|
|
144
|
+
affectedPackages: number;
|
|
145
|
+
affectedEntrypoints: number;
|
|
146
|
+
baselineApplied: boolean;
|
|
147
|
+
baselineProfileMismatch: boolean;
|
|
148
|
+
suppressedFindings: number;
|
|
149
|
+
newFindings: number;
|
|
150
|
+
focusApplied: boolean;
|
|
151
|
+
focusedFiles: number;
|
|
152
|
+
focusedFindings: number;
|
|
153
|
+
fullScopeRequired: boolean;
|
|
154
|
+
partialScope: boolean;
|
|
155
|
+
partialScopeReason?: string;
|
|
156
|
+
confidenceCounts: {
|
|
157
|
+
high: number;
|
|
158
|
+
medium: number;
|
|
159
|
+
low: number;
|
|
160
|
+
};
|
|
161
|
+
parityWarnings: string[];
|
|
162
|
+
cacheHits: number;
|
|
163
|
+
cacheMisses: number;
|
|
164
|
+
cacheEntriesRead: number;
|
|
165
|
+
cacheEntriesWritten: number;
|
|
166
|
+
affectedScopeIncomplete: boolean;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
type MigrationOutput = {
|
|
170
|
+
source: string;
|
|
171
|
+
config: PruneguardConfig;
|
|
172
|
+
warnings: string[];
|
|
173
|
+
};
|
|
174
|
+
type ImpactReport = {
|
|
175
|
+
target: string;
|
|
176
|
+
affectedEntrypoints: string[];
|
|
177
|
+
affectedPackages: string[];
|
|
178
|
+
affectedFiles: string[];
|
|
179
|
+
evidence: Array<{
|
|
180
|
+
kind: string;
|
|
181
|
+
file?: string;
|
|
182
|
+
line?: number;
|
|
183
|
+
description: string;
|
|
184
|
+
}>;
|
|
185
|
+
focusFiltered: boolean;
|
|
186
|
+
};
|
|
187
|
+
type ExplainReport = {
|
|
188
|
+
query: string;
|
|
189
|
+
matchedNode?: string;
|
|
190
|
+
queryKind: "finding" | "file" | "export";
|
|
191
|
+
proofs: Array<{
|
|
192
|
+
node: string;
|
|
193
|
+
relationship: string;
|
|
194
|
+
children: ExplainReport["proofs"];
|
|
195
|
+
}>;
|
|
196
|
+
relatedFindings: AnalysisReport["findings"];
|
|
197
|
+
focusFiltered: boolean;
|
|
198
|
+
};
|
|
199
|
+
type PruneguardConfig = Record<string, unknown>;
|
|
200
|
+
declare function scan(options?: ScanOptions): Promise<AnalysisReport>;
|
|
201
|
+
/** @experimental */
|
|
202
|
+
declare function scanDot(options?: ScanOptions): Promise<string>;
|
|
203
|
+
declare function impact(options: ImpactOptions): Promise<ImpactReport>;
|
|
204
|
+
declare function explain(options: ExplainOptions): Promise<ExplainReport>;
|
|
205
|
+
declare function loadConfig(options?: {
|
|
206
|
+
cwd?: string;
|
|
207
|
+
config?: string;
|
|
208
|
+
}): Promise<PruneguardConfig>;
|
|
209
|
+
declare function schemaPath(): string;
|
|
210
|
+
declare function binaryPath(): string;
|
|
211
|
+
declare function run(args: string[], options?: {
|
|
212
|
+
cwd?: string;
|
|
213
|
+
}): Promise<CommandResult>;
|
|
214
|
+
declare function debugResolve(options: DebugResolveOptions): Promise<string>;
|
|
215
|
+
declare function debugEntrypoints(options?: DebugEntrypointsOptions): Promise<string[]>;
|
|
216
|
+
/** @experimental */
|
|
217
|
+
declare function migrateKnip(options?: {
|
|
218
|
+
cwd?: string;
|
|
219
|
+
file?: string;
|
|
220
|
+
}): Promise<MigrationOutput>;
|
|
221
|
+
/** @experimental */
|
|
222
|
+
declare function migrateDepcruise(options?: {
|
|
223
|
+
cwd?: string;
|
|
224
|
+
file?: string;
|
|
225
|
+
node?: boolean;
|
|
226
|
+
}): Promise<MigrationOutput>;
|
|
227
|
+
//#endregion
|
|
228
|
+
export { AnalysisReport, type CommandResult, DebugEntrypointsOptions, DebugResolveOptions, ExplainOptions, ExplainReport, ImpactOptions, ImpactReport, MigrationOutput, Profile, PruneguardConfig, PruneguardExecutionError, ScanOptions, binaryPath, debugEntrypoints, debugResolve, explain, impact, loadConfig, migrateDepcruise, migrateKnip, run, scan, scanDot, schemaPath };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { n as binaryPath$1, r as run$1, t as PruneguardExecutionError } from "./runtime-BxlGT_W-.mjs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
//#region src-js/index.ts
|
|
4
|
+
function parseJson(result) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(result.stdout);
|
|
7
|
+
} catch {
|
|
8
|
+
throw new PruneguardExecutionError("PRUNEGUARD_JSON_PARSE_FAILED", `Failed to parse pruneguard JSON output: ${result.stderr || result.stdout.slice(0, 200)}`, {
|
|
9
|
+
exitCode: result.exitCode,
|
|
10
|
+
stdout: result.stdout,
|
|
11
|
+
stderr: result.stderr,
|
|
12
|
+
args: result.args
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function requireSuccess(result) {
|
|
17
|
+
if (result.exitCode !== 0) throw new PruneguardExecutionError("PRUNEGUARD_EXECUTION_FAILED", `pruneguard exited with code ${result.exitCode}: ${result.stderr}`, {
|
|
18
|
+
exitCode: result.exitCode,
|
|
19
|
+
stdout: result.stdout,
|
|
20
|
+
stderr: result.stderr,
|
|
21
|
+
args: result.args
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function pushGlobalFlags(args, options) {
|
|
25
|
+
if (options.config) args.push("--config", options.config);
|
|
26
|
+
if (options.profile) args.push("--profile", options.profile);
|
|
27
|
+
if (options.focus) args.push("--focus", options.focus);
|
|
28
|
+
}
|
|
29
|
+
async function scan(options = {}) {
|
|
30
|
+
const args = [
|
|
31
|
+
"--format",
|
|
32
|
+
"json",
|
|
33
|
+
"--severity",
|
|
34
|
+
"info"
|
|
35
|
+
];
|
|
36
|
+
pushGlobalFlags(args, options);
|
|
37
|
+
if (options.changedSince) args.push("--changed-since", options.changedSince);
|
|
38
|
+
if (options.noCache) args.push("--no-cache");
|
|
39
|
+
if (options.noBaseline) args.push("--no-baseline");
|
|
40
|
+
if (options.requireFullScope) args.push("--require-full-scope");
|
|
41
|
+
args.push("scan");
|
|
42
|
+
if (options.paths?.length) args.push(...options.paths);
|
|
43
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
44
|
+
if (result.exitCode !== 0 && result.exitCode !== 1) requireSuccess(result);
|
|
45
|
+
return parseJson(result);
|
|
46
|
+
}
|
|
47
|
+
/** @experimental */
|
|
48
|
+
async function scanDot(options = {}) {
|
|
49
|
+
const args = [
|
|
50
|
+
"--format",
|
|
51
|
+
"dot",
|
|
52
|
+
"--severity",
|
|
53
|
+
"info"
|
|
54
|
+
];
|
|
55
|
+
pushGlobalFlags(args, options);
|
|
56
|
+
if (options.changedSince) args.push("--changed-since", options.changedSince);
|
|
57
|
+
if (options.noCache) args.push("--no-cache");
|
|
58
|
+
if (options.noBaseline) args.push("--no-baseline");
|
|
59
|
+
if (options.requireFullScope) args.push("--require-full-scope");
|
|
60
|
+
args.push("scan");
|
|
61
|
+
if (options.paths?.length) args.push(...options.paths);
|
|
62
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
63
|
+
if (result.exitCode !== 0 && result.exitCode !== 1) requireSuccess(result);
|
|
64
|
+
return result.stdout;
|
|
65
|
+
}
|
|
66
|
+
async function impact(options) {
|
|
67
|
+
const args = ["--format", "json"];
|
|
68
|
+
pushGlobalFlags(args, options);
|
|
69
|
+
args.push("impact", options.target);
|
|
70
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
71
|
+
requireSuccess(result);
|
|
72
|
+
return parseJson(result);
|
|
73
|
+
}
|
|
74
|
+
async function explain(options) {
|
|
75
|
+
const args = ["--format", "json"];
|
|
76
|
+
pushGlobalFlags(args, options);
|
|
77
|
+
args.push("explain", options.query);
|
|
78
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
79
|
+
requireSuccess(result);
|
|
80
|
+
return parseJson(result);
|
|
81
|
+
}
|
|
82
|
+
async function loadConfig(options) {
|
|
83
|
+
const args = [];
|
|
84
|
+
if (options?.config) args.push("--config", options.config);
|
|
85
|
+
args.push("print-config");
|
|
86
|
+
const result = await run$1(args, { cwd: options?.cwd });
|
|
87
|
+
requireSuccess(result);
|
|
88
|
+
return parseJson(result);
|
|
89
|
+
}
|
|
90
|
+
function schemaPath() {
|
|
91
|
+
return fileURLToPath(new URL("../configuration_schema.json", import.meta.url));
|
|
92
|
+
}
|
|
93
|
+
function binaryPath() {
|
|
94
|
+
return binaryPath$1();
|
|
95
|
+
}
|
|
96
|
+
function run(args, options) {
|
|
97
|
+
return run$1(args, options);
|
|
98
|
+
}
|
|
99
|
+
async function debugResolve(options) {
|
|
100
|
+
const args = [];
|
|
101
|
+
if (options.config) args.push("--config", options.config);
|
|
102
|
+
args.push("debug", "resolve", "--from", options.from, options.specifier);
|
|
103
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
104
|
+
requireSuccess(result);
|
|
105
|
+
return result.stdout.trimEnd();
|
|
106
|
+
}
|
|
107
|
+
async function debugEntrypoints(options = {}) {
|
|
108
|
+
const args = [];
|
|
109
|
+
if (options.config) args.push("--config", options.config);
|
|
110
|
+
if (options.profile) args.push("--profile", options.profile);
|
|
111
|
+
args.push("debug", "entrypoints");
|
|
112
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
113
|
+
requireSuccess(result);
|
|
114
|
+
return result.stdout.trimEnd().split("\n").filter(Boolean);
|
|
115
|
+
}
|
|
116
|
+
/** @experimental */
|
|
117
|
+
async function migrateKnip(options = {}) {
|
|
118
|
+
const args = [
|
|
119
|
+
"--format",
|
|
120
|
+
"json",
|
|
121
|
+
"migrate",
|
|
122
|
+
"knip"
|
|
123
|
+
];
|
|
124
|
+
if (options.file) args.push(options.file);
|
|
125
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
126
|
+
requireSuccess(result);
|
|
127
|
+
return parseJson(result);
|
|
128
|
+
}
|
|
129
|
+
/** @experimental */
|
|
130
|
+
async function migrateDepcruise(options = {}) {
|
|
131
|
+
const args = [
|
|
132
|
+
"--format",
|
|
133
|
+
"json",
|
|
134
|
+
"migrate",
|
|
135
|
+
"depcruise"
|
|
136
|
+
];
|
|
137
|
+
if (options.node) args.push("--node");
|
|
138
|
+
if (options.file) args.push(options.file);
|
|
139
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
140
|
+
requireSuccess(result);
|
|
141
|
+
return parseJson(result);
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
export { PruneguardExecutionError, binaryPath, debugEntrypoints, debugResolve, explain, impact, loadConfig, migrateDepcruise, migrateKnip, run, scan, scanDot, schemaPath };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
//#region src-js/runtime.ts
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
var PruneguardExecutionError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
exitCode;
|
|
12
|
+
stdout;
|
|
13
|
+
stderr;
|
|
14
|
+
binaryPath;
|
|
15
|
+
args;
|
|
16
|
+
constructor(code, message, details) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "PruneguardExecutionError";
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.exitCode = details?.exitCode;
|
|
21
|
+
this.stdout = details?.stdout;
|
|
22
|
+
this.stderr = details?.stderr;
|
|
23
|
+
this.binaryPath = details?.binaryPath;
|
|
24
|
+
this.args = details?.args;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const PLATFORM_PACKAGES = {
|
|
28
|
+
"darwin-arm64": ["@pruneguard/cli-darwin-arm64"],
|
|
29
|
+
"darwin-x64": ["@pruneguard/cli-darwin-x64"],
|
|
30
|
+
"linux-arm64": ["@pruneguard/cli-linux-arm64-gnu", "@pruneguard/cli-linux-arm64-musl"],
|
|
31
|
+
"linux-x64": ["@pruneguard/cli-linux-x64-gnu", "@pruneguard/cli-linux-x64-musl"],
|
|
32
|
+
"win32-arm64": ["@pruneguard/cli-win32-arm64-msvc"],
|
|
33
|
+
"win32-x64": ["@pruneguard/cli-win32-x64-msvc"]
|
|
34
|
+
};
|
|
35
|
+
function exeName() {
|
|
36
|
+
return process.platform === "win32" ? "pruneguard.exe" : "pruneguard";
|
|
37
|
+
}
|
|
38
|
+
function findPlatformBinary() {
|
|
39
|
+
const candidates = PLATFORM_PACKAGES[`${process.platform}-${process.arch === "arm64" ? "arm64" : "x64"}`];
|
|
40
|
+
if (!candidates) return void 0;
|
|
41
|
+
for (const pkg of candidates) try {
|
|
42
|
+
const binPath = join(dirname(require.resolve(`${pkg}/package.json`)), "bin", exeName());
|
|
43
|
+
if (existsSync(binPath)) return binPath;
|
|
44
|
+
} catch {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function findDevBinary() {
|
|
49
|
+
const candidates = [join(__dirname, "..", "..", "..", "target", "release", exeName()), join(__dirname, "..", "..", "..", "target", "debug", exeName())];
|
|
50
|
+
for (const candidate of candidates) if (existsSync(candidate)) return candidate;
|
|
51
|
+
}
|
|
52
|
+
function findPathBinary() {
|
|
53
|
+
try {
|
|
54
|
+
const binPath = execFileSync(process.platform === "win32" ? "where" : "which", [exeName()], {
|
|
55
|
+
encoding: "utf8",
|
|
56
|
+
stdio: [
|
|
57
|
+
"pipe",
|
|
58
|
+
"pipe",
|
|
59
|
+
"pipe"
|
|
60
|
+
]
|
|
61
|
+
}).trim().split("\n")[0];
|
|
62
|
+
if (binPath) return binPath;
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
let cachedBinaryPath;
|
|
66
|
+
function binaryPath(options) {
|
|
67
|
+
if (cachedBinaryPath) return cachedBinaryPath;
|
|
68
|
+
const envPath = process.env.PRUNEGUARD_BINARY;
|
|
69
|
+
if (envPath) {
|
|
70
|
+
if (!existsSync(envPath)) throw new PruneguardExecutionError("PRUNEGUARD_BINARY_NOT_FOUND", `PRUNEGUARD_BINARY points to ${envPath} but the file does not exist`, { binaryPath: envPath });
|
|
71
|
+
cachedBinaryPath = envPath;
|
|
72
|
+
return envPath;
|
|
73
|
+
}
|
|
74
|
+
const platformBin = findPlatformBinary();
|
|
75
|
+
if (platformBin) {
|
|
76
|
+
cachedBinaryPath = platformBin;
|
|
77
|
+
return platformBin;
|
|
78
|
+
}
|
|
79
|
+
const devBin = findDevBinary();
|
|
80
|
+
if (devBin) {
|
|
81
|
+
cachedBinaryPath = devBin;
|
|
82
|
+
return devBin;
|
|
83
|
+
}
|
|
84
|
+
if (options?.allowPathFallback) {
|
|
85
|
+
const pathBin = findPathBinary();
|
|
86
|
+
if (pathBin) {
|
|
87
|
+
cachedBinaryPath = pathBin;
|
|
88
|
+
return pathBin;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new PruneguardExecutionError("PRUNEGUARD_BINARY_NOT_FOUND", "Could not find the pruneguard binary. Install a platform-specific package or set PRUNEGUARD_BINARY.");
|
|
92
|
+
}
|
|
93
|
+
function run(args, options) {
|
|
94
|
+
const binary = binaryPath();
|
|
95
|
+
const start = performance.now();
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const child = spawn(binary, args, {
|
|
98
|
+
cwd: options?.cwd,
|
|
99
|
+
stdio: [
|
|
100
|
+
"ignore",
|
|
101
|
+
"pipe",
|
|
102
|
+
"pipe"
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
let stdout = "";
|
|
106
|
+
let stderr = "";
|
|
107
|
+
child.stdout.on("data", (chunk) => {
|
|
108
|
+
stdout += chunk.toString();
|
|
109
|
+
});
|
|
110
|
+
child.stderr.on("data", (chunk) => {
|
|
111
|
+
stderr += chunk.toString();
|
|
112
|
+
});
|
|
113
|
+
child.on("error", (err) => {
|
|
114
|
+
reject(new PruneguardExecutionError("PRUNEGUARD_EXECUTION_FAILED", `Failed to spawn pruneguard: ${err.message}`, {
|
|
115
|
+
binaryPath: binary,
|
|
116
|
+
args
|
|
117
|
+
}));
|
|
118
|
+
});
|
|
119
|
+
child.on("close", (exitCode) => {
|
|
120
|
+
resolve({
|
|
121
|
+
args,
|
|
122
|
+
cwd: options?.cwd,
|
|
123
|
+
exitCode: exitCode ?? 1,
|
|
124
|
+
stdout,
|
|
125
|
+
stderr,
|
|
126
|
+
durationMs: Math.round(performance.now() - start)
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { binaryPath as n, run as r, PruneguardExecutionError as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pruneguard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Repo truth engine for JS/TS monorepos",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"javascript",
|
|
7
|
+
"typescript",
|
|
8
|
+
"monorepo",
|
|
9
|
+
"graph",
|
|
10
|
+
"unused-code",
|
|
11
|
+
"dead-code",
|
|
12
|
+
"dependency-graph",
|
|
13
|
+
"architecture"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/user/pruneguard",
|
|
16
|
+
"bugs": "https://github.com/user/pruneguard/issues",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "pruneguard contributors",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/user/pruneguard",
|
|
22
|
+
"directory": "npm/pruneguard"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"pruneguard": "bin/pruneguard"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"configuration_schema.json",
|
|
29
|
+
"report_schema.json",
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"bin/pruneguard"
|
|
33
|
+
],
|
|
34
|
+
"type": "module",
|
|
35
|
+
"main": "dist/index.mjs",
|
|
36
|
+
"types": "dist/index.d.mts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.mts",
|
|
40
|
+
"default": "./dist/index.mjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"optionalDependencies": {
|
|
44
|
+
"@pruneguard/cli-darwin-arm64": "0.1.0",
|
|
45
|
+
"@pruneguard/cli-darwin-x64": "0.1.0",
|
|
46
|
+
"@pruneguard/cli-linux-arm64-gnu": "0.1.0",
|
|
47
|
+
"@pruneguard/cli-linux-arm64-musl": "0.1.0",
|
|
48
|
+
"@pruneguard/cli-linux-x64-gnu": "0.1.0",
|
|
49
|
+
"@pruneguard/cli-linux-x64-musl": "0.1.0",
|
|
50
|
+
"@pruneguard/cli-win32-arm64-msvc": "0.1.0",
|
|
51
|
+
"@pruneguard/cli-win32-x64-msvc": "0.1.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"preferUnplugged": true
|
|
57
|
+
}
|