@angular/build 19.0.0-next.1 → 19.0.0-next.10

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 (115) hide show
  1. package/LICENSE +5 -5
  2. package/package.json +20 -16
  3. package/src/builders/application/build-action.js +9 -9
  4. package/src/builders/application/chunk-optimizer.js +1 -4
  5. package/src/builders/application/execute-build.js +19 -2
  6. package/src/builders/application/execute-post-bundle.d.ts +2 -2
  7. package/src/builders/application/execute-post-bundle.js +58 -20
  8. package/src/builders/application/i18n.d.ts +2 -2
  9. package/src/builders/application/i18n.js +6 -16
  10. package/src/builders/application/index.js +8 -5
  11. package/src/builders/application/options.d.ts +36 -1
  12. package/src/builders/application/options.js +60 -3
  13. package/src/builders/application/schema.d.ts +15 -0
  14. package/src/builders/application/schema.js +11 -1
  15. package/src/builders/application/schema.json +5 -0
  16. package/src/builders/application/setup-bundling.js +12 -9
  17. package/src/builders/dev-server/internal.d.ts +0 -1
  18. package/src/builders/dev-server/internal.js +1 -3
  19. package/src/builders/dev-server/vite-server.d.ts +8 -2
  20. package/src/builders/dev-server/vite-server.js +111 -56
  21. package/src/builders/extract-i18n/application-extraction.js +3 -3
  22. package/src/tools/angular/angular-host.d.ts +2 -1
  23. package/src/tools/angular/angular-host.js +20 -1
  24. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  25. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  26. package/src/tools/angular/compilation/aot-compilation.js +9 -1
  27. package/src/tools/angular/compilation/parallel-compilation.d.ts +2 -1
  28. package/src/tools/angular/compilation/parallel-compilation.js +2 -10
  29. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  30. package/src/tools/angular/compilation/parallel-worker.js +2 -1
  31. package/src/tools/babel/plugins/add-code-coverage.d.ts +14 -0
  32. package/src/tools/babel/plugins/add-code-coverage.js +44 -0
  33. package/src/tools/babel/plugins/types.d.ts +20 -0
  34. package/src/tools/esbuild/angular/compiler-plugin.d.ts +2 -0
  35. package/src/tools/esbuild/angular/compiler-plugin.js +44 -4
  36. package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
  37. package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
  38. package/src/tools/esbuild/angular/file-reference-tracker.d.ts +1 -1
  39. package/src/tools/esbuild/application-code-bundle.d.ts +2 -6
  40. package/src/tools/esbuild/application-code-bundle.js +208 -67
  41. package/src/tools/esbuild/budget-stats.js +1 -1
  42. package/src/tools/esbuild/bundler-context.d.ts +4 -3
  43. package/src/tools/esbuild/bundler-context.js +21 -13
  44. package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
  45. package/src/tools/esbuild/bundler-execution-result.js +7 -3
  46. package/src/tools/esbuild/cache.d.ts +6 -1
  47. package/src/tools/esbuild/cache.js +7 -0
  48. package/src/tools/esbuild/compiler-plugin-options.js +3 -1
  49. package/src/tools/esbuild/i18n-inliner.js +4 -4
  50. package/src/tools/esbuild/javascript-transformer-worker.d.ts +1 -0
  51. package/src/tools/esbuild/javascript-transformer-worker.js +5 -1
  52. package/src/tools/esbuild/javascript-transformer.d.ts +2 -2
  53. package/src/tools/esbuild/javascript-transformer.js +7 -12
  54. package/src/tools/esbuild/utils.d.ts +9 -0
  55. package/src/tools/esbuild/utils.js +21 -3
  56. package/src/tools/sass/sass-service.js +11 -13
  57. package/src/tools/sass/worker.d.ts +13 -32
  58. package/src/tools/sass/worker.js +1 -0
  59. package/src/tools/vite/middlewares/assets-middleware.d.ts +1 -1
  60. package/src/tools/vite/middlewares/assets-middleware.js +43 -4
  61. package/src/tools/vite/middlewares/headers-middleware.d.ts +19 -0
  62. package/src/tools/vite/middlewares/headers-middleware.js +34 -0
  63. package/src/tools/vite/middlewares/html-fallback-middleware.d.ts +1 -1
  64. package/src/tools/vite/middlewares/html-fallback-middleware.js +23 -7
  65. package/src/tools/vite/middlewares/index-html-middleware.js +1 -2
  66. package/src/tools/vite/middlewares/index.d.ts +2 -1
  67. package/src/tools/vite/middlewares/index.js +5 -2
  68. package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -4
  69. package/src/tools/vite/middlewares/ssr-middleware.js +75 -43
  70. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
  71. package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -40
  72. package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
  73. package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
  74. package/src/tools/vite/plugins/index.d.ts +12 -0
  75. package/src/tools/vite/plugins/index.js +21 -0
  76. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
  77. package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
  78. package/src/{utils/server-rendering/main-bundle-exports.js → tools/vite/plugins/ssr-transform-plugin.d.ts} +2 -2
  79. package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
  80. package/src/tools/vite/utils.d.ts +0 -3
  81. package/src/tools/vite/utils.js +0 -12
  82. package/src/typings.d.ts +26 -0
  83. package/src/utils/environment-options.d.ts +2 -0
  84. package/src/utils/environment-options.js +5 -1
  85. package/src/utils/index-file/index-html-generator.js +5 -0
  86. package/src/utils/index-file/inline-critical-css.js +43 -33
  87. package/src/utils/index-file/ngcm-attribute.d.ts +15 -0
  88. package/src/utils/index-file/ngcm-attribute.js +37 -0
  89. package/src/utils/index-file/valid-self-closing-tags.js +28 -0
  90. package/src/utils/normalize-cache.js +1 -1
  91. package/src/utils/server-rendering/fetch-patch.d.ts +1 -1
  92. package/src/utils/server-rendering/fetch-patch.js +5 -6
  93. package/src/utils/server-rendering/launch-server.d.ts +14 -0
  94. package/src/utils/server-rendering/launch-server.js +63 -0
  95. package/src/utils/server-rendering/load-esm-from-memory.d.ts +18 -2
  96. package/src/utils/server-rendering/manifest.d.ts +50 -0
  97. package/src/utils/server-rendering/manifest.js +126 -0
  98. package/src/utils/server-rendering/models.d.ts +27 -0
  99. package/src/utils/server-rendering/models.js +22 -0
  100. package/src/utils/server-rendering/prerender.d.ts +26 -10
  101. package/src/utils/server-rendering/prerender.js +122 -75
  102. package/src/utils/server-rendering/render-worker.d.ts +9 -8
  103. package/src/utils/server-rendering/render-worker.js +19 -14
  104. package/src/utils/server-rendering/routes-extractor-worker.d.ts +6 -10
  105. package/src/utils/server-rendering/routes-extractor-worker.js +16 -33
  106. package/src/utils/server-rendering/utils.d.ts +11 -0
  107. package/src/utils/server-rendering/utils.js +17 -0
  108. package/src/utils/worker-pool.d.ts +12 -0
  109. package/src/utils/worker-pool.js +43 -0
  110. package/src/tools/vite/angular-memory-plugin.d.ts +0 -21
  111. package/src/utils/server-rendering/main-bundle-exports.d.ts +0 -27
  112. package/src/utils/server-rendering/render-page.d.ts +0 -26
  113. package/src/utils/server-rendering/render-page.js +0 -114
  114. /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
  115. /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
@@ -28,7 +28,7 @@ function generateBudgetStats(metafile, outputFiles, initialFiles) {
28
28
  continue;
29
29
  }
30
30
  // Exclude server bundles
31
- if (type === bundler_context_1.BuildOutputFileType.Server) {
31
+ if (type === bundler_context_1.BuildOutputFileType.ServerApplication || type === bundler_context_1.BuildOutputFileType.ServerRoot) {
32
32
  continue;
33
33
  }
34
34
  const initialRecord = initialFiles.get(file);
@@ -31,9 +31,10 @@ export interface InitialFileRecord {
31
31
  depth: number;
32
32
  }
33
33
  export declare enum BuildOutputFileType {
34
- Browser = 1,
35
- Media = 2,
36
- Server = 3,
34
+ Browser = 0,
35
+ Media = 1,
36
+ ServerApplication = 2,
37
+ ServerRoot = 3,
37
38
  Root = 4
38
39
  }
39
40
  export interface BuildOutputFile extends OutputFile {
@@ -18,9 +18,10 @@ const load_result_cache_1 = require("./load-result-cache");
18
18
  const utils_1 = require("./utils");
19
19
  var BuildOutputFileType;
20
20
  (function (BuildOutputFileType) {
21
- BuildOutputFileType[BuildOutputFileType["Browser"] = 1] = "Browser";
22
- BuildOutputFileType[BuildOutputFileType["Media"] = 2] = "Media";
23
- BuildOutputFileType[BuildOutputFileType["Server"] = 3] = "Server";
21
+ BuildOutputFileType[BuildOutputFileType["Browser"] = 0] = "Browser";
22
+ BuildOutputFileType[BuildOutputFileType["Media"] = 1] = "Media";
23
+ BuildOutputFileType[BuildOutputFileType["ServerApplication"] = 2] = "ServerApplication";
24
+ BuildOutputFileType[BuildOutputFileType["ServerRoot"] = 3] = "ServerRoot";
24
25
  BuildOutputFileType[BuildOutputFileType["Root"] = 4] = "Root";
25
26
  })(BuildOutputFileType || (exports.BuildOutputFileType = BuildOutputFileType = {}));
26
27
  /**
@@ -138,6 +139,7 @@ class BundlerContext {
138
139
  }
139
140
  return result;
140
141
  }
142
+ // eslint-disable-next-line max-lines-per-function
141
143
  async #performBundle() {
142
144
  // Create esbuild options if not present
143
145
  if (this.#esbuildOptions === undefined) {
@@ -165,12 +167,6 @@ class BundlerContext {
165
167
  // For non-incremental builds, perform a single build
166
168
  result = await (0, esbuild_1.build)(this.#esbuildOptions);
167
169
  }
168
- if (this.#platformIsServer) {
169
- for (const entry of Object.values(result.metafile.outputs)) {
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
- entry['ng-platform-server'] = true;
172
- }
173
- }
174
170
  }
175
171
  catch (failure) {
176
172
  // Build failures will throw an exception which contains errors/warnings
@@ -280,6 +276,7 @@ class BundlerContext {
280
276
  for (const { imports } of Object.values(result.metafile.outputs)) {
281
277
  for (const importData of imports) {
282
278
  if (!importData.external ||
279
+ utils_1.SERVER_GENERATED_EXTERNALS.has(importData.path) ||
283
280
  (importData.kind !== 'import-statement' &&
284
281
  importData.kind !== 'dynamic-import' &&
285
282
  importData.kind !== 'require-call')) {
@@ -295,13 +292,24 @@ class BundlerContext {
295
292
  if (!/\.([cm]?js|css|wasm)(\.map)?$/i.test(file.path)) {
296
293
  fileType = BuildOutputFileType.Media;
297
294
  }
295
+ else if (this.#platformIsServer) {
296
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
297
+ fileType = result.metafile['ng-ssr-entry-bundle']
298
+ ? BuildOutputFileType.ServerRoot
299
+ : BuildOutputFileType.ServerApplication;
300
+ }
298
301
  else {
299
- fileType = this.#platformIsServer
300
- ? BuildOutputFileType.Server
301
- : BuildOutputFileType.Browser;
302
+ fileType = BuildOutputFileType.Browser;
302
303
  }
303
304
  return (0, utils_1.convertOutputFile)(file, fileType);
304
305
  });
306
+ let externalConfiguration = this.#esbuildOptions.external;
307
+ if (this.#platformIsServer && externalConfiguration) {
308
+ externalConfiguration = externalConfiguration.filter((dep) => !utils_1.SERVER_GENERATED_EXTERNALS.has(dep));
309
+ if (!externalConfiguration.length) {
310
+ externalConfiguration = undefined;
311
+ }
312
+ }
305
313
  // Return the successful build results
306
314
  return {
307
315
  ...result,
@@ -310,7 +318,7 @@ class BundlerContext {
310
318
  externalImports: {
311
319
  [this.#platformIsServer ? 'server' : 'browser']: externalImports,
312
320
  },
313
- externalConfiguration: this.#esbuildOptions.external,
321
+ externalConfiguration,
314
322
  errors: undefined,
315
323
  };
316
324
  }
@@ -24,6 +24,9 @@ export interface ExternalResultMetadata {
24
24
  implicitServer: string[];
25
25
  explicit: string[];
26
26
  }
27
+ export type PrerenderedRoutesRecord = Record<string, {
28
+ headers?: Record<string, string>;
29
+ }>;
27
30
  /**
28
31
  * Represents the result of a single builder execute call.
29
32
  */
@@ -33,7 +36,7 @@ export declare class ExecutionResult {
33
36
  outputFiles: BuildOutputFile[];
34
37
  assetFiles: BuildOutputAsset[];
35
38
  errors: (Message | PartialMessage)[];
36
- prerenderedRoutes: string[];
39
+ prerenderedRoutes: PrerenderedRoutesRecord;
37
40
  warnings: (Message | PartialMessage)[];
38
41
  logs: string[];
39
42
  externalMetadata?: ExternalResultMetadata;
@@ -46,7 +49,7 @@ export declare class ExecutionResult {
46
49
  addLog(value: string): void;
47
50
  addError(error: PartialMessage | string): void;
48
51
  addErrors(errors: (PartialMessage | string)[]): void;
49
- addPrerenderedRoutes(routes: string[]): void;
52
+ addPrerenderedRoutes(routes: PrerenderedRoutesRecord): void;
50
53
  addWarning(error: PartialMessage | string): void;
51
54
  addWarnings(errors: (PartialMessage | string)[]): void;
52
55
  /**
@@ -19,7 +19,7 @@ class ExecutionResult {
19
19
  outputFiles = [];
20
20
  assetFiles = [];
21
21
  errors = [];
22
- prerenderedRoutes = [];
22
+ prerenderedRoutes = {};
23
23
  warnings = [];
24
24
  logs = [];
25
25
  externalMetadata;
@@ -53,9 +53,13 @@ class ExecutionResult {
53
53
  }
54
54
  }
55
55
  addPrerenderedRoutes(routes) {
56
- this.prerenderedRoutes.push(...routes);
56
+ Object.assign(this.prerenderedRoutes, routes);
57
57
  // Sort the prerendered routes.
58
- this.prerenderedRoutes.sort((a, b) => a.localeCompare(b));
58
+ const sortedObj = {};
59
+ for (const key of Object.keys(this.prerenderedRoutes).sort()) {
60
+ sortedObj[key] = this.prerenderedRoutes[key];
61
+ }
62
+ this.prerenderedRoutes = sortedObj;
59
63
  }
60
64
  addWarning(error) {
61
65
  if (typeof error === 'string') {
@@ -84,5 +84,10 @@ export declare class MemoryCache<V> extends Cache<V, Map<string, V>> {
84
84
  * Provides all the values currently present in the cache instance.
85
85
  * @returns An iterable of all values in the cache.
86
86
  */
87
- values(): IterableIterator<V>;
87
+ values(): MapIterator<V>;
88
+ /**
89
+ * Provides all the keys/values currently present in the cache instance.
90
+ * @returns An iterable of all key/value pairs in the cache.
91
+ */
92
+ entries(): MapIterator<[string, V]>;
88
93
  }
@@ -88,5 +88,12 @@ class MemoryCache extends Cache {
88
88
  values() {
89
89
  return this.store.values();
90
90
  }
91
+ /**
92
+ * Provides all the keys/values currently present in the cache instance.
93
+ * @returns An iterable of all key/value pairs in the cache.
94
+ */
95
+ entries() {
96
+ return this.store.entries();
97
+ }
91
98
  }
92
99
  exports.MemoryCache = MemoryCache;
@@ -9,7 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createCompilerPluginOptions = createCompilerPluginOptions;
11
11
  function createCompilerPluginOptions(options, target, sourceFileCache) {
12
- const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, postcssConfiguration, publicPath, } = options;
12
+ const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, postcssConfiguration, publicPath, externalRuntimeStyles, instrumentForCoverage, } = options;
13
13
  return {
14
14
  // JS/TS options
15
15
  pluginOptions: {
@@ -22,6 +22,8 @@ function createCompilerPluginOptions(options, target, sourceFileCache) {
22
22
  sourceFileCache,
23
23
  loadResultCache: sourceFileCache?.loadResultCache,
24
24
  incremental: !!options.watch,
25
+ externalRuntimeStyles,
26
+ instrumentForCoverage,
25
27
  },
26
28
  // Component stylesheet options
27
29
  styleOptions: {
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.I18nInliner = void 0;
14
14
  const node_assert_1 = __importDefault(require("node:assert"));
15
- const piscina_1 = __importDefault(require("piscina"));
15
+ const worker_pool_1 = require("../../utils/worker-pool");
16
16
  const bundler_context_1 = require("./bundler-context");
17
17
  const utils_1 = require("./utils");
18
18
  /**
@@ -36,7 +36,8 @@ class I18nInliner {
36
36
  const files = new Map();
37
37
  const pendingMaps = [];
38
38
  for (const file of options.outputFiles) {
39
- if (file.type === bundler_context_1.BuildOutputFileType.Root) {
39
+ if (file.type === bundler_context_1.BuildOutputFileType.Root || file.type === bundler_context_1.BuildOutputFileType.ServerRoot) {
40
+ // Skip also the server entry-point.
40
41
  // Skip stats and similar files.
41
42
  continue;
42
43
  }
@@ -74,7 +75,7 @@ class I18nInliner {
74
75
  }
75
76
  }
76
77
  this.#localizeFiles = files;
77
- this.#workerPool = new piscina_1.default({
78
+ this.#workerPool = new worker_pool_1.WorkerPool({
78
79
  filename: require.resolve('./i18n-inliner-worker'),
79
80
  maxThreads,
80
81
  // Extract options to ensure only the named options are serialized and sent to the worker
@@ -83,7 +84,6 @@ class I18nInliner {
83
84
  shouldOptimize: options.shouldOptimize,
84
85
  files,
85
86
  },
86
- recordTiming: false,
87
87
  });
88
88
  }
89
89
  /**
@@ -14,6 +14,7 @@ interface JavaScriptTransformRequest {
14
14
  skipLinker?: boolean;
15
15
  sideEffects?: boolean;
16
16
  jit: boolean;
17
+ instrumentForCoverage?: boolean;
17
18
  }
18
19
  export default function transformJavaScript(request: JavaScriptTransformRequest): Promise<unknown>;
19
20
  export {};
@@ -63,8 +63,12 @@ async function transformWithBabel(filename, data, options) {
63
63
  // @ts-expect-error Import attribute syntax plugin does not currently have type definitions
64
64
  const { default: importAttributePlugin } = await Promise.resolve().then(() => __importStar(require('@babel/plugin-syntax-import-attributes')));
65
65
  const plugins = [importAttributePlugin];
66
- // Lazy load the linker plugin only when linking is required
66
+ if (options.instrumentForCoverage) {
67
+ const { default: coveragePlugin } = await Promise.resolve().then(() => __importStar(require('../babel/plugins/add-code-coverage.js')));
68
+ plugins.push(coveragePlugin);
69
+ }
67
70
  if (shouldLink) {
71
+ // Lazy load the linker plugin only when linking is required
68
72
  const linkerPlugin = await createLinkerPlugin(options);
69
73
  plugins.push(linkerPlugin);
70
74
  }
@@ -35,7 +35,7 @@ export declare class JavaScriptTransformer {
35
35
  * @param sideEffects If false, and `advancedOptimizations` is enabled tslib decorators are wrapped.
36
36
  * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
37
37
  */
38
- transformFile(filename: string, skipLinker?: boolean, sideEffects?: boolean): Promise<Uint8Array>;
38
+ transformFile(filename: string, skipLinker?: boolean, sideEffects?: boolean, instrumentForCoverage?: boolean): Promise<Uint8Array>;
39
39
  /**
40
40
  * Performs JavaScript transformations on the provided data of a file. The file does not need
41
41
  * to exist on the filesystem.
@@ -45,7 +45,7 @@ export declare class JavaScriptTransformer {
45
45
  * @param sideEffects If false, and `advancedOptimizations` is enabled tslib decorators are wrapped.
46
46
  * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
47
47
  */
48
- transformData(filename: string, data: string, skipLinker: boolean, sideEffects?: boolean): Promise<Uint8Array>;
48
+ transformData(filename: string, data: string, skipLinker: boolean, sideEffects?: boolean, instrumentForCoverage?: boolean): Promise<Uint8Array>;
49
49
  /**
50
50
  * Stops all active transformation tasks and shuts down all workers.
51
51
  * @returns A void promise that resolves when closing is complete.
@@ -6,14 +6,11 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
9
  Object.defineProperty(exports, "__esModule", { value: true });
13
10
  exports.JavaScriptTransformer = void 0;
14
11
  const node_crypto_1 = require("node:crypto");
15
12
  const promises_1 = require("node:fs/promises");
16
- const piscina_1 = __importDefault(require("piscina"));
13
+ const worker_pool_1 = require("../../utils/worker-pool");
17
14
  /**
18
15
  * A class that performs transformation of JavaScript files and raw data.
19
16
  * A worker pool is used to distribute the transformation actions and allow
@@ -41,13 +38,9 @@ class JavaScriptTransformer {
41
38
  this.#fileCacheKeyBase = Buffer.from(JSON.stringify(this.#commonOptions), 'utf-8');
42
39
  }
43
40
  #ensureWorkerPool() {
44
- this.#workerPool ??= new piscina_1.default({
41
+ this.#workerPool ??= new worker_pool_1.WorkerPool({
45
42
  filename: require.resolve('./javascript-transformer-worker'),
46
- minThreads: 1,
47
43
  maxThreads: this.maxThreads,
48
- // Shutdown idle threads after 1 second of inactivity
49
- idleTimeout: 1000,
50
- recordTiming: false,
51
44
  });
52
45
  return this.#workerPool;
53
46
  }
@@ -59,7 +52,7 @@ class JavaScriptTransformer {
59
52
  * @param sideEffects If false, and `advancedOptimizations` is enabled tslib decorators are wrapped.
60
53
  * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
61
54
  */
62
- async transformFile(filename, skipLinker, sideEffects) {
55
+ async transformFile(filename, skipLinker, sideEffects, instrumentForCoverage) {
63
56
  const data = await (0, promises_1.readFile)(filename);
64
57
  let result;
65
58
  let cacheKey;
@@ -86,6 +79,7 @@ class JavaScriptTransformer {
86
79
  data,
87
80
  skipLinker,
88
81
  sideEffects,
82
+ instrumentForCoverage,
89
83
  ...this.#commonOptions,
90
84
  }, {
91
85
  // The below is disable as with Yarn PNP this causes build failures with the below message
@@ -113,10 +107,10 @@ class JavaScriptTransformer {
113
107
  * @param sideEffects If false, and `advancedOptimizations` is enabled tslib decorators are wrapped.
114
108
  * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
115
109
  */
116
- async transformData(filename, data, skipLinker, sideEffects) {
110
+ async transformData(filename, data, skipLinker, sideEffects, instrumentForCoverage) {
117
111
  // Perform a quick test to determine if the data needs any transformations.
118
112
  // This allows directly returning the data without the worker communication overhead.
119
- if (skipLinker && !this.#commonOptions.advancedOptimizations) {
113
+ if (skipLinker && !this.#commonOptions.advancedOptimizations && !instrumentForCoverage) {
120
114
  const keepSourcemap = this.#commonOptions.sourcemap &&
121
115
  (!!this.#commonOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
122
116
  return Buffer.from(keepSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''), 'utf-8');
@@ -126,6 +120,7 @@ class JavaScriptTransformer {
126
120
  data,
127
121
  skipLinker,
128
122
  sideEffects,
123
+ instrumentForCoverage,
129
124
  ...this.#commonOptions,
130
125
  });
131
126
  }
@@ -47,3 +47,12 @@ export declare function logMessages(logger: BuilderContext['logger'], executionR
47
47
  */
48
48
  export declare function isZonelessApp(polyfills: string[] | undefined): boolean;
49
49
  export declare function getEntryPointName(entryPoint: string): string;
50
+ /**
51
+ * A set of server-generated dependencies that are treated as external.
52
+ *
53
+ * These dependencies are marked as external because they are produced by a
54
+ * separate bundling process and are not included in the primary bundle. This
55
+ * ensures that these generated files are resolved from an external source rather
56
+ * than being part of the main bundle.
57
+ */
58
+ export declare const SERVER_GENERATED_EXTERNALS: Set<string>;
@@ -7,6 +7,7 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.SERVER_GENERATED_EXTERNALS = void 0;
10
11
  exports.logBuildStats = logBuildStats;
11
12
  exports.getChunkNameFromMetafile = getChunkNameFromMetafile;
12
13
  exports.calculateEstimatedTransferSizes = calculateEstimatedTransferSizes;
@@ -29,6 +30,8 @@ const node_path_1 = require("node:path");
29
30
  const node_url_1 = require("node:url");
30
31
  const node_zlib_1 = require("node:zlib");
31
32
  const semver_1 = require("semver");
33
+ const schema_1 = require("../../builders/application/schema");
34
+ const manifest_1 = require("../../utils/server-rendering/manifest");
32
35
  const stats_table_1 = require("../../utils/stats-table");
33
36
  const bundler_context_1 = require("./bundler-context");
34
37
  function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, changedFiles, estimatedTransferSizes, ssrOutputEnabled, verbose) {
@@ -45,7 +48,7 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
45
48
  ++unchangedCount;
46
49
  continue;
47
50
  }
48
- const isPlatformServer = type === bundler_context_1.BuildOutputFileType.Server;
51
+ const isPlatformServer = type === bundler_context_1.BuildOutputFileType.ServerApplication || type === bundler_context_1.BuildOutputFileType.ServerRoot;
49
52
  if (isPlatformServer && !ssrOutputEnabled) {
50
53
  // Only log server build stats when SSR is enabled.
51
54
  continue;
@@ -340,7 +343,7 @@ function getSupportedNodeTargets() {
340
343
  return SUPPORTED_NODE_VERSIONS.split('||').map((v) => 'node' + (0, semver_1.coerce)(v)?.version);
341
344
  }
342
345
  async function createJsonBuildManifest(result, normalizedOptions) {
343
- const { colors: color, outputOptions: { base, server, browser }, ssrOptions, } = normalizedOptions;
346
+ const { colors: color, outputOptions: { base, server, browser }, ssrOptions, outputMode, } = normalizedOptions;
344
347
  const { warnings, errors, prerenderedRoutes } = result;
345
348
  const manifest = {
346
349
  errors: errors.length ? await (0, esbuild_1.formatMessages)(errors, { kind: 'error', color }) : [],
@@ -348,7 +351,9 @@ async function createJsonBuildManifest(result, normalizedOptions) {
348
351
  outputPaths: {
349
352
  root: (0, node_url_1.pathToFileURL)(base),
350
353
  browser: (0, node_url_1.pathToFileURL)((0, node_path_1.join)(base, browser)),
351
- server: ssrOptions ? (0, node_url_1.pathToFileURL)((0, node_path_1.join)(base, server)) : undefined,
354
+ server: outputMode !== schema_1.OutputMode.Static && ssrOptions
355
+ ? (0, node_url_1.pathToFileURL)((0, node_path_1.join)(base, server))
356
+ : undefined,
352
357
  },
353
358
  prerenderedRoutes,
354
359
  };
@@ -385,3 +390,16 @@ function getEntryPointName(entryPoint) {
385
390
  .replace(/\.[cm]?[jt]s$/, '')
386
391
  .replace(/[\\/.]/g, '-');
387
392
  }
393
+ /**
394
+ * A set of server-generated dependencies that are treated as external.
395
+ *
396
+ * These dependencies are marked as external because they are produced by a
397
+ * separate bundling process and are not included in the primary bundle. This
398
+ * ensures that these generated files are resolved from an external source rather
399
+ * than being part of the main bundle.
400
+ */
401
+ exports.SERVER_GENERATED_EXTERNALS = new Set([
402
+ './polyfills.server.mjs',
403
+ './' + manifest_1.SERVER_APP_MANIFEST_FILENAME,
404
+ './' + manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME,
405
+ ]);
@@ -34,17 +34,22 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
34
34
  env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
35
35
  env.hasError = true;
36
36
  }
37
+ var r, s = 0;
37
38
  function next() {
38
- while (env.stack.length) {
39
- var rec = env.stack.pop();
39
+ while (r = env.stack.pop()) {
40
40
  try {
41
- var result = rec.dispose && rec.dispose.call(rec.value);
42
- if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
41
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
42
+ if (r.dispose) {
43
+ var result = r.dispose.call(r.value);
44
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
45
+ }
46
+ else s |= 1;
43
47
  }
44
48
  catch (e) {
45
49
  fail(e);
46
50
  }
47
51
  }
52
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
48
53
  if (env.hasError) throw env.error;
49
54
  }
50
55
  return next();
@@ -61,8 +66,8 @@ exports.SassWorkerImplementation = void 0;
61
66
  const node_assert_1 = __importDefault(require("node:assert"));
62
67
  const node_url_1 = require("node:url");
63
68
  const node_worker_threads_1 = require("node:worker_threads");
64
- const piscina_1 = require("piscina");
65
69
  const environment_options_1 = require("../../utils/environment-options");
70
+ const worker_pool_1 = require("../../utils/worker-pool");
66
71
  // Polyfill Symbol.dispose if not present
67
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
73
  Symbol.dispose ??= Symbol('Symbol Dispose');
@@ -85,16 +90,9 @@ class SassWorkerImplementation {
85
90
  this.maxThreads = maxThreads;
86
91
  }
87
92
  #ensureWorkerPool() {
88
- this.#workerPool ??= new piscina_1.Piscina({
93
+ this.#workerPool ??= new worker_pool_1.WorkerPool({
89
94
  filename: require.resolve('./worker'),
90
- minThreads: 1,
91
95
  maxThreads: this.maxThreads,
92
- // Web containers do not support transferable objects with receiveOnMessagePort which
93
- // is used when the Atomics based wait loop is enable.
94
- useAtomics: !process.versions.webcontainer,
95
- // Shutdown idle threads after 1 second of inactivity
96
- idleTimeout: 1000,
97
- recordTiming: false,
98
96
  });
99
97
  return this.#workerPool;
100
98
  }
@@ -5,6 +5,7 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
+ import { RawSourceMap } from '@ampproject/remapping';
8
9
  import { MessagePort } from 'node:worker_threads';
9
10
  import { SourceSpan, StringOptions } from 'sass';
10
11
  import type { SerializableWarningMessage } from './sass-service';
@@ -38,45 +39,25 @@ interface RenderRequestMessage {
38
39
  */
39
40
  rebase: boolean;
40
41
  }
41
- export default function renderSassStylesheet(request: RenderRequestMessage): Promise<{
42
+ interface RenderResult {
42
43
  warnings: SerializableWarningMessage[] | undefined;
43
44
  result: {
44
- loadedUrls: string[];
45
45
  css: string;
46
- sourceMap?: import("source-map-js").RawSourceMap;
46
+ loadedUrls: string[];
47
+ sourceMap?: RawSourceMap;
47
48
  };
48
- error?: undefined;
49
- } | {
49
+ }
50
+ interface RenderError {
50
51
  warnings: SerializableWarningMessage[] | undefined;
51
52
  error: {
52
- span: Omit<SourceSpan, "url"> & {
53
+ message: string;
54
+ stack?: string;
55
+ span?: Omit<SourceSpan, 'url'> & {
53
56
  url?: string;
54
57
  };
55
- message: string;
56
- stack: string | undefined;
57
- sassMessage: string;
58
- sassStack: string;
59
- };
60
- result?: undefined;
61
- } | {
62
- warnings: SerializableWarningMessage[] | undefined;
63
- error: {
64
- message: string;
65
- stack: string | undefined;
66
- span?: undefined;
67
- sassMessage?: undefined;
68
- sassStack?: undefined;
58
+ sassMessage?: string;
59
+ sassStack?: string;
69
60
  };
70
- result?: undefined;
71
- } | {
72
- warnings: SerializableWarningMessage[] | undefined;
73
- error: {
74
- message: string;
75
- span?: undefined;
76
- stack?: undefined;
77
- sassMessage?: undefined;
78
- sassStack?: undefined;
79
- };
80
- result?: undefined;
81
- }>;
61
+ }
62
+ export default function renderSassStylesheet(request: RenderRequestMessage): Promise<RenderResult | RenderError>;
82
63
  export {};
@@ -95,6 +95,7 @@ async function renderSassStylesheet(request) {
95
95
  warnings,
96
96
  result: {
97
97
  ...result,
98
+ sourceMap: result.sourceMap,
98
99
  // URL is not serializable so to convert to string here and back to URL in the parent.
99
100
  loadedUrls: result.loadedUrls.map((p) => (0, node_url_1.fileURLToPath)(p)),
100
101
  },
@@ -7,4 +7,4 @@
7
7
  */
8
8
  import type { Connect, ViteDevServer } from 'vite';
9
9
  import { AngularMemoryOutputFiles } from '../utils';
10
- export declare function createAngularAssetsMiddleware(server: ViteDevServer, assets: Map<string, string>, outputFiles: AngularMemoryOutputFiles): Connect.NextHandleFunction;
10
+ export declare function createAngularAssetsMiddleware(server: ViteDevServer, assets: Map<string, string>, outputFiles: AngularMemoryOutputFiles, usedComponentStyles: Map<string, string[]>): Connect.NextHandleFunction;
@@ -10,9 +10,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createAngularAssetsMiddleware = createAngularAssetsMiddleware;
11
11
  const mrmime_1 = require("mrmime");
12
12
  const node_path_1 = require("node:path");
13
+ const load_esm_1 = require("../../../utils/load-esm");
13
14
  const utils_1 = require("../utils");
14
- function createAngularAssetsMiddleware(server, assets, outputFiles) {
15
- return function (req, res, next) {
15
+ function createAngularAssetsMiddleware(server, assets, outputFiles, usedComponentStyles) {
16
+ return function angularAssetsMiddleware(req, res, next) {
16
17
  if (req.url === undefined || res.writableEnded) {
17
18
  return;
18
19
  }
@@ -47,19 +48,57 @@ function createAngularAssetsMiddleware(server, assets, outputFiles) {
47
48
  next();
48
49
  return;
49
50
  }
51
+ // Support HTTP HEAD requests for the virtual output files from the Angular build
52
+ if (req.method === 'HEAD' && outputFiles.get(pathname)?.servable) {
53
+ // While a GET will also generate content, the rest of the response is equivalent
54
+ req.method = 'GET';
55
+ }
50
56
  // Resource files are handled directly.
51
57
  // Global stylesheets (CSS files) are currently considered resources to workaround
52
58
  // dev server sourcemap issues with stylesheets.
53
59
  if (extension !== '.js' && extension !== '.html') {
54
60
  const outputFile = outputFiles.get(pathname);
55
61
  if (outputFile?.servable) {
62
+ const data = outputFile.contents;
63
+ if (extension === '.css') {
64
+ // Inject component ID for view encapsulation if requested
65
+ const componentId = new URL(req.url, 'http://localhost').searchParams.get('ngcomp');
66
+ if (componentId !== null) {
67
+ // Record the component style usage for HMR updates
68
+ const usedIds = usedComponentStyles.get(pathname);
69
+ if (usedIds === undefined) {
70
+ usedComponentStyles.set(pathname, [componentId]);
71
+ }
72
+ else {
73
+ usedIds.push(componentId);
74
+ }
75
+ // Shim the stylesheet if a component ID is provided
76
+ if (componentId.length > 0) {
77
+ // Validate component ID
78
+ if (/^[_.\-\p{Letter}\d]+-c\d{9}$/u.test(componentId)) {
79
+ (0, load_esm_1.loadEsmModule)('@angular/compiler')
80
+ .then((compilerModule) => {
81
+ const encapsulatedData = compilerModule.encapsulateStyle(new TextDecoder().decode(data), componentId);
82
+ res.setHeader('Content-Type', 'text/css');
83
+ res.setHeader('Cache-Control', 'no-cache');
84
+ res.end(encapsulatedData);
85
+ })
86
+ .catch((e) => next(e));
87
+ return;
88
+ }
89
+ else {
90
+ // eslint-disable-next-line no-console
91
+ console.error('Invalid component stylesheet ID request: ' + componentId);
92
+ }
93
+ }
94
+ }
95
+ }
56
96
  const mimeType = (0, mrmime_1.lookup)(extension);
57
97
  if (mimeType) {
58
98
  res.setHeader('Content-Type', mimeType);
59
99
  }
60
100
  res.setHeader('Cache-Control', 'no-cache');
61
- (0, utils_1.appendServerConfiguredHeaders)(server, res);
62
- res.end(outputFile.contents);
101
+ res.end(data);
63
102
  return;
64
103
  }
65
104
  }