seroval 0.6.0 → 0.8.0

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.
Files changed (45) hide show
  1. package/README.md +15 -3
  2. package/dist/cjs/development/index.cjs +1431 -991
  3. package/dist/cjs/development/index.cjs.map +4 -4
  4. package/dist/cjs/production/index.cjs +3 -3
  5. package/dist/esm/development/index.mjs +1431 -991
  6. package/dist/esm/development/index.mjs.map +4 -4
  7. package/dist/esm/production/index.mjs +3 -3
  8. package/dist/types/assert.d.ts +1 -1
  9. package/dist/types/compat.d.ts +1 -1
  10. package/dist/types/context.d.ts +13 -7
  11. package/dist/types/index.d.ts +3 -4
  12. package/dist/types/string.d.ts +3 -0
  13. package/dist/types/tree/UnsupportedTypeError.d.ts +3 -0
  14. package/dist/types/tree/async.d.ts +3 -3
  15. package/dist/types/tree/constants.d.ts +11 -0
  16. package/dist/types/tree/deserialize.d.ts +2 -2
  17. package/dist/types/tree/primitives.d.ts +11 -14
  18. package/dist/types/tree/serialize.d.ts +2 -2
  19. package/dist/types/tree/shared.d.ts +10 -8
  20. package/dist/types/tree/sync.d.ts +3 -3
  21. package/dist/types/tree/types.d.ts +106 -70
  22. package/dist/types/tree/web-api.d.ts +4 -2
  23. package/dist/types/types.d.ts +4 -4
  24. package/package.json +11 -8
  25. package/src/assert.ts +2 -2
  26. package/src/compat.ts +1 -2
  27. package/src/context.ts +24 -13
  28. package/src/get-identifier.ts +1 -1
  29. package/src/index.ts +35 -26
  30. package/src/string.ts +53 -0
  31. package/src/tree/UnsupportedTypeError.ts +8 -0
  32. package/src/tree/async.ts +313 -222
  33. package/src/tree/constants.ts +72 -0
  34. package/src/tree/deserialize.ts +239 -119
  35. package/src/tree/primitives.ts +154 -143
  36. package/src/tree/reference.ts +4 -3
  37. package/src/tree/serialize.ts +394 -170
  38. package/src/tree/shared.ts +31 -22
  39. package/src/tree/symbols.ts +13 -13
  40. package/src/tree/sync.ts +288 -191
  41. package/src/tree/types.ts +173 -91
  42. package/src/tree/web-api.ts +59 -6
  43. package/src/types.ts +7 -3
  44. package/dist/types/quote.d.ts +0 -2
  45. package/src/quote.ts +0 -56
@@ -1,48 +1,68 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import { Feature } from '../compat';
3
- import {
3
+ import type {
4
4
  SerializationContext,
5
5
  Assignment,
6
+ } from '../context';
7
+ import {
6
8
  getRefParam,
7
9
  markRef,
8
10
  } from '../context';
9
- import quote from '../quote';
11
+ import { serializeConstant } from './constants';
10
12
  import { GLOBAL_KEY } from './reference';
11
13
  import { isValidIdentifier } from './shared';
12
14
  import { SYMBOL_STRING } from './symbols';
13
- import {
15
+ import type {
14
16
  SerovalAggregateErrorNode,
15
17
  SerovalArrayNode,
16
18
  SerovalBigIntTypedArrayNode,
17
19
  SerovalErrorNode,
18
- SerovalIterableNode,
19
20
  SerovalMapNode,
20
21
  SerovalNode,
21
- SerovalNodeType,
22
22
  SerovalNullConstructorNode,
23
23
  SerovalObjectNode,
24
- SerovalObjectRecordNode,
25
24
  SerovalPromiseNode,
26
25
  SerovalIndexedValueNode,
27
26
  SerovalSetNode,
28
27
  SerovalTypedArrayNode,
28
+ SerovalArrayBufferNode,
29
+ SerovalDataViewNode,
30
+ SerovalBlobNode,
31
+ SerovalFileNode,
32
+ SerovalHeadersNode,
33
+ SerovalRegExpNode,
34
+ SerovalDateNode,
35
+ SerovalURLNode,
36
+ SerovalURLSearchParamsNode,
37
+ SerovalReferenceNode,
38
+ SerovalFormDataNode,
39
+ SerovalObjectRecordNode,
40
+ SerovalObjectRecordKey,
41
+ SerovalBoxedNode,
42
+ SerovalWKSymbolNode,
43
+ } from './types';
44
+ import {
45
+ SerovalNodeType,
46
+ SerovalObjectRecordSpecialKey,
29
47
  } from './types';
30
48
 
31
49
  function getAssignmentExpression(assignment: Assignment): string {
32
50
  switch (assignment.t) {
33
51
  case 'index':
34
52
  return assignment.s + '=' + assignment.v;
35
- case 'map':
36
- return assignment.s + '.set(' + assignment.k + ',' + assignment.v + ')';
37
53
  case 'set':
54
+ return assignment.s + '.set(' + assignment.k + ',' + assignment.v + ')';
55
+ case 'add':
38
56
  return assignment.s + '.add(' + assignment.v + ')';
57
+ case 'append':
58
+ return assignment.s + '.append(' + assignment.k + ',' + assignment.v + ')';
39
59
  default:
40
60
  return '';
41
61
  }
42
62
  }
43
63
 
44
- function mergeAssignments(assignments: Assignment[]) {
45
- const newAssignments = [];
64
+ function mergeAssignments(assignments: Assignment[]): Assignment[] {
65
+ const newAssignments: Assignment[] = [];
46
66
  let current = assignments[0];
47
67
  let prev = current;
48
68
  let item: Assignment;
@@ -66,11 +86,11 @@ function mergeAssignments(assignments: Assignment[]) {
66
86
  current = item;
67
87
  }
68
88
  break;
69
- case 'map':
89
+ case 'set':
70
90
  if (item.s === prev.s) {
71
91
  // Maps has chaining methods, merge if source is the same
72
92
  current = {
73
- t: 'map',
93
+ t: 'set',
74
94
  s: getAssignmentExpression(current),
75
95
  k: item.k,
76
96
  v: item.v,
@@ -81,11 +101,11 @@ function mergeAssignments(assignments: Assignment[]) {
81
101
  current = item;
82
102
  }
83
103
  break;
84
- case 'set':
104
+ case 'add':
85
105
  if (item.s === prev.s) {
86
106
  // Sets has chaining methods too
87
107
  current = {
88
- t: 'set',
108
+ t: 'add',
89
109
  s: getAssignmentExpression(current),
90
110
  k: undefined,
91
111
  v: item.v,
@@ -96,6 +116,11 @@ function mergeAssignments(assignments: Assignment[]) {
96
116
  current = item;
97
117
  }
98
118
  break;
119
+ case 'append':
120
+ // Different assignment, push current
121
+ newAssignments.push(current);
122
+ current = item;
123
+ break;
99
124
  default:
100
125
  break;
101
126
  }
@@ -111,7 +136,7 @@ function mergeAssignments(assignments: Assignment[]) {
111
136
  return newAssignments;
112
137
  }
113
138
 
114
- function resolveAssignments(assignments: Assignment[]) {
139
+ function resolveAssignments(assignments: Assignment[]): string | undefined {
115
140
  if (assignments.length) {
116
141
  let result = '';
117
142
  const merged = mergeAssignments(assignments);
@@ -123,7 +148,7 @@ function resolveAssignments(assignments: Assignment[]) {
123
148
  return undefined;
124
149
  }
125
150
 
126
- export function resolvePatches(ctx: SerializationContext) {
151
+ export function resolvePatches(ctx: SerializationContext): string | undefined {
127
152
  return resolveAssignments(ctx.assignments);
128
153
  }
129
154
 
@@ -137,7 +162,7 @@ function createAssignment(
137
162
  ctx: SerializationContext,
138
163
  source: string,
139
164
  value: string,
140
- ) {
165
+ ): void {
141
166
  ctx.assignments.push({
142
167
  t: 'index',
143
168
  s: source,
@@ -146,29 +171,41 @@ function createAssignment(
146
171
  });
147
172
  }
148
173
 
149
- function createSetAdd(
174
+ function createAddAssignment(
150
175
  ctx: SerializationContext,
151
176
  ref: number,
152
177
  value: string,
153
- ) {
154
- markRef(ctx, ref);
178
+ ): void {
155
179
  ctx.assignments.push({
156
- t: 'set',
180
+ t: 'add',
157
181
  s: getRefParam(ctx, ref),
158
182
  k: undefined,
159
183
  v: value,
160
184
  });
161
185
  }
162
186
 
163
- function createMapSet(
187
+ function createSetAssignment(
164
188
  ctx: SerializationContext,
165
189
  ref: number,
166
190
  key: string,
167
191
  value: string,
168
- ) {
169
- markRef(ctx, ref);
192
+ ): void {
170
193
  ctx.assignments.push({
171
- t: 'map',
194
+ t: 'set',
195
+ s: getRefParam(ctx, ref),
196
+ k: key,
197
+ v: value,
198
+ });
199
+ }
200
+
201
+ function createAppendAssignment(
202
+ ctx: SerializationContext,
203
+ ref: number,
204
+ key: string,
205
+ value: string,
206
+ ): void {
207
+ ctx.assignments.push({
208
+ t: 'append',
172
209
  s: getRefParam(ctx, ref),
173
210
  k: key,
174
211
  v: value,
@@ -180,8 +217,7 @@ function createArrayAssign(
180
217
  ref: number,
181
218
  index: number | string,
182
219
  value: string,
183
- ) {
184
- markRef(ctx, ref);
220
+ ): void {
185
221
  createAssignment(ctx, getRefParam(ctx, ref) + '[' + index + ']', value);
186
222
  }
187
223
 
@@ -190,7 +226,7 @@ function createObjectAssign(
190
226
  ref: number,
191
227
  key: string,
192
228
  value: string,
193
- ) {
229
+ ): void {
194
230
  markRef(ctx, ref);
195
231
  createAssignment(ctx, getRefParam(ctx, ref) + '.' + key, value);
196
232
  }
@@ -199,7 +235,7 @@ function assignIndexedValue(
199
235
  ctx: SerializationContext,
200
236
  index: number,
201
237
  value: string,
202
- ) {
238
+ ): string {
203
239
  if (ctx.markedRefs.has(index)) {
204
240
  return getRefParam(ctx, index) + '=' + value;
205
241
  }
@@ -213,28 +249,31 @@ function isIndexedValueInStack(
213
249
  return node.t === SerovalNodeType.IndexedValue && ctx.stack.includes(node.i);
214
250
  }
215
251
 
216
- function serializeNodeList(
252
+ function serializeArray(
217
253
  ctx: SerializationContext,
218
- node: SerovalArrayNode | SerovalIterableNode | SerovalAggregateErrorNode,
219
- ) {
254
+ node: SerovalArrayNode,
255
+ ): string {
256
+ const id = node.i;
257
+ ctx.stack.push(id);
220
258
  // This is different than Map and Set
221
259
  // because we also need to serialize
222
260
  // the holes of the Array
223
- const size = node.l;
224
261
  let values = '';
225
- let item: SerovalNode;
262
+ let item: SerovalNode | undefined;
226
263
  let isHoley = false;
227
- for (let i = 0; i < size; i++) {
264
+ const list = node.a;
265
+ for (let i = 0, len = node.l; i < len; i++) {
228
266
  if (i !== 0) {
229
267
  // Add an empty item
230
268
  values += ',';
231
269
  }
232
- item = node.a[i];
270
+ item = list[i];
233
271
  // Check if index is a hole
234
272
  if (item) {
235
273
  // Check if item is a parent
236
274
  if (isIndexedValueInStack(ctx, item)) {
237
- createArrayAssign(ctx, node.i, i, getRefParam(ctx, item.i));
275
+ markRef(ctx, id);
276
+ createArrayAssign(ctx, id, i, getRefParam(ctx, item.i));
238
277
  isHoley = true;
239
278
  } else {
240
279
  values += serializeTree(ctx, item);
@@ -244,54 +283,81 @@ function serializeNodeList(
244
283
  isHoley = true;
245
284
  }
246
285
  }
247
- return '[' + values + (isHoley ? ',]' : ']');
286
+ ctx.stack.pop();
287
+ return assignIndexedValue(ctx, id, '[' + values + (isHoley ? ',]' : ']'));
248
288
  }
249
289
 
250
- function serializeArray(
290
+ function getIterableAccess(ctx: SerializationContext): string {
291
+ return ctx.features & Feature.ArrayPrototypeValues
292
+ ? '.values()'
293
+ : '[Symbol.iterator]()';
294
+ }
295
+
296
+ function serializeIterable(
251
297
  ctx: SerializationContext,
252
- node: SerovalArrayNode,
253
- ) {
254
- ctx.stack.push(node.i);
255
- const result = serializeNodeList(ctx, node);
256
- ctx.stack.pop();
257
- return assignIndexedValue(ctx, node.i, result);
298
+ node: SerovalNode,
299
+ ): string {
300
+ const parent = ctx.stack;
301
+ ctx.stack = [];
302
+ let serialized = serializeTree(ctx, node) + getIterableAccess(ctx);
303
+ ctx.stack = parent;
304
+ if (ctx.features & Feature.ArrowFunction) {
305
+ serialized = '[Symbol.iterator]:()=>' + serialized;
306
+ } else if (ctx.features & Feature.MethodShorthand) {
307
+ serialized = '[Symbol.iterator](){return ' + serialized + '}';
308
+ } else {
309
+ serialized = '[Symbol.iterator]:function(){return ' + serialized + '}';
310
+ }
311
+ return serialized;
258
312
  }
259
313
 
260
314
  function serializeProperties(
261
315
  ctx: SerializationContext,
262
316
  sourceID: number,
263
317
  node: SerovalObjectRecordNode,
264
- ) {
265
- if (node.s === 0) {
318
+ ): string {
319
+ const len = node.s;
320
+ if (len === 0) {
266
321
  return '{}';
267
322
  }
268
323
  let result = '';
269
324
  ctx.stack.push(sourceID);
270
- let key: string;
325
+ let key: SerovalObjectRecordKey;
271
326
  let val: SerovalNode;
272
327
  let check: number;
273
328
  let isIdentifier: boolean;
274
329
  let refParam: string;
275
330
  let hasPrev = false;
276
- for (let i = 0; i < node.s; i++) {
277
- key = node.k[i];
278
- val = node.v[i];
279
- check = Number(key);
280
- // Test if key is a valid number or JS identifier
281
- // so that we don't have to serialize the key and wrap with brackets
282
- isIdentifier = check >= 0 || isValidIdentifier(key);
283
- if (isIndexedValueInStack(ctx, val)) {
284
- refParam = getRefParam(ctx, val.i);
285
- if (isIdentifier && Number.isNaN(check)) {
286
- createObjectAssign(ctx, sourceID, key, refParam);
287
- } else {
288
- createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + quote(key) + '"'), refParam);
289
- }
290
- } else {
291
- result += (hasPrev ? ',' : '')
292
- + (isIdentifier ? key : ('"' + quote(key) + '"'))
293
- + ':' + serializeTree(ctx, val);
294
- hasPrev = true;
331
+ const keys = node.k;
332
+ const values = node.v;
333
+ for (let i = 0; i < len; i++) {
334
+ key = keys[i];
335
+ val = values[i];
336
+ switch (key) {
337
+ case SerovalObjectRecordSpecialKey.SymbolIterator:
338
+ result += (hasPrev ? ',' : '') + serializeIterable(ctx, val);
339
+ hasPrev = true;
340
+ break;
341
+ default:
342
+ check = Number(key);
343
+ // Test if key is a valid number or JS identifier
344
+ // so that we don't have to serialize the key and wrap with brackets
345
+ isIdentifier = check >= 0 || isValidIdentifier(key);
346
+ if (isIndexedValueInStack(ctx, val)) {
347
+ refParam = getRefParam(ctx, val.i);
348
+ markRef(ctx, sourceID);
349
+ if (isIdentifier && Number.isNaN(check)) {
350
+ createObjectAssign(ctx, sourceID, key, refParam);
351
+ } else {
352
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
353
+ }
354
+ } else {
355
+ result += (hasPrev ? ',' : '')
356
+ + (isIdentifier ? key : ('"' + key + '"'))
357
+ + ':' + serializeTree(ctx, val);
358
+ hasPrev = true;
359
+ }
360
+ break;
295
361
  }
296
362
  }
297
363
  ctx.stack.pop();
@@ -303,7 +369,7 @@ function serializeWithObjectAssign(
303
369
  value: SerovalObjectRecordNode,
304
370
  id: number,
305
371
  serialized: string,
306
- ) {
372
+ ): string {
307
373
  const fields = serializeProperties(ctx, id, value);
308
374
  if (fields !== '{}') {
309
375
  return 'Object.assign(' + serialized + ',' + fields + ')';
@@ -315,31 +381,47 @@ function serializeAssignments(
315
381
  ctx: SerializationContext,
316
382
  sourceID: number,
317
383
  node: SerovalObjectRecordNode,
318
- ) {
384
+ ): string | undefined {
319
385
  ctx.stack.push(sourceID);
320
386
  const mainAssignments: Assignment[] = [];
321
- let parentStack: number[];
322
- let refParam: string;
323
- let key: string;
324
- let check: number;
387
+ let key: SerovalObjectRecordKey;
388
+ let value: SerovalNode;
325
389
  let parentAssignment: Assignment[];
326
- let isIdentifier: boolean;
327
- for (let i = 0; i < node.s; i++) {
328
- parentStack = ctx.stack;
329
- ctx.stack = [];
330
- refParam = serializeTree(ctx, node.v[i]);
331
- ctx.stack = parentStack;
332
- key = node.k[i];
333
- check = Number(key);
390
+ const keys = node.k;
391
+ const values = node.v;
392
+ for (let i = 0, len = node.s; i < len; i++) {
393
+ key = keys[i];
394
+ value = values[i];
334
395
  parentAssignment = ctx.assignments;
335
396
  ctx.assignments = mainAssignments;
336
- // Test if key is a valid number or JS identifier
337
- // so that we don't have to serialize the key and wrap with brackets
338
- isIdentifier = check >= 0 || isValidIdentifier(key);
339
- if (isIdentifier && Number.isNaN(check)) {
340
- createObjectAssign(ctx, sourceID, key, refParam);
341
- } else {
342
- createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + quote(key) + '"'), refParam);
397
+ switch (key) {
398
+ case SerovalObjectRecordSpecialKey.SymbolIterator: {
399
+ const parent = ctx.stack;
400
+ ctx.stack = [];
401
+ const serialized = serializeTree(ctx, value) + getIterableAccess(ctx);
402
+ ctx.stack = parent;
403
+ createArrayAssign(
404
+ ctx,
405
+ sourceID,
406
+ 'Symbol.iterator',
407
+ ctx.features & Feature.ArrowFunction
408
+ ? '()=>' + serialized
409
+ : 'function(){return ' + serialized + '}',
410
+ );
411
+ }
412
+ break;
413
+ default: {
414
+ const serialized = serializeTree(ctx, value);
415
+ const check = Number(key);
416
+ // Test if key is a valid number or JS identifier
417
+ // so that we don't have to serialize the key and wrap with brackets
418
+ const isIdentifier = check >= 0 || isValidIdentifier(key);
419
+ if (isIdentifier && Number.isNaN(check)) {
420
+ createObjectAssign(ctx, sourceID, key, serialized);
421
+ } else {
422
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), serialized);
423
+ }
424
+ }
343
425
  }
344
426
  ctx.assignments = parentAssignment;
345
427
  }
@@ -352,7 +434,7 @@ function serializeDictionary(
352
434
  i: number,
353
435
  d: SerovalObjectRecordNode | undefined,
354
436
  init: string,
355
- ) {
437
+ ): string {
356
438
  if (d) {
357
439
  if (ctx.features & Feature.ObjectAssign) {
358
440
  init = serializeWithObjectAssign(ctx, d, i, init);
@@ -367,35 +449,40 @@ function serializeDictionary(
367
449
  return assignIndexedValue(ctx, i, init);
368
450
  }
369
451
 
452
+ const NULL_CONSTRUCTOR = 'Object.create(null)';
453
+
370
454
  function serializeNullConstructor(
371
455
  ctx: SerializationContext,
372
456
  node: SerovalNullConstructorNode,
373
- ) {
374
- return serializeDictionary(ctx, node.i, node.d, 'Object.create(null)');
457
+ ): string {
458
+ return serializeDictionary(ctx, node.i, node.d, NULL_CONSTRUCTOR);
375
459
  }
376
460
 
377
461
  function serializeObject(
378
462
  ctx: SerializationContext,
379
463
  node: SerovalObjectNode,
380
- ) {
464
+ ): string {
381
465
  return assignIndexedValue(ctx, node.i, serializeProperties(ctx, node.i, node.d));
382
466
  }
383
467
 
384
468
  function serializeSet(
385
469
  ctx: SerializationContext,
386
470
  node: SerovalSetNode,
387
- ) {
471
+ ): string {
388
472
  let serialized = 'new Set';
389
473
  const size = node.l;
474
+ const id = node.i;
390
475
  if (size) {
391
476
  let result = '';
392
- ctx.stack.push(node.i);
393
477
  let item: SerovalNode;
394
478
  let hasPrev = false;
479
+ const items = node.a;
480
+ ctx.stack.push(id);
395
481
  for (let i = 0; i < size; i++) {
396
- item = node.a[i];
482
+ item = items[i];
397
483
  if (isIndexedValueInStack(ctx, item)) {
398
- createSetAdd(ctx, node.i, getRefParam(ctx, item.i));
484
+ markRef(ctx, id);
485
+ createAddAssignment(ctx, id, getRefParam(ctx, item.i));
399
486
  } else {
400
487
  // Push directly
401
488
  result += (hasPrev ? ',' : '') + serializeTree(ctx, item);
@@ -407,37 +494,42 @@ function serializeSet(
407
494
  serialized += '([' + result + '])';
408
495
  }
409
496
  }
410
- return assignIndexedValue(ctx, node.i, serialized);
497
+ return assignIndexedValue(ctx, id, serialized);
411
498
  }
412
499
 
413
500
  function serializeMap(
414
501
  ctx: SerializationContext,
415
502
  node: SerovalMapNode,
416
- ) {
503
+ ): string {
417
504
  let serialized = 'new Map';
418
- if (node.d.s) {
505
+ const size = node.d.s;
506
+ const id = node.i;
507
+ if (size) {
419
508
  let result = '';
420
- ctx.stack.push(node.i);
421
509
  let key: SerovalNode;
422
510
  let val: SerovalNode;
423
511
  let keyRef: string;
424
512
  let valueRef: string;
425
513
  let parent: number[];
426
514
  let hasPrev = false;
427
- for (let i = 0; i < node.d.s; i++) {
515
+ const keys = node.d.k;
516
+ const vals = node.d.v;
517
+ ctx.stack.push(id);
518
+ for (let i = 0; i < size; i++) {
428
519
  // Check if key is a parent
429
- key = node.d.k[i];
430
- val = node.d.v[i];
520
+ key = keys[i];
521
+ val = vals[i];
431
522
  if (isIndexedValueInStack(ctx, key)) {
432
523
  // Create reference for the map instance
433
- keyRef = getRefParam(ctx, key.i);
524
+ keyRef = getRefParam(ctx, id);
525
+ markRef(ctx, id);
434
526
  // Check if value is a parent
435
527
  if (isIndexedValueInStack(ctx, val)) {
436
528
  valueRef = getRefParam(ctx, val.i);
437
529
  // Register an assignment since
438
530
  // both key and value are a parent of this
439
531
  // Map instance
440
- createMapSet(ctx, node.i, keyRef, valueRef);
532
+ createSetAssignment(ctx, id, keyRef, valueRef);
441
533
  } else {
442
534
  // Reset the stack
443
535
  // This is required because the serialized
@@ -446,16 +538,17 @@ function serializeMap(
446
538
  // assignment
447
539
  parent = ctx.stack;
448
540
  ctx.stack = [];
449
- createMapSet(ctx, node.i, keyRef, serializeTree(ctx, val));
541
+ createSetAssignment(ctx, id, keyRef, serializeTree(ctx, val));
450
542
  ctx.stack = parent;
451
543
  }
452
544
  } else if (isIndexedValueInStack(ctx, val)) {
453
545
  // Create ref for the Map instance
454
546
  valueRef = getRefParam(ctx, val.i);
547
+ markRef(ctx, id);
455
548
  // Reset stack for the key serialization
456
549
  parent = ctx.stack;
457
550
  ctx.stack = [];
458
- createMapSet(ctx, node.i, serializeTree(ctx, key), valueRef);
551
+ createSetAssignment(ctx, id, serializeTree(ctx, key), valueRef);
459
552
  ctx.stack = parent;
460
553
  } else {
461
554
  result += (hasPrev ? ',[' : '[') + serializeTree(ctx, key) + ',' + serializeTree(ctx, val) + ']';
@@ -470,93 +563,224 @@ function serializeMap(
470
563
  serialized += '([' + result + '])';
471
564
  }
472
565
  }
473
- return assignIndexedValue(ctx, node.i, serialized);
566
+ return assignIndexedValue(ctx, id, serialized);
474
567
  }
475
568
 
476
569
  function serializeAggregateError(
477
570
  ctx: SerializationContext,
478
571
  node: SerovalAggregateErrorNode,
479
- ) {
572
+ ): string {
480
573
  // Serialize the required arguments
481
- ctx.stack.push(node.i);
482
- const serialized = 'new AggregateError(' + serializeNodeList(ctx, node) + ',"' + quote(node.m) + '")';
574
+ const id = node.i;
575
+ ctx.stack.push(id);
576
+ const serialized = 'new AggregateError([],"' + node.m + '")';
483
577
  ctx.stack.pop();
484
578
  // `AggregateError` might've been extended
485
579
  // either through class or custom properties
486
580
  // Make sure to assign extra properties
487
- return serializeDictionary(ctx, node.i, node.d, serialized);
581
+ return serializeDictionary(ctx, id, node.d, serialized);
488
582
  }
489
583
 
490
584
  function serializeError(
491
585
  ctx: SerializationContext,
492
586
  node: SerovalErrorNode,
493
- ) {
494
- const serialized = 'new ' + node.c + '("' + quote(node.m) + '")';
495
- return serializeDictionary(ctx, node.i, node.d, serialized);
587
+ ): string {
588
+ return serializeDictionary(ctx, node.i, node.d, 'new ' + node.c + '("' + node.m + '")');
496
589
  }
497
590
 
498
591
  function serializePromise(
499
592
  ctx: SerializationContext,
500
593
  node: SerovalPromiseNode,
501
- ) {
594
+ ): string {
502
595
  let serialized: string;
503
596
  // Check if resolved value is a parent expression
504
- if (isIndexedValueInStack(ctx, node.f)) {
597
+ const fulfilled = node.f;
598
+ const id = node.i;
599
+ if (isIndexedValueInStack(ctx, fulfilled)) {
505
600
  // A Promise trick, reference the value
506
601
  // inside the `then` expression so that
507
602
  // the Promise evaluates after the parent
508
603
  // has initialized
509
- const ref = getRefParam(ctx, node.f.i);
604
+ const ref = getRefParam(ctx, fulfilled.i);
510
605
  if (ctx.features & Feature.ArrowFunction) {
511
606
  serialized = 'Promise.resolve().then(()=>' + ref + ')';
512
607
  } else {
513
608
  serialized = 'Promise.resolve().then(function(){return ' + ref + '})';
514
609
  }
515
610
  } else {
516
- ctx.stack.push(node.i);
517
- const result = serializeTree(ctx, node.f);
611
+ ctx.stack.push(id);
612
+ const result = serializeTree(ctx, fulfilled);
518
613
  ctx.stack.pop();
519
614
  // just inline the value/reference here
520
615
  serialized = 'Promise.resolve(' + result + ')';
521
616
  }
522
- return assignIndexedValue(ctx, node.i, serialized);
617
+ return assignIndexedValue(ctx, id, serialized);
618
+ }
619
+
620
+ function serializeArrayBuffer(
621
+ ctx: SerializationContext,
622
+ node: SerovalArrayBufferNode,
623
+ ): string {
624
+ let result = 'new Uint8Array(';
625
+ const buffer = node.s;
626
+ const len = buffer.length;
627
+ if (len) {
628
+ result += '[';
629
+ for (let i = 0; i < len; i++) {
630
+ result += ((i > 0) ? ',' : '') + buffer[i];
631
+ }
632
+ result += ']';
633
+ }
634
+ return assignIndexedValue(ctx, node.i, result + ').buffer');
523
635
  }
524
636
 
525
637
  function serializeTypedArray(
526
638
  ctx: SerializationContext,
527
639
  node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
528
- ) {
529
- let result = '';
530
- const isBigInt = node.t === SerovalNodeType.BigIntTypedArray;
531
- for (let i = 0, len = node.s.length; i < len; i++) {
532
- result += (i !== 0 ? ',' : '') + node.s[i] + (isBigInt ? 'n' : '');
640
+ ): string {
641
+ return assignIndexedValue(
642
+ ctx,
643
+ node.i,
644
+ 'new ' + node.c + '(' + serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l + ')',
645
+ );
646
+ }
647
+
648
+ function serializeDate(
649
+ ctx: SerializationContext,
650
+ node: SerovalDateNode,
651
+ ): string {
652
+ return assignIndexedValue(ctx, node.i, 'new Date("' + node.s + '")');
653
+ }
654
+
655
+ function serializeRegExp(
656
+ ctx: SerializationContext,
657
+ node: SerovalRegExpNode,
658
+ ): string {
659
+ return assignIndexedValue(ctx, node.i, '/' + node.c + '/' + node.m);
660
+ }
661
+
662
+ function serializeURL(
663
+ ctx: SerializationContext,
664
+ node: SerovalURLNode,
665
+ ): string {
666
+ return assignIndexedValue(ctx, node.i, 'new URL("' + node.s + '")');
667
+ }
668
+
669
+ function serializeURLSearchParams(
670
+ ctx: SerializationContext,
671
+ node: SerovalURLSearchParamsNode,
672
+ ): string {
673
+ return assignIndexedValue(
674
+ ctx,
675
+ node.i,
676
+ node.s ? 'new URLSearchParams("' + node.s + '")' : 'new URLSearchParams',
677
+ );
678
+ }
679
+
680
+ function serializeReference(
681
+ ctx: SerializationContext,
682
+ node: SerovalReferenceNode,
683
+ ): string {
684
+ return assignIndexedValue(ctx, node.i, GLOBAL_KEY + '.get("' + node.s + '")');
685
+ }
686
+
687
+ function serializeDataView(
688
+ ctx: SerializationContext,
689
+ node: SerovalDataViewNode,
690
+ ): string {
691
+ return assignIndexedValue(
692
+ ctx,
693
+ node.i,
694
+ 'new DataView(' + serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l + ')',
695
+ );
696
+ }
697
+
698
+ function serializeBlob(
699
+ ctx: SerializationContext,
700
+ node: SerovalBlobNode,
701
+ ): string {
702
+ return assignIndexedValue(
703
+ ctx,
704
+ node.i,
705
+ 'new Blob([' + serializeTree(ctx, node.f) + '],{type:"' + node.c + '"})',
706
+ );
707
+ }
708
+
709
+ function serializeFile(
710
+ ctx: SerializationContext,
711
+ node: SerovalFileNode,
712
+ ): string {
713
+ return assignIndexedValue(
714
+ ctx,
715
+ node.i,
716
+ 'new File([' + serializeTree(ctx, node.f) + '],"' + node.m + '",{type:"' + node.c + '",lastModified:' + node.b + '})',
717
+ );
718
+ }
719
+
720
+ function serializeHeaders(
721
+ ctx: SerializationContext,
722
+ node: SerovalHeadersNode,
723
+ ): string {
724
+ return assignIndexedValue(
725
+ ctx,
726
+ node.i,
727
+ 'new Headers(' + serializeProperties(ctx, node.i, node.d) + ')',
728
+ );
729
+ }
730
+
731
+ function serializeFormDataEntries(
732
+ ctx: SerializationContext,
733
+ node: SerovalFormDataNode,
734
+ ): string | undefined {
735
+ let value: string;
736
+ let key: string;
737
+ const keys = node.d.k;
738
+ const vals = node.d.v;
739
+ const id = node.i;
740
+ const mainAssignments: Assignment[] = [];
741
+ let parentAssignment: Assignment[];
742
+ ctx.stack.push(id);
743
+ for (let i = 0, len = node.d.s; i < len; i++) {
744
+ key = keys[i];
745
+ value = serializeTree(ctx, vals[i]);
746
+ parentAssignment = ctx.assignments;
747
+ ctx.assignments = mainAssignments;
748
+ createAppendAssignment(ctx, id, '"' + key + '"', value);
749
+ ctx.assignments = parentAssignment;
533
750
  }
534
- const args = '[' + result + ']' + (node.l !== 0 ? (',' + node.l) : '');
535
- return assignIndexedValue(ctx, node.i, 'new ' + node.c + '(' + args + ')');
751
+ ctx.stack.pop();
752
+ return resolveAssignments(mainAssignments);
536
753
  }
537
754
 
538
- function serializeIterable(
755
+ function serializeFormData(
539
756
  ctx: SerializationContext,
540
- node: SerovalIterableNode,
541
- ) {
542
- const parent = ctx.stack;
543
- ctx.stack = [];
544
- const values = serializeNodeList(ctx, node);
545
- ctx.stack = parent;
546
- let serialized = values;
547
- if (ctx.features & Feature.ArrayPrototypeValues) {
548
- serialized += '.values()';
549
- } else {
550
- serialized += '[Symbol.iterator]()';
757
+ node: SerovalFormDataNode,
758
+ ): string {
759
+ const size = node.d.s;
760
+ const id = node.i;
761
+ if (size) {
762
+ markRef(ctx, id);
551
763
  }
552
- if (ctx.features & Feature.ArrowFunction) {
553
- serialized = '{[Symbol.iterator]:()=>' + serialized + '}';
554
- } else if (ctx.features & Feature.MethodShorthand) {
555
- serialized = '{[Symbol.iterator](){return ' + serialized + '}}';
556
- } else {
557
- serialized = '{[Symbol.iterator]:function(){return ' + serialized + '}}';
764
+ const result = assignIndexedValue(ctx, id, 'new FormData()');
765
+ if (size) {
766
+ const entries = serializeFormDataEntries(ctx, node);
767
+ return '(' + result + ',' + (entries == null ? '' : entries) + getRefParam(ctx, id) + ')';
558
768
  }
559
- return serializeDictionary(ctx, node.i, node.d, serialized);
769
+ return result;
770
+ }
771
+
772
+ function serializeBoxed(
773
+ ctx: SerializationContext,
774
+ node: SerovalBoxedNode,
775
+ ): string {
776
+ return assignIndexedValue(ctx, node.i, 'Object(' + serializeTree(ctx, node.f) + ')');
777
+ }
778
+
779
+ function serializeWKSymbol(
780
+ ctx: SerializationContext,
781
+ node: SerovalWKSymbolNode,
782
+ ): string {
783
+ return assignIndexedValue(ctx, node.i, SYMBOL_STRING[node.s]);
560
784
  }
561
785
 
562
786
  export default function serializeTree(
@@ -568,20 +792,8 @@ export default function serializeTree(
568
792
  return '' + node.s;
569
793
  case SerovalNodeType.String:
570
794
  return '"' + node.s + '"';
571
- case SerovalNodeType.Boolean:
572
- return node.s ? '!0' : '!1';
573
- case SerovalNodeType.Undefined:
574
- return 'void 0';
575
- case SerovalNodeType.Null:
576
- return 'null';
577
- case SerovalNodeType.NegativeZero:
578
- return '-0';
579
- case SerovalNodeType.Infinity:
580
- return '1/0';
581
- case SerovalNodeType.NegativeInfinity:
582
- return '-1/0';
583
- case SerovalNodeType.NaN:
584
- return 'NaN';
795
+ case SerovalNodeType.Constant:
796
+ return serializeConstant(node);
585
797
  case SerovalNodeType.BigInt:
586
798
  return node.s + 'n';
587
799
  case SerovalNodeType.IndexedValue:
@@ -593,33 +805,45 @@ export default function serializeTree(
593
805
  case SerovalNodeType.NullConstructor:
594
806
  return serializeNullConstructor(ctx, node);
595
807
  case SerovalNodeType.Date:
596
- return assignIndexedValue(ctx, node.i, 'new Date("' + node.s + '")');
808
+ return serializeDate(ctx, node);
597
809
  case SerovalNodeType.RegExp:
598
- return assignIndexedValue(ctx, node.i, '/' + node.c + '/' + node.m);
810
+ return serializeRegExp(ctx, node);
599
811
  case SerovalNodeType.Set:
600
812
  return serializeSet(ctx, node);
601
813
  case SerovalNodeType.Map:
602
814
  return serializeMap(ctx, node);
815
+ case SerovalNodeType.ArrayBuffer:
816
+ return serializeArrayBuffer(ctx, node);
603
817
  case SerovalNodeType.BigIntTypedArray:
604
818
  case SerovalNodeType.TypedArray:
605
819
  return serializeTypedArray(ctx, node);
820
+ case SerovalNodeType.DataView:
821
+ return serializeDataView(ctx, node);
606
822
  case SerovalNodeType.AggregateError:
607
823
  return serializeAggregateError(ctx, node);
608
824
  case SerovalNodeType.Error:
609
825
  return serializeError(ctx, node);
610
- case SerovalNodeType.Iterable:
611
- return serializeIterable(ctx, node);
612
826
  case SerovalNodeType.Promise:
613
827
  return serializePromise(ctx, node);
614
828
  case SerovalNodeType.WKSymbol:
615
- return SYMBOL_STRING[node.s];
829
+ return serializeWKSymbol(ctx, node);
616
830
  case SerovalNodeType.URL:
617
- return assignIndexedValue(ctx, node.i, 'new URL("' + node.s + '")');
831
+ return serializeURL(ctx, node);
618
832
  case SerovalNodeType.URLSearchParams:
619
- return assignIndexedValue(ctx, node.i, node.s ? 'new URLSearchParams("' + node.s + '")' : 'new URLSearchParams');
833
+ return serializeURLSearchParams(ctx, node);
620
834
  case SerovalNodeType.Reference:
621
- return assignIndexedValue(ctx, node.i, GLOBAL_KEY + '.get("' + node.s + '")');
835
+ return serializeReference(ctx, node);
836
+ case SerovalNodeType.Blob:
837
+ return serializeBlob(ctx, node);
838
+ case SerovalNodeType.File:
839
+ return serializeFile(ctx, node);
840
+ case SerovalNodeType.Headers:
841
+ return serializeHeaders(ctx, node);
842
+ case SerovalNodeType.FormData:
843
+ return serializeFormData(ctx, node);
844
+ case SerovalNodeType.Boxed:
845
+ return serializeBoxed(ctx, node);
622
846
  default:
623
- throw new Error('Unsupported type');
847
+ throw new Error('invariant');
624
848
  }
625
849
  }