UNPKG

imba

Version:

Intuitive and powerful language for building webapps that fly

128 lines (121 loc) 4.27 kB
import { RollupError } from 'rollup'; import { ResolvedOptions, Warning } from './options'; import { buildExtendedLogMessage } from './log'; import { PartialMessage } from 'esbuild'; const defaultSource = "Compilation Error" /** * convert an error thrown by imba.compile to a RollupError so that vite displays it in a user friendly way * @param error a imba compiler error, which is a mix of Warning and an error * @returns {RollupError} the converted error */ export function toRollupError(error: Warning & Error): RollupError { if(!error?.errors?.length){ return { name: "Compilation error", id: error._sourcePath, message: buildExtendedLogMessage({message: `Compilation error: ${error.message}`}), } as RollupError; } const actualError = error?.errors?.length ? error.errors[0] : { diagnostics: [{source: defaultSource}], options: {filename: error._sourcePath}, range: {start: {line: 1, column: 1} }, message: error?.toString!, } const source = actualError.source || actualError?.diagnostics[0]?.source || defaultSource const name = `${source} error` const id = error.options.filename const {line, character: column} = actualError.range.start // include filename:line:column so that it's clickable const loc = { line: line + 1, column, file: id } const message = buildExtendedLogMessage({message: actualError.message}) const code = error.sourceCode + "\n" const frame = formatFrameForVite(code, loc) // the format this object is RollupError and vite's ErrorPayload.err // https://github.com/rollup/rollup/blob/master/src/rollup/types.d.ts#L12 // https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/types/hmrPayload.d.ts#L41C18-L41C30 return { name, message, id, frame, code, loc } as RollupError; } /** * convert an error thrown by imba.compile to an esbuild PartialMessage * @param error a imba compiler error, which is a mix of Warning and an error * @returns {PartialMessage} the converted error */ export function toESBuildError(error: Warning & Error, options: ResolvedOptions): PartialMessage { const { filename, frame, start, stack } = error; const partialMessage: PartialMessage = { text: buildExtendedLogMessage(error) }; if (start) { partialMessage.location = { line: start.line, column: start.column, file: filename, lineText: lineFromFrame(start.line, frame) // needed to get a meaningful error message on cli }; } if (options.isBuild || options.isDebug || !frame) { partialMessage.detail = stack; } return partialMessage; } /** * extract line with number from codeframe */ function lineFromFrame(lineNo: number, frame?: string): string { if (!frame) { return ''; } const lines = frame.split('\n'); const errorLine = lines.find((line) => line.trimStart().startsWith(`${lineNo}: `)); return errorLine ? errorLine.substring(errorLine.indexOf(': ') + 3) : ''; } /** * vite error overlay expects a specific format to show frames * this reformats imba frame (colon separated, less whitespace) * to one that vite displays on overlay ( pipe separated, more whitespace) * e.g. * ``` * 1: foo * 2: bar; * ^ * 3: baz * ``` * to * ``` * 1 | foo * 2 | bar; * ^ * 3 | baz * ``` * @see https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/src/client/overlay.ts#L116 */ function formatFrameForVite(code:string, start): string { if (!code) { return ''; } let frame = code.split("\n").slice(start.line - 2, start.line + 2) // TODO maybe: remove a max number of tabs in the begining // const prefix = frame.reduce((acc, line, i)=>{ // const lineTabs = (line.match(/^\t*/g) || [''])[0] // if(i == 0 || acc.length > lineTabs.length){ // return lineTabs // } // return acc // }, "") // let f = frame.map((line, i) => `${start.line + i + 1} |${line.replace(prefix2, "").replace(/\t/g, ' ')}`); // tabs are displayed differently using tab-size css property // it's better to replace tabs with spaces before displaying in browser let f = frame.map((line, i) => `${start.line + i - 2 + 1} |${line.replace(/\t/g, ' ')}`); f.splice(2, 0, `${" ".repeat(`${start.line}`.length)} |${" ".repeat(start.column)}^`); return f.join("\n"); }