@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
package/lib/utils/gjsify-shim.js
CHANGED
|
@@ -1,64 +1,44 @@
|
|
|
1
1
|
// A runnable `gjsify` on PATH, for the two trees where the npm bin is not one.
|
|
2
2
|
//
|
|
3
|
-
// The CLI's npm bin (`node_modules/.bin/gjsify`) is a DISPATCHER written at
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// searches PATH.
|
|
11
|
-
// - a compound script like `gjsify run a && gjsify run b` is executed via
|
|
12
|
-
// `/bin/sh -c …` (or `%COMSPEC% /c …`), and the shell resolves each
|
|
13
|
-
// `gjsify` from PATH.
|
|
3
|
+
// The CLI's npm bin (`node_modules/.bin/gjsify`) is a DISPATCHER written at install
|
|
4
|
+
// time: it prefers `packages/infra/cli/dist/cli.gjs.mjs` on a host with `gjs`, and
|
|
5
|
+
// otherwise execs the Node entry `packages/infra/cli/lib/index.js`. Orchestration
|
|
6
|
+
// goes through PATH to reach it — `gjsify workspace`/`foreach` spawn `<pm> run
|
|
7
|
+
// <script>` per package (under GJS the package manager is `gjsify` itself), and a
|
|
8
|
+
// compound script like `gjsify run a && gjsify run b` runs via `/bin/sh -c …`, whose
|
|
9
|
+
// shell resolves each `gjsify` from PATH.
|
|
14
10
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// actually exists.
|
|
11
|
+
// So: write a wrapper that re-invokes the RUNNING CLI and prepend its directory to
|
|
12
|
+
// `process.env.PATH`, so every child (and grandchild, via the inherited
|
|
13
|
+
// `GJSIFY_SHIM_DIR` marker) resolves `gjsify` to a CLI that exists.
|
|
19
14
|
//
|
|
20
15
|
// TWO HOSTS NEED IT, FOR TWO DIFFERENT REASONS
|
|
21
16
|
//
|
|
22
|
-
// GJS — always. The npm bin is the NODE entry
|
|
23
|
-
//
|
|
24
|
-
//
|
|
17
|
+
// GJS — always. The npm bin is the NODE entry, useless in a node-free environment
|
|
18
|
+
// (the Flatpak sandbox has no Node): the failures are `spawn npm ENOENT` and
|
|
19
|
+
// `gjsify: not found`.
|
|
25
20
|
//
|
|
26
|
-
// Node — only when the running CLI came from OUTSIDE the workspace,
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// nested `gjsify` — which for `gjsify run build:infra` is its first clause,
|
|
33
|
-
// `gjsify workspace @gjsify/vite-plugin-blueprint build`.
|
|
34
|
-
//
|
|
35
|
-
// THE INCIDENT, because a rule without it gets simplified back into the bug.
|
|
36
|
-
//
|
|
37
|
-
// `windows-suites.yml` (added #1021) and `macos-suites.yml` (#1022) both landed
|
|
38
|
-
// during the 2026-08-06 GitHub Actions degradation with NO checks reported on
|
|
39
|
-
// their PRs, and neither leg had ever been green. Both ordered the cold-tree
|
|
40
|
-
// bootstrap as `run build:infra` BEFORE `install`, which cannot work either:
|
|
41
|
-
// `@gjsify/vite-plugin-blueprint` declares `"types": ["node"]`, so with no
|
|
42
|
-
// `node_modules` its `gjsify tsc` fails `TS2688: Cannot find type definition
|
|
43
|
-
// file for 'node'`. Correcting the order to install-then-build then exposed
|
|
44
|
-
// THIS gap, one step later.
|
|
45
|
-
//
|
|
46
|
-
// Reproduced on Linux at de7f5525 in a cold worktree — nothing about it is
|
|
47
|
-
// Windows-specific:
|
|
21
|
+
// Node — only when the running CLI came from OUTSIDE the workspace, i.e. the
|
|
22
|
+
// cold-tree bootstrap (`npx @gjsify/cli@latest …`, or a global install). ADR 0002
|
|
23
|
+
// untracked BOTH bundles, so on an installed-but-unbuilt tree
|
|
24
|
+
// `node_modules/.bin/gjsify` exists and everything it dispatches to is missing: it
|
|
25
|
+
// resolves, then dies with `Cannot find module …/lib/index.js` on the FIRST nested
|
|
26
|
+
// `gjsify`. Reproducible on Linux in a cold worktree, nothing Windows-specific:
|
|
48
27
|
//
|
|
49
28
|
// npx --yes @gjsify/cli@latest install --immutable # ok
|
|
50
29
|
// npx --yes @gjsify/cli@latest run build:infra # Cannot find module …
|
|
51
30
|
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
31
|
+
// The bootstrap must therefore be ordered install-then-build, and the other order is
|
|
32
|
+
// not an alternative: `@gjsify/vite-plugin-blueprint` declares `"types": ["node"]`,
|
|
33
|
+
// so with no `node_modules` its `gjsify tsc` fails `TS2688: Cannot find type
|
|
34
|
+
// definition file for 'node'`. Linux CI never saw the gap because its bootstrap runs
|
|
35
|
+
// `gjs -m <published bundle> run build:infra` (`.github/actions/gjsify-setup`) and
|
|
36
|
+
// takes the GJS branch; Windows is the only leg with no `gjs` to fall back on.
|
|
56
37
|
//
|
|
57
|
-
// Scoped to an outside-the-workspace CLI ON PURPOSE: when the invocation
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// invocation would silently move in-repo nested builds off the GJS bundle.
|
|
38
|
+
// Scoped to an outside-the-workspace CLI ON PURPOSE: when the invocation already
|
|
39
|
+
// came through the workspace's own `node_modules/.bin/gjsify`, that shim IS the CLI
|
|
40
|
+
// the tree wants. Widening this to every Node invocation would silently move in-repo
|
|
41
|
+
// nested builds off the GJS bundle.
|
|
62
42
|
import { mkdtempSync, mkdirSync, writeFileSync, chmodSync, existsSync } from 'node:fs';
|
|
63
43
|
import { tmpdir } from 'node:os';
|
|
64
44
|
import { join, delimiter, relative, isAbsolute } from 'node:path';
|
|
@@ -74,20 +54,20 @@ let _selfShimActive = false;
|
|
|
74
54
|
* a self-shim rather than through the workspace's `node_modules/.bin/gjsify`.
|
|
75
55
|
*
|
|
76
56
|
* `detectPackageManager()` reads this: a bootstrap CLI must run package scripts
|
|
77
|
-
* ITSELF, because npm re-prepends `node_modules/.bin` for every lifecycle
|
|
78
|
-
*
|
|
79
|
-
*
|
|
57
|
+
* ITSELF, because npm re-prepends `node_modules/.bin` for every lifecycle script it
|
|
58
|
+
* starts — putting the tree's dead shim back ahead of this module's shim dir and
|
|
59
|
+
* undoing the repair one level down.
|
|
80
60
|
*/
|
|
81
61
|
export function usingSelfShim() {
|
|
82
62
|
return _selfShimActive;
|
|
83
63
|
}
|
|
84
64
|
/**
|
|
85
|
-
* Does this invocation need its own `gjsify` on PATH, rather than the
|
|
86
|
-
*
|
|
65
|
+
* Does this invocation need its own `gjsify` on PATH, rather than the workspace's
|
|
66
|
+
* npm bin?
|
|
87
67
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
68
|
+
* Host facts are PARAMETERS rather than read ambiently — the shape
|
|
69
|
+
* `platform-check.ts` documents, and what lets the Windows branch be tested from
|
|
70
|
+
* a Linux host.
|
|
91
71
|
*
|
|
92
72
|
* @param gjs running under GJS (the npm bin is the Node entry there)
|
|
93
73
|
* @param selfEntry absolute path of the CLI entry currently executing
|
|
@@ -96,9 +76,9 @@ export function usingSelfShim() {
|
|
|
96
76
|
export function needsSelfShim(opts) {
|
|
97
77
|
if (opts.gjs)
|
|
98
78
|
return true;
|
|
99
|
-
// Outside the workspace ⇒ a bootstrap CLI (npx / global install),
|
|
100
|
-
//
|
|
101
|
-
//
|
|
79
|
+
// Outside the workspace ⇒ a bootstrap CLI (npx / global install), whose tree's
|
|
80
|
+
// own `.bin/gjsify` may point at build outputs that do not exist yet. Inside ⇒
|
|
81
|
+
// the tree's CLI is the one running; leave PATH alone.
|
|
102
82
|
const rel = relative(opts.workspaceRoot, opts.selfEntry);
|
|
103
83
|
return rel === '' || rel.startsWith('..') || isAbsolute(rel);
|
|
104
84
|
}
|
|
@@ -111,9 +91,9 @@ export function ensureGjsifyShimOnPath() {
|
|
|
111
91
|
if (_ensured)
|
|
112
92
|
return;
|
|
113
93
|
_ensured = true;
|
|
114
|
-
// A parent gjsify already created the shim — reuse it so nested
|
|
115
|
-
//
|
|
116
|
-
//
|
|
94
|
+
// A parent gjsify already created the shim — reuse it so nested orchestration
|
|
95
|
+
// doesn't pile up temp dirs / PATH entries. The extension-less member is
|
|
96
|
+
// written on every host, so it stays the probe.
|
|
117
97
|
const inherited = process.env.GJSIFY_SHIM_DIR;
|
|
118
98
|
if (inherited && existsSync(join(inherited, 'gjsify'))) {
|
|
119
99
|
_selfShimActive = true;
|
|
@@ -123,33 +103,27 @@ export function ensureGjsifyShimOnPath() {
|
|
|
123
103
|
return;
|
|
124
104
|
}
|
|
125
105
|
const gjs = isGjs();
|
|
126
|
-
// Under GJS
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// `lib/index.js` (package.json `bin`), one hop up.
|
|
106
|
+
// Under GJS the whole CLI collapses into one `cli.gjs.mjs` bundle, so
|
|
107
|
+
// `import.meta.url` IS the entry. Under Node this module is
|
|
108
|
+
// `lib/utils/gjsify-shim.js` and the npm bin is `lib/index.js`, one hop up.
|
|
130
109
|
const selfEntry = gjs ? fileURLToPath(import.meta.url) : fileURLToPath(new URL('../index.js', import.meta.url));
|
|
131
110
|
const workspaceRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
132
111
|
if (!needsSelfShim({ gjs, selfEntry, workspaceRoot }))
|
|
133
112
|
return;
|
|
134
113
|
const dir = mkdtempSync(join(tmpdir(), 'gjsify-shim-'));
|
|
135
114
|
const shim = join(dir, 'gjsify');
|
|
136
|
-
// The interpreter is
|
|
137
|
-
// `
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// quote an interpreter argument (`<interpreter> "<target>" %*`), so a path
|
|
141
|
-
// like `C:\Program Files\nodejs\node.exe` would split, and a trio whose
|
|
142
|
-
// members disagree about which interpreter they re-enter is worse than one
|
|
143
|
-
// name resolved consistently.
|
|
115
|
+
// The interpreter is NAMED, not pathed, matching `buildShLauncher` and
|
|
116
|
+
// `buildLauncherShims`, so a missing one fails the same way on every OS. Not
|
|
117
|
+
// `process.execPath`: the batch member cannot quote an interpreter argument
|
|
118
|
+
// (`<interpreter> "<target>" %*`), so `C:\Program Files\nodejs\node.exe` splits.
|
|
144
119
|
const interpreter = gjs ? process.env.GJS_CONSOLE || 'gjs' : 'node';
|
|
145
120
|
const interpreterArgs = gjs ? ['-m'] : [];
|
|
146
121
|
const argv = interpreterArgs.length > 0 ? `${interpreterArgs.join(' ')} ` : '';
|
|
147
122
|
writeFileSync(shim, `#!/bin/sh\nexec "${interpreter}" ${argv}"${selfEntry}" "$@"\n`, { mode: 0o755 });
|
|
148
123
|
chmodSync(shim, 0o755);
|
|
149
|
-
// cmd.exe and pwsh cannot run the extension-less member:
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
// reviewed port of it — including the IF/ELSE ERRORLEVEL handling that
|
|
124
|
+
// cmd.exe and pwsh cannot run the extension-less member: not on PATHEXT, and
|
|
125
|
+
// Windows has no shebang handling. `buildLauncherShims` ports npm's
|
|
126
|
+
// three-sibling answer, including the IF/ELSE ERRORLEVEL handling that
|
|
153
127
|
// `&&`/`||` chaining gets wrong.
|
|
154
128
|
if (!gjs && process.platform === 'win32') {
|
|
155
129
|
const { cmd, ps1 } = buildLauncherShims({ interpreter, interpreterArgs, target: selfEntry });
|
|
@@ -161,10 +135,7 @@ export function ensureGjsifyShimOnPath() {
|
|
|
161
135
|
process.env.PATH = dir + delimiter + (process.env.PATH ?? '');
|
|
162
136
|
writeNodeShim(dir, gjs, selfEntry);
|
|
163
137
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Subdirectory holding the `node` shim. Deliberately NOT the shim dir itself —
|
|
166
|
-
* see {@link nodeShimDir}.
|
|
167
|
-
*/
|
|
138
|
+
/** Deliberately NOT the shim dir itself — see {@link nodeShimDir}. */
|
|
168
139
|
const NODE_SHIM_SUBDIR = 'node-shim';
|
|
169
140
|
/**
|
|
170
141
|
* Directory carrying a `node` that runs the file through
|
|
@@ -172,19 +143,16 @@ const NODE_SHIM_SUBDIR = 'node-shim';
|
|
|
172
143
|
*
|
|
173
144
|
* **It is NOT on `process.env.PATH`, and that is the whole point.** Only
|
|
174
145
|
* `runScript` puts it on the PATH of a PACKAGE SCRIPT's child (`commands/run.ts`).
|
|
146
|
+
* Prepending it globally breaks the CLI's ability to SEE a missing Node: `gjsify
|
|
147
|
+
* tsc`'s Node fallback spawns a real `node` for typescript's CJS entry, and
|
|
148
|
+
* `tests/e2e/tsc-node-fallback` asserts that on a PATH without one it fails loudly
|
|
149
|
+
* naming the missing interpreter — an assertion that exists because a silent exit
|
|
150
|
+
* 0 there once let `set-bin-mode.mjs` run against a `lib/index.js` tsc never wrote.
|
|
175
151
|
*
|
|
176
|
-
* The
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* silent exit 0 there once let `set-bin-mode.mjs` run against a `lib/index.js`
|
|
181
|
-
* tsc never wrote. A global shim answered that spawn, so the CLI stopped being
|
|
182
|
-
* able to see that Node was missing at all.
|
|
183
|
-
*
|
|
184
|
-
* The line the subdirectory draws: a PACKAGE SCRIPT saying `node x.mjs` means
|
|
185
|
-
* "run this script", and gjsify can serve that. The CLI's own internals asking
|
|
186
|
-
* for `node` mean "I need a real Node", and lying to them turns an honest
|
|
187
|
-
* diagnosis into a confusing one two layers down.
|
|
152
|
+
* The line the subdirectory draws: a PACKAGE SCRIPT saying `node x.mjs` means "run
|
|
153
|
+
* this script", which gjsify can serve; the CLI's own internals asking for `node`
|
|
154
|
+
* mean "I need a real Node", and lying to them turns an honest diagnosis into a
|
|
155
|
+
* confusing one two layers down.
|
|
188
156
|
*/
|
|
189
157
|
export function nodeShimDir() {
|
|
190
158
|
const dir = process.env.GJSIFY_SHIM_DIR;
|
|
@@ -197,28 +165,21 @@ export function nodeShimDir() {
|
|
|
197
165
|
* Write a `node` that runs the file through `gjsify run --node-script`, when the
|
|
198
166
|
* host is GJS and there is NO REAL `node`.
|
|
199
167
|
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* PREVIOUS RELEASE's CLI (`.github/workflows/release-cut.yml` documents the same
|
|
210
|
-
* trap for its own step). A manifest using a flag the last release does not know
|
|
211
|
-
* reds every cold-cache CI run until a release ships — and the release runs that
|
|
212
|
-
* same chain to build itself.
|
|
168
|
+
* A SHIM AND NOT A MANIFEST REWRITE. Every `node scripts/*.mjs` in this build chain
|
|
169
|
+
* imports only polyfilled `node:` builtins, so the sole blocker on a Node-less host
|
|
170
|
+
* is GJS's ESM loader being unable to resolve `node:` for a file on disk — which
|
|
171
|
+
* `--node-script` solves. Spelling that flag in `package.json` cannot work: CI
|
|
172
|
+
* bootstraps a cold tree with `gjs -m "$GJSIFY_BOOTSTRAP" run build:infra` where
|
|
173
|
+
* `$GJSIFY_BOOTSTRAP` is the PREVIOUS RELEASE's CLI
|
|
174
|
+
* (`.github/workflows/release-cut.yml` hits the same trap), so a manifest using a
|
|
175
|
+
* flag the last release does not know reds every cold-cache run until a release
|
|
176
|
+
* ships — and the release runs that chain to build itself.
|
|
213
177
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
* scripts (`gjsify tsc && node scripts/build-assets.mjs`) are executed through
|
|
218
|
-
* `/bin/sh`, which resolves `node` from PATH — as does anything those scripts
|
|
219
|
-
* spawn in turn.
|
|
178
|
+
* The shim leaves manifests saying `node scripts/x.mjs`, so an older CLI is
|
|
179
|
+
* unaffected, and covers what a per-script rewrite cannot: COMPOUND scripts go
|
|
180
|
+
* through `/bin/sh`, which resolves `node` from PATH, as does anything they spawn.
|
|
220
181
|
*
|
|
221
|
-
* Two things keep it from shadowing
|
|
182
|
+
* Two things keep it from shadowing a real Node: it is written only when `node`
|
|
222
183
|
* resolves NOWHERE, and it is reachable only from a package script's PATH (see
|
|
223
184
|
* {@link nodeShimDir}).
|
|
224
185
|
*/
|
|
@@ -231,13 +192,12 @@ function writeNodeShim(dir, gjs, selfEntry) {
|
|
|
231
192
|
if (resolveBinOnPath('node'))
|
|
232
193
|
return; // a real Node is present — leave it alone
|
|
233
194
|
const interpreter = process.env.GJS_CONSOLE || 'gjs';
|
|
234
|
-
// A leading FLAG is refused rather than forwarded. `node --test x.mjs` wants
|
|
235
|
-
// Node's own test runner and `node -e '…'` an eval; neither is something
|
|
236
|
-
// `--node-script` can honour, and yargs would silently take `--test` FOR the
|
|
237
|
-
// script path (`unknown-options-as-args` turns an unknown flag into a
|
|
238
|
-
// positional). A refusal naming the limit beats a mis-parse naming a file.
|
|
239
195
|
mkdirSync(sub, { recursive: true });
|
|
240
196
|
const shim = join(sub, 'node');
|
|
197
|
+
// A leading FLAG is refused rather than forwarded: `node --test x.mjs` wants
|
|
198
|
+
// Node's own test runner and `node -e '…'` an eval, neither of which
|
|
199
|
+
// `--node-script` can honour, and yargs would take `--test` FOR the script
|
|
200
|
+
// path (`unknown-options-as-args` turns an unknown flag into a positional).
|
|
241
201
|
writeFileSync(shim, '#!/bin/sh\n' +
|
|
242
202
|
'case "$1" in\n' +
|
|
243
203
|
' -*) echo "gjsify: this host has no node; the gjsify shim runs a SCRIPT FILE only" >&2\n' +
|
|
@@ -8,17 +8,14 @@ export interface ParsedHtmlEntry {
|
|
|
8
8
|
scriptSrc: string;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* like `src/**\/*.ts`) are never treated as html. The path is returned as
|
|
15
|
-
* authored (relative or absolute); the caller resolves + reads it.
|
|
11
|
+
* The `.html` path when `input` is a SINGLE html entry (a literal `.html` string, a 1-element
|
|
12
|
+
* array of one, or a 1-key record whose value is one) — else `null`. Multi-entry inputs and
|
|
13
|
+
* non-html entries are never treated as html. Returned as authored; the caller resolves + reads it.
|
|
16
14
|
*/
|
|
17
15
|
export declare function detectHtmlEntry(input: RolldownOptions['input'] | undefined): string | null;
|
|
18
16
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the HTML file's directory. Throws when no such tag exists.
|
|
17
|
+
* The FIRST `<script type="module" src="…">` whose `src` is a local (non-`http(s)://`, non-`//`)
|
|
18
|
+
* path, with the module resolved against the HTML file's directory. Throws when there is none.
|
|
22
19
|
*/
|
|
23
20
|
export declare function parseHtmlEntry(htmlPath: string, htmlSource: string): ParsedHtmlEntry;
|
|
24
21
|
export interface EmitBrowserHtmlInput {
|
|
@@ -34,10 +31,9 @@ export interface EmitBrowserHtmlInput {
|
|
|
34
31
|
outHtmlPath: string;
|
|
35
32
|
}
|
|
36
33
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* from). Everything else — `<title>`, meta tags, other markup — is preserved.
|
|
34
|
+
* The original source with the entry script's `src` rewritten to the built JS bundle, relative
|
|
35
|
+
* to the output HTML's own directory so the page is portable wherever `dist/` is served from.
|
|
36
|
+
* All other markup is preserved.
|
|
41
37
|
*/
|
|
42
38
|
export declare function emitBrowserHtml(input: EmitBrowserHtmlInput): string;
|
|
43
39
|
/** Convenience: the emitted HTML filename for a given JS outfile (sibling `index.html`). */
|
package/lib/utils/html-entry.js
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
// HTML-entry support for `gjsify build --app browser`.
|
|
2
2
|
//
|
|
3
|
-
// Vite
|
|
4
|
-
// `<script type="module" src="...">`,
|
|
5
|
-
// processed HTML
|
|
6
|
-
// only ever took a `.ts`/`.js` entry + `--outfile`, so browser apps hand-wrote
|
|
7
|
-
// their shell page (the `write-app-html.mjs` pattern). This util brings the
|
|
8
|
-
// Vite behaviour to the production Rolldown build.
|
|
3
|
+
// Brings Vite's HTML-entry behaviour to the production Rolldown build: treat `index.html` as
|
|
4
|
+
// the entry, find its `<script type="module" src="...">`, bundle that module, and emit a
|
|
5
|
+
// processed HTML whose script points at the built asset.
|
|
9
6
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// a string via css-as-string and is injected by the app — the unified pattern
|
|
17
|
-
// shared with the `--app gjs` target).
|
|
7
|
+
// Emission MUST run as a CLI post-bundle step (mirroring `applyShebang`), NOT as a Rolldown
|
|
8
|
+
// plugin `emitFile`: the native `@gjsify/rolldown-native` engine implements no `emitFile`, so a
|
|
9
|
+
// plugin-based emit would work on the npm engine (Node) and throw under the GJS-bundled CLI.
|
|
10
|
+
// Parsing is regex-based and tolerant on purpose — this is a shell-page rewrite, not a full HTML
|
|
11
|
+
// pipeline. CSS is NOT extracted here: it travels in the JS bundle as a string via
|
|
12
|
+
// css-as-string and is injected by the app, the same pattern the `--app gjs` target uses.
|
|
18
13
|
import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
19
14
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* like `src/**\/*.ts`) are never treated as html. The path is returned as
|
|
24
|
-
* authored (relative or absolute); the caller resolves + reads it.
|
|
15
|
+
* The `.html` path when `input` is a SINGLE html entry (a literal `.html` string, a 1-element
|
|
16
|
+
* array of one, or a 1-key record whose value is one) — else `null`. Multi-entry inputs and
|
|
17
|
+
* non-html entries are never treated as html. Returned as authored; the caller resolves + reads it.
|
|
25
18
|
*/
|
|
26
19
|
export function detectHtmlEntry(input) {
|
|
27
20
|
if (!input)
|
|
@@ -43,9 +36,8 @@ const SCRIPT_MODULE_RE = /<script\b[^>]*\btype\s*=\s*["']module["'][^>]*>(?:<\/s
|
|
|
43
36
|
const SRC_ATTR_RE = /\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
44
37
|
const EXTERNAL_SRC_RE = /^(?:https?:)?\/\//i;
|
|
45
38
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* the HTML file's directory. Throws when no such tag exists.
|
|
39
|
+
* The FIRST `<script type="module" src="…">` whose `src` is a local (non-`http(s)://`, non-`//`)
|
|
40
|
+
* path, with the module resolved against the HTML file's directory. Throws when there is none.
|
|
49
41
|
*/
|
|
50
42
|
export function parseHtmlEntry(htmlPath, htmlSource) {
|
|
51
43
|
for (const match of htmlSource.matchAll(SCRIPT_MODULE_RE)) {
|
|
@@ -63,18 +55,17 @@ export function parseHtmlEntry(htmlPath, htmlSource) {
|
|
|
63
55
|
`Add one pointing at your app entry (e.g. <script type="module" src="./main.ts">).`);
|
|
64
56
|
}
|
|
65
57
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* from). Everything else — `<title>`, meta tags, other markup — is preserved.
|
|
58
|
+
* The original source with the entry script's `src` rewritten to the built JS bundle, relative
|
|
59
|
+
* to the output HTML's own directory so the page is portable wherever `dist/` is served from.
|
|
60
|
+
* All other markup is preserved.
|
|
70
61
|
*/
|
|
71
62
|
export function emitBrowserHtml(input) {
|
|
72
63
|
const { htmlSource, scriptTag, scriptSrc, jsOutPath, outHtmlPath } = input;
|
|
73
64
|
let rel = relative(dirname(outHtmlPath), jsOutPath).split(/[\\/]/).join('/');
|
|
74
65
|
if (!rel.startsWith('.'))
|
|
75
66
|
rel = `./${rel}`;
|
|
76
|
-
// Rewrite
|
|
77
|
-
//
|
|
67
|
+
// Rewrite the src WITHIN the matched tag, then swap that tag back in — so no other `src=`
|
|
68
|
+
// in the document is touched.
|
|
78
69
|
const newTag = scriptTag.replace(SRC_ATTR_RE, (whole) => whole.replace(scriptSrc, rel));
|
|
79
70
|
return htmlSource.replace(scriptTag, newTag);
|
|
80
71
|
}
|