json-stream-stringify
Advanced tools
Comparing version 1.3.5 to 1.5.0
@@ -10,5 +10,5 @@ 'use strict'; | ||
class JSONStreamify extends CoStream { | ||
constructor(value, replacer) { | ||
constructor(value, replacer, space, _visited, _stack) { | ||
super(arguments); | ||
this._iter = new RecursiveIterable(replacer instanceof Function ? replacer(undefined, value) : value, replacer); | ||
this._iter = new RecursiveIterable(replacer instanceof Function ? replacer(undefined, value) : value, replacer, space, _visited, _stack); | ||
} | ||
@@ -56,5 +56,6 @@ | ||
const pass = new PassThrough(); | ||
let i = 0; | ||
obj.value.pipe(new Transform({ | ||
objectMode: true, | ||
transform: function(data, enc, next) { | ||
transform: (data, enc, next) => { | ||
if (!first) { | ||
@@ -64,3 +65,4 @@ pass.push(','); | ||
first = false; | ||
let stream = new JSONStreamify(data); | ||
let stream = new JSONStreamify(data, this._iter.replacer, this._iter.space, this._iter.visited); | ||
stream._iter._stack = obj.stack.concat(i++); | ||
stream._iter._parentCtxType = Array; | ||
@@ -77,4 +79,9 @@ stream.once('end', () => next(null, undefined)).pipe(pass, { | ||
if (obj.state === 'circular') { | ||
yield this.push(JSON.stringify({ $ref: `$${obj.value.map(v => `[${JSON.stringify(v)}]`).join('')}` })); | ||
} | ||
if (obj.value instanceof Promise) { | ||
obj.value = obj.attachChild(new RecursiveIterable(yield obj.value)[Symbol.iterator]()); | ||
let childIterator = new RecursiveIterable(yield obj.value, this._iter.replacer, this._iter.space, this._iter.visited, obj.stack.concat(obj.key || []))[Symbol.iterator](); | ||
obj.value = obj.attachChild(childIterator, obj.key); | ||
insertSeparator = false; | ||
@@ -94,4 +101,4 @@ continue; | ||
module.exports = function (obj, replacer) { | ||
module.exports = function(obj, replacer) { | ||
return new JSONStreamify(obj, replacer); | ||
}; |
{ | ||
"name": "json-stream-stringify", | ||
"version": "1.3.5", | ||
"version": "1.5.0", | ||
"description": "JSON.Stringify as a readable stream", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -93,3 +93,2 @@ # JSON Stream Stringify | ||
- Space option | ||
- Circular dependency detection/handling (infinite loops may occur as it is) | ||
@@ -102,2 +101,4 @@ Feel free to contribute. | ||
Circular structures are handled using a WeakMap based implementation of [Douglas Crockfords Decycle method](https://github.com/douglascrockford/JSON-js/blob/master/cycle.js). To restore circular structures; use Crockfords Retrocycle method on the parsed object. | ||
## Requirements | ||
@@ -104,0 +105,0 @@ NodeJS >4.2.2 |
@@ -7,3 +7,3 @@ 'use strict'; | ||
class RecursiveIterable { | ||
constructor(obj, replacer) { | ||
constructor(obj, replacer, space, visited, stack) { | ||
// Save a copy of the root object so we can be memory effective | ||
@@ -16,5 +16,15 @@ if (obj && typeof obj.toJSON === 'function') { | ||
}]; | ||
this.obj = this._shouldIterate(obj) ? (Array.isArray(obj) ? obj.slice(0) : Object.assign({}, obj)) : obj; | ||
this._stack = stack || []; | ||
this.visited = visited || new WeakMap(); | ||
if (this._shouldIterate(obj)) { | ||
this.isVisited = this.visited.has(obj); | ||
if (!this.isVisited) { | ||
// Save only unvisited stack to weakmap | ||
this.visited.set(obj, this._stack.slice(0)); | ||
} | ||
} | ||
this.obj = this._shouldIterate(obj) && !this.isVisited ? (Array.isArray(obj) ? obj.slice(0) : Object.assign({}, obj)) : obj; | ||
this.replacerIsArray = Array.isArray(replacer); | ||
this.replacer = replacer instanceof Function || this.replacerIsArray ? replacer : undefined; | ||
this.replacer = replacer instanceof Function || this.replacerIsArray ? replacer : undefined; | ||
this.space = space; | ||
} | ||
@@ -34,3 +44,3 @@ | ||
let nextIndex = 0; | ||
let keys = isObject && Object.keys(this.obj); | ||
let keys = isObject && (Array.isArray(this.obj) ? Array.from(Array(this.obj.length).keys()) : Object.keys(this.obj)); | ||
let childIterator; | ||
@@ -40,3 +50,7 @@ let closed = !isObject; | ||
const attachIterator = iterator => childIterator = iterator; | ||
const attachIterator = (iterator, addToStack) => { | ||
childIterator = iterator; | ||
childIterator._stack = this._stack.concat(addToStack || []); | ||
}; | ||
const ctx = { | ||
@@ -60,5 +74,12 @@ depth: 0, | ||
if (!opened) { | ||
state = 'open'; | ||
type = ctxType; | ||
opened = true; | ||
if (this.isVisited) { | ||
state = 'circular'; | ||
val = this.visited.get(this.obj); | ||
opened = closed = true; | ||
keys.length = 0; | ||
} else { | ||
state = 'open'; | ||
type = ctxType; | ||
opened = true; | ||
} | ||
} else if (!closed && !keys.length) { | ||
@@ -82,7 +103,3 @@ state = 'close'; | ||
if(state === 'value') { | ||
if (typeof val === 'function') { | ||
return ctx.next(); | ||
} | ||
if (state === 'value') { | ||
if (this.replacer && !this.replacerIsArray) { | ||
@@ -96,2 +113,6 @@ val = this.replacer(key, val); | ||
if (typeof val === 'function') { | ||
val = undefined; | ||
} | ||
if (val === undefined) { | ||
@@ -111,7 +132,12 @@ if (key) { | ||
if (this._shouldIterate(val)) { | ||
state = 'child'; | ||
childIterator = new RecursiveIterable(val)[Symbol.iterator](); | ||
childIterator.ctxType = ctx.type; | ||
childIterator.depth = ctx.depth + 1; | ||
childIterator.type = RecursiveIterable._getType(val); | ||
if (this.visited.has(val)) { | ||
state = 'circular'; | ||
val = this.visited.get(val); | ||
} else { | ||
state = 'child'; | ||
childIterator = new RecursiveIterable(val, this.replacer, this.space, this.visited, this._stack.concat(key))[Symbol.iterator](); | ||
childIterator.ctxType = ctx.type; | ||
childIterator.depth = ctx.depth + 1; | ||
childIterator.type = RecursiveIterable._getType(val); | ||
} | ||
} | ||
@@ -131,2 +157,3 @@ | ||
state: state, | ||
stack: this._stack.slice(0), | ||
type: type, | ||
@@ -133,0 +160,0 @@ ctxType: ctx.type, |
42
test.js
@@ -73,5 +73,7 @@ 'use strict'; | ||
it('{a:function(){}} should be {}', createTest({ | ||
a: function(){} | ||
a: function() {} | ||
}, '{}')); | ||
it('[function(){}] should be [null]', createTest([function() {}], '[null]')); | ||
it('{a:date} should be {"a":date.toJSON()}', createTest({ | ||
@@ -89,3 +91,4 @@ a: date | ||
it('{a:[1], "b": 2} should be {"a":[1],"b":2}', createTest({ | ||
a: [1], b: 2 | ||
a: [1], | ||
b: 2 | ||
}, '{"a":[1],"b":2}')); | ||
@@ -95,3 +98,10 @@ | ||
it('[[[]],[[]]] should be [[[]],[[]]]', createTest([[[]],[[]]], '[[[]],[[]]]')); | ||
it('[[[]],[[]]] should be [[[]],[[]]]', createTest([ | ||
[ | ||
[] | ||
], | ||
[ | ||
[] | ||
] | ||
], '[[[]],[[]]]')); | ||
@@ -134,2 +144,28 @@ it('[1, undefined, 2] should be [1,null,2]', createTest([1, undefined, 2], '[1,null,2]')); | ||
}, `{"a":[{"name":"name","arr":[],"date":"${date.toJSON()}"}]}`)); | ||
describe('circular structure', function() { | ||
let circularData0 = {}; | ||
circularData0.a = circularData0; | ||
it(`{ a: a } should be {"a":{"$ref":"$"}}`, createTest(circularData0, `{"a":{"$ref":"$"}}`)); | ||
let circularData1 = {}; | ||
circularData1.a = circularData1; | ||
circularData1.b = [circularData1, { | ||
a: circularData1 | ||
}] | ||
circularData1.b[3] = ReadableStream(circularData1.b[1]); | ||
it('{a: a, b: [a, { a: a },,ReadableStream(b.1)]} should be {"a":{"$ref":"$"},"b":[{"$ref":"$"},{"a":{"$ref":"$"}},null,[{"$ref":"$[\\"b\\"][1]"}]]}', createTest(circularData1, '{"a":{"$ref":"$"},"b":[{"$ref":"$"},{"a":{"$ref":"$"}},null,[{"$ref":"$[\\"b\\"][1]"}]]}')); | ||
let circularData2 = {}; | ||
let data2 = { | ||
a: 'deep' | ||
}; | ||
circularData2.a = Promise.resolve({ | ||
b: data2 | ||
}); | ||
circularData2.b = data2; | ||
it(`{ a: Promise({ b: { a: 'deep' } }), b: a.b } should be {"a":{"b":{"a":"deep"}},"b":{"$ref":"$[\\"a\\"][\\"b\\"]"}}`, createTest(circularData2, `{"a":{"b":{"a":"deep"}},"b":{"$ref":"$[\\"a\\"][\\"b\\"]"}}`)); | ||
}); | ||
}); |
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
24433
422
123