Skip to content

Commit

Permalink
chore: align warning and error objects, add frame property
Browse files Browse the repository at this point in the history
This aligns warning and error objects to contain the same properties and have their toString methods return the same shape. It's implemented by warnings becoming class objects, too, and sharing the same base class with errors. 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
  • Loading branch information
dummdidumm committed Jul 6, 2024
1 parent ae3bf9e commit a4b5d50
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 186 deletions.
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
/** @import { Location } from 'locate-character' */
import * as state from './state.js';
import { get_code_frame } from './utils/get_code_frame.js';
import { error_to_string } from './utils/error_to_string.js';
import { CompileDiagnostic } from './utils/compile_diagnostic.js';

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

export class InternalCompileError extends Error {
export class InternalCompileError extends CompileDiagnostic {
name = 'CompileError';
filename = state.filename;
/** @type {[number, number] | undefined} */
position = undefined;
/** @type {Location | undefined} */
start = undefined;
/** @type {Location | undefined} */
end = undefined;
/** @type {string | undefined} */
frame = undefined;

/**
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(message);
this.code = code;
this.position = position;

if (position) {
this.start = state.locator(position[0]);
this.end = state.locator(position[1]);
if (this.start && this.end) {
this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
}
}
}

toString() {
return error_to_string(this.code, this.message, this.filename, this.start, this.frame);
super(code, message, position);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { filename, locator, warnings, ignore_stack, ignore_map, source } from './state.js';
import { get_code_frame } from './utils/get_code_frame.js';
import { error_to_string } from './utils/error_to_string.js';
import { warnings, ignore_stack, ignore_map } from './state.js';
import { CompileDiagnostic } from './utils/compile_diagnostic.js';

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

export class InternalCompileWarning extends CompileDiagnostic {
name = 'CompileWarning';

/**
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(code, message, position);
}
}

/**
* @param {null | NodeLike} node
* @param {string} code
Expand All @@ -16,20 +28,9 @@ function w(node, code, message) {
}
if (stack && stack.at(-1)?.has(code)) return;

const start = node?.start !== undefined ? locator(node.start) : undefined;
const frame = start && get_code_frame(source, start.line - 1, start.column);

warnings.push({
code,
message,
filename,
position:
node?.start !== undefined && node?.end !== undefined ? [node.start, node.end] : undefined,
start,
end: node?.end !== undefined ? locator(node.end) : undefined,
frame,
toString: () => error_to_string(code, message, filename, start, frame)
});
warnings.push(
new InternalCompileWarning(code, message, node ? [node.start, node.end] : undefined)
);
}

export const codes = CODES;
Expand Down
33 changes: 3 additions & 30 deletions packages/svelte/src/compiler/errors.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,18 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */

/** @import { Location } from 'locate-character' */
import * as state from './state.js';
import { get_code_frame } from './utils/get_code_frame.js';
import { error_to_string } from './utils/error_to_string.js';
import { CompileDiagnostic } from './utils/compile_diagnostic.js';

/** @typedef {{ start?: number, end?: number }} NodeLike */
export class InternalCompileError extends Error {
export class InternalCompileError extends CompileDiagnostic {
name = 'CompileError';
filename = state.filename;
/** @type {[number, number] | undefined} */
position = undefined;
/** @type {Location | undefined} */
start = undefined;
/** @type {Location | undefined} */
end = undefined;
/** @type {string | undefined} */
frame = undefined;

/**
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(message);
this.code = code;
this.position = position;

if (position) {
this.start = state.locator(position[0]);
this.end = state.locator(position[1]);

if (this.start && this.end) {
this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
}
}
}

toString() {
return error_to_string(this.code, this.message, this.filename, this.start, this.frame);
super(code, message, position);
}
}

Expand Down
12 changes: 2 additions & 10 deletions packages/svelte/src/compiler/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import type {
Identifier,
ImportDeclaration
} from 'estree';
import type { Location } from 'locate-character';
import type { SourceMap } from 'magic-string';
import type { Context } from 'zimmerframe';
import type { Scope } from '../phases/scope.js';
import type { Css } from './css.js';
import type { EachBlock, Namespace, SvelteNode, SvelteOptions } from './template.js';
import type { InternalCompileError } from '../errors.js';
import type { InternalCompileWarning } from '../warnings.js';

/** The return value of `compile` from `svelte/compiler` */
export interface CompileResult {
Expand Down Expand Up @@ -51,15 +51,7 @@ export interface CompileResult {
ast: any;
}

export interface Warning {
code: string;
message: string;
filename?: string;
start?: Location;
end?: Location;
position?: [number, number];
frame?: string;
}
export interface Warning extends InternalCompileWarning {}

export interface CompileError extends InternalCompileError {}

Expand Down
98 changes: 98 additions & 0 deletions packages/svelte/src/compiler/utils/compile_diagnostic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/** @import { Location } from 'locate-character' */
import * as state from '../state.js';

const regex_tabs = /^\t+/;

/**
* @param {string} str
*/
function tabs_to_spaces(str) {
return str.replace(regex_tabs, (match) => match.split('\t').join(' '));
}

/**
* @param {string} source
* @param {number} line
* @param {number} column
*/
function get_code_frame(source, line, column) {
const lines = source.split('\n');
const frame_start = Math.max(0, line - 2);
const frame_end = Math.min(line + 3, lines.length);
const digits = String(frame_end + 1).length;
return lines
.slice(frame_start, frame_end)
.map((str, i) => {
const is_error_line = frame_start + i === line;
const line_num = String(i + frame_start + 1).padStart(digits, ' ');
if (is_error_line) {
const indicator =
' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
}
return `${line_num}: ${tabs_to_spaces(str)}`;
})
.join('\n');
}

export class CompileDiagnostic extends Error {
name = 'CompileDiagnostic';
filename = state.filename;
/** @type {[number, number] | undefined} */
position = undefined;
/** @type {Location | undefined} */
start = undefined;
/** @type {Location | undefined} */
end = undefined;
/** @type {string | undefined} */
frame = undefined;

/**
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(message);
this.code = code;
this.position = position;

if (position) {
this.start = state.locator(position[0]);
this.end = state.locator(position[1]);
if (this.start && this.end) {
this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
}
}
}

toString() {
let out = `${this.code}: ${this.message}`;

if (this.filename) {
out += `\n${this.filename}`;

if (this.start) {
out += `:${this.start.line}:${this.start.column}`;
}
}

if (this.frame) {
out += `\n${this.frame}`;
}

return out;
}

toJSON() {
return {
code: this.code,
message: this.message,
filename: this.filename,
start: this.start,
end: this.end,
position: this.position,
frame: this.frame
};
}
}
24 changes: 0 additions & 24 deletions packages/svelte/src/compiler/utils/error_to_string.js

This file was deleted.

33 changes: 0 additions & 33 deletions packages/svelte/src/compiler/utils/get_code_frame.js

This file was deleted.

41 changes: 16 additions & 25 deletions packages/svelte/src/compiler/warnings.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */

import {
filename,
locator,
warnings,
ignore_stack,
ignore_map,
source
} from './state.js';

import { get_code_frame } from './utils/get_code_frame.js';
import { error_to_string } from './utils/error_to_string.js';
import { warnings, ignore_stack, ignore_map } from './state.js';
import { CompileDiagnostic } from './utils/compile_diagnostic.js';

/** @typedef {{ start?: number, end?: number }} NodeLike */
export class InternalCompileWarning extends CompileDiagnostic {
name = 'CompileWarning';

/**
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(code, message, position);
}
}

/**
* @param {null | NodeLike} node
* @param {string} code
Expand All @@ -26,20 +30,7 @@ function w(node, code, message) {
}

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

const start = node?.start !== undefined ? locator(node.start) : undefined;
const frame = start && get_code_frame(source, start.line - 1, start.column);

warnings.push({
code,
message,
filename,
position: node?.start !== undefined && node?.end !== undefined ? [node.start, node.end] : undefined,
start,
end: node?.end !== undefined ? locator(node.end) : undefined,
frame,
toString: () => error_to_string(code, message, filename, start, frame)
});
warnings.push(new InternalCompileWarning(code, message, node ? [node.start, node.end] : undefined));
}

export const codes = [
Expand Down
Loading

0 comments on commit a4b5d50

Please sign in to comment.