@angular/build 19.0.0-next.6 → 19.0.0-next.8

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 (83) hide show
  1. package/package.json +11 -11
  2. package/src/builders/application/execute-build.js +19 -2
  3. package/src/builders/application/execute-post-bundle.d.ts +2 -2
  4. package/src/builders/application/execute-post-bundle.js +30 -11
  5. package/src/builders/application/i18n.d.ts +2 -2
  6. package/src/builders/application/i18n.js +4 -5
  7. package/src/builders/application/index.js +8 -5
  8. package/src/builders/application/options.d.ts +18 -1
  9. package/src/builders/application/options.js +30 -2
  10. package/src/builders/application/schema.d.ts +15 -0
  11. package/src/builders/application/schema.js +11 -1
  12. package/src/builders/application/schema.json +5 -0
  13. package/src/builders/application/setup-bundling.js +6 -3
  14. package/src/builders/dev-server/vite-server.d.ts +2 -1
  15. package/src/builders/dev-server/vite-server.js +64 -47
  16. package/src/builders/extract-i18n/application-extraction.js +3 -3
  17. package/src/tools/angular/angular-host.d.ts +2 -1
  18. package/src/tools/angular/angular-host.js +20 -1
  19. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  20. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  21. package/src/tools/angular/compilation/aot-compilation.js +9 -1
  22. package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -0
  23. package/src/tools/angular/compilation/parallel-compilation.js +2 -10
  24. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  25. package/src/tools/angular/compilation/parallel-worker.js +2 -1
  26. package/src/tools/esbuild/angular/compiler-plugin.d.ts +1 -0
  27. package/src/tools/esbuild/angular/compiler-plugin.js +42 -3
  28. package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
  29. package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
  30. package/src/tools/esbuild/application-code-bundle.d.ts +1 -0
  31. package/src/tools/esbuild/application-code-bundle.js +109 -2
  32. package/src/tools/esbuild/budget-stats.js +1 -1
  33. package/src/tools/esbuild/bundler-context.d.ts +4 -3
  34. package/src/tools/esbuild/bundler-context.js +8 -4
  35. package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
  36. package/src/tools/esbuild/bundler-execution-result.js +7 -3
  37. package/src/tools/esbuild/cache.d.ts +5 -0
  38. package/src/tools/esbuild/cache.js +7 -0
  39. package/src/tools/esbuild/compiler-plugin-options.js +2 -1
  40. package/src/tools/esbuild/i18n-inliner.js +4 -4
  41. package/src/tools/esbuild/javascript-transformer.js +2 -9
  42. package/src/tools/esbuild/utils.js +7 -3
  43. package/src/tools/sass/sass-service.js +2 -9
  44. package/src/tools/vite/middlewares/assets-middleware.js +8 -8
  45. package/src/tools/vite/middlewares/headers-middleware.d.ts +19 -0
  46. package/src/tools/vite/middlewares/headers-middleware.js +34 -0
  47. package/src/tools/vite/middlewares/html-fallback-middleware.d.ts +1 -1
  48. package/src/tools/vite/middlewares/html-fallback-middleware.js +23 -7
  49. package/src/tools/vite/middlewares/index-html-middleware.js +1 -2
  50. package/src/tools/vite/middlewares/index.d.ts +2 -1
  51. package/src/tools/vite/middlewares/index.js +5 -2
  52. package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -1
  53. package/src/tools/vite/middlewares/ssr-middleware.js +61 -17
  54. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
  55. package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -40
  56. package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
  57. package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
  58. package/src/tools/vite/plugins/index.d.ts +12 -0
  59. package/src/tools/vite/plugins/index.js +21 -0
  60. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
  61. package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
  62. package/src/tools/vite/plugins/ssr-transform-plugin.d.ts +9 -0
  63. package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
  64. package/src/tools/vite/utils.d.ts +0 -3
  65. package/src/tools/vite/utils.js +0 -12
  66. package/src/typings.d.ts +7 -0
  67. package/src/utils/environment-options.d.ts +2 -0
  68. package/src/utils/environment-options.js +5 -1
  69. package/src/utils/index-file/valid-self-closing-tags.js +1 -0
  70. package/src/utils/normalize-cache.js +1 -1
  71. package/src/utils/server-rendering/manifest.d.ts +8 -2
  72. package/src/utils/server-rendering/manifest.js +61 -12
  73. package/src/utils/server-rendering/models.d.ts +27 -0
  74. package/src/utils/server-rendering/models.js +22 -0
  75. package/src/utils/server-rendering/prerender.d.ts +6 -10
  76. package/src/utils/server-rendering/prerender.js +103 -71
  77. package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -6
  78. package/src/utils/server-rendering/routes-extractor-worker.js +7 -5
  79. package/src/utils/worker-pool.d.ts +12 -0
  80. package/src/utils/worker-pool.js +43 -0
  81. package/src/tools/vite/angular-memory-plugin.d.ts +0 -22
  82. /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
  83. /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
@@ -11,6 +11,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.ComponentStylesheetBundler = void 0;
14
+ const node_assert_1 = __importDefault(require("node:assert"));
14
15
  const node_crypto_1 = require("node:crypto");
15
16
  const node_path_1 = __importDefault(require("node:path"));
16
17
  const bundler_context_1 = require("../bundler-context");
@@ -35,21 +36,31 @@ class ComponentStylesheetBundler {
35
36
  this.options = options;
36
37
  this.incremental = incremental;
37
38
  }
38
- async bundleFile(entry) {
39
+ async bundleFile(entry, externalId) {
39
40
  const bundlerContext = await this.#fileContexts.getOrCreate(entry, () => {
40
41
  return new bundler_context_1.BundlerContext(this.options.workspaceRoot, this.incremental, (loadCache) => {
41
42
  const buildOptions = (0, bundle_options_1.createStylesheetBundleOptions)(this.options, loadCache);
42
- buildOptions.entryPoints = [entry];
43
+ if (externalId) {
44
+ (0, node_assert_1.default)(typeof externalId === 'string', 'Initial external component stylesheets must have a string identifier');
45
+ buildOptions.entryPoints = { [externalId]: entry };
46
+ delete buildOptions.publicPath;
47
+ }
48
+ else {
49
+ buildOptions.entryPoints = [entry];
50
+ }
43
51
  return buildOptions;
44
52
  });
45
53
  });
46
- return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
54
+ return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles, !!externalId);
47
55
  }
48
- async bundleInline(data, filename, language) {
56
+ async bundleInline(data, filename, language, externalId) {
49
57
  // Use a hash of the inline stylesheet content to ensure a consistent identifier. External stylesheets will resolve
50
58
  // to the actual stylesheet file path.
51
59
  // TODO: Consider xxhash instead for hashing
52
- const id = (0, node_crypto_1.createHash)('sha256').update(data).digest('hex');
60
+ const id = (0, node_crypto_1.createHash)('sha256')
61
+ .update(data)
62
+ .update(externalId ?? '')
63
+ .digest('hex');
53
64
  const entry = [language, id, filename].join(';');
54
65
  const bundlerContext = await this.#inlineContexts.getOrCreate(entry, () => {
55
66
  const namespace = 'angular:styles/component';
@@ -57,7 +68,13 @@ class ComponentStylesheetBundler {
57
68
  const buildOptions = (0, bundle_options_1.createStylesheetBundleOptions)(this.options, loadCache, {
58
69
  [entry]: data,
59
70
  });
60
- buildOptions.entryPoints = [`${namespace};${entry}`];
71
+ if (externalId) {
72
+ buildOptions.entryPoints = { [externalId]: `${namespace};${entry}` };
73
+ delete buildOptions.publicPath;
74
+ }
75
+ else {
76
+ buildOptions.entryPoints = [`${namespace};${entry}`];
77
+ }
61
78
  buildOptions.plugins.push({
62
79
  name: 'angular-component-styles',
63
80
  setup(build) {
@@ -83,19 +100,29 @@ class ComponentStylesheetBundler {
83
100
  });
84
101
  });
85
102
  // Extract the result of the bundling from the output files
86
- return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
103
+ return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles, !!externalId);
87
104
  }
105
+ /**
106
+ * Invalidates both file and inline based component style bundling state for a set of modified files.
107
+ * @param files The group of files that have been modified
108
+ * @returns An array of file based stylesheet entries if any were invalidated; otherwise, undefined.
109
+ */
88
110
  invalidate(files) {
89
111
  if (!this.incremental) {
90
112
  return;
91
113
  }
92
114
  const normalizedFiles = [...files].map(node_path_1.default.normalize);
93
- for (const bundler of this.#fileContexts.values()) {
94
- bundler.invalidate(normalizedFiles);
115
+ let entries;
116
+ for (const [entry, bundler] of this.#fileContexts.entries()) {
117
+ if (bundler.invalidate(normalizedFiles)) {
118
+ entries ??= [];
119
+ entries.push(entry);
120
+ }
95
121
  }
96
122
  for (const bundler of this.#inlineContexts.values()) {
97
123
  bundler.invalidate(normalizedFiles);
98
124
  }
125
+ return entries;
99
126
  }
100
127
  async dispose() {
101
128
  const contexts = [...this.#fileContexts.values(), ...this.#inlineContexts.values()];
@@ -103,7 +130,7 @@ class ComponentStylesheetBundler {
103
130
  this.#inlineContexts.clear();
104
131
  await Promise.allSettled(contexts.map((context) => context.dispose()));
105
132
  }
106
- extractResult(result, referencedFiles) {
133
+ extractResult(result, referencedFiles, external) {
107
134
  let contents = '';
108
135
  let metafile;
109
136
  const outputFiles = [];
@@ -122,7 +149,15 @@ class ComponentStylesheetBundler {
122
149
  outputFiles.push(clonedOutputFile);
123
150
  }
124
151
  else if (filename.endsWith('.css')) {
125
- contents = outputFile.text;
152
+ if (external) {
153
+ const clonedOutputFile = outputFile.clone();
154
+ clonedOutputFile.path = node_path_1.default.join(this.options.workspaceRoot, outputFile.path);
155
+ outputFiles.push(clonedOutputFile);
156
+ contents = node_path_1.default.posix.join(this.options.publicPath ?? '', filename);
157
+ }
158
+ else {
159
+ contents = outputFile.text;
160
+ }
126
161
  }
127
162
  else {
128
163
  throw new Error(`Unexpected non CSS/Media file "${filename}" outputted during component stylesheet processing.`);
@@ -13,3 +13,4 @@ export declare function createBrowserCodeBundleOptions(options: NormalizedApplic
13
13
  export declare function createBrowserPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | BundlerOptionsFactory | undefined;
14
14
  export declare function createServerPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BundlerOptionsFactory | undefined;
15
15
  export declare function createServerMainCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache: SourceFileCache): BuildOptions;
16
+ export declare function createSsrEntryCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache: SourceFileCache): BuildOptions;
@@ -14,6 +14,7 @@ exports.createBrowserCodeBundleOptions = createBrowserCodeBundleOptions;
14
14
  exports.createBrowserPolyfillBundleOptions = createBrowserPolyfillBundleOptions;
15
15
  exports.createServerPolyfillBundleOptions = createServerPolyfillBundleOptions;
16
16
  exports.createServerMainCodeBundleOptions = createServerMainCodeBundleOptions;
17
+ exports.createSsrEntryCodeBundleOptions = createSsrEntryCodeBundleOptions;
17
18
  const node_assert_1 = __importDefault(require("node:assert"));
18
19
  const node_crypto_1 = require("node:crypto");
19
20
  const node_path_1 = require("node:path");
@@ -156,7 +157,7 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
156
157
  return () => buildOptions;
157
158
  }
158
159
  function createServerMainCodeBundleOptions(options, target, sourceFileCache) {
159
- const { serverEntryPoint: mainServerEntryPoint, workspaceRoot, externalPackages, ssrOptions, polyfills, } = options;
160
+ const { serverEntryPoint: mainServerEntryPoint, workspaceRoot, outputMode, externalPackages, ssrOptions, polyfills, } = options;
160
161
  (0, node_assert_1.default)(mainServerEntryPoint, 'createServerCodeBundleOptions should not be called without a defined serverEntryPoint.');
161
162
  const { pluginOptions, styleOptions } = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, target, sourceFileCache);
162
163
  const mainServerNamespace = 'angular:main-server';
@@ -167,7 +168,8 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache) {
167
168
  'main.server': mainServerNamespace,
168
169
  };
169
170
  const ssrEntryPoint = ssrOptions?.entry;
170
- if (ssrEntryPoint) {
171
+ const isOldBehaviour = !outputMode;
172
+ if (ssrEntryPoint && isOldBehaviour) {
171
173
  // Old behavior: 'server.ts' was bundled together with the SSR (Server-Side Rendering) code.
172
174
  // This approach combined server-side logic and rendering into a single bundle.
173
175
  entryPoints['server'] = ssrEntryPoint;
@@ -249,6 +251,111 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache) {
249
251
  }
250
252
  return buildOptions;
251
253
  }
254
+ function createSsrEntryCodeBundleOptions(options, target, sourceFileCache) {
255
+ const { workspaceRoot, ssrOptions, externalPackages } = options;
256
+ const serverEntryPoint = ssrOptions?.entry;
257
+ (0, node_assert_1.default)(serverEntryPoint, 'createSsrEntryCodeBundleOptions should not be called without a defined serverEntryPoint.');
258
+ const { pluginOptions, styleOptions } = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, target, sourceFileCache);
259
+ const ssrEntryNamespace = 'angular:ssr-entry';
260
+ const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest';
261
+ const ssrInjectRequireNamespace = 'angular:ssr-entry-inject-require';
262
+ const buildOptions = {
263
+ ...getEsBuildServerCommonOptions(options),
264
+ target,
265
+ entryPoints: {
266
+ // TODO: consider renaming to index
267
+ 'server': ssrEntryNamespace,
268
+ },
269
+ supported: (0, utils_1.getFeatureSupport)(target, true),
270
+ plugins: [
271
+ (0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
272
+ (0, compiler_plugin_1.createCompilerPlugin)(
273
+ // JS/TS options
274
+ { ...pluginOptions, noopTypeScriptCompilation: true },
275
+ // Component stylesheet options
276
+ styleOptions),
277
+ ],
278
+ inject: [ssrInjectRequireNamespace, ssrInjectManifestNamespace],
279
+ };
280
+ buildOptions.plugins ??= [];
281
+ if (externalPackages) {
282
+ buildOptions.packages = 'external';
283
+ }
284
+ else {
285
+ buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
286
+ }
287
+ // Mark manifest file as external. As this will be generated later on.
288
+ (buildOptions.external ??= []).push('*/main.server.mjs', ...utils_1.SERVER_GENERATED_EXTERNALS);
289
+ buildOptions.plugins.push({
290
+ name: 'angular-ssr-metadata',
291
+ setup(build) {
292
+ build.onEnd((result) => {
293
+ if (result.metafile) {
294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
295
+ result.metafile['ng-ssr-entry-bundle'] = true;
296
+ }
297
+ });
298
+ },
299
+ }, (0, virtual_module_plugin_1.createVirtualModulePlugin)({
300
+ namespace: ssrInjectRequireNamespace,
301
+ cache: sourceFileCache?.loadResultCache,
302
+ loadContent: () => {
303
+ const contents = [
304
+ // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
305
+ // See: https://github.com/evanw/esbuild/issues/1921.
306
+ `import { createRequire } from 'node:module';`,
307
+ `globalThis['require'] ??= createRequire(import.meta.url);`,
308
+ ];
309
+ return {
310
+ contents: contents.join('\n'),
311
+ loader: 'js',
312
+ resolveDir: workspaceRoot,
313
+ };
314
+ },
315
+ }), (0, virtual_module_plugin_1.createVirtualModulePlugin)({
316
+ namespace: ssrInjectManifestNamespace,
317
+ cache: sourceFileCache?.loadResultCache,
318
+ loadContent: () => {
319
+ const contents = [
320
+ // Configure `@angular/ssr` app engine manifest.
321
+ `import manifest from './${manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME}';`,
322
+ `import { ɵsetAngularAppEngineManifest } from '@angular/ssr';`,
323
+ `ɵsetAngularAppEngineManifest(manifest);`,
324
+ ];
325
+ return {
326
+ contents: contents.join('\n'),
327
+ loader: 'js',
328
+ resolveDir: workspaceRoot,
329
+ };
330
+ },
331
+ }), (0, virtual_module_plugin_1.createVirtualModulePlugin)({
332
+ namespace: ssrEntryNamespace,
333
+ cache: sourceFileCache?.loadResultCache,
334
+ loadContent: () => {
335
+ const serverEntryPointJsImport = entryFileToWorkspaceRelative(workspaceRoot, serverEntryPoint);
336
+ const contents = [
337
+ // Re-export all symbols including default export
338
+ `import * as server from '${serverEntryPointJsImport}';`,
339
+ `export * from '${serverEntryPointJsImport}';`,
340
+ // The below is needed to avoid
341
+ // `Import "default" will always be undefined because there is no matching export` warning when no default is present.
342
+ `const defaultExportName = 'default';`,
343
+ `export default server[defaultExportName]`,
344
+ // Add @angular/ssr exports
345
+ `export { AngularAppEngine } from '@angular/ssr';`,
346
+ ];
347
+ return {
348
+ contents: contents.join('\n'),
349
+ loader: 'js',
350
+ resolveDir: workspaceRoot,
351
+ };
352
+ },
353
+ }));
354
+ if (options.plugins) {
355
+ buildOptions.plugins.push(...options.plugins);
356
+ }
357
+ return buildOptions;
358
+ }
252
359
  function getEsBuildServerCommonOptions(options) {
253
360
  return {
254
361
  ...getEsBuildCommonOptions(options),
@@ -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
  /**
@@ -292,7 +293,10 @@ class BundlerContext {
292
293
  fileType = BuildOutputFileType.Media;
293
294
  }
294
295
  else if (this.#platformIsServer) {
295
- fileType = BuildOutputFileType.Server;
296
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
297
+ fileType = result.metafile['ng-ssr-entry-bundle']
298
+ ? BuildOutputFileType.ServerRoot
299
+ : BuildOutputFileType.ServerApplication;
296
300
  }
297
301
  else {
298
302
  fileType = BuildOutputFileType.Browser;
@@ -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') {
@@ -85,4 +85,9 @@ export declare class MemoryCache<V> extends Cache<V, Map<string, V>> {
85
85
  * @returns An iterable of all values in the cache.
86
86
  */
87
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, } = options;
13
13
  return {
14
14
  // JS/TS options
15
15
  pluginOptions: {
@@ -22,6 +22,7 @@ function createCompilerPluginOptions(options, target, sourceFileCache) {
22
22
  sourceFileCache,
23
23
  loadResultCache: sourceFileCache?.loadResultCache,
24
24
  incremental: !!options.watch,
25
+ externalRuntimeStyles,
25
26
  },
26
27
  // Component stylesheet options
27
28
  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
  /**
@@ -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
  }
@@ -30,6 +30,7 @@ const node_path_1 = require("node:path");
30
30
  const node_url_1 = require("node:url");
31
31
  const node_zlib_1 = require("node:zlib");
32
32
  const semver_1 = require("semver");
33
+ const schema_1 = require("../../builders/application/schema");
33
34
  const manifest_1 = require("../../utils/server-rendering/manifest");
34
35
  const stats_table_1 = require("../../utils/stats-table");
35
36
  const bundler_context_1 = require("./bundler-context");
@@ -47,7 +48,7 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
47
48
  ++unchangedCount;
48
49
  continue;
49
50
  }
50
- const isPlatformServer = type === bundler_context_1.BuildOutputFileType.Server;
51
+ const isPlatformServer = type === bundler_context_1.BuildOutputFileType.ServerApplication || type === bundler_context_1.BuildOutputFileType.ServerRoot;
51
52
  if (isPlatformServer && !ssrOutputEnabled) {
52
53
  // Only log server build stats when SSR is enabled.
53
54
  continue;
@@ -342,7 +343,7 @@ function getSupportedNodeTargets() {
342
343
  return SUPPORTED_NODE_VERSIONS.split('||').map((v) => 'node' + (0, semver_1.coerce)(v)?.version);
343
344
  }
344
345
  async function createJsonBuildManifest(result, normalizedOptions) {
345
- const { colors: color, outputOptions: { base, server, browser }, ssrOptions, } = normalizedOptions;
346
+ const { colors: color, outputOptions: { base, server, browser }, ssrOptions, outputMode, } = normalizedOptions;
346
347
  const { warnings, errors, prerenderedRoutes } = result;
347
348
  const manifest = {
348
349
  errors: errors.length ? await (0, esbuild_1.formatMessages)(errors, { kind: 'error', color }) : [],
@@ -350,7 +351,9 @@ async function createJsonBuildManifest(result, normalizedOptions) {
350
351
  outputPaths: {
351
352
  root: (0, node_url_1.pathToFileURL)(base),
352
353
  browser: (0, node_url_1.pathToFileURL)((0, node_path_1.join)(base, browser)),
353
- 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,
354
357
  },
355
358
  prerenderedRoutes,
356
359
  };
@@ -398,4 +401,5 @@ function getEntryPointName(entryPoint) {
398
401
  exports.SERVER_GENERATED_EXTERNALS = new Set([
399
402
  './polyfills.server.mjs',
400
403
  './' + manifest_1.SERVER_APP_MANIFEST_FILENAME,
404
+ './' + manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME,
401
405
  ]);
@@ -66,8 +66,8 @@ exports.SassWorkerImplementation = void 0;
66
66
  const node_assert_1 = __importDefault(require("node:assert"));
67
67
  const node_url_1 = require("node:url");
68
68
  const node_worker_threads_1 = require("node:worker_threads");
69
- const piscina_1 = require("piscina");
70
69
  const environment_options_1 = require("../../utils/environment-options");
70
+ const worker_pool_1 = require("../../utils/worker-pool");
71
71
  // Polyfill Symbol.dispose if not present
72
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
73
  Symbol.dispose ??= Symbol('Symbol Dispose');
@@ -90,16 +90,9 @@ class SassWorkerImplementation {
90
90
  this.maxThreads = maxThreads;
91
91
  }
92
92
  #ensureWorkerPool() {
93
- this.#workerPool ??= new piscina_1.Piscina({
93
+ this.#workerPool ??= new worker_pool_1.WorkerPool({
94
94
  filename: require.resolve('./worker'),
95
- minThreads: 1,
96
95
  maxThreads: this.maxThreads,
97
- // Web containers do not support transferable objects with receiveOnMessagePort which
98
- // is used when the Atomics based wait loop is enable.
99
- useAtomics: !process.versions.webcontainer,
100
- // Shutdown idle threads after 1 second of inactivity
101
- idleTimeout: 1000,
102
- recordTiming: false,
103
96
  });
104
97
  return this.#workerPool;
105
98
  }
@@ -12,9 +12,8 @@ const mrmime_1 = require("mrmime");
12
12
  const node_path_1 = require("node:path");
13
13
  const load_esm_1 = require("../../../utils/load-esm");
14
14
  const utils_1 = require("../utils");
15
- const COMPONENT_REGEX = /%COMP%/g;
16
15
  function createAngularAssetsMiddleware(server, assets, outputFiles, usedComponentStyles) {
17
- return function (req, res, next) {
16
+ return function angularAssetsMiddleware(req, res, next) {
18
17
  if (req.url === undefined || res.writableEnded) {
19
18
  return;
20
19
  }
@@ -49,6 +48,11 @@ function createAngularAssetsMiddleware(server, assets, outputFiles, usedComponen
49
48
  next();
50
49
  return;
51
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
+ }
52
56
  // Resource files are handled directly.
53
57
  // Global stylesheets (CSS files) are currently considered resources to workaround
54
58
  // dev server sourcemap issues with stylesheets.
@@ -71,15 +75,12 @@ function createAngularAssetsMiddleware(server, assets, outputFiles, usedComponen
71
75
  // Shim the stylesheet if a component ID is provided
72
76
  if (componentId.length > 0) {
73
77
  // Validate component ID
74
- if (/[_.-A-Za-z0-9]+-c\d{9}$/.test(componentId)) {
78
+ if (/^[_.\-\p{Letter}\d]+-c\d{9}$/u.test(componentId)) {
75
79
  (0, load_esm_1.loadEsmModule)('@angular/compiler')
76
80
  .then((compilerModule) => {
77
- const encapsulatedData = compilerModule
78
- .encapsulateStyle(new TextDecoder().decode(data))
79
- .replaceAll(COMPONENT_REGEX, componentId);
81
+ const encapsulatedData = compilerModule.encapsulateStyle(new TextDecoder().decode(data), componentId);
80
82
  res.setHeader('Content-Type', 'text/css');
81
83
  res.setHeader('Cache-Control', 'no-cache');
82
- (0, utils_1.appendServerConfiguredHeaders)(server, res);
83
84
  res.end(encapsulatedData);
84
85
  })
85
86
  .catch((e) => next(e));
@@ -97,7 +98,6 @@ function createAngularAssetsMiddleware(server, assets, outputFiles, usedComponen
97
98
  res.setHeader('Content-Type', mimeType);
98
99
  }
99
100
  res.setHeader('Cache-Control', 'no-cache');
100
- (0, utils_1.appendServerConfiguredHeaders)(server, res);
101
101
  res.end(data);
102
102
  return;
103
103
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { Connect, ViteDevServer } from 'vite';
9
+ /**
10
+ * Creates a middleware for adding custom headers.
11
+ *
12
+ * This middleware is responsible for setting HTTP headers as configured in the Vite server options.
13
+ * If headers are defined in the server configuration, they are applied to the server response.
14
+ *
15
+ * @param server - The instance of `ViteDevServer` containing the configuration, including custom headers.
16
+ * @returns A middleware function that processes the incoming request, sets headers if available,
17
+ * and passes control to the next middleware in the chain.
18
+ */
19
+ export declare function createAngularHeadersMiddleware(server: ViteDevServer): Connect.NextHandleFunction;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createAngularHeadersMiddleware = createAngularHeadersMiddleware;
11
+ /**
12
+ * Creates a middleware for adding custom headers.
13
+ *
14
+ * This middleware is responsible for setting HTTP headers as configured in the Vite server options.
15
+ * If headers are defined in the server configuration, they are applied to the server response.
16
+ *
17
+ * @param server - The instance of `ViteDevServer` containing the configuration, including custom headers.
18
+ * @returns A middleware function that processes the incoming request, sets headers if available,
19
+ * and passes control to the next middleware in the chain.
20
+ */
21
+ function createAngularHeadersMiddleware(server) {
22
+ return function angularHeadersMiddleware(_req, res, next) {
23
+ const headers = server.config.server.headers;
24
+ if (!headers) {
25
+ return next();
26
+ }
27
+ for (const [name, value] of Object.entries(headers)) {
28
+ if (value !== undefined) {
29
+ res.setHeader(name, value);
30
+ }
31
+ }
32
+ next();
33
+ };
34
+ }