stackframes
Version:
252 lines (235 loc) • 11.5 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
const stackframes = require('..')
document.body.innerHTML = `<xmp>${demo}\ndemo()</xmp><hr><h2>open devtools console to check results:</h2>`//'<h1> open devtools console: </h1>'
demo()
function demo () {
var error
try {
function foobarbaz () { throw new Error('foobar') }
function bazbarfoo () { foobarbaz() }
bazbarfoo()
} catch (e) {
error = e
}
example()
function example () { foo() }
function foo () { bar() }
function bar () { baz() }
function baz () {
const defaultFlags = stackframes.defaultFlags
console.log(defaultFlags)
const flags = defaultFlags.filter((_, i) => i%2) // take every second flag
console.log('0', stackframes(error, flags))
console.log('1', stackframes())
console.log('2', stackframes({ exclude: foo }))
console.log('3', stackframes({ exclude: example }))
console.log('4', stackframes({ depths: 2, exclude: baz }))
console.log('5', stackframes({ depths: 2 }))
console.log('6', stackframes(null, flags))
}
}
},{"..":3}],2:[function(require,module,exports){
module.exports = jsonloop
function jsonloop (specialChar = '.') {
const safeSpecialChar = '\\x' + ('0' + specialChar.charCodeAt(0).toString(16)).slice(-2)
const escapedSafeSpecialChar = '\\' + safeSpecialChar
const specialCharRG = new RegExp(safeSpecialChar, 'g')
const safeSpecialCharRG = new RegExp(escapedSafeSpecialChar, 'g')
const safeStartWithSpecialCharRG = new RegExp('(?:^|([^\\\\]))' + escapedSafeSpecialChar)
const indexOf = [].indexOf || function (v) {
for(var i=this.length;i--&&this[i]!==v;);
return i
}
return { stringify, parse }
function parse (text, reviver) {
return JSON.parse(text, generateReviver(reviver))
}
function stringify (value, replacer, space) {
const replace = generateReplacer(value, replacer)
return JSON.stringify(value, replace, space)
}
function generateReplacer (value, replacer, resolve) {
replacer = typeof replacer === 'object' ? (key, value) => key !== '' && indexOf.call(replacer, key) < 0 ? void 0 : value : replacer
const path = []
const all = [value]
const seen = [value]
const mapp = [specialChar]
var doNotIgnore = false
var last = value
var lvl = 1
var i
return function (key, value) {
try {
// the replacer has rights to decide
// if a new object should be returned
// or if there's some key to drop
// let's call it here rather than "too late"
if (replacer) value = replacer.call(this, key, value)
if (doNotIgnore) { // first pass should be ignored, since it's just the initial object
if (last !== this) {
i = lvl - indexOf.call(all, this) - 1
lvl -= i
all.splice(lvl, all.length)
path.splice(lvl - 1, path.length)
last = this
}
if (typeof value === 'object' && value) {
// if object isn't referring to parent object, add to the
// object path stack. Otherwise it is already there.
if (indexOf.call(all, value) < 0) all.push(last = value)
lvl = all.length
i = indexOf.call(seen, value)
if (i < 0) {
i = seen.push(value) - 1
// key cannot contain specialChar but could be not a string
path.push(('' + key).replace(specialCharRG, safeSpecialChar))
mapp[i] = specialChar + path.join(specialChar)
} else {
value = `#${mapp[i]}` // https://tools.ietf.org/html/rfc6901
}
} else {
if (typeof value === 'string' && resolve) {
// ensure no special char involved on deserialization
// in this case only first char is important
// no need to replace all value (better performance)
value = value.replace(safeSpecialChar, escapedSafeSpecialChar).replace(specialChar, safeSpecialChar)
}
}
} else {
doNotIgnore = true
}
return value
} catch (e) {
console.log('ERROR', e)
return value
}
}
}
function retrieveFromPath (current, keys) {
for (var i = 0, length = keys.length; i < length; i++) {
// keys should be normalized back here
current = current[keys[i].replace(safeSpecialCharRG, specialChar)]
}
return current
}
function generateReviver (reviver) {
return function (key, value) {
var isString = typeof value === 'string'
if (isString && value.charAt(0) === '#' && value.charAt(1) === specialChar) return new String(value.slice(2))
if (key === '') value = regenerate(value, value, {})
// again, only one needed, do not use the RegExp for this replacement
// only keys need the RegExp
if (isString) value = value.replace(safeStartWithSpecialCharRG, '$1' + specialChar).replace(escapedSafeSpecialChar, safeSpecialChar)
return reviver ? reviver.call(this, key, value) : value
}
}
function regenerateArray(root, current, retrieve) {
for (var i = 0, length = current.length; i < length; i++) current[i] = regenerate(root, current[i], retrieve)
return current
}
function regenerateObject(root, current, retrieve) {
for (var key in current) if (current.hasOwnProperty(key)) current[key] = regenerate(root, current[key], retrieve)
return current
}
function regenerate(root, current, retrieve) {
return current instanceof Array
? regenerateArray(root, current, retrieve) // fast Array reconstruction
: current instanceof String
? current.length // root is an empty string
? retrieve.hasOwnProperty(current)
? retrieve[current]
: retrieve[current] = retrieveFromPath(root, current.split(specialChar))
: root
: current instanceof Object // dedicated Object parser
? regenerateObject(root, current, retrieve)
: current // value as it is
}
}
},{}],3:[function(require,module,exports){
const jsonloop = require('jsonloop')
const cJSON = jsonloop()
const methods = {
"getThis": callsite => { // getThis: returns the value of this
const self = callsite.getThis()
const ctor = self.constructor
const ctorName = ctor ? `:${ctor.name}` : ''
const isGlobal = self === globalThis
const type = `${isGlobal ? 'global' : 'local'}${ctorName}`
var json
try {
json = isGlobal ? `${self}` : cJSON.stringify(self)
} catch (error) {
json = `${self}:${error}`
}
return { type, json }
},
"getTypeName": callsite => callsite.getTypeName(), // getTypeName: returns the type of this as a string. This is the name of the function stored in the constructor field of this, if available, otherwise the object’s [[Class]] internal property.
"getFunctionSource": callsite => { // getFunction: returns the current function
const getF = callsite.getFunction()
if (getF) return `${getF}`
},
"getFunction": callsite => { // getFunction: returns the current function
const getF = callsite.getFunction()
if (getF) {
const name = getF.name || '(anonymous)'
const ctor = getF.constructor
if (ctor) return { async: false, name }
const ctorName = ctor.name === "AsyncFunction"
return { type: ctorName, name }
}
},
"getFunctionName": callsite => callsite.getFunctionName(), // getFunctionName: returns the name of the current function, typically its name property. If a name property is not available an attempt is made to infer a name from the function’s context.
"getMethodName": callsite => callsite.getMethodName(), // getMethodName: returns the name of the property of this or one of its prototypes that holds the current function
"getFileName": callsite => callsite.getFileName(), // getFileName: if this function was defined in a script returns the name of the script
"getLineNumber": callsite => callsite.getLineNumber(), // getLineNumber: if this function was defined in a script returns the current line number
"getColumnNumber": callsite => callsite.getColumnNumber(), // getColumnNumber: if this function was defined in a script returns the current column number
"getEvalOrigin": callsite => callsite.getEvalOrigin(), // getEvalOrigin: if this function was created using a call to eval returns a string representing the location where eval was called
"isToplevel": callsite => callsite.isToplevel(), // isToplevel: is this a top-level invocation, that is, is this the global object?
"isEval": callsite => callsite.isEval(), // isEval: does this call take place in code defined by a call to eval?
"isNative": callsite => callsite.isNative(), // isNative: is this call in native V8 code?
"isConstructor": callsite => callsite.isConstructor(), // isConstructor: is this a constructor call?
"isAsync": callsite => callsite.isAsync(), // isAsync: is this an async call (i.e. await or Promise.all())?
"isPromiseAll": callsite => callsite.isPromiseAll(), // isPromiseAll: is this an async call to Promise.all()?
"getPromiseIndex": callsite => callsite.getPromiseIndex(), // getPromiseIndex: returns the index of the promise element that was followed in Promise.all() for async stack traces, or null if the CallSite is not a Promise.all() call.
"NameOrSourceURL": callsite => callsite.getScriptNameOrSourceURL(),
"getPosition": callsite => callsite.getPosition()
}
const defaultFlags = Object.keys(methods)
stackframes.defaultFlags = defaultFlags
module.exports = stackframes
function stackframes (err = {}, flags = defaultFlags) {
var depths, exclude
if (!(err instanceof Error)) {
if (!err) err = {}
depths = err.depths
exclude = err.exclude
err = void 0
}
if (!(depths > -1)) depths = Infinity
if (typeof err === "number") (depths = err, err = void 0)
const exclude_this_and_below = exclude || stackframes
const oldLimit = Error.stackTraceLimit
const v8Handler = Error.prepareStackTrace
Error.stackTraceLimit = depths
Error.prepareStackTrace = prepareStackTrace
if (!err) (err = {}, Error.captureStackTrace(err, exclude_this_and_below))
const extractor = extract.bind(flags)
const frames = err.stack.map(extractor)
Error.prepareStackTrace = v8Handler
Error.stackTraceLimit = oldLimit
return frames
}
function prepareStackTrace (_, stack) {
return stack
}
function extract (callsite) {
const flags = this
const frame = {}
for (var i = 0, len = flags.length; i < len; i++) {
const flag = flags[i]
const fn = methods[flag]
if (fn) frame[flag] = fn(callsite)
}
return frame
}
},{"jsonloop":2}]},{},[1]);