Skip to content

Commit 71f7641

Browse files
committed
feat: improved serialization
1 parent 11196d4 commit 71f7641

File tree

5 files changed

+100
-22
lines changed

5 files changed

+100
-22
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
},
5555
"devDependencies": {
5656
"@babel/types": "^7.21.4",
57+
"@happy-dom/global-registrator": "^9.20.3",
5758
"@nuxtjs/eslint-config-typescript": "latest",
5859
"@supabase/supabase-js": "^2.21.0",
5960
"@trpc/server": "^10.21.1",

pnpm-lock.yaml

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { SourceType } from './types'
22

33
export const NO_SOURCE: SourceType = { filePath: 'unknown', lineNumber: -1 }
4+
45
export const FLYTRAP_UNSERIALIZABLE_VALUE = 'FLYTRAP_UNSERIALIZABLE_VALUE'
6+
export const FLYTRAP_FUNCTION = 'FLYTRAP_FUNCTION'
7+
export const FLYTRAP_DOM_EVENT = 'FLYTRAP_DOM_EVENT'
8+
export const FLYTRAP_CIRCULAR = 'FLYTRAP_CIRCULAR'

src/core/stringify.ts

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,59 @@
11
import SuperJSON from 'superjson'
2-
import { FLYTRAP_UNSERIALIZABLE_VALUE } from './constants'
3-
import { ignoreCircularReferences } from './util'
2+
import { FLYTRAP_CIRCULAR, FLYTRAP_DOM_EVENT, FLYTRAP_FUNCTION } from './constants'
3+
4+
function superJsonRegisterCustom() {
5+
// handle functions
6+
SuperJSON.registerCustom<any, string>(
7+
{
8+
isApplicable: (v): v is Function => typeof v === 'function',
9+
serialize: () => FLYTRAP_FUNCTION,
10+
deserialize: () => FLYTRAP_FUNCTION
11+
},
12+
'functions'
13+
)
14+
15+
// handle DOM events
16+
SuperJSON.registerCustom<any, string>(
17+
{
18+
isApplicable: (v): v is Event => {
19+
return typeof window !== 'undefined' ? v instanceof window.Event : false
20+
},
21+
serialize: () => FLYTRAP_DOM_EVENT,
22+
deserialize: () => FLYTRAP_DOM_EVENT
23+
},
24+
'dom events'
25+
)
26+
}
427

528
/**
629
* Stringifies an object, and removes all cyclical dependencies. When parsing, cyclical values become `null`.
730
* @param obj object to stringify
831
* @returns stringified object with cyclical dependencies removed
932
*/
1033
export function stringify(obj: any): string {
11-
// handle functions appropriately
12-
SuperJSON.registerCustom<any, string>(
13-
{
14-
isApplicable: (v): v is Function => typeof v === 'function',
15-
serialize: () => FLYTRAP_UNSERIALIZABLE_VALUE,
16-
deserialize: () => FLYTRAP_UNSERIALIZABLE_VALUE
17-
},
18-
'unserializable functions'
19-
)
34+
superJsonRegisterCustom()
2035

2136
const serialized = SuperJSON.serialize(obj)
2237
if (serialized.meta?.referentialEqualities) {
2338
delete serialized.meta.referentialEqualities
2439
}
2540

41+
function ignoreCircularReferences() {
42+
const seen = new WeakSet()
43+
return (key: any, value: any) => {
44+
if (key.startsWith('_')) return // Don't compare React's internal props.
45+
if (typeof value === 'object' && value !== null) {
46+
if (seen.has(value)) return FLYTRAP_CIRCULAR
47+
seen.add(value)
48+
}
49+
return value
50+
}
51+
}
52+
2653
return JSON.stringify(serialized, ignoreCircularReferences())
2754
}
2855

2956
export function parse<T = unknown>(stringified: string): T {
30-
// handle functions appropriately
31-
SuperJSON.registerCustom<any, string>(
32-
{
33-
isApplicable: (v): v is Function => typeof v === 'function',
34-
serialize: () => FLYTRAP_UNSERIALIZABLE_VALUE,
35-
deserialize: () => FLYTRAP_UNSERIALIZABLE_VALUE
36-
},
37-
'unserializable functions'
38-
)
39-
57+
superJsonRegisterCustom()
4058
return SuperJSON.parse<T>(stringified)
4159
}

test/stringify.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { expect, it } from 'vitest'
2-
import { stringify } from '../src/core/stringify'
2+
import { parse, stringify } from '../src/core/stringify'
33
import SuperJSON from 'superjson'
4+
import { FLYTRAP_DOM_EVENT } from '../src/core/constants'
5+
import { GlobalRegistrator } from '@happy-dom/global-registrator'
6+
GlobalRegistrator.register()
47

58
it('removes cyclical dependencies', () => {
69
const a = {
@@ -26,3 +29,7 @@ it('removes cyclical dependencies', () => {
2629
}
2730
})
2831
})
32+
33+
it('doesnt stringify DOM events', () => {
34+
expect(parse(stringify(new MouseEvent('click')))).toEqual(FLYTRAP_DOM_EVENT)
35+
})

0 commit comments

Comments
 (0)