@gjsify/rolldown-plugin-gjsify 0.51.1 → 0.52.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/browser.js +27 -0
- package/lib/app/nativescript.js +14 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/plugins/platform-resolve.d.ts +95 -5
- package/lib/plugins/platform-resolve.js +132 -11
- package/lib/utils/auto-globals.js +79 -5
- package/lib/utils/runtime.js +1 -1
- package/package.json +9 -9
package/lib/app/browser.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// `--app browser` Rolldown configuration factory.
|
|
2
2
|
//
|
|
3
|
+
// Platform-file forks resolve through `plugins/platform-resolve.ts` on the
|
|
4
|
+
// BROWSER chain (ADR 0032 § 9): `./foo.web.<ext>` ahead of `./foo.<ext>`, one
|
|
5
|
+
// rung, with `.gtk` / `.desktop` / `.native` refused and warned about.
|
|
6
|
+
//
|
|
3
7
|
// Browser builds redirect `@girs/*` and `gi://*` to an empty virtual module
|
|
4
8
|
// (they appear transitively via `@gjsify/unit` and similar packages with
|
|
5
9
|
// GJS-specific code paths) — unless `--gi-renderer` composes the ADR 0034
|
|
@@ -22,6 +26,7 @@ import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
|
22
26
|
import { ALIASES_NODE_FOR_BROWSER, GI_RENDERERS, getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
23
27
|
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
24
28
|
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
29
|
+
import { platformResolvePlugin, browserSuffixChain, BROWSER_REFUSED_SUFFIXES } from '../plugins/platform-resolve.js';
|
|
25
30
|
import { giRendererPlugin } from '../plugins/gi-renderer.js';
|
|
26
31
|
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
27
32
|
import { unresolvedWorkspaceImportPlugin } from '../plugins/unresolved-workspace-import.js';
|
|
@@ -98,6 +103,22 @@ export const setupForBrowser = async (input) => {
|
|
|
98
103
|
// transform can strip the annotations out from under it (see `GjsifyConfig`).
|
|
99
104
|
const prePlugins = [deepkitPlugin({ reflection: input.pluginOptions.reflection })];
|
|
100
105
|
const plugins = [
|
|
106
|
+
// Platform-file forks for the browser, ADR 0032 § 9: `.web` → base. One
|
|
107
|
+
// rung, and `browserSuffixChain`'s doc comment carries why each candidate
|
|
108
|
+
// second rung is absent. FIRST in the array for the reason `app/gjs.ts`
|
|
109
|
+
// states: the plugin claims RELATIVE imports only, and a platform fork of
|
|
110
|
+
// a module that also has a Node-builtin substitution must win over the
|
|
111
|
+
// substitution, because the fork is the more specific statement. `.gtk`,
|
|
112
|
+
// `.desktop` and `.native` are deliberately not rungs here and are warned
|
|
113
|
+
// about when present; `BROWSER_REFUSED_SUFFIXES` carries the reason.
|
|
114
|
+
// `siblingIndex: true` as on the desktop chain — same population (first-
|
|
115
|
+
// party relative imports), same directory-listing filter, and without it
|
|
116
|
+
// a one-rung chain plus three refusals is four failed resolves per import.
|
|
117
|
+
platformResolvePlugin({
|
|
118
|
+
suffixes: browserSuffixChain(),
|
|
119
|
+
refusedSuffixes: BROWSER_REFUSED_SUFFIXES,
|
|
120
|
+
siblingIndex: true,
|
|
121
|
+
}),
|
|
101
122
|
// ADR 0034 stage 9 — the `gi://` arm, ahead of the empty redirect so it
|
|
102
123
|
// claims the specifier first, exactly as `gjsGiNodePlugin` does on the node
|
|
103
124
|
// target. `emptyGirs` follows it: with the arm on, `@girs/<ns>-<ver>` must
|
|
@@ -106,6 +127,12 @@ export const setupForBrowser = async (input) => {
|
|
|
106
127
|
...(giRenderer ? [giRendererPlugin({ app: 'browser', ...giRenderer })] : []),
|
|
107
128
|
gjsImportsEmptyPlugin({ emptyGirs: !giRenderer }),
|
|
108
129
|
aliasPlugin({ entries: aliasEntries }),
|
|
130
|
+
// Blueprint has been registered here since it was registered anywhere, and until the
|
|
131
|
+
// `?shared-tree` exit existed the XML string it emitted had no reader on this target:
|
|
132
|
+
// a browser has no `Gtk.Builder`, so `import Template from './x.blp'` compiled, shipped
|
|
133
|
+
// its bytes and was never parsed by anything. It stays — a `--app browser` build of a
|
|
134
|
+
// GJS app's sources must not start failing on an import that used to resolve — and the
|
|
135
|
+
// exit a browser can actually use is now beside it.
|
|
109
136
|
blueprintPlugin(),
|
|
110
137
|
cssAsStringPlugin(),
|
|
111
138
|
// `order: 'post'` — see app/gjs.ts. The browser target's whole job is to
|
package/lib/app/nativescript.js
CHANGED
|
@@ -33,8 +33,12 @@
|
|
|
33
33
|
// PACKAGE NAME still routes per its declared slot in a single hop.
|
|
34
34
|
//
|
|
35
35
|
// No `cssAsStringPlugin` (NativeScript ships its own CSS pipeline as part
|
|
36
|
-
// of `@nativescript/core`)
|
|
36
|
+
// of `@nativescript/core`). `blueprintPlugin` IS here: a bare `.blp` import
|
|
37
|
+
// still compiles to GtkBuilder XML that nothing on this target reads, but
|
|
38
|
+
// `./x.blp?shared-tree` projects the same file into the node shape
|
|
39
|
+
// `@gjsify/adwaita-nativescript`'s `build` consumes.
|
|
37
40
|
import { aliasPlugin } from '../plugins/alias.js';
|
|
41
|
+
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
38
42
|
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
39
43
|
import { ALIASES_NODE_FOR_NATIVESCRIPT, GI_RENDERERS, getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
40
44
|
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
@@ -146,7 +150,15 @@ export const setupForNativescript = async (input) => {
|
|
|
146
150
|
// alias routing so a platform fork of a portable module is honored.
|
|
147
151
|
platformResolvePlugin({ suffixes: nativescriptSuffixChain(platform) }),
|
|
148
152
|
aliasPlugin({ entries: aliasEntries }),
|
|
149
|
-
//
|
|
153
|
+
// Blueprint: `./x.blp?shared-tree` is how ONE authored template reaches this
|
|
154
|
+
// target. The comment this replaced read "NO blueprintPlugin — Blueprint is a
|
|
155
|
+
// GTK-specific UI DSL", and that was true of the only exit the plugin had: a
|
|
156
|
+
// GtkBuilder-XML string, which no NativeScript runtime can load. It is the
|
|
157
|
+
// NOTATION that was never GTK-specific — ADR 0053 clause 1 made `.blp` a second
|
|
158
|
+
// READER of the shared node shape, and the projection is what this target
|
|
159
|
+
// consumes. A `.blp` whose projection loses anything is refused at build time
|
|
160
|
+
// rather than rendered partially; see `SHARED_TREE_QUERY` in the plugin.
|
|
161
|
+
blueprintPlugin(),
|
|
150
162
|
// NO cssAsStringPlugin — NS ships its own CSS pipeline via
|
|
151
163
|
// @nativescript/core; .css imports are handled by the consuming
|
|
152
164
|
// @nativescript/webpack or @nativescript/vite build
|
package/lib/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ 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, nativescriptSuffixChain, desktopSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
25
|
+
export { platformResolvePlugin, nativescriptSuffixChain, desktopSuffixChain, browserSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, BROWSER_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
26
26
|
export type { PlatformResolvePluginOptions, NativescriptPlatform } from './plugins/platform-resolve.js';
|
|
27
27
|
export { rnRouteManifestPlugin, renderRouteManifest, walkRoutes, RouteManifestError, RN_ROUTES_MODULE_ID, MAX_ROUTE_DEPTH, } from './plugins/rn-route-manifest.js';
|
|
28
28
|
export type { RnRouteManifestOptions, FoundRoute } from './plugins/rn-route-manifest.js';
|
package/lib/index.js
CHANGED
|
@@ -14,7 +14,7 @@ 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, nativescriptSuffixChain, desktopSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
17
|
+
export { platformResolvePlugin, nativescriptSuffixChain, desktopSuffixChain, browserSuffixChain, desktopOsSuffix, DESKTOP_OS_SUFFIXES, DESKTOP_REFUSED_SUFFIXES, BROWSER_REFUSED_SUFFIXES, PlatformVariantExternalError, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
18
18
|
export { rnRouteManifestPlugin, renderRouteManifest, walkRoutes, RouteManifestError, RN_ROUTES_MODULE_ID, MAX_ROUTE_DEPTH, } from './plugins/rn-route-manifest.js';
|
|
19
19
|
export { reactNativeAliasPlugin, classifyReactNativeSpecifier, couldBeSurfaceSpecifier, loadLayer, FALLBACK_SURFACES, REACT_NATIVE_ALIAS_TARGET, REACT_NATIVE_SPECIFIER, ReactNativeDeepImportError, ReactNativeAliasTargetMissingError, SURFACE_MENTION, SURFACE_NAME_PREFIXES, } from './plugins/react-native-alias.js';
|
|
20
20
|
export { reactNativeSupportGatePlugin, loadSupportTable, findSupportViolations, formatSupportViolations, formatOpaqueReference, formatUnreadableModule, SUPPORT_TABLE_SUBPATH, WATCHED_SPECIFIERS, watchedSpecifiers, ReactNativeUnsupportedImportError, SupportTableUnreadableError, } from './plugins/react-native-gate.js';
|
|
@@ -39,6 +39,48 @@ export declare const DESKTOP_OS_SUFFIXES: Readonly<Record<string, string>>;
|
|
|
39
39
|
* Without that, an author's fork is dead code with nothing anywhere saying so.
|
|
40
40
|
*/
|
|
41
41
|
export declare const DESKTOP_REFUSED_SUFFIXES: readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* Suffixes that must NEVER be a rung of the BROWSER chain — the mirror of
|
|
44
|
+
* {@link DESKTOP_REFUSED_SUFFIXES}, built by the same rule read from the other
|
|
45
|
+
* side: **refuse the other families' UMBRELLA rungs, not their per-target
|
|
46
|
+
* spellings.** The desktop list names `.web` (the whole browser chain) and
|
|
47
|
+
* `.native` (the phone family's umbrella) and deliberately leaves
|
|
48
|
+
* `.android`/`.ios`/`.visionos` out; from here the same rule names the GTK
|
|
49
|
+
* desktop family's two non-OS rungs plus that same phone umbrella.
|
|
50
|
+
*
|
|
51
|
+
* `.gtk` is the one a reader reaches for while chasing a missing widget, and it
|
|
52
|
+
* is the worst possible answer: `--app browser` redirects `gi://*` and `@girs/*`
|
|
53
|
+
* to an EMPTY module, so a `.gtk.tsx` reaching `Adw.ActionRow` does not fail to
|
|
54
|
+
* import — it gets `{}`, and `class X extends Adw.ActionRow` throws
|
|
55
|
+
* `Class extends value undefined` at load. That is measured, and the ADR 0034
|
|
56
|
+
* stage 9 e2e keeps exactly that row as its control.
|
|
57
|
+
*
|
|
58
|
+
* `.desktop` is named as well as `.gtk`, though only one of the two appears in
|
|
59
|
+
* this repository today: § 9 writes `.desktop` as the umbrella over `.gtk` and
|
|
60
|
+
* the three OS spellings, so refusing one and not the other is a gap, and a gap
|
|
61
|
+
* in a refusal list reads as a judgement that the omitted one is fine here.
|
|
62
|
+
*
|
|
63
|
+
* `.native` is the phone bridge — `NativeModules` exists in neither a browser
|
|
64
|
+
* nor a GTK host, which is why both lists carry it.
|
|
65
|
+
*
|
|
66
|
+
* NOT here, and each is a DECISION rather than an omission:
|
|
67
|
+
* - **the three OS spellings** (`linux`/`macos`/`windows`). They only ever
|
|
68
|
+
* appear as rungs of the desktop chain, whose umbrella is already named — the
|
|
69
|
+
* same reason `.android` is absent from the desktop list. Naming them would
|
|
70
|
+
* put a warning line on every OS-forked module of a normal dual-target tree,
|
|
71
|
+
* which is how a warning gets switched off.
|
|
72
|
+
* - **a refusal list for NativeScript at all.** That chain runs with
|
|
73
|
+
* `siblingIndex` OFF under a byte-identical mandate, and with no listing to
|
|
74
|
+
* filter them a refusal list costs two real `this.resolve` calls on every
|
|
75
|
+
* relative import that has no variant — the majority, inside
|
|
76
|
+
* `@nativescript/core` included. The desktop chain's own measurement (+14% on
|
|
77
|
+
* a ~1400-module bundle from six failed resolves) is what says failed resolves
|
|
78
|
+
* are not free. It is a cost with a measured-zero benefit on this tree: the
|
|
79
|
+
* 30 `.gtk.*`/`.native.*` pairs in `@gjsify/adwaita-react-native` all HIT the
|
|
80
|
+
* NS chain at `.native`, and the refusal probe only runs when the chain found
|
|
81
|
+
* nothing. Reopen it from a measurement of that tree with the index on.
|
|
82
|
+
*/
|
|
83
|
+
export declare const BROWSER_REFUSED_SUFFIXES: readonly string[];
|
|
42
84
|
/**
|
|
43
85
|
* The NativeScript chain: platform-specific first, then the platform-agnostic
|
|
44
86
|
* `native` suffix. Without a known platform only `native` applies.
|
|
@@ -57,6 +99,37 @@ export declare function nativescriptSuffixChain(platform?: NativescriptPlatform)
|
|
|
57
99
|
* guessing one.
|
|
58
100
|
*/
|
|
59
101
|
export declare function desktopSuffixChain(os?: string): readonly string[];
|
|
102
|
+
/**
|
|
103
|
+
* The BROWSER chain: `.web` → base. Exactly ONE rung, and the count is the
|
|
104
|
+
* decision.
|
|
105
|
+
*
|
|
106
|
+
* A desktop build knows three different things about itself and so gets three
|
|
107
|
+
* rungs: the toolkit (`.gtk`), the kernel (`.<os>`) and the target family
|
|
108
|
+
* (`.desktop`). A `--app browser` build knows ONE — it targets the DOM — and
|
|
109
|
+
* every candidate second rung was rejected for a stated reason rather than left
|
|
110
|
+
* out for tidiness:
|
|
111
|
+
*
|
|
112
|
+
* - **No engine rung** (`.firefox` / `.chromium`). The target compiles one
|
|
113
|
+
* `esnext` bundle with no per-engine branch anywhere in it; a rung nobody can
|
|
114
|
+
* fill is a resolution rule that can only ever surprise someone.
|
|
115
|
+
* - **No OS rung.** One browser bundle is served to every operating system, so
|
|
116
|
+
* an OS rung would resolve against the BUILD HOST and bake it into a
|
|
117
|
+
* platform-neutral artifact. That is the mistake `plugins/gi-runtime-paths.ts`
|
|
118
|
+
* already documents for the GI prologue, committed here at resolution time
|
|
119
|
+
* where the evidence is a file name and not a code path.
|
|
120
|
+
* - **No `.dom` synonym.** Two spellings for one concept is a priority order
|
|
121
|
+
* someone has to memorise, and `.web` is what § 9 writes and what every
|
|
122
|
+
* `.web.*` file in this repository already uses.
|
|
123
|
+
* - **No `.browser` umbrella.** `.desktop` earns its rung by sitting above four
|
|
124
|
+
* spellings; above `.web` there is nothing for an umbrella to cover.
|
|
125
|
+
*
|
|
126
|
+
* Takes no parameter, unlike {@link desktopSuffixChain}: nothing about this
|
|
127
|
+
* chain depends on the host, which is the previous point restated from the API
|
|
128
|
+
* side. It is a function rather than a constant so that
|
|
129
|
+
* {@link PlatformResolvePluginOptions.suffixes} keeps its rule — a hand-written
|
|
130
|
+
* array is a second resolution order the tree does not state.
|
|
131
|
+
*/
|
|
132
|
+
export declare function browserSuffixChain(): readonly string[];
|
|
60
133
|
/**
|
|
61
134
|
* The desktop suffix for the BUILD HOST, or `undefined` for a host outside
|
|
62
135
|
* ADR 0018's target set.
|
|
@@ -81,16 +154,19 @@ export declare function desktopOsSuffix(platform?: string): string | undefined;
|
|
|
81
154
|
export interface PlatformResolvePluginOptions {
|
|
82
155
|
/**
|
|
83
156
|
* The suffix chain, most specific first. Build it with
|
|
84
|
-
* {@link nativescriptSuffixChain}
|
|
85
|
-
* hand-written array is a second resolution
|
|
157
|
+
* {@link nativescriptSuffixChain}, {@link desktopSuffixChain} or
|
|
158
|
+
* {@link browserSuffixChain} — a hand-written array is a second resolution
|
|
159
|
+
* order the tree does not state.
|
|
86
160
|
*/
|
|
87
161
|
suffixes: readonly string[];
|
|
88
162
|
/**
|
|
89
163
|
* Suffixes deliberately NOT resolved, but whose presence on disk is worth
|
|
90
164
|
* saying out loud: {@link DESKTOP_REFUSED_SUFFIXES} for the desktop chain,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
165
|
+
* {@link BROWSER_REFUSED_SUFFIXES} for the browser chain, empty for
|
|
166
|
+
* NativeScript — where `.native` is a real rung and the rest is a measured
|
|
167
|
+
* decision written out on `BROWSER_REFUSED_SUFFIXES`. Probed only after the
|
|
168
|
+
* chain found nothing, so an empty list costs zero extra resolves and the NS
|
|
169
|
+
* path is byte-unchanged.
|
|
94
170
|
*/
|
|
95
171
|
refusedSuffixes?: readonly string[];
|
|
96
172
|
/**
|
|
@@ -121,6 +197,20 @@ export interface PlatformResolvePluginOptions {
|
|
|
121
197
|
* that chain is byte-identical behaviour — a filter that another plugin's
|
|
122
198
|
* virtual `resolveId` could see past is not something to switch on there
|
|
123
199
|
* without a measurement of that tree.
|
|
200
|
+
*
|
|
201
|
+
* ON WHEREVER THE CHAIN CARRIES A REFUSAL LIST, and that is not a preference.
|
|
202
|
+
* A refused suffix is probed on every miss, so a chain with refusals pays its
|
|
203
|
+
* probes on the MAJORITY of imports rather than the few that have a variant.
|
|
204
|
+
* Measured on `tests/e2e/vite-plugin-gjsify`'s fixture with the browser chain
|
|
205
|
+
* (one rung, three refusals) under `--max-old-space-size=4096`: index off is
|
|
206
|
+
* 1172 probes and `FATAL ERROR: Reached heap limit`; index on is 0 probes,
|
|
207
|
+
* 0.25 s and 191 MB. Under Vite each probe re-enters the whole plugin
|
|
208
|
+
* container, which is where the memory goes. Leaving the index off is
|
|
209
|
+
* therefore a choice only a chain with NO refusal list can afford.
|
|
210
|
+
*
|
|
211
|
+
* Kept fresh by {@link platformResolvePlugin}'s `watchChange` hook, so a
|
|
212
|
+
* long-lived host (a Vite dev server, `gjsify build --watch`) does not answer
|
|
213
|
+
* from a directory listing taken before the author added the variant.
|
|
124
214
|
*/
|
|
125
215
|
siblingIndex?: boolean;
|
|
126
216
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
// Platform file resolution — ONE plugin, ONE resolution order,
|
|
1
|
+
// Platform file resolution — ONE plugin, ONE resolution order, three chains.
|
|
2
2
|
//
|
|
3
3
|
// Lets a shared codebase fork a single module per target by file name. An
|
|
4
4
|
// `import './foo'` (or `import './foo.js'`) resolves to the most specific
|
|
5
5
|
// variant on disk, in the priority order the caller's chain declares:
|
|
6
6
|
//
|
|
7
|
-
// NativeScript (`--app nativescript`)
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// NativeScript (`--app nativescript`) GTK / desktop (`--app gjs|node`)
|
|
8
|
+
// ./foo.<android|ios|visionos>.<ext> ./foo.gtk.<ext>
|
|
9
|
+
// ./foo.native.<ext> ./foo.<linux|macos|windows>.<ext>
|
|
10
|
+
// ./foo.<ext> ./foo.desktop.<ext>
|
|
11
|
+
// ./foo.<ext>
|
|
12
12
|
//
|
|
13
|
-
//
|
|
13
|
+
// Browser (`--app browser`)
|
|
14
|
+
// ./foo.web.<ext>
|
|
15
|
+
// ./foo.<ext>
|
|
16
|
+
//
|
|
17
|
+
// THREE CHAINS, NOT THREE PLUGINS (ADR 0032 § 9). A second plugin would give the
|
|
14
18
|
// tree two resolution orders that no single file states, and the order IS the
|
|
15
19
|
// contract — `.gtk` before `.<os>` before `.desktop` is a decision, not an
|
|
16
20
|
// implementation detail. So the chain is a PARAMETER and the builders below are
|
|
@@ -82,6 +86,48 @@ export const DESKTOP_OS_SUFFIXES = {
|
|
|
82
86
|
* Without that, an author's fork is dead code with nothing anywhere saying so.
|
|
83
87
|
*/
|
|
84
88
|
export const DESKTOP_REFUSED_SUFFIXES = ['native', 'web'];
|
|
89
|
+
/**
|
|
90
|
+
* Suffixes that must NEVER be a rung of the BROWSER chain — the mirror of
|
|
91
|
+
* {@link DESKTOP_REFUSED_SUFFIXES}, built by the same rule read from the other
|
|
92
|
+
* side: **refuse the other families' UMBRELLA rungs, not their per-target
|
|
93
|
+
* spellings.** The desktop list names `.web` (the whole browser chain) and
|
|
94
|
+
* `.native` (the phone family's umbrella) and deliberately leaves
|
|
95
|
+
* `.android`/`.ios`/`.visionos` out; from here the same rule names the GTK
|
|
96
|
+
* desktop family's two non-OS rungs plus that same phone umbrella.
|
|
97
|
+
*
|
|
98
|
+
* `.gtk` is the one a reader reaches for while chasing a missing widget, and it
|
|
99
|
+
* is the worst possible answer: `--app browser` redirects `gi://*` and `@girs/*`
|
|
100
|
+
* to an EMPTY module, so a `.gtk.tsx` reaching `Adw.ActionRow` does not fail to
|
|
101
|
+
* import — it gets `{}`, and `class X extends Adw.ActionRow` throws
|
|
102
|
+
* `Class extends value undefined` at load. That is measured, and the ADR 0034
|
|
103
|
+
* stage 9 e2e keeps exactly that row as its control.
|
|
104
|
+
*
|
|
105
|
+
* `.desktop` is named as well as `.gtk`, though only one of the two appears in
|
|
106
|
+
* this repository today: § 9 writes `.desktop` as the umbrella over `.gtk` and
|
|
107
|
+
* the three OS spellings, so refusing one and not the other is a gap, and a gap
|
|
108
|
+
* in a refusal list reads as a judgement that the omitted one is fine here.
|
|
109
|
+
*
|
|
110
|
+
* `.native` is the phone bridge — `NativeModules` exists in neither a browser
|
|
111
|
+
* nor a GTK host, which is why both lists carry it.
|
|
112
|
+
*
|
|
113
|
+
* NOT here, and each is a DECISION rather than an omission:
|
|
114
|
+
* - **the three OS spellings** (`linux`/`macos`/`windows`). They only ever
|
|
115
|
+
* appear as rungs of the desktop chain, whose umbrella is already named — the
|
|
116
|
+
* same reason `.android` is absent from the desktop list. Naming them would
|
|
117
|
+
* put a warning line on every OS-forked module of a normal dual-target tree,
|
|
118
|
+
* which is how a warning gets switched off.
|
|
119
|
+
* - **a refusal list for NativeScript at all.** That chain runs with
|
|
120
|
+
* `siblingIndex` OFF under a byte-identical mandate, and with no listing to
|
|
121
|
+
* filter them a refusal list costs two real `this.resolve` calls on every
|
|
122
|
+
* relative import that has no variant — the majority, inside
|
|
123
|
+
* `@nativescript/core` included. The desktop chain's own measurement (+14% on
|
|
124
|
+
* a ~1400-module bundle from six failed resolves) is what says failed resolves
|
|
125
|
+
* are not free. It is a cost with a measured-zero benefit on this tree: the
|
|
126
|
+
* 30 `.gtk.*`/`.native.*` pairs in `@gjsify/adwaita-react-native` all HIT the
|
|
127
|
+
* NS chain at `.native`, and the refusal probe only runs when the chain found
|
|
128
|
+
* nothing. Reopen it from a measurement of that tree with the index on.
|
|
129
|
+
*/
|
|
130
|
+
export const BROWSER_REFUSED_SUFFIXES = ['gtk', 'desktop', 'native'];
|
|
85
131
|
/**
|
|
86
132
|
* The NativeScript chain: platform-specific first, then the platform-agnostic
|
|
87
133
|
* `native` suffix. Without a known platform only `native` applies.
|
|
@@ -104,6 +150,39 @@ export function nativescriptSuffixChain(platform) {
|
|
|
104
150
|
export function desktopSuffixChain(os) {
|
|
105
151
|
return os ? ['gtk', os, 'desktop'] : ['gtk', 'desktop'];
|
|
106
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The BROWSER chain: `.web` → base. Exactly ONE rung, and the count is the
|
|
155
|
+
* decision.
|
|
156
|
+
*
|
|
157
|
+
* A desktop build knows three different things about itself and so gets three
|
|
158
|
+
* rungs: the toolkit (`.gtk`), the kernel (`.<os>`) and the target family
|
|
159
|
+
* (`.desktop`). A `--app browser` build knows ONE — it targets the DOM — and
|
|
160
|
+
* every candidate second rung was rejected for a stated reason rather than left
|
|
161
|
+
* out for tidiness:
|
|
162
|
+
*
|
|
163
|
+
* - **No engine rung** (`.firefox` / `.chromium`). The target compiles one
|
|
164
|
+
* `esnext` bundle with no per-engine branch anywhere in it; a rung nobody can
|
|
165
|
+
* fill is a resolution rule that can only ever surprise someone.
|
|
166
|
+
* - **No OS rung.** One browser bundle is served to every operating system, so
|
|
167
|
+
* an OS rung would resolve against the BUILD HOST and bake it into a
|
|
168
|
+
* platform-neutral artifact. That is the mistake `plugins/gi-runtime-paths.ts`
|
|
169
|
+
* already documents for the GI prologue, committed here at resolution time
|
|
170
|
+
* where the evidence is a file name and not a code path.
|
|
171
|
+
* - **No `.dom` synonym.** Two spellings for one concept is a priority order
|
|
172
|
+
* someone has to memorise, and `.web` is what § 9 writes and what every
|
|
173
|
+
* `.web.*` file in this repository already uses.
|
|
174
|
+
* - **No `.browser` umbrella.** `.desktop` earns its rung by sitting above four
|
|
175
|
+
* spellings; above `.web` there is nothing for an umbrella to cover.
|
|
176
|
+
*
|
|
177
|
+
* Takes no parameter, unlike {@link desktopSuffixChain}: nothing about this
|
|
178
|
+
* chain depends on the host, which is the previous point restated from the API
|
|
179
|
+
* side. It is a function rather than a constant so that
|
|
180
|
+
* {@link PlatformResolvePluginOptions.suffixes} keeps its rule — a hand-written
|
|
181
|
+
* array is a second resolution order the tree does not state.
|
|
182
|
+
*/
|
|
183
|
+
export function browserSuffixChain() {
|
|
184
|
+
return ['web'];
|
|
185
|
+
}
|
|
107
186
|
/**
|
|
108
187
|
* The desktop suffix for the BUILD HOST, or `undefined` for a host outside
|
|
109
188
|
* ADR 0018's target set.
|
|
@@ -155,13 +234,21 @@ export function platformResolvePlugin(options) {
|
|
|
155
234
|
// EMPTY chain is one that was computed and came out empty — the shape a
|
|
156
235
|
// mis-wired orchestrator has — and it would send every import to base
|
|
157
236
|
// while looking installed.
|
|
158
|
-
throw new Error('gjsify platform resolve: the suffix chain is empty. Pass nativescriptSuffixChain(…)
|
|
159
|
-
'desktopSuffixChain(…), or omit the plugin.');
|
|
237
|
+
throw new Error('gjsify platform resolve: the suffix chain is empty. Pass nativescriptSuffixChain(…), ' +
|
|
238
|
+
'desktopSuffixChain(…) or browserSuffixChain(), or omit the plugin.');
|
|
160
239
|
}
|
|
161
240
|
const refused = options.refusedSuffixes ?? [];
|
|
162
241
|
const useIndex = options.siblingIndex ?? false;
|
|
163
242
|
// Per-directory listing cache, lowercased. `null` marks a directory that
|
|
164
243
|
// could not be read, which means "no opinion" — the resolver decides.
|
|
244
|
+
//
|
|
245
|
+
// CACHED FOR THE PLUGIN INSTANCE'S LIFE, so it needs the `watchChange` hook
|
|
246
|
+
// below: in a one-shot build the process ends before a listing can go stale,
|
|
247
|
+
// but `gjsify build --watch` and a Vite dev server both keep one plugin
|
|
248
|
+
// instance across rebuilds. Without invalidation an author who ADDS
|
|
249
|
+
// `foo.web.ts` beside `foo.ts` while the watcher runs keeps being served
|
|
250
|
+
// `foo.ts` until restart — the filter would be answering from a directory
|
|
251
|
+
// that no longer exists in that shape.
|
|
165
252
|
const listings = new Map();
|
|
166
253
|
const listingFor = (dir) => {
|
|
167
254
|
const cached = listings.get(dir);
|
|
@@ -196,6 +283,19 @@ export function platformResolvePlugin(options) {
|
|
|
196
283
|
const warned = new Set();
|
|
197
284
|
return {
|
|
198
285
|
name: 'gjsify-platform-resolve',
|
|
286
|
+
// The sibling index's invalidation. Both hosts call this for every path
|
|
287
|
+
// the watcher reports (`create` / `update` / `delete`), which is exactly
|
|
288
|
+
// the event that can make a cached listing wrong — a NEW `foo.web.ts` is
|
|
289
|
+
// a `create` in the directory the filter already answered for.
|
|
290
|
+
//
|
|
291
|
+
// Only the CHANGED path's directory is dropped, not the whole map: the
|
|
292
|
+
// map is refilled lazily, so a blanket clear would re-`readdirSync` every
|
|
293
|
+
// directory the next rebuild touches for one unrelated save. `dirname`
|
|
294
|
+
// of a virtual or `\0`-prefixed id is a directory that was never cached,
|
|
295
|
+
// and deleting an absent key is a no-op — no guard needed for that.
|
|
296
|
+
watchChange(id) {
|
|
297
|
+
listings.delete(dirname(id));
|
|
298
|
+
},
|
|
199
299
|
resolveId: {
|
|
200
300
|
order: 'pre',
|
|
201
301
|
async handler(source, importer, extraOptions) {
|
|
@@ -204,6 +304,23 @@ export function platformResolvePlugin(options) {
|
|
|
204
304
|
return null;
|
|
205
305
|
if (!source.startsWith('./') && !source.startsWith('../'))
|
|
206
306
|
return null;
|
|
307
|
+
// A QUERY NAMES A TRANSFORM OF ONE FILE, NOT A FILE TO FORK.
|
|
308
|
+
//
|
|
309
|
+
// `./x.blp?shared-tree`, `?raw`, `?url`: the plugin serving the query owns the
|
|
310
|
+
// resolution, and there is no second FILE for a platform chain to prefer —
|
|
311
|
+
// whatever the suffix is appended to, `./x.blp?shared-tree.native` is a
|
|
312
|
+
// specifier no convention gives a meaning to.
|
|
313
|
+
//
|
|
314
|
+
// Standing down is not merely tidier here, it is required. A probe that misses
|
|
315
|
+
// is normally free: `this.resolve('./x.native')` on a path that is not there
|
|
316
|
+
// returns null and the loop walks on. With a query in the specifier it is NOT
|
|
317
|
+
// — measured on `--app nativescript`, the missed probe surfaced as
|
|
318
|
+
// `UNLOADABLE_DEPENDENCY … No such file or directory (os error 2)` against the
|
|
319
|
+
// ORIGINAL import, and the chain never reached the plugin that would have
|
|
320
|
+
// resolved it. So the blind probe does not waste a lookup, it fails the build,
|
|
321
|
+
// and it does so pointing at a line that is correct.
|
|
322
|
+
if (source.includes('?'))
|
|
323
|
+
return null;
|
|
207
324
|
const extMatch = KNOWN_EXT_RE.exec(source);
|
|
208
325
|
const origExt = extMatch ? extMatch[0] : '';
|
|
209
326
|
const base = origExt ? source.slice(0, -origExt.length) : source;
|
|
@@ -253,12 +370,16 @@ export function platformResolvePlugin(options) {
|
|
|
253
370
|
if (!probe || warned.has(probe.id))
|
|
254
371
|
continue;
|
|
255
372
|
warned.add(probe.id);
|
|
373
|
+
// The remedy names THIS chain's own rungs. It used to end in
|
|
374
|
+
// a hardcoded `.desktop`, which was true of the only chain
|
|
375
|
+
// that had a refusal list and became advice to write a file
|
|
376
|
+
// the browser chain refuses the moment a second one did.
|
|
377
|
+
const ownVariants = suffixes.map((own) => `.${own}`).join(' / ');
|
|
256
378
|
this.warn(`gjsify platform resolve: "${base}.${suffix}" exists but is NOT a rung of this ` +
|
|
257
379
|
`target's chain (${suffixes.join(' → ')} → base), so ${importer} gets the base ` +
|
|
258
380
|
`file. ADR 0032 § 9: a .${suffix} variant is written for a runtime this build ` +
|
|
259
381
|
`is not, and reaching for it would hand this target code whose failure only ` +
|
|
260
|
-
`shows up on screen. Move what applies here into
|
|
261
|
-
`.desktop variant.`);
|
|
382
|
+
`shows up on screen. Move what applies here into ${ownVariants} or the base file.`);
|
|
262
383
|
}
|
|
263
384
|
return null;
|
|
264
385
|
},
|
|
@@ -189,6 +189,70 @@ function emitGiBackedDiagnostic(registerPaths, detected, cwd) {
|
|
|
189
189
|
function isFullConfig(v) {
|
|
190
190
|
return v !== null && typeof v === 'object' && !Array.isArray(v) && 'plugins' in v && 'options' in v;
|
|
191
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Merge the orchestrator's `transform` with the analysis side's, PER KEY — the
|
|
194
|
+
* analysis side wins for the keys it actually sets.
|
|
195
|
+
*
|
|
196
|
+
* `analysisOptions.transform ?? orchestratorOptions?.transform` was wrong for a
|
|
197
|
+
* MEASURED reason, not a stylistic one: the CLI's config merge does
|
|
198
|
+
* `bundler.transform ??= {}` (`@gjsify/cli` `src/config.ts`), so the analysis side is
|
|
199
|
+
* ALWAYS a non-nullish object and `??` therefore always took it — dropping the
|
|
200
|
+
* orchestrator's `target`, `define` and `inject` from every single analysis pass. The
|
|
201
|
+
* detector then reads a bundle transformed differently from the one that ships, which
|
|
202
|
+
* is exactly what the "analyse the bundled output AFTER tree-shaking" invariant exists
|
|
203
|
+
* to prevent.
|
|
204
|
+
*
|
|
205
|
+
* What that cost, measured on `--app gjs`, whose orchestrator defines
|
|
206
|
+
* `process.env.READABLE_STREAM` to `"disable"`. Entry:
|
|
207
|
+
*
|
|
208
|
+
* if (process.env.READABLE_STREAM !== 'disable') document.getElementById('app');
|
|
209
|
+
*
|
|
210
|
+
* The define makes that branch dead, so the emitted bundle carries no `document`
|
|
211
|
+
* reference at all — and the build injected `document` + `HTMLCanvasElement` + `Path2D`
|
|
212
|
+
* anyway: 24 globals and 276_726 bytes where the same entry now yields 21 and 176_487.
|
|
213
|
+
* 100 KB of DOM/canvas register code, one of it a GI-backed register that makes the
|
|
214
|
+
* artifact hard-require a GTK runtime at load. Silently, at exit 0.
|
|
215
|
+
*
|
|
216
|
+
* Note what does NOT discriminate here, so the next reproduction does not start with
|
|
217
|
+
* it: `process` is detected on every `--app gjs` build regardless, because the process
|
|
218
|
+
* stub the target prepends in `renderChunk` writes `globalThis.process` and the
|
|
219
|
+
* detector reads its own banner back.
|
|
220
|
+
*
|
|
221
|
+
* `define` and `inject` are string maps and merge per key; every other transform key is
|
|
222
|
+
* a scalar (`target`, `lang`, `sourceType`) or a coherent option bag (`typescript`,
|
|
223
|
+
* `decorator`, `assumptions`, `tsconfig`) that is replaced whole. This is deliberately
|
|
224
|
+
* the same shape as `mergeBundlerOptions` in `@gjsify/cli`, which merges the FINAL
|
|
225
|
+
* build's options: the two must agree, or the analysis is measuring a different bundle
|
|
226
|
+
* again one key further down.
|
|
227
|
+
*/
|
|
228
|
+
function mergeTransformOptions(orchestrator, analysis) {
|
|
229
|
+
if (!orchestrator)
|
|
230
|
+
return analysis;
|
|
231
|
+
if (!analysis)
|
|
232
|
+
return orchestrator;
|
|
233
|
+
const merged = { ...orchestrator, ...analysis };
|
|
234
|
+
if (orchestrator.define || analysis.define) {
|
|
235
|
+
merged.define = { ...orchestrator.define, ...analysis.define };
|
|
236
|
+
}
|
|
237
|
+
if (orchestrator.inject || analysis.inject) {
|
|
238
|
+
merged.inject = { ...orchestrator.inject, ...analysis.inject };
|
|
239
|
+
}
|
|
240
|
+
return merged;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Same per-key rule for `resolve`. No caller sets `analysisOptions.resolve` today, so
|
|
244
|
+
* this is inert right now — it is here because `resolve` is the third plain-object
|
|
245
|
+
* field on the same code path and a whole-object `??` on it would silently drop the
|
|
246
|
+
* orchestrator's `conditionNames`/`mainFields`, which is the divergence the class
|
|
247
|
+
* comment above describes.
|
|
248
|
+
*/
|
|
249
|
+
function mergeResolveOptions(orchestrator, analysis) {
|
|
250
|
+
if (!orchestrator)
|
|
251
|
+
return analysis;
|
|
252
|
+
if (!analysis)
|
|
253
|
+
return orchestrator;
|
|
254
|
+
return { ...orchestrator, ...analysis };
|
|
255
|
+
}
|
|
192
256
|
/**
|
|
193
257
|
* Run the iterative in-memory build with acorn-based global detection, each pass
|
|
194
258
|
* seeded by the previous pass's globals, until the detected set is stable. Returns the
|
|
@@ -237,10 +301,16 @@ export async function detectAutoGlobals(analysisOptions, pluginOptions, gjsifyPl
|
|
|
237
301
|
: analysisOptions.input;
|
|
238
302
|
// Take resolve/external/transform from the orchestrator so the analysis bundle
|
|
239
303
|
// goes through the same module resolution as the final build; explicit
|
|
240
|
-
// analysis-side overrides win.
|
|
241
|
-
|
|
304
|
+
// analysis-side overrides win PER KEY (see `mergeTransformOptions`).
|
|
305
|
+
//
|
|
306
|
+
// `external` stays a whole-value choice on purpose: it is not a plain object
|
|
307
|
+
// (string | RegExp | array | predicate), the orchestrator's array has already
|
|
308
|
+
// folded the caller's own entries in, and the shape rules an array cannot
|
|
309
|
+
// express live in `externalsPlugin` — which is part of `gjsifyPluginsArray`
|
|
310
|
+
// below, so it applies to the analysis bundle whichever array wins here.
|
|
311
|
+
const mergedResolve = mergeResolveOptions(orchestratorOptions?.resolve, analysisOptions.resolve);
|
|
242
312
|
const mergedExternal = analysisOptions.external ?? orchestratorOptions?.external;
|
|
243
|
-
const mergedTransform = analysisOptions.transform
|
|
313
|
+
const mergedTransform = mergeTransformOptions(orchestratorOptions?.transform, analysisOptions.transform);
|
|
244
314
|
const orchTreeshake = orchestratorOptions?.treeshake;
|
|
245
315
|
const gjsifyPluginsArray = Array.isArray(gjsifyInstance) ? gjsifyInstance : [gjsifyInstance];
|
|
246
316
|
const chunkCodes = await bundler({
|
|
@@ -408,8 +478,12 @@ export async function detectNodeGiGlobals(analysisOptions, pluginOptions, gjsify
|
|
|
408
478
|
...baseOptions,
|
|
409
479
|
input: analysisOptions.input,
|
|
410
480
|
external: analysisOptions.external ?? baseOptions.external,
|
|
411
|
-
|
|
412
|
-
|
|
481
|
+
// Per key, for the reason spelled out on `mergeTransformOptions`: the
|
|
482
|
+
// CLI always hands an object here, so a whole-object `??` dropped this
|
|
483
|
+
// target's `target: 'node24'` and `define: { global, window }` from
|
|
484
|
+
// every reverse-bridge analysis pass.
|
|
485
|
+
resolve: mergeResolveOptions(baseOptions.resolve, analysisOptions.resolve),
|
|
486
|
+
transform: mergeTransformOptions(baseOptions.transform, analysisOptions.transform),
|
|
413
487
|
plugins: [...callerPlugins, ...gjsifyPluginsArray],
|
|
414
488
|
logLevel: 'silent',
|
|
415
489
|
},
|
package/lib/utils/runtime.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// Kept deliberately pure — no `gi://` / `@girs/*` imports, no side effects —
|
|
12
12
|
// so it stays loadable on every host and so consumers can import it via the
|
|
13
13
|
// `@gjsify/rolldown-plugin-gjsify/runtime` subpath without pulling the rest of
|
|
14
|
-
// the plugin (which transitively loads
|
|
14
|
+
// the plugin (which transitively loads the Blueprint parser, deepkit, etc.).
|
|
15
15
|
/**
|
|
16
16
|
* `true` when running under Bun.
|
|
17
17
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/rolldown-plugin-gjsify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.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",
|
|
@@ -60,11 +60,11 @@
|
|
|
60
60
|
],
|
|
61
61
|
"license": "MIT",
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@gjsify/console": "^0.
|
|
64
|
-
"@gjsify/resolve-npm": "^0.
|
|
65
|
-
"@gjsify/rolldown-plugin-deepkit": "^0.
|
|
66
|
-
"@gjsify/rolldown-plugin-pnp": "^0.
|
|
67
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
63
|
+
"@gjsify/console": "^0.52.0",
|
|
64
|
+
"@gjsify/resolve-npm": "^0.52.0",
|
|
65
|
+
"@gjsify/rolldown-plugin-deepkit": "^0.52.0",
|
|
66
|
+
"@gjsify/rolldown-plugin-pnp": "^0.52.0",
|
|
67
|
+
"@gjsify/vite-plugin-blueprint": "^0.52.0",
|
|
68
68
|
"@rollup/pluginutils": "^5.4.0",
|
|
69
69
|
"acorn": "^8.17.0",
|
|
70
70
|
"acorn-typescript": "^1.4.13",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"sass": "^1.101.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@gjsify/lightningcss-native": "^0.
|
|
77
|
+
"@gjsify/lightningcss-native": "^0.52.0",
|
|
78
78
|
"rolldown": "^1.1.4"
|
|
79
79
|
},
|
|
80
80
|
"peerDependenciesMeta": {
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@gjsify/cli": "^0.
|
|
90
|
-
"@gjsify/unit": "^0.
|
|
89
|
+
"@gjsify/cli": "^0.52.0",
|
|
90
|
+
"@gjsify/unit": "^0.52.0",
|
|
91
91
|
"@types/node": "^25.9.2",
|
|
92
92
|
"rolldown": "^1.1.4",
|
|
93
93
|
"typescript": "^6.0.3"
|