Skip to content

Commit 2e8e9d8

Browse files
committed
perf(@angular-devkit/build-angular): patch fetch to load assets from memory
This commit refactors the assets SSG asset loading from memory to use a patched version of `fetch` instead of spawning a separate server. (cherry picked from commit 68b6a57)
1 parent d392d65 commit 2e8e9d8

File tree

11 files changed

+169
-189
lines changed

11 files changed

+169
-189
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@
209209
"ts-node": "^10.9.1",
210210
"tslib": "2.6.2",
211211
"typescript": "5.2.2",
212+
"undici": "5.27.0",
212213
"verdaccio": "5.27.0",
213214
"verdaccio-auth-memory": "^10.0.0",
214215
"vite": "4.5.0",

packages/angular_devkit/build_angular/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ ts_library(
201201
"@npm//tree-kill",
202202
"@npm//tslib",
203203
"@npm//typescript",
204+
"@npm//undici",
204205
"@npm//vite",
205206
"@npm//webpack",
206207
"@npm//webpack-dev-middleware",

packages/angular_devkit/build_angular/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"text-table": "0.2.0",
6565
"tree-kill": "1.2.2",
6666
"tslib": "2.6.2",
67+
"undici": "5.27.0",
6768
"vite": "4.5.0",
6869
"webpack": "5.89.0",
6970
"webpack-dev-middleware": "6.1.1",

packages/angular_devkit/build_angular/src/builders/prerender/routes-extractor-worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async function extract(): Promise<string[]> {
5151
);
5252

5353
const routes: string[] = [];
54-
for await (const { route, success } of extractRoutes(bootstrapAppFnOrModule, document, '')) {
54+
for await (const { route, success } of extractRoutes(bootstrapAppFnOrModule, document)) {
5555
if (success) {
5656
routes.push(route);
5757
}

packages/angular_devkit/build_angular/src/utils/routes-extractor/extractor.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,11 @@ async function* getRoutesFromRouterConfig(
7878
export async function* extractRoutes(
7979
bootstrapAppFnOrModule: (() => Promise<ApplicationRef>) | Type<unknown>,
8080
document: string,
81-
url: string,
8281
): AsyncIterableIterator<RouterResult> {
8382
const platformRef = createPlatformFactory(platformCore, 'server', [
8483
{
8584
provide: INITIAL_CONFIG,
86-
useValue: { document, url },
85+
useValue: { document, url: '' },
8786
},
8887
{
8988
provide: ɵConsole,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.io/license
7+
*/
8+
9+
import { lookup as lookupMimeType } from 'mrmime';
10+
import { readFile } from 'node:fs/promises';
11+
import { extname } from 'node:path';
12+
import { workerData } from 'node:worker_threads';
13+
import { Response, fetch } from 'undici';
14+
15+
/**
16+
* This is passed as workerData when setting up the worker via the `piscina` package.
17+
*/
18+
const { assetFiles } = workerData as {
19+
assetFiles: Record</** Destination */ string, /** Source */ string>;
20+
};
21+
22+
const assetsCache: Map<string, { headers: undefined | Record<string, string>; content: Buffer }> =
23+
new Map();
24+
25+
const RESOLVE_PROTOCOL = 'resolve:';
26+
27+
export function patchFetchToLoadInMemoryAssets(): void {
28+
const global = globalThis as unknown as { fetch: typeof fetch };
29+
const originalFetch = global.fetch;
30+
const patchedFetch: typeof fetch = async (input, init) => {
31+
let url: URL;
32+
if (input instanceof URL) {
33+
url = input;
34+
} else if (typeof input === 'string') {
35+
url = new URL(input, RESOLVE_PROTOCOL + '//');
36+
} else if (typeof input === 'object' && 'url' in input) {
37+
url = new URL(input.url, RESOLVE_PROTOCOL + '//');
38+
} else {
39+
return originalFetch(input, init);
40+
}
41+
42+
const { pathname, protocol } = url;
43+
44+
if (protocol !== RESOLVE_PROTOCOL || !assetFiles[pathname]) {
45+
// Only handle relative requests or files that are in assets.
46+
return originalFetch(input, init);
47+
}
48+
49+
const cachedAsset = assetsCache.get(pathname);
50+
if (cachedAsset) {
51+
const { content, headers } = cachedAsset;
52+
53+
return new Response(content, {
54+
headers,
55+
});
56+
}
57+
58+
const extension = extname(pathname);
59+
const mimeType = lookupMimeType(extension);
60+
const content = await readFile(assetFiles[pathname]);
61+
const headers = mimeType
62+
? {
63+
'Content-Type': mimeType,
64+
}
65+
: undefined;
66+
67+
assetsCache.set(pathname, { headers, content });
68+
69+
return new Response(content, {
70+
headers,
71+
});
72+
};
73+
74+
global.fetch = patchedFetch;
75+
}

packages/angular_devkit/build_angular/src/utils/server-rendering/prerender-server.ts

Lines changed: 0 additions & 121 deletions
This file was deleted.

0 commit comments

Comments
 (0)