safe-stable-stringify
Advanced tools
Comparing version 1.0.1 to 1.1.0
@@ -5,3 +5,3 @@ 'use strict' | ||
const suite = new Benchmark.Suite() | ||
const stringify = require('fast-safe-stringify').stable | ||
const stringify = require('.') | ||
@@ -8,0 +8,0 @@ const array = new Array(10).fill(0).map((_, i) => i) |
@@ -13,2 +13,3 @@ 'use strict' | ||
'faster-stable-stringify': true, | ||
'json-stringify-deterministic': true, | ||
'fast-safe-stringify': 'stable', | ||
@@ -15,0 +16,0 @@ 'this': require('.') |
@@ -1,3 +0,4 @@ | ||
declare function stringify(data: any): string; | ||
declare function stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; | ||
declare function stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string; | ||
export default stringify; |
{ | ||
"name": "safe-stable-stringify", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Deterministic and safely JSON.stringify to quickly serialize JavaScript objects", | ||
@@ -23,3 +23,5 @@ "keywords": [ | ||
"benchmark": "node benchmark.js", | ||
"compare": "node compare.js" | ||
"compare": "node compare.js", | ||
"coverage": "nyc --reporter=html --reporter=text npm test", | ||
"lint": "standard . --fix" | ||
}, | ||
@@ -32,11 +34,13 @@ "author": "Ruben Bridgewater", | ||
"benchmark": "^2.1.4", | ||
"clone": "^2.1.0", | ||
"clone": "^2.1.2", | ||
"fast-json-stable-stringify": "^2.0.0", | ||
"fast-safe-stringify": "^2.0.2", | ||
"fast-safe-stringify": "^2.0.6", | ||
"fast-stable-stringify": "^1.0.0", | ||
"faster-stable-stringify": "^1.0.0", | ||
"json-stable-stringify": "^1.0.1", | ||
"json-stringify-deterministic": "^1.0.1", | ||
"json-stringify-safe": "^5.0.1", | ||
"standard": "^11.0.0-beta.0", | ||
"tap": "^11.0.0" | ||
"nyc": "^13.0.1", | ||
"standard": "^12.0.1", | ||
"tap": "^12.0.1" | ||
}, | ||
@@ -43,0 +47,0 @@ "repository": { |
@@ -74,17 +74,2 @@ # safe-stable-stringify | ||
array: deep circular x 1,400,577 ops/sec ±1.00% (92 runs sampled) | ||
full replacer: simple object x 993,352 ops/sec ±1.49% (92 runs sampled) | ||
full replacer: circular x 467,264 ops/sec ±0.68% (95 runs sampled) | ||
full replacer: deep x 14,855 ops/sec ±0.82% (91 runs sampled) | ||
full replacer: deep circular x 14,608 ops/sec ±1.68% (95 runs sampled) | ||
full array: simple object x 1,233,430 ops/sec ±0.58% (92 runs sampled) | ||
full array: circular x 1,205,360 ops/sec ±1.33% (90 runs sampled) | ||
full array: deep x 1,175,758 ops/sec ±0.63% (92 runs sampled) | ||
full array: deep circular x 1,171,813 ops/sec ±1.08% (92 runs sampled) | ||
indentation: simple object x 1,385,853 ops/sec ±2.18% (94 runs sampled) | ||
indentation: circular x 598,650 ops/sec ±1.26% (92 runs sampled) | ||
indentation: deep x 16,060 ops/sec ±0.76% (93 runs sampled) | ||
indentation: deep circular x 15,784 ops/sec ±1.31% (95 runs sampled) | ||
``` | ||
@@ -95,8 +80,9 @@ | ||
```md | ||
fast-json-stable-stringify x 8,906 ops/sec ±0.87% (91 runs sampled) | ||
json-stable-stringify x 6,821 ops/sec ±2.93% (88 runs sampled) | ||
fast-stable-stringify x 10,540 ops/sec ±0.51% (92 runs sampled) | ||
faster-stable-stringify x 8,450 ops/sec ±1.62% (89 runs sampled) | ||
fast-safe-stringify x 15,640 ops/sec ±0.51% (95 runs sampled) | ||
this x 25,482 ops/sec ±1.59% (87 runs sampled) | ||
fast-json-stable-stringify x 9,336 ops/sec ±0.64% (90 runs sampled) | ||
json-stable-stringify x 7,512 ops/sec ±0.63% (91 runs sampled) | ||
fast-stable-stringify x 11,674 ops/sec ±0.58% (92 runs sampled) | ||
faster-stable-stringify x 8,893 ops/sec ±0.51% (92 runs sampled) | ||
json-stringify-deterministic x 6,240 ops/sec ±0.68% (94 runs sampled) | ||
fast-safe-stringify x 15,939 ops/sec ±0.42% (96 runs sampled) | ||
this x 24,048 ops/sec ±0.44% (91 runs sampled) | ||
@@ -106,2 +92,4 @@ The fastest is this | ||
The `fast-safe-stringify` comparison uses the modules stable implementation. | ||
## Acknowledgements | ||
@@ -108,0 +96,0 @@ |
132
stable.js
@@ -28,3 +28,5 @@ 'use strict' | ||
const escapeFn = (str) => meta[str.charCodeAt(0)] | ||
function escapeFn (str) { | ||
return meta[str.charCodeAt(0)] | ||
} | ||
@@ -67,3 +69,3 @@ // Escape control characters, double quotes and the backslash. | ||
var i, res, join | ||
const mind = gap | ||
const startGap = gap | ||
var value = parent[key] | ||
@@ -109,7 +111,7 @@ | ||
if (gap !== '') { | ||
res += `\n${mind}` | ||
res += `\n${startGap}` | ||
} | ||
res += ']' | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -130,29 +132,15 @@ } | ||
} | ||
var last = false | ||
for (i = 0; i < keys.length - 1; i++) { | ||
var separator = '' | ||
for (i = 0; i < keys.length; i++) { | ||
key = keys[i] | ||
const tmp = stringifyFullFn(key, value, stack, replacer, indent) | ||
if (tmp !== undefined) { | ||
if (last) { | ||
res += join | ||
} | ||
res += `"${strEscape(key)}"${gap !== '' ? ': ' : ':'}${tmp}` | ||
last = true | ||
res += `${separator}"${strEscape(key)}"${gap !== '' ? ': ' : ':'}${tmp}` | ||
separator = join | ||
} | ||
} | ||
key = keys[i] | ||
const tmp = stringifyFullFn(key, value, stack, replacer, indent) | ||
if (tmp !== undefined) { | ||
if (last) { | ||
res += join | ||
} | ||
if (gap === '') { | ||
res += `"${strEscape(key)}":${tmp}` | ||
if (gap !== '') { | ||
if (separator !== '') { | ||
res += `\n${startGap}` | ||
} else { | ||
res += `"${strEscape(key)}": ${tmp}\n${mind}` | ||
} | ||
} else if (gap !== '') { | ||
if (last) { | ||
res += `\n${mind}` | ||
} else { | ||
res = '{' | ||
@@ -163,3 +151,3 @@ } | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -178,3 +166,3 @@ case 'string': | ||
var i, res, join | ||
const mind = gap | ||
const startGap = gap | ||
@@ -218,7 +206,7 @@ if (typeof value === 'object' && value !== null && typeof value.toJSON === 'function') { | ||
if (gap !== '') { | ||
res += `\n${mind}` | ||
res += `\n${startGap}` | ||
} | ||
res += ']' | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -238,3 +226,3 @@ } | ||
} | ||
var last = false | ||
var separator = '' | ||
for (i = 0; i < replacer.length; i++) { | ||
@@ -245,7 +233,4 @@ if (typeof replacer[i] === 'string' || typeof replacer[i] === 'number') { | ||
if (tmp !== undefined) { | ||
if (last) { | ||
res += join | ||
} | ||
res += `"${strEscape(key)}"${gap ? ': ' : ':'}${tmp}` | ||
last = true | ||
res += `${separator}"${strEscape(key)}"${gap ? ': ' : ':'}${tmp}` | ||
separator = join | ||
} | ||
@@ -255,4 +240,4 @@ } | ||
if (gap !== '') { | ||
if (last) { | ||
res += `\n${mind}` | ||
if (separator !== '') { | ||
res += `\n${startGap}` | ||
} else { | ||
@@ -264,3 +249,3 @@ res = '{' | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -279,4 +264,4 @@ case 'string': | ||
function stringifyIndent (key, value, stack, indent) { | ||
var i, res, join, add | ||
const mind = gap | ||
var i, res, join | ||
const startGap = gap | ||
@@ -326,7 +311,7 @@ switch (typeof value) { | ||
if (gap !== '') { | ||
res += `\n${mind}` | ||
res += `\n${startGap}` | ||
} | ||
res += ']' | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -343,29 +328,25 @@ } | ||
join = ',' | ||
add = '' | ||
} else { | ||
add = `\n${gap}` | ||
res += `\n${gap}` | ||
join = `,\n${gap}` | ||
} | ||
for (i = 0; i < keys.length - 1; i++) { | ||
var separator = '' | ||
for (i = 0; i < keys.length; i++) { | ||
key = keys[i] | ||
const tmp = stringifyIndent(key, value[key], stack, indent) | ||
if (tmp !== undefined) { | ||
add += `"${strEscape(key)}"${gap ? ': ' : ':'}${tmp}${join}` | ||
res += `${separator}"${strEscape(key)}"${gap ? ': ' : ':'}${tmp}` | ||
separator = join | ||
} | ||
} | ||
key = keys[i] | ||
const tmp = stringifyIndent(key, value[key], stack, indent) | ||
if (tmp !== undefined) { | ||
if (gap === '') { | ||
add += `"${strEscape(key)}":${tmp}` | ||
if (gap !== '') { | ||
if (separator !== '') { | ||
res += `\n${startGap}` | ||
} else { | ||
add += `"${strEscape(key)}": ${tmp}\n${mind}` | ||
res = '{' | ||
} | ||
} | ||
if (add.length > gap.length + 1) { | ||
res += add | ||
} | ||
res += '}' | ||
stack.pop() | ||
gap = mind | ||
gap = startGap | ||
return res | ||
@@ -424,3 +405,3 @@ case 'string': | ||
res = '{' | ||
var last = false | ||
var separator = '' | ||
for (i = 0; i < replacer.length; i++) { | ||
@@ -431,7 +412,4 @@ if (typeof replacer[i] === 'string' || typeof replacer[i] === 'number') { | ||
if (tmp !== undefined) { | ||
if (last) { | ||
res += ',' | ||
} | ||
res += `"${strEscape(key)}":${tmp}` | ||
last = true | ||
res += `${separator}"${strEscape(key)}":${tmp}` | ||
separator = ',' | ||
} | ||
@@ -497,22 +475,11 @@ } | ||
res = '{' | ||
var last = false | ||
for (i = 0; i < keys.length - 1; i++) { | ||
var separator = '' | ||
for (i = 0; i < keys.length; i++) { | ||
key = keys[i] | ||
const tmp = stringifyReplacerFn(key, value, stack, replacer) | ||
if (tmp !== undefined) { | ||
if (last === true) { | ||
res += ',' | ||
} | ||
res += `"${strEscape(key)}":${tmp}` | ||
last = true | ||
res += `${separator}"${strEscape(key)}":${tmp}` | ||
separator = ',' | ||
} | ||
} | ||
key = keys[i] | ||
const tmp = stringifyReplacerFn(key, value, stack, replacer) | ||
if (tmp !== undefined) { | ||
if (last === true) { | ||
res += ',' | ||
} | ||
res += `"${strEscape(key)}":${tmp}` | ||
} | ||
res += '}' | ||
@@ -579,15 +546,12 @@ stack.pop() | ||
stack.push(value) | ||
var separator = '' | ||
res = '{' | ||
for (i = 0; i < keys.length - 1; i++) { | ||
for (i = 0; i < keys.length; i++) { | ||
key = keys[i] | ||
const tmp = stringifySimple(key, value[key], stack) | ||
if (tmp !== undefined) { | ||
res += `"${strEscape(key)}":${tmp},` | ||
res += `${separator}"${strEscape(key)}":${tmp}` | ||
separator = ',' | ||
} | ||
} | ||
key = keys[i] | ||
const tmp = stringifySimple(key, value[key], stack) | ||
if (tmp !== undefined) { | ||
res += `"${strEscape(key)}":${tmp}` | ||
} | ||
res += '}' | ||
@@ -594,0 +558,0 @@ stack.pop() |
74
test.js
@@ -17,6 +17,6 @@ const { test } = require('tap') | ||
test('nested circular reference to root', function (assert) { | ||
const fixture = { name: 'Tywin Lannister' } | ||
const fixture = { name: 'Tywin\n\t"Lannister' } | ||
fixture.id = { circle: fixture } | ||
const expected = JSON.stringify( | ||
{ id: { circle: '[Circular]' }, name: 'Tywin Lannister' } | ||
{ id: { circle: '[Circular]' }, name: 'Tywin\n\t"Lannister' } | ||
) | ||
@@ -29,7 +29,7 @@ const actual = stringify(fixture) | ||
test('child circular reference', function (assert) { | ||
const fixture = { name: 'Tywin Lannister', child: { name: 'Tyrion Lannister' } } | ||
const fixture = { name: 'Tywin Lannister', child: { name: 'Tyrion\n\t"Lannister'.repeat(20) } } | ||
fixture.child.dinklage = fixture.child | ||
const expected = JSON.stringify({ | ||
child: { | ||
dinklage: '[Circular]', name: 'Tyrion Lannister' | ||
dinklage: '[Circular]', name: 'Tyrion\n\t"Lannister'.repeat(20) | ||
}, | ||
@@ -299,4 +299,4 @@ name: 'Tywin Lannister' | ||
// The null element will be removed! | ||
const expected = JSON.stringify({ f: null, null: true, 1: false }, replacer, 2) | ||
const actual = stringify({ f: null, null: true, 1: false }, replacer, 2) | ||
const expected = JSON.stringify({ f: null, null: true, 1: [false, -Infinity, 't'] }, replacer, 2) | ||
const actual = stringify({ f: null, null: true, 1: [false, -Infinity, 't'] }, replacer, 2) | ||
assert.is(actual, expected) | ||
@@ -317,4 +317,4 @@ assert.end() | ||
} | ||
const expected = JSON.stringify({ f: 1 }, replacer, ' ') | ||
const actual = stringify({ f: 1 }, replacer, ' ') | ||
const expected = JSON.stringify({ f: 1, b: null, c: 't', d: Infinity, e: true }, replacer, ' ') | ||
const actual = stringify({ f: 1, b: null, c: 't', d: Infinity, e: true }, replacer, ' ') | ||
assert.is(actual, expected) | ||
@@ -326,4 +326,4 @@ assert.end() | ||
const replacer = [''] | ||
const expected = JSON.stringify({ f: 1 }, replacer, ' ') | ||
const actual = stringify({ f: 1 }, replacer, ' ') | ||
const expected = JSON.stringify({ f: 1, b: null, c: 't', d: Infinity, e: true }, replacer, ' ') | ||
const actual = stringify({ f: 1, b: null, c: 't', d: Infinity, e: true }, replacer, ' ') | ||
assert.is(actual, expected) | ||
@@ -348,4 +348,4 @@ assert.end() | ||
test('array nulls, replacer and indentation', function (assert) { | ||
const expected = JSON.stringify([null, null], (_, v) => v, 3) | ||
const actual = stringify([null, null], (_, v) => v, 3) | ||
const expected = JSON.stringify([null, Infinity, 5, true, false], (_, v) => v, 3) | ||
const actual = stringify([null, Infinity, 5, true, false], (_, v) => v, 3) | ||
assert.is(actual, expected) | ||
@@ -355,2 +355,9 @@ assert.end() | ||
test('array nulls and replacer', function (assert) { | ||
const expected = JSON.stringify([null, Infinity, 5, true, false], (_, v) => v) | ||
const actual = stringify([null, Infinity, 5, true, false], (_, v) => v) | ||
assert.is(actual, expected) | ||
assert.end() | ||
}) | ||
test('array nulls, array replacer and indentation', function (assert) { | ||
@@ -363,7 +370,46 @@ const expected = JSON.stringify([null, null], [false], 3) | ||
test('array and array replacer', function (assert) { | ||
const expected = JSON.stringify([null, null, 't', Infinity, true, false], [2]) | ||
const actual = stringify([null, null, 't', Infinity, true, false], [2]) | ||
assert.is(actual, expected) | ||
assert.end() | ||
}) | ||
test('indentation with elements', function (assert) { | ||
const expected = JSON.stringify({ a: 1 }, null, 5) | ||
const actual = stringify({ a: 1 }, null, 5) | ||
const expected = JSON.stringify({ a: 1, b: [null, 't', Infinity, true] }, null, 5) | ||
const actual = stringify({ a: 1, b: [null, 't', Infinity, true] }, null, 5) | ||
assert.is(actual, expected) | ||
assert.end() | ||
}) | ||
test('object with undefined values', function (assert) { | ||
let obj = { a: 1, c: undefined, b: 'hello' } | ||
let expected = JSON.stringify(obj) | ||
let actual = stringify(obj) | ||
assert.is(actual, expected) | ||
obj = { b: 'hello', a: undefined, c: 1 } | ||
expected = JSON.stringify(obj) | ||
actual = stringify(obj) | ||
assert.is(actual, expected) | ||
assert.end() | ||
}) | ||
test('undefined values and indented', function (assert) { | ||
let obj = { a: 1, c: undefined, b: 'hello' } | ||
let expected = JSON.stringify(obj, null, 2) | ||
let actual = stringify(obj, null, 2) | ||
assert.is(actual, expected) | ||
obj = { b: 'hello', a: undefined, c: 1 } | ||
expected = JSON.stringify(obj) | ||
actual = stringify(obj) | ||
assert.is(actual, expected) | ||
assert.end() | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
46514
13
104