diff --git a/.changeset/global-fetch-instanceof.md b/.changeset/global-fetch-instanceof.md new file mode 100644 index 00000000000..b9dff67555e --- /dev/null +++ b/.changeset/global-fetch-instanceof.md @@ -0,0 +1,5 @@ +--- +"@remix-run/node": patch +--- + +ensures fetch() return is instanceof global Response by removing extended classes for NodeRequest and NodeResponse in favor of custom interface type cast. diff --git a/contributors.yml b/contributors.yml index 91a7e07bbdd..ae7de85f83d 100644 --- a/contributors.yml +++ b/contributors.yml @@ -230,6 +230,7 @@ - jaydiablo - jca41 - jdeniau +- jeeyoungk - JeffBeltran - jenseng - jeremyjfleming diff --git a/integration/fetch-globals-test.ts b/integration/fetch-globals-test.ts new file mode 100644 index 00000000000..172602c8f3a --- /dev/null +++ b/integration/fetch-globals-test.ts @@ -0,0 +1,39 @@ +import { test, expect } from "@playwright/test"; + +import type { Fixture, AppFixture } from "./helpers/create-fixture"; +import { createAppFixture, createFixture, js } from "./helpers/create-fixture"; + +let fixture: Fixture; +let appFixture: AppFixture; + +test.beforeAll(async () => { + fixture = await createFixture({ + files: { + "app/routes/_index.tsx": js` + import { json } from "@remix-run/node"; + import { useLoaderData } from "@remix-run/react"; + export async function loader() { + const resp = await fetch('https://reqres.in/api/users?page=2'); + return (resp instanceof Response) ? 'is an instance of global Response' : 'is not an instance of global Response'; + } + export default function Index() { + let data = useLoaderData(); + return ( +
+ {data} +
+ ) + } + `, + }, + }); + + appFixture = await createAppFixture(fixture); +}); + +test.afterAll(async () => appFixture.close()); + +test("returned variable from fetch() should be instance of global Response", async () => { + let response = await fixture.requestDocument("/"); + expect(await response.text()).toMatch("is an instance of global Response"); +}); diff --git a/packages/remix-node/__tests__/fetch-test.ts b/packages/remix-node/__tests__/fetch-test.ts index 6f7c8480662..e8fac2dffc6 100644 --- a/packages/remix-node/__tests__/fetch-test.ts +++ b/packages/remix-node/__tests__/fetch-test.ts @@ -1,6 +1,10 @@ import { ReadableStream } from "@remix-run/web-stream"; +import { + Request as WebRequest, + Response as WebResponse, +} from "@remix-run/web-fetch"; -import { Request } from "../fetch"; +import { Request, Response } from "../fetch"; let test = { source: [ @@ -111,6 +115,22 @@ describe("Request", () => { expect(file.size).toBe(1023); expect(cloned instanceof Request).toBeTruthy(); + expect(cloned instanceof WebRequest).toBeTruthy(); + }); + + it("instanceOf", async () => { + let nodeReq = new Request("http://example.com"); + let webReq = new WebRequest("http://example.com"); + let nodeRes = new Response("http://example.com"); + let webRes = new WebResponse("http://example.com"); + expect(nodeReq instanceof Request).toBeTruthy(); + expect(nodeReq instanceof WebRequest).toBeTruthy(); + expect(webReq instanceof Request).toBeTruthy(); + expect(webReq instanceof WebRequest).toBeTruthy(); + expect(nodeRes instanceof Response).toBeTruthy(); + expect(nodeRes instanceof WebResponse).toBeTruthy(); + expect(webRes instanceof Response).toBeTruthy(); + expect(webRes instanceof WebResponse).toBeTruthy(); }); }); diff --git a/packages/remix-node/fetch.ts b/packages/remix-node/fetch.ts index b4182a055e7..1047e998ab4 100644 --- a/packages/remix-node/fetch.ts +++ b/packages/remix-node/fetch.ts @@ -9,6 +9,7 @@ export { FormData } from "@remix-run/web-fetch"; export { File, Blob } from "@remix-run/web-file"; type NodeHeadersInit = ConstructorParameters[0]; +type NodeResponseInfo = ConstructorParameters[0]; type NodeResponseInit = NonNullable< ConstructorParameters[1] >; @@ -31,30 +32,27 @@ export type { NodeResponseInit as ResponseInit, }; -class NodeRequest extends WebRequest { - constructor(info: NodeRequestInfo, init?: NodeRequestInit) { - super(info, init as RequestInit); - } +interface NodeRequest extends WebRequest { + get headers(): WebHeaders; - public get headers(): WebHeaders { - return super.headers as WebHeaders; - } - - public clone(): NodeRequest { - return new NodeRequest(this); - } + clone(): NodeRequest; } -class NodeResponse extends WebResponse { - public get headers(): WebHeaders { - return super.headers as WebHeaders; - } +interface NodeResponse extends WebResponse { + get headers(): WebHeaders; - public clone(): NodeResponse { - return super.clone() as NodeResponse; - } + clone(): NodeResponse; } +const NodeRequest = WebRequest as new ( + info: NodeRequestInfo, + init?: NodeRequestInit +) => NodeRequest; +const NodeResponse = WebResponse as unknown as new ( + info: NodeResponseInfo, + init?: NodeResponseInit +) => NodeResponse; + export { WebHeaders as Headers, NodeRequest as Request,