@angular/build 19.0.0-next.6 → 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.
- package/package.json +11 -11
- package/src/builders/application/execute-build.js +19 -2
- package/src/builders/application/execute-post-bundle.d.ts +2 -2
- package/src/builders/application/execute-post-bundle.js +30 -11
- package/src/builders/application/i18n.d.ts +2 -2
- package/src/builders/application/i18n.js +4 -5
- package/src/builders/application/index.js +8 -5
- package/src/builders/application/options.d.ts +18 -1
- package/src/builders/application/options.js +30 -2
- package/src/builders/application/schema.d.ts +15 -0
- package/src/builders/application/schema.js +11 -1
- package/src/builders/application/schema.json +5 -0
- package/src/builders/application/setup-bundling.js +6 -3
- package/src/builders/dev-server/vite-server.d.ts +2 -1
- package/src/builders/dev-server/vite-server.js +64 -47
- package/src/builders/extract-i18n/application-extraction.js +3 -3
- package/src/tools/angular/angular-host.d.ts +2 -1
- package/src/tools/angular/angular-host.js +20 -1
- package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/aot-compilation.js +9 -1
- package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/parallel-compilation.js +2 -10
- package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
- package/src/tools/angular/compilation/parallel-worker.js +2 -1
- package/src/tools/esbuild/angular/compiler-plugin.d.ts +1 -0
- package/src/tools/esbuild/angular/compiler-plugin.js +42 -3
- package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
- package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
- package/src/tools/esbuild/application-code-bundle.d.ts +1 -0
- package/src/tools/esbuild/application-code-bundle.js +109 -2
- package/src/tools/esbuild/budget-stats.js +1 -1
- package/src/tools/esbuild/bundler-context.d.ts +4 -3
- package/src/tools/esbuild/bundler-context.js +8 -4
- package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
- package/src/tools/esbuild/bundler-execution-result.js +7 -3
- package/src/tools/esbuild/cache.d.ts +5 -0
- package/src/tools/esbuild/cache.js +7 -0
- package/src/tools/esbuild/compiler-plugin-options.js +2 -1
- package/src/tools/esbuild/i18n-inliner.js +4 -4
- package/src/tools/esbuild/javascript-transformer.js +2 -9
- package/src/tools/esbuild/utils.js +7 -3
- package/src/tools/sass/sass-service.js +2 -9
- package/src/tools/vite/middlewares/assets-middleware.js +8 -8
- package/src/tools/vite/middlewares/headers-middleware.d.ts +19 -0
- package/src/tools/vite/middlewares/headers-middleware.js +34 -0
- package/src/tools/vite/middlewares/html-fallback-middleware.d.ts +1 -1
- package/src/tools/vite/middlewares/html-fallback-middleware.js +23 -7
- package/src/tools/vite/middlewares/index-html-middleware.js +1 -2
- package/src/tools/vite/middlewares/index.d.ts +2 -1
- package/src/tools/vite/middlewares/index.js +5 -2
- package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -1
- package/src/tools/vite/middlewares/ssr-middleware.js +61 -17
- package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
- package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -40
- package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
- package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
- package/src/tools/vite/plugins/index.d.ts +12 -0
- package/src/tools/vite/plugins/index.js +21 -0
- package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
- package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
- package/src/tools/vite/plugins/ssr-transform-plugin.d.ts +9 -0
- package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
- package/src/tools/vite/utils.d.ts +0 -3
- package/src/tools/vite/utils.js +0 -12
- package/src/typings.d.ts +7 -0
- package/src/utils/environment-options.d.ts +2 -0
- package/src/utils/environment-options.js +5 -1
- package/src/utils/index-file/valid-self-closing-tags.js +1 -0
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/manifest.d.ts +8 -2
- package/src/utils/server-rendering/manifest.js +61 -12
- package/src/utils/server-rendering/models.d.ts +27 -0
- package/src/utils/server-rendering/models.js +22 -0
- package/src/utils/server-rendering/prerender.d.ts +6 -10
- package/src/utils/server-rendering/prerender.js +103 -71
- package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -6
- package/src/utils/server-rendering/routes-extractor-worker.js +7 -5
- package/src/utils/worker-pool.d.ts +12 -0
- package/src/utils/worker-pool.js +43 -0
- package/src/tools/vite/angular-memory-plugin.d.ts +0 -22
- /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
- /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { NormalizedApplicationBuildOptions } from '../../builders/application/options';
|
|
9
9
|
import type { BuildOutputFile } from '../../tools/esbuild/bundler-context';
|
|
10
|
+
import { PrerenderedRoutesRecord } from '../../tools/esbuild/bundler-execution-result';
|
|
10
11
|
export declare const SERVER_APP_MANIFEST_FILENAME = "angular-app-manifest.mjs";
|
|
12
|
+
export declare const SERVER_APP_ENGINE_MANIFEST_FILENAME = "angular-app-engine-manifest.mjs";
|
|
11
13
|
/**
|
|
12
14
|
* Generates the server manifest for the App Engine environment.
|
|
13
15
|
*
|
|
@@ -19,9 +21,10 @@ export declare const SERVER_APP_MANIFEST_FILENAME = "angular-app-manifest.mjs";
|
|
|
19
21
|
* includes settings for inlining locales and determining the output structure.
|
|
20
22
|
* @param baseHref - The base HREF for the application. This is used to set the base URL
|
|
21
23
|
* for all relative URLs in the application.
|
|
24
|
+
* @param perenderedRoutes - A record mapping static paths to their associated data.
|
|
22
25
|
* @returns A string representing the content of the SSR server manifest for App Engine.
|
|
23
26
|
*/
|
|
24
|
-
export declare function generateAngularServerAppEngineManifest(i18nOptions: NormalizedApplicationBuildOptions['i18nOptions'], baseHref: string | undefined): string;
|
|
27
|
+
export declare function generateAngularServerAppEngineManifest(i18nOptions: NormalizedApplicationBuildOptions['i18nOptions'], baseHref: string | undefined, perenderedRoutes?: PrerenderedRoutesRecord | undefined): string;
|
|
25
28
|
/**
|
|
26
29
|
* Generates the server manifest for the standard Node.js environment.
|
|
27
30
|
*
|
|
@@ -38,7 +41,10 @@ export declare function generateAngularServerAppEngineManifest(i18nOptions: Norm
|
|
|
38
41
|
* in the server-side rendered pages.
|
|
39
42
|
* @param routes - An optional array of route definitions for the application, used for
|
|
40
43
|
* server-side rendering and routing.
|
|
44
|
+
* @param locale - An optional string representing the locale or language code to be used for
|
|
45
|
+
* the application, helping with localization and rendering content specific to the locale.
|
|
46
|
+
*
|
|
41
47
|
* @returns A string representing the content of the SSR server manifest for the Node.js
|
|
42
48
|
* environment.
|
|
43
49
|
*/
|
|
44
|
-
export declare function generateAngularServerAppManifest(additionalHtmlOutputFiles: Map<string, BuildOutputFile>, outputFiles: BuildOutputFile[], inlineCriticalCss: boolean, routes: readonly unknown[] | undefined): string;
|
|
50
|
+
export declare function generateAngularServerAppManifest(additionalHtmlOutputFiles: Map<string, BuildOutputFile>, outputFiles: BuildOutputFile[], inlineCriticalCss: boolean, routes: readonly unknown[] | undefined, locale: string | undefined): string;
|
|
@@ -7,12 +7,40 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.dev/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.SERVER_APP_MANIFEST_FILENAME = void 0;
|
|
10
|
+
exports.SERVER_APP_ENGINE_MANIFEST_FILENAME = exports.SERVER_APP_MANIFEST_FILENAME = void 0;
|
|
11
11
|
exports.generateAngularServerAppEngineManifest = generateAngularServerAppEngineManifest;
|
|
12
12
|
exports.generateAngularServerAppManifest = generateAngularServerAppManifest;
|
|
13
13
|
const options_1 = require("../../builders/application/options");
|
|
14
14
|
exports.SERVER_APP_MANIFEST_FILENAME = 'angular-app-manifest.mjs';
|
|
15
|
+
exports.SERVER_APP_ENGINE_MANIFEST_FILENAME = 'angular-app-engine-manifest.mjs';
|
|
15
16
|
const MAIN_SERVER_OUTPUT_FILENAME = 'main.server.mjs';
|
|
17
|
+
/**
|
|
18
|
+
* A mapping of unsafe characters to their escaped Unicode equivalents.
|
|
19
|
+
*/
|
|
20
|
+
const UNSAFE_CHAR_MAP = {
|
|
21
|
+
'<': '\\u003C',
|
|
22
|
+
'>': '\\u003E',
|
|
23
|
+
'/': '\\u002F',
|
|
24
|
+
'\\': '\\\\',
|
|
25
|
+
'\b': '\\b',
|
|
26
|
+
'\f': '\\f',
|
|
27
|
+
'\n': '\\n',
|
|
28
|
+
'\r': '\\r',
|
|
29
|
+
'\t': '\\t',
|
|
30
|
+
'\0': '\\0',
|
|
31
|
+
'\u2028': '\\u2028',
|
|
32
|
+
'\u2029': '\\u2029',
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Escapes unsafe characters in a given string by replacing them with
|
|
36
|
+
* their Unicode escape sequences.
|
|
37
|
+
*
|
|
38
|
+
* @param str - The string to be escaped.
|
|
39
|
+
* @returns The escaped string where unsafe characters are replaced.
|
|
40
|
+
*/
|
|
41
|
+
function escapeUnsafeChars(str) {
|
|
42
|
+
return str.replace(/[<>\b\f\n\r\t\0\u2028\u2029]/g, (c) => UNSAFE_CHAR_MAP[c]);
|
|
43
|
+
}
|
|
16
44
|
/**
|
|
17
45
|
* Generates the server manifest for the App Engine environment.
|
|
18
46
|
*
|
|
@@ -24,26 +52,43 @@ const MAIN_SERVER_OUTPUT_FILENAME = 'main.server.mjs';
|
|
|
24
52
|
* includes settings for inlining locales and determining the output structure.
|
|
25
53
|
* @param baseHref - The base HREF for the application. This is used to set the base URL
|
|
26
54
|
* for all relative URLs in the application.
|
|
55
|
+
* @param perenderedRoutes - A record mapping static paths to their associated data.
|
|
27
56
|
* @returns A string representing the content of the SSR server manifest for App Engine.
|
|
28
57
|
*/
|
|
29
|
-
function generateAngularServerAppEngineManifest(i18nOptions, baseHref) {
|
|
58
|
+
function generateAngularServerAppEngineManifest(i18nOptions, baseHref, perenderedRoutes = {}) {
|
|
30
59
|
const entryPointsContent = [];
|
|
31
60
|
if (i18nOptions.shouldInline) {
|
|
32
61
|
for (const locale of i18nOptions.inlineLocales) {
|
|
33
62
|
const importPath = './' + (i18nOptions.flatOutput ? '' : locale + '/') + MAIN_SERVER_OUTPUT_FILENAME;
|
|
34
|
-
|
|
35
|
-
|
|
63
|
+
let localeWithBaseHref = (0, options_1.getLocaleBaseHref)('', i18nOptions, locale) || '/';
|
|
64
|
+
// Remove leading and trailing slashes.
|
|
65
|
+
const start = localeWithBaseHref[0] === '/' ? 1 : 0;
|
|
66
|
+
const end = localeWithBaseHref[localeWithBaseHref.length - 1] === '/' ? -1 : undefined;
|
|
67
|
+
localeWithBaseHref = localeWithBaseHref.slice(start, end);
|
|
68
|
+
entryPointsContent.push(`['${localeWithBaseHref}', () => import('${importPath}')]`);
|
|
36
69
|
}
|
|
37
70
|
}
|
|
38
71
|
else {
|
|
39
|
-
entryPointsContent.push(`['
|
|
72
|
+
entryPointsContent.push(`['', () => import('./${MAIN_SERVER_OUTPUT_FILENAME}')]`);
|
|
73
|
+
}
|
|
74
|
+
const staticHeaders = [];
|
|
75
|
+
for (const [path, { headers }] of Object.entries(perenderedRoutes)) {
|
|
76
|
+
if (!headers) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const headersValues = [];
|
|
80
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
81
|
+
headersValues.push(`['${name}', '${encodeURIComponent(value)}']`);
|
|
82
|
+
}
|
|
83
|
+
staticHeaders.push(`['${path}', [${headersValues.join(', ')}]]`);
|
|
40
84
|
}
|
|
41
85
|
const manifestContent = `
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
86
|
+
export default {
|
|
87
|
+
basePath: '${baseHref ?? '/'}',
|
|
88
|
+
entryPoints: new Map([${entryPointsContent.join(', \n')}]),
|
|
89
|
+
staticPathsHeaders: new Map([${staticHeaders.join(', \n')}]),
|
|
90
|
+
};
|
|
91
|
+
`;
|
|
47
92
|
return manifestContent;
|
|
48
93
|
}
|
|
49
94
|
/**
|
|
@@ -62,16 +107,19 @@ function generateAngularServerAppEngineManifest(i18nOptions, baseHref) {
|
|
|
62
107
|
* in the server-side rendered pages.
|
|
63
108
|
* @param routes - An optional array of route definitions for the application, used for
|
|
64
109
|
* server-side rendering and routing.
|
|
110
|
+
* @param locale - An optional string representing the locale or language code to be used for
|
|
111
|
+
* the application, helping with localization and rendering content specific to the locale.
|
|
112
|
+
*
|
|
65
113
|
* @returns A string representing the content of the SSR server manifest for the Node.js
|
|
66
114
|
* environment.
|
|
67
115
|
*/
|
|
68
|
-
function generateAngularServerAppManifest(additionalHtmlOutputFiles, outputFiles, inlineCriticalCss, routes) {
|
|
116
|
+
function generateAngularServerAppManifest(additionalHtmlOutputFiles, outputFiles, inlineCriticalCss, routes, locale) {
|
|
69
117
|
const serverAssetsContent = [];
|
|
70
118
|
for (const file of [...additionalHtmlOutputFiles.values(), ...outputFiles]) {
|
|
71
119
|
if (file.path === options_1.INDEX_HTML_SERVER ||
|
|
72
120
|
file.path === options_1.INDEX_HTML_CSR ||
|
|
73
121
|
(inlineCriticalCss && file.path.endsWith('.css'))) {
|
|
74
|
-
serverAssetsContent.push(`['${file.path}', async () => ${JSON.stringify(file.text)}]`);
|
|
122
|
+
serverAssetsContent.push(`['${file.path}', async () => ${escapeUnsafeChars(JSON.stringify(file.text))}]`);
|
|
75
123
|
}
|
|
76
124
|
}
|
|
77
125
|
const manifestContent = `
|
|
@@ -80,6 +128,7 @@ export default {
|
|
|
80
128
|
inlineCriticalCss: ${inlineCriticalCss},
|
|
81
129
|
routes: ${JSON.stringify(routes, undefined, 2)},
|
|
82
130
|
assets: new Map([${serverAssetsContent.join(', \n')}]),
|
|
131
|
+
locale: ${locale !== undefined ? `'${locale}'` : undefined},
|
|
83
132
|
};
|
|
84
133
|
`;
|
|
85
134
|
return manifestContent;
|
|
@@ -0,0 +1,27 @@
|
|
|
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 { RenderMode, ɵextractRoutesAndCreateRouteTree } from '@angular/ssr';
|
|
9
|
+
import { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
|
|
10
|
+
type Writeable<T extends readonly unknown[]> = T extends readonly (infer U)[] ? U[] : never;
|
|
11
|
+
export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
|
|
12
|
+
assetFiles: Record</** Destination */ string, /** Source */ string>;
|
|
13
|
+
}
|
|
14
|
+
export type SerializableRouteTreeNode = ReturnType<Awaited<ReturnType<typeof ɵextractRoutesAndCreateRouteTree>>['routeTree']['toObject']>;
|
|
15
|
+
export type WritableSerializableRouteTreeNode = Writeable<SerializableRouteTreeNode>;
|
|
16
|
+
export interface RoutersExtractorWorkerResult {
|
|
17
|
+
serializedRouteTree: SerializableRouteTreeNode;
|
|
18
|
+
errors: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Local copy of `RenderMode` exported from `@angular/ssr`.
|
|
22
|
+
* This constant is needed to handle interop between CommonJS (CJS) and ES Modules (ESM) formats.
|
|
23
|
+
*
|
|
24
|
+
* It maps `RenderMode` enum values to their corresponding numeric identifiers.
|
|
25
|
+
*/
|
|
26
|
+
export declare const RouteRenderMode: Record<keyof typeof RenderMode, RenderMode>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
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.RouteRenderMode = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Local copy of `RenderMode` exported from `@angular/ssr`.
|
|
13
|
+
* This constant is needed to handle interop between CommonJS (CJS) and ES Modules (ESM) formats.
|
|
14
|
+
*
|
|
15
|
+
* It maps `RenderMode` enum values to their corresponding numeric identifiers.
|
|
16
|
+
*/
|
|
17
|
+
exports.RouteRenderMode = {
|
|
18
|
+
AppShell: 0,
|
|
19
|
+
Server: 1,
|
|
20
|
+
Client: 2,
|
|
21
|
+
Prerender: 3,
|
|
22
|
+
};
|
|
@@ -5,16 +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 { NormalizedApplicationBuildOptions } from '../../builders/application/options';
|
|
9
|
+
import { OutputMode } from '../../builders/application/schema';
|
|
8
10
|
import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
|
|
9
11
|
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
discoverRoutes?: boolean;
|
|
14
|
-
}
|
|
15
|
-
interface AppShellOptions {
|
|
16
|
-
route?: string;
|
|
17
|
-
}
|
|
12
|
+
import { SerializableRouteTreeNode } from './models';
|
|
13
|
+
type PrerenderOptions = NormalizedApplicationBuildOptions['prerenderOptions'];
|
|
14
|
+
type AppShellOptions = NormalizedApplicationBuildOptions['appShellOptions'];
|
|
18
15
|
/**
|
|
19
16
|
* Represents the output of a prerendering process.
|
|
20
17
|
*
|
|
@@ -33,11 +30,10 @@ type PrerenderOutput = Record<string, {
|
|
|
33
30
|
content: string;
|
|
34
31
|
appShellRoute: boolean;
|
|
35
32
|
}>;
|
|
36
|
-
export declare function prerenderPages(workspaceRoot: string, baseHref: string, appShellOptions: AppShellOptions | undefined, prerenderOptions: PrerenderOptions | undefined, outputFiles: Readonly<BuildOutputFile[]>, assets: Readonly<BuildOutputAsset[]>, sourcemap?: boolean, maxThreads?: number
|
|
33
|
+
export declare function prerenderPages(workspaceRoot: string, baseHref: string, appShellOptions: AppShellOptions | undefined, prerenderOptions: PrerenderOptions | undefined, outputFiles: Readonly<BuildOutputFile[]>, assets: Readonly<BuildOutputAsset[]>, outputMode: OutputMode | undefined, sourcemap?: boolean, maxThreads?: number): Promise<{
|
|
37
34
|
output: PrerenderOutput;
|
|
38
35
|
warnings: string[];
|
|
39
36
|
errors: string[];
|
|
40
|
-
prerenderedRoutes: Set<string>;
|
|
41
37
|
serializableRouteTreeNode: SerializableRouteTreeNode;
|
|
42
38
|
}>;
|
|
43
39
|
export {};
|
|
@@ -6,24 +6,24 @@
|
|
|
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
|
|
14
|
+
const schema_1 = require("../../builders/application/schema");
|
|
18
15
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
16
|
+
const error_1 = require("../error");
|
|
19
17
|
const url_1 = require("../url");
|
|
20
|
-
|
|
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) {
|
|
21
21
|
const outputFilesForWorker = {};
|
|
22
22
|
const serverBundlesSourceMaps = new Map();
|
|
23
23
|
const warnings = [];
|
|
24
24
|
const errors = [];
|
|
25
25
|
for (const { text, path, type } of outputFiles) {
|
|
26
|
-
if (type !== bundler_context_1.BuildOutputFileType.
|
|
26
|
+
if (type !== bundler_context_1.BuildOutputFileType.ServerApplication && type !== bundler_context_1.BuildOutputFileType.ServerRoot) {
|
|
27
27
|
continue;
|
|
28
28
|
}
|
|
29
29
|
// Contains the server runnable application code
|
|
@@ -51,39 +51,57 @@ async function prerenderPages(workspaceRoot, baseHref, appShellOptions = {}, pre
|
|
|
51
51
|
assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
|
|
52
52
|
}
|
|
53
53
|
// Get routes to prerender
|
|
54
|
-
const {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
+
}
|
|
60
85
|
}
|
|
61
|
-
if (
|
|
86
|
+
if (!serializableRouteTreeNodeForPrerender.length || errors.length > 0) {
|
|
62
87
|
return {
|
|
63
88
|
errors,
|
|
64
89
|
warnings,
|
|
65
90
|
output: {},
|
|
66
91
|
serializableRouteTreeNode,
|
|
67
|
-
prerenderedRoutes: allRoutes,
|
|
68
92
|
};
|
|
69
93
|
}
|
|
70
94
|
// Render routes
|
|
71
|
-
const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap,
|
|
95
|
+
const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, serializableRouteTreeNodeForPrerender, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, appShellOptions);
|
|
72
96
|
errors.push(...renderingErrors);
|
|
73
97
|
return {
|
|
74
98
|
errors,
|
|
75
99
|
warnings,
|
|
76
100
|
output,
|
|
77
101
|
serializableRouteTreeNode,
|
|
78
|
-
prerenderedRoutes: allRoutes,
|
|
79
102
|
};
|
|
80
103
|
}
|
|
81
|
-
|
|
82
|
-
add(value) {
|
|
83
|
-
return super.add(addLeadingSlash(value));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions) {
|
|
104
|
+
async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions) {
|
|
87
105
|
const output = {};
|
|
88
106
|
const errors = [];
|
|
89
107
|
const workerExecArgv = [
|
|
@@ -94,30 +112,35 @@ async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspace
|
|
|
94
112
|
if (sourcemap) {
|
|
95
113
|
workerExecArgv.push('--enable-source-maps');
|
|
96
114
|
}
|
|
97
|
-
const renderWorker = new
|
|
115
|
+
const renderWorker = new worker_pool_1.WorkerPool({
|
|
98
116
|
filename: require.resolve('./render-worker'),
|
|
99
|
-
maxThreads: Math.min(
|
|
117
|
+
maxThreads: Math.min(serializableRouteTreeNode.length, maxThreads),
|
|
100
118
|
workerData: {
|
|
101
119
|
workspaceRoot,
|
|
102
120
|
outputFiles: outputFilesForWorker,
|
|
103
121
|
assetFiles: assetFilesForWorker,
|
|
104
122
|
},
|
|
105
123
|
execArgv: workerExecArgv,
|
|
106
|
-
recordTiming: false,
|
|
107
124
|
});
|
|
108
125
|
try {
|
|
109
126
|
const renderingPromises = [];
|
|
110
|
-
const appShellRoute = appShellOptions
|
|
127
|
+
const appShellRoute = appShellOptions && addLeadingSlash(appShellOptions.route);
|
|
111
128
|
const baseHrefWithLeadingSlash = addLeadingSlash(baseHref);
|
|
112
|
-
for (const route of
|
|
129
|
+
for (const { route, redirectTo, renderMode } of serializableRouteTreeNode) {
|
|
113
130
|
// Remove base href from file output path.
|
|
114
131
|
const routeWithoutBaseHref = addLeadingSlash(route.slice(baseHrefWithLeadingSlash.length - 1));
|
|
115
|
-
const
|
|
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 });
|
|
116
141
|
const renderResult = render
|
|
117
142
|
.then((content) => {
|
|
118
143
|
if (content !== null) {
|
|
119
|
-
const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
|
|
120
|
-
const isAppShellRoute = appShellRoute === routeWithoutBaseHref;
|
|
121
144
|
output[outPath] = { content, appShellRoute: isAppShellRoute };
|
|
122
145
|
}
|
|
123
146
|
})
|
|
@@ -137,21 +160,24 @@ async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspace
|
|
|
137
160
|
output,
|
|
138
161
|
};
|
|
139
162
|
}
|
|
140
|
-
async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetFilesForWorker, appShellOptions, prerenderOptions, sourcemap,
|
|
141
|
-
const { routesFile, discoverRoutes } = prerenderOptions;
|
|
142
|
-
const routes =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
+
});
|
|
146
170
|
}
|
|
147
171
|
if (routesFile) {
|
|
148
172
|
const routesFromFile = (await (0, promises_1.readFile)(routesFile, 'utf8')).split(/\r?\n/);
|
|
149
173
|
for (const route of routesFromFile) {
|
|
150
|
-
routes.
|
|
174
|
+
routes.push({
|
|
175
|
+
route: (0, url_1.urlJoin)(baseHref, route.trim()),
|
|
176
|
+
});
|
|
151
177
|
}
|
|
152
178
|
}
|
|
153
179
|
if (!discoverRoutes) {
|
|
154
|
-
return {
|
|
180
|
+
return { errors: [], serializedRouteTree: routes };
|
|
155
181
|
}
|
|
156
182
|
const workerExecArgv = [
|
|
157
183
|
'--import',
|
|
@@ -161,7 +187,7 @@ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, asset
|
|
|
161
187
|
if (sourcemap) {
|
|
162
188
|
workerExecArgv.push('--enable-source-maps');
|
|
163
189
|
}
|
|
164
|
-
const renderWorker = new
|
|
190
|
+
const renderWorker = new worker_pool_1.WorkerPool({
|
|
165
191
|
filename: require.resolve('./routes-extractor-worker'),
|
|
166
192
|
maxThreads: 1,
|
|
167
193
|
workerData: {
|
|
@@ -170,41 +196,23 @@ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, asset
|
|
|
170
196
|
assetFiles: assetFilesForWorker,
|
|
171
197
|
},
|
|
172
198
|
execArgv: workerExecArgv,
|
|
173
|
-
recordTiming: false,
|
|
174
199
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
errors
|
|
180
|
-
})
|
|
181
|
-
.finally(() => {
|
|
182
|
-
void renderWorker.destroy();
|
|
183
|
-
});
|
|
184
|
-
const skippedRedirects = [];
|
|
185
|
-
const skippedOthers = [];
|
|
186
|
-
for (const { route, redirectTo } of serializableRouteTreeNode) {
|
|
187
|
-
if (redirectTo) {
|
|
188
|
-
skippedRedirects.push(route);
|
|
189
|
-
}
|
|
190
|
-
else if (route.includes('*')) {
|
|
191
|
-
skippedOthers.push(route);
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
routes.add(route);
|
|
195
|
-
}
|
|
200
|
+
try {
|
|
201
|
+
const { serializedRouteTree, errors } = await renderWorker.run({
|
|
202
|
+
outputMode,
|
|
203
|
+
});
|
|
204
|
+
return { errors, serializedRouteTree: [...routes, ...serializedRouteTree] };
|
|
196
205
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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();
|
|
206
215
|
}
|
|
207
|
-
return { routes, serializableRouteTreeNode, warnings };
|
|
208
216
|
}
|
|
209
217
|
function addLeadingSlash(value) {
|
|
210
218
|
return value.charAt(0) === '/' ? value : '/' + value;
|
|
@@ -212,3 +220,27 @@ function addLeadingSlash(value) {
|
|
|
212
220
|
function removeLeadingSlash(value) {
|
|
213
221
|
return value.charAt(0) === '/' ? value.slice(1) : value;
|
|
214
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,13 +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
|
|
9
|
-
import
|
|
10
|
-
export interface
|
|
11
|
-
|
|
8
|
+
import { OutputMode } from '../../builders/application/schema';
|
|
9
|
+
import { RoutersExtractorWorkerResult } from './models';
|
|
10
|
+
export interface ExtractRoutesOptions {
|
|
11
|
+
outputMode?: OutputMode;
|
|
12
12
|
}
|
|
13
|
-
export type RoutersExtractorWorkerResult = ReturnType<Awaited<ReturnType<typeof ɵextractRoutesAndCreateRouteTree>>['toObject']>;
|
|
14
13
|
/** Renders an application based on a provided options. */
|
|
15
|
-
declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
|
|
14
|
+
declare function extractRoutes({ outputMode, }: ExtractRoutesOptions): Promise<RoutersExtractorWorkerResult>;
|
|
16
15
|
declare const _default: typeof extractRoutes;
|
|
17
16
|
export default _default;
|
|
@@ -7,15 +7,17 @@
|
|
|
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 = await extractRoutesAndCreateRouteTree(new URL('http://local-angular-prerender/'),
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const { routeTree, errors } = await extractRoutesAndCreateRouteTree(new URL('http://local-angular-prerender/'), undefined /** manifest */, true /** invokeGetPrerenderParams */, outputMode === schema_1.OutputMode.Server /** includePrerenderFallbackRoutes */);
|
|
17
|
+
return {
|
|
18
|
+
errors,
|
|
19
|
+
serializedRouteTree: routeTree.toObject(),
|
|
20
|
+
};
|
|
19
21
|
}
|
|
20
22
|
function initialize() {
|
|
21
23
|
(0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
|
|
@@ -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,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;
|
|
File without changes
|
|
File without changes
|