@gjsify/rolldown-plugin-gjsify 0.4.35 → 0.4.37
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 +32 -18
- package/lib/app/gjs.d.ts +17 -0
- package/lib/app/gjs.js +63 -0
- package/lib/app/index.d.ts +3 -1
- package/lib/app/index.js +2 -1
- package/lib/app/nativescript.d.ts +17 -0
- package/lib/app/nativescript.js +130 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +1 -0
- package/lib/plugin.js +9 -1
- package/lib/plugins/platform-resolve.d.ts +34 -0
- package/lib/plugins/platform-resolve.js +135 -0
- package/lib/types/app.d.ts +1 -1
- package/package.json +11 -11
package/lib/app/browser.js
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Browser builds redirect `@girs/*` and `gi://*` to an empty virtual module
|
|
4
4
|
// (they appear transitively via `@gjsify/unit` and similar packages with
|
|
5
|
-
// GJS-specific code paths).
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// GJS-specific code paths). Bare Node specifiers + their `node:*` prefix
|
|
6
|
+
// variants are routed to `@gjsify/<X>` via the curated
|
|
7
|
+
// `ALIASES_NODE_FOR_BROWSER` table — the dynamic per-runtimes-triplet
|
|
8
|
+
// resolver (`getDerivedAliasesSync`) finishes the routing in a second pass
|
|
9
|
+
// (`@gjsify/<X>` → `@gjsify/<X>/globals` / `@gjsify/empty` depending on the
|
|
10
|
+
// slot declaration).
|
|
8
11
|
import { aliasPlugin } from '../plugins/alias.js';
|
|
9
12
|
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
10
13
|
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
11
|
-
import { getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
14
|
+
import { ALIASES_NODE_FOR_BROWSER, getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
12
15
|
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
13
16
|
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
14
17
|
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
@@ -17,23 +20,34 @@ export const setupForBrowser = async (input) => {
|
|
|
17
20
|
const external = [...userExternal];
|
|
18
21
|
const exclude = input.pluginOptions.exclude ?? [];
|
|
19
22
|
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
// Bare Node-builtin + `node:*` prefix aliases — both forms route to the
|
|
24
|
+
// same `@gjsify/<X>` target. Generated deterministically from
|
|
25
|
+
// `ALIASES_NODE_FOR_BROWSER` so a single source-of-truth in
|
|
26
|
+
// `@gjsify/resolve-npm` drives every browser-app build (no per-target
|
|
27
|
+
// hand-curation drift). The `process` entry here flips today's
|
|
28
|
+
// accidental `@gjsify/empty` to the polyfill (`@gjsify/process`) —
|
|
29
|
+
// `@gjsify/unit`'s `await import('process')` is unreachable in browser
|
|
30
|
+
// (typeof document check comes first), but Rolldown still resolves it
|
|
31
|
+
// statically; the polyfill is the more honest target.
|
|
32
|
+
const nodePrefixAliases = {};
|
|
33
|
+
for (const [bare, target] of Object.entries(ALIASES_NODE_FOR_BROWSER)) {
|
|
34
|
+
nodePrefixAliases[bare] = target;
|
|
35
|
+
nodePrefixAliases[`node:${bare}`] = target;
|
|
36
|
+
}
|
|
37
|
+
// Legacy overrides — kept as a separate Record so future per-target
|
|
38
|
+
// shims have an explicit landing pad. Today: empty. The `process` /
|
|
39
|
+
// `assert` entries that lived here historically have moved into
|
|
40
|
+
// `ALIASES_NODE_FOR_BROWSER` (assert → `@gjsify/assert`, process →
|
|
41
|
+
// `@gjsify/process`) — pulling them out of this layer means the new
|
|
42
|
+
// tabular values are not silently shadowed by a stale `@gjsify/empty`.
|
|
43
|
+
const browserPolyfillAliases = {};
|
|
31
44
|
// Derived `@gjsify/<X>` aliases driven by per-package `gjsify.runtimes`
|
|
32
|
-
// triplet declarations. Merge order: derived (lowest priority) →
|
|
33
|
-
//
|
|
34
|
-
//
|
|
45
|
+
// triplet declarations. Merge order: derived (lowest priority) → curated
|
|
46
|
+
// bare/`node:*` Node-builtin map → legacy per-target overrides → user.
|
|
47
|
+
// Higher tiers WIN on conflict — the user always retains final say.
|
|
35
48
|
const aliasMap = {
|
|
36
49
|
...getDerivedAliasesSync('browser'),
|
|
50
|
+
...nodePrefixAliases,
|
|
37
51
|
...browserPolyfillAliases,
|
|
38
52
|
...input.pluginOptions.aliases,
|
|
39
53
|
...input.userAliases,
|
package/lib/app/gjs.d.ts
CHANGED
|
@@ -30,3 +30,20 @@ export interface GjsFactoryInput {
|
|
|
30
30
|
pluginOptions: PluginOptions;
|
|
31
31
|
}
|
|
32
32
|
export declare const setupForGjs: (input: GjsFactoryInput) => Promise<GjsBuildConfig>;
|
|
33
|
+
/**
|
|
34
|
+
* Recognize the `/register` and `/register/<feature>` subpath shapes that
|
|
35
|
+
* `--globals auto` injects into the bundle as side-effect imports.
|
|
36
|
+
*
|
|
37
|
+
* Matches every shape that goes through the alias layer + Rolldown
|
|
38
|
+
* resolution chain to a `@gjsify/<pkg>/register*` target:
|
|
39
|
+
* - bare: `<pkg>/register`, `<pkg>/register/<feature>`
|
|
40
|
+
* - fully qualified `@gjsify/<pkg>/register`, `@gjsify/<pkg>/register/<feature>`
|
|
41
|
+
* - resolved disk paths under a real `node_modules/<scope>/<pkg>/lib/esm/register*`
|
|
42
|
+
*
|
|
43
|
+
* Used by the `--app gjs` externals predicate to force-inline these even
|
|
44
|
+
* when the user passes them via `bundler.external`. Exported for direct
|
|
45
|
+
* use by the regression test in `auto-globals.spec.ts`; this is the
|
|
46
|
+
* canonical contract — change-detector status. Keep in sync with the
|
|
47
|
+
* `AGENTS.md` §Tree-shakeable globals subpath convention.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isRegisterSubpath(id: string): boolean;
|
package/lib/app/gjs.js
CHANGED
|
@@ -58,6 +58,24 @@ export const setupForGjs = async (input) => {
|
|
|
58
58
|
// string specifiers stay externalised by name.
|
|
59
59
|
const exactExternal = ['cairo', 'gettext', 'system', ...userExternal];
|
|
60
60
|
const external = (id) => {
|
|
61
|
+
// `@gjsify/<pkg>/register[/<feature>]` and the bare-`<pkg>/register`
|
|
62
|
+
// form MUST NEVER be externalized for `--app gjs`. These are the
|
|
63
|
+
// side-effect entry points that `--globals auto` injects to wire
|
|
64
|
+
// up `globalThis.{Buffer,fetch,…}`; GJS's native ESM loader has no
|
|
65
|
+
// node_modules walker AND does not follow `package.json#exports`
|
|
66
|
+
// maps for bare specifiers, so an externalized
|
|
67
|
+
// `import '@gjsify/buffer/register/buffer'` at runtime would throw
|
|
68
|
+
// `Module not found` even when `<pkg>/lib/esm/register/buffer.js`
|
|
69
|
+
// is on disk via the exports map.
|
|
70
|
+
//
|
|
71
|
+
// Inlining is the only safe option — the exclusion is by SHAPE
|
|
72
|
+
// (`*/register` or `*/register/*` substring), not by an explicit
|
|
73
|
+
// package list, so the invariant scales to every package added
|
|
74
|
+
// by the tree-shakeable-globals convention. See AGENTS.md
|
|
75
|
+
// §Tree-shakeable globals — /register subpath convention, and
|
|
76
|
+
// §Build — Rolldown, platform plugins for the externals policy.
|
|
77
|
+
if (isRegisterSubpath(id))
|
|
78
|
+
return false;
|
|
61
79
|
if (id.startsWith('gi://'))
|
|
62
80
|
return true;
|
|
63
81
|
if (exactExternal.includes(id))
|
|
@@ -294,3 +312,48 @@ function flattenAliases(map) {
|
|
|
294
312
|
}
|
|
295
313
|
return out;
|
|
296
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Recognize the `/register` and `/register/<feature>` subpath shapes that
|
|
317
|
+
* `--globals auto` injects into the bundle as side-effect imports.
|
|
318
|
+
*
|
|
319
|
+
* Matches every shape that goes through the alias layer + Rolldown
|
|
320
|
+
* resolution chain to a `@gjsify/<pkg>/register*` target:
|
|
321
|
+
* - bare: `<pkg>/register`, `<pkg>/register/<feature>`
|
|
322
|
+
* - fully qualified `@gjsify/<pkg>/register`, `@gjsify/<pkg>/register/<feature>`
|
|
323
|
+
* - resolved disk paths under a real `node_modules/<scope>/<pkg>/lib/esm/register*`
|
|
324
|
+
*
|
|
325
|
+
* Used by the `--app gjs` externals predicate to force-inline these even
|
|
326
|
+
* when the user passes them via `bundler.external`. Exported for direct
|
|
327
|
+
* use by the regression test in `auto-globals.spec.ts`; this is the
|
|
328
|
+
* canonical contract — change-detector status. Keep in sync with the
|
|
329
|
+
* `AGENTS.md` §Tree-shakeable globals subpath convention.
|
|
330
|
+
*/
|
|
331
|
+
export function isRegisterSubpath(id) {
|
|
332
|
+
// Source-shape: a bare or fully-qualified specifier ending in
|
|
333
|
+
// `/register` or `/register/<feature>`. The leading `/` rules out
|
|
334
|
+
// false positives like `register` (the bare word) or
|
|
335
|
+
// `@scope/unregister`.
|
|
336
|
+
//
|
|
337
|
+
// ✓ `fetch/register`
|
|
338
|
+
// ✓ `@gjsify/buffer/register`
|
|
339
|
+
// ✓ `@gjsify/node-globals/register/buffer`
|
|
340
|
+
// ✗ `register` (no `/` prefix)
|
|
341
|
+
// ✗ `@scope/unregister` (the `/` is followed by `un`, not `register`)
|
|
342
|
+
// ✗ `foo/register.js?query=1` (query-suffix → treat as resolved-path,
|
|
343
|
+
// caught by the second branch only when
|
|
344
|
+
// the file extension is intact)
|
|
345
|
+
if (/\/register(?:\/[^?]*)?$/.test(id)) {
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
// Resolved disk-path shape — Rolldown sees these after the alias
|
|
349
|
+
// plugin + node_modules resolver run. Matches both ESM build output
|
|
350
|
+
// (`lib/esm/register/<feature>.js`) and any future TS-direct setup
|
|
351
|
+
// that points the export at `src/register/<feature>.ts`. Strictly
|
|
352
|
+
// requires the file extension at the end — a Rolldown synthetic-id
|
|
353
|
+
// suffix like `?query=1` therefore does NOT match (those callers
|
|
354
|
+
// expect to flow through the normal externals path).
|
|
355
|
+
if (/[/\\]register(?:[/\\][^/\\]+)?\.(?:[mc]?js|ts)$/.test(id)) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
}
|
package/lib/app/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { setupForGjs } from './gjs.js';
|
|
1
|
+
export { setupForGjs, isRegisterSubpath } from './gjs.js';
|
|
2
2
|
export type { GjsBuildConfig, GjsFactoryInput } from './gjs.js';
|
|
3
3
|
export { setupForNode } from './node.js';
|
|
4
4
|
export type { NodeBuildConfig, NodeFactoryInput } from './node.js';
|
|
5
5
|
export { setupForBrowser } from './browser.js';
|
|
6
6
|
export type { BrowserBuildConfig, BrowserFactoryInput } from './browser.js';
|
|
7
|
+
export { setupForNativescript } from './nativescript.js';
|
|
8
|
+
export type { NativescriptBuildConfig, NativescriptFactoryInput } from './nativescript.js';
|
package/lib/app/index.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
+
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
|
+
export interface NativescriptBuildConfig {
|
|
4
|
+
options: RolldownOptions;
|
|
5
|
+
plugins: RolldownPluginOption[];
|
|
6
|
+
}
|
|
7
|
+
export interface NativescriptFactoryInput {
|
|
8
|
+
input?: RolldownOptions['input'];
|
|
9
|
+
output: {
|
|
10
|
+
file?: string;
|
|
11
|
+
dir?: string;
|
|
12
|
+
};
|
|
13
|
+
userExternal?: string[];
|
|
14
|
+
userAliases?: Record<string, string>;
|
|
15
|
+
pluginOptions: PluginOptions;
|
|
16
|
+
}
|
|
17
|
+
export declare const setupForNativescript: (input: NativescriptFactoryInput) => Promise<NativescriptBuildConfig>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// `--app nativescript` Rolldown configuration factory.
|
|
2
|
+
//
|
|
3
|
+
// NativeScript builds target the V8 engine bundled into the NS Android (JNI)
|
|
4
|
+
// and iOS (Objective-C bridge) runtimes. Both runtimes have aligned V8
|
|
5
|
+
// versions since NS 8.5 (V8 10.3.22+) and ship full ES2024 surface, so the
|
|
6
|
+
// transform target is `esnext`.
|
|
7
|
+
//
|
|
8
|
+
// Native bridges (`java.*`, `android.*`, `androidx.*`, `kotlin.*`, `NS*`,
|
|
9
|
+
// `UI*`, `CG*`, `NSObject`, etc.) are exposed as GLOBAL identifiers by the
|
|
10
|
+
// NativeScript runtime at load time — like GJS's `imports.gi.*`, but ambient
|
|
11
|
+
// rather than module-namespaced. They are NOT aliased and NOT externalized;
|
|
12
|
+
// any value reference resolves at runtime against the host globals.
|
|
13
|
+
//
|
|
14
|
+
// `@girs/*` and `gi://*` imports are silenced via `gjsImportsEmptyPlugin` —
|
|
15
|
+
// they appear transitively through `@gjsify/unit` and similar packages with
|
|
16
|
+
// GJS-specific code paths that never execute on NS.
|
|
17
|
+
//
|
|
18
|
+
// Bare Node specifiers + their `node:*` prefix variants are routed to
|
|
19
|
+
// `@gjsify/<X>` via the curated `ALIASES_NODE_FOR_NATIVESCRIPT` table — the
|
|
20
|
+
// dynamic per-runtimes-triplet resolver (`getDerivedAliasesSync`) finishes
|
|
21
|
+
// the routing in a second pass (`@gjsify/<X>` → `@gjsify/<X>/globals` /
|
|
22
|
+
// `@gjsify/empty` depending on the slot declaration).
|
|
23
|
+
//
|
|
24
|
+
// No `cssAsStringPlugin` (NativeScript ships its own CSS pipeline as part
|
|
25
|
+
// of `@nativescript/core`) and no `blueprintPlugin` (Blueprint is GTK-only).
|
|
26
|
+
import { aliasPlugin } from '../plugins/alias.js';
|
|
27
|
+
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
28
|
+
import { ALIASES_NODE_FOR_NATIVESCRIPT, getDerivedAliasesSync } from '@gjsify/resolve-npm';
|
|
29
|
+
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
30
|
+
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
31
|
+
import { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from '../plugins/platform-resolve.js';
|
|
32
|
+
export const setupForNativescript = async (input) => {
|
|
33
|
+
const userExternal = input.userExternal ?? [];
|
|
34
|
+
const external = [...userExternal];
|
|
35
|
+
const exclude = input.pluginOptions.exclude ?? [];
|
|
36
|
+
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
37
|
+
// Target platform — discovered from the env NS' CLI sets when it spawns a
|
|
38
|
+
// bundler (else `undefined` → `.native`-only resolution + neutral defines).
|
|
39
|
+
// `gjsify build` is a production bundler, so `__DEV__` is `false` here.
|
|
40
|
+
const platform = detectNativescriptPlatform();
|
|
41
|
+
// Bare Node-builtin + `node:*` prefix aliases — both forms route to the
|
|
42
|
+
// same `@gjsify/<X>` target. Generated deterministically from
|
|
43
|
+
// `ALIASES_NODE_FOR_NATIVESCRIPT` so a single source-of-truth in
|
|
44
|
+
// `@gjsify/resolve-npm` drives every NS-app build (no per-target
|
|
45
|
+
// hand-curation drift).
|
|
46
|
+
const nodePrefixAliases = {};
|
|
47
|
+
for (const [bare, target] of Object.entries(ALIASES_NODE_FOR_NATIVESCRIPT)) {
|
|
48
|
+
nodePrefixAliases[bare] = target;
|
|
49
|
+
nodePrefixAliases[`node:${bare}`] = target;
|
|
50
|
+
}
|
|
51
|
+
// Legacy / per-target overrides — kept as a separate Record so future
|
|
52
|
+
// mobile-specific shims (e.g. an NS-equivalent of `@gjsify/process`)
|
|
53
|
+
// have an explicit landing pad. Today: empty. New entries SHOULD go
|
|
54
|
+
// into `ALIASES_NODE_FOR_NATIVESCRIPT` so the same wiring is reachable
|
|
55
|
+
// from the Vite-plugin track (`gjsifyNativescript()` preset) too.
|
|
56
|
+
const nativescriptOverrideAliases = {};
|
|
57
|
+
// Derived `@gjsify/<X>` aliases driven by per-package `gjsify.runtimes`
|
|
58
|
+
// triplet declarations. Merge order: derived (lowest priority) → curated
|
|
59
|
+
// bare/`node:*` Node-builtin map → per-target overrides → user.
|
|
60
|
+
// Higher tiers WIN on conflict — the user always retains final say.
|
|
61
|
+
const aliasMap = {
|
|
62
|
+
...getDerivedAliasesSync('nativescript'),
|
|
63
|
+
...nodePrefixAliases,
|
|
64
|
+
...nativescriptOverrideAliases,
|
|
65
|
+
...input.pluginOptions.aliases,
|
|
66
|
+
...input.userAliases,
|
|
67
|
+
};
|
|
68
|
+
const options = {
|
|
69
|
+
input: entryPoints,
|
|
70
|
+
// NS's V8 is a "browser-shaped" runtime from Rolldown's perspective:
|
|
71
|
+
// no Node-builtin auto-externals, no globalThis-shaped node-only
|
|
72
|
+
// semantics. `browser` platform is the closest match — the actual
|
|
73
|
+
// host environment is provided by the NS runtime at load time, not
|
|
74
|
+
// by V8 itself.
|
|
75
|
+
platform: 'browser',
|
|
76
|
+
external,
|
|
77
|
+
resolve: {
|
|
78
|
+
mainFields: ['nativescript', 'module', 'main'],
|
|
79
|
+
conditionNames: ['import', 'nativescript'],
|
|
80
|
+
},
|
|
81
|
+
transform: {
|
|
82
|
+
target: 'esnext',
|
|
83
|
+
define: {
|
|
84
|
+
global: 'globalThis',
|
|
85
|
+
// NO `window` define — NativeScript apps don't have a DOM
|
|
86
|
+
// and rely on the canonical absence of `window` to gate
|
|
87
|
+
// their cross-platform branches.
|
|
88
|
+
//
|
|
89
|
+
// Standard NS compile-time platform flags (`__ANDROID__` /
|
|
90
|
+
// `__IOS__` / `__APPLE__` / `__VISIONOS__` / `__DEV__`) so app
|
|
91
|
+
// code branching on them is statically resolved + dead-code
|
|
92
|
+
// eliminated per target — matching the globals
|
|
93
|
+
// `@nativescript/vite` seeds in its main entry.
|
|
94
|
+
...nativescriptPlatformDefines(platform, { dev: false }),
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
output: {
|
|
98
|
+
...input.output,
|
|
99
|
+
format: 'esm',
|
|
100
|
+
sourcemap: false,
|
|
101
|
+
// Single-bundle output. The NS bundler (@nativescript/webpack or
|
|
102
|
+
// @nativescript/vite) consumes the resulting `.mjs` as an entry
|
|
103
|
+
// file and produces the final app bundle from there.
|
|
104
|
+
codeSplitting: false,
|
|
105
|
+
},
|
|
106
|
+
treeshake: true,
|
|
107
|
+
};
|
|
108
|
+
const plugins = [
|
|
109
|
+
gjsImportsEmptyPlugin(),
|
|
110
|
+
// Platform-specific source variants (`*.android` / `*.ios` /
|
|
111
|
+
// `*.native`) win over the base file — resolved BEFORE the Node-builtin
|
|
112
|
+
// alias routing so a platform fork of a portable module is honored.
|
|
113
|
+
platformResolvePlugin({ platform }),
|
|
114
|
+
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
115
|
+
// NO blueprintPlugin — Blueprint is a GTK-specific UI DSL
|
|
116
|
+
// NO cssAsStringPlugin — NS ships its own CSS pipeline via
|
|
117
|
+
// @nativescript/core; .css imports are handled by the consuming
|
|
118
|
+
// @nativescript/webpack or @nativescript/vite build
|
|
119
|
+
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
120
|
+
];
|
|
121
|
+
return { options, plugins };
|
|
122
|
+
};
|
|
123
|
+
function flattenAliases(map) {
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const [from, to] of Object.entries(map)) {
|
|
126
|
+
if (to)
|
|
127
|
+
out[from] = to;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export type { TextLoaderPluginOptions, LoaderKind } from './plugins/text-loader.
|
|
|
12
12
|
export { shebangPlugin, GJS_SHEBANG, NODE_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
|
|
13
13
|
export type { ShebangPluginOptions } from './plugins/shebang.js';
|
|
14
14
|
export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
15
|
+
export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
16
|
+
export type { PlatformResolvePluginOptions, NativescriptPlatform } from './plugins/platform-resolve.js';
|
|
15
17
|
export * from './plugin.js';
|
|
16
18
|
import { gjsifyPlugin } from './plugin.js';
|
|
17
19
|
export { gjsifyPlugin };
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { cssAsStringPlugin } from './plugins/css-as-string.js';
|
|
|
9
9
|
export { textLoaderPlugin } from './plugins/text-loader.js';
|
|
10
10
|
export { shebangPlugin, GJS_SHEBANG, NODE_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
|
|
11
11
|
export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
12
|
+
export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
|
|
12
13
|
export * from './plugin.js';
|
|
13
14
|
import { gjsifyPlugin } from './plugin.js';
|
|
14
15
|
export { gjsifyPlugin };
|
package/lib/plugin.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// The CLI consumer (`@gjsify/cli`) calls `gjsifyPlugin(...)` to get back
|
|
11
11
|
// `{ options, plugins }`, then calls `rolldown({ ...options, plugins:
|
|
12
12
|
// [...userPlugins, ...plugins] })`.
|
|
13
|
-
import { setupForGjs, setupForNode, setupForBrowser } from './app/index.js';
|
|
13
|
+
import { setupForGjs, setupForNode, setupForBrowser, setupForNativescript } from './app/index.js';
|
|
14
14
|
import { setupLib } from './library/index.js';
|
|
15
15
|
/**
|
|
16
16
|
* Build the Rolldown configuration template + plugin array for the given
|
|
@@ -60,6 +60,14 @@ export const gjsifyPlugin = async (input, pluginOptions = {}) => {
|
|
|
60
60
|
userAliases: input.userAliases,
|
|
61
61
|
pluginOptions,
|
|
62
62
|
});
|
|
63
|
+
case 'nativescript':
|
|
64
|
+
return await setupForNativescript({
|
|
65
|
+
input: input.input,
|
|
66
|
+
output: input.output,
|
|
67
|
+
userExternal: input.userExternal,
|
|
68
|
+
userAliases: input.userAliases,
|
|
69
|
+
pluginOptions,
|
|
70
|
+
});
|
|
63
71
|
default:
|
|
64
72
|
throw new TypeError('Unknown app platform: ' + app);
|
|
65
73
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Plugin } from 'rolldown';
|
|
2
|
+
export type NativescriptPlatform = 'android' | 'ios' | 'visionos';
|
|
3
|
+
export interface PlatformResolvePluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Target platform. When omitted, only `.native.*` variants are tried
|
|
6
|
+
* (the platform-specific `.android.*` / `.ios.*` lookups are skipped).
|
|
7
|
+
*/
|
|
8
|
+
platform?: NativescriptPlatform;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve platform-specific source-file variants (`*.android` / `*.ios` /
|
|
12
|
+
* `*.native`) ahead of the base file. Relative imports only — bare/package
|
|
13
|
+
* specifiers are left to the normal resolver chain (package platform-`main`
|
|
14
|
+
* fields are a separate, rarer concern handled by the alias layer).
|
|
15
|
+
*/
|
|
16
|
+
export declare function platformResolvePlugin(options?: PlatformResolvePluginOptions): Plugin;
|
|
17
|
+
/**
|
|
18
|
+
* Best-effort detection of the NativeScript target platform from the
|
|
19
|
+
* environment NS' CLI sets when it spawns a bundler (`NATIVESCRIPT_BUNDLER_ENV`
|
|
20
|
+
* / `NATIVESCRIPT_WEBPACK_ENV` carry the platform; `NATIVESCRIPT_PLATFORM` is
|
|
21
|
+
* an explicit override). Returns `undefined` when no platform is discernible —
|
|
22
|
+
* callers then fall back to `.native`-only resolution + neutral defines.
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectNativescriptPlatform(env?: Record<string, string | undefined>): NativescriptPlatform | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The standard NativeScript compile-time platform flags, matching the globals
|
|
27
|
+
* `@nativescript/vite` seeds in its main entry (`__ANDROID__` / `__IOS__` /
|
|
28
|
+
* `__APPLE__` / `__VISIONOS__` / `__DEV__`). Fed into the bundler's
|
|
29
|
+
* `transform.define` (Rolldown) / `define` (Vite) so NS app code branching on
|
|
30
|
+
* these constants is statically resolved + dead-code-eliminated per target.
|
|
31
|
+
*/
|
|
32
|
+
export declare function nativescriptPlatformDefines(platform: NativescriptPlatform | undefined, opts?: {
|
|
33
|
+
dev?: boolean;
|
|
34
|
+
}): Record<string, string>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// NativeScript / React-Native-style platform file resolution.
|
|
2
|
+
//
|
|
3
|
+
// Lets a shared codebase fork a single module per target platform by file
|
|
4
|
+
// name — `foo.android.ts` / `foo.ios.ts` (platform-specific) or `foo.native.ts`
|
|
5
|
+
// (any native platform, as opposed to the browser's `foo.ts`). An
|
|
6
|
+
// `import './foo'` (or `import './foo.js'`) resolves to the most specific
|
|
7
|
+
// variant that exists, in priority order:
|
|
8
|
+
//
|
|
9
|
+
// ./foo.<platform>.<ext> (e.g. ./foo.android.ts) — when a platform is known
|
|
10
|
+
// ./foo.native.<ext> — native-but-not-browser
|
|
11
|
+
// ./foo.<ext> — the base file (default resolver fallthrough)
|
|
12
|
+
//
|
|
13
|
+
// WHY THIS LIVES IN GJSIFY (not just a dependency on @nativescript/vite):
|
|
14
|
+
// `@nativescript/vite` implements the same lookup as a Vite `resolve.alias`
|
|
15
|
+
// whose `replacement` is a FUNCTION. Vite 8 / Rolldown rejects function
|
|
16
|
+
// replacements on the native alias path (`Failed to convert builtin plugin
|
|
17
|
+
// 'ViteAlias' … function replacement into rust type String`), which breaks the
|
|
18
|
+
// NS production build under Vite 8. Implemented here as a proper `resolveId`
|
|
19
|
+
// plugin HOOK (not a `resolve.alias`), it works identically under Rolldown
|
|
20
|
+
// (the `gjsify build --app nativescript` CLI) AND under Vite 7/8 (the
|
|
21
|
+
// `gjsifyNativescript()` preset) — the function-alias limitation only affects
|
|
22
|
+
// the `resolve.alias` config shorthand, not plugin `resolveId` hooks.
|
|
23
|
+
//
|
|
24
|
+
// Reference: @nativescript/vite `helpers/package-platform-aliases.js`. Original
|
|
25
|
+
// Copyright (c) NativeScript contributors, Apache-2.0. Reimplemented for the
|
|
26
|
+
// gjsify Rolldown/Vite plugin pipeline.
|
|
27
|
+
const PLATFORMS = ['android', 'ios', 'visionos'];
|
|
28
|
+
// JS/TS extensions a source specifier may carry. Stripped so the platform
|
|
29
|
+
// suffix lands BEFORE the extension (`./foo.js` → `./foo.android`).
|
|
30
|
+
const KNOWN_EXT_RE = /\.(tsx?|jsx?|mts|cts|mjs|cjs)$/;
|
|
31
|
+
function isPlatform(value) {
|
|
32
|
+
return PLATFORMS.includes(value);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve platform-specific source-file variants (`*.android` / `*.ios` /
|
|
36
|
+
* `*.native`) ahead of the base file. Relative imports only — bare/package
|
|
37
|
+
* specifiers are left to the normal resolver chain (package platform-`main`
|
|
38
|
+
* fields are a separate, rarer concern handled by the alias layer).
|
|
39
|
+
*/
|
|
40
|
+
export function platformResolvePlugin(options = {}) {
|
|
41
|
+
const platform = options.platform;
|
|
42
|
+
// Priority: platform-specific suffix first, then the platform-agnostic
|
|
43
|
+
// `native` suffix. Without a known platform, only `native` applies.
|
|
44
|
+
const suffixes = platform ? [platform, 'native'] : ['native'];
|
|
45
|
+
return {
|
|
46
|
+
name: 'gjsify-nativescript-platform-resolve',
|
|
47
|
+
resolveId: {
|
|
48
|
+
order: 'pre',
|
|
49
|
+
async handler(source, importer, extraOptions) {
|
|
50
|
+
// Only relative source imports get platform variants.
|
|
51
|
+
if (!importer)
|
|
52
|
+
return null;
|
|
53
|
+
if (!source.startsWith('./') && !source.startsWith('../'))
|
|
54
|
+
return null;
|
|
55
|
+
const extMatch = KNOWN_EXT_RE.exec(source);
|
|
56
|
+
const origExt = extMatch ? extMatch[0] : '';
|
|
57
|
+
const base = origExt ? source.slice(0, -origExt.length) : source;
|
|
58
|
+
for (const suffix of suffixes) {
|
|
59
|
+
// Try the bare form first (the resolver extension-probes,
|
|
60
|
+
// so `./foo.android` finds `./foo.android.ts`), then the
|
|
61
|
+
// extension-preserving form as a fallback.
|
|
62
|
+
const candidates = origExt && `${base}.${suffix}` !== `${base}.${suffix}${origExt}`
|
|
63
|
+
? [`${base}.${suffix}`, `${base}.${suffix}${origExt}`]
|
|
64
|
+
: [`${base}.${suffix}`];
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
if (candidate === source)
|
|
67
|
+
continue;
|
|
68
|
+
// `skipSelf: true` re-runs the resolver chain WITHOUT
|
|
69
|
+
// this plugin → no recursion on the nested resolve.
|
|
70
|
+
const resolved = await this.resolve(candidate, importer, {
|
|
71
|
+
skipSelf: true,
|
|
72
|
+
...(extraOptions?.kind ? { kind: extraOptions.kind } : {}),
|
|
73
|
+
});
|
|
74
|
+
if (resolved && !resolved.external)
|
|
75
|
+
return resolved;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// No variant on disk → let the default chain resolve the base.
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Best-effort detection of the NativeScript target platform from the
|
|
86
|
+
* environment NS' CLI sets when it spawns a bundler (`NATIVESCRIPT_BUNDLER_ENV`
|
|
87
|
+
* / `NATIVESCRIPT_WEBPACK_ENV` carry the platform; `NATIVESCRIPT_PLATFORM` is
|
|
88
|
+
* an explicit override). Returns `undefined` when no platform is discernible —
|
|
89
|
+
* callers then fall back to `.native`-only resolution + neutral defines.
|
|
90
|
+
*/
|
|
91
|
+
export function detectNativescriptPlatform(env = process.env) {
|
|
92
|
+
const direct = env.NATIVESCRIPT_PLATFORM ?? env.NS_PLATFORM;
|
|
93
|
+
if (direct && isPlatform(direct))
|
|
94
|
+
return direct;
|
|
95
|
+
const raw = env.NATIVESCRIPT_BUNDLER_ENV ?? env.NATIVESCRIPT_WEBPACK_ENV;
|
|
96
|
+
if (raw) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(raw);
|
|
99
|
+
// NS' webpack/vite env historically uses `{ android: true }` /
|
|
100
|
+
// `{ ios: true }` booleans; newer paths may pass `platform`.
|
|
101
|
+
if (parsed.android === true)
|
|
102
|
+
return 'android';
|
|
103
|
+
if (parsed.ios === true)
|
|
104
|
+
return 'ios';
|
|
105
|
+
if (parsed.visionos === true)
|
|
106
|
+
return 'visionos';
|
|
107
|
+
if (typeof parsed.platform === 'string' && isPlatform(parsed.platform)) {
|
|
108
|
+
return parsed.platform;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Malformed env JSON → undefined (native-only fallback).
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The standard NativeScript compile-time platform flags, matching the globals
|
|
119
|
+
* `@nativescript/vite` seeds in its main entry (`__ANDROID__` / `__IOS__` /
|
|
120
|
+
* `__APPLE__` / `__VISIONOS__` / `__DEV__`). Fed into the bundler's
|
|
121
|
+
* `transform.define` (Rolldown) / `define` (Vite) so NS app code branching on
|
|
122
|
+
* these constants is statically resolved + dead-code-eliminated per target.
|
|
123
|
+
*/
|
|
124
|
+
export function nativescriptPlatformDefines(platform, opts = {}) {
|
|
125
|
+
const isAndroid = platform === 'android';
|
|
126
|
+
const isIos = platform === 'ios';
|
|
127
|
+
const isVisionOs = platform === 'visionos';
|
|
128
|
+
return {
|
|
129
|
+
__ANDROID__: String(isAndroid),
|
|
130
|
+
__IOS__: String(isIos),
|
|
131
|
+
__VISIONOS__: String(isVisionOs),
|
|
132
|
+
__APPLE__: String(isIos || isVisionOs),
|
|
133
|
+
__DEV__: String(opts.dev ?? false),
|
|
134
|
+
};
|
|
135
|
+
}
|
package/lib/types/app.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type App = 'gjs' | 'node' | 'browser';
|
|
1
|
+
export type App = 'gjs' | 'node' | 'browser' | 'nativescript';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/rolldown-plugin-gjsify",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.37",
|
|
4
4
|
"description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -48,20 +48,20 @@
|
|
|
48
48
|
],
|
|
49
49
|
"license": "MIT",
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@gjsify/console": "^0.4.
|
|
52
|
-
"@gjsify/resolve-npm": "^0.4.
|
|
53
|
-
"@gjsify/rolldown-plugin-deepkit": "^0.4.
|
|
54
|
-
"@gjsify/rolldown-plugin-pnp": "^0.4.
|
|
55
|
-
"@gjsify/vite-plugin-blueprint": "^0.4.
|
|
56
|
-
"@rollup/pluginutils": "^5.
|
|
51
|
+
"@gjsify/console": "^0.4.37",
|
|
52
|
+
"@gjsify/resolve-npm": "^0.4.37",
|
|
53
|
+
"@gjsify/rolldown-plugin-deepkit": "^0.4.37",
|
|
54
|
+
"@gjsify/rolldown-plugin-pnp": "^0.4.37",
|
|
55
|
+
"@gjsify/vite-plugin-blueprint": "^0.4.37",
|
|
56
|
+
"@rollup/pluginutils": "^5.4.0",
|
|
57
57
|
"acorn": "^8.16.0",
|
|
58
58
|
"acorn-walk": "^8.3.5",
|
|
59
59
|
"fast-glob": "^3.3.3",
|
|
60
60
|
"lightningcss": "^1.32.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@gjsify/lightningcss-native": "^0.4.
|
|
64
|
-
"rolldown": "^1.0.
|
|
63
|
+
"@gjsify/lightningcss-native": "^0.4.37",
|
|
64
|
+
"rolldown": "^1.0.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"@gjsify/lightningcss-native": {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@types/node": "^25.9.1",
|
|
76
|
-
"rolldown": "^1.0.
|
|
77
|
-
"typescript": "^
|
|
76
|
+
"rolldown": "^1.0.3",
|
|
77
|
+
"typescript": "^5.9.3"
|
|
78
78
|
}
|
|
79
79
|
}
|