Skip to content

Commit e546afc

Browse files
authored
Feat proposal enable fetching external resources using native fetch API in Node.js version 18 later (#935)
1 parent 0750d07 commit e546afc

File tree

6 files changed

+89
-4
lines changed

6 files changed

+89
-4
lines changed

.changeset/young-guests-tap.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'wmr': minor
3+
---
4+
5+
Add fetching external resources using native fetch API when Node.js version 18 later

packages/wmr/src/lib/prerender.js

+35-3
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,43 @@ async function workerCode({ cwd, out, publicPath, customRoutes }) {
9696
let head = { lang: '', title: '', elements: new Set() };
9797
globalThis.wmr = { ssr: { head } };
9898

99-
// @ts-ignore
100-
globalThis.fetch = async url => {
99+
async function localFetcher(url) {
101100
const text = () => fs.readFile(`${out}/${String(url).replace(/^\//, '')}`, 'utf-8');
102101
return { text, json: () => text().then(JSON.parse) };
103-
};
102+
}
103+
104+
const pattern = /^\//;
105+
if (globalThis.fetch === undefined) {
106+
// When Node.js version under 17, native fetch API undefined
107+
// So it will give a warning when the syntax to retrieve the file and the url is passed
108+
// @ts-ignore
109+
globalThis.fetch = async url => {
110+
if (pattern.test(String(url))) {
111+
return localFetcher(url);
112+
}
113+
114+
console.warn(`fetch is not defined in Node.js version under 17, please upgrade to Node.js version 18 later`);
115+
};
116+
} else {
117+
// At that time, implement using reassignment to avoid entering an infinite loop.
118+
const fetcher = fetch;
119+
// @ts-ignore
120+
delete globalThis.fetch;
121+
122+
// When Node.js version 18 later, native fetch API is defined
123+
// Override with own implementation if you want to retrieve local files in Node.js 18 later
124+
// ref https://github.com/preactjs/wmr/pull/935#discussion_r977168334
125+
// @ts-ignore
126+
globalThis.fetch = async (url, options) => {
127+
if (pattern.test(String(url))) {
128+
return localFetcher(url);
129+
}
130+
131+
// When fetching an external resource, it returns the native fetch API
132+
// though `fetcher` function is an alias created to avoid infinite loops
133+
return fetcher(url, options);
134+
};
135+
}
104136

105137
// Prevent Rollup from transforming `import()` here.
106138
const $import = new Function('s', 'return import(s)');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf8" />
5+
<title>default title</title>
6+
</head>
7+
<body>
8+
<script src="./index.js" type="module"></script>
9+
</body>
10+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export async function prerender() {
2+
const html = await fetch('https://preactjs.com').then(res => res.text());
3+
return { html, links: ['/'] };
4+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export async function prerender() {
2-
const md = await fetch('content.md').then(res => res.text());
2+
const md = await fetch('/content.md').then(res => res.text());
33
return { html: md, links: ['/'] };
44
}

packages/wmr/test/production.test.js

+34
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,40 @@ describe('production', () => {
891891
const index = await fs.readFile(indexHtml, 'utf8');
892892
expect(index).toMatch(/# hello world/);
893893
});
894+
895+
// Whether or not to run the test to fetch external resources depends on whether or not a Node.js version 18 later
896+
const isNodeVersionUnder18 = Number(process.version.split('.')[0].slice(1)) < 18;
897+
// Even if you are using Node.js v18 or higher, you will get the error `globalThis.fetch is undefined` here, so get the version from process.version
898+
// if (globalThis.fetch === undefined)
899+
if (isNodeVersionUnder18) {
900+
it.skip('skip should support fetching resources from external links during prerender on Node.js v18 later', () => {});
901+
it('should not support fetching resources from external links during prerender on Node.js < v18', async () => {
902+
await loadFixture('prerender-external-resource-fetch', env);
903+
instance = await runWmr(env.tmp.path, 'build', '--prerender');
904+
const code = await instance.done;
905+
906+
await withLog(instance.output, async () => {
907+
expect(code).toBe(1);
908+
expect(instance.output.join('\n')).toMatch(
909+
/fetch is not defined in Node.js version under 17, please upgrade to Node.js version 18 later/
910+
);
911+
});
912+
});
913+
} else {
914+
it.skip('skip should not support fetching resources from external links during prerender on Node.js < v18', () => {});
915+
it('should support fetching resources from external links during prerender on Node.js v18 later', async () => {
916+
await loadFixture('prerender-external-resource-fetch', env);
917+
instance = await runWmr(env.tmp.path, 'build', '--prerender');
918+
const code = await instance.done;
919+
920+
// when workflow of wmr runtime uses Node.js 18
921+
// Until then check locally.
922+
expect(code).toBe(0);
923+
const indexHtml = path.join(env.tmp.path, 'dist', 'index.html');
924+
const index = await fs.readFile(indexHtml, 'utf8');
925+
expect(index).toMatch(/Preact/);
926+
});
927+
}
894928
});
895929

896930
describe('Code Splitting', () => {

0 commit comments

Comments
 (0)