@nativescript/vite 8.0.0-alpha.5 → 8.0.0-alpha.50

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 (424) hide show
  1. package/README.md +51 -11
  2. package/bin/cli.cjs +8 -14
  3. package/configuration/angular.d.ts +34 -1
  4. package/configuration/angular.js +376 -165
  5. package/configuration/angular.js.map +1 -1
  6. package/configuration/base.js +241 -51
  7. package/configuration/base.js.map +1 -1
  8. package/configuration/javascript.js +14 -93
  9. package/configuration/javascript.js.map +1 -1
  10. package/configuration/react.js +41 -73
  11. package/configuration/react.js.map +1 -1
  12. package/configuration/solid.js +51 -6
  13. package/configuration/solid.js.map +1 -1
  14. package/configuration/typescript.js +12 -93
  15. package/configuration/typescript.js.map +1 -1
  16. package/helpers/app-components.d.ts +2 -1
  17. package/helpers/app-components.js.map +1 -1
  18. package/helpers/app-css-state.d.ts +8 -0
  19. package/helpers/app-css-state.js +8 -0
  20. package/helpers/app-css-state.js.map +1 -0
  21. package/helpers/bundler-context.d.ts +11 -0
  22. package/helpers/bundler-context.js +71 -0
  23. package/helpers/bundler-context.js.map +1 -0
  24. package/helpers/cli-flags.d.ts +12 -0
  25. package/helpers/cli-flags.js +34 -0
  26. package/helpers/cli-flags.js.map +1 -1
  27. package/helpers/css-platform-plugin.d.ts +14 -0
  28. package/helpers/css-platform-plugin.js +43 -25
  29. package/helpers/css-platform-plugin.js.map +1 -1
  30. package/helpers/dev-host.d.ts +360 -0
  31. package/helpers/dev-host.js +694 -0
  32. package/helpers/dev-host.js.map +1 -0
  33. package/helpers/dynamic-import-plugin.js +1 -1
  34. package/helpers/dynamic-import-plugin.js.map +1 -1
  35. package/helpers/esbuild-platform-resolver.js +4 -1
  36. package/helpers/esbuild-platform-resolver.js.map +1 -1
  37. package/helpers/external-configs.d.ts +10 -12
  38. package/helpers/external-configs.js +58 -35
  39. package/helpers/external-configs.js.map +1 -1
  40. package/helpers/global-defines.d.ts +142 -0
  41. package/helpers/global-defines.js +213 -11
  42. package/helpers/global-defines.js.map +1 -1
  43. package/helpers/hmr-scope.d.ts +26 -0
  44. package/helpers/hmr-scope.js +67 -0
  45. package/helpers/hmr-scope.js.map +1 -0
  46. package/helpers/init.js +0 -18
  47. package/helpers/init.js.map +1 -1
  48. package/helpers/logging.d.ts +1 -0
  49. package/helpers/logging.js +65 -4
  50. package/helpers/logging.js.map +1 -1
  51. package/helpers/main-entry.d.ts +3 -1
  52. package/helpers/main-entry.js +301 -53
  53. package/helpers/main-entry.js.map +1 -1
  54. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  55. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  56. package/helpers/nativeclass-transform.js +5 -6
  57. package/helpers/nativeclass-transform.js.map +1 -1
  58. package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
  59. package/helpers/nativeclass-transformer-plugin.js +157 -14
  60. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  61. package/helpers/nativescript-package-resolver.js +10 -62
  62. package/helpers/nativescript-package-resolver.js.map +1 -1
  63. package/helpers/normalize-id.d.ts +42 -0
  64. package/helpers/normalize-id.js +60 -0
  65. package/helpers/normalize-id.js.map +1 -0
  66. package/helpers/ns-core-url.d.ts +29 -7
  67. package/helpers/ns-core-url.js +69 -12
  68. package/helpers/ns-core-url.js.map +1 -1
  69. package/helpers/optimize-deps.d.ts +16 -0
  70. package/helpers/optimize-deps.js +17 -0
  71. package/helpers/optimize-deps.js.map +1 -0
  72. package/helpers/package-platform-aliases.js +34 -49
  73. package/helpers/package-platform-aliases.js.map +1 -1
  74. package/helpers/platform-types.d.ts +2 -0
  75. package/helpers/platform-types.js +2 -0
  76. package/helpers/platform-types.js.map +1 -0
  77. package/helpers/postcss-platform-config.d.ts +17 -1
  78. package/helpers/postcss-platform-config.js +20 -37
  79. package/helpers/postcss-platform-config.js.map +1 -1
  80. package/helpers/project.d.ts +35 -0
  81. package/helpers/project.js +120 -2
  82. package/helpers/project.js.map +1 -1
  83. package/helpers/resolve-main-field-platform.d.ts +20 -0
  84. package/helpers/resolve-main-field-platform.js +49 -0
  85. package/helpers/resolve-main-field-platform.js.map +1 -0
  86. package/helpers/resolver.js +17 -2
  87. package/helpers/resolver.js.map +1 -1
  88. package/helpers/theme-core-plugins.js +1 -1
  89. package/helpers/theme-core-plugins.js.map +1 -1
  90. package/helpers/ts-config-paths.d.ts +14 -0
  91. package/helpers/ts-config-paths.js +90 -9
  92. package/helpers/ts-config-paths.js.map +1 -1
  93. package/helpers/typescript-check.d.ts +2 -1
  94. package/helpers/typescript-check.js +2 -2
  95. package/helpers/typescript-check.js.map +1 -1
  96. package/helpers/ui-registration.d.ts +21 -0
  97. package/helpers/ui-registration.js +156 -0
  98. package/helpers/ui-registration.js.map +1 -0
  99. package/helpers/utils.d.ts +9 -0
  100. package/helpers/utils.js +22 -2
  101. package/helpers/utils.js.map +1 -1
  102. package/helpers/workers.d.ts +20 -19
  103. package/helpers/workers.js +624 -4
  104. package/helpers/workers.js.map +1 -1
  105. package/hmr/client/css-handler.d.ts +2 -1
  106. package/hmr/client/css-handler.js +50 -26
  107. package/hmr/client/css-handler.js.map +1 -1
  108. package/hmr/client/css-update-overlay.d.ts +18 -0
  109. package/hmr/client/css-update-overlay.js +27 -0
  110. package/hmr/client/css-update-overlay.js.map +1 -0
  111. package/hmr/client/framework-client-strategy.d.ts +79 -0
  112. package/hmr/client/framework-client-strategy.js +19 -0
  113. package/hmr/client/framework-client-strategy.js.map +1 -0
  114. package/hmr/client/hmr-pending-overlay.d.ts +13 -0
  115. package/hmr/client/hmr-pending-overlay.js +60 -0
  116. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  117. package/hmr/client/index.js +891 -220
  118. package/hmr/client/index.js.map +1 -1
  119. package/hmr/client/utils.d.ts +7 -1
  120. package/hmr/client/utils.js +207 -29
  121. package/hmr/client/utils.js.map +1 -1
  122. package/hmr/entry-runtime.d.ts +2 -1
  123. package/hmr/entry-runtime.js +256 -69
  124. package/hmr/entry-runtime.js.map +1 -1
  125. package/hmr/frameworks/angular/build/angular-linker.d.ts +12 -0
  126. package/hmr/frameworks/angular/build/angular-linker.js +109 -0
  127. package/hmr/frameworks/angular/build/angular-linker.js.map +1 -0
  128. package/hmr/frameworks/angular/build/inject-component-hmr-registration.d.ts +112 -0
  129. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js +291 -0
  130. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js.map +1 -0
  131. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.d.ts +75 -0
  132. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js +221 -0
  133. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js.map +1 -0
  134. package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.js +1 -170
  135. package/hmr/frameworks/angular/build/inline-decorator-component-templates.js.map +1 -0
  136. package/hmr/frameworks/angular/build/js-lexer.d.ts +4 -0
  137. package/{helpers/angular/synthesize-decorator-ctor-parameters.js → hmr/frameworks/angular/build/js-lexer.js} +22 -96
  138. package/hmr/frameworks/angular/build/js-lexer.js.map +1 -0
  139. package/hmr/frameworks/angular/build/shared-linker.d.ts +39 -0
  140. package/hmr/frameworks/angular/build/shared-linker.js +128 -0
  141. package/hmr/frameworks/angular/build/shared-linker.js.map +1 -0
  142. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js +88 -0
  143. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js.map +1 -0
  144. package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.js +1 -174
  145. package/hmr/frameworks/angular/build/synthesize-injectable-factories.js.map +1 -0
  146. package/{helpers/angular → hmr/frameworks/angular/build}/util.d.ts +1 -0
  147. package/hmr/frameworks/angular/build/util.js +155 -0
  148. package/hmr/frameworks/angular/build/util.js.map +1 -0
  149. package/hmr/frameworks/angular/client/index.d.ts +1 -0
  150. package/hmr/frameworks/angular/client/index.js +859 -21
  151. package/hmr/frameworks/angular/client/index.js.map +1 -1
  152. package/hmr/frameworks/angular/client/strategy.d.ts +9 -0
  153. package/hmr/frameworks/angular/client/strategy.js +19 -0
  154. package/hmr/frameworks/angular/client/strategy.js.map +1 -0
  155. package/hmr/frameworks/angular/server/angular-root-component.d.ts +79 -0
  156. package/hmr/frameworks/angular/server/angular-root-component.js +149 -0
  157. package/hmr/frameworks/angular/server/angular-root-component.js.map +1 -0
  158. package/hmr/frameworks/angular/server/linker.js +1 -4
  159. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  160. package/hmr/frameworks/angular/server/strategy.js +460 -12
  161. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  162. package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.js +2 -2
  163. package/hmr/frameworks/angular/server/websocket-angular-entry.js.map +1 -0
  164. package/hmr/{server → frameworks/angular/server}/websocket-angular-hot-update.d.ts +17 -11
  165. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +336 -0
  166. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -0
  167. package/hmr/frameworks/react/server/strategy.d.ts +2 -0
  168. package/hmr/frameworks/react/server/strategy.js +150 -0
  169. package/hmr/frameworks/react/server/strategy.js.map +1 -0
  170. package/hmr/frameworks/solid/build/solid-jsx-deps.d.ts +15 -0
  171. package/hmr/frameworks/solid/build/solid-jsx-deps.js +178 -0
  172. package/hmr/frameworks/solid/build/solid-jsx-deps.js.map +1 -0
  173. package/hmr/frameworks/solid/client/app-runtime.d.ts +54 -0
  174. package/hmr/frameworks/solid/client/app-runtime.js +184 -0
  175. package/hmr/frameworks/solid/client/app-runtime.js.map +1 -0
  176. package/hmr/frameworks/solid/server/strategy.js +291 -16
  177. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  178. package/hmr/frameworks/typescript/server/strategy.js +38 -14
  179. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  180. package/hmr/frameworks/vue/client/dep-propagation.d.ts +36 -0
  181. package/hmr/frameworks/vue/client/dep-propagation.js +101 -0
  182. package/hmr/frameworks/vue/client/dep-propagation.js.map +1 -0
  183. package/hmr/frameworks/vue/client/index.d.ts +8 -0
  184. package/hmr/frameworks/vue/client/index.js +56 -243
  185. package/hmr/frameworks/vue/client/index.js.map +1 -1
  186. package/hmr/frameworks/vue/client/strategy.d.ts +33 -0
  187. package/hmr/frameworks/vue/client/strategy.js +157 -0
  188. package/hmr/frameworks/vue/client/strategy.js.map +1 -0
  189. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.d.ts +49 -0
  190. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +142 -0
  191. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -0
  192. package/hmr/frameworks/vue/server/sfc-route-assemble.d.ts +7 -0
  193. package/hmr/frameworks/vue/server/sfc-route-assemble.js +747 -0
  194. package/hmr/frameworks/vue/server/sfc-route-assemble.js.map +1 -0
  195. package/hmr/frameworks/vue/server/sfc-route-meta.d.ts +7 -0
  196. package/hmr/frameworks/vue/server/sfc-route-meta.js +80 -0
  197. package/hmr/frameworks/vue/server/sfc-route-meta.js.map +1 -0
  198. package/hmr/frameworks/vue/server/sfc-route-serve.d.ts +8 -0
  199. package/hmr/frameworks/vue/server/sfc-route-serve.js +459 -0
  200. package/hmr/frameworks/vue/server/sfc-route-serve.js.map +1 -0
  201. package/hmr/frameworks/vue/server/sfc-route-shared.d.ts +38 -0
  202. package/hmr/frameworks/vue/server/sfc-route-shared.js +48 -0
  203. package/hmr/frameworks/vue/server/sfc-route-shared.js.map +1 -0
  204. package/hmr/frameworks/vue/server/strategy.d.ts +35 -0
  205. package/hmr/frameworks/vue/server/strategy.js +278 -1
  206. package/hmr/frameworks/vue/server/strategy.js.map +1 -1
  207. package/hmr/frameworks/vue/server/websocket-sfc.d.ts +15 -0
  208. package/hmr/frameworks/vue/server/websocket-sfc.js +20 -0
  209. package/hmr/frameworks/vue/server/websocket-sfc.js.map +1 -0
  210. package/hmr/helpers/ast-normalizer.d.ts +3 -1
  211. package/hmr/helpers/ast-normalizer.js +77 -10
  212. package/hmr/helpers/ast-normalizer.js.map +1 -1
  213. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  214. package/hmr/helpers/cjs-named-exports.js +152 -0
  215. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  216. package/hmr/helpers/package-exports.d.ts +16 -0
  217. package/hmr/helpers/package-exports.js +396 -0
  218. package/hmr/helpers/package-exports.js.map +1 -0
  219. package/hmr/server/constants.js +20 -5
  220. package/hmr/server/constants.js.map +1 -1
  221. package/hmr/server/core-sanitize.d.ts +86 -7
  222. package/hmr/server/core-sanitize.js +170 -45
  223. package/hmr/server/core-sanitize.js.map +1 -1
  224. package/hmr/server/device-transform-helpers.d.ts +24 -0
  225. package/hmr/server/device-transform-helpers.js +408 -0
  226. package/hmr/server/device-transform-helpers.js.map +1 -0
  227. package/hmr/server/framework-strategy.d.ts +108 -11
  228. package/hmr/server/hmr-module-graph.d.ts +37 -0
  229. package/hmr/server/hmr-module-graph.js +214 -0
  230. package/hmr/server/hmr-module-graph.js.map +1 -0
  231. package/hmr/server/import-map.d.ts +11 -2
  232. package/hmr/server/import-map.js +92 -45
  233. package/hmr/server/import-map.js.map +1 -1
  234. package/hmr/server/index.js +7 -16
  235. package/hmr/server/index.js.map +1 -1
  236. package/hmr/server/ns-core-cjs-shape.d.ts +2 -4
  237. package/hmr/server/ns-core-cjs-shape.js +4 -6
  238. package/hmr/server/ns-core-cjs-shape.js.map +1 -1
  239. package/hmr/server/ns-rt-bridge.d.ts +51 -0
  240. package/hmr/server/ns-rt-bridge.js +131 -0
  241. package/hmr/server/ns-rt-bridge.js.map +1 -0
  242. package/hmr/server/ns-rt-route.d.ts +5 -0
  243. package/hmr/server/ns-rt-route.js +38 -0
  244. package/hmr/server/ns-rt-route.js.map +1 -0
  245. package/hmr/server/perf-instrumentation.d.ts +114 -0
  246. package/hmr/server/perf-instrumentation.js +197 -0
  247. package/hmr/server/perf-instrumentation.js.map +1 -0
  248. package/hmr/server/process-code-for-device.d.ts +14 -0
  249. package/hmr/server/process-code-for-device.js +699 -0
  250. package/hmr/server/process-code-for-device.js.map +1 -0
  251. package/hmr/server/require-guard.d.ts +1 -0
  252. package/hmr/server/require-guard.js +12 -0
  253. package/hmr/server/require-guard.js.map +1 -0
  254. package/hmr/server/rewrite-imports.d.ts +2 -0
  255. package/hmr/server/rewrite-imports.js +630 -0
  256. package/hmr/server/rewrite-imports.js.map +1 -0
  257. package/hmr/server/route-helpers.d.ts +7 -0
  258. package/hmr/server/route-helpers.js +13 -0
  259. package/hmr/server/route-helpers.js.map +1 -0
  260. package/hmr/server/server-origin.d.ts +2 -0
  261. package/hmr/server/server-origin.js +83 -0
  262. package/hmr/server/server-origin.js.map +1 -0
  263. package/hmr/server/shared-transform-request.js +13 -7
  264. package/hmr/server/shared-transform-request.js.map +1 -1
  265. package/hmr/server/transform-cache-invalidation.d.ts +37 -0
  266. package/hmr/server/transform-cache-invalidation.js +156 -0
  267. package/hmr/server/transform-cache-invalidation.js.map +1 -0
  268. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  269. package/hmr/server/vendor-bare-module-shims.js +80 -0
  270. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  271. package/hmr/server/vite-plugin.js +78 -42
  272. package/hmr/server/vite-plugin.js.map +1 -1
  273. package/hmr/server/websocket-core-bridge.d.ts +45 -4
  274. package/hmr/server/websocket-core-bridge.js +50 -48
  275. package/hmr/server/websocket-core-bridge.js.map +1 -1
  276. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  277. package/hmr/server/websocket-css-hot-update.js +65 -0
  278. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  279. package/hmr/server/websocket-device-transform.d.ts +3 -0
  280. package/hmr/server/websocket-device-transform.js +7 -0
  281. package/hmr/server/websocket-device-transform.js.map +1 -0
  282. package/hmr/server/websocket-graph-upsert.d.ts +15 -0
  283. package/hmr/server/websocket-graph-upsert.js +20 -0
  284. package/hmr/server/websocket-graph-upsert.js.map +1 -1
  285. package/hmr/server/websocket-hmr-pending.d.ts +37 -0
  286. package/hmr/server/websocket-hmr-pending.js +55 -0
  287. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  288. package/hmr/server/websocket-hot-update.d.ts +77 -0
  289. package/hmr/server/websocket-hot-update.js +360 -0
  290. package/hmr/server/websocket-hot-update.js.map +1 -0
  291. package/hmr/server/websocket-import-map-route.d.ts +15 -0
  292. package/hmr/server/websocket-import-map-route.js +50 -0
  293. package/hmr/server/websocket-import-map-route.js.map +1 -0
  294. package/hmr/server/websocket-module-bindings.js +3 -3
  295. package/hmr/server/websocket-module-bindings.js.map +1 -1
  296. package/hmr/server/websocket-module-specifiers.d.ts +66 -2
  297. package/hmr/server/websocket-module-specifiers.js +191 -20
  298. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  299. package/hmr/server/websocket-ns-core.d.ts +21 -0
  300. package/hmr/server/websocket-ns-core.js +311 -0
  301. package/hmr/server/websocket-ns-core.js.map +1 -0
  302. package/hmr/server/websocket-ns-entry.d.ts +21 -0
  303. package/hmr/server/websocket-ns-entry.js +164 -0
  304. package/hmr/server/websocket-ns-entry.js.map +1 -0
  305. package/hmr/server/websocket-ns-m-paths.d.ts +1 -1
  306. package/hmr/server/websocket-ns-m-paths.js +58 -13
  307. package/hmr/server/websocket-ns-m-paths.js.map +1 -1
  308. package/hmr/server/websocket-ns-m-request.d.ts +11 -1
  309. package/hmr/server/websocket-ns-m-request.js +18 -25
  310. package/hmr/server/websocket-ns-m-request.js.map +1 -1
  311. package/hmr/server/websocket-ns-m.d.ts +33 -0
  312. package/hmr/server/websocket-ns-m.js +752 -0
  313. package/hmr/server/websocket-ns-m.js.map +1 -0
  314. package/hmr/server/websocket-served-module-helpers.d.ts +49 -3
  315. package/hmr/server/websocket-served-module-helpers.js +403 -87
  316. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  317. package/hmr/server/websocket-txn.js +2 -8
  318. package/hmr/server/websocket-txn.js.map +1 -1
  319. package/hmr/server/websocket-vendor-unifier.d.ts +0 -1
  320. package/hmr/server/websocket-vendor-unifier.js +4 -9
  321. package/hmr/server/websocket-vendor-unifier.js.map +1 -1
  322. package/hmr/server/websocket.d.ts +8 -39
  323. package/hmr/server/websocket.js +608 -6300
  324. package/hmr/server/websocket.js.map +1 -1
  325. package/hmr/shared/ns-globals.d.ts +118 -0
  326. package/hmr/shared/ns-globals.js +29 -0
  327. package/hmr/shared/ns-globals.js.map +1 -0
  328. package/hmr/shared/protocol.d.ts +145 -0
  329. package/hmr/shared/protocol.js +28 -0
  330. package/hmr/shared/protocol.js.map +1 -0
  331. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  332. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  333. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  334. package/hmr/shared/runtime/boot-progress.d.ts +44 -0
  335. package/hmr/shared/runtime/boot-progress.js +133 -0
  336. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  337. package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
  338. package/hmr/shared/runtime/boot-timeline.js +42 -0
  339. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  340. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -1
  341. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  342. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  343. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  344. package/hmr/shared/runtime/dev-overlay.d.ts +119 -26
  345. package/hmr/shared/runtime/dev-overlay.js +1196 -262
  346. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  347. package/hmr/shared/runtime/global-scope.d.ts +18 -0
  348. package/hmr/shared/runtime/global-scope.js +21 -0
  349. package/hmr/shared/runtime/global-scope.js.map +1 -0
  350. package/hmr/shared/runtime/hooks.js +2 -1
  351. package/hmr/shared/runtime/hooks.js.map +1 -1
  352. package/hmr/shared/runtime/http-only-boot.js +7 -6
  353. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  354. package/hmr/shared/runtime/module-provenance.js +4 -6
  355. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  356. package/hmr/shared/runtime/root-placeholder-view.d.ts +19 -0
  357. package/hmr/shared/runtime/root-placeholder-view.js +311 -0
  358. package/hmr/shared/runtime/root-placeholder-view.js.map +1 -0
  359. package/hmr/shared/runtime/root-placeholder.js +393 -200
  360. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  361. package/hmr/shared/runtime/session-bootstrap.js +168 -4
  362. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  363. package/hmr/shared/runtime/vendor-bootstrap.js +3 -10
  364. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  365. package/hmr/shared/vendor/manifest-collect.d.ts +4 -0
  366. package/hmr/shared/vendor/manifest-collect.js +549 -0
  367. package/hmr/shared/vendor/manifest-collect.js.map +1 -0
  368. package/hmr/shared/vendor/manifest-loader.d.ts +2 -1
  369. package/hmr/shared/vendor/manifest-loader.js +5 -3
  370. package/hmr/shared/vendor/manifest-loader.js.map +1 -1
  371. package/hmr/shared/vendor/manifest.d.ts +1 -7
  372. package/hmr/shared/vendor/manifest.js +84 -819
  373. package/hmr/shared/vendor/manifest.js.map +1 -1
  374. package/hmr/shared/vendor/vendor-device-shim.d.ts +1 -0
  375. package/hmr/shared/vendor/vendor-device-shim.js +208 -0
  376. package/hmr/shared/vendor/vendor-device-shim.js.map +1 -0
  377. package/hmr/shared/vendor/vendor-esbuild-plugins.d.ts +38 -0
  378. package/hmr/shared/vendor/vendor-esbuild-plugins.js +296 -0
  379. package/hmr/shared/vendor/vendor-esbuild-plugins.js.map +1 -0
  380. package/hmr/vendor-bootstrap.d.ts +1 -3
  381. package/hmr/vendor-bootstrap.js +4 -6
  382. package/hmr/vendor-bootstrap.js.map +1 -1
  383. package/index.d.ts +1 -0
  384. package/index.js +5 -0
  385. package/index.js.map +1 -1
  386. package/package.json +61 -11
  387. package/runtime/core-aliases-early.js +25 -55
  388. package/runtime/core-aliases-early.js.map +1 -1
  389. package/shims/react-jsx-runtime.d.ts +4 -0
  390. package/shims/react-jsx-runtime.js +37 -0
  391. package/shims/react-jsx-runtime.js.map +1 -0
  392. package/helpers/angular/angular-linker.d.ts +0 -13
  393. package/helpers/angular/angular-linker.js +0 -194
  394. package/helpers/angular/angular-linker.js.map +0 -1
  395. package/helpers/angular/inline-decorator-component-templates.js.map +0 -1
  396. package/helpers/angular/shared-linker.d.ts +0 -11
  397. package/helpers/angular/shared-linker.js +0 -75
  398. package/helpers/angular/shared-linker.js.map +0 -1
  399. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +0 -1
  400. package/helpers/angular/synthesize-injectable-factories.js.map +0 -1
  401. package/helpers/angular/util.js +0 -67
  402. package/helpers/angular/util.js.map +0 -1
  403. package/helpers/prelink-angular.d.ts +0 -2
  404. package/helpers/prelink-angular.js +0 -117
  405. package/helpers/prelink-angular.js.map +0 -1
  406. package/hmr/server/websocket-angular-entry.js.map +0 -1
  407. package/hmr/server/websocket-angular-hot-update.js +0 -239
  408. package/hmr/server/websocket-angular-hot-update.js.map +0 -1
  409. package/hmr/server/websocket-ns-m-finalize.d.ts +0 -22
  410. package/hmr/server/websocket-ns-m-finalize.js +0 -88
  411. package/hmr/server/websocket-ns-m-finalize.js.map +0 -1
  412. package/hmr/server/websocket-runtime-compat.d.ts +0 -19
  413. package/hmr/server/websocket-runtime-compat.js +0 -286
  414. package/hmr/server/websocket-runtime-compat.js.map +0 -1
  415. package/hmr/server/websocket-vue-sfc.d.ts +0 -27
  416. package/hmr/server/websocket-vue-sfc.js +0 -1117
  417. package/hmr/server/websocket-vue-sfc.js.map +0 -1
  418. package/transformers/NativeClass/index.d.ts +0 -2
  419. package/transformers/NativeClass/index.js +0 -222
  420. package/transformers/NativeClass/index.js.map +0 -1
  421. /package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.d.ts +0 -0
  422. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-decorator-ctor-parameters.d.ts +0 -0
  423. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.d.ts +0 -0
  424. /package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.d.ts +0 -0
@@ -1,3 +1,81 @@
1
+ import { getGlobalScope } from '../../../shared/runtime/global-scope.js';
2
+ // Opt-out flag for the apply-progress overlay (default: enabled).
3
+ // Driven by `NS_VITE_PROGRESS_OVERLAY=0` (or `false`/`off`/`no`) on the
4
+ // dev server; baked into the bundle via
5
+ // `__NS_HMR_PROGRESS_OVERLAY_ENABLED__` at build time. We collapse the
6
+ // build-time constant into a runtime boolean once so each call-site is
7
+ // a single property check rather than a try/typeof. Tests that re-run
8
+ // the angular client (via vitest) see `undefined` and default to
9
+ // enabled — matching the production dev-server experience.
10
+ const overlayEnabled = (() => {
11
+ // Define substitution does not reach this raw-served file — fall back to
12
+ // the globalThis seed planted by the entry's defines-seed module (which
13
+ // evaluates before this client is loaded) before defaulting to enabled.
14
+ try {
15
+ if (typeof __NS_HMR_PROGRESS_OVERLAY_ENABLED__ === 'boolean')
16
+ return __NS_HMR_PROGRESS_OVERLAY_ENABLED__;
17
+ }
18
+ catch { }
19
+ try {
20
+ const seeded = getGlobalScope().__NS_HMR_PROGRESS_OVERLAY_ENABLED__;
21
+ if (typeof seeded === 'boolean')
22
+ return seeded;
23
+ }
24
+ catch { }
25
+ return true;
26
+ })();
27
+ function getHmrOverlayApi() {
28
+ if (!overlayEnabled)
29
+ return null;
30
+ try {
31
+ return globalThis.__NS_HMR_DEV_OVERLAY__ || null;
32
+ }
33
+ catch { }
34
+ return null;
35
+ }
36
+ function setUpdateOverlayStage(stage, info) {
37
+ if (!overlayEnabled)
38
+ return;
39
+ try {
40
+ const api = getHmrOverlayApi();
41
+ if (api && typeof api.setUpdateStage === 'function') {
42
+ api.setUpdateStage(stage, info);
43
+ }
44
+ }
45
+ catch { }
46
+ }
47
+ function hideUpdateOverlay() {
48
+ if (!overlayEnabled)
49
+ return;
50
+ try {
51
+ const api = getHmrOverlayApi();
52
+ if (api && typeof api.hide === 'function') {
53
+ api.hide('hmr-applied');
54
+ }
55
+ }
56
+ catch { }
57
+ }
58
+ // Safe accessor for the build-time `__NS_ENV_VERBOSE__` constant.
59
+ //
60
+ // Vite replaces `__NS_ENV_VERBOSE__` at build time via `define`. In
61
+ // unit tests (vitest, plain ts-node, etc.) the identifier is
62
+ // undeclared at runtime, so a bare reference throws ReferenceError —
63
+ // `typeof` is the only safe way to probe it. We collapse the
64
+ // build-time constant into a runtime boolean once at module load and
65
+ // then short-circuit on `verbose` arguments at every call-site.
66
+ //
67
+ // Always order `verbose && envVerbose` so the runtime flag
68
+ // short-circuits the build-time constant; that prevents reference
69
+ // errors in test environments where the `define` substitution does
70
+ // not run.
71
+ const envVerbose = (() => {
72
+ try {
73
+ return typeof __NS_ENV_VERBOSE__ === 'boolean' ? __NS_ENV_VERBOSE__ : false;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ })();
1
79
  export function installAngularHmrClientHooks() {
2
80
  const g = globalThis;
3
81
  if (g.__NS_ANGULAR_HMR_CLIENT_INSTALLED__) {
@@ -5,16 +83,459 @@ export function installAngularHmrClientHooks() {
5
83
  }
6
84
  try {
7
85
  g.__NS_ANGULAR_HMR_CLIENT_INSTALLED__ = true;
8
- if (__NS_ENV_VERBOSE__) {
86
+ if (envVerbose) {
9
87
  console.log('[hmr-angular] client hooks installed');
10
88
  }
11
89
  }
12
90
  catch { }
13
91
  }
92
+ // HMR cycle serialization mutex.
93
+ //
94
+ // A back-to-back save (e.g., the user holds Cmd+S, or `runOnSave`
95
+ // saves a file twice during one tick) could overlap two HMR cycles:
96
+ // the first cycle's `import(entry)` would still be resolving when the
97
+ // second cycle's `__nsInvalidateModules` ran, leaving the registry in
98
+ // a half-evicted state and producing flaky "module already evaluated"
99
+ // errors.
100
+ //
101
+ // Each `handleAngularHotUpdateMessage` call publishes a promise to
102
+ // `inFlightHmrCycle` and awaits the previous publication before
103
+ // starting its own evict + import. The previous-cycle promise resolves
104
+ // regardless of success or failure (we always run `resolveCycle()` in
105
+ // `finally`), so a stuck cycle can't deadlock the queue. The mutex is
106
+ // intentionally process-wide and resets naturally on cold-boot or
107
+ // websocket reconnect (both blow away the JS realm).
108
+ let inFlightHmrCycle = null;
109
+ // Explicit module eviction.
110
+ //
111
+ // `__nsInvalidateModules` is a runtime-installed global that takes an
112
+ // array of canonical /ns/m/<rel> URLs and removes each one from V8's
113
+ // module registry (`g_moduleRegistry`). The runtime canonicalizer
114
+ // strips legacy `__ns_hmr__/<tag>/` and `__ns_boot__/b1/` segments
115
+ // before lookup, so the same URL evicts every cache entry historically
116
+ // created for that module — even if a stale tagged URL is still around.
117
+ //
118
+ // Soft-fails on older runtimes that don't expose the function. In that
119
+ // case we fall through to the legacy URL-versioning path and emit a
120
+ // one-time warning so the user knows the eviction protocol is
121
+ // unavailable.
122
+ // Dispatch one of Vite's standard HMR lifecycle events through the iOS
123
+ // runtime's `__NS_DISPATCH_HOT_EVENT__` global. The runtime owns the
124
+ // listener registry that `import.meta.hot.on(event, cb)` populates; this
125
+ // function is the producer side that lets the Angular HMR client emit the
126
+ // canonical Vite events (`vite:beforeUpdate`, `vite:afterUpdate`,
127
+ // `vite:beforeFullReload`, `vite:invalidate`, `vite:error`) at the right
128
+ // moments. User code that does
129
+ // `import.meta.hot.on('vite:beforeUpdate', cb)` only fires when this is
130
+ // called.
131
+ //
132
+ // Soft-fails on older runtimes that don't expose the dispatcher (the
133
+ // global is undefined; we no-op). Per-listener failures are swallowed
134
+ // inside the runtime, so a single bad listener can't break the HMR cycle.
135
+ function dispatchHotEvent(g, event, payload) {
136
+ try {
137
+ const fn = g && g.__NS_DISPATCH_HOT_EVENT__;
138
+ if (typeof fn === 'function') {
139
+ fn.call(g, event, payload);
140
+ }
141
+ }
142
+ catch (err) {
143
+ // Defensive: if the dispatcher itself throws, we'd already have
144
+ // no listeners (per-callback failures swallowed inside the
145
+ // runtime), so this is purely a safety net for runtime
146
+ // regressions. Log only under verbose to avoid noisy dev logs.
147
+ if (envVerbose) {
148
+ console.warn(`[ns-hmr][client] __NS_DISPATCH_HOT_EVENT__('${event}') threw:`, err?.message ?? err);
149
+ }
150
+ }
151
+ }
152
+ // Trigger a full app reload via the runtime's `__nsReloadDevApp` global,
153
+ // dispatching `vite:beforeFullReload` first so user-code listeners can
154
+ // react. Returns `true` if the reload was triggered, `false` if the
155
+ // runtime doesn't expose the API. Used both by `import.meta.hot.invalidate()`
156
+ // (handled directly inside the runtime) AND by the declined-module check
157
+ // below (where the JS layer makes the decision and asks for the reload).
158
+ function triggerFullReload(g, message) {
159
+ const fn = g && g.__nsReloadDevApp;
160
+ if (typeof fn !== 'function') {
161
+ return false;
162
+ }
163
+ dispatchHotEvent(g, 'vite:beforeFullReload', { message });
164
+ try {
165
+ // Fire-and-forget — `__nsReloadDevApp` returns a Promise, but we
166
+ // don't await it here because the JS realm is about to be torn
167
+ // down. The HMR cycle's caller treats this as terminal and
168
+ // short-circuits the rest of the cycle.
169
+ fn.call(g);
170
+ }
171
+ catch (err) {
172
+ if (envVerbose) {
173
+ console.warn('[ns-hmr][client] __nsReloadDevApp threw:', err?.message ?? err);
174
+ }
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+ // Check if any module being touched by this HMR update has called
180
+ // `import.meta.hot.decline()`. If so, per Vite spec we MUST do a full
181
+ // reload instead of a hot-update — declining means "I refuse to be
182
+ // hot-updated". Returns `true` if a full reload was triggered (caller
183
+ // should short-circuit the rest of the cycle).
184
+ //
185
+ // Older runtimes without `__nsHasDeclinedModule` skip the check entirely
186
+ // (legacy behaviour: HMR proceeds with the reboot, which is the same
187
+ // behaviour they had before this fix).
188
+ function checkDeclinedAndReload(g, msg, options) {
189
+ const checker = g && g.__nsHasDeclinedModule;
190
+ if (typeof checker !== 'function')
191
+ return false;
192
+ // `evictPaths` is the eviction set the dev server computed for this
193
+ // HMR cycle — every module that needs to be re-evaluated. If ANY of
194
+ // those modules declined HMR, the whole cycle has to convert to a
195
+ // full reload. Pass them through verbatim; the runtime canonicalizes
196
+ // internally when matching against `g_hotDeclined`.
197
+ const evictPaths = Array.isArray(msg?.evictPaths) ? msg.evictPaths : [];
198
+ let declined = false;
199
+ try {
200
+ declined = !!checker.call(g, evictPaths);
201
+ }
202
+ catch (err) {
203
+ if (options.verbose && envVerbose) {
204
+ console.warn('[ns-hmr][client] __nsHasDeclinedModule threw:', err?.message ?? err);
205
+ }
206
+ return false;
207
+ }
208
+ if (!declined)
209
+ return false;
210
+ const filePath = typeof msg?.path === 'string' ? msg.path : '<unknown>';
211
+ const message = `module declined HMR (file=${filePath}, evictPaths=${evictPaths.length})`;
212
+ console.info(`[ns-hmr][decline] ${message} — falling back to full reload`);
213
+ const reloaded = triggerFullReload(g, message);
214
+ if (!reloaded && options.verbose && envVerbose) {
215
+ console.warn('[ns-hmr][client] declined module detected but __nsReloadDevApp unavailable; HMR will proceed (older runtime)');
216
+ }
217
+ return reloaded;
218
+ }
219
+ // Drain `import.meta.hot.dispose(cb)` callbacks via the iOS runtime's
220
+ // `globalThis.__nsRunHmrDispose()` global before Angular's reboot.
221
+ //
222
+ // The runtime owns the dispose registry (`g_hotDispose` in
223
+ // `HMRSupport.mm`) — every call to `import.meta.hot.dispose(cb)` from
224
+ // user code lands there. This client just asks the runtime to drain.
225
+ // We pass NO key argument so the runtime drains every registered
226
+ // callback across every module, which matches the wholesale-reboot
227
+ // semantics of `__reboot_ng_modules__`: when Angular tears down its
228
+ // component tree and re-bootstraps, every module's accumulated side
229
+ // effects are conceptually being thrown away. (A future per-module
230
+ // HMR client could pass an explicit key list to limit the drain.)
231
+ //
232
+ // Older runtimes (before this hook shipped) silently no-op via the
233
+ // optional chain; in that case, dispose callbacks register but never
234
+ // fire, and the worker-specific safety net in `terminateTrackedWorkers`
235
+ // below still catches the worker pile-up case.
236
+ function runHmrDisposeCallbacks(g, options) {
237
+ let runner;
238
+ try {
239
+ const candidate = g && g.__nsRunHmrDispose;
240
+ if (typeof candidate === 'function') {
241
+ runner = candidate;
242
+ }
243
+ }
244
+ catch {
245
+ // `globalThis` access defensiveness — Proxy-wrapped globals can
246
+ // throw on property reads.
247
+ }
248
+ if (!runner) {
249
+ // Older runtime: the dispose callback registry exists in the
250
+ // native runtime but no JS-callable drain entrypoint is
251
+ // available. Silently no-op (worker terminator below still
252
+ // runs). The diagnostic message is gated to verbose so we
253
+ // don't nag users on every cycle of every project that hasn't
254
+ // adopted the new runtime yet.
255
+ if (options.verbose && envVerbose) {
256
+ console.info('[ns-hmr][client] runtime missing __nsRunHmrDispose; skipping import.meta.hot.dispose drain (upgrade @nativescript/ios for support)');
257
+ }
258
+ return;
259
+ }
260
+ let executed = null;
261
+ try {
262
+ const result = runner.call(g);
263
+ executed = typeof result === 'number' ? result : 0;
264
+ }
265
+ catch (err) {
266
+ // One bad disposer should already be swallowed inside the
267
+ // runtime per-callback try/catch. If the runtime itself throws
268
+ // (out-of-memory, isolate teardown race, etc.) we MUST NOT
269
+ // take down the HMR cycle.
270
+ if (options.verbose && envVerbose) {
271
+ console.warn('[ns-hmr][client] __nsRunHmrDispose threw:', err?.message ?? err);
272
+ }
273
+ return;
274
+ }
275
+ // Surface ONE concise log per HMR cycle that actually had disposers
276
+ // to run. Quiet on cycles where no module had registered any.
277
+ if (executed && executed > 0) {
278
+ console.info(`[ns-hmr][dispose] ran ${executed} import.meta.hot.dispose callback${executed === 1 ? '' : 's'} before reboot`);
279
+ }
280
+ }
281
+ // Terminate every live worker before Angular's reboot. Two-tier strategy:
282
+ //
283
+ // 1. PREFERRED — `globalThis.__nsTerminateAllWorkers()` (NS iOS runtime
284
+ // ≥ the version that ships `Worker::TerminateAllWorkersCallback`).
285
+ // Native code iterates `Caches::Workers` (the runtime's authoritative
286
+ // worker registry) and calls `WorkerWrapper::Terminate()` on each
287
+ // live entry. Universal: catches every worker regardless of how it
288
+ // was created — `new Worker(new URL(...))`, raw `Worker('~/x.js')`,
289
+ // dynamic-import-spawned workers, plugin-spawned workers, etc.
290
+ //
291
+ // 2. FALLBACK — drain `globalThis.__NS_HMR_WORKERS__` (a Set populated
292
+ // by `__nsHmrTrackWorker`, the helper that `workerHmrUrlPlugin`
293
+ // injects at the top of every dev module containing a
294
+ // `new Worker(new URL(...))` call). This keeps HMR worker cleanup
295
+ // working on older runtimes that don't yet expose
296
+ // `__nsTerminateAllWorkers`. It only catches workers spawned via
297
+ // the Vite-rewritten path, so the runtime API is strictly better
298
+ // coverage.
299
+ //
300
+ // Either way: producer-side wraps are still useful as diagnostic
301
+ // metadata (which user code spawned which worker), so we always clear
302
+ // the JS Set after termination — irrespective of which tier ran — so
303
+ // it doesn't grow unbounded across HMR cycles.
304
+ //
305
+ // Tolerant of:
306
+ // * Missing globals — non-worker apps have neither global; we no-op.
307
+ // * `terminate()` throwing — the runtime can throw on already-dead
308
+ // workers; we catch per-entry so subsequent terminations and the
309
+ // reboot itself still proceed.
310
+ // * Native API throwing — wrapped in try/catch so a runtime regression
311
+ // can't take down the HMR cycle; we degrade to the JS-Set fallback.
312
+ function terminateTrackedWorkers(g, options) {
313
+ let nativeApi;
314
+ try {
315
+ const candidate = g && g.__nsTerminateAllWorkers;
316
+ if (typeof candidate === 'function') {
317
+ nativeApi = candidate;
318
+ }
319
+ }
320
+ catch {
321
+ // Reading the global threw (extremely defensive — `globalThis`
322
+ // access shouldn't, but Proxy-wrapped globals can).
323
+ }
324
+ let nativeTerminated = null;
325
+ if (nativeApi) {
326
+ try {
327
+ const result = nativeApi.call(g);
328
+ // Native API returns the count of workers terminated as a
329
+ // number. Older betas may return undefined; treat any
330
+ // non-number as "ran successfully, count unknown".
331
+ nativeTerminated = typeof result === 'number' ? result : 0;
332
+ }
333
+ catch (err) {
334
+ if (options.verbose && envVerbose) {
335
+ console.warn('[ns-hmr][client] __nsTerminateAllWorkers threw; falling back to JS-tracked Set:', err?.message ?? err);
336
+ }
337
+ nativeApi = undefined; // force fallback below
338
+ }
339
+ }
340
+ // Always touch the JS-tracked Set so producer-side bookkeeping
341
+ // doesn't outlive its workers across HMR cycles. If the native API
342
+ // already terminated everything, this is just a `.clear()`. If the
343
+ // native API isn't available (older runtime), this is the actual
344
+ // cleanup path.
345
+ let workers;
346
+ try {
347
+ workers = g && g.__NS_HMR_WORKERS__;
348
+ }
349
+ catch {
350
+ // fall through with `workers === undefined`
351
+ }
352
+ let fallbackTerminated = 0;
353
+ let fallbackFailed = 0;
354
+ const fallbackTotal = workers && typeof workers.size === 'number' ? workers.size : 0;
355
+ if (!nativeApi && workers && fallbackTotal > 0) {
356
+ // No native API — drain the JS-tracked Set as the primary path.
357
+ for (const worker of workers) {
358
+ try {
359
+ if (worker && typeof worker.terminate === 'function') {
360
+ worker.terminate();
361
+ fallbackTerminated++;
362
+ }
363
+ }
364
+ catch {
365
+ fallbackFailed++;
366
+ }
367
+ }
368
+ }
369
+ if (workers) {
370
+ try {
371
+ workers.clear();
372
+ }
373
+ catch { }
374
+ }
375
+ // Surface ONE concise log per HMR cycle when there was actually work
376
+ // to do. This isn't gated on `verbose` so the user can see in the
377
+ // dev terminal which cleanup tier ran without flipping a flag —
378
+ // it's a single line per HMR-cycle-with-workers, never per cycle
379
+ // when no workers exist. Filter on "actually did something" so we
380
+ // stay quiet on no-worker apps and on cycles where neither tier
381
+ // found anything.
382
+ if (nativeApi && (nativeTerminated ?? 0) > 0) {
383
+ console.info(`[ns-hmr][workers] terminated ${nativeTerminated} via runtime __nsTerminateAllWorkers before reboot`);
384
+ }
385
+ else if (!nativeApi && fallbackTerminated > 0) {
386
+ console.info(`[ns-hmr][workers] terminated ${fallbackTerminated}/${fallbackTotal} via JS-tracked Set fallback before reboot (older runtime; consider upgrading @nativescript/ios for native API)`);
387
+ }
388
+ }
389
+ // Narrow an unknown eviction-list entry to a fetchable http(s) URL. Shared by
390
+ // the reboot path (refreshAngularBootstrapOptions) and the leaf re-import
391
+ // fallback so the two stay in lockstep on what counts as a valid evict URL.
392
+ const isHttpUrl = (u) => typeof u === 'string' && /^https?:\/\//.test(u);
393
+ function invalidateModules(urls, verbose) {
394
+ if (!urls || !urls.length)
395
+ return false;
396
+ const g = globalThis;
397
+ const fn = g.__nsInvalidateModules;
398
+ if (typeof fn !== 'function') {
399
+ console.warn(`[ns-hmr][client] runtime missing __nsInvalidateModules; falling back to legacy URL versioning. evict=${urls.length}`);
400
+ return false;
401
+ }
402
+ try {
403
+ if (verbose && envVerbose) {
404
+ console.info(`[ns-hmr][client] invalidateModules calling __nsInvalidateModules urls=${urls.length}`);
405
+ }
406
+ fn.call(null, urls);
407
+ if (verbose && envVerbose) {
408
+ console.info(`[ns-hmr][client] invalidateModules OK urls=${urls.length}`);
409
+ }
410
+ return true;
411
+ }
412
+ catch (error) {
413
+ // Real exception path — the runtime hook itself threw. Always
414
+ // surfaced for the same reason as the missing-hook warn above.
415
+ console.warn('[ns-hmr][client] __nsInvalidateModules threw', error?.message || error);
416
+ return false;
417
+ }
418
+ }
419
+ // Eviction-set size cap for the kickstart.
420
+ //
421
+ // `__NS_HMR_KICKSTART_MAX_URLS__` is a build-time literal injected
422
+ // by `helpers/global-defines.ts` (default 32). It is a pure
423
+ // performance gate: the kickstart never affects correctness, so
424
+ // skipping it for large fan-out edits simply reverts the runtime to
425
+ // sequential `HttpFetchText`. See the doc comment on
426
+ // `resolveHmrKickstartMaxUrls` (in global-defines.ts) for the
427
+ // rationale and the empirical numbers behind the default.
428
+ //
429
+ // The constant is read once at module load (the same pattern used
430
+ // for `overlayEnabled`) so each call site is a single inequality
431
+ // check rather than a try/typeof. Tests that re-import this module
432
+ // see `undefined` and fall back to the default.
433
+ const KICKSTART_DEFAULT_MAX_URLS = 32;
434
+ const kickstartMaxUrls = (() => {
435
+ // Define substitution does not reach this raw-served file — fall back to
436
+ // the globalThis seed planted by the entry's defines-seed module.
437
+ const normalize = (raw) => {
438
+ if (typeof raw !== 'number')
439
+ return null;
440
+ if (!Number.isFinite(raw))
441
+ return Number.POSITIVE_INFINITY;
442
+ if (raw < 0)
443
+ return null;
444
+ return Math.floor(raw);
445
+ };
446
+ try {
447
+ const substituted = normalize(__NS_HMR_KICKSTART_MAX_URLS__);
448
+ if (substituted !== null)
449
+ return substituted;
450
+ }
451
+ catch { }
452
+ try {
453
+ const seeded = normalize(getGlobalScope().__NS_HMR_KICKSTART_MAX_URLS__);
454
+ if (seeded !== null)
455
+ return seeded;
456
+ }
457
+ catch { }
458
+ return KICKSTART_DEFAULT_MAX_URLS;
459
+ })();
460
+ // Decide whether to run the kickstart for a given eviction set.
461
+ // Exported for unit testing — callers in this module use the inline
462
+ // `evictPaths.length` comparison below for a slightly cheaper
463
+ // hot-path.
464
+ //
465
+ // Semantics:
466
+ // - `0` urls → no work to do; skip (returns false).
467
+ // - `urls.length <= cap` → kickstart eligible (returns true).
468
+ // - `urls.length > cap` → skip kickstart (returns false). V8 falls
469
+ // back to per-module synchronous fetches inside its module walk.
470
+ // - `cap === 0` → kickstart disabled regardless of size.
471
+ // - `cap === Infinity` → kickstart always eligible (no cap).
472
+ export function shouldRunKickstart(urlCount, maxUrls = kickstartMaxUrls) {
473
+ if (!Number.isFinite(urlCount) || urlCount <= 0)
474
+ return false;
475
+ if (!Number.isFinite(maxUrls))
476
+ return maxUrls > 0;
477
+ if (maxUrls <= 0)
478
+ return false;
479
+ return urlCount <= maxUrls;
480
+ }
481
+ function kickstartHmrPrefetch(urls, verbose) {
482
+ if (!urls || !urls.length)
483
+ return null;
484
+ const g = globalThis;
485
+ const fn = g.__nsKickstartHmrPrefetch;
486
+ if (typeof fn !== 'function') {
487
+ if (verbose && envVerbose) {
488
+ console.info(`[hmr-angular] runtime missing __nsKickstartHmrPrefetch; serial fetches will be used. urls=${urls.length}`);
489
+ }
490
+ return null;
491
+ }
492
+ try {
493
+ // Concurrency cap of 16 matches the runtime's documented
494
+ // default for the kickstart BFS. Timeout at 10s tracks the
495
+ // runtime's `HttpFetchText` retry envelope so we don't
496
+ // hold the JS thread forever on a stalled dev server.
497
+ const result = fn.call(null, urls.slice(), { maxConcurrent: 16, timeoutMs: 10000 });
498
+ if (result && typeof result === 'object') {
499
+ const ok = !!result.ok;
500
+ const fetched = typeof result.fetched === 'number' ? result.fetched : 0;
501
+ const ms = typeof result.ms === 'number' ? result.ms : 0;
502
+ return { ok, fetched, ms };
503
+ }
504
+ return null;
505
+ }
506
+ catch (error) {
507
+ if (verbose && envVerbose) {
508
+ console.warn('[hmr-angular] __nsKickstartHmrPrefetch threw', error?.message || error);
509
+ }
510
+ return null;
511
+ }
512
+ }
14
513
  function getAngularBootstrapEntryCandidates(msg) {
15
- const root = typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__ ? __NS_APP_ROOT_VIRTUAL__ : '/src';
16
- const rawCandidates = Array.isArray(msg?.entryCandidates) && msg.entryCandidates.length ? msg.entryCandidates : [`${root}/main.ts`, `${root}/app.ts`];
17
- return rawCandidates.filter((entry) => typeof entry === 'string' && entry.startsWith('/'));
514
+ // Define substitution does not reach this raw-served file; prefer the
515
+ // globalThis seed from the entry's defines-seed module ('app/'-rooted
516
+ // projects would otherwise get the wrong '/src' default).
517
+ const root = (typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__) || (typeof getGlobalScope().__NS_APP_ROOT_VIRTUAL__ === 'string' && getGlobalScope().__NS_APP_ROOT_VIRTUAL__) || '/src';
518
+ // Server announces the canonical bootstrap entry as
519
+ // `importerEntry`, computed from `package.json#main`. Fall back to
520
+ // the legacy `entryCandidates` array (for older servers) and finally
521
+ // to a hard-coded list. All candidates must be project-relative
522
+ // posix paths (e.g. `/src/main.ts`) so they slot directly behind
523
+ // `${origin}/ns/m`.
524
+ const explicit = typeof msg?.importerEntry === 'string' && msg.importerEntry.startsWith('/') ? [msg.importerEntry] : [];
525
+ const legacy = Array.isArray(msg?.entryCandidates) && msg.entryCandidates.length ? msg.entryCandidates : [];
526
+ const fallback = [`${root}/main.ts`, `${root}/app.ts`];
527
+ const merged = [...explicit, ...legacy, ...fallback];
528
+ const seen = new Set();
529
+ const result = [];
530
+ for (const candidate of merged) {
531
+ if (typeof candidate !== 'string' || !candidate.startsWith('/'))
532
+ continue;
533
+ if (seen.has(candidate))
534
+ continue;
535
+ seen.add(candidate);
536
+ result.push(candidate);
537
+ }
538
+ return result;
18
539
  }
19
540
  async function importAngularBootstrapEntry(url) {
20
541
  const g = globalThis;
@@ -28,18 +549,113 @@ export async function refreshAngularBootstrapOptions(msg, options) {
28
549
  if (typeof g.__NS_UPDATE_ANGULAR_APP_OPTIONS__ !== 'function') {
29
550
  return;
30
551
  }
31
- const version = typeof msg?.version === 'number' ? msg.version : 0;
32
- if (!version) {
33
- return;
34
- }
35
552
  const originSource = typeof msg?.origin === 'string' && /^https?:\/\//.test(msg.origin) ? msg.origin : g.__NS_HTTP_ORIGIN__;
36
553
  if (typeof originSource !== 'string' || !/^https?:\/\//.test(originSource)) {
37
554
  return;
38
555
  }
39
556
  const origin = originSource.replace(/\/$/, '');
557
+ // Explicit eviction set takes precedence over URL versioning. The
558
+ // server walks the inverse-dep closure of the changed file
559
+ // (collectAngularEvictionUrls) and emits canonical /ns/m/<rel> URLs
560
+ // in `evictPaths`. We hand the list to the runtime before
561
+ // re-importing the entry; the runtime drops those entries from
562
+ // `g_moduleRegistry` so V8's subsequent `import(entry)` walks the
563
+ // dep graph and re-fetches ONLY those modules. Everything else stays
564
+ // hot in the cache.
565
+ const evictPaths = Array.isArray(msg?.evictPaths) ? msg.evictPaths.filter(isHttpUrl) : [];
566
+ // Diagnostic: log eviction set client-side so we can verify what
567
+ // the runtime is being asked to drop. Up to 32 entries are sampled
568
+ // so large constants edits don't swamp the console. Gated behind
569
+ // the standard `__NS_ENV_VERBOSE__` build define (`NS_VITE_VERBOSE`)
570
+ // so the lines stay silent on normal saves.
571
+ if (options.verbose && envVerbose) {
572
+ const _path = typeof msg?.path === 'string' ? msg.path : '(unknown)';
573
+ console.info(`[ns-hmr][client] received ns:angular-update path=${_path} evictPaths.length=${evictPaths.length} importerEntry=${msg?.importerEntry ?? '(none)'}`);
574
+ if (evictPaths.length) {
575
+ const sample = evictPaths.slice(0, 32);
576
+ console.info(`[ns-hmr][client] evictPaths firstN=`, sample);
577
+ if (evictPaths.length > sample.length) {
578
+ console.info(`[ns-hmr][client] evictPaths hidden=${evictPaths.length - sample.length}`);
579
+ }
580
+ }
581
+ }
582
+ // Drive the apply-progress overlay through the 'evicting' frame
583
+ // *before* the runtime invalidate call so the user sees the count
584
+ // even on the cheapest stage. Eviction count also doubles as a
585
+ // useful debug breadcrumb in the overlay's detail line — large
586
+ // counts (e.g. constants edits → 100+ importers) explain why a
587
+ // cycle takes longer than an HTML edit.
588
+ setUpdateOverlayStage('evicting', {
589
+ detail: evictPaths.length ? `Invalidating ${evictPaths.length} module${evictPaths.length === 1 ? '' : 's'}` : 'Invalidating module cache',
590
+ });
591
+ const evicted = invalidateModules(evictPaths, options.verbose);
592
+ if (options.verbose && envVerbose) {
593
+ console.info(`[ns-hmr][client] evict count=${evictPaths.length} ok=${evicted ? 'yes' : 'no'}`);
594
+ }
595
+ // Parallel HTTP prefetch for the freshly-evicted modules.
596
+ //
597
+ // Order matters here: the kickstart MUST run after the eviction so
598
+ // that it actually re-populates `g_prefetchCache` for the modules
599
+ // V8 will ask for. If we kickstarted before the eviction, every URL
600
+ // would skip on the "already cached" check (the walk consumes
601
+ // destructively but the prior non-evicted modules sit in V8's
602
+ // `g_moduleRegistry`, not the prefetch cache).
603
+ //
604
+ // We only kickstart when the eviction succeeded, because the
605
+ // fallback path (legacy URL-versioning, see `useStableUrls = evicted`
606
+ // below) doesn't go through canonical /ns/m URLs and the runtime
607
+ // would not match its prefetch cache lookups against the version-
608
+ // prefixed forms V8 will end up requesting. Better to skip the
609
+ // optimization than risk a confusing partial cache hit.
610
+ //
611
+ // `shouldRunKickstart` gates large eviction sets out of the
612
+ // parallel wave. A `.ts` file with deep inverse-dep fan-in
613
+ // (constants files, design-system enums) can produce a
614
+ // 200–300-URL closure that overwhelms Vite's single-threaded
615
+ // transform pipeline. The kickstart fan-out then makes the cycle
616
+ // 5–8× slower than letting V8 fetch sequentially as it walks the
617
+ // real forward path. The cap (default 32, override with
618
+ // `NS_VITE_KICKSTART_MAX_URLS`) keeps component-shaped closures
619
+ // (typically 5–30 URLs) on the fast path. Correctness is
620
+ // unaffected — V8's per-module synchronous fetch is the same fall-
621
+ // back code path the runtime has always used.
622
+ if (evicted && evictPaths.length > 0) {
623
+ if (shouldRunKickstart(evictPaths.length)) {
624
+ const result = kickstartHmrPrefetch(evictPaths, options.verbose);
625
+ if (options.verbose && envVerbose && result) {
626
+ console.info(`[ns-hmr][angular] kickstart urls=${evictPaths.length} fetched=${result.fetched} ok=${result.ok ? 'yes' : 'no'} ms=${result.ms}`);
627
+ }
628
+ }
629
+ else if (options.verbose && envVerbose) {
630
+ console.info(`[ns-hmr][angular] kickstart skipped urls=${evictPaths.length} cap=${Number.isFinite(kickstartMaxUrls) ? kickstartMaxUrls : 'Infinity'} (eviction set too large; falling back to sequential fetch)`);
631
+ }
632
+ }
633
+ // URL strategy:
634
+ //
635
+ // * `evicted=true` → modern runtime accepted the eviction; we
636
+ // re-import the entry under its STABLE canonical URL. V8's
637
+ // registry no longer holds the evicted modules so the import
638
+ // triggers fresh fetches for them; the rest of the graph is a
639
+ // cache hit.
640
+ // * `evicted=false` → either we have no eviction set (older server)
641
+ // or the runtime lacks `__nsInvalidateModules` (older runtime).
642
+ // Fall back to the legacy `__ns_hmr__/v<version>/` URL pattern so
643
+ // V8 sees a fresh URL and re-fetches the entry. The runtime
644
+ // canonicalizer collapses the path back to the stable key, which
645
+ // keeps cache identity consistent across saves.
646
+ const versionRaw = typeof msg?.version === 'number' ? msg.version : 0;
647
+ const useStableUrls = evicted;
40
648
  let lastError;
649
+ // 'reimporting' is the entry point for the long tail of an HMR
650
+ // cycle: V8 walks the freshly-evicted graph and the iOS runtime
651
+ // re-fetches each node from the dev server. We announce the stage
652
+ // once, before the loop, so the user sees a progress jump even when
653
+ // the import resolves on the first candidate (the common case).
654
+ setUpdateOverlayStage('reimporting', {
655
+ detail: 'Re-importing Angular bootstrap entry',
656
+ });
41
657
  for (const entry of getAngularBootstrapEntryCandidates(msg)) {
42
- const url = `${origin}/ns/m/__ns_hmr__/v${version}${entry}`;
658
+ const url = useStableUrls ? `${origin}/ns/m${entry}` : versionRaw ? `${origin}/ns/m/__ns_hmr__/v${versionRaw}${entry}` : `${origin}/ns/m${entry}`;
43
659
  try {
44
660
  g.__NS_ANGULAR_HMR_REGISTER_ONLY__ = true;
45
661
  await importAngularBootstrapEntry(url);
@@ -52,19 +668,83 @@ export async function refreshAngularBootstrapOptions(msg, options) {
52
668
  g.__NS_ANGULAR_HMR_REGISTER_ONLY__ = false;
53
669
  }
54
670
  }
55
- if (options.verbose && __NS_ENV_VERBOSE__ && lastError) {
56
- try {
57
- console.warn('[hmr-angular] failed to refresh Angular bootstrap entry', lastError?.message || lastError);
58
- }
59
- catch { }
671
+ if (options.verbose && envVerbose && lastError) {
672
+ console.warn('[hmr-angular] failed to refresh Angular bootstrap entry', lastError?.message || lastError);
60
673
  }
61
674
  }
62
675
  export async function handleAngularHotUpdateMessage(msg, options) {
63
676
  if (!msg || msg.type !== 'ns:angular-update') {
64
677
  return false;
65
678
  }
679
+ // Cycle mutex. See `inFlightHmrCycle` for why this is necessary. We
680
+ // publish `thisCycle` *before* awaiting the previous one so a third
681
+ // concurrent message sees the latest in-flight, not the stale
682
+ // previous. This serializes N concurrent updates into a single FIFO
683
+ // chain.
684
+ const previousCycle = inFlightHmrCycle;
685
+ let resolveCycle;
686
+ const thisCycle = new Promise((resolve) => {
687
+ resolveCycle = resolve;
688
+ });
689
+ inFlightHmrCycle = thisCycle;
690
+ if (previousCycle) {
691
+ try {
692
+ await previousCycle;
693
+ }
694
+ catch { }
695
+ }
696
+ // Single-line log lands in iOS device console as `CONSOLE INFO` so the user can
697
+ // correlate with the server's `[hmr-ws][update] ...` line and see
698
+ // where the save's wall time actually goes (refresh vs. reboot).
699
+ const t0 = Date.now();
700
+ let tAfterRefresh = t0;
701
+ let tEnd = t0;
702
+ const filePath = typeof msg?.path === 'string' ? msg.path : '<unknown>';
703
+ const emitTiming = (ok, errorMessage) => {
704
+ const refreshMs = Math.max(0, tAfterRefresh - t0);
705
+ const rebootMs = Math.max(0, tEnd - tAfterRefresh);
706
+ const totalMs = Math.max(0, tEnd - t0);
707
+ const status = ok ? 'ok' : 'FAILED';
708
+ const suffix = errorMessage ? ` error=${errorMessage}` : '';
709
+ console.info(`[ns-hmr][angular] ${status} file=${filePath} refresh=${refreshMs}ms reboot=${rebootMs}ms total=${totalMs}ms${suffix}`);
710
+ };
711
+ // Show the apply-progress overlay as soon as the
712
+ // mutex unblocks us. Posting this *after* the awaited
713
+ // previousCycle keeps each cycle's stages visually grouped in the
714
+ // overlay (cycle-1 finishes its 'complete' before cycle-2's
715
+ // 'received' overrides it).
716
+ //
717
+ // The detail line surfaces the changed file path so the user can
718
+ // confirm at a glance which save the overlay is reflecting.
719
+ const overlayDetail = filePath && filePath !== '<unknown>' ? `Updating ${filePath}` : 'Preparing update';
720
+ setUpdateOverlayStage('received', { detail: overlayDetail });
66
721
  try {
67
722
  const g = globalThis;
723
+ // Vite-spec entry: `vite:beforeUpdate` fires as soon as the
724
+ // client receives an update message, BEFORE any work begins
725
+ // (refresh, dispose, reboot). User listeners can use this to
726
+ // pause animations, cancel in-flight requests, etc. Payload
727
+ // matches Vite's `Update[]` shape loosely — `path` is the
728
+ // canonical changed file, `evictPaths` is the eviction
729
+ // closure the server already computed.
730
+ const updatePayload = {
731
+ type: 'js-update',
732
+ path: typeof msg?.path === 'string' ? msg.path : null,
733
+ evictPaths: Array.isArray(msg?.evictPaths) ? msg.evictPaths : [],
734
+ timestamp: t0,
735
+ };
736
+ dispatchHotEvent(g, 'vite:beforeUpdate', { updates: [updatePayload] });
737
+ // Declined-module short-circuit. If any updated module called
738
+ // `import.meta.hot.decline()`, we MUST do a full reload per
739
+ // Vite spec rather than the hot-update path. `triggerFullReload`
740
+ // also dispatches `vite:beforeFullReload`. The reload tears the
741
+ // JS realm down so we don't need to do any further bookkeeping
742
+ // here — return early.
743
+ if (checkDeclinedAndReload(g, msg, options)) {
744
+ tEnd = Date.now();
745
+ emitTiming(true);
746
+ return true;
747
+ }
68
748
  // Prefer __reboot_ng_modules__ — it properly disposes existing modules,
69
749
  // re-bootstraps Angular inside NgZone, and sets the root view internally.
70
750
  // __NS_ANGULAR_BOOTSTRAP__ is exposed as a secondary signal that the
@@ -72,10 +752,26 @@ export async function handleAngularHotUpdateMessage(msg, options) {
72
752
  // canonical reboot entry point.
73
753
  const reboot = g.__reboot_ng_modules__;
74
754
  if (typeof reboot === 'function') {
75
- if (options.verbose && __NS_ENV_VERBOSE__) {
76
- console.log('[hmr-angular] rebooting Angular modules via __reboot_ng_modules__');
755
+ if (options.verbose && envVerbose) {
756
+ console.info('[ns-hmr][client] calling __reboot_ng_modules__');
77
757
  }
758
+ // Pre-import reset: clear Angular's `GENERATED_COMP_IDS` map BEFORE
759
+ // the changed component modules are re-imported. If we don't, every
760
+ // touched component's `ɵɵdefineComponent` call hashes to the same
761
+ // id as its predecessor (selectors + className haven't changed),
762
+ // hits a "previousCompDefType !== componentDef.type" collision, and
763
+ // surfaces NG0912 "Component ID generation collision detected".
764
+ // The map is cleared again post-reboot as a safety net, but doing
765
+ // it once here suppresses the warning at the source.
766
+ try {
767
+ const preResetCompiled = g.__reset_ng_compiled_components__;
768
+ if (typeof preResetCompiled === 'function') {
769
+ preResetCompiled();
770
+ }
771
+ }
772
+ catch { }
78
773
  await refreshAngularBootstrapOptions(msg, options);
774
+ tAfterRefresh = Date.now();
79
775
  try {
80
776
  g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
81
777
  }
@@ -84,6 +780,51 @@ export async function handleAngularHotUpdateMessage(msg, options) {
84
780
  g.__NS_DEV_RESET_IN_PROGRESS__ = true;
85
781
  }
86
782
  catch { }
783
+ // Two-step pre-reboot cleanup. Order matters: dispose runs
784
+ // FIRST so user-code disposers see a still-live runtime
785
+ // (sockets, workers, etc.) and can issue graceful "I'm
786
+ // going away" messages; then we hard-terminate any worker
787
+ // the user didn't manually clean up.
788
+ //
789
+ // Step 1 — `import.meta.hot.dispose(cb)` callbacks
790
+ // ────────────────────────────────────────────────
791
+ // The NS iOS runtime exposes
792
+ // `globalThis.__nsRunHmrDispose()` (registered in
793
+ // `Worker.mm`'s sibling `HMRSupport.mm`). It walks
794
+ // `g_hotDispose` — the per-module callback registry that
795
+ // `import.meta.hot.dispose(cb)` populates — and invokes
796
+ // every callback with that module's `hot.data` object,
797
+ // matching the Vite spec. Soft-fails on older runtimes
798
+ // without the API (call is optional-chained).
799
+ //
800
+ // This is the standards-compliant cleanup hook every Vite
801
+ // user already knows. Apps register their intervals,
802
+ // listeners, sockets, store subscriptions, etc. via
803
+ // `import.meta.hot.dispose(...)` and they're guaranteed to
804
+ // fire before the next Angular reboot — no NS-specific
805
+ // knowledge required.
806
+ runHmrDisposeCallbacks(g, options);
807
+ // Step 2 — worker auto-terminate (defence in depth)
808
+ // ───────────────────────────────────────────────────
809
+ // Even with perfect dispose() coverage, workers spawned
810
+ // from constructors that re-run on Angular reboot (e.g.
811
+ // `new Worker(...)` inside `AppComponent`'s constructor)
812
+ // don't fire dispose for `app.component.ts` when only
813
+ // `login.component.html` changes — `app.component.ts`
814
+ // isn't being replaced, so its disposers don't run per
815
+ // Vite spec. The runtime API
816
+ // `__nsTerminateAllWorkers()` doesn't care about module
817
+ // boundaries; it just kills every live worker in
818
+ // `Caches::Workers`. Falls back to the
819
+ // `__NS_HMR_WORKERS__` JS Set on older runtimes.
820
+ terminateTrackedWorkers(g, options);
821
+ // 'rebooting' marks the long tail of the cycle: NgZone
822
+ // teardown, module re-instantiation, and resetRootView.
823
+ // The detail line shows refresh-so-far ms so a user can
824
+ // already see if the slow phase is re-import or reboot.
825
+ setUpdateOverlayStage('rebooting', {
826
+ detail: `Re-bootstrapping Angular (refresh ${Math.max(0, tAfterRefresh - t0)}ms)`,
827
+ });
87
828
  try {
88
829
  reboot(true);
89
830
  }
@@ -93,18 +834,115 @@ export async function handleAngularHotUpdateMessage(msg, options) {
93
834
  }
94
835
  catch { }
95
836
  }
837
+ tEnd = Date.now();
838
+ emitTiming(true);
839
+ // Vite-spec exit: `vite:afterUpdate` fires after the update
840
+ // has been successfully applied. User code can re-attach
841
+ // any state torn down by `vite:beforeUpdate` (resume
842
+ // animations, re-fetch, etc.). Same payload shape as
843
+ // `vite:beforeUpdate` so listeners can correlate.
844
+ dispatchHotEvent(g, 'vite:afterUpdate', { updates: [updatePayload] });
845
+ // 'complete' surfaces the wall-clock total so a user gets a
846
+ // single-glance confirmation matching the [ns-hmr][angular]
847
+ // log line in the terminal. The overlay auto-hides shortly
848
+ // after.
849
+ setUpdateOverlayStage('complete', {
850
+ detail: `Total ${Math.max(0, tEnd - t0)}ms`,
851
+ });
96
852
  return true;
97
853
  }
98
- if (options.verbose && __NS_ENV_VERBOSE__) {
854
+ // Reached only when the NgModule reboot path above was skipped — i.e.
855
+ // `g.__reboot_ng_modules__` was not a function at the time this update
856
+ // arrived, so no full re-bootstrap ran. (That handler is installed by
857
+ // `runNativeScriptAngularApp()`, so this is NOT about how the app boots;
858
+ // it covers the window where the handler isn't registered yet, or any
859
+ // update that never wires one up.) Fall back to the standard Vite
860
+ // leaf-module path: evict + re-import the changed module(s) so their
861
+ // top-level side-effects and `import.meta.hot.accept` callbacks re-run
862
+ // WITHOUT a full reboot. The server narrows leaf edits (no @Component/
863
+ // @Directive/@Pipe/@Injectable/@NgModule decorator) to just the changed
864
+ // file, so this is cheap. This is what makes self-accepting utility
865
+ // modules (e.g. a live-tuning module that posts values to native on
866
+ // evaluation) hot-reload at all — without it they were silently dropped
867
+ // here.
868
+ try {
869
+ const origin = (typeof g.__NS_HTTP_ORIGIN__ === 'string' ? g.__NS_HTTP_ORIGIN__ : '').replace(/\/$/, '');
870
+ let urls = Array.isArray(msg?.evictPaths) ? msg.evictPaths.filter(isHttpUrl) : [];
871
+ // Derive the canonical /ns/m/<rel> URL from msg.path when the server
872
+ // sent no evictPaths (V8 keys app modules by their extensionless URL).
873
+ if (!urls.length && typeof msg?.path === 'string' && origin) {
874
+ const rel = msg.path.replace(/^\//, '').replace(/\.(?:[mc]?[jt]sx?)$/i, '');
875
+ urls = [`${origin}/ns/m/${rel}`];
876
+ }
877
+ if (urls.length) {
878
+ setUpdateOverlayStage('reimporting', { detail: `Re-importing ${urls.length} module${urls.length === 1 ? '' : 's'}` });
879
+ invalidateModules(urls, options.verbose);
880
+ const nonce = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
881
+ g.__NS_HMR_IMPORT_NONCE__ = nonce;
882
+ let reimported = 0;
883
+ for (const u of urls) {
884
+ try {
885
+ await importAngularBootstrapEntry(`${u}${u.includes('?') ? '&' : '?'}hmr=${nonce}`);
886
+ reimported++;
887
+ }
888
+ catch (e) {
889
+ if (options.verbose) {
890
+ console.warn('[ns-hmr][leaf] re-import failed', u, (e && e.message) || e);
891
+ }
892
+ }
893
+ }
894
+ const ok = reimported > 0;
895
+ tEnd = Date.now();
896
+ emitTiming(ok, ok ? 'leaf-reimport' : 'leaf-reimport-failed');
897
+ hideUpdateOverlay();
898
+ return ok;
899
+ }
900
+ }
901
+ catch (e) {
902
+ if (options.verbose) {
903
+ console.warn('[ns-hmr][leaf] fallback threw', (e && e.message) || e);
904
+ }
905
+ }
906
+ if (options.verbose && envVerbose) {
99
907
  console.warn('[hmr-angular] No Angular HMR handler found. Ensure runNativeScriptAngularApp() has been called.');
100
908
  }
909
+ tEnd = Date.now();
910
+ emitTiming(false, 'no-reboot-handler');
911
+ // No reboot handler and nothing to re-import → hide the overlay rather
912
+ // than leaving stale progress on screen.
913
+ hideUpdateOverlay();
101
914
  }
102
915
  catch (error) {
103
916
  if (options.verbose) {
104
- try {
105
- console.warn('[hmr-angular] failed to handle update', (error && error.message) || error);
106
- }
107
- catch { }
917
+ console.warn('[hmr-angular] failed to handle update', (error && error.message) || error);
918
+ }
919
+ tEnd = Date.now();
920
+ const errorMessage = (error && error.message) || String(error);
921
+ emitTiming(false, errorMessage);
922
+ // Vite-spec error event — fires on any HMR cycle that throws.
923
+ // Payload shape mirrors Vite's `ErrorPayload`: `err: { message,
924
+ // stack? }` plus optional `path`. Soft-fails if no listeners.
925
+ dispatchHotEvent(globalThis, 'vite:error', {
926
+ type: 'error',
927
+ err: {
928
+ message: errorMessage,
929
+ stack: (error && error.stack) || undefined,
930
+ },
931
+ path: typeof msg?.path === 'string' ? msg.path : undefined,
932
+ });
933
+ // Errors → drop the overlay so the user isn't left looking
934
+ // at indefinite progress; the underlying error is already
935
+ // logged above (and surfaced via Vite's standard error
936
+ // pipeline through the websocket).
937
+ hideUpdateOverlay();
938
+ }
939
+ finally {
940
+ try {
941
+ resolveCycle();
942
+ }
943
+ catch { }
944
+ if (inFlightHmrCycle === thisCycle) {
945
+ inFlightHmrCycle = null;
108
946
  }
109
947
  }
110
948
  return true;