/stacktracey

Parses call stacks, reads sources and meta-information

Primary LanguageJavaScriptThe UnlicenseUnlicense

StackTracey

Platform-agnostic callstack access helper.

Why

  • Simple
  • Allows hiding library calls / ad-hoc exclusion (via // @hide marker)
  • Provides source text for call locations
  • Fetches sources synchronously, via get-source
  • Full sourcemap support
  • Pretty printing

What for

  • Better exception reporting for Node and browsers
  • Advanced logging (displaying call locations)
  • Assertion printing

How to

npm install stacktracey
StackTracey = require ('stacktracey')

Captures current call stack:

stack = new StackTracey ()            // captures current call stack

Parses stacks from Error object:

stack = new StackTracey (error)       // parses error.stack
stack = new StackTracey (error.stack) // raw string

It is an array instance:

stack instanceof Array // returns true
stack.length           // num entries
stack[0]               // top

Each item exposes:

{
    beforeParse: <original text>,
    callee:      <function name>,
    calleeShort: <shortened function name>,
    file:        <full path to file>,       // e.g. /Users/john/my_project/src/foo.js
    fileShort:   <shortened path to file>,  // e.g. src/foo.js
    fileName:    <file name>',              // e.g. foo.js
    line:        <line number>,             // starts from 1
    column:      <column number>,           // starts from 1

    index:          /* true if occured in HTML file at index page   */,
    native:         /* true if occured in native browser code       */,
    thirdParty:     /* true if occured in library code              */,
    hide:           /* true if marked as hidden by "// @hide" tag   */
}

Accessing sources:

stack = stack.withSources // will return a copy of stack with all items supplied with sources
top   = stack[0]
top = stack.withSource (0) // supplies source for an individiual item
top = StackTracey.withSource (stack[0]) // supplies source for an individiual item

This will return item supplied with source code info (already mapped through sourcemaps):

{
    ... // all previous fields

    line:       <original line number>,
    column:     <original column number>,
    sourceFile: <original source file object>,
    sourceLine: <original source line text>
}

To learn about sourceFile object, read get-source docs.

Cleaning output

stack = stack.withSources.clean
  1. Excludes locations marked with isThirdParty (library calls)
  2. Excludes locations marked with // @hide comment (user defined exclusion)
  3. Merges repeated lines (via .mergeRepeatedLines)

You can augment isThirdParty predicate with new rules:

StackTracey.isThirdParty.include (path => path.includes ('my-lib')) // paths including 'my-lib' will be marked as thirdParty
StackTracey.isThirdParty.except (path => path.includes ('jquery')) // jquery paths won't be marked as thirdParty

P.S. It is better to call .clean on stacks supplied with sources (i.e. after calling .withSources), to make // @hide magic work, and to make isThirdParty work by recognizing proper file names, if your source is compiled from other sources and has a sourcemap attached.

Pretty printing

const prettyPrintedString = new StackTracey ().pretty

It produces a nice compact table layout, supplied with source lines (if available). You can even replace the default NodeJS exception printer with this!

at shouldBeVisibleInStackTrace     test.js:25                              const shouldBeVisibleInStackTrace = () => new StackTracey ()
at it                              test.js:100                             const stack = shouldBeVisibleInStackTrace ()                
at callFn                          node_modules/mocha/lib/runnable.js:326  var result = fn.call(ctx);                                  
at run                             node_modules/mocha/lib/runnable.js:319  callFn(this.fn);                                            
at runTest                         node_modules/mocha/lib/runner.js:422    test.run(fn);                                               
at                                 node_modules/mocha/lib/runner.js:528    self.runTest(function(err) {                                
at next                            node_modules/mocha/lib/runner.js:342    return fn();                                                
at                                 node_modules/mocha/lib/runner.js:352    next(suites.pop());                                         
at next                            node_modules/mocha/lib/runner.js:284    return fn();                                                
at <anonymous>                     node_modules/mocha/lib/runner.js:320    next(0);                  

Array methods

All StackTracey instances expose map, filter, concat, reverse and slice methods. These methods will return mapped, filtered, joined, reversed and sliced stacks, respectively:

s = new StackTracey ().slice (1).filter (x => !x.isThirdParty) // current stack shifted by 1 and cleaned from library calls

s instanceof StackTracey // true
s instanceof Array       // true

Other methods of the Array are supported too, but they will return Array instances, not StackTracey instances. You can convert from array via this:

stack = new StackTracey (array)

..and to array via this (but generally this is not needed — you can pass around StackTracey instances as if they were real Arrays):

Array.from (stack)

Extra stuff

You can compare two locations via this predicate (tests file, line and column for equality):

StackTracey.locationsEqual (a, b)