@gjsify/cli 0.35.0 → 0.37.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/dist/cli.gjs.mjs +136 -126
- package/lib/actions/build.d.ts +19 -0
- package/lib/actions/build.js +2 -2
- package/lib/bundler-pick.js +72 -98
- package/lib/cli-fail.d.ts +4 -7
- package/lib/cli-fail.js +29 -55
- package/lib/commands/affected.js +161 -286
- package/lib/commands/install.d.ts +63 -66
- package/lib/commands/install.js +372 -536
- package/lib/commands/run.d.ts +1 -0
- package/lib/commands/run.js +37 -0
- package/lib/templates/install.mjs.tmpl +38 -68
- package/lib/types/cli-build-options.d.ts +33 -67
- package/lib/types/command.d.ts +9 -12
- package/lib/types/config-data.d.ts +125 -221
- package/lib/utils/auth-npmrc.d.ts +5 -10
- package/lib/utils/auth-npmrc.js +11 -21
- package/lib/utils/check-system-deps.d.ts +16 -0
- package/lib/utils/check-system-deps.js +49 -0
- package/lib/utils/clear-targets.d.ts +8 -11
- package/lib/utils/clear-targets.js +22 -29
- package/lib/utils/copy-targets.d.ts +13 -19
- package/lib/utils/copy-targets.js +31 -44
- package/lib/utils/dep-aggregation.d.ts +7 -15
- package/lib/utils/dep-aggregation.js +9 -22
- package/lib/utils/detect-native-packages.d.ts +83 -116
- package/lib/utils/detect-native-packages.js +111 -149
- package/lib/utils/dir-link.d.ts +29 -48
- package/lib/utils/dir-link.js +33 -56
- package/lib/utils/discover-showcases.d.ts +6 -7
- package/lib/utils/discover-showcases.js +11 -15
- package/lib/utils/dlx-cache.d.ts +29 -38
- package/lib/utils/dlx-cache.js +53 -63
- package/lib/utils/force-exit.d.ts +0 -3
- package/lib/utils/force-exit.js +13 -24
- package/lib/utils/gi-search-path.d.ts +16 -24
- package/lib/utils/gi-search-path.js +54 -82
- package/lib/utils/gjs-bundle-guard.d.ts +5 -13
- package/lib/utils/gjs-bundle-guard.js +35 -57
- package/lib/utils/gjs-source-escape.d.ts +2 -2
- package/lib/utils/gjs-source-escape.js +21 -39
- package/lib/utils/gjsify-shim.d.ts +26 -8
- package/lib/utils/gjsify-shim.js +128 -80
- package/lib/utils/html-entry.d.ts +8 -12
- package/lib/utils/html-entry.js +19 -28
- package/lib/utils/install-backend-native.d.ts +123 -161
- package/lib/utils/install-backend-native.js +536 -737
- package/lib/utils/install-backend.d.ts +42 -57
- package/lib/utils/install-backend.js +16 -21
- package/lib/utils/install-cache-fs.d.ts +10 -15
- package/lib/utils/install-cache-fs.js +18 -26
- package/lib/utils/install-lock.d.ts +5 -6
- package/lib/utils/install-lock.js +46 -65
- package/lib/utils/install-packument-cache.d.ts +27 -33
- package/lib/utils/install-packument-cache.js +44 -57
- package/lib/utils/install-progress.d.ts +2 -9
- package/lib/utils/install-progress.js +14 -39
- package/lib/utils/install-tarball-cache.d.ts +14 -20
- package/lib/utils/install-tarball-cache.js +41 -59
- package/lib/utils/library-output.d.ts +9 -22
- package/lib/utils/library-output.js +19 -37
- package/lib/utils/load-npmrc.d.ts +5 -6
- package/lib/utils/load-npmrc.js +18 -27
- package/lib/utils/node-bundle-guard.d.ts +4 -8
- package/lib/utils/node-bundle-guard.js +21 -35
- package/lib/utils/node-script.d.ts +12 -0
- package/lib/utils/node-script.js +95 -0
- package/lib/utils/node-version.d.ts +5 -14
- package/lib/utils/node-version.js +15 -24
- package/lib/utils/normalize-bundler-options.d.ts +7 -11
- package/lib/utils/normalize-bundler-options.js +24 -42
- package/lib/utils/npm-oidc.d.ts +23 -37
- package/lib/utils/npm-oidc.js +28 -53
- package/lib/utils/npm-otp-cache.d.ts +4 -4
- package/lib/utils/npm-otp-cache.js +20 -25
- package/lib/utils/npm-otp.d.ts +20 -30
- package/lib/utils/npm-otp.js +32 -51
- package/lib/utils/onboard-probe.d.ts +7 -13
- package/lib/utils/onboard-probe.js +16 -27
- package/lib/utils/parse-spec.d.ts +3 -3
- package/lib/utils/parse-spec.js +4 -5
- package/lib/utils/pkg-json-edit.d.ts +7 -12
- package/lib/utils/pkg-json-edit.js +13 -26
- package/lib/utils/pkg-json.d.ts +11 -18
- package/lib/utils/pkg-json.js +19 -28
- package/lib/utils/prefixed-output.d.ts +3 -4
- package/lib/utils/prefixed-output.js +13 -19
- package/lib/utils/prompt.d.ts +3 -4
- package/lib/utils/prompt.js +14 -18
- package/lib/utils/publish-diagnose.d.ts +11 -16
- package/lib/utils/publish-diagnose.js +15 -28
- package/lib/utils/publish-headers.d.ts +7 -11
- package/lib/utils/publish-headers.js +36 -65
- package/lib/utils/publishable-packages.d.ts +4 -6
- package/lib/utils/publishable-packages.js +14 -18
- package/lib/utils/resolve-gjs-entry.d.ts +5 -8
- package/lib/utils/resolve-gjs-entry.js +10 -27
- package/lib/utils/resolve-npm-package.d.ts +6 -11
- package/lib/utils/resolve-npm-package.js +29 -63
- package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
- package/lib/utils/resolve-plugin-by-name.js +9 -14
- package/lib/utils/run-gjs.d.ts +19 -17
- package/lib/utils/run-gjs.js +37 -51
- package/lib/utils/run-lifecycle-script.d.ts +5 -9
- package/lib/utils/run-lifecycle-script.js +23 -42
- package/lib/utils/run-node.d.ts +23 -33
- package/lib/utils/run-node.js +56 -60
- package/lib/utils/runtimes.d.ts +25 -35
- package/lib/utils/runtimes.js +34 -46
- package/lib/utils/spawn.d.ts +32 -48
- package/lib/utils/spawn.js +51 -84
- package/lib/utils/stdin.d.ts +2 -2
- package/lib/utils/stdin.js +10 -25
- package/lib/utils/suggest.d.ts +6 -7
- package/lib/utils/suggest.js +8 -10
- package/lib/utils/system-gi.d.ts +18 -27
- package/lib/utils/system-gi.js +60 -114
- package/lib/utils/trust-registry.d.ts +4 -8
- package/lib/utils/trust-registry.js +7 -15
- package/lib/utils/win32-command.d.ts +8 -14
- package/lib/utils/win32-command.js +29 -43
- package/lib/utils/workspace-root.js +7 -12
- package/package.json +18 -17
- package/showcases.json +6 -0
package/lib/utils/system-gi.js
CHANGED
|
@@ -3,110 +3,65 @@
|
|
|
3
3
|
// dynamic loader can find the ones a typelib names by BARE LEAF
|
|
4
4
|
// (`libgtk-4.1.dylib`, `libgdk_pixbuf-2.0.0.dylib`, …).
|
|
5
5
|
//
|
|
6
|
-
//
|
|
6
|
+
// A PINNED MIRROR of `packages/node-gi/node-gi/system-gi.js` — the two must agree
|
|
7
|
+
// and `system-gi.spec.ts` asserts it over a table of injected host shapes. Why a
|
|
8
|
+
// mirror and not an import, and what is owed to retire it:
|
|
9
|
+
// `status/open-todos.md` § "systemGiLibraryDirs() lives in two places".
|
|
7
10
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// WHY THIS EXISTS. A failed `dlopen('libgtk-4.1.dylib')` surfaces one layer up as
|
|
12
|
+
// something that reads like a type-system bug — `TypeError: Gtk.DrawingArea is not
|
|
13
|
+
// a subclassable GObject type` under Node, `Error: Unsupported type void, deriving
|
|
14
|
+
// from fundamental void` under a bare `gjs -c "imports.gi.Gtk; Gtk.init()"`:
|
|
15
|
+
// `gi_repository_require` resolves the typelib, `g_module_open` then fails, and no
|
|
16
|
+
// `gtk_*_get_type` is ever registered.
|
|
14
17
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// stalled 60+ packages), so it is tracked in `status/open-todos.md` instead of
|
|
21
|
-
// taken here. A spec may reach across the repo because it is never bundled into
|
|
22
|
-
// the published `lib/`, which is what lets the agreement be machine-checked
|
|
23
|
-
// without creating the forbidden edge.
|
|
18
|
+
// The gap is the LOADER, not the installation (measured on macOS 15.7.8 / Homebrew
|
|
19
|
+
// /usr/local / GTK 4.22.4). `node` has no rpath; Homebrew's `gjs` has one into
|
|
20
|
+
// GLIB's keg alone (`…/opt/glib/lib`), so it resolves the glib-family leaves it
|
|
21
|
+
// links against and no other keg's. `/usr/local/lib`, where `libgtk-4.1.dylib`
|
|
22
|
+
// actually is, is in neither, nor in dyld's default fallback path.
|
|
24
23
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// first release that shipped a darwin-x64 addon) — this is node-gi's report:
|
|
31
|
-
//
|
|
32
|
-
// GLib-GIRepository-WARNING: Failed to load shared library 'libgtk-4.1.dylib'
|
|
33
|
-
// referenced by the typelib: dlopen(libgtk-4.1.dylib, 0x0009): tried:
|
|
34
|
-
// 'libgtk-4.1.dylib' (no such file), …, '/usr/lib/libgtk-4.1.dylib' (no such file)
|
|
35
|
-
// TypeError: Gtk.DrawingArea is not a subclassable GObject type
|
|
36
|
-
//
|
|
37
|
-
// 2. GJS (same VM, gjs 1.88.1, `gjs -c "imports.gi.Gtk; Gtk.init()"`, nothing
|
|
38
|
-
// gjsify in the process at all) — the reason this CLI needs the module too:
|
|
39
|
-
//
|
|
40
|
-
// dlopen(libgtk-4.1.dylib, 0x0009): tried: 'libgtk-4.1.dylib' (no such file),
|
|
41
|
-
// '/System/Volumes/Preboot/Cryptexes/OSlibgtk-4.1.dylib' (no such file),
|
|
42
|
-
// '/usr/local/Cellar/gjs/1.88.1/bin/../../../../opt/glib/lib/libgtk-4.1.dylib'
|
|
43
|
-
// (no such file), '/usr/lib/libgtk-4.1.dylib' (no such file, not in dyld cache)
|
|
44
|
-
// JS ERROR: Error: Unsupported type void, deriving from fundamental void
|
|
45
|
-
//
|
|
46
|
-
// Read the third path dyld tried: Homebrew's `gjs` binary has an rpath, and it
|
|
47
|
-
// points into GLIB's keg (`…/opt/glib/lib`) — nowhere else. So Homebrew's gjs
|
|
48
|
-
// resolves the glib-family leaves it links against and NO other keg's. The
|
|
49
|
-
// difference between the two reports is cosmetic, not structural: `node` has no
|
|
50
|
-
// rpath at all, `gjs` has one into the wrong keg, and `/usr/local/lib` — where
|
|
51
|
-
// `libgtk-4.1.dylib` actually is — is in neither, nor in dyld's default fallback
|
|
52
|
-
// path. The gap is the LOADER, not the installation: `gi_repository_require`
|
|
53
|
-
// resolves the typelib, `g_module_open('libgtk-4.1.dylib')` then fails, no
|
|
54
|
-
// `gtk_*_get_type` is ever registered, and the failure surfaces one layer up as
|
|
55
|
-
// something that reads like a type-system bug.
|
|
56
|
-
//
|
|
57
|
-
// The value CANNOT come from `pkg-config --variable=libdir gtk4`. Measured on the
|
|
58
|
-
// same host: that answers `/usr/local/Cellar/gtk4/4.22.4/lib` — a PER-FORMULA
|
|
59
|
-
// directory that fixes GTK and leaves `libgdk_pixbuf-2.0.0.dylib` (a different
|
|
60
|
-
// formula, so a different Cellar libdir) still unresolvable. What works is the
|
|
61
|
-
// directory every keg is symlinked into, `<prefix>/lib`, because it is the UNION.
|
|
62
|
-
// So the question this module answers is "which directories hold the GI stack",
|
|
63
|
-
// never "where is one library".
|
|
24
|
+
// The value CANNOT come from `pkg-config --variable=libdir gtk4`: that answers
|
|
25
|
+
// `/usr/local/Cellar/gtk4/4.22.4/lib`, a PER-FORMULA directory that fixes GTK and
|
|
26
|
+
// leaves `libgdk_pixbuf-2.0.0.dylib` (different formula, different Cellar libdir)
|
|
27
|
+
// unresolvable. What works is `<prefix>/lib`, the UNION every keg is symlinked into.
|
|
28
|
+
// The question is "which directories hold the GI stack", never "where is one library".
|
|
64
29
|
//
|
|
65
30
|
// THE RULE — a typelib and the library it names are SIBLINGS by GI's own install
|
|
66
31
|
// layout: `<libdir>/girepository-1.0/Gtk-4.0.typelib` beside
|
|
67
|
-
// `<libdir>/libgtk-4.1.dylib`. A directory holding `girepository-1.0/` therefore
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// `/opt/homebrew/lib` (Homebrew arm64), `/opt/local/lib` (MacPorts) or a custom
|
|
71
|
-
// prefix without needing to know that any of them exist.
|
|
32
|
+
// `<libdir>/libgtk-4.1.dylib`. A directory holding `girepository-1.0/` therefore IS
|
|
33
|
+
// a GI library directory, which is what makes this package-manager-agnostic — it
|
|
34
|
+
// lands on Homebrew, MacPorts or a custom prefix without knowing any of them exist.
|
|
72
35
|
//
|
|
73
36
|
// PURE FUNCTION of injected host facts (platform / env / fs / pkg-config), like
|
|
74
|
-
// `resolvePrebuildDirName()` and `buildNativeEnv()` beside it
|
|
75
|
-
//
|
|
76
|
-
// the outermost call site, as defaults.
|
|
77
|
-
//
|
|
78
|
-
// ── HOW THE CONSUMER DIFFERS FROM NODE-GI'S ──────────────────────────────────
|
|
37
|
+
// `resolvePrebuildDirName()` and `buildNativeEnv()` beside it, so the darwin branch
|
|
38
|
+
// is unit-testable from Linux.
|
|
79
39
|
//
|
|
80
|
-
// node-gi RE-EXECS
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
// inside the bundle either way — GI resolves typelibs and their backers before the
|
|
87
|
-
// bundle's first line runs.
|
|
40
|
+
// node-gi RE-EXECS itself with the result on `DYLD_FALLBACK_LIBRARY_PATH`, because
|
|
41
|
+
// dyld captures that variable at process LAUNCH and an in-process `process.env`
|
|
42
|
+
// mutation does nothing. This CLI needs no re-exec: it is the one LAUNCHING the gjs
|
|
43
|
+
// child (`buildNativeEnv()` → `spawn('gjs', …, { env })`), the same repair one hop
|
|
44
|
+
// earlier. A launcher inside the bundle could not fix it either way — GI resolves
|
|
45
|
+
// typelibs and their backers before the bundle's first line runs.
|
|
88
46
|
import { spawnSync } from 'node:child_process';
|
|
89
47
|
import { statSync } from 'node:fs';
|
|
90
|
-
// POSIX path semantics, not the HOST's
|
|
91
|
-
// a
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
// three `systemGiLibraryDirs` specs returned `[]` where they assert a prefix.
|
|
97
|
-
// `PROBED_GI_LIBDIRS` is darwin-only, so POSIX is always the right dialect here.
|
|
48
|
+
// POSIX path semantics, not the HOST's: `platform` is a PARAMETER, so this answers
|
|
49
|
+
// a question about a darwin filesystem from any host. Node's host-following
|
|
50
|
+
// `join` turns `join('/usr/local/lib', 'girepository-1.0')` into
|
|
51
|
+
// `\usr\local\lib\girepository-1.0` on win32, matching nothing the caller (or the
|
|
52
|
+
// injected `existsDir`) holds — measured on the Windows leg, three
|
|
53
|
+
// `systemGiLibraryDirs` specs returned `[]` where they assert a prefix.
|
|
98
54
|
import { posix } from 'node:path';
|
|
99
55
|
const { basename, dirname, join } = posix;
|
|
100
56
|
/**
|
|
101
57
|
* Prefixes a macOS GI stack is installed under in practice — probed, never
|
|
102
58
|
* assumed: each is accepted only when it actually holds a `girepository-1.0/`.
|
|
103
59
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* or otherwise bespoke prefix — but it cannot be the only one.
|
|
60
|
+
* Load-bearing rather than belt-and-braces: `brew install gtk4` installs RUNTIME
|
|
61
|
+
* dependencies only, so a user running a published showcase routinely has the
|
|
62
|
+
* whole GTK stack and no `pkg-config` at all (a BUILD dependency of those
|
|
63
|
+
* formulae). The pkg-config source below is the general mechanism — it finds a
|
|
64
|
+
* jhbuild or otherwise bespoke prefix — but it cannot be the only one.
|
|
110
65
|
*/
|
|
111
66
|
const PROBED_GI_LIBDIRS = {
|
|
112
67
|
darwin: ['/opt/homebrew/lib', '/usr/local/lib', '/opt/local/lib'],
|
|
@@ -115,9 +70,7 @@ const PROBED_GI_LIBDIRS = {
|
|
|
115
70
|
const TYPELIB_SUBDIR = 'girepository-1.0';
|
|
116
71
|
/** Memoized `pkg-config` answer: the spawn happens at most once per process. */
|
|
117
72
|
let pkgConfigDirsCache = null;
|
|
118
|
-
/**
|
|
119
|
-
* Split an OS search-path variable, dropping empty segments.
|
|
120
|
-
*/
|
|
73
|
+
/** Split an OS search-path variable, dropping empty segments. */
|
|
121
74
|
export function splitSearchPath(value, separator = ':') {
|
|
122
75
|
return (value ?? '').split(separator).filter(Boolean);
|
|
123
76
|
}
|
|
@@ -134,15 +87,12 @@ function isDirectory(dir) {
|
|
|
134
87
|
/**
|
|
135
88
|
* `pkg-config`'s OWN configured `.pc` search path plus `$PKG_CONFIG_PATH`. Every
|
|
136
89
|
* entry is a `<libdir>/pkgconfig` (or `<datadir>/pkgconfig`) directory, so its
|
|
137
|
-
* parent is a candidate libdir — which is how a prefix nobody hardcoded is
|
|
138
|
-
* found. Measured on the reported host: `pc_path` =
|
|
139
|
-
* `/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/pkgconfig:…`,
|
|
140
|
-
* whose first parent is exactly the `/usr/local/lib` that resolves every leaf.
|
|
90
|
+
* parent is a candidate libdir — which is how a prefix nobody hardcoded is found.
|
|
141
91
|
*
|
|
142
|
-
* `spawnSync` reports a missing executable as `result.error`, not by throwing
|
|
143
|
-
* an absent `pkg-config`
|
|
144
|
-
*
|
|
145
|
-
*
|
|
92
|
+
* `spawnSync` reports a missing executable as `result.error`, not by throwing, so
|
|
93
|
+
* an absent `pkg-config` (expected on a runtime-only GTK install) is a checked
|
|
94
|
+
* return value and not an exception path. (`@gjsify/child_process` matches Node
|
|
95
|
+
* here deliberately; see its `spawnSync` note.)
|
|
146
96
|
*/
|
|
147
97
|
export function pkgConfigSearchDirs(env) {
|
|
148
98
|
if (pkgConfigDirsCache)
|
|
@@ -172,19 +122,16 @@ export function pkgConfigSearchDirs(env) {
|
|
|
172
122
|
* Sources 2 and 3 are GUESSES at a prefix, so each must show the
|
|
173
123
|
* `girepository-1.0/` marker before it is believed.
|
|
174
124
|
*
|
|
175
|
-
* Empty off darwin, and that is a statement about LOADERS, not about GI
|
|
176
|
-
* resolves these leaves through `ld.so`'s system-wide
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* NOR re-reads its search path after launch, which is why darwin needs the
|
|
180
|
-
* variable set at launch and nobody else does.
|
|
125
|
+
* Empty off darwin, and that is a statement about LOADERS, not about GI: Linux
|
|
126
|
+
* resolves these leaves through `ld.so`'s system-wide cache (`/etc/ld.so.conf.d`),
|
|
127
|
+
* which a package install populates. dyld is the one loader that neither consults
|
|
128
|
+
* a system-wide config NOR re-reads its search path after launch.
|
|
181
129
|
*
|
|
182
|
-
* win32 is deliberately absent rather than proven unnecessary
|
|
183
|
-
* search path, a Windows GTK distribution puts its `bin` there
|
|
184
|
-
* Windows re-reads that path at every `LoadLibrary` — so if a system
|
|
185
|
-
* turns out not to be on it, the repair is a `PATH` prepend (which
|
|
186
|
-
* {@link buildNativeEnv} already does for the prebuilds), never a second
|
|
187
|
-
* variable. Untested there.
|
|
130
|
+
* win32 is deliberately absent rather than proven unnecessary, and untested:
|
|
131
|
+
* `PATH` is the DLL search path, a Windows GTK distribution puts its `bin` there
|
|
132
|
+
* itself, and Windows re-reads that path at every `LoadLibrary` — so if a system
|
|
133
|
+
* GTK ever turns out not to be on it, the repair is a `PATH` prepend (which
|
|
134
|
+
* {@link buildNativeEnv} already does for the prebuilds), never a second variable.
|
|
188
135
|
*
|
|
189
136
|
* @returns absolute directories, or `[]` when this platform's loader needs none
|
|
190
137
|
*/
|
|
@@ -220,10 +167,9 @@ export function systemGiLibraryDirs({ platform = process.platform, env = process
|
|
|
220
167
|
* Whether `current` (a split `DYLD_FALLBACK_LIBRARY_PATH`) already covers every
|
|
221
168
|
* directory in `wanted` — the "nothing to do" test.
|
|
222
169
|
*
|
|
223
|
-
* Kept in the port even though this CLI's consumer does not branch on it:
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* both.
|
|
170
|
+
* Kept in the port even though this CLI's consumer does not branch on it: a mirror
|
|
171
|
+
* that drifts by one export is a mirror nobody can check. `system-gi.spec.ts`
|
|
172
|
+
* pins both.
|
|
227
173
|
*/
|
|
228
174
|
export function pathCovers(wanted, current) {
|
|
229
175
|
return wanted.every((dir) => current.includes(dir));
|
|
@@ -29,9 +29,8 @@ export interface TrustEntry {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* The POST body for `trust github
|
|
33
|
-
*
|
|
34
|
-
* required — `--allow-publish` → `['createPackage']`).
|
|
32
|
+
* The POST body for `trust github`: npm always sends an ARRAY with a single config object, and
|
|
33
|
+
* `permissions` is required alongside `type`/`claims`.
|
|
35
34
|
*/
|
|
36
35
|
export declare function githubTrustBody(opts: {
|
|
37
36
|
repository: string;
|
|
@@ -62,10 +61,7 @@ export declare function classifyTrustList(status: number, body: unknown, opts: {
|
|
|
62
61
|
workflow: string;
|
|
63
62
|
}): TrustState;
|
|
64
63
|
/**
|
|
65
|
-
* Derive `owner/repo` from a git remote URL.
|
|
66
|
-
*
|
|
67
|
-
* ssh://git@github.com/owner/repo.git
|
|
68
|
-
* https://github.com/owner/repo(.git)
|
|
69
|
-
* Returns null when it can't confidently extract two segments.
|
|
64
|
+
* Derive `owner/repo` from a git remote URL (scp-like `git@host:owner/repo.git` and the
|
|
65
|
+
* `ssh://`/`https://` forms). Null when two segments cannot be confidently extracted.
|
|
70
66
|
*/
|
|
71
67
|
export declare function parseRepoFromGitRemote(url: string): string | null;
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
// github: POST <registry>/-/package/<escaped>/trust body: [{type,claims}]
|
|
6
6
|
// revoke: DELETE <registry>/-/package/<escaped>/trust/<id>
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// Reference: refs/npm-cli/lib/trust-cmd.js + lib/commands/trust/*.js.
|
|
8
|
+
// Side-effect-free so it unit-tests without a network; `commands/trust.ts` does the fetch + OTP
|
|
9
|
+
// orchestration. Reference: refs/npm-cli/lib/trust-cmd.js + lib/commands/trust/*.js.
|
|
11
10
|
import { escapePackageName } from './publish-headers.js';
|
|
12
11
|
/** `<registry>/-/package/<@scope%2fname>/trust`. */
|
|
13
12
|
export function trustUrl(registry, pkgName) {
|
|
@@ -25,9 +24,8 @@ export function trustRevokeUrl(registry, pkgName, id) {
|
|
|
25
24
|
export const PERMISSION_PUBLISH = 'createPackage';
|
|
26
25
|
export const PERMISSION_STAGE_PUBLISH = 'createStagedPackage';
|
|
27
26
|
/**
|
|
28
|
-
* The POST body for `trust github
|
|
29
|
-
*
|
|
30
|
-
* required — `--allow-publish` → `['createPackage']`).
|
|
27
|
+
* The POST body for `trust github`: npm always sends an ARRAY with a single config object, and
|
|
28
|
+
* `permissions` is required alongside `type`/`claims`.
|
|
31
29
|
*/
|
|
32
30
|
export function githubTrustBody(opts) {
|
|
33
31
|
const claims = {
|
|
@@ -77,24 +75,18 @@ export function classifyTrustList(status, body, opts) {
|
|
|
77
75
|
return 'error';
|
|
78
76
|
}
|
|
79
77
|
/**
|
|
80
|
-
* Derive `owner/repo` from a git remote URL.
|
|
81
|
-
*
|
|
82
|
-
* ssh://git@github.com/owner/repo.git
|
|
83
|
-
* https://github.com/owner/repo(.git)
|
|
84
|
-
* Returns null when it can't confidently extract two segments.
|
|
78
|
+
* Derive `owner/repo` from a git remote URL (scp-like `git@host:owner/repo.git` and the
|
|
79
|
+
* `ssh://`/`https://` forms). Null when two segments cannot be confidently extracted.
|
|
85
80
|
*/
|
|
86
81
|
export function parseRepoFromGitRemote(url) {
|
|
87
82
|
let s = url.trim();
|
|
88
83
|
if (!s)
|
|
89
84
|
return null;
|
|
90
|
-
// Strip a trailing .git
|
|
91
85
|
s = s.replace(/\.git$/, '');
|
|
92
|
-
// scp-like: git@host:owner/repo
|
|
93
86
|
const scp = s.match(/^[^/@]+@[^:]+:(.+)$/);
|
|
94
87
|
if (scp) {
|
|
95
88
|
return twoSegments(scp[1]);
|
|
96
89
|
}
|
|
97
|
-
// URL forms: ssh://, https://, http://, git://
|
|
98
90
|
const m = s.match(/^[a-z]+:\/\/[^/]+\/(.+)$/i);
|
|
99
91
|
if (m) {
|
|
100
92
|
return twoSegments(m[1]);
|
|
@@ -105,6 +97,6 @@ function twoSegments(path) {
|
|
|
105
97
|
const parts = path.split('/').filter((p) => p.length > 0);
|
|
106
98
|
if (parts.length < 2)
|
|
107
99
|
return null;
|
|
108
|
-
//
|
|
100
|
+
// The LAST two segments, so a nested group path still yields repo + its owner.
|
|
109
101
|
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
110
102
|
}
|
|
@@ -21,10 +21,9 @@ export interface Win32ResolveContext {
|
|
|
21
21
|
/**
|
|
22
22
|
* Read `name` from `env` case-insensitively.
|
|
23
23
|
*
|
|
24
|
-
* Windows
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* otherwise silently resolve nothing.
|
|
24
|
+
* Windows env vars are case-insensitive and `process.env` mirrors that, but a plain object —
|
|
25
|
+
* what every spawn call site builds — does not, so a child env carrying `Path` rather than
|
|
26
|
+
* `PATH` would silently resolve nothing.
|
|
28
27
|
*/
|
|
29
28
|
export declare function lookupEnv(env: NodeJS.ProcessEnv, name: string): string | undefined;
|
|
30
29
|
/** Escape a command path for cmd.exe. Port of cross-spawn's `escape.command`. */
|
|
@@ -35,20 +34,15 @@ export declare function escapeCmdCommand(arg: string): string;
|
|
|
35
34
|
*/
|
|
36
35
|
export declare function escapeCmdArgument(arg: string, doubleEscapeMetaChars?: boolean): string;
|
|
37
36
|
/**
|
|
38
|
-
* Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* @param commandFile Resolved absolute path of the target (e.g. the `.cmd` shim).
|
|
42
|
-
* @param args The caller's arguments, unescaped.
|
|
43
|
-
* @param comspec The interpreter, normally `%COMSPEC%`.
|
|
37
|
+
* Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-executable target.
|
|
38
|
+
* `args` are the caller's UNESCAPED arguments; `comspec` is normally `%COMSPEC%`.
|
|
44
39
|
*/
|
|
45
40
|
export declare function buildCmdExeInvocation(commandFile: string, args: readonly string[], comspec?: string): Win32Invocation;
|
|
46
41
|
/**
|
|
47
42
|
* Rewrite a bare command name into something Windows can actually spawn.
|
|
48
43
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* (see the module header).
|
|
44
|
+
* `undefined` when nothing needs rewriting — not win32, not a bare name, or not found on
|
|
45
|
+
* `PATH`. The last case is deliberate: letting `spawn` fail with `ENOENT` preserves the
|
|
46
|
+
* contract callers key their install hints on (see the module header).
|
|
53
47
|
*/
|
|
54
48
|
export declare function resolveWin32Command(cmd: string, args: readonly string[], ctx: Win32ResolveContext): Win32Invocation | undefined;
|
|
@@ -6,36 +6,28 @@
|
|
|
6
6
|
// Linux host (the same shape `utils/bin-shim.ts` uses), and narrowed to the one
|
|
7
7
|
// case the CLI needs — resolving a bare command name before `spawn`.
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// problem. `CreateProcess` appends only `.exe` when it searches PATH for a bare
|
|
13
|
-
// name, and every npm-installed bin on Windows is a `.cmd` shim. Measured on
|
|
14
|
-
// win32 x64 / Node 24.18.1:
|
|
9
|
+
// `spawn('npm', …)` cannot work on Windows, and the failure is not a PATH problem:
|
|
10
|
+
// `CreateProcess` appends only `.exe` when it searches PATH for a bare name, and every
|
|
11
|
+
// npm-installed bin on Windows is a `.cmd` shim. Measured on win32 x64 / Node 24.18.1:
|
|
15
12
|
//
|
|
16
13
|
// spawn('npm', ['--version']) → ENOENT (.cmd is never tried)
|
|
17
14
|
// spawn('node', ['--version']) → ok (.exe is appended)
|
|
18
15
|
// spawn('npm.cmd', ['--version']) → EINVAL (Node refuses a batch file)
|
|
19
16
|
//
|
|
20
|
-
// The EINVAL is Node's CVE-2024-27980 mitigation: a `.cmd`/`.bat` target
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// package manager is `npm` on every non-GJS host, so on Windows each one died
|
|
28
|
-
// with `spawn npm ENOENT` before running a single script.
|
|
17
|
+
// The EINVAL is Node's CVE-2024-27980 mitigation: a `.cmd`/`.bat` target must go through a
|
|
18
|
+
// command interpreter, so resolving the shim path is NOT enough — it has to be invoked as
|
|
19
|
+
// `cmd.exe /d /s /c "…"` with every argument escaped for cmd's parser ({@link
|
|
20
|
+
// buildCmdExeInvocation}). The gap took out the whole orchestration layer, not one command:
|
|
21
|
+
// `gjsify workspace`/`foreach`/`check` spawn `<pm> run <script>` per package and the package
|
|
22
|
+
// manager is `npm` on every non-GJS host, so on Windows each died with `spawn npm ENOENT`
|
|
23
|
+
// before running a single script.
|
|
29
24
|
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// plus `commands/tsc.ts`'s transparent fall back to upstream tsc when `gjs` is
|
|
37
|
-
// absent both key on exactly that code. Resolving first keeps "not found" a
|
|
38
|
-
// spawn-time error and routes only what really is a batch file through cmd.exe.
|
|
25
|
+
// Resolution happens HERE and not in cmd.exe because handing the bare name to `cmd.exe /c`
|
|
26
|
+
// would also find the `.cmd` but destroy the ENOENT contract: a missing command would return a
|
|
27
|
+
// generic exit 1 with an unparsable message, and both `SpawnToCompletionOptions.notFound` (the
|
|
28
|
+
// install hints) and `commands/tsc.ts`'s fall back to upstream tsc when `gjs` is absent key on
|
|
29
|
+
// that code. Resolving first keeps "not found" a spawn-time error and routes only a real batch
|
|
30
|
+
// file through cmd.exe.
|
|
39
31
|
/** Default when `PATHEXT` is unset — the extensions `CreateProcess` users expect. */
|
|
40
32
|
const DEFAULT_PATHEXT = '.COM;.EXE;.BAT;.CMD';
|
|
41
33
|
/** Only `.com`/`.exe` can be executed without an interpreter. Verbatim from cross-spawn. */
|
|
@@ -47,17 +39,16 @@ const META_CHARS_RE = /([()\][%!^"`<>&|;, *?])/g;
|
|
|
47
39
|
/**
|
|
48
40
|
* Read `name` from `env` case-insensitively.
|
|
49
41
|
*
|
|
50
|
-
* Windows
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* otherwise silently resolve nothing.
|
|
42
|
+
* Windows env vars are case-insensitive and `process.env` mirrors that, but a plain object —
|
|
43
|
+
* what every spawn call site builds — does not, so a child env carrying `Path` rather than
|
|
44
|
+
* `PATH` would silently resolve nothing.
|
|
54
45
|
*/
|
|
55
46
|
export function lookupEnv(env, name) {
|
|
56
47
|
const wanted = name.toLowerCase();
|
|
57
48
|
for (const key of Object.keys(env)) {
|
|
58
|
-
// A key present but explicitly `undefined` means NOT SET — `spawn`
|
|
59
|
-
//
|
|
60
|
-
//
|
|
49
|
+
// A key present but explicitly `undefined` means NOT SET — `spawn` drops it from the
|
|
50
|
+
// child env. Returning it would shadow a later spelling (`{ PATH: undefined, Path:
|
|
51
|
+
// 'C:\\bin' }`) and resolve nothing.
|
|
61
52
|
if (key.toLowerCase() === wanted && env[key] !== undefined)
|
|
62
53
|
return env[key];
|
|
63
54
|
}
|
|
@@ -84,12 +75,8 @@ export function escapeCmdArgument(arg, doubleEscapeMetaChars = false) {
|
|
|
84
75
|
return out;
|
|
85
76
|
}
|
|
86
77
|
/**
|
|
87
|
-
* Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* @param commandFile Resolved absolute path of the target (e.g. the `.cmd` shim).
|
|
91
|
-
* @param args The caller's arguments, unescaped.
|
|
92
|
-
* @param comspec The interpreter, normally `%COMSPEC%`.
|
|
78
|
+
* Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-executable target.
|
|
79
|
+
* `args` are the caller's UNESCAPED arguments; `comspec` is normally `%COMSPEC%`.
|
|
93
80
|
*/
|
|
94
81
|
export function buildCmdExeInvocation(commandFile, args, comspec = 'cmd.exe') {
|
|
95
82
|
// A cmd-shim under `node_modules/.bin/` is itself a batch file that re-invokes
|
|
@@ -121,10 +108,9 @@ function lookupOnPath(cmd, ctx) {
|
|
|
121
108
|
/**
|
|
122
109
|
* Rewrite a bare command name into something Windows can actually spawn.
|
|
123
110
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* (see the module header).
|
|
111
|
+
* `undefined` when nothing needs rewriting — not win32, not a bare name, or not found on
|
|
112
|
+
* `PATH`. The last case is deliberate: letting `spawn` fail with `ENOENT` preserves the
|
|
113
|
+
* contract callers key their install hints on (see the module header).
|
|
128
114
|
*/
|
|
129
115
|
export function resolveWin32Command(cmd, args, ctx) {
|
|
130
116
|
if (ctx.platform !== 'win32' || !isBareName(cmd))
|
|
@@ -132,8 +118,8 @@ export function resolveWin32Command(cmd, args, ctx) {
|
|
|
132
118
|
const resolved = lookupOnPath(cmd, ctx);
|
|
133
119
|
if (!resolved)
|
|
134
120
|
return undefined;
|
|
135
|
-
// `.exe`/`.com` run without an interpreter; spawning the resolved absolute
|
|
136
|
-
//
|
|
121
|
+
// `.exe`/`.com` run without an interpreter; spawning the resolved absolute path skips a
|
|
122
|
+
// second PATH search and cannot be shadowed in between.
|
|
137
123
|
if (DIRECTLY_EXECUTABLE_RE.test(resolved))
|
|
138
124
|
return { cmd: resolved, args: [...args] };
|
|
139
125
|
return buildCmdExeInvocation(resolved, args, lookupEnv(ctx.env, 'COMSPEC') ?? 'cmd.exe');
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
// Walk up from a starting directory to the first ancestor whose
|
|
2
|
-
//
|
|
1
|
+
// Walk up from a starting directory to the first ancestor whose `package.json`
|
|
2
|
+
// declares a `workspaces` field — the monorepo root that `gjsify
|
|
3
|
+
// run`/`workspace`/`foreach` need to discover siblings, resolve `workspace:^`
|
|
4
|
+
// deps and walk the dep graph. They can be invoked from anywhere, because a
|
|
5
|
+
// chained script call puts the child CLI's cwd at the inner workspace.
|
|
3
6
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// child CLI invocation's cwd at the inner workspace's location), and
|
|
7
|
-
// every one of them needs the monorepo root to discover sibling
|
|
8
|
-
// workspaces, resolve `workspace:^` deps, and walk the dep graph.
|
|
9
|
-
//
|
|
10
|
-
// Sanity-checked via `discoverWorkspaces(candidate)`: the candidate
|
|
11
|
-
// monorepo must actually contain `start` as one of its workspaces.
|
|
12
|
-
// Without this guard, a grand-parent monorepo unrelated to `start`
|
|
13
|
-
// could be picked up.
|
|
7
|
+
// The candidate must also CONTAIN `start` (`discoverWorkspaces`), or an unrelated
|
|
8
|
+
// grand-parent monorepo gets picked up.
|
|
14
9
|
import { existsSync } from 'node:fs';
|
|
15
10
|
import { join, resolve } from 'node:path';
|
|
16
11
|
import { discoverWorkspaces } from '@gjsify/workspace';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -134,19 +134,20 @@
|
|
|
134
134
|
"cli"
|
|
135
135
|
],
|
|
136
136
|
"dependencies": {
|
|
137
|
-
"@gjsify/buffer": "^0.
|
|
138
|
-
"@gjsify/create-app": "^0.
|
|
139
|
-
"@gjsify/node-globals": "^0.
|
|
140
|
-
"@gjsify/node-polyfills": "^0.
|
|
141
|
-
"@gjsify/npm-registry": "^0.
|
|
142
|
-
"@gjsify/resolve-npm": "^0.
|
|
143
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.
|
|
144
|
-
"@gjsify/
|
|
145
|
-
"@gjsify/
|
|
146
|
-
"@gjsify/
|
|
147
|
-
"@gjsify/
|
|
148
|
-
"@gjsify/
|
|
149
|
-
"@gjsify/
|
|
137
|
+
"@gjsify/buffer": "^0.37.0",
|
|
138
|
+
"@gjsify/create-app": "^0.37.0",
|
|
139
|
+
"@gjsify/node-globals": "^0.37.0",
|
|
140
|
+
"@gjsify/node-polyfills": "^0.37.0",
|
|
141
|
+
"@gjsify/npm-registry": "^0.37.0",
|
|
142
|
+
"@gjsify/resolve-npm": "^0.37.0",
|
|
143
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.37.0",
|
|
144
|
+
"@gjsify/vite-plugin-blueprint": "^0.37.0",
|
|
145
|
+
"@gjsify/rolldown-plugin-pnp": "^0.37.0",
|
|
146
|
+
"@gjsify/semver": "^0.37.0",
|
|
147
|
+
"@gjsify/tar": "^0.37.0",
|
|
148
|
+
"@gjsify/tsc": "^0.37.0",
|
|
149
|
+
"@gjsify/web-polyfills": "^0.37.0",
|
|
150
|
+
"@gjsify/workspace": "^0.37.0",
|
|
150
151
|
"cosmiconfig": "^9.0.2",
|
|
151
152
|
"get-tsconfig": "^4.14.0",
|
|
152
153
|
"pkg-types": "^2.3.1",
|
|
@@ -154,13 +155,13 @@
|
|
|
154
155
|
"yargs": "^18.0.0"
|
|
155
156
|
},
|
|
156
157
|
"devDependencies": {
|
|
157
|
-
"@gjsify/unit": "^0.
|
|
158
|
+
"@gjsify/unit": "^0.37.0",
|
|
158
159
|
"@types/yargs": "^17.0.35",
|
|
159
160
|
"typescript": "^6.0.3"
|
|
160
161
|
},
|
|
161
162
|
"peerDependencies": {
|
|
162
|
-
"@gjsify/oxfmt-native": "^0.
|
|
163
|
-
"@gjsify/rolldown-native": "^0.
|
|
163
|
+
"@gjsify/oxfmt-native": "^0.37.0",
|
|
164
|
+
"@gjsify/rolldown-native": "^0.37.0"
|
|
164
165
|
},
|
|
165
166
|
"peerDependenciesMeta": {
|
|
166
167
|
"@gjsify/oxfmt-native": {
|
package/showcases.json
CHANGED
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
"package": "@gjsify/example-dom-three-geometry-teapot",
|
|
20
20
|
"description": "Three.js Utah teapot"
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"name": "three-loader-ldraw",
|
|
24
|
+
"category": "dom",
|
|
25
|
+
"package": "@gjsify/example-dom-three-loader-ldraw",
|
|
26
|
+
"description": "Three.js LDraw viewer — loads packed .mpd models over fetch and steps through their building instructions"
|
|
27
|
+
},
|
|
22
28
|
{
|
|
23
29
|
"name": "three-postprocessing-pixel",
|
|
24
30
|
"category": "dom",
|