@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.mjs
CHANGED
|
@@ -6,36 +6,56 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
// src/
|
|
9
|
+
// src/runtimes/node/commands.ts
|
|
10
10
|
import path6 from "path";
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
|
|
13
|
-
// src/cli/install.ts
|
|
13
|
+
// src/shared/cli/install.ts
|
|
14
|
+
var INSTALL_COMMANDS = {
|
|
15
|
+
npm: {
|
|
16
|
+
global: ["i", "-g"],
|
|
17
|
+
local: ["i"],
|
|
18
|
+
dev: ["i", "-D"]
|
|
19
|
+
},
|
|
20
|
+
bun: {
|
|
21
|
+
global: ["add", "-g"],
|
|
22
|
+
local: ["add"],
|
|
23
|
+
dev: ["add", "-d"]
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var MAX_BUFFER = 10 * 1024 * 1024;
|
|
27
|
+
function formatSpec(pkg) {
|
|
28
|
+
return pkg.version ? `${pkg.name}@${pkg.version}` : pkg.name;
|
|
29
|
+
}
|
|
14
30
|
async function getExecFileAsync() {
|
|
15
31
|
const { execFile } = await import("child_process");
|
|
16
|
-
const { promisify } = await import("util");
|
|
17
|
-
return
|
|
32
|
+
const { promisify: promisify2 } = await import("util");
|
|
33
|
+
return promisify2(execFile);
|
|
18
34
|
}
|
|
19
|
-
async function installFromReport(report,
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
35
|
+
async function installFromReport(report, options) {
|
|
36
|
+
const opts = typeof options === "string" ? { cwd: options } : options;
|
|
37
|
+
const { cwd, packageManager = "npm" } = opts;
|
|
38
|
+
const globalPkgs = report.global_packages.map(formatSpec).filter(Boolean);
|
|
39
|
+
const localPkgs = report.local_dependencies.map(formatSpec).filter(Boolean);
|
|
40
|
+
const devPkgs = report.local_dev_dependencies.map(formatSpec).filter(Boolean);
|
|
23
41
|
if (globalPkgs.length === 0 && localPkgs.length === 0 && devPkgs.length === 0) {
|
|
24
42
|
console.log("No packages to install from report.");
|
|
25
43
|
return;
|
|
26
44
|
}
|
|
27
45
|
const execFileAsync = await getExecFileAsync();
|
|
46
|
+
const cmd = INSTALL_COMMANDS[packageManager];
|
|
47
|
+
const binary = packageManager === "bun" ? "bun" : "npm";
|
|
28
48
|
if (globalPkgs.length > 0) {
|
|
29
49
|
console.log(`Installing global: ${globalPkgs.join(" ")}`);
|
|
30
|
-
await execFileAsync(
|
|
50
|
+
await execFileAsync(binary, [...cmd.global, ...globalPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
31
51
|
}
|
|
32
52
|
if (localPkgs.length > 0) {
|
|
33
53
|
console.log(`Installing local deps: ${localPkgs.join(" ")}`);
|
|
34
|
-
await execFileAsync(
|
|
54
|
+
await execFileAsync(binary, [...cmd.local, ...localPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
35
55
|
}
|
|
36
56
|
if (devPkgs.length > 0) {
|
|
37
57
|
console.log(`Installing local devDeps: ${devPkgs.join(" ")}`);
|
|
38
|
-
await execFileAsync(
|
|
58
|
+
await execFileAsync(binary, [...cmd.dev, ...devPkgs], { cwd, maxBuffer: MAX_BUFFER });
|
|
39
59
|
}
|
|
40
60
|
}
|
|
41
61
|
function printFromReport(report) {
|
|
@@ -66,10 +86,10 @@ function printFromReport(report) {
|
|
|
66
86
|
console.log(lines.join("\n"));
|
|
67
87
|
}
|
|
68
88
|
|
|
69
|
-
// src/cli/output.ts
|
|
89
|
+
// src/shared/cli/output.ts
|
|
70
90
|
import path from "path";
|
|
71
91
|
|
|
72
|
-
// src/report/json.ts
|
|
92
|
+
// src/shared/report/json.ts
|
|
73
93
|
function renderJson(report) {
|
|
74
94
|
const r = {
|
|
75
95
|
...report,
|
|
@@ -82,7 +102,7 @@ function renderJson(report) {
|
|
|
82
102
|
return JSON.stringify(r, null, 2);
|
|
83
103
|
}
|
|
84
104
|
|
|
85
|
-
// src/report/md.ts
|
|
105
|
+
// src/shared/report/md.ts
|
|
86
106
|
function table(headers, rows) {
|
|
87
107
|
const header = `| ${headers.join(" | ")} |`;
|
|
88
108
|
const sep = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
@@ -135,211 +155,9 @@ function renderMarkdown(report) {
|
|
|
135
155
|
return lines.join("\n");
|
|
136
156
|
}
|
|
137
157
|
|
|
138
|
-
// src/
|
|
139
|
-
function table2(headers, rows) {
|
|
140
|
-
const headerHtml = `<tr>${headers.map((h) => `<th>${h}</th>`).join("")}</tr>`;
|
|
141
|
-
const bodyHtml = rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
|
|
142
|
-
return `<table><thead>${headerHtml}</thead><tbody>${bodyHtml}</tbody></table>`;
|
|
143
|
-
}
|
|
144
|
-
function escapeHtml(text) {
|
|
145
|
-
const htmlEntities = {
|
|
146
|
-
"&": "&",
|
|
147
|
-
"<": "<",
|
|
148
|
-
">": ">",
|
|
149
|
-
'"': """,
|
|
150
|
-
"'": "'"
|
|
151
|
-
};
|
|
152
|
-
return text.replace(/[&<>"']/g, (char) => htmlEntities[char]);
|
|
153
|
-
}
|
|
154
|
-
function renderHtml(report) {
|
|
155
|
-
const hasProjectMeta = report.project_name || report.project_version || report.project_description || report.project_homepage || report.project_bugs;
|
|
156
|
-
return `<!DOCTYPE html>
|
|
157
|
-
<html lang="en">
|
|
158
|
-
<head>
|
|
159
|
-
<meta charset="UTF-8">
|
|
160
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
161
|
-
<title>GEX Report - ${report.project_name || "Dependency Audit"}</title>
|
|
162
|
-
<style>
|
|
163
|
-
body {
|
|
164
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
165
|
-
line-height: 1.6;
|
|
166
|
-
color: #333;
|
|
167
|
-
max-width: 1200px;
|
|
168
|
-
margin: 0 auto;
|
|
169
|
-
padding: 20px;
|
|
170
|
-
background-color: #f5f5f5;
|
|
171
|
-
}
|
|
172
|
-
.container {
|
|
173
|
-
background: white;
|
|
174
|
-
border-radius: 8px;
|
|
175
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
176
|
-
padding: 30px;
|
|
177
|
-
}
|
|
178
|
-
h1 {
|
|
179
|
-
color: #2c3e50;
|
|
180
|
-
border-bottom: 3px solid #3498db;
|
|
181
|
-
padding-bottom: 10px;
|
|
182
|
-
margin-top: 0;
|
|
183
|
-
}
|
|
184
|
-
h2 {
|
|
185
|
-
color: #34495e;
|
|
186
|
-
border-bottom: 2px solid #bdc3c7;
|
|
187
|
-
padding-bottom: 8px;
|
|
188
|
-
margin-top: 40px;
|
|
189
|
-
margin-bottom: 20px;
|
|
190
|
-
}
|
|
191
|
-
.metadata {
|
|
192
|
-
background: #ecf0f1;
|
|
193
|
-
border-radius: 6px;
|
|
194
|
-
padding: 20px;
|
|
195
|
-
margin-bottom: 20px;
|
|
196
|
-
}
|
|
197
|
-
.metadata dl {
|
|
198
|
-
margin: 0;
|
|
199
|
-
}
|
|
200
|
-
.metadata dt {
|
|
201
|
-
font-weight: bold;
|
|
202
|
-
color: #2c3e50;
|
|
203
|
-
margin-top: 10px;
|
|
204
|
-
}
|
|
205
|
-
.metadata dt:first-child {
|
|
206
|
-
margin-top: 0;
|
|
207
|
-
}
|
|
208
|
-
.metadata dd {
|
|
209
|
-
margin: 5px 0 0 20px;
|
|
210
|
-
color: #555;
|
|
211
|
-
}
|
|
212
|
-
.metadata dd a {
|
|
213
|
-
color: #3498db;
|
|
214
|
-
text-decoration: none;
|
|
215
|
-
}
|
|
216
|
-
.metadata dd a:hover {
|
|
217
|
-
text-decoration: underline;
|
|
218
|
-
}
|
|
219
|
-
table {
|
|
220
|
-
width: 100%;
|
|
221
|
-
border-collapse: collapse;
|
|
222
|
-
margin-bottom: 30px;
|
|
223
|
-
background: white;
|
|
224
|
-
border-radius: 6px;
|
|
225
|
-
overflow: hidden;
|
|
226
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
227
|
-
}
|
|
228
|
-
th {
|
|
229
|
-
background: #3498db;
|
|
230
|
-
color: white;
|
|
231
|
-
padding: 12px;
|
|
232
|
-
text-align: left;
|
|
233
|
-
font-weight: 600;
|
|
234
|
-
}
|
|
235
|
-
td {
|
|
236
|
-
padding: 12px;
|
|
237
|
-
border-bottom: 1px solid #ecf0f1;
|
|
238
|
-
font-family: 'SFMono-Regular', Consolas, monospace;
|
|
239
|
-
font-size: 14px;
|
|
240
|
-
}
|
|
241
|
-
tr:nth-child(even) {
|
|
242
|
-
background: #f8f9fa;
|
|
243
|
-
}
|
|
244
|
-
tr:hover {
|
|
245
|
-
background: #e8f4fd;
|
|
246
|
-
}
|
|
247
|
-
.footer {
|
|
248
|
-
text-align: center;
|
|
249
|
-
color: #7f8c8d;
|
|
250
|
-
font-size: 14px;
|
|
251
|
-
margin-top: 40px;
|
|
252
|
-
padding-top: 20px;
|
|
253
|
-
border-top: 1px solid #ecf0f1;
|
|
254
|
-
}
|
|
255
|
-
.footer strong {
|
|
256
|
-
color: #2c3e50;
|
|
257
|
-
}
|
|
258
|
-
.no-data {
|
|
259
|
-
text-align: center;
|
|
260
|
-
color: #7f8c8d;
|
|
261
|
-
font-style: italic;
|
|
262
|
-
padding: 40px;
|
|
263
|
-
}
|
|
264
|
-
.timestamp {
|
|
265
|
-
color: #95a5a6;
|
|
266
|
-
font-size: 14px;
|
|
267
|
-
margin-top: 10px;
|
|
268
|
-
}
|
|
269
|
-
</style>
|
|
270
|
-
</head>
|
|
271
|
-
<body>
|
|
272
|
-
<div class="container">
|
|
273
|
-
<h1>GEX Dependency Report</h1>
|
|
274
|
-
|
|
275
|
-
${hasProjectMeta ? `
|
|
276
|
-
<section class="metadata">
|
|
277
|
-
<h2>Project Information</h2>
|
|
278
|
-
<dl>
|
|
279
|
-
${report.project_name ? `<dt>Project Name</dt><dd>${escapeHtml(report.project_name)}</dd>` : ""}
|
|
280
|
-
${report.project_version ? `<dt>Version</dt><dd>${escapeHtml(report.project_version)}</dd>` : ""}
|
|
281
|
-
${report.project_description ? `<dt>Description</dt><dd>${escapeHtml(report.project_description)}</dd>` : ""}
|
|
282
|
-
${report.project_homepage ? `<dt>Homepage</dt><dd><a href="${escapeHtml(report.project_homepage)}" target="_blank">${escapeHtml(report.project_homepage)}</a></dd>` : ""}
|
|
283
|
-
${report.project_bugs ? `<dt>Bugs</dt><dd><a href="${escapeHtml(report.project_bugs)}" target="_blank">${escapeHtml(report.project_bugs)}</a></dd>` : ""}
|
|
284
|
-
<dt>Report Generated</dt><dd>${new Date(report.timestamp).toLocaleString()}</dd>
|
|
285
|
-
</dl>
|
|
286
|
-
</section>
|
|
287
|
-
` : ""}
|
|
288
|
-
|
|
289
|
-
${report.global_packages.length > 0 ? `
|
|
290
|
-
<section>
|
|
291
|
-
<h2>Global Packages <small>(${report.global_packages.length})</small></h2>
|
|
292
|
-
${table2(
|
|
293
|
-
["Name", "Version", "Path"],
|
|
294
|
-
report.global_packages.map((p) => [
|
|
295
|
-
escapeHtml(p.name),
|
|
296
|
-
escapeHtml(p.version || ""),
|
|
297
|
-
escapeHtml(p.resolved_path || "")
|
|
298
|
-
])
|
|
299
|
-
)}
|
|
300
|
-
</section>
|
|
301
|
-
` : '<section><h2>Global Packages</h2><div class="no-data">No global packages found</div></section>'}
|
|
302
|
-
|
|
303
|
-
${report.local_dependencies.length > 0 ? `
|
|
304
|
-
<section>
|
|
305
|
-
<h2>Local Dependencies <small>(${report.local_dependencies.length})</small></h2>
|
|
306
|
-
${table2(
|
|
307
|
-
["Name", "Version", "Path"],
|
|
308
|
-
report.local_dependencies.map((p) => [
|
|
309
|
-
escapeHtml(p.name),
|
|
310
|
-
escapeHtml(p.version || ""),
|
|
311
|
-
escapeHtml(p.resolved_path || "")
|
|
312
|
-
])
|
|
313
|
-
)}
|
|
314
|
-
</section>
|
|
315
|
-
` : '<section><h2>Local Dependencies</h2><div class="no-data">No local dependencies found</div></section>'}
|
|
316
|
-
|
|
317
|
-
${report.local_dev_dependencies.length > 0 ? `
|
|
318
|
-
<section>
|
|
319
|
-
<h2>Local Dev Dependencies <small>(${report.local_dev_dependencies.length})</small></h2>
|
|
320
|
-
${table2(
|
|
321
|
-
["Name", "Version", "Path"],
|
|
322
|
-
report.local_dev_dependencies.map((p) => [
|
|
323
|
-
escapeHtml(p.name),
|
|
324
|
-
escapeHtml(p.version || ""),
|
|
325
|
-
escapeHtml(p.resolved_path || "")
|
|
326
|
-
])
|
|
327
|
-
)}
|
|
328
|
-
</section>
|
|
329
|
-
` : '<section><h2>Local Dev Dependencies</h2><div class="no-data">No local dev dependencies found</div></section>'}
|
|
330
|
-
|
|
331
|
-
<footer class="footer">
|
|
332
|
-
<p>Generated by <strong>GEX v${escapeHtml(report.tool_version)}</strong> on ${new Date(report.timestamp).toLocaleString()}</p>
|
|
333
|
-
<p>Report format version: ${escapeHtml(report.report_version)}</p>
|
|
334
|
-
</footer>
|
|
335
|
-
</div>
|
|
336
|
-
</body>
|
|
337
|
-
</html>`;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// src/cli/output.ts
|
|
158
|
+
// src/shared/cli/output.ts
|
|
341
159
|
async function outputReport(report, format, outFile, markdownExtras) {
|
|
342
|
-
const content = format === "json" ? renderJson(report) :
|
|
160
|
+
const content = format === "json" ? renderJson(report) : renderMarkdown({ ...report, ...markdownExtras || {} });
|
|
343
161
|
if (outFile) {
|
|
344
162
|
const outDir = path.dirname(outFile);
|
|
345
163
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
@@ -351,7 +169,7 @@ async function outputReport(report, format, outFile, markdownExtras) {
|
|
|
351
169
|
}
|
|
352
170
|
}
|
|
353
171
|
|
|
354
|
-
// src/cli/parser.ts
|
|
172
|
+
// src/shared/cli/parser.ts
|
|
355
173
|
import { readFile } from "fs/promises";
|
|
356
174
|
import path2 from "path";
|
|
357
175
|
function isMarkdownReportFile(filePath) {
|
|
@@ -400,60 +218,227 @@ async function loadReportFromFile(reportPath) {
|
|
|
400
218
|
return JSON.parse(raw);
|
|
401
219
|
}
|
|
402
220
|
|
|
403
|
-
// src/cli
|
|
404
|
-
import {
|
|
405
|
-
import path5 from "path";
|
|
406
|
-
|
|
407
|
-
// src/npm.ts
|
|
221
|
+
// src/shared/npm-cli.ts
|
|
222
|
+
import { promisify } from "util";
|
|
408
223
|
async function getExecFileAsync2() {
|
|
409
224
|
const { execFile } = await import("child_process");
|
|
410
|
-
const { promisify } = await import("util");
|
|
411
225
|
return promisify(execFile);
|
|
412
226
|
}
|
|
413
|
-
async function
|
|
414
|
-
const args = ["
|
|
227
|
+
async function npmOutdated(options = {}) {
|
|
228
|
+
const args = ["outdated", "--json"];
|
|
415
229
|
if (options.global) args.push("--global");
|
|
416
|
-
if (options.omitDev) args.push("--omit=dev");
|
|
417
|
-
if (options.depth0) args.push("--depth=0");
|
|
418
230
|
try {
|
|
419
231
|
const execFileAsync = await getExecFileAsync2();
|
|
420
232
|
const { stdout } = await execFileAsync("npm", args, {
|
|
421
233
|
cwd: options.cwd,
|
|
422
234
|
maxBuffer: 10 * 1024 * 1024
|
|
423
235
|
});
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
return JSON.parse(stdout);
|
|
431
|
-
} catch (parseErr) {
|
|
432
|
-
if (process.env.DEBUG?.includes("gex")) {
|
|
433
|
-
console.warn("npm ls stdout parse failed:", parseErr);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
236
|
+
return normalizeOutdated(stdout);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
239
|
+
if (stdout.trim()) {
|
|
240
|
+
return normalizeOutdated(stdout);
|
|
436
241
|
}
|
|
437
|
-
|
|
438
|
-
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm ls failed";
|
|
439
|
-
throw new Error(`npm ls failed: ${msg}`);
|
|
242
|
+
throw formatNpmError(error, "npm outdated");
|
|
440
243
|
}
|
|
441
244
|
}
|
|
442
|
-
async function
|
|
245
|
+
async function npmUpdate(options) {
|
|
246
|
+
const args = ["update"];
|
|
247
|
+
if (options.global) args.push("-g");
|
|
248
|
+
if (options.packages && options.packages.length > 0) args.push(...options.packages);
|
|
443
249
|
try {
|
|
444
250
|
const execFileAsync = await getExecFileAsync2();
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
throw
|
|
251
|
+
await execFileAsync("npm", args, {
|
|
252
|
+
cwd: options.cwd,
|
|
253
|
+
maxBuffer: 10 * 1024 * 1024
|
|
254
|
+
});
|
|
255
|
+
} catch (error) {
|
|
256
|
+
throw formatNpmError(error, "npm update");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function normalizeOutdated(stdout) {
|
|
260
|
+
if (!stdout.trim()) return [];
|
|
261
|
+
let data;
|
|
262
|
+
try {
|
|
263
|
+
data = JSON.parse(stdout);
|
|
264
|
+
} catch {
|
|
265
|
+
return [];
|
|
451
266
|
}
|
|
267
|
+
if (!data) return [];
|
|
268
|
+
return Object.entries(data).map(([name, info]) => ({
|
|
269
|
+
name,
|
|
270
|
+
current: info?.current ? String(info.current) : "",
|
|
271
|
+
wanted: info?.wanted ? String(info.wanted) : "",
|
|
272
|
+
latest: info?.latest ? String(info.latest) : "",
|
|
273
|
+
type: info?.type ? String(info.type) : void 0
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
function formatNpmError(error, commandLabel) {
|
|
277
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
278
|
+
const message = stderr || error?.message || `${commandLabel} failed`;
|
|
279
|
+
return new Error(`${commandLabel} failed: ${message}`);
|
|
452
280
|
}
|
|
453
281
|
|
|
454
|
-
// src/
|
|
455
|
-
|
|
282
|
+
// src/shared/cli/loader.ts
|
|
283
|
+
var frames = ["-", "\\", "|", "/"];
|
|
284
|
+
function createLoader(message) {
|
|
285
|
+
if (!process.stdout.isTTY) {
|
|
286
|
+
console.log(`${message}...`);
|
|
287
|
+
return {
|
|
288
|
+
stop(finalMessage) {
|
|
289
|
+
if (finalMessage) console.log(finalMessage);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
let index = 0;
|
|
294
|
+
const interval = setInterval(() => {
|
|
295
|
+
const frame = frames[index % frames.length];
|
|
296
|
+
index += 1;
|
|
297
|
+
process.stdout.write(`\r${message} ${frame}`);
|
|
298
|
+
}, 80);
|
|
299
|
+
return {
|
|
300
|
+
stop(finalMessage) {
|
|
301
|
+
clearInterval(interval);
|
|
302
|
+
process.stdout.write("\r");
|
|
303
|
+
if (finalMessage) {
|
|
304
|
+
console.log(finalMessage);
|
|
305
|
+
} else {
|
|
306
|
+
process.stdout.write("\x1B[2K");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/shared/cli/outdated.ts
|
|
313
|
+
function normalizeUpdateSelection(value) {
|
|
314
|
+
if (value === void 0) {
|
|
315
|
+
return { shouldUpdate: false, updateAll: false, packages: [] };
|
|
316
|
+
}
|
|
317
|
+
if (value === true) {
|
|
318
|
+
return { shouldUpdate: true, updateAll: true, packages: [] };
|
|
319
|
+
}
|
|
320
|
+
const packages = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
321
|
+
const normalized = packages.flatMap((entry) => String(entry).split(",").map((part) => part.trim())).filter(Boolean);
|
|
322
|
+
return {
|
|
323
|
+
shouldUpdate: true,
|
|
324
|
+
updateAll: false,
|
|
325
|
+
packages: normalized
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function formatOutdatedTable(entries) {
|
|
329
|
+
const headers = ["Name", "Current", "Wanted", "Latest", "Type"];
|
|
330
|
+
const rows = entries.map((entry) => [
|
|
331
|
+
entry.name,
|
|
332
|
+
entry.current || "-",
|
|
333
|
+
entry.wanted || "-",
|
|
334
|
+
entry.latest || "-",
|
|
335
|
+
entry.type || "-"
|
|
336
|
+
]);
|
|
337
|
+
const widths = headers.map(
|
|
338
|
+
(header, index) => Math.max(header.length, ...rows.map((row) => row[index].length))
|
|
339
|
+
);
|
|
340
|
+
const formatRow = (columns) => columns.map((col, idx) => col.padEnd(widths[idx], " ")).join(" ");
|
|
341
|
+
const lines = [formatRow(headers), formatRow(widths.map((w) => "-".repeat(w)))];
|
|
342
|
+
for (const row of rows) {
|
|
343
|
+
lines.push(formatRow(row));
|
|
344
|
+
}
|
|
345
|
+
return lines.join("\n");
|
|
346
|
+
}
|
|
347
|
+
async function handleOutdatedWorkflow(opts) {
|
|
348
|
+
if (!opts.checkOutdated && !opts.selection.shouldUpdate) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
let fetchLoader;
|
|
352
|
+
if (opts.checkOutdated || opts.selection.shouldUpdate) {
|
|
353
|
+
fetchLoader = createLoader("Checking for outdated packages");
|
|
354
|
+
}
|
|
355
|
+
const outdated = await opts.fetchOutdated();
|
|
356
|
+
fetchLoader?.stop("Finished checking outdated packages.");
|
|
357
|
+
if (opts.checkOutdated) {
|
|
358
|
+
if (outdated.length === 0) {
|
|
359
|
+
console.log(`All ${opts.contextLabel} packages are up to date.`);
|
|
360
|
+
} else {
|
|
361
|
+
console.log(formatOutdatedTable(outdated));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (opts.selection.shouldUpdate && opts.updateRunner) {
|
|
365
|
+
const packagesToUpdate = opts.selection.updateAll ? outdated.map((entry) => entry.name) : opts.selection.packages;
|
|
366
|
+
if (!packagesToUpdate || packagesToUpdate.length === 0) {
|
|
367
|
+
if (opts.selection.updateAll) {
|
|
368
|
+
console.log("No outdated packages to update.");
|
|
369
|
+
} else {
|
|
370
|
+
console.log("No packages were specified for updating.");
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
const updateLoader = createLoader("Updating packages");
|
|
374
|
+
await opts.updateRunner(packagesToUpdate);
|
|
375
|
+
updateLoader.stop("Finished updating packages.");
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (opts.checkOutdated || opts.selection.shouldUpdate) {
|
|
379
|
+
if (!opts.outFile) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/shared/cli/utils.ts
|
|
387
|
+
import { existsSync } from "fs";
|
|
456
388
|
import { readFile as readFile2 } from "fs/promises";
|
|
389
|
+
import path3 from "path";
|
|
390
|
+
import { fileURLToPath } from "url";
|
|
391
|
+
function getPkgJsonPath() {
|
|
392
|
+
let startDir;
|
|
393
|
+
try {
|
|
394
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
395
|
+
startDir = path3.dirname(__filename);
|
|
396
|
+
} catch {
|
|
397
|
+
startDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
398
|
+
}
|
|
399
|
+
return findPackageJson(startDir);
|
|
400
|
+
}
|
|
401
|
+
function findPackageJson(startDir) {
|
|
402
|
+
let current = startDir;
|
|
403
|
+
const maxDepth = 6;
|
|
404
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
405
|
+
const candidate = path3.resolve(current, "package.json");
|
|
406
|
+
if (existsSync(candidate)) {
|
|
407
|
+
return candidate;
|
|
408
|
+
}
|
|
409
|
+
const parent = path3.dirname(current);
|
|
410
|
+
if (parent === current) break;
|
|
411
|
+
current = parent;
|
|
412
|
+
}
|
|
413
|
+
return path3.resolve(process.cwd(), "package.json");
|
|
414
|
+
}
|
|
415
|
+
async function getToolVersion() {
|
|
416
|
+
try {
|
|
417
|
+
const pkgPath = getPkgJsonPath();
|
|
418
|
+
const raw = await readFile2(pkgPath, "utf8");
|
|
419
|
+
const pkg = JSON.parse(raw);
|
|
420
|
+
return pkg.version || "0.0.0";
|
|
421
|
+
} catch {
|
|
422
|
+
return "0.0.0";
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
var ASCII_BANNER = String.raw`
|
|
426
|
+
________ __
|
|
427
|
+
/ _____/ ____ _____/ |_ ____ ____
|
|
428
|
+
/ \ ___ / _ \ / _ \ __\/ __ \ / \
|
|
429
|
+
\ \_\ ( <_> | <_> ) | \ ___/| | \
|
|
430
|
+
\______ /\____/ \____/|__| \___ >___| /
|
|
431
|
+
\/ \/ \/
|
|
432
|
+
GEX
|
|
433
|
+
`;
|
|
434
|
+
|
|
435
|
+
// src/runtimes/node/report.ts
|
|
436
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
437
|
+
import path5 from "path";
|
|
438
|
+
|
|
439
|
+
// src/shared/transform.ts
|
|
440
|
+
import path4 from "path";
|
|
441
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
457
442
|
function toPkgArray(obj) {
|
|
458
443
|
if (!obj) return [];
|
|
459
444
|
return Object.keys(obj).map((name) => ({ name, node: obj[name] })).filter((p) => p && p.node);
|
|
@@ -471,21 +456,30 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
471
456
|
if (opts.context === "local") {
|
|
472
457
|
let pkgMeta = null;
|
|
473
458
|
try {
|
|
474
|
-
const pkgJsonPath =
|
|
475
|
-
const raw = await
|
|
459
|
+
const pkgJsonPath = path4.join(opts.cwd || process.cwd(), "package.json");
|
|
460
|
+
const raw = await readFile3(pkgJsonPath, "utf8");
|
|
476
461
|
pkgMeta = JSON.parse(raw);
|
|
477
462
|
} catch {
|
|
478
463
|
}
|
|
479
464
|
if (pkgMeta?.name) report.project_name = pkgMeta.name;
|
|
480
465
|
if (pkgMeta?.version) report.project_version = pkgMeta.version;
|
|
481
466
|
const depsObj = tree?.dependencies;
|
|
482
|
-
const
|
|
483
|
-
const
|
|
484
|
-
|
|
467
|
+
const devDepsObj = tree?.devDependencies;
|
|
468
|
+
const prodItems = toPkgArray(depsObj);
|
|
469
|
+
const treeDevItems = toPkgArray(devDepsObj);
|
|
470
|
+
if (treeDevItems.length > 0) {
|
|
471
|
+
for (const { name, node } of treeDevItems) {
|
|
472
|
+
const version = node && node.version || "";
|
|
473
|
+
const resolvedPath = node && node.path || path4.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
474
|
+
report.local_dev_dependencies.push({ name, version, resolved_path: resolvedPath });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const devKeys = treeDevItems.length > 0 ? new Set(treeDevItems.map((entry) => entry.name)) : new Set(Object.keys(pkgMeta?.devDependencies || {}));
|
|
478
|
+
for (const { name, node } of prodItems) {
|
|
485
479
|
const version = node && node.version || "";
|
|
486
|
-
const resolvedPath = node && node.path ||
|
|
480
|
+
const resolvedPath = node && node.path || path4.join(opts.cwd || process.cwd(), "node_modules", name);
|
|
487
481
|
const pkg = { name, version, resolved_path: resolvedPath };
|
|
488
|
-
if (devKeys.has(name)) {
|
|
482
|
+
if (!treeDevItems.length && devKeys.has(name)) {
|
|
489
483
|
report.local_dev_dependencies.push(pkg);
|
|
490
484
|
} else {
|
|
491
485
|
report.local_dependencies.push(pkg);
|
|
@@ -498,7 +492,7 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
498
492
|
const items = toPkgArray(depsObj);
|
|
499
493
|
for (const { name, node } of items) {
|
|
500
494
|
const version = node && node.version || "";
|
|
501
|
-
const resolvedPath = node && node.path ||
|
|
495
|
+
const resolvedPath = node && node.path || path4.join(opts.globalRoot || "", name);
|
|
502
496
|
const pkg = { name, version, resolved_path: resolvedPath };
|
|
503
497
|
report.global_packages.push(pkg);
|
|
504
498
|
}
|
|
@@ -510,41 +504,54 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
510
504
|
return report;
|
|
511
505
|
}
|
|
512
506
|
|
|
513
|
-
// src/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
507
|
+
// src/runtimes/node/package-manager.ts
|
|
508
|
+
async function getExecFileAsync3() {
|
|
509
|
+
const { execFile } = await import("child_process");
|
|
510
|
+
const { promisify: promisify2 } = await import("util");
|
|
511
|
+
return promisify2(execFile);
|
|
512
|
+
}
|
|
513
|
+
async function npmLs(options = {}) {
|
|
514
|
+
const args = ["ls", "--json"];
|
|
515
|
+
if (options.global) args.push("--global");
|
|
516
|
+
if (options.omitDev) args.push("--omit=dev");
|
|
517
|
+
if (options.depth0) args.push("--depth=0");
|
|
518
518
|
try {
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
519
|
+
const execFileAsync = await getExecFileAsync3();
|
|
520
|
+
const { stdout } = await execFileAsync("npm", args, {
|
|
521
|
+
cwd: options.cwd,
|
|
522
|
+
maxBuffer: 10 * 1024 * 1024
|
|
523
|
+
});
|
|
524
|
+
if (stdout && stdout.trim()) return JSON.parse(stdout);
|
|
525
|
+
return {};
|
|
526
|
+
} catch (err) {
|
|
527
|
+
const stdout = err?.stdout;
|
|
528
|
+
if (typeof stdout === "string" && stdout.trim()) {
|
|
529
|
+
try {
|
|
530
|
+
return JSON.parse(stdout);
|
|
531
|
+
} catch (parseErr) {
|
|
532
|
+
if (process.env.DEBUG?.includes("gex")) {
|
|
533
|
+
console.warn("npm ls stdout parse failed:", parseErr);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const stderr = err?.stderr;
|
|
538
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm ls failed";
|
|
539
|
+
throw new Error(`npm ls failed: ${msg}`);
|
|
525
540
|
}
|
|
526
541
|
}
|
|
527
|
-
async function
|
|
542
|
+
async function npmRootGlobal() {
|
|
528
543
|
try {
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
544
|
+
const execFileAsync = await getExecFileAsync3();
|
|
545
|
+
const { stdout } = await execFileAsync("npm", ["root", "-g"]);
|
|
546
|
+
return stdout.trim();
|
|
547
|
+
} catch (err) {
|
|
548
|
+
const stderr = err?.stderr;
|
|
549
|
+
const msg = typeof stderr === "string" && stderr.trim() || err?.message || "npm root -g failed";
|
|
550
|
+
throw new Error(`npm root -g failed: ${msg}`);
|
|
535
551
|
}
|
|
536
552
|
}
|
|
537
|
-
var ASCII_BANNER = String.raw`
|
|
538
|
-
________ __
|
|
539
|
-
/ _____/ ____ _____/ |_ ____ ____
|
|
540
|
-
/ \ ___ / _ \ / _ \ __\/ __ \ / \
|
|
541
|
-
\ \_\ ( <_> | <_> ) | \ ___/| | \
|
|
542
|
-
\______ /\____/ \____/|__| \___ >___| /
|
|
543
|
-
\/ \/ \/
|
|
544
|
-
GEX
|
|
545
|
-
`;
|
|
546
553
|
|
|
547
|
-
// src/
|
|
554
|
+
// src/runtimes/node/report.ts
|
|
548
555
|
async function produceReport(ctx, options) {
|
|
549
556
|
const toolVersion = await getToolVersion();
|
|
550
557
|
const depth0 = !options.fullTree;
|
|
@@ -582,18 +589,17 @@ async function produceReport(ctx, options) {
|
|
|
582
589
|
return { report, markdownExtras };
|
|
583
590
|
}
|
|
584
591
|
|
|
585
|
-
// src/
|
|
592
|
+
// src/runtimes/node/commands.ts
|
|
586
593
|
function addCommonOptions(cmd, { allowOmitDev }) {
|
|
587
594
|
cmd.option(
|
|
588
595
|
"-f, --output-format <format>",
|
|
589
|
-
"Output format:
|
|
590
|
-
(val) =>
|
|
591
|
-
if (val === "md") return "md";
|
|
592
|
-
if (val === "html") return "html";
|
|
593
|
-
return "json";
|
|
594
|
-
},
|
|
596
|
+
"Output format: md or json",
|
|
597
|
+
(val) => val === "md" ? "md" : "json",
|
|
595
598
|
"json"
|
|
596
|
-
).option("-o, --out-file <path>", "Write report to file").option("--full-tree", "Include full npm ls tree (omit depth=0 default)", false)
|
|
599
|
+
).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(
|
|
600
|
+
"-u, --update-outdated [packages...]",
|
|
601
|
+
"Update outdated packages (omit package names to update every package)"
|
|
602
|
+
);
|
|
597
603
|
if (allowOmitDev) {
|
|
598
604
|
cmd.option("--omit-dev", "Exclude devDependencies (local only)", false);
|
|
599
605
|
}
|
|
@@ -607,6 +613,19 @@ function createLocalCommand(program) {
|
|
|
607
613
|
const outFile = opts.outFile;
|
|
608
614
|
const fullTree = Boolean(opts.fullTree);
|
|
609
615
|
const omitDev = Boolean(opts.omitDev);
|
|
616
|
+
const cwd = process.cwd();
|
|
617
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
618
|
+
const proceed = await handleOutdatedWorkflow({
|
|
619
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
620
|
+
selection,
|
|
621
|
+
contextLabel: "local",
|
|
622
|
+
outFile,
|
|
623
|
+
fetchOutdated: () => npmOutdated({ cwd }),
|
|
624
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
625
|
+
await npmUpdate({ cwd, packages });
|
|
626
|
+
} : void 0
|
|
627
|
+
});
|
|
628
|
+
if (!proceed) return;
|
|
610
629
|
const finalOutFile = outFile;
|
|
611
630
|
const { report, markdownExtras } = await produceReport("local", {
|
|
612
631
|
outputFormat,
|
|
@@ -625,6 +644,19 @@ function createGlobalCommand(program) {
|
|
|
625
644
|
const outputFormat = opts.outputFormat ?? "json";
|
|
626
645
|
const outFile = opts.outFile;
|
|
627
646
|
const fullTree = Boolean(opts.fullTree);
|
|
647
|
+
const cwd = process.cwd();
|
|
648
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
649
|
+
const proceed = await handleOutdatedWorkflow({
|
|
650
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
651
|
+
selection,
|
|
652
|
+
contextLabel: "global",
|
|
653
|
+
outFile,
|
|
654
|
+
fetchOutdated: () => npmOutdated({ cwd, global: true }),
|
|
655
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
656
|
+
await npmUpdate({ cwd, global: true, packages });
|
|
657
|
+
} : void 0
|
|
658
|
+
});
|
|
659
|
+
if (!proceed) return;
|
|
628
660
|
const finalOutFile = outFile;
|
|
629
661
|
const { report, markdownExtras } = await produceReport("global", {
|
|
630
662
|
outputFormat,
|
|
@@ -650,7 +682,7 @@ function createReadCommand(program) {
|
|
|
650
682
|
printFromReport(parsed);
|
|
651
683
|
}
|
|
652
684
|
if (doInstall) {
|
|
653
|
-
await installFromReport(parsed, process.cwd());
|
|
685
|
+
await installFromReport(parsed, { cwd: process.cwd(), packageManager: "npm" });
|
|
654
686
|
}
|
|
655
687
|
} catch (err) {
|
|
656
688
|
const isMd = isMarkdownReportFile(reportPath);
|
|
@@ -672,7 +704,7 @@ ${ASCII_BANNER}`);
|
|
|
672
704
|
return program;
|
|
673
705
|
}
|
|
674
706
|
|
|
675
|
-
// src/cli.ts
|
|
707
|
+
// src/runtimes/node/cli.ts
|
|
676
708
|
async function run(argv = process.argv) {
|
|
677
709
|
const program = await createProgram();
|
|
678
710
|
await program.parseAsync(argv);
|
|
@@ -696,7 +728,31 @@ if (isMainModule) {
|
|
|
696
728
|
process.exitCode = 1;
|
|
697
729
|
});
|
|
698
730
|
}
|
|
731
|
+
|
|
732
|
+
// src/cli.ts
|
|
733
|
+
async function run2(argv = process.argv) {
|
|
734
|
+
return run(argv);
|
|
735
|
+
}
|
|
736
|
+
var isMainModule2 = (() => {
|
|
737
|
+
try {
|
|
738
|
+
if (typeof __require !== "undefined" && typeof module !== "undefined") {
|
|
739
|
+
return __require.main === module;
|
|
740
|
+
}
|
|
741
|
+
if (typeof import.meta !== "undefined") {
|
|
742
|
+
return import.meta.url === `file://${process.argv[1]}`;
|
|
743
|
+
}
|
|
744
|
+
return false;
|
|
745
|
+
} catch {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
})();
|
|
749
|
+
if (isMainModule2) {
|
|
750
|
+
run2().catch((error) => {
|
|
751
|
+
console.error("CLI error:", error);
|
|
752
|
+
process.exitCode = 1;
|
|
753
|
+
});
|
|
754
|
+
}
|
|
699
755
|
export {
|
|
700
|
-
run
|
|
756
|
+
run2 as run
|
|
701
757
|
};
|
|
702
758
|
//# sourceMappingURL=cli.mjs.map
|