pull/11294/head
Rich Harris 2 years ago
parent f69b18eb12
commit 689ba3085c

@ -1,174 +0,0 @@
/** @typedef {{ start?: number, end?: number }} NodeLike */
/** @typedef {Record<string, (...args: any[]) => string>} Errors */
/**
* @param {Array<string | number>} items
* @param {string} conjunction
*/
function list(items, conjunction = 'or') {
if (items.length === 1) return items[0];
return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[items.length - 1]}`;
}
/** @satisfies {Errors} */
const internal = {
/** @param {string} message */
TODO: (message) => `TODO ${message}`,
/** @param {string} message */
INTERNAL: (message) =>
`Internal compiler error: ${message}. Please report this to https://github.com/sveltejs/svelte/issues`
};
/** @satisfies {Errors} */
const errors = {
...internal
// missing_contenteditable_attribute: {
// code: 'missing-contenteditable-attribute',
// message:
// "'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings"
// },
// textarea_duplicate_value: {
// code: 'textarea-duplicate-value',
// message:
// 'A <textarea> can have either a value attribute or (equivalently) child content, but not both'
// },
// invalid_attribute_head: {
// code: 'invalid-attribute',
// message: '<svelte:head> should not have any attributes or directives'
// },
// invalid_action: {
// code: 'invalid-action',
// message: 'Actions can only be applied to DOM elements, not components'
// },
// invalid_class: {
// code: 'invalid-class',
// message: 'Classes can only be applied to DOM elements, not components'
// },
// invalid_transition: {
// code: 'invalid-transition',
// message: 'Transitions can only be applied to DOM elements, not components'
// },
// invalid_let: {
// code: 'invalid-let',
// message: 'let directive value must be an identifier or an object/array pattern'
// },
// invalid_slot_directive: {
// code: 'invalid-slot-directive',
// message: '<slot> cannot have directives'
// },
// dynamic_slot_name: {
// code: 'dynamic-slot-name',
// message: '<slot> name cannot be dynamic'
// },
// invalid_slot_attribute_value_missing: {
// code: 'invalid-slot-attribute',
// message: 'slot attribute value is missing'
// },
// illegal_structure_title: {
// code: 'illegal-structure',
// message: '<title> can only contain text and {tags}'
// },
// duplicate_transition: /**
// * @param {string} directive
// * @param {string} parent_directive
// */ (directive, parent_directive) => {
// /** @param {string} _directive */
// function describe(_directive) {
// return _directive === 'transition' ? "a 'transition'" : `an '${_directive}'`;
// }
// const message =
// directive === parent_directive
// ? `An element can only have one '${directive}' directive`
// : `An element cannot have both ${describe(parent_directive)} directive and ${describe(
// directive
// )} directive`;
// return {
// code: 'duplicate-transition',
// message
// };
// },
// default_export: {
// code: 'default-export',
// message: 'A component cannot have a default export'
// },
// illegal_declaration: {
// code: 'illegal-declaration',
// message: 'The $ prefix is reserved, and cannot be used for variable and import names'
// },
// invalid_directive_value: {
// code: 'invalid-directive-value',
// message:
// 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)'
// },
};
// interface is duplicated between here (used internally) and ./interfaces.js
// (exposed publicly), and I'm not sure how to avoid that
export class CompileError extends Error {
name = 'CompileError';
/** @type {import('#compiler').CompileError['filename']} */
filename = undefined;
/** @type {import('#compiler').CompileError['position']} */
position = undefined;
/** @type {import('#compiler').CompileError['start']} */
start = undefined;
/** @type {import('#compiler').CompileError['end']} */
end = undefined;
/**
*
* @param {string} code
* @param {string} message
* @param {[number, number] | undefined} position
*/
constructor(code, message, position) {
super(message);
this.code = code;
this.position = position;
}
toString() {
let out = `${this.name}: ${this.message}`;
out += `\n(${this.code})`;
if (this.filename) {
out += `\n${this.filename}`;
if (this.start) {
out += `${this.start.line}:${this.start.column}`;
}
}
return out;
}
}
/**
* @template {Exclude<keyof typeof errors, 'TODO'>} T
* @param {NodeLike | number | null} node
* @param {T} code
* @param {Parameters<typeof errors[T]>} args
* @returns {never}
*/
export function error(node, code, ...args) {
const fn = errors[code];
// @ts-expect-error
const message = fn(...args);
const start = typeof node === 'number' ? node : node?.start;
const end = typeof node === 'number' ? node : node?.end;
throw new CompileError(
code,
message,
start !== undefined && end !== undefined ? [start, end] : undefined
);
}

@ -1,6 +1,6 @@
import { getLocator } from 'locate-character';
import { walk as zimmerframe_walk } from 'zimmerframe';
import { CompileError } from './errors-tmp.js';
import { CompileError } from './errors.js';
import { convert } from './legacy.js';
import { parse as parse_acorn } from './phases/1-parse/acorn.js';
import { parse as _parse } from './phases/1-parse/index.js';
@ -157,6 +157,6 @@ export function walk() {
);
}
export { CompileError } from './errors-tmp.js';
export { CompileError } from './errors.js';
export { VERSION } from '../version.js';

@ -4,7 +4,6 @@ import fragment from './state/fragment.js';
import { regex_whitespace } from '../patterns.js';
import { reserved } from './utils/names.js';
import full_char_code_at from './utils/full_char_code_at.js';
import { error } from '../../errors-tmp.js';
import * as e from '../../errors.js';
import { create_fragment } from './utils/create.js';
import read_options from './read/options.js';

@ -9,7 +9,6 @@ import {
} from '../utils/bracket.js';
import { parse_expression_at } from '../acorn.js';
import { regex_not_newline_characters } from '../../patterns.js';
import { error } from '../../../errors-tmp.js';
import * as e from '../../../errors.js';
/**

@ -1,6 +1,5 @@
import is_reference from 'is-reference';
import { walk } from 'zimmerframe';
import { error } from '../../errors-tmp.js';
import * as e from '../../errors.js';
import {
extract_identifiers,

@ -3,7 +3,6 @@ import {
interactive_elements,
is_tag_valid_with_parent
} from '../../../constants.js';
import { error } from '../../errors-tmp.js';
import * as e from '../../errors.js';
import {
extract_identifiers,
@ -367,7 +366,7 @@ const validation = {
if (node.name === 'group') {
if (!binding) {
error(node, 'INTERNAL', 'Cannot find declaration for bind:group');
throw new Error('Cannot find declaration for bind:group');
}
}

@ -1,5 +1,4 @@
import { walk } from 'zimmerframe';
import { error } from '../../../errors-tmp.js';
import * as b from '../../../utils/builders.js';
import { set_scope } from '../../scope.js';
import { template_visitors } from './visitors/template.js';
@ -52,35 +51,41 @@ export function client_component(source, analysis, options) {
get before_init() {
/** @type {any[]} */
const a = [];
a.push = () =>
error(null, 'INTERNAL', 'before_init.push should not be called outside create_block');
a.push = () => {
throw new Error('before_init.push should not be called outside create_block');
};
return a;
},
get init() {
/** @type {any[]} */
const a = [];
a.push = () => error(null, 'INTERNAL', 'init.push should not be called outside create_block');
a.push = () => {
throw new Error('init.push should not be called outside create_block');
};
return a;
},
get update() {
/** @type {any[]} */
const a = [];
a.push = () =>
error(null, 'INTERNAL', 'update.push should not be called outside create_block');
a.push = () => {
throw new Error('update.push should not be called outside create_block');
};
return a;
},
get after_update() {
/** @type {any[]} */
const a = [];
a.push = () =>
error(null, 'INTERNAL', 'after_update.push should not be called outside create_block');
a.push = () => {
throw new Error('after_update.push should not be called outside create_block');
};
return a;
},
get template() {
/** @type {any[]} */
const a = [];
a.push = () =>
error(null, 'INTERNAL', 'template.push should not be called outside create_block');
a.push = () => {
throw new Error('template.push should not be called outside create_block');
};
return a;
},
legacy_reactive_statements: new Map(),
@ -209,7 +214,7 @@ export function client_component(source, analysis, options) {
for (const [node] of analysis.reactive_statements) {
const statement = [...state.legacy_reactive_statements].find(([n]) => n === node);
if (statement === undefined) {
error(node, 'INTERNAL', 'Could not find reactive statement');
throw new Error('Could not find reactive statement');
}
instance.body.push(statement[1]);
}

@ -5,7 +5,6 @@ import {
is_simple_expression,
object
} from '../../../utils/ast.js';
import { error } from '../../../errors-tmp.js';
import {
PROPS_IS_LAZY_INITIAL,
PROPS_IS_IMMUTABLE,
@ -185,7 +184,7 @@ export function serialize_set_binding(node, context, fallback, options) {
}
if (assignee.type !== 'Identifier' && assignee.type !== 'MemberExpression') {
error(node, 'INTERNAL', `Unexpected assignment type ${assignee.type}`);
throw new Error(`Unexpected assignment type ${assignee.type}`);
}
// Handle class private/public state assignment cases

@ -16,7 +16,6 @@ import {
import { DOMProperties, PassiveEvents, VoidElements } from '../../../constants.js';
import { is_custom_element_node, is_element_node } from '../../../nodes.js';
import * as b from '../../../../utils/builders.js';
import { error } from '../../../../errors-tmp.js';
import {
with_loc,
function_visitor,
@ -1776,10 +1775,10 @@ export const template_visitors = {
);
},
ClassDirective(node, { state, next }) {
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
throw new Error('Node should have been handled elsewhere');
},
StyleDirective(node, { state, next }) {
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
throw new Error('Node should have been handled elsewhere');
},
TransitionDirective(node, { state, visit }) {
let flags = node.modifiers.includes('global') ? TRANSITION_GLOBAL : 0;
@ -2775,7 +2774,7 @@ export const template_visitors = {
}
default:
error(node, 'INTERNAL', 'unknown binding ' + node.name);
throw new Error('unknown binding ' + node.name);
}
}

@ -22,7 +22,6 @@ import {
transform_inspect_rune
} from '../utils.js';
import { create_attribute, is_custom_element_node, is_element_node } from '../../nodes.js';
import { error } from '../../../errors-tmp.js';
import { binding_properties } from '../../bindings.js';
import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patterns.js';
import {
@ -419,7 +418,7 @@ function serialize_set_binding(node, context, fallback) {
}
if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') {
error(node, 'INTERNAL', `Unexpected assignment type ${node.left.type}`);
throw new Error(`Unexpected assignment type ${node.left.type}`);
}
let left = node.left;
@ -1329,10 +1328,10 @@ const template_visitors = {
state.template.push(block_close);
},
ClassDirective(node) {
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
throw new Error('Node should have been handled elsewhere');
},
StyleDirective(node) {
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
throw new Error('Node should have been handled elsewhere');
},
RegularElement(node, context) {
const metadata = {
@ -2124,14 +2123,17 @@ export function server_component(analysis, options) {
get init() {
/** @type {any[]} */
const a = [];
a.push = () => error(null, 'INTERNAL', 'init.push should not be called outside create_block');
a.push = () => {
throw new Error('init.push should not be called outside create_block');
};
return a;
},
get template() {
/** @type {any[]} */
const a = [];
a.push = () =>
error(null, 'INTERNAL', 'template.push should not be called outside create_block');
a.push = () => {
throw new Error('template.push should not be called outside create_block');
};
return a;
},
metadata: {
@ -2199,7 +2201,7 @@ export function server_component(analysis, options) {
for (const [node] of analysis.reactive_statements) {
const statement = [...state.legacy_reactive_statements].find(([n]) => n === node);
if (statement === undefined) {
error(node, 'INTERNAL', 'Could not find reactive statement');
throw new Error('Could not find reactive statement');
}
if (

@ -2,7 +2,6 @@ import is_reference from 'is-reference';
import { walk } from 'zimmerframe';
import { is_element_node } from './nodes.js';
import * as b from '../utils/builders.js';
import { error } from '../errors-tmp.js';
import * as e from '../errors.js';
import { extract_identifiers, extract_identifiers_from_destructuring } from '../utils/ast.js';
import { JsKeywords, Runes } from './constants.js';
@ -167,7 +166,7 @@ export class Scope {
get_bindings(node) {
const bindings = this.declarators.get(node);
if (!bindings) {
error(node, 'INTERNAL', 'No binding found for declarator');
throw new Error('No binding found for declarator');
}
return bindings;
}

@ -1,12 +1,10 @@
import { error } from '../errors-tmp.js';
/**
* @template T
* @param {T} value
* @returns {asserts value is NonNullable<T>}
*/
export function ok(value) {
if (!value) error(null, 'INTERNAL', 'Assertion failed');
if (!value) throw new Error('Assertion failed');
}
/**
@ -16,5 +14,5 @@ export function ok(value) {
* @returns {asserts actual is T}
*/
export function equal(actual, expected) {
if (actual !== expected) error(null, 'INTERNAL', 'Assertion failed');
if (actual !== expected) throw new Error('Assertion failed');
}

@ -1,4 +1,3 @@
import { error } from './errors-tmp.js';
import * as e from './errors.js';
/**

Loading…
Cancel
Save