Skip to content

Commit ae3bf9e

Browse files
committed
chore: align warning and error objects, add frame property
This aligns warning and error objects to contain the same properties and have their toString methods return the same shape. It also adds back the `frame` property that got lost in the Svelte 4->5 transition. The only difference to Svelte 4 now is a slightly adjusted toString property (which is consistent between warnings and errors now) and a `position` property that contains a tuple of start/end offsets instead of a `pos` property only containing the start offset closes #12151
1 parent 5eff68f commit ae3bf9e

File tree

11 files changed

+121
-44
lines changed

11 files changed

+121
-44
lines changed

.changeset/small-owls-remain.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+
chore: align warning and error objects, add frame property

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

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,43 @@
11
/** @import { Location } from 'locate-character' */
22
import * as state from './state.js';
3+
import { get_code_frame } from './utils/get_code_frame.js';
4+
import { error_to_string } from './utils/error_to_string.js';
35

46
/** @typedef {{ start?: number, end?: number }} NodeLike */
57

68
export class InternalCompileError extends Error {
79
name = 'CompileError';
8-
910
filename = state.filename;
10-
1111
/** @type {[number, number] | undefined} */
1212
position = undefined;
13-
1413
/** @type {Location | undefined} */
1514
start = undefined;
16-
1715
/** @type {Location | undefined} */
1816
end = undefined;
17+
/** @type {string | undefined} */
18+
frame = undefined;
1919

2020
/**
21-
*
2221
* @param {string} code
2322
* @param {string} message
2423
* @param {[number, number] | undefined} position
2524
*/
2625
constructor(code, message, position) {
2726
super(message);
28-
2927
this.code = code;
3028
this.position = position;
3129

3230
if (position) {
3331
this.start = state.locator(position[0]);
3432
this.end = state.locator(position[1]);
33+
if (this.start && this.end) {
34+
this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
35+
}
3536
}
3637
}
3738

3839
toString() {
39-
let out = `${this.name}: ${this.message}`;
40-
41-
out += `\n(${this.code})`;
42-
43-
if (this.filename) {
44-
out += `\n${this.filename}`;
45-
46-
if (this.start) {
47-
out += `${this.start.line}:${this.start.column}`;
48-
}
49-
}
50-
51-
return out;
40+
return error_to_string(this.code, this.message, this.filename, this.start, this.frame);
5241
}
5342
}
5443

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { filename, locator, warnings, ignore_stack, ignore_map } from './state.js';
1+
import { filename, locator, warnings, ignore_stack, ignore_map, source } from './state.js';
2+
import { get_code_frame } from './utils/get_code_frame.js';
3+
import { error_to_string } from './utils/error_to_string.js';
24

35
/** @typedef {{ start?: number, end?: number }} NodeLike */
46

@@ -14,12 +16,19 @@ function w(node, code, message) {
1416
}
1517
if (stack && stack.at(-1)?.has(code)) return;
1618

19+
const start = node?.start !== undefined ? locator(node.start) : undefined;
20+
const frame = start && get_code_frame(source, start.line - 1, start.column);
21+
1722
warnings.push({
1823
code,
1924
message,
2025
filename,
21-
start: node?.start !== undefined ? locator(node.start) : undefined,
22-
end: node?.end !== undefined ? locator(node.end) : undefined
26+
position:
27+
node?.start !== undefined && node?.end !== undefined ? [node.start, node.end] : undefined,
28+
start,
29+
end: node?.end !== undefined ? locator(node.end) : undefined,
30+
frame,
31+
toString: () => error_to_string(code, message, filename, start, frame)
2332
});
2433
}
2534

packages/svelte/src/compiler/errors.js

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
/** @import { Location } from 'locate-character' */
44
import * as state from './state.js';
5+
import { get_code_frame } from './utils/get_code_frame.js';
6+
import { error_to_string } from './utils/error_to_string.js';
57

68
/** @typedef {{ start?: number, end?: number }} NodeLike */
79
export class InternalCompileError extends Error {
@@ -13,9 +15,10 @@ export class InternalCompileError extends Error {
1315
start = undefined;
1416
/** @type {Location | undefined} */
1517
end = undefined;
18+
/** @type {string | undefined} */
19+
frame = undefined;
1620

1721
/**
18-
*
1922
* @param {string} code
2023
* @param {string} message
2124
* @param {[number, number] | undefined} position
@@ -28,23 +31,15 @@ export class InternalCompileError extends Error {
2831
if (position) {
2932
this.start = state.locator(position[0]);
3033
this.end = state.locator(position[1]);
31-
}
32-
}
33-
34-
toString() {
35-
let out = `${this.name}: ${this.message}`;
3634

37-
out += `\n(${this.code})`;
38-
39-
if (this.filename) {
40-
out += `\n${this.filename}`;
41-
42-
if (this.start) {
43-
out += `${this.start.line}:${this.start.column}`;
35+
if (this.start && this.end) {
36+
this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
4437
}
4538
}
39+
}
4640

47-
return out;
41+
toString() {
42+
return error_to_string(this.code, this.message, this.filename, this.start, this.frame);
4843
}
4944
}
5045

packages/svelte/src/compiler/state.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ export let warnings = [];
1414
*/
1515
export let filename;
1616

17+
/**
18+
* The original source code
19+
* @type {string}
20+
*/
21+
export let source;
22+
1723
export let locator = getLocator('', { offsetLine: 1 });
1824

1925
/**
@@ -43,10 +49,11 @@ export function pop_ignore() {
4349
}
4450

4551
/**
46-
* @param {string} source
52+
* @param {string} _source
4753
* @param {{ filename?: string, rootDir?: string }} options
4854
*/
49-
export function reset(source, options) {
55+
export function reset(_source, options) {
56+
source = _source;
5057
const root_dir = options.rootDir?.replace(/\\/g, '/');
5158
filename = options.filename?.replace(/\\/g, '/');
5259

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ export interface CompileResult {
5252
}
5353

5454
export interface Warning {
55-
start?: Location;
56-
end?: Location;
57-
// TODO there was pos: number in Svelte 4 - do we want to add it back?
5855
code: string;
5956
message: string;
6057
filename?: string;
58+
start?: Location;
59+
end?: Location;
60+
position?: [number, number];
61+
frame?: string;
6162
}
6263

6364
export interface CompileError extends InternalCompileError {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @param {string} code
3+
* @param {string} message
4+
* @param {string | undefined} filename
5+
* @param {import("locate-character").Location | undefined} start
6+
* @param {string | undefined} frame
7+
*/
8+
export function error_to_string(code, message, filename, start, frame) {
9+
let out = `${code}: ${message}`;
10+
11+
if (filename) {
12+
out += `\n${filename}`;
13+
14+
if (start) {
15+
out += `:${start.line}:${start.column}`;
16+
}
17+
}
18+
19+
if (frame) {
20+
out += `\n${frame}`;
21+
}
22+
23+
return out;
24+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const regex_tabs = /^\t+/;
2+
3+
/**
4+
* @param {string} str
5+
*/
6+
function tabs_to_spaces(str) {
7+
return str.replace(regex_tabs, (match) => match.split('\t').join(' '));
8+
}
9+
10+
/**
11+
* @param {string} source
12+
* @param {number} line
13+
* @param {number} column
14+
*/
15+
export function get_code_frame(source, line, column) {
16+
const lines = source.split('\n');
17+
const frame_start = Math.max(0, line - 2);
18+
const frame_end = Math.min(line + 3, lines.length);
19+
const digits = String(frame_end + 1).length;
20+
return lines
21+
.slice(frame_start, frame_end)
22+
.map((str, i) => {
23+
const is_error_line = frame_start + i === line;
24+
const line_num = String(i + frame_start + 1).padStart(digits, ' ');
25+
if (is_error_line) {
26+
const indicator =
27+
' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
28+
return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
29+
}
30+
return `${line_num}: ${tabs_to_spaces(str)}`;
31+
})
32+
.join('\n');
33+
}

packages/svelte/src/compiler/warnings.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import {
55
locator,
66
warnings,
77
ignore_stack,
8-
ignore_map
8+
ignore_map,
9+
source
910
} from './state.js';
1011

12+
import { get_code_frame } from './utils/get_code_frame.js';
13+
import { error_to_string } from './utils/error_to_string.js';
14+
1115
/** @typedef {{ start?: number, end?: number }} NodeLike */
1216
/**
1317
* @param {null | NodeLike} node
@@ -23,12 +27,18 @@ function w(node, code, message) {
2327

2428
if (stack && stack.at(-1)?.has(code)) return;
2529

30+
const start = node?.start !== undefined ? locator(node.start) : undefined;
31+
const frame = start && get_code_frame(source, start.line - 1, start.column);
32+
2633
warnings.push({
2734
code,
2835
message,
2936
filename,
30-
start: node?.start !== undefined ? locator(node.start) : undefined,
31-
end: node?.end !== undefined ? locator(node.end) : undefined
37+
position: node?.start !== undefined && node?.end !== undefined ? [node.start, node.end] : undefined,
38+
start,
39+
end: node?.end !== undefined ? locator(node.end) : undefined,
40+
frame,
41+
toString: () => error_to_string(code, message, filename, start, frame)
3242
});
3343
}
3444

packages/svelte/tests/css/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import type { CompileOptions, Warning } from '#compiler';
1010

1111
function normalize_warning(warning: Warning) {
1212
delete warning.filename;
13+
delete warning.position;
14+
delete warning.frame;
1315
return warning;
1416
}
1517

packages/svelte/types/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,8 @@ declare module 'svelte/compiler' {
18921892
start: Location | undefined;
18931893

18941894
end: Location | undefined;
1895+
1896+
frame: string | undefined;
18951897
code: string;
18961898
}
18971899

0 commit comments

Comments
 (0)