diff --git a/.changeset/young-guests-tap.md b/.changeset/young-guests-tap.md
new file mode 100644
index 00000000..9ff143c7
--- /dev/null
+++ b/.changeset/young-guests-tap.md
@@ -0,0 +1,5 @@
+---
+'wmr': minor
+---
+
+Add fetching external resources using native fetch API when Node.js version 18 later
diff --git a/packages/wmr/src/lib/prerender.js b/packages/wmr/src/lib/prerender.js
index 9f5297ca..ad282919 100644
--- a/packages/wmr/src/lib/prerender.js
+++ b/packages/wmr/src/lib/prerender.js
@@ -96,11 +96,43 @@ async function workerCode({ cwd, out, publicPath, customRoutes }) {
let head = { lang: '', title: '', elements: new Set() };
globalThis.wmr = { ssr: { head } };
- // @ts-ignore
- globalThis.fetch = async url => {
+ async function localFetcher(url) {
const text = () => fs.readFile(`${out}/${String(url).replace(/^\//, '')}`, 'utf-8');
return { text, json: () => text().then(JSON.parse) };
- };
+ }
+
+ const pattern = /^\//;
+ if (globalThis.fetch === undefined) {
+ // When Node.js version under 17, native fetch API undefined
+ // So it will give a warning when the syntax to retrieve the file and the url is passed
+ // @ts-ignore
+ globalThis.fetch = async url => {
+ if (pattern.test(String(url))) {
+ return localFetcher(url);
+ }
+
+ console.warn(`fetch is not defined in Node.js version under 17, please upgrade to Node.js version 18 later`);
+ };
+ } else {
+ // At that time, implement using reassignment to avoid entering an infinite loop.
+ const fetcher = fetch;
+ // @ts-ignore
+ delete globalThis.fetch;
+
+ // When Node.js version 18 later, native fetch API is defined
+ // Override with own implementation if you want to retrieve local files in Node.js 18 later
+ // ref https://github.com/preactjs/wmr/pull/935#discussion_r977168334
+ // @ts-ignore
+ globalThis.fetch = async (url, options) => {
+ if (pattern.test(String(url))) {
+ return localFetcher(url);
+ }
+
+ // When fetching an external resource, it returns the native fetch API
+ // though `fetcher` function is an alias created to avoid infinite loops
+ return fetcher(url, options);
+ };
+ }
// Prevent Rollup from transforming `import()` here.
const $import = new Function('s', 'return import(s)');
diff --git a/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.html b/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.html
new file mode 100644
index 00000000..d2c9fb7d
--- /dev/null
+++ b/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ default title
+
+
+
+
+
diff --git a/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.js b/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.js
new file mode 100644
index 00000000..e6a8f375
--- /dev/null
+++ b/packages/wmr/test/fixtures/prerender-external-resource-fetch/index.js
@@ -0,0 +1,4 @@
+export async function prerender() {
+ const html = await fetch('https://preactjs.com').then(res => res.text());
+ return { html, links: ['/'] };
+}
diff --git a/packages/wmr/test/fixtures/prerender-resource-fetch/index.js b/packages/wmr/test/fixtures/prerender-resource-fetch/index.js
index f0ec977f..765a62ea 100644
--- a/packages/wmr/test/fixtures/prerender-resource-fetch/index.js
+++ b/packages/wmr/test/fixtures/prerender-resource-fetch/index.js
@@ -1,4 +1,4 @@
export async function prerender() {
- const md = await fetch('content.md').then(res => res.text());
+ const md = await fetch('/content.md').then(res => res.text());
return { html: md, links: ['/'] };
}
diff --git a/packages/wmr/test/production.test.js b/packages/wmr/test/production.test.js
index 417bd157..6588a1a7 100644
--- a/packages/wmr/test/production.test.js
+++ b/packages/wmr/test/production.test.js
@@ -891,6 +891,40 @@ describe('production', () => {
const index = await fs.readFile(indexHtml, 'utf8');
expect(index).toMatch(/# hello world/);
});
+
+ // Whether or not to run the test to fetch external resources depends on whether or not a Node.js version 18 later
+ const isNodeVersionUnder18 = Number(process.version.split('.')[0].slice(1)) < 18;
+ // 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
+ // if (globalThis.fetch === undefined)
+ if (isNodeVersionUnder18) {
+ it.skip('skip should support fetching resources from external links during prerender on Node.js v18 later', () => {});
+ it('should not support fetching resources from external links during prerender on Node.js < v18', async () => {
+ await loadFixture('prerender-external-resource-fetch', env);
+ instance = await runWmr(env.tmp.path, 'build', '--prerender');
+ const code = await instance.done;
+
+ await withLog(instance.output, async () => {
+ expect(code).toBe(1);
+ expect(instance.output.join('\n')).toMatch(
+ /fetch is not defined in Node.js version under 17, please upgrade to Node.js version 18 later/
+ );
+ });
+ });
+ } else {
+ it.skip('skip should not support fetching resources from external links during prerender on Node.js < v18', () => {});
+ it('should support fetching resources from external links during prerender on Node.js v18 later', async () => {
+ await loadFixture('prerender-external-resource-fetch', env);
+ instance = await runWmr(env.tmp.path, 'build', '--prerender');
+ const code = await instance.done;
+
+ // when workflow of wmr runtime uses Node.js 18
+ // Until then check locally.
+ expect(code).toBe(0);
+ const indexHtml = path.join(env.tmp.path, 'dist', 'index.html');
+ const index = await fs.readFile(indexHtml, 'utf8');
+ expect(index).toMatch(/Preact/);
+ });
+ }
});
describe('Code Splitting', () => {