From 79ac672ce31c30e7064b8160a7e552a385b66dc0 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 00:09:50 -0500 Subject: [PATCH 01/27] feat: poc --- packages/playground/runtime-error/index.html | 13 ++ .../playground/runtime-error/package.json | 11 ++ .../runtime-error/src/entry-client.ts | 3 + packages/vite/src/client/client.ts | 173 ++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 packages/playground/runtime-error/index.html create mode 100644 packages/playground/runtime-error/package.json create mode 100644 packages/playground/runtime-error/src/entry-client.ts diff --git a/packages/playground/runtime-error/index.html b/packages/playground/runtime-error/index.html new file mode 100644 index 00000000000000..e510450624386b --- /dev/null +++ b/packages/playground/runtime-error/index.html @@ -0,0 +1,13 @@ + + + + + + Run Time ErrorL + + +

Runtime Error

+ + + + diff --git a/packages/playground/runtime-error/package.json b/packages/playground/runtime-error/package.json new file mode 100644 index 00000000000000..f6bc0ee5ab956e --- /dev/null +++ b/packages/playground/runtime-error/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-runtime-error", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/packages/playground/runtime-error/src/entry-client.ts b/packages/playground/runtime-error/src/entry-client.ts new file mode 100644 index 00000000000000..fb6a9bee913b3b --- /dev/null +++ b/packages/playground/runtime-error/src/entry-client.ts @@ -0,0 +1,3 @@ +document.querySelector('#throwBtn').onclick = () => { + throw new Error('Why did you click the button') +} diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index c9a95a66604bb3..7f5268c4c4d339 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -10,6 +10,8 @@ import type { CustomEventName } from 'types/customEvent' import { ErrorOverlay, overlayId } from './overlay' // eslint-disable-next-line node/no-missing-import import '@vite/env' +import type { RawSourceMap } from 'source-map' +import { SourceMapConsumer } from 'source-map' // injected by the hmr plugin when served declare const __BASE__: string @@ -486,4 +488,175 @@ export function injectQuery(url: string, queryToInject: string): string { }` } +type ErrorOverlayCallback = (err: ErrorPayload['err']) => void + +const extractSourceMapData = (fileContetnt: string) => { + const re = /[#@]\ssourceMappingURL=\s*(\S+)/gm + let match: RegExpExecArray | null = null + // eslint-disable-next-line no-constant-condition + while (true) { + let next = re.exec(fileContetnt) + if (next === null) { + break + } + match = next + } + + if (!(match && match[0])) { + return null + } + + return match[1] +} + +const findRealPath = ( + fileName: string, + sources: string[] +): string | undefined => { + const filePath = new URL(fileName).pathname + return sources.find((path) => path.includes(filePath)) +} + +const getSourceMapForFile = async ( + fileName: string +): Promise<{ + sourceMapConsumer?: SourceMapConsumer + source: string + fileName?: string +}> => { + const source = await (await fetch(fileName)).text() + const sourceMapData = extractSourceMapData(source) + + if (!sourceMapData) { + return { source } + } + + // TODO: extract this string to a const + if (sourceMapData.indexOf('data:application/json;base64,') === 0) { + let sm = sourceMapData.substring('data:application/json;base64,'.length) + sm = window.atob(sm) + const sourceMapConsumer = new SourceMapConsumer( + JSON.parse(sm) as RawSourceMap + ) + const realPath = findRealPath( + fileName, + sourceMapConsumer.sources as string[] + ) + + if (!realPath) { + return { source } + } + + return { + sourceMapConsumer, + source: sourceMapConsumer.sourceContentFor(realPath), + fileName: realPath + } + } + + // TODO: add support for url source maps + throw new Error('Only base64 source maps are supported') +} + +const generateFrame = ( + line: number, + column: number, + source: string, + count = 2 +): string => { + const lines = source.split('\n') + const result: string[] = [] + + for ( + let index = Math.max(0, line - 1 - count); + index <= Math.min(lines.length - 1, line - 1 + count); + ++index + ) { + const lineNumber = index + 1 + result.push(`${lineNumber} | ${lines[index]}`) + if (index === line - 1) { + result.push( + Array(index.toString().length).fill(' ').join('') + + ' | ' + + Array(column).fill(' ').join('') + + '^' + ) + } + } + + return result.join('\n') +} + +// const regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/; +// const regexValidFrame_FireFox = /(^|@)\S+:\d+|.+line\s+\d+\s+>\s+(eval|Function).+/; + +// const transformStack = (stack: string) => { +// return stack.split('\n') +// .filter( +// e => regexValidFrame_Chrome.test(e) || regexValidFrame_FireFox.test(e) +// ) +// .map((e) => { +// return e +// }) +// .join('\n') +// } + +const exceptionHandler = + (callback: ErrorOverlayCallback) => + async (e: ErrorEvent): Promise => { + console.log(e) + let frame: string = '' + let fileName = e.filename + let position = { column: e.colno, line: e.lineno } + let stack = e.error.stack + try { + const { + sourceMapConsumer, + source, + fileName: sourceMapFileName + } = await getSourceMapForFile(e.filename) + if (sourceMapConsumer instanceof SourceMapConsumer) { + const { line, column } = sourceMapConsumer.originalPositionFor({ + line: e.lineno, + column: e.colno + }) + position = { line, column } + if (sourceMapFileName) { + fileName = sourceMapFileName + } + } + + frame = generateFrame(position.line, position.column, source) + + // stack = transformStack(stack) + } catch (err: Error) { + // TODO handle errors that we throw + console.log(err) + // frame = err.message; + } + + const payload: ErrorPayload['err'] = { + message: e.error.message, + stack, + loc: { + column: position.column, + line: position.line, + file: fileName + }, + frame + } + + callback(payload) + } + +const showErrorOverlay = (err: ErrorPayload['err']) => { + console.log(err) + const overlay = new ErrorOverlay(err) + document.body.appendChild(overlay) +} + +// TODO: respect __HMR_ENABLE_OVERLAY__ +window.addEventListener('error', exceptionHandler(showErrorOverlay)) +// window.addEventListener('unhandledrejection', rejectionHandler(showErrorOverlay)) + export { ErrorOverlay } From f90fdf5151c9114150edc06679e7662a933c9ad0 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 00:43:37 -0500 Subject: [PATCH 02/27] fix: better sourcemap parsing --- packages/vite/src/client/client.ts | 31 +++++++++++++----------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 7f5268c4c4d339..c3a96e7f1236d1 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -521,14 +521,13 @@ const getSourceMapForFile = async ( fileName: string ): Promise<{ sourceMapConsumer?: SourceMapConsumer - source: string - fileName?: string + baseSource: string }> => { const source = await (await fetch(fileName)).text() const sourceMapData = extractSourceMapData(source) if (!sourceMapData) { - return { source } + return { baseSource: source } } // TODO: extract this string to a const @@ -538,19 +537,10 @@ const getSourceMapForFile = async ( const sourceMapConsumer = new SourceMapConsumer( JSON.parse(sm) as RawSourceMap ) - const realPath = findRealPath( - fileName, - sourceMapConsumer.sources as string[] - ) - - if (!realPath) { - return { source } - } return { + baseSource: source, sourceMapConsumer, - source: sourceMapConsumer.sourceContentFor(realPath), - fileName: realPath } } @@ -612,17 +602,20 @@ const exceptionHandler = try { const { sourceMapConsumer, - source, - fileName: sourceMapFileName + baseSource, } = await getSourceMapForFile(e.filename) + + let source = baseSource; if (sourceMapConsumer instanceof SourceMapConsumer) { - const { line, column } = sourceMapConsumer.originalPositionFor({ + const { line, column, source: sourceFileName } = sourceMapConsumer.originalPositionFor({ line: e.lineno, column: e.colno }) + + source = sourceMapConsumer.sourceContentFor(sourceFileName) position = { line, column } - if (sourceMapFileName) { - fileName = sourceMapFileName + if (sourceFileName && !fileName.includes('@vite')) { + fileName = sourceFileName } } @@ -659,4 +652,6 @@ const showErrorOverlay = (err: ErrorPayload['err']) => { window.addEventListener('error', exceptionHandler(showErrorOverlay)) // window.addEventListener('unhandledrejection', rejectionHandler(showErrorOverlay)) +setTimeout(() => {throw new Error('External Module Error')}, 2000) + export { ErrorOverlay } From 3a51bc1427694f5f268a2e9164b2d2c617dd1613 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 01:29:24 -0500 Subject: [PATCH 03/27] chore: add more testing samples --- packages/playground/runtime-error/index.html | 1 + packages/playground/runtime-error/src/entry-client.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/packages/playground/runtime-error/index.html b/packages/playground/runtime-error/index.html index e510450624386b..a6684d1b9624ec 100644 --- a/packages/playground/runtime-error/index.html +++ b/packages/playground/runtime-error/index.html @@ -8,6 +8,7 @@

Runtime Error

+ diff --git a/packages/playground/runtime-error/src/entry-client.ts b/packages/playground/runtime-error/src/entry-client.ts index fb6a9bee913b3b..4aa462456eb00f 100644 --- a/packages/playground/runtime-error/src/entry-client.ts +++ b/packages/playground/runtime-error/src/entry-client.ts @@ -1,3 +1,7 @@ document.querySelector('#throwBtn').onclick = () => { throw new Error('Why did you click the button') } + +document.querySelector('#invalidAccessor').onclick = () => { + window.doesnt.exists = 5 +} From cd55215f0f24decab187cbb6bf70642be5077c59 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 01:29:48 -0500 Subject: [PATCH 04/27] chore: dead code removal --- packages/vite/src/client/client.ts | 45 +++++++++--------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index c3a96e7f1236d1..07bebd9154827b 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -509,14 +509,6 @@ const extractSourceMapData = (fileContetnt: string) => { return match[1] } -const findRealPath = ( - fileName: string, - sources: string[] -): string | undefined => { - const filePath = new URL(fileName).pathname - return sources.find((path) => path.includes(filePath)) -} - const getSourceMapForFile = async ( fileName: string ): Promise<{ @@ -540,7 +532,7 @@ const getSourceMapForFile = async ( return { baseSource: source, - sourceMapConsumer, + sourceMapConsumer } } @@ -577,20 +569,6 @@ const generateFrame = ( return result.join('\n') } -// const regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/; -// const regexValidFrame_FireFox = /(^|@)\S+:\d+|.+line\s+\d+\s+>\s+(eval|Function).+/; - -// const transformStack = (stack: string) => { -// return stack.split('\n') -// .filter( -// e => regexValidFrame_Chrome.test(e) || regexValidFrame_FireFox.test(e) -// ) -// .map((e) => { -// return e -// }) -// .join('\n') -// } - const exceptionHandler = (callback: ErrorOverlayCallback) => async (e: ErrorEvent): Promise => { @@ -600,14 +578,17 @@ const exceptionHandler = let position = { column: e.colno, line: e.lineno } let stack = e.error.stack try { - const { - sourceMapConsumer, - baseSource, - } = await getSourceMapForFile(e.filename) + const { sourceMapConsumer, baseSource } = await getSourceMapForFile( + e.filename + ) - let source = baseSource; + let source = baseSource if (sourceMapConsumer instanceof SourceMapConsumer) { - const { line, column, source: sourceFileName } = sourceMapConsumer.originalPositionFor({ + const { + line, + column, + source: sourceFileName + } = sourceMapConsumer.originalPositionFor({ line: e.lineno, column: e.colno }) @@ -620,8 +601,6 @@ const exceptionHandler = } frame = generateFrame(position.line, position.column, source) - - // stack = transformStack(stack) } catch (err: Error) { // TODO handle errors that we throw console.log(err) @@ -652,6 +631,8 @@ const showErrorOverlay = (err: ErrorPayload['err']) => { window.addEventListener('error', exceptionHandler(showErrorOverlay)) // window.addEventListener('unhandledrejection', rejectionHandler(showErrorOverlay)) -setTimeout(() => {throw new Error('External Module Error')}, 2000) +// setTimeout(() => { +// throw new Error('External Module Error') +// }, 2000) export { ErrorOverlay } From fb6ce0d461cdf622087b09e8cb4a937fd64fd46f Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 12:31:58 -0500 Subject: [PATCH 05/27] feat: transform stack traces --- packages/vite/src/client/client.ts | 83 ++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 07bebd9154827b..f794c0de32720d 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -495,8 +495,8 @@ const extractSourceMapData = (fileContetnt: string) => { let match: RegExpExecArray | null = null // eslint-disable-next-line no-constant-condition while (true) { - let next = re.exec(fileContetnt) - if (next === null) { + const next = re.exec(fileContetnt) + if (next == null) { break } match = next @@ -569,14 +569,87 @@ const generateFrame = ( return result.join('\n') } +const RE_CHROME_STACKTRACE = + /^ {4}at (?:(.+?)\s+\()?(?:(.+?)\?+)(?:(?:.+?):(\d+)(?::(\d+))?)\)?/ + +const RE_FIREFOX_STACKTRACE = + /(?:(?:(^|.+?)@)(?:(.+?)\?+))(?:(?:.+?):(\d+)(?::(\d+))?)\)?/ + +const transformStackTrace = async (stack: string) => { + const getStackInformation = (line: string) => { + let match = RE_CHROME_STACKTRACE.exec(line) + if (match) { + return { + input: match[0], + varName: match[1], + url: match[2], + lineNo: match[3], + columnNo: match[4], + vendor: 'chrome' as const + } + } + match = RE_FIREFOX_STACKTRACE.exec(line) + if (match) { + // TODO Handle cl + return { + input: match[0], + varName: match[1], + url: match[2], + lineNo: match[3], + columnNo: match[4], + vendor: 'firefox' as const + } + } + + return { + input: line + } + } + + return ( + await Promise.all( + stack.split('\n').map(async (line) => { + const { input, varName, url, lineNo, columnNo, vendor } = + getStackInformation(line) + + if (!url) return input + + const { sourceMapConsumer } = await getSourceMapForFile(url) + + if (!(sourceMapConsumer instanceof SourceMapConsumer)) { + return input + } + + const pos = sourceMapConsumer.originalPositionFor({ + line: Number(lineNo), + column: Number(columnNo) + }) + + if (!pos.source) { + return input + } + + if (vendor === 'chrome') { + const source = `${pos.source}:${pos.line || 0}:${pos.column || 0}` + if (!varName || varName === 'eval') { + return ` at ${source}` + } else { + return ` at ${varName} (${source})` + } + } else { + return `${varName}@${pos.source}:${pos.line || 0}:${pos.column || 0}` + } + }) + ) + ).join('\n') +} + const exceptionHandler = (callback: ErrorOverlayCallback) => async (e: ErrorEvent): Promise => { - console.log(e) let frame: string = '' let fileName = e.filename let position = { column: e.colno, line: e.lineno } - let stack = e.error.stack try { const { sourceMapConsumer, baseSource } = await getSourceMapForFile( e.filename @@ -609,7 +682,7 @@ const exceptionHandler = const payload: ErrorPayload['err'] = { message: e.error.message, - stack, + stack: await transformStackTrace(e.error.stack), loc: { column: position.column, line: position.line, From 50047e79df2c90ebaf46db2629113fb525b31297 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 12:32:34 -0500 Subject: [PATCH 06/27] feat: basic unhandledrejection handler --- packages/playground/runtime-error/index.html | 6 +++++ .../runtime-error/src/entry-client.ts | 8 +++++++ packages/vite/src/client/client.ts | 24 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/playground/runtime-error/index.html b/packages/playground/runtime-error/index.html index a6684d1b9624ec..2d1fecc3c77f93 100644 --- a/packages/playground/runtime-error/index.html +++ b/packages/playground/runtime-error/index.html @@ -9,6 +9,12 @@

Runtime Error

+ + diff --git a/packages/playground/runtime-error/src/entry-client.ts b/packages/playground/runtime-error/src/entry-client.ts index 4aa462456eb00f..0e30ef681b255e 100644 --- a/packages/playground/runtime-error/src/entry-client.ts +++ b/packages/playground/runtime-error/src/entry-client.ts @@ -5,3 +5,11 @@ document.querySelector('#throwBtn').onclick = () => { document.querySelector('#invalidAccessor').onclick = () => { window.doesnt.exists = 5 } + +const asyncFunc = async () => { + throw new Error('async failure') +} + +document.querySelector('#promise').onclick = async () => { + await asyncFunc() +} diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index f794c0de32720d..f02546009d54d4 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -694,6 +694,25 @@ const exceptionHandler = callback(payload) } +const rejectionHandler = + (callback: ErrorOverlayCallback) => + async (e: PromiseRejectionEvent): Promise => { + + // TODO: get frame from stack trace + const payload: ErrorPayload['err'] = { + message: e.reason.message, + stack: await transformStackTrace(e.reason.stack) + // loc: { + // column: position.column, + // line: position.line, + // file: fileName + // }, + // frame + } + + callback(payload) + } + const showErrorOverlay = (err: ErrorPayload['err']) => { console.log(err) const overlay = new ErrorOverlay(err) @@ -702,7 +721,10 @@ const showErrorOverlay = (err: ErrorPayload['err']) => { // TODO: respect __HMR_ENABLE_OVERLAY__ window.addEventListener('error', exceptionHandler(showErrorOverlay)) -// window.addEventListener('unhandledrejection', rejectionHandler(showErrorOverlay)) +window.addEventListener( + 'unhandledrejection', + rejectionHandler(showErrorOverlay) +) // setTimeout(() => { // throw new Error('External Module Error') From b975f80d332e5941be3ac55347fee2b252a9c58a Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Dec 2021 13:06:06 -0500 Subject: [PATCH 07/27] chore: formatting --- packages/vite/src/client/client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index f02546009d54d4..456ff5f116412f 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -697,7 +697,6 @@ const exceptionHandler = const rejectionHandler = (callback: ErrorOverlayCallback) => async (e: PromiseRejectionEvent): Promise => { - // TODO: get frame from stack trace const payload: ErrorPayload['err'] = { message: e.reason.message, From a387e1cac36f9bc8a20e586c6758b1e3c34160cf Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 12:03:24 -0500 Subject: [PATCH 08/27] fix: better regex parsing --- packages/vite/src/client/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 456ff5f116412f..2912fe601f85d3 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -570,10 +570,10 @@ const generateFrame = ( } const RE_CHROME_STACKTRACE = - /^ {4}at (?:(.+?)\s+\()?(?:(.+?)\?+)(?:(?:.+?):(\d+)(?::(\d+))?)\)?/ + /^ {4}at (?:(.+?)\s+)?\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/ const RE_FIREFOX_STACKTRACE = - /(?:(?:(^|.+?)@)(?:(.+?)\?+))(?:(?:.+?):(\d+)(?::(\d+))?)\)?/ + /^(?:(?:(^|.+?)@))\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/ const transformStackTrace = async (stack: string) => { const getStackInformation = (line: string) => { From 678e11fa36eb239caabb718706ba6890dccfb3f5 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 12:05:06 -0500 Subject: [PATCH 09/27] fix: consistant lineNo whitespace --- packages/vite/src/client/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 2912fe601f85d3..e6014c7e10177c 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -555,7 +555,7 @@ const generateFrame = ( ++index ) { const lineNumber = index + 1 - result.push(`${lineNumber} | ${lines[index]}`) + result.push(`${lineNumber.toString().padEnd(3)}| ${lines[index]}`) if (index === line - 1) { result.push( Array(index.toString().length).fill(' ').join('') + From a31eec77255db42b22444f9e3a48b669c5964fb4 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 12:05:47 -0500 Subject: [PATCH 10/27] feat: support unhandledRejection stack frames --- packages/vite/src/client/client.ts | 116 ++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index e6014c7e10177c..1a38d7aea84b26 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -575,37 +575,37 @@ const RE_CHROME_STACKTRACE = const RE_FIREFOX_STACKTRACE = /^(?:(?:(^|.+?)@))\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/ -const transformStackTrace = async (stack: string) => { - const getStackInformation = (line: string) => { - let match = RE_CHROME_STACKTRACE.exec(line) - if (match) { - return { - input: match[0], - varName: match[1], - url: match[2], - lineNo: match[3], - columnNo: match[4], - vendor: 'chrome' as const - } - } - match = RE_FIREFOX_STACKTRACE.exec(line) - if (match) { - // TODO Handle cl - return { - input: match[0], - varName: match[1], - url: match[2], - lineNo: match[3], - columnNo: match[4], - vendor: 'firefox' as const - } +const getStackInformation = (line: string) => { + let match = RE_CHROME_STACKTRACE.exec(line) + if (match) { + return { + input: match[0], + varName: match[1], + url: match[2], + lineNo: match[3], + columnNo: match[4], + vendor: 'chrome' as const } - + } + match = RE_FIREFOX_STACKTRACE.exec(line) + if (match) { + // TODO Handle cl return { - input: line + input: match[0], + varName: match[1], + url: match[2], + lineNo: match[3], + columnNo: match[4], + vendor: 'firefox' as const } } + return { + input: line + } +} + +const transformStackTrace = async (stack: string) => { return ( await Promise.all( stack.split('\n').map(async (line) => { @@ -697,16 +697,64 @@ const exceptionHandler = const rejectionHandler = (callback: ErrorOverlayCallback) => async (e: PromiseRejectionEvent): Promise => { - // TODO: get frame from stack trace + // Since promisejectection doesn't return the file we have to get it from the stack trace + const stackLines = e.reason.stack.split('\n') + let stackInfo = getStackInformation(stackLines[0]) + // Chromes will include the erroe message as the first line of the stack trace so we have to check for a match or check the next line + if (!stackInfo.url) { + stackInfo = getStackInformation(stackLines[1]) + console.log(stackInfo); + if (!stackInfo.url) { + console.error('failed to get source info for rejection') + return + } + } + + let fileName = stackInfo.url + let position = { + line: Number(stackInfo.lineNo), + column: Number(stackInfo.columnNo) + } + let frame = undefined + try { + const { sourceMapConsumer, baseSource } = await getSourceMapForFile( + fileName + ) + + let source = baseSource + if (sourceMapConsumer instanceof SourceMapConsumer) { + const { + line, + column, + source: sourceFileName + } = sourceMapConsumer.originalPositionFor({ + line: position.line, + column: position.column + }) + + source = sourceMapConsumer.sourceContentFor(sourceFileName) + position = { line, column } + if (sourceFileName && !fileName.includes('@vite')) { + fileName = sourceFileName + } + } + + frame = generateFrame(position.line, position.column, source) + } catch (err: Error) { + // TODO handle errors that we throw + console.log(err) + // frame = err.message; + } + const payload: ErrorPayload['err'] = { message: e.reason.message, - stack: await transformStackTrace(e.reason.stack) - // loc: { - // column: position.column, - // line: position.line, - // file: fileName - // }, - // frame + stack: await transformStackTrace(e.reason.stack), + loc: { + column: position.column, + line: position.line, + file: fileName + }, + frame } callback(payload) From 2cf71fc3c636eb20dc6936162c223f3381bfb715 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 13:42:01 -0500 Subject: [PATCH 11/27] fix: bundle source-map for client --- packages/vite/rollup.config.js | 4 +++- pnpm-lock.yaml | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/vite/rollup.config.js b/packages/vite/rollup.config.js index f8584336121517..0dfdfb80063694 100644 --- a/packages/vite/rollup.config.js +++ b/packages/vite/rollup.config.js @@ -40,6 +40,7 @@ const clientConfig = { input: path.resolve(__dirname, 'src/client/client.ts'), external: ['./env', '@vite/env'], plugins: [ + nodeResolve(), typescript({ target: 'es2018', include: ['src/client/**/*.ts'], @@ -47,7 +48,8 @@ const clientConfig = { paths: { 'types/*': ['../../types/*'] } - }) + }), + commonjs({ extensions: ['.js', '.ts'], sourceMap: true }) ], output: { file: path.resolve(__dirname, 'dist/client', 'client.mjs'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb552d8aabaddb..603ab522889d0c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -448,6 +448,9 @@ importers: packages/playground/resolve/inline-package: specifiers: {} + packages/playground/runtime-error: + specifiers: {} + packages/playground/ssr-deps: specifiers: bcrypt: ^5.0.1 @@ -713,6 +716,12 @@ importers: '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.16.5 hash-sum: 2.0.0 + packages/temp: + specifiers: + css-color-names: ^1.0.1 + devDependencies: + css-color-names: 1.0.1 + packages/vite: specifiers: '@ampproject/remapping': ^1.0.2 From 4a1c910f16735dffadc0542a9d0cbdef013f94f0 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 14:22:39 -0500 Subject: [PATCH 12/27] fix: remove development console.log --- packages/vite/src/client/client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 1a38d7aea84b26..e03381332ab0ea 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -703,7 +703,6 @@ const rejectionHandler = // Chromes will include the erroe message as the first line of the stack trace so we have to check for a match or check the next line if (!stackInfo.url) { stackInfo = getStackInformation(stackLines[1]) - console.log(stackInfo); if (!stackInfo.url) { console.error('failed to get source info for rejection') return From c2b086efcf7f74a65319de00575b06dcef3706cb Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 16:15:48 -0500 Subject: [PATCH 13/27] fix(overlay): support non-url stack traces --- packages/vite/src/client/overlay.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vite/src/client/overlay.ts b/packages/vite/src/client/overlay.ts index 150c570fbc8aaf..65ac9f1756bc6a 100644 --- a/packages/vite/src/client/overlay.ts +++ b/packages/vite/src/client/overlay.ts @@ -158,6 +158,12 @@ export class ErrorOverlay extends HTMLElement { if (!linkFiles) { el.textContent = text } else { + if(!fileRE.test(text)){ + el.textContent = text + return; + } else { + fileRE.lastIndex = 0 + } let curIndex = 0 let match: RegExpExecArray | null while ((match = fileRE.exec(text))) { From b8e11b31aa4b7a5a5ebdaa225359a3860644e097 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Wed, 29 Dec 2021 16:17:00 -0500 Subject: [PATCH 14/27] feat: refactor & support non-error errors --- packages/playground/runtime-error/index.html | 2 + .../runtime-error/src/entry-client.ts | 9 ++ packages/vite/src/client/client.ts | 140 +++++++----------- 3 files changed, 65 insertions(+), 86 deletions(-) diff --git a/packages/playground/runtime-error/index.html b/packages/playground/runtime-error/index.html index 2d1fecc3c77f93..d3abb0cb5fd93a 100644 --- a/packages/playground/runtime-error/index.html +++ b/packages/playground/runtime-error/index.html @@ -10,6 +10,8 @@

Runtime Error

+ +