Drop Node 14 support (#5782)
* chore: Update engines field * fix(deps): Remove node-fetch * feat(polyfills): Remove node-fetch for undici * feat(webapi): Remove node-fetch from the webapis polyfills for undici * feat(core): Remove node-fetch for undici in Astro core * feat(telemetry): Remove node-fetch for undici * feat(node): Remove node-fetch for undici in node integration * feat(vercel): Remove node-fetch for undici in Vercel integration * chore: update lockfile * chore: update lockfile * chore: changeset * fix(set): Fix set directives not streaming correctly on Node 16 * Try another approach * Debugging * Debug fetch * Use global fetch if there is one * changeset for lit * Remove web-streams-polyfill * Remove web-streams-polyfill license note * Update .changeset/stupid-wolves-explain.md Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com> Co-authored-by: Matthew Phillips <matthew@skypack.dev> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
parent
9bb08bfe8c
commit
1f92d64ea3
36 changed files with 147 additions and 387 deletions
5
.changeset/chatty-rivers-camp.md
Normal file
5
.changeset/chatty-rivers-camp.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/lit': patch
|
||||
---
|
||||
|
||||
Only shim fetch if not already present
|
16
.changeset/curvy-beds-warn.md
Normal file
16
.changeset/curvy-beds-warn.md
Normal file
|
@ -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
|
7
.changeset/stupid-wolves-explain.md
Normal file
7
.changeset/stupid-wolves-explain.md
Normal file
|
@ -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
|
|
@ -35,6 +35,6 @@
|
|||
"@types/prismjs": "1.26.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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} */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,6 @@
|
|||
"uvu": "^0.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = () => [];
|
||||
|
|
|
@ -71,6 +71,6 @@
|
|||
"vite": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -47,6 +47,6 @@
|
|||
"preact": "^10.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,6 @@
|
|||
"solid-js": "^1.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,6 @@
|
|||
"astro": "workspace:^2.0.0-beta.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -54,6 +54,6 @@
|
|||
"astro": "workspace:^2.0.0-beta.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.12.0"
|
||||
"node": ">=16.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import fetch from 'node-fetch';
|
||||
import { fetch } from 'undici';
|
||||
|
||||
const ASTRO_TELEMETRY_ENDPOINT = `https://telemetry.astro.build/api/v1/record`;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
2
packages/webapi/src/types.d.ts
vendored
2
packages/webapi/src/types.d.ts
vendored
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'}
|
||||
|
|
Loading…
Reference in a new issue