"use strict"; // FIXME: Move this into its own package! const execall = require("execall"); const defaultValue = require("default-value"); // I'm so, so sorry... let lineRegex = /\s+at\s+(?:(?:((?:[^\[\n])+)\[as ([^\]]+)\] |((?:[^\(\n])+))\(([^\)\n]+)\)|([^\n]+))(?:\n|$)/gm; let geckoLineRegex = /^([^@\n]*)@(.+)/gm; let positionRegex = /(.+):(\d+):(\d+)/; function maybeTrim(value) { if (value != null) { let trimmed = value.trim(); if (trimmed.length > 0) { return trimmed; } else { return null; } } else { return value; } } function parseLocation(locationString) { if (locationString === "") { return { anonymous: true }; } else { let match = positionRegex.exec(locationString); if (match != null) { return { path: match[1], line: parseInt(match[2]), column: parseInt(match[3]) }; } else { throw new Error(`Could not parse location from string: ${locationString}`); } } } function extractFramesV8(stack) { let matches = execall(lineRegex, stack); if (matches.length > 0) { return matches.map((match) => { let groups = match.subMatches; return { functionName: maybeTrim(defaultValue(groups[0], groups[2])), alias: groups[1], location: parseLocation(defaultValue(groups[3], groups[4])) }; }); } else { return null; } } function extractFramesGecko(stack) { let matches = execall(geckoLineRegex, stack); if (matches.length > 0) { return matches.map((match) => { let groups = match.subMatches; return { functionName: maybeTrim(groups[0]), location: parseLocation(groups[1]) }; }); } else { return null; } } function extractFrames(stack) { // TODO: Maybe make this code even more cautious, and match each stacktrace line individually, aborting as soon as any one line cannot be parsed? return defaultValue( extractFramesV8(stack), extractFramesGecko(stack) ); } module.exports = function parseStackTrace(error) { let stack = error.stack; let lines = stack.split("\n"); let firstStackLine = lines .map((line) => line.trim()) .findIndex((line) => positionRegex.test(line)); if (firstStackLine !== -1) { let cleanStack = lines .slice(firstStackLine) .join("\n"); let extracted = extractFrames(cleanStack); if (extracted != null) { return extracted; } else { throw new Error(`Could not extract stacktrace frames`); } } else { throw new Error(`Could not find a stacktrace frame`); } };