@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
@@ -1,143 +1,67 @@
1
1
  import { mergeConfig } from 'vite';
2
2
  import path from 'path';
3
3
  import fs from 'node:fs';
4
- import { createRequire } from 'node:module';
5
4
  import angular from '@analogjs/vite-plugin-angular';
6
5
  import { angularLinkerVitePlugin, angularLinkerVitePluginPost } from '../helpers/angular/angular-linker.js';
7
6
  import { synthesizeMissingInjectableFactories } from '../helpers/angular/synthesize-injectable-factories.js';
8
- import { ensureSharedAngularLinker, resolveAngularFileSystem } from '../helpers/angular/shared-linker.js';
7
+ import { getAngularLinkerFactory, runAngularLinker } from '../helpers/angular/shared-linker.js';
9
8
  import { inlineDecoratorComponentTemplates } from '../helpers/angular/inline-decorator-component-templates.js';
9
+ import { appendComponentHmrRegistration, findComponentClassNames, INJECTION_MARKER as HMR_REGISTER_MARKER } from '../helpers/angular/inject-component-hmr-registration.js';
10
+ import { injectAngularHmrViteIgnore } from '../helpers/angular/inject-hmr-vite-ignore.js';
10
11
  import { synthesizeDecoratorCtorParameters } from '../helpers/angular/synthesize-decorator-ctor-parameters.js';
11
- import { containsRealNgDeclare } from '../helpers/angular/util.js';
12
+ import { containsRealNgDeclare, stripJsComments } from '../helpers/angular/util.js';
12
13
  import { baseConfig } from './base.js';
13
14
  import { getCliFlags } from '../helpers/cli-flags.js';
14
15
  import { resolveRelativeToImportMeta } from '../helpers/import-meta-path.js';
15
- // Lazily import the Angular linker factory function. Used by chunk-level linkers
16
- // to create FRESH plugin instances per invocation (avoiding stale state in watch mode).
17
- let _cachedLinkerFactory = null;
18
- async function importLinkerFactory() {
19
- if (_cachedLinkerFactory)
20
- return _cachedLinkerFactory;
21
- const req = createRequire(process.cwd() + '/package.json');
22
- try {
23
- const linkerPath = req.resolve('@angular/compiler-cli/linker/babel');
24
- const linkerMod = await import(linkerPath);
25
- _cachedLinkerFactory = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin || null;
26
- }
27
- catch {
28
- try {
29
- const linkerMod = await import('@angular/compiler-cli/linker/babel');
30
- _cachedLinkerFactory = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin || null;
31
- }
32
- catch { }
33
- }
34
- return _cachedLinkerFactory;
16
+ import { resolveVerboseFlag } from '../helpers/logging.js';
17
+ function hasNgDeclarePartial(code) {
18
+ return code.indexOf('\u0275\u0275ngDeclare') !== -1 || code.indexOf('ɵɵngDeclare') !== -1 || code.indexOf('ngDeclare') !== -1;
35
19
  }
36
20
  // Rollup-level linker to guarantee Angular libraries are linked when included in the bundle graph.
37
21
  function angularRollupLinker(projectRoot) {
38
- let babel = null;
39
- let createLinker = null;
40
- let angularFileSystem = null;
41
22
  const FILTER = /node_modules\/(?:@angular|@nativescript\/angular)\/.*\.[mc]?js$/;
42
- async function ensureDeps() {
43
- if (babel && createLinker)
44
- return;
45
- try {
46
- const req = createRequire((projectRoot ? projectRoot + '/package.json' : import.meta.url));
47
- const babelPath = req.resolve('@babel/core');
48
- const linkerPath = req.resolve('@angular/compiler-cli/linker/babel');
49
- babel = await import(babelPath);
50
- const linkerMod = await import(linkerPath);
51
- createLinker = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin;
52
- }
53
- catch {
54
- try {
55
- babel = await import('@babel/core');
56
- }
57
- catch { }
58
- try {
59
- const linkerMod = await import('@angular/compiler-cli/linker/babel');
60
- createLinker = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin;
61
- }
62
- catch { }
63
- }
64
- if (!angularFileSystem) {
65
- angularFileSystem = await resolveAngularFileSystem(projectRoot);
66
- }
67
- }
23
+ const debug = process.env.VITE_DEBUG_LOGS === 'true' || process.env.VITE_DEBUG_LOGS === '1';
68
24
  return {
69
25
  name: 'ns-angular-linker-rollup',
70
26
  enforce: 'pre',
71
27
  apply: 'build',
72
28
  async load(id) {
73
- const debug = process.env.VITE_DEBUG_LOGS === 'true' || process.env.VITE_DEBUG_LOGS === '1';
74
- const cleanId = id.split('?', 1)[0];
29
+ const cleanId = normalizeAngularWatchPath(id);
75
30
  if (!FILTER.test(cleanId))
76
31
  return null;
77
32
  try {
78
- await ensureDeps();
79
- if (!babel || !createLinker)
80
- return null;
81
33
  const fs = await import('node:fs/promises');
82
34
  const code = await fs.readFile(cleanId, 'utf8');
83
35
  const forceLink = cleanId.includes('/node_modules/@nativescript/angular/') && cleanId.includes('polyfills');
84
36
  if (!code)
85
37
  return null;
86
- if (!forceLink && code.indexOf('\u0275\u0275ngDeclare') === -1 && code.indexOf('ɵɵngDeclare') === -1 && code.indexOf('ngDeclare') === -1)
38
+ if (!forceLink && !hasNgDeclarePartial(code))
87
39
  return null;
88
- const plugin = createLinker({ sourceMapping: false, fileSystem: angularFileSystem });
89
- if (debug) {
90
- try {
91
- console.log('[ns-angular-linker][rollup-load] linking', cleanId);
92
- }
93
- catch { }
94
- }
95
- const result = await babel.transformAsync(code, {
96
- filename: cleanId,
97
- configFile: false,
98
- babelrc: false,
99
- sourceMaps: false,
100
- compact: false,
101
- plugins: [plugin],
102
- });
103
- if (result?.code && result.code !== code) {
104
- return { code: result.code, map: null };
40
+ const linked = await runAngularLinker(code, { filename: cleanId, projectRoot, freshPlugin: true });
41
+ if (linked) {
42
+ if (debug)
43
+ console.log('[ns-angular-linker][rollup-load] linked', cleanId);
44
+ return { code: linked, map: null };
105
45
  }
106
46
  }
107
47
  catch { }
108
48
  return null;
109
49
  },
110
50
  async transform(code, id) {
111
- const debug = process.env.VITE_DEBUG_LOGS === 'true' || process.env.VITE_DEBUG_LOGS === '1';
112
- const cleanId = id.split('?', 1)[0];
51
+ const cleanId = normalizeAngularWatchPath(id);
113
52
  if (!FILTER.test(cleanId))
114
53
  return null;
115
54
  const forceLink = cleanId.includes('/node_modules/@nativescript/angular/') && cleanId.includes('polyfills');
116
55
  if (!code)
117
56
  return null;
118
- if (!forceLink && code.indexOf('\u0275\u0275ngDeclare') === -1 && code.indexOf('ɵɵngDeclare') === -1 && code.indexOf('ngDeclare') === -1)
119
- return null;
120
- await ensureDeps();
121
- if (!babel || !createLinker)
57
+ if (!forceLink && !hasNgDeclarePartial(code))
122
58
  return null;
123
59
  try {
124
- const plugin = createLinker({ sourceMapping: false, fileSystem: angularFileSystem });
125
- if (debug) {
126
- try {
127
- console.log('[ns-angular-linker][rollup] linking', cleanId);
128
- }
129
- catch { }
130
- }
131
- const result = await babel.transformAsync(code, {
132
- filename: cleanId,
133
- configFile: false,
134
- babelrc: false,
135
- sourceMaps: false,
136
- compact: false,
137
- plugins: [plugin],
138
- });
139
- if (result?.code && result.code !== code) {
140
- return { code: result.code, map: null };
60
+ const linked = await runAngularLinker(code, { filename: cleanId, projectRoot, freshPlugin: true });
61
+ if (linked) {
62
+ if (debug)
63
+ console.log('[ns-angular-linker][rollup] linked', cleanId);
64
+ return { code: linked, map: null };
141
65
  }
142
66
  }
143
67
  catch { }
@@ -148,6 +72,37 @@ function angularRollupLinker(projectRoot) {
148
72
  const cliFlags = getCliFlags();
149
73
  const isDevEnv = process.env.NODE_ENV !== 'production';
150
74
  const hmrActive = isDevEnv && !!cliFlags.hmr;
75
+ /**
76
+ * Web-style template HMR opt-out.
77
+ *
78
+ * When `hmrActive` is true we default to the in-place template-replacement
79
+ * pipeline (`liveReload: true` on Analog → `_enableHmr: true` on the Angular
80
+ * TS compiler → `angular:component-update` events served via the
81
+ * `/@ng/component` middleware → `ɵɵreplaceMetadata` on the live class).
82
+ * Setting `_enableHmr: true` also forces Analog to set
83
+ * `externalRuntimeStyles: true`, changing how component styles are emitted
84
+ * (URLs fetched lazily instead of inlined). On the web that's fine; for
85
+ * NativeScript it's a new code path that touches the SCSS / Tailwind
86
+ * pipeline and the iOS runtime's HTTP module loader.
87
+ *
88
+ * If a project hits a regression on day one (broken styling, unresolved
89
+ * `?ngcomp=` imports, etc.) the user can roll back to the legacy reboot
90
+ * pipeline without an upstream patch by setting the env flag. We honour
91
+ * `0`, `false`, `off`, and `no` (case-insensitive) as "off" — anything
92
+ * else (including unset) keeps the new path on.
93
+ *
94
+ * The flag is read once at module load, mirroring how `hmrActive` is
95
+ * computed, so a project can flip it via `cross-env` in their dev script
96
+ * and never look back.
97
+ */
98
+ const angularLiveReloadDisabledByEnv = (() => {
99
+ const raw = process.env.NS_VITE_ANGULAR_LIVE_RELOAD;
100
+ if (typeof raw !== 'string')
101
+ return false;
102
+ const v = raw.trim().toLowerCase();
103
+ return v === '0' || v === 'false' || v === 'off' || v === 'no';
104
+ })();
105
+ const hmrAngularLiveReload = hmrActive && !angularLiveReloadDisabledByEnv;
151
106
  const projectRoot = process.cwd();
152
107
  const tsConfigAppPath = path.resolve(projectRoot, 'tsconfig.app.json');
153
108
  const tsConfigPath = path.resolve(projectRoot, 'tsconfig.json');
@@ -193,15 +148,24 @@ function extractComponentAssetPaths(code, componentId) {
193
148
  const componentPath = normalizeAngularWatchPath(componentId);
194
149
  const assetPaths = new Set();
195
150
  const resolveAssetPath = (assetPath) => normalizeAngularWatchPath(path.resolve(path.dirname(componentPath), assetPath));
196
- const templateUrlMatch = code.match(/templateUrl\s*:\s*['"](.+?\.(?:html|htm))['"]/);
151
+ // Blank out `//` and `/* */` comments before scanning. The regexes below
152
+ // are intentionally simple (no JS parser) so they would otherwise match
153
+ // commented-out `templateUrl` / `styleUrls` declarations and register
154
+ // phantom asset deps via `addWatchFile`. In current Rolldown-Vite that
155
+ // also enrolls them as `_addedImports`, which `vite:import-analysis`
156
+ // then tries to resolve — surfacing as a misleading
157
+ // `Failed to resolve import "<file>" from "<importer>". Does the file
158
+ // exist?` pre-transform error if the file (predictably) doesn't exist.
159
+ const scanCode = stripJsComments(code);
160
+ const templateUrlMatch = scanCode.match(/templateUrl\s*:\s*['"](.+?\.(?:html|htm))['"]/);
197
161
  if (templateUrlMatch) {
198
162
  assetPaths.add(resolveAssetPath(templateUrlMatch[1]));
199
163
  }
200
- const styleUrlMatch = code.match(/styleUrl\s*:\s*['"](.+?\.(?:css|less|sass|scss))['"]/);
164
+ const styleUrlMatch = scanCode.match(/styleUrl\s*:\s*['"](.+?\.(?:css|less|sass|scss))['"]/);
201
165
  if (styleUrlMatch) {
202
166
  assetPaths.add(resolveAssetPath(styleUrlMatch[1]));
203
167
  }
204
- const styleUrlsMatch = code.match(/styleUrls\s*:\s*\[([\s\S]*?)\]/m);
168
+ const styleUrlsMatch = scanCode.match(/styleUrls\s*:\s*\[([\s\S]*?)\]/m);
205
169
  if (styleUrlsMatch) {
206
170
  for (const match of styleUrlsMatch[1].matchAll(/['"](.+?\.(?:css|less|sass|scss))['"]/g)) {
207
171
  assetPaths.add(resolveAssetPath(match[1]));
@@ -210,9 +174,18 @@ function extractComponentAssetPaths(code, componentId) {
210
174
  return Array.from(assetPaths);
211
175
  }
212
176
  function createAngularPlugins(opts) {
177
+ const verbose = resolveVerboseFlag();
213
178
  const assetToComponents = new Map();
214
179
  const componentToAssets = new Map();
215
180
  const pendingComponentInvalidations = new Set();
181
+ // Shared state between the `enforce: 'pre'` discovery plugin and the
182
+ // `enforce: 'post'` injection plugin. Maps a clean (no-querystring)
183
+ // .ts file id to the list of `@Component`-decorated class names found
184
+ // in its RAW TypeScript source. The pre plugin populates this map;
185
+ // the post plugin reads it to know which class names to register
186
+ // against the compiled output. Cleared on each pre-plugin invocation
187
+ // so renames or `@Component` removals don't leave stale entries.
188
+ const componentsByCleanId = new Map();
216
189
  const untrackComponentAssets = (componentPath) => {
217
190
  const previousAssets = componentToAssets.get(componentPath);
218
191
  if (!previousAssets)
@@ -240,7 +213,121 @@ function createAngularPlugins(opts) {
240
213
  assetToComponents.set(assetPath, components);
241
214
  }
242
215
  };
216
+ const isCandidateComponentTs = (cleanId) => {
217
+ if (!cleanId.endsWith('.ts'))
218
+ return false;
219
+ if (cleanId.includes('/node_modules/'))
220
+ return false;
221
+ if (cleanId.endsWith('.d.ts'))
222
+ return false;
223
+ if (cleanId.endsWith('.spec.ts') || cleanId.endsWith('.test.ts'))
224
+ return false;
225
+ return true;
226
+ };
243
227
  return [
228
+ // HMR self-registration runs in two phases:
229
+ //
230
+ // 1. (this plugin, `enforce: 'pre'`) Walk the raw TypeScript
231
+ // source for each user `.ts` file and record the names of
232
+ // any `@Component`-decorated classes into the shared
233
+ // `componentsByCleanId` map. Discovery has to happen on the
234
+ // raw source because the Analog Angular plugin rewrites
235
+ // `@Component(...)` into static metadata calls and removes
236
+ // the textual decorator pattern.
237
+ //
238
+ // 2. (`ns-component-hmr-register-post`, `enforce: 'post'`)
239
+ // After the Analog Angular plugin has compiled the file,
240
+ // append the global `__NS_HMR_REGISTER_COMPONENT__`
241
+ // registration calls keyed by the names recorded in step 1.
242
+ //
243
+ // Why the two-phase split: the Analog Angular plugin's `transform`
244
+ // returns its OWN regenerated compiled output (from its internal
245
+ // `outputFiles` cache populated at `buildStart`), discarding any
246
+ // code modifications applied earlier in the pipeline. We
247
+ // previously appended the registration snippet here, in the pre
248
+ // plugin, and the snippet was silently dropped — leaving the
249
+ // HMR class registry empty and `getFreshComponentClass` returning
250
+ // `found=false reason=no-registry` after every reboot.
251
+ //
252
+ // Placement notes that still apply:
253
+ // - `apply: 'serve'`: the registry runtime hook is dev-only;
254
+ // production builds never need self-registration.
255
+ // - Intentionally NOT gated on `hmrActive`. The injected
256
+ // snippet self-guards with
257
+ // `typeof globalThis.__NS_HMR_REGISTER_COMPONENT__ === 'function'`,
258
+ // so it's a no-op when the runtime hook isn't installed
259
+ // (e.g. `--no-hmr` users still serving modules through
260
+ // Vite). Gating the transform itself on `hmrActive` produced
261
+ // a silent failure mode where `--no-hmr` users got HMR
262
+ // machinery up but never got the registration calls
263
+ // injected, leaving the registry empty.
264
+ {
265
+ name: 'ns-component-hmr-register',
266
+ enforce: 'pre',
267
+ apply: 'serve',
268
+ transform(code, id) {
269
+ const cleanId = normalizeAngularWatchPath(id);
270
+ if (!isCandidateComponentTs(cleanId))
271
+ return null;
272
+ const componentNames = findComponentClassNames(code);
273
+ if (componentNames.length === 0) {
274
+ // Drop any stale entry from a previous transform
275
+ // pass; the file may have lost its `@Component`
276
+ // decorator across a rename/refactor.
277
+ componentsByCleanId.delete(cleanId);
278
+ return null;
279
+ }
280
+ componentsByCleanId.set(cleanId, componentNames);
281
+ if (verbose) {
282
+ console.info(`[ns-hmr][ns-component-hmr-register] discovered ${componentNames.length} component(s) in ${cleanId} (${componentNames.join(', ')})`);
283
+ }
284
+ // Discovery only — never modify the raw TS source. Any
285
+ // modification here is discarded by the Analog Angular
286
+ // plugin downstream; the actual snippet append happens
287
+ // in `ns-component-hmr-register-post`.
288
+ return null;
289
+ },
290
+ },
291
+ // Phase 2: append the HMR registration snippet to the compiled
292
+ // JS output produced by `@analogjs/vite-plugin-angular`. Runs
293
+ // `enforce: 'post'` so we see the post-Angular code (where the
294
+ // pre plugin's work would otherwise be discarded). Reads the
295
+ // component names recorded by the pre plugin via
296
+ // `componentsByCleanId`.
297
+ {
298
+ name: 'ns-component-hmr-register-post',
299
+ enforce: 'post',
300
+ apply: 'serve',
301
+ transform(code, id) {
302
+ const cleanId = normalizeAngularWatchPath(id);
303
+ if (!isCandidateComponentTs(cleanId))
304
+ return null;
305
+ const componentNames = componentsByCleanId.get(cleanId);
306
+ if (!componentNames || componentNames.length === 0)
307
+ return null;
308
+ // Idempotency: the Vite cache may replay the transform
309
+ // pipeline on cached modules. The marker comment is
310
+ // inserted by `appendComponentHmrRegistration` and
311
+ // guards against double-injection. We also defensively
312
+ // short-circuit here so we don't have to allocate the
313
+ // suffix string on every cached re-run.
314
+ if (code.includes(HMR_REGISTER_MARKER))
315
+ return null;
316
+ const result = appendComponentHmrRegistration(code, componentNames);
317
+ if (!result.code)
318
+ return null;
319
+ if (verbose) {
320
+ console.info(`[ns-hmr][ns-component-hmr-register-post] appended registrations for ${result.componentNames.length} component(s) in ${cleanId} (${result.componentNames.join(', ')})`);
321
+ }
322
+ // Returning `null` for the source map is acceptable for
323
+ // dev: lines 1..N (the original compiled body) keep
324
+ // the upstream Angular source map; the appended snippet
325
+ // is invisible to debuggers but harmless. For
326
+ // production-grade source maps a MagicString-based
327
+ // pass-through could be used; not required for HMR.
328
+ return { code: result.code, map: null };
329
+ },
330
+ },
244
331
  // Allow external html template changes to trigger hot reload: Make .ts files depend on their .html templates
245
332
  {
246
333
  name: 'angular-template-deps',
@@ -255,6 +342,15 @@ function createAngularPlugins(opts) {
255
342
  for (const assetPath of assetPaths) {
256
343
  this.addWatchFile(assetPath);
257
344
  }
345
+ // Diagnostic: surface which .ts files we've registered
346
+ // asset (template/styleUrls) dependencies for. This is
347
+ // the first fence the HTML→TS invalidation pipeline must
348
+ // pass — if we never see a [tracking] log for the
349
+ // component we're editing, the watcher will never fire
350
+ // and `pendingComponentInvalidations` stays empty.
351
+ if (verbose) {
352
+ console.info(`[ns-hmr][angular-template-deps] [tracking] componentKey=${componentKey} assets=${assetPaths.length} (${assetPaths.slice(0, 4).join(', ')})`);
353
+ }
258
354
  return null;
259
355
  },
260
356
  watchChange(id) {
@@ -269,12 +365,27 @@ function createAngularPlugins(opts) {
269
365
  for (const componentPath of components) {
270
366
  pendingComponentInvalidations.add(componentPath);
271
367
  }
368
+ if (verbose) {
369
+ console.info(`[ns-hmr][angular-template-deps] watchChange [via assetToComponents] changed=${changedPath} → invalidating ${components.size} component(s):`, Array.from(components));
370
+ }
272
371
  return;
273
372
  }
274
373
  if (/\.(html|htm)$/i.test(changedPath)) {
275
374
  const componentPath = changedPath.replace(/\.(html|htm)$/i, '.ts');
276
- if (fs.existsSync(resolveAngularWatchFilePath(componentPath))) {
277
- pendingComponentInvalidations.add(normalizeAngularWatchKey(componentPath));
375
+ const exists = fs.existsSync(resolveAngularWatchFilePath(componentPath));
376
+ if (exists) {
377
+ const componentKey = normalizeAngularWatchKey(componentPath);
378
+ pendingComponentInvalidations.add(componentKey);
379
+ if (verbose) {
380
+ console.info(`[ns-hmr][angular-template-deps] watchChange [via fallback .html→.ts] changed=${changedPath} componentKey=${componentKey}`);
381
+ }
382
+ }
383
+ else {
384
+ // Truly anomalous: a watched template/style asset has no companion
385
+ // `.ts` file, so we cannot route the edit through the Angular
386
+ // HMR pipeline. Always-on warning so it surfaces in non-verbose
387
+ // runs — silent fallback would hide a real wiring break.
388
+ console.warn(`[ns-hmr][angular-template-deps] watchChange [no companion .ts found] changed=${changedPath} expectedTs=${componentPath}`);
278
389
  }
279
390
  }
280
391
  },
@@ -283,6 +394,9 @@ function createAngularPlugins(opts) {
283
394
  if (!pendingComponentInvalidations.has(componentPath))
284
395
  return null;
285
396
  pendingComponentInvalidations.delete(componentPath);
397
+ if (verbose) {
398
+ console.info(`[ns-hmr][angular-template-deps] shouldTransformCachedModule → re-transform componentKey=${componentPath}`);
399
+ }
286
400
  return true;
287
401
  },
288
402
  },
@@ -295,11 +409,140 @@ function createAngularPlugins(opts) {
295
409
  experimental: {
296
410
  useAngularCompilationAPI: opts.useAngularCompilationAPI,
297
411
  },
298
- liveReload: false, // Disable live reload in favor of HMR
412
+ // `liveReload` is Analog's flag for Angular's web-style template HMR
413
+ // pipeline. When ON, Analog:
414
+ // 1. Sets `_enableHmr = true` on the TS compiler so each compiled
415
+ // component `.mjs` emits `<ClassName>_HmrLoad` plus an
416
+ // `import.meta.hot.on('angular:component-update', ...)` listener.
417
+ // 2. Registers a `/@ng/component?c=<id>` middleware that serves the
418
+ // recompiled template's `_UpdateMetadata` source on demand.
419
+ // 3. In `handleHotUpdate` for `.html` / `.css` / `.scss` edits, sends
420
+ // `server.ws.send('angular:component-update', { id, timestamp })`
421
+ // so the runtime can call `ɵɵreplaceMetadata` on the live class —
422
+ // swapping the template definition AND walking live LViews to
423
+ // recreate matching views in-place. NO Angular reboot, NO route
424
+ // navigation.
425
+ //
426
+ // Previously this was `false` because the NativeScript HMR pipeline
427
+ // rebuilt every save through `__reboot_ng_modules__`. That works but
428
+ // has two big downsides on mobile: every save triggers a full app
429
+ // reboot AND the captured route-history replay (see
430
+ // `@nativescript/angular`'s `hmr-route-replay.ts`), which produces 2-3
431
+ // re-navigations per save and re-instantiates the page component
432
+ // multiple times. For pure template/style edits — the common case —
433
+ // the web-style component-replacement path keeps the page mounted and
434
+ // only swaps the changed bits.
435
+ //
436
+ // We only enable this when HMR itself is active (`hmrActive` already
437
+ // gates on `--hmr` and `NODE_ENV !== 'production'`). With HMR off the
438
+ // behaviour is unchanged: production builds and `--no-hmr` dev still
439
+ // see `liveReload: false`, the compiler skips the HMR initializers,
440
+ // and the middleware is not registered.
441
+ //
442
+ // Important interactions to be aware of:
443
+ // - When `_enableHmr` is true, Analog also sets
444
+ // `externalRuntimeStyles = true`, changing how component styles
445
+ // are emitted (URLs fetched at runtime instead of inlined). For
446
+ // NativeScript, the existing CSS pipeline expects inlined styles;
447
+ // `ns-component-hmr-style-overrides` (below) restores the
448
+ // pre-HMR style-emission strategy so Tailwind/global SCSS
449
+ // packaging keeps working.
450
+ // - The runtime dynamic-import resolves the metadata URL relative
451
+ // to `import.meta.url`, e.g. `http://host:port/ns/m/<componentDir>/@ng/component?c=...`.
452
+ // Analog's middleware uses `req.url.includes('/@ng/component')`
453
+ // (substring match), so the request still matches even with the
454
+ // `/ns/m/` prefix in the path.
455
+ // - The NS HMR client (`packages/vite/hmr/client/index.ts`)
456
+ // forwards Vite's standard `{ type: 'custom', event, data }`
457
+ // payloads to `import.meta.hot.on` listeners via
458
+ // `__NS_DISPATCH_HOT_EVENT__`, and short-circuits before the
459
+ // reboot path for `angular:component-update`.
460
+ // - The NS server-side hot-update handler in
461
+ // `packages/vite/hmr/server/websocket.ts` skips its own
462
+ // `ns:angular-update` broadcast for `.html` / component-style
463
+ // edits so we don't double-fire (the reboot path stays for `.ts`
464
+ // edits).
465
+ // - To roll back to the legacy reboot-only pipeline (e.g. while
466
+ // debugging an `externalRuntimeStyles` regression), set
467
+ // `NS_VITE_ANGULAR_LIVE_RELOAD=0` in the dev environment.
468
+ // `hmrAngularLiveReload` collapses both gates above.
469
+ liveReload: hmrAngularLiveReload,
470
+ // NativeScript can't consume Angular's `externalRuntimeStyles`
471
+ // mode — that emits component styles as runtime-loaded
472
+ // `<hash>.css` URL references which only a browser CSSOM/`<link>`
473
+ // pipeline can resolve. We tell our patched Analog plugin (see
474
+ // `patches/@analogjs+vite-plugin-angular+2.3.1.patch` in
475
+ // downstream apps; upstream PR pending) to keep the legacy
476
+ // behavior of inlining preprocessed CSS strings into the
477
+ // component metadata's `styles: [...]` array, which the NS
478
+ // renderer's CSS-bundle pipeline already knows how to apply.
479
+ // The option is independent from `liveReload` (`_enableHmr`
480
+ // still wires up `ɵɵreplaceMetadata` for in-place template
481
+ // HMR) — we keep HMR ON, just opt out of the URL-style
482
+ // emission. Note the option lands as a no-op on stock
483
+ // Analog releases that haven't merged the patch; once
484
+ // merged, this will switch the compiler off external styles
485
+ // for NativeScript without affecting web builds.
486
+ //
487
+ // `@ts-expect-error` because the option is not yet in
488
+ // `@analogjs/vite-plugin-angular`'s published `PluginOptions`
489
+ // type. When the upstream PR (https://github.com/analogjs/analog)
490
+ // adds it, this `@ts-expect-error` will itself become an
491
+ // "unused suppression" error — that's the signal to remove
492
+ // this comment AND the surrounding explanation, and bump
493
+ // the Analog peer dep to the version that ships the type.
494
+ // @ts-expect-error -- pending upstream Analog type publish
495
+ externalRuntimeStyles: false,
299
496
  tsconfig: tsConfig,
497
+ // Forward Angular-style file replacements (e.g. `environment.ts`
498
+ // → `environment.stg.ts`) directly into the Analog plugin so the
499
+ // Angular TypeScript host (which reads source files via its own
500
+ // CompilerHost, bypassing Vite's load chain) sees the swap. This
501
+ // is the same hook Angular CLI uses for `fileReplacements` in
502
+ // `angular.json` build configurations.
503
+ fileReplacements: opts.fileReplacements ?? [],
504
+ workspaceRoot: opts.workspaceRoot ?? process.cwd(),
300
505
  }),
301
506
  // Post-phase linker to catch any declarations introduced after other transforms (including project code)
302
507
  angularLinkerVitePluginPost(process.cwd()),
508
+ // Re-inject the `/* @vite-ignore */` annotation onto Angular's HMR
509
+ // initializer dynamic imports.
510
+ //
511
+ // Angular's compiler emits each component's HMR loader as
512
+ // `import(/* @vite-ignore */ i0.ɵɵgetReplaceMetadataURL(...))` so
513
+ // Vite leaves the runtime-computed URL alone. The annotation goes
514
+ // missing somewhere in the post-Angular pipeline (empirically the
515
+ // linker's `compact: false` Babel pass loses it on some files),
516
+ // causing Vite's static analyzer to flag the import and rewrite
517
+ // the call site through its runtime resolver — which then throws
518
+ // `TypeError at ɵɵgetReplaceMetadataURL` on the iOS device because
519
+ // the resolver expects a statically known specifier.
520
+ //
521
+ // Running `enforce: 'post'` and `apply: 'serve'` here ensures we
522
+ // see the file AFTER every other transform has had its chance to
523
+ // strip comments, AND only in dev (the HMR initializer is gated
524
+ // behind `ngDevMode` and never runs in a production build, so the
525
+ // fix would be wasted work outside `serve`). The helper is
526
+ // idempotent: if the annotation is already present, the file is
527
+ // returned unchanged.
528
+ {
529
+ name: 'ns-angular-hmr-vite-ignore',
530
+ enforce: 'post',
531
+ apply: 'serve',
532
+ transform(code, id) {
533
+ if (!hmrAngularLiveReload)
534
+ return null;
535
+ const cleanId = normalizeAngularWatchPath(id);
536
+ if (!cleanId.endsWith('.ts') && !cleanId.endsWith('.mjs') && !cleanId.endsWith('.js'))
537
+ return null;
538
+ if (cleanId.includes('/node_modules/'))
539
+ return null;
540
+ const next = injectAngularHmrViteIgnore(code);
541
+ if (next === code)
542
+ return null;
543
+ return { code: next, map: null };
544
+ },
545
+ },
303
546
  // Enforce: fully disable dependency optimization during serve to avoid rxjs esm5 crawling and OOM
304
547
  {
305
548
  name: 'ns-disable-optimize-deps',
@@ -338,7 +581,7 @@ function createAngularPlugins(opts) {
338
581
  },
339
582
  ];
340
583
  }
341
- export const angularConfig = ({ mode }) => {
584
+ export const angularConfig = ({ mode, fileReplacements, workspaceRoot, }) => {
342
585
  const useSingleBundleDevOutput = mode === 'development' && !hmrActive;
343
586
  const plugins = createAngularPlugins({
344
587
  // Vite build --watch with the legacy Analog compilation path can regress
@@ -346,6 +589,8 @@ export const angularConfig = ({ mode }) => {
346
589
  // Restrict the newer compilation API to NativeScript's development no-HMR
347
590
  // flow, which is where the unstable rebuilds occur today.
348
591
  useAngularCompilationAPI: useSingleBundleDevOutput,
592
+ fileReplacements,
593
+ workspaceRoot,
349
594
  });
350
595
  const disableAnimations = true;
351
596
  //process.env.NS_DISABLE_NG_ANIMATIONS === "1" ||
@@ -366,17 +611,16 @@ export const angularConfig = ({ mode }) => {
366
611
  apply: 'build',
367
612
  enforce: 'post',
368
613
  async generateBundle(_options, bundle) {
614
+ function isNsAngularPolyfillsModule(id) {
615
+ return normalizeAngularWatchPath(id).includes('node_modules/@nativescript/angular/fesm2022/nativescript-angular-polyfills.mjs');
616
+ }
369
617
  function isNsAngularPolyfillsChunk(chunk) {
370
618
  if (!chunk || !chunk.modules)
371
619
  return false;
372
- return Object.keys(chunk.modules).some((m) => m.includes('node_modules/@nativescript/angular/fesm2022/nativescript-angular-polyfills.mjs'));
620
+ return Object.keys(chunk.modules).some(isNsAngularPolyfillsModule);
373
621
  }
374
- const { babel } = await ensureSharedAngularLinker(process.cwd());
375
- if (!babel)
376
- return;
377
- const fileSystem = await resolveAngularFileSystem(process.cwd());
378
- const linkerFactory = await importLinkerFactory();
379
- if (!linkerFactory)
622
+ const { babel, createLinker } = await getAngularLinkerFactory(process.cwd());
623
+ if (!babel || !createLinker)
380
624
  return;
381
625
  const strict = process.env.NS_STRICT_NG_LINK === '1' || process.env.NS_STRICT_NG_LINK === 'true';
382
626
  const enforceStrict = process.env.NS_STRICT_NG_LINK_ENFORCE === '1' || process.env.NS_STRICT_NG_LINK_ENFORCE === 'true';
@@ -396,26 +640,13 @@ export const angularConfig = ({ mode }) => {
396
640
  continue;
397
641
  const isNsPolyfills = isNsAngularPolyfillsChunk(chunk);
398
642
  try {
399
- // Create a FRESH linker plugin per chunk — the linker may have
400
- // internal state that becomes stale across watch-mode rebuild cycles.
401
- const freshPlugin = linkerFactory({ sourceMapping: false, fileSystem });
402
- const res = await babel.transformAsync(code, {
403
- filename: fileName,
404
- configFile: false,
405
- babelrc: false,
406
- sourceMaps: false,
407
- compact: false,
408
- plugins: [freshPlugin],
409
- });
410
- const linkedCode = res?.code && res.code !== code ? res.code : code;
411
- const finalCode = applyAngularChunkPostProcessing(linkedCode, { vendorInjectExport });
643
+ // Fresh plugin per chunk — avoids stale linker state across watch-mode rebuilds.
644
+ const linked = await runAngularLinker(code, { filename: fileName, projectRoot: process.cwd(), freshPlugin: true });
645
+ const finalCode = applyAngularChunkPostProcessing(linked ?? code, { vendorInjectExport });
412
646
  if (finalCode !== code) {
413
647
  chunk.code = finalCode;
414
648
  if (debug) {
415
- try {
416
- console.log('[ns-angular-linker][post] linked', fileName, isNsPolyfills ? '(polyfills)' : '');
417
- }
418
- catch { }
649
+ console.log('[ns-angular-linker][post] linked', fileName, isNsPolyfills ? '(polyfills)' : '');
419
650
  }
420
651
  }
421
652
  if (strict && !isNsPolyfills && containsRealNgDeclare(finalCode)) {
@@ -435,10 +666,10 @@ export const angularConfig = ({ mode }) => {
435
666
  const chunk = bundle[fname];
436
667
  const modIds = chunk?.modules
437
668
  ? Object.keys(chunk.modules)
438
- .filter((m) => /node_modules\/(?:@angular|@nativescript\/angular)\//.test(m))
669
+ .filter((m) => /node_modules\/(?:@angular|@nativescript\/angular)\//.test(normalizeAngularWatchPath(m)))
439
670
  .slice(0, 8)
440
671
  : [];
441
- const isOnlyPolyfills = modIds.length > 0 && modIds.every((m) => m.includes('node_modules/@nativescript/angular/fesm2022/nativescript-angular-polyfills.mjs'));
672
+ const isOnlyPolyfills = modIds.length > 0 && modIds.every(isNsAngularPolyfillsModule);
442
673
  if (isOnlyPolyfills)
443
674
  continue;
444
675
  let snippet = '';
@@ -461,10 +692,7 @@ export const angularConfig = ({ mode }) => {
461
692
  throw new Error(message);
462
693
  }
463
694
  else {
464
- try {
465
- console.warn(`[ns-angular-linker-post] ${message}`);
466
- }
467
- catch { }
695
+ console.warn(`[ns-angular-linker-post] ${message}`);
468
696
  }
469
697
  }
470
698
  },
@@ -484,37 +712,19 @@ export const angularConfig = ({ mode }) => {
484
712
  try {
485
713
  let transformed = code;
486
714
  if (containsRealNgDeclare(code)) {
487
- const { babel } = await ensureSharedAngularLinker(process.cwd());
488
- if (!babel)
715
+ const { babel, createLinker } = await getAngularLinkerFactory(process.cwd());
716
+ if (!babel || !createLinker)
489
717
  return null;
490
- const fileSystem = await resolveAngularFileSystem(process.cwd());
491
- // Fresh plugin per chunk avoids stale linker state across watch-mode rebuilds
492
- const freshPlugin = (await importLinkerFactory())?.({ sourceMapping: false, fileSystem });
493
- if (!freshPlugin)
494
- return null;
495
- const runLink = async (input) => {
496
- const result = await babel.transformAsync(input, {
497
- filename,
498
- configFile: false,
499
- babelrc: false,
500
- sourceMaps: false,
501
- compact: false,
502
- plugins: [freshPlugin],
503
- });
504
- return result?.code ?? input;
505
- };
506
- transformed = await runLink(code);
718
+ // Fresh plugin per pass — avoids stale linker state across watch-mode rebuilds.
719
+ transformed = (await runAngularLinker(code, { filename, projectRoot: process.cwd(), freshPlugin: true })) ?? code;
507
720
  if (containsRealNgDeclare(transformed)) {
508
- transformed = await runLink(transformed);
721
+ transformed = (await runAngularLinker(transformed, { filename, projectRoot: process.cwd(), freshPlugin: true })) ?? transformed;
509
722
  }
510
723
  }
511
724
  transformed = applyAngularChunkPostProcessing(transformed);
512
725
  if (transformed !== code) {
513
726
  if (debug) {
514
- try {
515
- console.log('[ns-angular-linker][render] linked', filename);
516
- }
517
- catch { }
727
+ console.log('[ns-angular-linker][render] linked', filename);
518
728
  }
519
729
  return { code: transformed, map: null };
520
730
  }