@nativescript/vite 8.0.0-alpha.2 → 8.0.0-alpha.20

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 (209) hide show
  1. package/configuration/angular.d.ts +34 -1
  2. package/configuration/angular.js +380 -34
  3. package/configuration/angular.js.map +1 -1
  4. package/configuration/base.js +171 -7
  5. package/configuration/base.js.map +1 -1
  6. package/configuration/solid.js +27 -1
  7. package/configuration/solid.js.map +1 -1
  8. package/configuration/typescript.js +1 -1
  9. package/configuration/typescript.js.map +1 -1
  10. package/helpers/angular/angular-linker.js +3 -12
  11. package/helpers/angular/angular-linker.js.map +1 -1
  12. package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
  13. package/helpers/angular/inject-component-hmr-registration.js +359 -0
  14. package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
  15. package/helpers/angular/inject-hmr-vite-ignore.d.ts +75 -0
  16. package/helpers/angular/inject-hmr-vite-ignore.js +288 -0
  17. package/helpers/angular/inject-hmr-vite-ignore.js.map +1 -0
  18. package/helpers/angular/util.d.ts +1 -0
  19. package/helpers/angular/util.js +88 -0
  20. package/helpers/angular/util.js.map +1 -1
  21. package/helpers/commonjs-plugins.d.ts +5 -2
  22. package/helpers/commonjs-plugins.js +126 -0
  23. package/helpers/commonjs-plugins.js.map +1 -1
  24. package/helpers/config-as-json.js +10 -0
  25. package/helpers/config-as-json.js.map +1 -1
  26. package/helpers/dev-host.d.ts +274 -0
  27. package/helpers/dev-host.js +491 -0
  28. package/helpers/dev-host.js.map +1 -0
  29. package/helpers/global-defines.d.ts +51 -0
  30. package/helpers/global-defines.js +77 -0
  31. package/helpers/global-defines.js.map +1 -1
  32. package/helpers/logging.d.ts +1 -0
  33. package/helpers/logging.js +63 -3
  34. package/helpers/logging.js.map +1 -1
  35. package/helpers/main-entry.d.ts +3 -1
  36. package/helpers/main-entry.js +450 -125
  37. package/helpers/main-entry.js.map +1 -1
  38. package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
  39. package/helpers/nativeclass-transformer-plugin.js +157 -14
  40. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  41. package/helpers/ns-core-url.d.ts +88 -0
  42. package/helpers/ns-core-url.js +191 -0
  43. package/helpers/ns-core-url.js.map +1 -0
  44. package/helpers/prelink-angular.js +1 -4
  45. package/helpers/prelink-angular.js.map +1 -1
  46. package/helpers/project.d.ts +35 -0
  47. package/helpers/project.js +120 -2
  48. package/helpers/project.js.map +1 -1
  49. package/helpers/resolver.js +9 -1
  50. package/helpers/resolver.js.map +1 -1
  51. package/helpers/solid-jsx-deps.d.ts +15 -0
  52. package/helpers/solid-jsx-deps.js +178 -0
  53. package/helpers/solid-jsx-deps.js.map +1 -0
  54. package/helpers/ts-config-paths.js +50 -2
  55. package/helpers/ts-config-paths.js.map +1 -1
  56. package/helpers/workers.d.ts +20 -19
  57. package/helpers/workers.js +620 -3
  58. package/helpers/workers.js.map +1 -1
  59. package/hmr/client/css-handler.d.ts +1 -0
  60. package/hmr/client/css-handler.js +34 -5
  61. package/hmr/client/css-handler.js.map +1 -1
  62. package/hmr/client/css-update-overlay.d.ts +18 -0
  63. package/hmr/client/css-update-overlay.js +27 -0
  64. package/hmr/client/css-update-overlay.js.map +1 -0
  65. package/hmr/client/hmr-pending-overlay.d.ts +27 -0
  66. package/hmr/client/hmr-pending-overlay.js +50 -0
  67. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  68. package/hmr/client/index.js +483 -33
  69. package/hmr/client/index.js.map +1 -1
  70. package/hmr/client/utils.d.ts +5 -0
  71. package/hmr/client/utils.js +283 -12
  72. package/hmr/client/utils.js.map +1 -1
  73. package/hmr/client/vue-sfc-update-overlay.d.ts +82 -0
  74. package/hmr/client/vue-sfc-update-overlay.js +133 -0
  75. package/hmr/client/vue-sfc-update-overlay.js.map +1 -0
  76. package/hmr/entry-runtime.d.ts +2 -1
  77. package/hmr/entry-runtime.js +253 -66
  78. package/hmr/entry-runtime.js.map +1 -1
  79. package/hmr/frameworks/angular/client/index.d.ts +3 -1
  80. package/hmr/frameworks/angular/client/index.js +802 -10
  81. package/hmr/frameworks/angular/client/index.js.map +1 -1
  82. package/hmr/frameworks/angular/server/linker.js +1 -4
  83. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  84. package/hmr/frameworks/angular/server/strategy.js +30 -6
  85. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  86. package/hmr/frameworks/typescript/server/strategy.js +8 -2
  87. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  88. package/hmr/frameworks/vue/client/index.js +18 -42
  89. package/hmr/frameworks/vue/client/index.js.map +1 -1
  90. package/hmr/helpers/ast-normalizer.js +52 -5
  91. package/hmr/helpers/ast-normalizer.js.map +1 -1
  92. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  93. package/hmr/helpers/cjs-named-exports.js +152 -0
  94. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  95. package/hmr/helpers/package-exports.d.ts +16 -0
  96. package/hmr/helpers/package-exports.js +396 -0
  97. package/hmr/helpers/package-exports.js.map +1 -0
  98. package/hmr/server/constants.js +13 -4
  99. package/hmr/server/constants.js.map +1 -1
  100. package/hmr/server/core-sanitize.d.ts +93 -8
  101. package/hmr/server/core-sanitize.js +222 -49
  102. package/hmr/server/core-sanitize.js.map +1 -1
  103. package/hmr/server/import-map.js +80 -22
  104. package/hmr/server/import-map.js.map +1 -1
  105. package/hmr/server/index.d.ts +2 -1
  106. package/hmr/server/index.js.map +1 -1
  107. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  108. package/hmr/server/ns-core-cjs-shape.js +271 -0
  109. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  110. package/hmr/server/ns-rt-bridge.d.ts +51 -0
  111. package/hmr/server/ns-rt-bridge.js +131 -0
  112. package/hmr/server/ns-rt-bridge.js.map +1 -0
  113. package/hmr/server/perf-instrumentation.d.ts +114 -0
  114. package/hmr/server/perf-instrumentation.js +195 -0
  115. package/hmr/server/perf-instrumentation.js.map +1 -0
  116. package/hmr/server/runtime-graph-filter.d.ts +5 -0
  117. package/hmr/server/runtime-graph-filter.js +21 -0
  118. package/hmr/server/runtime-graph-filter.js.map +1 -0
  119. package/hmr/server/shared-transform-request.d.ts +12 -0
  120. package/hmr/server/shared-transform-request.js +144 -0
  121. package/hmr/server/shared-transform-request.js.map +1 -0
  122. package/hmr/server/vite-plugin.d.ts +21 -1
  123. package/hmr/server/vite-plugin.js +497 -58
  124. package/hmr/server/vite-plugin.js.map +1 -1
  125. package/hmr/server/websocket-angular-entry.d.ts +2 -0
  126. package/hmr/server/websocket-angular-entry.js +68 -0
  127. package/hmr/server/websocket-angular-entry.js.map +1 -0
  128. package/hmr/server/websocket-angular-hot-update.d.ts +78 -0
  129. package/hmr/server/websocket-angular-hot-update.js +413 -0
  130. package/hmr/server/websocket-angular-hot-update.js.map +1 -0
  131. package/hmr/server/websocket-core-bridge.d.ts +58 -0
  132. package/hmr/server/websocket-core-bridge.js +368 -0
  133. package/hmr/server/websocket-core-bridge.js.map +1 -0
  134. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  135. package/hmr/server/websocket-css-hot-update.js +65 -0
  136. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  137. package/hmr/server/websocket-graph-upsert.d.ts +21 -0
  138. package/hmr/server/websocket-graph-upsert.js +33 -0
  139. package/hmr/server/websocket-graph-upsert.js.map +1 -0
  140. package/hmr/server/websocket-hmr-pending.d.ts +43 -0
  141. package/hmr/server/websocket-hmr-pending.js +55 -0
  142. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  143. package/hmr/server/websocket-module-bindings.d.ts +6 -0
  144. package/hmr/server/websocket-module-bindings.js +471 -0
  145. package/hmr/server/websocket-module-bindings.js.map +1 -0
  146. package/hmr/server/websocket-module-specifiers.d.ts +101 -0
  147. package/hmr/server/websocket-module-specifiers.js +820 -0
  148. package/hmr/server/websocket-module-specifiers.js.map +1 -0
  149. package/hmr/server/websocket-ns-m-finalize.d.ts +22 -0
  150. package/hmr/server/websocket-ns-m-finalize.js +88 -0
  151. package/hmr/server/websocket-ns-m-finalize.js.map +1 -0
  152. package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
  153. package/hmr/server/websocket-ns-m-paths.js +92 -0
  154. package/hmr/server/websocket-ns-m-paths.js.map +1 -0
  155. package/hmr/server/websocket-ns-m-request.d.ts +45 -0
  156. package/hmr/server/websocket-ns-m-request.js +196 -0
  157. package/hmr/server/websocket-ns-m-request.js.map +1 -0
  158. package/hmr/server/websocket-served-module-helpers.d.ts +36 -0
  159. package/hmr/server/websocket-served-module-helpers.js +644 -0
  160. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  161. package/hmr/server/websocket-txn.d.ts +6 -0
  162. package/hmr/server/websocket-txn.js +45 -0
  163. package/hmr/server/websocket-txn.js.map +1 -0
  164. package/hmr/server/websocket-vendor-unifier.d.ts +10 -0
  165. package/hmr/server/websocket-vendor-unifier.js +51 -0
  166. package/hmr/server/websocket-vendor-unifier.js.map +1 -0
  167. package/hmr/server/websocket-vue-sfc.d.ts +26 -0
  168. package/hmr/server/websocket-vue-sfc.js +1053 -0
  169. package/hmr/server/websocket-vue-sfc.js.map +1 -0
  170. package/hmr/server/websocket.d.ts +58 -75
  171. package/hmr/server/websocket.js +2230 -1802
  172. package/hmr/server/websocket.js.map +1 -1
  173. package/hmr/shared/package-classifier.d.ts +9 -0
  174. package/hmr/shared/package-classifier.js +58 -0
  175. package/hmr/shared/package-classifier.js.map +1 -0
  176. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  177. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  178. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  179. package/hmr/shared/runtime/boot-progress.d.ts +40 -0
  180. package/hmr/shared/runtime/boot-progress.js +128 -0
  181. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  182. package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
  183. package/hmr/shared/runtime/boot-timeline.js +52 -0
  184. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  185. package/hmr/shared/runtime/browser-runtime-contract.d.ts +64 -0
  186. package/hmr/shared/runtime/browser-runtime-contract.js +54 -0
  187. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -0
  188. package/hmr/shared/runtime/dev-overlay.d.ts +78 -3
  189. package/hmr/shared/runtime/dev-overlay.js +1094 -26
  190. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  191. package/hmr/shared/runtime/module-provenance.js +1 -4
  192. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  193. package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
  194. package/hmr/shared/runtime/root-placeholder.js +1019 -151
  195. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  196. package/hmr/shared/runtime/session-bootstrap.d.ts +1 -0
  197. package/hmr/shared/runtime/session-bootstrap.js +309 -0
  198. package/hmr/shared/runtime/session-bootstrap.js.map +1 -0
  199. package/hmr/shared/runtime/vendor-bootstrap.js +1 -9
  200. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  201. package/hmr/shared/vendor/manifest.d.ts +32 -0
  202. package/hmr/shared/vendor/manifest.js +411 -46
  203. package/hmr/shared/vendor/manifest.js.map +1 -1
  204. package/index.d.ts +1 -0
  205. package/index.js +5 -0
  206. package/index.js.map +1 -1
  207. package/package.json +9 -1
  208. package/runtime/core-aliases-early.js +94 -67
  209. package/runtime/core-aliases-early.js.map +1 -1
@@ -1,12 +1,13 @@
1
1
  import * as esbuild from 'esbuild';
2
2
  import { readFile } from 'fs/promises';
3
3
  import path from 'path';
4
- import { readFileSync } from 'fs';
4
+ import fs, { readFileSync } from 'fs';
5
5
  import { createHash } from 'crypto';
6
6
  import { createRequire } from 'node:module';
7
7
  import { registerVendorManifest, clearVendorManifest, getVendorManifest } from './registry.js';
8
8
  import { generatePlatformPolyfills } from '../runtime/platform-polyfills.js';
9
9
  import { createNativeClassEsbuildPlugin } from '../../../helpers/nativeclass-esbuild-plugin.js';
10
+ import { getGlobalDefines } from '../../../helpers/global-defines.js';
10
11
  export const VENDOR_MANIFEST_ID = '@nativescript/vendor-manifest';
11
12
  export const VENDOR_MANIFEST_VIRTUAL_ID = '\0' + VENDOR_MANIFEST_ID;
12
13
  export const VENDOR_BUNDLE_ID = '@nativescript/vendor';
@@ -162,7 +163,7 @@ export function vendorManifestPlugin(options) {
162
163
  const result = await ensureResult('server');
163
164
  if (req.url === SERVER_VENDOR_PATH) {
164
165
  res.setHeader('Content-Type', 'application/javascript');
165
- res.end(result.code);
166
+ res.end(createVendorBundleRuntimeModule(result));
166
167
  return true;
167
168
  }
168
169
  if (req.url === SERVER_MANIFEST_PATH) {
@@ -229,15 +230,8 @@ export function vendorManifestPlugin(options) {
229
230
  }
230
231
  if (id === VENDOR_BUNDLE_VIRTUAL_ID) {
231
232
  const result = await ensureResult('load-bundle');
232
- // Return a single self-contained module that includes both the vendor module map
233
- // and the vendor manifest to avoid extra imports that can influence chunking.
234
- // - result.code exports `__nsVendorModuleMap`
235
- // - we append an inline manifest export
236
233
  return {
237
- code: `${result.code}
238
- export const vendorManifest = ${JSON.stringify(result.manifest)};
239
- export default vendorManifest;
240
- `,
234
+ code: createVendorBundleRuntimeModule(result),
241
235
  moduleType: 'js',
242
236
  };
243
237
  }
@@ -275,7 +269,62 @@ async function generateVendorBundle(options) {
275
269
  const { projectRoot, platform, mode, flavor } = options;
276
270
  const collected = collectVendorModules(projectRoot, platform, flavor);
277
271
  const entryCode = createVendorEntry(collected.entries);
272
+ // Externalize @nativescript/core and its subpaths in the vendor bundle so
273
+ // vendored packages (e.g. @nativescript-community/ui-material-bottomsheet)
274
+ // do NOT bring their own copy of core with them. The iOS import map maps
275
+ // `@nativescript/core` → `/ns/core` (the core bridge) at runtime, and the
276
+ // bridge delegates to the canonical Application/View/etc. already set up
277
+ // by bundle.mjs via installCoreAliasesEarly + the globalThis.Application
278
+ // seed. Without this externalization, vendor.mjs bundles a second copy of
279
+ // iOSApplication/View/LayoutBase — the second iosApp never receives the
280
+ // iOS launch event (bundle.mjs's iosApp registered first as
281
+ // UIApplication.delegate), so Application.getRootView() on it returns
282
+ // undefined, and any vendor-internal code that reads .getRootView() or
283
+ // checks `instanceof View` against bundle's classes fails under HMR.
284
+ const nsCoreExternalPlugin = {
285
+ name: 'ns-core-external',
286
+ setup(build) {
287
+ build.onResolve({ filter: /^@nativescript\/core(?:\/.*)?$/ }, (args) => ({
288
+ path: args.path,
289
+ external: true,
290
+ }));
291
+ },
292
+ };
293
+ // Externalize the `solid-js` root specifier for the Solid flavor so
294
+ // vendor-bundled packages (e.g. `@nativescript-community/solid-js`,
295
+ // `solid-navigation`, transitively `solid-js/universal` and
296
+ // `solid-js/store`) don't bake their own copy of solid-js into
297
+ // vendor.mjs. The dev server's import map (see `import-map.ts`)
298
+ // redirects bare `solid-js` to the same `/ns/m/node_modules/solid-js/...`
299
+ // URL that Vite's alias produces for user code and `@solid-refresh`,
300
+ // so V8 dedupes the three import paths down to a single module
301
+ // instance — one `Owner` module-local, one reactive graph, one
302
+ // `$DEVCOMP`/`$PROXY` symbol identity across the whole app.
303
+ //
304
+ // Important: scope this to the EXACT bare `solid-js` specifier. Subpaths
305
+ // like `solid-js/store`, `solid-js/universal`, `solid-js/web` stay
306
+ // bundleable so vendor packages that import them keep working out of
307
+ // the box; those subpaths still go through the unified solid-js
308
+ // runtime via their own `import 'solid-js'` statements (which we just
309
+ // externalized).
310
+ const nsSolidJsExternalPlugin = {
311
+ name: 'ns-solid-js-external',
312
+ setup(build) {
313
+ build.onResolve({ filter: /^solid-js$/ }, (args) => ({
314
+ path: args.path,
315
+ external: true,
316
+ }));
317
+ },
318
+ };
278
319
  const plugins = [
320
+ // Mark @nativescript/core external BEFORE other plugins so esbuild never
321
+ // tries to read/transform core's source files. See comment above.
322
+ nsCoreExternalPlugin,
323
+ // For the Solid flavor, externalize `solid-js` so vendor.mjs and the
324
+ // HTTP-served `@solid-refresh` / user code converge on one runtime
325
+ // realm. See `nsSolidJsExternalPlugin`'s definition for the full
326
+ // duplicate-instance rationale.
327
+ ...(flavor === 'solid' ? [nsSolidJsExternalPlugin] : []),
279
328
  // Apply NativeClass transformer to convert @NativeClass decorated classes to ES5 IIFE pattern.
280
329
  // This MUST run before other plugins to ensure proper transformation.
281
330
  createNativeClassEsbuildPlugin(platform),
@@ -336,9 +385,31 @@ async function generateVendorBundle(options) {
336
385
  '.css': 'text',
337
386
  '.json': 'json',
338
387
  },
339
- define: {
340
- 'process.env.NODE_ENV': JSON.stringify(mode),
341
- },
388
+ // Mirror Vite's main-bundle DefinePlugin in the vendor esbuild build.
389
+ //
390
+ // esbuild's `define` requires every value to be a JS expression
391
+ // expressed as a string (the same constraint Vite normalises away).
392
+ // `getGlobalDefines()` returns a few raw `boolean` values for
393
+ // historical reasons (e.g. `__COMMONJS__: false`,
394
+ // `__UI_USE_XML_PARSER__: true`), so coerce any non-string entries
395
+ // through `JSON.stringify` before handing the table to esbuild.
396
+ define: (() => {
397
+ const raw = getGlobalDefines({
398
+ platform,
399
+ targetMode: mode,
400
+ verbose: !!options.verbose,
401
+ flavor: flavor ?? '',
402
+ isCI: !!process.env.CI,
403
+ });
404
+ const out = {};
405
+ for (const [key, value] of Object.entries(raw)) {
406
+ out[key] = typeof value === 'string' ? value : JSON.stringify(value);
407
+ }
408
+ // Belt-and-suspenders: keep the original NODE_ENV define explicit so
409
+ // future changes to `getGlobalDefines()` can't silently drop it.
410
+ out['process.env.NODE_ENV'] = JSON.stringify(mode);
411
+ return out;
412
+ })(),
342
413
  plugins,
343
414
  // Externalize ALL Node built-in modules. The vendor bundle runs on the
344
415
  // NativeScript device runtime, not Node, so any Node API reference must
@@ -464,9 +535,17 @@ function collectVendorModules(projectRoot, platform, flavor) {
464
535
  const packageJsonPath = path.resolve(projectRoot, 'package.json');
465
536
  const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
466
537
  const projectRequire = createRequire(packageJsonPath);
538
+ const debug = process.env.VITE_DEBUG_LOGS === 'true' || process.env.VITE_DEBUG_LOGS === '1';
467
539
  const vendor = new Set();
468
540
  const visited = new Set();
469
541
  const queue = [];
542
+ // Local-source deps (file: pointing to a directory, link:, workspace:) are
543
+ // app code, not pre-packaged libraries. esbuild's vendor pipeline has none
544
+ // of the user's tsconfig path aliases or other Vite plugin resolvers, so
545
+ // any aliased import inside their source will fail with "Could not
546
+ // resolve". We collect their names here so that peer-dep traversal can
547
+ // also skip them.
548
+ const localSourceNames = new Set();
470
549
  const isPackageRootSpecifier = (name) => {
471
550
  if (!name)
472
551
  return false;
@@ -479,6 +558,7 @@ function collectVendorModules(projectRoot, platform, flavor) {
479
558
  return !name.includes('/');
480
559
  };
481
560
  const isAngularFlavor = flavor === 'angular';
561
+ const isSolidFlavor = flavor === 'solid';
482
562
  const addCandidate = (name) => {
483
563
  if (!name || shouldSkipDependency(name)) {
484
564
  return;
@@ -490,6 +570,29 @@ function collectVendorModules(projectRoot, platform, flavor) {
490
570
  if (!isAngularFlavor && (name === '@angular/compiler' || name.startsWith('@angular/'))) {
491
571
  return;
492
572
  }
573
+ // For the Solid flavor, keep `solid-js` itself OUT of the vendor bundle.
574
+ //
575
+ // Both `@solid-refresh` (served via HTTP) and Vite-aliased user code
576
+ // import `solid-js` through the dev server, while the vendor bundle
577
+ // pulls it in as a peerDependency of `@nativescript-community/solid-js`.
578
+ // Two copies → two `Owner` module-locals → the proxy memo created by
579
+ // `solid-refresh`'s `HMRComp` is registered on the HTTP copy's Owner
580
+ // (which is always null — hence the
581
+ // `computations created outside a createRoot or render` warning), while
582
+ // `render(App, doc)` runs against the vendor copy's Owner. Same chain
583
+ // breaks HMR propagation: `patchRegistry`'s `setComp` ticks the HTTP
584
+ // copy's signal but the live page tree subscribes through the vendor
585
+ // copy's reactive graph, so the new component body never reaches the
586
+ // screen.
587
+ //
588
+ // We pair this skip with the matching esbuild externalization
589
+ // (`nsSolidJsExternalPlugin`) and an import-map redirect in
590
+ // `import-map.ts` that points `solid-js` at the HTTP URL. All three
591
+ // converge on the same dev-server URL, V8 dedupes by URL, and the
592
+ // app sees a single `solid-js` realm.
593
+ if (isSolidFlavor && name === 'solid-js') {
594
+ return;
595
+ }
493
596
  // Skip already-visited packages to avoid redundant queue processing
494
597
  if (visited.has(name)) {
495
598
  return;
@@ -506,14 +609,35 @@ function collectVendorModules(projectRoot, platform, flavor) {
506
609
  if (!deps) {
507
610
  return;
508
611
  }
509
- for (const name of Object.keys(deps)) {
612
+ for (const [name, spec] of Object.entries(deps)) {
613
+ if (isUnvendorableLocalSource(name, spec, projectRequire, platform)) {
614
+ // Defer to the regular Vite/Rolldown pipeline (HTTP-served in
615
+ // dev, bundled in production) where the
616
+ // ns-tsconfig-paths-resolver and the rest of the plugin chain
617
+ // can handle aliased imports. Local .tgz file: refs ARE proper
618
+ // packaged libraries and DO stay in vendor; so do file:
619
+ // directory refs that point at packages with compiled JS
620
+ // entry points (a common NativeScript monorepo pattern that
621
+ // hoists installs and re-exposes them from the app's
622
+ // package.json via `file:../../node_modules/<name>`).
623
+ localSourceNames.add(name);
624
+ if (debug) {
625
+ console.log(`[vendor] skipping local source dependency ${name} (spec: ${String(spec)})`);
626
+ }
627
+ continue;
628
+ }
510
629
  addCandidate(name);
511
630
  }
512
631
  };
513
632
  addDeps(pkg.dependencies);
514
633
  addDeps(pkg.optionalDependencies);
515
634
  for (const name of ALWAYS_INCLUDE) {
516
- addCandidate(name);
635
+ // Some force-included packages are only present transitively in apps that
636
+ // actually use them. Skip missing packages quietly so unrelated projects do
637
+ // not fail vendor collection just because the policy list names them.
638
+ if (canResolveDependencyPackageJson(name, projectRequire)) {
639
+ addCandidate(name);
640
+ }
517
641
  }
518
642
  // Ensure Android Activity proxy is present for SBG scanning in dev/HMR
519
643
  // and non-HMR builds alike: explicitly include the side-effect module
@@ -524,16 +648,10 @@ function collectVendorModules(projectRoot, platform, flavor) {
524
648
  if (pkg.dependencies?.['nativescript-vue'] && pkg.devDependencies?.vue) {
525
649
  addCandidate('vue');
526
650
  }
527
- if (pkg.dependencies?.['@nativescript/angular']) {
528
- if (pkg.dependencies?.['@angular/core']) {
529
- addCandidate('@angular/core');
530
- }
531
- if (pkg.dependencies?.['@angular/common']) {
532
- addCandidate('@angular/common');
533
- }
534
- // RxJS is large and not required inside the vendor bundle for dev HMR.
535
- // Avoid bundling to reduce memory pressure; let app import via HTTP loader.
536
- }
651
+ // Angular framework packages are intentionally NOT added to vendor. They are
652
+ // served via HTTP only so every importer resolves to a single module realm.
653
+ // See shouldSkipDependency() for the full rationale. RxJS is also left out of
654
+ // vendor (large, and not required in the vendor bundle for dev HMR).
537
655
  if (pkg.dependencies?.['react-nativescript']) {
538
656
  if (pkg.dependencies?.react) {
539
657
  addCandidate('react');
@@ -571,7 +689,7 @@ function collectVendorModules(projectRoot, platform, flavor) {
571
689
  }
572
690
  const peerDependencies = Object.keys(dependencyPkg.peerDependencies ?? {});
573
691
  for (const peer of peerDependencies) {
574
- if (shouldSkipDependency(peer)) {
692
+ if (shouldSkipDependency(peer) || localSourceNames.has(peer)) {
575
693
  continue;
576
694
  }
577
695
  if (projectDeps.dependencies.has(peer) || projectDeps.optional.has(peer) || projectDeps.dev.has(peer)) {
@@ -599,6 +717,16 @@ function shouldSkipDependency(name) {
599
717
  if (ALWAYS_EXCLUDE.has(name)) {
600
718
  return true;
601
719
  }
720
+ // Angular framework packages must only be served via the HTTP path so every
721
+ // importer resolves to a single module realm. When these packages are present
722
+ // in the vendor bundle AND imported by app modules via HTTP subpath, every
723
+ // @Component/@Injectable gets defined twice (once per realm), producing NG0912
724
+ // selector collisions, cross-realm `instanceof` failures, and dual class
725
+ // identities throughout Angular's DI container. HTTP-only is the single-realm
726
+ // invariant for user-level framework code.
727
+ if (name === '@nativescript/angular' || name === 'nativescript-angular' || name.startsWith('@angular/')) {
728
+ return true;
729
+ }
602
730
  // All Babel packages are build tools — never bundle into device runtime.
603
731
  // They require Node built-ins (fs, path, url) that don't exist on device.
604
732
  if (name.startsWith('@babel/') || name.startsWith('babel-')) {
@@ -641,7 +769,160 @@ function shouldSkipDependency(name) {
641
769
  }
642
770
  return false;
643
771
  }
772
+ const COMPILED_JS_ENTRY_EXTENSIONS = ['.js', '.mjs', '.cjs'];
773
+ const COMPILED_JS_ENTRY_REGEX = /\.(?:c|m)?jsx?$/;
774
+ function compiledJsExtensionsForPlatform(platform) {
775
+ const exts = [...COMPILED_JS_ENTRY_EXTENSIONS];
776
+ switch (platform) {
777
+ case 'android':
778
+ exts.push('.android.js');
779
+ break;
780
+ case 'ios':
781
+ exts.push('.ios.js', '.visionos.js');
782
+ break;
783
+ case 'visionos':
784
+ exts.push('.visionos.js', '.ios.js');
785
+ break;
786
+ }
787
+ return exts;
788
+ }
789
+ /**
790
+ * Determine whether a `package.json` dependency must be excluded from the
791
+ * HMR vendor bundle because esbuild's standalone vendor pipeline can't
792
+ * resolve its source.
793
+ *
794
+ * The HMR vendor bundle is generated by a standalone esbuild build that
795
+ * has none of the Vite plugin chain (most notably
796
+ * `ns-tsconfig-paths-resolver`), so any aliased import inside the
797
+ * package's **source** files will fail with "Could not resolve" and abort
798
+ * the whole bundle.
799
+ *
800
+ * Skip:
801
+ * - `link:` and `workspace:` refs (always app-side source).
802
+ * - `file:` refs to a directory whose installed package only ships
803
+ * TypeScript/JSX source (no compiled `.js`/`.mjs`/`.cjs` entry).
804
+ *
805
+ * Keep (return false):
806
+ * - Regular semver / git / url specs (normal third-party libraries).
807
+ * - Local `.tgz` file refs (pre-packaged libraries extracted at install).
808
+ * - `file:` directory refs that resolve to a package with a compiled JS
809
+ * entry — a common NativeScript monorepo convention where the app's
810
+ * `package.json` redirects to `../../node_modules/<name>` to avoid
811
+ * duplicate installs while letting the NativeScript CLI discover
812
+ * plugins from the app directory.
813
+ */
814
+ export function isUnvendorableLocalSource(name, spec, projectRequire, platform) {
815
+ if (typeof spec !== 'string')
816
+ return false;
817
+ if (spec.startsWith('link:') || spec.startsWith('workspace:'))
818
+ return true;
819
+ if (!spec.startsWith('file:'))
820
+ return false;
821
+ // Tarballs are already pre-packaged libraries — install extracts them
822
+ // into a normal node_modules entry.
823
+ if (/\.t(?:ar\.)?gz(?:[?#].*)?$/.test(spec))
824
+ return false;
825
+ // Directory file: refs need a deeper check: peek at the installed
826
+ // package.json and ask "does this ship compiled JS?". If yes, vendor
827
+ // it. If no (TS source only), defer to the regular Vite pipeline.
828
+ return !packageHasCompiledJsEntry(name, projectRequire, platform);
829
+ }
830
+ function packageHasCompiledJsEntry(name, projectRequire, platform) {
831
+ let pkgJsonPath;
832
+ try {
833
+ pkgJsonPath = projectRequire.resolve(`${name}/package.json`);
834
+ }
835
+ catch {
836
+ return false;
837
+ }
838
+ let pkg;
839
+ try {
840
+ pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
841
+ }
842
+ catch {
843
+ return false;
844
+ }
845
+ const pkgDir = path.dirname(pkgJsonPath);
846
+ const candidates = [];
847
+ const pushCandidate = (value) => {
848
+ if (typeof value === 'string' && value)
849
+ candidates.push(value);
850
+ };
851
+ pushCandidate(pkg.module);
852
+ pushCandidate(pkg.main);
853
+ // Recursively flatten conditional `exports` maps to surface concrete
854
+ // file paths. We only need `string` leaves; anything else (function-
855
+ // based exports, etc.) doesn't apply to esbuild's resolution.
856
+ const visitExports = (node) => {
857
+ if (!node)
858
+ return;
859
+ if (typeof node === 'string') {
860
+ pushCandidate(node);
861
+ return;
862
+ }
863
+ if (Array.isArray(node)) {
864
+ for (const item of node)
865
+ visitExports(item);
866
+ return;
867
+ }
868
+ if (typeof node === 'object') {
869
+ for (const value of Object.values(node))
870
+ visitExports(value);
871
+ }
872
+ };
873
+ visitExports(pkg.exports);
874
+ if (candidates.length === 0) {
875
+ // node's default lookup falls back to `index.js`.
876
+ candidates.push('index');
877
+ }
878
+ const extensionsToTry = compiledJsExtensionsForPlatform(platform);
879
+ for (const cand of candidates) {
880
+ const abs = path.resolve(pkgDir, cand);
881
+ const ext = path.extname(cand);
882
+ if (COMPILED_JS_ENTRY_REGEX.test(cand)) {
883
+ // Explicit JS extension. If the file exists, vendor it. If not
884
+ // (a NativeScript plugin commonly declares `main: "index.js"`
885
+ // but ships only platform variants like `index.ios.js`), try
886
+ // the platform-specific variants which esbuild's
887
+ // `resolveExtensions` will pick up at bundle time.
888
+ if (fs.existsSync(abs))
889
+ return true;
890
+ const baseAbs = abs.slice(0, -ext.length);
891
+ for (const e of extensionsToTry) {
892
+ if (fs.existsSync(baseAbs + e))
893
+ return true;
894
+ }
895
+ continue;
896
+ }
897
+ if (!ext) {
898
+ // Extensionless main — try plain JS variants AND the
899
+ // platform-specific variants NativeScript plugins commonly use
900
+ // (`index.ios.js`, `index.android.js`, `index.visionos.js`).
901
+ for (const e of extensionsToTry) {
902
+ if (fs.existsSync(abs + e))
903
+ return true;
904
+ }
905
+ continue;
906
+ }
907
+ // A non-JS extension (typically `.ts`/`.tsx`) means the package
908
+ // only ships TS source — esbuild's vendor pipeline can't resolve
909
+ // any tsconfig-aliased imports inside it.
910
+ }
911
+ return false;
912
+ }
644
913
  export const __test_collectVendorModules = collectVendorModules;
914
+ export const __test_createVendorBundleRuntimeModule = createVendorBundleRuntimeModule;
915
+ export const __test_isUnvendorableLocalSource = isUnvendorableLocalSource;
916
+ export const __test_packageHasCompiledJsEntry = packageHasCompiledJsEntry;
917
+ function canResolveDependencyPackageJson(specifier, projectRequire) {
918
+ try {
919
+ projectRequire.resolve(`${specifier}/package.json`);
920
+ return true;
921
+ }
922
+ catch {
923
+ return false;
924
+ }
925
+ }
645
926
  function readDependencyPackageJson(specifier, projectRequire) {
646
927
  try {
647
928
  const packageJsonPath = projectRequire.resolve(`${specifier}/package.json`);
@@ -668,9 +949,31 @@ function createVendorEntry(entries) {
668
949
  return `export const __nsVendorModuleMap = {};
669
950
  `;
670
951
  }
671
- const imports = entries.map((specifier, index) => `import * as __nsVendor_${index} from ${JSON.stringify(specifier)};`).join('\n');
952
+ // Emit a side-effect-only import FIRST for each entry, then the namespace
953
+ // import we actually expose through `__nsVendorModuleMap`. Per the ESM
954
+ // spec, `import "pkg";` (no clause) guarantees module body evaluation, and
955
+ // esbuild treats these as DCE-immune even when the package declares
956
+ // `"sideEffects": false`. Without this, packages whose top-level
957
+ // statements install runtime patches (e.g. `@nativescript-community/text`
958
+ // monkey-patching `TextBase.prototype.setTextDecorationAndTransform` via
959
+ // `overrideSpanAndFormattedString()` invoked from
960
+ // `@nativescript-community/ui-label/index-common.js` line 12) can have
961
+ // their bodies elided by esbuild — exports stay resolvable via the
962
+ // namespace, but the runtime patches never fire, producing line-height /
963
+ // letter-spacing / formatted-text rendering divergence between HMR
964
+ // (vendor.mjs via HTTP) and no-HMR (single Rolldown bundle that inlines
965
+ // everything anyway). This affects any NS plugin that relies on top-level
966
+ // side-effects to wire up renderer behavior.
967
+ const sideEffectImports = entries.map((specifier) => `import ${JSON.stringify(specifier)};`).join('\n');
968
+ const namespaceImports = entries.map((specifier, index) => `import * as __nsVendor_${index} from ${JSON.stringify(specifier)};`).join('\n');
672
969
  const modules = entries.map((specifier, index) => `${JSON.stringify(specifier)}: __nsVendor_${index}`).join(',\n ');
673
- return `${imports}\n\nexport const __nsVendorModuleMap = {\n ${modules}\n};\n`;
970
+ return `${sideEffectImports}\n\n${namespaceImports}\n\nexport const __nsVendorModuleMap = {\n ${modules}\n};\n`;
971
+ }
972
+ function createVendorBundleRuntimeModule(result) {
973
+ return `${result.code}
974
+ export const vendorManifest = ${JSON.stringify(result.manifest)};
975
+ export default vendorManifest;
976
+ `;
674
977
  }
675
978
  function resolveExtensionsForPlatform(platform) {
676
979
  const base = ['.tsx', '.jsx', '.ts', '.js', '.mjs', '.cjs', '.json'];
@@ -753,10 +1056,7 @@ function createVendorEsbuildPlugin(projectRoot) {
753
1056
  // @angular/platform-browser/animations -> provide concrete named stubs
754
1057
  build.onResolve({ filter: /^@angular\/platform-browser\/animations(?:\/.*)?$/ }, (args) => {
755
1058
  if (debug) {
756
- try {
757
- console.log('[vendor] map', args.path, '->', PB_ANIMATIONS_ID);
758
- }
759
- catch { }
1059
+ console.log('[vendor] map', args.path, '->', PB_ANIMATIONS_ID);
760
1060
  }
761
1061
  return { path: PB_ANIMATIONS_ID, namespace: 'ns-vendor' };
762
1062
  });
@@ -782,10 +1082,7 @@ function createVendorEsbuildPlugin(projectRoot) {
782
1082
  // @angular/animations/browser -> provide ɵ* engine/renderer/style normalizer stubs
783
1083
  build.onResolve({ filter: /^@angular\/animations\/browser(?:\/.*)?$/ }, (args) => {
784
1084
  if (debug) {
785
- try {
786
- console.log('[vendor] map', args.path, '->', ANIMATIONS_BROWSER_ID);
787
- }
788
- catch { }
1085
+ console.log('[vendor] map', args.path, '->', ANIMATIONS_BROWSER_ID);
789
1086
  }
790
1087
  return { path: ANIMATIONS_BROWSER_ID, namespace: 'ns-vendor' };
791
1088
  });
@@ -807,10 +1104,7 @@ function createVendorEsbuildPlugin(projectRoot) {
807
1104
  // Keep generic mapping for @angular/animations base; /browser is handled above
808
1105
  { filter: /^@angular\/animations(?:$|\/)$/ }, (args) => {
809
1106
  if (debug) {
810
- try {
811
- console.log('[vendor] map', args.path, '->', ANIMATIONS_ID);
812
- }
813
- catch { }
1107
+ console.log('[vendor] map', args.path, '->', ANIMATIONS_ID);
814
1108
  }
815
1109
  return { path: ANIMATIONS_ID, namespace: 'ns-vendor' };
816
1110
  });
@@ -990,10 +1284,7 @@ function angularLinkerEsbuildPlugin(projectRoot) {
990
1284
  fileSystem: angularFileSystem,
991
1285
  });
992
1286
  if (debug) {
993
- try {
994
- console.log('[ns-angular-linker][vendor] linking', args.path);
995
- }
996
- catch { }
1287
+ console.log('[ns-angular-linker][vendor] linking', args.path);
997
1288
  }
998
1289
  const result = await babel.transformAsync(source, {
999
1290
  filename: args.path,
@@ -1099,15 +1390,84 @@ function normalizeSpecifier(spec) {
1099
1390
  return value;
1100
1391
  }
1101
1392
 
1393
+ function normalizeCoreSubLocal(s) {
1394
+ if (!s) return "";
1395
+ let t = String(s).split("?")[0].split("#")[0].trim();
1396
+ t = t.replace(/^\\/+/, "").replace(/\\/+$/, "");
1397
+ t = t.replace(/\\.(?:mjs|cjs|js)$/, "");
1398
+ if (t.length >= 6 && t.substring(t.length - 6) === "/index") {
1399
+ t = t.substring(0, t.length - 6);
1400
+ }
1401
+ if (!t || t === "index") return "";
1402
+ return t;
1403
+ }
1404
+
1405
+ // Invariant D: shape ESM namespaces before returning to CJS callers.
1406
+ //
1407
+ // The /ns/core handler's registration footer (see websocket.ts) and the
1408
+ // main-entry require shim (see helpers/main-entry.ts) both install the
1409
+ // shape function on globalThis. In practice, entries stored in
1410
+ // __NS_CORE_MODULES__ are ALREADY shaped, so this pass-through is a
1411
+ // fast no-op (the shape function's fast path returns obj as-is when it
1412
+ // already has Object.prototype). We call it anyway as defense-in-depth
1413
+ // against future callers that might populate the registry without
1414
+ // shaping.
1415
+ function shapeForCjs(value) {
1416
+ if (!value || typeof value !== "object") return value;
1417
+ const shape = g.__NS_CJS_SHAPE__;
1418
+ if (typeof shape === "function") {
1419
+ try { return shape(value); } catch (e) {}
1420
+ }
1421
+ return value;
1422
+ }
1423
+
1424
+ function resolveCoreFromRegistry(normalizedId) {
1425
+ if (
1426
+ normalizedId !== "@nativescript/core" &&
1427
+ normalizedId.indexOf("@nativescript/core/") !== 0
1428
+ ) {
1429
+ return null;
1430
+ }
1431
+ const table = g.__NS_CORE_MODULES__;
1432
+ if (!table) return null;
1433
+ if (table[normalizedId]) return shapeForCjs(table[normalizedId]);
1434
+ const rawSub =
1435
+ normalizedId === "@nativescript/core"
1436
+ ? ""
1437
+ : normalizedId.slice("@nativescript/core/".length);
1438
+ const normSub = normalizeCoreSubLocal(rawSub);
1439
+ const bareKey = normSub ? "@nativescript/core/" + normSub : "@nativescript/core";
1440
+ if (table[bareKey]) return shapeForCjs(table[bareKey]);
1441
+ if (normSub && table[normSub]) return shapeForCjs(table[normSub]);
1442
+ if (!normSub && table[""]) return shapeForCjs(table[""]);
1443
+ return null;
1444
+ }
1445
+
1102
1446
  export function createRequire(_url) {
1103
1447
  const nsRequire = getNativeScriptRequire();
1104
1448
  if (!nsRequire) {
1105
- return function () {
1449
+ return function (id) {
1450
+ // Even without nsRequire, the @nativescript/core registry populated by
1451
+ // the /ns/core bridge may already have the module — return it so
1452
+ // vendor install() hooks that run against the HMR-served core don't
1453
+ // crash at module-instantiation time.
1454
+ const fromRegistry = resolveCoreFromRegistry(normalizeSpecifier(id));
1455
+ if (fromRegistry) return fromRegistry;
1106
1456
  throw new Error("NativeScript require() is not available in this context");
1107
1457
  };
1108
1458
  }
1109
1459
  const req = function (id) {
1110
1460
  const normalizedId = normalizeSpecifier(id);
1461
+ // Invariant C: @nativescript/core and its subpaths are served ONCE via
1462
+ // the /ns/core HTTP bridge, which self-registers each module's ESM
1463
+ // namespace on globalThis.__NS_CORE_MODULES__. CommonJS require() from
1464
+ // vendor packages (e.g. \`require('@nativescript/core/ui/core/view').View\`
1465
+ // in @nativescript-community/gesturehandler, or \`require('@nativescript/core').View\`
1466
+ // in @nativescript-community/ui-material-core) MUST resolve to that
1467
+ // same namespace; otherwise we re-trigger class identity splits and
1468
+ // applyMixins() crashes with "Cannot read properties of undefined".
1469
+ const fromRegistry = resolveCoreFromRegistry(normalizedId);
1470
+ if (fromRegistry) return fromRegistry;
1111
1471
  if (
1112
1472
  normalizedId.includes("../data/patch.json") ||
1113
1473
  normalizedId.includes("css-tree/lib/data/patch.json")
@@ -1130,7 +1490,12 @@ export function createRequire(_url) {
1130
1490
  if (normalizedId.endsWith(".json")) {
1131
1491
  return {};
1132
1492
  }
1133
- return nsRequire(normalizedId);
1493
+ // Shape the native require result too. The NativeScript CJS loader
1494
+ // may serve an @nativescript/core subpath over HTTP before the
1495
+ // /ns/core footer has run (e.g., during initial boot). In that
1496
+ // window, the result is a raw ESM namespace with null [[Prototype]]
1497
+ // and zone.js/vendor install() hooks crash on hasOwnProperty.
1498
+ return shapeForCjs(nsRequire(normalizedId));
1134
1499
  };
1135
1500
  req.resolve = nsRequire.resolve
1136
1501
  ? function (id) {