Skip to content

Commit f5a8740

Browse files
committed
Change circular handling to circular-json compatible handling. Fix undefined values in arrays.
1 parent 01b2496 commit f5a8740

File tree

3 files changed

+24
-85
lines changed

3 files changed

+24
-85
lines changed

jsonStreamify.js

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class JSONStreamify extends CoStream {
6060
}
6161
first = false;
6262
let stream = new JSONStreamify(data, this._iter.replacer, this._iter.space, this._iter.visited);
63+
stream._iter._stack = this._iter._stack;
6364
stream._iter._parentCtxType = Array;
6465
stream.once('end', () => next(null, undefined)).pipe(pass, {
6566
end: false
@@ -72,26 +73,12 @@ class JSONStreamify extends CoStream {
7273
}
7374

7475
if (obj.state === 'circular') {
75-
let replacer;
76-
this.emit('circular', Object.assign(obj, {
77-
replace: (promise) => {
78-
if (promise instanceof Promise) {
79-
obj.value = promise;
80-
}
81-
}
82-
}));
83-
84-
// Wait for replace
85-
yield new Promise(resolve => process.nextTick(resolve));
86-
87-
if (!(obj.value instanceof Promise)) {
88-
yield this.push('"[Circular]"');
89-
}
76+
yield this.push(JSON.stringify(`~${obj.value.join('~')}`));
9077
}
9178

9279
if (obj.value instanceof Promise) {
93-
let childIterator = new RecursiveIterable(yield obj.value, this._iter.replacer, this._iter.space, this._iter.visited)[Symbol.iterator]();
94-
obj.value = obj.attachChild(childIterator);
80+
let childIterator = new RecursiveIterable(yield obj.value, this._iter.replacer, this._iter.space, this._iter.visited, this._iter._stack)[Symbol.iterator]();
81+
obj.value = obj.attachChild(childIterator, obj.key);
9582
insertSeparator = false;
9683
continue;
9784
}

recursiveIterable.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ const Readable = require('stream').Readable;
44
const isReadableStream = require('./utils').isReadableStream;
55

66
class RecursiveIterable {
7-
constructor(obj, replacer, space, visited) {
7+
constructor(obj, replacer, space, visited, stack) {
88
// Save a copy of the root object so we can be memory effective
99
if (obj && typeof obj.toJSON === 'function') {
1010
obj = obj.toJSON();
1111
}
1212
this.exclude = [Promise, {
1313
__shouldExclude: isReadableStream
1414
}];
15-
this.visited = visited || new WeakSet();
15+
this._stack = stack || [];
16+
this.visited = visited || new WeakMap();
1617
if (this._shouldIterate(obj)) {
17-
this.visited.add(obj);
18+
this.visited.set(obj, this._stack.slice(0));
1819
}
1920
this.obj = this._shouldIterate(obj) ? (Array.isArray(obj) ? obj.slice(0) : Object.assign({}, obj)) : obj;
2021
this.replacerIsArray = Array.isArray(replacer);
@@ -34,12 +35,16 @@ class RecursiveIterable {
3435
let isObject = this._shouldIterate(this.obj);
3536
let ctxType = RecursiveIterable._getType(this.obj);
3637
let nextIndex = 0;
37-
let keys = isObject && Object.keys(this.obj);
38+
let keys = isObject && (Array.isArray(this.obj) ? Array.from(Array(this.obj.length).keys()) : Object.keys(this.obj));
3839
let childIterator;
3940
let closed = !isObject;
4041
let opened = closed;
4142

42-
const attachIterator = iterator => childIterator = iterator;
43+
const attachIterator = (iterator, addToStack) => {
44+
childIterator = iterator;
45+
childIterator._stack = this._stack.concat(addToStack || []);
46+
};
47+
4348
const ctx = {
4449
depth: 0,
4550
type: ctxType,
@@ -108,9 +113,10 @@ class RecursiveIterable {
108113
if (this._shouldIterate(val)) {
109114
if (this.visited.has(val)) {
110115
state = 'circular';
116+
val = this.visited.get(val);
111117
} else {
112118
state = 'child';
113-
childIterator = new RecursiveIterable(val, this.replacer, this.space, this.visited)[Symbol.iterator]();
119+
childIterator = new RecursiveIterable(val, this.replacer, this.space, this.visited, this._stack.concat(key))[Symbol.iterator]();
114120
childIterator.ctxType = ctx.type;
115121
childIterator.depth = ctx.depth + 1;
116122
childIterator.type = RecursiveIterable._getType(val);
@@ -129,6 +135,7 @@ class RecursiveIterable {
129135
key: key,
130136
value: val,
131137
state: state,
138+
stack: this._stack.slice(0),
132139
type: type,
133140
ctxType: ctx.type,
134141
attachChild: attachIterator

test.js

Lines changed: 7 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -139,68 +139,13 @@ describe('Streamify', () => {
139139
})
140140
}, `{"a":[{"name":"name","arr":[],"date":"${date.toJSON()}"}]}`));
141141

142-
it(`{ a: [Circular] } should be {"a":"[Circular]"}`, () => {
143-
let data = {};
144-
data.a = data;
145-
146-
let deferred0 = Promise.defer();
147-
let deferred1 = Promise.defer();
148-
let str = '';
149-
new JSONStreamify(data)
150-
.once('circular', err => {
151-
try {
152-
expect(err).to.be.ok();
153-
} catch (err) {
154-
return deferred1.reject(err);
155-
}
156-
deferred1.resolve();
157-
})
158-
.on('data', data => str += data.toString())
159-
.once('end', () => {
160-
try {
161-
expect(str).to.equal(`{"a":"[Circular]"}`);
162-
} catch (err) {
163-
return deferred0.reject(err);
164-
}
165-
deferred0.resolve();
166-
});
167-
168-
return Promise.all([deferred0.promise, deferred1.promise]);
169-
});
170-
171-
it(`{ a: [Circular] } should be {"a":"custom"}`, () => {
172-
let data = {};
173-
data.a = data;
174-
175-
let deferred0 = Promise.defer();
176-
let deferred1 = Promise.defer();
177-
let str = '';
178-
new JSONStreamify(data)
179-
.once('circular', err => {
180-
try {
181-
expect(err).to.be.ok();
182-
expect(err.replace).to.be.a(Function);
183-
err.replace(Promise.resolve('custom'));
184-
} catch (err) {
185-
return deferred1.reject(err);
186-
}
187-
deferred1.resolve();
188-
})
189-
.on('data', data => str += data.toString())
190-
.once('end', () => {
191-
try {
192-
expect(str).to.equal(`{"a":"custom"}`);
193-
} catch (err) {
194-
return deferred0.reject(err);
195-
}
196-
deferred0.resolve();
197-
});
198-
199-
return Promise.all([deferred0.promise, deferred1.promise]);
200-
});
201-
202142
let circularData0 = {};
203143
circularData0.a = circularData0;
204-
circularData0.b = [circularData0, { a: circularData0 }]
205-
it('{a: Circular, b: [Circular, { a: Circular }]} should be {"a":"[Circular]","b":["[Circular]",{"a":"[Circular]"}]}', createTest(circularData0, '{"a":"[Circular]","b":["[Circular]",{"a":"[Circular]"}]}'));
144+
it(`{ a: a } should be {"a":"~"}`, createTest(circularData0, `{"a":"~"}`));
145+
146+
let circularData1 = {};
147+
circularData1.a = circularData1;
148+
circularData1.b = [circularData1, { a: circularData1 }]
149+
circularData1.b[3] = circularData1.b[1];
150+
it('{a: a, b: [a, { a: a },,a.b.1]} should be {"a":"~","b":["~",{"a":"~"},null,"~b~1"]}', createTest(circularData1, '{"a":"~","b":["~",{"a":"~"},null,"~b~1"]}'));
206151
});

0 commit comments

Comments
 (0)