Skip to content

Commit

Permalink
fix: make compiler error extend from Error (#14036)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dummdidumm authored Oct 30, 2024
1 parent f52a303 commit 1434f48
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-timers-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: make compiler error extend from `Error`
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,33 @@ 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
* @param {string} message
* @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();
}
}

Expand Down
22 changes: 19 additions & 3 deletions packages/svelte/src/compiler/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,32 @@
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
* @param {string} message
* @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();
}
}

Expand Down
2 changes: 0 additions & 2 deletions packages/svelte/src/compiler/utils/compile_diagnostic.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ export default test({
3: unterminated template
^`,
message: 'Unexpected end of input',
name: 'CompileError',
stack: '',
position: [30, 30],
start: {
character: 30,
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/tests/migrate/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
};
}

Expand Down

0 comments on commit 1434f48

Please sign in to comment.