123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- /* !
- * loupe
- * Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
- * MIT Licensed
- */
- import inspectArray from './lib/array'
- import inspectTypedArray from './lib/typedarray'
- import inspectDate from './lib/date'
- import inspectFunction from './lib/function'
- import inspectMap from './lib/map'
- import inspectNumber from './lib/number'
- import inspectBigInt from './lib/bigint'
- import inspectRegExp from './lib/regexp'
- import inspectSet from './lib/set'
- import inspectString from './lib/string'
- import inspectSymbol from './lib/symbol'
- import inspectPromise from './lib/promise'
- import inspectClass from './lib/class'
- import inspectObject from './lib/object'
- import inspectArguments from './lib/arguments'
- import inspectError from './lib/error'
- import inspectHTMLElement, { inspectHTMLCollection } from './lib/html'
- import { normaliseOptions } from './lib/helpers'
- 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
- const nodeUtil = require('util')
- nodeInspect = nodeUtil.inspect ? nodeUtil.inspect.custom : false
- } catch (noNodeInspect) {
- nodeInspect = false
- }
- function FakeMap() {
- // eslint-disable-next-line prefer-template
- this.key = 'chai/loupe__' + Math.random() + Date.now()
- }
- FakeMap.prototype = {
- // eslint-disable-next-line object-shorthand
- get: function get(key) {
- return key[this.key]
- },
- // eslint-disable-next-line object-shorthand
- has: function has(key) {
- return this.key in key
- },
- // eslint-disable-next-line object-shorthand
- set: function set(key, value) {
- if (Object.isExtensible(key)) {
- Object.defineProperty(key, this.key, {
- // eslint-disable-next-line object-shorthand
- value: value,
- configurable: true,
- })
- }
- },
- }
- const constructorMap = new (typeof WeakMap === 'function' ? WeakMap : FakeMap)()
- const stringTagMap = {}
- const baseTypesMap = {
- undefined: (value, options) => options.stylize('undefined', 'undefined'),
- null: (value, options) => options.stylize(null, 'null'),
- boolean: (value, options) => options.stylize(value, 'boolean'),
- Boolean: (value, options) => options.stylize(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, options) {
- options = normaliseOptions(options)
- options.inspect = 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 (baseTypesMap[type]) {
- 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
- // eslint-disable-next-line no-undef
- 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
|