diff --git a/contributors.yml b/contributors.yml index 7818781f43..05183c5d03 100644 --- a/contributors.yml +++ b/contributors.yml @@ -296,6 +296,7 @@ - rtzll - rubeonline - ruidi-huang +- rururux - ryanflorence - ryanhiebert - saengmotmi diff --git a/integration/single-fetch-test.ts b/integration/single-fetch-test.ts index dc38fe2764..ca54396aa7 100644 --- a/integration/single-fetch-test.ts +++ b/integration/single-fetch-test.ts @@ -170,6 +170,24 @@ const files = { ) } `, + + "app/routes/invalid-date.tsx": js` + import { useLoaderData, data } from "react-router"; + + export function loader({ request }) { + return data({ invalidDate: new Date("invalid") }); + } + + export default function InvalidDate() { + let data = useLoaderData(); + return ( + <> +
{data.invalidDate.toISOString()}
+ > + ) + } + ` }; test.describe("single-fetch", () => { @@ -216,6 +234,23 @@ test.describe("single-fetch", () => { }, }, }); + + res = await fixture.requestSingleFetchData("/invalid-date.data"); + expect(res.data).toEqual({ + root: { + data: { + message: "ROOT", + }, + }, + "routes/invalid-date": { + data: { + invalidDate: expect.any(Date), + }, + }, + }); + + let date = (res.data as { ["routes/invalid-date"]: { data: { invalidDate: Date } } })["routes/invalid-date"].data.invalidDate; + expect(isNaN(date.getTime())).toBe(true); }); test("loads proper errors on single fetch loader requests", async () => { diff --git a/packages/react-router/__tests__/vendor/turbo-stream-test.ts b/packages/react-router/__tests__/vendor/turbo-stream-test.ts index 0ddc1131dd..54332b1703 100644 --- a/packages/react-router/__tests__/vendor/turbo-stream-test.ts +++ b/packages/react-router/__tests__/vendor/turbo-stream-test.ts @@ -50,6 +50,13 @@ test("should encode and decode Date", async () => { expect(output).toEqual(input); }); +test("should encode and decode invalid Date", async () => { + const input = new Date("invalid"); + const output = await quickDecode(encode(input)); + expect(isNaN(input.getTime())).toBe(true); + expect(isNaN(output.getTime())).toBe(true); +}); + test("should encode and decode NaN", async () => { const input = NaN; const output = await quickDecode(encode(input)); diff --git a/packages/react-router/vendor/turbo-stream-v2/flatten.ts b/packages/react-router/vendor/turbo-stream-v2/flatten.ts index a5e4567825..5e69f20979 100644 --- a/packages/react-router/vendor/turbo-stream-v2/flatten.ts +++ b/packages/react-router/vendor/turbo-stream-v2/flatten.ts @@ -106,7 +106,8 @@ function stringify(this: ThisEncode, input: unknown, index: number) { (i in input ? flatten.call(this, input[i]) : HOLE); str[index] = `${result}]`; } else if (input instanceof Date) { - str[index] = `["${TYPE_DATE}",${input.getTime()}]`; + const dateTime = input.getTime(); + str[index] = `["${TYPE_DATE}",${Number.isNaN(dateTime)? JSON.stringify("invalid") : dateTime}]`; } else if (input instanceof URL) { str[index] = `["${TYPE_URL}",${JSON.stringify(input.href)}]`; } else if (input instanceof RegExp) {