UNPKG

@stacktrace-lite/core

Version:

> Parse, filter, and format JavaScript stack traces with plugins for browsers and Node.js.

79 lines (78 loc) 3.1 kB
import { bold, dim, yellow, gray } from 'colorette'; /** * Format an array of {@link StackFrame} entries into a string based on the desired output mode. * * @param frames - Parsed stack frames to format. * @param mode - Output style: * - `"cli"` – styled for terminal (default) * - `"html"` – safe for rendering into web UIs * - `"json"` – structured JSON * - `"raw"` – concatenated original lines (`frame.raw`) * @returns A formatted string representing the stack trace. * * @remarks * **Terminal styling (`"cli"`)** * Uses the [colorette](https://www.npmjs.com/package/colorette) library to render ANSI styling: * - `bold(...)` for function names * - `dim('at')` for the “at” constant * - `yellow(...)` for file names * - `gray(...)` for line/column position * Colorette is chosen for its speed and zero dependencies—efficient and NO_COLOR friendly.:contentReference[oaicite:1]{index=1} * * **HTML mode (`"html"`)** * Safely escapes HTML entities and assembles a `<pre>` block with semantic `<span>` tags (`function`, `at`, `file`, `pos`)—great for embedding in browser error overlays or dev UI. * * **JSON mode (`"json"`)** * Outputs the full frames array as pretty JSON (`null, 2` spacing), ideal for logging or backend ingestion. * * **Raw mode (`"raw"`)** * Joins the `raw` values of frames, preserving their original formatting without transformation. * * @example * ```ts * const trace = formatStack(frames, 'cli'); * console.log(trace); * * const htmlTrace = formatStack(frames, 'html'); * document.body.innerHTML = htmlTrace; * * const jsonTrace = formatStack(frames, 'json'); * sendToServer(jsonTrace); * ``` * * @throws Error when an unsupported mode string is provided. */ export function formatStack(frames, mode = 'cli') { if (mode === 'cli') { return frames .map((f) => bold(f.functionName ?? '<anonymous>') + ' ' + dim('at') + ' ' + yellow(f.fileName ?? '') + gray(f.lineNumber != null && f.columnNumber != null ? `:${f.lineNumber}:${f.columnNumber}` : '')) .join('\n'); } if (mode === 'html') { const esc = (s) => s.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]); return ('<pre class="stacktrace-lite">\n' + frames .map((f) => `<span class="function">${esc(f.functionName ?? '<anonymous>')}</span> ` + `<span class="at">at</span> ` + `<span class="file">${esc(f.fileName ?? '')}</span>` + (f.lineNumber != null && f.columnNumber != null ? `<span class="pos">:${f.lineNumber}:${f.columnNumber}</span>` : '')) .join('<br>\n') + '\n</pre>'); } if (mode === 'json') { return JSON.stringify(frames, null, 2); } if (mode === 'raw') { return frames.map((f) => f.raw).join('\n'); } throw new Error(`Unknown format mode: ${mode}`); }