Skip to content

Commit

Permalink
feat: can handle bigint data (#186)
Browse files Browse the repository at this point in the history
* feat: can handle bigint data

* test: add bigint util test
  • Loading branch information
kesoji authored Feb 18, 2025
1 parent 6499e1a commit 53069ac
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 6 deletions.
3 changes: 2 additions & 1 deletion src/client/context/RDTContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Dispatch } from "react"
import type React from "react"
import { createContext, useEffect, useMemo, useReducer } from "react"
import { bigIntReplacer } from "../../shared/bigint-util.js"
import { useRemoveBody } from "../hooks/detached/useRemoveBody.js"
import { checkIsDetached, checkIsDetachedOwner, checkIsDetachedWindow } from "../utils/detached.js"
import { tryParseJson } from "../utils/sanitize.js"
Expand Down Expand Up @@ -126,7 +127,7 @@ export const RDTContextProvider = ({ children, config }: ContextProps) => {
// Store user settings for dev tools into local storage
setStorageItem(REACT_ROUTER_DEV_TOOLS_SETTINGS, JSON.stringify(settings))
// Store general state into local storage
setStorageItem(REACT_ROUTER_DEV_TOOLS_STATE, JSON.stringify(rest))
setStorageItem(REACT_ROUTER_DEV_TOOLS_STATE, JSON.stringify(rest, bigIntReplacer))
}, [state])

return <RDTContext.Provider value={value}>{children}</RDTContext.Provider>
Expand Down
4 changes: 4 additions & 0 deletions src/client/hof.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { ClientActionFunctionArgs, ClientLoaderFunctionArgs, LinksFunction } from "react-router"
import { convertBigIntToString } from "../shared/bigint-util"
import type { RequestEvent } from "../shared/request-event"

const sendEventToDevServer = (req: RequestEvent) => {
if (req.data) {
req.data = convertBigIntToString(req.data)
}
import.meta.hot?.send("request-event", req)
}

Expand Down
66 changes: 66 additions & 0 deletions src/shared/bigint-util.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { bigIntReplacer, convertBigIntToString } from "./bigint-util"

const BIG_INT_VALUE = BigInt(10 ** 20)
const BIG_INT_STRING_VALUE = "100000000000000000000"

describe("bigIntReplacer", () => {
it("should convert bigint to string", () => {
const sut = {
key: BIG_INT_VALUE,
nestedKey: {
key: BIG_INT_VALUE,
},
}
const result = JSON.stringify(sut, bigIntReplacer)
expect(result).toBe(`{"key":"${BIG_INT_STRING_VALUE}","nestedKey":{"key":"${BIG_INT_STRING_VALUE}"}}`)
})

it("should return value as is if not a bigint", () => {
const sut = {
key: 100,
}
const result = JSON.stringify(sut, bigIntReplacer)
expect(result).toBe('{"key":100}')
})
})

describe("convertBigIntToString", () => {
it("should convert bigint to string", () => {
const result = convertBigIntToString(BIG_INT_VALUE)
expect(result).toBe(BIG_INT_STRING_VALUE)
})

it("should convert bigint in array to string", () => {
const result = convertBigIntToString([BIG_INT_VALUE, 123])
expect(result).toEqual([BIG_INT_STRING_VALUE, 123])
})

it("should convert bigint in object to string", () => {
const result = convertBigIntToString({ key: BIG_INT_VALUE, anotherKey: 123 })
expect(result).toEqual({ key: BIG_INT_STRING_VALUE, anotherKey: 123 })
})

it("should handle nested structures", () => {
const data = {
key: BIG_INT_VALUE,
nested: {
anotherKey: BIG_INT_VALUE,
array: [BIG_INT_VALUE, 123],
},
}
const expected = {
key: BIG_INT_STRING_VALUE,
nested: {
anotherKey: BIG_INT_STRING_VALUE,
array: [BIG_INT_STRING_VALUE, 123],
},
}
const result = convertBigIntToString(data)
expect(result).toEqual(expected)
})

it("should return non-bigint values as is", () => {
const result = convertBigIntToString(123)
expect(result).toBe(123)
})
})
17 changes: 17 additions & 0 deletions src/shared/bigint-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const bigIntReplacer = (key: any, value: any) => (typeof value === "bigint" ? value.toString() : value)

export const convertBigIntToString = (data: any): any => {
if (typeof data === "bigint") {
return data.toString()
}

if (Array.isArray(data)) {
return data.map((item) => convertBigIntToString(item))
}

if (data !== null && typeof data === "object") {
return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, convertBigIntToString(value)]))
}

return data
}
3 changes: 2 additions & 1 deletion src/shared/send-event.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { bigIntReplacer } from "./bigint-util"
import type { RequestEvent } from "./request-event"

export const sendEvent = (event: RequestEvent) => {
Expand All @@ -9,7 +10,7 @@ export const sendEvent = (event: RequestEvent) => {
if (port) {
fetch(`http://localhost:${port}/react-router-devtools-request`, {
method: "POST",
body: JSON.stringify({ routine: "request-event", ...event }),
body: JSON.stringify({ routine: "request-event", ...event }, bigIntReplacer),
})
.then(async (res) => {
// avoid memory leaks
Expand Down
6 changes: 3 additions & 3 deletions test-apps/react-router-vite/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const loader = ({context, devTools }: LoaderFunctionArgs) => {
});
const start =devTools?.tracing.start("test")!;
devTools?.tracing.end("test", start);
return data({ message: "Hello World", mainPromise }, { headers: { "Cache-Control": "max-age=3600, private" } });
return data({ message: "Hello World", mainPromise, bigInt: BigInt(10) }, { headers: { "Cache-Control": "max-age=3600, private" } });
}

export const action =async ({devTools}: ActionFunctionArgs) => {
Expand All @@ -39,7 +39,7 @@ export const action =async ({devTools}: ActionFunctionArgs) => {
}, 2000);
});
devTools?.tracing.end("action submission", start!)
return ({ message: "Hello World" });
return ({ message: "Hello World", bigInt: BigInt(10) });
}

export default function App() {
Expand All @@ -65,4 +65,4 @@ export default function App() {
</body>
</html>
);
}
}
2 changes: 1 addition & 1 deletion test-apps/react-router-vite/app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const loader = async ({ request, context,devTools, params }: LoaderFunct
resolve("test1");
}, 3500);
});
return { message: "Hello World!", test, test1, data };
return { message: "Hello World!", test, test1, data, bigInt: BigInt(10) };
};

export const clientLoader = async ({ request, serverLoader, devTools }: ClientLoaderFunctionArgs) => {
Expand Down
23 changes: 23 additions & 0 deletions vitest.workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,27 @@ export default defineWorkspace([
},
},
},
{
test: {
globals: true,
exclude: ["**/node_modules/**", "**/dist/**", "**/docs/**", "**/public/**", "**/test-apps/**"],
environment: "node",
root: "./src/shared",
name: "react-router-devtools/shared",
// @ts-expect-error
coverage: {
provider: "v8",
include: ["src/**/*"],
reporter: ["text", "json-summary", "json", "html"],
reportOnFailure: true,
all: false,
thresholds: {
statements: 70,
branches: 75,
functions: 70,
lines: 70,
},
},
},
},
])

0 comments on commit 53069ac

Please sign in to comment.