@gjsify/vite-plugin-gjsify 0.4.36 → 0.4.38

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.
Files changed (2) hide show
  1. package/lib/index.js +175 -7
  2. package/package.json +7 -6
package/lib/index.js CHANGED
@@ -28,7 +28,10 @@
28
28
  // `aliasPlugin` from rolldown-plugin-gjsify is intentionally NOT used here
29
29
  // (it is not part of that package's public API). Vite has a native
30
30
  // `resolve.alias`, which is what the inline config hook below sets.
31
- import { gjsImportsEmptyPlugin } from '@gjsify/rolldown-plugin-gjsify';
31
+ import { createRequire } from 'node:module';
32
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
33
+ import * as nodePath from 'node:path';
34
+ import { gjsImportsEmptyPlugin, platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from '@gjsify/rolldown-plugin-gjsify';
32
35
  import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
33
36
  import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
34
37
  import { ALIASES_NODE_FOR_NATIVESCRIPT } from '@gjsify/resolve-npm';
@@ -99,6 +102,153 @@ export function gjsifyBrowser(options = {}) {
99
102
  ];
100
103
  }
101
104
  export default gjsifyBrowser;
105
+ /**
106
+ * `@nativescript/core`'s CSS parser pulls in `css-tree`, whose data modules
107
+ * (`data.js` / `data-patch.js` / `version.js`) load JSON via
108
+ * `createRequire(import.meta.url)('mdn-data/*.json' | '../package.json')` at
109
+ * module-evaluation time. Rolldown can't statically resolve those dynamic
110
+ * requires, so they survive into the bundle and throw on the NativeScript V8
111
+ * runtime ("Module evaluation promise rejected") — crashing every NS app that
112
+ * uses `@nativescript/core`'s styling. css-tree ships a self-contained
113
+ * `dist/csstree.esm.js` with the data inlined (no `createRequire`); aliasing the
114
+ * bare `css-tree` specifier to it keeps those requires out of the bundle.
115
+ *
116
+ * Resolved from the consuming project (`css-tree` is `@nativescript/core`'s
117
+ * transitive dep, not a direct one here). Returns no alias when css-tree isn't
118
+ * installed — a non-NS-core consumer keeps resolving `css-tree` normally.
119
+ */
120
+ function nativescriptCssTreeAlias(fromDir) {
121
+ try {
122
+ const require = createRequire(import.meta.url);
123
+ return { 'css-tree': require.resolve('css-tree/dist/csstree.esm', { paths: [fromDir] }) };
124
+ }
125
+ catch {
126
+ return {};
127
+ }
128
+ }
129
+ /**
130
+ * Collect the barrel modules referenced from the app's XML via `xmlns="~/MOD"`
131
+ * that `@nativescript/vite`'s `ns-bundler-context` does NOT register — i.e. those
132
+ * with no paired `.xml` sibling. Returns the NS nickname + a root-relative import
133
+ * specifier for each.
134
+ */
135
+ function collectXmlnsBarrels(appDir, root) {
136
+ const xmlFiles = [];
137
+ const walk = (dir) => {
138
+ let entries;
139
+ try {
140
+ entries = readdirSync(dir);
141
+ }
142
+ catch {
143
+ return;
144
+ }
145
+ for (const entry of entries) {
146
+ const full = nodePath.join(dir, entry);
147
+ let isDir;
148
+ try {
149
+ isDir = statSync(full).isDirectory();
150
+ }
151
+ catch {
152
+ continue;
153
+ }
154
+ if (isDir)
155
+ walk(full);
156
+ else if (full.endsWith('.xml'))
157
+ xmlFiles.push(full);
158
+ }
159
+ };
160
+ walk(appDir);
161
+ const names = new Set();
162
+ const xmlnsRe = /xmlns:[\w-]+\s*=\s*["']~\/([^"']+)["']/g;
163
+ for (const file of xmlFiles) {
164
+ let src;
165
+ try {
166
+ src = readFileSync(file, 'utf8');
167
+ }
168
+ catch {
169
+ continue;
170
+ }
171
+ for (const match of src.matchAll(xmlnsRe)) {
172
+ names.add(match[1].replace(/\/+$/, ''));
173
+ }
174
+ }
175
+ const barrels = [];
176
+ for (const name of names) {
177
+ // ns-bundler-context already registers any module that has an `.xml` sibling.
178
+ if (existsSync(nodePath.join(appDir, `${name}.xml`)))
179
+ continue;
180
+ const file = ['.ts', '.js', '/index.ts', '/index.js']
181
+ .map((suffix) => nodePath.join(appDir, `${name}${suffix}`))
182
+ .find((candidate) => existsSync(candidate));
183
+ if (!file)
184
+ continue;
185
+ const spec = '/' + nodePath.relative(root, file).split(nodePath.sep).join('/');
186
+ barrels.push({ name, spec });
187
+ }
188
+ return barrels;
189
+ }
190
+ /**
191
+ * Register barrel modules referenced from XML via `xmlns` namespaces, which
192
+ * `@nativescript/vite`'s `ns-bundler-context` leaves unregistered.
193
+ *
194
+ * NativeScript resolves a custom element — e.g. `<w:SourceView>` declared with
195
+ * `xmlns:w="~/widgets/index"` — at runtime via `global.loadModule("widgets/index")`,
196
+ * a lookup in the registry populated by `registerModule` / `registerBundlerModules`.
197
+ * `ns-bundler-context` registers every XML file, its paired code-behind and CSS,
198
+ * but deliberately NOT standalone barrels (`index.ts` with no `.xml` sibling). So a
199
+ * barrel reachable only via `xmlns` (`~/widgets/index`, `~/mdx/index`) is never
200
+ * registered; an ESM bundle has no `global.require` fallback, so `loadModule(...)`
201
+ * returns `null` → "Module 'SourceView' not found". `@nativescript/webpack`'s
202
+ * `xml-namespace-loader` registered every `.ts`, so these resolved for free there —
203
+ * this plugin reproduces that behaviour for the Vite build.
204
+ *
205
+ * It augments `@nativescript/vite`'s generated `virtual:ns-bundler-context` module
206
+ * (guaranteed imported early, before `Application.run`) by prepending a namespace
207
+ * import of each missing barrel and appending matching `registerModule` calls. The
208
+ * barrels enter the bundle via the added imports; registration runs right after the
209
+ * upstream `registerBundlerModules`.
210
+ */
211
+ function nativescriptXmlnsBarrelsPlugin() {
212
+ const BUNDLER_CONTEXT_ID = '\0virtual:ns-bundler-context';
213
+ let root = process.cwd();
214
+ let appDir;
215
+ return {
216
+ name: 'gjsify-nativescript-xmlns-barrels',
217
+ configResolved(config) {
218
+ root = config.root || process.cwd();
219
+ // `~/` maps to the NativeScript app directory (`appPath`, default `app`;
220
+ // `src` is the other common layout). Probe both; XML lives under it.
221
+ appDir = ['app', 'src'].map((dir) => nodePath.join(root, dir)).find((dir) => existsSync(dir));
222
+ },
223
+ transform(code, id) {
224
+ if (id !== BUNDLER_CONTEXT_ID || !appDir)
225
+ return null;
226
+ const barrels = collectXmlnsBarrels(appDir, root);
227
+ if (barrels.length === 0)
228
+ return null;
229
+ const imports = barrels
230
+ .map((barrel, i) => `import * as __gjsifyXmlnsBarrel${i} from ${JSON.stringify(barrel.spec)};`)
231
+ .join('\n');
232
+ const registrations = barrels
233
+ .map((barrel, i) => ` global.registerModule(${JSON.stringify(barrel.name)}, () => __gjsifyXmlnsBarrel${i});`)
234
+ .join('\n');
235
+ const footer = [
236
+ '',
237
+ ';(function () {',
238
+ ' try {',
239
+ " if (typeof global !== 'undefined' && typeof global.registerModule === 'function') {",
240
+ registrations,
241
+ ' }',
242
+ ' } catch (e) {',
243
+ " try { console.warn('[gjsify-nativescript] xmlns barrel registration failed', e); } catch (_) {}",
244
+ ' }',
245
+ '})();',
246
+ '',
247
+ ].join('\n');
248
+ return { code: `${imports}\n${code}${footer}`, map: null };
249
+ },
250
+ };
251
+ }
102
252
  /**
103
253
  * Returns the Vite plugin array that brings `gjsify build --app nativescript`'s
104
254
  * NS-target transforms to a Vite project (dev + build). Spread it into a
@@ -124,23 +274,32 @@ export function gjsifyNativescript(options = {}) {
124
274
  nodePrefixAliases[bare] = target;
125
275
  nodePrefixAliases[`node:${bare}`] = target;
126
276
  }
127
- const alias = {
128
- ...nodePrefixAliases,
129
- ...options.aliases,
130
- };
131
277
  const optimizeDepsExclude = ['@gjsify/unit', ...(options.optimizeDepsExclude ?? [])];
278
+ // Target platform from the env NS' CLI sets when it spawns the bundler
279
+ // (else `undefined` → `.native`-only resolution + neutral defines).
280
+ const platform = detectNativescriptPlatform();
132
281
  const configPlugin = {
133
282
  name: 'gjsify-nativescript-config',
134
- config() {
283
+ config(userConfig, env) {
284
+ // `__DEV__` tracks Vite's mode (dev server vs `vite build`).
285
+ const dev = env?.mode !== 'production';
286
+ // Resolve css-tree from the project root (its dist is the consumer's
287
+ // transitive dep). User `aliases` are merged last so they win.
288
+ const root = userConfig?.root ?? process.cwd();
135
289
  return {
136
290
  resolve: {
137
- alias,
291
+ alias: { ...nodePrefixAliases, ...nativescriptCssTreeAlias(root), ...options.aliases },
138
292
  conditions: ['import', 'nativescript'],
139
293
  mainFields: ['nativescript', 'module', 'main'],
140
294
  },
141
295
  define: {
142
296
  global: 'globalThis',
143
297
  // NO `window` define — NS apps have no DOM
298
+ //
299
+ // Standard NS compile-time platform flags, matching the
300
+ // Rolldown `--app nativescript` factory + the globals
301
+ // `@nativescript/vite` seeds in its main entry.
302
+ ...nativescriptPlatformDefines(platform, { dev }),
144
303
  },
145
304
  build: {
146
305
  target: 'esnext',
@@ -153,6 +312,15 @@ export function gjsifyNativescript(options = {}) {
153
312
  };
154
313
  return [
155
314
  gjsImportsEmptyPlugin(),
315
+ // Platform-specific source variants (`*.android` / `*.ios` /
316
+ // `*.native`). A `resolveId` HOOK, NOT a `resolve.alias` — so it works
317
+ // under Vite 8 / Rolldown, where @nativescript/vite's function-based
318
+ // alias for the same feature is rejected.
319
+ platformResolvePlugin({ platform }),
320
+ // Register `xmlns`-referenced barrel modules that @nativescript/vite's
321
+ // ns-bundler-context leaves unregistered (its tree-shaking gap for
322
+ // barrels with no `.xml` sibling). Reproduces webpack's xml-namespace-loader.
323
+ nativescriptXmlnsBarrelsPlugin(),
156
324
  // NO blueprintPlugin — Blueprint is GTK-specific
157
325
  // NO cssAsStringPlugin — NS handles CSS via @nativescript/core
158
326
  deepkitPlugin({ reflection: options.reflection }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/vite-plugin-gjsify",
3
- "version": "0.4.36",
3
+ "version": "0.4.38",
4
4
  "description": "Vite plugin presets mirroring `gjsify build --app browser` / `--app nativescript` — dev (Vite/HMR) parity with the gjsify CLI targets.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -39,10 +39,11 @@
39
39
  ],
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@gjsify/resolve-npm": "^0.4.36",
43
- "@gjsify/rolldown-plugin-deepkit": "^0.4.36",
44
- "@gjsify/rolldown-plugin-gjsify": "^0.4.36",
45
- "@gjsify/vite-plugin-blueprint": "^0.4.36"
42
+ "@gjsify/empty": "^0.4.38",
43
+ "@gjsify/resolve-npm": "^0.4.38",
44
+ "@gjsify/rolldown-plugin-deepkit": "^0.4.38",
45
+ "@gjsify/rolldown-plugin-gjsify": "^0.4.38",
46
+ "@gjsify/vite-plugin-blueprint": "^0.4.38"
46
47
  },
47
48
  "peerDependencies": {
48
49
  "vite": "^8.0.14"
@@ -54,7 +55,7 @@
54
55
  },
55
56
  "devDependencies": {
56
57
  "@types/node": "^25.9.1",
57
- "typescript": "^5.9.3",
58
+ "typescript": "^6.0.3",
58
59
  "vite": "^8.0.14"
59
60
  }
60
61
  }