171 lines
5.7 KiB
JavaScript
171 lines
5.7 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const argumentsValue = require('./complexValues/arguments')
|
||
|
const arrayBufferValue = require('./complexValues/arrayBuffer')
|
||
|
const boxedValue = require('./complexValues/boxed')
|
||
|
const dataViewValue = require('./complexValues/dataView')
|
||
|
const dateValue = require('./complexValues/date')
|
||
|
const errorValue = require('./complexValues/error')
|
||
|
const functionValue = require('./complexValues/function')
|
||
|
const globalValue = require('./complexValues/global')
|
||
|
const mapValue = require('./complexValues/map')
|
||
|
const objectValue = require('./complexValues/object')
|
||
|
const promiseValue = require('./complexValues/promise')
|
||
|
const regexpValue = require('./complexValues/regexp')
|
||
|
const setValue = require('./complexValues/set')
|
||
|
const typedArrayValue = require('./complexValues/typedArray')
|
||
|
|
||
|
const itemDescriptor = require('./metaDescriptors/item')
|
||
|
const mapEntryDescriptor = require('./metaDescriptors/mapEntry')
|
||
|
const propertyDescriptor = require('./metaDescriptors/property')
|
||
|
|
||
|
const booleanValue = require('./primitiveValues/boolean')
|
||
|
const nullValue = require('./primitiveValues/null')
|
||
|
const numberValue = require('./primitiveValues/number')
|
||
|
const stringValue = require('./primitiveValues/string')
|
||
|
const symbolValue = require('./primitiveValues/symbol')
|
||
|
const undefinedValue = require('./primitiveValues/undefined')
|
||
|
|
||
|
const getCtor = require('./getCtor')
|
||
|
const getStringTag = require('./getStringTag')
|
||
|
const pluginRegistry = require('./pluginRegistry')
|
||
|
const Registry = require('./Registry')
|
||
|
|
||
|
const SpecializedComplexes = new Map([
|
||
|
['Arguments', argumentsValue.describe],
|
||
|
['ArrayBuffer', arrayBufferValue.describe],
|
||
|
['DataView', dataViewValue.describe],
|
||
|
['Date', dateValue.describe],
|
||
|
['Error', errorValue.describe],
|
||
|
['Float32Array', typedArrayValue.describe],
|
||
|
['Float64Array', typedArrayValue.describe],
|
||
|
['Function', functionValue.describe],
|
||
|
['GeneratorFunction', functionValue.describe],
|
||
|
['global', globalValue.describe],
|
||
|
['Int16Array', typedArrayValue.describe],
|
||
|
['Int32Array', typedArrayValue.describe],
|
||
|
['Int8Array', typedArrayValue.describe],
|
||
|
['Map', mapValue.describe],
|
||
|
['Promise', promiseValue.describe],
|
||
|
['RegExp', regexpValue.describe],
|
||
|
['Set', setValue.describe],
|
||
|
['Uint16Array', typedArrayValue.describe],
|
||
|
['Uint32Array', typedArrayValue.describe],
|
||
|
['Uint8Array', typedArrayValue.describe],
|
||
|
['Uint8ClampedArray', typedArrayValue.describe]
|
||
|
])
|
||
|
|
||
|
function describePrimitive (value) {
|
||
|
if (value === null) return nullValue.describe()
|
||
|
if (value === undefined) return undefinedValue.describe()
|
||
|
if (value === true || value === false) return booleanValue.describe(value)
|
||
|
|
||
|
const type = typeof value
|
||
|
if (type === 'number') return numberValue.describe(value)
|
||
|
if (type === 'string') return stringValue.describe(value)
|
||
|
if (type === 'symbol') return symbolValue.describe(value)
|
||
|
|
||
|
return null
|
||
|
}
|
||
|
|
||
|
function unboxComplex (tag, complex) {
|
||
|
// Try to unbox by calling `valueOf()`. `describePrimitive()` will return
|
||
|
// `null` if the resulting value is not a primitive, in which case it's
|
||
|
// ignored.
|
||
|
if (typeof complex.valueOf === 'function') {
|
||
|
const value = complex.valueOf()
|
||
|
if (value !== complex) return describePrimitive(value)
|
||
|
}
|
||
|
|
||
|
return null
|
||
|
}
|
||
|
|
||
|
function registerPlugins (plugins) {
|
||
|
if (!Array.isArray(plugins) || plugins.length === 0) return () => null
|
||
|
|
||
|
const tryFns = pluginRegistry.getTryDescribeValues(plugins)
|
||
|
return (value, stringTag, ctor) => {
|
||
|
for (const tryDescribeValue of tryFns) {
|
||
|
const describeValue = tryDescribeValue(value, stringTag, ctor)
|
||
|
if (describeValue) return describeValue
|
||
|
}
|
||
|
|
||
|
return null
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function describeComplex (value, registry, tryPlugins, describeAny, describeItem, describeMapEntry, describeProperty) {
|
||
|
if (registry.has(value)) return registry.get(value)
|
||
|
|
||
|
const stringTag = getStringTag(value)
|
||
|
const ctor = getCtor(stringTag, value)
|
||
|
const pointer = registry.alloc(value)
|
||
|
|
||
|
let unboxed
|
||
|
let describeValue = tryPlugins(value, stringTag, ctor)
|
||
|
if (describeValue === null) {
|
||
|
if (SpecializedComplexes.has(stringTag)) {
|
||
|
describeValue = SpecializedComplexes.get(stringTag)
|
||
|
} else {
|
||
|
unboxed = unboxComplex(stringTag, value)
|
||
|
if (unboxed !== null) {
|
||
|
describeValue = boxedValue.describe
|
||
|
} else {
|
||
|
describeValue = objectValue.describe
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const descriptor = describeValue({
|
||
|
ctor,
|
||
|
describeAny,
|
||
|
describeItem,
|
||
|
describeMapEntry,
|
||
|
describeProperty,
|
||
|
pointer: pointer.index,
|
||
|
stringTag,
|
||
|
unboxed,
|
||
|
value
|
||
|
})
|
||
|
pointer.descriptor = descriptor
|
||
|
return descriptor
|
||
|
}
|
||
|
|
||
|
function describe (value, options) {
|
||
|
const primitive = describePrimitive(value)
|
||
|
if (primitive !== null) return primitive
|
||
|
|
||
|
const registry = new Registry()
|
||
|
const tryPlugins = registerPlugins(options && options.plugins)
|
||
|
const curriedComplex = c => {
|
||
|
return describeComplex(c, registry, tryPlugins, describeAny, describeItem, describeMapEntry, describeProperty)
|
||
|
}
|
||
|
|
||
|
const describeAny = any => {
|
||
|
const descriptor = describePrimitive(any)
|
||
|
return descriptor !== null
|
||
|
? descriptor
|
||
|
: curriedComplex(any)
|
||
|
}
|
||
|
|
||
|
const describeItem = (index, valueDescriptor) => {
|
||
|
return valueDescriptor.isPrimitive === true
|
||
|
? itemDescriptor.describePrimitive(index, valueDescriptor)
|
||
|
: itemDescriptor.describeComplex(index, valueDescriptor)
|
||
|
}
|
||
|
|
||
|
const describeMapEntry = (keyDescriptor, valueDescriptor) => {
|
||
|
return mapEntryDescriptor.describe(keyDescriptor, valueDescriptor)
|
||
|
}
|
||
|
|
||
|
const describeProperty = (key, valueDescriptor) => {
|
||
|
const keyDescriptor = describePrimitive(key)
|
||
|
return valueDescriptor.isPrimitive === true
|
||
|
? propertyDescriptor.describePrimitive(keyDescriptor, valueDescriptor)
|
||
|
: propertyDescriptor.describeComplex(keyDescriptor, valueDescriptor)
|
||
|
}
|
||
|
|
||
|
return curriedComplex(value)
|
||
|
}
|
||
|
module.exports = describe
|