@yabasha/gex 1.3.6 → 1.3.7
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 +55 -31
- package/dist/cli-bun.mjs +2 -3
- package/dist/cli-bun.mjs.map +1 -1
- package/dist/cli-node.cjs +3 -17
- package/dist/cli-node.cjs.map +1 -1
- package/dist/cli-node.mjs +2 -3
- package/dist/cli-node.mjs.map +1 -1
- package/dist/cli.cjs +1310 -36
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1307 -34
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -31,60 +31,1334 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/cli.ts
|
|
32
32
|
var cli_exports = {};
|
|
33
33
|
__export(cli_exports, {
|
|
34
|
-
run: () =>
|
|
34
|
+
run: () => run3
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(cli_exports);
|
|
37
37
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
38
|
+
|
|
39
|
+
// src/runtimes/node/commands.ts
|
|
40
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
41
|
+
var import_commander = require("commander");
|
|
42
|
+
|
|
43
|
+
// src/shared/cli/install.ts
|
|
44
|
+
var INSTALL_COMMANDS = {
|
|
45
|
+
npm: {
|
|
46
|
+
global: ["i", "-g"],
|
|
47
|
+
local: ["i"],
|
|
48
|
+
dev: ["i", "-D"]
|
|
49
|
+
},
|
|
50
|
+
bun: {
|
|
51
|
+
global: ["add", "-g"],
|
|
52
|
+
local: ["add"],
|
|
53
|
+
dev: ["add", "-d"]
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var MAX_BUFFER = 10 * 1024 * 1024;
|
|
57
|
+
function formatSpec(pkg) {
|
|
58
|
+
return pkg.version ? `${pkg.name}@${pkg.version}` : pkg.name;
|
|
59
|
+
}
|
|
60
|
+
async function getExecFileAsync() {
|
|
61
|
+
const { execFile } = await import("child_process");
|
|
62
|
+
const { promisify: promisify3 } = await import("util");
|
|
63
|
+
return promisify3(execFile);
|
|
64
|
+
}
|
|
65
|
+
async function installFromReport(report, options) {
|
|
66
|
+
const opts = typeof options === "string" ? { cwd: options } : options;
|
|
67
|
+
const { cwd, packageManager = "npm" } = opts;
|
|
68
|
+
const globalPkgs = report.global_packages.map(formatSpec).filter(Boolean);
|
|
69
|
+
const localPkgs = report.local_dependencies.map(formatSpec).filter(Boolean);
|
|
70
|
+
const devPkgs = report.local_dev_dependencies.map(formatSpec).filter(Boolean);
|
|
71
|
+
if (globalPkgs.length === 0 && localPkgs.length === 0 && devPkgs.length === 0) {
|
|
72
|
+
console.log("No packages to install from report.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const execFileAsync = await getExecFileAsync();
|
|
76
|
+
const cmd = INSTALL_COMMANDS[packageManager];
|
|
77
|
+
const binary = packageManager === "bun" ? "bun" : "npm";
|
|
78
|
+
if (globalPkgs.length > 0) {
|
|
79
|
+
console.log(`Installing global: ${globalPkgs.join(" ")}`);
|
|
80
|
+
await execFileAsync(binary, [...cmd.global, ...globalPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
81
|
+
}
|
|
82
|
+
if (localPkgs.length > 0) {
|
|
83
|
+
console.log(`Installing local deps: ${localPkgs.join(" ")}`);
|
|
84
|
+
await execFileAsync(binary, [...cmd.local, ...localPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
85
|
+
}
|
|
86
|
+
if (devPkgs.length > 0) {
|
|
87
|
+
console.log(`Installing local devDeps: ${devPkgs.join(" ")}`);
|
|
88
|
+
await execFileAsync(binary, [...cmd.dev, ...devPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function printFromReport(report) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
if (report.global_packages.length > 0) {
|
|
94
|
+
lines.push("Global Packages:");
|
|
95
|
+
for (const p of report.global_packages) {
|
|
96
|
+
lines.push(`- ${p.name}@${p.version}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (report.local_dependencies.length > 0) {
|
|
100
|
+
if (lines.length) lines.push("");
|
|
101
|
+
lines.push("Local Dependencies:");
|
|
102
|
+
for (const p of report.local_dependencies) {
|
|
103
|
+
lines.push(`- ${p.name}@${p.version}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (report.local_dev_dependencies.length > 0) {
|
|
107
|
+
if (lines.length) lines.push("");
|
|
108
|
+
lines.push("Local Dev Dependencies:");
|
|
109
|
+
for (const p of report.local_dev_dependencies) {
|
|
110
|
+
lines.push(`- ${p.name}@${p.version}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (lines.length === 0) {
|
|
114
|
+
lines.push("(no packages found in report)");
|
|
115
|
+
}
|
|
116
|
+
console.log(lines.join("\n"));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/shared/cli/output.ts
|
|
120
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
121
|
+
|
|
122
|
+
// src/shared/report/json.ts
|
|
123
|
+
function renderJson(report) {
|
|
124
|
+
const r = {
|
|
125
|
+
...report,
|
|
126
|
+
global_packages: [...report.global_packages].sort((a, b) => a.name.localeCompare(b.name)),
|
|
127
|
+
local_dependencies: [...report.local_dependencies].sort((a, b) => a.name.localeCompare(b.name)),
|
|
128
|
+
local_dev_dependencies: [...report.local_dev_dependencies].sort(
|
|
129
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
130
|
+
)
|
|
131
|
+
};
|
|
132
|
+
return JSON.stringify(r, null, 2);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/shared/report/md.ts
|
|
136
|
+
function table(headers, rows) {
|
|
137
|
+
const header = `| ${headers.join(" | ")} |`;
|
|
138
|
+
const sep = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
139
|
+
const body = rows.map((r) => `| ${r.join(" | ")} |`).join("\n");
|
|
140
|
+
return [header, sep, body].filter(Boolean).join("\n");
|
|
141
|
+
}
|
|
142
|
+
function renderMarkdown(report) {
|
|
143
|
+
const lines = [];
|
|
144
|
+
lines.push("# GEX Report");
|
|
145
|
+
lines.push("");
|
|
146
|
+
if (report.project_name || report.project_version || report.project_description || report.project_homepage || report.project_bugs) {
|
|
147
|
+
lines.push("## Project Metadata");
|
|
148
|
+
if (report.project_name) lines.push(`- Name: ${report.project_name}`);
|
|
149
|
+
if (report.project_version) lines.push(`- Version: ${report.project_version}`);
|
|
150
|
+
if (report.project_description)
|
|
151
|
+
lines.push(`- Description: ${report.project_description}`);
|
|
152
|
+
if (report.project_homepage)
|
|
153
|
+
lines.push(`- Homepage: ${report.project_homepage}`);
|
|
154
|
+
if (report.project_bugs) lines.push(`- Bugs: ${report.project_bugs}`);
|
|
155
|
+
lines.push("");
|
|
156
|
+
}
|
|
157
|
+
if (report.global_packages.length > 0) {
|
|
158
|
+
lines.push("## Global Packages");
|
|
159
|
+
const rows = report.global_packages.map((p) => [p.name, p.version || "", p.resolved_path || ""]);
|
|
160
|
+
lines.push(table(["Name", "Version", "Path"], rows));
|
|
161
|
+
lines.push("");
|
|
162
|
+
}
|
|
163
|
+
if (report.local_dependencies.length > 0) {
|
|
164
|
+
lines.push("## Local Dependencies");
|
|
165
|
+
const rows = report.local_dependencies.map((p) => [
|
|
166
|
+
p.name,
|
|
167
|
+
p.version || "",
|
|
168
|
+
p.resolved_path || ""
|
|
169
|
+
]);
|
|
170
|
+
lines.push(table(["Name", "Version", "Path"], rows));
|
|
171
|
+
lines.push("");
|
|
172
|
+
}
|
|
173
|
+
if (report.local_dev_dependencies.length > 0) {
|
|
174
|
+
lines.push("## Local Dev Dependencies");
|
|
175
|
+
const rows = report.local_dev_dependencies.map((p) => [
|
|
176
|
+
p.name,
|
|
177
|
+
p.version || "",
|
|
178
|
+
p.resolved_path || ""
|
|
179
|
+
]);
|
|
180
|
+
lines.push(table(["Name", "Version", "Path"], rows));
|
|
181
|
+
lines.push("");
|
|
182
|
+
}
|
|
183
|
+
lines.push("---");
|
|
184
|
+
lines.push("_Generated by GEX_");
|
|
185
|
+
return lines.join("\n");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/shared/cli/output.ts
|
|
189
|
+
async function outputReport(report, format, outFile, markdownExtras) {
|
|
190
|
+
const content = format === "json" ? renderJson(report) : renderMarkdown({ ...report, ...markdownExtras || {} });
|
|
191
|
+
if (outFile) {
|
|
192
|
+
const outDir = import_node_path.default.dirname(outFile);
|
|
193
|
+
const { mkdir, writeFile } = await import("fs/promises");
|
|
194
|
+
await mkdir(outDir, { recursive: true });
|
|
195
|
+
await writeFile(outFile, content, "utf8");
|
|
196
|
+
console.log(`Wrote report to ${outFile}`);
|
|
197
|
+
} else {
|
|
198
|
+
console.log(content);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/shared/cli/parser.ts
|
|
203
|
+
var import_promises = require("fs/promises");
|
|
204
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
205
|
+
function isMarkdownReportFile(filePath) {
|
|
206
|
+
const ext = import_node_path2.default.extname(filePath).toLowerCase();
|
|
207
|
+
return ext === ".md" || ext === ".markdown";
|
|
208
|
+
}
|
|
209
|
+
function parseMarkdownPackagesTable(lines, startIndex) {
|
|
210
|
+
const rows = [];
|
|
211
|
+
if (!lines[startIndex] || !lines[startIndex].trim().startsWith("|")) return rows;
|
|
212
|
+
let i = startIndex + 2;
|
|
213
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
214
|
+
const cols = lines[i].split("|").map((c) => c.trim()).filter((_, idx, arr) => !(idx === 0 || idx === arr.length - 1));
|
|
215
|
+
const [name = "", version = "", resolved_path = ""] = cols;
|
|
216
|
+
if (name) rows.push({ name, version, resolved_path });
|
|
217
|
+
i++;
|
|
218
|
+
}
|
|
219
|
+
return rows;
|
|
220
|
+
}
|
|
221
|
+
function parseMarkdownReport(md) {
|
|
222
|
+
const lines = md.split(/\r?\n/);
|
|
223
|
+
const findSection = (title) => lines.findIndex((l) => l.trim().toLowerCase() === `## ${title}`.toLowerCase());
|
|
224
|
+
const parseSection = (idx) => {
|
|
225
|
+
if (idx < 0) return [];
|
|
226
|
+
let i = idx + 1;
|
|
227
|
+
while (i < lines.length && !lines[i].trim().startsWith("|")) i++;
|
|
228
|
+
return parseMarkdownPackagesTable(lines, i);
|
|
229
|
+
};
|
|
230
|
+
const global_packages = parseSection(findSection("Global Packages"));
|
|
231
|
+
const local_dependencies = parseSection(findSection("Local Dependencies"));
|
|
232
|
+
const local_dev_dependencies = parseSection(findSection("Local Dev Dependencies"));
|
|
233
|
+
const report = {
|
|
234
|
+
report_version: "1.0",
|
|
235
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
236
|
+
tool_version: "unknown",
|
|
237
|
+
global_packages,
|
|
238
|
+
local_dependencies,
|
|
239
|
+
local_dev_dependencies
|
|
240
|
+
};
|
|
241
|
+
return report;
|
|
242
|
+
}
|
|
243
|
+
async function loadReportFromFile(reportPath) {
|
|
244
|
+
const raw = await (0, import_promises.readFile)(reportPath, "utf8");
|
|
245
|
+
if (isMarkdownReportFile(reportPath) || raw.startsWith("# GEX Report")) {
|
|
246
|
+
return parseMarkdownReport(raw);
|
|
247
|
+
}
|
|
248
|
+
return JSON.parse(raw);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/shared/npm-cli.ts
|
|
252
|
+
var import_node_util = require("util");
|
|
253
|
+
async function getExecFileAsync2() {
|
|
254
|
+
const { execFile } = await import("child_process");
|
|
255
|
+
return (0, import_node_util.promisify)(execFile);
|
|
256
|
+
}
|
|
257
|
+
async function npmOutdated(options = {}) {
|
|
258
|
+
const args = ["outdated", "--json"];
|
|
259
|
+
if (options.global) args.push("--global");
|
|
260
|
+
try {
|
|
261
|
+
const execFileAsync = await getExecFileAsync2();
|
|
262
|
+
const { stdout } = await execFileAsync("npm", args, {
|
|
263
|
+
cwd: options.cwd,
|
|
264
|
+
maxBuffer: 10 * 1024 * 1024
|
|
265
|
+
});
|
|
266
|
+
return normalizeOutdated(stdout);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
const stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
269
|
+
if (stdout.trim()) {
|
|
270
|
+
return normalizeOutdated(stdout);
|
|
271
|
+
}
|
|
272
|
+
throw formatNpmError(error, "npm outdated");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async function npmUpdate(options) {
|
|
276
|
+
const args = ["update"];
|
|
277
|
+
if (options.global) args.push("-g");
|
|
278
|
+
if (options.packages && options.packages.length > 0) args.push(...options.packages);
|
|
279
|
+
try {
|
|
280
|
+
const execFileAsync = await getExecFileAsync2();
|
|
281
|
+
await execFileAsync("npm", args, {
|
|
282
|
+
cwd: options.cwd,
|
|
283
|
+
maxBuffer: 10 * 1024 * 1024
|
|
284
|
+
});
|
|
285
|
+
} catch (error) {
|
|
286
|
+
throw formatNpmError(error, "npm update");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function normalizeOutdated(stdout) {
|
|
290
|
+
if (!stdout.trim()) return [];
|
|
291
|
+
let data;
|
|
292
|
+
try {
|
|
293
|
+
data = JSON.parse(stdout);
|
|
294
|
+
} catch {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
if (!data) return [];
|
|
298
|
+
return Object.entries(data).map(([name, info]) => ({
|
|
299
|
+
name,
|
|
300
|
+
current: info?.current ? String(info.current) : "",
|
|
301
|
+
wanted: info?.wanted ? String(info.wanted) : "",
|
|
302
|
+
latest: info?.latest ? String(info.latest) : "",
|
|
303
|
+
type: info?.type ? String(info.type) : void 0
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
function formatNpmError(error, commandLabel) {
|
|
307
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
308
|
+
const message = stderr || error?.message || `${commandLabel} failed`;
|
|
309
|
+
return new Error(`${commandLabel} failed: ${message}`);
|
|
310
|
+
}
|
|
311
|
+
async function npmViewVersion(packageName) {
|
|
312
|
+
try {
|
|
313
|
+
const execFileAsync = await getExecFileAsync2();
|
|
314
|
+
const { stdout } = await execFileAsync("npm", ["view", packageName, "version", "--json"], {
|
|
315
|
+
maxBuffer: 5 * 1024 * 1024
|
|
316
|
+
});
|
|
317
|
+
const parsed = JSON.parse(stdout);
|
|
318
|
+
if (typeof parsed === "string") return parsed;
|
|
319
|
+
if (Array.isArray(parsed)) return parsed[parsed.length - 1] ?? "";
|
|
320
|
+
return "";
|
|
321
|
+
} catch (error) {
|
|
322
|
+
throw formatNpmError(error, `npm view ${packageName}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/shared/cli/loader.ts
|
|
327
|
+
var frames = ["-", "\\", "|", "/"];
|
|
328
|
+
function createLoader(message) {
|
|
329
|
+
if (!process.stdout.isTTY) {
|
|
330
|
+
console.log(`${message}...`);
|
|
331
|
+
return {
|
|
332
|
+
stop(finalMessage) {
|
|
333
|
+
if (finalMessage) console.log(finalMessage);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
let index = 0;
|
|
338
|
+
const interval = globalThis.setInterval(() => {
|
|
339
|
+
const frame = frames[index % frames.length];
|
|
340
|
+
index += 1;
|
|
341
|
+
process.stdout.write(`\r${message} ${frame}`);
|
|
342
|
+
}, 80);
|
|
343
|
+
return {
|
|
344
|
+
stop(finalMessage) {
|
|
345
|
+
globalThis.clearInterval(interval);
|
|
346
|
+
process.stdout.write("\r");
|
|
347
|
+
if (finalMessage) {
|
|
348
|
+
console.log(finalMessage);
|
|
349
|
+
} else {
|
|
350
|
+
process.stdout.write("\x1B[2K");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/shared/cli/outdated.ts
|
|
357
|
+
function normalizeUpdateSelection(value) {
|
|
358
|
+
if (value === void 0) {
|
|
359
|
+
return { shouldUpdate: false, updateAll: false, packages: [] };
|
|
360
|
+
}
|
|
361
|
+
if (value === true) {
|
|
362
|
+
return { shouldUpdate: true, updateAll: true, packages: [] };
|
|
363
|
+
}
|
|
364
|
+
const packages = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
365
|
+
const normalized = packages.flatMap(
|
|
366
|
+
(entry) => String(entry).split(",").map((part) => part.trim())
|
|
367
|
+
).filter(Boolean);
|
|
368
|
+
return {
|
|
369
|
+
shouldUpdate: true,
|
|
370
|
+
updateAll: false,
|
|
371
|
+
packages: normalized
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function formatOutdatedTable(entries) {
|
|
375
|
+
const headers = ["Name", "Current", "Wanted", "Latest", "Type"];
|
|
376
|
+
const rows = entries.map((entry) => [
|
|
377
|
+
entry.name,
|
|
378
|
+
entry.current || "-",
|
|
379
|
+
entry.wanted || "-",
|
|
380
|
+
entry.latest || "-",
|
|
381
|
+
entry.type || "-"
|
|
382
|
+
]);
|
|
383
|
+
const widths = headers.map(
|
|
384
|
+
(header, index) => Math.max(header.length, ...rows.map((row) => row[index].length))
|
|
385
|
+
);
|
|
386
|
+
const formatRow = (columns) => columns.map((col, idx) => col.padEnd(widths[idx], " ")).join(" ");
|
|
387
|
+
const lines = [formatRow(headers), formatRow(widths.map((w) => "-".repeat(w)))];
|
|
388
|
+
for (const row of rows) {
|
|
389
|
+
lines.push(formatRow(row));
|
|
390
|
+
}
|
|
391
|
+
return lines.join("\n");
|
|
392
|
+
}
|
|
393
|
+
async function handleOutdatedWorkflow(opts) {
|
|
394
|
+
if (!opts.checkOutdated && !opts.selection.shouldUpdate) {
|
|
395
|
+
return { proceed: true, outdated: [] };
|
|
396
|
+
}
|
|
397
|
+
let fetchLoader;
|
|
398
|
+
if (opts.checkOutdated || opts.selection.shouldUpdate) {
|
|
399
|
+
fetchLoader = createLoader("Checking for outdated packages");
|
|
400
|
+
}
|
|
401
|
+
const outdated = await opts.fetchOutdated();
|
|
402
|
+
fetchLoader?.stop("Finished checking outdated packages.");
|
|
403
|
+
if (opts.selection.shouldUpdate && opts.updateRunner) {
|
|
404
|
+
const packagesToUpdate = opts.selection.updateAll ? outdated.map((entry) => entry.name) : opts.selection.packages;
|
|
405
|
+
if (!packagesToUpdate || packagesToUpdate.length === 0) {
|
|
406
|
+
if (opts.selection.updateAll) {
|
|
407
|
+
console.log("No outdated packages to update.");
|
|
408
|
+
} else {
|
|
409
|
+
console.log("No packages were specified for updating.");
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
const updateLoader = createLoader("Updating packages");
|
|
413
|
+
await opts.updateRunner(packagesToUpdate);
|
|
414
|
+
updateLoader.stop("Finished updating packages.");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const proceed = !((opts.checkOutdated || opts.selection.shouldUpdate) && !opts.outFile);
|
|
418
|
+
return { proceed, outdated };
|
|
419
|
+
}
|
|
420
|
+
async function resolveOutdatedWithNpmView(packages) {
|
|
421
|
+
const results = [];
|
|
422
|
+
for (const pkg of packages) {
|
|
423
|
+
try {
|
|
424
|
+
const latest = await npmViewVersion(pkg.name);
|
|
425
|
+
if (latest && pkg.current && latest !== pkg.current) {
|
|
426
|
+
results.push({
|
|
427
|
+
name: pkg.name,
|
|
428
|
+
current: pkg.current,
|
|
429
|
+
wanted: pkg.declared || latest,
|
|
430
|
+
latest,
|
|
431
|
+
type: pkg.type
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
} catch {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return results;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/shared/cli/utils.ts
|
|
442
|
+
var import_node_fs = require("fs");
|
|
443
|
+
var import_promises2 = require("fs/promises");
|
|
444
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
445
|
+
var import_node_url = require("url");
|
|
38
446
|
var import_meta = {};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
447
|
+
function getPkgJsonPath() {
|
|
448
|
+
let startDir;
|
|
449
|
+
try {
|
|
450
|
+
const __filename = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
451
|
+
startDir = import_node_path3.default.dirname(__filename);
|
|
452
|
+
} catch {
|
|
453
|
+
startDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
454
|
+
}
|
|
455
|
+
return findPackageJson(startDir);
|
|
456
|
+
}
|
|
457
|
+
function findPackageJson(startDir) {
|
|
458
|
+
let current = startDir;
|
|
459
|
+
const maxDepth = 6;
|
|
460
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
461
|
+
const candidate = import_node_path3.default.resolve(current, "package.json");
|
|
462
|
+
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
463
|
+
return candidate;
|
|
464
|
+
}
|
|
465
|
+
const parent = import_node_path3.default.dirname(current);
|
|
466
|
+
if (parent === current) break;
|
|
467
|
+
current = parent;
|
|
468
|
+
}
|
|
469
|
+
return import_node_path3.default.resolve(process.cwd(), "package.json");
|
|
470
|
+
}
|
|
471
|
+
async function getToolVersion() {
|
|
472
|
+
try {
|
|
473
|
+
const pkgPath = getPkgJsonPath();
|
|
474
|
+
const raw = await (0, import_promises2.readFile)(pkgPath, "utf8");
|
|
475
|
+
const pkg = JSON.parse(raw);
|
|
476
|
+
return pkg.version || "0.0.0";
|
|
477
|
+
} catch {
|
|
478
|
+
return "0.0.0";
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
var ASCII_BANNER = String.raw`
|
|
482
|
+
________ __
|
|
483
|
+
/ _____/ ____ _____/ |_ ____ ____
|
|
484
|
+
/ \ ___ / _ \ / _ \ __\/ __ \ / \
|
|
485
|
+
\ \_\ ( <_> | <_> ) | \ ___/| | \
|
|
486
|
+
\______ /\____/ \____/|__| \___ >___| /
|
|
487
|
+
\/ \/ \/
|
|
488
|
+
GEX
|
|
489
|
+
`;
|
|
490
|
+
|
|
491
|
+
// src/runtimes/node/report.ts
|
|
492
|
+
var import_promises4 = require("fs/promises");
|
|
493
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
494
|
+
|
|
495
|
+
// src/shared/transform.ts
|
|
496
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
497
|
+
var import_promises3 = require("fs/promises");
|
|
498
|
+
function toPkgArray(obj) {
|
|
499
|
+
if (!obj) return [];
|
|
500
|
+
return Object.keys(obj).map((name) => ({ name, node: obj[name] })).filter((p) => p && p.node);
|
|
501
|
+
}
|
|
502
|
+
async function buildReportFromNpmTree(tree, opts) {
|
|
503
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
504
|
+
const report = {
|
|
505
|
+
report_version: "1.0",
|
|
506
|
+
timestamp,
|
|
507
|
+
tool_version: opts.toolVersion,
|
|
508
|
+
global_packages: [],
|
|
509
|
+
local_dependencies: [],
|
|
510
|
+
local_dev_dependencies: []
|
|
511
|
+
};
|
|
512
|
+
if (opts.context === "local") {
|
|
513
|
+
let pkgMeta = null;
|
|
514
|
+
try {
|
|
515
|
+
const pkgJsonPath = import_node_path4.default.join(opts.cwd || process.cwd(), "package.json");
|
|
516
|
+
const raw = await (0, import_promises3.readFile)(pkgJsonPath, "utf8");
|
|
517
|
+
pkgMeta = JSON.parse(raw);
|
|
518
|
+
} catch {
|
|
519
|
+
}
|
|
520
|
+
if (pkgMeta?.name) report.project_name = pkgMeta.name;
|
|
521
|
+
if (pkgMeta?.version) report.project_version = pkgMeta.version;
|
|
522
|
+
const depsObj = tree?.dependencies;
|
|
523
|
+
const devDepsObj = tree?.devDependencies;
|
|
524
|
+
const prodItems = toPkgArray(depsObj);
|
|
525
|
+
const treeDevItems = toPkgArray(devDepsObj);
|
|
526
|
+
if (treeDevItems.length > 0) {
|
|
527
|
+
for (const { name, node } of treeDevItems) {
|
|
528
|
+
const version = node && node.version || "";
|
|
529
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
530
|
+
report.local_dev_dependencies.push({ name, version, resolved_path: resolvedPath });
|
|
53
531
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
532
|
+
}
|
|
533
|
+
const devKeys = treeDevItems.length > 0 ? new Set(treeDevItems.map((entry) => entry.name)) : new Set(Object.keys(pkgMeta?.devDependencies || {}));
|
|
534
|
+
for (const { name, node } of prodItems) {
|
|
535
|
+
const version = node && node.version || "";
|
|
536
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
537
|
+
const pkg = { name, version, resolved_path: resolvedPath };
|
|
538
|
+
if (!treeDevItems.length && devKeys.has(name)) {
|
|
539
|
+
report.local_dev_dependencies.push(pkg);
|
|
540
|
+
} else {
|
|
541
|
+
report.local_dependencies.push(pkg);
|
|
57
542
|
}
|
|
58
|
-
|
|
543
|
+
}
|
|
544
|
+
report.local_dependencies.sort((a, b) => a.name.localeCompare(b.name));
|
|
545
|
+
report.local_dev_dependencies.sort((a, b) => a.name.localeCompare(b.name));
|
|
546
|
+
} else if (opts.context === "global") {
|
|
547
|
+
const depsObj = tree?.dependencies;
|
|
548
|
+
const items = toPkgArray(depsObj);
|
|
549
|
+
for (const { name, node } of items) {
|
|
550
|
+
const version = node && node.version || "";
|
|
551
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.globalRoot || "", name);
|
|
552
|
+
const pkg = { name, version, resolved_path: resolvedPath };
|
|
553
|
+
report.global_packages.push(pkg);
|
|
554
|
+
}
|
|
555
|
+
report.global_packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
556
|
+
}
|
|
557
|
+
if (opts.includeTree) {
|
|
558
|
+
report.tree = tree;
|
|
559
|
+
}
|
|
560
|
+
return report;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/runtimes/node/package-manager.ts
|
|
564
|
+
async function getExecFileAsync3() {
|
|
565
|
+
const { execFile } = await import("child_process");
|
|
566
|
+
const { promisify: promisify3 } = await import("util");
|
|
567
|
+
return promisify3(execFile);
|
|
568
|
+
}
|
|
569
|
+
async function npmLs(options = {}) {
|
|
570
|
+
const args = ["ls", "--json"];
|
|
571
|
+
if (options.global) args.push("--global");
|
|
572
|
+
if (options.omitDev) args.push("--omit=dev");
|
|
573
|
+
if (options.depth0) args.push("--depth=0");
|
|
574
|
+
try {
|
|
575
|
+
const execFileAsync = await getExecFileAsync3();
|
|
576
|
+
const { stdout } = await execFileAsync("npm", args, {
|
|
577
|
+
cwd: options.cwd,
|
|
578
|
+
maxBuffer: 10 * 1024 * 1024
|
|
579
|
+
});
|
|
580
|
+
if (stdout && stdout.trim()) return JSON.parse(stdout);
|
|
581
|
+
return {};
|
|
582
|
+
} catch (err) {
|
|
583
|
+
const stdout = err?.stdout;
|
|
584
|
+
if (typeof stdout === "string" && stdout.trim()) {
|
|
585
|
+
try {
|
|
586
|
+
return JSON.parse(stdout);
|
|
587
|
+
} catch (parseErr) {
|
|
588
|
+
if (process.env.DEBUG?.includes("gex")) {
|
|
589
|
+
console.warn("npm ls stdout parse failed:", parseErr);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const stderr = err?.stderr;
|
|
594
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm ls failed";
|
|
595
|
+
throw new Error(`npm ls failed: ${msg}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
async function npmRootGlobal() {
|
|
599
|
+
try {
|
|
600
|
+
const execFileAsync = await getExecFileAsync3();
|
|
601
|
+
const { stdout } = await execFileAsync("npm", ["root", "-g"]);
|
|
602
|
+
return stdout.trim();
|
|
603
|
+
} catch (err) {
|
|
604
|
+
const stderr = err?.stderr;
|
|
605
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm root -g failed";
|
|
606
|
+
throw new Error(`npm root -g failed: ${msg}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/runtimes/node/report.ts
|
|
611
|
+
async function produceReport(ctx, options) {
|
|
612
|
+
const toolVersion = await getToolVersion();
|
|
613
|
+
const depth0 = !options.fullTree;
|
|
614
|
+
const cwd = options.cwd || process.cwd();
|
|
615
|
+
const tree = await npmLs({
|
|
616
|
+
global: ctx === "global",
|
|
617
|
+
omitDev: ctx === "local" ? Boolean(options.omitDev) : false,
|
|
618
|
+
depth0,
|
|
619
|
+
cwd
|
|
620
|
+
});
|
|
621
|
+
let project_description;
|
|
622
|
+
let project_homepage;
|
|
623
|
+
let project_bugs;
|
|
624
|
+
if (ctx === "local") {
|
|
625
|
+
try {
|
|
626
|
+
const pkgRaw = await (0, import_promises4.readFile)(import_node_path5.default.join(cwd, "package.json"), "utf8");
|
|
627
|
+
const pkg = JSON.parse(pkgRaw);
|
|
628
|
+
project_description = pkg.description;
|
|
629
|
+
project_homepage = pkg.homepage;
|
|
630
|
+
if (typeof pkg.bugs === "string") project_bugs = pkg.bugs;
|
|
631
|
+
else if (pkg.bugs && typeof pkg.bugs.url === "string") project_bugs = pkg.bugs.url;
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const globalRoot = ctx === "global" ? await npmRootGlobal().catch(() => void 0) : void 0;
|
|
636
|
+
const report = await buildReportFromNpmTree(tree, {
|
|
637
|
+
context: ctx,
|
|
638
|
+
includeTree: Boolean(options.fullTree),
|
|
639
|
+
omitDev: Boolean(options.omitDev),
|
|
640
|
+
cwd,
|
|
641
|
+
toolVersion,
|
|
642
|
+
globalRoot
|
|
643
|
+
});
|
|
644
|
+
const markdownExtras = { project_description, project_homepage, project_bugs };
|
|
645
|
+
return { report, markdownExtras };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/runtimes/node/commands.ts
|
|
649
|
+
function addCommonOptions(cmd, { allowOmitDev }) {
|
|
650
|
+
cmd.option(
|
|
651
|
+
"-f, --output-format <format>",
|
|
652
|
+
"Output format: md or json",
|
|
653
|
+
(val) => val === "md" ? "md" : "json",
|
|
654
|
+
"json"
|
|
655
|
+
).option("-o, --out-file <path>", "Write report to file").option("--full-tree", "Include full npm ls tree (omit depth=0 default)", false).option("-c, --check-outdated", "List outdated packages instead of printing the report", false).option(
|
|
656
|
+
"-u, --update-outdated [packages...]",
|
|
657
|
+
"Update outdated packages (omit package names to update every package)"
|
|
658
|
+
);
|
|
659
|
+
if (allowOmitDev) {
|
|
660
|
+
cmd.option("--omit-dev", "Exclude devDependencies (local only)", false);
|
|
661
|
+
}
|
|
662
|
+
return cmd;
|
|
663
|
+
}
|
|
664
|
+
function createLocalCommand(program) {
|
|
665
|
+
const localCmd = program.command("local", { isDefault: true }).description("Generate a report for the current project's dependencies");
|
|
666
|
+
addCommonOptions(localCmd, { allowOmitDev: true });
|
|
667
|
+
localCmd.action(async (opts) => {
|
|
668
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
669
|
+
const outFile = opts.outFile;
|
|
670
|
+
const fullTree = Boolean(opts.fullTree);
|
|
671
|
+
const omitDev = Boolean(opts.omitDev);
|
|
672
|
+
const cwd = process.cwd();
|
|
673
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
674
|
+
const result = await handleOutdatedWorkflow({
|
|
675
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
676
|
+
selection,
|
|
677
|
+
contextLabel: "local",
|
|
678
|
+
outFile,
|
|
679
|
+
fetchOutdated: () => npmOutdated({ cwd }),
|
|
680
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
681
|
+
await npmUpdate({ cwd, packages });
|
|
682
|
+
} : void 0
|
|
683
|
+
});
|
|
684
|
+
if (opts.checkOutdated) {
|
|
685
|
+
if (result.outdated.length === 0) console.log("All local packages are up to date.");
|
|
686
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
687
|
+
}
|
|
688
|
+
if (!result.proceed) return;
|
|
689
|
+
const finalOutFile = outFile;
|
|
690
|
+
const { report, markdownExtras } = await produceReport("local", {
|
|
691
|
+
outputFormat,
|
|
692
|
+
outFile: finalOutFile,
|
|
693
|
+
fullTree,
|
|
694
|
+
omitDev
|
|
695
|
+
});
|
|
696
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
697
|
+
});
|
|
698
|
+
return localCmd;
|
|
699
|
+
}
|
|
700
|
+
function createGlobalCommand(program) {
|
|
701
|
+
const globalCmd = program.command("global").description("Generate a report of globally installed packages");
|
|
702
|
+
addCommonOptions(globalCmd, { allowOmitDev: false });
|
|
703
|
+
globalCmd.action(async (opts) => {
|
|
704
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
705
|
+
const outFile = opts.outFile;
|
|
706
|
+
const fullTree = Boolean(opts.fullTree);
|
|
707
|
+
const cwd = process.cwd();
|
|
708
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
709
|
+
const result = await handleOutdatedWorkflow({
|
|
710
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
711
|
+
selection,
|
|
712
|
+
contextLabel: "global",
|
|
713
|
+
outFile,
|
|
714
|
+
fetchOutdated: () => npmOutdated({ cwd, global: true }),
|
|
715
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
716
|
+
await npmUpdate({ cwd, global: true, packages });
|
|
717
|
+
} : void 0
|
|
718
|
+
});
|
|
719
|
+
if (opts.checkOutdated) {
|
|
720
|
+
if (result.outdated.length === 0) console.log("All global packages are up to date.");
|
|
721
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
722
|
+
}
|
|
723
|
+
if (!result.proceed) return;
|
|
724
|
+
const finalOutFile = outFile;
|
|
725
|
+
const { report, markdownExtras } = await produceReport("global", {
|
|
726
|
+
outputFormat,
|
|
727
|
+
outFile: finalOutFile,
|
|
728
|
+
fullTree
|
|
59
729
|
});
|
|
730
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
60
731
|
});
|
|
732
|
+
return globalCmd;
|
|
733
|
+
}
|
|
734
|
+
function createReadCommand(program) {
|
|
735
|
+
const readCmd = program.command("read").description(
|
|
736
|
+
"Read a previously generated report (JSON or Markdown) and either print package names or install them"
|
|
737
|
+
).argument("[report]", "Path to report file (JSON or Markdown)", "gex-report.json").option("-r, --report <path>", "Path to report file (JSON or Markdown)").option("-p, --print", "Print package names/versions from the report (default)", false).option("-i, --install", "Install packages from the report", false);
|
|
738
|
+
readCmd.action(async (reportArg, opts) => {
|
|
739
|
+
const chosen = opts.report || reportArg || "gex-report.json";
|
|
740
|
+
const reportPath = import_node_path6.default.resolve(process.cwd(), chosen);
|
|
741
|
+
try {
|
|
742
|
+
const parsed = await loadReportFromFile(reportPath);
|
|
743
|
+
const doInstall = Boolean(opts.install);
|
|
744
|
+
const doPrint = Boolean(opts.print) || !doInstall;
|
|
745
|
+
if (doPrint) {
|
|
746
|
+
printFromReport(parsed);
|
|
747
|
+
}
|
|
748
|
+
if (doInstall) {
|
|
749
|
+
await installFromReport(parsed, { cwd: process.cwd(), packageManager: "npm" });
|
|
750
|
+
}
|
|
751
|
+
} catch (err) {
|
|
752
|
+
const isMd = isMarkdownReportFile(reportPath);
|
|
753
|
+
const hint = isMd ? "Try generating a JSON report with: gex global -f json -o global.json, then: gex read global.json" : "Specify a report path with: gex read <path-to-report.json>";
|
|
754
|
+
console.error(`Failed to read report at ${reportPath}: ${err?.message || err}`);
|
|
755
|
+
console.error(hint);
|
|
756
|
+
process.exitCode = 1;
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
return readCmd;
|
|
760
|
+
}
|
|
761
|
+
async function createProgram() {
|
|
762
|
+
const program = new import_commander.Command().name("gex").description("GEX: Dependency auditing and documentation for Node.js (local and global).").version(await getToolVersion());
|
|
763
|
+
program.addHelpText("beforeAll", `
|
|
764
|
+
${ASCII_BANNER}`);
|
|
765
|
+
createLocalCommand(program);
|
|
766
|
+
createGlobalCommand(program);
|
|
767
|
+
createReadCommand(program);
|
|
768
|
+
return program;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// src/runtimes/node/cli.ts
|
|
772
|
+
async function run(argv = process.argv) {
|
|
773
|
+
const program = await createProgram();
|
|
774
|
+
await program.parseAsync(argv);
|
|
61
775
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
776
|
+
|
|
777
|
+
// src/runtimes/bun/commands.ts
|
|
778
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
779
|
+
var import_promises7 = require("fs/promises");
|
|
780
|
+
var import_commander2 = require("commander");
|
|
781
|
+
|
|
782
|
+
// src/runtimes/bun/report.ts
|
|
783
|
+
var import_promises6 = require("fs/promises");
|
|
784
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
785
|
+
|
|
786
|
+
// src/runtimes/bun/package-manager.ts
|
|
787
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
788
|
+
var import_node_fs2 = require("fs");
|
|
789
|
+
var import_promises5 = require("fs/promises");
|
|
790
|
+
var import_node_util2 = require("util");
|
|
791
|
+
var IGNORED_ENTRIES = /* @__PURE__ */ new Set([".bin"]);
|
|
792
|
+
async function bunPmLs(options = {}) {
|
|
793
|
+
if (options.global) {
|
|
794
|
+
const root = await bunPmRootGlobal();
|
|
795
|
+
const manifest2 = await readJson(import_node_path7.default.join(import_node_path7.default.dirname(root), "package.json"));
|
|
796
|
+
const selections2 = buildSelections(manifest2, { includeDev: false });
|
|
797
|
+
const dependencies2 = selections2.prod.size > 0 ? await collectPackagesForNames(root, mapSelections(selections2.prod)) : await collectPackagesFromNodeModules(root);
|
|
798
|
+
return { dependencies: dependencies2, node_modules_path: root };
|
|
799
|
+
}
|
|
800
|
+
const cwd = options.cwd || process.cwd();
|
|
801
|
+
const nodeModulesPath = await bunPmRootLocal(cwd);
|
|
802
|
+
const manifest = await readJson(import_node_path7.default.join(cwd, "package.json"));
|
|
803
|
+
const includeDev = !options.omitDev;
|
|
804
|
+
const selections = buildSelections(manifest, { includeDev });
|
|
805
|
+
const dependencies = selections.prod.size > 0 ? await collectPackagesForNames(nodeModulesPath, mapSelections(selections.prod)) : await collectPackagesFromNodeModules(nodeModulesPath);
|
|
806
|
+
let devDependencies;
|
|
807
|
+
if (includeDev) {
|
|
808
|
+
if (selections.dev.size > 0) {
|
|
809
|
+
devDependencies = await collectPackagesForNames(
|
|
810
|
+
nodeModulesPath,
|
|
811
|
+
mapSelections(selections.dev)
|
|
812
|
+
);
|
|
813
|
+
} else {
|
|
814
|
+
devDependencies = {};
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return { dependencies, devDependencies, node_modules_path: nodeModulesPath };
|
|
818
|
+
}
|
|
819
|
+
async function bunPmRootGlobal() {
|
|
820
|
+
if (process.env.GEX_BUN_GLOBAL_ROOT) {
|
|
821
|
+
return process.env.GEX_BUN_GLOBAL_ROOT;
|
|
822
|
+
}
|
|
823
|
+
const candidates = getGlobalRootCandidates();
|
|
824
|
+
for (const candidate of candidates) {
|
|
825
|
+
if (candidate && await pathExists(candidate)) {
|
|
826
|
+
return candidate;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return candidates.find((c) => Boolean(c)) || import_node_path7.default.join(process.env.HOME || process.cwd(), ".bun", "install", "global", "node_modules");
|
|
830
|
+
}
|
|
831
|
+
async function bunPmRootLocal(cwd = process.cwd()) {
|
|
832
|
+
if (process.env.GEX_BUN_LOCAL_ROOT) {
|
|
833
|
+
return process.env.GEX_BUN_LOCAL_ROOT;
|
|
834
|
+
}
|
|
835
|
+
return import_node_path7.default.join(cwd, "node_modules");
|
|
836
|
+
}
|
|
837
|
+
async function bunUpdate(options) {
|
|
838
|
+
const execFileAsync = await getExecFileAsync4();
|
|
839
|
+
const packages = options.packages && options.packages.length > 0 ? options.packages : [];
|
|
840
|
+
if (options.global) {
|
|
841
|
+
const targets = packages.length > 0 ? packages : [];
|
|
842
|
+
const list = targets.length > 0 ? targets : [];
|
|
843
|
+
const cmdPackages = list.map((name) => `${name}@latest`);
|
|
844
|
+
try {
|
|
845
|
+
await execFileAsync("bun", ["add", "-g", ...cmdPackages], {
|
|
846
|
+
cwd: options.cwd,
|
|
847
|
+
maxBuffer: 10 * 1024 * 1024
|
|
848
|
+
});
|
|
849
|
+
} catch (error) {
|
|
850
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
851
|
+
throw new Error(`bun add -g failed: ${stderr || error?.message || "unknown error"}`);
|
|
852
|
+
}
|
|
70
853
|
return;
|
|
71
854
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
855
|
+
const args = ["update"];
|
|
856
|
+
if (packages.length > 0) args.push(...packages);
|
|
857
|
+
try {
|
|
858
|
+
await execFileAsync("bun", args, {
|
|
859
|
+
cwd: options.cwd,
|
|
860
|
+
maxBuffer: 10 * 1024 * 1024
|
|
861
|
+
});
|
|
862
|
+
} catch (error) {
|
|
863
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
864
|
+
throw new Error(`bun update failed: ${stderr || error?.message || "unknown error"}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
async function collectPackagesForNames(nodeModulesPath, packages) {
|
|
868
|
+
const result = {};
|
|
869
|
+
await Promise.all(
|
|
870
|
+
packages.map(async ({ name, declared }) => {
|
|
871
|
+
const pkgDir = packageDir(nodeModulesPath, name);
|
|
872
|
+
const manifest = await readJson(import_node_path7.default.join(pkgDir, "package.json"));
|
|
873
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : name;
|
|
874
|
+
const version = typeof manifest?.version === "string" ? manifest.version : declared || "";
|
|
875
|
+
result[pkgName] = { version, path: pkgDir };
|
|
876
|
+
})
|
|
877
|
+
);
|
|
878
|
+
return result;
|
|
879
|
+
}
|
|
880
|
+
async function collectPackagesFromNodeModules(root) {
|
|
881
|
+
const result = {};
|
|
882
|
+
const entries = await safeReadDir(root);
|
|
883
|
+
for (const entry of entries) {
|
|
884
|
+
if (!entry || !entry.name || entry.name.startsWith(".") || IGNORED_ENTRIES.has(entry.name)) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
const entryPath = import_node_path7.default.join(root, entry.name);
|
|
888
|
+
if (!await isDir(entry, entryPath)) continue;
|
|
889
|
+
if (entry.name.startsWith("@")) {
|
|
890
|
+
const scopedEntries = await safeReadDir(entryPath);
|
|
891
|
+
for (const scopedEntry of scopedEntries) {
|
|
892
|
+
if (!scopedEntry || !scopedEntry.name || scopedEntry.name.startsWith(".")) continue;
|
|
893
|
+
const scopedPath = import_node_path7.default.join(entryPath, scopedEntry.name);
|
|
894
|
+
if (!await isDir(scopedEntry, scopedPath)) continue;
|
|
895
|
+
const manifest = await readJson(import_node_path7.default.join(scopedPath, "package.json"));
|
|
896
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : `${entry.name}/${scopedEntry.name}`;
|
|
897
|
+
const version = typeof manifest?.version === "string" ? manifest.version : "";
|
|
898
|
+
result[pkgName] = { version, path: scopedPath };
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
const manifest = await readJson(import_node_path7.default.join(entryPath, "package.json"));
|
|
902
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : entry.name;
|
|
903
|
+
const version = typeof manifest?.version === "string" ? manifest.version : "";
|
|
904
|
+
result[pkgName] = { version, path: entryPath };
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return result;
|
|
908
|
+
}
|
|
909
|
+
async function readJson(file) {
|
|
910
|
+
try {
|
|
911
|
+
const raw = await (0, import_promises5.readFile)(file, "utf8");
|
|
912
|
+
return JSON.parse(raw);
|
|
913
|
+
} catch {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function packageDir(root, packageName) {
|
|
918
|
+
const segments = packageName.startsWith("@") ? packageName.split("/") : [packageName];
|
|
919
|
+
return import_node_path7.default.join(root, ...segments);
|
|
920
|
+
}
|
|
921
|
+
function buildSelections(manifest, { includeDev }) {
|
|
922
|
+
const prod = /* @__PURE__ */ new Map();
|
|
923
|
+
const dev = /* @__PURE__ */ new Map();
|
|
924
|
+
const addAll = (target, record) => {
|
|
925
|
+
if (!record) return;
|
|
926
|
+
for (const [name, range] of Object.entries(record)) {
|
|
927
|
+
if (!target.has(name)) {
|
|
928
|
+
target.set(name, range);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
addAll(prod, manifest?.dependencies);
|
|
933
|
+
addAll(prod, manifest?.optionalDependencies);
|
|
934
|
+
if (includeDev) addAll(dev, manifest?.devDependencies);
|
|
935
|
+
return { prod, dev };
|
|
936
|
+
}
|
|
937
|
+
function mapSelections(map) {
|
|
938
|
+
return Array.from(map.entries()).map(([name, declared]) => ({ name, declared }));
|
|
939
|
+
}
|
|
940
|
+
async function safeReadDir(dir) {
|
|
941
|
+
try {
|
|
942
|
+
return await (0, import_promises5.readdir)(dir, { withFileTypes: true });
|
|
943
|
+
} catch {
|
|
944
|
+
return [];
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
async function isDir(entry, fullPath) {
|
|
948
|
+
if (entry.isDirectory()) return true;
|
|
949
|
+
if (entry.isSymbolicLink()) {
|
|
950
|
+
try {
|
|
951
|
+
const stats = await (0, import_promises5.stat)(fullPath);
|
|
952
|
+
return stats.isDirectory();
|
|
953
|
+
} catch {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
async function pathExists(target) {
|
|
960
|
+
try {
|
|
961
|
+
await (0, import_promises5.access)(target, import_node_fs2.constants.R_OK);
|
|
962
|
+
return true;
|
|
963
|
+
} catch {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function getGlobalRootCandidates() {
|
|
968
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
969
|
+
const bunInstall = process.env.BUN_INSTALL || (process.env.HOME ? import_node_path7.default.join(process.env.HOME, ".bun") : void 0);
|
|
970
|
+
const maybeAdd = (value) => {
|
|
971
|
+
if (value) candidates.add(value);
|
|
972
|
+
};
|
|
973
|
+
if (bunInstall) {
|
|
974
|
+
maybeAdd(import_node_path7.default.join(bunInstall, "install", "global", "node_modules"));
|
|
975
|
+
maybeAdd(import_node_path7.default.join(bunInstall, "global", "node_modules"));
|
|
976
|
+
}
|
|
977
|
+
if (process.env.XDG_DATA_HOME) {
|
|
978
|
+
maybeAdd(import_node_path7.default.join(process.env.XDG_DATA_HOME, "bun", "install", "global", "node_modules"));
|
|
979
|
+
}
|
|
980
|
+
maybeAdd("/usr/local/share/bun/global/node_modules");
|
|
981
|
+
maybeAdd("/opt/homebrew/var/bun/install/global/node_modules");
|
|
982
|
+
return Array.from(candidates);
|
|
983
|
+
}
|
|
984
|
+
async function getExecFileAsync4() {
|
|
985
|
+
const { execFile } = await import("child_process");
|
|
986
|
+
return (0, import_node_util2.promisify)(execFile);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/runtimes/bun/report.ts
|
|
990
|
+
async function produceReport2(ctx, options) {
|
|
991
|
+
const toolVersion = await getToolVersion();
|
|
992
|
+
const cwd = options.cwd || process.cwd();
|
|
993
|
+
const tree = await bunPmLs({
|
|
994
|
+
global: ctx === "global",
|
|
995
|
+
omitDev: ctx === "local" ? Boolean(options.omitDev) : false,
|
|
996
|
+
cwd
|
|
997
|
+
});
|
|
998
|
+
const nodeModulesPath = tree?.node_modules_path;
|
|
999
|
+
let project_description;
|
|
1000
|
+
let project_homepage;
|
|
1001
|
+
let project_bugs;
|
|
1002
|
+
if (ctx === "local") {
|
|
1003
|
+
try {
|
|
1004
|
+
const pkgRaw = await (0, import_promises6.readFile)(import_node_path8.default.join(cwd, "package.json"), "utf8");
|
|
1005
|
+
const pkg = JSON.parse(pkgRaw);
|
|
1006
|
+
project_description = pkg.description;
|
|
1007
|
+
project_homepage = pkg.homepage;
|
|
1008
|
+
if (typeof pkg.bugs === "string") project_bugs = pkg.bugs;
|
|
1009
|
+
else if (pkg.bugs && typeof pkg.bugs.url === "string") project_bugs = pkg.bugs.url;
|
|
1010
|
+
} catch {
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const resolvedRoot = nodeModulesPath ? nodeModulesPath : ctx === "global" ? await bunPmRootGlobal().catch(() => void 0) : await bunPmRootLocal(cwd).catch(() => `${cwd}/node_modules`);
|
|
1014
|
+
const report = await buildReportFromNpmTree(tree, {
|
|
1015
|
+
context: ctx,
|
|
1016
|
+
includeTree: Boolean(options.fullTree),
|
|
1017
|
+
omitDev: Boolean(options.omitDev),
|
|
1018
|
+
cwd,
|
|
1019
|
+
toolVersion,
|
|
1020
|
+
globalRoot: resolvedRoot
|
|
1021
|
+
});
|
|
1022
|
+
const markdownExtras = { project_description, project_homepage, project_bugs };
|
|
1023
|
+
return { report, markdownExtras };
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/runtimes/bun/commands.ts
|
|
1027
|
+
function addCommonOptions2(cmd, { allowOmitDev }) {
|
|
1028
|
+
cmd.option(
|
|
1029
|
+
"-f, --output-format <format>",
|
|
1030
|
+
"Output format: md or json",
|
|
1031
|
+
(val) => val === "md" ? "md" : "json",
|
|
1032
|
+
"json"
|
|
1033
|
+
).option("-o, --out-file <path>", "Write report to file").option("--full-tree", "Include full bun pm ls tree (when available)", false).option("-c, --check-outdated", "List outdated packages instead of printing the report", false).option(
|
|
1034
|
+
"-u, --update-outdated [packages...]",
|
|
1035
|
+
"Update outdated packages (omit package names to update every package)"
|
|
1036
|
+
);
|
|
1037
|
+
if (allowOmitDev) {
|
|
1038
|
+
cmd.option("--omit-dev", "Exclude devDependencies (local only)", false);
|
|
1039
|
+
}
|
|
1040
|
+
return cmd;
|
|
1041
|
+
}
|
|
1042
|
+
function createLocalCommand2(program) {
|
|
1043
|
+
const localCmd = program.command("local", { isDefault: true }).description("Generate a report for the current Bun project's dependencies");
|
|
1044
|
+
addCommonOptions2(localCmd, { allowOmitDev: true });
|
|
1045
|
+
localCmd.action(async (opts) => {
|
|
1046
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
1047
|
+
const outFile = opts.outFile;
|
|
1048
|
+
const fullTree = Boolean(opts.fullTree);
|
|
1049
|
+
const omitDev = Boolean(opts.omitDev);
|
|
1050
|
+
const cwd = process.cwd();
|
|
1051
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
1052
|
+
const result = await handleOutdatedWorkflow({
|
|
1053
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
1054
|
+
selection,
|
|
1055
|
+
contextLabel: "local",
|
|
1056
|
+
outFile,
|
|
1057
|
+
fetchOutdated: async () => {
|
|
1058
|
+
const tree = await bunPmLs({ cwd, omitDev });
|
|
1059
|
+
const manifest = await readPackageManifest(cwd);
|
|
1060
|
+
const declared = {
|
|
1061
|
+
...manifest?.dependencies || {},
|
|
1062
|
+
...manifest?.optionalDependencies || {},
|
|
1063
|
+
...manifest?.devDependencies || {}
|
|
1064
|
+
};
|
|
1065
|
+
const packages = Object.entries(tree.dependencies).map(([name, node]) => ({
|
|
1066
|
+
name,
|
|
1067
|
+
current: node.version,
|
|
1068
|
+
declared: declared[name],
|
|
1069
|
+
type: "prod"
|
|
1070
|
+
}));
|
|
1071
|
+
if (tree.devDependencies) {
|
|
1072
|
+
for (const [name, node] of Object.entries(tree.devDependencies)) {
|
|
1073
|
+
packages.push({
|
|
1074
|
+
name,
|
|
1075
|
+
current: node.version,
|
|
1076
|
+
declared: declared[name],
|
|
1077
|
+
type: "dev"
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return resolveOutdatedWithNpmView(packages);
|
|
1082
|
+
},
|
|
1083
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
1084
|
+
await bunUpdate({ cwd, packages });
|
|
1085
|
+
} : void 0
|
|
1086
|
+
});
|
|
1087
|
+
if (opts.checkOutdated) {
|
|
1088
|
+
if (result.outdated.length === 0) console.log("All local packages are up to date.");
|
|
1089
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
1090
|
+
}
|
|
1091
|
+
if (!result.proceed) return;
|
|
1092
|
+
const finalOutFile = outFile;
|
|
1093
|
+
const { report, markdownExtras } = await produceReport2("local", {
|
|
1094
|
+
outputFormat,
|
|
1095
|
+
outFile: finalOutFile,
|
|
1096
|
+
fullTree,
|
|
1097
|
+
omitDev
|
|
1098
|
+
});
|
|
1099
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
1100
|
+
});
|
|
1101
|
+
return localCmd;
|
|
1102
|
+
}
|
|
1103
|
+
function createGlobalCommand2(program) {
|
|
1104
|
+
const globalCmd = program.command("global").description("Generate a report of globally installed Bun packages");
|
|
1105
|
+
addCommonOptions2(globalCmd, { allowOmitDev: false });
|
|
1106
|
+
globalCmd.action(async (opts) => {
|
|
1107
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
1108
|
+
const outFile = opts.outFile;
|
|
1109
|
+
const fullTree = Boolean(opts.fullTree);
|
|
1110
|
+
const cwd = process.cwd();
|
|
1111
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
1112
|
+
const result = await handleOutdatedWorkflow({
|
|
1113
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
1114
|
+
selection,
|
|
1115
|
+
contextLabel: "global",
|
|
1116
|
+
outFile,
|
|
1117
|
+
fetchOutdated: async () => {
|
|
1118
|
+
const tree = await bunPmLs({ global: true });
|
|
1119
|
+
const packages = Object.entries(tree.dependencies).map(([name, node]) => ({
|
|
1120
|
+
name,
|
|
1121
|
+
current: node.version,
|
|
1122
|
+
type: "global"
|
|
1123
|
+
}));
|
|
1124
|
+
return resolveOutdatedWithNpmView(packages);
|
|
1125
|
+
},
|
|
1126
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
1127
|
+
await bunUpdate({ cwd, global: true, packages });
|
|
1128
|
+
} : void 0
|
|
1129
|
+
});
|
|
1130
|
+
if (opts.checkOutdated) {
|
|
1131
|
+
if (result.outdated.length === 0) console.log("All global packages are up to date.");
|
|
1132
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
1133
|
+
}
|
|
1134
|
+
if (!result.proceed) return;
|
|
1135
|
+
const finalOutFile = outFile;
|
|
1136
|
+
const { report, markdownExtras } = await produceReport2("global", {
|
|
1137
|
+
outputFormat,
|
|
1138
|
+
outFile: finalOutFile,
|
|
1139
|
+
fullTree
|
|
1140
|
+
});
|
|
1141
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
1142
|
+
});
|
|
1143
|
+
return globalCmd;
|
|
1144
|
+
}
|
|
1145
|
+
function createReadCommand2(program) {
|
|
1146
|
+
const readCmd = program.command("read").description(
|
|
1147
|
+
"Read a previously generated report (JSON or Markdown) and either print package names or install them"
|
|
1148
|
+
).argument("[report]", "Path to report file (JSON or Markdown)", "bun-report.json").option("-r, --report <path>", "Path to report file (JSON or Markdown)").option("-p, --print", "Print package names/versions from the report (default)", false).option("-i, --install", "Install packages from the report using Bun", false);
|
|
1149
|
+
readCmd.action(async (reportArg, opts) => {
|
|
1150
|
+
const chosen = opts.report || reportArg || "bun-report.json";
|
|
1151
|
+
const reportPath = import_node_path9.default.resolve(process.cwd(), chosen);
|
|
1152
|
+
try {
|
|
1153
|
+
const parsed = await loadReportFromFile(reportPath);
|
|
1154
|
+
const doInstall = Boolean(opts.install);
|
|
1155
|
+
const doPrint = Boolean(opts.print) || !doInstall;
|
|
1156
|
+
if (doPrint) {
|
|
1157
|
+
printFromReport(parsed);
|
|
1158
|
+
}
|
|
1159
|
+
if (doInstall) {
|
|
1160
|
+
await installFromReport(parsed, { cwd: process.cwd(), packageManager: "bun" });
|
|
1161
|
+
}
|
|
1162
|
+
} catch (err) {
|
|
1163
|
+
const isMd = isMarkdownReportFile(reportPath);
|
|
1164
|
+
const hint = isMd ? "Try generating a JSON report with: gex-bun global -f json -o global.json, then: gex-bun read global.json" : "Specify a report path with: gex-bun read <path-to-report.json>";
|
|
1165
|
+
console.error(`Failed to read report at ${reportPath}: ${err?.message || err}`);
|
|
1166
|
+
console.error(hint);
|
|
1167
|
+
process.exitCode = 1;
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
return readCmd;
|
|
1171
|
+
}
|
|
1172
|
+
async function createProgram2() {
|
|
1173
|
+
const program = new import_commander2.Command().name("gex-bun").description("GEX: Dependency auditing and documentation for Bun (local and global).").version(await getToolVersion());
|
|
1174
|
+
program.addHelpText("beforeAll", `
|
|
1175
|
+
${ASCII_BANNER}`);
|
|
1176
|
+
createLocalCommand2(program);
|
|
1177
|
+
createGlobalCommand2(program);
|
|
1178
|
+
createReadCommand2(program);
|
|
1179
|
+
return program;
|
|
1180
|
+
}
|
|
1181
|
+
async function readPackageManifest(cwd) {
|
|
1182
|
+
try {
|
|
1183
|
+
const raw = await (0, import_promises7.readFile)(import_node_path9.default.join(cwd, "package.json"), "utf8");
|
|
1184
|
+
return JSON.parse(raw);
|
|
1185
|
+
} catch {
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// src/runtimes/bun/cli.ts
|
|
1191
|
+
async function run2(argv = process.argv) {
|
|
1192
|
+
const program = await createProgram2();
|
|
1193
|
+
await program.parseAsync(argv);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// src/cli.ts
|
|
1197
|
+
var import_meta2 = {};
|
|
1198
|
+
var MENU_ITEMS = [
|
|
1199
|
+
{
|
|
1200
|
+
id: "1",
|
|
1201
|
+
runtime: "node",
|
|
1202
|
+
command: "local",
|
|
1203
|
+
label: "gex-node local \u2013 Node (npm) local project report"
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
id: "2",
|
|
1207
|
+
runtime: "node",
|
|
1208
|
+
command: "global",
|
|
1209
|
+
label: "gex-node global \u2013 Node (npm) global packages report"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
id: "3",
|
|
1213
|
+
runtime: "node",
|
|
1214
|
+
command: "read",
|
|
1215
|
+
label: "gex-node read \u2013 Node (npm) read existing report"
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
id: "4",
|
|
1219
|
+
runtime: "bun",
|
|
1220
|
+
command: "local",
|
|
1221
|
+
label: "gex-bun local \u2013 Bun local project report"
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
id: "5",
|
|
1225
|
+
runtime: "bun",
|
|
1226
|
+
command: "global",
|
|
1227
|
+
label: "gex-bun global \u2013 Bun global packages report"
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
id: "6",
|
|
1231
|
+
runtime: "bun",
|
|
1232
|
+
command: "read",
|
|
1233
|
+
label: "gex-bun read \u2013 Bun read existing report"
|
|
1234
|
+
}
|
|
1235
|
+
];
|
|
1236
|
+
function getIO(io) {
|
|
1237
|
+
return {
|
|
1238
|
+
input: io.input ?? process.stdin,
|
|
1239
|
+
output: io.output ?? process.stdout
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
async function askQuestion(prompt, io = {}) {
|
|
1243
|
+
const { input, output } = getIO(io);
|
|
1244
|
+
return new Promise((resolve) => {
|
|
1245
|
+
const rl = import_node_readline.default.createInterface({ input, output });
|
|
1246
|
+
rl.question(prompt, (answer) => {
|
|
1247
|
+
rl.close();
|
|
1248
|
+
resolve(answer);
|
|
1249
|
+
});
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
function parseArgLine(line) {
|
|
1253
|
+
const result = [];
|
|
1254
|
+
let current = "";
|
|
1255
|
+
let inSingleQuote = false;
|
|
1256
|
+
let inDoubleQuote = false;
|
|
1257
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
1258
|
+
const ch = line[i];
|
|
1259
|
+
if (inSingleQuote) {
|
|
1260
|
+
if (ch === "'") {
|
|
1261
|
+
inSingleQuote = false;
|
|
1262
|
+
} else {
|
|
1263
|
+
current += ch;
|
|
1264
|
+
}
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (inDoubleQuote) {
|
|
1268
|
+
if (ch === '"') {
|
|
1269
|
+
inDoubleQuote = false;
|
|
1270
|
+
} else {
|
|
1271
|
+
current += ch;
|
|
1272
|
+
}
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
if (ch === "'") {
|
|
1276
|
+
inSingleQuote = true;
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (ch === '"') {
|
|
1280
|
+
inDoubleQuote = true;
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
if (ch === " " || ch === " ") {
|
|
1284
|
+
if (current) {
|
|
1285
|
+
result.push(current);
|
|
1286
|
+
current = "";
|
|
1287
|
+
}
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
current += ch;
|
|
1291
|
+
}
|
|
1292
|
+
if (current) {
|
|
1293
|
+
result.push(current);
|
|
1294
|
+
}
|
|
1295
|
+
return result;
|
|
1296
|
+
}
|
|
1297
|
+
async function promptMenuSelection(io = {}) {
|
|
1298
|
+
const { output } = getIO(io);
|
|
1299
|
+
output.write("\nGEX interactive launcher\n");
|
|
1300
|
+
output.write("Choose a runtime and command to run:\n\n");
|
|
1301
|
+
for (const item of MENU_ITEMS) {
|
|
1302
|
+
output.write(` ${item.id}) ${item.label}
|
|
1303
|
+
`);
|
|
1304
|
+
}
|
|
1305
|
+
output.write(" q) Quit without running anything\n\n");
|
|
1306
|
+
const answer = (await askQuestion("Enter your choice (1-6 or q): ", io)).trim().toLowerCase();
|
|
1307
|
+
if (answer === "q" || answer === "quit" || answer === "exit") {
|
|
1308
|
+
output.write("\nExiting without running a command.\n");
|
|
1309
|
+
return null;
|
|
1310
|
+
}
|
|
1311
|
+
const selected = MENU_ITEMS.find((item) => item.id === answer);
|
|
1312
|
+
if (!selected) {
|
|
1313
|
+
output.write("Invalid selection. Please run `gex` again and choose a valid option.\n");
|
|
1314
|
+
process.exitCode = 1;
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
return selected;
|
|
1318
|
+
}
|
|
1319
|
+
async function runInteractive(io = {}) {
|
|
1320
|
+
const selection = await promptMenuSelection(io);
|
|
1321
|
+
if (!selection) return;
|
|
1322
|
+
const { output } = getIO(io);
|
|
1323
|
+
const binaryName = selection.runtime === "node" ? "gex-node" : "gex-bun";
|
|
1324
|
+
output.write(`
|
|
1325
|
+
Selected: ${binaryName} ${selection.command}
|
|
1326
|
+
`);
|
|
1327
|
+
const extraLine = await askQuestion(
|
|
1328
|
+
`Enter extra flags/arguments for "${binaryName} ${selection.command}" (or press Enter for none): `,
|
|
1329
|
+
io
|
|
1330
|
+
);
|
|
1331
|
+
const extraArgs = parseArgLine(extraLine.trim());
|
|
1332
|
+
const argv = [binaryName, selection.command, ...extraArgs];
|
|
1333
|
+
const renderedArgs = extraArgs.join(" ");
|
|
1334
|
+
const finalCommand = renderedArgs.length > 0 ? `${binaryName} ${selection.command} ${renderedArgs}` : `${binaryName} ${selection.command}`;
|
|
1335
|
+
output.write(`
|
|
1336
|
+
Running:
|
|
1337
|
+
|
|
1338
|
+
${finalCommand}
|
|
1339
|
+
|
|
1340
|
+
`);
|
|
1341
|
+
if (selection.runtime === "node") {
|
|
1342
|
+
await run(argv);
|
|
1343
|
+
} else {
|
|
1344
|
+
await run2(argv);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
async function run3(argv = process.argv, io = {}) {
|
|
1348
|
+
const [, , ...rest] = argv;
|
|
1349
|
+
if (rest.length > 0) {
|
|
1350
|
+
await run(argv);
|
|
76
1351
|
return;
|
|
77
1352
|
}
|
|
78
|
-
|
|
79
|
-
process.exitCode = 1;
|
|
1353
|
+
await runInteractive(io);
|
|
80
1354
|
}
|
|
81
1355
|
var isMainModule = (() => {
|
|
82
1356
|
try {
|
|
83
1357
|
if (typeof require !== "undefined" && typeof module !== "undefined") {
|
|
84
1358
|
return require.main === module;
|
|
85
1359
|
}
|
|
86
|
-
if (typeof
|
|
87
|
-
return
|
|
1360
|
+
if (typeof import_meta2 !== "undefined") {
|
|
1361
|
+
return import_meta2.url === `file://${process.argv[1]}`;
|
|
88
1362
|
}
|
|
89
1363
|
return false;
|
|
90
1364
|
} catch {
|
|
@@ -92,7 +1366,7 @@ var isMainModule = (() => {
|
|
|
92
1366
|
}
|
|
93
1367
|
})();
|
|
94
1368
|
if (isMainModule) {
|
|
95
|
-
|
|
1369
|
+
run3().catch((error) => {
|
|
96
1370
|
console.error("CLI error:", error);
|
|
97
1371
|
process.exitCode = 1;
|
|
98
1372
|
});
|