@yabasha/gex 1.0.1 → 1.3.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 +16 -4
- package/dist/cli-bun.mjs +916 -0
- package/dist/cli-bun.mjs.map +1 -0
- package/dist/cli-node.cjs +766 -0
- package/dist/cli-node.cjs.map +1 -0
- package/dist/cli-node.mjs +734 -0
- package/dist/cli-node.mjs.map +1 -0
- package/dist/cli.cjs +359 -302
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +356 -300
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +18 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +18 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/cli.cjs
CHANGED
|
@@ -31,40 +31,60 @@ 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: () => run2
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(cli_exports);
|
|
37
37
|
|
|
38
|
-
// src/
|
|
38
|
+
// src/runtimes/node/commands.ts
|
|
39
39
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
40
40
|
var import_commander = require("commander");
|
|
41
41
|
|
|
42
|
-
// src/cli/install.ts
|
|
42
|
+
// src/shared/cli/install.ts
|
|
43
|
+
var INSTALL_COMMANDS = {
|
|
44
|
+
npm: {
|
|
45
|
+
global: ["i", "-g"],
|
|
46
|
+
local: ["i"],
|
|
47
|
+
dev: ["i", "-D"]
|
|
48
|
+
},
|
|
49
|
+
bun: {
|
|
50
|
+
global: ["add", "-g"],
|
|
51
|
+
local: ["add"],
|
|
52
|
+
dev: ["add", "-d"]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var MAX_BUFFER = 10 * 1024 * 1024;
|
|
56
|
+
function formatSpec(pkg) {
|
|
57
|
+
return pkg.version ? `${pkg.name}@${pkg.version}` : pkg.name;
|
|
58
|
+
}
|
|
43
59
|
async function getExecFileAsync() {
|
|
44
60
|
const { execFile } = await import("child_process");
|
|
45
|
-
const { promisify } = await import("util");
|
|
46
|
-
return
|
|
61
|
+
const { promisify: promisify2 } = await import("util");
|
|
62
|
+
return promisify2(execFile);
|
|
47
63
|
}
|
|
48
|
-
async function installFromReport(report,
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
64
|
+
async function installFromReport(report, options) {
|
|
65
|
+
const opts = typeof options === "string" ? { cwd: options } : options;
|
|
66
|
+
const { cwd, packageManager = "npm" } = opts;
|
|
67
|
+
const globalPkgs = report.global_packages.map(formatSpec).filter(Boolean);
|
|
68
|
+
const localPkgs = report.local_dependencies.map(formatSpec).filter(Boolean);
|
|
69
|
+
const devPkgs = report.local_dev_dependencies.map(formatSpec).filter(Boolean);
|
|
52
70
|
if (globalPkgs.length === 0 && localPkgs.length === 0 && devPkgs.length === 0) {
|
|
53
71
|
console.log("No packages to install from report.");
|
|
54
72
|
return;
|
|
55
73
|
}
|
|
56
74
|
const execFileAsync = await getExecFileAsync();
|
|
75
|
+
const cmd = INSTALL_COMMANDS[packageManager];
|
|
76
|
+
const binary = packageManager === "bun" ? "bun" : "npm";
|
|
57
77
|
if (globalPkgs.length > 0) {
|
|
58
78
|
console.log(`Installing global: ${globalPkgs.join(" ")}`);
|
|
59
|
-
await execFileAsync(
|
|
79
|
+
await execFileAsync(binary, [...cmd.global, ...globalPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
60
80
|
}
|
|
61
81
|
if (localPkgs.length > 0) {
|
|
62
82
|
console.log(`Installing local deps: ${localPkgs.join(" ")}`);
|
|
63
|
-
await execFileAsync(
|
|
83
|
+
await execFileAsync(binary, [...cmd.local, ...localPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
64
84
|
}
|
|
65
85
|
if (devPkgs.length > 0) {
|
|
66
86
|
console.log(`Installing local devDeps: ${devPkgs.join(" ")}`);
|
|
67
|
-
await execFileAsync(
|
|
87
|
+
await execFileAsync(binary, [...cmd.dev, ...devPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
68
88
|
}
|
|
69
89
|
}
|
|
70
90
|
function printFromReport(report) {
|
|
@@ -95,10 +115,10 @@ function printFromReport(report) {
|
|
|
95
115
|
console.log(lines.join("\n"));
|
|
96
116
|
}
|
|
97
117
|
|
|
98
|
-
// src/cli/output.ts
|
|
118
|
+
// src/shared/cli/output.ts
|
|
99
119
|
var import_node_path = __toESM(require("path"), 1);
|
|
100
120
|
|
|
101
|
-
// src/report/json.ts
|
|
121
|
+
// src/shared/report/json.ts
|
|
102
122
|
function renderJson(report) {
|
|
103
123
|
const r = {
|
|
104
124
|
...report,
|
|
@@ -111,7 +131,7 @@ function renderJson(report) {
|
|
|
111
131
|
return JSON.stringify(r, null, 2);
|
|
112
132
|
}
|
|
113
133
|
|
|
114
|
-
// src/report/md.ts
|
|
134
|
+
// src/shared/report/md.ts
|
|
115
135
|
function table(headers, rows) {
|
|
116
136
|
const header = `| ${headers.join(" | ")} |`;
|
|
117
137
|
const sep = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
@@ -164,211 +184,9 @@ function renderMarkdown(report) {
|
|
|
164
184
|
return lines.join("\n");
|
|
165
185
|
}
|
|
166
186
|
|
|
167
|
-
// src/
|
|
168
|
-
function table2(headers, rows) {
|
|
169
|
-
const headerHtml = `<tr>${headers.map((h) => `<th>${h}</th>`).join("")}</tr>`;
|
|
170
|
-
const bodyHtml = rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
|
|
171
|
-
return `<table><thead>${headerHtml}</thead><tbody>${bodyHtml}</tbody></table>`;
|
|
172
|
-
}
|
|
173
|
-
function escapeHtml(text) {
|
|
174
|
-
const htmlEntities = {
|
|
175
|
-
"&": "&",
|
|
176
|
-
"<": "<",
|
|
177
|
-
">": ">",
|
|
178
|
-
'"': """,
|
|
179
|
-
"'": "'"
|
|
180
|
-
};
|
|
181
|
-
return text.replace(/[&<>"']/g, (char) => htmlEntities[char]);
|
|
182
|
-
}
|
|
183
|
-
function renderHtml(report) {
|
|
184
|
-
const hasProjectMeta = report.project_name || report.project_version || report.project_description || report.project_homepage || report.project_bugs;
|
|
185
|
-
return `<!DOCTYPE html>
|
|
186
|
-
<html lang="en">
|
|
187
|
-
<head>
|
|
188
|
-
<meta charset="UTF-8">
|
|
189
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
190
|
-
<title>GEX Report - ${report.project_name || "Dependency Audit"}</title>
|
|
191
|
-
<style>
|
|
192
|
-
body {
|
|
193
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
194
|
-
line-height: 1.6;
|
|
195
|
-
color: #333;
|
|
196
|
-
max-width: 1200px;
|
|
197
|
-
margin: 0 auto;
|
|
198
|
-
padding: 20px;
|
|
199
|
-
background-color: #f5f5f5;
|
|
200
|
-
}
|
|
201
|
-
.container {
|
|
202
|
-
background: white;
|
|
203
|
-
border-radius: 8px;
|
|
204
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
205
|
-
padding: 30px;
|
|
206
|
-
}
|
|
207
|
-
h1 {
|
|
208
|
-
color: #2c3e50;
|
|
209
|
-
border-bottom: 3px solid #3498db;
|
|
210
|
-
padding-bottom: 10px;
|
|
211
|
-
margin-top: 0;
|
|
212
|
-
}
|
|
213
|
-
h2 {
|
|
214
|
-
color: #34495e;
|
|
215
|
-
border-bottom: 2px solid #bdc3c7;
|
|
216
|
-
padding-bottom: 8px;
|
|
217
|
-
margin-top: 40px;
|
|
218
|
-
margin-bottom: 20px;
|
|
219
|
-
}
|
|
220
|
-
.metadata {
|
|
221
|
-
background: #ecf0f1;
|
|
222
|
-
border-radius: 6px;
|
|
223
|
-
padding: 20px;
|
|
224
|
-
margin-bottom: 20px;
|
|
225
|
-
}
|
|
226
|
-
.metadata dl {
|
|
227
|
-
margin: 0;
|
|
228
|
-
}
|
|
229
|
-
.metadata dt {
|
|
230
|
-
font-weight: bold;
|
|
231
|
-
color: #2c3e50;
|
|
232
|
-
margin-top: 10px;
|
|
233
|
-
}
|
|
234
|
-
.metadata dt:first-child {
|
|
235
|
-
margin-top: 0;
|
|
236
|
-
}
|
|
237
|
-
.metadata dd {
|
|
238
|
-
margin: 5px 0 0 20px;
|
|
239
|
-
color: #555;
|
|
240
|
-
}
|
|
241
|
-
.metadata dd a {
|
|
242
|
-
color: #3498db;
|
|
243
|
-
text-decoration: none;
|
|
244
|
-
}
|
|
245
|
-
.metadata dd a:hover {
|
|
246
|
-
text-decoration: underline;
|
|
247
|
-
}
|
|
248
|
-
table {
|
|
249
|
-
width: 100%;
|
|
250
|
-
border-collapse: collapse;
|
|
251
|
-
margin-bottom: 30px;
|
|
252
|
-
background: white;
|
|
253
|
-
border-radius: 6px;
|
|
254
|
-
overflow: hidden;
|
|
255
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
256
|
-
}
|
|
257
|
-
th {
|
|
258
|
-
background: #3498db;
|
|
259
|
-
color: white;
|
|
260
|
-
padding: 12px;
|
|
261
|
-
text-align: left;
|
|
262
|
-
font-weight: 600;
|
|
263
|
-
}
|
|
264
|
-
td {
|
|
265
|
-
padding: 12px;
|
|
266
|
-
border-bottom: 1px solid #ecf0f1;
|
|
267
|
-
font-family: 'SFMono-Regular', Consolas, monospace;
|
|
268
|
-
font-size: 14px;
|
|
269
|
-
}
|
|
270
|
-
tr:nth-child(even) {
|
|
271
|
-
background: #f8f9fa;
|
|
272
|
-
}
|
|
273
|
-
tr:hover {
|
|
274
|
-
background: #e8f4fd;
|
|
275
|
-
}
|
|
276
|
-
.footer {
|
|
277
|
-
text-align: center;
|
|
278
|
-
color: #7f8c8d;
|
|
279
|
-
font-size: 14px;
|
|
280
|
-
margin-top: 40px;
|
|
281
|
-
padding-top: 20px;
|
|
282
|
-
border-top: 1px solid #ecf0f1;
|
|
283
|
-
}
|
|
284
|
-
.footer strong {
|
|
285
|
-
color: #2c3e50;
|
|
286
|
-
}
|
|
287
|
-
.no-data {
|
|
288
|
-
text-align: center;
|
|
289
|
-
color: #7f8c8d;
|
|
290
|
-
font-style: italic;
|
|
291
|
-
padding: 40px;
|
|
292
|
-
}
|
|
293
|
-
.timestamp {
|
|
294
|
-
color: #95a5a6;
|
|
295
|
-
font-size: 14px;
|
|
296
|
-
margin-top: 10px;
|
|
297
|
-
}
|
|
298
|
-
</style>
|
|
299
|
-
</head>
|
|
300
|
-
<body>
|
|
301
|
-
<div class="container">
|
|
302
|
-
<h1>GEX Dependency Report</h1>
|
|
303
|
-
|
|
304
|
-
${hasProjectMeta ? `
|
|
305
|
-
<section class="metadata">
|
|
306
|
-
<h2>Project Information</h2>
|
|
307
|
-
<dl>
|
|
308
|
-
${report.project_name ? `<dt>Project Name</dt><dd>${escapeHtml(report.project_name)}</dd>` : ""}
|
|
309
|
-
${report.project_version ? `<dt>Version</dt><dd>${escapeHtml(report.project_version)}</dd>` : ""}
|
|
310
|
-
${report.project_description ? `<dt>Description</dt><dd>${escapeHtml(report.project_description)}</dd>` : ""}
|
|
311
|
-
${report.project_homepage ? `<dt>Homepage</dt><dd><a href="${escapeHtml(report.project_homepage)}" target="_blank">${escapeHtml(report.project_homepage)}</a></dd>` : ""}
|
|
312
|
-
${report.project_bugs ? `<dt>Bugs</dt><dd><a href="${escapeHtml(report.project_bugs)}" target="_blank">${escapeHtml(report.project_bugs)}</a></dd>` : ""}
|
|
313
|
-
<dt>Report Generated</dt><dd>${new Date(report.timestamp).toLocaleString()}</dd>
|
|
314
|
-
</dl>
|
|
315
|
-
</section>
|
|
316
|
-
` : ""}
|
|
317
|
-
|
|
318
|
-
${report.global_packages.length > 0 ? `
|
|
319
|
-
<section>
|
|
320
|
-
<h2>Global Packages <small>(${report.global_packages.length})</small></h2>
|
|
321
|
-
${table2(
|
|
322
|
-
["Name", "Version", "Path"],
|
|
323
|
-
report.global_packages.map((p) => [
|
|
324
|
-
escapeHtml(p.name),
|
|
325
|
-
escapeHtml(p.version || ""),
|
|
326
|
-
escapeHtml(p.resolved_path || "")
|
|
327
|
-
])
|
|
328
|
-
)}
|
|
329
|
-
</section>
|
|
330
|
-
` : '<section><h2>Global Packages</h2><div class="no-data">No global packages found</div></section>'}
|
|
331
|
-
|
|
332
|
-
${report.local_dependencies.length > 0 ? `
|
|
333
|
-
<section>
|
|
334
|
-
<h2>Local Dependencies <small>(${report.local_dependencies.length})</small></h2>
|
|
335
|
-
${table2(
|
|
336
|
-
["Name", "Version", "Path"],
|
|
337
|
-
report.local_dependencies.map((p) => [
|
|
338
|
-
escapeHtml(p.name),
|
|
339
|
-
escapeHtml(p.version || ""),
|
|
340
|
-
escapeHtml(p.resolved_path || "")
|
|
341
|
-
])
|
|
342
|
-
)}
|
|
343
|
-
</section>
|
|
344
|
-
` : '<section><h2>Local Dependencies</h2><div class="no-data">No local dependencies found</div></section>'}
|
|
345
|
-
|
|
346
|
-
${report.local_dev_dependencies.length > 0 ? `
|
|
347
|
-
<section>
|
|
348
|
-
<h2>Local Dev Dependencies <small>(${report.local_dev_dependencies.length})</small></h2>
|
|
349
|
-
${table2(
|
|
350
|
-
["Name", "Version", "Path"],
|
|
351
|
-
report.local_dev_dependencies.map((p) => [
|
|
352
|
-
escapeHtml(p.name),
|
|
353
|
-
escapeHtml(p.version || ""),
|
|
354
|
-
escapeHtml(p.resolved_path || "")
|
|
355
|
-
])
|
|
356
|
-
)}
|
|
357
|
-
</section>
|
|
358
|
-
` : '<section><h2>Local Dev Dependencies</h2><div class="no-data">No local dev dependencies found</div></section>'}
|
|
359
|
-
|
|
360
|
-
<footer class="footer">
|
|
361
|
-
<p>Generated by <strong>GEX v${escapeHtml(report.tool_version)}</strong> on ${new Date(report.timestamp).toLocaleString()}</p>
|
|
362
|
-
<p>Report format version: ${escapeHtml(report.report_version)}</p>
|
|
363
|
-
</footer>
|
|
364
|
-
</div>
|
|
365
|
-
</body>
|
|
366
|
-
</html>`;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// src/cli/output.ts
|
|
187
|
+
// src/shared/cli/output.ts
|
|
370
188
|
async function outputReport(report, format, outFile, markdownExtras) {
|
|
371
|
-
const content = format === "json" ? renderJson(report) :
|
|
189
|
+
const content = format === "json" ? renderJson(report) : renderMarkdown({ ...report, ...markdownExtras || {} });
|
|
372
190
|
if (outFile) {
|
|
373
191
|
const outDir = import_node_path.default.dirname(outFile);
|
|
374
192
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
@@ -380,7 +198,7 @@ async function outputReport(report, format, outFile, markdownExtras) {
|
|
|
380
198
|
}
|
|
381
199
|
}
|
|
382
200
|
|
|
383
|
-
// src/cli/parser.ts
|
|
201
|
+
// src/shared/cli/parser.ts
|
|
384
202
|
var import_promises = require("fs/promises");
|
|
385
203
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
386
204
|
function isMarkdownReportFile(filePath) {
|
|
@@ -429,60 +247,228 @@ async function loadReportFromFile(reportPath) {
|
|
|
429
247
|
return JSON.parse(raw);
|
|
430
248
|
}
|
|
431
249
|
|
|
432
|
-
// src/cli
|
|
433
|
-
var
|
|
434
|
-
var import_node_path5 = __toESM(require("path"), 1);
|
|
435
|
-
|
|
436
|
-
// src/npm.ts
|
|
250
|
+
// src/shared/npm-cli.ts
|
|
251
|
+
var import_node_util = require("util");
|
|
437
252
|
async function getExecFileAsync2() {
|
|
438
253
|
const { execFile } = await import("child_process");
|
|
439
|
-
|
|
440
|
-
return promisify(execFile);
|
|
254
|
+
return (0, import_node_util.promisify)(execFile);
|
|
441
255
|
}
|
|
442
|
-
async function
|
|
443
|
-
const args = ["
|
|
256
|
+
async function npmOutdated(options = {}) {
|
|
257
|
+
const args = ["outdated", "--json"];
|
|
444
258
|
if (options.global) args.push("--global");
|
|
445
|
-
if (options.omitDev) args.push("--omit=dev");
|
|
446
|
-
if (options.depth0) args.push("--depth=0");
|
|
447
259
|
try {
|
|
448
260
|
const execFileAsync = await getExecFileAsync2();
|
|
449
261
|
const { stdout } = await execFileAsync("npm", args, {
|
|
450
262
|
cwd: options.cwd,
|
|
451
263
|
maxBuffer: 10 * 1024 * 1024
|
|
452
264
|
});
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
try {
|
|
459
|
-
return JSON.parse(stdout);
|
|
460
|
-
} catch (parseErr) {
|
|
461
|
-
if (process.env.DEBUG?.includes("gex")) {
|
|
462
|
-
console.warn("npm ls stdout parse failed:", parseErr);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
265
|
+
return normalizeOutdated(stdout);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
const stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
268
|
+
if (stdout.trim()) {
|
|
269
|
+
return normalizeOutdated(stdout);
|
|
465
270
|
}
|
|
466
|
-
|
|
467
|
-
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm ls failed";
|
|
468
|
-
throw new Error(`npm ls failed: ${msg}`);
|
|
271
|
+
throw formatNpmError(error, "npm outdated");
|
|
469
272
|
}
|
|
470
273
|
}
|
|
471
|
-
async function
|
|
274
|
+
async function npmUpdate(options) {
|
|
275
|
+
const args = ["update"];
|
|
276
|
+
if (options.global) args.push("-g");
|
|
277
|
+
if (options.packages && options.packages.length > 0) args.push(...options.packages);
|
|
472
278
|
try {
|
|
473
279
|
const execFileAsync = await getExecFileAsync2();
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
throw
|
|
280
|
+
await execFileAsync("npm", args, {
|
|
281
|
+
cwd: options.cwd,
|
|
282
|
+
maxBuffer: 10 * 1024 * 1024
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
throw formatNpmError(error, "npm update");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function normalizeOutdated(stdout) {
|
|
289
|
+
if (!stdout.trim()) return [];
|
|
290
|
+
let data;
|
|
291
|
+
try {
|
|
292
|
+
data = JSON.parse(stdout);
|
|
293
|
+
} catch {
|
|
294
|
+
return [];
|
|
480
295
|
}
|
|
296
|
+
if (!data) return [];
|
|
297
|
+
return Object.entries(data).map(([name, info]) => ({
|
|
298
|
+
name,
|
|
299
|
+
current: info?.current ? String(info.current) : "",
|
|
300
|
+
wanted: info?.wanted ? String(info.wanted) : "",
|
|
301
|
+
latest: info?.latest ? String(info.latest) : "",
|
|
302
|
+
type: info?.type ? String(info.type) : void 0
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
function formatNpmError(error, commandLabel) {
|
|
306
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
307
|
+
const message = stderr || error?.message || `${commandLabel} failed`;
|
|
308
|
+
return new Error(`${commandLabel} failed: ${message}`);
|
|
481
309
|
}
|
|
482
310
|
|
|
483
|
-
// src/
|
|
484
|
-
var
|
|
311
|
+
// src/shared/cli/loader.ts
|
|
312
|
+
var frames = ["-", "\\", "|", "/"];
|
|
313
|
+
function createLoader(message) {
|
|
314
|
+
if (!process.stdout.isTTY) {
|
|
315
|
+
console.log(`${message}...`);
|
|
316
|
+
return {
|
|
317
|
+
stop(finalMessage) {
|
|
318
|
+
if (finalMessage) console.log(finalMessage);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
let index = 0;
|
|
323
|
+
const interval = setInterval(() => {
|
|
324
|
+
const frame = frames[index % frames.length];
|
|
325
|
+
index += 1;
|
|
326
|
+
process.stdout.write(`\r${message} ${frame}`);
|
|
327
|
+
}, 80);
|
|
328
|
+
return {
|
|
329
|
+
stop(finalMessage) {
|
|
330
|
+
clearInterval(interval);
|
|
331
|
+
process.stdout.write("\r");
|
|
332
|
+
if (finalMessage) {
|
|
333
|
+
console.log(finalMessage);
|
|
334
|
+
} else {
|
|
335
|
+
process.stdout.write("\x1B[2K");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/shared/cli/outdated.ts
|
|
342
|
+
function normalizeUpdateSelection(value) {
|
|
343
|
+
if (value === void 0) {
|
|
344
|
+
return { shouldUpdate: false, updateAll: false, packages: [] };
|
|
345
|
+
}
|
|
346
|
+
if (value === true) {
|
|
347
|
+
return { shouldUpdate: true, updateAll: true, packages: [] };
|
|
348
|
+
}
|
|
349
|
+
const packages = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
350
|
+
const normalized = packages.flatMap((entry) => String(entry).split(",").map((part) => part.trim())).filter(Boolean);
|
|
351
|
+
return {
|
|
352
|
+
shouldUpdate: true,
|
|
353
|
+
updateAll: false,
|
|
354
|
+
packages: normalized
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function formatOutdatedTable(entries) {
|
|
358
|
+
const headers = ["Name", "Current", "Wanted", "Latest", "Type"];
|
|
359
|
+
const rows = entries.map((entry) => [
|
|
360
|
+
entry.name,
|
|
361
|
+
entry.current || "-",
|
|
362
|
+
entry.wanted || "-",
|
|
363
|
+
entry.latest || "-",
|
|
364
|
+
entry.type || "-"
|
|
365
|
+
]);
|
|
366
|
+
const widths = headers.map(
|
|
367
|
+
(header, index) => Math.max(header.length, ...rows.map((row) => row[index].length))
|
|
368
|
+
);
|
|
369
|
+
const formatRow = (columns) => columns.map((col, idx) => col.padEnd(widths[idx], " ")).join(" ");
|
|
370
|
+
const lines = [formatRow(headers), formatRow(widths.map((w) => "-".repeat(w)))];
|
|
371
|
+
for (const row of rows) {
|
|
372
|
+
lines.push(formatRow(row));
|
|
373
|
+
}
|
|
374
|
+
return lines.join("\n");
|
|
375
|
+
}
|
|
376
|
+
async function handleOutdatedWorkflow(opts) {
|
|
377
|
+
if (!opts.checkOutdated && !opts.selection.shouldUpdate) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
let fetchLoader;
|
|
381
|
+
if (opts.checkOutdated || opts.selection.shouldUpdate) {
|
|
382
|
+
fetchLoader = createLoader("Checking for outdated packages");
|
|
383
|
+
}
|
|
384
|
+
const outdated = await opts.fetchOutdated();
|
|
385
|
+
fetchLoader?.stop("Finished checking outdated packages.");
|
|
386
|
+
if (opts.checkOutdated) {
|
|
387
|
+
if (outdated.length === 0) {
|
|
388
|
+
console.log(`All ${opts.contextLabel} packages are up to date.`);
|
|
389
|
+
} else {
|
|
390
|
+
console.log(formatOutdatedTable(outdated));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (opts.selection.shouldUpdate && opts.updateRunner) {
|
|
394
|
+
const packagesToUpdate = opts.selection.updateAll ? outdated.map((entry) => entry.name) : opts.selection.packages;
|
|
395
|
+
if (!packagesToUpdate || packagesToUpdate.length === 0) {
|
|
396
|
+
if (opts.selection.updateAll) {
|
|
397
|
+
console.log("No outdated packages to update.");
|
|
398
|
+
} else {
|
|
399
|
+
console.log("No packages were specified for updating.");
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
const updateLoader = createLoader("Updating packages");
|
|
403
|
+
await opts.updateRunner(packagesToUpdate);
|
|
404
|
+
updateLoader.stop("Finished updating packages.");
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (opts.checkOutdated || opts.selection.shouldUpdate) {
|
|
408
|
+
if (!opts.outFile) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/shared/cli/utils.ts
|
|
416
|
+
var import_node_fs = require("fs");
|
|
485
417
|
var import_promises2 = require("fs/promises");
|
|
418
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
419
|
+
var import_node_url = require("url");
|
|
420
|
+
var import_meta = {};
|
|
421
|
+
function getPkgJsonPath() {
|
|
422
|
+
let startDir;
|
|
423
|
+
try {
|
|
424
|
+
const __filename = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
425
|
+
startDir = import_node_path3.default.dirname(__filename);
|
|
426
|
+
} catch {
|
|
427
|
+
startDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
428
|
+
}
|
|
429
|
+
return findPackageJson(startDir);
|
|
430
|
+
}
|
|
431
|
+
function findPackageJson(startDir) {
|
|
432
|
+
let current = startDir;
|
|
433
|
+
const maxDepth = 6;
|
|
434
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
435
|
+
const candidate = import_node_path3.default.resolve(current, "package.json");
|
|
436
|
+
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
437
|
+
return candidate;
|
|
438
|
+
}
|
|
439
|
+
const parent = import_node_path3.default.dirname(current);
|
|
440
|
+
if (parent === current) break;
|
|
441
|
+
current = parent;
|
|
442
|
+
}
|
|
443
|
+
return import_node_path3.default.resolve(process.cwd(), "package.json");
|
|
444
|
+
}
|
|
445
|
+
async function getToolVersion() {
|
|
446
|
+
try {
|
|
447
|
+
const pkgPath = getPkgJsonPath();
|
|
448
|
+
const raw = await (0, import_promises2.readFile)(pkgPath, "utf8");
|
|
449
|
+
const pkg = JSON.parse(raw);
|
|
450
|
+
return pkg.version || "0.0.0";
|
|
451
|
+
} catch {
|
|
452
|
+
return "0.0.0";
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
var ASCII_BANNER = String.raw`
|
|
456
|
+
________ __
|
|
457
|
+
/ _____/ ____ _____/ |_ ____ ____
|
|
458
|
+
/ \ ___ / _ \ / _ \ __\/ __ \ / \
|
|
459
|
+
\ \_\ ( <_> | <_> ) | \ ___/| | \
|
|
460
|
+
\______ /\____/ \____/|__| \___ >___| /
|
|
461
|
+
\/ \/ \/
|
|
462
|
+
GEX
|
|
463
|
+
`;
|
|
464
|
+
|
|
465
|
+
// src/runtimes/node/report.ts
|
|
466
|
+
var import_promises4 = require("fs/promises");
|
|
467
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
468
|
+
|
|
469
|
+
// src/shared/transform.ts
|
|
470
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
471
|
+
var import_promises3 = require("fs/promises");
|
|
486
472
|
function toPkgArray(obj) {
|
|
487
473
|
if (!obj) return [];
|
|
488
474
|
return Object.keys(obj).map((name) => ({ name, node: obj[name] })).filter((p) => p && p.node);
|
|
@@ -500,21 +486,30 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
500
486
|
if (opts.context === "local") {
|
|
501
487
|
let pkgMeta = null;
|
|
502
488
|
try {
|
|
503
|
-
const pkgJsonPath =
|
|
504
|
-
const raw = await (0,
|
|
489
|
+
const pkgJsonPath = import_node_path4.default.join(opts.cwd || process.cwd(), "package.json");
|
|
490
|
+
const raw = await (0, import_promises3.readFile)(pkgJsonPath, "utf8");
|
|
505
491
|
pkgMeta = JSON.parse(raw);
|
|
506
492
|
} catch {
|
|
507
493
|
}
|
|
508
494
|
if (pkgMeta?.name) report.project_name = pkgMeta.name;
|
|
509
495
|
if (pkgMeta?.version) report.project_version = pkgMeta.version;
|
|
510
496
|
const depsObj = tree?.dependencies;
|
|
511
|
-
const
|
|
512
|
-
const
|
|
513
|
-
|
|
497
|
+
const devDepsObj = tree?.devDependencies;
|
|
498
|
+
const prodItems = toPkgArray(depsObj);
|
|
499
|
+
const treeDevItems = toPkgArray(devDepsObj);
|
|
500
|
+
if (treeDevItems.length > 0) {
|
|
501
|
+
for (const { name, node } of treeDevItems) {
|
|
502
|
+
const version = node && node.version || "";
|
|
503
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
504
|
+
report.local_dev_dependencies.push({ name, version, resolved_path: resolvedPath });
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const devKeys = treeDevItems.length > 0 ? new Set(treeDevItems.map((entry) => entry.name)) : new Set(Object.keys(pkgMeta?.devDependencies || {}));
|
|
508
|
+
for (const { name, node } of prodItems) {
|
|
514
509
|
const version = node && node.version || "";
|
|
515
|
-
const resolvedPath = node && node.path ||
|
|
510
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
516
511
|
const pkg = { name, version, resolved_path: resolvedPath };
|
|
517
|
-
if (devKeys.has(name)) {
|
|
512
|
+
if (!treeDevItems.length && devKeys.has(name)) {
|
|
518
513
|
report.local_dev_dependencies.push(pkg);
|
|
519
514
|
} else {
|
|
520
515
|
report.local_dependencies.push(pkg);
|
|
@@ -527,7 +522,7 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
527
522
|
const items = toPkgArray(depsObj);
|
|
528
523
|
for (const { name, node } of items) {
|
|
529
524
|
const version = node && node.version || "";
|
|
530
|
-
const resolvedPath = node && node.path ||
|
|
525
|
+
const resolvedPath = node && node.path || import_node_path4.default.join(opts.globalRoot || "", name);
|
|
531
526
|
const pkg = { name, version, resolved_path: resolvedPath };
|
|
532
527
|
report.global_packages.push(pkg);
|
|
533
528
|
}
|
|
@@ -539,42 +534,54 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
539
534
|
return report;
|
|
540
535
|
}
|
|
541
536
|
|
|
542
|
-
// src/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
537
|
+
// src/runtimes/node/package-manager.ts
|
|
538
|
+
async function getExecFileAsync3() {
|
|
539
|
+
const { execFile } = await import("child_process");
|
|
540
|
+
const { promisify: promisify2 } = await import("util");
|
|
541
|
+
return promisify2(execFile);
|
|
542
|
+
}
|
|
543
|
+
async function npmLs(options = {}) {
|
|
544
|
+
const args = ["ls", "--json"];
|
|
545
|
+
if (options.global) args.push("--global");
|
|
546
|
+
if (options.omitDev) args.push("--omit=dev");
|
|
547
|
+
if (options.depth0) args.push("--depth=0");
|
|
548
548
|
try {
|
|
549
|
-
const
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
549
|
+
const execFileAsync = await getExecFileAsync3();
|
|
550
|
+
const { stdout } = await execFileAsync("npm", args, {
|
|
551
|
+
cwd: options.cwd,
|
|
552
|
+
maxBuffer: 10 * 1024 * 1024
|
|
553
|
+
});
|
|
554
|
+
if (stdout && stdout.trim()) return JSON.parse(stdout);
|
|
555
|
+
return {};
|
|
556
|
+
} catch (err) {
|
|
557
|
+
const stdout = err?.stdout;
|
|
558
|
+
if (typeof stdout === "string" && stdout.trim()) {
|
|
559
|
+
try {
|
|
560
|
+
return JSON.parse(stdout);
|
|
561
|
+
} catch (parseErr) {
|
|
562
|
+
if (process.env.DEBUG?.includes("gex")) {
|
|
563
|
+
console.warn("npm ls stdout parse failed:", parseErr);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const stderr = err?.stderr;
|
|
568
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm ls failed";
|
|
569
|
+
throw new Error(`npm ls failed: ${msg}`);
|
|
555
570
|
}
|
|
556
571
|
}
|
|
557
|
-
async function
|
|
572
|
+
async function npmRootGlobal() {
|
|
558
573
|
try {
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
574
|
+
const execFileAsync = await getExecFileAsync3();
|
|
575
|
+
const { stdout } = await execFileAsync("npm", ["root", "-g"]);
|
|
576
|
+
return stdout.trim();
|
|
577
|
+
} catch (err) {
|
|
578
|
+
const stderr = err?.stderr;
|
|
579
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm root -g failed";
|
|
580
|
+
throw new Error(`npm root -g failed: ${msg}`);
|
|
565
581
|
}
|
|
566
582
|
}
|
|
567
|
-
var ASCII_BANNER = String.raw`
|
|
568
|
-
________ __
|
|
569
|
-
/ _____/ ____ _____/ |_ ____ ____
|
|
570
|
-
/ \ ___ / _ \ / _ \ __\/ __ \ / \
|
|
571
|
-
\ \_\ ( <_> | <_> ) | \ ___/| | \
|
|
572
|
-
\______ /\____/ \____/|__| \___ >___| /
|
|
573
|
-
\/ \/ \/
|
|
574
|
-
GEX
|
|
575
|
-
`;
|
|
576
583
|
|
|
577
|
-
// src/
|
|
584
|
+
// src/runtimes/node/report.ts
|
|
578
585
|
async function produceReport(ctx, options) {
|
|
579
586
|
const toolVersion = await getToolVersion();
|
|
580
587
|
const depth0 = !options.fullTree;
|
|
@@ -612,18 +619,17 @@ async function produceReport(ctx, options) {
|
|
|
612
619
|
return { report, markdownExtras };
|
|
613
620
|
}
|
|
614
621
|
|
|
615
|
-
// src/
|
|
622
|
+
// src/runtimes/node/commands.ts
|
|
616
623
|
function addCommonOptions(cmd, { allowOmitDev }) {
|
|
617
624
|
cmd.option(
|
|
618
625
|
"-f, --output-format <format>",
|
|
619
|
-
"Output format:
|
|
620
|
-
(val) =>
|
|
621
|
-
if (val === "md") return "md";
|
|
622
|
-
if (val === "html") return "html";
|
|
623
|
-
return "json";
|
|
624
|
-
},
|
|
626
|
+
"Output format: md or json",
|
|
627
|
+
(val) => val === "md" ? "md" : "json",
|
|
625
628
|
"json"
|
|
626
|
-
).option("-o, --out-file <path>", "Write report to file").option("--full-tree", "Include full npm ls tree (omit depth=0 default)", false)
|
|
629
|
+
).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(
|
|
630
|
+
"-u, --update-outdated [packages...]",
|
|
631
|
+
"Update outdated packages (omit package names to update every package)"
|
|
632
|
+
);
|
|
627
633
|
if (allowOmitDev) {
|
|
628
634
|
cmd.option("--omit-dev", "Exclude devDependencies (local only)", false);
|
|
629
635
|
}
|
|
@@ -637,6 +643,19 @@ function createLocalCommand(program) {
|
|
|
637
643
|
const outFile = opts.outFile;
|
|
638
644
|
const fullTree = Boolean(opts.fullTree);
|
|
639
645
|
const omitDev = Boolean(opts.omitDev);
|
|
646
|
+
const cwd = process.cwd();
|
|
647
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
648
|
+
const proceed = await handleOutdatedWorkflow({
|
|
649
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
650
|
+
selection,
|
|
651
|
+
contextLabel: "local",
|
|
652
|
+
outFile,
|
|
653
|
+
fetchOutdated: () => npmOutdated({ cwd }),
|
|
654
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
655
|
+
await npmUpdate({ cwd, packages });
|
|
656
|
+
} : void 0
|
|
657
|
+
});
|
|
658
|
+
if (!proceed) return;
|
|
640
659
|
const finalOutFile = outFile;
|
|
641
660
|
const { report, markdownExtras } = await produceReport("local", {
|
|
642
661
|
outputFormat,
|
|
@@ -655,6 +674,19 @@ function createGlobalCommand(program) {
|
|
|
655
674
|
const outputFormat = opts.outputFormat ?? "json";
|
|
656
675
|
const outFile = opts.outFile;
|
|
657
676
|
const fullTree = Boolean(opts.fullTree);
|
|
677
|
+
const cwd = process.cwd();
|
|
678
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
679
|
+
const proceed = await handleOutdatedWorkflow({
|
|
680
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
681
|
+
selection,
|
|
682
|
+
contextLabel: "global",
|
|
683
|
+
outFile,
|
|
684
|
+
fetchOutdated: () => npmOutdated({ cwd, global: true }),
|
|
685
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
686
|
+
await npmUpdate({ cwd, global: true, packages });
|
|
687
|
+
} : void 0
|
|
688
|
+
});
|
|
689
|
+
if (!proceed) return;
|
|
658
690
|
const finalOutFile = outFile;
|
|
659
691
|
const { report, markdownExtras } = await produceReport("global", {
|
|
660
692
|
outputFormat,
|
|
@@ -680,7 +712,7 @@ function createReadCommand(program) {
|
|
|
680
712
|
printFromReport(parsed);
|
|
681
713
|
}
|
|
682
714
|
if (doInstall) {
|
|
683
|
-
await installFromReport(parsed, process.cwd());
|
|
715
|
+
await installFromReport(parsed, { cwd: process.cwd(), packageManager: "npm" });
|
|
684
716
|
}
|
|
685
717
|
} catch (err) {
|
|
686
718
|
const isMd = isMarkdownReportFile(reportPath);
|
|
@@ -702,7 +734,7 @@ ${ASCII_BANNER}`);
|
|
|
702
734
|
return program;
|
|
703
735
|
}
|
|
704
736
|
|
|
705
|
-
// src/cli.ts
|
|
737
|
+
// src/runtimes/node/cli.ts
|
|
706
738
|
var import_meta2 = {};
|
|
707
739
|
async function run(argv = process.argv) {
|
|
708
740
|
const program = await createProgram();
|
|
@@ -727,6 +759,31 @@ if (isMainModule) {
|
|
|
727
759
|
process.exitCode = 1;
|
|
728
760
|
});
|
|
729
761
|
}
|
|
762
|
+
|
|
763
|
+
// src/cli.ts
|
|
764
|
+
var import_meta3 = {};
|
|
765
|
+
async function run2(argv = process.argv) {
|
|
766
|
+
return run(argv);
|
|
767
|
+
}
|
|
768
|
+
var isMainModule2 = (() => {
|
|
769
|
+
try {
|
|
770
|
+
if (typeof require !== "undefined" && typeof module !== "undefined") {
|
|
771
|
+
return require.main === module;
|
|
772
|
+
}
|
|
773
|
+
if (typeof import_meta3 !== "undefined") {
|
|
774
|
+
return import_meta3.url === `file://${process.argv[1]}`;
|
|
775
|
+
}
|
|
776
|
+
return false;
|
|
777
|
+
} catch {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
})();
|
|
781
|
+
if (isMainModule2) {
|
|
782
|
+
run2().catch((error) => {
|
|
783
|
+
console.error("CLI error:", error);
|
|
784
|
+
process.exitCode = 1;
|
|
785
|
+
});
|
|
786
|
+
}
|
|
730
787
|
// Annotate the CommonJS export names for ESM import in node:
|
|
731
788
|
0 && (module.exports = {
|
|
732
789
|
run
|