Skip to content

Commit f7935c5

Browse files
committed
inspect: Limit maximum depth of printed objects
1 parent e8b827f commit f7935c5

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

src/jsutils/__tests__/inspect-test.js

+21
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ describe('inspect', () => {
5555
expect(inspect([1, NaN])).to.equal('[1, NaN]');
5656
expect(inspect([['a', 'b'], 'c'])).to.equal('[["a", "b"], "c"]');
5757

58+
expect(inspect([[[]]])).to.equal('[[[]]]');
59+
expect(inspect([[['a']]])).to.equal('[[[Array]]]');
60+
5861
expect(inspect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])).to.equal(
5962
'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]',
6063
);
@@ -74,6 +77,9 @@ describe('inspect', () => {
7477
expect(inspect({ a: 1, b: 2 })).to.equal('{ a: 1, b: 2 }');
7578
expect(inspect({ array: [null, 0] })).to.equal('{ array: [null, 0] }');
7679

80+
expect(inspect({ a: { b: {} } })).to.equal('{ a: { b: {} } }');
81+
expect(inspect({ a: { b: { c: 1 } } })).to.equal('{ a: { b: [Object] } }');
82+
7783
const map = Object.create(null);
7884
map['a'] = true;
7985
map['b'] = null;
@@ -125,4 +131,19 @@ describe('inspect', () => {
125131

126132
expect(inspect(object)).to.equal('Hello World!');
127133
});
134+
135+
it('Use class names for the shortform of an object', () => {
136+
class Foo {
137+
foo: string;
138+
139+
constructor() {
140+
this.foo = 'bar';
141+
}
142+
}
143+
144+
expect(inspect([[new Foo()]])).to.equal('[[[Foo]]]');
145+
146+
(Foo.prototype: any)[Symbol.toStringTag] = 'Bar';
147+
expect(inspect([[new Foo()]])).to.equal('[[[Bar]]]');
148+
});
128149
});

src/jsutils/inspect.js

+34-9
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol';
1111

1212
const MAX_ARRAY_LENGTH = 10;
13+
const MAX_RECURSIVE_DEPTH = 2;
1314

1415
/**
1516
* Used to print values in error messages.
1617
*/
1718
export default function inspect(value: mixed): string {
18-
return formatValue(value);
19+
return formatValue(value, 0);
1920
}
2021

21-
function formatValue(value) {
22+
function formatValue(value, recurseTimes) {
2223
switch (typeof value) {
2324
case 'string':
2425
return JSON.stringify(value);
@@ -33,12 +34,12 @@ function formatValue(value) {
3334
const customValue = customInspectFn.call(value);
3435
return typeof customValue === 'string'
3536
? customValue
36-
: formatValue(customValue);
37+
: formatValue(customValue, recurseTimes);
3738
} else if (Array.isArray(value)) {
38-
return formatArray(value);
39+
return formatArray(value, recurseTimes);
3940
}
4041

41-
return formatObject(value);
42+
return formatObject(value, recurseTimes);
4243
}
4344

4445
return String(value);
@@ -47,31 +48,39 @@ function formatValue(value) {
4748
}
4849
}
4950

50-
function formatObject(object) {
51+
function formatObject(object, recurseTimes) {
5152
const keys = Object.keys(object);
5253
if (keys.length === 0) {
5354
return '{}';
5455
}
5556

57+
if (recurseTimes === MAX_RECURSIVE_DEPTH) {
58+
return '[' + getObjectTag(object) + ']';
59+
}
60+
5661
const properties = keys.map(key => {
57-
const value = formatValue(object[key]);
62+
const value = formatValue(object[key], recurseTimes + 1);
5863
return key + ': ' + value;
5964
});
6065

6166
return '{ ' + properties.join(', ') + ' }';
6267
}
6368

64-
function formatArray(array) {
69+
function formatArray(array, recurseTimes) {
6570
if (array.length === 0) {
6671
return '[]';
6772
}
6873

74+
if (recurseTimes === MAX_RECURSIVE_DEPTH) {
75+
return '[Array]';
76+
}
77+
6978
const len = Math.min(MAX_ARRAY_LENGTH, array.length);
7079
const remaining = array.length - len;
7180
const items = [];
7281

7382
for (let i = 0; i < len; ++i) {
74-
items.push(formatValue(array[i]));
83+
items.push(formatValue(array[i], recurseTimes + 1));
7584
}
7685

7786
if (remaining === 1) {
@@ -94,3 +103,19 @@ function getCustomFn(object) {
94103
return object.inspect;
95104
}
96105
}
106+
107+
function getObjectTag(object) {
108+
const tag = Object.prototype.toString
109+
.call(object)
110+
.replace(/^\[object /, '')
111+
.replace(/]$/, '');
112+
113+
if (tag === 'Object' && typeof object.constructor === 'function') {
114+
const name = object.constructor.name;
115+
if (typeof name === 'string') {
116+
return name;
117+
}
118+
}
119+
120+
return tag;
121+
}

0 commit comments

Comments
 (0)