eruda2
Version:
Console for Mobile Browsers
193 lines (170 loc) • 4.9 kB
JavaScript
// Simple version for stringify, used for displaying object abstract.
import {
escape,
toStr,
contain,
startWith,
escapeJsonStr,
each,
getObjType,
endWith,
isEmpty,
} from './util'
import evalCss from './evalCss'
let hasEvalCss = false
// Modified from: https://jsconsole.com/
export default function getAbstract(
obj,
{ topObj, level = 0, getterVal = false, unenumerable = true } = {}
) {
if (!hasEvalCss) {
evalCss(require('./abstract.scss'))
hasEvalCss = true
}
let json = ''
let type = ''
const keyNum = 5
const parts = []
let names = []
let objEllipsis = ''
const circular = false
let i
topObj = topObj || obj
const passOpts = { getterVal, unenumerable, level: level + 1 }
const doStringify = level === 0
const keyWrapper = '<span class="eruda-key">'
const numWrapper = '<span class="eruda-number">'
const nullWrapper = '<span class="eruda-null">'
const strWrapper = '<span class="eruda-string">'
const boolWrapper = '<span class="eruda-boolean">'
const specialWrapper = '<span class="eruda-special">'
const strEscape = (str) =>
escape(str)
.replace(/\\n/g, '↵')
.replace(/\\f|\\r|\\t/g, '')
.replace(/\\/g, '')
const wrapperEnd = '</span>'
const wrapKey = (key) => keyWrapper + strEscape(key) + wrapperEnd
const wrapNum = (num) => numWrapper + num + wrapperEnd
const wrapRegExp = (str) => strWrapper + str + wrapperEnd
const wrapBool = (bool) => boolWrapper + bool + wrapperEnd
const wrapNull = (str) => nullWrapper + str + wrapperEnd
function wrapStr(str) {
str = toStr(str)
if (contain(SPECIAL_VAL, str) || startWith(str, 'Array[')) {
return specialWrapper + strEscape(str) + wrapperEnd
}
return strWrapper + strEscape(`"${str}"`) + wrapperEnd
}
function objIteratee(name) {
if (i > keyNum) {
objEllipsis = ', …'
return
}
const key = wrapKey(escapeJsonStr(name))
if (!getterVal) {
const descriptor = Object.getOwnPropertyDescriptor(obj, name)
if (descriptor.get) {
parts.push(`${key}: ${wrapStr('(...)')}`)
i++
return
}
}
parts.push(`${key}: ${getAbstract(topObj[name], passOpts)}`)
i++
}
try {
type = {}.toString.call(obj)
} catch (e) {
type = '[object Object]'
}
const isStr = type == '[object String]'
const isArr = type == '[object Array]'
const isObj = type == '[object Object]'
const isNum = type == '[object Number]'
const isRegExp = type == '[object RegExp]'
const isSymbol = type == '[object Symbol]'
const isFn = type == '[object Function]'
const isBool = type == '[object Boolean]'
if (circular) {
json = wrapStr('[circular]')
} else if (isStr) {
json = wrapStr(escapeJsonStr(obj))
} else if (isRegExp) {
json = wrapRegExp(escapeJsonStr(obj.toString()))
} else if (isFn) {
json = wrapStr('ƒ')
} else if (isArr) {
if (doStringify) {
json = '['
let len = obj.length
let arrEllipsis = ''
if (len > 100) {
len = 100
arrEllipsis = ', …'
}
for (let i = 0; i < len; i++) {
parts.push(`${getAbstract(obj[i], passOpts)}`)
}
json += parts.join(', ') + arrEllipsis + ']'
} else {
json = `Array(${obj.length})`
}
} else if (isObj) {
if (canBeProto(obj)) {
obj = Object.getPrototypeOf(obj)
}
names = unenumerable ? Object.getOwnPropertyNames(obj) : Object.keys(obj)
if (doStringify) {
i = 1
json = '{ '
each(names, objIteratee)
json += parts.join(', ') + objEllipsis + ' }'
} else {
json = getObjType(obj)
if (json === 'Object') json = '{…}'
}
} else if (isNum) {
json = obj + ''
if (endWith(json, 'Infinity') || json === 'NaN') {
json = `"${json}"`
} else {
json = wrapNum(json)
}
} else if (isBool) {
json = wrapBool(obj ? 'true' : 'false')
} else if (obj === null) {
json = wrapNull('null')
} else if (isSymbol) {
json = wrapStr('Symbol')
} else if (obj === undefined) {
json = wrapStr('undefined')
} else {
try {
if (canBeProto(obj)) {
obj = Object.getPrototypeOf(obj)
}
if (doStringify) {
i = 1
json = '{ '
names = unenumerable
? Object.getOwnPropertyNames(obj)
: Object.keys(obj)
each(names, objIteratee)
json += parts.join(', ') + objEllipsis + ' }'
} else {
json = getObjType(obj)
if (json === 'Object') json = '{…}'
}
} catch (e) {
json = wrapStr(obj)
}
}
return json
}
const SPECIAL_VAL = ['(...)', 'undefined', 'Symbol', 'Object', 'ƒ']
function canBeProto(obj) {
const emptyObj = isEmpty(Object.getOwnPropertyNames(obj))
const proto = Object.getPrototypeOf(obj)
return emptyObj && proto && proto !== Object.prototype
}