@gjsify/cli 0.36.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 +3 -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/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/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 +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 +33 -54
- 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 +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 -18
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
// Guard against the recurring "library build leaks into src/" footgun.
|
|
2
2
|
//
|
|
3
|
-
// `BuildAction.buildLibrary` derives its
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// tree
|
|
9
|
-
// `src/_virtual/` helper dir, and a nested `src/<pkg>/src/**` preserve-modules
|
|
10
|
-
// tree. These pure helpers let the build action refuse that up front instead
|
|
11
|
-
// of silently corrupting the source tree.
|
|
3
|
+
// `BuildAction.buildLibrary` derives its outdir from `dirname(package.json "module" ?? "main")`.
|
|
4
|
+
// When a package points `main` at its runtime SOURCE entry (`src/index.ts` — the intended entry
|
|
5
|
+
// for Vite/gjsify-compiled consumers, e.g. `@gjsify/adwaita-web`), that outdir is `src/`, so
|
|
6
|
+
// `gjsify build --library` writes the compiled tree next to the sources: `src/*.js` / `*2.js`
|
|
7
|
+
// duplicates, a `src/_virtual/` helper dir, and a nested `src/<pkg>/src/**` preserve-modules
|
|
8
|
+
// tree. These pure helpers let the build action refuse that up front.
|
|
12
9
|
import { dirname, extname, isAbsolute, relative, resolve } from 'node:path';
|
|
13
10
|
/**
|
|
14
|
-
* The literal (non-glob) base directories the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - `src/index.ts` → `<cwd>/src`
|
|
19
|
-
* - `src/**` + `/*.ts` → `<cwd>/src`
|
|
20
|
-
* - `src` → `<cwd>/src`
|
|
11
|
+
* The literal (non-glob) base directories the input source files live in, derived from the
|
|
12
|
+
* bundler `input` patterns: a file entry maps to its containing dir, a glob is reduced to the
|
|
13
|
+
* literal prefix before the first wildcard (`src/index.ts`, `src` and `src/**` + `/*.ts` all
|
|
14
|
+
* give `<cwd>/src`).
|
|
21
15
|
*
|
|
22
|
-
* The project root
|
|
23
|
-
*
|
|
24
|
-
* not the `src/`-subdir footgun this guard targets.
|
|
16
|
+
* The project root is intentionally excluded — a flat library keeping its source at the root and
|
|
17
|
+
* emitting there is an explicit layout, not the `src/`-subdir footgun this guard targets.
|
|
25
18
|
*/
|
|
26
19
|
export function inputSourceDirs(input, cwd) {
|
|
27
20
|
const patterns = [];
|
|
@@ -39,18 +32,16 @@ export function inputSourceDirs(input, cwd) {
|
|
|
39
32
|
const dirs = new Set();
|
|
40
33
|
for (const raw of patterns) {
|
|
41
34
|
const norm = raw.replace(/\\/g, '/');
|
|
42
|
-
//
|
|
43
|
-
// it is a literal path prefix.
|
|
35
|
+
// Everything before the first glob metacharacter is a literal path prefix.
|
|
44
36
|
const globIdx = norm.search(/[*?{}[\]()!+@]/);
|
|
45
37
|
let base = globIdx === -1 ? norm : norm.slice(0, globIdx);
|
|
46
38
|
if (globIdx === -1) {
|
|
47
|
-
//
|
|
48
|
-
// a bare directory stays as-is.
|
|
39
|
+
// Wholly literal: a file path collapses to its dirname, a bare directory stays.
|
|
49
40
|
base = extname(base) ? dirname(base) : base;
|
|
50
41
|
}
|
|
51
42
|
else {
|
|
52
|
-
// The glob cut mid-segment (`src
|
|
53
|
-
//
|
|
43
|
+
// The glob cut mid-segment (`src/fo*`) — drop the trailing partial segment so only
|
|
44
|
+
// the complete literal directory remains.
|
|
54
45
|
base = base.replace(/\/[^/]*$/, '');
|
|
55
46
|
}
|
|
56
47
|
const abs = resolve(root, base || '.');
|
|
@@ -65,23 +56,14 @@ function isInsideOrEqual(child, parent) {
|
|
|
65
56
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
66
57
|
}
|
|
67
58
|
/**
|
|
68
|
-
* `true` when
|
|
69
|
-
*
|
|
70
|
-
* equals, or is nested under, any input source directory.
|
|
71
|
-
*
|
|
72
|
-
* isOutdirInsideSource('src', ['<cwd>/src']) → true (leak)
|
|
73
|
-
* isOutdirInsideSource('src/nest', ['<cwd>/src']) → true (leak)
|
|
74
|
-
* isOutdirInsideSource('dist/esm', ['<cwd>/src']) → false (safe)
|
|
75
|
-
* isOutdirInsideSource('lib', ['<cwd>/src']) → false (safe)
|
|
59
|
+
* `true` when library output written to `outdir` would land inside (or on top of) the input
|
|
60
|
+
* source tree — i.e. `outdir` equals, or is nested under, any input source directory.
|
|
76
61
|
*/
|
|
77
62
|
export function isOutdirInsideSource(outdir, sourceDirs, cwd) {
|
|
78
63
|
const absOut = resolve(cwd, outdir);
|
|
79
64
|
return sourceDirs.some((dir) => isInsideOrEqual(absOut, dir));
|
|
80
65
|
}
|
|
81
|
-
/**
|
|
82
|
-
* The error thrown when a `--library` build would emit into its own source
|
|
83
|
-
* tree. Names both remedies so the fix is actionable at the point of failure.
|
|
84
|
-
*/
|
|
66
|
+
/** The error thrown when a `--library` build would emit into its own source tree. */
|
|
85
67
|
export function libraryOutputLeakError(outdir) {
|
|
86
68
|
return new Error(`gjsify build --library: refusing to write output into "${outdir}", which holds the ` +
|
|
87
69
|
`input source — a library build there produces src/*.js duplicates, *2.js, a ` +
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { type NpmrcConfig } from '@gjsify/npm-registry';
|
|
2
2
|
export declare function loadNpmrc(cwd: string): Promise<NpmrcConfig>;
|
|
3
3
|
/**
|
|
4
|
-
* True iff the parsed npmrc contains *any* `_authToken` entry
|
|
5
|
-
*
|
|
6
|
-
* instead of a generic 401/empty response.
|
|
4
|
+
* True iff the parsed npmrc contains *any* `_authToken` entry — lets a command
|
|
5
|
+
* say "no token configured" instead of relaying a generic 401.
|
|
7
6
|
*/
|
|
8
7
|
export declare function hasAnyAuthToken(npmrc: NpmrcConfig): boolean;
|
|
9
8
|
/**
|
|
10
|
-
* True iff the parsed npmrc has *any* credential (bearer token or basic
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* True iff the parsed npmrc has *any* credential (bearer token or basic auth).
|
|
10
|
+
* `gjsify whoami` uses it to tell "no token configured" apart from "token
|
|
11
|
+
* configured but rejected by the registry".
|
|
13
12
|
*/
|
|
14
13
|
export declare function hasAnyCredential(npmrc: NpmrcConfig): boolean;
|
package/lib/utils/load-npmrc.js
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
// Shared npmrc loader for
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Shared npmrc loader for the auth-aware commands (`gjsify publish`,
|
|
2
|
+
// `gjsify whoami`, …), so that both a maintainer's `~/.npmrc` token and a
|
|
3
|
+
// CI-injected `NPM_CONFIG_USERCONFIG` token reach the registry call.
|
|
4
|
+
// `actions/setup-node` writes its auth-token npmrc to `$RUNNER_TEMP/.npmrc` and
|
|
5
|
+
// exports `NPM_CONFIG_USERCONFIG` at it — it never touches `~/.npmrc`, so
|
|
6
|
+
// honouring that env var is what makes CI authentication work, and `${VAR}`
|
|
7
|
+
// placeholders must be expanded on read (npm's own behaviour):
|
|
8
|
+
// `_authToken=${NODE_AUTH_TOKEN}` arrives as a literal placeholder.
|
|
5
9
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// 2. userconfig: $NPM_CONFIG_USERCONFIG (overrides ~/.npmrc)
|
|
12
|
-
// or ~/.npmrc (default)
|
|
13
|
-
// 3. projectconfig: ./.npmrc (closest workspace)
|
|
14
|
-
//
|
|
15
|
-
// `actions/setup-node` writes the auth-token npmrc to $RUNNER_TEMP/.npmrc
|
|
16
|
-
// and exports NPM_CONFIG_USERCONFIG pointing at it — it does NOT touch
|
|
17
|
-
// ~/.npmrc. Honor the env var so CI authentication works end-to-end.
|
|
18
|
-
//
|
|
19
|
-
// `${VAR}` placeholders are expanded inline (npm CLI's expand-on-read
|
|
20
|
-
// behavior). The auth-token npmrc from actions/setup-node ships
|
|
21
|
-
// `_authToken=${NODE_AUTH_TOKEN}` as a literal placeholder; the env var is
|
|
22
|
-
// set on the publish step.
|
|
10
|
+
// `/etc/npmrc` (globalconfig) is deliberately NOT read — these commands operate
|
|
11
|
+
// on per-user credentials, matching `npm whoami`. The remaining sources are
|
|
12
|
+
// concatenated and re-parsed, so LAST WRITE WINS on a key collision: appended
|
|
13
|
+
// project-first then user, the user/CI config overrides the project `.npmrc`,
|
|
14
|
+
// which is the inverse of npm's own precedence.
|
|
23
15
|
import { existsSync, readFileSync } from 'node:fs';
|
|
24
16
|
import { homedir } from 'node:os';
|
|
25
17
|
import { join } from 'node:path';
|
|
@@ -44,17 +36,16 @@ export async function loadNpmrc(cwd) {
|
|
|
44
36
|
return parseNpmrc(merged);
|
|
45
37
|
}
|
|
46
38
|
/**
|
|
47
|
-
* True iff the parsed npmrc contains *any* `_authToken` entry
|
|
48
|
-
*
|
|
49
|
-
* instead of a generic 401/empty response.
|
|
39
|
+
* True iff the parsed npmrc contains *any* `_authToken` entry — lets a command
|
|
40
|
+
* say "no token configured" instead of relaying a generic 401.
|
|
50
41
|
*/
|
|
51
42
|
export function hasAnyAuthToken(npmrc) {
|
|
52
43
|
return Object.keys(npmrc.authTokens).length > 0;
|
|
53
44
|
}
|
|
54
45
|
/**
|
|
55
|
-
* True iff the parsed npmrc has *any* credential (bearer token or basic
|
|
56
|
-
*
|
|
57
|
-
*
|
|
46
|
+
* True iff the parsed npmrc has *any* credential (bearer token or basic auth).
|
|
47
|
+
* `gjsify whoami` uses it to tell "no token configured" apart from "token
|
|
48
|
+
* configured but rejected by the registry".
|
|
58
49
|
*/
|
|
59
50
|
export function hasAnyCredential(npmrc) {
|
|
60
51
|
return hasAnyAuthToken(npmrc) || Object.keys(npmrc.basicAuth).length > 0;
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The GJS ambient globals referenced as BARE identifiers in `code`, sorted.
|
|
3
3
|
*
|
|
4
|
-
* Empty for a bundle that only uses the `globalThis.imports` isomorphic-guard
|
|
5
|
-
*
|
|
6
|
-
* ignores it for exactly that reason.
|
|
4
|
+
* Empty for a bundle that only uses the `globalThis.imports` isomorphic-guard shape — that
|
|
5
|
+
* form is a runtime probe, not a dependency, and the detector ignores it for that reason.
|
|
7
6
|
*/
|
|
8
7
|
export declare function findUnshimmedGjsGlobals(code: string): string[];
|
|
9
8
|
/**
|
|
10
|
-
* Throw when a `--app node` bundle references GJS ambient globals but was built
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @param code the emitted bundle source
|
|
14
|
-
* @param outfile path of the bundle, for the error message
|
|
9
|
+
* Throw when a `--app node` bundle references GJS ambient globals but was built without
|
|
10
|
+
* the `@gjsify/node-gi/globals` shim.
|
|
15
11
|
*/
|
|
16
12
|
export declare function assertNodeBundleGlobalsShimmed(code: string, outfile: string): void;
|
|
@@ -1,35 +1,25 @@
|
|
|
1
1
|
// Build-time guard: a `--app node` bundle that USES the GJS ambient globals
|
|
2
2
|
// must also have been given `@gjsify/node-gi/globals`.
|
|
3
3
|
//
|
|
4
|
-
// `print`/`printerr`/`log`/`logError`/`ARGV`/`imports` exist implicitly under
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// so a cross-platform package's node bundle would stop loading on plain Node.
|
|
4
|
+
// `print`/`printerr`/`log`/`logError`/`ARGV`/`imports` exist implicitly under gjs and
|
|
5
|
+
// nowhere else. On the node target the side-effect shim `@gjsify/node-gi/globals` seeds
|
|
6
|
+
// them, injected only when `detectNodeGiGlobals` finds one in an analysis bundle —
|
|
7
|
+
// injecting unconditionally would eagerly load the native node-gi addon, so a
|
|
8
|
+
// cross-platform package's node bundle would stop loading on plain Node.
|
|
10
9
|
//
|
|
11
|
-
// That makes the decision a PREDICTION about an artifact that does not exist
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
10
|
+
// That makes the decision a PREDICTION about an artifact that does not exist yet, and it
|
|
11
|
+
// was wrong once: the analysis pass ran before the explicit-`--globals` register stub was
|
|
12
|
+
// resolved, and that stub is one of the two signals `isGjsSourceBuild()` reads, so the
|
|
13
|
+
// analysis bundle routed `/register` subpaths to `@gjsify/empty` and emptied `@girs/*`
|
|
14
|
+
// while the REAL build resolved them — making every ambient global reachable only through
|
|
15
|
+
// a register module invisible to the detector. The build exited 0 and the bundle died at
|
|
16
|
+
// its first canvas op with `ReferenceError: imports is not defined` on node AND bun (the
|
|
17
|
+
// excalibur-jelly-jumper showcase), while gjs ran fine.
|
|
19
18
|
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// globals are ambient — ran fine. Nothing between `gjsify build --app node` and
|
|
25
|
-
// a user running the bundle ever looked at the artifact.
|
|
26
|
-
//
|
|
27
|
-
// So this is a POST-condition on what was actually emitted: whatever the
|
|
28
|
-
// detector predicted, check it against the bundle. It is deliberately NOT a
|
|
29
|
-
// second detector — it runs the SAME `detectGjsAmbientGlobals` over the SAME
|
|
30
|
-
// kind of input (bundled, tree-shaken output, parsed by acorn, bare identifiers
|
|
31
|
-
// only). Two passes over one oracle that must agree; when they don't, the graph
|
|
32
|
-
// they saw differed, which is a bug in the build pipeline, not in user code.
|
|
19
|
+
// So this is a POST-condition on what was actually emitted, deliberately NOT a second
|
|
20
|
+
// detector: it runs the SAME `detectGjsAmbientGlobals` over the SAME kind of input
|
|
21
|
+
// (bundled, tree-shaken output, parsed by acorn, bare identifiers only). Two passes over
|
|
22
|
+
// one oracle that must agree; a disagreement is a bug in the build pipeline, not user code.
|
|
33
23
|
//
|
|
34
24
|
// Scope: only when the CLI decided NOT to inject (`--globals auto`, no shim).
|
|
35
25
|
// `--globals none` opts out of the whole mechanism explicitly and is skipped.
|
|
@@ -37,19 +27,15 @@ import { detectGjsAmbientGlobals } from '@gjsify/rolldown-plugin-gjsify/globals'
|
|
|
37
27
|
/**
|
|
38
28
|
* The GJS ambient globals referenced as BARE identifiers in `code`, sorted.
|
|
39
29
|
*
|
|
40
|
-
* Empty for a bundle that only uses the `globalThis.imports` isomorphic-guard
|
|
41
|
-
*
|
|
42
|
-
* ignores it for exactly that reason.
|
|
30
|
+
* Empty for a bundle that only uses the `globalThis.imports` isomorphic-guard shape — that
|
|
31
|
+
* form is a runtime probe, not a dependency, and the detector ignores it for that reason.
|
|
43
32
|
*/
|
|
44
33
|
export function findUnshimmedGjsGlobals(code) {
|
|
45
34
|
return [...detectGjsAmbientGlobals(code)].sort();
|
|
46
35
|
}
|
|
47
36
|
/**
|
|
48
|
-
* Throw when a `--app node` bundle references GJS ambient globals but was built
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* @param code the emitted bundle source
|
|
52
|
-
* @param outfile path of the bundle, for the error message
|
|
37
|
+
* Throw when a `--app node` bundle references GJS ambient globals but was built without
|
|
38
|
+
* the `@gjsify/node-gi/globals` shim.
|
|
53
39
|
*/
|
|
54
40
|
export function assertNodeBundleGlobalsShimmed(code, outfile) {
|
|
55
41
|
const offenders = findUnshimmedGjsGlobals(code);
|
package/lib/utils/node-script.js
CHANGED
|
@@ -1,43 +1,30 @@
|
|
|
1
1
|
// `gjsify run --node-script <file>` — run an UNBUNDLED `.mjs`/`.js` that imports
|
|
2
2
|
// `node:*` builtins, on whatever runtime the CLI itself is executing on.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// A repo script that imports only `node:fs`/`node:path`/`node:url` — all polyfilled — is not
|
|
5
|
+
// intrinsically Node-bound; what was missing is a way to RUN one under GJS, whose ESM loader
|
|
6
|
+
// has no `node:` URI scheme and no node_modules walker, so `gjs -m <script>.mjs` dies on the
|
|
7
|
+
// first import. That gap is why a Node-less host could install gjsify, resolve every native
|
|
8
|
+
// prebuild, and then fail `build:infra` at `node scripts/process-template.mjs` (see
|
|
9
|
+
// `status/open-todos.md`).
|
|
5
10
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// under GJS: GJS's ESM loader has no `node:` URI scheme and no node_modules
|
|
10
|
-
// walker, so `gjs -m scripts/build-assets.mjs` dies on the first import. That
|
|
11
|
-
// single gap is why a Node-less host could install gjsify, resolve every native
|
|
12
|
-
// prebuild, and then fail `build:infra` at `node scripts/process-template.mjs`
|
|
13
|
-
// (`status/open-todos.md`, "A Node-less host cannot bootstrap a fresh CLONE").
|
|
11
|
+
// Rejected alternative: commit a `dist/*.gjs.mjs` per script — one more artifact each under
|
|
12
|
+
// the committed-bundle freshness gate, scaling with the number of scripts. This is ONE
|
|
13
|
+
// mechanism serving every script in the repo and every consumer's too.
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// node / bun / deno host → spawn the file on that runtime; no bundling, no cost.
|
|
16
|
+
// gjs host → bundle `--app gjs` (which maps `node:fs` → `@gjsify/fs`,
|
|
17
|
+
// injects the globals the script reads, and resolves bare
|
|
18
|
+
// specifiers), then run the bundle.
|
|
19
19
|
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// `import.meta.url` IS THE WHOLE TRICK
|
|
30
|
-
//
|
|
31
|
-
// The bundle does not live where the source lives, and a script's own location
|
|
32
|
-
// is how it finds what it operates on — all four scripts this was built for open
|
|
33
|
-
// with `dirname(fileURLToPath(import.meta.url))` and derive their package root
|
|
34
|
-
// from it. Left alone, `import.meta.url` in the bundle is the BUNDLE's URL, so
|
|
35
|
-
// every one of them would write its output into the cache directory. So the
|
|
36
|
-
// source file's URL is baked in as a compile-time define, exactly as
|
|
37
|
-
// `config.ts`'s `loadConfigViaGjsBundle` does for a `gjsify.config.js` that
|
|
38
|
-
// reads its own siblings. `process.argv` needs no such care: the `--app gjs`
|
|
39
|
-
// bundle already exposes `[gjs, <bundle>, ...args]`, so `argv.slice(2)` and
|
|
40
|
-
// `argv.includes('--flag')` behave as they do on Node.
|
|
20
|
+
// `import.meta.url` is the whole trick: the bundle does not live where the source lives, and
|
|
21
|
+
// a script's own location is how it finds what it operates on — all four scripts this was
|
|
22
|
+
// built for derive their package root from `dirname(fileURLToPath(import.meta.url))`. Left
|
|
23
|
+
// alone that is the BUNDLE's URL, so every one would write its output into the cache
|
|
24
|
+
// directory; the source file's URL is therefore baked in as a compile-time define, exactly
|
|
25
|
+
// as `config.ts`'s `loadConfigViaGjsBundle` does for a `gjsify.config.js` that reads its own
|
|
26
|
+
// siblings. `process.argv` needs no such care: the `--app gjs` bundle already exposes
|
|
27
|
+
// `[gjs, <bundle>, ...args]`.
|
|
41
28
|
import { existsSync, statSync } from 'node:fs';
|
|
42
29
|
import { basename, resolve } from 'node:path';
|
|
43
30
|
import { pathToFileURL } from 'node:url';
|
|
@@ -47,19 +34,14 @@ import { runRuntimeBundle } from './run-node.js';
|
|
|
47
34
|
/**
|
|
48
35
|
* Whether the on-the-fly GJS bundle may be reused across runs.
|
|
49
36
|
*
|
|
50
|
-
* OFF by default
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* script runs and EMITS BUILD OUTPUT from code that no longer exists in the
|
|
56
|
-
* tree. That is the failure this repo has paid for repeatedly under a different
|
|
57
|
-
* name ("it never runs the source you just edited"), and a build script is the
|
|
58
|
-
* worst place to reintroduce it.
|
|
37
|
+
* OFF by default: `bundleFileForGjsCached` invalidates on the ENTRY file's mtime plus the
|
|
38
|
+
* project lockfile, which covers a plugin or a config but NOT a repo script, whose imports
|
|
39
|
+
* are workspace packages that change without any lockfile write. A stale bundle there does
|
|
40
|
+
* not fail loudly — the script runs and EMITS BUILD OUTPUT from code that no longer exists
|
|
41
|
+
* in the tree, and a build script is the worst place to reintroduce that.
|
|
59
42
|
*
|
|
60
|
-
* The cost
|
|
61
|
-
*
|
|
62
|
-
* host that would rather have the seconds back.
|
|
43
|
+
* The cost is one bundle per invocation, and only under GJS. `GJSIFY_NODE_SCRIPT_CACHE=1`
|
|
44
|
+
* opts in for a host that would rather have the seconds back.
|
|
63
45
|
*/
|
|
64
46
|
function scriptCacheEnabled() {
|
|
65
47
|
const v = process.env['GJSIFY_NODE_SCRIPT_CACHE'];
|
|
@@ -81,12 +63,10 @@ export async function runNodeScript(file, extraArgs = [], options = {}) {
|
|
|
81
63
|
}
|
|
82
64
|
const host = hostRuntime();
|
|
83
65
|
if (host !== 'gjs') {
|
|
84
|
-
// Node/bun/deno
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// that a build script never loads. Nothing is lost by leaving it out, and
|
|
89
|
-
// a build chain calls this a dozen times.
|
|
66
|
+
// Node/bun/deno load the file as-is; bundling would only add a step and a way to
|
|
67
|
+
// differ from what the runtime would have done itself. `quiet` because the echo
|
|
68
|
+
// would print `node <file>` — what the package.json script already reads — behind
|
|
69
|
+
// ~2 kB of native-library env, a dozen times per build chain.
|
|
90
70
|
await runRuntimeBundle(host, scriptPath, extraArgs, { exitOnSuccess: options.exitOnSuccess, quiet: true });
|
|
91
71
|
return;
|
|
92
72
|
}
|
|
@@ -110,7 +90,6 @@ export async function runNodeScript(file, extraArgs = [], options = {}) {
|
|
|
110
90
|
`workspace packages, pure-JS npm deps). (${err.message})`);
|
|
111
91
|
}
|
|
112
92
|
// Quiet for the same reason as the node path above; the bundle's location is
|
|
113
|
-
// deterministic (`node_modules/.cache/gjsify/node-scripts/<name>-<hash>.mjs`)
|
|
114
|
-
// for anyone who needs to inspect it.
|
|
93
|
+
// deterministic (`node_modules/.cache/gjsify/node-scripts/<name>-<hash>.mjs`).
|
|
115
94
|
await runGjsBundle(bundlePath, extraArgs, { exitOnSuccess: options.exitOnSuccess, quiet: true });
|
|
116
95
|
}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimum Node.js major the native install backend supports — its prebuilt native
|
|
3
|
-
* modules are ABI-locked to this line (kept in sync with `.nvmrc`).
|
|
4
|
-
*/
|
|
1
|
+
/** Minimum Node major the native install backend supports — keep in sync with `.nvmrc`. */
|
|
5
2
|
export declare const MIN_NODE_MAJOR = 24;
|
|
6
3
|
/** Parse the major from a `process.versions.node`-style string (`'24.18.0'` → 24). */
|
|
7
4
|
export declare function parseNodeMajor(version: string): number;
|
|
8
5
|
/**
|
|
9
|
-
* The preflight verdict
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* supported Node, on GJS (which fakes `process.versions.node` and does not use
|
|
13
|
-
* these prebuilds), or on a version string that does not parse.
|
|
6
|
+
* The preflight verdict, factored out so it can be unit-tested without spawning a
|
|
7
|
+
* foreign Node. `null` (pass) on a supported Node, under GJS, and on a version
|
|
8
|
+
* string that does not parse — an unparseable version must not block an install.
|
|
14
9
|
*/
|
|
15
10
|
export declare function nativeBackendNodeError(version: string, onRealNode: boolean): string | null;
|
|
16
|
-
/**
|
|
17
|
-
* Guard the native install backend: throw a clear error on an unsupported Node
|
|
18
|
-
* major instead of letting the ABI-mismatched prebuilds SIGSEGV mid-install.
|
|
19
|
-
* No-op under GJS and on supported Node.
|
|
20
|
-
*/
|
|
11
|
+
/** Throw the preflight verdict as an error; no-op under GJS and on a supported Node. */
|
|
21
12
|
export declare function assertNativeBackendNodeVersion(): void;
|
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Node-version preflight for the native install backend.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// This preflight turns that into a clear, actionable error.
|
|
4
|
+
// Its prebuilt binaries are ABI-locked to the Node line gjsify targets (`.nvmrc`).
|
|
5
|
+
// On an older major they load a mismatched ABI and SIGSEGV mid-install instead of
|
|
6
|
+
// failing cleanly — measured: Node 22 in a Fedora container segfaults
|
|
7
|
+
// `gjsify install --backend native` during download/extract. This turns that into
|
|
8
|
+
// an actionable error.
|
|
10
9
|
import { isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
11
|
-
/**
|
|
12
|
-
* Minimum Node.js major the native install backend supports — its prebuilt native
|
|
13
|
-
* modules are ABI-locked to this line (kept in sync with `.nvmrc`).
|
|
14
|
-
*/
|
|
10
|
+
/** Minimum Node major the native install backend supports — keep in sync with `.nvmrc`. */
|
|
15
11
|
export const MIN_NODE_MAJOR = 24;
|
|
16
12
|
/** Parse the major from a `process.versions.node`-style string (`'24.18.0'` → 24). */
|
|
17
13
|
export function parseNodeMajor(version) {
|
|
18
14
|
return Number.parseInt(String(version).split('.', 1)[0] ?? '', 10);
|
|
19
15
|
}
|
|
20
16
|
/**
|
|
21
|
-
* The preflight verdict
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* supported Node, on GJS (which fakes `process.versions.node` and does not use
|
|
25
|
-
* these prebuilds), or on a version string that does not parse.
|
|
17
|
+
* The preflight verdict, factored out so it can be unit-tested without spawning a
|
|
18
|
+
* foreign Node. `null` (pass) on a supported Node, under GJS, and on a version
|
|
19
|
+
* string that does not parse — an unparseable version must not block an install.
|
|
26
20
|
*/
|
|
27
21
|
export function nativeBackendNodeError(version, onRealNode) {
|
|
28
22
|
if (!onRealNode)
|
|
@@ -34,21 +28,18 @@ export function nativeBackendNodeError(version, onRealNode) {
|
|
|
34
28
|
`Its prebuilt native modules are ABI-locked to Node ${MIN_NODE_MAJOR} and crash on older majors. ` +
|
|
35
29
|
`Use Node ${MIN_NODE_MAJOR} (nvm/asdf locally, actions/setup-node in CI), or run with --backend npm.`);
|
|
36
30
|
}
|
|
37
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* True when the CLI runs on real Node. GJS must be ruled out FIRST: `@gjsify/process`
|
|
33
|
+
* fakes `process.versions.node = '20.0.0'` for compat, so reading that field would
|
|
34
|
+
* be a false Node positive there (and a false preflight failure, since 20 < 24).
|
|
35
|
+
*/
|
|
38
36
|
function onRealNode() {
|
|
39
|
-
// GJS exposes `imports.system.version`; @gjsify/process fakes
|
|
40
|
-
// `process.versions.node = '20.0.0'` for compat, so check GJS FIRST — a plain
|
|
41
|
-
// `process.versions.node` read would be a false Node positive under GJS.
|
|
42
37
|
const sysVersion = gjsSystemVersion();
|
|
43
38
|
if (sysVersion !== undefined)
|
|
44
39
|
return false;
|
|
45
40
|
return isNode();
|
|
46
41
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Guard the native install backend: throw a clear error on an unsupported Node
|
|
49
|
-
* major instead of letting the ABI-mismatched prebuilds SIGSEGV mid-install.
|
|
50
|
-
* No-op under GJS and on supported Node.
|
|
51
|
-
*/
|
|
42
|
+
/** Throw the preflight verdict as an error; no-op under GJS and on a supported Node. */
|
|
52
43
|
export function assertNativeBackendNodeVersion() {
|
|
53
44
|
const message = nativeBackendNodeError(process.versions.node, onRealNode());
|
|
54
45
|
if (message)
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import type { ConfigData, BundlerOptions } from '../types/config-data.js';
|
|
2
2
|
export declare function normalizeBundlerOptions(configData: ConfigData): BundlerOptions;
|
|
3
3
|
/**
|
|
4
|
-
* Shallow merge with deep-merge of `output`, `transform
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Shallow merge with deep-merge of `output`, `transform` and `resolve`; `overrides` wins,
|
|
5
|
+
* matching `merge(target, ...sources)` from `@gjsify/rolldown-plugin-gjsify/utils/merge`. `base`
|
|
6
|
+
* is the orchestrator's Rolldown-generic shape, `overrides` the user's config plus CLI flags.
|
|
7
7
|
*
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* The orchestrator-side `input` is authoritative — it's the post-glob-expansion
|
|
13
|
-
* value. Overriding it with the user's raw glob string would re-introduce
|
|
14
|
-
* unresolved glob patterns into the final Rolldown call. Same for `external`,
|
|
15
|
-
* which the orchestrator concatenates with platform defaults already.
|
|
8
|
+
* The orchestrator's `input` is authoritative because it is the post-glob-expansion value —
|
|
9
|
+
* taking the user's raw glob string would re-introduce unresolved patterns into the Rolldown
|
|
10
|
+
* call. Same for `external`, which the orchestrator has already concatenated with the platform
|
|
11
|
+
* defaults.
|
|
16
12
|
*/
|
|
17
13
|
export declare function mergeBundlerOptions(base: BundlerOptions, overrides: BundlerOptions): BundlerOptions;
|