@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/gjsify-shim.js
CHANGED
|
@@ -1,70 +1,51 @@
|
|
|
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
|
-
|
|
62
|
-
import { mkdtempSync, writeFileSync, chmodSync, existsSync } from 'node:fs';
|
|
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.
|
|
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';
|
|
65
45
|
import { fileURLToPath } from 'node:url';
|
|
66
46
|
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
67
47
|
import { buildLauncherShims } from './bin-shim.js';
|
|
48
|
+
import { resolveBinOnPath } from './install-global.js';
|
|
68
49
|
import { findWorkspaceRoot } from './workspace-root.js';
|
|
69
50
|
let _ensured = false;
|
|
70
51
|
let _selfShimActive = false;
|
|
@@ -73,20 +54,20 @@ let _selfShimActive = false;
|
|
|
73
54
|
* a self-shim rather than through the workspace's `node_modules/.bin/gjsify`.
|
|
74
55
|
*
|
|
75
56
|
* `detectPackageManager()` reads this: a bootstrap CLI must run package scripts
|
|
76
|
-
* ITSELF, because npm re-prepends `node_modules/.bin` for every lifecycle
|
|
77
|
-
*
|
|
78
|
-
*
|
|
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.
|
|
79
60
|
*/
|
|
80
61
|
export function usingSelfShim() {
|
|
81
62
|
return _selfShimActive;
|
|
82
63
|
}
|
|
83
64
|
/**
|
|
84
|
-
* Does this invocation need its own `gjsify` on PATH, rather than the
|
|
85
|
-
*
|
|
65
|
+
* Does this invocation need its own `gjsify` on PATH, rather than the workspace's
|
|
66
|
+
* npm bin?
|
|
86
67
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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.
|
|
90
71
|
*
|
|
91
72
|
* @param gjs running under GJS (the npm bin is the Node entry there)
|
|
92
73
|
* @param selfEntry absolute path of the CLI entry currently executing
|
|
@@ -95,9 +76,9 @@ export function usingSelfShim() {
|
|
|
95
76
|
export function needsSelfShim(opts) {
|
|
96
77
|
if (opts.gjs)
|
|
97
78
|
return true;
|
|
98
|
-
// Outside the workspace ⇒ a bootstrap CLI (npx / global install),
|
|
99
|
-
//
|
|
100
|
-
//
|
|
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.
|
|
101
82
|
const rel = relative(opts.workspaceRoot, opts.selfEntry);
|
|
102
83
|
return rel === '' || rel.startsWith('..') || isAbsolute(rel);
|
|
103
84
|
}
|
|
@@ -110,9 +91,9 @@ export function ensureGjsifyShimOnPath() {
|
|
|
110
91
|
if (_ensured)
|
|
111
92
|
return;
|
|
112
93
|
_ensured = true;
|
|
113
|
-
// A parent gjsify already created the shim — reuse it so nested
|
|
114
|
-
//
|
|
115
|
-
//
|
|
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.
|
|
116
97
|
const inherited = process.env.GJSIFY_SHIM_DIR;
|
|
117
98
|
if (inherited && existsSync(join(inherited, 'gjsify'))) {
|
|
118
99
|
_selfShimActive = true;
|
|
@@ -122,33 +103,27 @@ export function ensureGjsifyShimOnPath() {
|
|
|
122
103
|
return;
|
|
123
104
|
}
|
|
124
105
|
const gjs = isGjs();
|
|
125
|
-
// Under GJS
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// `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.
|
|
129
109
|
const selfEntry = gjs ? fileURLToPath(import.meta.url) : fileURLToPath(new URL('../index.js', import.meta.url));
|
|
130
110
|
const workspaceRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
131
111
|
if (!needsSelfShim({ gjs, selfEntry, workspaceRoot }))
|
|
132
112
|
return;
|
|
133
113
|
const dir = mkdtempSync(join(tmpdir(), 'gjsify-shim-'));
|
|
134
114
|
const shim = join(dir, 'gjsify');
|
|
135
|
-
// The interpreter is
|
|
136
|
-
// `
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// quote an interpreter argument (`<interpreter> "<target>" %*`), so a path
|
|
140
|
-
// like `C:\Program Files\nodejs\node.exe` would split, and a trio whose
|
|
141
|
-
// members disagree about which interpreter they re-enter is worse than one
|
|
142
|
-
// 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.
|
|
143
119
|
const interpreter = gjs ? process.env.GJS_CONSOLE || 'gjs' : 'node';
|
|
144
120
|
const interpreterArgs = gjs ? ['-m'] : [];
|
|
145
121
|
const argv = interpreterArgs.length > 0 ? `${interpreterArgs.join(' ')} ` : '';
|
|
146
122
|
writeFileSync(shim, `#!/bin/sh\nexec "${interpreter}" ${argv}"${selfEntry}" "$@"\n`, { mode: 0o755 });
|
|
147
123
|
chmodSync(shim, 0o755);
|
|
148
|
-
// cmd.exe and pwsh cannot run the extension-less member:
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
// 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
|
|
152
127
|
// `&&`/`||` chaining gets wrong.
|
|
153
128
|
if (!gjs && process.platform === 'win32') {
|
|
154
129
|
const { cmd, ps1 } = buildLauncherShims({ interpreter, interpreterArgs, target: selfEntry });
|
|
@@ -158,4 +133,77 @@ export function ensureGjsifyShimOnPath() {
|
|
|
158
133
|
_selfShimActive = true;
|
|
159
134
|
process.env.GJSIFY_SHIM_DIR = dir;
|
|
160
135
|
process.env.PATH = dir + delimiter + (process.env.PATH ?? '');
|
|
136
|
+
writeNodeShim(dir, gjs, selfEntry);
|
|
137
|
+
}
|
|
138
|
+
/** Deliberately NOT the shim dir itself — see {@link nodeShimDir}. */
|
|
139
|
+
const NODE_SHIM_SUBDIR = 'node-shim';
|
|
140
|
+
/**
|
|
141
|
+
* Directory carrying a `node` that runs the file through
|
|
142
|
+
* `gjsify run --node-script`, or null when this host needs none.
|
|
143
|
+
*
|
|
144
|
+
* **It is NOT on `process.env.PATH`, and that is the whole point.** Only
|
|
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.
|
|
151
|
+
*
|
|
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.
|
|
156
|
+
*/
|
|
157
|
+
export function nodeShimDir() {
|
|
158
|
+
const dir = process.env.GJSIFY_SHIM_DIR;
|
|
159
|
+
if (!dir)
|
|
160
|
+
return null;
|
|
161
|
+
const sub = join(dir, NODE_SHIM_SUBDIR);
|
|
162
|
+
return existsSync(join(sub, 'node')) ? sub : null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Write a `node` that runs the file through `gjsify run --node-script`, when the
|
|
166
|
+
* host is GJS and there is NO REAL `node`.
|
|
167
|
+
*
|
|
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.
|
|
177
|
+
*
|
|
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.
|
|
181
|
+
*
|
|
182
|
+
* Two things keep it from shadowing a real Node: it is written only when `node`
|
|
183
|
+
* resolves NOWHERE, and it is reachable only from a package script's PATH (see
|
|
184
|
+
* {@link nodeShimDir}).
|
|
185
|
+
*/
|
|
186
|
+
function writeNodeShim(dir, gjs, selfEntry) {
|
|
187
|
+
if (!gjs)
|
|
188
|
+
return;
|
|
189
|
+
const sub = join(dir, NODE_SHIM_SUBDIR);
|
|
190
|
+
if (existsSync(join(sub, 'node')))
|
|
191
|
+
return; // inherited from a parent gjsify
|
|
192
|
+
if (resolveBinOnPath('node'))
|
|
193
|
+
return; // a real Node is present — leave it alone
|
|
194
|
+
const interpreter = process.env.GJS_CONSOLE || 'gjs';
|
|
195
|
+
mkdirSync(sub, { recursive: true });
|
|
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).
|
|
201
|
+
writeFileSync(shim, '#!/bin/sh\n' +
|
|
202
|
+
'case "$1" in\n' +
|
|
203
|
+
' -*) echo "gjsify: this host has no node; the gjsify shim runs a SCRIPT FILE only" >&2\n' +
|
|
204
|
+
' echo "gjsify: got: node $*" >&2\n' +
|
|
205
|
+
' exit 127 ;;\n' +
|
|
206
|
+
'esac\n' +
|
|
207
|
+
`exec "${interpreter}" -m "${selfEntry}" run --node-script "$@"\n`, { mode: 0o755 });
|
|
208
|
+
chmodSync(shim, 0o755);
|
|
161
209
|
}
|
|
@@ -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
|
}
|