Skip to content

Commit d7c85a4

Browse files
committed
Support custom inspect through 'nodejs.util.inspect.custom' symbol
Fixes #1599 See https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
1 parent 80766ed commit d7c85a4

File tree

6 files changed

+57
-9
lines changed

6 files changed

+57
-9
lines changed

src/jsutils/__tests__/inspect-test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import { expect } from 'chai';
1111
import { describe, it } from 'mocha';
1212
import inspect from '../inspect';
13+
import invariant from '../invariant';
14+
import nodejsCustomInspectSymbol from '../nodejsCustomInspectSymbol';
1315

1416
describe('inspect', () => {
1517
it('undefined', () => {
@@ -75,4 +77,19 @@ describe('inspect', () => {
7577

7678
expect(inspect(object)).to.equal('<custom inspect>');
7779
});
80+
81+
it('custom inspect', () => {
82+
invariant(nodejsCustomInspectSymbol);
83+
84+
const object = {
85+
inspect() {
86+
return '<custom inspect>';
87+
},
88+
[String(nodejsCustomInspectSymbol)]() {
89+
return '<custom symbol inspect>';
90+
},
91+
};
92+
93+
expect(inspect(object)).to.equal('<custom symbol inspect>');
94+
});
7895
});

src/jsutils/defineToJSON.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@
77
* @flow strict
88
*/
99

10+
import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol';
11+
1012
/**
1113
* The `defineToJSON()` function defines toJSON() and inspect() prototype
12-
* methods which are aliases for toString().
14+
* methods, if no function provided they become aliases for toString().
1315
*/
14-
export default function defineToJSON(classObject: Class<any>): void {
15-
classObject.prototype.toJSON = classObject.prototype.inspect =
16-
classObject.prototype.toString;
16+
export default function defineToJSON(
17+
// eslint-disable-next-line flowtype/no-weak-types
18+
classObject: Class<any> | Function,
19+
fn?: () => any = classObject.prototype.toString,
20+
): void {
21+
classObject.prototype.toJSON = fn;
22+
classObject.prototype.inspect = fn;
23+
if (nodejsCustomInspectSymbol) {
24+
classObject.prototype[nodejsCustomInspectSymbol] = fn;
25+
}
1726
}

src/jsutils/inspect.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* @flow strict
88
*/
99

10+
import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol';
11+
1012
/**
1113
* Used to print values in error messages.
1214
*/
@@ -18,7 +20,10 @@ export default function inspect(value: mixed): string {
1820
return value.name ? `[function ${value.name}]` : '[function]';
1921
case 'object':
2022
if (value) {
21-
if (typeof value.inspect === 'function') {
23+
const customInspectFn = value[String(nodejsCustomInspectSymbol)];
24+
if (typeof customInspectFn === 'function') {
25+
return customInspectFn();
26+
} else if (typeof value.inspect === 'function') {
2227
return value.inspect();
2328
} else if (Array.isArray(value)) {
2429
return '[' + value.map(inspect).join(', ') + ']';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
const nodejsCustomInspectSymbol =
11+
typeof Symbol === 'function'
12+
? Symbol.for('nodejs.util.inspect.custom')
13+
: undefined;
14+
15+
export default nodejsCustomInspectSymbol;

src/language/lexer.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* @flow strict
88
*/
99

10+
import defineToJSON from '../jsutils/defineToJSON';
1011
import type { Token } from './ast';
1112
import type { Source } from './source';
1213
import { syntaxError } from '../error';
@@ -162,14 +163,14 @@ function Tok(
162163
}
163164

164165
// Print a simplified form when appearing in JSON/util.inspect.
165-
Tok.prototype.toJSON = Tok.prototype.inspect = function toJSON() {
166+
defineToJSON(Tok, function() {
166167
return {
167168
kind: this.kind,
168169
value: this.value,
169170
line: this.line,
170171
column: this.column,
171172
};
172-
};
173+
});
173174

174175
function printCharCode(code) {
175176
return (

src/language/parser.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import inspect from '../jsutils/inspect';
11+
import defineToJSON from '../jsutils/defineToJSON';
1112
import { Source } from './source';
1213
import { syntaxError } from '../error';
1314
import type { GraphQLError } from '../error';
@@ -1444,9 +1445,9 @@ function Loc(startToken: Token, endToken: Token, source: Source) {
14441445
}
14451446

14461447
// Print a simplified form when appearing in JSON/util.inspect.
1447-
Loc.prototype.toJSON = Loc.prototype.inspect = function toJSON() {
1448+
defineToJSON(Loc, function() {
14481449
return { start: this.start, end: this.end };
1449-
};
1450+
});
14501451

14511452
/**
14521453
* Determines if the next token is of a given kind

0 commit comments

Comments
 (0)