Skip to content

Commit 53069ac

Browse files
authored
feat: can handle bigint data (#186)
* feat: can handle bigint data * test: add bigint util test
1 parent 6499e1a commit 53069ac

File tree

8 files changed

+118
-6
lines changed

8 files changed

+118
-6
lines changed

Diff for: src/client/context/RDTContext.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Dispatch } from "react"
22
import type React from "react"
33
import { createContext, useEffect, useMemo, useReducer } from "react"
4+
import { bigIntReplacer } from "../../shared/bigint-util.js"
45
import { useRemoveBody } from "../hooks/detached/useRemoveBody.js"
56
import { checkIsDetached, checkIsDetachedOwner, checkIsDetachedWindow } from "../utils/detached.js"
67
import { tryParseJson } from "../utils/sanitize.js"
@@ -126,7 +127,7 @@ export const RDTContextProvider = ({ children, config }: ContextProps) => {
126127
// Store user settings for dev tools into local storage
127128
setStorageItem(REACT_ROUTER_DEV_TOOLS_SETTINGS, JSON.stringify(settings))
128129
// Store general state into local storage
129-
setStorageItem(REACT_ROUTER_DEV_TOOLS_STATE, JSON.stringify(rest))
130+
setStorageItem(REACT_ROUTER_DEV_TOOLS_STATE, JSON.stringify(rest, bigIntReplacer))
130131
}, [state])
131132

132133
return <RDTContext.Provider value={value}>{children}</RDTContext.Provider>

Diff for: src/client/hof.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import type { ClientActionFunctionArgs, ClientLoaderFunctionArgs, LinksFunction } from "react-router"
2+
import { convertBigIntToString } from "../shared/bigint-util"
23
import type { RequestEvent } from "../shared/request-event"
34

45
const sendEventToDevServer = (req: RequestEvent) => {
6+
if (req.data) {
7+
req.data = convertBigIntToString(req.data)
8+
}
59
import.meta.hot?.send("request-event", req)
610
}
711

Diff for: src/shared/bigint-util.test.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { bigIntReplacer, convertBigIntToString } from "./bigint-util"
2+
3+
const BIG_INT_VALUE = BigInt(10 ** 20)
4+
const BIG_INT_STRING_VALUE = "100000000000000000000"
5+
6+
describe("bigIntReplacer", () => {
7+
it("should convert bigint to string", () => {
8+
const sut = {
9+
key: BIG_INT_VALUE,
10+
nestedKey: {
11+
key: BIG_INT_VALUE,
12+
},
13+
}
14+
const result = JSON.stringify(sut, bigIntReplacer)
15+
expect(result).toBe(`{"key":"${BIG_INT_STRING_VALUE}","nestedKey":{"key":"${BIG_INT_STRING_VALUE}"}}`)
16+
})
17+
18+
it("should return value as is if not a bigint", () => {
19+
const sut = {
20+
key: 100,
21+
}
22+
const result = JSON.stringify(sut, bigIntReplacer)
23+
expect(result).toBe('{"key":100}')
24+
})
25+
})
26+
27+
describe("convertBigIntToString", () => {
28+
it("should convert bigint to string", () => {
29+
const result = convertBigIntToString(BIG_INT_VALUE)
30+
expect(result).toBe(BIG_INT_STRING_VALUE)
31+
})
32+
33+
it("should convert bigint in array to string", () => {
34+
const result = convertBigIntToString([BIG_INT_VALUE, 123])
35+
expect(result).toEqual([BIG_INT_STRING_VALUE, 123])
36+
})
37+
38+
it("should convert bigint in object to string", () => {
39+
const result = convertBigIntToString({ key: BIG_INT_VALUE, anotherKey: 123 })
40+
expect(result).toEqual({ key: BIG_INT_STRING_VALUE, anotherKey: 123 })
41+
})
42+
43+
it("should handle nested structures", () => {
44+
const data = {
45+
key: BIG_INT_VALUE,
46+
nested: {
47+
anotherKey: BIG_INT_VALUE,
48+
array: [BIG_INT_VALUE, 123],
49+
},
50+
}
51+
const expected = {
52+
key: BIG_INT_STRING_VALUE,
53+
nested: {
54+
anotherKey: BIG_INT_STRING_VALUE,
55+
array: [BIG_INT_STRING_VALUE, 123],
56+
},
57+
}
58+
const result = convertBigIntToString(data)
59+
expect(result).toEqual(expected)
60+
})
61+
62+
it("should return non-bigint values as is", () => {
63+
const result = convertBigIntToString(123)
64+
expect(result).toBe(123)
65+
})
66+
})

Diff for: src/shared/bigint-util.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export const bigIntReplacer = (key: any, value: any) => (typeof value === "bigint" ? value.toString() : value)
2+
3+
export const convertBigIntToString = (data: any): any => {
4+
if (typeof data === "bigint") {
5+
return data.toString()
6+
}
7+
8+
if (Array.isArray(data)) {
9+
return data.map((item) => convertBigIntToString(item))
10+
}
11+
12+
if (data !== null && typeof data === "object") {
13+
return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, convertBigIntToString(value)]))
14+
}
15+
16+
return data
17+
}

Diff for: src/shared/send-event.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { bigIntReplacer } from "./bigint-util"
12
import type { RequestEvent } from "./request-event"
23

34
export const sendEvent = (event: RequestEvent) => {
@@ -9,7 +10,7 @@ export const sendEvent = (event: RequestEvent) => {
910
if (port) {
1011
fetch(`http://localhost:${port}/react-router-devtools-request`, {
1112
method: "POST",
12-
body: JSON.stringify({ routine: "request-event", ...event }),
13+
body: JSON.stringify({ routine: "request-event", ...event }, bigIntReplacer),
1314
})
1415
.then(async (res) => {
1516
// avoid memory leaks

Diff for: test-apps/react-router-vite/app/root.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const loader = ({context, devTools }: LoaderFunctionArgs) => {
2828
});
2929
const start =devTools?.tracing.start("test")!;
3030
devTools?.tracing.end("test", start);
31-
return data({ message: "Hello World", mainPromise }, { headers: { "Cache-Control": "max-age=3600, private" } });
31+
return data({ message: "Hello World", mainPromise, bigInt: BigInt(10) }, { headers: { "Cache-Control": "max-age=3600, private" } });
3232
}
3333

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

4545
export default function App() {
@@ -65,4 +65,4 @@ export default function App() {
6565
</body>
6666
</html>
6767
);
68-
}
68+
}

Diff for: test-apps/react-router-vite/app/routes/_index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const loader = async ({ request, context,devTools, params }: LoaderFunct
3838
resolve("test1");
3939
}, 3500);
4040
});
41-
return { message: "Hello World!", test, test1, data };
41+
return { message: "Hello World!", test, test1, data, bigInt: BigInt(10) };
4242
};
4343

4444
export const clientLoader = async ({ request, serverLoader, devTools }: ClientLoaderFunctionArgs) => {

Diff for: vitest.workspace.ts

+23
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,27 @@ export default defineWorkspace([
7777
},
7878
},
7979
},
80+
{
81+
test: {
82+
globals: true,
83+
exclude: ["**/node_modules/**", "**/dist/**", "**/docs/**", "**/public/**", "**/test-apps/**"],
84+
environment: "node",
85+
root: "./src/shared",
86+
name: "react-router-devtools/shared",
87+
// @ts-expect-error
88+
coverage: {
89+
provider: "v8",
90+
include: ["src/**/*"],
91+
reporter: ["text", "json-summary", "json", "html"],
92+
reportOnFailure: true,
93+
all: false,
94+
thresholds: {
95+
statements: 70,
96+
branches: 75,
97+
functions: 70,
98+
lines: 70,
99+
},
100+
},
101+
},
102+
},
80103
])

0 commit comments

Comments
 (0)