blob: 49e0d8babc3e11fe3e655d39ae978d8add665a83 [file] [log] [blame]
// @flow strict
import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol';
const MAX_ARRAY_LENGTH = 10;
const MAX_RECURSIVE_DEPTH = 2;
/**
* Used to print values in error messages.
*/
export default function inspect(value: mixed): string {
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':
if (value === null) {
return 'null';
}
return formatObjectValue(value, seenValues);
default:
return String(value);
}
}
function formatObjectValue(value, previouslySeenValues) {
if (previouslySeenValues.indexOf(value) !== -1) {
return '[Circular]';
}
const seenValues = [...previouslySeenValues, value];
const customInspectFn = getCustomFn(value);
if (customInspectFn !== undefined) {
// $FlowFixMe(>=0.90.0)
const customValue = customInspectFn.call(value);
// check for infinite recursion
if (customValue !== value) {
return typeof customValue === 'string'
? customValue
: formatValue(customValue, seenValues);
}
} else if (Array.isArray(value)) {
return formatArray(value, seenValues);
}
return formatObject(value, seenValues);
}
function formatObject(object, seenValues) {
const keys = Object.keys(object);
if (keys.length === 0) {
return '{}';
}
if (seenValues.length > MAX_RECURSIVE_DEPTH) {
return '[' + getObjectTag(object) + ']';
}
const properties = keys.map(key => {
const value = formatValue(object[key], seenValues);
return key + ': ' + value;
});
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 getCustomFn(object) {
const customInspectFn = object[String(nodejsCustomInspectSymbol)];
if (typeof customInspectFn === 'function') {
return customInspectFn;
}
if (typeof object.inspect === 'function') {
return object.inspect;
}
}
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;
}