| const MAX_ARRAY_LENGTH = 10; |
| const MAX_RECURSIVE_DEPTH = 2; |
| /** |
| * Used to print values in error messages. |
| */ |
|
|
| export function inspect(value) { |
| return formatValue(value, []); |
| } |
|
|
| function formatValue(value, seenValues) { |
| switch (typeof value) { |
| case 'string': |
| return JSON.stringify(value); |
|
|
| case 'function': |
| return value.name ? `[function ${value.name}]` : '[function]'; |
|
|
| case 'object': |
| return formatObjectValue(value, seenValues); |
|
|
| default: |
| return String(value); |
| } |
| } |
|
|
| function formatObjectValue(value, previouslySeenValues) { |
| if (value === null) { |
| return 'null'; |
| } |
|
|
| if (previouslySeenValues.includes(value)) { |
| return '[Circular]'; |
| } |
|
|
| const seenValues = [...previouslySeenValues, value]; |
|
|
| if (isJSONable(value)) { |
| const jsonValue = value.toJSON(); // check for infinite recursion |
|
|
| if (jsonValue !== value) { |
| return typeof jsonValue === 'string' |
| ? jsonValue |
| : formatValue(jsonValue, seenValues); |
| } |
| } else if (Array.isArray(value)) { |
| return formatArray(value, seenValues); |
| } |
|
|
| return formatObject(value, seenValues); |
| } |
|
|
| function isJSONable(value) { |
| return typeof value.toJSON === 'function'; |
| } |
|
|
| function formatObject(object, seenValues) { |
| const entries = Object.entries(object); |
|
|
| if (entries.length === 0) { |
| return '{}'; |
| } |
|
|
| if (seenValues.length > MAX_RECURSIVE_DEPTH) { |
| return '[' + getObjectTag(object) + ']'; |
| } |
|
|
| const properties = entries.map( |
| ([key, value]) => key + ': ' + formatValue(value, seenValues), |
| ); |
| return '{ ' + properties.join(', ') + ' }'; |
| } |
|
|
| function formatArray(array, seenValues) { |
| if (array.length === 0) { |
| return '[]'; |
| } |
|
|
| if (seenValues.length > MAX_RECURSIVE_DEPTH) { |
| return '[Array]'; |
| } |
|
|
| const len = Math.min(MAX_ARRAY_LENGTH, array.length); |
| const remaining = array.length - len; |
| const items = []; |
|
|
| for (let i = 0; i < len; ++i) { |
| items.push(formatValue(array[i], seenValues)); |
| } |
|
|
| if (remaining === 1) { |
| items.push('... 1 more item'); |
| } else if (remaining > 1) { |
| items.push(`... ${remaining} more items`); |
| } |
|
|
| return '[' + items.join(', ') + ']'; |
| } |
|
|
| function getObjectTag(object) { |
| const tag = Object.prototype.toString |
| .call(object) |
| .replace(/^\[object /, '') |
| .replace(/]$/, ''); |
|
|
| if (tag === 'Object' && typeof object.constructor === 'function') { |
| const name = object.constructor.name; |
|
|
| if (typeof name === 'string' && name !== '') { |
| return name; |
| } |
| } |
|
|
| return tag; |
| } |