/* ! * loupe * Copyright(c) 2013 Jake Luer * MIT Licensed */ import inspectArray from './array.js'; import inspectTypedArray from './typedarray.js'; import inspectDate from './date.js'; import inspectFunction from './function.js'; import inspectMap from './map.js'; import inspectNumber from './number.js'; import inspectBigInt from './bigint.js'; import inspectRegExp from './regexp.js'; import inspectSet from './set.js'; import inspectString from './string.js'; import inspectSymbol from './symbol.js'; import inspectPromise from './promise.js'; import inspectClass from './class.js'; import inspectObject from './object.js'; import inspectArguments from './arguments.js'; import inspectError from './error.js'; import inspectHTMLElement, { inspectHTMLCollection } from './html.js'; import { normaliseOptions } from './helpers.js'; const symbolsSupported = typeof Symbol === 'function' && typeof Symbol.for === 'function'; const chaiInspect = symbolsSupported ? Symbol.for('chai/inspect') : '@@chai/inspect'; let nodeInspect = false; try { // eslint-disable-next-line global-require // @ts-ignore const nodeUtil = require('util'); nodeInspect = nodeUtil.inspect ? nodeUtil.inspect.custom : false; } catch (noNodeInspect) { nodeInspect = false; } const constructorMap = new WeakMap(); const stringTagMap = {}; const baseTypesMap = { undefined: (value, options) => options.stylize('undefined', 'undefined'), null: (value, options) => options.stylize('null', 'null'), boolean: (value, options) => options.stylize(String(value), 'boolean'), Boolean: (value, options) => options.stylize(String(value), 'boolean'), number: inspectNumber, Number: inspectNumber, bigint: inspectBigInt, BigInt: inspectBigInt, string: inspectString, String: inspectString, function: inspectFunction, Function: inspectFunction, symbol: inspectSymbol, // A Symbol polyfill will return `Symbol` not `symbol` from typedetect Symbol: inspectSymbol, Array: inspectArray, Date: inspectDate, Map: inspectMap, Set: inspectSet, RegExp: inspectRegExp, Promise: inspectPromise, // WeakSet, WeakMap are totally opaque to us WeakSet: (value, options) => options.stylize('WeakSet{…}', 'special'), WeakMap: (value, options) => options.stylize('WeakMap{…}', 'special'), Arguments: inspectArguments, Int8Array: inspectTypedArray, Uint8Array: inspectTypedArray, Uint8ClampedArray: inspectTypedArray, Int16Array: inspectTypedArray, Uint16Array: inspectTypedArray, Int32Array: inspectTypedArray, Uint32Array: inspectTypedArray, Float32Array: inspectTypedArray, Float64Array: inspectTypedArray, Generator: () => '', DataView: () => '', ArrayBuffer: () => '', Error: inspectError, HTMLCollection: inspectHTMLCollection, NodeList: inspectHTMLCollection, }; // eslint-disable-next-line complexity const inspectCustom = (value, options, type) => { if (chaiInspect in value && typeof value[chaiInspect] === 'function') { return value[chaiInspect](options); } if (nodeInspect && nodeInspect in value && typeof value[nodeInspect] === 'function') { return value[nodeInspect](options.depth, options); } if ('inspect' in value && typeof value.inspect === 'function') { return value.inspect(options.depth, options); } if ('constructor' in value && constructorMap.has(value.constructor)) { return constructorMap.get(value.constructor)(value, options); } if (stringTagMap[type]) { return stringTagMap[type](value, options); } return ''; }; const toString = Object.prototype.toString; // eslint-disable-next-line complexity export function inspect(value, opts = {}) { const options = normaliseOptions(opts, inspect); const { customInspect } = options; let type = value === null ? 'null' : typeof value; if (type === 'object') { type = toString.call(value).slice(8, -1); } // If it is a base value that we already support, then use Loupe's inspector if (type in baseTypesMap) { return baseTypesMap[type](value, options); } // If `options.customInspect` is set to true then try to use the custom inspector if (customInspect && value) { const output = inspectCustom(value, options, type); if (output) { if (typeof output === 'string') return output; return inspect(output, options); } } const proto = value ? Object.getPrototypeOf(value) : false; // If it's a plain Object then use Loupe's inspector if (proto === Object.prototype || proto === null) { return inspectObject(value, options); } // Specifically account for HTMLElements // @ts-ignore if (value && typeof HTMLElement === 'function' && value instanceof HTMLElement) { return inspectHTMLElement(value, options); } if ('constructor' in value) { // If it is a class, inspect it like an object but add the constructor name if (value.constructor !== Object) { return inspectClass(value, options); } // If it is an object with an anonymous prototype, display it as an object. return inspectObject(value, options); } // last chance to check if it's an object if (value === Object(value)) { return inspectObject(value, options); } // We have run out of options! Just stringify the value return options.stylize(String(value), type); } export function registerConstructor(constructor, inspector) { if (constructorMap.has(constructor)) { return false; } constructorMap.set(constructor, inspector); return true; } export function registerStringTag(stringTag, inspector) { if (stringTag in stringTagMap) { return false; } stringTagMap[stringTag] = inspector; return true; } export const custom = chaiInspect; export default inspect;