fix: make compiler error extend from `Error` (#14036)

We originally didn't extend from `Error` anymore because its fields are of no real value to us, and has problems with serialization in a worker context.

Turns out this was a mistake, because various build tools rely on errors being thrown as something that extends Error, else they try to wrap it in their own error.

We therefore revert that change while still trying to preserve most of the advantages of not extending `Error`, namely nuking the useless stack trace and making sure the message is enumerable.
pull/14040/head
Simon H 6 days ago committed by GitHub
parent f52a30347f
commit 1434f48f7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: make compiler error extend from `Error`

@ -2,8 +2,9 @@ import { CompileDiagnostic } from './utils/compile_diagnostic.js';
/** @typedef {{ start?: number, end?: number }} NodeLike */
class InternalCompileError extends CompileDiagnostic {
name = 'CompileError';
class InternalCompileError extends Error {
message = ''; // ensure this property is enumerable
#diagnostic;
/**
* @param {string} code
@ -11,7 +12,23 @@ class InternalCompileError extends CompileDiagnostic {
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(code, message, position);
super(message);
this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable
// We want to extend from Error so that various bundler plugins properly handle it.
// But we also want to share the same object shape with that of warnings, therefore
// we create an instance of the shared class an copy over its properties.
this.#diagnostic = new CompileDiagnostic(code, message, position);
Object.assign(this, this.#diagnostic);
this.name = 'CompileError';
}
toString() {
return this.#diagnostic.toString();
}
toJSON() {
return this.#diagnostic.toJSON();
}
}

@ -3,8 +3,9 @@
import { CompileDiagnostic } from './utils/compile_diagnostic.js';
/** @typedef {{ start?: number, end?: number }} NodeLike */
class InternalCompileError extends CompileDiagnostic {
name = 'CompileError';
class InternalCompileError extends Error {
message = ''; // ensure this property is enumerable
#diagnostic;
/**
* @param {string} code
@ -12,7 +13,22 @@ class InternalCompileError extends CompileDiagnostic {
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(code, message, position);
super(message);
this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable
// We want to extend from Error so that various bundler plugins properly handle it.
// But we also want to share the same object shape with that of warnings, therefore
// we create an instance of the shared class an copy over its properties.
this.#diagnostic = new CompileDiagnostic(code, message, position);
Object.assign(this, this.#diagnostic);
this.name = 'CompileError';
}
toString() {
return this.#diagnostic.toString();
}
toJSON() {
return this.#diagnostic.toJSON();
}
}

@ -51,8 +51,6 @@ function get_code_frame(source, line, column) {
/** @implements {ICompileDiagnostic} */
export class CompileDiagnostic {
name = 'CompileDiagnostic';
// adding an empty stack so that vite will show the file and frame during build
stack = '';
/**
* @param {string} code

@ -19,8 +19,6 @@ export default test({
3: unterminated template
^`,
message: 'Unexpected end of input',
name: 'CompileError',
stack: '',
position: [30, 30],
start: {
character: 30,

@ -28,7 +28,7 @@ const { test, run } = suite<ParserTest>(async (config, cwd) => {
if (config.errors) {
console.error = (...args) => {
errors.push(...args);
errors.push(...args.map((arg) => arg.toJSON?.() ?? arg));
};
}

Loading…
Cancel
Save