UNPKG

pnpm

Version:

Fast, disk space efficient package manager

296 lines (212 loc) 9.85 kB
"use strict"; /* ------------------------------------------------------------------------ */ const O = Object, isBrowser = (typeof window !== 'undefined') && (window.window === window) && window.navigator, lastOf = x => x[x.length - 1], getSource = require ('get-source'), partition = require ('./impl/partition'), asTable = require ('as-table'), nixSlashes = x => x.replace (/\\/g, '/'), pathRoot = isBrowser ? window.location.href : (nixSlashes (process.cwd ()) + '/') /* ------------------------------------------------------------------------ */ class StackTracey extends Array { constructor (input, offset) { const originalInput = input , isParseableSyntaxError = input && (input instanceof SyntaxError && !isBrowser) super () /* Fixes for Safari */ this.constructor = StackTracey this.__proto__ = StackTracey.prototype /* new StackTracey () */ if (!input) { input = new Error () offset = (offset === undefined) ? 1 : offset } /* new StackTracey (Error) */ if (input instanceof Error) { input = input[StackTracey.stack] || input.stack || '' } /* new StackTracey (string) */ if (typeof input === 'string') { input = StackTracey.rawParse (input).slice (offset).map (StackTracey.extractEntryMetadata) } /* new StackTracey (array) */ if (Array.isArray (input)) { if (isParseableSyntaxError) { const rawLines = module.require ('util').inspect (originalInput).split ('\n') , fileLine = rawLines[0].split (':') , line = fileLine.pop () , file = fileLine.join (':') if (file) { input.unshift ({ file: nixSlashes (file), line: line, column: (rawLines[2] || '').indexOf ('^') + 1, sourceLine: rawLines[1], callee: '(syntax error)', syntaxError: true }) } } this.length = input.length input.forEach ((x, i) => this[i] = x) } } static extractEntryMetadata (e) { const fileRelative = StackTracey.relativePath (e.file || '') return O.assign (e, { calleeShort: e.calleeShort || lastOf ((e.callee || '').split ('.')), fileRelative: fileRelative, fileShort: StackTracey.shortenPath (fileRelative), fileName: lastOf ((e.file || '').split ('/')), thirdParty: StackTracey.isThirdParty (fileRelative) && !e.index }) } static shortenPath (relativePath) { return relativePath.replace (/^node_modules\//, '') .replace (/^webpack\/bootstrap\//, '') } static relativePath (fullPath) { return fullPath.replace (pathRoot, '') .replace (/^.*\:\/\/?\/?/, '') } static isThirdParty (relativePath) { return (relativePath[0] === '~') || // webpack-specific heuristic (relativePath[0] === '/') || // external source (relativePath.indexOf ('node_modules') === 0) || (relativePath.indexOf ('webpack/bootstrap') === 0) } static rawParse (str) { const lines = (str || '').split ('\n') const entries = lines.map (line => { line = line.trim () var callee, fileLineColumn = [], native, planA, planB if ((planA = line.match (/at (.+) \((.+)\)/)) || (planA = line.match (/(.*)@(.*)/))) { callee = planA[1] native = (planA[2] === 'native') fileLineColumn = (planA[2].match (/(.*):(.+):(.+)/) || []).slice (1) } else if ((planB = line.match (/^(at\s+)*(.+):([0-9]+):([0-9]+)/) )) { fileLineColumn = (planB).slice (2) } else { return undefined } /* Detect things like Array.reduce TODO: detect more built-in types */ if (callee && !fileLineColumn[0]) { const type = callee.split ('.')[0] if (type === 'Array') { native = true } } return { beforeParse: line, callee: callee || '', index: isBrowser && (fileLineColumn[0] === window.location.href), native: native || false, file: nixSlashes (fileLineColumn[0] || ''), line: parseInt (fileLineColumn[1] || '', 10) || undefined, column: parseInt (fileLineColumn[2] || '', 10) || undefined } }) return entries.filter (x => (x !== undefined)) } withSource (i) { return this[i] && StackTracey.withSource (this[i]) } static withSource (loc) { if (loc.sourceFile || (loc.file && loc.file.indexOf ('<') >= 0)) { // skip things like <anonymous> and stuff that was already fetched return loc } else { let resolved = getSource (loc.file || '').resolve (loc) if (!resolved.sourceFile) { return loc } if (!resolved.sourceFile.error) { resolved.file = nixSlashes (resolved.sourceFile.path) resolved = StackTracey.extractEntryMetadata (resolved) } if (!resolved.sourceLine.error && resolved.sourceLine.includes ('// @hide')) { resolved.sourceLine = resolved.sourceLine.replace ('// @hide', '') resolved.hide = true } return O.assign ({ sourceLine: '' }, loc, resolved) } } get withSources () { return new StackTracey (this.map (StackTracey.withSource)) } get mergeRepeatedLines () { return new StackTracey ( partition (this, e => e.file + e.line).map ( group => { return group.items.slice (1).reduce ((memo, entry) => { memo.callee = (memo.callee || '<anonymous>') + ' → ' + (entry.callee || '<anonymous>') memo.calleeShort = (memo.calleeShort || '<anonymous>') + ' → ' + (entry.calleeShort || '<anonymous>') return memo }, O.assign ({}, group.items[0])) })) } get clean () { return this.withSources.mergeRepeatedLines.filter ((e, i) => (i === 0) || !(e.thirdParty || e.hide || e.native)) } at (i) { return O.assign ({ beforeParse: '', callee: '<???>', index: false, native: false, file: '<???>', line: 0, column: 0 }, this[i]) } static locationsEqual (a, b) { return (a.file === b.file) && (a.line === b.line) && (a.column === b.column) } get pretty () { const trimEnd = (s, n) => s && ((s.length > n) ? (s.slice (0, n-1) + '…') : s) const trimStart = (s, n) => s && ((s.length > n) ? ('…' + s.slice (-(n-1))) : s) return asTable (this.withSources.map ( e => [ ('at ' + trimEnd (e.calleeShort, StackTracey.maxColumnWidths.callee)), trimStart ((e.fileShort && (e.fileShort + ':' + e.line)) || '', StackTracey.maxColumnWidths.file), trimEnd (((e.sourceLine || '').trim () || ''), StackTracey.maxColumnWidths.sourceLine) ])) } static resetCache () { getSource.resetCache () } } /* Some default configuration options ------------------------------------------------------------------------ */ StackTracey.maxColumnWidths = { callee: 30, file: 40, sourceLine: 80 } /* Chaining helper for .isThirdParty ------------------------------------------------------------------------ */ ;(() => { const methods = { include (pred) { const f = StackTracey.isThirdParty O.assign (StackTracey.isThirdParty = (path => f (path) || pred (path)), methods) }, except (pred) { const f = StackTracey.isThirdParty O.assign (StackTracey.isThirdParty = (path => f (path) && !pred (path)), methods) }, } O.assign (StackTracey.isThirdParty, methods) }) () /* Array methods ------------------------------------------------------------------------ */ ;['map', 'filter', 'slice', 'concat', 'reverse'].forEach (name => { StackTracey.prototype[name] = function (/*...args */) { // no support for ...args in Node v4 :( const arr = Array.from (this) return new StackTracey (arr[name].apply (arr, arguments)) } }) /* A private field that an Error instance can expose ------------------------------------------------------------------------ */ StackTracey.stack = /* istanbul ignore next */ (typeof Symbol !== 'undefined') ? Symbol.for ('StackTracey') : '__StackTracey' /* ------------------------------------------------------------------------ */ module.exports = StackTracey /* ------------------------------------------------------------------------ */