@angular/build 19.0.0-next.0 → 19.0.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +5 -5
  2. package/package.json +20 -16
  3. package/src/builders/application/build-action.js +9 -9
  4. package/src/builders/application/chunk-optimizer.js +1 -4
  5. package/src/builders/application/execute-build.js +19 -2
  6. package/src/builders/application/execute-post-bundle.d.ts +2 -2
  7. package/src/builders/application/execute-post-bundle.js +58 -20
  8. package/src/builders/application/i18n.d.ts +2 -2
  9. package/src/builders/application/i18n.js +6 -16
  10. package/src/builders/application/index.js +8 -5
  11. package/src/builders/application/options.d.ts +36 -1
  12. package/src/builders/application/options.js +60 -3
  13. package/src/builders/application/schema.d.ts +15 -0
  14. package/src/builders/application/schema.js +11 -1
  15. package/src/builders/application/schema.json +5 -0
  16. package/src/builders/application/setup-bundling.js +12 -9
  17. package/src/builders/dev-server/internal.d.ts +0 -1
  18. package/src/builders/dev-server/internal.js +1 -3
  19. package/src/builders/dev-server/vite-server.d.ts +8 -2
  20. package/src/builders/dev-server/vite-server.js +111 -56
  21. package/src/builders/extract-i18n/application-extraction.js +7 -3
  22. package/src/tools/angular/angular-host.d.ts +2 -1
  23. package/src/tools/angular/angular-host.js +20 -1
  24. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  25. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  26. package/src/tools/angular/compilation/aot-compilation.js +9 -1
  27. package/src/tools/angular/compilation/jit-compilation.js +2 -1
  28. package/src/tools/angular/compilation/parallel-compilation.d.ts +2 -1
  29. package/src/tools/angular/compilation/parallel-compilation.js +2 -10
  30. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  31. package/src/tools/angular/compilation/parallel-worker.js +2 -1
  32. package/src/tools/babel/plugins/add-code-coverage.d.ts +14 -0
  33. package/src/tools/babel/plugins/add-code-coverage.js +44 -0
  34. package/src/tools/babel/plugins/types.d.ts +20 -0
  35. package/src/tools/esbuild/angular/compiler-plugin.d.ts +2 -0
  36. package/src/tools/esbuild/angular/compiler-plugin.js +46 -4
  37. package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
  38. package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
  39. package/src/tools/esbuild/angular/file-reference-tracker.d.ts +1 -1
  40. package/src/tools/esbuild/application-code-bundle.d.ts +2 -6
  41. package/src/tools/esbuild/application-code-bundle.js +208 -92
  42. package/src/tools/esbuild/budget-stats.js +1 -1
  43. package/src/tools/esbuild/bundler-context.d.ts +4 -3
  44. package/src/tools/esbuild/bundler-context.js +21 -13
  45. package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
  46. package/src/tools/esbuild/bundler-execution-result.js +7 -3
  47. package/src/tools/esbuild/cache.d.ts +6 -1
  48. package/src/tools/esbuild/cache.js +7 -0
  49. package/src/tools/esbuild/compiler-plugin-options.js +3 -1
  50. package/src/tools/esbuild/i18n-inliner.js +4 -4
  51. package/src/tools/esbuild/javascript-transformer-worker.d.ts +1 -0
  52. package/src/tools/esbuild/javascript-transformer-worker.js +5 -1
  53. package/src/tools/esbuild/javascript-transformer.d.ts +2 -2
  54. package/src/tools/esbuild/javascript-transformer.js +7 -12
  55. package/src/tools/esbuild/utils.d.ts +9 -0
  56. package/src/tools/esbuild/utils.js +21 -3
  57. package/src/tools/sass/sass-service.js +11 -13
  58. package/src/tools/sass/worker.d.ts +13 -32
  59. package/src/tools/sass/worker.js +1 -0
  60. package/src/tools/vite/middlewares/assets-middleware.d.ts +1 -1
  61. package/src/tools/vite/middlewares/assets-middleware.js +43 -4
  62. package/src/tools/vite/middlewares/headers-middleware.d.ts +19 -0
  63. package/src/tools/vite/middlewares/headers-middleware.js +34 -0
  64. package/src/tools/vite/middlewares/html-fallback-middleware.d.ts +1 -1
  65. package/src/tools/vite/middlewares/html-fallback-middleware.js +23 -7
  66. package/src/tools/vite/middlewares/index-html-middleware.js +1 -2
  67. package/src/tools/vite/middlewares/index.d.ts +2 -1
  68. package/src/tools/vite/middlewares/index.js +5 -2
  69. package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -4
  70. package/src/tools/vite/middlewares/ssr-middleware.js +75 -43
  71. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
  72. package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -40
  73. package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
  74. package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
  75. package/src/tools/vite/plugins/index.d.ts +12 -0
  76. package/src/tools/vite/plugins/index.js +21 -0
  77. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
  78. package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
  79. package/src/{utils/server-rendering/main-bundle-exports.js → tools/vite/plugins/ssr-transform-plugin.d.ts} +2 -2
  80. package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
  81. package/src/tools/vite/utils.d.ts +0 -3
  82. package/src/tools/vite/utils.js +0 -12
  83. package/src/typings.d.ts +26 -0
  84. package/src/utils/environment-options.d.ts +2 -0
  85. package/src/utils/environment-options.js +5 -1
  86. package/src/utils/index-file/index-html-generator.js +5 -0
  87. package/src/utils/index-file/inline-critical-css.js +43 -33
  88. package/src/utils/index-file/ngcm-attribute.d.ts +15 -0
  89. package/src/utils/index-file/ngcm-attribute.js +37 -0
  90. package/src/utils/index-file/valid-self-closing-tags.js +28 -0
  91. package/src/utils/normalize-cache.js +1 -1
  92. package/src/utils/server-rendering/fetch-patch.d.ts +1 -1
  93. package/src/utils/server-rendering/fetch-patch.js +5 -6
  94. package/src/utils/server-rendering/launch-server.d.ts +14 -0
  95. package/src/utils/server-rendering/launch-server.js +63 -0
  96. package/src/utils/server-rendering/load-esm-from-memory.d.ts +18 -2
  97. package/src/utils/server-rendering/manifest.d.ts +50 -0
  98. package/src/utils/server-rendering/manifest.js +126 -0
  99. package/src/utils/server-rendering/models.d.ts +27 -0
  100. package/src/utils/server-rendering/models.js +22 -0
  101. package/src/utils/server-rendering/prerender.d.ts +26 -10
  102. package/src/utils/server-rendering/prerender.js +126 -67
  103. package/src/utils/server-rendering/render-worker.d.ts +9 -8
  104. package/src/utils/server-rendering/render-worker.js +19 -14
  105. package/src/utils/server-rendering/routes-extractor-worker.d.ts +6 -10
  106. package/src/utils/server-rendering/routes-extractor-worker.js +16 -33
  107. package/src/utils/server-rendering/utils.d.ts +11 -0
  108. package/src/utils/server-rendering/utils.js +17 -0
  109. package/src/utils/worker-pool.d.ts +12 -0
  110. package/src/utils/worker-pool.js +43 -0
  111. package/src/tools/vite/angular-memory-plugin.d.ts +0 -21
  112. package/src/utils/server-rendering/main-bundle-exports.d.ts +0 -27
  113. package/src/utils/server-rendering/render-page.d.ts +0 -26
  114. package/src/utils/server-rendering/render-page.js +0 -114
  115. /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
  116. /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
@@ -6,29 +6,31 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
9
  Object.defineProperty(exports, "__esModule", { value: true });
13
10
  exports.prerenderPages = prerenderPages;
14
11
  const promises_1 = require("node:fs/promises");
15
12
  const node_path_1 = require("node:path");
16
13
  const node_url_1 = require("node:url");
17
- const piscina_1 = __importDefault(require("piscina"));
14
+ const schema_1 = require("../../builders/application/schema");
18
15
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
19
- async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOptions = {}, outputFiles, assets, document, sourcemap = false, inlineCriticalCss = false, maxThreads = 1, verbose = false) {
16
+ const error_1 = require("../error");
17
+ const url_1 = require("../url");
18
+ const worker_pool_1 = require("../worker-pool");
19
+ const models_1 = require("./models");
20
+ async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerenderOptions, outputFiles, assets, outputMode, sourcemap = false, maxThreads = 1) {
20
21
  const outputFilesForWorker = {};
21
22
  const serverBundlesSourceMaps = new Map();
22
23
  const warnings = [];
23
24
  const errors = [];
24
25
  for (const { text, path, type } of outputFiles) {
25
- const fileExt = (0, node_path_1.extname)(path);
26
- if (type === bundler_context_1.BuildOutputFileType.Server && fileExt === '.map') {
26
+ if (type !== bundler_context_1.BuildOutputFileType.ServerApplication && type !== bundler_context_1.BuildOutputFileType.ServerRoot) {
27
+ continue;
28
+ }
29
+ // Contains the server runnable application code
30
+ if ((0, node_path_1.extname)(path) === '.map') {
27
31
  serverBundlesSourceMaps.set(path.slice(0, -4), text);
28
32
  }
29
- else if (type === bundler_context_1.BuildOutputFileType.Server || // Contains the server runnable application code
30
- (type === bundler_context_1.BuildOutputFileType.Browser && fileExt === '.css') // Global styles for critical CSS inlining.
31
- ) {
33
+ else {
32
34
  outputFilesForWorker[path] = text;
33
35
  }
34
36
  }
@@ -49,37 +51,58 @@ async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOpti
49
51
  assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
50
52
  }
51
53
  // Get routes to prerender
52
- const { routes: allRoutes, warnings: routesWarnings } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed, document, appShellOptions, prerenderOptions, sourcemap, verbose);
53
- if (routesWarnings?.length) {
54
- 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
+ }
55
85
  }
56
- if (allRoutes.size < 1) {
86
+ if (!serializableRouteTreeNodeForPrerender.length || errors.length > 0) {
57
87
  return {
58
88
  errors,
59
89
  warnings,
60
90
  output: {},
61
- prerenderedRoutes: allRoutes,
91
+ serializableRouteTreeNode,
62
92
  };
63
93
  }
64
94
  // Render routes
65
- const { warnings: renderingWarnings, errors: renderingErrors, output, } = await renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, inlineCriticalCss, document, appShellOptions);
95
+ const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, serializableRouteTreeNodeForPrerender, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, appShellOptions, outputMode);
66
96
  errors.push(...renderingErrors);
67
- warnings.push(...renderingWarnings);
68
97
  return {
69
98
  errors,
70
99
  warnings,
71
100
  output,
72
- prerenderedRoutes: allRoutes,
101
+ serializableRouteTreeNode,
73
102
  };
74
103
  }
75
- class RoutesSet extends Set {
76
- add(value) {
77
- return super.add(addLeadingSlash(value));
78
- }
79
- }
80
- async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, inlineCriticalCss, document, appShellOptions) {
104
+ async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions, outputMode) {
81
105
  const output = {};
82
- const warnings = [];
83
106
  const errors = [];
84
107
  const workerExecArgv = [
85
108
  '--import',
@@ -89,39 +112,43 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
89
112
  if (sourcemap) {
90
113
  workerExecArgv.push('--enable-source-maps');
91
114
  }
92
- const renderWorker = new piscina_1.default({
115
+ const renderWorker = new worker_pool_1.WorkerPool({
93
116
  filename: require.resolve('./render-worker'),
94
- maxThreads: Math.min(allRoutes.size, maxThreads),
117
+ maxThreads: Math.min(serializableRouteTreeNode.length, maxThreads),
95
118
  workerData: {
96
119
  workspaceRoot,
97
120
  outputFiles: outputFilesForWorker,
98
121
  assetFiles: assetFilesForWorker,
99
- inlineCriticalCss,
100
- document,
122
+ outputMode,
123
+ hasSsrEntry: !!outputFilesForWorker['server.mjs'],
101
124
  },
102
125
  execArgv: workerExecArgv,
103
- recordTiming: false,
104
126
  });
105
127
  try {
106
128
  const renderingPromises = [];
107
- const appShellRoute = appShellOptions.route && addLeadingSlash(appShellOptions.route);
108
- for (const route of allRoutes) {
109
- const isAppShellRoute = appShellRoute === route;
110
- const serverContext = isAppShellRoute ? 'app-shell' : 'ssg';
111
- const render = renderWorker.run({ route, serverContext });
112
- const renderResult = render.then(({ content, warnings, errors }) => {
113
- if (content !== undefined) {
114
- const outPath = isAppShellRoute
115
- ? 'index.html'
116
- : node_path_1.posix.join(removeLeadingSlash(route), 'index.html');
117
- output[outPath] = content;
118
- }
119
- if (warnings) {
120
- warnings.push(...warnings);
121
- }
122
- if (errors) {
123
- errors.push(...errors);
129
+ const appShellRoute = appShellOptions && addLeadingSlash(appShellOptions.route);
130
+ const baseHrefWithLeadingSlash = addLeadingSlash(baseHref);
131
+ for (const { route, redirectTo, renderMode } of serializableRouteTreeNode) {
132
+ // Remove base href from file output path.
133
+ const routeWithoutBaseHref = addLeadingSlash(route.slice(baseHrefWithLeadingSlash.length - 1));
134
+ const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
135
+ if (typeof redirectTo === 'string') {
136
+ output[outPath] = { content: generateRedirectStaticPage(redirectTo), appShellRoute: false };
137
+ continue;
138
+ }
139
+ const isAppShellRoute = renderMode === models_1.RouteRenderMode.AppShell ||
140
+ // Legacy handling
141
+ (renderMode === undefined && appShellRoute === routeWithoutBaseHref);
142
+ const render = renderWorker.run({ url: route, isAppShellRoute });
143
+ const renderResult = render
144
+ .then((content) => {
145
+ if (content !== null) {
146
+ output[outPath] = { content, appShellRoute: isAppShellRoute };
124
147
  }
148
+ })
149
+ .catch((err) => {
150
+ errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack ?? err.message ?? err.code ?? err}`);
151
+ void renderWorker.destroy();
125
152
  });
126
153
  renderingPromises.push(renderResult);
127
154
  }
@@ -132,25 +159,27 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
132
159
  }
133
160
  return {
134
161
  errors,
135
- warnings,
136
162
  output,
137
163
  };
138
164
  }
139
- async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose) {
140
- const { routesFile, discoverRoutes } = prerenderOptions;
141
- const routes = new RoutesSet();
142
- const { route: appShellRoute } = appShellOptions;
143
- if (appShellRoute !== undefined) {
144
- routes.add(appShellRoute);
165
+ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetFilesForWorker, appShellOptions, prerenderOptions, sourcemap, outputMode) {
166
+ const { routesFile, discoverRoutes } = prerenderOptions ?? {};
167
+ const routes = [];
168
+ if (appShellOptions) {
169
+ routes.push({
170
+ route: (0, url_1.urlJoin)(baseHref, appShellOptions.route),
171
+ });
145
172
  }
146
173
  if (routesFile) {
147
174
  const routesFromFile = (await (0, promises_1.readFile)(routesFile, 'utf8')).split(/\r?\n/);
148
175
  for (const route of routesFromFile) {
149
- routes.add(route.trim());
176
+ routes.push({
177
+ route: (0, url_1.urlJoin)(baseHref, route.trim()),
178
+ });
150
179
  }
151
180
  }
152
181
  if (!discoverRoutes) {
153
- return { routes };
182
+ return { errors: [], serializedRouteTree: routes };
154
183
  }
155
184
  const workerExecArgv = [
156
185
  '--import',
@@ -160,28 +189,34 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWo
160
189
  if (sourcemap) {
161
190
  workerExecArgv.push('--enable-source-maps');
162
191
  }
163
- const renderWorker = new piscina_1.default({
192
+ const renderWorker = new worker_pool_1.WorkerPool({
164
193
  filename: require.resolve('./routes-extractor-worker'),
165
194
  maxThreads: 1,
166
195
  workerData: {
167
196
  workspaceRoot,
168
197
  outputFiles: outputFilesForWorker,
169
198
  assetFiles: assetFilesForWorker,
170
- document,
171
- verbose,
199
+ outputMode,
200
+ hasSsrEntry: !!outputFilesForWorker['server.mjs'],
172
201
  },
173
202
  execArgv: workerExecArgv,
174
- recordTiming: false,
175
203
  });
176
- const { routes: extractedRoutes, warnings } = await renderWorker
177
- .run({})
178
- .finally(() => {
204
+ try {
205
+ const { serializedRouteTree, errors } = await renderWorker.run({});
206
+ return { errors, serializedRouteTree: [...routes, ...serializedRouteTree] };
207
+ }
208
+ catch (err) {
209
+ (0, error_1.assertIsError)(err);
210
+ return {
211
+ errors: [
212
+ `An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err.code ?? err}`,
213
+ ],
214
+ serializedRouteTree: [],
215
+ };
216
+ }
217
+ finally {
179
218
  void renderWorker.destroy();
180
- });
181
- for (const route of extractedRoutes) {
182
- routes.add(route);
183
219
  }
184
- return { routes, warnings };
185
220
  }
186
221
  function addLeadingSlash(value) {
187
222
  return value.charAt(0) === '/' ? value : '/' + value;
@@ -189,3 +224,27 @@ function addLeadingSlash(value) {
189
224
  function removeLeadingSlash(value) {
190
225
  return value.charAt(0) === '/' ? value.slice(1) : value;
191
226
  }
227
+ /**
228
+ * Generates a static HTML page with a meta refresh tag to redirect the user to a specified URL.
229
+ *
230
+ * This function creates a simple HTML page that performs a redirect using a meta tag.
231
+ * It includes a fallback link in case the meta-refresh doesn't work.
232
+ *
233
+ * @param url - The URL to which the page should redirect.
234
+ * @returns The HTML content of the static redirect page.
235
+ */
236
+ function generateRedirectStaticPage(url) {
237
+ return `
238
+ <!DOCTYPE html>
239
+ <html>
240
+ <head>
241
+ <meta charset="utf-8">
242
+ <title>Redirecting</title>
243
+ <meta http-equiv="refresh" content="0; url=${url}">
244
+ </head>
245
+ <body>
246
+ <pre>Redirecting to <a href="${url}">${url}</a></pre>
247
+ </body>
248
+ </html>
249
+ `.trim();
250
+ }
@@ -5,18 +5,19 @@
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 { OutputMode } from '../../builders/application/schema';
8
9
  import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
9
- import { RenderResult, ServerContext } from './render-page';
10
10
  export interface RenderWorkerData extends ESMInMemoryFileLoaderWorkerData {
11
- document: string;
12
- inlineCriticalCss?: boolean;
13
11
  assetFiles: Record</** Destination */ string, /** Source */ string>;
12
+ outputMode: OutputMode | undefined;
13
+ hasSsrEntry: boolean;
14
14
  }
15
15
  export interface RenderOptions {
16
- route: string;
17
- serverContext: ServerContext;
16
+ url: string;
18
17
  }
19
- /** Renders an application based on a provided options. */
20
- declare function render(options: RenderOptions): Promise<RenderResult>;
21
- declare const _default: typeof render;
18
+ /**
19
+ * Renders each route in routes and writes them to <outputPath>/<route>/index.html.
20
+ */
21
+ declare function renderPage({ url }: RenderOptions): Promise<string | null>;
22
+ declare const _default: Promise<typeof renderPage>;
22
23
  export default _default;
@@ -7,24 +7,29 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- const node_worker_threads_1 = require("node:worker_threads");
10
+ const worker_threads_1 = require("worker_threads");
11
11
  const fetch_patch_1 = require("./fetch-patch");
12
- const render_page_1 = require("./render-page");
12
+ const launch_server_1 = require("./launch-server");
13
+ const load_esm_from_memory_1 = require("./load-esm-from-memory");
13
14
  /**
14
15
  * This is passed as workerData when setting up the worker via the `piscina` package.
15
16
  */
16
- const { outputFiles, document, inlineCriticalCss } = node_worker_threads_1.workerData;
17
- /** Renders an application based on a provided options. */
18
- function render(options) {
19
- return (0, render_page_1.renderPage)({
20
- ...options,
21
- outputFiles,
22
- document,
23
- inlineCriticalCss,
24
- });
17
+ const { outputMode, hasSsrEntry } = worker_threads_1.workerData;
18
+ let serverURL = launch_server_1.DEFAULT_URL;
19
+ /**
20
+ * Renders each route in routes and writes them to <outputPath>/<route>/index.html.
21
+ */
22
+ async function renderPage({ url }) {
23
+ const { ɵgetOrCreateAngularServerApp: getOrCreateAngularServerApp } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./main.server.mjs');
24
+ const angularServerApp = getOrCreateAngularServerApp();
25
+ const response = await angularServerApp.renderStatic(new URL(url, serverURL), AbortSignal.timeout(30_000));
26
+ return response ? response.text() : null;
25
27
  }
26
- function initialize() {
27
- (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
28
- return render;
28
+ async function initialize() {
29
+ if (outputMode !== undefined && hasSsrEntry) {
30
+ serverURL = await (0, launch_server_1.launchServer)();
31
+ }
32
+ (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)(serverURL);
33
+ return renderPage;
29
34
  }
30
35
  exports.default = initialize();
@@ -5,17 +5,13 @@
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 { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
9
- export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
10
- document: string;
11
- verbose: boolean;
12
- assetFiles: Record</** Destination */ string, /** Source */ string>;
13
- }
14
- export interface RoutersExtractorWorkerResult {
15
- routes: string[];
16
- warnings?: string[];
8
+ import { OutputMode } from '../../builders/application/schema';
9
+ import { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
10
+ import { RoutersExtractorWorkerResult } from './models';
11
+ export interface ExtractRoutesWorkerData extends ESMInMemoryFileLoaderWorkerData {
12
+ outputMode: OutputMode | undefined;
17
13
  }
18
14
  /** Renders an application based on a provided options. */
19
15
  declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
20
- declare const _default: typeof extractRoutes;
16
+ declare const _default: Promise<typeof extractRoutes>;
21
17
  export default _default;
@@ -7,47 +7,30 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- const node_worker_threads_1 = require("node:worker_threads");
10
+ const worker_threads_1 = require("worker_threads");
11
+ const schema_1 = require("../../builders/application/schema");
11
12
  const fetch_patch_1 = require("./fetch-patch");
13
+ const launch_server_1 = require("./launch-server");
12
14
  const load_esm_from_memory_1 = require("./load-esm-from-memory");
13
15
  /**
14
16
  * This is passed as workerData when setting up the worker via the `piscina` package.
15
17
  */
16
- const { document, verbose } = node_worker_threads_1.workerData;
18
+ const { outputMode, hasSsrEntry } = worker_threads_1.workerData;
19
+ let serverURL = launch_server_1.DEFAULT_URL;
17
20
  /** Renders an application based on a provided options. */
18
21
  async function extractRoutes() {
19
- const { ɵgetRoutesFromAngularRouterConfig: getRoutesFromAngularRouterConfig } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./render-utils.server.mjs');
20
- const { default: bootstrapAppFnOrModule } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./main.server.mjs');
21
- const skippedRedirects = [];
22
- const skippedOthers = [];
23
- const routes = [];
24
- const { routes: extractRoutes } = await getRoutesFromAngularRouterConfig(bootstrapAppFnOrModule, document, new URL('http://localhost'));
25
- for (const { route, redirectTo } of extractRoutes) {
26
- if (redirectTo !== undefined) {
27
- skippedRedirects.push(route);
28
- }
29
- else if (/[:*]/.test(route)) {
30
- skippedOthers.push(route);
31
- }
32
- else {
33
- routes.push(route);
34
- }
35
- }
36
- if (!verbose) {
37
- return { routes };
38
- }
39
- let warnings;
40
- if (skippedOthers.length) {
41
- (warnings ??= []).push('The following routes were skipped from prerendering because they contain routes with dynamic parameters:\n' +
42
- skippedOthers.join('\n'));
43
- }
44
- if (skippedRedirects.length) {
45
- (warnings ??= []).push('The following routes were skipped from prerendering because they contain redirects:\n', skippedRedirects.join('\n'));
46
- }
47
- return { routes, warnings };
22
+ const { ɵextractRoutesAndCreateRouteTree: extractRoutesAndCreateRouteTree } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./main.server.mjs');
23
+ const { routeTree, errors } = await extractRoutesAndCreateRouteTree(serverURL, undefined /** manifest */, true /** invokeGetPrerenderParams */, outputMode === schema_1.OutputMode.Server /** includePrerenderFallbackRoutes */);
24
+ return {
25
+ errors,
26
+ serializedRouteTree: routeTree.toObject(),
27
+ };
48
28
  }
49
- function initialize() {
50
- (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
29
+ async function initialize() {
30
+ if (outputMode !== undefined && hasSsrEntry) {
31
+ serverURL = await (0, launch_server_1.launchServer)();
32
+ }
33
+ (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)(serverURL);
51
34
  return extractRoutes;
52
35
  }
53
36
  exports.default = initialize();
@@ -0,0 +1,11 @@
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 { createRequestHandler } from '@angular/ssr';
9
+ import type { createNodeRequestHandler } from '@angular/ssr/node';
10
+ export declare function isSsrNodeRequestHandler(value: unknown): value is ReturnType<typeof createNodeRequestHandler>;
11
+ export declare function isSsrRequestHandler(value: unknown): value is ReturnType<typeof createRequestHandler>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.isSsrNodeRequestHandler = isSsrNodeRequestHandler;
11
+ exports.isSsrRequestHandler = isSsrRequestHandler;
12
+ function isSsrNodeRequestHandler(value) {
13
+ return typeof value === 'function' && '__ng_node_request_handler__' in value;
14
+ }
15
+ function isSsrRequestHandler(value) {
16
+ return typeof value === 'function' && '__ng_request_handler__' in value;
17
+ }
@@ -0,0 +1,12 @@
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 { Piscina } from 'piscina';
9
+ export type WorkerPoolOptions = ConstructorParameters<typeof Piscina>[0];
10
+ export declare class WorkerPool extends Piscina {
11
+ constructor(options: WorkerPoolOptions);
12
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.WorkerPool = void 0;
11
+ const node_module_1 = require("node:module");
12
+ const piscina_1 = require("piscina");
13
+ class WorkerPool extends piscina_1.Piscina {
14
+ constructor(options) {
15
+ const piscinaOptions = {
16
+ minThreads: 1,
17
+ idleTimeout: 1000,
18
+ // Web containers do not support transferable objects with receiveOnMessagePort which
19
+ // is used when the Atomics based wait loop is enable.
20
+ useAtomics: !process.versions.webcontainer,
21
+ recordTiming: false,
22
+ ...options,
23
+ };
24
+ // Enable compile code caching if enabled for the main process (only exists on Node.js v22.8+).
25
+ // Skip if running inside Bazel via a RUNFILES environment variable check. The cache does not work
26
+ // well with Bazel's hermeticity requirements.
27
+ const compileCacheDirectory = process.env['RUNFILES'] ? undefined : (0, node_module_1.getCompileCacheDir)?.();
28
+ if (compileCacheDirectory) {
29
+ if (typeof piscinaOptions.env === 'object') {
30
+ piscinaOptions.env['NODE_COMPILE_CACHE'] = compileCacheDirectory;
31
+ }
32
+ else {
33
+ // Default behavior of `env` option is to copy current process values
34
+ piscinaOptions.env = {
35
+ ...process.env,
36
+ 'NODE_COMPILE_CACHE': compileCacheDirectory,
37
+ };
38
+ }
39
+ }
40
+ super(piscinaOptions);
41
+ }
42
+ }
43
+ exports.WorkerPool = WorkerPool;
@@ -1,21 +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
- }
21
- export declare function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): Plugin;
@@ -1,27 +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 { ApplicationRef, Type, ɵConsole } from '@angular/core';
9
- import type { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server';
10
- import type { ɵgetRoutesFromAngularRouterConfig } from '@angular/ssr';
11
- export interface MainServerBundleExports {
12
- /** Standalone application bootstrapping function. */
13
- default: (() => Promise<ApplicationRef>) | Type<unknown>;
14
- }
15
- export interface RenderUtilsServerBundleExports {
16
- /** An internal token that allows providing extra information about the server context. */
17
- ɵSERVER_CONTEXT: typeof ɵSERVER_CONTEXT;
18
- /** Render an NgModule application. */
19
- renderModule: typeof renderModule;
20
- /** Method to render a standalone application. */
21
- renderApplication: typeof renderApplication;
22
- /** Method to extract routes from the router config. */
23
- ɵgetRoutesFromAngularRouterConfig: typeof ɵgetRoutesFromAngularRouterConfig;
24
- ɵresetCompiledComponents?: () => void;
25
- /** Angular Console token/class. */
26
- ɵConsole: typeof ɵConsole;
27
- }
@@ -1,26 +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 { MainServerBundleExports, RenderUtilsServerBundleExports } from './main-bundle-exports';
9
- export interface RenderOptions {
10
- route: string;
11
- serverContext: ServerContext;
12
- outputFiles: Record<string, string>;
13
- document: string;
14
- inlineCriticalCss?: boolean;
15
- loadBundle?: ((path: './main.server.mjs') => Promise<MainServerBundleExports>) & ((path: './render-utils.server.mjs') => Promise<RenderUtilsServerBundleExports>);
16
- }
17
- export interface RenderResult {
18
- errors?: string[];
19
- warnings?: string[];
20
- content?: string;
21
- }
22
- export type ServerContext = 'app-shell' | 'ssg' | 'ssr';
23
- /**
24
- * Renders each route in routes and writes them to <outputPath>/<route>/index.html.
25
- */
26
- export declare function renderPage({ route, serverContext, document, inlineCriticalCss, outputFiles, loadBundle, }: RenderOptions): Promise<RenderResult>;