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