@gjsify/rolldown-plugin-gjsify 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/lib/app/gjs.d.ts +28 -54
- package/lib/app/gjs.js +113 -208
- package/lib/app/node.d.ts +31 -41
- package/lib/app/node.js +118 -221
- package/lib/plugins/napi-node-addon.d.ts +46 -83
- package/lib/plugins/napi-node-addon.js +188 -308
- 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/node.d.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
2
|
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
3
|
/**
|
|
4
|
-
* Reverse-bridge register routing: a node build of a GENUINE GJS SOURCE needs
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* on plain Node.
|
|
9
|
-
*
|
|
10
|
-
* Applied by `setupForNode` when `isGjsSourceBuild` is true — see the comment
|
|
11
|
-
* on that flag for why BOTH reverse-bridge signals qualify, not just the
|
|
12
|
-
* explicit `--globals` stub.
|
|
4
|
+
* Reverse-bridge register routing: a node build of a GENUINE GJS SOURCE needs the
|
|
5
|
+
* REAL `@gjsify/*` register bodies (document, HTMLCanvasElement, the `'2d'` context
|
|
6
|
+
* factory, matchMedia, …) over `@gjsify/node-gi`, not the default `@gjsify/empty`
|
|
7
|
+
* stubs that keep CROSS-PLATFORM node bundles loadable on plain Node.
|
|
13
8
|
*
|
|
14
9
|
* Two adjustments over the standard node alias map:
|
|
15
|
-
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the
|
|
16
|
-
*
|
|
17
|
-
* 2. the gjs target's bare→scoped register routes (`
|
|
18
|
-
* `@gjsify/fetch/register/fetch`, `xmlhttprequest/register` →
|
|
10
|
+
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the register
|
|
11
|
+
* import resolves to its real body;
|
|
12
|
+
* 2. the gjs target's bare→scoped register routes (`xmlhttprequest/register` →
|
|
19
13
|
* `@gjsify/fetch/register/xhr`, …) are merged in so the inject stub's bare
|
|
20
14
|
* specifiers resolve on node exactly as they do on gjs.
|
|
21
15
|
*
|
|
22
|
-
* Applied to the BASE map only — `pluginOptions.aliases` /
|
|
23
|
-
* override
|
|
24
|
-
* loadability guarantee for cross-platform packages is not regressed).
|
|
16
|
+
* Applied to the BASE map only, when `isGjsSourceBuild` — `pluginOptions.aliases` /
|
|
17
|
+
* user aliases still override, and a non-reverse-bridge build is untouched.
|
|
25
18
|
*/
|
|
26
19
|
export declare function enableGjsRegistersForNode(baseAliases: Record<string, string>): Record<string, string>;
|
|
27
20
|
/**
|
|
@@ -30,34 +23,31 @@ export declare function enableGjsRegistersForNode(baseAliases: Record<string, st
|
|
|
30
23
|
*
|
|
31
24
|
* Two signals qualify, and BOTH must gate the same things:
|
|
32
25
|
* - `nodeGiGlobalsInject` — the CLI's post-tree-shake detection found bare GJS
|
|
33
|
-
* ambient globals (`print`/`imports`/`ARGV`/…)
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* asked for GJS registers on the node target explicitly.
|
|
26
|
+
* ambient globals (`print`/`imports`/`ARGV`/…) or a static `@gjsify/node-gi/*`
|
|
27
|
+
* import (the externalised spelling of the bare built-ins) in the output;
|
|
28
|
+
* - `registerInject` — the `--globals` inject stub, i.e. the user asked for GJS
|
|
29
|
+
* registers on the node target explicitly.
|
|
38
30
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
31
|
+
* ONE named function because the two consumers used to ask separately and
|
|
32
|
+
* disagree: with `emitGirs` taking the union and the register-alias lift taking
|
|
33
|
+
* only the explicit stub, a reverse-bridge build got `@girs/*` routed to
|
|
34
|
+
* `requireGi` while a `/register` import in the SAME graph was still emptied to
|
|
35
|
+
* `@gjsify/empty` — silently dropping `@gjsify/dom-elements/register/canvas`'s
|
|
36
|
+
* `'2d'` context factory, so `@gjsify/canvas2d`'s `Canvas2DBridge` never fired
|
|
37
|
+
* `onReady`.
|
|
46
38
|
*
|
|
47
|
-
* A build with NEITHER signal is byte-unchanged:
|
|
48
|
-
*
|
|
49
|
-
*
|
|
39
|
+
* A build with NEITHER signal is byte-unchanged: register routing and `@girs/*`
|
|
40
|
+
* emptying both stay, so a cross-platform package's node bundle keeps loading on
|
|
41
|
+
* plain Node without node-gi installed.
|
|
50
42
|
*
|
|
51
|
-
* KNOWN NARROWNESS (
|
|
52
|
-
* `
|
|
53
|
-
* `@
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* node-gi
|
|
58
|
-
*
|
|
59
|
-
* pinned by `tests/e2e/node-gi-globals-inject`), and injecting the eager
|
|
60
|
-
* globals shim for it would break exactly that.
|
|
43
|
+
* KNOWN NARROWNESS (tracked in status/open-todos.md): a genuine GJS source whose
|
|
44
|
+
* ONLY platform reach is `gi://` (no ambient global, no bare built-in) is not
|
|
45
|
+
* recognised — its `@girs/*` and its registers are both emptied. A surviving
|
|
46
|
+
* `gi://` import cannot simply become a third signal: its shim loads node-gi
|
|
47
|
+
* lazily precisely so a gjs-gated `gi://` import keeps a cross-platform package's
|
|
48
|
+
* node bundle loadable on plain Node (#641, pinned by
|
|
49
|
+
* `tests/e2e/node-gi-globals-inject`), and injecting the eager globals shim would
|
|
50
|
+
* break exactly that.
|
|
61
51
|
*/
|
|
62
52
|
export declare function isGjsSourceBuild(options: {
|
|
63
53
|
nodeGiGlobalsInject?: boolean;
|
package/lib/app/node.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
// `--app node` Rolldown configuration factory.
|
|
2
|
-
//
|
|
3
|
-
// Same external set + alias map as the esbuild predecessor. The
|
|
4
|
-
// `createRequire` banner that esbuild needed for ESM-output CJS interop
|
|
5
|
-
// translates to Rolldown's `output.banner` directly — Rolldown itself does
|
|
6
|
-
// not synthesise a `require()` shim for ESM consumers of bundled CJS code.
|
|
7
2
|
import { aliasPlugin } from '../plugins/alias.js';
|
|
8
3
|
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
9
4
|
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
@@ -18,60 +13,46 @@ import { unresolvedWorkspaceImportPlugin } from '../plugins/unresolved-workspace
|
|
|
18
13
|
import { wrapInputWithSideEffects } from '../utils/entry-wrapper.js';
|
|
19
14
|
/**
|
|
20
15
|
* Side-effect specifier the node target injects when GJS ambient globals
|
|
21
|
-
* (`print`/`imports`/…) are detected in the bundled output
|
|
22
|
-
*
|
|
23
|
-
*
|
|
16
|
+
* (`print`/`imports`/…) are detected in the bundled output; importing it seeds
|
|
17
|
+
* them on `globalThis` via the node-gi backend. Kept EXTERNAL — it loads the
|
|
18
|
+
* native addon and must never be bundled.
|
|
24
19
|
*/
|
|
25
20
|
const NODE_GI_GLOBALS_SPECIFIER = '@gjsify/node-gi/globals';
|
|
26
21
|
/**
|
|
27
|
-
* The node-gi standalone GJS built-in modules
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* be bundled.
|
|
22
|
+
* The node-gi standalone GJS built-in modules — the targets `ALIASES_GJS_FOR_NODE`
|
|
23
|
+
* rewrites a GJS source's bare `system`/`gettext`/`cairo` imports to (node-target
|
|
24
|
+
* only, via `gjsBuiltinModulesNodePlugin`). Kept EXTERNAL like
|
|
25
|
+
* `@gjsify/node-gi/gi`: they load the native node-gi backend and must resolve at
|
|
26
|
+
* runtime against the consumer's node_modules.
|
|
33
27
|
*
|
|
34
|
-
* DERIVED from
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* (the
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `gettext` were listed), so under the GJS bundler `cairo` was left as a bare,
|
|
44
|
-
* unresolvable specifier — breaking every Cairo/PangoCairo consumer
|
|
45
|
-
* (`@gjsify/canvas2d-core`) built via the reverse bridge. Under npm `rolldown`
|
|
46
|
-
* (Node) the resolveId `external` flag IS honoured, so the gap was invisible until
|
|
47
|
-
* a Cairo consumer was built on the GJS bundler. e2e: `tests/e2e/node-gi-build`;
|
|
48
|
-
* unit: `packages/infra/cli/src/node-gi-externals.spec.ts`.
|
|
28
|
+
* DERIVED from the alias map's VALUES so the two cannot drift. That completeness
|
|
29
|
+
* is LOAD-BEARING under `@gjsify/rolldown-native`: its JSON options boundary drops
|
|
30
|
+
* the resolveId `{ external: true }` flag `gjsBuiltinModulesNodePlugin` returns
|
|
31
|
+
* (only the plain `external` string array survives), so a rewritten
|
|
32
|
+
* `@gjsify/node-gi/<mod>` stays external ONLY if it is in this array. A hardcoded
|
|
33
|
+
* list missing `cairo` left it bare and unresolvable under the GJS bundler,
|
|
34
|
+
* breaking every Cairo/PangoCairo reverse-bridge consumer — invisible under npm
|
|
35
|
+
* `rolldown`, which does honour the flag. e2e `tests/e2e/node-gi-build`; unit
|
|
36
|
+
* `packages/infra/cli/src/node-gi-externals.spec.ts`.
|
|
49
37
|
*/
|
|
50
38
|
const NODE_GI_BARE_MODULE_SPECIFIERS = Object.values(ALIASES_GJS_FOR_NODE);
|
|
51
39
|
/** Matches a register subpath in an alias key: `<pkg>/register` or `<pkg>/register/<feature>`. */
|
|
52
40
|
const REGISTER_SUBPATH_RE = /\/register(\/|$)/;
|
|
53
41
|
/**
|
|
54
|
-
* Reverse-bridge register routing: a node build of a GENUINE GJS SOURCE needs
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* on plain Node.
|
|
59
|
-
*
|
|
60
|
-
* Applied by `setupForNode` when `isGjsSourceBuild` is true — see the comment
|
|
61
|
-
* on that flag for why BOTH reverse-bridge signals qualify, not just the
|
|
62
|
-
* explicit `--globals` stub.
|
|
42
|
+
* Reverse-bridge register routing: a node build of a GENUINE GJS SOURCE needs the
|
|
43
|
+
* REAL `@gjsify/*` register bodies (document, HTMLCanvasElement, the `'2d'` context
|
|
44
|
+
* factory, matchMedia, …) over `@gjsify/node-gi`, not the default `@gjsify/empty`
|
|
45
|
+
* stubs that keep CROSS-PLATFORM node bundles loadable on plain Node.
|
|
63
46
|
*
|
|
64
47
|
* Two adjustments over the standard node alias map:
|
|
65
|
-
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the
|
|
66
|
-
*
|
|
67
|
-
* 2. the gjs target's bare→scoped register routes (`
|
|
68
|
-
* `@gjsify/fetch/register/fetch`, `xmlhttprequest/register` →
|
|
48
|
+
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the register
|
|
49
|
+
* import resolves to its real body;
|
|
50
|
+
* 2. the gjs target's bare→scoped register routes (`xmlhttprequest/register` →
|
|
69
51
|
* `@gjsify/fetch/register/xhr`, …) are merged in so the inject stub's bare
|
|
70
52
|
* specifiers resolve on node exactly as they do on gjs.
|
|
71
53
|
*
|
|
72
|
-
* Applied to the BASE map only — `pluginOptions.aliases` /
|
|
73
|
-
* override
|
|
74
|
-
* loadability guarantee for cross-platform packages is not regressed).
|
|
54
|
+
* Applied to the BASE map only, when `isGjsSourceBuild` — `pluginOptions.aliases` /
|
|
55
|
+
* user aliases still override, and a non-reverse-bridge build is untouched.
|
|
75
56
|
*/
|
|
76
57
|
export function enableGjsRegistersForNode(baseAliases) {
|
|
77
58
|
const out = {};
|
|
@@ -92,74 +73,53 @@ export function enableGjsRegistersForNode(baseAliases) {
|
|
|
92
73
|
*
|
|
93
74
|
* Two signals qualify, and BOTH must gate the same things:
|
|
94
75
|
* - `nodeGiGlobalsInject` — the CLI's post-tree-shake detection found bare GJS
|
|
95
|
-
* ambient globals (`print`/`imports`/`ARGV`/…)
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* asked for GJS registers on the node target explicitly.
|
|
76
|
+
* ambient globals (`print`/`imports`/`ARGV`/…) or a static `@gjsify/node-gi/*`
|
|
77
|
+
* import (the externalised spelling of the bare built-ins) in the output;
|
|
78
|
+
* - `registerInject` — the `--globals` inject stub, i.e. the user asked for GJS
|
|
79
|
+
* registers on the node target explicitly.
|
|
100
80
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
81
|
+
* ONE named function because the two consumers used to ask separately and
|
|
82
|
+
* disagree: with `emitGirs` taking the union and the register-alias lift taking
|
|
83
|
+
* only the explicit stub, a reverse-bridge build got `@girs/*` routed to
|
|
84
|
+
* `requireGi` while a `/register` import in the SAME graph was still emptied to
|
|
85
|
+
* `@gjsify/empty` — silently dropping `@gjsify/dom-elements/register/canvas`'s
|
|
86
|
+
* `'2d'` context factory, so `@gjsify/canvas2d`'s `Canvas2DBridge` never fired
|
|
87
|
+
* `onReady`.
|
|
108
88
|
*
|
|
109
|
-
* A build with NEITHER signal is byte-unchanged:
|
|
110
|
-
*
|
|
111
|
-
*
|
|
89
|
+
* A build with NEITHER signal is byte-unchanged: register routing and `@girs/*`
|
|
90
|
+
* emptying both stay, so a cross-platform package's node bundle keeps loading on
|
|
91
|
+
* plain Node without node-gi installed.
|
|
112
92
|
*
|
|
113
|
-
* KNOWN NARROWNESS (
|
|
114
|
-
* `
|
|
115
|
-
* `@
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* node-gi
|
|
120
|
-
*
|
|
121
|
-
* pinned by `tests/e2e/node-gi-globals-inject`), and injecting the eager
|
|
122
|
-
* globals shim for it would break exactly that.
|
|
93
|
+
* KNOWN NARROWNESS (tracked in status/open-todos.md): a genuine GJS source whose
|
|
94
|
+
* ONLY platform reach is `gi://` (no ambient global, no bare built-in) is not
|
|
95
|
+
* recognised — its `@girs/*` and its registers are both emptied. A surviving
|
|
96
|
+
* `gi://` import cannot simply become a third signal: its shim loads node-gi
|
|
97
|
+
* lazily precisely so a gjs-gated `gi://` import keeps a cross-platform package's
|
|
98
|
+
* node bundle loadable on plain Node (#641, pinned by
|
|
99
|
+
* `tests/e2e/node-gi-globals-inject`), and injecting the eager globals shim would
|
|
100
|
+
* break exactly that.
|
|
123
101
|
*/
|
|
124
102
|
export function isGjsSourceBuild(options) {
|
|
125
103
|
return Boolean(options.nodeGiGlobalsInject || options.registerInject);
|
|
126
104
|
}
|
|
127
105
|
export const setupForNode = async (input) => {
|
|
128
106
|
const userExternal = input.userExternal ?? [];
|
|
129
|
-
// node-datachannel
|
|
130
|
-
//
|
|
131
|
-
//
|
|
107
|
+
// node-datachannel and `@gjsify/node-gi` are native addons that cannot be
|
|
108
|
+
// bundled — their loaders resolve a `.node` binary relative to their own
|
|
109
|
+
// installed location, so both must stay external.
|
|
132
110
|
//
|
|
133
|
-
// GJS-specific specifiers (`gi://*`, `@girs/*`) are NOT
|
|
134
|
-
// `
|
|
135
|
-
// is handled by `gjsGiNodePlugin` + `gjsImportsEmptyPlugin
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// cross-platform package the `@girs/*` empty-module redirect makes node
|
|
140
|
-
// bundles (which transitively import @girs/* via *.gjs.spec / direct
|
|
141
|
-
// internal imports) loadable on Node — the GJS-only code paths are still
|
|
142
|
-
// gated at runtime by `on('Gjs', …)` or `typeof globalThis.imports` guards.
|
|
143
|
-
// For a GENUINE GJS source built via the node-gi reverse bridge (detected
|
|
144
|
-
// by `nodeGiGlobalsInject`) the `@girs/*` empty redirect is OFF so the
|
|
145
|
-
// namespace resolves through to `requireGi` — see the plugins array.
|
|
146
|
-
// `@gjsify/node-gi` is the Axis-5 GI runtime the `gjsGiNodePlugin` rewrites
|
|
147
|
-
// `gi://` onto. It is a native (node-gyp) addon whose loader resolves its
|
|
148
|
-
// `.node` binary relative to its own installed location, so it must NOT be
|
|
149
|
-
// bundled — keep it external so the `import … from '@gjsify/node-gi/gi'` the
|
|
150
|
-
// virtual shim emits resolves at runtime against the consumer's node_modules.
|
|
111
|
+
// GJS-specific specifiers (`gi://*`, `@girs/*`) are deliberately NOT
|
|
112
|
+
// externalised: `gjsGiNodePlugin` rewrites `gi://` to `requireGi`, and
|
|
113
|
+
// `@girs/*` is handled by `gjsGiNodePlugin` + `gjsImportsEmptyPlugin`.
|
|
114
|
+
// Externalising them would leave bare `import 'gi://Gio?version=2.0'` in the
|
|
115
|
+
// output, which Node's ESM loader rejects with
|
|
116
|
+
// `ERR_UNSUPPORTED_ESM_URL_SCHEME`.
|
|
151
117
|
const exactExternal = [
|
|
152
118
|
...EXTERNALS_NODE,
|
|
153
119
|
'node-datachannel',
|
|
154
120
|
'@gjsify/node-gi',
|
|
155
121
|
'@gjsify/node-gi/gi',
|
|
156
|
-
// The GJS-ambient-globals shim (auto-injected below when `print`/
|
|
157
|
-
// `imports`/… are referenced). Like `@gjsify/node-gi/gi` it loads the
|
|
158
|
-
// native addon and must resolve at runtime, never be bundled.
|
|
159
122
|
NODE_GI_GLOBALS_SPECIFIER,
|
|
160
|
-
// The node-gi standalone GJS built-in modules (`system`/`gettext`),
|
|
161
|
-
// reached when the alias layer rewrites the bare specifiers. Kept
|
|
162
|
-
// external for the same reason as the gi/globals shims above.
|
|
163
123
|
...NODE_GI_BARE_MODULE_SPECIFIERS,
|
|
164
124
|
...userExternal,
|
|
165
125
|
];
|
|
@@ -173,16 +133,11 @@ export const setupForNode = async (input) => {
|
|
|
173
133
|
const format = input.pluginOptions.format ?? 'esm';
|
|
174
134
|
const exclude = input.pluginOptions.exclude ?? [];
|
|
175
135
|
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
176
|
-
// When
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
// `print`/`imports` nor retains a bare `system`/`gettext`/`cairo` import
|
|
182
|
-
// stays free of `@gjsify/node-gi/globals`, so it loads on plain Node
|
|
183
|
-
// without node-gi installed (the eager-native-load regression guard, same
|
|
184
|
-
// lesson as the lazy `gi://` shim in #641). Reuses the SAME virtual-entry
|
|
185
|
-
// wrapper the GJS `--globals auto` machinery uses.
|
|
136
|
+
// When `detectNodeGiGlobals` recognised a genuine GJS source, wrap the entry
|
|
137
|
+
// so it side-effect imports the node-gi globals shim before user code runs —
|
|
138
|
+
// ONLY then, so an undetected bundle stays free of `@gjsify/node-gi/globals`
|
|
139
|
+
// and loads on plain Node without node-gi installed (the eager-native-load
|
|
140
|
+
// guard, same lesson as the lazy `gi://` shim in #641).
|
|
186
141
|
const sideEffectImports = [];
|
|
187
142
|
if (input.pluginOptions.nodeGiGlobalsInject)
|
|
188
143
|
sideEffectImports.push(NODE_GI_GLOBALS_SPECIFIER);
|
|
@@ -194,35 +149,9 @@ export const setupForNode = async (input) => {
|
|
|
194
149
|
sideEffectImports.push(registerInject);
|
|
195
150
|
const virtualEntries = wrapInputWithSideEffects(entryPoints, sideEffectImports);
|
|
196
151
|
const finalInput = virtualEntries.input;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
*
|
|
201
|
-
* TWO signals answer that ONE question and both must count:
|
|
202
|
-
*
|
|
203
|
-
* - `nodeGiGlobalsInject` — the CLI detected BARE GJS ambient globals
|
|
204
|
-
* (`print`/`imports`/`ARGV`) or a static `@gjsify/node-gi/*` import
|
|
205
|
-
* (a retained bare `system`/`gettext`/`cairo` built-in) in the
|
|
206
|
-
* tree-shaken output. Only genuine GJS source has those (the detector
|
|
207
|
-
* deliberately ignores the `globalThis.imports` isomorphic-guard shape).
|
|
208
|
-
* - `autoGlobalsInject` — the user named EXPLICIT `--globals` (e.g.
|
|
209
|
-
* `--globals auto,dom`), i.e. asked for the DOM surface on Node.
|
|
210
|
-
*
|
|
211
|
-
* Both already gate `emptyGirs` below. The register routing used to consult
|
|
212
|
-
* ONLY the explicit stub, and that asymmetry was a
|
|
213
|
-
* bug: a reverse-bridge build got its `@girs/*` value imports routed
|
|
214
|
-
* through to `requireGi` while a `/register` side-effect import in the very
|
|
215
|
-
* same graph was still emptied to `@gjsify/empty`. It surfaced as
|
|
216
|
-
* `@gjsify/canvas2d`'s `Canvas2DBridge` silently losing the `'2d'` context
|
|
217
|
-
* factory (`@gjsify/dom-elements/register/canvas` → empty → `getContext('2d')`
|
|
218
|
-
* null → `onReady` never fires) on a plain `gjsify build … --app node`, with
|
|
219
|
-
* no `--globals` flag anywhere — guarded by the node-gi
|
|
220
|
-
* `test/canvas2d-bridge.test.mjs` golden.
|
|
221
|
-
*
|
|
222
|
-
* A build with NEITHER signal is byte-unchanged: the `@gjsify/empty`
|
|
223
|
-
* register routing stays, so a cross-platform package's node bundle keeps
|
|
224
|
-
* loading on plain Node without node-gi installed.
|
|
225
|
-
*/
|
|
152
|
+
// Gates both the register-alias lift and `emptyGirs` below — see the
|
|
153
|
+
// predicate's doc comment. The node-gi golden `test/canvas2d-bridge.test.mjs`
|
|
154
|
+
// pins the asymmetry it fixed.
|
|
226
155
|
const gjsSourceBuild = isGjsSourceBuild({ ...input.pluginOptions, registerInject });
|
|
227
156
|
const baseAliases = getAliasesForNode({ external });
|
|
228
157
|
const aliasMap = {
|
|
@@ -235,42 +164,29 @@ export const setupForNode = async (input) => {
|
|
|
235
164
|
// promise the alias layer made.
|
|
236
165
|
const aliasEntries = flattenAliases(aliasMap);
|
|
237
166
|
const bundleDir = getBundleDirFromOutput(input.output);
|
|
238
|
-
// Rolldown
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
// `const require = createRequire(...)` (e.g. yargs's ESM platform
|
|
244
|
-
// shim) — `SyntaxError: Identifier 'require' has already been
|
|
245
|
-
// declared`.
|
|
167
|
+
// No banner on purpose: Rolldown injects its own CJS interop (`__commonJSMin`,
|
|
168
|
+
// `__require`), so a top-of-bundle `const require = createRequire(...)` is both
|
|
169
|
+
// unnecessary and harmful — it collides with bundled CJS sources declaring
|
|
170
|
+
// their own (yargs's ESM platform shim): `SyntaxError: Identifier 'require'
|
|
171
|
+
// has already been declared`.
|
|
246
172
|
const banner = undefined;
|
|
247
173
|
const options = {
|
|
248
174
|
input: finalInput,
|
|
249
175
|
platform: 'node',
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
// and fails the `--app node` build with `Module not found`. The
|
|
259
|
-
// predicate here is pure exact membership, so the array is
|
|
260
|
-
// behaviourally identical under both engines AND JSON-serializable.
|
|
261
|
-
// (The gjs target follows the same rule: exact names as an array,
|
|
262
|
-
// plus an `externalsPlugin` resolveId hook for its gi://-prefix and
|
|
263
|
-
// register-subpath shape rules — see app/gjs.ts. The browser target
|
|
264
|
-
// already uses a plain array.)
|
|
265
|
-
// The function form is still used for `getAliasesForNode({ external })`
|
|
266
|
-
// above — that runs in-process and is never serialized.
|
|
176
|
+
// A plain string array, never the `external` predicate function:
|
|
177
|
+
// `@gjsify/rolldown-native` JSON.stringify's the options to its Rust core,
|
|
178
|
+
// so a function value is silently dropped and every `EXTERNALS_NODE` entry
|
|
179
|
+
// gets bundled — the build then descends into node-datachannel's
|
|
180
|
+
// `require('…/node_datachannel.node')` and fails with `Module not found`.
|
|
181
|
+
// Exact membership makes the array behaviourally identical on both engines.
|
|
182
|
+
// The function form survives only for `getAliasesForNode({ external })`,
|
|
183
|
+
// which runs in-process and is never serialized.
|
|
267
184
|
external: exactExternal,
|
|
268
185
|
resolve: {
|
|
269
186
|
mainFields: format === 'esm' ? ['module', 'main', 'browser'] : ['main', 'module', 'browser'],
|
|
270
|
-
// CJS-priority conditions
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
// their incomplete ESM wrapper.
|
|
187
|
+
// CJS-priority conditions. Rolldown takes the package's first matching
|
|
188
|
+
// key, so adding 'import' would route ws v8 (which lists 'import'
|
|
189
|
+
// before 'require') through its incomplete ESM wrapper.
|
|
274
190
|
conditionNames: format === 'esm' ? ['require', 'node', 'module'] : ['require'],
|
|
275
191
|
},
|
|
276
192
|
transform: {
|
|
@@ -285,76 +201,57 @@ export const setupForNode = async (input) => {
|
|
|
285
201
|
format,
|
|
286
202
|
sourcemap: false,
|
|
287
203
|
banner,
|
|
288
|
-
// Single-bundle output.
|
|
289
|
-
// deprecated `inlineDynamicImports: true`.
|
|
204
|
+
// Single-bundle output.
|
|
290
205
|
codeSplitting: false,
|
|
291
206
|
},
|
|
292
207
|
treeshake: true,
|
|
293
208
|
};
|
|
294
209
|
const plugins = [
|
|
295
|
-
// Virtual-entry plugin runs FIRST so its resolveId/load match the
|
|
296
|
-
//
|
|
297
|
-
//
|
|
210
|
+
// Virtual-entry plugin runs FIRST so its resolveId/load match the synthetic
|
|
211
|
+
// `\0gjsify-entry:` ids `wrapInputWithSideEffects` produces (no-op when
|
|
212
|
+
// nothing was injected).
|
|
298
213
|
...(virtualEntries.plugin ? [virtualEntries.plugin] : []),
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
// null for `@girs/*`.
|
|
214
|
+
// Claims `gi://Ns?version=X` (resolveId `pre` + array order) and rewrites it
|
|
215
|
+
// onto the `@gjsify/node-gi` runtime so a real GJS/GI source builds and runs
|
|
216
|
+
// on Node. Returns null for `@girs/*`.
|
|
303
217
|
gjsGiNodePlugin(),
|
|
304
|
-
// Bare GJS built-
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
// `exactExternal` array (the resolveId `external` flag is dropped under
|
|
310
|
-
// @gjsify/rolldown-native — see that const's doc-comment).
|
|
218
|
+
// Bare GJS built-ins: claimed BEFORE aliasPlugin so they resolve to the
|
|
219
|
+
// EXTERNAL `@gjsify/node-gi/<mod>` shims without touching disk (works
|
|
220
|
+
// without node-gi installed, like gi://). Node-target-only. The
|
|
221
|
+
// externalisation itself rides `NODE_GI_BARE_MODULE_SPECIFIERS` in
|
|
222
|
+
// `exactExternal` — see that const's doc comment.
|
|
311
223
|
gjsBuiltinModulesNodePlugin(ALIASES_GJS_FOR_NODE),
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// •
|
|
316
|
-
//
|
|
317
|
-
// `@
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
// whose inner `gi://` is rewritten to `requireGi` by gjsGiNodePlugin
|
|
324
|
-
// above. This routes `@girs/adw-1` → `requireGi('Adw','1')` WITHOUT
|
|
325
|
-
// a lowercased-pkg→namespace map (the proper-cased namespace comes
|
|
326
|
-
// from the @girs package's own `gi://` specifier). `gi://*` is
|
|
327
|
-
// already claimed by gjsGiNodePlugin, so this plugin's `gi://`
|
|
328
|
-
// branch is dead on the node target either way.
|
|
329
|
-
// `emptyGirs` also lifts for the explicit-register reverse-bridge case:
|
|
330
|
-
// the injected register bodies (dom-elements, canvas2d, …) are genuine
|
|
331
|
-
// GJS code whose `@girs/*` value imports must route through to
|
|
332
|
-
// `requireGi`, exactly like the ambient-globals case. Same predicate as
|
|
333
|
-
// the register-alias lift above — one question, one flag.
|
|
224
|
+
// Decides the fate of `@girs/*` before `aliasPlugin` and the default
|
|
225
|
+
// resolver (same composition order as `app/browser.ts`). `emptyGirs` is
|
|
226
|
+
// gated on `gjsSourceBuild`:
|
|
227
|
+
// • plain Node build → `@girs/*` → empty module, so a cross-platform
|
|
228
|
+
// polyfill whose GJS-only paths import `@girs/*` transitively (e.g. via
|
|
229
|
+
// `@gjsify/unit`) stays loadable without node-gi installed.
|
|
230
|
+
// • reverse-bridge build → `@girs/<ns>-<ver>` falls through to its real
|
|
231
|
+
// package body (`import Adw from 'gi://Adw?version=1'`), whose inner
|
|
232
|
+
// `gi://` gjsGiNodePlugin already rewrote — so `@girs/adw-1` reaches
|
|
233
|
+
// `requireGi('Adw','1')` with no lowercased-pkg→namespace map needed.
|
|
234
|
+
// This plugin's own `gi://` branch is dead on the node target.
|
|
334
235
|
gjsImportsEmptyPlugin({ emptyGirs: !gjsSourceBuild }),
|
|
335
236
|
aliasPlugin({ entries: aliasEntries }),
|
|
336
|
-
// Blueprint (.blp → XML string): the reverse bridge runs REAL GTK on
|
|
337
|
-
//
|
|
338
|
-
// `--app node` too.
|
|
339
|
-
// bundles. Same plugin the gjs/browser targets compose.
|
|
237
|
+
// Blueprint (.blp → XML string): the reverse bridge runs REAL GTK on Node,
|
|
238
|
+
// so a GJS app entry with composite-template windows must build for
|
|
239
|
+
// `--app node` too. Claims only `.blp` — inert for plain-Node bundles.
|
|
340
240
|
blueprintPlugin(),
|
|
341
241
|
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
// CSS parser rejects nested rules at runtime ("Expected ';'/an identifier"
|
|
348
|
-
// theme-parser warnings, styles silently dropped — the Learn6502 app-gnome
|
|
349
|
-
// symptom). A plain-Node bundle (no node-gi) keeps the CSS pristine.
|
|
242
|
+
// On a node-gi build (REAL GTK on Node) lower the CSS to GTK 4's subset just
|
|
243
|
+
// as `--app gjs` does, so nesting/`&` is FLATTENED before it reaches
|
|
244
|
+
// `Gtk.CssProvider.load_from_string` — GTK 4's parser otherwise rejects
|
|
245
|
+
// nested rules at runtime and silently drops the styles (the Learn6502
|
|
246
|
+
// app-gnome symptom). A plain-Node bundle keeps the CSS pristine.
|
|
350
247
|
cssAsStringPlugin(input.pluginOptions.nodeGiGlobalsInject ? { targets: { firefox: 60 << 16 } } : {}),
|
|
351
248
|
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
352
|
-
// `order: 'post'` — fires only for ids
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
249
|
+
// `order: 'post'` — fires only for ids headed to Rolldown's
|
|
250
|
+
// unresolved-import → external fallback. The node target has no
|
|
251
|
+
// `node:`-shaped symptom the emit-time guard could detect (its `node:`
|
|
252
|
+
// builtins ARE external by policy), so this is the ONLY thing between an
|
|
253
|
+
// unbuilt `@gjsify/*` workspace edge and a bundle that re-imports the bare
|
|
254
|
+
// specifier at runtime.
|
|
358
255
|
unresolvedWorkspaceImportPlugin({ target: 'node', aliases: aliasEntries, isExternal: external }),
|
|
359
256
|
];
|
|
360
257
|
return { options, plugins };
|