@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
@@ -11,16 +11,19 @@ exports.prerenderPages = prerenderPages;
11
11
  const promises_1 = require("node:fs/promises");
12
12
  const node_path_1 = require("node:path");
13
13
  const node_url_1 = require("node:url");
14
+ const schema_1 = require("../../builders/application/schema");
14
15
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
16
+ const error_1 = require("../error");
15
17
  const url_1 = require("../url");
16
18
  const worker_pool_1 = require("../worker-pool");
17
- async function prerenderPages(workspaceRoot, baseHref, appShellOptions = {}, prerenderOptions = {}, outputFiles, assets, sourcemap = false, maxThreads = 1, verbose = false) {
19
+ const models_1 = require("./models");
20
+ async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerenderOptions, outputFiles, assets, outputMode, sourcemap = false, maxThreads = 1) {
18
21
  const outputFilesForWorker = {};
19
22
  const serverBundlesSourceMaps = new Map();
20
23
  const warnings = [];
21
24
  const errors = [];
22
25
  for (const { text, path, type } of outputFiles) {
23
- if (type !== bundler_context_1.BuildOutputFileType.Server) {
26
+ if (type !== bundler_context_1.BuildOutputFileType.ServerApplication && type !== bundler_context_1.BuildOutputFileType.ServerRoot) {
24
27
  continue;
25
28
  }
26
29
  // Contains the server runnable application code
@@ -48,39 +51,57 @@ async function prerenderPages(workspaceRoot, baseHref, appShellOptions = {}, pre
48
51
  assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
49
52
  }
50
53
  // Get routes to prerender
51
- const { routes: allRoutes, warnings: routesWarnings, errors: routesErrors, serializableRouteTreeNode, } = await getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetsReversed, appShellOptions, prerenderOptions, sourcemap, verbose);
52
- if (routesErrors?.length) {
53
- errors.push(...routesErrors);
54
- }
55
- if (routesWarnings?.length) {
56
- warnings.push(...routesWarnings);
54
+ const { errors: extractionErrors, serializedRouteTree: serializableRouteTreeNode } = await getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetsReversed, appShellOptions, prerenderOptions, sourcemap, outputMode).catch((err) => {
55
+ return {
56
+ errors: [
57
+ `An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err}`,
58
+ ],
59
+ serializedRouteTree: [],
60
+ };
61
+ });
62
+ errors.push(...extractionErrors);
63
+ const serializableRouteTreeNodeForPrerender = [];
64
+ for (const metadata of serializableRouteTreeNode) {
65
+ if (outputMode !== schema_1.OutputMode.Static && metadata.redirectTo) {
66
+ // Skip redirects if output mode is not static.
67
+ continue;
68
+ }
69
+ if (metadata.route.includes('*')) {
70
+ // Skip catch all routes from prerender.
71
+ continue;
72
+ }
73
+ switch (metadata.renderMode) {
74
+ case undefined: /* Legacy building mode */
75
+ case models_1.RouteRenderMode.Prerender:
76
+ case models_1.RouteRenderMode.AppShell:
77
+ serializableRouteTreeNodeForPrerender.push(metadata);
78
+ break;
79
+ case models_1.RouteRenderMode.Server:
80
+ if (outputMode === schema_1.OutputMode.Static) {
81
+ errors.push(`Route '${metadata.route}' is configured with server render mode, but the build 'outputMode' is set to 'static'.`);
82
+ }
83
+ break;
84
+ }
57
85
  }
58
- if (allRoutes.size < 1 || errors.length > 0) {
86
+ if (!serializableRouteTreeNodeForPrerender.length || errors.length > 0) {
59
87
  return {
60
88
  errors,
61
89
  warnings,
62
90
  output: {},
63
91
  serializableRouteTreeNode,
64
- prerenderedRoutes: allRoutes,
65
92
  };
66
93
  }
67
94
  // Render routes
68
- const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, appShellOptions);
95
+ const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, serializableRouteTreeNodeForPrerender, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, appShellOptions);
69
96
  errors.push(...renderingErrors);
70
97
  return {
71
98
  errors,
72
99
  warnings,
73
100
  output,
74
101
  serializableRouteTreeNode,
75
- prerenderedRoutes: allRoutes,
76
102
  };
77
103
  }
78
- class RoutesSet extends Set {
79
- add(value) {
80
- return super.add(addLeadingSlash(value));
81
- }
82
- }
83
- async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions) {
104
+ async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions) {
84
105
  const output = {};
85
106
  const errors = [];
86
107
  const workerExecArgv = [
@@ -93,7 +114,7 @@ async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspace
93
114
  }
94
115
  const renderWorker = new worker_pool_1.WorkerPool({
95
116
  filename: require.resolve('./render-worker'),
96
- maxThreads: Math.min(allRoutes.size, maxThreads),
117
+ maxThreads: Math.min(serializableRouteTreeNode.length, maxThreads),
97
118
  workerData: {
98
119
  workspaceRoot,
99
120
  outputFiles: outputFilesForWorker,
@@ -103,17 +124,23 @@ async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspace
103
124
  });
104
125
  try {
105
126
  const renderingPromises = [];
106
- const appShellRoute = appShellOptions.route && addLeadingSlash(appShellOptions.route);
127
+ const appShellRoute = appShellOptions && addLeadingSlash(appShellOptions.route);
107
128
  const baseHrefWithLeadingSlash = addLeadingSlash(baseHref);
108
- for (const route of allRoutes) {
129
+ for (const { route, redirectTo, renderMode } of serializableRouteTreeNode) {
109
130
  // Remove base href from file output path.
110
131
  const routeWithoutBaseHref = addLeadingSlash(route.slice(baseHrefWithLeadingSlash.length - 1));
111
- const render = renderWorker.run({ url: route });
132
+ const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
133
+ if (typeof redirectTo === 'string') {
134
+ output[outPath] = { content: generateRedirectStaticPage(redirectTo), appShellRoute: false };
135
+ continue;
136
+ }
137
+ const isAppShellRoute = renderMode === models_1.RouteRenderMode.AppShell ||
138
+ // Legacy handling
139
+ (renderMode === undefined && appShellRoute === routeWithoutBaseHref);
140
+ const render = renderWorker.run({ url: route, isAppShellRoute });
112
141
  const renderResult = render
113
142
  .then((content) => {
114
143
  if (content !== null) {
115
- const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
116
- const isAppShellRoute = appShellRoute === routeWithoutBaseHref;
117
144
  output[outPath] = { content, appShellRoute: isAppShellRoute };
118
145
  }
119
146
  })
@@ -133,21 +160,24 @@ async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspace
133
160
  output,
134
161
  };
135
162
  }
136
- async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetFilesForWorker, appShellOptions, prerenderOptions, sourcemap, verbose) {
137
- const { routesFile, discoverRoutes } = prerenderOptions;
138
- const routes = new RoutesSet();
139
- const { route: appShellRoute } = appShellOptions;
140
- if (appShellRoute !== undefined) {
141
- routes.add((0, url_1.urlJoin)(baseHref, appShellRoute));
163
+ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetFilesForWorker, appShellOptions, prerenderOptions, sourcemap, outputMode) {
164
+ const { routesFile, discoverRoutes } = prerenderOptions ?? {};
165
+ const routes = [];
166
+ if (appShellOptions) {
167
+ routes.push({
168
+ route: (0, url_1.urlJoin)(baseHref, appShellOptions.route),
169
+ });
142
170
  }
143
171
  if (routesFile) {
144
172
  const routesFromFile = (await (0, promises_1.readFile)(routesFile, 'utf8')).split(/\r?\n/);
145
173
  for (const route of routesFromFile) {
146
- routes.add((0, url_1.urlJoin)(baseHref, route.trim()));
174
+ routes.push({
175
+ route: (0, url_1.urlJoin)(baseHref, route.trim()),
176
+ });
147
177
  }
148
178
  }
149
179
  if (!discoverRoutes) {
150
- return { routes, serializableRouteTreeNode: [] };
180
+ return { errors: [], serializedRouteTree: routes };
151
181
  }
152
182
  const workerExecArgv = [
153
183
  '--import',
@@ -167,39 +197,22 @@ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, asset
167
197
  },
168
198
  execArgv: workerExecArgv,
169
199
  });
170
- const errors = [];
171
- const { serializedRouteTree: serializableRouteTreeNode } = await renderWorker
172
- .run({})
173
- .catch((err) => {
174
- errors.push(`An error occurred while extracting routes.\n\n${err.stack}`);
175
- })
176
- .finally(() => {
177
- void renderWorker.destroy();
178
- });
179
- const skippedRedirects = [];
180
- const skippedOthers = [];
181
- for (const { route, redirectTo } of serializableRouteTreeNode) {
182
- if (redirectTo) {
183
- skippedRedirects.push(route);
184
- }
185
- else if (route.includes('*')) {
186
- skippedOthers.push(route);
187
- }
188
- else {
189
- routes.add(route);
190
- }
200
+ try {
201
+ const { serializedRouteTree, errors } = await renderWorker.run({
202
+ outputMode,
203
+ });
204
+ return { errors, serializedRouteTree: [...routes, ...serializedRouteTree] };
191
205
  }
192
- let warnings;
193
- if (verbose) {
194
- if (skippedOthers.length) {
195
- (warnings ??= []).push('The following routes were skipped from prerendering because they contain routes with dynamic parameters:\n' +
196
- skippedOthers.join('\n'));
197
- }
198
- if (skippedRedirects.length) {
199
- (warnings ??= []).push('The following routes were skipped from prerendering because they contain redirects:\n', skippedRedirects.join('\n'));
200
- }
206
+ catch (err) {
207
+ (0, error_1.assertIsError)(err);
208
+ return {
209
+ errors: [`An error occurred while extracting routes.\n\n${err.stack}`],
210
+ serializedRouteTree: [],
211
+ };
212
+ }
213
+ finally {
214
+ void renderWorker.destroy();
201
215
  }
202
- return { routes, serializableRouteTreeNode, warnings };
203
216
  }
204
217
  function addLeadingSlash(value) {
205
218
  return value.charAt(0) === '/' ? value : '/' + value;
@@ -207,3 +220,27 @@ function addLeadingSlash(value) {
207
220
  function removeLeadingSlash(value) {
208
221
  return value.charAt(0) === '/' ? value.slice(1) : value;
209
222
  }
223
+ /**
224
+ * Generates a static HTML page with a meta refresh tag to redirect the user to a specified URL.
225
+ *
226
+ * This function creates a simple HTML page that performs a redirect using a meta tag.
227
+ * It includes a fallback link in case the meta-refresh doesn't work.
228
+ *
229
+ * @param url - The URL to which the page should redirect.
230
+ * @returns The HTML content of the static redirect page.
231
+ */
232
+ function generateRedirectStaticPage(url) {
233
+ return `
234
+ <!DOCTYPE html>
235
+ <html>
236
+ <head>
237
+ <meta charset="utf-8">
238
+ <title>Redirecting</title>
239
+ <meta http-equiv="refresh" content="0; url=${url}">
240
+ </head>
241
+ <body>
242
+ <pre>Redirecting to <a href="${url}">${url}</a></pre>
243
+ </body>
244
+ </html>
245
+ `.trim();
246
+ }
@@ -5,17 +5,12 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import type { ɵextractRoutesAndCreateRouteTree } from '@angular/ssr';
9
- import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
10
- export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
11
- assetFiles: Record</** Destination */ string, /** Source */ string>;
12
- }
13
- export type SerializableRouteTreeNode = ReturnType<Awaited<ReturnType<typeof ɵextractRoutesAndCreateRouteTree>>['routeTree']['toObject']>;
14
- export interface RoutersExtractorWorkerResult {
15
- serializedRouteTree: SerializableRouteTreeNode;
16
- errors: string[];
8
+ import { OutputMode } from '../../builders/application/schema';
9
+ import { RoutersExtractorWorkerResult } from './models';
10
+ export interface ExtractRoutesOptions {
11
+ outputMode?: OutputMode;
17
12
  }
18
13
  /** Renders an application based on a provided options. */
19
- declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
14
+ declare function extractRoutes({ outputMode, }: ExtractRoutesOptions): Promise<RoutersExtractorWorkerResult>;
20
15
  declare const _default: typeof extractRoutes;
21
16
  export default _default;
@@ -7,14 +7,13 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
+ const schema_1 = require("../../builders/application/schema");
10
11
  const fetch_patch_1 = require("./fetch-patch");
11
12
  const load_esm_from_memory_1 = require("./load-esm-from-memory");
12
13
  /** Renders an application based on a provided options. */
13
- async function extractRoutes() {
14
+ async function extractRoutes({ outputMode, }) {
14
15
  const { ɵextractRoutesAndCreateRouteTree: extractRoutesAndCreateRouteTree } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./main.server.mjs');
15
- const { routeTree, errors } = await extractRoutesAndCreateRouteTree(new URL('http://local-angular-prerender/'),
16
- /** manifest */ undefined,
17
- /** invokeGetPrerenderParams */ true);
16
+ const { routeTree, errors } = await extractRoutesAndCreateRouteTree(new URL('http://local-angular-prerender/'), undefined /** manifest */, true /** invokeGetPrerenderParams */, outputMode === schema_1.OutputMode.Server /** includePrerenderFallbackRoutes */);
18
17
  return {
19
18
  errors,
20
19
  serializedRouteTree: routeTree.toObject(),
@@ -1,22 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright Google LLC All Rights Reserved.
4
- *
5
- * Use of this source code is governed by an MIT-style license that can be
6
- * found in the LICENSE file at https://angular.dev/license
7
- */
8
- import type { Connect, Plugin } from 'vite';
9
- import { AngularMemoryOutputFiles } from './utils';
10
- export interface AngularMemoryPluginOptions {
11
- workspaceRoot: string;
12
- virtualProjectRoot: string;
13
- outputFiles: AngularMemoryOutputFiles;
14
- assets: Map<string, string>;
15
- ssr: boolean;
16
- external?: string[];
17
- extensionMiddleware?: Connect.NextHandleFunction[];
18
- indexHtmlTransformer?: (content: string) => Promise<string>;
19
- normalizePath: (path: string) => string;
20
- usedComponentStyles: Map<string, string[]>;
21
- }
22
- export declare function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): Plugin;