@nativescript/vite 8.0.0-alpha.3 → 8.0.0-alpha.30

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 (295) hide show
  1. package/README.md +51 -11
  2. package/configuration/angular.d.ts +34 -1
  3. package/configuration/angular.js +369 -159
  4. package/configuration/angular.js.map +1 -1
  5. package/configuration/base.js +184 -14
  6. package/configuration/base.js.map +1 -1
  7. package/configuration/javascript.js +5 -72
  8. package/configuration/javascript.js.map +1 -1
  9. package/configuration/solid.js +27 -1
  10. package/configuration/solid.js.map +1 -1
  11. package/configuration/typescript.js +5 -75
  12. package/configuration/typescript.js.map +1 -1
  13. package/helpers/angular/angular-linker.d.ts +5 -6
  14. package/helpers/angular/angular-linker.js +36 -121
  15. package/helpers/angular/angular-linker.js.map +1 -1
  16. package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
  17. package/helpers/angular/inject-component-hmr-registration.js +291 -0
  18. package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
  19. package/helpers/angular/inject-hmr-vite-ignore.d.ts +75 -0
  20. package/helpers/angular/inject-hmr-vite-ignore.js +221 -0
  21. package/helpers/angular/inject-hmr-vite-ignore.js.map +1 -0
  22. package/helpers/angular/inline-decorator-component-templates.js +1 -170
  23. package/helpers/angular/inline-decorator-component-templates.js.map +1 -1
  24. package/helpers/angular/js-lexer.d.ts +4 -0
  25. package/helpers/angular/js-lexer.js +182 -0
  26. package/helpers/angular/js-lexer.js.map +1 -0
  27. package/helpers/angular/shared-linker.d.ts +31 -3
  28. package/helpers/angular/shared-linker.js +67 -14
  29. package/helpers/angular/shared-linker.js.map +1 -1
  30. package/helpers/angular/synthesize-decorator-ctor-parameters.js +2 -170
  31. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -1
  32. package/helpers/angular/synthesize-injectable-factories.js +1 -174
  33. package/helpers/angular/synthesize-injectable-factories.js.map +1 -1
  34. package/helpers/angular/util.d.ts +1 -0
  35. package/helpers/angular/util.js +88 -0
  36. package/helpers/angular/util.js.map +1 -1
  37. package/helpers/app-components.d.ts +2 -1
  38. package/helpers/app-components.js.map +1 -1
  39. package/helpers/app-css-state.d.ts +8 -0
  40. package/helpers/app-css-state.js +8 -0
  41. package/helpers/app-css-state.js.map +1 -0
  42. package/helpers/bundler-context.d.ts +11 -0
  43. package/helpers/bundler-context.js +71 -0
  44. package/helpers/bundler-context.js.map +1 -0
  45. package/helpers/config-as-json.js +10 -0
  46. package/helpers/config-as-json.js.map +1 -1
  47. package/helpers/dev-host.d.ts +341 -0
  48. package/helpers/dev-host.js +617 -0
  49. package/helpers/dev-host.js.map +1 -0
  50. package/helpers/esbuild-platform-resolver.js +4 -1
  51. package/helpers/esbuild-platform-resolver.js.map +1 -1
  52. package/helpers/global-defines.d.ts +51 -0
  53. package/helpers/global-defines.js +77 -0
  54. package/helpers/global-defines.js.map +1 -1
  55. package/helpers/hmr-scope.d.ts +26 -0
  56. package/helpers/hmr-scope.js +67 -0
  57. package/helpers/hmr-scope.js.map +1 -0
  58. package/helpers/init.js +0 -18
  59. package/helpers/init.js.map +1 -1
  60. package/helpers/logging.d.ts +1 -0
  61. package/helpers/logging.js +63 -3
  62. package/helpers/logging.js.map +1 -1
  63. package/helpers/main-entry.d.ts +2 -1
  64. package/helpers/main-entry.js +430 -47
  65. package/helpers/main-entry.js.map +1 -1
  66. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  67. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  68. package/helpers/nativeclass-transform.js.map +1 -1
  69. package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
  70. package/helpers/nativeclass-transformer-plugin.js +157 -14
  71. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  72. package/helpers/nativescript-package-resolver.js +8 -3
  73. package/helpers/nativescript-package-resolver.js.map +1 -1
  74. package/helpers/normalize-id.d.ts +42 -0
  75. package/helpers/normalize-id.js +60 -0
  76. package/helpers/normalize-id.js.map +1 -0
  77. package/helpers/ns-core-url.d.ts +88 -0
  78. package/helpers/ns-core-url.js +191 -0
  79. package/helpers/ns-core-url.js.map +1 -0
  80. package/helpers/package-platform-aliases.js +4 -3
  81. package/helpers/package-platform-aliases.js.map +1 -1
  82. package/helpers/platform-types.d.ts +2 -0
  83. package/helpers/platform-types.js +2 -0
  84. package/helpers/platform-types.js.map +1 -0
  85. package/helpers/prelink-angular.js +12 -33
  86. package/helpers/prelink-angular.js.map +1 -1
  87. package/helpers/project.d.ts +35 -0
  88. package/helpers/project.js +120 -2
  89. package/helpers/project.js.map +1 -1
  90. package/helpers/resolver.js +17 -2
  91. package/helpers/resolver.js.map +1 -1
  92. package/helpers/solid-jsx-deps.d.ts +15 -0
  93. package/helpers/solid-jsx-deps.js +178 -0
  94. package/helpers/solid-jsx-deps.js.map +1 -0
  95. package/helpers/ts-config-paths.d.ts +14 -0
  96. package/helpers/ts-config-paths.js +89 -8
  97. package/helpers/ts-config-paths.js.map +1 -1
  98. package/helpers/typescript-check.d.ts +2 -1
  99. package/helpers/typescript-check.js.map +1 -1
  100. package/helpers/workers.d.ts +20 -19
  101. package/helpers/workers.js +624 -4
  102. package/helpers/workers.js.map +1 -1
  103. package/hmr/client/css-handler.d.ts +1 -0
  104. package/hmr/client/css-handler.js +33 -20
  105. package/hmr/client/css-handler.js.map +1 -1
  106. package/hmr/client/css-update-overlay.d.ts +18 -0
  107. package/hmr/client/css-update-overlay.js +27 -0
  108. package/hmr/client/css-update-overlay.js.map +1 -0
  109. package/hmr/client/hmr-pending-overlay.d.ts +27 -0
  110. package/hmr/client/hmr-pending-overlay.js +50 -0
  111. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  112. package/hmr/client/index.js +419 -15
  113. package/hmr/client/index.js.map +1 -1
  114. package/hmr/client/utils.d.ts +6 -1
  115. package/hmr/client/utils.js +184 -8
  116. package/hmr/client/utils.js.map +1 -1
  117. package/hmr/client/vue-sfc-update-overlay.d.ts +82 -0
  118. package/hmr/client/vue-sfc-update-overlay.js +133 -0
  119. package/hmr/client/vue-sfc-update-overlay.js.map +1 -0
  120. package/hmr/entry-runtime.d.ts +2 -1
  121. package/hmr/entry-runtime.js +252 -65
  122. package/hmr/entry-runtime.js.map +1 -1
  123. package/hmr/frameworks/angular/client/index.d.ts +1 -0
  124. package/hmr/frameworks/angular/client/index.js +778 -20
  125. package/hmr/frameworks/angular/client/index.js.map +1 -1
  126. package/hmr/frameworks/angular/server/linker.js +1 -4
  127. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  128. package/hmr/frameworks/angular/server/strategy.js +13 -15
  129. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  130. package/hmr/frameworks/solid/server/strategy.js +3 -18
  131. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  132. package/hmr/frameworks/typescript/server/strategy.js +2 -15
  133. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  134. package/hmr/frameworks/vue/client/index.js +30 -199
  135. package/hmr/frameworks/vue/client/index.js.map +1 -1
  136. package/hmr/helpers/ast-normalizer.js +52 -5
  137. package/hmr/helpers/ast-normalizer.js.map +1 -1
  138. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  139. package/hmr/helpers/cjs-named-exports.js +152 -0
  140. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  141. package/hmr/helpers/package-exports.d.ts +16 -0
  142. package/hmr/helpers/package-exports.js +396 -0
  143. package/hmr/helpers/package-exports.js.map +1 -0
  144. package/hmr/server/angular-root-component.d.ts +79 -0
  145. package/hmr/server/angular-root-component.js +149 -0
  146. package/hmr/server/angular-root-component.js.map +1 -0
  147. package/hmr/server/constants.js +13 -4
  148. package/hmr/server/constants.js.map +1 -1
  149. package/hmr/server/core-sanitize.d.ts +90 -7
  150. package/hmr/server/core-sanitize.js +211 -56
  151. package/hmr/server/core-sanitize.js.map +1 -1
  152. package/hmr/server/framework-strategy.d.ts +9 -19
  153. package/hmr/server/hmr-module-graph.d.ts +37 -0
  154. package/hmr/server/hmr-module-graph.js +214 -0
  155. package/hmr/server/hmr-module-graph.js.map +1 -0
  156. package/hmr/server/import-map.js +60 -8
  157. package/hmr/server/import-map.js.map +1 -1
  158. package/hmr/server/index.js +1 -0
  159. package/hmr/server/index.js.map +1 -1
  160. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  161. package/hmr/server/ns-core-cjs-shape.js +271 -0
  162. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  163. package/hmr/server/ns-rt-bridge.d.ts +51 -0
  164. package/hmr/server/ns-rt-bridge.js +131 -0
  165. package/hmr/server/ns-rt-bridge.js.map +1 -0
  166. package/hmr/server/ns-rt-route.d.ts +5 -0
  167. package/hmr/server/ns-rt-route.js +35 -0
  168. package/hmr/server/ns-rt-route.js.map +1 -0
  169. package/hmr/server/perf-instrumentation.d.ts +114 -0
  170. package/hmr/server/perf-instrumentation.js +195 -0
  171. package/hmr/server/perf-instrumentation.js.map +1 -0
  172. package/hmr/server/require-guard.d.ts +1 -0
  173. package/hmr/server/require-guard.js +12 -0
  174. package/hmr/server/require-guard.js.map +1 -0
  175. package/hmr/server/route-helpers.d.ts +7 -0
  176. package/hmr/server/route-helpers.js +13 -0
  177. package/hmr/server/route-helpers.js.map +1 -0
  178. package/hmr/server/server-origin.d.ts +12 -0
  179. package/hmr/server/server-origin.js +66 -0
  180. package/hmr/server/server-origin.js.map +1 -0
  181. package/hmr/server/shared-transform-request.js +12 -5
  182. package/hmr/server/shared-transform-request.js.map +1 -1
  183. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  184. package/hmr/server/vendor-bare-module-shims.js +80 -0
  185. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  186. package/hmr/server/vite-plugin.js +60 -42
  187. package/hmr/server/vite-plugin.js.map +1 -1
  188. package/hmr/server/websocket-angular-entry.js +1 -1
  189. package/hmr/server/websocket-angular-entry.js.map +1 -1
  190. package/hmr/server/websocket-angular-hot-update.d.ts +17 -0
  191. package/hmr/server/websocket-angular-hot-update.js +176 -2
  192. package/hmr/server/websocket-angular-hot-update.js.map +1 -1
  193. package/hmr/server/websocket-core-bridge.d.ts +41 -6
  194. package/hmr/server/websocket-core-bridge.js +72 -75
  195. package/hmr/server/websocket-core-bridge.js.map +1 -1
  196. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  197. package/hmr/server/websocket-css-hot-update.js +65 -0
  198. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  199. package/hmr/server/websocket-device-transform.d.ts +21 -0
  200. package/hmr/server/websocket-device-transform.js +1570 -0
  201. package/hmr/server/websocket-device-transform.js.map +1 -0
  202. package/hmr/server/websocket-graph-upsert.d.ts +15 -0
  203. package/hmr/server/websocket-graph-upsert.js +20 -0
  204. package/hmr/server/websocket-graph-upsert.js.map +1 -1
  205. package/hmr/server/websocket-hmr-pending.d.ts +43 -0
  206. package/hmr/server/websocket-hmr-pending.js +55 -0
  207. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  208. package/hmr/server/websocket-hot-update.d.ts +51 -0
  209. package/hmr/server/websocket-hot-update.js +1160 -0
  210. package/hmr/server/websocket-hot-update.js.map +1 -0
  211. package/hmr/server/websocket-import-map-route.d.ts +15 -0
  212. package/hmr/server/websocket-import-map-route.js +44 -0
  213. package/hmr/server/websocket-import-map-route.js.map +1 -0
  214. package/hmr/server/websocket-module-bindings.js +3 -3
  215. package/hmr/server/websocket-module-bindings.js.map +1 -1
  216. package/hmr/server/websocket-module-specifiers.d.ts +66 -2
  217. package/hmr/server/websocket-module-specifiers.js +202 -19
  218. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  219. package/hmr/server/websocket-ns-core.d.ts +21 -0
  220. package/hmr/server/websocket-ns-core.js +305 -0
  221. package/hmr/server/websocket-ns-core.js.map +1 -0
  222. package/hmr/server/websocket-ns-entry.d.ts +22 -0
  223. package/hmr/server/websocket-ns-entry.js +150 -0
  224. package/hmr/server/websocket-ns-entry.js.map +1 -0
  225. package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
  226. package/hmr/server/websocket-ns-m-paths.js +92 -0
  227. package/hmr/server/websocket-ns-m-paths.js.map +1 -0
  228. package/hmr/server/websocket-ns-m-request.d.ts +45 -0
  229. package/hmr/server/websocket-ns-m-request.js +196 -0
  230. package/hmr/server/websocket-ns-m-request.js.map +1 -0
  231. package/hmr/server/websocket-ns-m.d.ts +34 -0
  232. package/hmr/server/websocket-ns-m.js +853 -0
  233. package/hmr/server/websocket-ns-m.js.map +1 -0
  234. package/hmr/server/websocket-served-module-helpers.d.ts +39 -0
  235. package/hmr/server/websocket-served-module-helpers.js +654 -0
  236. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  237. package/hmr/server/websocket-sfc.d.ts +24 -0
  238. package/hmr/server/websocket-sfc.js +1223 -0
  239. package/hmr/server/websocket-sfc.js.map +1 -0
  240. package/hmr/server/websocket-txn.d.ts +6 -0
  241. package/hmr/server/websocket-txn.js +39 -0
  242. package/hmr/server/websocket-txn.js.map +1 -0
  243. package/hmr/server/websocket-vendor-unifier.d.ts +10 -0
  244. package/hmr/server/websocket-vendor-unifier.js +45 -0
  245. package/hmr/server/websocket-vendor-unifier.js.map +1 -0
  246. package/hmr/server/websocket.d.ts +0 -30
  247. package/hmr/server/websocket.js +599 -6038
  248. package/hmr/server/websocket.js.map +1 -1
  249. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  250. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  251. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  252. package/hmr/shared/runtime/boot-progress.d.ts +40 -0
  253. package/hmr/shared/runtime/boot-progress.js +128 -0
  254. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  255. package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
  256. package/hmr/shared/runtime/boot-timeline.js +52 -0
  257. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  258. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  259. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  260. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  261. package/hmr/shared/runtime/dev-overlay.d.ts +75 -26
  262. package/hmr/shared/runtime/dev-overlay.js +990 -260
  263. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  264. package/hmr/shared/runtime/module-provenance.js +1 -4
  265. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  266. package/hmr/shared/runtime/root-placeholder-view.d.ts +19 -0
  267. package/hmr/shared/runtime/root-placeholder-view.js +310 -0
  268. package/hmr/shared/runtime/root-placeholder-view.js.map +1 -0
  269. package/hmr/shared/runtime/root-placeholder.js +352 -194
  270. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  271. package/hmr/shared/runtime/session-bootstrap.js +164 -1
  272. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  273. package/hmr/shared/runtime/vendor-bootstrap.js +1 -9
  274. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  275. package/hmr/shared/vendor/manifest-collect.d.ts +32 -0
  276. package/hmr/shared/vendor/manifest-collect.js +512 -0
  277. package/hmr/shared/vendor/manifest-collect.js.map +1 -0
  278. package/hmr/shared/vendor/manifest-loader.d.ts +2 -1
  279. package/hmr/shared/vendor/manifest-loader.js +3 -2
  280. package/hmr/shared/vendor/manifest-loader.js.map +1 -1
  281. package/hmr/shared/vendor/manifest.d.ts +1 -5
  282. package/hmr/shared/vendor/manifest.js +102 -739
  283. package/hmr/shared/vendor/manifest.js.map +1 -1
  284. package/hmr/shared/vendor/vendor-device-shim.d.ts +1 -0
  285. package/hmr/shared/vendor/vendor-device-shim.js +208 -0
  286. package/hmr/shared/vendor/vendor-device-shim.js.map +1 -0
  287. package/hmr/shared/vendor/vendor-esbuild-plugins.d.ts +16 -0
  288. package/hmr/shared/vendor/vendor-esbuild-plugins.js +203 -0
  289. package/hmr/shared/vendor/vendor-esbuild-plugins.js.map +1 -0
  290. package/index.d.ts +1 -0
  291. package/index.js +5 -0
  292. package/index.js.map +1 -1
  293. package/package.json +55 -11
  294. package/runtime/core-aliases-early.js +17 -41
  295. package/runtime/core-aliases-early.js.map +1 -1
@@ -0,0 +1,853 @@
1
+ import * as path from 'path';
2
+ import { parse as babelParse } from '@babel/parser';
3
+ import traverse from '@babel/traverse';
4
+ import { sanitizeStrayCoreReferences } from './core-sanitize.js';
5
+ import { getMonorepoWorkspaceRoot } from '../../helpers/project.js';
6
+ import { isRuntimeGraphExcludedPath } from './runtime-graph-filter.js';
7
+ import { buildPiniaVendorShim, buildVueVendorShim } from './vendor-bare-module-shims.js';
8
+ import { getNumericServeVersionTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
9
+ import { setDeviceModuleHeaders } from './route-helpers.js';
10
+ import { filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, resolveCandidateFilePath, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
11
+ import { assertNoOptimizedArtifacts, buildBootProgressSnippet, dedupeRtNamedImportsAgainstDestructures, deduplicateLinkerImports, ensureDestructureCoreImports, ensureGuardPlainDynamicImports, ensureVariableDynamicImportHelper, ensureVersionedRtImports, expandStarExports, hoistTopLevelStaticImports, MODULE_IMPORT_ANALYSIS_PLUGINS, wrapCommonJsModuleForDevice } from './websocket-served-module-helpers.js';
12
+ import { cleanCode, collectImportDependencies, prepareAngularEntryForDevice, processCodeForDevice, rewriteImports } from './websocket-device-transform.js';
13
+ import { REQUIRE_GUARD_SNIPPET } from './require-guard.js';
14
+ const babelTraverse = traverse?.default || traverse;
15
+ /**
16
+ * Registers the `/ns/m/*` device module server: the HTTP endpoint every
17
+ * NativeScript device uses to fetch app/source modules (and AnalogJS Angular
18
+ * `/@ng/component` live-reload requests) as device-ready ESM. This is the
19
+ * single hottest route in the HMR server.
20
+ */
21
+ export function registerNsModuleServerRoute(server, options) {
22
+ server.middlewares.use(async (req, res, next) => {
23
+ try {
24
+ const urlObj = new URL(req.url || '', 'http://localhost');
25
+ if (!urlObj.pathname.startsWith('/ns/m'))
26
+ return next();
27
+ // Closure deps injected by createHmrWebSocketPlugin (see websocket.ts).
28
+ const verbose = options.verbose;
29
+ const APP_VIRTUAL_WITH_SLASH = options.appVirtualWithSlash;
30
+ const sfcFileMap = options.sfcFileMap;
31
+ const depFileMap = options.depFileMap;
32
+ const getServerOrigin = options.getServerOrigin;
33
+ const sharedTransformRequest = options.sharedTransformRequest;
34
+ const ensureInitialGraphPopulationStarted = options.ensureInitialGraphPopulationStarted;
35
+ const strategy = options.getStrategy();
36
+ // Delegate AnalogJS Angular component live-reload endpoints.
37
+ //
38
+ // Angular 21's `ɵɵgetReplaceMetadataURL` (in @angular/core
39
+ // _debug_node-chunk.mjs) builds the metadata-replacement URL as
40
+ // `new URL('./@ng/component?c=<id>&t=<ts>', import.meta.url).href`.
41
+ // Because `import.meta.url` for a NS-served module is
42
+ // `http://host:port/ns/m/<project-relative>/component.ts`, the
43
+ // resolved metadata URL ends up *nested* under the component's
44
+ // directory: `/ns/m/<dir>/@ng/component?c=...&t=...`.
45
+ //
46
+ // AnalogJS's `liveReloadPlugin` registers a middleware that matches
47
+ // `/@ng/component` anywhere in `req.url` and returns either an empty
48
+ // module body (no HMR update available) or the metadata-replacement
49
+ // code (after a save invalidates the file). Without this delegation
50
+ // the NS `/ns/m/` middleware would treat the path as a file lookup,
51
+ // fail to resolve `@ng/component` against disk, and respond with
52
+ // 404 — which surfaces as `HTTP fetch/compile failed` at the
53
+ // component's own `_HmrLoad(Date.now())` call on initial boot and
54
+ // blocks Angular component bootstrapping.
55
+ //
56
+ // Calling `next()` here lets AnalogJS's middleware (or any other
57
+ // middleware later in the chain) handle the request. Analog's
58
+ // middleware reads only the `?c=` query string and is pathname-
59
+ // agnostic, so we don't need to rewrite `req.url` for it to work.
60
+ //
61
+ // HOWEVER: AnalogJS responds with an EMPTY body (`res.end('')`)
62
+ // for non-invalidated component IDs (initial boot, before any
63
+ // file save). The iOS HTTP ESM loader's
64
+ // `LoadHttpModuleForUrl` (ModuleInternalCallbacks.mm) treats an
65
+ // empty body as a fetch failure (`body.empty() → reject`), even
66
+ // when the HTTP status is 200 OK. That bubbles up as
67
+ // `HTTP fetch/compile failed` at the device's `__ns_import(...)`
68
+ // inside each component's `_HmrLoad(Date.now())` and crashes
69
+ // Angular's component bootstrap. To make Analog's empty
70
+ // "no-update" response acceptable to the iOS loader, we wrap
71
+ // `res.write` / `res.end` and substitute a minimal valid ESM
72
+ // module body (`export {}`) when downstream writes nothing.
73
+ // Non-empty bodies (real HMR update payloads after a save)
74
+ // pass through unchanged.
75
+ if (urlObj.pathname.includes('/@ng/component')) {
76
+ const chunks = [];
77
+ const origEnd = res.end.bind(res);
78
+ let ended = false;
79
+ const captureChunk = (chunk) => {
80
+ if (chunk == null)
81
+ return;
82
+ if (typeof chunk === 'string') {
83
+ chunks.push(chunk);
84
+ }
85
+ else if (Buffer.isBuffer(chunk)) {
86
+ chunks.push(chunk.toString('utf8'));
87
+ }
88
+ else {
89
+ chunks.push(String(chunk));
90
+ }
91
+ };
92
+ res.write = function (chunk, ..._args) {
93
+ captureChunk(chunk);
94
+ return true;
95
+ };
96
+ res.end = function (chunk, ..._args) {
97
+ if (ended)
98
+ return true;
99
+ ended = true;
100
+ captureChunk(chunk);
101
+ let body = chunks.join('');
102
+ if (body.length === 0) {
103
+ body = '// [ns:m] empty Angular component metadata — substituted with valid empty module to satisfy iOS HTTP loader (rejects empty bodies)\nexport {};\n';
104
+ }
105
+ try {
106
+ res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'));
107
+ }
108
+ catch { }
109
+ return origEnd(body);
110
+ };
111
+ return next();
112
+ }
113
+ // Previously we awaited `populateInitialGraph(server)` here so
114
+ // graphVersion would be non-zero for the first /ns/m request.
115
+ // That gave deterministic URL tags but blocked the cold boot on a
116
+ // full src/ tree walk (hundreds of transformRequest calls, 3-6s).
117
+ //
118
+ // graphVersion now starts at 1 and stays stable during cold boot
119
+ // (see `upsertGraphModule`'s bumpVersion option and the inline
120
+ // comment at the graphVersion declaration). We kick off the
121
+ // initial population in the background so it doesn't block the
122
+ // first response. `handleHotUpdate` awaits the same promise so
123
+ // the first HMR event still sees a fully populated graph.
124
+ ensureInitialGraphPopulationStarted(server);
125
+ // Cold-boot counter is now hooked via the leading boot-trace
126
+ // middleware (see `configureServer` — it records the request
127
+ // and tracks finish() via res.on('close'/'finish')). This
128
+ // handler used to record here but that missed the
129
+ // round-trip timing and didn't track per-route breakdowns.
130
+ setDeviceModuleHeaders(res);
131
+ // Support both query (?path=/abs) and path-style (/ns/m/abs)
132
+ let spec = urlObj.searchParams.get('path') || '';
133
+ // Optional graph version pin for deterministic boot
134
+ let forcedVer = urlObj.searchParams.get('v');
135
+ let bootTaggedRequest = false;
136
+ if (!spec) {
137
+ const base = '/ns/m';
138
+ let rest = urlObj.pathname.slice(base.length);
139
+ if (rest && rest !== '/')
140
+ spec = rest;
141
+ }
142
+ // Special-case stub for anomalous '@' imports emitted as '/__invalid_at__.mjs'
143
+ if (spec === '/__invalid_at__.mjs' || spec === '__invalid_at__.mjs') {
144
+ res.statusCode = 200;
145
+ res.end("// invalid '@' import stub\nexport {}\n");
146
+ return;
147
+ }
148
+ if (!spec) {
149
+ res.statusCode = 200;
150
+ res.end('export {}\n');
151
+ return;
152
+ }
153
+ const serverRoot = (server.config?.root || process.cwd());
154
+ const monorepoWorkspaceRoot = getMonorepoWorkspaceRoot(serverRoot);
155
+ spec = spec.replace(/[?#].*$/, '');
156
+ // Accept path-based boot/HMR prefixes:
157
+ // /ns/m/__ns_boot__/b1/<real-spec>
158
+ // /ns/m/__ns_hmr__/<tag>/<real-spec>
159
+ // /ns/m/__ns_boot__/b1/__ns_hmr__/<tag>/<real-spec>
160
+ // The iOS HTTP ESM loader canonicalizes cache keys by stripping query params,
161
+ // so we must carry the cache-buster in the path.
162
+ try {
163
+ const decorated = stripDecoratedServePrefixes(spec);
164
+ spec = decorated.cleanedSpec;
165
+ bootTaggedRequest = decorated.bootTaggedRequest;
166
+ forcedVer || (forcedVer = decorated.forcedVer);
167
+ }
168
+ catch { }
169
+ // Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
170
+ try {
171
+ const toPosix = (p) => p.replace(/\\/g, '/');
172
+ const rootPosix = toPosix(serverRoot);
173
+ const specPosix = toPosix(spec);
174
+ // If spec is an absolute path under the project root, convert to '/'+relative
175
+ const isAbsFs = /^\//.test(specPosix) || /^[A-Za-z]:\//.test(spec); // posix or win drive
176
+ if (isAbsFs) {
177
+ let rel = specPosix.startsWith(rootPosix) ? specPosix.slice(rootPosix.length) : require('path').posix.relative(rootPosix, specPosix);
178
+ if (!rel.startsWith('..')) {
179
+ if (!rel.startsWith('/'))
180
+ rel = '/' + rel;
181
+ // Ensure leading '/src' style when path maps into src
182
+ spec = rel;
183
+ }
184
+ }
185
+ }
186
+ catch { }
187
+ // Serve Vite virtual modules (/@id/ prefix). These are internal
188
+ // virtual modules (e.g., \0nsvite:nsconfig-json for ~/package.json)
189
+ // that don't exist on disk. Decode the ID and load via plugin container.
190
+ if (spec.startsWith('/@id/')) {
191
+ try {
192
+ // First try Vite's transform pipeline directly
193
+ const vr = await sharedTransformRequest(spec);
194
+ if (vr?.code) {
195
+ res.statusCode = 200;
196
+ res.end(vr.code);
197
+ return;
198
+ }
199
+ }
200
+ catch { }
201
+ try {
202
+ // Fallback: decode the virtual module ID (__x00__ → \0) and
203
+ // load through the plugin container directly
204
+ const rawId = spec.slice('/@id/'.length).replace(/__x00__/g, '\0');
205
+ const loadResult = await server.pluginContainer.load(rawId);
206
+ if (loadResult) {
207
+ const code = typeof loadResult === 'string' ? loadResult : loadResult.code;
208
+ if (code) {
209
+ res.statusCode = 200;
210
+ res.end(code);
211
+ return;
212
+ }
213
+ }
214
+ }
215
+ catch { }
216
+ }
217
+ if (spec.startsWith('@/'))
218
+ spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
219
+ if (spec.startsWith('./'))
220
+ spec = spec.slice(1);
221
+ const blockedNodeModulesReason = getBlockedDeviceNodeModulesReason(spec);
222
+ if (blockedNodeModulesReason) {
223
+ res.statusCode = 404;
224
+ res.end(`// [ns:m] blocked device import\nthrow new Error(${JSON.stringify(`[ns/m] ${blockedNodeModulesReason}`)});\nexport {};\n`);
225
+ return;
226
+ }
227
+ if (!spec.startsWith('/'))
228
+ spec = '/' + spec;
229
+ const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
230
+ const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
231
+ const candidates = [...(hasExt ? [spec] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
232
+ const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, serverRoot, monorepoWorkspaceRoot);
233
+ let transformed = null;
234
+ let resolvedCandidate = null;
235
+ const rawExplicitModule = tryReadRawExplicitJavaScriptModule(spec, serverRoot);
236
+ if (rawExplicitModule) {
237
+ transformed = { code: rawExplicitModule.code };
238
+ resolvedCandidate = rawExplicitModule.resolvedId;
239
+ }
240
+ // Queue and dedupe transformRequest calls so heavy app graphs do not
241
+ // overwhelm Vite with concurrent work. Slow-transform warnings start only
242
+ // when the transform actually begins executing, and requests stay pending
243
+ // until Vite returns a real result.
244
+ const transformWithTimeout = (url, timeoutMs = 120000) => {
245
+ return sharedTransformRequest(url, timeoutMs);
246
+ };
247
+ if (!transformed?.code) {
248
+ for (const cand of transformCandidates) {
249
+ try {
250
+ const r = await transformWithTimeout(cand);
251
+ if (r?.code) {
252
+ transformed = r;
253
+ resolvedCandidate = cand;
254
+ break;
255
+ }
256
+ }
257
+ catch { }
258
+ }
259
+ }
260
+ // Fallback 1: ask Vite to resolve the id, then transform the resolved id (handles aliases and virtual ids)
261
+ if (!transformed?.code) {
262
+ try {
263
+ const rid = await server.pluginContainer?.resolveId?.(spec, undefined);
264
+ const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
265
+ if (ridStr) {
266
+ const r = await transformWithTimeout(ridStr);
267
+ if (r?.code) {
268
+ transformed = r;
269
+ resolvedCandidate = ridStr;
270
+ }
271
+ }
272
+ }
273
+ catch { }
274
+ }
275
+ // Fallback 1b: if spec is a /node_modules/ path, extract bare specifier
276
+ // and try resolveId with that. This handles package.json "exports" field
277
+ // resolution (e.g., solid-js/jsx-runtime → solid-js/dist/solid.js).
278
+ if (!transformed?.code && spec.includes('/node_modules/')) {
279
+ try {
280
+ const nmIdx = spec.lastIndexOf('/node_modules/');
281
+ const bare = spec.slice(nmIdx + '/node_modules/'.length);
282
+ if (bare && !bare.startsWith('.')) {
283
+ const rid = await server.pluginContainer?.resolveId?.(bare, undefined);
284
+ const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
285
+ if (ridStr) {
286
+ const r = await sharedTransformRequest(ridStr);
287
+ if (r?.code) {
288
+ transformed = r;
289
+ resolvedCandidate = ridStr;
290
+ }
291
+ }
292
+ }
293
+ }
294
+ catch { }
295
+ }
296
+ // Fallback 2: try /@fs absolute path under project root (Vite file system alias).
297
+ // In a monorepo with hoisted node_modules the file may live above
298
+ // `serverRoot`, so try the workspace root next.
299
+ if (!transformed?.code) {
300
+ try {
301
+ const toPosix = (p) => p.replace(/\\/g, '/');
302
+ const rootsToTry = [serverRoot, ...(monorepoWorkspaceRoot && path.resolve(monorepoWorkspaceRoot) !== path.resolve(serverRoot) ? [monorepoWorkspaceRoot] : [])];
303
+ for (const root of rootsToTry) {
304
+ const rootPosix = toPosix(root).replace(/\/$/, '');
305
+ const absPosix = `${rootPosix}${spec.startsWith('/') ? '' : '/'}${spec}`;
306
+ const fsId = `/@fs${absPosix}`;
307
+ if (resolveCandidateFilePath(fsId, serverRoot, monorepoWorkspaceRoot)) {
308
+ const r = await transformWithTimeout(fsId);
309
+ if (r?.code) {
310
+ transformed = r;
311
+ resolvedCandidate = fsId;
312
+ break;
313
+ }
314
+ }
315
+ }
316
+ }
317
+ catch { }
318
+ }
319
+ // Fallback 3: try adding ?import to hint Vite's transform pipeline
320
+ if (!transformed?.code) {
321
+ for (const cand of transformCandidates) {
322
+ try {
323
+ const r = await transformWithTimeout(`${cand}${cand.includes('?') ? '&' : '?'}import`);
324
+ if (r?.code) {
325
+ transformed = r;
326
+ resolvedCandidate = `${cand}?import`;
327
+ break;
328
+ }
329
+ }
330
+ catch { }
331
+ }
332
+ }
333
+ // Solid HMR: patch @@solid-refresh's $$refreshESM to do inline patching
334
+ // during module re-evaluation instead of deferring to hot.accept() callback.
335
+ // In NativeScript's HTTP ESM environment, accept callbacks are registered
336
+ // but not invoked by the HMR client. By adding a direct patchRegistry()
337
+ // call when hot.data already has a stored registry, component updates
338
+ // apply immediately when the module re-evaluates.
339
+ try {
340
+ if (transformed?.code && strategy?.flavor === 'solid' && (resolvedCandidate || spec || '').includes('@solid-refresh')) {
341
+ const PATCH_SENTINEL = '/* __ns_solid_refresh_patched__ */';
342
+ const alreadyPatched = transformed.code.includes(PATCH_SENTINEL);
343
+ if (verbose) {
344
+ console.log('[hmr-ws][solid] @solid-refresh patch check:', { spec: resolvedCandidate || spec, alreadyPatched, codeLen: transformed.code.length });
345
+ }
346
+ if (!alreadyPatched) {
347
+ let patchedCode = transformed.code;
348
+ // Patch 1: Bypass shouldWarnAndDecline() — the vendor-bundled solid-js
349
+ // may not have the 'development' condition active, making DEV empty/undefined.
350
+ // In NativeScript HMR mode we are always in dev, so force it to return false.
351
+ const declineCheck = 'function shouldWarnAndDecline() {';
352
+ if (patchedCode.includes(declineCheck)) {
353
+ patchedCode = patchedCode.replace(declineCheck, `${PATCH_SENTINEL}\nfunction shouldWarnAndDecline() { return false; /* NS HMR: always allow refresh */ }\nfunction __original_shouldWarnAndDecline() {`);
354
+ if (verbose) {
355
+ console.log('[hmr-ws][solid] bypassed shouldWarnAndDecline() for NativeScript HMR');
356
+ }
357
+ }
358
+ // Patch 2: Force createMemo path in createProxy.
359
+ // Without the 'development' condition, $DEVCOMP is not set on components,
360
+ // so createProxy falls through to `return s(props)` — a direct call with
361
+ // no reactive subscription. When patchComponent fires update() (the signal
362
+ // setter), nobody is listening. By forcing the createMemo path, HMRComp
363
+ // subscribes to the signal and re-renders when the component changes.
364
+ const proxyCondition = 'if (!s || $DEVCOMP in s) {';
365
+ if (patchedCode.includes(proxyCondition)) {
366
+ patchedCode = patchedCode.replace(proxyCondition, 'if (true) { /* NS HMR: always use createMemo for reactive HMR updates */');
367
+ if (verbose) {
368
+ console.log('[hmr-ws][solid] forced createMemo path in createProxy for NativeScript HMR');
369
+ }
370
+ }
371
+ // Patch 2b (NS HMR escape hatch): expose `source` on the proxy
372
+ // itself via a non-enumerable, well-known symbol-ish key. This lets
373
+ // our HMR remount path bypass solid-refresh's proxy chain and call
374
+ // the freshly-patched underlying Home function directly. solid-refresh's
375
+ // proxy wraps everything in nested createMemo's, which under
376
+ // universal-renderer + nested-context (TanStack RouterContextProvider)
377
+ // causes accumulating zombie memos that all subscribe to M_initial.source
378
+ // — the visible symptom is "every other save applies".
379
+ //
380
+ // With this hatch we can do `proxy.__$$ns_resolve()` to obtain the
381
+ // current underlying component (e.g. M_initial.proxy after first save,
382
+ // or the actual Home function on the deepest hop) and then call
383
+ // untrack(() => extract until we reach the actual function), then mount
384
+ // against THAT function — no HMRComp memo proxy chain at the page level.
385
+ // NS HMR escape hatch: stash `source` on the HMRComp function
386
+ // itself AND short-circuit the Proxy's `get` handler so that
387
+ // `proxy.__$ns_resolveSource` returns our exposed function.
388
+ // Without the get-handler short-circuit, accessing the property
389
+ // on the Proxy goes through `return source()[property]` — which
390
+ // asks the *current source value* (which may itself be another
391
+ // solid-refresh proxy or the underlying user function) for the
392
+ // property. The user function doesn't have `__$ns_resolveSource`,
393
+ // and a chained proxy would re-enter its own get handler. Either
394
+ // way we end up with `undefined` at the page-level remount and
395
+ // can't unwrap.
396
+ //
397
+ // NOTE: `$$` in String.prototype.replace replacement is treated
398
+ // as a literal `$`. We use a single `$` to avoid that footgun.
399
+ // Match only the unique opening fragment to avoid getting tripped up
400
+ // by whitespace differences after the AST normalizer ran.
401
+ const newProxyMarker = `if (property === 'location' || property === 'name') {`;
402
+ if (patchedCode.includes(newProxyMarker)) {
403
+ // 1. Inject `__$ns_resolveSource` as a property on the HMRComp
404
+ // function itself (so its closure captures `source`).
405
+ // CRITICAL: assign at module-eval time (right after HMRComp
406
+ // is defined / before `return new Proxy(...)`), NOT inside
407
+ // the HMRComp body — the body only runs when the proxy is
408
+ // called, so before first call the property is undefined
409
+ // and the page-level remount unwrap finds nothing.
410
+ const setupMarker = `setComponentProperty(HMRComp, 'name', refreshName);`;
411
+ patchedCode = patchedCode.replace(setupMarker, `HMRComp.__$ns_resolveSource = function() { return source(); }; ${setupMarker}`);
412
+ // 2. Make the Proxy `get` handler short-circuit our property
413
+ // so callers can do `proxy.__$ns_resolveSource()` without
414
+ // going through `source()[property]` (which would unwrap one
415
+ // hop early or reach the user function which doesn't have it).
416
+ patchedCode = patchedCode.replace(newProxyMarker, `if (property === '__$ns_resolveSource') { return HMRComp.__$ns_resolveSource; } ${newProxyMarker}`);
417
+ if (verbose) {
418
+ console.log('[hmr-ws][solid] exposed __$ns_resolveSource on createProxy for NS HMR escape hatch');
419
+ }
420
+ }
421
+ // Patch 3: Inline patchRegistry call so updates apply immediately
422
+ // on module re-evaluation (accept callbacks are not invoked by the HMR client).
423
+ //
424
+ // The injected diagnostic logs are gated on
425
+ // `globalThis.__NS_ENV_VERBOSE__` so they're silent in
426
+ // normal use but resurface immediately when the user
427
+ // re-runs with verbose logging enabled. The flag is
428
+ // seeded by `mainEntryPlugin` from the same `verbose`
429
+ // option that drives this server-side log gating.
430
+ // Without these the visible HMR signal is just "did it
431
+ // apply" — with them, devs can answer "did `hot.data`
432
+ // persist", "did `patchRegistry` actually swap the
433
+ // proxy's signal source", and "did the registry
434
+ // component count change" without reaching for an
435
+ // inspector.
436
+ const marker = 'hot.data[SOLID_REFRESH] = hot.data[SOLID_REFRESH] || registry;';
437
+ if (patchedCode.includes(marker)) {
438
+ const patchCode = [
439
+ `var __nsRefreshVerbose = (typeof globalThis !== 'undefined') && !!globalThis.__NS_ENV_VERBOSE__;`,
440
+ `if (__nsRefreshVerbose) console.log('[ns-hmr][solid-refresh][$$refreshESM] hot.data has SOLID_REFRESH=', !!(hot.data && hot.data[SOLID_REFRESH]), 'registry components=', registry.components ? registry.components.size : 0);`,
441
+ `if (hot.data[SOLID_REFRESH]) {`,
442
+ ` var __nsOldComponents = hot.data[SOLID_REFRESH].components ? hot.data[SOLID_REFRESH].components.size : 0;`,
443
+ ` var __nsShouldInvalidate = patchRegistry(hot.data[SOLID_REFRESH], registry);`,
444
+ ` if (__nsRefreshVerbose) console.log('[ns-hmr][solid-refresh][$$refreshESM] patched: oldComponents=', __nsOldComponents, 'newComponents=', registry.components ? registry.components.size : 0, 'shouldInvalidate=', __nsShouldInvalidate);`,
445
+ `} else {`,
446
+ ` if (__nsRefreshVerbose) console.log('[ns-hmr][solid-refresh][$$refreshESM] first load — no prior registry to patch');`,
447
+ `}`,
448
+ ].join('\n ');
449
+ patchedCode = patchedCode.replace(marker, `${patchCode}\n ${marker}`);
450
+ if (verbose) {
451
+ console.log('[hmr-ws][solid] added inline patchRegistry (with diagnostics) for NativeScript HMR');
452
+ }
453
+ }
454
+ // Work on a copy to avoid mutating Vite's cached TransformResult
455
+ transformed = { ...transformed, code: patchedCode };
456
+ }
457
+ }
458
+ }
459
+ catch { }
460
+ // NOTE: Path-based cache busting for /ns/m/* imports is applied in the
461
+ // finalize step below (after rewriteImports adds the /ns/m/ prefix).
462
+ // The block here only handles TypeScript-specific graph population.
463
+ try {
464
+ if (transformed?.code) {
465
+ const code = transformed.code;
466
+ // TypeScript-specific graph population: when TS flavor is active
467
+ // and this is an application module under the virtual app root,
468
+ // upsert it into the HMR graph so ns:hmr-full-graph is non-empty.
469
+ try {
470
+ if (strategy?.flavor === 'typescript') {
471
+ const id = (resolvedCandidate || spec).replace(/[?#].*$/, '');
472
+ // Only track app modules (under APP_VIRTUAL_WITH_SLASH) and ts/js/tsx/jsx/mjs.
473
+ const isApp = id.startsWith(APP_VIRTUAL_WITH_SLASH) || id.startsWith('/app/');
474
+ if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
475
+ const deps = Array.from(collectImportDependencies(code, id));
476
+ if (verbose) {
477
+ console.log('[hmr-ws][ts-graph] candidate', { id, depsCount: deps.length });
478
+ }
479
+ // Serve-time warm-up: no live edit happened, so don't bump
480
+ // graphVersion.
481
+ options.upsertGraphModule(id, code, deps, { bumpVersion: false });
482
+ }
483
+ }
484
+ }
485
+ catch { }
486
+ }
487
+ }
488
+ catch { }
489
+ // If transformRequest failed, handle bare-module vendor shims for 'vue' and 'pinia'
490
+ if (!transformed?.code) {
491
+ const bare = spec.startsWith('/') ? spec.slice(1) : spec;
492
+ const isBare = bare && !bare.includes('/') && !/\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(bare);
493
+ if (isBare && (bare === 'vue' || bare === 'nativescript-vue' || bare === 'pinia')) {
494
+ const pkg = bare;
495
+ let code = '';
496
+ if (pkg === 'vue' || pkg === 'nativescript-vue') {
497
+ code = buildVueVendorShim(pkg);
498
+ }
499
+ else if (pkg === 'pinia') {
500
+ code = buildPiniaVendorShim();
501
+ }
502
+ res.statusCode = 200;
503
+ res.end(code || 'export {}\n');
504
+ return;
505
+ }
506
+ // Generic bare module resolution via Vite plugin container
507
+ if (isBare) {
508
+ try {
509
+ const resolved = await server.pluginContainer?.resolveId?.(spec, undefined);
510
+ const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
511
+ if (resolvedId) {
512
+ const r = await server.transformRequest(resolvedId);
513
+ if (r?.code) {
514
+ transformed = r;
515
+ resolvedCandidate = resolvedId;
516
+ }
517
+ }
518
+ }
519
+ catch { }
520
+ }
521
+ if (!transformed?.code) {
522
+ // Emit a module that throws with context for easier on-device debugging
523
+ try {
524
+ const tried = Array.from(new Set(transformCandidates.length > 0 ? transformCandidates : candidates)).slice(0, 12);
525
+ const out = `// [ns:m] transform miss path=${spec} tried=${tried.length}\n` + `throw new Error(${JSON.stringify(`[ns/m] transform failed for ${spec} (tried ${tried.length} candidates).`)});\nexport {};\n`;
526
+ res.statusCode = 404;
527
+ res.end(out);
528
+ return;
529
+ }
530
+ catch {
531
+ res.statusCode = 404;
532
+ res.end('export {}\n');
533
+ return;
534
+ }
535
+ }
536
+ }
537
+ let code = transformed.code;
538
+ // Prepend guard to capture any URL-based require attempts
539
+ code = REQUIRE_GUARD_SNIPPET + code;
540
+ code = cleanCode(code, strategy);
541
+ const isNodeMod = /(?:^|\/)node_modules\//.test(resolvedCandidate || spec || '');
542
+ code = processCodeForDevice(code, false, true, isNodeMod, resolvedCandidate || spec);
543
+ // Solid HMR: The NativeScript iOS/Android runtime provides import.meta.hot
544
+ // natively (via InitializeImportMetaHot in HMRSupport.mm) with C++-backed
545
+ // persistent hot.data that survives across module re-evaluations.
546
+ // cleanCode() strips Vite's __vite__createHotContext assignment, which is
547
+ // correct — the runtime's native hot context is better.
548
+ const projectRoot = server.config?.root || process.cwd();
549
+ const serverOrigin = getServerOrigin(server);
550
+ if (strategy?.flavor === 'angular') {
551
+ code = prepareAngularEntryForDevice(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
552
+ }
553
+ else {
554
+ code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
555
+ }
556
+ // Expand `export * from "url"` into explicit named re-exports.
557
+ // NativeScript's HTTP ESM loader may not propagate star-re-exports across
558
+ // HTTP module boundaries (the namespace object gets direct exports but
559
+ // misses re-exported names). By expanding to `export { a, b } from "url"`,
560
+ // the engine sees explicit named exports and resolves them correctly.
561
+ try {
562
+ code = await expandStarExports(code, server, server.config?.root || process.cwd(), verbose, sharedTransformRequest);
563
+ }
564
+ catch (e) {
565
+ if (verbose)
566
+ console.warn('[ns/m] export* expansion failed:', e?.message);
567
+ }
568
+ // Dedupe any /ns/rt named imports that duplicate destructured bindings off default /ns/rt
569
+ try {
570
+ code = dedupeRtNamedImportsAgainstDestructures(code);
571
+ }
572
+ catch { }
573
+ code = ensureVariableDynamicImportHelper(code);
574
+ // Final safety: guard any plain dynamic import(...) occurrences to reroute anomalous '@' specs
575
+ try {
576
+ code = ensureGuardPlainDynamicImports(code);
577
+ }
578
+ catch { }
579
+ // Extra hardening before the fast-fail assertion: run the
580
+ // consolidated stray-core-reference safety net. If any
581
+ // rewrite occurred, leave a diagnostic marker so the
582
+ // pipeline review log explains why the served code carries
583
+ // it.
584
+ try {
585
+ const __before = code;
586
+ code = sanitizeStrayCoreReferences(code);
587
+ if (code !== __before) {
588
+ code = `// [hmr-sanitize] core-literal->bridge\n` + code;
589
+ }
590
+ }
591
+ catch { }
592
+ // Final pass: deduplicate/resolve any bare-specifier imports that slipped
593
+ // through the pipeline (e.g., extracted from JSDoc comments by import-splitting
594
+ // regexes, or injected by the Angular linker on already-resolved code).
595
+ try {
596
+ code = deduplicateLinkerImports(code);
597
+ }
598
+ catch { }
599
+ // CJS/UMD wrapping: if a module uses module.exports but has no ESM export default,
600
+ // wrap it with CJS shims so the device HTTP ESM loader can consume it.
601
+ // This handles npm packages that use CommonJS but aren't pre-bundled by Vite.
602
+ //
603
+ // Key constraints this must handle:
604
+ // - CJS modules often declare local vars with the same names as their exports
605
+ // (e.g. `function createLTTB() {...}; exports.createLTTB = createLTTB;`)
606
+ // so `export var { createLTTB }` would cause a duplicate declaration.
607
+ // - UMD modules reference `this` at top level (undefined in ESM) but
608
+ // typically fall back to `self` or `globalThis`.
609
+ // - `module`, `exports` must be shims since they don't exist in ESM.
610
+ try {
611
+ code = wrapCommonJsModuleForDevice(code, resolvedCandidate || null);
612
+ }
613
+ catch { }
614
+ try {
615
+ assertNoOptimizedArtifacts(code, `NS M ${resolvedCandidate || spec}`);
616
+ }
617
+ catch (e) {
618
+ res.statusCode = 500;
619
+ return void res.end(`throw new Error(${JSON.stringify(e?.message || String(e))});\nexport {};`);
620
+ }
621
+ // Defensive export normalization: if a module defines `routes` and only exports it named,
622
+ // add a default export alias so both `import { routes }` and `import routes` work.
623
+ try {
624
+ if (!/\bexport\s+default\b/.test(code)) {
625
+ const hasNamedRoutes = /\bexport\s*\{\s*routes\s*\}/.test(code);
626
+ const hasConstRoutes = /\bconst\s+routes\s*=/.test(code) || /\bvar\s+routes\s*=/.test(code) || /\blet\s+routes\s*=/.test(code);
627
+ if (hasNamedRoutes && hasConstRoutes) {
628
+ code += `\nexport default routes;\n`;
629
+ }
630
+ }
631
+ }
632
+ catch { }
633
+ // `/ns/rt` and `/ns/core` URL versioning.
634
+ //
635
+ // Older versions of the server emitted `/ns/rt/<ver>` and
636
+ // `/ns/core/<ver>` so V8's HTTP module cache would see a
637
+ // fresh URL on every save. The runtime canonicalizer
638
+ // (`CanonicalizeHttpUrlKey` in HMRSupport.mm) collapses
639
+ // these version segments to the bare `/ns/rt` and
640
+ // `/ns/core` keys before lookup, so V8 actually saw a
641
+ // single cache entry — but the server was doing extra
642
+ // work to inject a version segment that the runtime then
643
+ // immediately stripped. Now that the runtime supports
644
+ // explicit eviction (and these bridge endpoints don't
645
+ // change at HMR time anyway), the version segment is
646
+ // purely vestigial.
647
+ //
648
+ // Rather than rip the helpers out (which would touch
649
+ // every ensureVersionedImports caller and risk bumping
650
+ // older runtimes), we keep them but pass `verNum=0`. The
651
+ // helpers still normalize URL shape (strip the absolute
652
+ // origin prefix when present) but emit a stable
653
+ // `/ns/rt/0` / `/ns/core/0` URL — which collapses to
654
+ // `/ns/rt` / `/ns/core` in the runtime.
655
+ try {
656
+ const verNum = 0;
657
+ code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
658
+ code = strategy.ensureVersionedImports?.(code, getServerOrigin(server), verNum) ?? code;
659
+ }
660
+ catch { }
661
+ // `/ns/m` URL finalize step.
662
+ //
663
+ // `rewriteNsMImportPathForHmr` is a canonicalizer: it
664
+ // strips legacy `__ns_hmr__/<tag>/` segments and adds
665
+ // `__ns_boot__/b1/` only for boot-tagged requests. The
666
+ // `ver` parameter is preserved on the signature for API
667
+ // compatibility but is ignored for app modules (cache
668
+ // busting is driven by `__nsInvalidateModules`, not URL
669
+ // versioning). We pass `'v0'` as a stable placeholder —
670
+ // the canonicalizer emits the same URL regardless of
671
+ // this value, but a constant placeholder makes the
672
+ // contract explicit.
673
+ //
674
+ // SFC URLs (line below, `/ns/sfc/${verTag}/...`) still
675
+ // embed a version because the Vue SFC pathway does not
676
+ // yet have an eviction protocol. The runtime
677
+ // canonicalizer does NOT strip `/ns/sfc/<ver>/`, so Vue
678
+ // users still see per-save SFC re-fetches — that's a
679
+ // known follow-up.
680
+ try {
681
+ const verTag = (() => {
682
+ const numeric = getNumericServeVersionTag(forcedVer, Number(options.getGraphVersion() || 0));
683
+ return numeric > 0 ? `v${numeric}` : 'v0';
684
+ })();
685
+ const origin = getServerOrigin(server);
686
+ const rewritePath = (p) => rewriteNsMImportPathForHmr(p, 'v0', bootTaggedRequest);
687
+ // /ns/m URL forms — all collapse to canonical stable
688
+ // URLs via the Phase 3a rewriter.
689
+ // 1) Static imports: import ... from "/ns/m/..."
690
+ code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
691
+ // 2) Side-effect imports: import "/ns/m/..."
692
+ code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
693
+ // 3) Dynamic imports: import("/ns/m/...")
694
+ code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
695
+ // 4) new URL("/ns/m/...", import.meta.url)
696
+ code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
697
+ // 5) __ns_import(new URL('/ns/m/...', import.meta.url).href)
698
+ code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\)\.href)/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
699
+ // 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → canonical stable URL.
700
+ try {
701
+ code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${rewritePath(p1)}`)}`);
702
+ }
703
+ catch { }
704
+ // 7) SFC URLs (Vue) — still versioned. See header comment.
705
+ try {
706
+ code = code.replace(/new\s+URL\(\s*["']\/ns\/sfc(\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}/ns/sfc/${verTag}${p1}`)}`);
707
+ }
708
+ catch { }
709
+ }
710
+ catch { }
711
+ // Final guard: eliminate any lingering named imports from /ns/core to avoid
712
+ // evaluation-time "does not provide an export named ..." in the device runtime.
713
+ try {
714
+ code = ensureDestructureCoreImports(code);
715
+ }
716
+ catch { }
717
+ // Boot-time module graph progress: while the app is still replacing the
718
+ // placeholder, emit lightweight progress updates as /ns/m modules begin
719
+ // evaluating. This keeps the overlay moving during large initial graphs.
720
+ try {
721
+ if (bootTaggedRequest) {
722
+ const bootModuleLabel = String(spec || '').replace(/\\/g, '/');
723
+ const bootProgressSnippet = buildBootProgressSnippet(bootModuleLabel);
724
+ code = bootProgressSnippet + code;
725
+ code = hoistTopLevelStaticImports(code);
726
+ }
727
+ }
728
+ catch { }
729
+ // Dev-only: link-check static imports to surface missing bindings early
730
+ try {
731
+ const devCheck = process.env.NODE_ENV !== 'production';
732
+ if (devCheck) {
733
+ const ast = babelParse(code, {
734
+ sourceType: 'module',
735
+ plugins: MODULE_IMPORT_ANALYSIS_PLUGINS,
736
+ });
737
+ const imports = [];
738
+ babelTraverse(ast, {
739
+ ImportDeclaration(p) {
740
+ const src = p.node.source.value;
741
+ if (typeof src !== 'string')
742
+ return;
743
+ const wantsDefault = p.node.specifiers.some((s) => s.type === 'ImportDefaultSpecifier');
744
+ imports.push({ src, wantsDefault });
745
+ },
746
+ });
747
+ // Resolve and verify each static import that asks for default
748
+ for (const imp of imports) {
749
+ if (!imp.wantsDefault)
750
+ continue;
751
+ // Only check our served endpoints and app modules
752
+ if (/^https?:\/\/[^\s]+\/ns\//.test(imp.src) || /^https?:\/\/[^\s]+\/.+/.test(imp.src)) {
753
+ const u = new URL(imp.src, 'http://localhost');
754
+ // Fetch target module's sanitized code using server.transformRequest or by routing through our own endpoints heuristically
755
+ let targetCode = '';
756
+ try {
757
+ if (u.pathname.startsWith('/ns/asm')) {
758
+ // Reconstruct: call our own assembler handler to get code (preferred)
759
+ const target = await server.transformRequest(imp.src.replace(/^https?:\/\/[^/]+/, ''));
760
+ targetCode = target?.code || '';
761
+ }
762
+ else if (u.pathname.startsWith('/ns/sfc')) {
763
+ // Delegator re-exports default from /ns/asm — skip; assembler will be checked when imported by upstream
764
+ continue;
765
+ }
766
+ else if (u.pathname.startsWith('/ns/m')) {
767
+ // Resolve to local project path and transform with same candidate logic as /ns/m handler
768
+ let local = u.pathname.replace(/^\/ns\/m/, '');
769
+ try {
770
+ // Normalize project-relative path
771
+ if (local.startsWith('@/'))
772
+ local = APP_VIRTUAL_WITH_SLASH + local.slice(2);
773
+ if (local.startsWith('./'))
774
+ local = local.slice(1);
775
+ if (!local.startsWith('/'))
776
+ local = '/' + local;
777
+ const hasExt = /(\.ts|\.tsx|\.js|\.jsx|\.mjs|\.mts|\.cts|\.vue)$/i.test(local);
778
+ const baseNoExt = hasExt ? local.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : local;
779
+ const cands = [...(hasExt ? [local] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
780
+ let t = null;
781
+ for (const cand of cands) {
782
+ try {
783
+ const r = await server.transformRequest(cand);
784
+ if (r?.code) {
785
+ t = r;
786
+ break;
787
+ }
788
+ }
789
+ catch { }
790
+ if (t?.code)
791
+ break;
792
+ }
793
+ if (!t?.code) {
794
+ try {
795
+ const rid = await server.pluginContainer?.resolveId?.(local, undefined);
796
+ const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
797
+ if (ridStr) {
798
+ const r2 = await server.transformRequest(ridStr);
799
+ if (r2?.code)
800
+ t = r2;
801
+ }
802
+ }
803
+ catch { }
804
+ }
805
+ targetCode = t?.code || '';
806
+ }
807
+ catch { }
808
+ }
809
+ else if (u.pathname.startsWith('/ns/rt') || u.pathname.startsWith('/ns/core')) {
810
+ // Bridges export named/default as needed; skip default check
811
+ continue;
812
+ }
813
+ }
814
+ catch { }
815
+ if (!targetCode)
816
+ continue;
817
+ const hasDefault = /\bexport\s+default\b/.test(targetCode) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(targetCode);
818
+ if (!hasDefault) {
819
+ // CJS/UMD modules won't have `export default` — they get CJS-wrapped
820
+ // by the serving pipeline. Only warn, don't fatally block the importer.
821
+ const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
822
+ if (hasCjsPattern) {
823
+ if (verbose) {
824
+ console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
825
+ }
826
+ continue;
827
+ }
828
+ const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
829
+ // Emit a module that throws to surface the exact offender
830
+ res.statusCode = 200;
831
+ res.end(`throw new Error(${JSON.stringify(msg)});\nexport {};`);
832
+ return;
833
+ }
834
+ }
835
+ }
836
+ }
837
+ }
838
+ catch (eLC) {
839
+ if (verbose) {
840
+ console.warn('[ns:m][link-check] failed', eLC?.message || eLC);
841
+ }
842
+ }
843
+ res.statusCode = 200;
844
+ res.end(code);
845
+ }
846
+ catch (e) {
847
+ console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
848
+ res.statusCode = 500;
849
+ res.end('export {}\n');
850
+ }
851
+ });
852
+ }
853
+ //# sourceMappingURL=websocket-ns-m.js.map