diff options
Diffstat (limited to 'node_modules/stack-utils')
| -rw-r--r-- | node_modules/stack-utils/index.js | 338 | ||||
| -rw-r--r-- | node_modules/stack-utils/license | 21 | ||||
| -rw-r--r-- | node_modules/stack-utils/package.json | 39 | ||||
| -rw-r--r-- | node_modules/stack-utils/readme.md | 143 | 
4 files changed, 541 insertions, 0 deletions
diff --git a/node_modules/stack-utils/index.js b/node_modules/stack-utils/index.js new file mode 100644 index 0000000..ed14bd3 --- /dev/null +++ b/node_modules/stack-utils/index.js @@ -0,0 +1,338 @@ +'use strict'; + +const escapeStringRegexp = require('escape-string-regexp'); + +const cwd = typeof process === 'object' && process && typeof process.cwd === 'function' +  ? process.cwd() +  : '.' + +const natives = [].concat( +  require('module').builtinModules, +  'bootstrap_node', +  'node', +).map(n => new RegExp(`(?:\\((?:node:)?${n}(?:\\.js)?:\\d+:\\d+\\)$|^\\s*at (?:node:)?${n}(?:\\.js)?:\\d+:\\d+$)`)); + +natives.push( +  /\((?:node:)?internal\/[^:]+:\d+:\d+\)$/, +  /\s*at (?:node:)?internal\/[^:]+:\d+:\d+$/, +  /\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/ +); + +class StackUtils { +  constructor (opts) { +    opts = { +      ignoredPackages: [], +      ...opts +    }; + +    if ('internals' in opts === false) { +      opts.internals = StackUtils.nodeInternals(); +    } + +    if ('cwd' in opts === false) { +      opts.cwd = cwd +    } + +    this._cwd = opts.cwd.replace(/\\/g, '/'); +    this._internals = [].concat( +      opts.internals, +      ignoredPackagesRegExp(opts.ignoredPackages) +    ); + +    this._wrapCallSite = opts.wrapCallSite || false; +  } + +  static nodeInternals () { +    return [...natives]; +  } + +  clean (stack, indent = 0) { +    indent = ' '.repeat(indent); + +    if (!Array.isArray(stack)) { +      stack = stack.split('\n'); +    } + +    if (!(/^\s*at /.test(stack[0])) && (/^\s*at /.test(stack[1]))) { +      stack = stack.slice(1); +    } + +    let outdent = false; +    let lastNonAtLine = null; +    const result = []; + +    stack.forEach(st => { +      st = st.replace(/\\/g, '/'); + +      if (this._internals.some(internal => internal.test(st))) { +        return; +      } + +      const isAtLine = /^\s*at /.test(st); + +      if (outdent) { +        st = st.trimEnd().replace(/^(\s+)at /, '$1'); +      } else { +        st = st.trim(); +        if (isAtLine) { +          st = st.slice(3); +        } +      } + +      st = st.replace(`${this._cwd}/`, ''); + +      if (st) { +        if (isAtLine) { +          if (lastNonAtLine) { +            result.push(lastNonAtLine); +            lastNonAtLine = null; +          } + +          result.push(st); +        } else { +          outdent = true; +          lastNonAtLine = st; +        } +      } +    }); + +    return result.map(line => `${indent}${line}\n`).join(''); +  } + +  captureString (limit, fn = this.captureString) { +    if (typeof limit === 'function') { +      fn = limit; +      limit = Infinity; +    } + +    const {stackTraceLimit} = Error; +    if (limit) { +      Error.stackTraceLimit = limit; +    } + +    const obj = {}; + +    Error.captureStackTrace(obj, fn); +    const {stack} = obj; +    Error.stackTraceLimit = stackTraceLimit; + +    return this.clean(stack); +  } + +  capture (limit, fn = this.capture) { +    if (typeof limit === 'function') { +      fn = limit; +      limit = Infinity; +    } + +    const {prepareStackTrace, stackTraceLimit} = Error; +    Error.prepareStackTrace = (obj, site) => { +      if (this._wrapCallSite) { +        return site.map(this._wrapCallSite); +      } + +      return site; +    }; + +    if (limit) { +      Error.stackTraceLimit = limit; +    } + +    const obj = {}; +    Error.captureStackTrace(obj, fn); +    const { stack } = obj; +    Object.assign(Error, {prepareStackTrace, stackTraceLimit}); + +    return stack; +  } + +  at (fn = this.at) { +    const [site] = this.capture(1, fn); + +    if (!site) { +      return {}; +    } + +    const res = { +      line: site.getLineNumber(), +      column: site.getColumnNumber() +    }; + +    setFile(res, site.getFileName(), this._cwd); + +    if (site.isConstructor()) { +      res.constructor = true; +    } + +    if (site.isEval()) { +      res.evalOrigin = site.getEvalOrigin(); +    } + +    // Node v10 stopped with the isNative() on callsites, apparently +    /* istanbul ignore next */ +    if (site.isNative()) { +      res.native = true; +    } + +    let typename; +    try { +      typename = site.getTypeName(); +    } catch (_) { +    } + +    if (typename && typename !== 'Object' && typename !== '[object Object]') { +      res.type = typename; +    } + +    const fname = site.getFunctionName(); +    if (fname) { +      res.function = fname; +    } + +    const meth = site.getMethodName(); +    if (meth && fname !== meth) { +      res.method = meth; +    } + +    return res; +  } + +  parseLine (line) { +    const match = line && line.match(re); +    if (!match) { +      return null; +    } + +    const ctor = match[1] === 'new'; +    let fname = match[2]; +    const evalOrigin = match[3]; +    const evalFile = match[4]; +    const evalLine = Number(match[5]); +    const evalCol = Number(match[6]); +    let file = match[7]; +    const lnum = match[8]; +    const col = match[9]; +    const native = match[10] === 'native'; +    const closeParen = match[11] === ')'; +    let method; + +    const res = {}; + +    if (lnum) { +      res.line = Number(lnum); +    } + +    if (col) { +      res.column = Number(col); +    } + +    if (closeParen && file) { +      // make sure parens are balanced +      // if we have a file like "asdf) [as foo] (xyz.js", then odds are +      // that the fname should be += " (asdf) [as foo]" and the file +      // should be just "xyz.js" +      // walk backwards from the end to find the last unbalanced ( +      let closes = 0; +      for (let i = file.length - 1; i > 0; i--) { +        if (file.charAt(i) === ')') { +          closes++; +        } else if (file.charAt(i) === '(' && file.charAt(i - 1) === ' ') { +          closes--; +          if (closes === -1 && file.charAt(i - 1) === ' ') { +            const before = file.slice(0, i - 1); +            const after = file.slice(i + 1); +            file = after; +            fname += ` (${before}`; +            break; +          } +        } +      } +    } + +    if (fname) { +      const methodMatch = fname.match(methodRe); +      if (methodMatch) { +        fname = methodMatch[1]; +        method = methodMatch[2]; +      } +    } + +    setFile(res, file, this._cwd); + +    if (ctor) { +      res.constructor = true; +    } + +    if (evalOrigin) { +      res.evalOrigin = evalOrigin; +      res.evalLine = evalLine; +      res.evalColumn = evalCol; +      res.evalFile = evalFile && evalFile.replace(/\\/g, '/'); +    } + +    if (native) { +      res.native = true; +    } + +    if (fname) { +      res.function = fname; +    } + +    if (method && fname !== method) { +      res.method = method; +    } + +    return res; +  } +} + +function setFile (result, filename, cwd) { +  if (filename) { +    filename = filename.replace(/\\/g, '/'); +    if (filename.startsWith(`${cwd}/`)) { +      filename = filename.slice(cwd.length + 1); +    } + +    result.file = filename; +  } +} + +function ignoredPackagesRegExp(ignoredPackages) { +  if (ignoredPackages.length === 0) { +    return []; +  } + +  const packages = ignoredPackages.map(mod => escapeStringRegexp(mod)); + +  return new RegExp(`[\/\\\\]node_modules[\/\\\\](?:${packages.join('|')})[\/\\\\][^:]+:\\d+:\\d+`) +} + +const re = new RegExp( +  '^' + +    // Sometimes we strip out the '    at' because it's noisy +  '(?:\\s*at )?' + +    // $1 = ctor if 'new' +  '(?:(new) )?' + +    // $2 = function name (can be literally anything) +    // May contain method at the end as [as xyz] +  '(?:(.*?) \\()?' + +    // (eval at <anonymous> (file.js:1:1), +    // $3 = eval origin +    // $4:$5:$6 are eval file/line/col, but not normally reported +  '(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?' + +    // file:line:col +    // $7:$8:$9 +    // $10 = 'native' if native +  '(?:(.+?):(\\d+):(\\d+)|(native))' + +    // maybe close the paren, then end +    // if $11 is ), then we only allow balanced parens in the filename +    // any imbalance is placed on the fname.  This is a heuristic, and +    // bound to be incorrect in some edge cases.  The bet is that +    // having weird characters in method names is more common than +    // having weird characters in filenames, which seems reasonable. +  '(\\)?)$' +); + +const methodRe = /^(.*?) \[as (.*?)\]$/; + +module.exports = StackUtils; diff --git a/node_modules/stack-utils/license b/node_modules/stack-utils/license new file mode 100644 index 0000000..28441ca --- /dev/null +++ b/node_modules/stack-utils/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Isaac Z. Schlueter <i@izs.me>, James Talmage <james@talmage.io> (github.com/jamestalmage), and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/stack-utils/package.json b/node_modules/stack-utils/package.json new file mode 100644 index 0000000..565acf6 --- /dev/null +++ b/node_modules/stack-utils/package.json @@ -0,0 +1,39 @@ +{ +  "name": "stack-utils", +  "version": "2.0.5", +  "description": "Captures and cleans stack traces", +  "license": "MIT", +  "repository": "tapjs/stack-utils", +  "author": { +    "name": "James Talmage", +    "email": "james@talmage.io", +    "url": "github.com/jamestalmage" +  }, +  "engines": { +    "node": ">=10" +  }, +  "scripts": { +    "test": "tap", +    "preversion": "npm test", +    "postversion": "npm publish", +    "prepublishOnly": "git push origin --follow-tags" +  }, +  "tap": { +    "check-coverage": true +  }, +  "files": [ +    "index.js" +  ], +  "dependencies": { +    "escape-string-regexp": "^2.0.0" +  }, +  "devDependencies": { +    "bluebird": "^3.7.2", +    "coveralls": "^3.0.9", +    "nested-error-stacks": "^2.1.0", +    "pify": "^4.0.1", +    "q": "^1.5.1", +    "source-map-support": "^0.5.20", +    "tap": "^15.0.9" +  } +} diff --git a/node_modules/stack-utils/readme.md b/node_modules/stack-utils/readme.md new file mode 100644 index 0000000..c91f9d0 --- /dev/null +++ b/node_modules/stack-utils/readme.md @@ -0,0 +1,143 @@ +# stack-utils  + +> Captures and cleans stack traces. + +[](https://travis-ci.org/tapjs/stack-utils) [](https://ci.appveyor.com/project/jamestalmage/stack-utils-oiw96/branch/master)  [](https://coveralls.io/github/tapjs/stack-utils?branch=master) + + +Extracted from `lib/stack.js` in the [`node-tap` project](https://github.com/tapjs/node-tap) + +## Install + +``` +$ npm install --save stack-utils +``` + + +## Usage + +```js +const StackUtils = require('stack-utils'); +const stack = new StackUtils({cwd: process.cwd(), internals: StackUtils.nodeInternals()}); + +console.log(stack.clean(new Error().stack)); +// outputs a beautified stack trace +``` + + +## API + + +### new StackUtils([options]) + +Creates a new `stackUtils` instance. + +#### options + +##### internals + +Type: `array` of `RegularExpression`s   + +A set of regular expressions that match internal stack stack trace lines which should be culled from the stack trace. +The default is `StackUtils.nodeInternals()`, this can be disabled by setting `[]` or appended using +`StackUtils.nodeInternals().concat(additionalRegExp)`.  See also `ignoredPackages`. + +##### ignoredPackages + +Type: `array` of `string`s + +An array of npm modules to be culled from the stack trace.  This list will mapped to regular +expressions and merged with the `internals`. + +Default `''`. + +##### cwd + +Type: `string` + +The path to the current working directory. File names in the stack trace will be shown relative to this directory. + +##### 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() + +Returns an array of regular expressions that be used to cull lines from the stack trace that reference common Node.js internal files. + + +### stackUtils.clean(stack, indent = 0) + +Cleans up a stack trace by deleting any lines that match the `internals` passed to the constructor, and shortening file names relative to `cwd`. + +Returns a `string` with the cleaned up stack (always terminated with a `\n` newline character). +Spaces at the start of each line are trimmed, indentation can be added by setting `indent` to the desired number of spaces. + +#### stack + +*Required*   +Type: `string` or an `array` of `string`s + + +### stackUtils.capture([limit], [startStackFunction]) + +Captures the current stack trace, returning an array of `CallSite`s. There are good overviews of the available CallSite methods [here](https://github.com/v8/v8/wiki/Stack%20Trace%20API#customizing-stack-traces), and [here](https://github.com/sindresorhus/callsites#api). + +#### limit + +Type: `number` +Default: `Infinity` + +Limits the number of lines returned by dropping all lines in excess of the limit. This removes lines from the stack trace. + +#### startStackFunction + +Type: `function` + +The function where the stack trace should start. The first line of the stack trace will be the function that called `startStackFunction`. This removes lines from the end of the stack trace. + + +### stackUtils.captureString([limit], [startStackFunction]) + +Captures the current stack trace, cleans it using `stackUtils.clean(stack)`, and returns a string with the cleaned stack trace. It takes the same arguments as `stackUtils.capture`. + + +### stackUtils.at([startStackFunction]) + +Captures the first line of the stack trace (or the first line after `startStackFunction` if supplied), and returns a `CallSite` like object that is serialization friendly (properties are actual values instead of getter functions).  + +The available properties are: + + - `line`: `number`  + - `column`: `number` + - `file`: `string` + - `constructor`: `boolean` + - `evalOrigin`: `string` + - `native`: `boolean` + - `type`: `string` + - `function`: `string` + - `method`: `string` + +### stackUtils.parseLine(line) + +Parses a `string` (which should be a single line from a stack trace), and generates an object with the following properties: + + - `line`: `number`  + - `column`: `number` + - `file`: `string` + - `constructor`: `boolean` + - `evalOrigin`: `string` + - `evalLine`: `number` + - `evalColumn`: `number` + - `evalFile`: `string` + - `native`: `boolean` + - `function`: `string` + - `method`: `string` + + +## License + +MIT © [Isaac Z. Schlueter](http://github.com/isaacs), [James Talmage](http://github.com/jamestalmage)  | 
