@gjsify/vite-plugin-gjsify 0.4.37 → 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 +129 -0
  2. package/package.json +7 -7
package/lib/index.js CHANGED
@@ -29,6 +29,8 @@
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
31
  import { createRequire } from 'node:module';
32
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
33
+ import * as nodePath from 'node:path';
32
34
  import { gjsImportsEmptyPlugin, platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from '@gjsify/rolldown-plugin-gjsify';
33
35
  import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
34
36
  import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
@@ -124,6 +126,129 @@ function nativescriptCssTreeAlias(fromDir) {
124
126
  return {};
125
127
  }
126
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
+ }
127
252
  /**
128
253
  * Returns the Vite plugin array that brings `gjsify build --app nativescript`'s
129
254
  * NS-target transforms to a Vite project (dev + build). Spread it into a
@@ -192,6 +317,10 @@ export function gjsifyNativescript(options = {}) {
192
317
  // under Vite 8 / Rolldown, where @nativescript/vite's function-based
193
318
  // alias for the same feature is rejected.
194
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(),
195
324
  // NO blueprintPlugin — Blueprint is GTK-specific
196
325
  // NO cssAsStringPlugin — NS handles CSS via @nativescript/core
197
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.37",
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,11 +39,11 @@
39
39
  ],
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@gjsify/empty": "^0.4.37",
43
- "@gjsify/resolve-npm": "^0.4.37",
44
- "@gjsify/rolldown-plugin-deepkit": "^0.4.37",
45
- "@gjsify/rolldown-plugin-gjsify": "^0.4.37",
46
- "@gjsify/vite-plugin-blueprint": "^0.4.37"
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"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "vite": "^8.0.14"
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^25.9.1",
58
- "typescript": "^5.9.3",
58
+ "typescript": "^6.0.3",
59
59
  "vite": "^8.0.14"
60
60
  }
61
61
  }