summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/chatty-rivers-camp.md5
-rw-r--r--.changeset/curvy-beds-warn.md16
-rw-r--r--.changeset/stupid-wolves-explain.md7
-rw-r--r--packages/astro-prism/package.json2
-rwxr-xr-xpackages/astro/astro.js2
-rw-r--r--packages/astro/package.json4
-rw-r--r--packages/astro/src/runtime/server/escape.ts21
-rw-r--r--packages/astro/src/runtime/server/response.ts10
-rw-r--r--packages/astro/src/runtime/server/util.ts14
-rw-r--r--packages/astro/test/ssr-api-route.test.js4
-rw-r--r--packages/astro/test/streaming.test.js10
-rw-r--r--packages/astro/test/test-utils.js16
-rw-r--r--packages/create-astro/package.json2
-rw-r--r--packages/integrations/lit/server-shim.js9
-rw-r--r--packages/integrations/mdx/package.json2
-rw-r--r--packages/integrations/node/package.json4
-rw-r--r--packages/integrations/node/src/response-iterator.ts2
-rw-r--r--packages/integrations/preact/package.json2
-rw-r--r--packages/integrations/react/package.json2
-rw-r--r--packages/integrations/solid/package.json2
-rw-r--r--packages/integrations/svelte/package.json2
-rw-r--r--packages/integrations/vercel/src/serverless/entrypoint.ts11
-rw-r--r--packages/integrations/vercel/src/serverless/request-transform.ts (renamed from packages/integrations/vercel/src/serverless/request-transform/node18.ts)0
-rw-r--r--packages/integrations/vercel/src/serverless/request-transform/legacy.ts111
-rw-r--r--packages/integrations/vue/package.json2
-rw-r--r--packages/telemetry/package.json4
-rw-r--r--packages/telemetry/src/post.ts2
-rw-r--r--packages/webapi/LICENSE4
-rw-r--r--packages/webapi/README.md4
-rw-r--r--packages/webapi/package.json5
-rw-r--r--packages/webapi/run/build.js20
-rw-r--r--packages/webapi/src/lib/fetch.ts102
-rw-r--r--packages/webapi/src/ponyfill.ts9
-rw-r--r--packages/webapi/src/types.d.ts2
-rw-r--r--packages/webapi/test/fetch.js60
-rw-r--r--pnpm-lock.yaml60
36 files changed, 147 insertions, 387 deletions
diff --git a/.changeset/chatty-rivers-camp.md b/.changeset/chatty-rivers-camp.md
new file mode 100644
index 000000000..99e24e82e
--- /dev/null
+++ b/.changeset/chatty-rivers-camp.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/lit': patch
+---
+
+Only shim fetch if not already present
diff --git a/.changeset/curvy-beds-warn.md b/.changeset/curvy-beds-warn.md
new file mode 100644
index 000000000..a09fb9a81
--- /dev/null
+++ b/.changeset/curvy-beds-warn.md
@@ -0,0 +1,16 @@
+---
+'astro': major
+'@astrojs/prism': major
+'create-astro': major
+'@astrojs/mdx': minor
+'@astrojs/node': major
+'@astrojs/preact': major
+'@astrojs/react': major
+'@astrojs/solid-js': major
+'@astrojs/svelte': major
+'@astrojs/vercel': major
+'@astrojs/vue': major
+'@astrojs/telemetry': major
+---
+
+Remove support for Node 14. Minimum supported Node version is now >=16.12.0
diff --git a/.changeset/stupid-wolves-explain.md b/.changeset/stupid-wolves-explain.md
new file mode 100644
index 000000000..742e90147
--- /dev/null
+++ b/.changeset/stupid-wolves-explain.md
@@ -0,0 +1,7 @@
+---
+'@astrojs/webapi': major
+---
+
+Replace node-fetch's polyfill with undici.
+
+Since `undici` does not support it, this change also removes custom support for the `file:` protocol
diff --git a/packages/astro-prism/package.json b/packages/astro-prism/package.json
index 938ffe757..1de10b595 100644
--- a/packages/astro-prism/package.json
+++ b/packages/astro-prism/package.json
@@ -35,6 +35,6 @@
"@types/prismjs": "1.26.0"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/astro/astro.js b/packages/astro/astro.js
index 311f9cdda..b873016bc 100755
--- a/packages/astro/astro.js
+++ b/packages/astro/astro.js
@@ -50,7 +50,7 @@ async function main() {
// it's okay to hard-code the valid Node versions here since they will not change over time.
if (typeof require === 'undefined') {
console.error(`\nNode.js v${version} is not supported by Astro!
-Please upgrade to a version of Node.js with complete ESM support: "^14.18.0 || >=16.12.0"\n`);
+Please upgrade to a supported version of Node.js: ">=16.12.0"\n`);
}
// Not supported: Report the most helpful error message possible.
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 43f25171d..e572c07f5 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -199,7 +199,6 @@
"eol": "^0.9.1",
"memfs": "^3.4.7",
"mocha": "^9.2.2",
- "node-fetch": "^3.2.5",
"node-mocks-http": "^1.11.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
@@ -208,10 +207,11 @@
"rollup": "^3.9.0",
"sass": "^1.52.2",
"srcset-parse": "^1.1.0",
+ "undici": "^5.14.0",
"unified": "^10.1.2"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0",
+ "node": ">=16.12.0",
"npm": ">=6.14.0"
}
}
diff --git a/packages/astro/src/runtime/server/escape.ts b/packages/astro/src/runtime/server/escape.ts
index 48041a96b..879f1e75b 100644
--- a/packages/astro/src/runtime/server/escape.ts
+++ b/packages/astro/src/runtime/server/escape.ts
@@ -1,4 +1,5 @@
import { escape } from 'html-escaper';
+import { streamAsyncIterator } from './util.js';
// Leverage the battle-tested `html-escaper` npm package.
export const escapeHTML = escape;
@@ -58,9 +59,19 @@ export function isHTMLBytes(value: any): value is HTMLBytes {
return Object.prototype.toString.call(value) === '[object HTMLBytes]';
}
-async function* unescapeChunksAsync(iterable: AsyncIterable<Uint8Array>): any {
- for await (const chunk of iterable) {
- yield unescapeHTML(chunk as BlessedType);
+function hasGetReader(obj: unknown): obj is ReadableStream {
+ return typeof (obj as any).getReader === 'function';
+}
+
+async function* unescapeChunksAsync(iterable: ReadableStream | string): any {
+ if (hasGetReader(iterable)) {
+ for await (const chunk of streamAsyncIterator(iterable)) {
+ yield unescapeHTML(chunk as BlessedType);
+ }
+ } else {
+ for await (const chunk of iterable) {
+ yield unescapeHTML(chunk as BlessedType);
+ }
}
}
@@ -82,7 +93,7 @@ export function unescapeHTML(
}
// If a response, stream out the chunks
else if (str instanceof Response && str.body) {
- const body = str.body as unknown as AsyncIterable<Uint8Array>;
+ const body = str.body;
return unescapeChunksAsync(body);
}
// If a promise, await the result and mark that.
@@ -92,7 +103,7 @@ export function unescapeHTML(
});
} else if (Symbol.iterator in str) {
return unescapeChunks(str);
- } else if (Symbol.asyncIterator in str) {
+ } else if (Symbol.asyncIterator in str || hasGetReader(str)) {
return unescapeChunksAsync(str);
}
}
diff --git a/packages/astro/src/runtime/server/response.ts b/packages/astro/src/runtime/server/response.ts
index ae374d1aa..a39ceaa43 100644
--- a/packages/astro/src/runtime/server/response.ts
+++ b/packages/astro/src/runtime/server/response.ts
@@ -1,3 +1,5 @@
+import { streamAsyncIterator } from './util.js';
+
const isNodeJS =
typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
@@ -21,9 +23,9 @@ function createResponseClass() {
async text(): Promise<string> {
if (this.#isStream && isNodeJS) {
let decoder = new TextDecoder();
- let body = this.#body as AsyncIterable<Uint8Array>;
+ let body = this.#body;
let out = '';
- for await (let chunk of body) {
+ for await (let chunk of streamAsyncIterator(body)) {
out += decoder.decode(chunk);
}
return out;
@@ -33,10 +35,10 @@ function createResponseClass() {
async arrayBuffer(): Promise<ArrayBuffer> {
if (this.#isStream && isNodeJS) {
- let body = this.#body as AsyncIterable<Uint8Array>;
+ let body = this.#body;
let chunks: Uint8Array[] = [];
let len = 0;
- for await (let chunk of body) {
+ for await (let chunk of streamAsyncIterator(body)) {
chunks.push(chunk);
len += chunk.length;
}
diff --git a/packages/astro/src/runtime/server/util.ts b/packages/astro/src/runtime/server/util.ts
index 9f0fdbec2..b38fe5ef1 100644
--- a/packages/astro/src/runtime/server/util.ts
+++ b/packages/astro/src/runtime/server/util.ts
@@ -31,3 +31,17 @@ export function serializeListValue(value: any) {
export function isPromise<T = any>(value: any): value is Promise<T> {
return !!value && typeof value === 'object' && typeof value.then === 'function';
}
+
+export async function* streamAsyncIterator(stream: ReadableStream) {
+ const reader = stream.getReader();
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) return;
+ yield value;
+ }
+ } finally {
+ reader.releaseLock();
+ }
+}
diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js
index 33b1ffdab..cafbdf32c 100644
--- a/packages/astro/test/ssr-api-route.test.js
+++ b/packages/astro/test/ssr-api-route.test.js
@@ -1,7 +1,7 @@
import { expect } from 'chai';
-import { loadFixture } from './test-utils.js';
+import { File, FormData } from 'undici';
import testAdapter from './test-adapter.js';
-import { FormData, File } from 'node-fetch';
+import { loadFixture } from './test-utils.js';
describe('API routes in SSR', () => {
/** @type {import('./test-utils').Fixture} */
diff --git a/packages/astro/test/streaming.test.js b/packages/astro/test/streaming.test.js
index bc50c0318..47dedac22 100644
--- a/packages/astro/test/streaming.test.js
+++ b/packages/astro/test/streaming.test.js
@@ -1,7 +1,7 @@
-import { isWindows, loadFixture } from './test-utils.js';
import { expect } from 'chai';
-import testAdapter from './test-adapter.js';
import * as cheerio from 'cheerio';
+import testAdapter from './test-adapter.js';
+import { isWindows, loadFixture, streamAsyncIterator } from './test-utils.js';
describe('Streaming', () => {
if (isWindows) return;
@@ -32,7 +32,7 @@ describe('Streaming', () => {
it('Body is chunked', async () => {
let res = await fixture.fetch('/');
let chunks = [];
- for await (const bytes of res.body) {
+ for await (const bytes of streamAsyncIterator(res.body)) {
let chunk = bytes.toString('utf-8');
chunks.push(chunk);
}
@@ -61,7 +61,7 @@ describe('Streaming', () => {
const response = await app.render(request);
let chunks = [];
let decoder = new TextDecoder();
- for await (const bytes of response.body) {
+ for await (const bytes of streamAsyncIterator(response.body)) {
let chunk = decoder.decode(bytes);
chunks.push(chunk);
}
@@ -102,7 +102,7 @@ describe('Streaming disabled', () => {
it('Body is chunked', async () => {
let res = await fixture.fetch('/');
let chunks = [];
- for await (const bytes of res.body) {
+ for await (const bytes of streamAsyncIterator(res.body)) {
let chunk = bytes.toString('utf-8');
chunks.push(chunk);
}
diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js
index 63b79d763..27e4caa5e 100644
--- a/packages/astro/test/test-utils.js
+++ b/packages/astro/test/test-utils.js
@@ -19,7 +19,7 @@ polyfill(globalThis, {
});
/**
- * @typedef {import('node-fetch').Response} Response
+ * @typedef {import('undici').Response} Response
* @typedef {import('../src/core/dev/dev').DedvServer} DevServer
* @typedef {import('../src/@types/astro').AstroConfig} AstroConfig
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
@@ -303,3 +303,17 @@ export const isWindows = os.platform() === 'win32';
export function fixLineEndings(str) {
return str.replace(/\r\n/g, '\n');
}
+
+export async function* streamAsyncIterator(stream) {
+ const reader = stream.getReader();
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) return;
+ yield value;
+ }
+ } finally {
+ reader.releaseLock();
+ }
+}
diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json
index 05e2ecebf..b009db1a8 100644
--- a/packages/create-astro/package.json
+++ b/packages/create-astro/package.json
@@ -54,6 +54,6 @@
"uvu": "^0.5.3"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/lit/server-shim.js b/packages/integrations/lit/server-shim.js
index 9a4c7e408..873d3cd82 100644
--- a/packages/integrations/lit/server-shim.js
+++ b/packages/integrations/lit/server-shim.js
@@ -1,5 +1,12 @@
import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js';
-installWindowOnGlobal();
+
+if(typeof fetch === 'function') {
+ const _fetch = fetch;
+ installWindowOnGlobal();
+ globalThis.fetch = window.fetch = _fetch;
+} else {
+ installWindowOnGlobal();
+}
window.global = window;
document.getElementsByTagName = () => [];
diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json
index d9e138893..8ca5c9bfd 100644
--- a/packages/integrations/mdx/package.json
+++ b/packages/integrations/mdx/package.json
@@ -71,6 +71,6 @@
"vite": "^4.0.3"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json
index 3e3bd2258..779b8792b 100644
--- a/packages/integrations/node/package.json
+++ b/packages/integrations/node/package.json
@@ -37,12 +37,12 @@
"astro": "workspace:^2.0.0-beta.0"
},
"devDependencies": {
- "@types/node-fetch": "^2.6.2",
"@types/send": "^0.17.1",
"astro": "workspace:*",
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
"mocha": "^9.2.2",
- "node-mocks-http": "^1.11.0"
+ "node-mocks-http": "^1.11.0",
+ "undici": "^5.14.0"
}
}
diff --git a/packages/integrations/node/src/response-iterator.ts b/packages/integrations/node/src/response-iterator.ts
index 7700e9331..becd8be1b 100644
--- a/packages/integrations/node/src/response-iterator.ts
+++ b/packages/integrations/node/src/response-iterator.ts
@@ -4,7 +4,7 @@
* - https://github.com/apollographql/apollo-client/blob/main/src/utilities/common/responseIterator.ts
*/
-import type { Response as NodeResponse } from 'node-fetch';
+import type { Response as NodeResponse } from 'undici';
import { Readable as NodeReadableStream } from 'stream';
interface NodeStreamIterator<T> {
diff --git a/packages/integrations/preact/package.json b/packages/integrations/preact/package.json
index 609321616..acb94c320 100644
--- a/packages/integrations/preact/package.json
+++ b/packages/integrations/preact/package.json
@@ -47,6 +47,6 @@
"preact": "^10.6.5"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/react/package.json b/packages/integrations/react/package.json
index 677d0d24a..4bc1c1afa 100644
--- a/packages/integrations/react/package.json
+++ b/packages/integrations/react/package.json
@@ -52,6 +52,6 @@
"@types/react-dom": "^17.0.17 || ^18.0.6"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/solid/package.json b/packages/integrations/solid/package.json
index 94f654e82..334b0fb81 100644
--- a/packages/integrations/solid/package.json
+++ b/packages/integrations/solid/package.json
@@ -44,6 +44,6 @@
"solid-js": "^1.4.3"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/svelte/package.json b/packages/integrations/svelte/package.json
index 6a073d4b8..1ad7abcc5 100644
--- a/packages/integrations/svelte/package.json
+++ b/packages/integrations/svelte/package.json
@@ -47,6 +47,6 @@
"astro": "workspace:^2.0.0-beta.0"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts
index daa811015..71ad2bfae 100644
--- a/packages/integrations/vercel/src/serverless/entrypoint.ts
+++ b/packages/integrations/vercel/src/serverless/entrypoint.ts
@@ -3,21 +3,12 @@ import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import type { IncomingMessage, ServerResponse } from 'node:http';
-import * as requestTransformLegacy from './request-transform/legacy.js';
-import * as requestTransformNode18 from './request-transform/node18.js';
+import { getRequest, setResponse } from './request-transform';
polyfill(globalThis, {
exclude: 'window document',
});
-// Node 18+ has a new API for request/response, while older versions use node-fetch
-// When we drop support for Node 14, we can remove the legacy code by switching to undici
-
-const nodeVersion = parseInt(process.version.split('.')[0].slice(1)); // 'v14.17.0' -> 14
-
-const { getRequest, setResponse } =
- nodeVersion >= 18 ? requestTransformNode18 : requestTransformLegacy;
-
export const createExports = (manifest: SSRManifest) => {
const app = new App(manifest);
diff --git a/packages/integrations/vercel/src/serverless/request-transform/node18.ts b/packages/integrations/vercel/src/serverless/request-transform.ts
index a42bac1f5..a42bac1f5 100644
--- a/packages/integrations/vercel/src/serverless/request-transform/node18.ts
+++ b/packages/integrations/vercel/src/serverless/request-transform.ts
diff --git a/packages/integrations/vercel/src/serverless/request-transform/legacy.ts b/packages/integrations/vercel/src/serverless/request-transform/legacy.ts
deleted file mode 100644
index 7212431c7..000000000
--- a/packages/integrations/vercel/src/serverless/request-transform/legacy.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import type { App } from 'astro/app';
-import type { IncomingMessage, ServerResponse } from 'node:http';
-import { Readable } from 'node:stream';
-
-const clientAddressSymbol = Symbol.for('astro.clientAddress');
-
-/*
- Credits to the SvelteKit team
- https://github.com/sveltejs/kit/blob/69913e9fda054fa6a62a80e2bb4ee7dca1005796/packages/kit/src/node.js
-*/
-
-function get_raw_body(req: IncomingMessage) {
- return new Promise<Uint8Array | null>((fulfil, reject) => {
- const h = req.headers;
-
- if (!h['content-type']) {
- return fulfil(null);
- }
-
- req.on('error', reject);
-
- const length = Number(h['content-length']);
-
- // https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95
- if (isNaN(length) && h['transfer-encoding'] == null) {
- return fulfil(null);
- }
-
- let data = new Uint8Array(length || 0);
-
- if (length > 0) {
- let offset = 0;
- req.on('data', (chunk) => {
- const new_len = offset + Buffer.byteLength(chunk);
-
- if (new_len > length) {
- return reject({
- status: 413,
- reason: 'Exceeded "Content-Length" limit',
- });
- }
-
- data.set(chunk, offset);
- offset = new_len;
- });
- } else {
- req.on('data', (chunk) => {
- const new_data = new Uint8Array(data.length + chunk.length);
- new_data.set(data, 0);
- new_data.set(chunk, data.length);
- data = new_data;
- });
- }
-
- req.on('end', () => {
- fulfil(data);
- });
- });
-}
-
-export async function getRequest(base: string, req: IncomingMessage): Promise<Request> {
- let headers = req.headers as Record<string, string>;
- if (req.httpVersionMajor === 2) {
- // we need to strip out the HTTP/2 pseudo-headers because node-fetch's
- // Request implementation doesn't like them
- headers = Object.assign({}, headers);
- delete headers[':method'];
- delete headers[':path'];
- delete headers[':authority'];
- delete headers[':scheme'];
- }
- const request = new Request(base + req.url, {
- method: req.method,
- headers,
- body: await get_raw_body(req), // TODO stream rather than buffer
- });
- Reflect.set(request, clientAddressSymbol, headers['x-forwarded-for']);
- return request;
-}
-
-export async function setResponse(
- app: App,
- res: ServerResponse,
- response: Response
-): Promise<void> {
- const headers = Object.fromEntries(response.headers);
-
- if (response.headers.has('set-cookie')) {
- // @ts-expect-error (headers.raw() is non-standard)
- headers['set-cookie'] = response.headers.raw()['set-cookie'];
- }
-
- if (app.setCookieHeaders) {
- const setCookieHeaders: Array<string> = Array.from(app.setCookieHeaders(response));
- if (setCookieHeaders.length) {
- res.setHeader('Set-Cookie', setCookieHeaders);
- }
- }
-
- res.writeHead(response.status, headers);
-
- if (response.body instanceof Readable) {
- response.body.pipe(res);
- } else {
- if (response.body) {
- res.write(await response.arrayBuffer());
- }
-
- res.end();
- }
-}
diff --git a/packages/integrations/vue/package.json b/packages/integrations/vue/package.json
index c0655a85d..a8b9b8fd8 100644
--- a/packages/integrations/vue/package.json
+++ b/packages/integrations/vue/package.json
@@ -54,6 +54,6 @@
"astro": "workspace:^2.0.0-beta.0"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json
index bf45c1560..32bd2a1d1 100644
--- a/packages/telemetry/package.json
+++ b/packages/telemetry/package.json
@@ -32,7 +32,7 @@
"dset": "^3.1.2",
"is-docker": "^3.0.0",
"is-wsl": "^2.2.0",
- "node-fetch": "^3.2.5",
+ "undici": "^5.14.0",
"which-pm-runs": "^1.1.0"
},
"devDependencies": {
@@ -45,6 +45,6 @@
"mocha": "^9.2.2"
},
"engines": {
- "node": "^14.18.0 || >=16.12.0"
+ "node": ">=16.12.0"
}
}
diff --git a/packages/telemetry/src/post.ts b/packages/telemetry/src/post.ts
index a0647075f..4ce227388 100644
--- a/packages/telemetry/src/post.ts
+++ b/packages/telemetry/src/post.ts
@@ -1,4 +1,4 @@
-import fetch from 'node-fetch';
+import { fetch } from 'undici';
const ASTRO_TELEMETRY_ENDPOINT = `https://telemetry.astro.build/api/v1/record`;
diff --git a/packages/webapi/LICENSE b/packages/webapi/LICENSE
index 9dda027eb..7dc74ec38 100644
--- a/packages/webapi/LICENSE
+++ b/packages/webapi/LICENSE
@@ -31,7 +31,3 @@ Code from [event-target-shim](https://www.npmjs.com/package/event-target-shim) i
Code from [fetch-blob](https://www.npmjs.com/package/fetch-blob) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [formdata-polyfill](https://www.npmjs.com/package/formdata-polyfill) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
-
-Code from [node-fetch](https://www.npmjs.com/package/node-fetch) is licensed under the MIT License (MIT), Copyright Node Fetch Team.
-
-Code from [web-streams-polyfill](https://www.npmjs.com/package/web-streams-polyfill) is licensed under the MIT License (MIT), Copyright Mattias Buelens and Diwank Singh Tomer.
diff --git a/packages/webapi/README.md b/packages/webapi/README.md
index 8e1c66280..2f7726e9a 100644
--- a/packages/webapi/README.md
+++ b/packages/webapi/README.md
@@ -173,7 +173,3 @@ Code from [event-target-shim](https://www.npmjs.com/package/event-target-shim) i
Code from [fetch-blob](https://www.npmjs.com/package/fetch-blob) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [formdata-polyfill](https://www.npmjs.com/package/formdata-polyfill) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
-
-Code from [node-fetch](https://www.npmjs.com/package/node-fetch) is licensed under the MIT License (MIT), Copyright Node Fetch Team.
-
-Code from [web-streams-polyfill](https://www.npmjs.com/package/web-streams-polyfill) is licensed under the MIT License (MIT), Copyright Mattias Buelens and Diwank Singh Tomer.
diff --git a/packages/webapi/package.json b/packages/webapi/package.json
index f4e417b47..e712e541d 100644
--- a/packages/webapi/package.json
+++ b/packages/webapi/package.json
@@ -51,7 +51,7 @@
"homepage": "https://github.com/withastro/astro/tree/main/packages/webapi#readme",
"dependencies": {
"global-agent": "^3.0.0",
- "node-fetch": "^3.2.5"
+ "undici": "^5.14.0"
},
"devDependencies": {
"@rollup/plugin-alias": "^3.1.9",
@@ -74,8 +74,7 @@
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.4.0",
"typescript": "~4.7.3",
- "urlpattern-polyfill": "^1.0.0-rc5",
- "web-streams-polyfill": "^3.2.1"
+ "urlpattern-polyfill": "^1.0.0-rc5"
},
"scripts": {
"build": "node run/build.js",
diff --git a/packages/webapi/run/build.js b/packages/webapi/run/build.js
index 63e17e84c..154d6ffbd 100644
--- a/packages/webapi/run/build.js
+++ b/packages/webapi/run/build.js
@@ -1,17 +1,17 @@
-import { rollup } from 'rollup'
+import { default as alias } from '@rollup/plugin-alias'
+import { default as inject } from '@rollup/plugin-inject'
import { nodeResolve } from '@rollup/plugin-node-resolve'
-import path from 'node:path'
-import { createRequire } from 'node:module'
+import { default as typescript } from '@rollup/plugin-typescript'
+import { default as MagicString } from 'magic-string'
import {
readFile as nodeReadFile,
rename,
rm,
writeFile,
} from 'node:fs/promises'
-import { default as MagicString } from 'magic-string'
-import { default as alias } from '@rollup/plugin-alias'
-import { default as inject } from '@rollup/plugin-inject'
-import { default as typescript } from '@rollup/plugin-typescript'
+import { createRequire } from 'node:module'
+import path from 'node:path'
+import { rollup } from 'rollup'
const readFileCache = Object.create(null)
const require = createRequire(import.meta.url)
@@ -76,13 +76,13 @@ const plugins = [
MediaQueryList: ['./MediaQueryList', 'MediaQueryList'],
Node: ['./Node', 'Node'],
ReadableStream: [
- 'web-streams-polyfill/dist/ponyfill.es6.mjs',
+ 'node:stream/web',
'ReadableStream',
],
ShadowRoot: ['./Node', 'ShadowRoot'],
Window: ['./Window', 'Window'],
'globalThis.ReadableStream': [
- 'web-streams-polyfill/dist/ponyfill.es6.mjs',
+ 'node:stream/web',
'ReadableStream',
],
}),
@@ -178,7 +178,7 @@ async function build() {
inputOptions: {
input: 'src/polyfill.ts',
plugins: plugins,
- external: ['node-fetch', 'global-agent'],
+ external: ['undici', 'global-agent'],
onwarn(warning, warn) {
if (warning.code !== 'UNRESOLVED_IMPORT') warn(warning)
},
diff --git a/packages/webapi/src/lib/fetch.ts b/packages/webapi/src/lib/fetch.ts
deleted file mode 100644
index f8500a846..000000000
--- a/packages/webapi/src/lib/fetch.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { bootstrap as bootstrapGlobalAgent } from 'global-agent'
-import type { RequestInit } from 'node-fetch'
-import { default as nodeFetch, Headers, Request, Response } from 'node-fetch'
-import Stream from 'node:stream'
-import * as _ from './utils'
-
-bootstrapGlobalAgent({
- environmentVariableNamespace: '',
-})
-
-export { Headers, Request, Response }
-
-export const fetch = {
- fetch(
- resource: string | Request,
- init?: Partial<FetchInit>
- ): Promise<Response> {
- const resourceURL = new URL(
- _.__object_isPrototypeOf(Request.prototype, resource)
- ? (resource as Request).url
- : _.pathToPosix(resource),
- typeof Object(globalThis.process).cwd === 'function'
- ? 'file:' + _.pathToPosix(process.cwd()) + '/'
- : 'file:'
- )
-
- if (resourceURL.protocol.toLowerCase() === 'file:') {
- return import('node:fs').then((fs) => {
- try {
- const stats = fs.statSync(resourceURL)
- const body = fs.createReadStream(resourceURL)
-
- return new Response(body, {
- status: 200,
- statusText: '',
- headers: {
- 'content-length': String(stats.size),
- date: new Date().toUTCString(),
- 'last-modified': new Date(stats.mtimeMs).toUTCString(),
- },
- })
- } catch (error) {
- const body = new Stream.Readable()
-
- body._read = () => {}
- body.push(null)
-
- return new Response(body, {
- status: 404,
- statusText: '',
- headers: {
- date: new Date().toUTCString(),
- },
- })
- }
- })
- } else {
- return nodeFetch(resource, init)
- }
- },
-}.fetch
-
-type USVString = {} & string
-
-interface FetchInit {
- body: RequestInit['body']
- cache:
- | 'default'
- | 'no-store'
- | 'reload'
- | 'no-cache'
- | 'force-cache'
- | 'only-if-cached'
- credentials: 'omit' | 'same-origin' | 'include'
- headers: Headers | Record<string, string>
- method:
- | 'GET'
- | 'HEAD'
- | 'POST'
- | 'PUT'
- | 'DELETE'
- | 'CONNECT'
- | 'OPTIONS'
- | 'TRACE'
- | 'PATCH'
- | USVString
- mode: 'cors' | 'no-cors' | 'same-origin' | USVString
- redirect: 'follow' | 'manual' | 'error'
- referrer: USVString
- referrerPolicy:
- | 'no-referrer'
- | 'no-referrer-when-downgrade'
- | 'same-origin'
- | 'origin'
- | 'strict-origin'
- | 'origin-when-cross-origin'
- | 'strict-origin-when-cross-origin'
- | 'unsafe-url'
- integrity: USVString
- keepalive: boolean
- signal: AbortSignal
-}
diff --git a/packages/webapi/src/ponyfill.ts b/packages/webapi/src/ponyfill.ts
index fc92975b5..a1088bdb4 100644
--- a/packages/webapi/src/ponyfill.ts
+++ b/packages/webapi/src/ponyfill.ts
@@ -7,6 +7,7 @@ import {
import { Event, EventTarget } from 'event-target-shim'
import { Blob, File } from 'fetch-blob/from.js'
import { FormData } from 'formdata-polyfill/esm.min.js'
+import * as undici from 'undici'
import { URLPattern } from 'urlpattern-polyfill'
import {
ByteLengthQueuingStrategy,
@@ -21,7 +22,7 @@ import {
WritableStream,
WritableStreamDefaultController,
WritableStreamDefaultWriter,
-} from 'web-streams-polyfill/dist/ponyfill.es6.mjs'
+} from 'node:stream/web'
import {
cancelAnimationFrame,
requestAnimationFrame,
@@ -30,7 +31,6 @@ import { atob, btoa } from './lib/Base64'
import { CharacterData, Comment, Text } from './lib/CharacterData'
import { CustomEvent } from './lib/CustomEvent'
import { DOMException } from './lib/DOMException'
-import { fetch, Headers, Request, Response } from './lib/fetch'
import { cancelIdleCallback, requestIdleCallback } from './lib/IdleCallback'
import structuredClone from './lib/structuredClone'
import { clearTimeout, setTimeout } from './lib/Timeout'
@@ -83,6 +83,11 @@ import { initPromise } from './lib/Promise'
import { initRelativeIndexingMethod } from './lib/RelativeIndexingMethod'
import { initString } from './lib/String'
+const fetch = undici.fetch
+const Headers = undici.Headers
+const Response = undici.Response
+const Request = undici.Request
+
export {
AbortController,
AbortSignal,
diff --git a/packages/webapi/src/types.d.ts b/packages/webapi/src/types.d.ts
index 09c57387b..2597566ee 100644
--- a/packages/webapi/src/types.d.ts
+++ b/packages/webapi/src/types.d.ts
@@ -3,5 +3,3 @@ declare module '@ungap/structured-clone/esm/index.js'
declare module '@ungap/structured-clone/esm/deserialize.js'
declare module '@ungap/structured-clone/esm/serialize.js'
declare module 'abort-controller/dist/abort-controller.mjs'
-declare module 'node-fetch/src/index.js'
-declare module 'web-streams-polyfill/dist/ponyfill.es6.mjs'
diff --git a/packages/webapi/test/fetch.js b/packages/webapi/test/fetch.js
index ae5ae0386..49aab31dc 100644
--- a/packages/webapi/test/fetch.js
+++ b/packages/webapi/test/fetch.js
@@ -22,66 +22,6 @@ describe('Fetch', () => {
expect(json).to.be.an('array')
})
- it('Fetch with file', async () => {
- const { fetch } = target
-
- const url = new URL('../package.json', import.meta.url)
-
- const response = await fetch(url)
-
- expect(response.constructor).to.equal(target.Response)
-
- expect(response.status).to.equal(200)
- expect(response.statusText).to.be.empty
- expect(response.headers.has('date')).to.equal(true)
- expect(response.headers.has('content-length')).to.equal(true)
- expect(response.headers.has('last-modified')).to.equal(true)
-
- const json = await response.json()
-
- expect(json.name).to.equal('@astrojs/webapi')
- })
-
- it('Fetch with missing file', async () => {
- const { fetch } = target
-
- const url = new URL('../missing.json', import.meta.url)
-
- const response = await fetch(url)
-
- expect(response.constructor).to.equal(target.Response)
-
- expect(response.status).to.equal(404)
- expect(response.statusText).to.be.empty
- expect(response.headers.has('date')).to.equal(true)
- expect(response.headers.has('content-length')).to.equal(false)
- expect(response.headers.has('last-modified')).to.equal(false)
- })
-
- it('Fetch with (file) Request', async () => {
- const { Request, fetch } = target
-
- const request = new Request(new URL('../package.json', import.meta.url))
-
- const response = await fetch(request)
-
- expect(response.constructor).to.equal(target.Response)
-
- const json = await response.json()
-
- expect(json.name).to.equal('@astrojs/webapi')
- })
-
- it('Fetch with relative file', async () => {
- const { fetch } = target
-
- const response = await fetch('package.json')
-
- const json = await response.json()
-
- expect(json.name).to.equal('@astrojs/webapi')
- })
-
it('Fetch with data', async () => {
const { fetch } = target
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6434adaf6..6de33571e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -448,7 +448,6 @@ importers:
memfs: ^3.4.7
mime: ^3.0.0
mocha: ^9.2.2
- node-fetch: ^3.2.5
node-mocks-http: ^1.11.0
ora: ^6.1.0
path-browserify: ^1.0.1
@@ -476,6 +475,7 @@ importers:
supports-esm: ^1.0.0
tsconfig-resolver: ^3.0.1
typescript: '*'
+ undici: ^5.14.0
unified: ^10.1.2
unist-util-visit: ^4.1.0
vfile: ^5.3.2
@@ -573,7 +573,6 @@ importers:
eol: 0.9.1
memfs: 3.4.13
mocha: 9.2.2
- node-fetch: 3.3.0
node-mocks-http: 1.12.1
rehype-autolink-headings: 6.1.1
rehype-slug: 5.1.0
@@ -582,6 +581,7 @@ importers:
rollup: 3.9.1
sass: 1.57.1
srcset-parse: 1.1.0
+ undici: 5.14.0
unified: 10.1.2
packages/astro-prism:
@@ -3077,7 +3077,6 @@ importers:
packages/integrations/node:
specifiers:
'@astrojs/webapi': ^1.1.1
- '@types/node-fetch': ^2.6.2
'@types/send': ^0.17.1
astro: workspace:*
astro-scripts: workspace:*
@@ -3085,17 +3084,18 @@ importers:
mocha: ^9.2.2
node-mocks-http: ^1.11.0
send: ^0.18.0
+ undici: ^5.14.0
dependencies:
'@astrojs/webapi': link:../../webapi
send: 0.18.0
devDependencies:
- '@types/node-fetch': 2.6.2
'@types/send': 0.17.1
astro: link:../../astro
astro-scripts: link:../../../scripts
chai: 4.3.7
mocha: 9.2.2
node-mocks-http: 1.12.1
+ undici: 5.14.0
packages/integrations/node/test/fixtures/api-route:
specifiers:
@@ -3536,7 +3536,7 @@ importers:
is-docker: ^3.0.0
is-wsl: ^2.2.0
mocha: ^9.2.2
- node-fetch: ^3.2.5
+ undici: ^5.14.0
which-pm-runs: ^1.1.0
dependencies:
ci-info: 3.7.1
@@ -3545,7 +3545,7 @@ importers:
dset: 3.1.2
is-docker: 3.0.0
is-wsl: 2.2.0
- node-fetch: 3.3.0
+ undici: 5.14.0
which-pm-runs: 1.1.0
devDependencies:
'@types/debug': 4.1.7
@@ -3575,16 +3575,15 @@ importers:
global-agent: ^3.0.0
magic-string: ^0.25.9
mocha: ^9.2.2
- node-fetch: ^3.2.5
rollup: ^2.79.1
rollup-plugin-terser: ^7.0.2
tslib: ^2.4.0
typescript: ~4.7.3
+ undici: ^5.14.0
urlpattern-polyfill: ^1.0.0-rc5
- web-streams-polyfill: ^3.2.1
dependencies:
global-agent: 3.0.0
- node-fetch: 3.3.0
+ undici: 5.14.0
devDependencies:
'@rollup/plugin-alias': 3.1.9_rollup@2.79.1
'@rollup/plugin-inject': 4.0.4_rollup@2.79.1
@@ -3607,7 +3606,6 @@ importers:
tslib: 2.4.1
typescript: 4.7.4
urlpattern-polyfill: 1.0.0-rc5
- web-streams-polyfill: 3.2.1
scripts:
specifiers:
@@ -7044,13 +7042,6 @@ packages:
'@types/unist': 2.0.6
dev: false
- /@types/node-fetch/2.6.2:
- resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
- dependencies:
- '@types/node': 18.11.18
- form-data: 3.0.1
- dev: true
-
/@types/node/12.20.55:
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
dev: true
@@ -8007,10 +7998,6 @@ packages:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
dev: false
- /asynckit/0.4.0:
- resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
- dev: true
-
/at-least-node/1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
@@ -8272,7 +8259,6 @@ packages:
engines: {node: '>=10.16.0'}
dependencies:
streamsearch: 1.1.0
- dev: true
/bytes/3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
@@ -8559,13 +8545,6 @@ packages:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: false
- /combined-stream/1.0.8:
- resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
- engines: {node: '>= 0.8'}
- dependencies:
- delayed-stream: 1.0.0
- dev: true
-
/comma-separated-tokens/2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
dev: false
@@ -8775,6 +8754,7 @@ packages:
/data-uri-to-buffer/4.0.0:
resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
engines: {node: '>= 12'}
+ dev: false
/dataloader/1.4.0:
resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
@@ -8927,11 +8907,6 @@ packages:
slash: 4.0.0
dev: true
- /delayed-stream/1.0.0:
- resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
- engines: {node: '>=0.4.0'}
- dev: true
-
/delegates/1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
dev: false
@@ -10094,15 +10069,6 @@ packages:
dependencies:
is-callable: 1.2.7
- /form-data/3.0.1:
- resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
- engines: {node: '>= 6'}
- dependencies:
- asynckit: 0.4.0
- combined-stream: 1.0.8
- mime-types: 2.1.35
- dev: true
-
/format/0.2.2:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
engines: {node: '>=0.4.x'}
@@ -12347,6 +12313,7 @@ packages:
data-uri-to-buffer: 4.0.0
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
+ dev: false
/node-forge/1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
@@ -14297,7 +14264,6 @@ packages:
/streamsearch/1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
- dev: true
/string-width/4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
@@ -14978,6 +14944,12 @@ packages:
jiti: 1.16.1
dev: false
+ /undici/5.14.0:
+ resolution: {integrity: sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==}
+ engines: {node: '>=12.18'}
+ dependencies:
+ busboy: 1.6.0
+
/undici/5.9.1:
resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==}
engines: {node: '>=12.18'}