'use strict' const { SourceMapConsumer } = require('source-map') module.exports = function stackTraceMapper (stack, map) { if (!(map instanceof SourceMapConsumer)) { throw new Error('map is not a consumer') } const setups = [ { // settings for Chrome, Edge and IE (Plus?) regex: /\s+at(?:\s+([^\s]+))?\s\(?([/a-z0-9:.-]+):([0-9]+):([0-9]+)/, // regex: /\s+at(?:\s+([^)]+))?\s\(?([/a-z0-9:.-]+):([0-9]+):([0-9]+)/, skip: 1, split: /\n?(?= at )/, }, { // settings for Firefox and Safari regex: /([a-zA-Z0-9]*)@(.*):([0-9]+):([0-9]+)/, skip: 0, split: /\s+/, }, ] const setup = setups.find((setup) => { return setup.regex.test(stack) }) if (!setup) { throw new Error('Stack trace is not recognised') } const split = stack.trim().split(setup.split) // TODO whether the error name and similar preludes the stack varies between browsers perhaps we should create one const pre = split.slice(0, setup.skip) console.log('split', split) console.log('pre', pre) const lines = split.slice(setup.skip).map((source) => { // TODO handle lines that have not matched const match = source.match(setup.regex) if (!match) { return { source } } const [ , fnc, file, line, column ] = match return { fnc, file, line: parseInt(line), column: parseInt(column), source } }) const originalLines = lines.map((obj) => { if (obj.line) { return map.originalPositionFor({ line: obj.line, column: obj.column }) } return obj }) // need to do research as to why, but the original line objects returned // contain the function call names are shifted one line compared with // the sourcemapped stack printed in chrome dev tools const adjustedLines = originalLines.map((obj, index, arr) => { if (obj.line) { const prev = arr[index + 1] obj.name = (prev && prev.name) || undefined } return obj }) return pre.concat(adjustedLines.map(formatLine)) } function formatLine ({ source, line, column, name = '(unknown)' }) { if (!line || !column) { return ` ${source}` } return ` at ${name} (${source}:${line}:${column})` }