Skip to content

Commit df7c570

Browse files
dominikgbluwy
andauthored
feat: improve compile error reporting (#220)
* feat: improve compile error reporting * feat: improve error handling in esbuild dependency optimization * fix: use lineText instead of suggestion/detail so that esbuild produces a more compact/readable output on error * fix: reformat code frame so that it is displayed in vite error overlay * chore: add changeset * Apply suggestions from code review Co-authored-by: Bjorn Lu <[email protected]> Co-authored-by: Bjorn Lu <[email protected]>
1 parent aadb92d commit df7c570

File tree

5 files changed

+112
-4
lines changed

5 files changed

+112
-4
lines changed

.changeset/eleven-frogs-rhyme.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': minor
3+
---
4+
5+
Improved error reporting for svelte compiler errors

packages/vite-plugin-svelte/src/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ensureWatchedFile, setupWatchers } from './utils/watch';
1717
import { resolveViaPackageJsonSvelte } from './utils/resolve';
1818
import { addExtraPreprocessors } from './utils/preprocess';
1919
import { PartialResolvedId } from 'rollup';
20+
import { toRollupError } from './utils/error';
2021

2122
export function svelte(inlineOptions?: Partial<Options>): Plugin {
2223
if (process.env.DEBUG != null) {
@@ -169,7 +170,12 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin {
169170
log.error('failed to transform tagged svelte request', svelteRequest);
170171
throw new Error(`failed to transform tagged svelte request for id ${id}`);
171172
}
172-
const compileData = await compileSvelte(svelteRequest, code, options);
173+
let compileData;
174+
try {
175+
compileData = await compileSvelte(svelteRequest, code, options);
176+
} catch (e) {
177+
throw toRollupError(e);
178+
}
173179
logCompilerWarnings(compileData.compiled.warnings, options);
174180
cache.update(compileData);
175181
if (compileData.dependencies?.length && options.server) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { RollupError } from 'rollup';
2+
import { Warning } from './options';
3+
import { buildExtendedLogMessage } from './log';
4+
import { PartialMessage } from 'esbuild';
5+
6+
/**
7+
* convert an error thrown by svelte.compile to a RollupError so that vite displays it in a user friendly way
8+
* @param error a svelte compiler error, which is a mix of Warning and an error
9+
* @returns {RollupError} the converted error
10+
*/
11+
export function toRollupError(error: Warning & Error): RollupError {
12+
const { filename, frame, start, code, name } = error;
13+
const rollupError: RollupError = {
14+
name, // needed otherwise sveltekit coalesce_to_error turns it into a string
15+
id: filename,
16+
message: buildExtendedLogMessage(error), // include filename:line:column so that it's clickable
17+
frame: formatFrameForVite(frame),
18+
code,
19+
stack: ''
20+
};
21+
if (start) {
22+
rollupError.loc = {
23+
line: start.line,
24+
column: start.column,
25+
file: filename
26+
};
27+
}
28+
return rollupError;
29+
}
30+
31+
/**
32+
* convert an error thrown by svelte.compile to an esbuild PartialMessage
33+
* @param error a svelte compiler error, which is a mix of Warning and an error
34+
* @returns {PartialMessage} the converted error
35+
*/
36+
export function toESBuildError(error: Warning & Error): PartialMessage {
37+
const { filename, frame, start } = error;
38+
const partialMessage: PartialMessage = {
39+
text: buildExtendedLogMessage(error)
40+
};
41+
if (start) {
42+
partialMessage.location = {
43+
line: start.line,
44+
column: start.column,
45+
file: filename,
46+
lineText: lineFromFrame(start.line, frame) // needed to get a meaningful error message on cli
47+
};
48+
}
49+
return partialMessage;
50+
}
51+
52+
/**
53+
* extract line with number from codeframe
54+
*/
55+
function lineFromFrame(lineNo: number, frame?: string): string {
56+
if (!frame) {
57+
return '';
58+
}
59+
const lines = frame.split('\n');
60+
const errorLine = lines.find((line) => line.trimStart().startsWith(`${lineNo}: `));
61+
return errorLine ? errorLine.substring(errorLine.indexOf(': ') + 3) : '';
62+
}
63+
64+
/**
65+
* vite error overlay expects a specific format to show frames
66+
* this reformats svelte frame (colon separated, less whitespace)
67+
* to one that vite displays on overlay ( pipe separated, more whitespace)
68+
* e.g.
69+
* ```
70+
* 1: foo
71+
* 2: bar;
72+
* ^
73+
* 3: baz
74+
* ```
75+
* to
76+
* ```
77+
* 1 | foo
78+
* 2 | bar;
79+
* ^
80+
* 3 | baz
81+
* ```
82+
* @see https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/src/client/overlay.ts#L116
83+
*/
84+
function formatFrameForVite(frame?: string): string {
85+
if (!frame) {
86+
return '';
87+
}
88+
return frame
89+
.split('\n')
90+
.map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
91+
.join('\n');
92+
}

packages/vite-plugin-svelte/src/utils/esbuild.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DepOptimizationOptions } from 'vite';
44
import { Compiled } from './compile';
55
import { log } from './log';
66
import { CompileOptions, ResolvedOptions } from './options';
7+
import { toESBuildError } from './error';
78

89
type EsbuildOptions = NonNullable<DepOptimizationOptions['esbuildOptions']>;
910
type EsbuildPlugin = NonNullable<EsbuildOptions['plugins']>[number];
@@ -20,8 +21,12 @@ export function esbuildSveltePlugin(options: ResolvedOptions): EsbuildPlugin {
2021

2122
build.onLoad({ filter: svelteFilter }, async ({ path: filename }) => {
2223
const code = await fs.readFile(filename, 'utf8');
23-
const contents = await compileSvelte(options, { filename, code });
24-
return { contents };
24+
try {
25+
const contents = await compileSvelte(options, { filename, code });
26+
return { contents };
27+
} catch (e) {
28+
return { errors: [toESBuildError(e)] };
29+
}
2530
});
2631
}
2732
};

packages/vite-plugin-svelte/src/utils/log.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function warnBuild(w: Warning) {
155155
log.warn.enabled && log.warn(buildExtendedLogMessage(w), w.frame);
156156
}
157157

158-
function buildExtendedLogMessage(w: Warning) {
158+
export function buildExtendedLogMessage(w: Warning) {
159159
const parts = [];
160160
if (w.filename) {
161161
parts.push(w.filename);

0 commit comments

Comments
 (0)