Skip to content

Commit 7f87c17

Browse files
committed
feat: improve serialize-javascript
1 parent 65125aa commit 7f87c17

File tree

1 file changed

+45
-24
lines changed

1 file changed

+45
-24
lines changed

test/utils/serialize-javascript.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export function stringify(value: any, options?: StringifyOptions): string {
5858
const types: TypeInfo[] = [];
5959
const circular = new WeakMap<any, PathType[]>();
6060
const refs: RefInfo[] = [];
61-
const source = expandPrototypeChain(value, { ...options, patches, descriptors, types, circular, refs });
61+
const apis: JsonApi[] = [];
62+
const source = expandPrototypeChain(value, { ...options, patches, descriptors, types, apis, circular, refs });
6263
const serialized = serializeRecursively(source, {
6364
...options,
6465
parentPath: [],
@@ -82,6 +83,7 @@ export function stringify(value: any, options?: StringifyOptions): string {
8283
},
8384
}) as PatchInfo[],
8485
types,
86+
apis,
8587
refs,
8688
};
8789
if (preserveDescriptors) {
@@ -112,39 +114,35 @@ export function stringify(value: any, options?: StringifyOptions): string {
112114
* @param options - Options to control the expansion behavior.
113115
*/
114116
export function expandPrototypeChain(source: any, options?: ExpandPrototypeChainOptions): typeof source {
115-
const { parentPath, patches = [], descriptors = [], types = [], refs = [], circular = new WeakMap() } = options ?? {};
117+
const {
118+
parentPath,
119+
patches = [],
120+
descriptors = [],
121+
types = [],
122+
refs = [],
123+
apis = [],
124+
circular = new WeakMap(),
125+
} = options ?? {};
116126
return expandPrototypeChainRecursively(source, {
117127
...options,
128+
paths: parentPath ?? [],
118129
patches,
119130
descriptors,
120131
types,
121132
refs,
133+
apis,
122134
circular,
123-
paths: parentPath,
124135
});
125136
}
126137

127138
function expandPrototypeChainRecursively(
128139
source: any,
129140
options: {
130-
paths?: PathType[];
131-
patches: PatchInfo[];
132-
descriptors: DescriptorInfo[];
133-
types: TypeInfo[];
134-
circular: WeakMap<any, PathType[]>;
135-
refs: RefInfo[];
136-
} & Pick<StringifyOptions, 'preserveClassConstructor' | 'preserveDescriptors' | 'debug'>
141+
paths: PathType[];
142+
} & Pick<ExpandPrototypeChainOptions, 'patches' | 'descriptors' | 'types' | 'refs' | 'apis' | 'circular'> &
143+
Pick<StringifyOptions, 'preserveClassConstructor' | 'preserveDescriptors' | 'debug'>
137144
): typeof source {
138-
const {
139-
debug,
140-
patches,
141-
preserveDescriptors = true,
142-
descriptors = [],
143-
types = [],
144-
paths = [],
145-
refs = [],
146-
circular,
147-
} = options ?? {};
145+
const { debug, patches, preserveDescriptors = true, descriptors, types, paths, refs, apis, circular } = options;
148146
if (source == null || source === Array.prototype || source === Object.prototype) {
149147
return source;
150148
}
@@ -177,6 +175,19 @@ function expandPrototypeChainRecursively(
177175
// should return `null` instead of `undefined`, since undefined will be ignored in JSON.stringify
178176
return null;
179177
}
178+
if ('isRawJSON' in JSON && typeof JSON.isRawJSON === 'function' && JSON.isRawJSON(source)) {
179+
return source;
180+
} else if (source.toJSON && typeof source.toJSON === 'function') {
181+
const api: JsonApi = {
182+
toJSON: stringToBase64(serializeFunction(source.toJSON.toString())!),
183+
};
184+
if (source.fromJSON && typeof source.fromJSON === 'function') {
185+
api.fromJSON = stringToBase64(serializeFunction(source.fromJSON.toString())!);
186+
}
187+
apis.push(api);
188+
// todo: 1. 序列化result 2. 解析apis
189+
result = source.toJSON();
190+
}
180191
if (Array.isArray(source)) {
181192
result = [...source];
182193
} else if (source instanceof Map) {
@@ -361,6 +372,8 @@ function serializeRecursively(
361372
return `${ST}null${ET}`;
362373
} else if (source === undefined) {
363374
return undefined;
375+
} else if ('isRawJSON' in JSON && typeof JSON.isRawJSON === 'function' && JSON.isRawJSON(source)) {
376+
return source;
364377
} else if (typeof source === 'number') {
365378
if (Number.isNaN(source)) {
366379
return `${ST}NaN${ET}`;
@@ -460,11 +473,17 @@ export function serializeFunction(funcStr: string) {
460473
return undefined;
461474
}
462475
if (
476+
// function () {}
463477
!funcStr.startsWith('function') &&
478+
// async function () {}
464479
!funcStr.startsWith('async function') &&
480+
// class {}
465481
!funcStr.startsWith('class') &&
482+
// function* () {}
466483
!funcStr.startsWith('function*') &&
484+
// async function* () {}
467485
!funcStr.startsWith('async function*') &&
486+
// () => {}
468487
!funcStr.replace(/\s/g, '').match(/^\(?[^)]+\)?=>/)
469488
) {
470489
// If it's a computed property function, for example: { [Symbol.toPrimitive]() { return 1; } }
@@ -890,10 +909,10 @@ interface RefInfo {
890909
path: PathType[];
891910
from: PathType[];
892911
}
893-
// interface Jsoner<T> {
894-
// fromJson: (value: T) => string;
895-
// deserialize: (value: string) => T;
896-
// }
912+
interface JsonApi {
913+
fromJSON?: string;
914+
toJSON: string;
915+
}
897916

898917
interface SerializedResult {
899918
version?: string;
@@ -902,6 +921,7 @@ interface SerializedResult {
902921
endTag?: string;
903922
source: string | undefined;
904923
types: TypeInfo[];
924+
apis: JsonApi[];
905925
patches: PatchInfo[];
906926
refs: RefInfo[];
907927
descriptors?: DescriptorInfo[];
@@ -969,6 +989,7 @@ export type ExpandPrototypeChainOptions = {
969989
descriptors: DescriptorInfo[];
970990
/** The output types for the object after expanding the prototype chain. */
971991
types: TypeInfo[];
992+
apis: JsonApi[];
972993
circular: WeakMap<any, PathType[]>;
973994
/** The circular refs for the object after expanding the prototype chain. */
974995
refs: RefInfo[];

0 commit comments

Comments
 (0)