Skip to content

feat: improve compile error reporting #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eleven-frogs-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/vite-plugin-svelte': minor
---

Improved error reporting for svelte compiler errors
8 changes: 7 additions & 1 deletion packages/vite-plugin-svelte/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ensureWatchedFile, setupWatchers } from './utils/watch';
import { resolveViaPackageJsonSvelte } from './utils/resolve';
import { addExtraPreprocessors } from './utils/preprocess';
import { PartialResolvedId } from 'rollup';
import { toRollupError } from './utils/error';

export function svelte(inlineOptions?: Partial<Options>): Plugin {
if (process.env.DEBUG != null) {
Expand Down Expand Up @@ -169,7 +170,12 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin {
log.error('failed to transform tagged svelte request', svelteRequest);
throw new Error(`failed to transform tagged svelte request for id ${id}`);
}
const compileData = await compileSvelte(svelteRequest, code, options);
let compileData;
try {
compileData = await compileSvelte(svelteRequest, code, options);
} catch (e) {
throw toRollupError(e);
}
logCompilerWarnings(compileData.compiled.warnings, options);
cache.update(compileData);
if (compileData.dependencies?.length && options.server) {
Expand Down
92 changes: 92 additions & 0 deletions packages/vite-plugin-svelte/src/utils/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { RollupError } from 'rollup';
import { Warning } from './options';
import { buildExtendedLogMessage } from './log';
import { PartialMessage } from 'esbuild';

/**
* convert an error thrown by svelte.compile to a RollupError so that vite displays it in a user friendly way
* @param error a svelte compiler error, which is a mix of Warning and an error
* @returns {RollupError} the converted error
*/
export function toRollupError(error: Warning & Error): RollupError {
const { filename, frame, start, code, name } = error;
const rollupError: RollupError = {
name, // needed otherwise sveltekit coalesce_to_error turns it into a string
id: filename,
message: buildExtendedLogMessage(error), // include filename:line:column so that it's clickable
frame: formatFrameForVite(frame),
code,
stack: ''
};
if (start) {
rollupError.loc = {
line: start.line,
column: start.column,
file: filename
};
}
return rollupError;
}

/**
* convert an error thrown by svelte.compile to an esbuild PartialMessage
* @param error a svelte compiler error, which is a mix of Warning and an error
* @returns {PartialMessage} the converted error
*/
export function toESBuildError(error: Warning & Error): PartialMessage {
const { filename, frame, start } = 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
};
}
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 svelte 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(frame?: string): string {
if (!frame) {
return '';
}
return frame
.split('\n')
.map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
.join('\n');
}
9 changes: 7 additions & 2 deletions packages/vite-plugin-svelte/src/utils/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DepOptimizationOptions } from 'vite';
import { Compiled } from './compile';
import { log } from './log';
import { CompileOptions, ResolvedOptions } from './options';
import { toESBuildError } from './error';

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

build.onLoad({ filter: svelteFilter }, async ({ path: filename }) => {
const code = await fs.readFile(filename, 'utf8');
const contents = await compileSvelte(options, { filename, code });
return { contents };
try {
const contents = await compileSvelte(options, { filename, code });
return { contents };
} catch (e) {
return { errors: [toESBuildError(e)] };
}
});
}
};
Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin-svelte/src/utils/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ function warnBuild(w: Warning) {
log.warn.enabled && log.warn(buildExtendedLogMessage(w), w.frame);
}

function buildExtendedLogMessage(w: Warning) {
export function buildExtendedLogMessage(w: Warning) {
const parts = [];
if (w.filename) {
parts.push(w.filename);
Expand Down