seroval 0.4.0-alpha.13 → 0.4.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/development/index.cjs +639 -598
- package/dist/cjs/development/index.cjs.map +3 -3
- package/dist/cjs/production/index.cjs +2 -2
- package/dist/esm/development/index.mjs +639 -598
- package/dist/esm/development/index.mjs.map +3 -3
- package/dist/esm/production/index.mjs +2 -2
- package/dist/types/context.d.ts +25 -23
- package/dist/types/tree/primitives.d.ts +10 -1
- package/dist/types/tree/serialize.d.ts +21 -2
- package/dist/types/tree/shared.d.ts +0 -3
- package/dist/types/tree/types.d.ts +38 -57
- package/package.json +2 -2
- package/src/context.ts +53 -49
- package/src/index.ts +37 -36
- package/src/tree/async.ts +112 -49
- package/src/tree/primitives.ts +143 -7
- package/src/tree/serialize.ts +359 -360
- package/src/tree/shared.ts +0 -13
- package/src/tree/sync.ts +95 -46
- package/src/tree/types.ts +53 -202
package/src/tree/serialize.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
markRef,
|
|
8
8
|
} from '../context';
|
|
9
9
|
import quote from '../quote';
|
|
10
|
-
import { isReferenceInStack } from './shared';
|
|
11
10
|
import {
|
|
12
11
|
SerovalAggregateErrorNode,
|
|
13
12
|
SerovalArrayNode,
|
|
@@ -20,18 +19,19 @@ import {
|
|
|
20
19
|
SerovalNullConstructorNode,
|
|
21
20
|
SerovalObjectRecordNode,
|
|
22
21
|
SerovalPromiseNode,
|
|
22
|
+
SerovalReferenceNode,
|
|
23
23
|
SerovalSetNode,
|
|
24
24
|
SerovalTypedArrayNode,
|
|
25
25
|
} from './types';
|
|
26
26
|
|
|
27
27
|
function getAssignmentExpression(assignment: Assignment): string {
|
|
28
|
-
switch (assignment.
|
|
28
|
+
switch (assignment.t) {
|
|
29
29
|
case 'index':
|
|
30
|
-
return `${assignment.
|
|
30
|
+
return `${assignment.s}=${assignment.v}`;
|
|
31
31
|
case 'map':
|
|
32
|
-
return `${assignment.
|
|
32
|
+
return `${assignment.s}.set(${assignment.k},${assignment.v})`;
|
|
33
33
|
case 'set':
|
|
34
|
-
return `${assignment.
|
|
34
|
+
return `${assignment.s}.add(${assignment.v})`;
|
|
35
35
|
default:
|
|
36
36
|
return '';
|
|
37
37
|
}
|
|
@@ -43,29 +43,31 @@ function mergeAssignments(assignments: Assignment[]) {
|
|
|
43
43
|
let prev = current;
|
|
44
44
|
for (let i = 1, len = assignments.length; i < len; i++) {
|
|
45
45
|
const item = assignments[i];
|
|
46
|
-
if (item.
|
|
47
|
-
if (item.
|
|
46
|
+
if (item.t === prev.t) {
|
|
47
|
+
if (item.t === 'index' && item.v === prev.v) {
|
|
48
48
|
// Merge if the right-hand value is the same
|
|
49
49
|
// saves at least 2 chars
|
|
50
50
|
current = {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
t: 'index',
|
|
52
|
+
s: item.s,
|
|
53
|
+
k: undefined,
|
|
54
|
+
v: getAssignmentExpression(current),
|
|
54
55
|
};
|
|
55
|
-
} else if (item.
|
|
56
|
+
} else if (item.t === 'map' && item.s === prev.s) {
|
|
56
57
|
// Maps has chaining methods, merge if source is the same
|
|
57
58
|
current = {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
t: 'map',
|
|
60
|
+
s: getAssignmentExpression(current),
|
|
61
|
+
k: item.k,
|
|
62
|
+
v: item.v,
|
|
62
63
|
};
|
|
63
|
-
} else if (item.
|
|
64
|
+
} else if (item.t === 'set' && item.s === prev.s) {
|
|
64
65
|
// Sets has chaining methods too
|
|
65
66
|
current = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
t: 'set',
|
|
68
|
+
s: getAssignmentExpression(current),
|
|
69
|
+
k: undefined,
|
|
70
|
+
v: item.v,
|
|
69
71
|
};
|
|
70
72
|
} else {
|
|
71
73
|
// Different assignment, push current
|
|
@@ -105,16 +107,6 @@ export function resolvePatches(ctx: SerializationContext) {
|
|
|
105
107
|
* This is different from the assignments array as this one
|
|
106
108
|
* signifies creation rather than mutation
|
|
107
109
|
*/
|
|
108
|
-
function assignRef(
|
|
109
|
-
ctx: SerializationContext,
|
|
110
|
-
index: number,
|
|
111
|
-
value: string,
|
|
112
|
-
) {
|
|
113
|
-
if (ctx.markedRefs[index]) {
|
|
114
|
-
return `${getRefParam(ctx, index)}=${value}`;
|
|
115
|
-
}
|
|
116
|
-
return value;
|
|
117
|
-
}
|
|
118
110
|
|
|
119
111
|
function createAssignment(
|
|
120
112
|
ctx: SerializationContext,
|
|
@@ -122,9 +114,10 @@ function createAssignment(
|
|
|
122
114
|
value: string,
|
|
123
115
|
) {
|
|
124
116
|
ctx.assignments.push({
|
|
125
|
-
|
|
126
|
-
source,
|
|
127
|
-
|
|
117
|
+
t: 'index',
|
|
118
|
+
s: source,
|
|
119
|
+
k: undefined,
|
|
120
|
+
v: value,
|
|
128
121
|
});
|
|
129
122
|
}
|
|
130
123
|
|
|
@@ -135,9 +128,10 @@ function createSetAdd(
|
|
|
135
128
|
) {
|
|
136
129
|
markRef(ctx, ref);
|
|
137
130
|
ctx.assignments.push({
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
t: 'set',
|
|
132
|
+
s: getRefParam(ctx, ref),
|
|
133
|
+
k: undefined,
|
|
134
|
+
v: value,
|
|
141
135
|
});
|
|
142
136
|
}
|
|
143
137
|
|
|
@@ -149,10 +143,10 @@ function createMapSet(
|
|
|
149
143
|
) {
|
|
150
144
|
markRef(ctx, ref);
|
|
151
145
|
ctx.assignments.push({
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
key,
|
|
155
|
-
value,
|
|
146
|
+
t: 'map',
|
|
147
|
+
s: getRefParam(ctx, ref),
|
|
148
|
+
k: key,
|
|
149
|
+
v: value,
|
|
156
150
|
});
|
|
157
151
|
}
|
|
158
152
|
|
|
@@ -178,374 +172,379 @@ function createObjectAssign(
|
|
|
178
172
|
|
|
179
173
|
const IDENTIFIER_CHECK = /^([$A-Z_][0-9A-Z_$]*)$/i;
|
|
180
174
|
|
|
181
|
-
|
|
182
|
-
ctx: SerializationContext
|
|
183
|
-
targetRef: number,
|
|
184
|
-
node: SerovalObjectRecordNode,
|
|
185
|
-
) {
|
|
186
|
-
ctx.stack.push(targetRef);
|
|
187
|
-
const mainAssignments: Assignment[] = [];
|
|
188
|
-
for (let i = 0; i < node.s; i++) {
|
|
189
|
-
const parentStack = ctx.stack;
|
|
190
|
-
ctx.stack = [];
|
|
191
|
-
const refParam = serializeTree(ctx, node.v[i]);
|
|
192
|
-
ctx.stack = parentStack;
|
|
193
|
-
const key = node.k[i];
|
|
194
|
-
const check = Number(key);
|
|
195
|
-
const parentAssignment = ctx.assignments;
|
|
196
|
-
ctx.assignments = mainAssignments;
|
|
197
|
-
// Test if key is a valid number or JS identifier
|
|
198
|
-
// so that we don't have to serialize the key and wrap with brackets
|
|
199
|
-
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
|
|
200
|
-
if (isIdentifier && Number.isNaN(check)) {
|
|
201
|
-
createObjectAssign(ctx, targetRef, key, refParam);
|
|
202
|
-
} else {
|
|
203
|
-
createArrayAssign(ctx, targetRef, isIdentifier ? key : quote(key), refParam);
|
|
204
|
-
}
|
|
205
|
-
ctx.assignments = parentAssignment;
|
|
206
|
-
}
|
|
207
|
-
ctx.stack.pop();
|
|
208
|
-
return resolveAssignments(mainAssignments);
|
|
209
|
-
}
|
|
175
|
+
export default class SerovalSerializer {
|
|
176
|
+
ctx: SerializationContext;
|
|
210
177
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
sourceID: number,
|
|
214
|
-
node: SerovalObjectRecordNode,
|
|
215
|
-
) {
|
|
216
|
-
if (node.s === 0) {
|
|
217
|
-
return '{}';
|
|
178
|
+
constructor(ctx: SerializationContext) {
|
|
179
|
+
this.ctx = ctx;
|
|
218
180
|
}
|
|
219
|
-
let result = '';
|
|
220
|
-
ctx.stack.push(sourceID);
|
|
221
|
-
for (let i = 0; i < node.s; i++) {
|
|
222
|
-
const key = node.k[i];
|
|
223
|
-
const val = node.v[i];
|
|
224
|
-
const check = Number(key);
|
|
225
|
-
// Test if key is a valid number or JS identifier
|
|
226
|
-
// so that we don't have to serialize the key and wrap with brackets
|
|
227
|
-
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
|
|
228
|
-
if (isReferenceInStack(ctx, val)) {
|
|
229
|
-
const refParam = getRefParam(ctx, val.i);
|
|
230
|
-
if (isIdentifier && Number.isNaN(check)) {
|
|
231
|
-
createObjectAssign(ctx, sourceID, key, refParam);
|
|
232
|
-
} else {
|
|
233
|
-
createArrayAssign(ctx, sourceID, isIdentifier ? key : quote(key), refParam);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
result += `${isIdentifier ? key : quote(key)}:${serializeTree(ctx, val)},`;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
ctx.stack.pop();
|
|
240
|
-
return `{${result.substring(0, result.length - 1)}}`;
|
|
241
|
-
}
|
|
242
181
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Check if resolved value is a parent expression
|
|
249
|
-
if (isReferenceInStack(ctx, node.n)) {
|
|
250
|
-
// A Promise trick, reference the value
|
|
251
|
-
// inside the `then` expression so that
|
|
252
|
-
// the Promise evaluates after the parent
|
|
253
|
-
// has initialized
|
|
254
|
-
serialized = `Promise.resolve().then(()=>${getRefParam(ctx, node.n.i)})`;
|
|
255
|
-
} else {
|
|
256
|
-
ctx.stack.push(node.i);
|
|
257
|
-
const result = serializeTree(ctx, node.n);
|
|
258
|
-
ctx.stack.pop();
|
|
259
|
-
// just inline the value/reference here
|
|
260
|
-
serialized = `Promise.resolve(${result})`;
|
|
182
|
+
assignRef(index: number, value: string) {
|
|
183
|
+
if (this.ctx.markedRefs.has(index)) {
|
|
184
|
+
return `${getRefParam(this.ctx, index)}=${value}`;
|
|
185
|
+
}
|
|
186
|
+
return value;
|
|
261
187
|
}
|
|
262
|
-
return assignRef(ctx, node.i, serialized);
|
|
263
|
-
}
|
|
264
188
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
)
|
|
269
|
-
let args = `[${node.s}]`;
|
|
270
|
-
if (node.l !== 0) {
|
|
271
|
-
args += `,${node.l}`;
|
|
189
|
+
isReferenceInStack(
|
|
190
|
+
node: SerovalNode,
|
|
191
|
+
): node is SerovalReferenceNode {
|
|
192
|
+
return node.t === SerovalNodeType.Reference && this.ctx.stack.includes(node.i);
|
|
272
193
|
}
|
|
273
|
-
return assignRef(ctx, node.i, `new ${node.c}(${args})`);
|
|
274
|
-
}
|
|
275
194
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
let
|
|
284
|
-
ctx.stack.push(node.i);
|
|
195
|
+
serializeNodeList(
|
|
196
|
+
node: SerovalArrayNode | SerovalIterableNode,
|
|
197
|
+
) {
|
|
198
|
+
// This is different than Map and Set
|
|
199
|
+
// because we also need to serialize
|
|
200
|
+
// the holes of the Array
|
|
201
|
+
const size = node.a.length;
|
|
202
|
+
let values = '';
|
|
285
203
|
for (let i = 0; i < size; i++) {
|
|
286
204
|
const item = node.a[i];
|
|
287
|
-
if
|
|
288
|
-
|
|
205
|
+
// Check if index is a hole
|
|
206
|
+
if (item) {
|
|
207
|
+
// Check if item is a parent
|
|
208
|
+
if (this.isReferenceInStack(item)) {
|
|
209
|
+
createArrayAssign(this.ctx, node.i, i, getRefParam(this.ctx, item.i));
|
|
210
|
+
values += ',';
|
|
211
|
+
} else {
|
|
212
|
+
values += this.serialize(item);
|
|
213
|
+
if (i < size - 1) {
|
|
214
|
+
values += ',';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
289
217
|
} else {
|
|
290
|
-
//
|
|
291
|
-
|
|
218
|
+
// Add an empty item
|
|
219
|
+
values += ',';
|
|
292
220
|
}
|
|
293
221
|
}
|
|
294
|
-
|
|
295
|
-
if (result) {
|
|
296
|
-
serialized += `([${result.substring(0, result.length - 1)}])`;
|
|
297
|
-
}
|
|
222
|
+
return `[${values}]`;
|
|
298
223
|
}
|
|
299
|
-
return assignRef(ctx, node.i, serialized);
|
|
300
|
-
}
|
|
301
224
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
|
|
225
|
+
serializeArray(
|
|
226
|
+
node: SerovalArrayNode,
|
|
227
|
+
) {
|
|
228
|
+
this.ctx.stack.push(node.i);
|
|
229
|
+
const result = this.serializeNodeList(node);
|
|
230
|
+
this.ctx.stack.pop();
|
|
231
|
+
return this.assignRef(node.i, result);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
serializeObject(
|
|
235
|
+
sourceID: number,
|
|
236
|
+
node: SerovalObjectRecordNode,
|
|
237
|
+
) {
|
|
238
|
+
if (node.s === 0) {
|
|
239
|
+
return '{}';
|
|
240
|
+
}
|
|
308
241
|
let result = '';
|
|
309
|
-
ctx.stack.push(
|
|
310
|
-
for (let i = 0; i < node.
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
if
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
// both key and value are a parent of this
|
|
322
|
-
// Map instance
|
|
323
|
-
createMapSet(ctx, node.i, keyRef, valueRef);
|
|
242
|
+
this.ctx.stack.push(sourceID);
|
|
243
|
+
for (let i = 0; i < node.s; i++) {
|
|
244
|
+
const key = node.k[i];
|
|
245
|
+
const val = node.v[i];
|
|
246
|
+
const check = Number(key);
|
|
247
|
+
// Test if key is a valid number or JS identifier
|
|
248
|
+
// so that we don't have to serialize the key and wrap with brackets
|
|
249
|
+
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
|
|
250
|
+
if (this.isReferenceInStack(val)) {
|
|
251
|
+
const refParam = getRefParam(this.ctx, val.i);
|
|
252
|
+
if (isIdentifier && Number.isNaN(check)) {
|
|
253
|
+
createObjectAssign(this.ctx, sourceID, key, refParam);
|
|
324
254
|
} else {
|
|
325
|
-
|
|
326
|
-
// This is required because the serialized
|
|
327
|
-
// value is no longer part of the expression
|
|
328
|
-
// tree and has been moved to the deferred
|
|
329
|
-
// assignment
|
|
330
|
-
const parent = ctx.stack;
|
|
331
|
-
ctx.stack = [];
|
|
332
|
-
createMapSet(ctx, node.i, keyRef, serializeTree(ctx, val));
|
|
333
|
-
ctx.stack = parent;
|
|
255
|
+
createArrayAssign(this.ctx, sourceID, isIdentifier ? key : quote(key), refParam);
|
|
334
256
|
}
|
|
335
|
-
} else if (isReferenceInStack(ctx, val)) {
|
|
336
|
-
// Create ref for the Map instance
|
|
337
|
-
const valueRef = getRefParam(ctx, val.i);
|
|
338
|
-
// Reset stack for the key serialization
|
|
339
|
-
const parent = ctx.stack;
|
|
340
|
-
ctx.stack = [];
|
|
341
|
-
createMapSet(ctx, node.i, serializeTree(ctx, key), valueRef);
|
|
342
|
-
ctx.stack = parent;
|
|
343
257
|
} else {
|
|
344
|
-
result +=
|
|
258
|
+
result += `${isIdentifier ? key : quote(key)}:${this.serialize(val)},`;
|
|
345
259
|
}
|
|
346
260
|
}
|
|
347
|
-
ctx.stack.pop();
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
261
|
+
this.ctx.stack.pop();
|
|
262
|
+
return `{${result.substring(0, result.length - 1)}}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
serializeWithObjectAssign(
|
|
266
|
+
value: SerovalObjectRecordNode,
|
|
267
|
+
id: number,
|
|
268
|
+
serialized: string,
|
|
269
|
+
) {
|
|
270
|
+
const fields = this.serializeObject(id, value);
|
|
271
|
+
if (fields !== '{}') {
|
|
272
|
+
return `Object.assign(${serialized},${fields})`;
|
|
353
273
|
}
|
|
274
|
+
return serialized;
|
|
354
275
|
}
|
|
355
|
-
return assignRef(ctx, node.i, serialized);
|
|
356
|
-
}
|
|
357
276
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
277
|
+
serializeAssignments(
|
|
278
|
+
sourceID: number,
|
|
279
|
+
node: SerovalObjectRecordNode,
|
|
280
|
+
) {
|
|
281
|
+
this.ctx.stack.push(sourceID);
|
|
282
|
+
const mainAssignments: Assignment[] = [];
|
|
283
|
+
for (let i = 0; i < node.s; i++) {
|
|
284
|
+
const parentStack = this.ctx.stack;
|
|
285
|
+
this.ctx.stack = [];
|
|
286
|
+
const refParam = this.serialize(node.v[i]);
|
|
287
|
+
this.ctx.stack = parentStack;
|
|
288
|
+
const key = node.k[i];
|
|
289
|
+
const check = Number(key);
|
|
290
|
+
const parentAssignment = this.ctx.assignments;
|
|
291
|
+
this.ctx.assignments = mainAssignments;
|
|
292
|
+
// Test if key is a valid number or JS identifier
|
|
293
|
+
// so that we don't have to serialize the key and wrap with brackets
|
|
294
|
+
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
|
|
295
|
+
if (isIdentifier && Number.isNaN(check)) {
|
|
296
|
+
createObjectAssign(this.ctx, sourceID, key, refParam);
|
|
375
297
|
} else {
|
|
376
|
-
|
|
377
|
-
if (i < size - 1) {
|
|
378
|
-
values += ',';
|
|
379
|
-
}
|
|
298
|
+
createArrayAssign(this.ctx, sourceID, isIdentifier ? key : quote(key), refParam);
|
|
380
299
|
}
|
|
300
|
+
this.ctx.assignments = parentAssignment;
|
|
301
|
+
}
|
|
302
|
+
this.ctx.stack.pop();
|
|
303
|
+
return resolveAssignments(mainAssignments);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
serializeNullConstructor(
|
|
307
|
+
node: SerovalNullConstructorNode,
|
|
308
|
+
) {
|
|
309
|
+
let serialized = 'Object.create(null)';
|
|
310
|
+
if (this.ctx.features & Feature.ObjectAssign) {
|
|
311
|
+
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized);
|
|
381
312
|
} else {
|
|
382
|
-
|
|
383
|
-
|
|
313
|
+
markRef(this.ctx, node.i);
|
|
314
|
+
const assignments = this.serializeAssignments(node.i, node.d);
|
|
315
|
+
if (assignments) {
|
|
316
|
+
const ref = getRefParam(this.ctx, node.i);
|
|
317
|
+
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`;
|
|
318
|
+
}
|
|
384
319
|
}
|
|
320
|
+
return this.assignRef(node.i, serialized);
|
|
385
321
|
}
|
|
386
|
-
return `[${values}]`;
|
|
387
|
-
}
|
|
388
322
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
323
|
+
serializeSet(
|
|
324
|
+
node: SerovalSetNode,
|
|
325
|
+
) {
|
|
326
|
+
let serialized = 'new Set';
|
|
327
|
+
const size = node.a.length;
|
|
328
|
+
if (size) {
|
|
329
|
+
let result = '';
|
|
330
|
+
this.ctx.stack.push(node.i);
|
|
331
|
+
for (let i = 0; i < size; i++) {
|
|
332
|
+
const item = node.a[i];
|
|
333
|
+
if (this.isReferenceInStack(item)) {
|
|
334
|
+
createSetAdd(this.ctx, node.i, getRefParam(this.ctx, item.i));
|
|
335
|
+
} else {
|
|
336
|
+
// Push directly
|
|
337
|
+
result += `${this.serialize(item)},`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
this.ctx.stack.pop();
|
|
341
|
+
if (result) {
|
|
342
|
+
serialized += `([${result.substring(0, result.length - 1)}])`;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return this.assignRef(node.i, serialized);
|
|
346
|
+
}
|
|
398
347
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
348
|
+
serializeMap(
|
|
349
|
+
node: SerovalMapNode,
|
|
350
|
+
) {
|
|
351
|
+
let serialized = 'new Map';
|
|
352
|
+
if (node.d.s) {
|
|
353
|
+
let result = '';
|
|
354
|
+
this.ctx.stack.push(node.i);
|
|
355
|
+
for (let i = 0; i < node.d.s; i++) {
|
|
356
|
+
// Check if key is a parent
|
|
357
|
+
const key = node.d.k[i];
|
|
358
|
+
const val = node.d.v[i];
|
|
359
|
+
if (this.isReferenceInStack(key)) {
|
|
360
|
+
// Create reference for the map instance
|
|
361
|
+
const keyRef = getRefParam(this.ctx, key.i);
|
|
362
|
+
// Check if value is a parent
|
|
363
|
+
if (this.isReferenceInStack(val)) {
|
|
364
|
+
const valueRef = getRefParam(this.ctx, val.i);
|
|
365
|
+
// Register an assignment since
|
|
366
|
+
// both key and value are a parent of this
|
|
367
|
+
// Map instance
|
|
368
|
+
createMapSet(this.ctx, node.i, keyRef, valueRef);
|
|
369
|
+
} else {
|
|
370
|
+
// Reset the stack
|
|
371
|
+
// This is required because the serialized
|
|
372
|
+
// value is no longer part of the expression
|
|
373
|
+
// tree and has been moved to the deferred
|
|
374
|
+
// assignment
|
|
375
|
+
const parent = this.ctx.stack;
|
|
376
|
+
this.ctx.stack = [];
|
|
377
|
+
createMapSet(this.ctx, node.i, keyRef, this.serialize(val));
|
|
378
|
+
this.ctx.stack = parent;
|
|
379
|
+
}
|
|
380
|
+
} else if (this.isReferenceInStack(val)) {
|
|
381
|
+
// Create ref for the Map instance
|
|
382
|
+
const valueRef = getRefParam(this.ctx, val.i);
|
|
383
|
+
// Reset stack for the key serialization
|
|
384
|
+
const parent = this.ctx.stack;
|
|
385
|
+
this.ctx.stack = [];
|
|
386
|
+
createMapSet(this.ctx, node.i, this.serialize(key), valueRef);
|
|
387
|
+
this.ctx.stack = parent;
|
|
388
|
+
} else {
|
|
389
|
+
result += `[${this.serialize(key)},${this.serialize(val)}],`;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
this.ctx.stack.pop();
|
|
393
|
+
// Check if there are any values
|
|
394
|
+
// so that the empty Map constructor
|
|
395
|
+
// can be used instead
|
|
396
|
+
if (result) {
|
|
397
|
+
serialized += `([${result.substring(0, result.length - 1)}])`;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return this.assignRef(node.i, serialized);
|
|
408
401
|
}
|
|
409
|
-
return serialized;
|
|
410
|
-
}
|
|
411
402
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
403
|
+
serializeAggregateError(
|
|
404
|
+
node: SerovalAggregateErrorNode,
|
|
405
|
+
) {
|
|
406
|
+
// Serialize the required arguments
|
|
407
|
+
this.ctx.stack.push(node.i);
|
|
408
|
+
let serialized = `new AggregateError(${this.serialize(node.n)},${quote(node.m)})`;
|
|
409
|
+
this.ctx.stack.pop();
|
|
410
|
+
// `AggregateError` might've been extended
|
|
411
|
+
// either through class or custom properties
|
|
412
|
+
// Make sure to assign extra properties
|
|
413
|
+
if (node.d) {
|
|
414
|
+
if (this.ctx.features & Feature.ObjectAssign) {
|
|
415
|
+
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized);
|
|
416
|
+
} else {
|
|
417
|
+
markRef(this.ctx, node.i);
|
|
418
|
+
const assignments = this.serializeAssignments(node.i, node.d);
|
|
419
|
+
if (assignments) {
|
|
420
|
+
const ref = getRefParam(this.ctx, node.i);
|
|
421
|
+
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`;
|
|
422
|
+
}
|
|
432
423
|
}
|
|
433
424
|
}
|
|
425
|
+
return this.assignRef(node.i, serialized);
|
|
434
426
|
}
|
|
435
|
-
return assignRef(ctx, node.i, serialized);
|
|
436
|
-
}
|
|
437
427
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
428
|
+
serializeError(
|
|
429
|
+
node: SerovalErrorNode,
|
|
430
|
+
) {
|
|
431
|
+
let serialized = `new ${node.c}(${quote(node.m)})`;
|
|
432
|
+
if (node.d) {
|
|
433
|
+
if (this.ctx.features & Feature.ObjectAssign) {
|
|
434
|
+
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized);
|
|
435
|
+
} else {
|
|
436
|
+
markRef(this.ctx, node.i);
|
|
437
|
+
const assignments = this.serializeAssignments(node.i, node.d);
|
|
438
|
+
if (assignments) {
|
|
439
|
+
const ref = getRefParam(this.ctx, node.i);
|
|
440
|
+
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`;
|
|
441
|
+
}
|
|
452
442
|
}
|
|
453
443
|
}
|
|
444
|
+
return this.assignRef(node.i, serialized);
|
|
454
445
|
}
|
|
455
|
-
return assignRef(ctx, node.i, serialized);
|
|
456
|
-
}
|
|
457
446
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
447
|
+
serializePromise(
|
|
448
|
+
node: SerovalPromiseNode,
|
|
449
|
+
) {
|
|
450
|
+
let serialized: string;
|
|
451
|
+
// Check if resolved value is a parent expression
|
|
452
|
+
if (this.isReferenceInStack(node.n)) {
|
|
453
|
+
// A Promise trick, reference the value
|
|
454
|
+
// inside the `then` expression so that
|
|
455
|
+
// the Promise evaluates after the parent
|
|
456
|
+
// has initialized
|
|
457
|
+
serialized = `Promise.resolve().then(()=>${getRefParam(this.ctx, node.n.i)})`;
|
|
458
|
+
} else {
|
|
459
|
+
this.ctx.stack.push(node.i);
|
|
460
|
+
const result = this.serialize(node.n);
|
|
461
|
+
this.ctx.stack.pop();
|
|
462
|
+
// just inline the value/reference here
|
|
463
|
+
serialized = `Promise.resolve(${result})`;
|
|
464
|
+
}
|
|
465
|
+
return this.assignRef(node.i, serialized);
|
|
471
466
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
467
|
+
|
|
468
|
+
serializeTypedArray(
|
|
469
|
+
node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
|
|
470
|
+
) {
|
|
471
|
+
let args = `[${node.s}]`;
|
|
472
|
+
if (node.l !== 0) {
|
|
473
|
+
args += `,${node.l}`;
|
|
474
|
+
}
|
|
475
|
+
return this.assignRef(node.i, `new ${node.c}(${args})`);
|
|
478
476
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
477
|
+
|
|
478
|
+
serializeIterable(
|
|
479
|
+
node: SerovalIterableNode,
|
|
480
|
+
) {
|
|
481
|
+
const parent = this.ctx.stack;
|
|
482
|
+
this.ctx.stack = [];
|
|
483
|
+
const values = this.serializeNodeList(node);
|
|
484
|
+
this.ctx.stack = parent;
|
|
485
|
+
let serialized: string;
|
|
486
|
+
if (this.ctx.features & Feature.ArrayPrototypeValues) {
|
|
487
|
+
serialized = `${values}.values()`;
|
|
482
488
|
} else {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
489
|
+
serialized = `${values}[Symbol.iterator]()`;
|
|
490
|
+
}
|
|
491
|
+
if (this.ctx.features & Feature.ArrowFunction) {
|
|
492
|
+
serialized = `{[Symbol.iterator]:()=>${serialized}}`;
|
|
493
|
+
} else if (this.ctx.features & Feature.MethodShorthand) {
|
|
494
|
+
serialized = `{[Symbol.iterator](){return ${serialized}}}`;
|
|
495
|
+
} else {
|
|
496
|
+
serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`;
|
|
497
|
+
}
|
|
498
|
+
if (node.d) {
|
|
499
|
+
if (this.ctx.features & Feature.ObjectAssign) {
|
|
500
|
+
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized);
|
|
501
|
+
} else {
|
|
502
|
+
markRef(this.ctx, node.i);
|
|
503
|
+
const assignments = this.serializeAssignments(node.i, node.d);
|
|
504
|
+
if (assignments) {
|
|
505
|
+
const ref = getRefParam(this.ctx, node.i);
|
|
506
|
+
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`;
|
|
507
|
+
}
|
|
488
508
|
}
|
|
489
509
|
}
|
|
510
|
+
return this.assignRef(node.i, serialized);
|
|
490
511
|
}
|
|
491
|
-
return assignRef(ctx, node.i, serialized);
|
|
492
|
-
}
|
|
493
512
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
513
|
+
serialize(node: SerovalNode): string {
|
|
514
|
+
switch (node.t) {
|
|
515
|
+
case SerovalNodeType.Primitive:
|
|
516
|
+
return String(node.s);
|
|
517
|
+
case SerovalNodeType.BigInt:
|
|
518
|
+
return node.s;
|
|
519
|
+
case SerovalNodeType.Reference:
|
|
520
|
+
return getRefParam(this.ctx, node.i);
|
|
521
|
+
case SerovalNodeType.Array:
|
|
522
|
+
return this.serializeArray(node);
|
|
523
|
+
case SerovalNodeType.Object:
|
|
524
|
+
return this.assignRef(node.i, this.serializeObject(node.i, node.d));
|
|
525
|
+
case SerovalNodeType.NullConstructor:
|
|
526
|
+
return this.serializeNullConstructor(node);
|
|
527
|
+
case SerovalNodeType.Date:
|
|
528
|
+
return this.assignRef(node.i, `new Date("${node.s}")`);
|
|
529
|
+
case SerovalNodeType.RegExp:
|
|
530
|
+
return this.assignRef(node.i, node.s);
|
|
531
|
+
case SerovalNodeType.Set:
|
|
532
|
+
return this.serializeSet(node);
|
|
533
|
+
case SerovalNodeType.Map:
|
|
534
|
+
return this.serializeMap(node);
|
|
535
|
+
case SerovalNodeType.BigIntTypedArray:
|
|
536
|
+
case SerovalNodeType.TypedArray:
|
|
537
|
+
return this.serializeTypedArray(node);
|
|
538
|
+
case SerovalNodeType.AggregateError:
|
|
539
|
+
return this.serializeAggregateError(node);
|
|
540
|
+
case SerovalNodeType.Error:
|
|
541
|
+
return this.serializeError(node);
|
|
542
|
+
case SerovalNodeType.Iterable:
|
|
543
|
+
return this.serializeIterable(node);
|
|
544
|
+
case SerovalNodeType.Promise:
|
|
545
|
+
return this.serializePromise(node);
|
|
546
|
+
default:
|
|
547
|
+
throw new Error('Unsupported type');
|
|
507
548
|
}
|
|
508
549
|
}
|
|
509
|
-
return assignRef(ctx, node.i, serialized);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
export default function serializeTree(
|
|
513
|
-
ctx: SerializationContext,
|
|
514
|
-
node: SerovalNode,
|
|
515
|
-
): string {
|
|
516
|
-
switch (node.t) {
|
|
517
|
-
case SerovalNodeType.Primitive:
|
|
518
|
-
return String(node.s);
|
|
519
|
-
case SerovalNodeType.Reference:
|
|
520
|
-
return getRefParam(ctx, node.i);
|
|
521
|
-
case SerovalNodeType.Promise:
|
|
522
|
-
return serializePromise(ctx, node);
|
|
523
|
-
case SerovalNodeType.BigInt:
|
|
524
|
-
return node.s;
|
|
525
|
-
case SerovalNodeType.Date:
|
|
526
|
-
return assignRef(ctx, node.i, `new Date("${node.s}")`);
|
|
527
|
-
case SerovalNodeType.RegExp:
|
|
528
|
-
return assignRef(ctx, node.i, node.s);
|
|
529
|
-
case SerovalNodeType.BigIntTypedArray:
|
|
530
|
-
case SerovalNodeType.TypedArray:
|
|
531
|
-
return serializeTypedArray(ctx, node);
|
|
532
|
-
case SerovalNodeType.Set:
|
|
533
|
-
return serializeSet(ctx, node);
|
|
534
|
-
case SerovalNodeType.Map:
|
|
535
|
-
return serializeMap(ctx, node);
|
|
536
|
-
case SerovalNodeType.Array:
|
|
537
|
-
return serializeArray(ctx, node);
|
|
538
|
-
case SerovalNodeType.AggregateError:
|
|
539
|
-
return serializeAggregateError(ctx, node);
|
|
540
|
-
case SerovalNodeType.Error:
|
|
541
|
-
return serializeError(ctx, node);
|
|
542
|
-
case SerovalNodeType.Iterable:
|
|
543
|
-
return serializeIterable(ctx, node);
|
|
544
|
-
case SerovalNodeType.NullConstructor:
|
|
545
|
-
return serializeNullConstructor(ctx, node);
|
|
546
|
-
case SerovalNodeType.Object:
|
|
547
|
-
return assignRef(ctx, node.i, serializeObject(ctx, node.i, node.d));
|
|
548
|
-
default:
|
|
549
|
-
throw new Error('Unsupported type');
|
|
550
|
-
}
|
|
551
550
|
}
|