@gjsify/rolldown-plugin-gjsify 0.43.0 → 0.45.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.js +28 -0
- package/lib/app/nativescript.js +2 -2
- package/lib/app/node.d.ts +5 -2
- package/lib/app/node.js +118 -2
- package/lib/index.d.ts +9 -1
- package/lib/index.js +5 -1
- package/lib/plugins/platform-resolve.d.ts +133 -8
- package/lib/plugins/platform-resolve.js +214 -30
- package/lib/plugins/react-native-alias.d.ts +63 -0
- package/lib/plugins/react-native-alias.js +128 -0
- package/lib/plugins/react-native-gate.d.ts +117 -0
- package/lib/plugins/react-native-gate.js +240 -0
- package/lib/plugins/rn-route-manifest.d.ts +67 -0
- package/lib/plugins/rn-route-manifest.js +166 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/plugin-options.d.ts +20 -0
- package/lib/types/source-dialect.d.ts +42 -0
- package/lib/types/source-dialect.js +21 -0
- package/lib/utils/scan-named-imports.d.ts +47 -0
- package/lib/utils/scan-named-imports.js +239 -0
- package/package.json +15 -9
package/lib/app/gjs.js
CHANGED
|
@@ -9,6 +9,9 @@ import { aliasPlugin } from '../plugins/alias.js';
|
|
|
9
9
|
import { externalsPlugin } from '../plugins/externals.js';
|
|
10
10
|
import { napiNodeAddonPlugin } from '../plugins/napi-node-addon.js';
|
|
11
11
|
import { unresolvedWorkspaceImportPlugin } from '../plugins/unresolved-workspace-import.js';
|
|
12
|
+
import { platformResolvePlugin, desktopSuffixChain, desktopOsSuffix, DESKTOP_REFUSED_SUFFIXES, } from '../plugins/platform-resolve.js';
|
|
13
|
+
import { reactNativeAliasPlugin } from '../plugins/react-native-alias.js';
|
|
14
|
+
import { reactNativeSupportGatePlugin } from '../plugins/react-native-gate.js';
|
|
12
15
|
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
13
16
|
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
14
17
|
import { getAliasesForGjs } from '../utils/alias.js';
|
|
@@ -161,6 +164,15 @@ export const setupForGjs = async (input) => {
|
|
|
161
164
|
// Reflection reads the ORIGINAL TypeScript, so it runs before any user
|
|
162
165
|
// transform can strip the annotations out from under it (see `GjsifyConfig`).
|
|
163
166
|
const prePlugins = [deepkitPlugin({ reflection: input.pluginOptions.reflection })];
|
|
167
|
+
// ADR 0032 § 8's build-time gate reads the ORIGINAL source for the same reason
|
|
168
|
+
// reflection does: `import type { ViewProps }` is what tells it a name costs
|
|
169
|
+
// nothing, and a normal-order transform may already have stripped it.
|
|
170
|
+
// This target composes a dialect, so it is one of `DIALECT_APPS` — the set
|
|
171
|
+
// the CLI refuses `--dialect` against for every OTHER target. Adding a
|
|
172
|
+
// dialect here without adding the target there means a flag accepted and
|
|
173
|
+
// silently ignored.
|
|
174
|
+
if (input.pluginOptions.dialect === 'react-native')
|
|
175
|
+
prePlugins.push(reactNativeSupportGatePlugin());
|
|
164
176
|
const plugins = [
|
|
165
177
|
// Virtual-entry plugin runs FIRST so its resolveId/load match the
|
|
166
178
|
// synthetic input ids that `wrapInputWithSideEffects` produces.
|
|
@@ -170,6 +182,22 @@ export const setupForGjs = async (input) => {
|
|
|
170
182
|
// banner, and acorn (the auto-globals detector) rejects the `#` byte.
|
|
171
183
|
// The final-output shebang is composed by shebangPlugin's renderChunk.
|
|
172
184
|
inputShebangStripPlugin(),
|
|
185
|
+
// Platform-file forks for the desktop, ADR 0032 § 9: `.gtk` → `.<os>` →
|
|
186
|
+
// `.desktop` → base. BEFORE the alias layer, so a platform fork of a
|
|
187
|
+
// module that also has a Node-builtin substitution wins over the
|
|
188
|
+
// substitution — the fork is the more specific statement. `.native` and
|
|
189
|
+
// `.web` are deliberately absent from the chain and warned about when
|
|
190
|
+
// present; `DESKTOP_REFUSED_SUFFIXES` carries the reason.
|
|
191
|
+
platformResolvePlugin({
|
|
192
|
+
suffixes: desktopSuffixChain(desktopOsSuffix()),
|
|
193
|
+
refusedSuffixes: DESKTOP_REFUSED_SUFFIXES,
|
|
194
|
+
siblingIndex: true,
|
|
195
|
+
}),
|
|
196
|
+
// ADR 0032 § 2's alias line, only when the consumer asked for it
|
|
197
|
+
// (`--dialect react-native`). `pre`, ahead of the substitution table and the
|
|
198
|
+
// externals policy — a redirect after `externalsPlugin` would find the
|
|
199
|
+
// specifier already externalised.
|
|
200
|
+
...(input.pluginOptions.dialect === 'react-native' ? [reactNativeAliasPlugin()] : []),
|
|
173
201
|
// random-access-file's 'browser' field maps to a throwing stub; the alias
|
|
174
202
|
// table forces the fs-backed Node entry.
|
|
175
203
|
aliasPlugin({ entries: aliasEntries }),
|
package/lib/app/nativescript.js
CHANGED
|
@@ -37,7 +37,7 @@ import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
|
37
37
|
import { ALIASES_NODE_FOR_NATIVESCRIPT, getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
38
38
|
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
39
39
|
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
40
|
-
import { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from '../plugins/platform-resolve.js';
|
|
40
|
+
import { platformResolvePlugin, nativescriptSuffixChain, detectNativescriptPlatform, nativescriptPlatformDefines, } from '../plugins/platform-resolve.js';
|
|
41
41
|
import { unresolvedWorkspaceImportPlugin } from '../plugins/unresolved-workspace-import.js';
|
|
42
42
|
// Never bundled: an app works only with the ONE core instance the runtime boots, and what
|
|
43
43
|
// this target emits is an ENTRY the NS bundler resolves it for. An optional peer absent from
|
|
@@ -132,7 +132,7 @@ export const setupForNativescript = async (input) => {
|
|
|
132
132
|
// Platform-specific source variants (`*.android` / `*.ios` /
|
|
133
133
|
// `*.native`) win over the base file — resolved BEFORE the Node-builtin
|
|
134
134
|
// alias routing so a platform fork of a portable module is honored.
|
|
135
|
-
platformResolvePlugin({ platform }),
|
|
135
|
+
platformResolvePlugin({ suffixes: nativescriptSuffixChain(platform) }),
|
|
136
136
|
aliasPlugin({ entries: aliasEntries }),
|
|
137
137
|
// NO blueprintPlugin — Blueprint is a GTK-specific UI DSL
|
|
138
138
|
// NO cssAsStringPlugin — NS ships its own CSS pipeline via
|
package/lib/app/node.d.ts
CHANGED
|
@@ -6,12 +6,15 @@ import type { PluginOptions } from '../types/plugin-options.js';
|
|
|
6
6
|
* factory, matchMedia, …) over `@gjsify/node-gi`, not the default `@gjsify/empty`
|
|
7
7
|
* stubs that keep CROSS-PLATFORM node bundles loadable on plain Node.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Three adjustments over the standard node alias map:
|
|
10
10
|
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the register
|
|
11
11
|
* import resolves to its real body;
|
|
12
12
|
* 2. the gjs target's bare→scoped register routes (`xmlhttprequest/register` →
|
|
13
13
|
* `@gjsify/fetch/register/xhr`, …) are merged in so the inject stub's bare
|
|
14
|
-
* specifiers resolve on node exactly as they do on gjs
|
|
14
|
+
* specifiers resolve on node exactly as they do on gjs;
|
|
15
|
+
* 3. `REVERSE_BRIDGE_ENTRY_ROUTES` is merged in, so a package whose `node`
|
|
16
|
+
* export condition is a different program (solid-js's SSR build) is routed
|
|
17
|
+
* to the entry the `--app gjs` build of the same source gets.
|
|
15
18
|
*
|
|
16
19
|
* Applied to the BASE map only, when `isGjsSourceBuild` — `pluginOptions.aliases` /
|
|
17
20
|
* user aliases still override, and a non-reverse-bridge build is untouched.
|
package/lib/app/node.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// `--app node` Rolldown configuration factory.
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import { aliasPlugin } from '../plugins/alias.js';
|
|
3
4
|
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
4
5
|
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
@@ -10,6 +11,9 @@ import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
|
10
11
|
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
11
12
|
import { gjsGiNodePlugin, gjsBuiltinModulesNodePlugin } from '../plugins/gjs-gi-node.js';
|
|
12
13
|
import { unresolvedWorkspaceImportPlugin } from '../plugins/unresolved-workspace-import.js';
|
|
14
|
+
import { platformResolvePlugin, desktopSuffixChain, desktopOsSuffix, DESKTOP_REFUSED_SUFFIXES, } from '../plugins/platform-resolve.js';
|
|
15
|
+
import { reactNativeAliasPlugin } from '../plugins/react-native-alias.js';
|
|
16
|
+
import { reactNativeSupportGatePlugin } from '../plugins/react-native-gate.js';
|
|
13
17
|
import { wrapInputWithSideEffects } from '../utils/entry-wrapper.js';
|
|
14
18
|
/**
|
|
15
19
|
* Side-effect specifier the node target injects when GJS ambient globals
|
|
@@ -38,18 +42,100 @@ const NODE_GI_GLOBALS_SPECIFIER = '@gjsify/node-gi/globals';
|
|
|
38
42
|
const NODE_GI_BARE_MODULE_SPECIFIERS = Object.values(ALIASES_GJS_FOR_NODE);
|
|
39
43
|
/** Matches a register subpath in an alias key: `<pkg>/register` or `<pkg>/register/<feature>`. */
|
|
40
44
|
const REGISTER_SUBPATH_RE = /\/register(\/|$)/;
|
|
45
|
+
/**
|
|
46
|
+
* npm packages a REVERSE-BRIDGE build must be routed away from, because their
|
|
47
|
+
* `node` export condition is not a node build of the same program — it is a
|
|
48
|
+
* DIFFERENT program, chosen for an assumption a GJS-on-node bundle does not
|
|
49
|
+
* hold. The value is the entry the `--app gjs` build of the same source gets,
|
|
50
|
+
* so the two ADR 0030 legs run one program.
|
|
51
|
+
*
|
|
52
|
+
* `solid-js` is the measured case and so far the only one. Its export map
|
|
53
|
+
* routes `node` (and `deno`, and `worker`) to `dist/server.js` — the SSR
|
|
54
|
+
* build, whose `createEffect` is `function createEffect(fn, value) {}`, an
|
|
55
|
+
* empty body. The initial render is therefore PERFECT and every reactive
|
|
56
|
+
* update silently never reaches GTK: sixteen of @gjsify/gtk-host's node-leg
|
|
57
|
+
* suites failed as `Expected ["second"], Actual ["first"]` while the identical
|
|
58
|
+
* source passed on gjs, and under ADR 0030's "same suite green on gjs ⇒
|
|
59
|
+
* node-gi defect" that read as a binding defect for as long as it stood. The
|
|
60
|
+
* map's own top-level `import`/`require` keys already point at the client
|
|
61
|
+
* build — `node` merely shadows them, being declared first.
|
|
62
|
+
*
|
|
63
|
+
* A ROUTE and not a condition change, deliberately. The obvious fix is to stop
|
|
64
|
+
* applying `node`, and it does not work: `platform: 'node'` implies that
|
|
65
|
+
* condition whatever `conditionNames` says (measured — dropping it from the
|
|
66
|
+
* list left solid-js on the SSR build). The reachable lever is `browser`,
|
|
67
|
+
* which outranks `node` in solid-js's map, and taking it wholesale is the
|
|
68
|
+
* SYMMETRIC defect: the gjs target can afford `browser` only because
|
|
69
|
+
* `getAliasesForGjs` has already replaced the node-facing npm packages, while
|
|
70
|
+
* the reverse bridge lifts the `/register` routes and keeps the rest real. Then
|
|
71
|
+
* `ws` — whose map declares `browser` FIRST, pointing at a one-line
|
|
72
|
+
* `throw new Error('ws does not work in the browser…')` — took the node-gi
|
|
73
|
+
* consumer harness from `pass 19/19` to `0/5 passed, 10 failed`, every one
|
|
74
|
+
* `W.WebSocket is not a constructor`. One named package resolves differently;
|
|
75
|
+
* every other package, and every cross-platform node bundle, is byte-unchanged.
|
|
76
|
+
*/
|
|
77
|
+
const REVERSE_BRIDGE_ENTRY_ROUTES = {
|
|
78
|
+
// The client build both legs share. Subpaths need no route: `solid-js/universal`
|
|
79
|
+
// (the renderer @gjsify/gtk-host binds to) declares no `node` condition at all,
|
|
80
|
+
// and reaches the root through this same specifier.
|
|
81
|
+
'solid-js': 'solid-js/dist/solid.js',
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* A dangling route above is SILENT, and silence here restores exactly the bug
|
|
85
|
+
* the route exists to prevent. MEASURED: point the solid-js route at a file
|
|
86
|
+
* that does not exist and the build exits 0 with an EMPTY log — `aliasPlugin`
|
|
87
|
+
* answers an unresolvable target with `null`, which a `pre`-order `resolveId`
|
|
88
|
+
* means as "let the default chain continue", and the default chain is the
|
|
89
|
+
* `node` condition, i.e. the SSR build with the empty `createEffect`. So a
|
|
90
|
+
* route that stops resolving fails the BUILD instead, naming the package.
|
|
91
|
+
*
|
|
92
|
+
* The CONTROL is the unrouted resolution of the same specifier — what the
|
|
93
|
+
* route replaces. If that does not resolve, the package is not in this
|
|
94
|
+
* project's graph at all and there is nothing to route (the routes are a
|
|
95
|
+
* table, not a dependency list). If it DOES resolve, the routed entry must
|
|
96
|
+
* too: a package that is installed and whose client entry has moved is the
|
|
97
|
+
* one case that must not pass quietly.
|
|
98
|
+
*
|
|
99
|
+
* `createRequire` is used only as an `exports`-map-aware RESOLVER, as in
|
|
100
|
+
* `app/gjs.ts`'s `resolveShim`; nothing is loaded through it.
|
|
101
|
+
*/
|
|
102
|
+
function assertReverseBridgeRoutes() {
|
|
103
|
+
const require_ = createRequire(import.meta.url);
|
|
104
|
+
const resolves = (specifier) => {
|
|
105
|
+
try {
|
|
106
|
+
require_.resolve(specifier);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
for (const [pkg, target] of Object.entries(REVERSE_BRIDGE_ENTRY_ROUTES)) {
|
|
114
|
+
if (!resolves(pkg))
|
|
115
|
+
continue; // not in this graph — see the CONTROL note
|
|
116
|
+
if (resolves(target))
|
|
117
|
+
continue;
|
|
118
|
+
throw new Error(`@gjsify/rolldown-plugin-gjsify: the reverse-bridge entry route for "${pkg}" is dangling — ` +
|
|
119
|
+
`"${target}" does not resolve, while "${pkg}" does. Without the route this build silently ` +
|
|
120
|
+
`takes ${pkg}'s "node" export condition, which is why the route exists; update ` +
|
|
121
|
+
`REVERSE_BRIDGE_ENTRY_ROUTES in app/node.ts to the entry the --app gjs build resolves.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
41
124
|
/**
|
|
42
125
|
* Reverse-bridge register routing: a node build of a GENUINE GJS SOURCE needs the
|
|
43
126
|
* REAL `@gjsify/*` register bodies (document, HTMLCanvasElement, the `'2d'` context
|
|
44
127
|
* factory, matchMedia, …) over `@gjsify/node-gi`, not the default `@gjsify/empty`
|
|
45
128
|
* stubs that keep CROSS-PLATFORM node bundles loadable on plain Node.
|
|
46
129
|
*
|
|
47
|
-
*
|
|
130
|
+
* Three adjustments over the standard node alias map:
|
|
48
131
|
* 1. every `<pkg>/register…` → `@gjsify/empty` entry is DROPPED so the register
|
|
49
132
|
* import resolves to its real body;
|
|
50
133
|
* 2. the gjs target's bare→scoped register routes (`xmlhttprequest/register` →
|
|
51
134
|
* `@gjsify/fetch/register/xhr`, …) are merged in so the inject stub's bare
|
|
52
|
-
* specifiers resolve on node exactly as they do on gjs
|
|
135
|
+
* specifiers resolve on node exactly as they do on gjs;
|
|
136
|
+
* 3. `REVERSE_BRIDGE_ENTRY_ROUTES` is merged in, so a package whose `node`
|
|
137
|
+
* export condition is a different program (solid-js's SSR build) is routed
|
|
138
|
+
* to the entry the `--app gjs` build of the same source gets.
|
|
53
139
|
*
|
|
54
140
|
* Applied to the BASE map only, when `isGjsSourceBuild` — `pluginOptions.aliases` /
|
|
55
141
|
* user aliases still override, and a non-reverse-bridge build is untouched.
|
|
@@ -65,6 +151,8 @@ export function enableGjsRegistersForNode(baseAliases) {
|
|
|
65
151
|
if (REGISTER_SUBPATH_RE.test(key))
|
|
66
152
|
out[key] = value;
|
|
67
153
|
}
|
|
154
|
+
// 3. the packages whose `node` export is a different PROGRAM — see the table.
|
|
155
|
+
Object.assign(out, REVERSE_BRIDGE_ENTRY_ROUTES);
|
|
68
156
|
return out;
|
|
69
157
|
}
|
|
70
158
|
/**
|
|
@@ -153,6 +241,9 @@ export const setupForNode = async (input) => {
|
|
|
153
241
|
// predicate's doc comment. The node-gi golden `test/canvas2d-bridge.test.mjs`
|
|
154
242
|
// pins the asymmetry it fixed.
|
|
155
243
|
const gjsSourceBuild = isGjsSourceBuild({ ...input.pluginOptions, registerInject });
|
|
244
|
+
// Only a reverse-bridge build carries the routes, so only it is held to them.
|
|
245
|
+
if (gjsSourceBuild)
|
|
246
|
+
assertReverseBridgeRoutes();
|
|
156
247
|
const baseAliases = getAliasesForNode({ external });
|
|
157
248
|
const aliasMap = {
|
|
158
249
|
...(gjsSourceBuild ? enableGjsRegistersForNode(baseAliases) : baseAliases),
|
|
@@ -209,6 +300,15 @@ export const setupForNode = async (input) => {
|
|
|
209
300
|
// Reflection reads the ORIGINAL TypeScript, so it runs before any user
|
|
210
301
|
// transform can strip the annotations out from under it (see `GjsifyConfig`).
|
|
211
302
|
const prePlugins = [deepkitPlugin({ reflection: input.pluginOptions.reflection })];
|
|
303
|
+
// ADR 0032 § 8's build-time gate reads the ORIGINAL source for the same reason
|
|
304
|
+
// reflection does: `import type { ViewProps }` is what tells it a name costs
|
|
305
|
+
// nothing, and a normal-order transform may already have stripped it.
|
|
306
|
+
// This target composes a dialect, so it is one of `DIALECT_APPS` — the set
|
|
307
|
+
// the CLI refuses `--dialect` against for every OTHER target. Adding a
|
|
308
|
+
// dialect here without adding the target there means a flag accepted and
|
|
309
|
+
// silently ignored.
|
|
310
|
+
if (input.pluginOptions.dialect === 'react-native')
|
|
311
|
+
prePlugins.push(reactNativeSupportGatePlugin());
|
|
212
312
|
const plugins = [
|
|
213
313
|
// Virtual-entry plugin runs FIRST so its resolveId/load match the synthetic
|
|
214
314
|
// `\0gjsify-entry:` ids `wrapInputWithSideEffects` produces (no-op when
|
|
@@ -236,6 +336,22 @@ export const setupForNode = async (input) => {
|
|
|
236
336
|
// `requireGi('Adw','1')` with no lowercased-pkg→namespace map needed.
|
|
237
337
|
// This plugin's own `gi://` branch is dead on the node target.
|
|
238
338
|
gjsImportsEmptyPlugin({ emptyGirs: !gjsSourceBuild }),
|
|
339
|
+
// Platform-file forks for the desktop, ADR 0032 § 9: `.gtk` → `.<os>` →
|
|
340
|
+
// `.desktop` → base. BEFORE the alias layer, so a platform fork of a
|
|
341
|
+
// module that also has a Node-builtin substitution wins over the
|
|
342
|
+
// substitution — the fork is the more specific statement. `.native` and
|
|
343
|
+
// `.web` are deliberately absent from the chain and warned about when
|
|
344
|
+
// present; `DESKTOP_REFUSED_SUFFIXES` carries the reason.
|
|
345
|
+
platformResolvePlugin({
|
|
346
|
+
suffixes: desktopSuffixChain(desktopOsSuffix()),
|
|
347
|
+
refusedSuffixes: DESKTOP_REFUSED_SUFFIXES,
|
|
348
|
+
siblingIndex: true,
|
|
349
|
+
}),
|
|
350
|
+
// ADR 0032 § 2's alias line, only when the consumer asked for it
|
|
351
|
+
// (`--dialect react-native`). `pre`, ahead of the substitution table and the
|
|
352
|
+
// externals policy — a redirect after `externalsPlugin` would find the
|
|
353
|
+
// specifier already externalised.
|
|
354
|
+
...(input.pluginOptions.dialect === 'react-native' ? [reactNativeAliasPlugin()] : []),
|
|
239
355
|
aliasPlugin({ entries: aliasEntries }),
|
|
240
356
|
// Blueprint (.blp → XML string): the reverse bridge runs REAL GTK on Node,
|
|
241
357
|
// so a GJS app entry with composite-template windows must build for
|
package/lib/index.d.ts
CHANGED
|
@@ -22,8 +22,16 @@ export { unresolvedWorkspaceImportPlugin, classifyImport, isWorkspaceSpecifier,
|
|
|
22
22
|
export type { WorkspaceImportGuardOptions, WorkspaceImportGuardTarget, ImportVerdict, ClassifyImportInput, UnresolvedWorkspaceImportDetails, } from './plugins/unresolved-workspace-import.js';
|
|
23
23
|
export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, isGjsifyNativeBridge, detectNapiRsEntry, hostNapiRsTriple, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
|
|
24
24
|
export type { NapiNodeAddonPluginOptions, AddonPackageJson } from './plugins/napi-node-addon.js';
|
|
25
|
-
export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
25
|
+
export { platformResolvePlugin, nativescriptSuffixChain, desktopSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
26
26
|
export type { PlatformResolvePluginOptions, NativescriptPlatform } from './plugins/platform-resolve.js';
|
|
27
|
+
export { rnRouteManifestPlugin, renderRouteManifest, walkRoutes, RouteManifestError, RN_ROUTES_MODULE_ID, MAX_ROUTE_DEPTH, } from './plugins/rn-route-manifest.js';
|
|
28
|
+
export type { RnRouteManifestOptions, FoundRoute } from './plugins/rn-route-manifest.js';
|
|
29
|
+
export { reactNativeAliasPlugin, classifyReactNativeSpecifier, REACT_NATIVE_ALIAS_TARGET, REACT_NATIVE_SPECIFIER, ReactNativeDeepImportError, ReactNativeAliasTargetMissingError, } from './plugins/react-native-alias.js';
|
|
30
|
+
export type { ReactNativeSpecifierKind } from './plugins/react-native-alias.js';
|
|
31
|
+
export { reactNativeSupportGatePlugin, loadSupportTable, findSupportViolations, formatSupportViolations, formatOpaqueReference, formatUnreadableModule, SUPPORT_TABLE_SUBPATH, WATCHED_SPECIFIERS, ReactNativeUnsupportedImportError, SupportTableUnreadableError, } from './plugins/react-native-gate.js';
|
|
32
|
+
export type { ReactNativeSupportGateOptions, SupportTableReader, SupportTableResolver, SupportViolation, } from './plugins/react-native-gate.js';
|
|
33
|
+
export { scanNamedImports, ImportScanParseError } from './utils/scan-named-imports.js';
|
|
34
|
+
export type { NamedImport, OpaqueReference, ScanResult } from './utils/scan-named-imports.js';
|
|
27
35
|
export * from './plugin.js';
|
|
28
36
|
import { gjsifyPlugin } from './plugin.js';
|
|
29
37
|
export { gjsifyPlugin };
|
package/lib/index.js
CHANGED
|
@@ -14,7 +14,11 @@ export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
|
14
14
|
export { externalsPlugin } from './plugins/externals.js';
|
|
15
15
|
export { unresolvedWorkspaceImportPlugin, classifyImport, isWorkspaceSpecifier, formatUnresolvedWorkspaceImport, buildReverseAliasIndex, UnresolvedWorkspaceImportError, } from './plugins/unresolved-workspace-import.js';
|
|
16
16
|
export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, isGjsifyNativeBridge, detectNapiRsEntry, hostNapiRsTriple, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
|
|
17
|
-
export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
17
|
+
export { platformResolvePlugin, nativescriptSuffixChain, desktopSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
18
|
+
export { rnRouteManifestPlugin, renderRouteManifest, walkRoutes, RouteManifestError, RN_ROUTES_MODULE_ID, MAX_ROUTE_DEPTH, } from './plugins/rn-route-manifest.js';
|
|
19
|
+
export { reactNativeAliasPlugin, classifyReactNativeSpecifier, REACT_NATIVE_ALIAS_TARGET, REACT_NATIVE_SPECIFIER, ReactNativeDeepImportError, ReactNativeAliasTargetMissingError, } from './plugins/react-native-alias.js';
|
|
20
|
+
export { reactNativeSupportGatePlugin, loadSupportTable, findSupportViolations, formatSupportViolations, formatOpaqueReference, formatUnreadableModule, SUPPORT_TABLE_SUBPATH, WATCHED_SPECIFIERS, ReactNativeUnsupportedImportError, SupportTableUnreadableError, } from './plugins/react-native-gate.js';
|
|
21
|
+
export { scanNamedImports, ImportScanParseError } from './utils/scan-named-imports.js';
|
|
18
22
|
export * from './plugin.js';
|
|
19
23
|
import { gjsifyPlugin } from './plugin.js';
|
|
20
24
|
export { gjsifyPlugin };
|
|
@@ -1,19 +1,144 @@
|
|
|
1
1
|
import type { Plugin } from 'rolldown';
|
|
2
2
|
export type NativescriptPlatform = 'android' | 'ios' | 'visionos';
|
|
3
|
+
/**
|
|
4
|
+
* The desktop suffix for one host OS, keyed by `process.platform`.
|
|
5
|
+
*
|
|
6
|
+
* TWO VOCABULARIES MEET HERE and neither is negotiable. The KEYS are
|
|
7
|
+
* `process.platform`'s (`linux`/`darwin`/`win32`) — ADR 0018's target set, the
|
|
8
|
+
* spelling `gjsify.os` and `gjsify.platforms` use, and what a running process
|
|
9
|
+
* computes about itself. The VALUES are React Native's `Platform.OS` spelling
|
|
10
|
+
* (`linux`/`macos`/`windows`), which is what ADR 0032 § 9 writes and what
|
|
11
|
+
* `@gjsify/react-native`'s own `Platform.OS` reports — so a `foo.macos.tsx`
|
|
12
|
+
* agrees with the `Platform.OS === 'macos'` branch beside it. Spelling the file
|
|
13
|
+
* `foo.darwin.tsx` would make those two disagree in the one place a reader looks
|
|
14
|
+
* to check them against each other.
|
|
15
|
+
*
|
|
16
|
+
* Exported so `platform-resolve.spec.ts` can iterate the real values instead of
|
|
17
|
+
* a copy: nothing in the type system stops someone adding a `web` row here, and
|
|
18
|
+
* that row is exactly what § 9 forbids.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DESKTOP_OS_SUFFIXES: Readonly<Record<string, string>>;
|
|
21
|
+
/**
|
|
22
|
+
* Suffixes that must NEVER be a rung of the DESKTOP chain, with § 9's reason.
|
|
23
|
+
*
|
|
24
|
+
* `.native` looks like an oversight and is the opposite: a `.native.tsx` is by
|
|
25
|
+
* definition written for a React Native runtime, so reaching for it would
|
|
26
|
+
* silently feed the GTK build code that expects `NativeModules` — a bridge that
|
|
27
|
+
* exists only in the phone host. No import error, no type error, no failing
|
|
28
|
+
* test; the divergence surfaces as a window that does not work. Falling through
|
|
29
|
+
* to the BASE file is the honest outcome, because the base file at least has to
|
|
30
|
+
* compile against what this target really has.
|
|
31
|
+
*
|
|
32
|
+
* `.web` is worse. It reads as the obvious pick for a desktop target — a desktop
|
|
33
|
+
* is not a phone, so surely the web variant? — and it carries exactly the DOM
|
|
34
|
+
* assumptions ADR 0032 rules out, the same two-lossy-mappings-stacked shape that
|
|
35
|
+
* rules out react-native-web over a DOM (§ 3, Rejected).
|
|
36
|
+
*
|
|
37
|
+
* Not silence, though: when the desktop chain falls through to base and one of
|
|
38
|
+
* these siblings exists, {@link platformResolvePlugin} warns naming the file.
|
|
39
|
+
* Without that, an author's fork is dead code with nothing anywhere saying so.
|
|
40
|
+
*/
|
|
41
|
+
export declare const DESKTOP_REFUSED_SUFFIXES: readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* The NativeScript chain: platform-specific first, then the platform-agnostic
|
|
44
|
+
* `native` suffix. Without a known platform only `native` applies.
|
|
45
|
+
*/
|
|
46
|
+
export declare function nativescriptSuffixChain(platform?: NativescriptPlatform): readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* The GTK/desktop chain (ADR 0032 § 9): `.gtk` → `.<os>` → `.desktop` → base.
|
|
49
|
+
*
|
|
50
|
+
* `.gtk` outranks the OS because a GTK fork is a statement about the TOOLKIT,
|
|
51
|
+
* which is the more specific claim: `foo.gtk.tsx` says "this is how the widget
|
|
52
|
+
* tree does it", `foo.linux.tsx` says "this is how this kernel does it", and a
|
|
53
|
+
* project holding both means the first on a GTK build.
|
|
54
|
+
*
|
|
55
|
+
* `os` is a suffix from {@link DESKTOP_OS_SUFFIXES}, never a `process.platform`
|
|
56
|
+
* token — see {@link desktopOsSuffix}. Omitting it drops the OS rung rather than
|
|
57
|
+
* guessing one.
|
|
58
|
+
*/
|
|
59
|
+
export declare function desktopSuffixChain(os?: string): readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* The desktop suffix for the BUILD HOST, or `undefined` for a host outside
|
|
62
|
+
* ADR 0018's target set.
|
|
63
|
+
*
|
|
64
|
+
* The host, not a cross-target: there is no cross-OS desktop build in this chain
|
|
65
|
+
* (ADR 0032's Consequences put macOS and Windows on Node + `@gjsify/node-gi`,
|
|
66
|
+
* whose installable artifact is ADR 0024 stages 4 and 5 and does not exist yet).
|
|
67
|
+
* A future cross-build states its own suffix by passing one to
|
|
68
|
+
* {@link desktopSuffixChain}, which is why the decision function takes the value
|
|
69
|
+
* and only this one function reads the ambient.
|
|
70
|
+
*
|
|
71
|
+
* The ambient read is `process.platform` behind a defaulted parameter — the
|
|
72
|
+
* shape `extractPackageSpec` and `isAbsoluteFsPath` already use in this package.
|
|
73
|
+
* The canonical repo-wide spelling is `@gjsify/utils/core`'s `hostOs()`, and
|
|
74
|
+
* `os-axis.mjs` sanctions infra keeping its own raw read for the purity reason
|
|
75
|
+
* `cli/src/utils/platform-check.ts` documents. Here there is a second, harder
|
|
76
|
+
* reason: `@gjsify/utils` is BUILT BY the CLI this package is part of, so
|
|
77
|
+
* depending on it would make `build:infra` need its own output.
|
|
78
|
+
* `package.json#gjsify.os` on this package already carries the ADR 0018 claim.
|
|
79
|
+
*/
|
|
80
|
+
export declare function desktopOsSuffix(platform?: string): string | undefined;
|
|
3
81
|
export interface PlatformResolvePluginOptions {
|
|
4
82
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
83
|
+
* The suffix chain, most specific first. Build it with
|
|
84
|
+
* {@link nativescriptSuffixChain} or {@link desktopSuffixChain} — a
|
|
85
|
+
* hand-written array is a second resolution order the tree does not state.
|
|
86
|
+
*/
|
|
87
|
+
suffixes: readonly string[];
|
|
88
|
+
/**
|
|
89
|
+
* Suffixes deliberately NOT resolved, but whose presence on disk is worth
|
|
90
|
+
* saying out loud: {@link DESKTOP_REFUSED_SUFFIXES} for the desktop chain,
|
|
91
|
+
* empty for NativeScript (where `.native` is a real rung). Probed only after
|
|
92
|
+
* the chain found nothing, so an empty list costs zero extra resolves and
|
|
93
|
+
* the NS path is byte-unchanged.
|
|
7
94
|
*/
|
|
8
|
-
|
|
95
|
+
refusedSuffixes?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* Consult the importer's directory listing before entering the resolver.
|
|
98
|
+
*
|
|
99
|
+
* MEASURED, and the reason this option exists at all. The NativeScript chain
|
|
100
|
+
* runs on one build target; the desktop chain runs on every `--app gjs` and
|
|
101
|
+
* `--app node` build in the repository, and a three-rung chain costs up to
|
|
102
|
+
* six failed `this.resolve` calls per relative import. On this repository's
|
|
103
|
+
* largest bundle (`dist/cli.gjs.mjs`, ~1400 modules) that was 21.9–22.2 s
|
|
104
|
+
* without the plugin against 25.0–26.3 s with it — about +14%, and Rolldown
|
|
105
|
+
* reported the plugin as 49% of all plugin time. Skipping `node_modules`
|
|
106
|
+
* importers recovered under a second of it: the cost is FIRST-PARTY imports,
|
|
107
|
+
* which is exactly the population the feature is for.
|
|
108
|
+
*
|
|
109
|
+
* So the filter is a directory listing, cached per directory: one
|
|
110
|
+
* `readdirSync` answers every suffix for every import in that directory, and
|
|
111
|
+
* the resolver is entered only for a candidate that has a matching sibling.
|
|
112
|
+
* Same bundle, 22.0–22.8 s and 25% of plugin time — the surcharge is gone and
|
|
113
|
+
* the plugin now sits alongside its four siblings instead of ahead of them.
|
|
114
|
+
* The listing is a FILTER and never an answer — a directory it cannot read
|
|
115
|
+
* (a virtual importer id, a `\0`-prefixed module) falls straight through to
|
|
116
|
+
* the resolver, and names are compared lowercased so a case-insensitive
|
|
117
|
+
* filesystem can only make the filter more permissive, never less.
|
|
118
|
+
*
|
|
119
|
+
* OFF for NativeScript, deliberately: `@nativescript/core` ships
|
|
120
|
+
* `.android`/`.ios` siblings and imports them relatively, and the mandate on
|
|
121
|
+
* that chain is byte-identical behaviour — a filter that another plugin's
|
|
122
|
+
* virtual `resolveId` could see past is not something to switch on there
|
|
123
|
+
* without a measurement of that tree.
|
|
124
|
+
*/
|
|
125
|
+
siblingIndex?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/** Thrown when a variant is on disk but the resolver hands it back as external. */
|
|
128
|
+
export declare class PlatformVariantExternalError extends Error {
|
|
129
|
+
readonly candidate: string;
|
|
130
|
+
readonly importer: string;
|
|
131
|
+
readonly name = "PlatformVariantExternalError";
|
|
132
|
+
constructor(candidate: string, importer: string);
|
|
9
133
|
}
|
|
10
134
|
/**
|
|
11
|
-
* Resolve platform-specific source-file variants
|
|
12
|
-
*
|
|
13
|
-
* specifiers are left to the normal
|
|
14
|
-
* fields are a separate, rarer concern
|
|
135
|
+
* Resolve platform-specific source-file variants ahead of the base file.
|
|
136
|
+
*
|
|
137
|
+
* Relative imports only — bare/package specifiers are left to the normal
|
|
138
|
+
* resolver chain (package platform-`main` fields are a separate, rarer concern
|
|
139
|
+
* handled by the alias layer).
|
|
15
140
|
*/
|
|
16
|
-
export declare function platformResolvePlugin(options
|
|
141
|
+
export declare function platformResolvePlugin(options: PlatformResolvePluginOptions): Plugin;
|
|
17
142
|
/**
|
|
18
143
|
* Best-effort detection of the NativeScript target platform from the
|
|
19
144
|
* environment NS' CLI sets when it spawns a bundler (`NATIVESCRIPT_BUNDLER_ENV`
|