@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
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2017 Google, Inc.
3
+ Copyright (c) 2010-2024 Google LLC. https://angular.dev/license
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "19.0.0-next.1",
3
+ "version": "19.0.0-next.10",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,41 +23,45 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1900.0-next.1",
27
- "@babel/core": "7.25.2",
28
- "@babel/helper-annotate-as-pure": "7.24.7",
26
+ "@angular-devkit/architect": "0.1900.0-next.10",
27
+ "@babel/core": "7.25.7",
28
+ "@babel/helper-annotate-as-pure": "7.25.7",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
30
- "@babel/plugin-syntax-import-attributes": "7.24.7",
31
- "@inquirer/confirm": "3.1.22",
30
+ "@babel/plugin-syntax-import-attributes": "7.25.7",
31
+ "@inquirer/confirm": "5.0.0",
32
32
  "@vitejs/plugin-basic-ssl": "1.1.0",
33
33
  "browserslist": "^4.23.0",
34
34
  "critters": "0.0.24",
35
- "esbuild": "0.23.1",
35
+ "esbuild": "0.24.0",
36
36
  "fast-glob": "3.3.2",
37
37
  "https-proxy-agent": "7.0.5",
38
- "listr2": "8.2.4",
39
- "lmdb": "3.0.14",
38
+ "istanbul-lib-instrument": "6.0.3",
39
+ "listr2": "8.2.5",
40
40
  "magic-string": "0.30.11",
41
41
  "mrmime": "2.0.0",
42
42
  "parse5-html-rewriting-stream": "7.0.0",
43
43
  "picomatch": "4.0.2",
44
- "piscina": "4.6.1",
45
- "rollup": "4.21.0",
46
- "sass": "1.77.8",
44
+ "piscina": "4.7.0",
45
+ "rollup": "4.24.0",
46
+ "sass": "1.79.4",
47
47
  "semver": "7.6.3",
48
- "vite": "5.4.2",
48
+ "vite": "5.4.8",
49
49
  "watchpack": "2.4.2"
50
50
  },
51
+ "optionalDependencies": {
52
+ "lmdb": "3.1.3"
53
+ },
51
54
  "peerDependencies": {
55
+ "@angular/compiler": "^19.0.0-next.0",
52
56
  "@angular/compiler-cli": "^19.0.0-next.0",
53
57
  "@angular/localize": "^19.0.0-next.0",
54
58
  "@angular/platform-server": "^19.0.0-next.0",
55
59
  "@angular/service-worker": "^19.0.0-next.0",
56
- "@angular/ssr": "^19.0.0-next.1",
60
+ "@angular/ssr": "^19.0.0-next.10",
57
61
  "less": "^4.2.0",
58
62
  "postcss": "^8.4.0",
59
63
  "tailwindcss": "^2.0.0 || ^3.0.0",
60
- "typescript": ">=5.4 <5.6"
64
+ "typescript": ">=5.5 <5.7"
61
65
  },
62
66
  "peerDependenciesMeta": {
63
67
  "@angular/localize": {
@@ -82,7 +86,7 @@
82
86
  "optional": true
83
87
  }
84
88
  },
85
- "packageManager": "yarn@4.4.0",
89
+ "packageManager": "yarn@4.5.0",
86
90
  "repository": {
87
91
  "type": "git",
88
92
  "url": "https://github.com/angular/angular-cli.git"
@@ -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
  }
@@ -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,8 +6,12 @@
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
+ };
9
12
  Object.defineProperty(exports, "__esModule", { value: true });
10
13
  exports.executeBuild = executeBuild;
14
+ const node_assert_1 = __importDefault(require("node:assert"));
11
15
  const source_file_cache_1 = require("../../tools/esbuild/angular/source-file-cache");
12
16
  const budget_stats_1 = require("../../tools/esbuild/budget-stats");
13
17
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
@@ -19,13 +23,16 @@ const utils_1 = require("../../tools/esbuild/utils");
19
23
  const bundle_calculator_1 = require("../../utils/bundle-calculator");
20
24
  const environment_options_1 = require("../../utils/environment-options");
21
25
  const resolve_assets_1 = require("../../utils/resolve-assets");
26
+ const manifest_1 = require("../../utils/server-rendering/manifest");
22
27
  const supported_browsers_1 = require("../../utils/supported-browsers");
23
28
  const chunk_optimizer_1 = require("./chunk-optimizer");
24
29
  const execute_post_bundle_1 = require("./execute-post-bundle");
25
30
  const i18n_1 = require("./i18n");
31
+ const schema_1 = require("./schema");
26
32
  const setup_bundling_1 = require("./setup-bundling");
33
+ // eslint-disable-next-line max-lines-per-function
27
34
  async function executeBuild(options, context, rebuildState) {
28
- const { projectRoot, workspaceRoot, i18nOptions, optimizationOptions, assets, cacheOptions, prerenderOptions, ssrOptions, verbose, colors, jsonLogs, } = options;
35
+ const { projectRoot, workspaceRoot, i18nOptions, optimizationOptions, assets, outputMode, cacheOptions, serverEntryPoint, baseHref, ssrOptions, verbose, colors, jsonLogs, } = options;
29
36
  // TODO: Consider integrating into watch mode. Would require full rebuild on target changes.
30
37
  const browsers = (0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger);
31
38
  // Load active translations if inlining
@@ -104,6 +111,10 @@ async function executeBuild(options, context, rebuildState) {
104
111
  executionResult.htmlIndexPath = options.indexHtmlOptions.output;
105
112
  executionResult.htmlBaseHref = options.baseHref;
106
113
  }
114
+ // Create server app engine manifest
115
+ if (serverEntryPoint) {
116
+ executionResult.addOutputFile(manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppEngineManifest)(i18nOptions, baseHref, undefined), bundler_context_1.BuildOutputFileType.ServerRoot);
117
+ }
107
118
  // Perform i18n translation inlining if enabled
108
119
  if (i18nOptions.shouldInline) {
109
120
  const result = await (0, i18n_1.inlineI18n)(options, executionResult, initialFiles);
@@ -121,8 +132,14 @@ async function executeBuild(options, context, rebuildState) {
121
132
  executionResult.outputFiles.push(...result.additionalOutputFiles);
122
133
  executionResult.assetFiles.push(...result.additionalAssets);
123
134
  }
124
- if (prerenderOptions) {
135
+ if (serverEntryPoint) {
125
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
+ }
126
143
  executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: prerenderedRoutes }, null, 2), bundler_context_1.BuildOutputFileType.Root);
127
144
  }
128
145
  // Write metafile if stats option is enabled
@@ -6,7 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { BuildOutputFile, InitialFileRecord } from '../../tools/esbuild/bundler-context';
9
- import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
9
+ import { BuildOutputAsset, PrerenderedRoutesRecord } from '../../tools/esbuild/bundler-execution-result';
10
10
  import { NormalizedApplicationBuildOptions } from './options';
11
11
  /**
12
12
  * Run additional builds steps including SSG, AppShell, Index HTML file and Service worker generation.
@@ -21,5 +21,5 @@ export declare function executePostBundleSteps(options: NormalizedApplicationBui
21
21
  warnings: string[];
22
22
  additionalOutputFiles: BuildOutputFile[];
23
23
  additionalAssets: BuildOutputAsset[];
24
- prerenderedRoutes: string[];
24
+ prerenderedRoutes: PrerenderedRoutesRecord;
25
25
  }>;
@@ -16,8 +16,12 @@ const bundler_context_1 = require("../../tools/esbuild/bundler-context");
16
16
  const index_html_generator_1 = require("../../tools/esbuild/index-html-generator");
17
17
  const utils_1 = require("../../tools/esbuild/utils");
18
18
  const environment_options_1 = require("../../utils/environment-options");
19
+ const manifest_1 = require("../../utils/server-rendering/manifest");
20
+ const models_1 = require("../../utils/server-rendering/models");
19
21
  const prerender_1 = require("../../utils/server-rendering/prerender");
20
22
  const service_worker_1 = require("../../utils/service-worker");
23
+ const options_1 = require("./options");
24
+ const schema_1 = require("./schema");
21
25
  /**
22
26
  * Run additional builds steps including SSG, AppShell, Index HTML file and Service worker generation.
23
27
  * @param options The normalized application builder options used to create the build.
@@ -31,16 +35,12 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
31
35
  const additionalOutputFiles = [];
32
36
  const allErrors = [];
33
37
  const allWarnings = [];
34
- const prerenderedRoutes = [];
35
- const { serviceWorker, indexHtmlOptions, optimizationOptions, sourcemapOptions, prerenderOptions, appShellOptions, workspaceRoot, verbose, } = options;
36
- /**
37
- * Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
38
- *
39
- * NOTE: we don't perform critical CSS inlining as this will be done during server rendering.
40
- */
41
- let ssrIndexContent;
42
- // When using prerender/app-shell the index HTML file can be regenerated.
43
- // Thus, we use a Map so that we do not generate 2 files with the same filename.
38
+ const prerenderedRoutes = {};
39
+ const { baseHref = '/', serviceWorker, indexHtmlOptions, optimizationOptions, sourcemapOptions, outputMode, serverEntryPoint, prerenderOptions, appShellOptions, workspaceRoot, partialSSRBuild, } = options;
40
+ // Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
41
+ // NOTE: Critical CSS inlining is deliberately omitted here, as it will be handled during server rendering.
42
+ // Additionally, when using prerendering or AppShell, the index HTML file may be regenerated.
43
+ // To prevent generating duplicate files with the same filename, a `Map` is used to store and manage the files.
44
44
  const additionalHtmlOutputFiles = new Map();
45
45
  // Generate index HTML file
46
46
  // If localization is enabled, index generation is handled in the inlining process.
@@ -50,21 +50,59 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
50
50
  allWarnings.push(...warnings);
51
51
  additionalHtmlOutputFiles.set(indexHtmlOptions.output, (0, utils_1.createOutputFile)(indexHtmlOptions.output, csrContent, bundler_context_1.BuildOutputFileType.Browser));
52
52
  if (ssrContent) {
53
- const serverIndexHtmlFilename = 'index.server.html';
54
- additionalHtmlOutputFiles.set(serverIndexHtmlFilename, (0, utils_1.createOutputFile)(serverIndexHtmlFilename, ssrContent, bundler_context_1.BuildOutputFileType.Server));
55
- ssrIndexContent = ssrContent;
53
+ additionalHtmlOutputFiles.set(options_1.INDEX_HTML_SERVER, (0, utils_1.createOutputFile)(options_1.INDEX_HTML_SERVER, ssrContent, bundler_context_1.BuildOutputFileType.ServerApplication));
56
54
  }
57
55
  }
56
+ // Create server manifest
57
+ 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
+ }
58
60
  // Pre-render (SSG) and App-shell
59
61
  // If localization is enabled, prerendering is handled in the inlining process.
60
- if (prerenderOptions || appShellOptions) {
61
- (0, node_assert_1.default)(ssrIndexContent, 'The "index" option is required when using the "ssg" or "appShell" options.');
62
- const { output, warnings, errors, prerenderedRoutes: generatedRoutes, } = await (0, prerender_1.prerenderPages)(workspaceRoot, appShellOptions, prerenderOptions, outputFiles, assetFiles, ssrIndexContent, sourcemapOptions.scripts, optimizationOptions.styles.inlineCritical, environment_options_1.maxWorkers, verbose);
62
+ if (!partialSSRBuild &&
63
+ (prerenderOptions || appShellOptions || (outputMode && serverEntryPoint)) &&
64
+ !allErrors.length) {
65
+ (0, node_assert_1.default)(indexHtmlOptions, 'The "index" option is required when using the "ssg" or "appShell" options.');
66
+ const { output, warnings, errors, serializableRouteTreeNode } = await (0, prerender_1.prerenderPages)(workspaceRoot, baseHref, appShellOptions, prerenderOptions, [...outputFiles, ...additionalOutputFiles], assetFiles, outputMode, sourcemapOptions.scripts, environment_options_1.maxWorkers);
63
67
  allErrors.push(...errors);
64
68
  allWarnings.push(...warnings);
65
- prerenderedRoutes.push(...Array.from(generatedRoutes));
66
- for (const [path, content] of Object.entries(output)) {
67
- additionalHtmlOutputFiles.set(path, (0, utils_1.createOutputFile)(path, content, bundler_context_1.BuildOutputFileType.Browser));
69
+ const indexHasBeenPrerendered = output[indexHtmlOptions.output];
70
+ for (const [path, { content, appShellRoute }] of Object.entries(output)) {
71
+ // Update the index contents with the app shell under these conditions:
72
+ // - Replace 'index.html' with the app shell only if it hasn't been prerendered yet.
73
+ // - Always replace 'index.csr.html' with the app shell.
74
+ let filePath = path;
75
+ if (appShellRoute && !indexHasBeenPrerendered) {
76
+ if (outputMode !== schema_1.OutputMode.Server && indexHtmlOptions.output === options_1.INDEX_HTML_CSR) {
77
+ filePath = 'index.html';
78
+ }
79
+ else {
80
+ filePath = indexHtmlOptions.output;
81
+ }
82
+ }
83
+ additionalHtmlOutputFiles.set(filePath, (0, utils_1.createOutputFile)(filePath, content, bundler_context_1.BuildOutputFileType.Browser));
84
+ }
85
+ const serializableRouteTreeNodeForManifest = [];
86
+ for (const metadata of serializableRouteTreeNode) {
87
+ switch (metadata.renderMode) {
88
+ case models_1.RouteRenderMode.Prerender:
89
+ case /* Legacy building mode */ undefined: {
90
+ if (!metadata.redirectTo || outputMode === schema_1.OutputMode.Static) {
91
+ prerenderedRoutes[metadata.route] = { headers: metadata.headers };
92
+ }
93
+ break;
94
+ }
95
+ case models_1.RouteRenderMode.Server:
96
+ case models_1.RouteRenderMode.Client:
97
+ serializableRouteTreeNodeForManifest.push(metadata);
98
+ break;
99
+ }
100
+ }
101
+ if (outputMode === schema_1.OutputMode.Server) {
102
+ // Regenerate the manifest to append route tree. This is only needed if SSR is enabled.
103
+ const manifest = additionalOutputFiles.find((f) => f.path === manifest_1.SERVER_APP_MANIFEST_FILENAME);
104
+ (0, node_assert_1.default)(manifest, `${manifest_1.SERVER_APP_MANIFEST_FILENAME} was not found in output files.`);
105
+ manifest.contents = new TextEncoder().encode((0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, serializableRouteTreeNodeForManifest, locale));
68
106
  }
69
107
  }
70
108
  additionalOutputFiles.push(...additionalHtmlOutputFiles.values());
@@ -72,7 +110,7 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
72
110
  // If localization is enabled, service worker is handled in the inlining process.
73
111
  if (serviceWorker) {
74
112
  try {
75
- const serviceWorkerResult = await (0, service_worker_1.augmentAppWithServiceWorkerEsbuild)(workspaceRoot, serviceWorker, options.baseHref || '/', options.indexHtmlOptions?.output,
113
+ const serviceWorkerResult = await (0, service_worker_1.augmentAppWithServiceWorkerEsbuild)(workspaceRoot, serviceWorker, baseHref, options.indexHtmlOptions?.output,
76
114
  // Ensure additional files recently added are used
77
115
  [...outputFiles, ...additionalOutputFiles], assetFiles);
78
116
  additionalOutputFiles.push((0, utils_1.createOutputFile)('ngsw.json', serviceWorkerResult.manifest, bundler_context_1.BuildOutputFileType.Browser));
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { BuilderContext } from '@angular-devkit/architect';
9
9
  import { InitialFileRecord } from '../../tools/esbuild/bundler-context';
10
- import { ExecutionResult } from '../../tools/esbuild/bundler-execution-result';
10
+ import { ExecutionResult, PrerenderedRoutesRecord } from '../../tools/esbuild/bundler-execution-result';
11
11
  import { NormalizedApplicationBuildOptions } from './options';
12
12
  /**
13
13
  * Inlines all active locales as specified by the application build options into all
@@ -19,7 +19,7 @@ import { NormalizedApplicationBuildOptions } from './options';
19
19
  export declare function inlineI18n(options: NormalizedApplicationBuildOptions, executionResult: ExecutionResult, initialFiles: Map<string, InitialFileRecord>): Promise<{
20
20
  errors: string[];
21
21
  warnings: string[];
22
- prerenderedRoutes: string[];
22
+ prerenderedRoutes: PrerenderedRoutesRecord;
23
23
  }>;
24
24
  /**
25
25
  * Loads all active translations using the translation loaders from the `@angular/localize` package.
@@ -15,8 +15,8 @@ const i18n_inliner_1 = require("../../tools/esbuild/i18n-inliner");
15
15
  const environment_options_1 = require("../../utils/environment-options");
16
16
  const i18n_options_1 = require("../../utils/i18n-options");
17
17
  const load_translations_1 = require("../../utils/load-translations");
18
- const url_1 = require("../../utils/url");
19
18
  const execute_post_bundle_1 = require("./execute-post-bundle");
19
+ const options_1 = require("./options");
20
20
  /**
21
21
  * Inlines all active locales as specified by the application build options into all
22
22
  * application JavaScript files created during the build.
@@ -34,7 +34,7 @@ async function inlineI18n(options, executionResult, initialFiles) {
34
34
  const inlineResult = {
35
35
  errors: [],
36
36
  warnings: [],
37
- prerenderedRoutes: [],
37
+ prerenderedRoutes: {},
38
38
  };
39
39
  // For each active locale, use the inliner to process the output files of the build.
40
40
  const updatedOutputFiles = [];
@@ -46,7 +46,7 @@ async function inlineI18n(options, executionResult, initialFiles) {
46
46
  const localeOutputFiles = localeInlineResult.outputFiles;
47
47
  inlineResult.errors.push(...localeInlineResult.errors);
48
48
  inlineResult.warnings.push(...localeInlineResult.warnings);
49
- const baseHref = getLocaleBaseHref(options.baseHref, options.i18nOptions, locale) ?? options.baseHref;
49
+ const baseHref = (0, options_1.getLocaleBaseHref)(options.baseHref, options.i18nOptions, locale) ?? options.baseHref;
50
50
  const { errors, warnings, additionalAssets, additionalOutputFiles, prerenderedRoutes: generatedRoutes, } = await (0, execute_post_bundle_1.executePostBundleSteps)({
51
51
  ...options,
52
52
  baseHref,
@@ -65,12 +65,11 @@ async function inlineI18n(options, executionResult, initialFiles) {
65
65
  destination: (0, node_path_1.join)(locale, assetFile.destination),
66
66
  });
67
67
  }
68
- inlineResult.prerenderedRoutes.push(...generatedRoutes.map((route) => node_path_1.posix.join('/', locale, route)));
69
68
  }
70
69
  else {
71
- inlineResult.prerenderedRoutes.push(...generatedRoutes);
72
70
  executionResult.assetFiles.push(...additionalAssets);
73
71
  }
72
+ inlineResult.prerenderedRoutes = { ...inlineResult.prerenderedRoutes, ...generatedRoutes };
74
73
  updatedOutputFiles.push(...localeOutputFiles);
75
74
  }
76
75
  }
@@ -79,8 +78,8 @@ async function inlineI18n(options, executionResult, initialFiles) {
79
78
  }
80
79
  // Update the result with all localized files.
81
80
  executionResult.outputFiles = [
82
- // Root files are not modified.
83
- ...executionResult.outputFiles.filter(({ type }) => type === bundler_context_1.BuildOutputFileType.Root),
81
+ // Root and SSR entry files are not modified.
82
+ ...executionResult.outputFiles.filter(({ type }) => type === bundler_context_1.BuildOutputFileType.Root || type === bundler_context_1.BuildOutputFileType.ServerRoot),
84
83
  // Updated files for each locale.
85
84
  ...updatedOutputFiles,
86
85
  ];
@@ -90,15 +89,6 @@ async function inlineI18n(options, executionResult, initialFiles) {
90
89
  }
91
90
  return inlineResult;
92
91
  }
93
- function getLocaleBaseHref(baseHref, i18n, locale) {
94
- if (i18n.flatOutput) {
95
- return undefined;
96
- }
97
- if (i18n.locales[locale] && i18n.locales[locale].baseHref !== '') {
98
- return (0, url_1.urlJoin)(baseHref || '', i18n.locales[locale].baseHref ?? `/${locale}/`);
99
- }
100
- return undefined;
101
- }
102
92
  /**
103
93
  * Loads all active translations using the translation loaders from the `@angular/localize` package.
104
94
  * @param context The architect builder context for the current build.
@@ -65,15 +65,15 @@ context, extensions) {
65
65
  context.addTeardown(() => controller.abort('builder-teardown'));
66
66
  }
67
67
  yield* (0, build_action_1.runEsBuildBuildAction)(async (rebuildState) => {
68
- const { prerenderOptions, jsonLogs } = normalizedOptions;
68
+ const { serverEntryPoint, jsonLogs, partialSSRBuild } = normalizedOptions;
69
69
  const startTime = process.hrtime.bigint();
70
70
  const result = await (0, execute_build_1.executeBuild)(normalizedOptions, context, rebuildState);
71
71
  if (jsonLogs) {
72
72
  result.addLog(await (0, utils_1.createJsonBuildManifest)(result, normalizedOptions));
73
73
  }
74
74
  else {
75
- if (prerenderOptions) {
76
- const prerenderedRoutesLength = result.prerenderedRoutes.length;
75
+ if (serverEntryPoint && !partialSSRBuild) {
76
+ const prerenderedRoutesLength = Object.keys(result.prerenderedRoutes).length;
77
77
  let prerenderMsg = `Prerendered ${prerenderedRoutesLength} static route`;
78
78
  prerenderMsg += prerenderedRoutesLength !== 1 ? 's.' : '.';
79
79
  result.addLog(color_1.colors.magenta(prerenderMsg));
@@ -137,7 +137,9 @@ async function* buildApplication(options, context, pluginsOrExtensions) {
137
137
  // Writes the output files to disk and ensures the containing directories are present
138
138
  const directoryExists = new Set();
139
139
  await (0, utils_1.emitFilesToDisk)(Object.entries(result.files), async ([filePath, file]) => {
140
- if (outputOptions.ignoreServer && file.type === bundler_context_1.BuildOutputFileType.Server) {
140
+ if (outputOptions.ignoreServer &&
141
+ (file.type === bundler_context_1.BuildOutputFileType.ServerApplication ||
142
+ file.type === bundler_context_1.BuildOutputFileType.ServerRoot)) {
141
143
  return;
142
144
  }
143
145
  let typeDirectory;
@@ -146,7 +148,8 @@ async function* buildApplication(options, context, pluginsOrExtensions) {
146
148
  case bundler_context_1.BuildOutputFileType.Media:
147
149
  typeDirectory = outputOptions.browser;
148
150
  break;
149
- case bundler_context_1.BuildOutputFileType.Server:
151
+ case bundler_context_1.BuildOutputFileType.ServerApplication:
152
+ case bundler_context_1.BuildOutputFileType.ServerRoot:
150
153
  typeDirectory = outputOptions.server;
151
154
  break;
152
155
  case bundler_context_1.BuildOutputFileType.Root:
@@ -9,7 +9,17 @@ 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, OutputPathClass } from './schema';
12
+ import { Schema as ApplicationBuilderOptions, I18NTranslation, OutputMode, OutputPathClass } from './schema';
13
+ /**
14
+ * The filename for the client-side rendered HTML template.
15
+ * This template is used for client-side rendering (CSR) in a web application.
16
+ */
17
+ export declare const INDEX_HTML_CSR = "index.csr.html";
18
+ /**
19
+ * The filename for the server-side rendered HTML template.
20
+ * This template is used for server-side rendering (SSR) in a web application.
21
+ */
22
+ export declare const INDEX_HTML_SERVER = "index.server.html";
13
23
  export type NormalizedOutputOptions = Required<OutputPathClass> & {
14
24
  clean: boolean;
15
25
  ignoreServer: boolean;
@@ -42,6 +52,26 @@ interface InternalOptions {
42
52
  * This is only used by the development server which currently only supports a single locale per build.
43
53
  */
44
54
  forceI18nFlatOutput?: boolean;
55
+ /**
56
+ * When set to `true`, enables fast SSR in development mode by disabling the full manifest generation and prerendering.
57
+ *
58
+ * This option is intended to optimize performance during development by skipping prerendering and route extraction when not required.
59
+ * @default false
60
+ */
61
+ partialSSRBuild?: boolean;
62
+ /**
63
+ * Enables the use of AOT compiler emitted external runtime styles.
64
+ * External runtime styles use `link` elements instead of embedded style content in the output JavaScript.
65
+ * This option is only intended to be used with a development server that can process and serve component
66
+ * styles.
67
+ */
68
+ externalRuntimeStyles?: boolean;
69
+ /**
70
+ * Enables instrumentation to collect code coverage data for specific files.
71
+ *
72
+ * Used exclusively for tests and shouldn't be used for other kinds of builds.
73
+ */
74
+ instrumentForCoverage?: (filename: string) => boolean;
45
75
  }
46
76
  /** Full set of options for `application` builder. */
47
77
  export type ApplicationBuilderInternalOptions = Omit<ApplicationBuilderOptions & InternalOptions, 'browser'> & {
@@ -86,6 +116,7 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
86
116
  appShellOptions: {
87
117
  route: string;
88
118
  } | undefined;
119
+ outputMode: OutputMode | undefined;
89
120
  ssrOptions: {
90
121
  entry?: undefined;
91
122
  } | {
@@ -147,5 +178,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
147
178
  define: {
148
179
  [key: string]: string;
149
180
  } | undefined;
181
+ partialSSRBuild: boolean;
182
+ externalRuntimeStyles: boolean | undefined;
183
+ instrumentForCoverage: ((filename: string) => boolean) | undefined;
150
184
  }>;
185
+ export declare function getLocaleBaseHref(baseHref: string | undefined, i18n: NormalizedApplicationBuildOptions['i18nOptions'], locale: string): string | undefined;
151
186
  export {};