Skip to content

Commit 1434f48

Browse files
authored
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.
1 parent f52a303 commit 1434f48

File tree

6 files changed

+45
-11
lines changed

6 files changed

+45
-11
lines changed

.changeset/wet-timers-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: make compiler error extend from `Error`

packages/svelte/scripts/process-messages/templates/compile-errors.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,33 @@ import { CompileDiagnostic } from './utils/compile_diagnostic.js';
22

33
/** @typedef {{ start?: number, end?: number }} NodeLike */
44

5-
class InternalCompileError extends CompileDiagnostic {
6-
name = 'CompileError';
5+
class InternalCompileError extends Error {
6+
message = ''; // ensure this property is enumerable
7+
#diagnostic;
78

89
/**
910
* @param {string} code
1011
* @param {string} message
1112
* @param {[number, number] | undefined} position
1213
*/
1314
constructor(code, message, position) {
14-
super(code, message, position);
15+
super(message);
16+
this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable
17+
18+
// We want to extend from Error so that various bundler plugins properly handle it.
19+
// But we also want to share the same object shape with that of warnings, therefore
20+
// we create an instance of the shared class an copy over its properties.
21+
this.#diagnostic = new CompileDiagnostic(code, message, position);
22+
Object.assign(this, this.#diagnostic);
23+
this.name = 'CompileError';
24+
}
25+
26+
toString() {
27+
return this.#diagnostic.toString();
28+
}
29+
30+
toJSON() {
31+
return this.#diagnostic.toJSON();
1532
}
1633
}
1734

packages/svelte/src/compiler/errors.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,32 @@
33
import { CompileDiagnostic } from './utils/compile_diagnostic.js';
44

55
/** @typedef {{ start?: number, end?: number }} NodeLike */
6-
class InternalCompileError extends CompileDiagnostic {
7-
name = 'CompileError';
6+
class InternalCompileError extends Error {
7+
message = ''; // ensure this property is enumerable
8+
#diagnostic;
89

910
/**
1011
* @param {string} code
1112
* @param {string} message
1213
* @param {[number, number] | undefined} position
1314
*/
1415
constructor(code, message, position) {
15-
super(code, message, position);
16+
super(message);
17+
this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable
18+
// We want to extend from Error so that various bundler plugins properly handle it.
19+
// But we also want to share the same object shape with that of warnings, therefore
20+
// we create an instance of the shared class an copy over its properties.
21+
this.#diagnostic = new CompileDiagnostic(code, message, position);
22+
Object.assign(this, this.#diagnostic);
23+
this.name = 'CompileError';
24+
}
25+
26+
toString() {
27+
return this.#diagnostic.toString();
28+
}
29+
30+
toJSON() {
31+
return this.#diagnostic.toJSON();
1632
}
1733
}
1834

packages/svelte/src/compiler/utils/compile_diagnostic.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ function get_code_frame(source, line, column) {
5151
/** @implements {ICompileDiagnostic} */
5252
export class CompileDiagnostic {
5353
name = 'CompileDiagnostic';
54-
// adding an empty stack so that vite will show the file and frame during build
55-
stack = '';
5654

5755
/**
5856
* @param {string} code

packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export default test({
1919
3: unterminated template
2020
^`,
2121
message: 'Unexpected end of input',
22-
name: 'CompileError',
23-
stack: '',
2422
position: [30, 30],
2523
start: {
2624
character: 30,

packages/svelte/tests/migrate/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const { test, run } = suite<ParserTest>(async (config, cwd) => {
2828

2929
if (config.errors) {
3030
console.error = (...args) => {
31-
errors.push(...args);
31+
errors.push(...args.map((arg) => arg.toJSON?.() ?? arg));
3232
};
3333
}
3434

0 commit comments

Comments
 (0)