@yabasha/gex 1.3.4 → 1.3.5
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 +49 -26
- package/dist/cli.cjs +516 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +517 -7
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,15 +37,38 @@ node dist/cli.cjs --help
|
|
|
37
37
|
|
|
38
38
|
## Usage
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
### Top-level entry: `gex` (interactive selector)
|
|
41
|
+
|
|
42
|
+
Starting with v1.3.5, the primary `gex` binary is an interactive launcher that lets you choose which runtime to use:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
gex
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
You will be prompted with:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
Select a runtime to use:
|
|
52
|
+
1) gex-bun (Bun package manager)
|
|
53
|
+
2) gex-npm (npm / Node.js package manager)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Enter `1` for the Bun-based CLI (`gex-bun`) or `2` for the Node/npm-based CLI (`gex-npm`). Any additional arguments you pass after `gex` are forwarded to the selected runtime, e.g.:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gex local --check-outdated # choose Bun or npm, then run `local` on that runtime
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Direct runtimes: `gex-npm` (Node) and `gex-bun` (Bun)
|
|
63
|
+
|
|
64
|
+
You can still call each runtime directly without the interactive selector:
|
|
41
65
|
|
|
42
66
|
```bash
|
|
43
|
-
gex [options]
|
|
44
|
-
gex
|
|
45
|
-
gex global [options]
|
|
67
|
+
gex-npm [command] [options] # Node.js / npm runtime (formerly `gex`)
|
|
68
|
+
gex-bun [command] [options] # Bun runtime
|
|
46
69
|
```
|
|
47
70
|
|
|
48
|
-
Common options:
|
|
71
|
+
Common command options:
|
|
49
72
|
|
|
50
73
|
- -f, --output-format <md|json> (default: json)
|
|
51
74
|
- -o, --out-file <path>
|
|
@@ -54,45 +77,45 @@ Common options:
|
|
|
54
77
|
- -c, --check-outdated Print a table of outdated packages (shows a lightweight spinner while checking; skips console report output unless `-o` is set so you can write the report to file instead)
|
|
55
78
|
- -u, --update-outdated [pkg1 pkg2 ...] Update outdated packages (omit names to update everything). Node CLI shells out to `npm update`; Bun CLI mirrors `bun update` for locals and reinstalls globals via `bun add -g pkg@latest`.
|
|
56
79
|
|
|
57
|
-
Examples:
|
|
80
|
+
Examples (Node/npm runtime via `gex-npm`):
|
|
58
81
|
|
|
59
82
|
```bash
|
|
60
83
|
# Local (default): JSON output to console
|
|
61
|
-
gex
|
|
62
|
-
gex -o report.json
|
|
63
|
-
gex -f md
|
|
64
|
-
gex -f md -o report.md # writes markdown to file
|
|
84
|
+
gex-npm # prints JSON to console (same as: gex-npm local)
|
|
85
|
+
gex-npm -o report.json # writes JSON to file
|
|
86
|
+
gex-npm -f md # prints markdown to console
|
|
87
|
+
gex-npm -f md -o report.md # writes markdown to file
|
|
65
88
|
|
|
66
89
|
# Local: exclude devDependencies
|
|
67
|
-
gex local --omit-dev
|
|
68
|
-
gex local --omit-dev -o deps.json # writes JSON to file
|
|
90
|
+
gex-npm local --omit-dev # prints JSON to console
|
|
91
|
+
gex-npm local --omit-dev -o deps.json # writes JSON to file
|
|
69
92
|
|
|
70
93
|
# Global packages
|
|
71
|
-
gex global
|
|
72
|
-
gex global -o global.json
|
|
73
|
-
gex global -f md
|
|
94
|
+
gex-npm global # prints JSON to console
|
|
95
|
+
gex-npm global -o global.json # writes JSON to file
|
|
96
|
+
gex-npm global -f md # prints markdown to console
|
|
74
97
|
|
|
75
98
|
# Read a previous report (JSON or Markdown)
|
|
76
99
|
# Default prints names@versions; add -i to install
|
|
77
100
|
# Positional path or -r/--report are accepted
|
|
78
101
|
# JSON
|
|
79
|
-
gex read
|
|
80
|
-
gex read -r path/to/report.json -i
|
|
102
|
+
gex-npm read
|
|
103
|
+
gex-npm read -r path/to/report.json -i
|
|
81
104
|
# Markdown
|
|
82
|
-
gex read global.md
|
|
83
|
-
gex read global.md -i
|
|
105
|
+
gex-npm read global.md
|
|
106
|
+
gex-npm read global.md -i
|
|
84
107
|
|
|
85
108
|
# Shell redirection (alternative to -o flag)
|
|
86
|
-
gex > report.json
|
|
87
|
-
gex global | jq '.global_packages' # pipe output to jq for processing
|
|
109
|
+
gex-npm > report.json # redirect JSON output to file
|
|
110
|
+
gex-npm global | jq '.global_packages' # pipe output to jq for processing
|
|
88
111
|
|
|
89
112
|
# Check outdated packages / update them (Node runtime)
|
|
90
|
-
gex local --check-outdated # show outdated local deps as a table
|
|
91
|
-
gex global -c # short flag works too
|
|
92
|
-
gex local --update-outdated # update every outdated local dependency
|
|
93
|
-
gex local -u axios react # update specific packages
|
|
113
|
+
gex-npm local --check-outdated # show outdated local deps as a table
|
|
114
|
+
gex-npm global -c # short flag works too
|
|
115
|
+
gex-npm local --update-outdated # update every outdated local dependency
|
|
116
|
+
gex-npm local -u axios react # update specific packages
|
|
94
117
|
|
|
95
|
-
# Bun runtime uses the same flags
|
|
118
|
+
# Bun runtime uses the same flags with Bun semantics
|
|
96
119
|
gex-bun local --check-outdated
|
|
97
120
|
gex-bun global --update-outdated # updates global Bun installs via `bun update`/`bun add -g`
|
|
98
121
|
```
|
package/dist/cli.cjs
CHANGED
|
@@ -31,9 +31,10 @@ 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
|
+
var import_node_readline = __toESM(require("readline"), 1);
|
|
37
38
|
|
|
38
39
|
// src/runtimes/node/commands.ts
|
|
39
40
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
@@ -58,8 +59,8 @@ function formatSpec(pkg) {
|
|
|
58
59
|
}
|
|
59
60
|
async function getExecFileAsync() {
|
|
60
61
|
const { execFile } = await import("child_process");
|
|
61
|
-
const { promisify:
|
|
62
|
-
return
|
|
62
|
+
const { promisify: promisify3 } = await import("util");
|
|
63
|
+
return promisify3(execFile);
|
|
63
64
|
}
|
|
64
65
|
async function installFromReport(report, options) {
|
|
65
66
|
const opts = typeof options === "string" ? { cwd: options } : options;
|
|
@@ -307,6 +308,20 @@ function formatNpmError(error, commandLabel) {
|
|
|
307
308
|
const message = stderr || error?.message || `${commandLabel} failed`;
|
|
308
309
|
return new Error(`${commandLabel} failed: ${message}`);
|
|
309
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
|
+
}
|
|
310
325
|
|
|
311
326
|
// src/shared/cli/loader.ts
|
|
312
327
|
var frames = ["-", "\\", "|", "/"];
|
|
@@ -402,6 +417,26 @@ async function handleOutdatedWorkflow(opts) {
|
|
|
402
417
|
const proceed = !((opts.checkOutdated || opts.selection.shouldUpdate) && !opts.outFile);
|
|
403
418
|
return { proceed, outdated };
|
|
404
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
|
+
}
|
|
405
440
|
|
|
406
441
|
// src/shared/cli/utils.ts
|
|
407
442
|
var import_node_fs = require("fs");
|
|
@@ -528,8 +563,8 @@ async function buildReportFromNpmTree(tree, opts) {
|
|
|
528
563
|
// src/runtimes/node/package-manager.ts
|
|
529
564
|
async function getExecFileAsync3() {
|
|
530
565
|
const { execFile } = await import("child_process");
|
|
531
|
-
const { promisify:
|
|
532
|
-
return
|
|
566
|
+
const { promisify: promisify3 } = await import("util");
|
|
567
|
+
return promisify3(execFile);
|
|
533
568
|
}
|
|
534
569
|
async function npmLs(options = {}) {
|
|
535
570
|
const args = ["ls", "--json"];
|
|
@@ -759,10 +794,424 @@ if (isMainModule) {
|
|
|
759
794
|
});
|
|
760
795
|
}
|
|
761
796
|
|
|
762
|
-
// src/
|
|
797
|
+
// src/runtimes/bun/commands.ts
|
|
798
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
799
|
+
var import_promises7 = require("fs/promises");
|
|
800
|
+
var import_commander2 = require("commander");
|
|
801
|
+
|
|
802
|
+
// src/runtimes/bun/report.ts
|
|
803
|
+
var import_promises6 = require("fs/promises");
|
|
804
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
805
|
+
|
|
806
|
+
// src/runtimes/bun/package-manager.ts
|
|
807
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
808
|
+
var import_node_fs2 = require("fs");
|
|
809
|
+
var import_promises5 = require("fs/promises");
|
|
810
|
+
var import_node_util2 = require("util");
|
|
811
|
+
var IGNORED_ENTRIES = /* @__PURE__ */ new Set([".bin"]);
|
|
812
|
+
async function bunPmLs(options = {}) {
|
|
813
|
+
if (options.global) {
|
|
814
|
+
const root = await bunPmRootGlobal();
|
|
815
|
+
const manifest2 = await readJson(import_node_path7.default.join(import_node_path7.default.dirname(root), "package.json"));
|
|
816
|
+
const selections2 = buildSelections(manifest2, { includeDev: false });
|
|
817
|
+
const dependencies2 = selections2.prod.size > 0 ? await collectPackagesForNames(root, mapSelections(selections2.prod)) : await collectPackagesFromNodeModules(root);
|
|
818
|
+
return { dependencies: dependencies2, node_modules_path: root };
|
|
819
|
+
}
|
|
820
|
+
const cwd = options.cwd || process.cwd();
|
|
821
|
+
const nodeModulesPath = await bunPmRootLocal(cwd);
|
|
822
|
+
const manifest = await readJson(import_node_path7.default.join(cwd, "package.json"));
|
|
823
|
+
const includeDev = !options.omitDev;
|
|
824
|
+
const selections = buildSelections(manifest, { includeDev });
|
|
825
|
+
const dependencies = selections.prod.size > 0 ? await collectPackagesForNames(nodeModulesPath, mapSelections(selections.prod)) : await collectPackagesFromNodeModules(nodeModulesPath);
|
|
826
|
+
let devDependencies;
|
|
827
|
+
if (includeDev) {
|
|
828
|
+
if (selections.dev.size > 0) {
|
|
829
|
+
devDependencies = await collectPackagesForNames(
|
|
830
|
+
nodeModulesPath,
|
|
831
|
+
mapSelections(selections.dev)
|
|
832
|
+
);
|
|
833
|
+
} else {
|
|
834
|
+
devDependencies = {};
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return { dependencies, devDependencies, node_modules_path: nodeModulesPath };
|
|
838
|
+
}
|
|
839
|
+
async function bunPmRootGlobal() {
|
|
840
|
+
if (process.env.GEX_BUN_GLOBAL_ROOT) {
|
|
841
|
+
return process.env.GEX_BUN_GLOBAL_ROOT;
|
|
842
|
+
}
|
|
843
|
+
const candidates = getGlobalRootCandidates();
|
|
844
|
+
for (const candidate of candidates) {
|
|
845
|
+
if (candidate && await pathExists(candidate)) {
|
|
846
|
+
return candidate;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return candidates.find((c) => Boolean(c)) || import_node_path7.default.join(process.env.HOME || process.cwd(), ".bun", "install", "global", "node_modules");
|
|
850
|
+
}
|
|
851
|
+
async function bunPmRootLocal(cwd = process.cwd()) {
|
|
852
|
+
if (process.env.GEX_BUN_LOCAL_ROOT) {
|
|
853
|
+
return process.env.GEX_BUN_LOCAL_ROOT;
|
|
854
|
+
}
|
|
855
|
+
return import_node_path7.default.join(cwd, "node_modules");
|
|
856
|
+
}
|
|
857
|
+
async function bunUpdate(options) {
|
|
858
|
+
const execFileAsync = await getExecFileAsync4();
|
|
859
|
+
const packages = options.packages && options.packages.length > 0 ? options.packages : [];
|
|
860
|
+
if (options.global) {
|
|
861
|
+
const targets = packages.length > 0 ? packages : [];
|
|
862
|
+
const list = targets.length > 0 ? targets : [];
|
|
863
|
+
const cmdPackages = list.map((name) => `${name}@latest`);
|
|
864
|
+
try {
|
|
865
|
+
await execFileAsync("bun", ["add", "-g", ...cmdPackages], {
|
|
866
|
+
cwd: options.cwd,
|
|
867
|
+
maxBuffer: 10 * 1024 * 1024
|
|
868
|
+
});
|
|
869
|
+
} catch (error) {
|
|
870
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
871
|
+
throw new Error(`bun add -g failed: ${stderr || error?.message || "unknown error"}`);
|
|
872
|
+
}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const args = ["update"];
|
|
876
|
+
if (packages.length > 0) args.push(...packages);
|
|
877
|
+
try {
|
|
878
|
+
await execFileAsync("bun", args, {
|
|
879
|
+
cwd: options.cwd,
|
|
880
|
+
maxBuffer: 10 * 1024 * 1024
|
|
881
|
+
});
|
|
882
|
+
} catch (error) {
|
|
883
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
884
|
+
throw new Error(`bun update failed: ${stderr || error?.message || "unknown error"}`);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async function collectPackagesForNames(nodeModulesPath, packages) {
|
|
888
|
+
const result = {};
|
|
889
|
+
await Promise.all(
|
|
890
|
+
packages.map(async ({ name, declared }) => {
|
|
891
|
+
const pkgDir = packageDir(nodeModulesPath, name);
|
|
892
|
+
const manifest = await readJson(import_node_path7.default.join(pkgDir, "package.json"));
|
|
893
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : name;
|
|
894
|
+
const version = typeof manifest?.version === "string" ? manifest.version : declared || "";
|
|
895
|
+
result[pkgName] = { version, path: pkgDir };
|
|
896
|
+
})
|
|
897
|
+
);
|
|
898
|
+
return result;
|
|
899
|
+
}
|
|
900
|
+
async function collectPackagesFromNodeModules(root) {
|
|
901
|
+
const result = {};
|
|
902
|
+
const entries = await safeReadDir(root);
|
|
903
|
+
for (const entry of entries) {
|
|
904
|
+
if (!entry || !entry.name || entry.name.startsWith(".") || IGNORED_ENTRIES.has(entry.name)) {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
const entryPath = import_node_path7.default.join(root, entry.name);
|
|
908
|
+
if (!await isDir(entry, entryPath)) continue;
|
|
909
|
+
if (entry.name.startsWith("@")) {
|
|
910
|
+
const scopedEntries = await safeReadDir(entryPath);
|
|
911
|
+
for (const scopedEntry of scopedEntries) {
|
|
912
|
+
if (!scopedEntry || !scopedEntry.name || scopedEntry.name.startsWith(".")) continue;
|
|
913
|
+
const scopedPath = import_node_path7.default.join(entryPath, scopedEntry.name);
|
|
914
|
+
if (!await isDir(scopedEntry, scopedPath)) continue;
|
|
915
|
+
const manifest = await readJson(import_node_path7.default.join(scopedPath, "package.json"));
|
|
916
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : `${entry.name}/${scopedEntry.name}`;
|
|
917
|
+
const version = typeof manifest?.version === "string" ? manifest.version : "";
|
|
918
|
+
result[pkgName] = { version, path: scopedPath };
|
|
919
|
+
}
|
|
920
|
+
} else {
|
|
921
|
+
const manifest = await readJson(import_node_path7.default.join(entryPath, "package.json"));
|
|
922
|
+
const pkgName = typeof manifest?.name === "string" ? manifest.name : entry.name;
|
|
923
|
+
const version = typeof manifest?.version === "string" ? manifest.version : "";
|
|
924
|
+
result[pkgName] = { version, path: entryPath };
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return result;
|
|
928
|
+
}
|
|
929
|
+
async function readJson(file) {
|
|
930
|
+
try {
|
|
931
|
+
const raw = await (0, import_promises5.readFile)(file, "utf8");
|
|
932
|
+
return JSON.parse(raw);
|
|
933
|
+
} catch {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function packageDir(root, packageName) {
|
|
938
|
+
const segments = packageName.startsWith("@") ? packageName.split("/") : [packageName];
|
|
939
|
+
return import_node_path7.default.join(root, ...segments);
|
|
940
|
+
}
|
|
941
|
+
function buildSelections(manifest, { includeDev }) {
|
|
942
|
+
const prod = /* @__PURE__ */ new Map();
|
|
943
|
+
const dev = /* @__PURE__ */ new Map();
|
|
944
|
+
const addAll = (target, record) => {
|
|
945
|
+
if (!record) return;
|
|
946
|
+
for (const [name, range] of Object.entries(record)) {
|
|
947
|
+
if (!target.has(name)) {
|
|
948
|
+
target.set(name, range);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
addAll(prod, manifest?.dependencies);
|
|
953
|
+
addAll(prod, manifest?.optionalDependencies);
|
|
954
|
+
if (includeDev) addAll(dev, manifest?.devDependencies);
|
|
955
|
+
return { prod, dev };
|
|
956
|
+
}
|
|
957
|
+
function mapSelections(map) {
|
|
958
|
+
return Array.from(map.entries()).map(([name, declared]) => ({ name, declared }));
|
|
959
|
+
}
|
|
960
|
+
async function safeReadDir(dir) {
|
|
961
|
+
try {
|
|
962
|
+
return await (0, import_promises5.readdir)(dir, { withFileTypes: true });
|
|
963
|
+
} catch {
|
|
964
|
+
return [];
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
async function isDir(entry, fullPath) {
|
|
968
|
+
if (entry.isDirectory()) return true;
|
|
969
|
+
if (entry.isSymbolicLink()) {
|
|
970
|
+
try {
|
|
971
|
+
const stats = await (0, import_promises5.stat)(fullPath);
|
|
972
|
+
return stats.isDirectory();
|
|
973
|
+
} catch {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
async function pathExists(target) {
|
|
980
|
+
try {
|
|
981
|
+
await (0, import_promises5.access)(target, import_node_fs2.constants.R_OK);
|
|
982
|
+
return true;
|
|
983
|
+
} catch {
|
|
984
|
+
return false;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
function getGlobalRootCandidates() {
|
|
988
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
989
|
+
const bunInstall = process.env.BUN_INSTALL || (process.env.HOME ? import_node_path7.default.join(process.env.HOME, ".bun") : void 0);
|
|
990
|
+
const maybeAdd = (value) => {
|
|
991
|
+
if (value) candidates.add(value);
|
|
992
|
+
};
|
|
993
|
+
if (bunInstall) {
|
|
994
|
+
maybeAdd(import_node_path7.default.join(bunInstall, "install", "global", "node_modules"));
|
|
995
|
+
maybeAdd(import_node_path7.default.join(bunInstall, "global", "node_modules"));
|
|
996
|
+
}
|
|
997
|
+
if (process.env.XDG_DATA_HOME) {
|
|
998
|
+
maybeAdd(import_node_path7.default.join(process.env.XDG_DATA_HOME, "bun", "install", "global", "node_modules"));
|
|
999
|
+
}
|
|
1000
|
+
maybeAdd("/usr/local/share/bun/global/node_modules");
|
|
1001
|
+
maybeAdd("/opt/homebrew/var/bun/install/global/node_modules");
|
|
1002
|
+
return Array.from(candidates);
|
|
1003
|
+
}
|
|
1004
|
+
async function getExecFileAsync4() {
|
|
1005
|
+
const { execFile } = await import("child_process");
|
|
1006
|
+
return (0, import_node_util2.promisify)(execFile);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/runtimes/bun/report.ts
|
|
1010
|
+
async function produceReport2(ctx, options) {
|
|
1011
|
+
const toolVersion = await getToolVersion();
|
|
1012
|
+
const cwd = options.cwd || process.cwd();
|
|
1013
|
+
const tree = await bunPmLs({
|
|
1014
|
+
global: ctx === "global",
|
|
1015
|
+
omitDev: ctx === "local" ? Boolean(options.omitDev) : false,
|
|
1016
|
+
cwd
|
|
1017
|
+
});
|
|
1018
|
+
const nodeModulesPath = tree?.node_modules_path;
|
|
1019
|
+
let project_description;
|
|
1020
|
+
let project_homepage;
|
|
1021
|
+
let project_bugs;
|
|
1022
|
+
if (ctx === "local") {
|
|
1023
|
+
try {
|
|
1024
|
+
const pkgRaw = await (0, import_promises6.readFile)(import_node_path8.default.join(cwd, "package.json"), "utf8");
|
|
1025
|
+
const pkg = JSON.parse(pkgRaw);
|
|
1026
|
+
project_description = pkg.description;
|
|
1027
|
+
project_homepage = pkg.homepage;
|
|
1028
|
+
if (typeof pkg.bugs === "string") project_bugs = pkg.bugs;
|
|
1029
|
+
else if (pkg.bugs && typeof pkg.bugs.url === "string") project_bugs = pkg.bugs.url;
|
|
1030
|
+
} catch {
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const resolvedRoot = nodeModulesPath ? nodeModulesPath : ctx === "global" ? await bunPmRootGlobal().catch(() => void 0) : await bunPmRootLocal(cwd).catch(() => `${cwd}/node_modules`);
|
|
1034
|
+
const report = await buildReportFromNpmTree(tree, {
|
|
1035
|
+
context: ctx,
|
|
1036
|
+
includeTree: Boolean(options.fullTree),
|
|
1037
|
+
omitDev: Boolean(options.omitDev),
|
|
1038
|
+
cwd,
|
|
1039
|
+
toolVersion,
|
|
1040
|
+
globalRoot: resolvedRoot
|
|
1041
|
+
});
|
|
1042
|
+
const markdownExtras = { project_description, project_homepage, project_bugs };
|
|
1043
|
+
return { report, markdownExtras };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/runtimes/bun/commands.ts
|
|
1047
|
+
function addCommonOptions2(cmd, { allowOmitDev }) {
|
|
1048
|
+
cmd.option(
|
|
1049
|
+
"-f, --output-format <format>",
|
|
1050
|
+
"Output format: md or json",
|
|
1051
|
+
(val) => val === "md" ? "md" : "json",
|
|
1052
|
+
"json"
|
|
1053
|
+
).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(
|
|
1054
|
+
"-u, --update-outdated [packages...]",
|
|
1055
|
+
"Update outdated packages (omit package names to update every package)"
|
|
1056
|
+
);
|
|
1057
|
+
if (allowOmitDev) {
|
|
1058
|
+
cmd.option("--omit-dev", "Exclude devDependencies (local only)", false);
|
|
1059
|
+
}
|
|
1060
|
+
return cmd;
|
|
1061
|
+
}
|
|
1062
|
+
function createLocalCommand2(program) {
|
|
1063
|
+
const localCmd = program.command("local", { isDefault: true }).description("Generate a report for the current Bun project's dependencies");
|
|
1064
|
+
addCommonOptions2(localCmd, { allowOmitDev: true });
|
|
1065
|
+
localCmd.action(async (opts) => {
|
|
1066
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
1067
|
+
const outFile = opts.outFile;
|
|
1068
|
+
const fullTree = Boolean(opts.fullTree);
|
|
1069
|
+
const omitDev = Boolean(opts.omitDev);
|
|
1070
|
+
const cwd = process.cwd();
|
|
1071
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
1072
|
+
const result = await handleOutdatedWorkflow({
|
|
1073
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
1074
|
+
selection,
|
|
1075
|
+
contextLabel: "local",
|
|
1076
|
+
outFile,
|
|
1077
|
+
fetchOutdated: async () => {
|
|
1078
|
+
const tree = await bunPmLs({ cwd, omitDev });
|
|
1079
|
+
const manifest = await readPackageManifest(cwd);
|
|
1080
|
+
const declared = {
|
|
1081
|
+
...manifest?.dependencies || {},
|
|
1082
|
+
...manifest?.optionalDependencies || {},
|
|
1083
|
+
...manifest?.devDependencies || {}
|
|
1084
|
+
};
|
|
1085
|
+
const packages = Object.entries(tree.dependencies).map(([name, node]) => ({
|
|
1086
|
+
name,
|
|
1087
|
+
current: node.version,
|
|
1088
|
+
declared: declared[name],
|
|
1089
|
+
type: "prod"
|
|
1090
|
+
}));
|
|
1091
|
+
if (tree.devDependencies) {
|
|
1092
|
+
for (const [name, node] of Object.entries(tree.devDependencies)) {
|
|
1093
|
+
packages.push({
|
|
1094
|
+
name,
|
|
1095
|
+
current: node.version,
|
|
1096
|
+
declared: declared[name],
|
|
1097
|
+
type: "dev"
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return resolveOutdatedWithNpmView(packages);
|
|
1102
|
+
},
|
|
1103
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
1104
|
+
await bunUpdate({ cwd, packages });
|
|
1105
|
+
} : void 0
|
|
1106
|
+
});
|
|
1107
|
+
if (opts.checkOutdated) {
|
|
1108
|
+
if (result.outdated.length === 0) console.log("All local packages are up to date.");
|
|
1109
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
1110
|
+
}
|
|
1111
|
+
if (!result.proceed) return;
|
|
1112
|
+
const finalOutFile = outFile;
|
|
1113
|
+
const { report, markdownExtras } = await produceReport2("local", {
|
|
1114
|
+
outputFormat,
|
|
1115
|
+
outFile: finalOutFile,
|
|
1116
|
+
fullTree,
|
|
1117
|
+
omitDev
|
|
1118
|
+
});
|
|
1119
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
1120
|
+
});
|
|
1121
|
+
return localCmd;
|
|
1122
|
+
}
|
|
1123
|
+
function createGlobalCommand2(program) {
|
|
1124
|
+
const globalCmd = program.command("global").description("Generate a report of globally installed Bun packages");
|
|
1125
|
+
addCommonOptions2(globalCmd, { allowOmitDev: false });
|
|
1126
|
+
globalCmd.action(async (opts) => {
|
|
1127
|
+
const outputFormat = opts.outputFormat ?? "json";
|
|
1128
|
+
const outFile = opts.outFile;
|
|
1129
|
+
const fullTree = Boolean(opts.fullTree);
|
|
1130
|
+
const cwd = process.cwd();
|
|
1131
|
+
const selection = normalizeUpdateSelection(opts.updateOutdated);
|
|
1132
|
+
const result = await handleOutdatedWorkflow({
|
|
1133
|
+
checkOutdated: Boolean(opts.checkOutdated),
|
|
1134
|
+
selection,
|
|
1135
|
+
contextLabel: "global",
|
|
1136
|
+
outFile,
|
|
1137
|
+
fetchOutdated: async () => {
|
|
1138
|
+
const tree = await bunPmLs({ global: true });
|
|
1139
|
+
const packages = Object.entries(tree.dependencies).map(([name, node]) => ({
|
|
1140
|
+
name,
|
|
1141
|
+
current: node.version,
|
|
1142
|
+
type: "global"
|
|
1143
|
+
}));
|
|
1144
|
+
return resolveOutdatedWithNpmView(packages);
|
|
1145
|
+
},
|
|
1146
|
+
updateRunner: selection.shouldUpdate ? async (packages) => {
|
|
1147
|
+
await bunUpdate({ cwd, global: true, packages });
|
|
1148
|
+
} : void 0
|
|
1149
|
+
});
|
|
1150
|
+
if (opts.checkOutdated) {
|
|
1151
|
+
if (result.outdated.length === 0) console.log("All global packages are up to date.");
|
|
1152
|
+
else console.log(formatOutdatedTable(result.outdated));
|
|
1153
|
+
}
|
|
1154
|
+
if (!result.proceed) return;
|
|
1155
|
+
const finalOutFile = outFile;
|
|
1156
|
+
const { report, markdownExtras } = await produceReport2("global", {
|
|
1157
|
+
outputFormat,
|
|
1158
|
+
outFile: finalOutFile,
|
|
1159
|
+
fullTree
|
|
1160
|
+
});
|
|
1161
|
+
await outputReport(report, outputFormat, finalOutFile, markdownExtras);
|
|
1162
|
+
});
|
|
1163
|
+
return globalCmd;
|
|
1164
|
+
}
|
|
1165
|
+
function createReadCommand2(program) {
|
|
1166
|
+
const readCmd = program.command("read").description(
|
|
1167
|
+
"Read a previously generated report (JSON or Markdown) and either print package names or install them"
|
|
1168
|
+
).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);
|
|
1169
|
+
readCmd.action(async (reportArg, opts) => {
|
|
1170
|
+
const chosen = opts.report || reportArg || "bun-report.json";
|
|
1171
|
+
const reportPath = import_node_path9.default.resolve(process.cwd(), chosen);
|
|
1172
|
+
try {
|
|
1173
|
+
const parsed = await loadReportFromFile(reportPath);
|
|
1174
|
+
const doInstall = Boolean(opts.install);
|
|
1175
|
+
const doPrint = Boolean(opts.print) || !doInstall;
|
|
1176
|
+
if (doPrint) {
|
|
1177
|
+
printFromReport(parsed);
|
|
1178
|
+
}
|
|
1179
|
+
if (doInstall) {
|
|
1180
|
+
await installFromReport(parsed, { cwd: process.cwd(), packageManager: "bun" });
|
|
1181
|
+
}
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
const isMd = isMarkdownReportFile(reportPath);
|
|
1184
|
+
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>";
|
|
1185
|
+
console.error(`Failed to read report at ${reportPath}: ${err?.message || err}`);
|
|
1186
|
+
console.error(hint);
|
|
1187
|
+
process.exitCode = 1;
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
return readCmd;
|
|
1191
|
+
}
|
|
1192
|
+
async function createProgram2() {
|
|
1193
|
+
const program = new import_commander2.Command().name("gex-bun").description("GEX: Dependency auditing and documentation for Bun (local and global).").version(await getToolVersion());
|
|
1194
|
+
program.addHelpText("beforeAll", `
|
|
1195
|
+
${ASCII_BANNER}`);
|
|
1196
|
+
createLocalCommand2(program);
|
|
1197
|
+
createGlobalCommand2(program);
|
|
1198
|
+
createReadCommand2(program);
|
|
1199
|
+
return program;
|
|
1200
|
+
}
|
|
1201
|
+
async function readPackageManifest(cwd) {
|
|
1202
|
+
try {
|
|
1203
|
+
const raw = await (0, import_promises7.readFile)(import_node_path9.default.join(cwd, "package.json"), "utf8");
|
|
1204
|
+
return JSON.parse(raw);
|
|
1205
|
+
} catch {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// src/runtimes/bun/cli.ts
|
|
763
1211
|
var import_meta3 = {};
|
|
764
1212
|
async function run2(argv = process.argv) {
|
|
765
|
-
|
|
1213
|
+
const program = await createProgram2();
|
|
1214
|
+
await program.parseAsync(argv);
|
|
766
1215
|
}
|
|
767
1216
|
var isMainModule2 = (() => {
|
|
768
1217
|
try {
|
|
@@ -779,6 +1228,66 @@ var isMainModule2 = (() => {
|
|
|
779
1228
|
})();
|
|
780
1229
|
if (isMainModule2) {
|
|
781
1230
|
run2().catch((error) => {
|
|
1231
|
+
console.error("Bun CLI error:", error);
|
|
1232
|
+
process.exitCode = 1;
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// src/cli.ts
|
|
1237
|
+
var import_meta4 = {};
|
|
1238
|
+
async function promptRuntimeSelection(io = {}) {
|
|
1239
|
+
const input = io.input ?? process.stdin;
|
|
1240
|
+
const output = io.output ?? process.stdout;
|
|
1241
|
+
return new Promise((resolve) => {
|
|
1242
|
+
const rl = import_node_readline.default.createInterface({ input, output });
|
|
1243
|
+
output.write("\nSelect a runtime to use:\n");
|
|
1244
|
+
output.write(" 1) gex-bun (Bun package manager)\n");
|
|
1245
|
+
output.write(" 2) gex-npm (npm / Node.js package manager)\n\n");
|
|
1246
|
+
rl.question("Enter your choice (1-2): ", (answer) => {
|
|
1247
|
+
rl.close();
|
|
1248
|
+
const choice = answer.trim().toLowerCase();
|
|
1249
|
+
if (choice === "1" || choice === "gex-bun" || choice === "bun") {
|
|
1250
|
+
resolve("bun");
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
if (choice === "2" || choice === "gex-npm" || choice === "gex-node" || choice === "npm" || choice === "node") {
|
|
1254
|
+
resolve("npm");
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
resolve(null);
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
async function run3(argv = process.argv, io = {}) {
|
|
1262
|
+
const effectiveArgv = argv ?? process.argv;
|
|
1263
|
+
const choice = await promptRuntimeSelection(io);
|
|
1264
|
+
if (choice === "bun") {
|
|
1265
|
+
await run2(effectiveArgv);
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
if (choice === "npm") {
|
|
1269
|
+
await run(effectiveArgv);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const output = io.output ?? process.stdout;
|
|
1273
|
+
output.write("Invalid selection. Please run `gex` again and choose 1 or 2.\n");
|
|
1274
|
+
process.exitCode = 1;
|
|
1275
|
+
}
|
|
1276
|
+
var isMainModule3 = (() => {
|
|
1277
|
+
try {
|
|
1278
|
+
if (typeof require !== "undefined" && typeof module !== "undefined") {
|
|
1279
|
+
return require.main === module;
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof import_meta4 !== "undefined") {
|
|
1282
|
+
return import_meta4.url === `file://${process.argv[1]}`;
|
|
1283
|
+
}
|
|
1284
|
+
return false;
|
|
1285
|
+
} catch {
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
})();
|
|
1289
|
+
if (isMainModule3) {
|
|
1290
|
+
run3().catch((error) => {
|
|
782
1291
|
console.error("CLI error:", error);
|
|
783
1292
|
process.exitCode = 1;
|
|
784
1293
|
});
|