@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
|
@@ -1,107 +1,73 @@
|
|
|
1
1
|
// Detect free (unbound) global identifiers in bundled JS output.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Drives the `--globals auto` iterative build (`auto-globals.ts`): each analysis pass
|
|
4
|
+
// bundles UNMINIFIED and without injection, this module parses that output with acorn
|
|
5
|
+
// and finds references to known GJS globals that are not locally declared, and the
|
|
6
|
+
// result feeds the next pass's inject stub so only actually-needed globals are
|
|
7
|
+
// registered. Minification is forbidden in those passes because it aliases
|
|
8
|
+
// `globalThis` to a short variable and defeats the MemberExpression patterns below.
|
|
8
9
|
import * as acorn from 'acorn';
|
|
9
10
|
import * as walk from 'acorn-walk';
|
|
10
11
|
import { GJS_GLOBALS_MAP } from '@gjsify/resolve-npm/globals-map';
|
|
11
12
|
const KNOWN_GLOBALS = new Set(Object.keys(GJS_GLOBALS_MAP));
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* `<host>.<method>` patterns that must inject a global even though the identifier
|
|
15
|
+
* itself never appears in the bundle: a project calling `navigator.getGamepads()`
|
|
16
|
+
* references no gamepad identifier, yet still needs `@gjsify/gamepad/register` to
|
|
17
|
+
* patch the method onto `navigator`.
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* needs `@gjsify/gamepad/register` to patch `navigator` with the method.
|
|
20
|
-
* This marker maps `navigator.getGamepads` → inject the `GamepadEvent`
|
|
21
|
-
* register path (which is the gamepad package's register entry).
|
|
22
|
-
*
|
|
23
|
-
* Keyed by `host.method` (lowercase host, exact method name). Values are
|
|
24
|
-
* KNOWN_GLOBALS identifiers — the detector adds them as free globals if
|
|
25
|
-
* the corresponding member expression is found in the bundle.
|
|
19
|
+
* Keyed `host.method` (exact names); values are KNOWN_GLOBALS identifiers the detector
|
|
20
|
+
* adds as free globals when the member expression is found.
|
|
26
21
|
*/
|
|
27
22
|
const METHOD_MARKERS = {
|
|
28
|
-
//
|
|
23
|
+
// Patched on by @gjsify/gamepad/register
|
|
29
24
|
'navigator.getGamepads': 'GamepadEvent',
|
|
30
|
-
//
|
|
25
|
+
// Patched on by @gjsify/webrtc/register/media-devices
|
|
31
26
|
'navigator.mediaDevices': 'MediaDevices',
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// synchronous `new WebAssembly.{Module,Instance}` constructors.
|
|
27
|
+
// The runtime stubs throw at first call, so any reference needs the
|
|
28
|
+
// `@gjsify/webassembly` polyfill, whose register replaces them with wrappers around
|
|
29
|
+
// the working synchronous `new WebAssembly.{Module,Instance}` constructors.
|
|
36
30
|
'WebAssembly.compile': 'WebAssembly',
|
|
37
31
|
'WebAssembly.compileStreaming': 'WebAssembly',
|
|
38
32
|
'WebAssembly.instantiate': 'WebAssembly',
|
|
39
33
|
'WebAssembly.instantiateStreaming': 'WebAssembly',
|
|
40
34
|
'WebAssembly.validate': 'WebAssembly',
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
// @gjsify/node-globals/register/url in GJS_GLOBALS_MAP) already pulls in
|
|
45
|
-
// the correct register module.
|
|
35
|
+
// `URL.createObjectURL`/`revokeObjectURL` need no marker: they are first-class
|
|
36
|
+
// statics on @gjsify/url's URL class, so the directly-detected free `URL` already
|
|
37
|
+
// pulls the right register module.
|
|
46
38
|
};
|
|
47
39
|
/**
|
|
48
|
-
* wasm-bindgen
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* function __wbg_crypto_574e78ad8b13b65f(arg0) {
|
|
55
|
-
* const ret = getObject(arg0).crypto;
|
|
56
|
-
* return addHeapObject(ret);
|
|
57
|
-
* }
|
|
58
|
-
*
|
|
59
|
-
* `getObject(arg0)` is a runtime heap dereference (the object is one
|
|
60
|
-
* of the host bridges registered by wasm-bindgen at init time —
|
|
61
|
-
* typically `globalThis`, `window`, or `self`). The MemberExpression
|
|
62
|
-
* visitor can't follow that — `node.object` is a CallExpression, not
|
|
63
|
-
* an Identifier — so the underlying `.crypto` access is invisible to
|
|
64
|
-
* the static scan.
|
|
40
|
+
* wasm-bindgen function-name patterns that must inject a global. wasm-bindgen emits its
|
|
41
|
+
* host-API import bindings as top-level `__wbg_<jsName>_<hash>` functions whose body is
|
|
42
|
+
* `getObject(arg0).<jsName>` — a runtime heap dereference the MemberExpression visitor
|
|
43
|
+
* cannot follow (`node.object` is a CallExpression), so the underlying property access
|
|
44
|
+
* is invisible to the static scan.
|
|
65
45
|
*
|
|
66
|
-
* Matching
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* package surfaces a needed global that the static scan misses.
|
|
71
|
-
*
|
|
72
|
-
* Keyed by the `<jsName>` extracted from the `__wbg_<jsName>_<hash>`
|
|
73
|
-
* function name; value is the gjsify global to inject.
|
|
46
|
+
* Matching the FUNCTION NAME instead is high precision (`__wbg_` is
|
|
47
|
+
* wasm-bindgen-reserved) and high recall (the names are stable across versions). Add an
|
|
48
|
+
* entry whenever a wasm-bindgen-built package needs a global the scan misses. Keyed by
|
|
49
|
+
* `<jsName>`; value is the gjsify global to inject.
|
|
74
50
|
*/
|
|
75
51
|
const WASM_BINDGEN_MARKERS = {
|
|
76
|
-
// crypto
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
// randomness or hashing. Loro is the driving real-world consumer:
|
|
80
|
-
// its CRDT operations need crypto.getRandomValues for peer-id
|
|
81
|
-
// generation and ChangeID nonces.
|
|
52
|
+
// wasm-bindgen's canonical crypto-import binding chain — ed25519-dalek, ring, rand
|
|
53
|
+
// and most Rust crates touching randomness or hashing. Loro drives it: its CRDT ops
|
|
54
|
+
// need crypto.getRandomValues for peer ids and ChangeID nonces.
|
|
82
55
|
crypto: 'crypto',
|
|
83
56
|
getRandomValues: 'crypto',
|
|
84
|
-
// Legacy IE
|
|
85
|
-
//
|
|
86
|
-
// (we ship a real `crypto`), but flagging the marker keeps the
|
|
87
|
-
// detector self-documenting.
|
|
57
|
+
// Legacy IE fallback wasm-bindgen probes when `crypto` is absent. Irrelevant to GJS
|
|
58
|
+
// (we ship a real `crypto`); mapped so the marker set is self-documenting.
|
|
88
59
|
msCrypto: 'crypto',
|
|
89
60
|
};
|
|
90
61
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* 8–16 hex chars in practice, but we accept any alphanumeric trailer
|
|
94
|
-
* so the pattern survives future format tweaks.
|
|
62
|
+
* The `<jsName>` part of `__wbg_<jsName>_<hash>`, or null. Hashes are 8–16 hex chars in
|
|
63
|
+
* practice; any alphanumeric trailer is accepted so the pattern survives format tweaks.
|
|
95
64
|
*/
|
|
96
65
|
const WBG_NAME_RE = /^__wbg_([A-Za-z][A-Za-z0-9]*)_[A-Za-z0-9]+$/;
|
|
97
66
|
function wbgJsNameFor(fnName) {
|
|
98
67
|
const match = fnName.match(WBG_NAME_RE);
|
|
99
68
|
return match?.[1] ?? null;
|
|
100
69
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Extract all bound names from a binding pattern
|
|
103
|
-
* (Identifier, ObjectPattern, ArrayPattern, AssignmentPattern, RestElement).
|
|
104
|
-
*/
|
|
70
|
+
/** Every name bound by a binding pattern. */
|
|
105
71
|
function extractBindingNames(node) {
|
|
106
72
|
if (!node)
|
|
107
73
|
return [];
|
|
@@ -123,10 +89,20 @@ function extractBindingNames(node) {
|
|
|
123
89
|
}
|
|
124
90
|
}
|
|
125
91
|
/**
|
|
126
|
-
* Core scanner
|
|
127
|
-
* `detectGjsAmbientGlobals` (the `--app node` GJS-ambient set)
|
|
128
|
-
*
|
|
129
|
-
*
|
|
92
|
+
* Core scanner behind `detectFreeGlobals` (web/Node globals map) and
|
|
93
|
+
* `detectGjsAmbientGlobals` (the `--app node` GJS-ambient set), parametrised so the node
|
|
94
|
+
* path can use a different known-globals set and the stricter host-member handling.
|
|
95
|
+
*
|
|
96
|
+
* "Free" = referenced but never declared in the module
|
|
97
|
+
* (var/let/const/function/class/import/param/catch).
|
|
98
|
+
*
|
|
99
|
+
* A `typeof X` presence-check guard (`typeof document !== 'undefined'`) that is the SOLE
|
|
100
|
+
* occurrence of `X` does NOT count as a use. Such guards are pervasive in isomorphic npm
|
|
101
|
+
* packages; on GJS the guarded branch is dead, but a bundler cannot DCE a `typeof`
|
|
102
|
+
* expression since its value depends on the runtime. Injecting for one of those can hard-
|
|
103
|
+
* error the analysis bundle when the polyfill package is not installed. If `X` also
|
|
104
|
+
* appears in a genuine reference position the injection still fires; the resolvability
|
|
105
|
+
* gate in `auto-globals.ts` covers the remaining case.
|
|
130
106
|
*/
|
|
131
107
|
function scanFreeGlobals(code, opts) {
|
|
132
108
|
const knownGlobals = opts.knownGlobals;
|
|
@@ -135,14 +111,11 @@ function scanFreeGlobals(code, opts) {
|
|
|
135
111
|
const ast = acorn.parse(code, {
|
|
136
112
|
ecmaVersion: 'latest',
|
|
137
113
|
sourceType: 'module',
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// `#!/usr/bin/env -S gjs -m` shebang hoisted to byte 0.
|
|
141
|
-
// Acorn rejects shebangs by default; allow them so the
|
|
142
|
-
// free-globals analyzer doesn't choke on its own input.
|
|
114
|
+
// Acorn rejects shebangs by default, and any project bundling its own CLI gets
|
|
115
|
+
// one hoisted to byte 0.
|
|
143
116
|
allowHashBang: true,
|
|
144
117
|
});
|
|
145
|
-
//
|
|
118
|
+
// Pass 1: every declared name in the module.
|
|
146
119
|
const declaredNames = new Set();
|
|
147
120
|
walk.simple(ast, {
|
|
148
121
|
VariableDeclarator(node) {
|
|
@@ -196,24 +169,17 @@ function scanFreeGlobals(code, opts) {
|
|
|
196
169
|
}
|
|
197
170
|
},
|
|
198
171
|
});
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// `
|
|
202
|
-
// config replaces `global`/`window` with `globalThis`, but we accept
|
|
203
|
-
// all four host-object names for safety (esbuild also never renames
|
|
204
|
-
// these because they are language keywords / pre-defined globals).
|
|
172
|
+
// Pass 2: Identifier nodes in reference position, plus `host.X` MemberExpressions
|
|
173
|
+
// where X is a known global. `transform.define` already maps `global`/`window` to
|
|
174
|
+
// `globalThis`, but every host-object name is accepted for safety.
|
|
205
175
|
const freeGlobals = new Set();
|
|
206
|
-
// Globals seen
|
|
207
|
-
//
|
|
208
|
-
// position. Names that remain here at the end were only ever
|
|
209
|
-
// presence-checked — not genuinely used — and are excluded from the
|
|
210
|
-
// injection set (see JSDoc above).
|
|
176
|
+
// Globals seen ONLY inside `typeof X` so far; promoted to freeGlobals the moment the
|
|
177
|
+
// name appears in a real-use position, and excluded from injection if it never does.
|
|
211
178
|
const typeofGuardGlobals = new Set();
|
|
212
179
|
const HOST_OBJECTS = new Set(['globalThis', 'global', 'window', 'self', 'globalObject']);
|
|
213
180
|
walk.ancestor(ast, {
|
|
214
181
|
MemberExpression(node) {
|
|
215
|
-
//
|
|
216
|
-
// property is then a dynamic Expression, not a known name.
|
|
182
|
+
// Dot-access only: a computed property is a dynamic Expression, not a name.
|
|
217
183
|
if (node.computed)
|
|
218
184
|
return;
|
|
219
185
|
if (node.object.type !== 'Identifier')
|
|
@@ -222,12 +188,8 @@ function scanFreeGlobals(code, opts) {
|
|
|
222
188
|
return;
|
|
223
189
|
const objName = node.object.name;
|
|
224
190
|
const propName = node.property.name;
|
|
225
|
-
// Pattern A:
|
|
226
|
-
// The property is a known global identifier itself.
|
|
191
|
+
// Pattern A: `<host>.X` where the property is itself a known global.
|
|
227
192
|
if (HOST_OBJECTS.has(objName)) {
|
|
228
|
-
// The node GJS-ambient detector ignores host-member forms — they
|
|
229
|
-
// are the isomorphic-guard shape, not genuine GJS source (see
|
|
230
|
-
// `ignoreHostMembers`). Bare-identifier detection still applies.
|
|
231
193
|
if (opts.ignoreHostMembers)
|
|
232
194
|
return;
|
|
233
195
|
if (knownGlobals.has(propName)) {
|
|
@@ -235,10 +197,7 @@ function scanFreeGlobals(code, opts) {
|
|
|
235
197
|
}
|
|
236
198
|
return;
|
|
237
199
|
}
|
|
238
|
-
// Pattern B:
|
|
239
|
-
// `navigator.getGamepads` → marker map forwards to a global
|
|
240
|
-
// identifier that triggers the right register path even though
|
|
241
|
-
// the identifier itself never appears in the bundle.
|
|
200
|
+
// Pattern B: METHOD_MARKERS.
|
|
242
201
|
const markerKey = `${objName}.${propName}`;
|
|
243
202
|
const markerTarget = methodMarkers[markerKey];
|
|
244
203
|
if (markerTarget && knownGlobals.has(markerTarget)) {
|
|
@@ -246,13 +205,8 @@ function scanFreeGlobals(code, opts) {
|
|
|
246
205
|
}
|
|
247
206
|
},
|
|
248
207
|
FunctionDeclaration(node) {
|
|
249
|
-
// Pattern C:
|
|
250
|
-
//
|
|
251
|
-
// host API. The body would be `getObject(arg0).<jsName>` —
|
|
252
|
-
// an unfollowable runtime heap dereference — but the
|
|
253
|
-
// function NAME tells us exactly which global is needed.
|
|
254
|
-
// See WASM_BINDGEN_MARKERS for the why + which jsNames are
|
|
255
|
-
// mapped.
|
|
208
|
+
// Pattern C: WASM_BINDGEN_MARKERS, keyed on the function NAME because the
|
|
209
|
+
// body's property access is an unfollowable heap dereference.
|
|
256
210
|
if (!node.id)
|
|
257
211
|
return;
|
|
258
212
|
const jsName = wbgJsNameFor(node.id.name);
|
|
@@ -265,14 +219,11 @@ function scanFreeGlobals(code, opts) {
|
|
|
265
219
|
},
|
|
266
220
|
Identifier(node, ancestors) {
|
|
267
221
|
const name = node.name;
|
|
268
|
-
// Quick filter: only check known globals
|
|
269
222
|
if (!knownGlobals.has(name))
|
|
270
223
|
return;
|
|
271
|
-
// Skip if locally declared
|
|
272
224
|
if (declaredNames.has(name))
|
|
273
225
|
return;
|
|
274
|
-
//
|
|
275
|
-
// by checking the parent node.
|
|
226
|
+
// Reference position is decided from the parent node.
|
|
276
227
|
const parent = ancestors[ancestors.length - 2];
|
|
277
228
|
if (!parent) {
|
|
278
229
|
freeGlobals.add(name);
|
|
@@ -293,7 +244,6 @@ function scanFreeGlobals(code, opts) {
|
|
|
293
244
|
return;
|
|
294
245
|
break;
|
|
295
246
|
}
|
|
296
|
-
// Method/property definitions in classes
|
|
297
247
|
case 'MethodDefinition':
|
|
298
248
|
case 'PropertyDefinition': {
|
|
299
249
|
const def = parent;
|
|
@@ -315,8 +265,7 @@ function scanFreeGlobals(code, opts) {
|
|
|
315
265
|
return;
|
|
316
266
|
break;
|
|
317
267
|
}
|
|
318
|
-
// Declaration ids
|
|
319
|
-
// are already in declaredNames, but guard anyway
|
|
268
|
+
// Declaration ids are already in declaredNames; guarded anyway.
|
|
320
269
|
case 'FunctionDeclaration':
|
|
321
270
|
case 'FunctionExpression':
|
|
322
271
|
case 'ClassDeclaration':
|
|
@@ -332,15 +281,11 @@ function scanFreeGlobals(code, opts) {
|
|
|
332
281
|
return;
|
|
333
282
|
break;
|
|
334
283
|
}
|
|
335
|
-
// import { X } / import X — already in declaredNames
|
|
336
284
|
case 'ImportSpecifier':
|
|
337
285
|
case 'ImportDefaultSpecifier':
|
|
338
286
|
case 'ImportNamespaceSpecifier':
|
|
339
287
|
return;
|
|
340
|
-
// `typeof X` — presence-check guard
|
|
341
|
-
// typeof-guard set and only promote to freeGlobals if the
|
|
342
|
-
// same name also appears in a real-use position elsewhere
|
|
343
|
-
// in the bundle. See JSDoc on detectFreeGlobals.
|
|
288
|
+
// `typeof X` — presence-check guard, deferred until a real use shows up.
|
|
344
289
|
case 'UnaryExpression': {
|
|
345
290
|
const unary = parent;
|
|
346
291
|
if (unary.operator === 'typeof') {
|
|
@@ -352,8 +297,7 @@ function scanFreeGlobals(code, opts) {
|
|
|
352
297
|
break;
|
|
353
298
|
}
|
|
354
299
|
}
|
|
355
|
-
// Real
|
|
356
|
-
// this name was previously seen only as a typeof argument.
|
|
300
|
+
// Real use — promote out of the typeof-guard set.
|
|
357
301
|
typeofGuardGlobals.delete(name);
|
|
358
302
|
freeGlobals.add(name);
|
|
359
303
|
},
|
|
@@ -361,9 +305,8 @@ function scanFreeGlobals(code, opts) {
|
|
|
361
305
|
return freeGlobals;
|
|
362
306
|
}
|
|
363
307
|
/**
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* `--app gjs --globals auto` injection. See `scanFreeGlobals` for the rules.
|
|
308
|
+
* Free identifiers in bundled output that match the known web/Node globals in
|
|
309
|
+
* `GJS_GLOBALS_MAP` — drives `--app gjs --globals auto`. Rules: `scanFreeGlobals`.
|
|
367
310
|
*/
|
|
368
311
|
export function detectFreeGlobals(code) {
|
|
369
312
|
return scanFreeGlobals(code, {
|
|
@@ -373,14 +316,9 @@ export function detectFreeGlobals(code) {
|
|
|
373
316
|
});
|
|
374
317
|
}
|
|
375
318
|
/**
|
|
376
|
-
* The
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* `@gjsify/node-gi/globals` seeds these on `globalThis` when imported.
|
|
380
|
-
*
|
|
381
|
-
* This is the REVERSE of `GJS_GLOBALS_MAP` (which maps Node/Web globals onto
|
|
382
|
-
* GJS): it's the set the `--app node` target injects the node-gi globals shim
|
|
383
|
-
* for. Kept here next to the detector so the two never drift.
|
|
319
|
+
* The globals that exist implicitly under gjs but not on Node, seeded on `globalThis` by
|
|
320
|
+
* `@gjsify/node-gi/globals`. The REVERSE of `GJS_GLOBALS_MAP`: the set whose presence
|
|
321
|
+
* makes `--app node` inject that shim. Kept beside the detector so the two never drift.
|
|
384
322
|
*/
|
|
385
323
|
export const GJS_AMBIENT_GLOBALS = new Set([
|
|
386
324
|
'print',
|
|
@@ -391,21 +329,12 @@ export const GJS_AMBIENT_GLOBALS = new Set([
|
|
|
391
329
|
'imports',
|
|
392
330
|
]);
|
|
393
331
|
/**
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
* `@gjsify/node-gi/globals` injected.
|
|
332
|
+
* References to `GJS_AMBIENT_GLOBALS` in a bundled `--app node` chunk — the signal that
|
|
333
|
+
* the bundle needs `@gjsify/node-gi/globals`.
|
|
397
334
|
*
|
|
398
|
-
* Runs on the
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* GJS-source shape (`print(...)`, `imports.gi.Gtk`). The `globalThis.imports` /
|
|
402
|
-
* `globalThis.print` host-member form is the cross-platform isomorphic-GUARD
|
|
403
|
-
* shape (`if (typeof globalThis.imports !== 'undefined') { … }`) and is
|
|
404
|
-
* deliberately ignored: injecting `@gjsify/node-gi/globals` for it would break
|
|
405
|
-
* plain-Node loadability of a package that merely gates GJS usage (the bare
|
|
406
|
-
* external import is unresolvable without node-gi; the shim eagerly loads the
|
|
407
|
-
* native addon). Pure `typeof imports` probes are deferred too. See
|
|
408
|
-
* `ignoreHostMembers`.
|
|
335
|
+
* Runs on the output AFTER tree-shaking, so a reference behind a statically-dead branch
|
|
336
|
+
* does not trigger injection, and matches only BARE identifiers (`print(...)`,
|
|
337
|
+
* `imports.gi.Gtk`) — see `ignoreHostMembers` for why the host-member form is ignored.
|
|
409
338
|
*/
|
|
410
339
|
export function detectGjsAmbientGlobals(code) {
|
|
411
340
|
return scanFreeGlobals(code, {
|
|
@@ -413,25 +342,21 @@ export function detectGjsAmbientGlobals(code) {
|
|
|
413
342
|
ignoreHostMembers: true,
|
|
414
343
|
});
|
|
415
344
|
}
|
|
416
|
-
/** The node-gi reverse-bridge package every `--app node` GJS shim resolves to. */
|
|
417
345
|
import { ALIASES_GJS_FOR_NODE } from '@gjsify/resolve-npm';
|
|
346
|
+
/** The node-gi reverse-bridge package every `--app node` GJS shim resolves to. */
|
|
418
347
|
const NODE_GI_PACKAGE = '@gjsify/node-gi';
|
|
419
348
|
/**
|
|
420
|
-
* The BARE GJS built-ins
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* Why both spellings must count: the analysis pass that calls this runs with
|
|
424
|
-
* `nodeGiGlobalsInject` still FALSE, so the GJS-source routing that rewrites
|
|
425
|
-
* `system` → `@gjsify/node-gi/system` has not happened yet. Looking only for
|
|
426
|
-
* the aliased form makes the signal appear only AFTER the decision it is
|
|
427
|
-
* supposed to trigger — chicken-and-egg, and the same shape as the bug this
|
|
428
|
-
* detector was added to fix.
|
|
349
|
+
* The BARE GJS built-ins — the PRE-ALIAS spelling of the same fact, derived from the
|
|
350
|
+
* alias table so the two cannot drift.
|
|
429
351
|
*
|
|
430
|
-
*
|
|
431
|
-
* `
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* `
|
|
352
|
+
* Both spellings must count because the analysis pass that calls this runs with
|
|
353
|
+
* `nodeGiGlobalsInject` still FALSE: the routing that rewrites `system` →
|
|
354
|
+
* `@gjsify/node-gi/system` has not happened yet, so looking only for the aliased form
|
|
355
|
+
* makes the signal appear AFTER the decision it is supposed to trigger. Measured on
|
|
356
|
+
* adwaita-storybook `--app node`: the final bundle carried `import e from
|
|
357
|
+
* "@gjsify/node-gi/system"` and zero `gi://` — inject never fired, `@girs/*` were
|
|
358
|
+
* emptied to `{}`, nothing was left to rewrite. It passes `node --check` and dies at
|
|
359
|
+
* runtime with `class extends undefined`.
|
|
435
360
|
*/
|
|
436
361
|
const GJS_BARE_BUILTINS = new Set(Object.keys(ALIASES_GJS_FOR_NODE));
|
|
437
362
|
/**
|
|
@@ -446,30 +371,26 @@ function isNodeGiSpecifier(source) {
|
|
|
446
371
|
return GJS_BARE_BUILTINS.has(source);
|
|
447
372
|
}
|
|
448
373
|
/**
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
* `detectGjsAmbientGlobals`.
|
|
374
|
+
* A STATIC import of `@gjsify/node-gi` (root or subpath) in a bundled `--app node`
|
|
375
|
+
* chunk — the second genuine-GJS-source signal beside `detectGjsAmbientGlobals`.
|
|
452
376
|
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* `
|
|
461
|
-
* bodies and its globals shim in one go — `gjsify storybook --runtime node`
|
|
462
|
-
* emitted a bundle with no `requireGi` at all.
|
|
377
|
+
* It is a signal because the bare GJS built-ins are rewritten to
|
|
378
|
+
* `@gjsify/node-gi/<name>` and kept EXTERNAL as top-level imports: a tree-shaken graph
|
|
379
|
+
* that retains one is ALREADY bound to the bridge at load (Node resolves the external
|
|
380
|
+
* at link time and fails without node-gi), so treating it as a GJS-source build cannot
|
|
381
|
+
* cost plain-Node loadability. NOT treating it as one is how a portable GJS app
|
|
382
|
+
* spelling `ARGV` as `system.programArgs` lost its `@girs/*` bodies and its globals
|
|
383
|
+
* shim in one go — `gjsify storybook --runtime node` emitted a bundle with no
|
|
384
|
+
* `requireGi` at all.
|
|
463
385
|
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
* and flipping injection for it would break exactly that.
|
|
386
|
+
* A surviving `gi://` import is deliberately NOT a signal: its shim loads node-gi LAZILY
|
|
387
|
+
* through a `require('@gjsify/node-gi/gi')` CALL, so this scan is structurally blind to
|
|
388
|
+
* it by design. A cross-platform package with a gjs-gated `gi://` import stays loadable
|
|
389
|
+
* on plain Node (#641, pinned by `tests/e2e/node-gi-globals-inject`), and flipping
|
|
390
|
+
* injection for it would break exactly that.
|
|
470
391
|
*
|
|
471
|
-
* Scans import/re-export STATEMENTS via the AST
|
|
472
|
-
*
|
|
392
|
+
* Scans import/re-export STATEMENTS via the AST, never text, so a quoted specifier in a
|
|
393
|
+
* test name or template cannot trigger.
|
|
473
394
|
*/
|
|
474
395
|
export function detectNodeGiModuleImports(code) {
|
|
475
396
|
const ast = acorn.parse(code, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/rolldown-plugin-gjsify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
],
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@gjsify/console": "^0.
|
|
59
|
-
"@gjsify/resolve-npm": "^0.
|
|
60
|
-
"@gjsify/rolldown-plugin-deepkit": "^0.
|
|
61
|
-
"@gjsify/rolldown-plugin-pnp": "^0.
|
|
62
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
58
|
+
"@gjsify/console": "^0.38.0",
|
|
59
|
+
"@gjsify/resolve-npm": "^0.38.0",
|
|
60
|
+
"@gjsify/rolldown-plugin-deepkit": "^0.38.0",
|
|
61
|
+
"@gjsify/rolldown-plugin-pnp": "^0.38.0",
|
|
62
|
+
"@gjsify/vite-plugin-blueprint": "^0.38.0",
|
|
63
63
|
"@rollup/pluginutils": "^5.4.0",
|
|
64
64
|
"acorn": "^8.17.0",
|
|
65
65
|
"acorn-walk": "^8.3.5",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"sass": "^1.101.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@gjsify/lightningcss-native": "^0.
|
|
71
|
+
"@gjsify/lightningcss-native": "^0.38.0",
|
|
72
72
|
"rolldown": "^1.1.4"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|