@angular/build 19.0.0-next.2 → 19.0.0-next.4
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 +10 -10
- package/src/builders/application/execute-post-bundle.js +29 -18
- package/src/builders/application/i18n.js +2 -11
- package/src/builders/application/options.d.ts +11 -0
- package/src/builders/application/options.js +23 -1
- package/src/builders/application/setup-bundling.js +7 -7
- package/src/builders/dev-server/internal.d.ts +0 -1
- package/src/builders/dev-server/internal.js +1 -3
- package/src/builders/dev-server/vite-server.d.ts +7 -2
- package/src/builders/dev-server/vite-server.js +27 -12
- package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -1
- package/src/tools/angular/compilation/parallel-compilation.js +5 -0
- package/src/tools/esbuild/angular/file-reference-tracker.d.ts +1 -1
- package/src/tools/esbuild/application-code-bundle.d.ts +1 -6
- package/src/tools/esbuild/application-code-bundle.js +105 -70
- package/src/tools/esbuild/bundler-context.js +14 -10
- package/src/tools/esbuild/cache.d.ts +1 -1
- package/src/tools/esbuild/javascript-transformer.js +6 -0
- package/src/tools/esbuild/utils.d.ts +9 -0
- package/src/tools/esbuild/utils.js +14 -0
- package/src/tools/sass/sass-service.js +15 -4
- package/src/tools/sass/worker.d.ts +13 -32
- package/src/tools/sass/worker.js +1 -0
- package/src/tools/vite/angular-memory-plugin.js +2 -2
- package/src/tools/vite/middlewares/ssr-middleware.d.ts +1 -4
- package/src/tools/vite/middlewares/ssr-middleware.js +25 -38
- package/src/typings.d.ts +7 -0
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/fetch-patch.js +4 -5
- package/src/utils/server-rendering/load-esm-from-memory.d.ts +12 -2
- package/src/utils/server-rendering/manifest.d.ts +44 -0
- package/src/utils/server-rendering/manifest.js +86 -0
- package/src/utils/server-rendering/prerender.d.ts +22 -2
- package/src/utils/server-rendering/prerender.js +51 -40
- package/src/utils/server-rendering/render-worker.d.ts +7 -8
- package/src/utils/server-rendering/render-worker.js +10 -13
- package/src/utils/server-rendering/routes-extractor-worker.d.ts +2 -6
- package/src/utils/server-rendering/routes-extractor-worker.js +3 -34
- package/src/utils/server-rendering/main-bundle-exports.d.ts +0 -27
- package/src/utils/server-rendering/main-bundle-exports.js +0 -9
- package/src/utils/server-rendering/render-page.d.ts +0 -26
- package/src/utils/server-rendering/render-page.js +0 -114
|
@@ -17,7 +17,6 @@ const node_worker_threads_1 = require("node:worker_threads");
|
|
|
17
17
|
*/
|
|
18
18
|
const { assetFiles } = node_worker_threads_1.workerData;
|
|
19
19
|
const assetsCache = new Map();
|
|
20
|
-
const RESOLVE_PROTOCOL = 'resolve:';
|
|
21
20
|
function patchFetchToLoadInMemoryAssets() {
|
|
22
21
|
const originalFetch = globalThis.fetch;
|
|
23
22
|
const patchedFetch = async (input, init) => {
|
|
@@ -26,17 +25,17 @@ function patchFetchToLoadInMemoryAssets() {
|
|
|
26
25
|
url = input;
|
|
27
26
|
}
|
|
28
27
|
else if (typeof input === 'string') {
|
|
29
|
-
url = new URL(input
|
|
28
|
+
url = new URL(input);
|
|
30
29
|
}
|
|
31
30
|
else if (typeof input === 'object' && 'url' in input) {
|
|
32
|
-
url = new URL(input.url
|
|
31
|
+
url = new URL(input.url);
|
|
33
32
|
}
|
|
34
33
|
else {
|
|
35
34
|
return originalFetch(input, init);
|
|
36
35
|
}
|
|
37
|
-
const {
|
|
36
|
+
const { hostname } = url;
|
|
38
37
|
const pathname = decodeURIComponent(url.pathname);
|
|
39
|
-
if (
|
|
38
|
+
if (hostname !== 'local-angular-prerender' || !assetFiles[pathname]) {
|
|
40
39
|
// Only handle relative requests or files that are in assets.
|
|
41
40
|
return originalFetch(input, init);
|
|
42
41
|
}
|
|
@@ -5,6 +5,16 @@
|
|
|
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 {
|
|
8
|
+
import type { ApplicationRef, Type } from '@angular/core';
|
|
9
|
+
import type { ɵServerRenderContext, ɵextractRoutesAndCreateRouteTree, ɵgetOrCreateAngularServerApp } from '@angular/ssr';
|
|
10
|
+
/**
|
|
11
|
+
* Represents the exports available from the main server bundle.
|
|
12
|
+
*/
|
|
13
|
+
interface MainServerBundleExports {
|
|
14
|
+
default: (() => Promise<ApplicationRef>) | Type<unknown>;
|
|
15
|
+
ɵServerRenderContext: typeof ɵServerRenderContext;
|
|
16
|
+
ɵextractRoutesAndCreateRouteTree: typeof ɵextractRoutesAndCreateRouteTree;
|
|
17
|
+
ɵgetOrCreateAngularServerApp: typeof ɵgetOrCreateAngularServerApp;
|
|
18
|
+
}
|
|
9
19
|
export declare function loadEsmModuleFromMemory(path: './main.server.mjs'): Promise<MainServerBundleExports>;
|
|
10
|
-
export
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { NormalizedApplicationBuildOptions } from '../../builders/application/options';
|
|
9
|
+
import type { BuildOutputFile } from '../../tools/esbuild/bundler-context';
|
|
10
|
+
export declare const SERVER_APP_MANIFEST_FILENAME = "angular-app-manifest.mjs";
|
|
11
|
+
/**
|
|
12
|
+
* Generates the server manifest for the App Engine environment.
|
|
13
|
+
*
|
|
14
|
+
* This manifest is used to configure the server-side rendering (SSR) setup for the
|
|
15
|
+
* Angular application when deployed to Google App Engine. It includes the entry points
|
|
16
|
+
* for different locales and the base HREF for the application.
|
|
17
|
+
*
|
|
18
|
+
* @param i18nOptions - The internationalization options for the application build. This
|
|
19
|
+
* includes settings for inlining locales and determining the output structure.
|
|
20
|
+
* @param baseHref - The base HREF for the application. This is used to set the base URL
|
|
21
|
+
* for all relative URLs in the application.
|
|
22
|
+
* @returns A string representing the content of the SSR server manifest for App Engine.
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateAngularServerAppEngineManifest(i18nOptions: NormalizedApplicationBuildOptions['i18nOptions'], baseHref: string | undefined): string;
|
|
25
|
+
/**
|
|
26
|
+
* Generates the server manifest for the standard Node.js environment.
|
|
27
|
+
*
|
|
28
|
+
* This manifest is used to configure the server-side rendering (SSR) setup for the
|
|
29
|
+
* Angular application when running in a standard Node.js environment. It includes
|
|
30
|
+
* information about the bootstrap module, whether to inline critical CSS, and any
|
|
31
|
+
* additional HTML and CSS output files.
|
|
32
|
+
*
|
|
33
|
+
* @param additionalHtmlOutputFiles - A map of additional HTML output files generated
|
|
34
|
+
* during the build process, keyed by their file paths.
|
|
35
|
+
* @param outputFiles - An array of all output files from the build process, including
|
|
36
|
+
* JavaScript and CSS files.
|
|
37
|
+
* @param inlineCriticalCss - A boolean indicating whether critical CSS should be inlined
|
|
38
|
+
* in the server-side rendered pages.
|
|
39
|
+
* @param routes - An optional array of route definitions for the application, used for
|
|
40
|
+
* server-side rendering and routing.
|
|
41
|
+
* @returns A string representing the content of the SSR server manifest for the Node.js
|
|
42
|
+
* environment.
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateAngularServerAppManifest(additionalHtmlOutputFiles: Map<string, BuildOutputFile>, outputFiles: BuildOutputFile[], inlineCriticalCss: boolean, routes: readonly unknown[] | undefined): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
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.SERVER_APP_MANIFEST_FILENAME = void 0;
|
|
11
|
+
exports.generateAngularServerAppEngineManifest = generateAngularServerAppEngineManifest;
|
|
12
|
+
exports.generateAngularServerAppManifest = generateAngularServerAppManifest;
|
|
13
|
+
const options_1 = require("../../builders/application/options");
|
|
14
|
+
exports.SERVER_APP_MANIFEST_FILENAME = 'angular-app-manifest.mjs';
|
|
15
|
+
const MAIN_SERVER_OUTPUT_FILENAME = 'main.server.mjs';
|
|
16
|
+
/**
|
|
17
|
+
* Generates the server manifest for the App Engine environment.
|
|
18
|
+
*
|
|
19
|
+
* This manifest is used to configure the server-side rendering (SSR) setup for the
|
|
20
|
+
* Angular application when deployed to Google App Engine. It includes the entry points
|
|
21
|
+
* for different locales and the base HREF for the application.
|
|
22
|
+
*
|
|
23
|
+
* @param i18nOptions - The internationalization options for the application build. This
|
|
24
|
+
* includes settings for inlining locales and determining the output structure.
|
|
25
|
+
* @param baseHref - The base HREF for the application. This is used to set the base URL
|
|
26
|
+
* for all relative URLs in the application.
|
|
27
|
+
* @returns A string representing the content of the SSR server manifest for App Engine.
|
|
28
|
+
*/
|
|
29
|
+
function generateAngularServerAppEngineManifest(i18nOptions, baseHref) {
|
|
30
|
+
const entryPointsContent = [];
|
|
31
|
+
if (i18nOptions.shouldInline) {
|
|
32
|
+
for (const locale of i18nOptions.inlineLocales) {
|
|
33
|
+
const importPath = './' + (i18nOptions.flatOutput ? '' : locale + '/') + MAIN_SERVER_OUTPUT_FILENAME;
|
|
34
|
+
const localWithBaseHref = (0, options_1.getLocaleBaseHref)('', i18nOptions, locale) || '/';
|
|
35
|
+
entryPointsContent.push(`['${localWithBaseHref}', () => import('${importPath}')]`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
entryPointsContent.push(`['/', () => import('./${MAIN_SERVER_OUTPUT_FILENAME}')]`);
|
|
40
|
+
}
|
|
41
|
+
const manifestContent = `
|
|
42
|
+
{
|
|
43
|
+
basePath: '${baseHref ?? '/'}',
|
|
44
|
+
entryPoints: new Map([${entryPointsContent.join(', \n')}]),
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
return manifestContent;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generates the server manifest for the standard Node.js environment.
|
|
51
|
+
*
|
|
52
|
+
* This manifest is used to configure the server-side rendering (SSR) setup for the
|
|
53
|
+
* Angular application when running in a standard Node.js environment. It includes
|
|
54
|
+
* information about the bootstrap module, whether to inline critical CSS, and any
|
|
55
|
+
* additional HTML and CSS output files.
|
|
56
|
+
*
|
|
57
|
+
* @param additionalHtmlOutputFiles - A map of additional HTML output files generated
|
|
58
|
+
* during the build process, keyed by their file paths.
|
|
59
|
+
* @param outputFiles - An array of all output files from the build process, including
|
|
60
|
+
* JavaScript and CSS files.
|
|
61
|
+
* @param inlineCriticalCss - A boolean indicating whether critical CSS should be inlined
|
|
62
|
+
* in the server-side rendered pages.
|
|
63
|
+
* @param routes - An optional array of route definitions for the application, used for
|
|
64
|
+
* server-side rendering and routing.
|
|
65
|
+
* @returns A string representing the content of the SSR server manifest for the Node.js
|
|
66
|
+
* environment.
|
|
67
|
+
*/
|
|
68
|
+
function generateAngularServerAppManifest(additionalHtmlOutputFiles, outputFiles, inlineCriticalCss, routes) {
|
|
69
|
+
const serverAssetsContent = [];
|
|
70
|
+
for (const file of [...additionalHtmlOutputFiles.values(), ...outputFiles]) {
|
|
71
|
+
if (file.path === options_1.INDEX_HTML_SERVER ||
|
|
72
|
+
file.path === options_1.INDEX_HTML_CSR ||
|
|
73
|
+
(inlineCriticalCss && file.path.endsWith('.css'))) {
|
|
74
|
+
serverAssetsContent.push(`['${file.path}', async () => ${JSON.stringify(file.text)}]`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const manifestContent = `
|
|
78
|
+
export default {
|
|
79
|
+
bootstrap: () => import('./main.server.mjs').then(m => m.default),
|
|
80
|
+
inlineCriticalCss: ${inlineCriticalCss},
|
|
81
|
+
routes: ${JSON.stringify(routes, undefined, 2)},
|
|
82
|
+
assets: new Map([${serverAssetsContent.join(', \n')}]),
|
|
83
|
+
};
|
|
84
|
+
`;
|
|
85
|
+
return manifestContent;
|
|
86
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
|
|
9
9
|
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
|
|
10
|
+
import type { RoutersExtractorWorkerResult as SerializableRouteTreeNode } from './routes-extractor-worker';
|
|
10
11
|
interface PrerenderOptions {
|
|
11
12
|
routesFile?: string;
|
|
12
13
|
discoverRoutes?: boolean;
|
|
@@ -14,10 +15,29 @@ interface PrerenderOptions {
|
|
|
14
15
|
interface AppShellOptions {
|
|
15
16
|
route?: string;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Represents the output of a prerendering process.
|
|
20
|
+
*
|
|
21
|
+
* The key is the file path, and the value is an object containing the following properties:
|
|
22
|
+
*
|
|
23
|
+
* - `content`: The HTML content or output generated for the corresponding file path.
|
|
24
|
+
* - `appShellRoute`: A boolean flag indicating whether the content is an app shell.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* {
|
|
28
|
+
* '/index.html': { content: '<html>...</html>', appShell: false },
|
|
29
|
+
* '/shell/index.html': { content: '<html>...</html>', appShellRoute: true }
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
type PrerenderOutput = Record<string, {
|
|
33
|
+
content: string;
|
|
34
|
+
appShellRoute: boolean;
|
|
35
|
+
}>;
|
|
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, verbose?: boolean): Promise<{
|
|
37
|
+
output: PrerenderOutput;
|
|
19
38
|
warnings: string[];
|
|
20
39
|
errors: string[];
|
|
21
40
|
prerenderedRoutes: Set<string>;
|
|
41
|
+
serializableRouteTreeNode: SerializableRouteTreeNode;
|
|
22
42
|
}>;
|
|
23
43
|
export {};
|
|
@@ -16,19 +16,21 @@ const node_path_1 = require("node:path");
|
|
|
16
16
|
const node_url_1 = require("node:url");
|
|
17
17
|
const piscina_1 = __importDefault(require("piscina"));
|
|
18
18
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
19
|
-
|
|
19
|
+
const url_1 = require("../url");
|
|
20
|
+
async function prerenderPages(workspaceRoot, baseHref, appShellOptions = {}, prerenderOptions = {}, outputFiles, assets, sourcemap = false, maxThreads = 1, verbose = false) {
|
|
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
|
-
|
|
26
|
-
|
|
26
|
+
if (type !== bundler_context_1.BuildOutputFileType.Server) {
|
|
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
|
|
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,7 +51,7 @@ 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, errors: routesErrors, } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed,
|
|
54
|
+
const { routes: allRoutes, warnings: routesWarnings, errors: routesErrors, serializableRouteTreeNode, } = await getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetsReversed, appShellOptions, prerenderOptions, sourcemap, verbose);
|
|
53
55
|
if (routesErrors?.length) {
|
|
54
56
|
errors.push(...routesErrors);
|
|
55
57
|
}
|
|
@@ -61,17 +63,18 @@ async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOpti
|
|
|
61
63
|
errors,
|
|
62
64
|
warnings,
|
|
63
65
|
output: {},
|
|
66
|
+
serializableRouteTreeNode,
|
|
64
67
|
prerenderedRoutes: allRoutes,
|
|
65
68
|
};
|
|
66
69
|
}
|
|
67
70
|
// Render routes
|
|
68
|
-
const {
|
|
71
|
+
const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, appShellOptions);
|
|
69
72
|
errors.push(...renderingErrors);
|
|
70
|
-
warnings.push(...renderingWarnings);
|
|
71
73
|
return {
|
|
72
74
|
errors,
|
|
73
75
|
warnings,
|
|
74
76
|
output,
|
|
77
|
+
serializableRouteTreeNode,
|
|
75
78
|
prerenderedRoutes: allRoutes,
|
|
76
79
|
};
|
|
77
80
|
}
|
|
@@ -80,9 +83,8 @@ class RoutesSet extends Set {
|
|
|
80
83
|
return super.add(addLeadingSlash(value));
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker,
|
|
86
|
+
async function renderPages(baseHref, sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, appShellOptions) {
|
|
84
87
|
const output = {};
|
|
85
|
-
const warnings = [];
|
|
86
88
|
const errors = [];
|
|
87
89
|
const workerExecArgv = [
|
|
88
90
|
'--import',
|
|
@@ -99,8 +101,6 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
99
101
|
workspaceRoot,
|
|
100
102
|
outputFiles: outputFilesForWorker,
|
|
101
103
|
assetFiles: assetFilesForWorker,
|
|
102
|
-
inlineCriticalCss,
|
|
103
|
-
document,
|
|
104
104
|
},
|
|
105
105
|
execArgv: workerExecArgv,
|
|
106
106
|
recordTiming: false,
|
|
@@ -108,27 +108,21 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
108
108
|
try {
|
|
109
109
|
const renderingPromises = [];
|
|
110
110
|
const appShellRoute = appShellOptions.route && addLeadingSlash(appShellOptions.route);
|
|
111
|
+
const baseHrefWithLeadingSlash = addLeadingSlash(baseHref);
|
|
111
112
|
for (const route of allRoutes) {
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
// Remove base href from file output path.
|
|
114
|
+
const routeWithoutBaseHref = addLeadingSlash(route.slice(baseHrefWithLeadingSlash.length - 1));
|
|
115
|
+
const isAppShellRoute = appShellRoute === routeWithoutBaseHref;
|
|
116
|
+
const render = renderWorker.run({ url: route, isAppShellRoute });
|
|
115
117
|
const renderResult = render
|
|
116
|
-
.then((
|
|
117
|
-
if (content !==
|
|
118
|
-
const outPath =
|
|
119
|
-
|
|
120
|
-
: node_path_1.posix.join(removeLeadingSlash(route), 'index.html');
|
|
121
|
-
output[outPath] = content;
|
|
122
|
-
}
|
|
123
|
-
if (warnings) {
|
|
124
|
-
warnings.push(...warnings);
|
|
125
|
-
}
|
|
126
|
-
if (errors) {
|
|
127
|
-
errors.push(...errors);
|
|
118
|
+
.then((content) => {
|
|
119
|
+
if (content !== null) {
|
|
120
|
+
const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
|
|
121
|
+
output[outPath] = { content, appShellRoute: isAppShellRoute };
|
|
128
122
|
}
|
|
129
123
|
})
|
|
130
124
|
.catch((err) => {
|
|
131
|
-
errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack}`);
|
|
125
|
+
errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack ?? err.message ?? err.code ?? err}`);
|
|
132
126
|
void renderWorker.destroy();
|
|
133
127
|
});
|
|
134
128
|
renderingPromises.push(renderResult);
|
|
@@ -140,25 +134,24 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
140
134
|
}
|
|
141
135
|
return {
|
|
142
136
|
errors,
|
|
143
|
-
warnings,
|
|
144
137
|
output,
|
|
145
138
|
};
|
|
146
139
|
}
|
|
147
|
-
async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWorker,
|
|
140
|
+
async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetFilesForWorker, appShellOptions, prerenderOptions, sourcemap, verbose) {
|
|
148
141
|
const { routesFile, discoverRoutes } = prerenderOptions;
|
|
149
142
|
const routes = new RoutesSet();
|
|
150
143
|
const { route: appShellRoute } = appShellOptions;
|
|
151
144
|
if (appShellRoute !== undefined) {
|
|
152
|
-
routes.add(appShellRoute);
|
|
145
|
+
routes.add((0, url_1.urlJoin)(baseHref, appShellRoute));
|
|
153
146
|
}
|
|
154
147
|
if (routesFile) {
|
|
155
148
|
const routesFromFile = (await (0, promises_1.readFile)(routesFile, 'utf8')).split(/\r?\n/);
|
|
156
149
|
for (const route of routesFromFile) {
|
|
157
|
-
routes.add(route.trim());
|
|
150
|
+
routes.add((0, url_1.urlJoin)(baseHref, route.trim()));
|
|
158
151
|
}
|
|
159
152
|
}
|
|
160
153
|
if (!discoverRoutes) {
|
|
161
|
-
return { routes };
|
|
154
|
+
return { routes, serializableRouteTreeNode: [] };
|
|
162
155
|
}
|
|
163
156
|
const workerExecArgv = [
|
|
164
157
|
'--import',
|
|
@@ -175,14 +168,12 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWo
|
|
|
175
168
|
workspaceRoot,
|
|
176
169
|
outputFiles: outputFilesForWorker,
|
|
177
170
|
assetFiles: assetFilesForWorker,
|
|
178
|
-
document,
|
|
179
|
-
verbose,
|
|
180
171
|
},
|
|
181
172
|
execArgv: workerExecArgv,
|
|
182
173
|
recordTiming: false,
|
|
183
174
|
});
|
|
184
175
|
const errors = [];
|
|
185
|
-
const
|
|
176
|
+
const serializableRouteTreeNode = await renderWorker
|
|
186
177
|
.run({})
|
|
187
178
|
.catch((err) => {
|
|
188
179
|
errors.push(`An error occurred while extracting routes.\n\n${err.stack}`);
|
|
@@ -190,10 +181,30 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWo
|
|
|
190
181
|
.finally(() => {
|
|
191
182
|
void renderWorker.destroy();
|
|
192
183
|
});
|
|
193
|
-
|
|
194
|
-
|
|
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
|
+
}
|
|
196
|
+
}
|
|
197
|
+
let warnings;
|
|
198
|
+
if (verbose) {
|
|
199
|
+
if (skippedOthers.length) {
|
|
200
|
+
(warnings ??= []).push('The following routes were skipped from prerendering because they contain routes with dynamic parameters:\n' +
|
|
201
|
+
skippedOthers.join('\n'));
|
|
202
|
+
}
|
|
203
|
+
if (skippedRedirects.length) {
|
|
204
|
+
(warnings ??= []).push('The following routes were skipped from prerendering because they contain redirects:\n', skippedRedirects.join('\n'));
|
|
205
|
+
}
|
|
195
206
|
}
|
|
196
|
-
return { routes,
|
|
207
|
+
return { routes, serializableRouteTreeNode, warnings };
|
|
197
208
|
}
|
|
198
209
|
function addLeadingSlash(value) {
|
|
199
210
|
return value.charAt(0) === '/' ? value : '/' + value;
|
|
@@ -6,17 +6,16 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
8
|
import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
|
|
9
|
-
import { RenderResult, ServerContext } from './render-page';
|
|
10
9
|
export interface RenderWorkerData extends ESMInMemoryFileLoaderWorkerData {
|
|
11
|
-
document: string;
|
|
12
|
-
inlineCriticalCss?: boolean;
|
|
13
10
|
assetFiles: Record</** Destination */ string, /** Source */ string>;
|
|
14
11
|
}
|
|
15
12
|
export interface RenderOptions {
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
url: string;
|
|
14
|
+
isAppShellRoute: boolean;
|
|
18
15
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Renders each route in routes and writes them to <outputPath>/<route>/index.html.
|
|
18
|
+
*/
|
|
19
|
+
declare function renderPage({ url, isAppShellRoute }: RenderOptions): Promise<string | null>;
|
|
20
|
+
declare const _default: typeof renderPage;
|
|
22
21
|
export default _default;
|
|
@@ -7,24 +7,21 @@
|
|
|
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");
|
|
11
10
|
const fetch_patch_1 = require("./fetch-patch");
|
|
12
|
-
const
|
|
11
|
+
const load_esm_from_memory_1 = require("./load-esm-from-memory");
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* Renders each route in routes and writes them to <outputPath>/<route>/index.html.
|
|
15
14
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
inlineCriticalCss,
|
|
24
|
-
});
|
|
15
|
+
async function renderPage({ url, isAppShellRoute }) {
|
|
16
|
+
const { ɵgetOrCreateAngularServerApp: getOrCreateAngularServerApp, ɵServerRenderContext: ServerRenderContext, } = await (0, load_esm_from_memory_1.loadEsmModuleFromMemory)('./main.server.mjs');
|
|
17
|
+
const angularServerApp = getOrCreateAngularServerApp();
|
|
18
|
+
const response = await angularServerApp.render(new Request(new URL(url, 'http://local-angular-prerender'), {
|
|
19
|
+
signal: AbortSignal.timeout(30_000),
|
|
20
|
+
}), undefined, isAppShellRoute ? ServerRenderContext.AppShell : ServerRenderContext.SSG);
|
|
21
|
+
return response ? response.text() : null;
|
|
25
22
|
}
|
|
26
23
|
function initialize() {
|
|
27
24
|
(0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
|
|
28
|
-
return
|
|
25
|
+
return renderPage;
|
|
29
26
|
}
|
|
30
27
|
exports.default = initialize();
|
|
@@ -5,16 +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';
|
|
8
9
|
import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loader-hooks';
|
|
9
10
|
export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
|
|
10
|
-
document: string;
|
|
11
|
-
verbose: boolean;
|
|
12
11
|
assetFiles: Record</** Destination */ string, /** Source */ string>;
|
|
13
12
|
}
|
|
14
|
-
export
|
|
15
|
-
routes: string[];
|
|
16
|
-
warnings?: string[];
|
|
17
|
-
}
|
|
13
|
+
export type RoutersExtractorWorkerResult = ReturnType<Awaited<ReturnType<typeof ɵextractRoutesAndCreateRouteTree>>['toObject']>;
|
|
18
14
|
/** Renders an application based on a provided options. */
|
|
19
15
|
declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
|
|
20
16
|
declare const _default: typeof extractRoutes;
|
|
@@ -7,44 +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 node_worker_threads_1 = require("node:worker_threads");
|
|
11
10
|
const fetch_patch_1 = require("./fetch-patch");
|
|
12
11
|
const load_esm_from_memory_1 = require("./load-esm-from-memory");
|
|
13
|
-
/**
|
|
14
|
-
* This is passed as workerData when setting up the worker via the `piscina` package.
|
|
15
|
-
*/
|
|
16
|
-
const { document, verbose } = node_worker_threads_1.workerData;
|
|
17
12
|
/** Renders an application based on a provided options. */
|
|
18
13
|
async function extractRoutes() {
|
|
19
|
-
const {
|
|
20
|
-
const
|
|
21
|
-
|
|
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 };
|
|
14
|
+
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
|
+
return routeTree.toObject();
|
|
48
17
|
}
|
|
49
18
|
function initialize() {
|
|
50
19
|
(0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
|
|
@@ -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,9 +0,0 @@
|
|
|
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 });
|
|
@@ -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>;
|