-
Notifications
You must be signed in to change notification settings - Fork 12k
/
Copy pathfetch-patch.ts
75 lines (62 loc) · 2.13 KB
/
fetch-patch.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { lookup as lookupMimeType } from 'mrmime';
import { readFile } from 'node:fs/promises';
import { extname } from 'node:path';
import { workerData } from 'node:worker_threads';
import { Response, fetch } from 'undici';
/**
* This is passed as workerData when setting up the worker via the `piscina` package.
*/
const { assetFiles } = workerData as {
assetFiles: Record</** Destination */ string, /** Source */ string>;
};
const assetsCache: Map<string, { headers: undefined | Record<string, string>; content: Buffer }> =
new Map();
const RESOLVE_PROTOCOL = 'resolve:';
export function patchFetchToLoadInMemoryAssets(): void {
const global = globalThis as unknown as { fetch: typeof fetch };
const originalFetch = global.fetch;
const patchedFetch: typeof fetch = async (input, init) => {
let url: URL;
if (input instanceof URL) {
url = input;
} else if (typeof input === 'string') {
url = new URL(input, RESOLVE_PROTOCOL + '//');
} else if (typeof input === 'object' && 'url' in input) {
url = new URL(input.url, RESOLVE_PROTOCOL + '//');
} else {
return originalFetch(input, init);
}
const { pathname, protocol } = url;
if (protocol !== RESOLVE_PROTOCOL || !assetFiles[pathname]) {
// Only handle relative requests or files that are in assets.
return originalFetch(input, init);
}
const cachedAsset = assetsCache.get(pathname);
if (cachedAsset) {
const { content, headers } = cachedAsset;
return new Response(content, {
headers,
});
}
const extension = extname(pathname);
const mimeType = lookupMimeType(extension);
const content = await readFile(assetFiles[pathname]);
const headers = mimeType
? {
'Content-Type': mimeType,
}
: undefined;
assetsCache.set(pathname, { headers, content });
return new Response(content, {
headers,
});
};
global.fetch = patchedFetch;
}