@nativescript/vite 8.0.0-alpha.1 → 8.0.0-alpha.11

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 (220) hide show
  1. package/configuration/angular.d.ts +1 -1
  2. package/configuration/angular.js +486 -140
  3. package/configuration/angular.js.map +1 -1
  4. package/configuration/base.js +159 -29
  5. package/configuration/base.js.map +1 -1
  6. package/configuration/javascript.js +3 -3
  7. package/configuration/javascript.js.map +1 -1
  8. package/configuration/solid.js +27 -0
  9. package/configuration/solid.js.map +1 -1
  10. package/configuration/typescript.js +4 -4
  11. package/configuration/typescript.js.map +1 -1
  12. package/helpers/angular/angular-linker.js +38 -42
  13. package/helpers/angular/angular-linker.js.map +1 -1
  14. package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
  15. package/helpers/angular/inject-component-hmr-registration.js +359 -0
  16. package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
  17. package/helpers/angular/inline-decorator-component-templates.d.ts +3 -0
  18. package/helpers/angular/inline-decorator-component-templates.js +400 -0
  19. package/helpers/angular/inline-decorator-component-templates.js.map +1 -0
  20. package/helpers/angular/shared-linker.d.ts +7 -0
  21. package/helpers/angular/shared-linker.js +37 -1
  22. package/helpers/angular/shared-linker.js.map +1 -1
  23. package/helpers/angular/synthesize-decorator-ctor-parameters.d.ts +1 -0
  24. package/helpers/angular/synthesize-decorator-ctor-parameters.js +256 -0
  25. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -0
  26. package/helpers/angular/synthesize-injectable-factories.d.ts +3 -0
  27. package/helpers/angular/synthesize-injectable-factories.js +414 -0
  28. package/helpers/angular/synthesize-injectable-factories.js.map +1 -0
  29. package/helpers/angular/util.d.ts +1 -0
  30. package/helpers/angular/util.js +88 -0
  31. package/helpers/angular/util.js.map +1 -1
  32. package/helpers/commonjs-plugins.d.ts +5 -2
  33. package/helpers/commonjs-plugins.js +126 -0
  34. package/helpers/commonjs-plugins.js.map +1 -1
  35. package/helpers/config-as-json.js +10 -0
  36. package/helpers/config-as-json.js.map +1 -1
  37. package/helpers/esbuild-platform-resolver.js +5 -5
  38. package/helpers/esbuild-platform-resolver.js.map +1 -1
  39. package/helpers/external-configs.d.ts +9 -1
  40. package/helpers/external-configs.js +31 -6
  41. package/helpers/external-configs.js.map +1 -1
  42. package/helpers/global-defines.d.ts +51 -0
  43. package/helpers/global-defines.js +77 -0
  44. package/helpers/global-defines.js.map +1 -1
  45. package/helpers/import-meta-path.d.ts +4 -0
  46. package/helpers/import-meta-path.js +5 -0
  47. package/helpers/import-meta-path.js.map +1 -0
  48. package/helpers/import-specifier.d.ts +1 -0
  49. package/helpers/import-specifier.js +18 -0
  50. package/helpers/import-specifier.js.map +1 -0
  51. package/helpers/logging.d.ts +1 -0
  52. package/helpers/logging.js +63 -3
  53. package/helpers/logging.js.map +1 -1
  54. package/helpers/main-entry.d.ts +5 -2
  55. package/helpers/main-entry.js +375 -116
  56. package/helpers/main-entry.js.map +1 -1
  57. package/helpers/nativeclass-transform.js +8 -127
  58. package/helpers/nativeclass-transform.js.map +1 -1
  59. package/helpers/nativeclass-transformer-plugin.d.ts +19 -1
  60. package/helpers/nativeclass-transformer-plugin.js +318 -36
  61. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  62. package/helpers/ns-core-url.d.ts +83 -0
  63. package/helpers/ns-core-url.js +167 -0
  64. package/helpers/ns-core-url.js.map +1 -0
  65. package/helpers/prelink-angular.js +1 -4
  66. package/helpers/prelink-angular.js.map +1 -1
  67. package/helpers/project.d.ts +35 -0
  68. package/helpers/project.js +120 -2
  69. package/helpers/project.js.map +1 -1
  70. package/helpers/ts-config-paths.js +50 -2
  71. package/helpers/ts-config-paths.js.map +1 -1
  72. package/helpers/workers.d.ts +20 -19
  73. package/helpers/workers.js +620 -3
  74. package/helpers/workers.js.map +1 -1
  75. package/hmr/client/css-handler.js +60 -19
  76. package/hmr/client/css-handler.js.map +1 -1
  77. package/hmr/client/hmr-pending-overlay.d.ts +27 -0
  78. package/hmr/client/hmr-pending-overlay.js +50 -0
  79. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  80. package/hmr/client/index.js +767 -24
  81. package/hmr/client/index.js.map +1 -1
  82. package/hmr/client/utils.d.ts +5 -0
  83. package/hmr/client/utils.js +283 -12
  84. package/hmr/client/utils.js.map +1 -1
  85. package/hmr/entry-runtime.d.ts +10 -0
  86. package/hmr/entry-runtime.js +330 -42
  87. package/hmr/entry-runtime.js.map +1 -1
  88. package/hmr/frameworks/angular/client/index.d.ts +3 -1
  89. package/hmr/frameworks/angular/client/index.js +821 -25
  90. package/hmr/frameworks/angular/client/index.js.map +1 -1
  91. package/hmr/frameworks/angular/server/linker.js +37 -6
  92. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  93. package/hmr/frameworks/angular/server/strategy.js +30 -6
  94. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  95. package/hmr/frameworks/typescript/server/strategy.js +8 -2
  96. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  97. package/hmr/frameworks/vue/client/index.js +18 -42
  98. package/hmr/frameworks/vue/client/index.js.map +1 -1
  99. package/hmr/helpers/ast-normalizer.js +22 -10
  100. package/hmr/helpers/ast-normalizer.js.map +1 -1
  101. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  102. package/hmr/helpers/cjs-named-exports.js +152 -0
  103. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  104. package/hmr/server/constants.d.ts +1 -0
  105. package/hmr/server/constants.js +14 -3
  106. package/hmr/server/constants.js.map +1 -1
  107. package/hmr/server/core-sanitize.d.ts +49 -2
  108. package/hmr/server/core-sanitize.js +267 -24
  109. package/hmr/server/core-sanitize.js.map +1 -1
  110. package/hmr/server/import-map.d.ts +65 -0
  111. package/hmr/server/import-map.js +222 -0
  112. package/hmr/server/import-map.js.map +1 -0
  113. package/hmr/server/index.d.ts +2 -1
  114. package/hmr/server/index.js.map +1 -1
  115. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  116. package/hmr/server/ns-core-cjs-shape.js +271 -0
  117. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  118. package/hmr/server/perf-instrumentation.d.ts +114 -0
  119. package/hmr/server/perf-instrumentation.js +195 -0
  120. package/hmr/server/perf-instrumentation.js.map +1 -0
  121. package/hmr/server/runtime-graph-filter.d.ts +5 -0
  122. package/hmr/server/runtime-graph-filter.js +21 -0
  123. package/hmr/server/runtime-graph-filter.js.map +1 -0
  124. package/hmr/server/shared-transform-request.d.ts +12 -0
  125. package/hmr/server/shared-transform-request.js +144 -0
  126. package/hmr/server/shared-transform-request.js.map +1 -0
  127. package/hmr/server/vite-plugin.d.ts +21 -1
  128. package/hmr/server/vite-plugin.js +461 -22
  129. package/hmr/server/vite-plugin.js.map +1 -1
  130. package/hmr/server/websocket-angular-entry.d.ts +2 -0
  131. package/hmr/server/websocket-angular-entry.js +68 -0
  132. package/hmr/server/websocket-angular-entry.js.map +1 -0
  133. package/hmr/server/websocket-angular-hot-update.d.ts +78 -0
  134. package/hmr/server/websocket-angular-hot-update.js +413 -0
  135. package/hmr/server/websocket-angular-hot-update.js.map +1 -0
  136. package/hmr/server/websocket-core-bridge.d.ts +21 -0
  137. package/hmr/server/websocket-core-bridge.js +357 -0
  138. package/hmr/server/websocket-core-bridge.js.map +1 -0
  139. package/hmr/server/websocket-graph-upsert.d.ts +21 -0
  140. package/hmr/server/websocket-graph-upsert.js +33 -0
  141. package/hmr/server/websocket-graph-upsert.js.map +1 -0
  142. package/hmr/server/websocket-hmr-pending.d.ts +43 -0
  143. package/hmr/server/websocket-hmr-pending.js +55 -0
  144. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  145. package/hmr/server/websocket-module-bindings.d.ts +6 -0
  146. package/hmr/server/websocket-module-bindings.js +471 -0
  147. package/hmr/server/websocket-module-bindings.js.map +1 -0
  148. package/hmr/server/websocket-module-specifiers.d.ts +101 -0
  149. package/hmr/server/websocket-module-specifiers.js +820 -0
  150. package/hmr/server/websocket-module-specifiers.js.map +1 -0
  151. package/hmr/server/websocket-ns-m-finalize.d.ts +22 -0
  152. package/hmr/server/websocket-ns-m-finalize.js +88 -0
  153. package/hmr/server/websocket-ns-m-finalize.js.map +1 -0
  154. package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
  155. package/hmr/server/websocket-ns-m-paths.js +92 -0
  156. package/hmr/server/websocket-ns-m-paths.js.map +1 -0
  157. package/hmr/server/websocket-ns-m-request.d.ts +45 -0
  158. package/hmr/server/websocket-ns-m-request.js +196 -0
  159. package/hmr/server/websocket-ns-m-request.js.map +1 -0
  160. package/hmr/server/websocket-runtime-compat.d.ts +19 -0
  161. package/hmr/server/websocket-runtime-compat.js +287 -0
  162. package/hmr/server/websocket-runtime-compat.js.map +1 -0
  163. package/hmr/server/websocket-served-module-helpers.d.ts +36 -0
  164. package/hmr/server/websocket-served-module-helpers.js +631 -0
  165. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  166. package/hmr/server/websocket-txn.d.ts +6 -0
  167. package/hmr/server/websocket-txn.js +45 -0
  168. package/hmr/server/websocket-txn.js.map +1 -0
  169. package/hmr/server/websocket-vendor-unifier.d.ts +10 -0
  170. package/hmr/server/websocket-vendor-unifier.js +51 -0
  171. package/hmr/server/websocket-vendor-unifier.js.map +1 -0
  172. package/hmr/server/websocket-vue-sfc.d.ts +27 -0
  173. package/hmr/server/websocket-vue-sfc.js +1069 -0
  174. package/hmr/server/websocket-vue-sfc.js.map +1 -0
  175. package/hmr/server/websocket.d.ts +26 -3
  176. package/hmr/server/websocket.js +2492 -798
  177. package/hmr/server/websocket.js.map +1 -1
  178. package/hmr/shared/package-classifier.d.ts +9 -0
  179. package/hmr/shared/package-classifier.js +58 -0
  180. package/hmr/shared/package-classifier.js.map +1 -0
  181. package/hmr/shared/runtime/boot-timeline.d.ts +17 -0
  182. package/hmr/shared/runtime/boot-timeline.js +51 -0
  183. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  184. package/hmr/shared/runtime/browser-runtime-contract.d.ts +64 -0
  185. package/hmr/shared/runtime/browser-runtime-contract.js +54 -0
  186. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -0
  187. package/hmr/shared/runtime/dev-overlay.d.ts +85 -0
  188. package/hmr/shared/runtime/dev-overlay.js +1236 -0
  189. package/hmr/shared/runtime/dev-overlay.js.map +1 -0
  190. package/hmr/shared/runtime/http-only-boot.d.ts +1 -0
  191. package/hmr/shared/runtime/http-only-boot.js +53 -6
  192. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  193. package/hmr/shared/runtime/module-provenance.d.ts +1 -0
  194. package/hmr/shared/runtime/module-provenance.js +63 -0
  195. package/hmr/shared/runtime/module-provenance.js.map +1 -0
  196. package/hmr/shared/runtime/platform-polyfills.d.ts +26 -0
  197. package/hmr/shared/runtime/platform-polyfills.js +122 -0
  198. package/hmr/shared/runtime/platform-polyfills.js.map +1 -0
  199. package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
  200. package/hmr/shared/runtime/root-placeholder.js +552 -82
  201. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  202. package/hmr/shared/runtime/session-bootstrap.d.ts +1 -0
  203. package/hmr/shared/runtime/session-bootstrap.js +195 -0
  204. package/hmr/shared/runtime/session-bootstrap.js.map +1 -0
  205. package/hmr/shared/runtime/vendor-bootstrap.js +52 -15
  206. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  207. package/hmr/shared/vendor/manifest.d.ts +37 -0
  208. package/hmr/shared/vendor/manifest.js +677 -57
  209. package/hmr/shared/vendor/manifest.js.map +1 -1
  210. package/hmr/shared/vendor/registry.js +104 -7
  211. package/hmr/shared/vendor/registry.js.map +1 -1
  212. package/index.d.ts +1 -0
  213. package/index.js +5 -0
  214. package/index.js.map +1 -1
  215. package/package.json +14 -2
  216. package/runtime/core-aliases-early.js +94 -67
  217. package/runtime/core-aliases-early.js.map +1 -1
  218. package/shims/solid-jsx-runtime.d.ts +7 -0
  219. package/shims/solid-jsx-runtime.js +17 -0
  220. package/shims/solid-jsx-runtime.js.map +1 -0
@@ -4,14 +4,42 @@ import fs from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import angular from '@analogjs/vite-plugin-angular';
6
6
  import { angularLinkerVitePlugin, angularLinkerVitePluginPost } from '../helpers/angular/angular-linker.js';
7
- import { ensureSharedAngularLinker } from '../helpers/angular/shared-linker.js';
8
- import { containsRealNgDeclare } from '../helpers/angular/util.js';
7
+ import { synthesizeMissingInjectableFactories } from '../helpers/angular/synthesize-injectable-factories.js';
8
+ import { ensureSharedAngularLinker, resolveAngularFileSystem } from '../helpers/angular/shared-linker.js';
9
+ import { inlineDecoratorComponentTemplates } from '../helpers/angular/inline-decorator-component-templates.js';
10
+ import { appendComponentHmrRegistration, findComponentClassNames, INJECTION_MARKER as HMR_REGISTER_MARKER } from '../helpers/angular/inject-component-hmr-registration.js';
11
+ import { synthesizeDecoratorCtorParameters } from '../helpers/angular/synthesize-decorator-ctor-parameters.js';
12
+ import { containsRealNgDeclare, stripJsComments } from '../helpers/angular/util.js';
9
13
  import { baseConfig } from './base.js';
10
14
  import { getCliFlags } from '../helpers/cli-flags.js';
15
+ import { resolveRelativeToImportMeta } from '../helpers/import-meta-path.js';
16
+ import { resolveVerboseFlag } from '../helpers/logging.js';
17
+ // Lazily import the Angular linker factory function. Used by chunk-level linkers
18
+ // to create FRESH plugin instances per invocation (avoiding stale state in watch mode).
19
+ let _cachedLinkerFactory = null;
20
+ async function importLinkerFactory() {
21
+ if (_cachedLinkerFactory)
22
+ return _cachedLinkerFactory;
23
+ const req = createRequire(process.cwd() + '/package.json');
24
+ try {
25
+ const linkerPath = req.resolve('@angular/compiler-cli/linker/babel');
26
+ const linkerMod = await import(linkerPath);
27
+ _cachedLinkerFactory = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin || null;
28
+ }
29
+ catch {
30
+ try {
31
+ const linkerMod = await import('@angular/compiler-cli/linker/babel');
32
+ _cachedLinkerFactory = linkerMod.createLinkerPlugin || linkerMod.createEs2015LinkerPlugin || null;
33
+ }
34
+ catch { }
35
+ }
36
+ return _cachedLinkerFactory;
37
+ }
11
38
  // Rollup-level linker to guarantee Angular libraries are linked when included in the bundle graph.
12
39
  function angularRollupLinker(projectRoot) {
13
40
  let babel = null;
14
41
  let createLinker = null;
42
+ let angularFileSystem = null;
15
43
  const FILTER = /node_modules\/(?:@angular|@nativescript\/angular)\/.*\.[mc]?js$/;
16
44
  async function ensureDeps() {
17
45
  if (babel && createLinker)
@@ -35,6 +63,9 @@ function angularRollupLinker(projectRoot) {
35
63
  }
36
64
  catch { }
37
65
  }
66
+ if (!angularFileSystem) {
67
+ angularFileSystem = await resolveAngularFileSystem(projectRoot);
68
+ }
38
69
  }
39
70
  return {
40
71
  name: 'ns-angular-linker-rollup',
@@ -56,12 +87,9 @@ function angularRollupLinker(projectRoot) {
56
87
  return null;
57
88
  if (!forceLink && code.indexOf('\u0275\u0275ngDeclare') === -1 && code.indexOf('ɵɵngDeclare') === -1 && code.indexOf('ngDeclare') === -1)
58
89
  return null;
59
- const plugin = createLinker({ sourceMapping: false });
90
+ const plugin = createLinker({ sourceMapping: false, fileSystem: angularFileSystem });
60
91
  if (debug) {
61
- try {
62
- console.log('[ns-angular-linker][rollup-load] linking', cleanId);
63
- }
64
- catch { }
92
+ console.log('[ns-angular-linker][rollup-load] linking', cleanId);
65
93
  }
66
94
  const result = await babel.transformAsync(code, {
67
95
  filename: cleanId,
@@ -92,12 +120,9 @@ function angularRollupLinker(projectRoot) {
92
120
  if (!babel || !createLinker)
93
121
  return null;
94
122
  try {
95
- const plugin = createLinker({ sourceMapping: false });
123
+ const plugin = createLinker({ sourceMapping: false, fileSystem: angularFileSystem });
96
124
  if (debug) {
97
- try {
98
- console.log('[ns-angular-linker][rollup] linking', cleanId);
99
- }
100
- catch { }
125
+ console.log('[ns-angular-linker][rollup] linking', cleanId);
101
126
  }
102
127
  const result = await babel.transformAsync(code, {
103
128
  filename: cleanId,
@@ -126,76 +151,367 @@ let tsConfig = tsConfigAppPath;
126
151
  if (!fs.existsSync(tsConfigAppPath) && fs.existsSync(tsConfigPath)) {
127
152
  tsConfig = tsConfigPath;
128
153
  }
129
- const plugins = [
130
- // Allow external html template changes to trigger hot reload: Make .ts files depend on their .html templates
131
- {
132
- name: 'angular-template-deps',
133
- transform(code, id) {
134
- // For .ts files that reference templateUrl, add the .html file as a dependency
135
- if (id.endsWith('.ts') && code.includes('templateUrl')) {
136
- const templateUrlMatch = code.match(/templateUrl:\s*['"]\.\/(.*?\.html)['"]/);
137
- if (templateUrlMatch) {
138
- const htmlPath = path.resolve(path.dirname(id), templateUrlMatch[1]);
139
- // Add the HTML file as a dependency so Vite watches it
140
- this.addWatchFile(htmlPath);
141
- }
154
+ function normalizeAngularWatchPath(filePath) {
155
+ return filePath
156
+ .split('?', 1)[0]
157
+ .replace(/\\/g, '/')
158
+ .replace(/^file:\/\//, '');
159
+ }
160
+ function normalizeAngularWatchKey(filePath) {
161
+ const normalizedPath = normalizeAngularWatchPath(filePath);
162
+ const fileSystemPath = normalizedPath.startsWith('/@fs/') ? normalizedPath.slice('/@fs'.length) : normalizedPath;
163
+ const normalizedProjectRoot = projectRoot.replace(/\\/g, '/').replace(/\/$/, '');
164
+ if (normalizedProjectRoot && fileSystemPath.startsWith(normalizedProjectRoot)) {
165
+ const relative = fileSystemPath.slice(normalizedProjectRoot.length);
166
+ return relative.startsWith('/') ? relative : `/${relative}`;
167
+ }
168
+ return fileSystemPath;
169
+ }
170
+ function getAngularWatchKeys(filePath) {
171
+ const normalizedPath = normalizeAngularWatchPath(filePath);
172
+ const keys = new Set();
173
+ keys.add(normalizedPath);
174
+ keys.add(normalizeAngularWatchKey(normalizedPath));
175
+ return Array.from(keys).filter(Boolean);
176
+ }
177
+ function resolveAngularWatchFilePath(filePath) {
178
+ const normalizedPath = normalizeAngularWatchPath(filePath);
179
+ const fileSystemPath = normalizedPath.startsWith('/@fs/') ? normalizedPath.slice('/@fs'.length) : normalizedPath;
180
+ if (path.isAbsolute(fileSystemPath) && fs.existsSync(fileSystemPath)) {
181
+ return fileSystemPath;
182
+ }
183
+ if (fileSystemPath.startsWith('/')) {
184
+ return path.resolve(projectRoot, `.${fileSystemPath}`);
185
+ }
186
+ return path.resolve(projectRoot, fileSystemPath);
187
+ }
188
+ function extractComponentAssetPaths(code, componentId) {
189
+ const componentPath = normalizeAngularWatchPath(componentId);
190
+ const assetPaths = new Set();
191
+ const resolveAssetPath = (assetPath) => normalizeAngularWatchPath(path.resolve(path.dirname(componentPath), assetPath));
192
+ // Blank out `//` and `/* */` comments before scanning. The regexes below
193
+ // are intentionally simple (no JS parser) so they would otherwise match
194
+ // commented-out `templateUrl` / `styleUrls` declarations and register
195
+ // phantom asset deps via `addWatchFile`. In current Rolldown-Vite that
196
+ // also enrolls them as `_addedImports`, which `vite:import-analysis`
197
+ // then tries to resolve — surfacing as a misleading
198
+ // `Failed to resolve import "<file>" from "<importer>". Does the file
199
+ // exist?` pre-transform error if the file (predictably) doesn't exist.
200
+ const scanCode = stripJsComments(code);
201
+ const templateUrlMatch = scanCode.match(/templateUrl\s*:\s*['"](.+?\.(?:html|htm))['"]/);
202
+ if (templateUrlMatch) {
203
+ assetPaths.add(resolveAssetPath(templateUrlMatch[1]));
204
+ }
205
+ const styleUrlMatch = scanCode.match(/styleUrl\s*:\s*['"](.+?\.(?:css|less|sass|scss))['"]/);
206
+ if (styleUrlMatch) {
207
+ assetPaths.add(resolveAssetPath(styleUrlMatch[1]));
208
+ }
209
+ const styleUrlsMatch = scanCode.match(/styleUrls\s*:\s*\[([\s\S]*?)\]/m);
210
+ if (styleUrlsMatch) {
211
+ for (const match of styleUrlsMatch[1].matchAll(/['"](.+?\.(?:css|less|sass|scss))['"]/g)) {
212
+ assetPaths.add(resolveAssetPath(match[1]));
213
+ }
214
+ }
215
+ return Array.from(assetPaths);
216
+ }
217
+ function createAngularPlugins(opts) {
218
+ const verbose = resolveVerboseFlag();
219
+ const assetToComponents = new Map();
220
+ const componentToAssets = new Map();
221
+ const pendingComponentInvalidations = new Set();
222
+ // Shared state between the `enforce: 'pre'` discovery plugin and the
223
+ // `enforce: 'post'` injection plugin. Maps a clean (no-querystring)
224
+ // .ts file id to the list of `@Component`-decorated class names found
225
+ // in its RAW TypeScript source. The pre plugin populates this map;
226
+ // the post plugin reads it to know which class names to register
227
+ // against the compiled output. Cleared on each pre-plugin invocation
228
+ // so renames or `@Component` removals don't leave stale entries.
229
+ const componentsByCleanId = new Map();
230
+ const untrackComponentAssets = (componentPath) => {
231
+ const previousAssets = componentToAssets.get(componentPath);
232
+ if (!previousAssets)
233
+ return;
234
+ for (const assetPath of previousAssets) {
235
+ const components = assetToComponents.get(assetPath);
236
+ if (!components)
237
+ continue;
238
+ components.delete(componentPath);
239
+ if (components.size === 0) {
240
+ assetToComponents.delete(assetPath);
142
241
  }
143
- return null;
242
+ }
243
+ componentToAssets.delete(componentPath);
244
+ };
245
+ const trackComponentAssets = (componentPath, assetPaths) => {
246
+ untrackComponentAssets(componentPath);
247
+ if (assetPaths.length === 0)
248
+ return;
249
+ const normalizedAssets = new Set(assetPaths.flatMap((assetPath) => getAngularWatchKeys(assetPath)));
250
+ componentToAssets.set(componentPath, normalizedAssets);
251
+ for (const assetPath of normalizedAssets) {
252
+ const components = assetToComponents.get(assetPath) || new Set();
253
+ components.add(componentPath);
254
+ assetToComponents.set(assetPath, components);
255
+ }
256
+ };
257
+ const isCandidateComponentTs = (cleanId) => {
258
+ if (!cleanId.endsWith('.ts'))
259
+ return false;
260
+ if (cleanId.includes('/node_modules/'))
261
+ return false;
262
+ if (cleanId.endsWith('.d.ts'))
263
+ return false;
264
+ if (cleanId.endsWith('.spec.ts') || cleanId.endsWith('.test.ts'))
265
+ return false;
266
+ return true;
267
+ };
268
+ return [
269
+ // HMR self-registration runs in two phases:
270
+ //
271
+ // 1. (this plugin, `enforce: 'pre'`) Walk the raw TypeScript
272
+ // source for each user `.ts` file and record the names of
273
+ // any `@Component`-decorated classes into the shared
274
+ // `componentsByCleanId` map. Discovery has to happen on the
275
+ // raw source because the Analog Angular plugin rewrites
276
+ // `@Component(...)` into static metadata calls and removes
277
+ // the textual decorator pattern.
278
+ //
279
+ // 2. (`ns-component-hmr-register-post`, `enforce: 'post'`)
280
+ // After the Analog Angular plugin has compiled the file,
281
+ // append the global `__NS_HMR_REGISTER_COMPONENT__`
282
+ // registration calls keyed by the names recorded in step 1.
283
+ //
284
+ // Why the two-phase split: the Analog Angular plugin's `transform`
285
+ // returns its OWN regenerated compiled output (from its internal
286
+ // `outputFiles` cache populated at `buildStart`), discarding any
287
+ // code modifications applied earlier in the pipeline. We
288
+ // previously appended the registration snippet here, in the pre
289
+ // plugin, and the snippet was silently dropped — leaving the
290
+ // HMR class registry empty and `getFreshComponentClass` returning
291
+ // `found=false reason=no-registry` after every reboot.
292
+ //
293
+ // Placement notes that still apply:
294
+ // - `apply: 'serve'`: the registry runtime hook is dev-only;
295
+ // production builds never need self-registration.
296
+ // - Intentionally NOT gated on `hmrActive`. The injected
297
+ // snippet self-guards with
298
+ // `typeof globalThis.__NS_HMR_REGISTER_COMPONENT__ === 'function'`,
299
+ // so it's a no-op when the runtime hook isn't installed
300
+ // (e.g. `--no-hmr` users still serving modules through
301
+ // Vite). Gating the transform itself on `hmrActive` produced
302
+ // a silent failure mode where `--no-hmr` users got HMR
303
+ // machinery up but never got the registration calls
304
+ // injected, leaving the registry empty.
305
+ {
306
+ name: 'ns-component-hmr-register',
307
+ enforce: 'pre',
308
+ apply: 'serve',
309
+ transform(code, id) {
310
+ const cleanId = id.split('?', 1)[0];
311
+ if (!isCandidateComponentTs(cleanId))
312
+ return null;
313
+ const componentNames = findComponentClassNames(code);
314
+ if (componentNames.length === 0) {
315
+ // Drop any stale entry from a previous transform
316
+ // pass; the file may have lost its `@Component`
317
+ // decorator across a rename/refactor.
318
+ componentsByCleanId.delete(cleanId);
319
+ return null;
320
+ }
321
+ componentsByCleanId.set(cleanId, componentNames);
322
+ if (verbose) {
323
+ console.info(`[ns-hmr-diag][ns-component-hmr-register] discovered ${componentNames.length} component(s) in ${cleanId} (${componentNames.join(', ')})`);
324
+ }
325
+ // Discovery only — never modify the raw TS source. Any
326
+ // modification here is discarded by the Analog Angular
327
+ // plugin downstream; the actual snippet append happens
328
+ // in `ns-component-hmr-register-post`.
329
+ return null;
330
+ },
144
331
  },
145
- },
146
- // Transform Angular partial declarations in node_modules to avoid runtime JIT
147
- // Pass the project root so linker deps resolve from the app, not the plugin package.
148
- angularLinkerVitePlugin(process.cwd()),
149
- // Simplify: rely on Vite pre plugin (load/transform) for linking; Rollup safety net disabled unless re-enabled later
150
- // angularRollupLinker(process.cwd()),
151
- angular({
152
- liveReload: false, // Disable live reload in favor of HMR
153
- tsconfig: tsConfig,
154
- }),
155
- // Post-phase linker to catch any declarations introduced after other transforms (including project code)
156
- angularLinkerVitePluginPost(process.cwd()),
157
- // Enforce: fully disable dependency optimization during serve to avoid rxjs esm5 crawling and OOM
158
- {
159
- name: 'ns-disable-optimize-deps',
160
- enforce: 'post',
161
- apply: 'serve',
162
- config(userConfig) {
163
- const od = userConfig?.optimizeDeps || {};
164
- const prevExclude = Array.isArray(od.exclude) ? od.exclude : [];
165
- const exclude = new Set(prevExclude);
166
- ['@nativescript/core', 'rxjs', '@valor/nativescript-websockets', 'set-value', 'react', 'react-reconciler', 'react-nativescript'].forEach((x) => exclude.add(x));
167
- return {
168
- optimizeDeps: {
169
- noDiscovery: true,
170
- entries: [],
171
- include: [],
172
- exclude: Array.from(exclude),
173
- rolldownOptions: {
174
- ...(od.rolldownOptions || {}),
175
- plugins: [],
176
- },
177
- },
178
- };
332
+ // Phase 2: append the HMR registration snippet to the compiled
333
+ // JS output produced by `@analogjs/vite-plugin-angular`. Runs
334
+ // `enforce: 'post'` so we see the post-Angular code (where the
335
+ // pre plugin's work would otherwise be discarded). Reads the
336
+ // component names recorded by the pre plugin via
337
+ // `componentsByCleanId`.
338
+ {
339
+ name: 'ns-component-hmr-register-post',
340
+ enforce: 'post',
341
+ apply: 'serve',
342
+ transform(code, id) {
343
+ const cleanId = id.split('?', 1)[0];
344
+ if (!isCandidateComponentTs(cleanId))
345
+ return null;
346
+ const componentNames = componentsByCleanId.get(cleanId);
347
+ if (!componentNames || componentNames.length === 0)
348
+ return null;
349
+ // Idempotency: the Vite cache may replay the transform
350
+ // pipeline on cached modules. The marker comment is
351
+ // inserted by `appendComponentHmrRegistration` and
352
+ // guards against double-injection. We also defensively
353
+ // short-circuit here so we don't have to allocate the
354
+ // suffix string on every cached re-run.
355
+ if (code.includes(HMR_REGISTER_MARKER))
356
+ return null;
357
+ const result = appendComponentHmrRegistration(code, componentNames);
358
+ if (!result.code)
359
+ return null;
360
+ if (verbose) {
361
+ console.info(`[ns-hmr-diag][ns-component-hmr-register-post] appended registrations for ${result.componentNames.length} component(s) in ${cleanId} (${result.componentNames.join(', ')})`);
362
+ }
363
+ // Returning `null` for the source map is acceptable for
364
+ // dev: lines 1..N (the original compiled body) keep
365
+ // the upstream Angular source map; the appended snippet
366
+ // is invisible to debuggers but harmless. For
367
+ // production-grade source maps a MagicString-based
368
+ // pass-through could be used; not required for HMR.
369
+ return { code: result.code, map: null };
370
+ },
371
+ },
372
+ // Allow external html template changes to trigger hot reload: Make .ts files depend on their .html templates
373
+ {
374
+ name: 'angular-template-deps',
375
+ enforce: 'pre',
376
+ transform(code, id) {
377
+ const componentPath = normalizeAngularWatchPath(id);
378
+ const componentKey = normalizeAngularWatchKey(id);
379
+ if (!componentKey.endsWith('.ts'))
380
+ return null;
381
+ const assetPaths = extractComponentAssetPaths(code, componentPath);
382
+ trackComponentAssets(componentKey, assetPaths);
383
+ for (const assetPath of assetPaths) {
384
+ this.addWatchFile(assetPath);
385
+ }
386
+ // Diagnostic: surface which .ts files we've registered
387
+ // asset (template/styleUrls) dependencies for. This is
388
+ // the first fence the HTML→TS invalidation pipeline must
389
+ // pass — if we never see a [tracking] log for the
390
+ // component we're editing, the watcher will never fire
391
+ // and `pendingComponentInvalidations` stays empty.
392
+ if (verbose) {
393
+ console.info(`[ns-hmr-diag][angular-template-deps] [tracking] componentKey=${componentKey} assets=${assetPaths.length} (${assetPaths.slice(0, 4).join(', ')})`);
394
+ }
395
+ return null;
396
+ },
397
+ watchChange(id) {
398
+ const changedPath = normalizeAngularWatchPath(id);
399
+ const components = new Set();
400
+ for (const assetKey of getAngularWatchKeys(changedPath)) {
401
+ for (const componentPath of assetToComponents.get(assetKey) || []) {
402
+ components.add(componentPath);
403
+ }
404
+ }
405
+ if (components?.size) {
406
+ for (const componentPath of components) {
407
+ pendingComponentInvalidations.add(componentPath);
408
+ }
409
+ if (verbose) {
410
+ console.info(`[ns-hmr-diag][angular-template-deps] watchChange [via assetToComponents] changed=${changedPath} → invalidating ${components.size} component(s):`, Array.from(components));
411
+ }
412
+ return;
413
+ }
414
+ if (/\.(html|htm)$/i.test(changedPath)) {
415
+ const componentPath = changedPath.replace(/\.(html|htm)$/i, '.ts');
416
+ const exists = fs.existsSync(resolveAngularWatchFilePath(componentPath));
417
+ if (exists) {
418
+ const componentKey = normalizeAngularWatchKey(componentPath);
419
+ pendingComponentInvalidations.add(componentKey);
420
+ if (verbose) {
421
+ console.info(`[ns-hmr-diag][angular-template-deps] watchChange [via fallback .html→.ts] changed=${changedPath} componentKey=${componentKey}`);
422
+ }
423
+ }
424
+ else if (verbose) {
425
+ console.info(`[ns-hmr-diag][angular-template-deps] watchChange [no companion .ts found] changed=${changedPath} expectedTs=${componentPath}`);
426
+ }
427
+ }
428
+ },
429
+ shouldTransformCachedModule({ id }) {
430
+ const componentPath = normalizeAngularWatchKey(id);
431
+ if (!pendingComponentInvalidations.has(componentPath))
432
+ return null;
433
+ pendingComponentInvalidations.delete(componentPath);
434
+ if (verbose) {
435
+ console.info(`[ns-hmr-diag][angular-template-deps] shouldTransformCachedModule → re-transform componentKey=${componentPath}`);
436
+ }
437
+ return true;
438
+ },
179
439
  },
180
- configResolved(resolved) {
181
- const deps = (resolved.optimizeDeps || (resolved.optimizeDeps = {}));
182
- deps.noDiscovery = true;
183
- deps.entries = [];
184
- deps.include = [];
185
- const exclude = new Set(Array.isArray(deps.exclude) ? deps.exclude : []);
186
- ['@nativescript/core', 'rxjs', '@valor/nativescript-websockets', 'set-value', 'react', 'react-reconciler', 'react-nativescript'].forEach((x) => exclude.add(x));
187
- deps.exclude = Array.from(exclude);
188
- const rolldownOptions = (deps.rolldownOptions || (deps.rolldownOptions = {}));
189
- rolldownOptions.plugins = [];
440
+ // Transform Angular partial declarations in node_modules to avoid runtime JIT
441
+ // Pass the project root so linker deps resolve from the app, not the plugin package.
442
+ angularLinkerVitePlugin(process.cwd()),
443
+ // Simplify: rely on Vite pre plugin (load/transform) for linking; Rollup safety net disabled unless re-enabled later
444
+ // angularRollupLinker(process.cwd()),
445
+ ...angular({
446
+ experimental: {
447
+ useAngularCompilationAPI: opts.useAngularCompilationAPI,
448
+ },
449
+ liveReload: false, // Disable live reload in favor of HMR
450
+ tsconfig: tsConfig,
451
+ }),
452
+ // Post-phase linker to catch any declarations introduced after other transforms (including project code)
453
+ angularLinkerVitePluginPost(process.cwd()),
454
+ // Enforce: fully disable dependency optimization during serve to avoid rxjs esm5 crawling and OOM
455
+ {
456
+ name: 'ns-disable-optimize-deps',
457
+ enforce: 'post',
458
+ apply: 'serve',
459
+ config(userConfig) {
460
+ const od = userConfig?.optimizeDeps || {};
461
+ const prevExclude = Array.isArray(od.exclude) ? od.exclude : [];
462
+ const exclude = new Set(prevExclude);
463
+ ['@nativescript/core', 'rxjs', '@valor/nativescript-websockets', 'set-value', 'react', 'react-reconciler', 'react-nativescript'].forEach((x) => exclude.add(x));
464
+ return {
465
+ optimizeDeps: {
466
+ noDiscovery: true,
467
+ entries: [],
468
+ include: [],
469
+ exclude: Array.from(exclude),
470
+ rolldownOptions: {
471
+ ...(od.rolldownOptions || {}),
472
+ plugins: [],
473
+ },
474
+ },
475
+ };
476
+ },
477
+ configResolved(resolved) {
478
+ const resolvedConfig = resolved;
479
+ const deps = (resolvedConfig.optimizeDeps || (resolvedConfig.optimizeDeps = {}));
480
+ deps.noDiscovery = true;
481
+ deps.entries = [];
482
+ deps.include = [];
483
+ const exclude = new Set(Array.isArray(deps.exclude) ? deps.exclude : []);
484
+ ['@nativescript/core', 'rxjs', '@valor/nativescript-websockets', 'set-value', 'react', 'react-reconciler', 'react-nativescript'].forEach((x) => exclude.add(x));
485
+ deps.exclude = Array.from(exclude);
486
+ const rolldownOptions = (deps.rolldownOptions || (deps.rolldownOptions = {}));
487
+ rolldownOptions.plugins = [];
488
+ },
190
489
  },
191
- },
192
- ];
490
+ ];
491
+ }
193
492
  export const angularConfig = ({ mode }) => {
493
+ const useSingleBundleDevOutput = mode === 'development' && !hmrActive;
494
+ const plugins = createAngularPlugins({
495
+ // Vite build --watch with the legacy Analog compilation path can regress
496
+ // Angular app sources from Ivy output back to decorator emit on rebuild.
497
+ // Restrict the newer compilation API to NativeScript's development no-HMR
498
+ // flow, which is where the unstable rebuilds occur today.
499
+ useAngularCompilationAPI: useSingleBundleDevOutput,
500
+ });
194
501
  const disableAnimations = true;
195
502
  //process.env.NS_DISABLE_NG_ANIMATIONS === "1" ||
196
503
  //process.env.NS_DISABLE_NG_ANIMATIONS === "true";
197
504
  // Post-link emitted chunks to catch any remaining partial declarations that slipped through
198
505
  // due to plugin order or external transforms.
506
+ const applyAngularChunkPostProcessing = (code, options = {}) => {
507
+ const codeWithInjectables = synthesizeMissingInjectableFactories(code, {
508
+ vendorInjectExport: options.vendorInjectExport,
509
+ });
510
+ const codeWithCtorParameters = synthesizeDecoratorCtorParameters(codeWithInjectables);
511
+ return inlineDecoratorComponentTemplates(codeWithCtorParameters, {
512
+ projectRoot: process.cwd(),
513
+ });
514
+ };
199
515
  const postLinker = {
200
516
  name: 'ns-angular-linker-post',
201
517
  apply: 'build',
@@ -206,13 +522,22 @@ export const angularConfig = ({ mode }) => {
206
522
  return false;
207
523
  return Object.keys(chunk.modules).some((m) => m.includes('node_modules/@nativescript/angular/fesm2022/nativescript-angular-polyfills.mjs'));
208
524
  }
209
- const { babel, linkerPlugin } = await ensureSharedAngularLinker(process.cwd());
210
- if (!babel || !linkerPlugin)
525
+ const { babel } = await ensureSharedAngularLinker(process.cwd());
526
+ if (!babel)
527
+ return;
528
+ const fileSystem = await resolveAngularFileSystem(process.cwd());
529
+ const linkerFactory = await importLinkerFactory();
530
+ if (!linkerFactory)
211
531
  return;
212
532
  const strict = process.env.NS_STRICT_NG_LINK === '1' || process.env.NS_STRICT_NG_LINK === 'true';
213
533
  const enforceStrict = process.env.NS_STRICT_NG_LINK_ENFORCE === '1' || process.env.NS_STRICT_NG_LINK_ENFORCE === 'true';
214
534
  const debug = process.env.VITE_DEBUG_LOGS === '1' || process.env.VITE_DEBUG_LOGS === 'true';
215
535
  const unlinked = [];
536
+ const vendorInjectExport = (() => {
537
+ const vendorChunk = Object.entries(bundle).find(([name, value]) => value && value.type === 'chunk' && /(^|\/)vendor\.mjs$/.test(name));
538
+ const vendorCode = vendorChunk ? vendorChunk[1].code : undefined;
539
+ return vendorCode?.match(/\binject as ([A-Za-z_$][\w$]*)/)?.[1];
540
+ })();
216
541
  for (const [fileName, chunk] of Object.entries(bundle)) {
217
542
  if (!fileName.endsWith('.mjs') && !fileName.endsWith('.js'))
218
543
  continue;
@@ -222,29 +547,31 @@ export const angularConfig = ({ mode }) => {
222
547
  continue;
223
548
  const isNsPolyfills = isNsAngularPolyfillsChunk(chunk);
224
549
  try {
550
+ // Create a FRESH linker plugin per chunk — the linker may have
551
+ // internal state that becomes stale across watch-mode rebuild cycles.
552
+ const freshPlugin = linkerFactory({ sourceMapping: false, fileSystem });
225
553
  const res = await babel.transformAsync(code, {
226
554
  filename: fileName,
227
555
  configFile: false,
228
556
  babelrc: false,
229
557
  sourceMaps: false,
230
558
  compact: false,
231
- plugins: [linkerPlugin],
559
+ plugins: [freshPlugin],
232
560
  });
233
- const finalCode = res?.code && res.code !== code ? res.code : code;
561
+ const linkedCode = res?.code && res.code !== code ? res.code : code;
562
+ const finalCode = applyAngularChunkPostProcessing(linkedCode, { vendorInjectExport });
234
563
  if (finalCode !== code) {
235
564
  chunk.code = finalCode;
236
565
  if (debug) {
237
- try {
238
- console.log('[ns-angular-linker][post] linked', fileName, isNsPolyfills ? '(polyfills)' : '');
239
- }
240
- catch { }
566
+ console.log('[ns-angular-linker][post] linked', fileName, isNsPolyfills ? '(polyfills)' : '');
241
567
  }
242
568
  }
243
569
  if (strict && !isNsPolyfills && containsRealNgDeclare(finalCode)) {
244
570
  unlinked.push(fileName);
245
571
  }
246
572
  }
247
- catch {
573
+ catch (e) {
574
+ console.warn(`[ns-angular-linker][post] linking FAILED for ${fileName}:`, e?.message || e);
248
575
  if (strict)
249
576
  unlinked.push(fileName);
250
577
  }
@@ -282,89 +609,94 @@ export const angularConfig = ({ mode }) => {
282
609
  throw new Error(message);
283
610
  }
284
611
  else {
285
- try {
286
- console.warn(`[ns-angular-linker-post] ${message}`);
287
- }
288
- catch { }
612
+ console.warn(`[ns-angular-linker-post] ${message}`);
289
613
  }
290
614
  }
291
615
  },
292
616
  };
293
- // Safety net: transform each rendered chunk to link any remaining ɵɵngDeclare* call sites
617
+ // Safety net: transform each rendered chunk to link any remaining ɵɵngDeclare* call sites.
618
+ // IMPORTANT: create a FRESH linker plugin per invocation — the shared instance may have
619
+ // stale internal state from a prior build cycle, causing silent failures in watch-mode rebuilds.
294
620
  const renderChunkLinker = {
295
621
  name: 'ns-angular-linker-render',
296
622
  apply: 'build',
297
623
  enforce: 'post',
298
624
  async renderChunk(code, chunk) {
625
+ if (!code)
626
+ return null;
627
+ const filename = chunk.fileName || chunk.name || 'chunk.mjs';
628
+ const debug = process.env.VITE_DEBUG_LOGS === '1' || process.env.VITE_DEBUG_LOGS === 'true';
299
629
  try {
300
- if (!code)
301
- return null;
302
- if (!containsRealNgDeclare(code))
303
- return null;
304
- const { babel, linkerPlugin } = await ensureSharedAngularLinker(process.cwd());
305
- if (!babel || !linkerPlugin)
306
- return null;
307
- const filename = chunk.fileName || chunk.name || 'chunk.mjs';
308
- const debug = process.env.VITE_DEBUG_LOGS === '1' || process.env.VITE_DEBUG_LOGS === 'true';
309
- const runLink = async (input) => {
310
- const result = await babel.transformAsync(input, {
311
- filename,
312
- configFile: false,
313
- babelrc: false,
314
- sourceMaps: false,
315
- compact: false,
316
- plugins: [linkerPlugin],
317
- });
318
- return result?.code ?? input;
319
- };
320
- let transformed = await runLink(code);
321
- if (containsRealNgDeclare(transformed)) {
322
- transformed = await runLink(transformed);
630
+ let transformed = code;
631
+ if (containsRealNgDeclare(code)) {
632
+ const { babel } = await ensureSharedAngularLinker(process.cwd());
633
+ if (!babel)
634
+ return null;
635
+ const fileSystem = await resolveAngularFileSystem(process.cwd());
636
+ // Fresh plugin per chunk — avoids stale linker state across watch-mode rebuilds
637
+ const freshPlugin = (await importLinkerFactory())?.({ sourceMapping: false, fileSystem });
638
+ if (!freshPlugin)
639
+ return null;
640
+ const runLink = async (input) => {
641
+ const result = await babel.transformAsync(input, {
642
+ filename,
643
+ configFile: false,
644
+ babelrc: false,
645
+ sourceMaps: false,
646
+ compact: false,
647
+ plugins: [freshPlugin],
648
+ });
649
+ return result?.code ?? input;
650
+ };
651
+ transformed = await runLink(code);
652
+ if (containsRealNgDeclare(transformed)) {
653
+ transformed = await runLink(transformed);
654
+ }
323
655
  }
656
+ transformed = applyAngularChunkPostProcessing(transformed);
324
657
  if (transformed !== code) {
325
658
  if (debug) {
326
- try {
327
- console.log('[ns-angular-linker][render] linked', filename);
328
- }
329
- catch { }
659
+ console.log('[ns-angular-linker][render] linked', filename);
330
660
  }
331
661
  return { code: transformed, map: null };
332
662
  }
333
663
  }
334
- catch { }
664
+ catch (e) {
665
+ console.warn(`[ns-angular-linker][render] linking FAILED for ${filename}:`, e?.message || e);
666
+ }
335
667
  return null;
336
668
  },
337
669
  };
338
670
  const enableRollupLinker = process.env.NS_ENABLE_ROLLUP_LINKER === '1' || process.env.NS_ENABLE_ROLLUP_LINKER === 'true' || hmrActive;
339
- return mergeConfig(baseConfig({ mode, flavor: 'angular' }), {
671
+ // Build a single merged alias array to avoid property override conflicts.
672
+ // Previously the disableAnimations spread added a second `resolve` key
673
+ // that silently clobbered the fesm2022 and RxJS aliases.
674
+ const angularAliases = [
675
+ // Map Angular deep ESM paths to bare package ids
676
+ { find: /^@angular\/([^/]+)\/fesm2022\/.*\.mjs$/, replacement: '@angular/$1' },
677
+ { find: /^@nativescript\/angular\/fesm2022\/.*\.mjs$/, replacement: '@nativescript/angular' },
678
+ // RxJS esm5 → esm redirects removed: Vite 8 enforces the rxjs `exports` field,
679
+ // blocking deep path aliases. Instead, the `es2015` resolve condition is added
680
+ // below so rxjs resolves to its modern ESM builds via the exports map.
681
+ ];
682
+ if (disableAnimations) {
683
+ angularAliases.push({
684
+ find: /^@angular\/animations(\/.+)?$/, // match subpaths too
685
+ replacement: resolveRelativeToImportMeta(import.meta.url, '../shims/angular-animations-stub.js'),
686
+ }, {
687
+ find: /^@angular\/platform-browser\/animations(\/.+)?$/,
688
+ replacement: resolveRelativeToImportMeta(import.meta.url, '../shims/angular-animations-stub.js'),
689
+ });
690
+ }
691
+ const config = mergeConfig(baseConfig({ mode, flavor: 'angular' }), {
340
692
  plugins: [...plugins, ...(enableRollupLinker ? [angularRollupLinker(process.cwd())] : []), renderChunkLinker, postLinker],
341
- // Always alias fesm2022 deep imports to package root so vendor bridge can externalize properly
342
693
  resolve: {
343
- alias: [
344
- // Map Angular deep ESM paths to bare package ids
345
- { find: /^@angular\/([^/]+)\/fesm2022\/.*\.mjs$/, replacement: '@angular/$1' },
346
- { find: /^@nativescript\/angular\/fesm2022\/.*\.mjs$/, replacement: '@nativescript/angular' },
347
- // Prefer modern RxJS builds; avoid esm5 which explodes module count and memory
348
- { find: /^rxjs\/dist\/esm5\/(.*)$/, replacement: 'rxjs/dist/esm2015/$1' },
349
- { find: /^rxjs\/operators$/, replacement: 'rxjs/dist/esm2015/operators/index.js' },
350
- { find: /^rxjs$/, replacement: 'rxjs/dist/esm2015/index.js' },
351
- // Existing optional animations shims
352
- ],
694
+ alias: angularAliases,
695
+ // Add 'es2015' condition so RxJS resolves to dist/esm (modern ESM) rather
696
+ // than dist/esm5 via the 'default' condition. This avoids the esm5 module
697
+ // explosion and OOM while respecting the package's exports field in Vite 8.
698
+ conditions: ['es2015'],
353
699
  },
354
- ...(disableAnimations && {
355
- resolve: {
356
- alias: [
357
- {
358
- find: /^@angular\/animations(\/.+)?$/, // match subpaths too
359
- replacement: new URL('../shims/angular-animations-stub.js', import.meta.url).pathname,
360
- },
361
- {
362
- find: /^@angular\/platform-browser\/animations(\/.+)?$/,
363
- replacement: new URL('../shims/angular-animations-stub.js', import.meta.url).pathname,
364
- },
365
- ],
366
- },
367
- }),
368
700
  // Disable dependency optimization entirely for NativeScript Angular HMR.
369
701
  // Vite 5.1+: use noDiscovery with an empty include list (disabled was removed).
370
702
  // The HTTP loader + vendor bridge manage dependencies; pre-bundling can OOM.
@@ -376,5 +708,19 @@ export const angularConfig = ({ mode }) => {
376
708
  rolldownOptions: { plugins: [] },
377
709
  },
378
710
  });
711
+ if (useSingleBundleDevOutput) {
712
+ const build = (config.build ?? (config.build = {}));
713
+ const rolldownOptions = (build.rolldownOptions ?? (build.rolldownOptions = {}));
714
+ const outputConfigs = Array.isArray(rolldownOptions.output) ? rolldownOptions.output : [rolldownOptions.output ?? {}];
715
+ for (const output of outputConfigs) {
716
+ // Angular non-HMR reloads are more reliable when rebuilds keep a single boot bundle.
717
+ // This avoids watch-time chunk alias/name drift that can leave the native app reloading into stale split points.
718
+ delete output.manualChunks;
719
+ delete output.chunkFileNames;
720
+ output.codeSplitting = false;
721
+ }
722
+ rolldownOptions.output = Array.isArray(rolldownOptions.output) ? outputConfigs : outputConfigs[0];
723
+ }
724
+ return config;
379
725
  };
380
726
  //# sourceMappingURL=angular.js.map