@angular/build 19.0.0-next.9 → 19.0.0-rc.1

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 (90) hide show
  1. package/package.json +21 -19
  2. package/src/builders/application/build-action.js +22 -10
  3. package/src/builders/application/chunk-optimizer.js +1 -4
  4. package/src/builders/application/execute-build.js +59 -24
  5. package/src/builders/application/execute-post-bundle.js +28 -5
  6. package/src/builders/application/index.d.ts +0 -16
  7. package/src/builders/application/index.js +15 -10
  8. package/src/builders/application/options.d.ts +14 -2
  9. package/src/builders/application/options.js +25 -10
  10. package/src/builders/application/results.d.ts +5 -3
  11. package/src/builders/application/schema.d.ts +86 -0
  12. package/src/builders/application/schema.js +19 -1
  13. package/src/builders/application/schema.json +73 -4
  14. package/src/builders/application/setup-bundling.d.ts +6 -1
  15. package/src/builders/application/setup-bundling.js +47 -13
  16. package/src/builders/dev-server/options.d.ts +2 -2
  17. package/src/builders/dev-server/options.js +2 -2
  18. package/src/builders/dev-server/schema.d.ts +2 -1
  19. package/src/builders/dev-server/schema.json +1 -2
  20. package/src/builders/dev-server/vite-server.d.ts +3 -2
  21. package/src/builders/dev-server/vite-server.js +123 -61
  22. package/src/index.d.ts +1 -0
  23. package/src/tools/angular/angular-host.d.ts +1 -1
  24. package/src/tools/angular/angular-host.js +14 -6
  25. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  26. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  27. package/src/tools/angular/compilation/aot-compilation.js +39 -0
  28. package/src/tools/angular/compilation/parallel-compilation.js +2 -2
  29. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  30. package/src/tools/angular/compilation/parallel-worker.js +5 -2
  31. package/src/tools/esbuild/angular/compiler-plugin.d.ts +3 -4
  32. package/src/tools/esbuild/angular/compiler-plugin.js +58 -33
  33. package/src/tools/esbuild/angular/component-stylesheets.d.ts +18 -18
  34. package/src/tools/esbuild/angular/component-stylesheets.js +66 -38
  35. package/src/tools/esbuild/angular/jit-plugin-callbacks.d.ts +1 -1
  36. package/src/tools/esbuild/angular/jit-plugin-callbacks.js +11 -3
  37. package/src/tools/esbuild/angular/source-file-cache.d.ts +1 -1
  38. package/src/tools/esbuild/angular/source-file-cache.js +6 -2
  39. package/src/tools/esbuild/application-code-bundle.d.ts +7 -5
  40. package/src/tools/esbuild/application-code-bundle.js +280 -249
  41. package/src/tools/esbuild/bundler-context.d.ts +2 -1
  42. package/src/tools/esbuild/bundler-context.js +10 -12
  43. package/src/tools/esbuild/bundler-execution-result.d.ts +14 -3
  44. package/src/tools/esbuild/bundler-execution-result.js +15 -8
  45. package/src/tools/esbuild/commonjs-checker.js +2 -2
  46. package/src/tools/esbuild/compiler-plugin-options.d.ts +2 -4
  47. package/src/tools/esbuild/compiler-plugin-options.js +15 -37
  48. package/src/tools/esbuild/global-scripts.js +1 -1
  49. package/src/tools/esbuild/global-styles.js +4 -1
  50. package/src/tools/esbuild/index-html-generator.js +8 -0
  51. package/src/tools/esbuild/javascript-transformer.js +3 -0
  52. package/src/tools/esbuild/server-bundle-metadata-plugin.d.ts +22 -0
  53. package/src/tools/esbuild/server-bundle-metadata-plugin.js +36 -0
  54. package/src/tools/esbuild/stylesheets/bundle-options.d.ts +2 -0
  55. package/src/tools/esbuild/stylesheets/bundle-options.js +2 -1
  56. package/src/tools/esbuild/stylesheets/sass-language.js +4 -0
  57. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.d.ts +9 -0
  58. package/src/tools/esbuild/utils.js +13 -31
  59. package/src/tools/sass/worker.js +19 -0
  60. package/src/tools/vite/middlewares/assets-middleware.d.ts +6 -1
  61. package/src/tools/vite/middlewares/assets-middleware.js +42 -22
  62. package/src/tools/vite/middlewares/component-middleware.d.ts +9 -0
  63. package/src/tools/vite/middlewares/component-middleware.js +33 -0
  64. package/src/tools/vite/middlewares/index.d.ts +2 -1
  65. package/src/tools/vite/middlewares/index.js +3 -1
  66. package/src/tools/vite/middlewares/ssr-middleware.js +11 -8
  67. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +1 -0
  68. package/src/tools/vite/plugins/angular-memory-plugin.js +5 -13
  69. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +3 -1
  70. package/src/tools/vite/plugins/setup-middlewares-plugin.js +12 -3
  71. package/src/tools/vite/utils.d.ts +1 -0
  72. package/src/typings.d.ts +1 -1
  73. package/src/utils/environment-options.d.ts +1 -0
  74. package/src/utils/environment-options.js +4 -2
  75. package/src/utils/index-file/auto-csp.d.ts +23 -0
  76. package/src/utils/index-file/auto-csp.js +283 -0
  77. package/src/utils/index-file/html-rewriting-stream.d.ts +5 -1
  78. package/src/utils/index-file/index-html-generator.d.ts +4 -0
  79. package/src/utils/index-file/index-html-generator.js +11 -0
  80. package/src/utils/index-file/inline-critical-css.js +17 -18
  81. package/src/utils/normalize-cache.js +1 -1
  82. package/src/utils/server-rendering/esm-in-memory-loader/utils.d.ts +8 -0
  83. package/src/utils/server-rendering/esm-in-memory-loader/utils.js +13 -0
  84. package/src/utils/server-rendering/launch-server.js +5 -5
  85. package/src/utils/server-rendering/load-esm-from-memory.d.ts +1 -1
  86. package/src/utils/server-rendering/manifest.d.ts +9 -8
  87. package/src/utils/server-rendering/manifest.js +17 -23
  88. package/src/utils/server-rendering/prerender.js +30 -19
  89. package/src/utils/server-rendering/render-worker.js +4 -2
  90. package/src/utils/supported-browsers.js +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "19.0.0-next.9",
3
+ "version": "19.0.0-rc.1",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,39 +23,41 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1900.0-next.9",
27
- "@babel/core": "7.25.2",
28
- "@babel/helper-annotate-as-pure": "7.24.7",
26
+ "@angular-devkit/architect": "0.1900.0-rc.1",
27
+ "@babel/core": "7.26.0",
28
+ "@babel/helper-annotate-as-pure": "7.25.9",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
30
- "@babel/plugin-syntax-import-attributes": "7.25.6",
31
- "@inquirer/confirm": "4.0.1",
30
+ "@babel/plugin-syntax-import-attributes": "7.26.0",
31
+ "@inquirer/confirm": "5.0.1",
32
32
  "@vitejs/plugin-basic-ssl": "1.1.0",
33
+ "beasties": "0.1.0",
33
34
  "browserslist": "^4.23.0",
34
- "critters": "0.0.24",
35
35
  "esbuild": "0.24.0",
36
36
  "fast-glob": "3.3.2",
37
37
  "https-proxy-agent": "7.0.5",
38
38
  "istanbul-lib-instrument": "6.0.3",
39
- "listr2": "8.2.4",
40
- "lmdb": "3.1.3",
41
- "magic-string": "0.30.11",
39
+ "listr2": "8.2.5",
40
+ "magic-string": "0.30.12",
42
41
  "mrmime": "2.0.0",
43
42
  "parse5-html-rewriting-stream": "7.0.0",
44
43
  "picomatch": "4.0.2",
45
44
  "piscina": "4.7.0",
46
- "rollup": "4.22.5",
47
- "sass": "1.79.4",
45
+ "rollup": "4.24.4",
46
+ "sass": "1.80.6",
48
47
  "semver": "7.6.3",
49
- "vite": "5.4.8",
48
+ "vite": "5.4.10",
50
49
  "watchpack": "2.4.2"
51
50
  },
51
+ "optionalDependencies": {
52
+ "lmdb": "3.1.4"
53
+ },
52
54
  "peerDependencies": {
53
- "@angular/compiler": "^19.0.0-next.0",
54
- "@angular/compiler-cli": "^19.0.0-next.0",
55
- "@angular/localize": "^19.0.0-next.0",
56
- "@angular/platform-server": "^19.0.0-next.0",
57
- "@angular/service-worker": "^19.0.0-next.0",
58
- "@angular/ssr": "^19.0.0-next.9",
55
+ "@angular/compiler": "^19.0.0-next.9",
56
+ "@angular/compiler-cli": "^19.0.0-next.9",
57
+ "@angular/localize": "^19.0.0-next.9",
58
+ "@angular/platform-server": "^19.0.0-next.9",
59
+ "@angular/service-worker": "^19.0.0-next.9",
60
+ "@angular/ssr": "^19.0.0-rc.1",
59
61
  "less": "^4.2.0",
60
62
  "postcss": "^8.4.0",
61
63
  "tailwindcss": "^2.0.0 || ^3.0.0",
@@ -83,12 +83,6 @@ async function* runEsBuildBuildAction(action, options) {
83
83
  cacheOptions.basePath,
84
84
  `${workspaceRoot.replace(/\\/g, '/')}/**/.*/**`,
85
85
  ];
86
- if (!preserveSymlinks) {
87
- // Ignore all node modules directories to avoid excessive file watchers.
88
- // Package changes are handled below by watching manifest and lock files.
89
- // NOTE: this is not enable when preserveSymlinks is true as this would break `npm link` usages.
90
- ignored.push('**/node_modules/**');
91
- }
92
86
  // Setup a watcher
93
87
  const { createWatcher } = await Promise.resolve().then(() => __importStar(require('../../tools/esbuild/watcher')));
94
88
  watcher = createWatcher({
@@ -101,11 +95,17 @@ async function* runEsBuildBuildAction(action, options) {
101
95
  options.signal?.addEventListener('abort', () => void watcher?.close());
102
96
  // Watch the entire project root if 'NG_BUILD_WATCH_ROOT' environment variable is set
103
97
  if (environment_options_1.shouldWatchRoot) {
98
+ if (!preserveSymlinks) {
99
+ // Ignore all node modules directories to avoid excessive file watchers.
100
+ // Package changes are handled below by watching manifest and lock files.
101
+ // NOTE: this is not enable when preserveSymlinks is true as this would break `npm link` usages.
102
+ ignored.push('**/node_modules/**');
103
+ watcher.add(packageWatchFiles
104
+ .map((file) => node_path_1.default.join(workspaceRoot, file))
105
+ .filter((file) => (0, node_fs_1.existsSync)(file)));
106
+ }
104
107
  watcher.add(projectRoot);
105
108
  }
106
- watcher.add(packageWatchFiles
107
- .map((file) => node_path_1.default.join(workspaceRoot, file))
108
- .filter((file) => (0, node_fs_1.existsSync)(file)));
109
109
  // Watch locations provided by the initial build result
110
110
  watcher.add(result.watchFiles);
111
111
  }
@@ -162,7 +162,7 @@ async function* runEsBuildBuildAction(action, options) {
162
162
  (0, sass_language_1.shutdownSassWorkerPool)();
163
163
  }
164
164
  }
165
- async function emitOutputResult({ outputFiles, assetFiles, errors, warnings, externalMetadata, htmlIndexPath, htmlBaseHref, }, outputOptions) {
165
+ async function emitOutputResult({ outputFiles, assetFiles, errors, warnings, externalMetadata, htmlIndexPath, htmlBaseHref, templateUpdates, }, outputOptions) {
166
166
  if (errors.length > 0) {
167
167
  return {
168
168
  kind: results_1.ResultKind.Failure,
@@ -173,6 +173,18 @@ async function emitOutputResult({ outputFiles, assetFiles, errors, warnings, ext
173
173
  },
174
174
  };
175
175
  }
176
+ // Template updates only exist if no other changes have occurred
177
+ if (templateUpdates?.size) {
178
+ const updateResult = {
179
+ kind: results_1.ResultKind.ComponentUpdate,
180
+ updates: Array.from(templateUpdates).map(([id, content]) => ({
181
+ type: 'template',
182
+ id,
183
+ content,
184
+ })),
185
+ };
186
+ return updateResult;
187
+ }
176
188
  const result = {
177
189
  kind: results_1.ResultKind.Full,
178
190
  warnings: warnings,
@@ -83,10 +83,7 @@ async function optimizeChunks(original, sourcemap) {
83
83
  const result = await bundle.generate({
84
84
  compact: true,
85
85
  sourcemap,
86
- chunkFileNames(chunkInfo) {
87
- // Do not add hash to file name if already present
88
- return /-[a-zA-Z0-9]{8}$/.test(chunkInfo.name) ? '[name].js' : '[name]-[hash].js';
89
- },
86
+ chunkFileNames: (chunkInfo) => `${chunkInfo.name.replace(/-[a-zA-Z0-9]{8}$/, '')}-[hash].js`,
90
87
  });
91
88
  optimizedOutput = result.output;
92
89
  }
@@ -6,12 +6,8 @@
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.executeBuild = executeBuild;
14
- const node_assert_1 = __importDefault(require("node:assert"));
15
11
  const source_file_cache_1 = require("../../tools/esbuild/angular/source-file-cache");
16
12
  const budget_stats_1 = require("../../tools/esbuild/budget-stats");
17
13
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
@@ -28,11 +24,10 @@ const supported_browsers_1 = require("../../utils/supported-browsers");
28
24
  const chunk_optimizer_1 = require("./chunk-optimizer");
29
25
  const execute_post_bundle_1 = require("./execute-post-bundle");
30
26
  const i18n_1 = require("./i18n");
31
- const schema_1 = require("./schema");
32
27
  const setup_bundling_1 = require("./setup-bundling");
33
28
  // eslint-disable-next-line max-lines-per-function
34
29
  async function executeBuild(options, context, rebuildState) {
35
- const { projectRoot, workspaceRoot, i18nOptions, optimizationOptions, assets, outputMode, cacheOptions, serverEntryPoint, baseHref, ssrOptions, verbose, colors, jsonLogs, } = options;
30
+ const { projectRoot, workspaceRoot, i18nOptions, optimizationOptions, assets, cacheOptions, serverEntryPoint, baseHref, ssrOptions, verbose, colors, jsonLogs, } = options;
36
31
  // TODO: Consider integrating into watch mode. Would require full rebuild on target changes.
37
32
  const browsers = (0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger);
38
33
  // Load active translations if inlining
@@ -41,18 +36,63 @@ async function executeBuild(options, context, rebuildState) {
41
36
  await (0, i18n_1.loadActiveTranslations)(context, i18nOptions);
42
37
  }
43
38
  // Reuse rebuild state or create new bundle contexts for code and global stylesheets
44
- let bundlerContexts = rebuildState?.rebuildContexts;
45
- const codeBundleCache = rebuildState?.codeBundleCache ??
46
- new source_file_cache_1.SourceFileCache(cacheOptions.enabled ? cacheOptions.path : undefined);
47
- if (bundlerContexts === undefined) {
48
- bundlerContexts = (0, setup_bundling_1.setupBundlerContexts)(options, browsers, codeBundleCache);
39
+ let bundlerContexts;
40
+ let componentStyleBundler;
41
+ let codeBundleCache;
42
+ let bundlingResult;
43
+ let templateUpdates;
44
+ if (rebuildState) {
45
+ bundlerContexts = rebuildState.rebuildContexts;
46
+ componentStyleBundler = rebuildState.componentStyleBundler;
47
+ codeBundleCache = rebuildState.codeBundleCache;
48
+ templateUpdates = rebuildState.templateUpdates;
49
+ // Reset template updates for new rebuild
50
+ templateUpdates?.clear();
51
+ const allFileChanges = rebuildState.fileChanges.all;
52
+ // Bundle all contexts that do not require TypeScript changed file checks.
53
+ // These will automatically use cached results based on the changed files.
54
+ bundlingResult = await bundler_context_1.BundlerContext.bundleAll(bundlerContexts.otherContexts, allFileChanges);
55
+ // Check the TypeScript code bundling cache for changes. If invalid, force a rebundle of
56
+ // all TypeScript related contexts.
57
+ const forceTypeScriptRebuild = codeBundleCache?.invalidate(allFileChanges);
58
+ const typescriptResults = [];
59
+ for (const typescriptContext of bundlerContexts.typescriptContexts) {
60
+ typescriptContext.invalidate(allFileChanges);
61
+ const result = await typescriptContext.bundle(forceTypeScriptRebuild);
62
+ typescriptResults.push(result);
63
+ }
64
+ bundlingResult = bundler_context_1.BundlerContext.mergeResults([bundlingResult, ...typescriptResults]);
65
+ }
66
+ else {
67
+ const target = (0, utils_1.transformSupportedBrowsersToTargets)(browsers);
68
+ codeBundleCache = new source_file_cache_1.SourceFileCache(cacheOptions.enabled ? cacheOptions.path : undefined);
69
+ componentStyleBundler = (0, setup_bundling_1.createComponentStyleBundler)(options, target);
70
+ if (options.templateUpdates) {
71
+ templateUpdates = new Map();
72
+ }
73
+ bundlerContexts = (0, setup_bundling_1.setupBundlerContexts)(options, target, codeBundleCache, componentStyleBundler, templateUpdates);
74
+ // Bundle everything on initial build
75
+ bundlingResult = await bundler_context_1.BundlerContext.bundleAll([
76
+ ...bundlerContexts.typescriptContexts,
77
+ ...bundlerContexts.otherContexts,
78
+ ]);
79
+ }
80
+ // Update any external component styles if enabled and rebuilding.
81
+ // TODO: Only attempt rebundling of invalidated styles once incremental build results are supported.
82
+ if (rebuildState && options.externalRuntimeStyles) {
83
+ componentStyleBundler.invalidate(rebuildState.fileChanges.all);
84
+ const componentResults = await componentStyleBundler.bundleAllFiles(true, true);
85
+ bundlingResult = bundler_context_1.BundlerContext.mergeResults([bundlingResult, ...componentResults]);
49
86
  }
50
- let bundlingResult = await bundler_context_1.BundlerContext.bundleAll(bundlerContexts, rebuildState?.fileChanges.all);
51
87
  if (options.optimizationOptions.scripts && environment_options_1.shouldOptimizeChunks) {
52
88
  bundlingResult = await (0, profiling_1.profileAsync)('OPTIMIZE_CHUNKS', () => (0, chunk_optimizer_1.optimizeChunks)(bundlingResult, options.sourcemapOptions.scripts ? !options.sourcemapOptions.hidden || 'hidden' : false));
53
89
  }
54
- const executionResult = new bundler_execution_result_1.ExecutionResult(bundlerContexts, codeBundleCache);
90
+ const executionResult = new bundler_execution_result_1.ExecutionResult(bundlerContexts, componentStyleBundler, codeBundleCache, templateUpdates);
55
91
  executionResult.addWarnings(bundlingResult.warnings);
92
+ // Add used external component style referenced files to be watched
93
+ if (options.externalRuntimeStyles) {
94
+ executionResult.extraWatchFiles.push(...componentStyleBundler.collectReferencedFiles());
95
+ }
56
96
  // Return if the bundling has errors
57
97
  if (bundlingResult.errors) {
58
98
  executionResult.addErrors(bundlingResult.errors);
@@ -113,7 +153,11 @@ async function executeBuild(options, context, rebuildState) {
113
153
  }
114
154
  // Create server app engine manifest
115
155
  if (serverEntryPoint) {
116
- executionResult.addOutputFile(manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppEngineManifest)(i18nOptions, baseHref, undefined), bundler_context_1.BuildOutputFileType.ServerRoot);
156
+ executionResult.addOutputFile(manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppEngineManifest)(i18nOptions, baseHref), bundler_context_1.BuildOutputFileType.ServerRoot);
157
+ }
158
+ // Override auto-CSP settings if we are serving through Vite middleware.
159
+ if (context.builder.builderName === 'dev-server' && options.security) {
160
+ options.security.autoCsp = false;
117
161
  }
118
162
  // Perform i18n translation inlining if enabled
119
163
  if (i18nOptions.shouldInline) {
@@ -132,16 +176,7 @@ async function executeBuild(options, context, rebuildState) {
132
176
  executionResult.outputFiles.push(...result.additionalOutputFiles);
133
177
  executionResult.assetFiles.push(...result.additionalAssets);
134
178
  }
135
- if (serverEntryPoint) {
136
- const prerenderedRoutes = executionResult.prerenderedRoutes;
137
- // Regenerate the manifest to append prerendered routes data. This is only needed if SSR is enabled.
138
- if (outputMode === schema_1.OutputMode.Server && Object.keys(prerenderedRoutes).length) {
139
- const manifest = executionResult.outputFiles.find((f) => f.path === manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME);
140
- (0, node_assert_1.default)(manifest, `${manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME} was not found in output files.`);
141
- manifest.contents = new TextEncoder().encode((0, manifest_1.generateAngularServerAppEngineManifest)(i18nOptions, baseHref, prerenderedRoutes));
142
- }
143
- executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: prerenderedRoutes }, null, 2), bundler_context_1.BuildOutputFileType.Root);
144
- }
179
+ executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: executionResult.prerenderedRoutes }, null, 2), bundler_context_1.BuildOutputFileType.Root);
145
180
  // Write metafile if stats option is enabled
146
181
  if (options.stats) {
147
182
  executionResult.addOutputFile('stats.json', JSON.stringify(metafile, null, 2), bundler_context_1.BuildOutputFileType.Root);
@@ -30,6 +30,7 @@ const schema_1 = require("./schema");
30
30
  * @param initialFiles A map containing initial file information for the executed build.
31
31
  * @param locale A language locale to insert in the index.html.
32
32
  */
33
+ // eslint-disable-next-line max-lines-per-function
33
34
  async function executePostBundleSteps(options, outputFiles, assetFiles, initialFiles, locale) {
34
35
  const additionalAssets = [];
35
36
  const additionalOutputFiles = [];
@@ -55,7 +56,8 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
55
56
  }
56
57
  // Create server manifest
57
58
  if (serverEntryPoint) {
58
- additionalOutputFiles.push((0, utils_1.createOutputFile)(manifest_1.SERVER_APP_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, undefined, locale), bundler_context_1.BuildOutputFileType.ServerApplication));
59
+ const { manifestContent, serverAssetsChunks } = (0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, undefined, locale);
60
+ additionalOutputFiles.push(...serverAssetsChunks, (0, utils_1.createOutputFile)(manifest_1.SERVER_APP_MANIFEST_FILENAME, manifestContent, bundler_context_1.BuildOutputFileType.ServerApplication));
59
61
  }
60
62
  // Pre-render (SSG) and App-shell
61
63
  // If localization is enabled, prerendering is handled in the inlining process.
@@ -71,7 +73,15 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
71
73
  // Update the index contents with the app shell under these conditions:
72
74
  // - Replace 'index.html' with the app shell only if it hasn't been prerendered yet.
73
75
  // - Always replace 'index.csr.html' with the app shell.
74
- const filePath = appShellRoute && !indexHasBeenPrerendered ? indexHtmlOptions.output : path;
76
+ let filePath = path;
77
+ if (appShellRoute && !indexHasBeenPrerendered) {
78
+ if (outputMode !== schema_1.OutputMode.Server && indexHtmlOptions.output === options_1.INDEX_HTML_CSR) {
79
+ filePath = 'index.html';
80
+ }
81
+ else {
82
+ filePath = indexHtmlOptions.output;
83
+ }
84
+ }
75
85
  additionalHtmlOutputFiles.set(filePath, (0, utils_1.createOutputFile)(filePath, content, bundler_context_1.BuildOutputFileType.Browser));
76
86
  }
77
87
  const serializableRouteTreeNodeForManifest = [];
@@ -79,8 +89,11 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
79
89
  switch (metadata.renderMode) {
80
90
  case models_1.RouteRenderMode.Prerender:
81
91
  case /* Legacy building mode */ undefined: {
82
- if (!metadata.redirectTo || outputMode === schema_1.OutputMode.Static) {
83
- prerenderedRoutes[metadata.route] = { headers: metadata.headers };
92
+ if (!metadata.redirectTo) {
93
+ serializableRouteTreeNodeForManifest.push(metadata);
94
+ if (!metadata.route.includes('*')) {
95
+ prerenderedRoutes[metadata.route] = { headers: metadata.headers };
96
+ }
84
97
  }
85
98
  break;
86
99
  }
@@ -94,7 +107,17 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
94
107
  // Regenerate the manifest to append route tree. This is only needed if SSR is enabled.
95
108
  const manifest = additionalOutputFiles.find((f) => f.path === manifest_1.SERVER_APP_MANIFEST_FILENAME);
96
109
  (0, node_assert_1.default)(manifest, `${manifest_1.SERVER_APP_MANIFEST_FILENAME} was not found in output files.`);
97
- manifest.contents = new TextEncoder().encode((0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, serializableRouteTreeNodeForManifest, locale));
110
+ const { manifestContent, serverAssetsChunks } = (0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, serializableRouteTreeNodeForManifest, locale);
111
+ for (const chunk of serverAssetsChunks) {
112
+ const idx = additionalOutputFiles.findIndex(({ path }) => path === chunk.path);
113
+ if (idx === -1) {
114
+ additionalOutputFiles.push(chunk);
115
+ }
116
+ else {
117
+ additionalOutputFiles[idx] = chunk;
118
+ }
119
+ }
120
+ manifest.contents = new TextEncoder().encode(manifestContent);
98
121
  }
99
122
  }
100
123
  additionalOutputFiles.push(...additionalHtmlOutputFiles.values());
@@ -6,7 +6,6 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
- import type { Plugin } from 'esbuild';
10
9
  import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
11
10
  import { ApplicationBuilderExtensions, ApplicationBuilderInternalOptions } from './options';
12
11
  import { Result } from './results';
@@ -22,21 +21,6 @@ export interface ApplicationBuilderOutput extends BuilderOutput {
22
21
  destination: string;
23
22
  }[];
24
23
  }
25
- /**
26
- * Builds an application using the `application` builder with the provided
27
- * options.
28
- *
29
- * Usage of the `plugins` parameter is NOT supported and may cause unexpected
30
- * build output or build failures.
31
- *
32
- * @experimental Direct usage of this function is considered experimental.
33
- *
34
- * @param options The options defined by the builder's schema to use.
35
- * @param context An Architect builder context instance.
36
- * @param plugins An array of plugins to apply to the main code bundling.
37
- * @returns The build output results of the build.
38
- */
39
- export declare function buildApplication(options: ApplicationBuilderOptions, context: BuilderContext, plugins?: Plugin[]): AsyncIterable<ApplicationBuilderOutput>;
40
24
  /**
41
25
  * Builds an application using the `application` builder with the provided
42
26
  * options.
@@ -100,16 +100,21 @@ context, extensions) {
100
100
  signal,
101
101
  });
102
102
  }
103
- async function* buildApplication(options, context, pluginsOrExtensions) {
104
- let extensions;
105
- if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) {
106
- extensions = {
107
- codePlugins: pluginsOrExtensions,
108
- };
109
- }
110
- else {
111
- extensions = pluginsOrExtensions;
112
- }
103
+ /**
104
+ * Builds an application using the `application` builder with the provided
105
+ * options.
106
+ *
107
+ * Usage of the `extensions` parameter is NOT supported and may cause unexpected
108
+ * build output or build failures.
109
+ *
110
+ * @experimental Direct usage of this function is considered experimental.
111
+ *
112
+ * @param options The options defined by the builder's schema to use.
113
+ * @param context An Architect builder context instance.
114
+ * @param extensions An object contain extension points for the build.
115
+ * @returns The build output results of the build.
116
+ */
117
+ async function* buildApplication(options, context, extensions) {
113
118
  let initial = true;
114
119
  for await (const result of buildApplicationInternal(options, context, extensions)) {
115
120
  const outputOptions = result.detail?.['outputOptions'];
@@ -9,7 +9,7 @@ import type { BuilderContext } from '@angular-devkit/architect';
9
9
  import type { Plugin } from 'esbuild';
10
10
  import { I18nOptions } from '../../utils/i18n-options';
11
11
  import { IndexHtmlTransform } from '../../utils/index-file/index-html-generator';
12
- import { Schema as ApplicationBuilderOptions, I18NTranslation, OutputMode, OutputPathClass } from './schema';
12
+ import { Schema as ApplicationBuilderOptions, ExperimentalPlatform, I18NTranslation, OutputMode, OutputPathClass } from './schema';
13
13
  /**
14
14
  * The filename for the client-side rendered HTML template.
15
15
  * This template is used for client-side rendering (CSR) in a web application.
@@ -36,8 +36,10 @@ interface InternalOptions {
36
36
  * If given a relative path, it is resolved relative to the current workspace and will generate an output at the same relative location
37
37
  * in the output directory. If given an absolute path, the output will be generated in the root of the output directory with the same base
38
38
  * name.
39
+ *
40
+ * If provided a Map, the key is the name of the output bundle and the value is the entry point file.
39
41
  */
40
- entryPoints?: Set<string>;
42
+ entryPoints?: Set<string> | Map<string, string>;
41
43
  /** File extension to use for the generated output files. */
42
44
  outExtension?: 'js' | 'mjs';
43
45
  /**
@@ -66,6 +68,12 @@ interface InternalOptions {
66
68
  * styles.
67
69
  */
68
70
  externalRuntimeStyles?: boolean;
71
+ /**
72
+ * Enables the AOT compiler to generate template component update functions.
73
+ * This option is only intended to be used with a development server that can process and serve component
74
+ * template updates.
75
+ */
76
+ templateUpdates?: boolean;
69
77
  /**
70
78
  * Enables instrumentation to collect code coverage data for specific files.
71
79
  *
@@ -119,8 +127,10 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
119
127
  outputMode: OutputMode | undefined;
120
128
  ssrOptions: {
121
129
  entry?: undefined;
130
+ platform?: undefined;
122
131
  } | {
123
132
  entry: string | undefined;
133
+ platform: ExperimentalPlatform;
124
134
  } | undefined;
125
135
  verbose: boolean | undefined;
126
136
  watch: boolean | undefined;
@@ -181,6 +191,8 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
181
191
  partialSSRBuild: boolean;
182
192
  externalRuntimeStyles: boolean | undefined;
183
193
  instrumentForCoverage: ((filename: string) => boolean) | undefined;
194
+ security: import("./schema").Security | undefined;
195
+ templateUpdates: boolean;
184
196
  }>;
185
197
  export declare function getLocaleBaseHref(baseHref: string | undefined, i18n: NormalizedApplicationBuildOptions['i18nOptions'], locale: string): string | undefined;
186
198
  export {};
@@ -116,14 +116,12 @@ async function normalizeOptions(context, projectName, options, extensions) {
116
116
  if (!options.server) {
117
117
  options.ssr = false;
118
118
  }
119
- if (options.prerender) {
120
- context.logger.warn('The "prerender" option is no longer needed when "outputMode" is specified.');
119
+ if (options.prerender !== undefined) {
120
+ context.logger.warn('The "prerender" option is not considered when "outputMode" is specified.');
121
121
  }
122
- else {
123
- options.prerender = !!options.server;
124
- }
125
- if (options.appShell) {
126
- context.logger.warn('The "appShell" option is no longer needed when "outputMode" is specified.');
122
+ options.prerender = !!options.server;
123
+ if (options.appShell !== undefined) {
124
+ context.logger.warn('The "appShell" option is not considered when "outputMode" is specified.');
127
125
  }
128
126
  }
129
127
  // A configuration file can exist in the project or workspace root
@@ -153,9 +151,10 @@ async function normalizeOptions(context, projectName, options, extensions) {
153
151
  ssrOptions = {};
154
152
  }
155
153
  else if (typeof options.ssr === 'object') {
156
- const { entry } = options.ssr;
154
+ const { entry, experimentalPlatform = schema_1.ExperimentalPlatform.Node } = options.ssr;
157
155
  ssrOptions = {
158
156
  entry: entry && node_path_1.default.join(workspaceRoot, entry),
157
+ platform: experimentalPlatform,
159
158
  };
160
159
  }
161
160
  let appShellOptions;
@@ -199,10 +198,16 @@ async function normalizeOptions(context, projectName, options, extensions) {
199
198
  * If SSR is activated, create a distinct entry file for the `index.html`.
200
199
  * This is necessary because numerous server/cloud providers automatically serve the `index.html` as a static file
201
200
  * if it exists (handling SSG).
201
+ *
202
202
  * For instance, accessing `foo.com/` would lead to `foo.com/index.html` being served instead of hitting the server.
203
+ *
204
+ * This approach can also be applied to service workers, where the `index.csr.html` is served instead of the prerendered `index.html`.
203
205
  */
204
206
  const indexBaseName = node_path_1.default.basename(options.index);
205
- indexOutput = ssrOptions && indexBaseName === 'index.html' ? exports.INDEX_HTML_CSR : indexBaseName;
207
+ indexOutput =
208
+ (ssrOptions || prerenderOptions) && indexBaseName === 'index.html'
209
+ ? exports.INDEX_HTML_CSR
210
+ : indexBaseName;
206
211
  }
207
212
  else {
208
213
  indexOutput = options.index.output || 'index.html';
@@ -231,7 +236,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
231
236
  }
232
237
  }
233
238
  // Initial options to keep
234
- const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, outputMode, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, namedChunks, budgets, deployUrl, clearScreen, define, partialSSRBuild = false, externalRuntimeStyles, instrumentForCoverage, } = options;
239
+ const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, outputMode, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, namedChunks, budgets, deployUrl, clearScreen, define, partialSSRBuild = false, externalRuntimeStyles, instrumentForCoverage, security, } = options;
235
240
  // Return all the normalized options
236
241
  return {
237
242
  advancedOptimizations: !!aot && optimizationOptions.scripts,
@@ -290,6 +295,8 @@ async function normalizeOptions(context, projectName, options, extensions) {
290
295
  partialSSRBuild: environment_options_1.usePartialSsrBuild || partialSSRBuild,
291
296
  externalRuntimeStyles,
292
297
  instrumentForCoverage,
298
+ security,
299
+ templateUpdates: !!options.templateUpdates,
293
300
  };
294
301
  }
295
302
  async function getTailwindConfig(searchDirectories, workspaceRoot, context) {
@@ -342,6 +349,14 @@ function normalizeEntryPoints(workspaceRoot, browser, entryPoints = new Set()) {
342
349
  // Use `browser` alone.
343
350
  return { 'main': node_path_1.default.join(workspaceRoot, browser) };
344
351
  }
352
+ else if (entryPoints instanceof Map) {
353
+ return Object.fromEntries(Array.from(entryPoints.entries(), ([name, entryPoint]) => {
354
+ // Get the full file path to a relative entry point input. Leave bare specifiers alone so they are resolved as modules.
355
+ const isRelativePath = entryPoint.startsWith('.');
356
+ const entryPointPath = isRelativePath ? node_path_1.default.join(workspaceRoot, entryPoint) : entryPoint;
357
+ return [name, entryPointPath];
358
+ }));
359
+ }
345
360
  else {
346
361
  // Use `entryPoints` alone.
347
362
  const entryPointPaths = {};
@@ -61,7 +61,9 @@ export interface ResultMessage {
61
61
  }
62
62
  export interface ComponentUpdateResult extends BaseResult {
63
63
  kind: ResultKind.ComponentUpdate;
64
- id: string;
65
- type: 'style' | 'template';
66
- content: string;
64
+ updates: {
65
+ id: string;
66
+ type: 'style' | 'template';
67
+ content: string;
68
+ }[];
67
69
  }