1 |
|
2 |
|
3 | import { GraphQLError } from 'graphql';
|
4 | import { isAggregateError } from './AggregateError.js';
|
5 | const MAX_RECURSIVE_DEPTH = 3;
|
6 |
|
7 |
|
8 |
|
9 | export function inspect(value) {
|
10 | return formatValue(value, []);
|
11 | }
|
12 | function formatValue(value, seenValues) {
|
13 | switch (typeof value) {
|
14 | case 'string':
|
15 | return JSON.stringify(value);
|
16 | case 'function':
|
17 | return value.name ? `[function ${value.name}]` : '[function]';
|
18 | case 'object':
|
19 | return formatObjectValue(value, seenValues);
|
20 | default:
|
21 | return String(value);
|
22 | }
|
23 | }
|
24 | function formatError(value) {
|
25 | if (value instanceof GraphQLError) {
|
26 | return value.toString();
|
27 | }
|
28 | return `${value.name}: ${value.message};\n ${value.stack}`;
|
29 | }
|
30 | function formatObjectValue(value, previouslySeenValues) {
|
31 | if (value === null) {
|
32 | return 'null';
|
33 | }
|
34 | if (value instanceof Error) {
|
35 | if (isAggregateError(value)) {
|
36 | return formatError(value) + '\n' + formatArray(value.errors, previouslySeenValues);
|
37 | }
|
38 | return formatError(value);
|
39 | }
|
40 | if (previouslySeenValues.includes(value)) {
|
41 | return '[Circular]';
|
42 | }
|
43 | const seenValues = [...previouslySeenValues, value];
|
44 | if (isJSONable(value)) {
|
45 | const jsonValue = value.toJSON();
|
46 |
|
47 | if (jsonValue !== value) {
|
48 | return typeof jsonValue === 'string' ? jsonValue : formatValue(jsonValue, seenValues);
|
49 | }
|
50 | }
|
51 | else if (Array.isArray(value)) {
|
52 | return formatArray(value, seenValues);
|
53 | }
|
54 | return formatObject(value, seenValues);
|
55 | }
|
56 | function isJSONable(value) {
|
57 | return typeof value.toJSON === 'function';
|
58 | }
|
59 | function formatObject(object, seenValues) {
|
60 | const entries = Object.entries(object);
|
61 | if (entries.length === 0) {
|
62 | return '{}';
|
63 | }
|
64 | if (seenValues.length > MAX_RECURSIVE_DEPTH) {
|
65 | return '[' + getObjectTag(object) + ']';
|
66 | }
|
67 | const properties = entries.map(([key, value]) => key + ': ' + formatValue(value, seenValues));
|
68 | return '{ ' + properties.join(', ') + ' }';
|
69 | }
|
70 | function formatArray(array, seenValues) {
|
71 | if (array.length === 0) {
|
72 | return '[]';
|
73 | }
|
74 | if (seenValues.length > MAX_RECURSIVE_DEPTH) {
|
75 | return '[Array]';
|
76 | }
|
77 | const len = array.length;
|
78 | const remaining = array.length;
|
79 | const items = [];
|
80 | for (let i = 0; i < len; ++i) {
|
81 | items.push(formatValue(array[i], seenValues));
|
82 | }
|
83 | if (remaining === 1) {
|
84 | items.push('... 1 more item');
|
85 | }
|
86 | else if (remaining > 1) {
|
87 | items.push(`... ${remaining} more items`);
|
88 | }
|
89 | return '[' + items.join(', ') + ']';
|
90 | }
|
91 | function getObjectTag(object) {
|
92 | const tag = Object.prototype.toString
|
93 | .call(object)
|
94 | .replace(/^\[object /, '')
|
95 | .replace(/]$/, '');
|
96 | if (tag === 'Object' && typeof object.constructor === 'function') {
|
97 | const name = object.constructor.name;
|
98 | if (typeof name === 'string' && name !== '') {
|
99 | return name;
|
100 | }
|
101 | }
|
102 | return tag;
|
103 | }
|