1"use strict"; 2 3/* ------------------------------------------------------------------------ */ 4 5const O = Object, 6 isBrowser = (typeof window !== 'undefined') && (window.window === window) && window.navigator, 7 nodeRequire = isBrowser ? null : module.require, // to prevent bundlers from expanding the require call 8 lastOf = x => x[x.length - 1], 9 nixSlashes = x => x.replace (/\\/g, '/'), 10 pathRoot = isBrowser ? window.location.href : (nixSlashes (process.cwd ()) + '/') 11 12/* ------------------------------------------------------------------------ */ 13 14class StackTracey { 15 constructor(input, offset) { 16 17 const originalInput = input 18 , isParseableSyntaxError = input && (input instanceof SyntaxError && !isBrowser) 19 20 /* new StackTracey () */ 21 22 if (!input) { 23 input = new Error() 24 offset = (offset === undefined) ? 1 : offset 25 } 26 27 /* new StackTracey (Error) */ 28 29 if (input instanceof Error) { 30 input = input.stack || '' 31 } 32 33 /* new StackTracey (string) */ 34 35 if (typeof input === 'string') { 36 input = this.rawParse(input).slice(offset).map(x => this.extractEntryMetadata(x)) 37 } 38 39 /* new StackTracey (array) */ 40 41 if (Array.isArray(input)) { 42 43 if (isParseableSyntaxError) { 44 45 const rawLines = nodeRequire('util').inspect(originalInput).split('\n') 46 , fileLine = rawLines[0].split(':') 47 , line = fileLine.pop() 48 , file = fileLine.join(':') 49 50 if (file) { 51 input.unshift({ 52 file: nixSlashes(file), 53 line: line, 54 column: (rawLines[2] || '').indexOf('^') + 1, 55 sourceLine: rawLines[1], 56 callee: '(syntax error)', 57 syntaxError: true 58 }) 59 } 60 } 61 62 this.items = input 63 64 } else { 65 this.items = [] 66 } 67 } 68 69 extractEntryMetadata(e) { 70 71 const decomposedPath = this.decomposePath(e.file || '') 72 const fileRelative = decomposedPath[0] 73 const externalDomain = decomposedPath[1] 74 75 return O.assign(e, { 76 77 calleeShort: e.calleeShort || lastOf((e.callee || '').split('.')), 78 fileRelative: fileRelative, 79 fileShort: this.shortenPath(fileRelative), 80 fileName: lastOf((e.file || '').split('/')), 81 thirdParty: this.isThirdParty(fileRelative, externalDomain) && !e.index, 82 externalDomain: externalDomain 83 }) 84 } 85 86 shortenPath(relativePath) { 87 return relativePath.replace(/^node_modules\//, '') 88 .replace(/^webpack\/bootstrap\//, '') 89 .replace(/^__parcel_source_root\//, '') 90 } 91 92 decomposePath(fullPath) { 93 let result = fullPath 94 95 if (isBrowser) result = result.replace(pathRoot, '') 96 97 const externalDomainMatch = result.match(/^(http|https)\:\/\/?([^\/]+)\/(.*)/) 98 const externalDomain = externalDomainMatch ? externalDomainMatch[2] : undefined 99 result = externalDomainMatch ? externalDomainMatch[3] : result 100 101 if (!isBrowser) result = nodeRequire('path').relative(pathRoot, result) 102 103 return [ 104 nixSlashes(result).replace(/^.*\:\/\/?\/?/, ''), // cut webpack:/// and webpack:/ things 105 externalDomain 106 ] 107 } 108 109 isThirdParty(relativePath, externalDomain) { 110 return externalDomain || 111 (relativePath[0] === '~') || // webpack-specific heuristic 112 (relativePath[0] === '/') || // external source 113 (relativePath.indexOf('node_modules') === 0) || 114 (relativePath.indexOf('webpack/bootstrap') === 0) 115 } 116 117 rawParse(str) { 118 119 const lines = (str || '').split('\n') 120 121 const entries = lines.map(line => { 122 123 line = line.trim() 124 125 let callee, fileLineColumn = [], native, planA, planB 126 127 if ((planA = line.match(/at (.+) \(eval at .+ \((.+)\), .+\)/)) || // eval calls 128 (planA = line.match(/at (.+) \((.+)\)/)) || 129 ((line.slice(0, 3) !== 'at ') && (planA = line.match(/(.*)@(.*)/)))) { 130 131 callee = planA[1] 132 native = (planA[2] === 'native') 133 fileLineColumn = (planA[2].match(/(.*):(\d+):(\d+)/) || 134 planA[2].match(/(.*):(\d+)/) || []).slice(1) 135 136 } else if ((planB = line.match(/^(at\s+)*(.+):(\d+):(\d+)/))) { 137 fileLineColumn = (planB).slice(2) 138 139 } else { 140 return undefined 141 } 142 143 /* Detect things like Array.reduce 144 TODO: detect more built-in types */ 145 146 if (callee && !fileLineColumn[0]) { 147 const type = callee.split('.')[0] 148 if (type === 'Array') { 149 native = true 150 } 151 } 152 153 return { 154 beforeParse: line, 155 callee: callee || '', 156 index: isBrowser && (fileLineColumn[0] === window.location.href), 157 native: native || false, 158 file: nixSlashes(fileLineColumn[0] || ''),
159 line: parseInt(fileLineColumn[1] || '', 10) || undefined, 160 column: parseInt(fileLineColumn[2] || '', 10) || undefined 161 } 162 }) 163 164 return entries.filter(x => (x !== undefined)) 165 } 166} 167 168 169 170 171 172 173/* Array methods 174 ------------------------------------------------------------------------ */ 175 176// ;['map', 'filter', 'slice', 'concat'].forEach (method => { 177// 178// StackTracey.prototype[method] = function (/*...args */) { // no support for ...args in Node v4 :( 179// return new StackTracey (this.items[method].apply (this.items, arguments)) 180// } 181// }) 182 183/* ------------------------------------------------------------------------ */ 184 185window.StackTracey = StackTracey 186
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.