Last active
June 17, 2025 02:42
-
-
Save hyrious/b8fcf2c525de1800113af3d4ee883709 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <reference types="vite/client" /> | |
// The contents of index.html: | |
// <script src="./node_modules/source-map/dist/source-map.js"></script> | |
// <script type="module" src="/using-source-map-in-browser.ts"></script> | |
import * as esbuild from 'esbuild-wasm/esm/browser.js' | |
import wasmURL from 'esbuild-wasm/esbuild.wasm?url' | |
import sourceMapWasmURL from 'source-map/lib/mappings.wasm?url' | |
declare const sourceMap: any // The source-map lib can only be initialized outside :/ | |
sourceMap.SourceMapConsumer.initialize({ 'lib/mappings.wasm': sourceMapWasmURL }) | |
const $ = document.createElement.bind(document) as typeof document.createElement | |
const $textarea = document.body.appendChild($('div')) | |
$textarea.style.font = '13px/1.22 monospace' | |
$textarea.style.whiteSpace = 'pre' | |
const logToTextarea = (s: string, br = true) => { | |
$textarea.appendChild(document.createTextNode(br ? s + '\n' : s)) | |
} | |
const fakeFileName = 'virtual://main.js' | |
addEventListener('error', ev => { | |
if (ev.filename == fakeFileName) { | |
ev.preventDefault() | |
logError(ev.error) | |
} | |
}) | |
logToTextarea('initializing... ', false) | |
let t = Date.now() | |
await esbuild.initialize({ wasmURL }) | |
await esbuild.transform('let a = 1') | |
logToTextarea(`done in ${Date.now() - t}ms`) | |
let filename = 'hello.ts' | |
let contents = 'throw new Error( "hello TypeScript" as null!)' | |
let { code, map } = await esbuild.transform(contents, { | |
loader: 'ts', | |
sourcefile: filename, | |
sourcemap: true | |
}) | |
let consumer = await new sourceMap.SourceMapConsumer(map) | |
let blob = new Blob([code, `//# sourceURL=${fakeFileName}`], { type: 'application/javascript' }) | |
let url = URL.createObjectURL(blob) | |
let $script = $('script') | |
$script.src = url | |
document.body.appendChild($script) | |
// ref: https://github.com/evanw/node-source-map-support/blob/7b5b81/source-map-support.js#L485 | |
function logError(error: Error) { | |
const stackRegex = new RegExp(`^\\s+at ${fakeFileName.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')}:(\\d+):(\\d+)`, 'gm') | |
const replace: [start: number, length: number, content: string][] = [] | |
const debug: [line: number, column: number][] = [] | |
let stack = error.stack || '' | |
let match: RegExpMatchArray | null = null | |
while ((match = stackRegex.exec(stack))) { | |
// Location in the final code, will be transformed to the source contents | |
let line = +match[1] | |
let column = +match[2] | |
let source: string | |
({ source, line, column } = consumer.originalPositionFor({ source: fakeFileName, line, column })) | |
replace.push([match.index!, match[0].length, ` at ${source}:${line}:${column + 1}`]) | |
debug.push([line, column + 1]) | |
} | |
let offset = 0 | |
for (let [i, len, text] of replace) { | |
stack = stack.slice(0, i + offset) + text + stack.slice(i + len + offset) | |
offset += text.length - len | |
} | |
logToTextarea('') | |
logToTextarea(stack) | |
// Debug -- show sources | |
// Better using a formatter, like https://babeljs.io/docs/babel-code-frame | |
logToTextarea('') | |
logToTextarea(`// File: ${filename}`) | |
contents.split(/\r?\n/g).forEach((line, i) => { | |
logToTextarea(`${i + 1}: ${line}`) | |
let columns = debug.filter(([l]) => l === i + 1).map(([_, c]) => c) | |
if (columns.length) { | |
let cs = Array(columns.reduce((a, b) => Math.max(a, b))).fill(' ') | |
columns.forEach(c => cs[c - 1] = '^') | |
logToTextarea(' ' + cs.join('')) | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo output: