fast-json-stringify 6.2.0 → 6.3.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.
- package/README.md +4 -5
- package/benchmark/bench-cmp-lib.js +5 -5
- package/benchmark/bench-thread.js +3 -3
- package/index.js +110 -42
- package/package.json +11 -10
- package/test/array.test.js +28 -0
package/README.md
CHANGED
|
@@ -79,7 +79,6 @@ compile-json-stringify date format x 803,214 ops/sec ±0.23% (97 runs sampled)
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
<a name="example"></a>
|
|
82
|
-
Try it out on RunKit: <a href="https://runkit.com/npm/fast-json-stringify">https://runkit.com/npm/fast-json-stringify</a>
|
|
83
82
|
## Example
|
|
84
83
|
|
|
85
84
|
```js
|
|
@@ -347,9 +346,9 @@ const stringify = fastJson({
|
|
|
347
346
|
properties: {
|
|
348
347
|
'undecidedType': {
|
|
349
348
|
'anyOf': [{
|
|
350
|
-
|
|
349
|
+
type: 'string'
|
|
351
350
|
}, {
|
|
352
|
-
|
|
351
|
+
type: 'boolean'
|
|
353
352
|
}]
|
|
354
353
|
}
|
|
355
354
|
}
|
|
@@ -645,8 +644,8 @@ const stringify = fastJson({
|
|
|
645
644
|
type: 'object',
|
|
646
645
|
properties: {
|
|
647
646
|
'code': {
|
|
648
|
-
|
|
649
|
-
|
|
647
|
+
type: 'string',
|
|
648
|
+
format: 'unsafe'
|
|
650
649
|
}
|
|
651
650
|
}
|
|
652
651
|
})
|
|
@@ -84,13 +84,13 @@ const arraySchemaAJVJTD = {
|
|
|
84
84
|
const dateFormatSchema = {
|
|
85
85
|
description: 'Date of birth',
|
|
86
86
|
type: 'string',
|
|
87
|
-
format: '
|
|
87
|
+
format: 'datetime'
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
const dateFormatSchemaCJS = {
|
|
91
91
|
description: 'Date of birth',
|
|
92
92
|
type: 'string',
|
|
93
|
-
format: '
|
|
93
|
+
format: 'datetime'
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const obj = {
|
|
@@ -310,9 +310,9 @@ suite.add('compile-json-stringify date format', function () {
|
|
|
310
310
|
|
|
311
311
|
suite.run().then(() => {
|
|
312
312
|
for (const task of suite.tasks) {
|
|
313
|
-
const hz = task.result.
|
|
314
|
-
const rme = task.result.rme // relative margin of error (%)
|
|
315
|
-
const samples = task.result.
|
|
313
|
+
const hz = task.result.throughput.mean // ops/sec
|
|
314
|
+
const rme = task.result.latency.rme // relative margin of error (%)
|
|
315
|
+
const samples = task.result.latency.df + 1 // degrees of freedom + 1 = sample count
|
|
316
316
|
|
|
317
317
|
const formattedHz = hz.toLocaleString('en-US', { maximumFractionDigits: 0 })
|
|
318
318
|
const formattedRme = rme.toFixed(2)
|
|
@@ -21,9 +21,9 @@ bench.add(benchmark.name, () => {
|
|
|
21
21
|
stringify(benchmark.input)
|
|
22
22
|
}).run().then(() => {
|
|
23
23
|
const task = bench.tasks[0]
|
|
24
|
-
const hz = task.result.
|
|
25
|
-
const rme = task.result.rme // relative margin of error (%)
|
|
26
|
-
const samples = task.result.
|
|
24
|
+
const hz = task.result.throughput.mean // ops/sec
|
|
25
|
+
const rme = task.result.latency.rme // relative margin of error (%)
|
|
26
|
+
const samples = task.result.latency.df + 1 // degrees of freedom + 1 = sample count
|
|
27
27
|
|
|
28
28
|
const formattedHz = hz.toLocaleString('en-US', { maximumFractionDigits: 0 })
|
|
29
29
|
const formattedRme = rme.toFixed(2)
|
package/index.js
CHANGED
|
@@ -304,14 +304,14 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma, objV
|
|
|
304
304
|
const propertiesKeys = Object.keys(schema.properties || {})
|
|
305
305
|
|
|
306
306
|
let code = `
|
|
307
|
-
const
|
|
308
|
-
for (const [key, value] of Object.entries(${objVar})) {
|
|
307
|
+
for (const key of Object.keys(${objVar})) {
|
|
309
308
|
if (
|
|
310
|
-
propertiesKeys.
|
|
311
|
-
|
|
312
|
-
typeof
|
|
313
|
-
typeof
|
|
309
|
+
${propertiesKeys.length > 0 ? propertiesKeys.map(k => `key === ${JSON.stringify(k)}`).join(' || ') + ' ||' : ''}
|
|
310
|
+
${objVar}[key] === undefined ||
|
|
311
|
+
typeof ${objVar}[key] === 'function' ||
|
|
312
|
+
typeof ${objVar}[key] === 'symbol'
|
|
314
313
|
) continue
|
|
314
|
+
const value = ${objVar}[key]
|
|
315
315
|
`
|
|
316
316
|
|
|
317
317
|
const patternPropertiesLocation = location.getPropertyLocation('patternProperties')
|
|
@@ -385,45 +385,99 @@ function buildInnerObject (context, location, objVar) {
|
|
|
385
385
|
|
|
386
386
|
const localUid = context.uid++
|
|
387
387
|
let addComma = ''
|
|
388
|
-
const needsRuntimeComma = propertiesKeys.length > 1 || schema.patternProperties || (schema.additionalProperties !== undefined && schema.additionalProperties !== false)
|
|
389
388
|
|
|
390
|
-
if (
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
389
|
+
if (requiredProperties.length > 0) {
|
|
390
|
+
// If we have required properties, we know that at least one property will be serialized.
|
|
391
|
+
// We can avoid the runtime check for the comma.
|
|
394
392
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (propertyLocation.schema.$ref) {
|
|
398
|
-
propertyLocation = resolveRef(context, propertyLocation)
|
|
399
|
-
}
|
|
393
|
+
// The first property is required, so we don't need a comma.
|
|
394
|
+
// For the subsequent properties, we can blindly add a comma.
|
|
400
395
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const isRequired = requiredProperties.includes(key)
|
|
396
|
+
for (let i = 0; i < propertiesKeys.length; i++) {
|
|
397
|
+
const key = propertiesKeys[i]
|
|
398
|
+
const propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
405
399
|
|
|
406
|
-
|
|
400
|
+
let resolvedLocation = propertyLocation
|
|
401
|
+
if (propertyLocation.schema.$ref) {
|
|
402
|
+
resolvedLocation = resolveRef(context, propertyLocation)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const sanitizedKey = JSON.stringify(key)
|
|
406
|
+
const value = `value_${key.replace(/[^a-zA-Z0-9]/g, '_')}_${context.uid++}`
|
|
407
|
+
const defaultValue = resolvedLocation.schema.default
|
|
408
|
+
const isRequired = requiredProperties.includes(key)
|
|
409
|
+
|
|
410
|
+
// i === 0 means it's the first property, and it IS required (due to sort). No comma.
|
|
411
|
+
// i > 0 means it follows a required property (or a sequence starting with one). Unconditional comma.
|
|
412
|
+
const currentAddComma = i === 0 ? '' : 'json += JSON_STR_COMMA'
|
|
413
|
+
|
|
414
|
+
code += `
|
|
407
415
|
const ${value} = ${objVar}[${sanitizedKey}]
|
|
408
416
|
if (${value} !== undefined) {
|
|
409
|
-
${
|
|
417
|
+
${currentAddComma}
|
|
410
418
|
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
411
|
-
${buildValue(context,
|
|
419
|
+
${buildValue(context, resolvedLocation, `${value}`)}
|
|
412
420
|
}`
|
|
413
421
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
${
|
|
422
|
+
if (defaultValue !== undefined) {
|
|
423
|
+
code += ` else {
|
|
424
|
+
${currentAddComma}
|
|
417
425
|
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
418
426
|
}
|
|
419
427
|
`
|
|
420
|
-
|
|
421
|
-
|
|
428
|
+
} else if (isRequired) {
|
|
429
|
+
code += ` else {
|
|
422
430
|
throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')
|
|
423
431
|
}
|
|
424
432
|
`
|
|
425
|
-
|
|
426
|
-
|
|
433
|
+
} else {
|
|
434
|
+
code += '\n'
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
addComma = 'json += JSON_STR_COMMA'
|
|
439
|
+
} else {
|
|
440
|
+
const needsRuntimeComma = propertiesKeys.length > 1 || schema.patternProperties || (schema.additionalProperties !== undefined && schema.additionalProperties !== false)
|
|
441
|
+
|
|
442
|
+
if (needsRuntimeComma) {
|
|
443
|
+
code += `let addComma_${localUid} = false\n`
|
|
444
|
+
addComma = `!addComma_${localUid} && (addComma_${localUid} = true) || (json += JSON_STR_COMMA)`
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
for (const key of propertiesKeys) {
|
|
448
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
449
|
+
if (propertyLocation.schema.$ref) {
|
|
450
|
+
propertyLocation = resolveRef(context, propertyLocation)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const sanitizedKey = JSON.stringify(key)
|
|
454
|
+
const value = `value_${key.replace(/[^a-zA-Z0-9]/g, '_')}_${context.uid++}`
|
|
455
|
+
const defaultValue = propertyLocation.schema.default
|
|
456
|
+
const isRequired = requiredProperties.includes(key) // Should be false here but good to keep
|
|
457
|
+
|
|
458
|
+
code += `
|
|
459
|
+
const ${value} = ${objVar}[${sanitizedKey}]
|
|
460
|
+
if (${value} !== undefined) {
|
|
461
|
+
${addComma}
|
|
462
|
+
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
463
|
+
${buildValue(context, propertyLocation, `${value}`)}
|
|
464
|
+
}`
|
|
465
|
+
|
|
466
|
+
if (defaultValue !== undefined) {
|
|
467
|
+
code += ` else {
|
|
468
|
+
${addComma}
|
|
469
|
+
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
470
|
+
}
|
|
471
|
+
`
|
|
472
|
+
} else if (isRequired) {
|
|
473
|
+
// Should not happen if requiredProperties.length === 0 but safety
|
|
474
|
+
code += ` else {
|
|
475
|
+
throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')
|
|
476
|
+
}
|
|
477
|
+
`
|
|
478
|
+
} else {
|
|
479
|
+
code += '\n'
|
|
480
|
+
}
|
|
427
481
|
}
|
|
428
482
|
}
|
|
429
483
|
|
|
@@ -611,10 +665,15 @@ function buildArray (context, location, input) {
|
|
|
611
665
|
|
|
612
666
|
if (Array.isArray(itemsSchema)) {
|
|
613
667
|
for (let i = 0, itemsSchemaLength = itemsSchema.length; i < itemsSchemaLength; i++) {
|
|
614
|
-
|
|
668
|
+
let item = itemsSchema[i]
|
|
669
|
+
let itemLocation = itemsLocation.getPropertyLocation(i)
|
|
670
|
+
if (itemLocation.schema.$ref) {
|
|
671
|
+
itemLocation = resolveRef(context, itemLocation)
|
|
672
|
+
item = itemLocation.schema
|
|
673
|
+
}
|
|
615
674
|
const value = `value_${i}`
|
|
616
675
|
functionCode += `const ${value} = obj[${i}]`
|
|
617
|
-
const tmpRes = buildValue(context,
|
|
676
|
+
const tmpRes = buildValue(context, itemLocation, value)
|
|
618
677
|
functionCode += `
|
|
619
678
|
if (${i} < arrayLength) {
|
|
620
679
|
if (${buildArrayTypeCondition(item.type, value)}) {
|
|
@@ -641,12 +700,14 @@ function buildArray (context, location, input) {
|
|
|
641
700
|
} else {
|
|
642
701
|
const code = buildValue(context, itemsLocation, 'value')
|
|
643
702
|
functionCode += `
|
|
644
|
-
|
|
645
|
-
|
|
703
|
+
if (arrayLength > 0) {
|
|
704
|
+
const value = obj[0]
|
|
705
|
+
${code}
|
|
706
|
+
for (let i = 1; i < arrayLength; i++) {
|
|
646
707
|
json += JSON_STR_COMMA
|
|
708
|
+
const value = obj[i]
|
|
709
|
+
${code}
|
|
647
710
|
}
|
|
648
|
-
const value = obj[i]
|
|
649
|
-
${code}
|
|
650
711
|
}`
|
|
651
712
|
}
|
|
652
713
|
|
|
@@ -691,10 +752,15 @@ function buildArray (context, location, input) {
|
|
|
691
752
|
const localUid = context.uid++
|
|
692
753
|
inlinedCode += `let addComma_${localUid} = false\n`
|
|
693
754
|
for (let i = 0, itemsSchemaLength = itemsSchema.length; i < itemsSchemaLength; i++) {
|
|
694
|
-
|
|
755
|
+
let item = itemsSchema[i]
|
|
756
|
+
let itemLocation = itemsLocation.getPropertyLocation(i)
|
|
757
|
+
if (itemLocation.schema.$ref) {
|
|
758
|
+
itemLocation = resolveRef(context, itemLocation)
|
|
759
|
+
item = itemLocation.schema
|
|
760
|
+
}
|
|
695
761
|
const value = `value_${i}_${context.uid++}`
|
|
696
762
|
inlinedCode += `const ${value} = ${objVar}[${i}]`
|
|
697
|
-
const tmpRes = buildValue(context,
|
|
763
|
+
const tmpRes = buildValue(context, itemLocation, value)
|
|
698
764
|
inlinedCode += `
|
|
699
765
|
if (${i} < arrayLength_${objVar}) {
|
|
700
766
|
if (${buildArrayTypeCondition(item.type, value)}) {
|
|
@@ -717,12 +783,14 @@ function buildArray (context, location, input) {
|
|
|
717
783
|
} else {
|
|
718
784
|
const code = buildValue(context, itemsLocation, 'value')
|
|
719
785
|
inlinedCode += `
|
|
720
|
-
|
|
721
|
-
|
|
786
|
+
if (arrayLength_${objVar} > 0) {
|
|
787
|
+
const value = ${objVar}[0]
|
|
788
|
+
${code}
|
|
789
|
+
for (let i = 1; i < arrayLength_${objVar}; i++) {
|
|
722
790
|
json += JSON_STR_COMMA
|
|
791
|
+
const value = ${objVar}[i]
|
|
792
|
+
${code}
|
|
723
793
|
}
|
|
724
|
-
const value = ${objVar}[i]
|
|
725
|
-
${code}
|
|
726
794
|
}`
|
|
727
795
|
}
|
|
728
796
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-json-stringify",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Stringify your JSON at max speed",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -62,19 +62,20 @@
|
|
|
62
62
|
}
|
|
63
63
|
],
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@sinclair/typebox": "^0.34.
|
|
66
|
-
"c8": "^10.1.
|
|
65
|
+
"@sinclair/typebox": "^0.34.48",
|
|
66
|
+
"c8": "^10.1.3",
|
|
67
67
|
"cli-select": "^1.1.2",
|
|
68
68
|
"compile-json-stringify": "^0.1.2",
|
|
69
|
-
"eslint": "^9.
|
|
69
|
+
"eslint": "^9.39.2",
|
|
70
70
|
"fast-json-stringify": ".",
|
|
71
71
|
"is-my-json-valid": "^2.20.6",
|
|
72
|
-
"json-accelerator": "^0.
|
|
73
|
-
"neostandard": "^0.12.
|
|
74
|
-
"simple-git": "^3.
|
|
75
|
-
"tinybench": "^
|
|
76
|
-
"tsd": "^0.
|
|
77
|
-
"
|
|
72
|
+
"json-accelerator": "^0.1.7",
|
|
73
|
+
"neostandard": "^0.12.2",
|
|
74
|
+
"simple-git": "^3.30.0",
|
|
75
|
+
"tinybench": "^6.0.0",
|
|
76
|
+
"tsd": "^0.33.0",
|
|
77
|
+
"typescript": "^5.9.3",
|
|
78
|
+
"webpack": "^5.104.1"
|
|
78
79
|
},
|
|
79
80
|
"dependencies": {
|
|
80
81
|
"@fastify/merge-json-schemas": "^0.2.0",
|
package/test/array.test.js
CHANGED
|
@@ -61,6 +61,34 @@ buildTest({
|
|
|
61
61
|
dates: [new Date(1), new Date(2)]
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
+
buildTest({
|
|
65
|
+
title: 'dates tuple $ref',
|
|
66
|
+
definitions: {
|
|
67
|
+
dateTime: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
format: 'date-time'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
dates: {
|
|
75
|
+
type: 'array',
|
|
76
|
+
minItems: 2,
|
|
77
|
+
maxItems: 2,
|
|
78
|
+
items: [
|
|
79
|
+
{
|
|
80
|
+
$ref: '#/definitions/dateTime'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
$ref: '#/definitions/dateTime'
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}, {
|
|
89
|
+
dates: [new Date(1), new Date(2)]
|
|
90
|
+
})
|
|
91
|
+
|
|
64
92
|
buildTest({
|
|
65
93
|
title: 'string array',
|
|
66
94
|
type: 'object',
|