stack-utils
Advanced tools
Comparing version 0.4.0 to 1.0.0
421
index.js
module.exports = StackUtils; | ||
function StackUtils(opts) { | ||
if (!(this instanceof StackUtils)) { | ||
throw new Error('StackUtils constructor must be called with new'); | ||
} | ||
opts = opts || {}; | ||
this._cwd = (opts.cwd || process.cwd()).replace(/\\/g, '/'); | ||
this._internals = opts.internals || []; | ||
if (!(this instanceof StackUtils)) { | ||
throw new Error('StackUtils constructor must be called with new'); | ||
} | ||
opts = opts || {}; | ||
this._cwd = (opts.cwd || process.cwd()).replace(/\\/g, '/'); | ||
this._internals = opts.internals || []; | ||
this._wrapCallSite = opts.wrapCallSite || false; | ||
} | ||
@@ -15,265 +16,269 @@ | ||
function nodeInternals() { | ||
return [ | ||
/\(native\)$/, | ||
/\(domain.js:\d+:\d+\)$/, | ||
/\(events.js:\d+:\d+\)$/, | ||
/\(node.js:\d+:\d+\)$/, | ||
/\(timers.js:\d+:\d+\)$/, | ||
/\(module.js:\d+:\d+\)$/, | ||
/\(internal\/[\w_-]+\.js:\d+:\d+\)$/, | ||
/\s*at node\.js:\d+:\d+?$/, | ||
/\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/ | ||
]; | ||
if (!module.exports.natives) { | ||
module.exports.natives = Object.keys(process.binding('natives')); | ||
module.exports.natives.push('bootstrap_node', 'node'); | ||
} | ||
return module.exports.natives.map(function (n) { | ||
return new RegExp('\\(' + n + '\\.js:\\d+:\\d+\\)$'); | ||
}).concat([ | ||
/\s*at (bootstrap_)?node\.js:\d+:\d+?$/, | ||
/\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/ | ||
]); | ||
} | ||
StackUtils.prototype.clean = function (stack) { | ||
if (!Array.isArray(stack)) { | ||
stack = stack.split('\n'); | ||
} | ||
if (!Array.isArray(stack)) { | ||
stack = stack.split('\n'); | ||
} | ||
if (!(/^\s*at /.test(stack[0])) && | ||
(/^\s*at /.test(stack[1]))) { | ||
stack = stack.slice(1); | ||
} | ||
if (!(/^\s*at /.test(stack[0])) && | ||
(/^\s*at /.test(stack[1]))) { | ||
stack = stack.slice(1); | ||
} | ||
var outdent = false; | ||
var lastNonAtLine = null; | ||
var result = []; | ||
var outdent = false; | ||
var lastNonAtLine = null; | ||
var result = []; | ||
stack.forEach(function (st) { | ||
st = st.replace(/\\/g, '/'); | ||
var isInternal = this._internals.some(function (internal) { | ||
return internal.test(st); | ||
}); | ||
stack.forEach(function (st) { | ||
st = st.replace(/\\/g, '/'); | ||
var isInternal = this._internals.some(function (internal) { | ||
return internal.test(st); | ||
}); | ||
if (isInternal) { | ||
return null; | ||
} | ||
if (isInternal) { | ||
return null; | ||
} | ||
var isAtLine = /^\s*at /.test(st); | ||
var isAtLine = /^\s*at /.test(st); | ||
if (outdent) { | ||
st = st.replace(/\s+$/, '').replace(/^(\s+)at /, '$1'); | ||
} else { | ||
st = st.trim(); | ||
if (isAtLine) { | ||
st = st.substring(3); | ||
} | ||
} | ||
if (outdent) { | ||
st = st.replace(/\s+$/, '').replace(/^(\s+)at /, '$1'); | ||
} else { | ||
st = st.trim(); | ||
if (isAtLine) { | ||
st = st.substring(3); | ||
} | ||
} | ||
st = st.replace(this._cwd + '/', ''); | ||
st = st.replace(this._cwd + '/', ''); | ||
if (st) { | ||
if (isAtLine) { | ||
if (lastNonAtLine) { | ||
result.push(lastNonAtLine); | ||
lastNonAtLine = null; | ||
} | ||
result.push(st); | ||
} else { | ||
outdent = true; | ||
lastNonAtLine = st; | ||
} | ||
} | ||
}, this); | ||
if (st) { | ||
if (isAtLine) { | ||
if (lastNonAtLine) { | ||
result.push(lastNonAtLine); | ||
lastNonAtLine = null; | ||
} | ||
result.push(st); | ||
} else { | ||
outdent = true; | ||
lastNonAtLine = st; | ||
} | ||
} | ||
}, this); | ||
stack = result.join('\n').trim(); | ||
stack = result.join('\n').trim(); | ||
if (stack) { | ||
return stack + '\n'; | ||
} | ||
return ''; | ||
if (stack) { | ||
return stack + '\n'; | ||
} | ||
return ''; | ||
}; | ||
StackUtils.prototype.captureString = function (limit, fn) { | ||
if (typeof limit === 'function') { | ||
fn = limit; | ||
limit = Infinity; | ||
} | ||
if (!fn) { | ||
fn = this.captureString; | ||
} | ||
if (typeof limit === 'function') { | ||
fn = limit; | ||
limit = Infinity; | ||
} | ||
if (!fn) { | ||
fn = this.captureString; | ||
} | ||
var limitBefore = Error.stackTraceLimit; | ||
if (limit) { | ||
Error.stackTraceLimit = limit; | ||
} | ||
var limitBefore = Error.stackTraceLimit; | ||
if (limit) { | ||
Error.stackTraceLimit = limit; | ||
} | ||
var obj = {}; | ||
var obj = {}; | ||
Error.captureStackTrace(obj, fn); | ||
var stack = obj.stack; | ||
Error.stackTraceLimit = limitBefore; | ||
Error.captureStackTrace(obj, fn); | ||
var stack = obj.stack; | ||
Error.stackTraceLimit = limitBefore; | ||
return this.clean(stack); | ||
return this.clean(stack); | ||
}; | ||
StackUtils.prototype.capture = function (limit, fn) { | ||
if (typeof limit === 'function') { | ||
fn = limit; | ||
limit = Infinity; | ||
} | ||
if (!fn) { | ||
fn = this.capture; | ||
} | ||
var prepBefore = Error.prepareStackTrace; | ||
var limitBefore = Error.stackTraceLimit; | ||
if (typeof limit === 'function') { | ||
fn = limit; | ||
limit = Infinity; | ||
} | ||
if (!fn) { | ||
fn = this.capture; | ||
} | ||
var prepBefore = Error.prepareStackTrace; | ||
var limitBefore = Error.stackTraceLimit; | ||
var wrapCallSite = this._wrapCallSite; | ||
Error.prepareStackTrace = function (obj, site) { | ||
return site; | ||
}; | ||
Error.prepareStackTrace = function (obj, site) { | ||
if (wrapCallSite) { | ||
return site.map(wrapCallSite); | ||
} | ||
return site; | ||
}; | ||
if (limit) { | ||
Error.stackTraceLimit = limit; | ||
} | ||
if (limit) { | ||
Error.stackTraceLimit = limit; | ||
} | ||
var obj = {}; | ||
Error.captureStackTrace(obj, fn); | ||
var stack = obj.stack; | ||
Error.prepareStackTrace = prepBefore; | ||
Error.stackTraceLimit = limitBefore; | ||
var obj = {}; | ||
Error.captureStackTrace(obj, fn); | ||
var stack = obj.stack; | ||
Error.prepareStackTrace = prepBefore; | ||
Error.stackTraceLimit = limitBefore; | ||
return stack; | ||
return stack; | ||
}; | ||
StackUtils.prototype.at = function at(fn) { | ||
if (!fn) { | ||
fn = at; | ||
} | ||
if (!fn) { | ||
fn = at; | ||
} | ||
var site = this.capture(1, fn)[0]; | ||
var site = this.capture(1, fn)[0]; | ||
if (!site) { | ||
return {}; | ||
} | ||
if (!site) { | ||
return {}; | ||
} | ||
var res = { | ||
line: site.getLineNumber(), | ||
column: site.getColumnNumber() | ||
}; | ||
var res = { | ||
line: site.getLineNumber(), | ||
column: site.getColumnNumber() | ||
}; | ||
this._setFile(res, site.getFileName()); | ||
this._setFile(res, site.getFileName()); | ||
if (site.isConstructor()) { | ||
res.constructor = true; | ||
} | ||
if (site.isConstructor()) { | ||
res.constructor = true; | ||
} | ||
if (site.isEval()) { | ||
res.evalOrigin = site.getEvalOrigin(); | ||
} | ||
if (site.isEval()) { | ||
res.evalOrigin = site.getEvalOrigin(); | ||
} | ||
if (site.isNative()) { | ||
res.native = true; | ||
} | ||
if (site.isNative()) { | ||
res.native = true; | ||
} | ||
var typename = null; | ||
try { | ||
typename = site.getTypeName(); | ||
} catch (er) {} | ||
var typename = null; | ||
try { | ||
typename = site.getTypeName(); | ||
} catch (er) {} | ||
if (typename && | ||
typename !== 'Object' && | ||
typename !== '[object Object]') { | ||
res.type = typename; | ||
} | ||
if (typename && | ||
typename !== 'Object' && | ||
typename !== '[object Object]') { | ||
res.type = typename; | ||
} | ||
var fname = site.getFunctionName(); | ||
if (fname) { | ||
res.function = fname; | ||
} | ||
var fname = site.getFunctionName(); | ||
if (fname) { | ||
res.function = fname; | ||
} | ||
var meth = site.getMethodName(); | ||
if (meth && fname !== meth) { | ||
res.method = meth; | ||
} | ||
var meth = site.getMethodName(); | ||
if (meth && fname !== meth) { | ||
res.method = meth; | ||
} | ||
return res; | ||
return res; | ||
}; | ||
StackUtils.prototype._setFile = function (result, filename) { | ||
if (filename) { | ||
filename = filename.replace(/\\/g, '/'); | ||
if ((filename.indexOf(this._cwd + '/') === 0)) { | ||
filename = filename.substr(this._cwd.length + 1); | ||
} | ||
result.file = filename; | ||
} | ||
if (filename) { | ||
filename = filename.replace(/\\/g, '/'); | ||
if ((filename.indexOf(this._cwd + '/') === 0)) { | ||
filename = filename.substr(this._cwd.length + 1); | ||
} | ||
result.file = filename; | ||
} | ||
}; | ||
var re = new RegExp( | ||
'^' + | ||
// Sometimes we strip out the ' at' because it's noisy | ||
'(?:\\s*at )?' + | ||
// $1 = ctor if 'new' | ||
'(?:(new) )?' + | ||
// Object.method [as foo] (, maybe | ||
// $2 = function name | ||
// $3 = method name | ||
'(?:([^\\(\\[]*)(?: \\[as ([^\\]]+)\\])? \\()?' + | ||
// (eval at <anonymous> (file.js:1:1), | ||
// $4 = eval origin | ||
// $5:$6:$7 are eval file/line/col, but not normally reported | ||
'(?:eval at ([^ ]+) \\(([^\\)]+):(\\d+):(\\d+)\\), )?' + | ||
// file:line:col | ||
// $8:$9:$10 | ||
// $11 = 'native' if native | ||
'(?:([^\\)]+):(\\d+):(\\d+)|(native))' + | ||
// maybe close the paren, then end | ||
'\\)?$' | ||
'^' + | ||
// Sometimes we strip out the ' at' because it's noisy | ||
'(?:\\s*at )?' + | ||
// $1 = ctor if 'new' | ||
'(?:(new) )?' + | ||
// Object.method [as foo] (, maybe | ||
// $2 = function name | ||
// $3 = method name | ||
'(?:([^\\(\\[]*)(?: \\[as ([^\\]]+)\\])? \\()?' + | ||
// (eval at <anonymous> (file.js:1:1), | ||
// $4 = eval origin | ||
// $5:$6:$7 are eval file/line/col, but not normally reported | ||
'(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?' + | ||
// file:line:col | ||
// $8:$9:$10 | ||
// $11 = 'native' if native | ||
'(?:(.+?):(\\d+):(\\d+)|(native))' + | ||
// maybe close the paren, then end | ||
'\\)?$' | ||
); | ||
StackUtils.prototype.parseLine = function parseLine(line) { | ||
var match = line && line.match(re); | ||
if (!match) { | ||
return null; | ||
} | ||
var match = line && line.match(re); | ||
if (!match) { | ||
return null; | ||
} | ||
var ctor = match[1] === 'new'; | ||
var fname = match[2]; | ||
var meth = match[3]; | ||
var evalOrigin = match[4]; | ||
var evalFile = match[5]; | ||
var evalLine = Number(match[6]); | ||
var evalCol = Number(match[7]); | ||
var file = match[8]; | ||
var lnum = match[9]; | ||
var col = match[10]; | ||
var native = match[11] === 'native'; | ||
var ctor = match[1] === 'new'; | ||
var fname = match[2]; | ||
var meth = match[3]; | ||
var evalOrigin = match[4]; | ||
var evalFile = match[5]; | ||
var evalLine = Number(match[6]); | ||
var evalCol = Number(match[7]); | ||
var file = match[8]; | ||
var lnum = match[9]; | ||
var col = match[10]; | ||
var native = match[11] === 'native'; | ||
var res = {}; | ||
var res = {}; | ||
if (lnum) { | ||
res.line = Number(lnum); | ||
} | ||
if (lnum) { | ||
res.line = Number(lnum); | ||
} | ||
if (col) { | ||
res.column = Number(col); | ||
} | ||
if (col) { | ||
res.column = Number(col); | ||
} | ||
this._setFile(res, file); | ||
this._setFile(res, file); | ||
if (ctor) { | ||
res.constructor = true; | ||
} | ||
if (ctor) { | ||
res.constructor = true; | ||
} | ||
if (evalOrigin) { | ||
res.evalOrigin = evalOrigin; | ||
res.evalLine = evalLine; | ||
res.evalColumn = evalCol; | ||
res.evalFile = evalFile && evalFile.replace(/\\/g, '/'); | ||
} | ||
if (evalOrigin) { | ||
res.evalOrigin = evalOrigin; | ||
res.evalLine = evalLine; | ||
res.evalColumn = evalCol; | ||
res.evalFile = evalFile && evalFile.replace(/\\/g, '/'); | ||
} | ||
if (native) { | ||
res.native = true; | ||
} | ||
if (native) { | ||
res.native = true; | ||
} | ||
if (fname) { | ||
res.function = fname; | ||
} | ||
if (fname) { | ||
res.function = fname; | ||
} | ||
if (meth && fname !== meth) { | ||
res.method = meth; | ||
} | ||
if (meth && fname !== meth) { | ||
res.method = meth; | ||
} | ||
return res; | ||
return res; | ||
}; | ||
@@ -284,3 +289,3 @@ | ||
Object.keys(StackUtils.prototype).forEach(function (key) { | ||
StackUtils[key] = bound[key].bind(bound); | ||
StackUtils[key] = bound[key].bind(bound); | ||
}); |
{ | ||
"name": "stack-utils", | ||
"version": "0.4.0", | ||
"version": "1.0.0", | ||
"description": "Captures and cleans stack traces", | ||
@@ -16,4 +16,3 @@ "license": "MIT", | ||
"scripts": { | ||
"test": "xo && nyc --reporter lcov --reporter text --cache ava --verbose", | ||
"test-win": "ava --verbose" | ||
"test": "tap test/*.js --cov" | ||
}, | ||
@@ -28,12 +27,10 @@ "files": [ | ||
"devDependencies": { | ||
"ava": "^0.8.0", | ||
"bluebird": "^3.1.1", | ||
"coveralls": "^2.11.6", | ||
"flatten": "0.0.1", | ||
"nested-error-stacks": "^1.0.2", | ||
"nyc": "^5.2.0", | ||
"nested-error-stacks": "^2.0.0", | ||
"pify": "^2.3.0", | ||
"q": "^1.4.1", | ||
"xo": "^0.12.1" | ||
"tap": "^10.0.0" | ||
} | ||
} |
@@ -24,3 +24,3 @@ # stack-utils | ||
console.log(stack.clean(new Error().stack)); | ||
// outputs a beutified stack trace | ||
// outputs a beautified stack trace | ||
``` | ||
@@ -51,3 +51,9 @@ | ||
##### wrapCallSite | ||
Type: `function(CallSite)` | ||
A mapping function for manipulating CallSites before processing. The first argument is a CallSite instance, and the function should return a modified CallSite. This is useful for providing source map support. | ||
### StackUtils.nodeInternals() | ||
@@ -54,0 +60,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
12078
7
235
1
133
1