@angular/ssr 19.0.0-next.1 → 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.
- package/LICENSE +21 -0
- package/fesm2022/node.mjs +425 -0
- package/fesm2022/node.mjs.map +1 -0
- package/fesm2022/ssr.mjs +1302 -758
- package/fesm2022/ssr.mjs.map +1 -1
- package/index.d.ts +556 -95
- package/node/index.d.ts +207 -0
- package/package.json +13 -11
- package/third_party/critters/THIRD_PARTY_LICENSES.txt +350 -0
- package/{esm2022/index.mjs → third_party/critters/index.d.ts} +3 -2
- package/third_party/critters/index.js +13160 -0
- package/third_party/critters/index.js.map +7 -0
- package/esm2022/private_export.mjs +0 -10
- package/esm2022/public_api.mjs +0 -9
- package/esm2022/src/app-engine.mjs +0 -84
- package/esm2022/src/app.mjs +0 -167
- package/esm2022/src/assets.mjs +0 -44
- package/esm2022/src/common-engine/common-engine.mjs +0 -137
- package/esm2022/src/common-engine/inline-css-processor.mjs +0 -181
- package/esm2022/src/common-engine/peformance-profiler.mjs +0 -50
- package/esm2022/src/console.mjs +0 -34
- package/esm2022/src/hooks.mjs +0 -94
- package/esm2022/src/i18n.mjs +0 -41
- package/esm2022/src/manifest.mjs +0 -59
- package/esm2022/src/request.mjs +0 -63
- package/esm2022/src/response.mjs +0 -58
- package/esm2022/src/routes/ng-routes.mjs +0 -157
- package/esm2022/src/routes/route-tree.mjs +0 -180
- package/esm2022/src/routes/router.mjs +0 -88
- package/esm2022/src/tokens.mjs +0 -20
- package/esm2022/src/utils/ng.mjs +0 -51
- package/esm2022/src/utils/url.mjs +0 -85
|
@@ -1,50 +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
|
-
const PERFORMANCE_MARK_PREFIX = '🅰️';
|
|
9
|
-
export function printPerformanceLogs() {
|
|
10
|
-
let maxWordLength = 0;
|
|
11
|
-
const benchmarks = [];
|
|
12
|
-
for (const { name, duration } of performance.getEntriesByType('measure')) {
|
|
13
|
-
if (!name.startsWith(PERFORMANCE_MARK_PREFIX)) {
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
// `🅰️:Retrieve SSG Page` -> `Retrieve SSG Page:`
|
|
17
|
-
const step = name.slice(PERFORMANCE_MARK_PREFIX.length + 1) + ':';
|
|
18
|
-
if (step.length > maxWordLength) {
|
|
19
|
-
maxWordLength = step.length;
|
|
20
|
-
}
|
|
21
|
-
benchmarks.push([step, `${duration.toFixed(1)}ms`]);
|
|
22
|
-
performance.clearMeasures(name);
|
|
23
|
-
}
|
|
24
|
-
/* eslint-disable no-console */
|
|
25
|
-
console.log('********** Performance results **********');
|
|
26
|
-
for (const [step, value] of benchmarks) {
|
|
27
|
-
const spaces = maxWordLength - step.length + 5;
|
|
28
|
-
console.log(step + ' '.repeat(spaces) + value);
|
|
29
|
-
}
|
|
30
|
-
console.log('*****************************************');
|
|
31
|
-
/* eslint-enable no-console */
|
|
32
|
-
}
|
|
33
|
-
export async function runMethodAndMeasurePerf(label, asyncMethod) {
|
|
34
|
-
const labelName = `${PERFORMANCE_MARK_PREFIX}:${label}`;
|
|
35
|
-
const startLabel = `start:${labelName}`;
|
|
36
|
-
const endLabel = `end:${labelName}`;
|
|
37
|
-
try {
|
|
38
|
-
performance.mark(startLabel);
|
|
39
|
-
return await asyncMethod();
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
performance.mark(endLabel);
|
|
43
|
-
performance.measure(labelName, startLabel, endLabel);
|
|
44
|
-
performance.clearMarks(startLabel);
|
|
45
|
-
performance.clearMarks(endLabel);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
export function noopRunMethodAndMeasurePerf(label, asyncMethod) {
|
|
49
|
-
return asyncMethod();
|
|
50
|
-
}
|
package/esm2022/src/console.mjs
DELETED
|
@@ -1,34 +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 { ɵConsole } from '@angular/core';
|
|
9
|
-
/**
|
|
10
|
-
* Custom implementation of the Angular Console service that filters out specific log messages.
|
|
11
|
-
*
|
|
12
|
-
* This class extends the internal Angular `ɵConsole` class to provide customized logging behavior.
|
|
13
|
-
* It overrides the `log` method to suppress logs that match certain predefined messages.
|
|
14
|
-
*/
|
|
15
|
-
export class Console extends ɵConsole {
|
|
16
|
-
/**
|
|
17
|
-
* A set of log messages that should be ignored and not printed to the console.
|
|
18
|
-
*/
|
|
19
|
-
ignoredLogs = new Set(['Angular is running in development mode.']);
|
|
20
|
-
/**
|
|
21
|
-
* Logs a message to the console if it is not in the set of ignored messages.
|
|
22
|
-
*
|
|
23
|
-
* @param message - The message to log to the console.
|
|
24
|
-
*
|
|
25
|
-
* This method overrides the `log` method of the `ɵConsole` class. It checks if the
|
|
26
|
-
* message is in the `ignoredLogs` set. If it is not, it delegates the logging to
|
|
27
|
-
* the parent class's `log` method. Otherwise, the message is suppressed.
|
|
28
|
-
*/
|
|
29
|
-
log(message) {
|
|
30
|
-
if (!this.ignoredLogs.has(message)) {
|
|
31
|
-
super.log(message);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
package/esm2022/src/hooks.mjs
DELETED
|
@@ -1,94 +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
|
-
/**
|
|
9
|
-
* Manages a collection of hooks and provides methods to register and execute them.
|
|
10
|
-
* Hooks are functions that can be invoked with specific arguments to allow modifications or enhancements.
|
|
11
|
-
*/
|
|
12
|
-
export class Hooks {
|
|
13
|
-
/**
|
|
14
|
-
* A map of hook names to arrays of hook functions.
|
|
15
|
-
* Each hook name can have multiple associated functions, which are executed in sequence.
|
|
16
|
-
*/
|
|
17
|
-
store = new Map();
|
|
18
|
-
/**
|
|
19
|
-
* Executes all hooks associated with the specified name, passing the given argument to each hook function.
|
|
20
|
-
* The hooks are invoked sequentially, and the argument may be modified by each hook.
|
|
21
|
-
*
|
|
22
|
-
* @template Hook - The type of the hook name. It should be one of the keys of `HooksMapping`.
|
|
23
|
-
* @param name - The name of the hook whose functions will be executed.
|
|
24
|
-
* @param context - The input value to be passed to each hook function. The value is mutated by each hook function.
|
|
25
|
-
* @returns A promise that resolves once all hook functions have been executed.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```typescript
|
|
29
|
-
* const hooks = new Hooks();
|
|
30
|
-
* hooks.on('html:transform:pre', async (ctx) => {
|
|
31
|
-
* ctx.html = ctx.html.replace(/foo/g, 'bar');
|
|
32
|
-
* return ctx.html;
|
|
33
|
-
* });
|
|
34
|
-
* const result = await hooks.run('html:transform:pre', { html: '<div>foo</div>' });
|
|
35
|
-
* console.log(result); // '<div>bar</div>'
|
|
36
|
-
* ```
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
async run(name, context) {
|
|
40
|
-
const hooks = this.store.get(name);
|
|
41
|
-
switch (name) {
|
|
42
|
-
case 'html:transform:pre': {
|
|
43
|
-
if (!hooks) {
|
|
44
|
-
return context.html;
|
|
45
|
-
}
|
|
46
|
-
const ctx = { ...context };
|
|
47
|
-
for (const hook of hooks) {
|
|
48
|
-
ctx.html = await hook(ctx);
|
|
49
|
-
}
|
|
50
|
-
return ctx.html;
|
|
51
|
-
}
|
|
52
|
-
default:
|
|
53
|
-
throw new Error(`Running hook "${name}" is not supported.`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Registers a new hook function under the specified hook name.
|
|
58
|
-
* This function should be a function that takes an argument of type `T` and returns a `string` or `Promise<string>`.
|
|
59
|
-
*
|
|
60
|
-
* @template Hook - The type of the hook name. It should be one of the keys of `HooksMapping`.
|
|
61
|
-
* @param name - The name of the hook under which the function will be registered.
|
|
62
|
-
* @param handler - A function to be executed when the hook is triggered. The handler will be called with an argument
|
|
63
|
-
* that may be modified by the hook functions.
|
|
64
|
-
*
|
|
65
|
-
* @remarks
|
|
66
|
-
* - If there are existing handlers registered under the given hook name, the new handler will be added to the list.
|
|
67
|
-
* - If no handlers are registered under the given hook name, a new list will be created with the handler as its first element.
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```typescript
|
|
71
|
-
* hooks.on('html:transform:pre', async (ctx) => {
|
|
72
|
-
* return ctx.html.replace(/foo/g, 'bar');
|
|
73
|
-
* });
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
on(name, handler) {
|
|
77
|
-
const hooks = this.store.get(name);
|
|
78
|
-
if (hooks) {
|
|
79
|
-
hooks.push(handler);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
this.store.set(name, [handler]);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Checks if there are any hooks registered under the specified name.
|
|
87
|
-
*
|
|
88
|
-
* @param name - The name of the hook to check.
|
|
89
|
-
* @returns `true` if there are hooks registered under the specified name, otherwise `false`.
|
|
90
|
-
*/
|
|
91
|
-
has(name) {
|
|
92
|
-
return !!this.store.get(name)?.length;
|
|
93
|
-
}
|
|
94
|
-
}
|
package/esm2022/src/i18n.mjs
DELETED
|
@@ -1,41 +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
|
-
/**
|
|
9
|
-
* Extracts a potential locale ID from a given URL based on the specified base path.
|
|
10
|
-
*
|
|
11
|
-
* This function parses the URL to locate a potential locale identifier that immediately
|
|
12
|
-
* follows the base path segment in the URL's pathname. If the URL does not contain a valid
|
|
13
|
-
* locale ID, an empty string is returned.
|
|
14
|
-
*
|
|
15
|
-
* @param url - The full URL from which to extract the locale ID.
|
|
16
|
-
* @param basePath - The base path used as the reference point for extracting the locale ID.
|
|
17
|
-
* @returns The extracted locale ID if present, or an empty string if no valid locale ID is found.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```js
|
|
21
|
-
* const url = new URL('https://example.com/base/en/page');
|
|
22
|
-
* const basePath = '/base';
|
|
23
|
-
* const localeId = getPotentialLocaleIdFromUrl(url, basePath);
|
|
24
|
-
* console.log(localeId); // Output: 'en'
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export function getPotentialLocaleIdFromUrl(url, basePath) {
|
|
28
|
-
const { pathname } = url;
|
|
29
|
-
// Move forward of the base path section.
|
|
30
|
-
let start = basePath.length;
|
|
31
|
-
if (pathname[start] === '/') {
|
|
32
|
-
start++;
|
|
33
|
-
}
|
|
34
|
-
// Find the next forward slash.
|
|
35
|
-
let end = pathname.indexOf('/', start);
|
|
36
|
-
if (end === -1) {
|
|
37
|
-
end = pathname.length;
|
|
38
|
-
}
|
|
39
|
-
// Extract the potential locale id.
|
|
40
|
-
return pathname.slice(start, end);
|
|
41
|
-
}
|
package/esm2022/src/manifest.mjs
DELETED
|
@@ -1,59 +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
|
-
/**
|
|
9
|
-
* The Angular app manifest object.
|
|
10
|
-
* This is used internally to store the current Angular app manifest.
|
|
11
|
-
*/
|
|
12
|
-
let angularAppManifest;
|
|
13
|
-
/**
|
|
14
|
-
* Sets the Angular app manifest.
|
|
15
|
-
*
|
|
16
|
-
* @param manifest - The manifest object to set for the Angular application.
|
|
17
|
-
*/
|
|
18
|
-
export function setAngularAppManifest(manifest) {
|
|
19
|
-
angularAppManifest = manifest;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Gets the Angular app manifest.
|
|
23
|
-
*
|
|
24
|
-
* @returns The Angular app manifest.
|
|
25
|
-
* @throws Will throw an error if the Angular app manifest is not set.
|
|
26
|
-
*/
|
|
27
|
-
export function getAngularAppManifest() {
|
|
28
|
-
if (!angularAppManifest) {
|
|
29
|
-
throw new Error('Angular app manifest is not set. ' +
|
|
30
|
-
`Please ensure you are using the '@angular/build:application' builder to build your server application.`);
|
|
31
|
-
}
|
|
32
|
-
return angularAppManifest;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* The Angular app engine manifest object.
|
|
36
|
-
* This is used internally to store the current Angular app engine manifest.
|
|
37
|
-
*/
|
|
38
|
-
let angularAppEngineManifest;
|
|
39
|
-
/**
|
|
40
|
-
* Sets the Angular app engine manifest.
|
|
41
|
-
*
|
|
42
|
-
* @param manifest - The engine manifest object to set.
|
|
43
|
-
*/
|
|
44
|
-
export function setAngularAppEngineManifest(manifest) {
|
|
45
|
-
angularAppEngineManifest = manifest;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Gets the Angular app engine manifest.
|
|
49
|
-
*
|
|
50
|
-
* @returns The Angular app engine manifest.
|
|
51
|
-
* @throws Will throw an error if the Angular app engine manifest is not set.
|
|
52
|
-
*/
|
|
53
|
-
export function getAngularAppEngineManifest() {
|
|
54
|
-
if (!angularAppEngineManifest) {
|
|
55
|
-
throw new Error('Angular app engine manifest is not set. ' +
|
|
56
|
-
`Please ensure you are using the '@angular/build:application' builder to build your server application.`);
|
|
57
|
-
}
|
|
58
|
-
return angularAppEngineManifest;
|
|
59
|
-
}
|
package/esm2022/src/request.mjs
DELETED
|
@@ -1,63 +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
|
-
/**
|
|
9
|
-
* Converts a Node.js `IncomingMessage` into a Web Standard `Request`.
|
|
10
|
-
*
|
|
11
|
-
* @param nodeRequest - The Node.js `IncomingMessage` object to convert.
|
|
12
|
-
* @returns A Web Standard `Request` object.
|
|
13
|
-
*/
|
|
14
|
-
export function createWebRequestFromNodeRequest(nodeRequest) {
|
|
15
|
-
const { headers, method = 'GET' } = nodeRequest;
|
|
16
|
-
const withBody = method !== 'GET' && method !== 'HEAD';
|
|
17
|
-
return new Request(createRequestUrl(nodeRequest), {
|
|
18
|
-
method,
|
|
19
|
-
headers: createRequestHeaders(headers),
|
|
20
|
-
body: withBody ? nodeRequest : undefined,
|
|
21
|
-
duplex: withBody ? 'half' : undefined,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Creates a `Headers` object from Node.js `IncomingHttpHeaders`.
|
|
26
|
-
*
|
|
27
|
-
* @param nodeHeaders - The Node.js `IncomingHttpHeaders` object to convert.
|
|
28
|
-
* @returns A `Headers` object containing the converted headers.
|
|
29
|
-
*/
|
|
30
|
-
function createRequestHeaders(nodeHeaders) {
|
|
31
|
-
const headers = new Headers();
|
|
32
|
-
for (const [name, value] of Object.entries(nodeHeaders)) {
|
|
33
|
-
if (typeof value === 'string') {
|
|
34
|
-
headers.append(name, value);
|
|
35
|
-
}
|
|
36
|
-
else if (Array.isArray(value)) {
|
|
37
|
-
for (const item of value) {
|
|
38
|
-
headers.append(name, item);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return headers;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Creates a `URL` object from a Node.js `IncomingMessage`, taking into account the protocol, host, and port.
|
|
46
|
-
*
|
|
47
|
-
* @param nodeRequest - The Node.js `IncomingMessage` object to extract URL information from.
|
|
48
|
-
* @returns A `URL` object representing the request URL.
|
|
49
|
-
*/
|
|
50
|
-
function createRequestUrl(nodeRequest) {
|
|
51
|
-
const { headers, socket, url = '' } = nodeRequest;
|
|
52
|
-
const protocol = headers['x-forwarded-proto'] ?? ('encrypted' in socket && socket.encrypted ? 'https' : 'http');
|
|
53
|
-
const hostname = headers['x-forwarded-host'] ?? headers.host ?? headers[':authority'];
|
|
54
|
-
const port = headers['x-forwarded-port'] ?? socket.localPort;
|
|
55
|
-
if (Array.isArray(hostname)) {
|
|
56
|
-
throw new Error('host value cannot be an array.');
|
|
57
|
-
}
|
|
58
|
-
let hostnameWithPort = hostname;
|
|
59
|
-
if (port && !hostname?.includes(':')) {
|
|
60
|
-
hostnameWithPort += `:${port}`;
|
|
61
|
-
}
|
|
62
|
-
return new URL(url, `${protocol}://${hostnameWithPort}`);
|
|
63
|
-
}
|
package/esm2022/src/response.mjs
DELETED
|
@@ -1,58 +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
|
-
/**
|
|
9
|
-
* Streams a web-standard `Response` into a Node.js `ServerResponse`.
|
|
10
|
-
*
|
|
11
|
-
* @param source - The web-standard `Response` object to stream from.
|
|
12
|
-
* @param destination - The Node.js `ServerResponse` object to stream into.
|
|
13
|
-
* @returns A promise that resolves once the streaming operation is complete.
|
|
14
|
-
*/
|
|
15
|
-
export async function writeResponseToNodeResponse(source, destination) {
|
|
16
|
-
const { status, headers, body } = source;
|
|
17
|
-
destination.statusCode = status;
|
|
18
|
-
let cookieHeaderSet = false;
|
|
19
|
-
for (const [name, value] of headers.entries()) {
|
|
20
|
-
if (name === 'set-cookie') {
|
|
21
|
-
if (cookieHeaderSet) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
// Sets the 'set-cookie' header only once to ensure it is correctly applied.
|
|
25
|
-
// Concatenating 'set-cookie' values can lead to incorrect behavior, so we use a single value from `headers.getSetCookie()`.
|
|
26
|
-
destination.setHeader(name, headers.getSetCookie());
|
|
27
|
-
cookieHeaderSet = true;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
destination.setHeader(name, value);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (!body) {
|
|
34
|
-
destination.end();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
const reader = body.getReader();
|
|
39
|
-
destination.on('close', () => {
|
|
40
|
-
reader.cancel().catch((error) => {
|
|
41
|
-
// eslint-disable-next-line no-console
|
|
42
|
-
console.error(`An error occurred while writing the response body for: ${destination.req.url}.`, error);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
// eslint-disable-next-line no-constant-condition
|
|
46
|
-
while (true) {
|
|
47
|
-
const { done, value } = await reader.read();
|
|
48
|
-
if (done) {
|
|
49
|
-
destination.end();
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
destination.write(value);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
destination.end('Internal server error.');
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,157 +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 { APP_BASE_HREF, PlatformLocation } from '@angular/common';
|
|
9
|
-
import { ApplicationRef, Compiler, createPlatformFactory, platformCore, ɵwhenStable as whenStable, ɵConsole, ɵresetCompiledComponents, } from '@angular/core';
|
|
10
|
-
import { INITIAL_CONFIG, ɵINTERNAL_SERVER_PLATFORM_PROVIDERS as INTERNAL_SERVER_PLATFORM_PROVIDERS, } from '@angular/platform-server';
|
|
11
|
-
import { Router, ɵloadChildren as loadChildrenHelper } from '@angular/router';
|
|
12
|
-
import { Console } from '../console';
|
|
13
|
-
import { isNgModule } from '../utils/ng';
|
|
14
|
-
import { joinUrlParts } from '../utils/url';
|
|
15
|
-
/**
|
|
16
|
-
* Recursively traverses the Angular router configuration to retrieve routes.
|
|
17
|
-
*
|
|
18
|
-
* Iterates through the router configuration, yielding each route along with its potential
|
|
19
|
-
* redirection or error status. Handles nested routes and lazy-loaded child routes.
|
|
20
|
-
*
|
|
21
|
-
* @param options - An object containing the parameters for traversing routes.
|
|
22
|
-
* @returns An async iterator yielding `RouteResult` objects.
|
|
23
|
-
*/
|
|
24
|
-
async function* traverseRoutesConfig(options) {
|
|
25
|
-
const { routes, compiler, parentInjector, parentRoute } = options;
|
|
26
|
-
for (const route of routes) {
|
|
27
|
-
const { path = '', redirectTo, loadChildren, children } = route;
|
|
28
|
-
const currentRoutePath = joinUrlParts(parentRoute, path);
|
|
29
|
-
yield {
|
|
30
|
-
route: currentRoutePath,
|
|
31
|
-
redirectTo: typeof redirectTo === 'string'
|
|
32
|
-
? resolveRedirectTo(currentRoutePath, redirectTo)
|
|
33
|
-
: undefined,
|
|
34
|
-
};
|
|
35
|
-
if (children?.length) {
|
|
36
|
-
// Recursively process child routes.
|
|
37
|
-
yield* traverseRoutesConfig({
|
|
38
|
-
routes: children,
|
|
39
|
-
compiler,
|
|
40
|
-
parentInjector,
|
|
41
|
-
parentRoute: currentRoutePath,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
if (loadChildren) {
|
|
45
|
-
// Load and process lazy-loaded child routes.
|
|
46
|
-
const loadedChildRoutes = await loadChildrenHelper(route, compiler, parentInjector).toPromise();
|
|
47
|
-
if (loadedChildRoutes) {
|
|
48
|
-
const { routes: childRoutes, injector = parentInjector } = loadedChildRoutes;
|
|
49
|
-
yield* traverseRoutesConfig({
|
|
50
|
-
routes: childRoutes,
|
|
51
|
-
compiler,
|
|
52
|
-
parentInjector: injector,
|
|
53
|
-
parentRoute: currentRoutePath,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Resolves the `redirectTo` property for a given route.
|
|
61
|
-
*
|
|
62
|
-
* This function processes the `redirectTo` property to ensure that it correctly
|
|
63
|
-
* resolves relative to the current route path. If `redirectTo` is an absolute path,
|
|
64
|
-
* it is returned as is. If it is a relative path, it is resolved based on the current route path.
|
|
65
|
-
*
|
|
66
|
-
* @param routePath - The current route path.
|
|
67
|
-
* @param redirectTo - The target path for redirection.
|
|
68
|
-
* @returns The resolved redirect path as a string.
|
|
69
|
-
*/
|
|
70
|
-
function resolveRedirectTo(routePath, redirectTo) {
|
|
71
|
-
if (redirectTo[0] === '/') {
|
|
72
|
-
// If the redirectTo path is absolute, return it as is.
|
|
73
|
-
return redirectTo;
|
|
74
|
-
}
|
|
75
|
-
// Resolve relative redirectTo based on the current route path.
|
|
76
|
-
const segments = routePath.split('/');
|
|
77
|
-
segments.pop(); // Remove the last segment to make it relative.
|
|
78
|
-
return joinUrlParts(...segments, redirectTo);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Retrieves routes from the given Angular application.
|
|
82
|
-
*
|
|
83
|
-
* This function initializes an Angular platform, bootstraps the application or module,
|
|
84
|
-
* and retrieves routes from the Angular router configuration. It handles both module-based
|
|
85
|
-
* and function-based bootstrapping. It yields the resulting routes as `RouteResult` objects.
|
|
86
|
-
*
|
|
87
|
-
* @param bootstrap - A function that returns a promise resolving to an `ApplicationRef` or an Angular module to bootstrap.
|
|
88
|
-
* @param document - The initial HTML document used for server-side rendering.
|
|
89
|
-
* This document is necessary to render the application on the server.
|
|
90
|
-
* @param url - The URL for server-side rendering. The URL is used to configure `ServerPlatformLocation`. This configuration is crucial
|
|
91
|
-
* for ensuring that API requests for relative paths succeed, which is essential for accurate route extraction.
|
|
92
|
-
* See:
|
|
93
|
-
* - https://github.com/angular/angular/blob/d608b857c689d17a7ffa33bbb510301014d24a17/packages/platform-server/src/location.ts#L51
|
|
94
|
-
* - https://github.com/angular/angular/blob/6882cc7d9eed26d3caeedca027452367ba25f2b9/packages/platform-server/src/http.ts#L44
|
|
95
|
-
* @returns A promise that resolves to an object of type `AngularRouterConfigResult`.
|
|
96
|
-
*/
|
|
97
|
-
export async function getRoutesFromAngularRouterConfig(bootstrap, document, url) {
|
|
98
|
-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
99
|
-
// Need to clean up GENERATED_COMP_IDS map in `@angular/core`.
|
|
100
|
-
// Otherwise an incorrect component ID generation collision detected warning will be displayed in development.
|
|
101
|
-
// See: https://github.com/angular/angular-cli/issues/25924
|
|
102
|
-
ɵresetCompiledComponents();
|
|
103
|
-
}
|
|
104
|
-
const { protocol, host } = url;
|
|
105
|
-
// Create and initialize the Angular platform for server-side rendering.
|
|
106
|
-
const platformRef = createPlatformFactory(platformCore, 'server', [
|
|
107
|
-
{
|
|
108
|
-
provide: INITIAL_CONFIG,
|
|
109
|
-
useValue: { document, url: `${protocol}//${host}/` },
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
provide: ɵConsole,
|
|
113
|
-
useFactory: () => new Console(),
|
|
114
|
-
},
|
|
115
|
-
...INTERNAL_SERVER_PLATFORM_PROVIDERS,
|
|
116
|
-
])();
|
|
117
|
-
try {
|
|
118
|
-
let applicationRef;
|
|
119
|
-
if (isNgModule(bootstrap)) {
|
|
120
|
-
const moduleRef = await platformRef.bootstrapModule(bootstrap);
|
|
121
|
-
applicationRef = moduleRef.injector.get(ApplicationRef);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
applicationRef = await bootstrap();
|
|
125
|
-
}
|
|
126
|
-
// Wait until the application is stable.
|
|
127
|
-
await whenStable(applicationRef);
|
|
128
|
-
const injector = applicationRef.injector;
|
|
129
|
-
const router = injector.get(Router);
|
|
130
|
-
const routesResults = [];
|
|
131
|
-
if (router.config.length) {
|
|
132
|
-
const compiler = injector.get(Compiler);
|
|
133
|
-
// Retrieve all routes from the Angular router configuration.
|
|
134
|
-
const traverseRoutes = traverseRoutesConfig({
|
|
135
|
-
routes: router.config,
|
|
136
|
-
compiler,
|
|
137
|
-
parentInjector: injector,
|
|
138
|
-
parentRoute: '',
|
|
139
|
-
});
|
|
140
|
-
for await (const result of traverseRoutes) {
|
|
141
|
-
routesResults.push(result);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
routesResults.push({ route: '' });
|
|
146
|
-
}
|
|
147
|
-
const baseHref = injector.get(APP_BASE_HREF, null, { optional: true }) ??
|
|
148
|
-
injector.get(PlatformLocation).getBaseHrefFromDOM();
|
|
149
|
-
return {
|
|
150
|
-
baseHref,
|
|
151
|
-
routes: routesResults,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
platformRef.destroy();
|
|
156
|
-
}
|
|
157
|
-
}
|