@gjsify/cli 0.36.0 → 0.38.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 +169 -169
- package/lib/actions/build.d.ts +7 -0
- package/lib/actions/build.js +18 -3
- package/lib/bundler-pick.js +61 -87
- 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/config.d.ts +14 -0
- package/lib/config.js +18 -1
- package/lib/templates/install.mjs.tmpl +164 -75
- 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 +137 -221
- package/lib/utils/auth-npmrc.d.ts +5 -10
- package/lib/utils/auth-npmrc.js +11 -21
- package/lib/utils/bin-shim.d.ts +40 -25
- package/lib/utils/bin-shim.js +51 -26
- 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 +89 -122
- package/lib/utils/detect-native-packages.js +119 -169
- 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 +17 -20
- package/lib/utils/gjsify-shim.js +80 -120
- 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.js +40 -53
- 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 +16 -21
- package/lib/utils/run-gjs.js +31 -56
- 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 +21 -33
- package/lib/utils/run-node.js +43 -64
- 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 +36 -27
- package/lib/utils/system-gi.js +85 -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 -18
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// Make the native prebuilds this process can see resolvable to the RUNNING GJS
|
|
2
2
|
// process, with nothing exported into the environment beforehand. ADR 0021.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
// from a bare `gjs -m`)
|
|
6
|
-
// differently:
|
|
4
|
+
// There are TWO lookups, not one, and they fail differently (measured on gjs
|
|
5
|
+
// 1.88.1 / GLib 2.88.3, importing `gi://GjsifyRolldown` from a bare `gjs -m`):
|
|
7
6
|
//
|
|
8
7
|
// neither var "Typelib file for namespace 'GjsifyRolldown' (any
|
|
9
8
|
// version) not found"
|
|
@@ -12,30 +11,22 @@
|
|
|
12
11
|
// "Unsupported type void, deriving from fundamental void"
|
|
13
12
|
// both loads
|
|
14
13
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// is an OS property and is why the `gjsify` launcher still exists for user
|
|
20
|
-
// bundles.
|
|
21
|
-
//
|
|
22
|
-
// It is the wrong frame, because girepository delegates NEITHER lookup to the
|
|
23
|
-
// loader's environment. It keeps a typelib search path AND a library search
|
|
24
|
-
// path of its own, consults both before falling back to the system loader, and
|
|
25
|
-
// both are writable at runtime. `prepend_library_path()` is the half nobody
|
|
26
|
-
// went looking for: `LD_LIBRARY_PATH` was assumed to BE the mechanism when it
|
|
27
|
-
// is only girepository's fallback — which is exactly what the middle row above
|
|
14
|
+
// girepository delegates NEITHER lookup to the loader's environment: it keeps a
|
|
15
|
+
// typelib search path AND a library search path of its own, consults both before
|
|
16
|
+
// falling back to the system loader, and both are writable at runtime.
|
|
17
|
+
// `LD_LIBRARY_PATH` is only girepository's fallback — which the middle row above
|
|
28
18
|
// proves, since girepository's own library path starts empty and no environment
|
|
29
|
-
// variable seeds it.
|
|
19
|
+
// variable seeds it. That variable genuinely cannot be changed in-process (ld.so
|
|
20
|
+
// freezes its search path at startup), which is why the `gjsify` launcher still
|
|
21
|
+
// exists for user bundles — but it is not this lookup's mechanism.
|
|
30
22
|
//
|
|
31
|
-
// NOT an rpath problem, and the rpath is not the fix. `libgjsifyrolldown.so`
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// the callee is never consulted to find the callee, so no artifact changes.
|
|
23
|
+
// NOT an rpath problem, and the rpath is not the fix. `libgjsifyrolldown.so` already
|
|
24
|
+
// carries `RUNPATH=$ORIGIN` (every bridge's `meson.build` sets it so the Vala library
|
|
25
|
+
// finds the Rust cdylib beside it). That structurally cannot help gjs FIND
|
|
26
|
+
// `libgjsifyrolldown.so` itself: the typelib names it by bare SONAME, and `dlopen()`
|
|
27
|
+
// of a bare name searches the RPATH/RUNPATH of the CALLING object — libgirepository,
|
|
28
|
+
// whose `$ORIGIN` is /usr/lib64. An rpath on the callee is never consulted to find
|
|
29
|
+
// the callee, so no artifact changes.
|
|
39
30
|
import { dirname } from 'node:path';
|
|
40
31
|
import { fileURLToPath } from 'node:url';
|
|
41
32
|
import { detectNativePackages } from './detect-native-packages.js';
|
|
@@ -43,18 +34,15 @@ import { detectNativePackages } from './detect-native-packages.js';
|
|
|
43
34
|
* girepository's process-global default repository — the one GJS's own `gi://`
|
|
44
35
|
* importer resolves against — or `null` when there is none to write to.
|
|
45
36
|
*
|
|
46
|
-
* `globalThis.imports?.gi` is both the GJS probe
|
|
47
|
-
*
|
|
48
|
-
* is
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* never wants a prebuild.
|
|
37
|
+
* `globalThis.imports?.gi` is both the sanctioned GJS probe spelling
|
|
38
|
+
* (`.oxlintrc.json`) and the correct CONDITION, so no `isGjs()` beside it: the
|
|
39
|
+
* question is "is there a GIRepository to prepend to", and a Node host answers
|
|
40
|
+
* no by itself — the required no-op, since on Node the CLI uses the npm
|
|
41
|
+
* `rolldown` crate and never wants a prebuild.
|
|
52
42
|
*
|
|
53
|
-
* `dup_default()` is GLib >= 2.86
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* environment, or an unchanged diagnosis), so the change cannot make any host
|
|
57
|
-
* worse than it is.
|
|
43
|
+
* `dup_default()` is GLib >= 2.86, probed as a capability rather than caught:
|
|
44
|
+
* an older GLib simply keeps today's behaviour (the launcher's environment, or
|
|
45
|
+
* an unchanged diagnosis), so no host can be made worse.
|
|
58
46
|
*/
|
|
59
47
|
function defaultRepository() {
|
|
60
48
|
const gi = globalThis.imports?.gi;
|
|
@@ -68,32 +56,24 @@ let activated = null;
|
|
|
68
56
|
* Put every detected prebuild directory on girepository's typelib AND library
|
|
69
57
|
* search paths. Idempotent — the first call decides, later ones return it.
|
|
70
58
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* `
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* engines to maintain, and a bridge added later is covered without touching
|
|
77
|
-
* this file. Today it covers all three the CLI loads in-process —
|
|
78
|
-
* `rolldown-native`, `lightningcss-native` (via the `css-as-string` plugin) and
|
|
79
|
-
* `oxfmt-native`.
|
|
59
|
+
* The launcher's environment applied to the already-running process: it feeds on
|
|
60
|
+
* the same `detectNativePackages()` whose output the `gjsify` bin turns into
|
|
61
|
+
* `GI_TYPELIB_PATH` + the host library-path variable (`buildNativeEnv`), so the two
|
|
62
|
+
* mechanisms cannot resolve different sets and a bridge added later needs no change
|
|
63
|
+
* here.
|
|
80
64
|
*
|
|
81
|
-
* TWO ANCHORS, merged first-wins
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* into `dist/cli.gjs.mjs`, so `import.meta.url` is the bundle's own URL, which
|
|
87
|
-
* is precisely the "where does the CLI live" question being asked.
|
|
65
|
+
* TWO ANCHORS, merged first-wins as `computeNativeEnvForBundle()` does: the cwd, and
|
|
66
|
+
* this module's own directory. The second is not redundant — with a GLOBAL CLI the
|
|
67
|
+
* engine sits next to the bundle while the cwd is an unrelated project (the case
|
|
68
|
+
* `tryLoadNative()` needs its `bundleUrl` anchor for), and after bundling
|
|
69
|
+
* `import.meta.url` is the bundle's own URL.
|
|
88
70
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* watching another mechanism is the shape `docs/governance.md` § simplicity
|
|
96
|
-
* warns about.
|
|
71
|
+
* Callers are memoized, so the node_modules sweep (~65 ms per anchor on this
|
|
72
|
+
* 960-workspace tree) is paid at most once per process, by a command that wants an
|
|
73
|
+
* engine. Deliberately NOT skipped when the launcher already exported the same
|
|
74
|
+
* directories: that check can be wrong, prepending an already-resolving directory
|
|
75
|
+
* changes nothing, and a guard watching another mechanism is the shape
|
|
76
|
+
* `docs/governance.md` § simplicity warns about.
|
|
97
77
|
*
|
|
98
78
|
* @returns the packages activated — empty when there is no repository to write
|
|
99
79
|
* to (Node, or GLib < 2.86), which every caller reads as "carry on".
|
|
@@ -101,10 +81,9 @@ let activated = null;
|
|
|
101
81
|
export function activateNativePrebuilds() {
|
|
102
82
|
if (activated)
|
|
103
83
|
return activated;
|
|
104
|
-
// Memoized BEFORE the work, so every outcome — including a throw — is
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// that nothing it calls may throw while explaining a failure.
|
|
84
|
+
// Memoized BEFORE the work, so every outcome — including a throw — is decided
|
|
85
|
+
// once. `diagnoseNativeEngine()` depends on that: nothing it calls may throw
|
|
86
|
+
// while explaining a failure.
|
|
108
87
|
activated = [];
|
|
109
88
|
try {
|
|
110
89
|
const repository = defaultRepository();
|
|
@@ -121,26 +100,19 @@ export function activateNativePrebuilds() {
|
|
|
121
100
|
activated = packages;
|
|
122
101
|
}
|
|
123
102
|
catch {
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// "Requiring GIRepository, version none: Typelib file for namespace
|
|
129
|
-
// 'GIRepository' (any version) not found" when that typelib is
|
|
130
|
-
// absent — verified by probe. It is a SEPARATE FILE from the
|
|
131
|
-
// girepository library GJS links against, and distributions split
|
|
132
|
-
// them (Debian ships `gir1.2-girepository-3.0` apart from
|
|
103
|
+
// Two real throw paths:
|
|
104
|
+
// 1. `imports.gi.GIRepository` LOADS the namespace, and that typelib is
|
|
105
|
+
// a SEPARATE FILE from the girepository library GJS links against —
|
|
106
|
+
// distributions split them (Debian's `gir1.2-girepository-3.0` vs
|
|
133
107
|
// `libgirepository-2.0-0`), so a lean host can have the second
|
|
134
|
-
// without the first
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// mid-walk).
|
|
108
|
+
// without the first and GJS raises "Typelib file for namespace
|
|
109
|
+
// 'GIRepository' (any version) not found".
|
|
110
|
+
// 2. The two `detectNativePackages()` walks are filesystem I/O (EACCES
|
|
111
|
+
// on an unreadable node_modules, a dir removed mid-walk).
|
|
138
112
|
//
|
|
139
|
-
// Both mean
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// environment, and the unchanged diagnosis if that is absent too), so
|
|
143
|
-
// no host can be made worse by it.
|
|
113
|
+
// Both mean no activation, and swallowing leaves the caller exactly where
|
|
114
|
+
// it was before this function existed (the launcher's environment, or the
|
|
115
|
+
// unchanged diagnosis), so no host can be made worse by it.
|
|
144
116
|
}
|
|
145
117
|
return activated;
|
|
146
118
|
}
|
|
@@ -8,14 +8,11 @@ export interface GuardedChunk {
|
|
|
8
8
|
/**
|
|
9
9
|
* Bare `node:` specifiers a `--app gjs` bundle may keep as static imports.
|
|
10
10
|
*
|
|
11
|
-
* Deliberately EMPTY
|
|
12
|
-
*
|
|
13
|
-
* module graph and loads under GJS. Reading the graph instead of the text is
|
|
14
|
-
* what keeps this list empty — the entries an earlier text-scanning version
|
|
15
|
-
* needed (`node:module`, `node:process`) were quoted lookalikes, never imports.
|
|
11
|
+
* Deliberately EMPTY: every Node builtin has an `@gjsify/*` polyfill that `--app gjs`
|
|
12
|
+
* aliases to, so no specifier both survives into the module graph and loads under GJS.
|
|
16
13
|
*
|
|
17
|
-
* Add an entry ONLY with a comment naming why GJS can resolve it. Each one is a
|
|
18
|
-
*
|
|
14
|
+
* Add an entry ONLY with a comment naming why GJS can resolve it. Each one is a specifier
|
|
15
|
+
* this guard can no longer catch for real.
|
|
19
16
|
*/
|
|
20
17
|
export declare const GJS_ALLOWED_NODE_IMPORTS: readonly string[];
|
|
21
18
|
/**
|
|
@@ -23,10 +20,5 @@ export declare const GJS_ALLOWED_NODE_IMPORTS: readonly string[];
|
|
|
23
20
|
* allowlist. Sorted, so the error message and any test are stable.
|
|
24
21
|
*/
|
|
25
22
|
export declare function findDisallowedNodeImports(chunks: readonly GuardedChunk[], allowed?: readonly string[]): string[];
|
|
26
|
-
/**
|
|
27
|
-
* Throw when a built `--app gjs` bundle statically imports Node builtins.
|
|
28
|
-
*
|
|
29
|
-
* @param chunks the emitted chunks, as reported by the bundler
|
|
30
|
-
* @param outfile path of the bundle, for the error message
|
|
31
|
-
*/
|
|
23
|
+
/** Throw when a built `--app gjs` bundle statically imports Node builtins. */
|
|
32
24
|
export declare function assertGjsBundleLoadable(chunks: readonly GuardedChunk[], outfile: string): void;
|
|
@@ -1,67 +1,50 @@
|
|
|
1
1
|
// Build-time guard: a `--app gjs` bundle must not keep bare `node:` imports.
|
|
2
2
|
//
|
|
3
|
-
// Stock GJS has NO `node:` module loader. A single surviving
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// Stock GJS has NO `node:` module loader. A single surviving `import … from "node:fs"`
|
|
4
|
+
// makes it abort the ENTIRE module graph before one line executes, with
|
|
5
|
+
// "ImportError: Unsupported URI scheme for importing: node".
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// `--app gjs` maps every Node builtin to its `@gjsify/*` polyfill, so a healthy bundle has
|
|
8
|
+
// ZERO of them. But when an alias TARGET cannot be resolved — classically a workspace
|
|
9
|
+
// checkout whose `@gjsify/*` packages have no built `lib/`, so `exports["."]` points at a
|
|
10
|
+
// file that does not exist — the bundler falls back to external and re-emits the ORIGINAL
|
|
11
|
+
// `node:*` specifier. That is a WARNING, not an error: the build "succeeds" and writes a
|
|
12
|
+
// bundle that cannot load anywhere. It cost three red CI jobs to notice, because nothing
|
|
13
|
+
// between `gjsify build` and CI ever executes the emitted GJS bundle (tsc, lint, format and
|
|
14
|
+
// the unit/e2e suites all run the Node entry). The pre-commit hook makes it worse rather
|
|
15
|
+
// than better: it rebuilds and AUTO-STAGES `dist/affected.gjs.mjs`, built through this same
|
|
16
|
+
// `--app gjs` path, so a developer whose checkout cannot resolve the polyfills commits a
|
|
17
|
+
// broken artifact without ever seeing it.
|
|
9
18
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// bundler falls back to treating the import as external and re-emits the
|
|
15
|
-
// ORIGINAL `node:*` specifier. That is a WARNING, not an error: the build
|
|
16
|
-
// "succeeds" and writes a bundle that cannot load anywhere. It cost three red
|
|
17
|
-
// CI jobs to notice, because nothing between `gjsify build` and CI ever
|
|
18
|
-
// executes the emitted GJS bundle (tsc, lint, format and the unit/e2e suites
|
|
19
|
-
// all run the Node entry).
|
|
20
|
-
//
|
|
21
|
-
// The pre-commit hook makes it worse rather than better: it rebuilds and
|
|
22
|
-
// AUTO-STAGES `dist/cli.gjs.mjs`, so a developer whose checkout cannot resolve
|
|
23
|
-
// the polyfills commits a broken artifact without ever seeing it.
|
|
24
|
-
//
|
|
25
|
-
// So the refusal belongs at build time, in `gjsify build --app gjs` itself —
|
|
26
|
-
// not in a git hook and not in one package's `build:gjs-bundle` script. The
|
|
27
|
-
// invariant is a property of the ARTIFACT, and every showcase, every downstream
|
|
28
|
-
// app (buchhaltung, bauplaner, Learn6502) and CI all produce it through this
|
|
29
|
-
// one code path. A hook would protect only commits in this repo; a per-package
|
|
30
|
-
// script would protect one package.
|
|
19
|
+
// So the refusal belongs at build time, in `gjsify build --app gjs` itself — not in a git
|
|
20
|
+
// hook and not in one package's `build:gjs-bundle` script. The invariant is a property of
|
|
21
|
+
// the ARTIFACT, and every showcase, every downstream app (buchhaltung, bauplaner,
|
|
22
|
+
// Learn6502) and CI all produce it through this one code path.
|
|
31
23
|
//
|
|
32
24
|
// ## The oracle is the module graph, NOT the emitted text
|
|
33
25
|
//
|
|
34
|
-
// The first cut
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// so the phrase appears verbatim inside a STRING, in a bundle that loads and
|
|
40
|
-
// runs under GJS perfectly well. The same trap sits in `@gjsify/cli`'s own
|
|
26
|
+
// The first cut grepped the output for `from "node:…"`, and CI caught it:
|
|
27
|
+
// `@gjsify/process`'s own test bundle names a case `named-import hrtime (from
|
|
28
|
+
// "node:process") preserves .bigint`, so the phrase appears verbatim inside a STRING in a
|
|
29
|
+
// bundle that runs under GJS perfectly well. The same trap sits in `@gjsify/cli`'s own
|
|
41
30
|
// bundle, which embeds `giNodeShimSource()`'s `--app node` codegen template
|
|
42
|
-
// (`import { createRequire } from 'node:module';`). Telling a statement from a
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// `@gjsify/rolldown-native` forwards it through `synthRolldownOutput`), and
|
|
47
|
-
// free.
|
|
31
|
+
// (`import { createRequire } from 'node:module';`). Telling a statement from a quoted
|
|
32
|
+
// lookalike needs a JS lexer — but the bundler already did that work: every chunk carries
|
|
33
|
+
// the external specifiers it actually imports, exact and engine-independent (npm `rolldown`
|
|
34
|
+
// returns it directly, `@gjsify/rolldown-native` forwards it via `synthRolldownOutput`).
|
|
48
35
|
//
|
|
49
|
-
// Scope is STATIC imports, which is
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// for.
|
|
36
|
+
// Scope is STATIC imports, which is the failure mode above: they are resolved when GJS
|
|
37
|
+
// links the module, so one bad specifier kills the whole bundle. A dynamic
|
|
38
|
+
// `import('node:x')` throws lazily at its call site — a real bug, but not "the artifact
|
|
39
|
+
// cannot load", and not what this gate is for.
|
|
54
40
|
/**
|
|
55
41
|
* Bare `node:` specifiers a `--app gjs` bundle may keep as static imports.
|
|
56
42
|
*
|
|
57
|
-
* Deliberately EMPTY
|
|
58
|
-
*
|
|
59
|
-
* module graph and loads under GJS. Reading the graph instead of the text is
|
|
60
|
-
* what keeps this list empty — the entries an earlier text-scanning version
|
|
61
|
-
* needed (`node:module`, `node:process`) were quoted lookalikes, never imports.
|
|
43
|
+
* Deliberately EMPTY: every Node builtin has an `@gjsify/*` polyfill that `--app gjs`
|
|
44
|
+
* aliases to, so no specifier both survives into the module graph and loads under GJS.
|
|
62
45
|
*
|
|
63
|
-
* Add an entry ONLY with a comment naming why GJS can resolve it. Each one is a
|
|
64
|
-
*
|
|
46
|
+
* Add an entry ONLY with a comment naming why GJS can resolve it. Each one is a specifier
|
|
47
|
+
* this guard can no longer catch for real.
|
|
65
48
|
*/
|
|
66
49
|
export const GJS_ALLOWED_NODE_IMPORTS = [];
|
|
67
50
|
/**
|
|
@@ -81,12 +64,7 @@ export function findDisallowedNodeImports(chunks, allowed = GJS_ALLOWED_NODE_IMP
|
|
|
81
64
|
}
|
|
82
65
|
return [...found].sort();
|
|
83
66
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Throw when a built `--app gjs` bundle statically imports Node builtins.
|
|
86
|
-
*
|
|
87
|
-
* @param chunks the emitted chunks, as reported by the bundler
|
|
88
|
-
* @param outfile path of the bundle, for the error message
|
|
89
|
-
*/
|
|
67
|
+
/** Throw when a built `--app gjs` bundle statically imports Node builtins. */
|
|
90
68
|
export function assertGjsBundleLoadable(chunks, outfile) {
|
|
91
69
|
const offenders = findDisallowedNodeImports(chunks);
|
|
92
70
|
if (offenders.length === 0)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Replace every raw U+0000 in `code` with the `\x00` escape.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Reports how many were replaced so the caller can skip writing the file (and reporting)
|
|
5
|
+
* in the overwhelmingly common zero case.
|
|
6
6
|
*/
|
|
7
7
|
export declare function escapeRawNulForGjs(code: string): {
|
|
8
8
|
code: string;
|
|
@@ -1,49 +1,31 @@
|
|
|
1
1
|
// Build-time rewrite: a `--app gjs` bundle must not contain a raw U+0000 byte.
|
|
2
2
|
//
|
|
3
|
-
// GJS hands module source to SpiderMonkey as a NUL-TERMINATED C string, so a raw
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// so the same code builds and runs unminified and breaks under `--minify`, which is
|
|
20
|
-
// the default. A real consumer hit exactly this with an IMAP literal marker — NUL
|
|
21
|
-
// being the one byte that cannot occur in IMAP response text is precisely why it
|
|
22
|
-
// was chosen as the marker.
|
|
23
|
-
//
|
|
24
|
-
// Node and browsers load a raw NUL in source without complaint (U+0000 is a legal
|
|
25
|
-
// source character per the spec), so no other target notices and nothing upstream
|
|
26
|
-
// is going to change. Escaping it on the way out is gjsify's job.
|
|
27
|
-
//
|
|
28
|
-
// The rewrite is safe as a plain text substitution: in valid JS a raw NUL can only
|
|
29
|
-
// appear inside a string, a template literal, a regex or a comment, and `\x00`
|
|
30
|
-
// denotes exactly U+0000 in all four — so the meaning is preserved wherever it sits.
|
|
31
|
-
//
|
|
32
|
-
// `\x00` and NOT `\0`: `\0` followed by a digit is a legacy octal escape, which is a
|
|
33
|
-
// SyntaxError in a template literal and under strict mode. A digit right after the
|
|
34
|
-
// marker is exactly the shape that triggered this (`\0${index}\0`), so `\0` would
|
|
35
|
-
// trade one unloadable bundle for another.
|
|
3
|
+
// GJS hands module source to SpiderMonkey as a NUL-TERMINATED C string, so a raw U+0000
|
|
4
|
+
// truncates the file there. Everything after is silently discarded and the error names
|
|
5
|
+
// whatever construct happened to be open when the text ran out — "SyntaxError: `` literal
|
|
6
|
+
// not terminated before end of script" — never the NUL.
|
|
7
|
+
//
|
|
8
|
+
// Easy to hit by accident: `'\u0000'` in TypeScript source is a six-character ESCAPE, but
|
|
9
|
+
// the minifier may inline the constant and emit the CHARACTER, so the same code runs
|
|
10
|
+
// unminified and breaks under `--minify` (the default). A consumer hit this with an IMAP
|
|
11
|
+
// literal marker — NUL cannot occur in IMAP response text, which is why it was the marker.
|
|
12
|
+
// Node and browsers load a raw NUL in source per spec, so no other target notices and
|
|
13
|
+
// nothing upstream will change; escaping it on the way out is gjsify's job.
|
|
14
|
+
//
|
|
15
|
+
// Safe as a plain text substitution: in valid JS a raw NUL can only sit inside a string, a
|
|
16
|
+
// template literal, a regex or a comment, and `\x00` denotes U+0000 in all four. NOT `\0`,
|
|
17
|
+
// which is a legacy octal escape when followed by a digit — a SyntaxError in a template
|
|
18
|
+
// literal and under strict mode, and `\0${index}\0` is exactly the shape that triggered it.
|
|
36
19
|
/**
|
|
37
20
|
* Replace every raw U+0000 in `code` with the `\x00` escape.
|
|
38
21
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
22
|
+
* Reports how many were replaced so the caller can skip writing the file (and reporting)
|
|
23
|
+
* in the overwhelmingly common zero case.
|
|
41
24
|
*/
|
|
42
25
|
export function escapeRawNulForGjs(code) {
|
|
43
|
-
// split/join rather than a regex: a NUL inside a regex is
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// byte it exists to remove.
|
|
26
|
+
// split/join rather than a regex: a NUL inside a regex is what oxlint's `no-control-regex`
|
|
27
|
+
// exists to flag, and the rule is right in general even though this use is deliberate.
|
|
28
|
+
// fromCharCode also keeps THIS file free of the raw byte it exists to remove.
|
|
47
29
|
const parts = code.split(String.fromCharCode(0));
|
|
48
30
|
return { code: parts.join('\\x00'), replaced: parts.length - 1 };
|
|
49
31
|
}
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* a self-shim rather than through the workspace's `node_modules/.bin/gjsify`.
|
|
4
4
|
*
|
|
5
5
|
* `detectPackageManager()` reads this: a bootstrap CLI must run package scripts
|
|
6
|
-
* ITSELF, because npm re-prepends `node_modules/.bin` for every lifecycle
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* ITSELF, because npm re-prepends `node_modules/.bin` for every lifecycle script it
|
|
7
|
+
* starts — putting the tree's dead shim back ahead of this module's shim dir and
|
|
8
|
+
* undoing the repair one level down.
|
|
9
9
|
*/
|
|
10
10
|
export declare function usingSelfShim(): boolean;
|
|
11
11
|
/**
|
|
12
|
-
* Does this invocation need its own `gjsify` on PATH, rather than the
|
|
13
|
-
*
|
|
12
|
+
* Does this invocation need its own `gjsify` on PATH, rather than the workspace's
|
|
13
|
+
* npm bin?
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Host facts are PARAMETERS rather than read ambiently — the shape
|
|
16
|
+
* `platform-check.ts` documents, and what lets the Windows branch be tested from
|
|
17
|
+
* a Linux host.
|
|
18
18
|
*
|
|
19
19
|
* @param gjs running under GJS (the npm bin is the Node entry there)
|
|
20
20
|
* @param selfEntry absolute path of the CLI entry currently executing
|
|
@@ -37,18 +37,15 @@ export declare function ensureGjsifyShimOnPath(): void;
|
|
|
37
37
|
*
|
|
38
38
|
* **It is NOT on `process.env.PATH`, and that is the whole point.** Only
|
|
39
39
|
* `runScript` puts it on the PATH of a PACKAGE SCRIPT's child (`commands/run.ts`).
|
|
40
|
+
* Prepending it globally breaks the CLI's ability to SEE a missing Node: `gjsify
|
|
41
|
+
* tsc`'s Node fallback spawns a real `node` for typescript's CJS entry, and
|
|
42
|
+
* `tests/e2e/tsc-node-fallback` asserts that on a PATH without one it fails loudly
|
|
43
|
+
* naming the missing interpreter — an assertion that exists because a silent exit
|
|
44
|
+
* 0 there once let `set-bin-mode.mjs` run against a `lib/index.js` tsc never wrote.
|
|
40
45
|
*
|
|
41
|
-
* The
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* silent exit 0 there once let `set-bin-mode.mjs` run against a `lib/index.js`
|
|
46
|
-
* tsc never wrote. A global shim answered that spawn, so the CLI stopped being
|
|
47
|
-
* able to see that Node was missing at all.
|
|
48
|
-
*
|
|
49
|
-
* The line the subdirectory draws: a PACKAGE SCRIPT saying `node x.mjs` means
|
|
50
|
-
* "run this script", and gjsify can serve that. The CLI's own internals asking
|
|
51
|
-
* for `node` mean "I need a real Node", and lying to them turns an honest
|
|
52
|
-
* diagnosis into a confusing one two layers down.
|
|
46
|
+
* The line the subdirectory draws: a PACKAGE SCRIPT saying `node x.mjs` means "run
|
|
47
|
+
* this script", which gjsify can serve; the CLI's own internals asking for `node`
|
|
48
|
+
* mean "I need a real Node", and lying to them turns an honest diagnosis into a
|
|
49
|
+
* confusing one two layers down.
|
|
53
50
|
*/
|
|
54
51
|
export declare function nodeShimDir(): string | null;
|