From 89eb3cf95078bfea2262cc6ad5839783b4b9e477 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Thu, 4 Apr 2024 12:03:18 -0700 Subject: [PATCH] chore(remix-node): put native fetch behind config option (#9198) Co-authored-by: Matt Brophy --- .changeset/good-buses-divide.md | 9 +++ integration/error-data-request-test.ts | 4 +- integration/helpers/create-fixture.ts | 3 +- integration/helpers/vite.ts | 2 +- integration/hmr-test.ts | 2 +- integration/vite-basename-test.ts | 4 +- package.json | 1 + packages/remix-architect/__tests__/setup.ts | 2 +- packages/remix-express/__tests__/setup.ts | 2 +- packages/remix-node/__tests__/setup.ts | 2 +- packages/remix-node/globals.ts | 56 +++++++++++-------- packages/remix-node/package.json | 1 + packages/remix-node/rollup.config.js | 2 + packages/remix-react/__tests__/setup.ts | 2 +- packages/remix-serve/cli.ts | 3 +- .../remix-server-runtime/__tests__/setup.ts | 2 +- packages/remix-testing/jest.config.js | 2 +- packages/remix-testing/jest.setup.js | 2 +- pnpm-lock.yaml | 32 +++++++++++ .../classic-remix-compiler/arc/server.ts | 2 +- 20 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 .changeset/good-buses-divide.md diff --git a/.changeset/good-buses-divide.md b/.changeset/good-buses-divide.md new file mode 100644 index 00000000000..913eb1bc877 --- /dev/null +++ b/.changeset/good-buses-divide.md @@ -0,0 +1,9 @@ +--- +"@remix-run/node": patch +"@remix-run/serve": patch +--- + +- Put `undici` fetch polyfill behind a new `installGlobals({ nativeFetch: true })` parameter +- `remix-serve` will default to using `undici` for the fetch polyfill if `future._unstable_singleFetch` is enabled because the single fetch implementation relies on the `undici` polyfill + - Any users opting into Single Fetch and managing their own polfill will need to pass the flag to `installGlobals` on their own to avoid runtime errors with Single Fetch + diff --git a/integration/error-data-request-test.ts b/integration/error-data-request-test.ts index b19e8cea4f2..714c328bde8 100644 --- a/integration/error-data-request-test.ts +++ b/integration/error-data-request-test.ts @@ -327,9 +327,7 @@ test.describe("single fetch", () => { fixture.requestSingleFetchData("/loader-return-json.data", { method: "TRACE", }) - ).rejects.toThrowError( - `Failed to construct 'Request': 'TRACE' HTTP method is unsupported.` - ); + ).rejects.toThrow(/'TRACE' HTTP method is unsupported\./); }); test("returns a 404 on a data fetch to a path with no matches", async () => { diff --git a/integration/helpers/create-fixture.ts b/integration/helpers/create-fixture.ts index 78be4f2294f..a0631874bd1 100644 --- a/integration/helpers/create-fixture.ts +++ b/integration/helpers/create-fixture.ts @@ -44,8 +44,9 @@ export function json(value: JsonObject) { } export async function createFixture(init: FixtureInit, mode?: ServerMode) { - installGlobals(); + installGlobals({ nativeFetch: true }); let compiler = init.compiler ?? "remix"; + let projectDir = await createFixtureProject(init, mode); let buildPath = url.pathToFileURL( path.join( diff --git a/integration/helpers/vite.ts b/integration/helpers/vite.ts index 0216d50f59d..65c805288ff 100644 --- a/integration/helpers/vite.ts +++ b/integration/helpers/vite.ts @@ -53,7 +53,7 @@ export const EXPRESS_SERVER = (args: { import { installGlobals } from "@remix-run/node"; import express from "express"; - installGlobals(); + installGlobals({ nativeFetch: true }); let viteDevServer = process.env.NODE_ENV === "production" diff --git a/integration/hmr-test.ts b/integration/hmr-test.ts index 159b404341c..a2f5f2ad2a3 100644 --- a/integration/hmr-test.ts +++ b/integration/hmr-test.ts @@ -180,7 +180,7 @@ let customServer = (options: { appPort: number; devReady: string }) => { import { createRequestHandler } from "@remix-run/express"; import { ${options.devReady}, installGlobals } from "@remix-run/node"; - installGlobals(); + installGlobals({ nativeFetch: true }); const app = express(); app.use(express.static("public", { immutable: true, maxAge: "1y" })); diff --git a/integration/vite-basename-test.ts b/integration/vite-basename-test.ts index 115a653cc50..9c0ceb8690d 100644 --- a/integration/vite-basename-test.ts +++ b/integration/vite-basename-test.ts @@ -97,7 +97,7 @@ const customServerFile = ({ import { createRequestHandler } from "@remix-run/express"; import { installGlobals } from "@remix-run/node"; import express from "express"; - installGlobals(); + installGlobals({ nativeFetch: true }); const viteDevServer = process.env.NODE_ENV === "production" @@ -416,7 +416,7 @@ test.describe("Vite base / Remix basename / express build", async () => { import { createRequestHandler } from "@remix-run/express"; import { installGlobals } from "@remix-run/node"; import express from "express"; - installGlobals(); + installGlobals({ nativeFetch: true }); const app = express(); app.all( diff --git a/package.json b/package.json index 8d563372985..9ce4b79c709 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@remix-run/react": "workspace:*", "@remix-run/testing": "workspace:*", "@rollup/plugin-babel": "^5.2.2", + "@rollup/plugin-commonjs": "^21.1.0", "@rollup/plugin-json": "^5.0.0", "@rollup/plugin-node-resolve": "^11.0.1", "@rollup/plugin-replace": "^5.0.2", diff --git a/packages/remix-architect/__tests__/setup.ts b/packages/remix-architect/__tests__/setup.ts index 917305ac938..01b7b7e57b9 100644 --- a/packages/remix-architect/__tests__/setup.ts +++ b/packages/remix-architect/__tests__/setup.ts @@ -1,2 +1,2 @@ import { installGlobals } from "@remix-run/node"; -installGlobals(); +installGlobals({ nativeFetch: true }); diff --git a/packages/remix-express/__tests__/setup.ts b/packages/remix-express/__tests__/setup.ts index 917305ac938..01b7b7e57b9 100644 --- a/packages/remix-express/__tests__/setup.ts +++ b/packages/remix-express/__tests__/setup.ts @@ -1,2 +1,2 @@ import { installGlobals } from "@remix-run/node"; -installGlobals(); +installGlobals({ nativeFetch: true }); diff --git a/packages/remix-node/__tests__/setup.ts b/packages/remix-node/__tests__/setup.ts index 917305ac938..01b7b7e57b9 100644 --- a/packages/remix-node/__tests__/setup.ts +++ b/packages/remix-node/__tests__/setup.ts @@ -1,2 +1,2 @@ import { installGlobals } from "@remix-run/node"; -installGlobals(); +installGlobals({ nativeFetch: true }); diff --git a/packages/remix-node/globals.ts b/packages/remix-node/globals.ts index 2ae3f0daa57..c16012da783 100644 --- a/packages/remix-node/globals.ts +++ b/packages/remix-node/globals.ts @@ -1,12 +1,3 @@ -import { - File as NodeFile, - fetch as nodeFetch, - FormData as NodeFormData, - Headers as NodeHeaders, - Request as NodeRequest, - Response as NodeResponse, -} from "undici"; - declare global { namespace NodeJS { interface ProcessEnv { @@ -32,17 +23,38 @@ declare global { } } -export function installGlobals() { - global.File = NodeFile as unknown as typeof File; - - // @ts-expect-error - overriding globals - global.Headers = NodeHeaders; - // @ts-expect-error - overriding globals - global.Request = NodeRequest; - // @ts-expect-error - overriding globals - global.Response = NodeResponse; - // @ts-expect-error - overriding globals - global.fetch = nodeFetch; - // @ts-expect-error - overriding globals - global.FormData = NodeFormData; +export function installGlobals({ + nativeFetch, +}: { nativeFetch?: boolean } = {}) { + if (nativeFetch) { + let { + File: UndiciFile, + fetch: undiciFetch, + FormData: UndiciFormData, + Headers: UndiciHeaders, + Request: UndiciRequest, + Response: UndiciResponse, + } = require("undici"); + global.File = UndiciFile as unknown as typeof File; + global.Headers = UndiciHeaders; + global.Request = UndiciRequest; + global.Response = UndiciResponse; + global.fetch = undiciFetch; + global.FormData = UndiciFormData; + } else { + let { + File: RemixFile, + fetch: RemixFetch, + FormData: RemixFormData, + Headers: RemixHeaders, + Request: RemixRequest, + Response: RemixResponse, + } = require("@remix-run/web-fetch"); + global.File = RemixFile; + global.Headers = RemixHeaders; + global.Request = RemixRequest; + global.Response = RemixResponse; + global.fetch = RemixFetch; + global.FormData = RemixFormData; + } } diff --git a/packages/remix-node/package.json b/packages/remix-node/package.json index 629c4c269f4..a46308f4b72 100644 --- a/packages/remix-node/package.json +++ b/packages/remix-node/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@remix-run/server-runtime": "workspace:*", + "@remix-run/web-fetch": "^4.4.2", "@web3-storage/multipart-parser": "^1.0.0", "cookie-signature": "^1.1.0", "source-map-support": "^0.5.21", diff --git a/packages/remix-node/rollup.config.js b/packages/remix-node/rollup.config.js index f61aa0306c0..55e36856248 100644 --- a/packages/remix-node/rollup.config.js +++ b/packages/remix-node/rollup.config.js @@ -1,5 +1,6 @@ const path = require("node:path"); const babel = require("@rollup/plugin-babel").default; +const commonjs = require("@rollup/plugin-commonjs"); const nodeResolve = require("@rollup/plugin-node-resolve").default; const copy = require("rollup-plugin-copy"); @@ -37,6 +38,7 @@ module.exports = function rollup() { extensions: [".ts", ".tsx"], }), nodeResolve({ extensions: [".ts", ".tsx"] }), + commonjs({ ignoreDynamicRequires: true }), copy({ targets: [ { src: "LICENSE.md", dest: [outputDir, sourceDir] }, diff --git a/packages/remix-react/__tests__/setup.ts b/packages/remix-react/__tests__/setup.ts index 87f5cefb48f..dab603502ce 100644 --- a/packages/remix-react/__tests__/setup.ts +++ b/packages/remix-react/__tests__/setup.ts @@ -4,5 +4,5 @@ global.TextEncoder = require("util").TextEncoder; global.ReadableStream = require("stream/web").ReadableStream; global.WritableStream = require("stream/web").WritableStream; -require("@remix-run/node").installGlobals(); +require("@remix-run/node").installGlobals({ nativeFetch: true }); global.FormData = JSDOMFormData; diff --git a/packages/remix-serve/cli.ts b/packages/remix-serve/cli.ts index 31c8b765087..6bf903e157e 100644 --- a/packages/remix-serve/cli.ts +++ b/packages/remix-serve/cli.ts @@ -34,7 +34,6 @@ sourceMapSupport.install({ return null; }, }); -installGlobals(); run(); @@ -101,6 +100,8 @@ async function run() { let build: ServerBuild = await reimportServer(); + installGlobals({ nativeFetch: build.future.unstable_singleFetch }); + let onListen = () => { let address = process.env.HOST || diff --git a/packages/remix-server-runtime/__tests__/setup.ts b/packages/remix-server-runtime/__tests__/setup.ts index 451031301ac..2752bf1a72d 100644 --- a/packages/remix-server-runtime/__tests__/setup.ts +++ b/packages/remix-server-runtime/__tests__/setup.ts @@ -1,3 +1,3 @@ import { installGlobals } from "@remix-run/node"; -installGlobals(); +installGlobals({ nativeFetch: true }); diff --git a/packages/remix-testing/jest.config.js b/packages/remix-testing/jest.config.js index 483aa32c42d..c5d645167a2 100644 --- a/packages/remix-testing/jest.config.js +++ b/packages/remix-testing/jest.config.js @@ -4,5 +4,5 @@ module.exports = { displayName: "testing", setupFiles: [], testEnvironment: "jsdom", - setupFilesAfterEnv: ["@testing-library/jest-dom", "./jest.setup.js"], + setupFilesAfterEnv: ["./jest.setup.js", "@testing-library/jest-dom"], }; diff --git a/packages/remix-testing/jest.setup.js b/packages/remix-testing/jest.setup.js index 87f5cefb48f..dab603502ce 100644 --- a/packages/remix-testing/jest.setup.js +++ b/packages/remix-testing/jest.setup.js @@ -4,5 +4,5 @@ global.TextEncoder = require("util").TextEncoder; global.ReadableStream = require("stream/web").ReadableStream; global.WritableStream = require("stream/web").WritableStream; -require("@remix-run/node").installGlobals(); +require("@remix-run/node").installGlobals({ nativeFetch: true }); global.FormData = JSDOMFormData; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45b44141300..e1ba8025df1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,9 @@ importers: '@rollup/plugin-babel': specifier: ^5.2.2 version: 5.3.1(@babel/core@7.23.7)(rollup@2.75.7) + '@rollup/plugin-commonjs': + specifier: ^21.1.0 + version: 21.1.0(rollup@2.75.7) '@rollup/plugin-json': specifier: ^5.0.0 version: 5.0.2(rollup@2.75.7) @@ -1182,6 +1185,9 @@ importers: '@remix-run/server-runtime': specifier: workspace:* version: link:../remix-server-runtime + '@remix-run/web-fetch': + specifier: ^4.4.2 + version: 4.4.2 '@web3-storage/multipart-parser': specifier: ^1.0.0 version: 1.0.0 @@ -4254,6 +4260,22 @@ packages: rollup: 2.75.7 dev: false + /@rollup/plugin-commonjs@21.1.0(rollup@2.75.7): + resolution: {integrity: sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^2.38.3 + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.75.7) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 7.2.0 + is-reference: 1.2.1 + magic-string: 0.25.9 + resolve: 1.22.8 + rollup: 2.75.7 + dev: false + /@rollup/plugin-json@5.0.2(rollup@2.75.7): resolution: {integrity: sha512-D1CoOT2wPvadWLhVcmpkDnesTzjhNIQRWLsc3fA49IFOP2Y84cFOOJ+nKGYedvXHKUsPeq07HR4hXpBBr+CHlA==} engines: {node: '>=14.0.0'} @@ -6514,6 +6536,10 @@ packages: engines: {node: '>=4.0.0'} dev: false + /commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + dev: false + /component-emitter@1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} @@ -9331,6 +9357,12 @@ packages: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true + /is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + dependencies: + '@types/estree': 1.0.0 + dev: false + /is-reference@3.0.0: resolution: {integrity: sha512-Eo1W3wUoHWoCoVM4GVl/a+K0IgiqE5aIo4kJABFyMum1ZORlPkC+UC357sSQUL5w5QCE5kCC9upl75b7+7CY/Q==} dependencies: diff --git a/templates/classic-remix-compiler/arc/server.ts b/templates/classic-remix-compiler/arc/server.ts index 4db5dcdece6..90b5192011d 100644 --- a/templates/classic-remix-compiler/arc/server.ts +++ b/templates/classic-remix-compiler/arc/server.ts @@ -4,6 +4,6 @@ import { installGlobals } from "@remix-run/node"; import sourceMapSupport from "source-map-support"; sourceMapSupport.install(); -installGlobals(); +installGlobals({ nativeFetch: true }); export const handler = createRequestHandler({ build });