"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 positionRegex = /(.+):(\d+):(\d+)/; function maybeTrim(value) { if (value != null) { return value.trim(); } else { return value; } } function parseLocation(locationString) { 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 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? let matches = execall(lineRegex, stack); 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])) }; }); } module.exports = function parseStackTrace(error) { let stack = error.stack; let lines = stack.split("\n"); let firstStackLine = lines .map((line) => line.trim()) .findIndex((line) => line.startsWith("at ") && positionRegex.test(line)); if (firstStackLine !== -1) { let cleanStack = lines .slice(firstStackLine) .join("\n"); return extractFrames(cleanStack); } else { throw new Error(`Could not find a stacktrace frame`); } };