@gjsify/rolldown-plugin-gjsify 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/lib/app/gjs.d.ts +28 -54
- package/lib/app/gjs.js +124 -211
- package/lib/app/node.d.ts +31 -41
- package/lib/app/node.js +118 -221
- package/lib/plugins/css-as-string.js +10 -2
- package/lib/plugins/napi-node-addon.d.ts +46 -83
- package/lib/plugins/napi-node-addon.js +188 -308
- package/lib/plugins/process-stub.js +27 -1
- package/lib/plugins/rewrite-node-modules-paths.d.ts +12 -36
- package/lib/plugins/rewrite-node-modules-paths.js +60 -144
- package/lib/plugins/unresolved-workspace-import.d.ts +20 -38
- package/lib/plugins/unresolved-workspace-import.js +65 -157
- package/lib/shims/console-gjs.js +11 -19
- package/lib/utils/auto-globals.d.ts +56 -102
- package/lib/utils/auto-globals.js +100 -171
- package/lib/utils/detect-free-globals.d.ts +27 -46
- package/lib/utils/detect-free-globals.js +106 -185
- package/package.json +7 -7
package/lib/app/gjs.d.ts
CHANGED
|
@@ -31,68 +31,42 @@ export interface GjsFactoryInput {
|
|
|
31
31
|
}
|
|
32
32
|
export declare const setupForGjs: (input: GjsFactoryInput) => Promise<GjsBuildConfig>;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* The canonical `--app gjs` externals predicate — used both for the in-process
|
|
35
|
+
* alias layer and as the predicate behind `externalsPlugin`. Pinned by
|
|
36
|
+
* `packages/infra/cli/src/auto-globals.spec.ts`.
|
|
35
37
|
*
|
|
36
|
-
* -
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* (`isRegisterSubpath`), not an explicit package list, so it scales
|
|
46
|
-
* to every package added by the tree-shakeable-globals convention.
|
|
47
|
-
* It short-circuits BEFORE the user-external check so `bundler.external`
|
|
48
|
-
* can never override it. See AGENTS.md §Tree-shakeable globals.
|
|
49
|
-
* - `gi://*` URIs are external by prefix (GJS-native imports).
|
|
50
|
-
* - `cairo`/`gettext`/`system` + user externals match by exact name.
|
|
51
|
-
*
|
|
52
|
-
* Used by `setupForGjs` for the in-process alias layer AND as the
|
|
53
|
-
* predicate behind `externalsPlugin` (resolveId `{ external: true }`,
|
|
54
|
-
* honoured by both bundler engines). Exported for the regression tests
|
|
55
|
-
* in `auto-globals.spec.ts` — canonical contract, change-detector status.
|
|
38
|
+
* - `<pkg>/register[/<feature>]` MUST NEVER be externalized: GJS's ESM loader
|
|
39
|
+
* has no node_modules walker and does not follow `exports` maps for bare
|
|
40
|
+
* specifiers, so an externalized `import '@gjsify/buffer/register/buffer'`
|
|
41
|
+
* throws `Module not found` at runtime even with the file on disk. Matched by
|
|
42
|
+
* SHAPE (`isRegisterSubpath`) so it scales to every package following the
|
|
43
|
+
* tree-shakeable-globals convention, and short-circuited BEFORE the
|
|
44
|
+
* user-external check so `bundler.external` cannot override it.
|
|
45
|
+
* - `gi://*` external by prefix; `cairo`/`gettext`/`system` + user externals by
|
|
46
|
+
* exact name.
|
|
56
47
|
*/
|
|
57
48
|
export declare function createGjsExternalsPredicate(userExternal?: string[]): (id: string) => boolean;
|
|
58
49
|
/**
|
|
59
|
-
* Recognize the shims this plugin INJECTS into a `--app gjs` bundle
|
|
60
|
-
*
|
|
61
|
-
* both bare-specifier and resolved-disk-path form.
|
|
62
|
-
*
|
|
63
|
-
* They must be force-inlined for exactly the reason `/register` subpaths are,
|
|
64
|
-
* and the failure is the same one: GJS has no `require` and its ESM loader has
|
|
65
|
-
* neither a node_modules walker nor `exports`-map support, so an EXTERNALIZED
|
|
66
|
-
* shim aborts the program at load:
|
|
67
|
-
*
|
|
68
|
-
* Error: Calling `require` for
|
|
69
|
-
* "@gjsify/rolldown-plugin-gjsify/shims/module-resolve" in an environment
|
|
70
|
-
* that doesn't expose the `require` function
|
|
50
|
+
* Recognize the shims this plugin INJECTS into a `--app gjs` bundle, in both
|
|
51
|
+
* bare-specifier and resolved-disk-path form.
|
|
71
52
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
53
|
+
* Force-inlined for the same reason `/register` subpaths are, with the same
|
|
54
|
+
* failure: GJS has no `require` and its ESM loader follows neither node_modules
|
|
55
|
+
* nor `exports` maps, so an externalized shim aborts at load with `Calling
|
|
56
|
+
* \`require\` for "…/shims/module-resolve" in an environment that doesn't expose
|
|
57
|
+
* the \`require\` function`. The shim is never optional — the bundler injected it
|
|
58
|
+
* because the bundle needs it. Short-circuits BEFORE the user-external check, and
|
|
59
|
+
* matches by SHAPE so a later shim is covered without touching this predicate.
|
|
60
|
+
* Deliberately scoped to OUR shim directory only.
|
|
80
61
|
*/
|
|
81
62
|
export declare function isGjsifyShim(id: string): boolean;
|
|
82
63
|
/**
|
|
83
|
-
* Recognize the `/register
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* Matches every shape that goes through the alias layer + Rolldown
|
|
87
|
-
* resolution chain to a `@gjsify/<pkg>/register*` target:
|
|
88
|
-
* - bare: `<pkg>/register`, `<pkg>/register/<feature>`
|
|
89
|
-
* - fully qualified `@gjsify/<pkg>/register`, `@gjsify/<pkg>/register/<feature>`
|
|
90
|
-
* - resolved disk paths under a real `node_modules/<scope>/<pkg>/lib/esm/register*`
|
|
64
|
+
* Recognize the `/register[/<feature>]` subpath shapes `--globals auto` injects
|
|
65
|
+
* as side-effect imports, in bare, fully-qualified and resolved-disk-path form.
|
|
91
66
|
*
|
|
92
|
-
* Used by the `--app gjs` externals predicate to force-inline these even
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* `AGENTS.md` §Tree-shakeable globals subpath convention.
|
|
67
|
+
* Used by the `--app gjs` externals predicate to force-inline these even when the
|
|
68
|
+
* user lists them in `bundler.external`. Pinned by
|
|
69
|
+
* `packages/infra/cli/src/auto-globals.spec.ts`; keep in sync with AGENTS.md
|
|
70
|
+
* § Tree-shakeable globals.
|
|
97
71
|
*/
|
|
98
72
|
export declare function isRegisterSubpath(id: string): boolean;
|
package/lib/app/gjs.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
// `--app gjs` Rolldown configuration factory
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// effective build behaviour: same externals, same alias map, same target
|
|
5
|
-
// (firefox140 for JS, firefox60 for CSS), same console-shim injection,
|
|
6
|
-
// same process-stub banner, same `random-access-file` fs-backed-fallback.
|
|
7
|
-
//
|
|
8
|
-
// Returns a partial `RolldownOptions` template plus the plugin array the
|
|
9
|
-
// caller should compose with their user-supplied options. Library mode is
|
|
10
|
-
// handled separately by `setupLib`.
|
|
1
|
+
// `--app gjs` Rolldown configuration factory: returns a partial
|
|
2
|
+
// `RolldownOptions` template plus the plugin array the caller composes with
|
|
3
|
+
// their user options. Library mode is `setupLib`.
|
|
11
4
|
import { fileURLToPath } from 'node:url';
|
|
12
5
|
import { dirname, resolve } from 'node:path';
|
|
13
6
|
import { existsSync } from 'node:fs';
|
|
@@ -29,55 +22,41 @@ const _shimDir = dirname(fileURLToPath(import.meta.url));
|
|
|
29
22
|
/**
|
|
30
23
|
* Resolve one of this package's bundled shims to an absolute path.
|
|
31
24
|
*
|
|
32
|
-
* `createRequire` is
|
|
33
|
-
*
|
|
34
|
-
* no bare `require`"). It is used here for its RESOLVER, which is
|
|
35
|
-
* `exports`-map aware, and only to answer "where does this subpath live";
|
|
36
|
-
* nothing is loaded through it.
|
|
25
|
+
* `createRequire` is used only for its `exports`-map-aware RESOLVER; nothing is
|
|
26
|
+
* loaded through it (this source is ESM — see AGENTS.md "no bare `require`").
|
|
37
27
|
*/
|
|
38
28
|
function resolveShim(shimName) {
|
|
39
|
-
//
|
|
40
|
-
// normal Node consumer flow where `_shimDir` = `<pkg>/lib/app/`.
|
|
29
|
+
// Normal Node consumption: `_shimDir` = `<pkg>/lib/app/`.
|
|
41
30
|
const relative = resolve(_shimDir, `../shims/${shimName}.js`);
|
|
42
31
|
if (existsSync(relative))
|
|
43
32
|
return relative;
|
|
44
|
-
// Fallback
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
// further walking. Each shim MUST be a `./shims/<name>` subpath export
|
|
49
|
-
// in package.json for this fallback to resolve.
|
|
33
|
+
// Fallback for the bundled-orchestrator case (GJS-CLI self-host loop):
|
|
34
|
+
// `_shimDir` collapses to the bundle's own directory and the relative lookup
|
|
35
|
+
// misses. Every shim MUST therefore be a `./shims/<name>` subpath export in
|
|
36
|
+
// package.json.
|
|
50
37
|
try {
|
|
51
38
|
return createRequire(import.meta.url).resolve(`@gjsify/rolldown-plugin-gjsify/shims/${shimName}`);
|
|
52
39
|
}
|
|
53
40
|
catch (cause) {
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// unrelated "could not resolve" naming a path nobody wrote. Name the
|
|
58
|
-
// actual problem, at the site that knows what it was looking for.
|
|
41
|
+
// Throw rather than return the unverified `relative`: a non-existent
|
|
42
|
+
// path in the alias map resurfaces much later as an unrelated "could not
|
|
43
|
+
// resolve" naming a path nobody wrote.
|
|
59
44
|
throw new Error(`@gjsify/rolldown-plugin-gjsify: cannot locate the "${shimName}" shim. ` +
|
|
60
45
|
`Tried ${relative} and the "./shims/${shimName}" subpath export.`, { cause });
|
|
61
46
|
}
|
|
62
47
|
}
|
|
63
48
|
export const setupForGjs = async (input) => {
|
|
64
49
|
const userExternal = input.userExternal ?? [];
|
|
65
|
-
// Externals policy
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// rules an exact array can't express (`gi://` prefix match) —
|
|
76
|
-
// a resolveId hook returning `{ external: true }` is honoured by
|
|
77
|
-
// both engines via `normalizeResolveIdResult`.
|
|
78
|
-
//
|
|
79
|
-
// `exactExternal` filters out register subpaths so a user external
|
|
80
|
-
// entry can never override the force-inline carve-out below.
|
|
50
|
+
// Externals policy in TWO forms, because `@gjsify/rolldown-native` (the
|
|
51
|
+
// GJS-default engine) JSON.stringify's its options to the Rust core and
|
|
52
|
+
// silently DROPS function values:
|
|
53
|
+
// 1. `options.external` = a plain string array of EXACT names — arrays
|
|
54
|
+
// serialize on both engines.
|
|
55
|
+
// 2. `externalsPlugin(external)` for the SHAPE rules an exact array can't
|
|
56
|
+
// express (`gi://` prefix) — a resolveId `{ external: true }` is
|
|
57
|
+
// honoured by both engines.
|
|
58
|
+
// `exactExternal` filters out register subpaths so a user external entry can
|
|
59
|
+
// never override the force-inline carve-out below.
|
|
81
60
|
const exactExternal = [
|
|
82
61
|
'cairo',
|
|
83
62
|
'gettext',
|
|
@@ -88,12 +67,9 @@ export const setupForGjs = async (input) => {
|
|
|
88
67
|
const format = input.pluginOptions.format ?? 'esm';
|
|
89
68
|
const exclude = input.pluginOptions.exclude ?? [];
|
|
90
69
|
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
91
|
-
// unicorn-magic gates its full API behind the "node"
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// around `conditionNames` below). Route the package to our
|
|
95
|
-
// bundled shim so the API is reachable under --app gjs without
|
|
96
|
-
// turning on the node condition globally.
|
|
70
|
+
// unicorn-magic gates its full API behind the "node" condition, which we
|
|
71
|
+
// deliberately omit globally (see `conditionNames` below). Route it to our
|
|
72
|
+
// bundled shim instead of turning the condition on for every package.
|
|
97
73
|
const unicornMagicShim = resolveShim('unicorn-magic');
|
|
98
74
|
const aliasMap = {
|
|
99
75
|
...getAliasesForGjs({ external }),
|
|
@@ -103,22 +79,14 @@ export const setupForGjs = async (input) => {
|
|
|
103
79
|
};
|
|
104
80
|
// The console shim replaces all `console` references with print()/printerr()-
|
|
105
81
|
// based implementations that bypass GLib.log_structured() — no prefix,
|
|
106
|
-
// ANSI codes work.
|
|
107
|
-
//
|
|
108
|
-
// Path resolution: `resolve(_shimDir, '../shims/...')` works in normal
|
|
109
|
-
// Node consumption (_shimDir = `<pkg>/lib/app/`). When the CLI is
|
|
110
|
-
// bundled into a single .mjs (e.g. the GJS-CLI self-host loop),
|
|
111
|
-
// `import.meta.url` collapses to the bundle's path and the relative
|
|
112
|
-
// resolution lands at a non-existent location. Walk up via
|
|
113
|
-
// createRequire's node_modules-aware resolver as a fallback.
|
|
82
|
+
// ANSI codes work.
|
|
114
83
|
const consoleShimEnabled = input.pluginOptions.consoleShim !== false;
|
|
115
84
|
const consoleShimPath = consoleShimEnabled ? resolveShim('console-gjs') : null;
|
|
116
|
-
// The auto-globals inject stub
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
// from isomorphic guards / bracket access — see AGENTS.md).
|
|
85
|
+
// The auto-globals inject stub is side-effect-imported via a virtual entry:
|
|
86
|
+
// its register modules write to globalThis, so the import chain matters but
|
|
87
|
+
// no name binding does. Rolldown's `inject` is not usable here — the
|
|
88
|
+
// auto-globals invariant forbids source-AST rewrites for global identifiers
|
|
89
|
+
// (see AGENTS.md § `--globals` modes).
|
|
122
90
|
const sideEffectImports = [];
|
|
123
91
|
if (input.pluginOptions.autoGlobalsInject)
|
|
124
92
|
sideEffectImports.push(input.pluginOptions.autoGlobalsInject);
|
|
@@ -129,61 +97,55 @@ export const setupForGjs = async (input) => {
|
|
|
129
97
|
const options = {
|
|
130
98
|
input: finalInput,
|
|
131
99
|
platform: 'neutral',
|
|
132
|
-
// EXACT names only — the
|
|
133
|
-
// rules live in `externalsPlugin` below (see the policy note at
|
|
134
|
-
// the top of this function). A function predicate here would be
|
|
135
|
-
// silently dropped by the native engine's JSON options boundary.
|
|
100
|
+
// EXACT names only — see the externals policy note above.
|
|
136
101
|
external: exactExternal,
|
|
137
|
-
// 'browser'
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// the crypto → @gjsify/crypto alias).
|
|
102
|
+
// 'browser' first so packages like create-hash/create-hmac/randombytes
|
|
103
|
+
// take their pure-JS browser entry instead of an index.js that
|
|
104
|
+
// `require('crypto')`s and cycles back through the @gjsify/crypto alias.
|
|
141
105
|
resolve: {
|
|
142
106
|
mainFields: format === 'esm' ? ['browser', 'module', 'main'] : ['browser', 'main', 'module'],
|
|
143
|
-
//
|
|
144
|
-
//
|
|
107
|
+
// NEITHER 'import' NOR 'require' belongs here — rolldown adds the
|
|
108
|
+
// one matching each CALL SITE, and naming either explicitly applies
|
|
109
|
+
// it to both kinds. The exports-map resolver takes the PACKAGE's
|
|
110
|
+
// first key our list contains, so `'import'` here made a require()
|
|
111
|
+
// call match an `import` key declared first, handing a CJS consumer
|
|
112
|
+
// an ESM namespace where it expects `module.exports`. MEASURED on the
|
|
113
|
+
// express showcase: `is-promise@4` declares `[{import,require,default}, …]`
|
|
114
|
+
// in that order, so `router`'s `require('is-promise')` bound
|
|
115
|
+
// `{default: fn}` and every request threw `TypeError: n is not a
|
|
116
|
+
// function` — answered 200 anyway, and express logged `err.stack`,
|
|
117
|
+
// which carries no message line on SpiderMonkey, so the flagship
|
|
118
|
+
// slide printed ~59 anonymous frames per request naming nothing.
|
|
145
119
|
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// `
|
|
151
|
-
|
|
152
|
-
// `fileFrom` (from native `node:fetch`). With `node` enabled,
|
|
153
|
-
// the resolver picks that branch over `browser` and the bundle
|
|
154
|
-
// breaks at link time. Packages that genuinely need their `node`
|
|
155
|
-
// export under GJS (rare — only one known case so far,
|
|
156
|
-
// `unicorn-magic`'s `traversePathUp`) are handled with explicit
|
|
157
|
-
// resolve aliases instead.
|
|
158
|
-
conditionNames: format === 'esm' ? ['browser', 'import'] : ['browser', 'require', 'import'],
|
|
120
|
+
// `'node'` is absent for a different reason: it hands
|
|
121
|
+
// `cross-fetch-ponyfill` its Node-only entry, which imports
|
|
122
|
+
// `blobFrom`/`fileFrom` and breaks the bundle at link time. Packages
|
|
123
|
+
// that genuinely need their `node` export under GJS (so far only
|
|
124
|
+
// `unicorn-magic`'s `traversePathUp`) get an explicit alias.
|
|
125
|
+
conditionNames: ['browser'],
|
|
159
126
|
},
|
|
160
127
|
transform: {
|
|
161
|
-
//
|
|
128
|
+
// GJS 1.86 / SpiderMonkey 140 ≈ firefox140.
|
|
162
129
|
target: 'firefox140',
|
|
163
130
|
define: {
|
|
164
131
|
global: 'globalThis',
|
|
165
132
|
window: 'globalThis',
|
|
166
133
|
'process.env.READABLE_STREAM': '"disable"',
|
|
167
134
|
},
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
// tree-shake-aware detection concern that motivated the
|
|
175
|
-
// auto-globals invariant.
|
|
135
|
+
// Rewrite bare `console` to a named import from our shim. Rolldown's
|
|
136
|
+
// `inject` (not a register-style global write) because GJS defines
|
|
137
|
+
// `globalThis.console` non-writable + non-configurable, so assigning
|
|
138
|
+
// to it silently no-ops. Safe here where the auto-globals invariant
|
|
139
|
+
// forbids AST rewrites: console is replaced unconditionally, so there
|
|
140
|
+
// is no tree-shake-aware detection to defeat.
|
|
176
141
|
...(consoleShimPath ? { inject: { console: [consoleShimPath, 'console'] } } : {}),
|
|
177
142
|
},
|
|
178
143
|
output: {
|
|
179
144
|
...input.output,
|
|
180
145
|
format,
|
|
181
146
|
sourcemap: false,
|
|
182
|
-
// App builds emit a single bundle file
|
|
183
|
-
//
|
|
184
|
-
// in one chunk that matches `gjsify build --outfile foo.js`.
|
|
185
|
-
// (`codeSplitting: false` replaces the deprecated
|
|
186
|
-
// `inlineDynamicImports: true` in Rolldown ≥ 1.0-rc.18.)
|
|
147
|
+
// App builds emit a single bundle file: dynamic imports inline and
|
|
148
|
+
// the whole program lands in one chunk matching `--outfile`.
|
|
187
149
|
codeSplitting: false,
|
|
188
150
|
},
|
|
189
151
|
treeshake: true,
|
|
@@ -200,32 +162,26 @@ export const setupForGjs = async (input) => {
|
|
|
200
162
|
// Virtual-entry plugin runs FIRST so its resolveId/load match the
|
|
201
163
|
// synthetic input ids that `wrapInputWithSideEffects` produces.
|
|
202
164
|
...(virtualEntries.plugin ? [virtualEntries.plugin] : []),
|
|
203
|
-
// Strip leading #! from
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
// by shebangPlugin's renderChunk hook.
|
|
165
|
+
// Strip leading #! from every input module BEFORE bundling: a shebang in
|
|
166
|
+
// e.g. the CLI's own entry would end up mid-chunk after the process-stub
|
|
167
|
+
// banner, and acorn (the auto-globals detector) rejects the `#` byte.
|
|
168
|
+
// The final-output shebang is composed by shebangPlugin's renderChunk.
|
|
208
169
|
inputShebangStripPlugin(),
|
|
209
|
-
// random-access-file's 'browser' field maps to a throwing stub;
|
|
210
|
-
// the fs-backed Node entry.
|
|
211
|
-
// as a direct entry-table override.
|
|
170
|
+
// random-access-file's 'browser' field maps to a throwing stub; the alias
|
|
171
|
+
// table forces the fs-backed Node entry.
|
|
212
172
|
aliasPlugin({ entries: aliasEntries }),
|
|
213
|
-
// Transparent N-API `.node`-addon loader
|
|
214
|
-
// `gjsGiNodePlugin
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
// Inert unless such a specifier is in the graph — every other id passes
|
|
222
|
-
// through. Always-on for `--app gjs`; never registered for node/browser/ns.
|
|
173
|
+
// Transparent N-API `.node`-addon loader (the forward mirror of
|
|
174
|
+
// `gjsGiNodePlugin`): claims a native-addon acquisition
|
|
175
|
+
// (`bindings`/`node-gyp-build`/a direct `.node`/a napi-rs platform
|
|
176
|
+
// sibling) and rewrites it to a virtual module returning `loadAddon()`
|
|
177
|
+
// from `@gjsify/napi`, which is BUNDLED, not external. Order matters:
|
|
178
|
+
// after aliasPlugin so a user alias pinning an addon's native entry wins,
|
|
179
|
+
// before externalsPlugin so the acquisition is claimed first. Inert
|
|
180
|
+
// otherwise; `--app gjs` only.
|
|
223
181
|
napiNodeAddonPlugin(),
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
// function `external` options at its JSON boundary). Runs after
|
|
228
|
-
// the alias plugin's `pre`-order resolveId so aliases apply first.
|
|
182
|
+
// Externals policy as a resolveId hook — the only form BOTH engines
|
|
183
|
+
// honour. Runs after the alias plugin's `pre` resolveId so aliases apply
|
|
184
|
+
// first.
|
|
229
185
|
externalsPlugin(external, { name: 'gjsify-gjs-externalize' }),
|
|
230
186
|
blueprintPlugin(),
|
|
231
187
|
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
@@ -236,27 +192,24 @@ export const setupForGjs = async (input) => {
|
|
|
236
192
|
cssAsStringPlugin({ targets: { firefox: 60 << 16 } }),
|
|
237
193
|
nodeModulesPathRewritePlugin({ bundleDir, runtimeResolve: format === 'esm' }),
|
|
238
194
|
processStubPlugin({ userBanner: input.userBanner, captureBundleUrl: format === 'esm' }),
|
|
239
|
-
// resolveShebangLine returns null when disabled
|
|
240
|
-
//
|
|
195
|
+
// resolveShebangLine returns null when disabled, else the resolved line
|
|
196
|
+
// with `${env:…}` expanded.
|
|
241
197
|
(() => {
|
|
242
198
|
const line = resolveShebangLine(input.shebang);
|
|
243
199
|
return shebangPlugin({ enabled: line !== null, line: line ?? undefined });
|
|
244
200
|
})(),
|
|
245
|
-
// LAST claim on an id nothing else wanted (`order: 'post'`):
|
|
246
|
-
// `@gjsify/*` substitution
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
201
|
+
// LAST claim on an id nothing else wanted (`order: 'post'`): an
|
|
202
|
+
// unresolvable `@gjsify/*` substitution is a build ERROR, not a silent
|
|
203
|
+
// external. Otherwise Rolldown externalises the ORIGINAL specifier, exits
|
|
204
|
+
// 0, and writes a bundle stock GJS aborts on at load (`ImportError:
|
|
205
|
+
// Unsupported URI scheme for importing: node`) — the same failure
|
|
206
|
+
// `utils/gjs-bundle-guard.ts` catches at emit time, caught here at its
|
|
207
|
+
// source with the importer and cause named.
|
|
252
208
|
unresolvedWorkspaceImportPlugin({ target: 'gjs', aliases: aliasEntries, isExternal: external }),
|
|
253
209
|
];
|
|
254
210
|
return { options, plugins };
|
|
255
211
|
};
|
|
256
|
-
/**
|
|
257
|
-
* Flatten the legacy `Record<string, string>` alias map into the
|
|
258
|
-
* `@rollup/plugin-alias` `entries` array shape, dropping empty values.
|
|
259
|
-
*/
|
|
212
|
+
/** Copy the alias map, dropping entries with an empty target. */
|
|
260
213
|
function flattenAliases(map) {
|
|
261
214
|
const out = {};
|
|
262
215
|
for (const [from, to] of Object.entries(map)) {
|
|
@@ -266,28 +219,19 @@ function flattenAliases(map) {
|
|
|
266
219
|
return out;
|
|
267
220
|
}
|
|
268
221
|
/**
|
|
269
|
-
*
|
|
222
|
+
* The canonical `--app gjs` externals predicate — used both for the in-process
|
|
223
|
+
* alias layer and as the predicate behind `externalsPlugin`. Pinned by
|
|
224
|
+
* `packages/infra/cli/src/auto-globals.spec.ts`.
|
|
270
225
|
*
|
|
271
|
-
* -
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* `
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* (`isRegisterSubpath`), not an explicit package list, so it scales
|
|
281
|
-
* to every package added by the tree-shakeable-globals convention.
|
|
282
|
-
* It short-circuits BEFORE the user-external check so `bundler.external`
|
|
283
|
-
* can never override it. See AGENTS.md §Tree-shakeable globals.
|
|
284
|
-
* - `gi://*` URIs are external by prefix (GJS-native imports).
|
|
285
|
-
* - `cairo`/`gettext`/`system` + user externals match by exact name.
|
|
286
|
-
*
|
|
287
|
-
* Used by `setupForGjs` for the in-process alias layer AND as the
|
|
288
|
-
* predicate behind `externalsPlugin` (resolveId `{ external: true }`,
|
|
289
|
-
* honoured by both bundler engines). Exported for the regression tests
|
|
290
|
-
* in `auto-globals.spec.ts` — canonical contract, change-detector status.
|
|
226
|
+
* - `<pkg>/register[/<feature>]` MUST NEVER be externalized: GJS's ESM loader
|
|
227
|
+
* has no node_modules walker and does not follow `exports` maps for bare
|
|
228
|
+
* specifiers, so an externalized `import '@gjsify/buffer/register/buffer'`
|
|
229
|
+
* throws `Module not found` at runtime even with the file on disk. Matched by
|
|
230
|
+
* SHAPE (`isRegisterSubpath`) so it scales to every package following the
|
|
231
|
+
* tree-shakeable-globals convention, and short-circuited BEFORE the
|
|
232
|
+
* user-external check so `bundler.external` cannot override it.
|
|
233
|
+
* - `gi://*` external by prefix; `cairo`/`gettext`/`system` + user externals by
|
|
234
|
+
* exact name.
|
|
291
235
|
*/
|
|
292
236
|
export function createGjsExternalsPredicate(userExternal = []) {
|
|
293
237
|
const exact = ['cairo', 'gettext', 'system', ...userExternal];
|
|
@@ -302,27 +246,17 @@ export function createGjsExternalsPredicate(userExternal = []) {
|
|
|
302
246
|
};
|
|
303
247
|
}
|
|
304
248
|
/**
|
|
305
|
-
* Recognize the shims this plugin INJECTS into a `--app gjs` bundle
|
|
306
|
-
*
|
|
307
|
-
* both bare-specifier and resolved-disk-path form.
|
|
308
|
-
*
|
|
309
|
-
* They must be force-inlined for exactly the reason `/register` subpaths are,
|
|
310
|
-
* and the failure is the same one: GJS has no `require` and its ESM loader has
|
|
311
|
-
* neither a node_modules walker nor `exports`-map support, so an EXTERNALIZED
|
|
312
|
-
* shim aborts the program at load:
|
|
249
|
+
* Recognize the shims this plugin INJECTS into a `--app gjs` bundle, in both
|
|
250
|
+
* bare-specifier and resolved-disk-path form.
|
|
313
251
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
* The shim is
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* a shim added later is covered without touching this predicate.
|
|
323
|
-
*
|
|
324
|
-
* Scope is deliberately narrow: only OUR shim directory. A consumer package
|
|
325
|
-
* that happens to have a `shims/` folder is unaffected.
|
|
252
|
+
* Force-inlined for the same reason `/register` subpaths are, with the same
|
|
253
|
+
* failure: GJS has no `require` and its ESM loader follows neither node_modules
|
|
254
|
+
* nor `exports` maps, so an externalized shim aborts at load with `Calling
|
|
255
|
+
* \`require\` for "…/shims/module-resolve" in an environment that doesn't expose
|
|
256
|
+
* the \`require\` function`. The shim is never optional — the bundler injected it
|
|
257
|
+
* because the bundle needs it. Short-circuits BEFORE the user-external check, and
|
|
258
|
+
* matches by SHAPE so a later shim is covered without touching this predicate.
|
|
259
|
+
* Deliberately scoped to OUR shim directory only.
|
|
326
260
|
*/
|
|
327
261
|
export function isGjsifyShim(id) {
|
|
328
262
|
// Bare/fully-qualified: `@gjsify/rolldown-plugin-gjsify/shims/<name>`.
|
|
@@ -332,45 +266,24 @@ export function isGjsifyShim(id) {
|
|
|
332
266
|
return /[\\/]rolldown-plugin-gjsify[\\/](?:lib|src)[\\/]shims[\\/]/.test(id);
|
|
333
267
|
}
|
|
334
268
|
/**
|
|
335
|
-
* Recognize the `/register
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
* Matches every shape that goes through the alias layer + Rolldown
|
|
339
|
-
* resolution chain to a `@gjsify/<pkg>/register*` target:
|
|
340
|
-
* - bare: `<pkg>/register`, `<pkg>/register/<feature>`
|
|
341
|
-
* - fully qualified `@gjsify/<pkg>/register`, `@gjsify/<pkg>/register/<feature>`
|
|
342
|
-
* - resolved disk paths under a real `node_modules/<scope>/<pkg>/lib/esm/register*`
|
|
269
|
+
* Recognize the `/register[/<feature>]` subpath shapes `--globals auto` injects
|
|
270
|
+
* as side-effect imports, in bare, fully-qualified and resolved-disk-path form.
|
|
343
271
|
*
|
|
344
|
-
* Used by the `--app gjs` externals predicate to force-inline these even
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* `AGENTS.md` §Tree-shakeable globals subpath convention.
|
|
272
|
+
* Used by the `--app gjs` externals predicate to force-inline these even when the
|
|
273
|
+
* user lists them in `bundler.external`. Pinned by
|
|
274
|
+
* `packages/infra/cli/src/auto-globals.spec.ts`; keep in sync with AGENTS.md
|
|
275
|
+
* § Tree-shakeable globals.
|
|
349
276
|
*/
|
|
350
277
|
export function isRegisterSubpath(id) {
|
|
351
|
-
// Source
|
|
352
|
-
//
|
|
353
|
-
// false positives like `register` (the bare word) or
|
|
354
|
-
// `@scope/unregister`.
|
|
355
|
-
//
|
|
356
|
-
// ✓ `fetch/register`
|
|
357
|
-
// ✓ `@gjsify/buffer/register`
|
|
358
|
-
// ✓ `@gjsify/node-globals/register/buffer`
|
|
359
|
-
// ✗ `register` (no `/` prefix)
|
|
360
|
-
// ✗ `@scope/unregister` (the `/` is followed by `un`, not `register`)
|
|
361
|
-
// ✗ `foo/register.js?query=1` (query-suffix → treat as resolved-path,
|
|
362
|
-
// caught by the second branch only when
|
|
363
|
-
// the file extension is intact)
|
|
278
|
+
// Source shape. The required leading `/` rules out the bare word `register`
|
|
279
|
+
// and `@scope/unregister`.
|
|
364
280
|
if (/\/register(?:\/[^?]*)?$/.test(id)) {
|
|
365
281
|
return true;
|
|
366
282
|
}
|
|
367
|
-
// Resolved disk-path shape
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
// requires the file extension at the end — a Rolldown synthetic-id
|
|
372
|
-
// suffix like `?query=1` therefore does NOT match (those callers
|
|
373
|
-
// expect to flow through the normal externals path).
|
|
283
|
+
// Resolved disk-path shape (post alias plugin + node_modules resolver), for
|
|
284
|
+
// both `lib/esm/register/<feature>.js` and a TS-direct `src/register/<feature>.ts` export. The
|
|
285
|
+
// extension is required, so a Rolldown synthetic-id suffix (`?query=1`) falls
|
|
286
|
+
// through to the normal externals path on purpose.
|
|
374
287
|
if (/[/\\]register(?:[/\\][^/\\]+)?\.(?:[mc]?js|ts)$/.test(id)) {
|
|
375
288
|
return true;
|
|
376
289
|
}
|