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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/package.json +9 -9
  2. package/src/builders/application/execute-build.js +19 -2
  3. package/src/builders/application/execute-post-bundle.d.ts +2 -2
  4. package/src/builders/application/execute-post-bundle.js +30 -11
  5. package/src/builders/application/i18n.d.ts +2 -2
  6. package/src/builders/application/i18n.js +4 -5
  7. package/src/builders/application/index.js +8 -5
  8. package/src/builders/application/options.d.ts +18 -1
  9. package/src/builders/application/options.js +30 -2
  10. package/src/builders/application/schema.d.ts +15 -0
  11. package/src/builders/application/schema.js +11 -1
  12. package/src/builders/application/schema.json +5 -0
  13. package/src/builders/application/setup-bundling.js +6 -3
  14. package/src/builders/dev-server/vite-server.d.ts +2 -1
  15. package/src/builders/dev-server/vite-server.js +64 -47
  16. package/src/builders/extract-i18n/application-extraction.js +3 -3
  17. package/src/tools/angular/angular-host.d.ts +2 -1
  18. package/src/tools/angular/angular-host.js +20 -1
  19. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  20. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  21. package/src/tools/angular/compilation/aot-compilation.js +9 -1
  22. package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -0
  23. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  24. package/src/tools/angular/compilation/parallel-worker.js +2 -1
  25. package/src/tools/esbuild/angular/compiler-plugin.d.ts +1 -0
  26. package/src/tools/esbuild/angular/compiler-plugin.js +42 -3
  27. package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
  28. package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
  29. package/src/tools/esbuild/application-code-bundle.d.ts +1 -0
  30. package/src/tools/esbuild/application-code-bundle.js +109 -2
  31. package/src/tools/esbuild/budget-stats.js +1 -1
  32. package/src/tools/esbuild/bundler-context.d.ts +4 -3
  33. package/src/tools/esbuild/bundler-context.js +8 -4
  34. package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
  35. package/src/tools/esbuild/bundler-execution-result.js +7 -3
  36. package/src/tools/esbuild/cache.d.ts +5 -0
  37. package/src/tools/esbuild/cache.js +7 -0
  38. package/src/tools/esbuild/compiler-plugin-options.js +2 -1
  39. package/src/tools/esbuild/i18n-inliner.js +2 -1
  40. package/src/tools/esbuild/utils.js +7 -3
  41. package/src/tools/vite/middlewares/assets-middleware.js +2 -5
  42. package/src/tools/vite/middlewares/html-fallback-middleware.js +22 -6
  43. package/src/tools/vite/middlewares/index.d.ts +1 -1
  44. package/src/tools/vite/middlewares/index.js +3 -2
  45. package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -1
  46. package/src/tools/vite/middlewares/ssr-middleware.js +61 -15
  47. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
  48. package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -41
  49. package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
  50. package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
  51. package/src/tools/vite/plugins/index.d.ts +12 -0
  52. package/src/tools/vite/plugins/index.js +21 -0
  53. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
  54. package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
  55. package/src/tools/vite/plugins/ssr-transform-plugin.d.ts +9 -0
  56. package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
  57. package/src/utils/environment-options.d.ts +2 -0
  58. package/src/utils/environment-options.js +5 -1
  59. package/src/utils/index-file/valid-self-closing-tags.js +1 -0
  60. package/src/utils/normalize-cache.js +1 -1
  61. package/src/utils/server-rendering/manifest.d.ts +8 -2
  62. package/src/utils/server-rendering/manifest.js +61 -12
  63. package/src/utils/server-rendering/models.d.ts +27 -0
  64. package/src/utils/server-rendering/models.js +22 -0
  65. package/src/utils/server-rendering/prerender.d.ts +6 -10
  66. package/src/utils/server-rendering/prerender.js +100 -63
  67. package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -10
  68. package/src/utils/server-rendering/routes-extractor-worker.js +3 -4
  69. package/src/tools/vite/angular-memory-plugin.d.ts +0 -22
  70. /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
  71. /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
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.8",
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.8",
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,20 @@
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
38
  "listr2": "8.2.4",
39
- "lmdb": "3.1.2",
39
+ "lmdb": "3.1.3",
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
44
  "piscina": "4.7.0",
45
- "rollup": "4.21.3",
46
- "sass": "1.79.1",
45
+ "rollup": "4.22.4",
46
+ "sass": "1.79.3",
47
47
  "semver": "7.6.3",
48
- "vite": "5.4.6",
48
+ "vite": "5.4.8",
49
49
  "watchpack": "2.4.2"
50
50
  },
51
51
  "peerDependencies": {
@@ -54,7 +54,7 @@
54
54
  "@angular/localize": "^19.0.0-next.0",
55
55
  "@angular/platform-server": "^19.0.0-next.0",
56
56
  "@angular/service-worker": "^19.0.0-next.0",
57
- "@angular/ssr": "^19.0.0-next.7",
57
+ "@angular/ssr": "^19.0.0-next.8",
58
58
  "less": "^4.2.0",
59
59
  "postcss": "^8.4.0",
60
60
  "tailwindcss": "^2.0.0 || ^3.0.0",
@@ -83,7 +83,7 @@
83
83
  "optional": true
84
84
  }
85
85
  },
86
- "packageManager": "yarn@4.4.0",
86
+ "packageManager": "yarn@4.5.0",
87
87
  "repository": {
88
88
  "type": "git",
89
89
  "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,20 @@ 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;
55
69
  }
56
70
  /** Full set of options for `application` builder. */
57
71
  export type ApplicationBuilderInternalOptions = Omit<ApplicationBuilderOptions & InternalOptions, 'browser'> & {
@@ -96,6 +110,7 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
96
110
  appShellOptions: {
97
111
  route: string;
98
112
  } | undefined;
113
+ outputMode: OutputMode | undefined;
99
114
  ssrOptions: {
100
115
  entry?: undefined;
101
116
  } | {
@@ -157,6 +172,8 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
157
172
  define: {
158
173
  [key: string]: string;
159
174
  } | undefined;
175
+ partialSSRBuild: boolean;
176
+ externalRuntimeStyles: boolean | undefined;
160
177
  }>;
161
178
  export declare function getLocaleBaseHref(baseHref: string | undefined, i18n: NormalizedApplicationBuildOptions['i18nOptions'], locale: string): string | undefined;
162
179
  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, } = 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,8 @@ 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,
264
292
  };
265
293
  }
266
294
  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 {};