UNPKG

@neabyte/stackz

Version:

Beautiful stack trace formatter with source code context for JavaScript

6 lines (5 loc) 3.06 kB
"use strict";class r{static getRuntimeType(){return typeof Deno<"u"&&Deno?.version?.deno?"deno":typeof globalThis<"u"&&"Bun"in globalThis&&globalThis.Bun?.version?"bun":typeof globalThis<"u"&&"process"in globalThis&&globalThis.process?.versions?.node?"nodejs":typeof window<"u"&&typeof document<"u"?"browser":"unknown"}static async readFile(t){const n=this.getRuntimeType();switch(n){case"deno":return await Deno.readTextFile(t);case"nodejs":return await(await import("node:fs/promises")).readFile(t,"utf-8");case"bun":{const s=globalThis;if(!s.Bun)throw new Error("Bun runtime detected but Bun API not available");return await s.Bun.file(t).text()}case"browser":throw new Error("File reading not supported in browser environment");default:throw new Error(`Unsupported runtime: ${n}`)}}}function formatCompact(e){const t=u(),n=[m(e,t).trim()];return e.frames.forEach(s=>{n.push(p(s,t,"compact"))}),n.join(` `)}async function formatDetailed(e){const t=u(),n=[m(e,t).trim(),""];if(e.frames.length>0){const s=e.frames[0];if(s){n.push(`Path: ${s.file}:${s.line}:${s.column}`);const o=await g(s,t);n.push(...o),o.length>0&&n.push("")}n.push("Call Stack:"),e.frames.forEach(o=>{n.push(p(o,t,"detailed"))})}return n.join(` `)}function m(e,t){return`${t.redBg} ${t.white}${e.type} ${t.reset} ${e.message}`}function p(e,t,n){const s=y$1(e.file),o=`${s}:${e.line}:${e.column}`;return n==="compact"?e.function==="anonymous"||e.function===s?`${t.dim} \u2192 ${o}${t.reset}`:`${t.dim} \u2192 ${e.function} @ ${o}${t.reset}`:`${t.dim} \u2022 ${e.function} \u2192 ${o}${t.reset}`}function u(){const e=r.getRuntimeType(),t=e==="browser"||e==="unknown";return{dim:t?"":"\x1B[2m",red:t?"":"\x1B[91m",redBg:t?"":"\x1B[41m",reset:t?"":"\x1B[0m",white:t?"":"\x1B[97m"}}async function g(e,t){const n=[];try{const s=(await r.readFile(e.file)).split(` `),o=Math.max(0,e.line-4),w=Math.min(s.length,e.line+1);for(let a=o;a<w;a++){const c=String(a+1).padStart(2," "),l=s[a]||"";if(a===e.line-1){n.push(`${t.redBg}${t.white} ${c} ${t.reset} ${t.red}${l}${t.reset}`);const i=Math.max(0,e.column-1),f=`${" ".repeat(i)}${"^".repeat(Math.max(1,l.length-i))}`;n.push(`${t.dim} ${t.red}${f}${t.reset}`)}else n.push(`${t.dim} ${c} | ${l}${t.reset}`)}}catch{}return n}function y$1(e){return e.split("/").pop()||e}class y{static format(t,n){const s=this.parseError(t);if(n==="compact")return formatCompact(s);if(n==="detailed")return formatDetailed(s);throw new Error(`Unknown format style: ${n}`)}static parseError(t){const n=(t.stack||"").split(` `).filter(c=>c.trim()),s=(n[0]||"").match(/^(\w+):\s*(.*)/),o=s?.[1]||"Error",w=s?.[2]||t.message,a=[];for(let c=1;c<n.length;c++){const l=n[c]?.trim();if(!l)continue;const i=l.match(/at\s+(?:async\s+)?(.+?)\s+\((.+?):(\d+):(\d+)\)|at\s+(.+?):(\d+):(\d+)/);if(i){let f,h,d,$;i[1]?(f=i[1]||"anonymous",h=i[2]||"",d=i[3]||"0",$=i[4]||"0"):(f="anonymous",h=i[5]||"",d=i[6]||"0",$=i[7]||"0"),h&&d&&$&&a.push({function:f,file:h.replace(/^file:\/\//,""),line:parseInt(d),column:parseInt($)})}}return{type:o,message:w,frames:a}}}module.exports=y;