@angular/build 19.0.0-next.7 → 19.0.0-next.9

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 (91) hide show
  1. package/LICENSE +5 -5
  2. package/package.json +10 -9
  3. package/src/builders/application/execute-build.js +19 -2
  4. package/src/builders/application/execute-post-bundle.d.ts +2 -2
  5. package/src/builders/application/execute-post-bundle.js +30 -11
  6. package/src/builders/application/i18n.d.ts +2 -2
  7. package/src/builders/application/i18n.js +4 -5
  8. package/src/builders/application/index.js +8 -5
  9. package/src/builders/application/options.d.ts +25 -1
  10. package/src/builders/application/options.js +31 -2
  11. package/src/builders/application/schema.d.ts +15 -0
  12. package/src/builders/application/schema.js +11 -1
  13. package/src/builders/application/schema.json +5 -0
  14. package/src/builders/application/setup-bundling.js +6 -3
  15. package/src/builders/dev-server/vite-server.d.ts +2 -1
  16. package/src/builders/dev-server/vite-server.js +71 -53
  17. package/src/builders/extract-i18n/application-extraction.js +3 -3
  18. package/src/tools/angular/angular-host.d.ts +2 -1
  19. package/src/tools/angular/angular-host.js +20 -1
  20. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  21. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  22. package/src/tools/angular/compilation/aot-compilation.js +9 -1
  23. package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -0
  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/babel/plugins/add-code-coverage.d.ts +14 -0
  27. package/src/tools/babel/plugins/add-code-coverage.js +44 -0
  28. package/src/tools/babel/plugins/types.d.ts +20 -0
  29. package/src/tools/esbuild/angular/compiler-plugin.d.ts +2 -0
  30. package/src/tools/esbuild/angular/compiler-plugin.js +44 -4
  31. package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
  32. package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
  33. package/src/tools/esbuild/application-code-bundle.d.ts +1 -0
  34. package/src/tools/esbuild/application-code-bundle.js +109 -2
  35. package/src/tools/esbuild/budget-stats.js +1 -1
  36. package/src/tools/esbuild/bundler-context.d.ts +4 -3
  37. package/src/tools/esbuild/bundler-context.js +8 -4
  38. package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
  39. package/src/tools/esbuild/bundler-execution-result.js +7 -3
  40. package/src/tools/esbuild/cache.d.ts +5 -0
  41. package/src/tools/esbuild/cache.js +7 -0
  42. package/src/tools/esbuild/compiler-plugin-options.js +3 -1
  43. package/src/tools/esbuild/i18n-inliner.js +2 -1
  44. package/src/tools/esbuild/javascript-transformer-worker.d.ts +1 -0
  45. package/src/tools/esbuild/javascript-transformer-worker.js +5 -1
  46. package/src/tools/esbuild/javascript-transformer.d.ts +2 -2
  47. package/src/tools/esbuild/javascript-transformer.js +5 -3
  48. package/src/tools/esbuild/utils.js +7 -3
  49. package/src/tools/vite/middlewares/assets-middleware.js +2 -5
  50. package/src/tools/vite/middlewares/html-fallback-middleware.js +22 -6
  51. package/src/tools/vite/middlewares/index.d.ts +1 -1
  52. package/src/tools/vite/middlewares/index.js +3 -2
  53. package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -1
  54. package/src/tools/vite/middlewares/ssr-middleware.js +62 -15
  55. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
  56. package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -41
  57. package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
  58. package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
  59. package/src/tools/vite/plugins/index.d.ts +12 -0
  60. package/src/tools/vite/plugins/index.js +21 -0
  61. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
  62. package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
  63. package/src/tools/vite/plugins/ssr-transform-plugin.d.ts +9 -0
  64. package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
  65. package/src/utils/environment-options.d.ts +2 -0
  66. package/src/utils/environment-options.js +5 -1
  67. package/src/utils/index-file/index-html-generator.js +5 -0
  68. package/src/utils/index-file/ngcm-attribute.d.ts +15 -0
  69. package/src/utils/index-file/ngcm-attribute.js +37 -0
  70. package/src/utils/index-file/valid-self-closing-tags.js +28 -0
  71. package/src/utils/normalize-cache.js +1 -1
  72. package/src/utils/server-rendering/fetch-patch.d.ts +1 -1
  73. package/src/utils/server-rendering/fetch-patch.js +2 -2
  74. package/src/utils/server-rendering/launch-server.d.ts +14 -0
  75. package/src/utils/server-rendering/launch-server.js +63 -0
  76. package/src/utils/server-rendering/load-esm-from-memory.d.ts +7 -0
  77. package/src/utils/server-rendering/manifest.d.ts +8 -2
  78. package/src/utils/server-rendering/manifest.js +52 -12
  79. package/src/utils/server-rendering/models.d.ts +27 -0
  80. package/src/utils/server-rendering/models.js +22 -0
  81. package/src/utils/server-rendering/prerender.d.ts +6 -10
  82. package/src/utils/server-rendering/prerender.js +102 -63
  83. package/src/utils/server-rendering/render-worker.d.ts +4 -1
  84. package/src/utils/server-rendering/render-worker.js +13 -3
  85. package/src/utils/server-rendering/routes-extractor-worker.d.ts +6 -10
  86. package/src/utils/server-rendering/routes-extractor-worker.js +14 -5
  87. package/src/utils/server-rendering/utils.d.ts +11 -0
  88. package/src/utils/server-rendering/utils.js +17 -0
  89. package/src/tools/vite/angular-memory-plugin.d.ts +0 -22
  90. /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
  91. /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.7",
3
+ "version": "19.0.0-next.9",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,7 +23,7 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1900.0-next.7",
26
+ "@angular-devkit/architect": "0.1900.0-next.9",
27
27
  "@babel/core": "7.25.2",
28
28
  "@babel/helper-annotate-as-pure": "7.24.7",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
@@ -32,20 +32,21 @@
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
+ "istanbul-lib-instrument": "6.0.3",
38
39
  "listr2": "8.2.4",
39
- "lmdb": "3.1.2",
40
+ "lmdb": "3.1.3",
40
41
  "magic-string": "0.30.11",
41
42
  "mrmime": "2.0.0",
42
43
  "parse5-html-rewriting-stream": "7.0.0",
43
44
  "picomatch": "4.0.2",
44
45
  "piscina": "4.7.0",
45
- "rollup": "4.21.3",
46
- "sass": "1.79.1",
46
+ "rollup": "4.22.5",
47
+ "sass": "1.79.4",
47
48
  "semver": "7.6.3",
48
- "vite": "5.4.6",
49
+ "vite": "5.4.8",
49
50
  "watchpack": "2.4.2"
50
51
  },
51
52
  "peerDependencies": {
@@ -54,7 +55,7 @@
54
55
  "@angular/localize": "^19.0.0-next.0",
55
56
  "@angular/platform-server": "^19.0.0-next.0",
56
57
  "@angular/service-worker": "^19.0.0-next.0",
57
- "@angular/ssr": "^19.0.0-next.7",
58
+ "@angular/ssr": "^19.0.0-next.9",
58
59
  "less": "^4.2.0",
59
60
  "postcss": "^8.4.0",
60
61
  "tailwindcss": "^2.0.0 || ^3.0.0",
@@ -83,7 +84,7 @@
83
84
  "optional": true
84
85
  }
85
86
  },
86
- "packageManager": "yarn@4.4.0",
87
+ "packageManager": "yarn@4.5.0",
87
88
  "repository": {
88
89
  "type": "git",
89
90
  "url": "https://github.com/angular/angular-cli.git"
@@ -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
  }>;
@@ -17,9 +17,11 @@ 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
19
  const manifest_1 = require("../../utils/server-rendering/manifest");
20
+ const models_1 = require("../../utils/server-rendering/models");
20
21
  const prerender_1 = require("../../utils/server-rendering/prerender");
21
22
  const service_worker_1 = require("../../utils/service-worker");
22
23
  const options_1 = require("./options");
24
+ const schema_1 = require("./schema");
23
25
  /**
24
26
  * Run additional builds steps including SSG, AppShell, Index HTML file and Service worker generation.
25
27
  * @param options The normalized application builder options used to create the build.
@@ -33,8 +35,8 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
33
35
  const additionalOutputFiles = [];
34
36
  const allErrors = [];
35
37
  const allWarnings = [];
36
- const prerenderedRoutes = [];
37
- const { baseHref = '/', serviceWorker, indexHtmlOptions, optimizationOptions, sourcemapOptions, ssrOptions, prerenderOptions, appShellOptions, workspaceRoot, verbose, } = options;
38
+ const prerenderedRoutes = {};
39
+ const { baseHref = '/', serviceWorker, indexHtmlOptions, optimizationOptions, sourcemapOptions, outputMode, serverEntryPoint, prerenderOptions, appShellOptions, workspaceRoot, partialSSRBuild, } = options;
38
40
  // Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
39
41
  // NOTE: Critical CSS inlining is deliberately omitted here, as it will be handled during server rendering.
40
42
  // Additionally, when using prerendering or AppShell, the index HTML file may be regenerated.
@@ -48,22 +50,23 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
48
50
  allWarnings.push(...warnings);
49
51
  additionalHtmlOutputFiles.set(indexHtmlOptions.output, (0, utils_1.createOutputFile)(indexHtmlOptions.output, csrContent, bundler_context_1.BuildOutputFileType.Browser));
50
52
  if (ssrContent) {
51
- additionalHtmlOutputFiles.set(options_1.INDEX_HTML_SERVER, (0, utils_1.createOutputFile)(options_1.INDEX_HTML_SERVER, ssrContent, bundler_context_1.BuildOutputFileType.Server));
53
+ additionalHtmlOutputFiles.set(options_1.INDEX_HTML_SERVER, (0, utils_1.createOutputFile)(options_1.INDEX_HTML_SERVER, ssrContent, bundler_context_1.BuildOutputFileType.ServerApplication));
52
54
  }
53
55
  }
54
56
  // Create server manifest
55
- if (prerenderOptions || appShellOptions || ssrOptions) {
56
- additionalOutputFiles.push((0, utils_1.createOutputFile)(manifest_1.SERVER_APP_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, undefined), bundler_context_1.BuildOutputFileType.Server));
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));
57
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) && !allErrors.length) {
62
+ if (!partialSSRBuild &&
63
+ (prerenderOptions || appShellOptions || (outputMode && serverEntryPoint)) &&
64
+ !allErrors.length) {
61
65
  (0, node_assert_1.default)(indexHtmlOptions, 'The "index" option is required when using the "ssg" or "appShell" options.');
62
- const { output, warnings, errors, prerenderedRoutes: generatedRoutes, serializableRouteTreeNode, } = await (0, prerender_1.prerenderPages)(workspaceRoot, baseHref, appShellOptions, prerenderOptions, [...outputFiles, ...additionalOutputFiles], assetFiles, sourcemapOptions.scripts, environment_options_1.maxWorkers, verbose);
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
- const indexHasBeenPrerendered = generatedRoutes.has(indexHtmlOptions.output);
69
+ const indexHasBeenPrerendered = output[indexHtmlOptions.output];
67
70
  for (const [path, { content, appShellRoute }] of Object.entries(output)) {
68
71
  // Update the index contents with the app shell under these conditions:
69
72
  // - Replace 'index.html' with the app shell only if it hasn't been prerendered yet.
@@ -71,11 +74,27 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
71
74
  const filePath = appShellRoute && !indexHasBeenPrerendered ? indexHtmlOptions.output : path;
72
75
  additionalHtmlOutputFiles.set(filePath, (0, utils_1.createOutputFile)(filePath, content, bundler_context_1.BuildOutputFileType.Browser));
73
76
  }
74
- if (ssrOptions) {
77
+ const serializableRouteTreeNodeForManifest = [];
78
+ for (const metadata of serializableRouteTreeNode) {
79
+ switch (metadata.renderMode) {
80
+ case models_1.RouteRenderMode.Prerender:
81
+ case /* Legacy building mode */ undefined: {
82
+ if (!metadata.redirectTo || outputMode === schema_1.OutputMode.Static) {
83
+ prerenderedRoutes[metadata.route] = { headers: metadata.headers };
84
+ }
85
+ break;
86
+ }
87
+ case models_1.RouteRenderMode.Server:
88
+ case models_1.RouteRenderMode.Client:
89
+ serializableRouteTreeNodeForManifest.push(metadata);
90
+ break;
91
+ }
92
+ }
93
+ if (outputMode === schema_1.OutputMode.Server) {
75
94
  // Regenerate the manifest to append route tree. This is only needed if SSR is enabled.
76
95
  const manifest = additionalOutputFiles.find((f) => f.path === manifest_1.SERVER_APP_MANIFEST_FILENAME);
77
96
  (0, node_assert_1.default)(manifest, `${manifest_1.SERVER_APP_MANIFEST_FILENAME} was not found in output files.`);
78
- manifest.contents = new TextEncoder().encode((0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, serializableRouteTreeNode));
97
+ manifest.contents = new TextEncoder().encode((0, manifest_1.generateAngularServerAppManifest)(additionalHtmlOutputFiles, outputFiles, optimizationOptions.styles.inlineCritical ?? false, serializableRouteTreeNodeForManifest, locale));
79
98
  }
80
99
  }
81
100
  additionalOutputFiles.push(...additionalHtmlOutputFiles.values());
@@ -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.
@@ -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 = [];
@@ -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
  ];
@@ -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,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, OutputPathClass } from './schema';
12
+ import { Schema as ApplicationBuilderOptions, 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.
@@ -52,6 +52,26 @@ interface InternalOptions {
52
52
  * This is only used by the development server which currently only supports a single locale per build.
53
53
  */
54
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;
55
75
  }
56
76
  /** Full set of options for `application` builder. */
57
77
  export type ApplicationBuilderInternalOptions = Omit<ApplicationBuilderOptions & InternalOptions, 'browser'> & {
@@ -96,6 +116,7 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
96
116
  appShellOptions: {
97
117
  route: string;
98
118
  } | undefined;
119
+ outputMode: OutputMode | undefined;
99
120
  ssrOptions: {
100
121
  entry?: undefined;
101
122
  } | {
@@ -157,6 +178,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
157
178
  define: {
158
179
  [key: string]: string;
159
180
  } | undefined;
181
+ partialSSRBuild: boolean;
182
+ externalRuntimeStyles: boolean | undefined;
183
+ instrumentForCoverage: ((filename: string) => boolean) | undefined;
160
184
  }>;
161
185
  export declare function getLocaleBaseHref(baseHref: string | undefined, i18n: NormalizedApplicationBuildOptions['i18nOptions'], locale: string): string | undefined;
162
186
  export {};
@@ -103,6 +103,29 @@ async function normalizeOptions(context, projectName, options, extensions) {
103
103
  loaderExtensions[extension] = value;
104
104
  }
105
105
  }
106
+ // Validate prerender and ssr options when using the outputMode
107
+ if (options.outputMode === schema_1.OutputMode.Server) {
108
+ if (!options.server) {
109
+ throw new Error('The "server" option is required when "outputMode" is set to "server".');
110
+ }
111
+ if (typeof options.ssr === 'boolean' || !options.ssr?.entry) {
112
+ throw new Error('The "ssr.entry" option is required when "outputMode" is set to "server".');
113
+ }
114
+ }
115
+ if (options.outputMode) {
116
+ if (!options.server) {
117
+ options.ssr = false;
118
+ }
119
+ if (options.prerender) {
120
+ context.logger.warn('The "prerender" option is no longer needed when "outputMode" is specified.');
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.');
127
+ }
128
+ }
106
129
  // A configuration file can exist in the project or workspace root
107
130
  const searchDirectories = await (0, postcss_configuration_1.generateSearchDirectories)([projectRoot, workspaceRoot]);
108
131
  const postcssConfiguration = await (0, postcss_configuration_1.loadPostcssConfiguration)(searchDirectories);
@@ -151,7 +174,9 @@ async function normalizeOptions(context, projectName, options, extensions) {
151
174
  clean: options.deleteOutputPath ?? true,
152
175
  // For app-shell and SSG server files are not required by users.
153
176
  // Omit these when SSR is not enabled.
154
- ignoreServer: ssrOptions === undefined || serverEntryPoint === undefined,
177
+ ignoreServer: ((ssrOptions === undefined || serverEntryPoint === undefined) &&
178
+ options.outputMode === undefined) ||
179
+ options.outputMode === schema_1.OutputMode.Static,
155
180
  };
156
181
  const outputNames = {
157
182
  bundles: options.outputHashing === schema_1.OutputHashing.All || options.outputHashing === schema_1.OutputHashing.Bundles
@@ -206,7 +231,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
206
231
  }
207
232
  }
208
233
  // Initial options to keep
209
- const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, namedChunks, budgets, deployUrl, clearScreen, define, } = options;
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;
210
235
  // Return all the normalized options
211
236
  return {
212
237
  advancedOptimizations: !!aot && optimizationOptions.scripts,
@@ -229,6 +254,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
229
254
  serverEntryPoint,
230
255
  prerenderOptions,
231
256
  appShellOptions,
257
+ outputMode,
232
258
  ssrOptions,
233
259
  verbose,
234
260
  watch,
@@ -261,6 +287,9 @@ async function normalizeOptions(context, projectName, options, extensions) {
261
287
  colors: (0, color_1.supportColor)(),
262
288
  clearScreen,
263
289
  define,
290
+ partialSSRBuild: environment_options_1.usePartialSsrBuild || partialSSRBuild,
291
+ externalRuntimeStyles,
292
+ instrumentForCoverage,
264
293
  };
265
294
  }
266
295
  async function getTailwindConfig(searchDirectories, workspaceRoot, context) {
@@ -117,6 +117,12 @@ export interface Schema {
117
117
  * Define the output filename cache-busting hashing mode.
118
118
  */
119
119
  outputHashing?: OutputHashing;
120
+ /**
121
+ * Defines the build output target. 'static': Generates a static site for deployment on any
122
+ * static hosting service. 'server': Produces an application designed for deployment on a
123
+ * server that supports server-side rendering (SSR).
124
+ */
125
+ outputMode?: OutputMode;
120
126
  /**
121
127
  * Specify the output path relative to workspace root.
122
128
  */
@@ -393,6 +399,15 @@ export declare enum OutputHashing {
393
399
  Media = "media",
394
400
  None = "none"
395
401
  }
402
+ /**
403
+ * Defines the build output target. 'static': Generates a static site for deployment on any
404
+ * static hosting service. 'server': Produces an application designed for deployment on a
405
+ * server that supports server-side rendering (SSR).
406
+ */
407
+ export declare enum OutputMode {
408
+ Server = "server",
409
+ Static = "static"
410
+ }
396
411
  /**
397
412
  * Specify the output path relative to workspace root.
398
413
  */
@@ -2,7 +2,7 @@
2
2
  // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
3
3
  // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.OutputHashing = exports.InlineStyleLanguage = exports.I18NTranslation = exports.CrossOrigin = exports.Type = void 0;
5
+ exports.OutputMode = exports.OutputHashing = exports.InlineStyleLanguage = exports.I18NTranslation = exports.CrossOrigin = exports.Type = void 0;
6
6
  /**
7
7
  * The type of budget.
8
8
  */
@@ -56,3 +56,13 @@ var OutputHashing;
56
56
  OutputHashing["Media"] = "media";
57
57
  OutputHashing["None"] = "none";
58
58
  })(OutputHashing || (exports.OutputHashing = OutputHashing = {}));
59
+ /**
60
+ * Defines the build output target. 'static': Generates a static site for deployment on any
61
+ * static hosting service. 'server': Produces an application designed for deployment on a
62
+ * server that supports server-side rendering (SSR).
63
+ */
64
+ var OutputMode;
65
+ (function (OutputMode) {
66
+ OutputMode["Server"] = "server";
67
+ OutputMode["Static"] = "static";
68
+ })(OutputMode || (exports.OutputMode = OutputMode = {}));
@@ -528,6 +528,11 @@
528
528
  "type": "boolean",
529
529
  "description": "Generates an application shell during build time.",
530
530
  "default": false
531
+ },
532
+ "outputMode": {
533
+ "type": "string",
534
+ "description": "Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR).",
535
+ "enum": ["static", "server"]
531
536
  }
532
537
  },
533
538
  "additionalProperties": false,
@@ -22,7 +22,7 @@ const utils_1 = require("../../tools/esbuild/utils");
22
22
  * @returns An array of BundlerContext objects.
23
23
  */
24
24
  function setupBundlerContexts(options, browsers, codeBundleCache) {
25
- const { appShellOptions, prerenderOptions, serverEntryPoint, ssrOptions, workspaceRoot, watch = false, } = options;
25
+ const { outputMode, serverEntryPoint, appShellOptions, prerenderOptions, ssrOptions, workspaceRoot, watch = false, } = options;
26
26
  const target = (0, utils_1.transformSupportedBrowsersToTargets)(browsers);
27
27
  const bundlerContexts = [];
28
28
  // Browser application code
@@ -51,10 +51,13 @@ function setupBundlerContexts(options, browsers, codeBundleCache) {
51
51
  }
52
52
  }
53
53
  // Skip server build when none of the features are enabled.
54
- if (serverEntryPoint && (prerenderOptions || appShellOptions || ssrOptions)) {
54
+ if (serverEntryPoint && (outputMode || prerenderOptions || appShellOptions || ssrOptions)) {
55
55
  const nodeTargets = [...target, ...(0, utils_1.getSupportedNodeTargets)()];
56
- // Server application code
57
56
  bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, watch, (0, application_code_bundle_1.createServerMainCodeBundleOptions)(options, nodeTargets, codeBundleCache)));
57
+ if (outputMode && ssrOptions?.entry) {
58
+ // New behavior introduced: 'server.ts' is now bundled separately from 'main.server.ts'.
59
+ bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, watch, (0, application_code_bundle_1.createSsrEntryCodeBundleOptions)(options, nodeTargets, codeBundleCache)));
60
+ }
58
61
  // Server polyfills code
59
62
  const serverPolyfillBundleOptions = (0, application_code_bundle_1.createServerPolyfillBundleOptions)(options, nodeTargets, codeBundleCache);
60
63
  if (serverPolyfillBundleOptions) {
@@ -8,6 +8,7 @@
8
8
  import type { BuilderContext } from '@angular-devkit/architect';
9
9
  import type { Plugin } from 'esbuild';
10
10
  import type { Connect, DepOptimizationConfig, InlineConfig } from 'vite';
11
+ import { ServerSsrMode } from '../../tools/vite/plugins';
11
12
  import { Result } from '../application/results';
12
13
  import { type ApplicationBuilderInternalOptions, BuildOutputFileType, type ExternalResultMetadata, JavaScriptTransformer } from './internal';
13
14
  import type { NormalizedDevServerOptions } from './options';
@@ -31,6 +32,6 @@ export declare function serveWithVite(serverOptions: NormalizedDevServerOptions,
31
32
  middleware?: Connect.NextHandleFunction[];
32
33
  buildPlugins?: Plugin[];
33
34
  }): AsyncIterableIterator<DevServerBuilderOutput>;
34
- export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: DevServerExternalResultMetadata, ssr: boolean, prebundleTransformer: JavaScriptTransformer, target: string[], zoneless: boolean, usedComponentStyles: Map<string, string[]>, prebundleLoaderExtensions: EsbuildLoaderOption | undefined, extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>, thirdPartySourcemaps?: boolean): Promise<InlineConfig>;
35
+ export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: DevServerExternalResultMetadata, ssrMode: ServerSsrMode, prebundleTransformer: JavaScriptTransformer, target: string[], zoneless: boolean, usedComponentStyles: Map<string, string[]>, prebundleLoaderExtensions: EsbuildLoaderOption | undefined, extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>, thirdPartySourcemaps?: boolean): Promise<InlineConfig>;
35
36
  type EsbuildLoaderOption = Exclude<DepOptimizationConfig['esbuildOptions'], undefined>['loader'];
36
37
  export {};