start moving errors into a single location

pull/6519/head
pngwn 6 years ago committed by tanhauhau
parent 8f1ae80a99
commit a90f7f94b1

@ -0,0 +1,96 @@
// All parser errors should be listed and accessed from here
/**
* @internal
*/
export const template_errors = {
duplicate_style: () => ({
code: 'duplicate-style',
message: 'You can only have one top-level <style> tag per component'
}),
duplicate_instance_script: () => ({
code: `invalid-script`,
message: `A component can only have one instance-level <script> element`
}),
duplicate_module_script: () => ({
code: `invalid-script`,
message: `A component can only have one <script context="module"> element`
}),
dynamic_context_attribute: () => ({
code: 'invalid-script',
message: `context attribute must be static`
}),
fixed_context_attribute: () => ({
code: `invalid-script`,
message: `If the context attribute is supplied, its value must be "module"`
}),
invalid_elseif: () => ({
code: 'invalid-elseif',
message: `'elseif' should be 'else if'`
}),
else_if_without_if: () => ({
code: `invalid-elseif-placement`,
message: `Cannot have an {:else if ...} block outside an {#if ...} block`
}),
else_if_before_block_close: (block) => ({
code: `invalid-elseif-placement`,
message: `Expected to close ${block} before seeing {:else if ...} block`
}),
else_without_if_each: () => ({
code: `invalid-else-placement`,
message: `Cannot have an {:else} block outside an {#if ...} or {#each ...} block`
}),
else_before_block_close: (block) => ({
code: `invalid-else-placement`,
message: `Expected to close ${block} before seeing {:else} block`
}),
unexpected_block_close: () => ({
code: `unexpected-block-close`,
message: `Unexpected block closing tag`
}),
unclosed_script: () => ({
code: `unclosed-script`,
message: `<script> must have a closing tag`
}),
unclosed_style: () => ({
code: `unclosed-style`,
message: `<style> must have a closing tag`
}),
unclosed_comment: () => ({
code: `unclosed-comment`,
message: `comment was left open, expected -->`
}),
unexpected_token: () => ({
code: 'unexpected-token',
message: 'Expected )'
})
};
export const css_errors = {
invalid_ref_selector: () => ({
code: `invalid-ref-selector`,
message: 'ref selectors are no longer supported'
}),
invalid_declaration: () => ({
code: `invalid-declaration`,
message: `Declaration cannot be empty`
}),
empty_global_selector: () => ({
code: `css-syntax-error`,
message: `:global() must contain a selector`
}),
syntax_error: (message) => ({
code: `css-syntax-error`,
message
})
};
export const js_errors = {
invalid_ref_selector: () => ({
code: `invalid-ref-selector`,
message: 'ref selectors are no longer supported'
})
};

@ -5,6 +5,7 @@ import { reserved } from '../utils/names';
import full_char_code_at from '../utils/full_char_code_at';
import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces';
import error from '../utils/error';
import { template_errors } from './errors';
type ParserState = (parser: Parser) => (ParserState | void);
@ -218,27 +219,18 @@ export default function parse(
// TODO we may want to allow multiple <style> tags —
// one scoped, one global. for now, only allow one
if (parser.css.length > 1) {
parser.error({
code: 'duplicate-style',
message: 'You can only have one top-level <style> tag per component'
}, parser.css[1].start);
parser.error(template_errors.duplicate_style(), parser.css[1].start);
}
const instance_scripts = parser.js.filter(script => script.context === 'default');
const module_scripts = parser.js.filter(script => script.context === 'module');
if (instance_scripts.length > 1) {
parser.error({
code: 'invalid-script',
message: 'A component can only have one instance-level <script> element'
}, instance_scripts[1].start);
parser.error(template_errors.duplicate_instance_script(), instance_scripts[1].start);
}
if (module_scripts.length > 1) {
parser.error({
code: 'invalid-script',
message: 'A component can only have one <script context="module"> element'
}, module_scripts[1].start);
parser.error(template_errors.duplicate_module_script(), module_scripts[1].start);
}
return {

@ -2,25 +2,20 @@ import * as acorn from '../acorn';
import { Parser } from '../index';
import { Script } from '../../interfaces';
import { Node, Program } from 'estree';
import { template_errors } from '../errors';
function get_context(parser: Parser, attributes: any[], start: number): string {
const context = attributes.find(attribute => attribute.name === 'context');
if (!context) return 'default';
if (context.value.length !== 1 || context.value[0].type !== 'Text') {
parser.error({
code: 'invalid-script',
message: 'context attribute must be static'
}, start);
parser.error(template_errors.dynamic_context_attribute(), start);
}
const value = context.value[0].data;
if (value !== 'module') {
parser.error({
code: 'invalid-script',
message: 'If the context attribute is supplied, its value must be "module"'
}, context.start);
parser.error(template_errors.fixed_context_attribute(), context.start);
}
return value;
@ -28,10 +23,7 @@ function get_context(parser: Parser, attributes: any[], start: number): string {
export default function read_script(parser: Parser, start: number, attributes: Node[]): Script {
const script_start = parser.index;
const error_message = {
code: 'unclosed-script',
message: '<script> must have a closing tag'
};
const error_message = template_errors.unclosed_script();
const data = parser.read_until(/<\/script\s*>/, error_message);
if (parser.index >= parser.template.length) {
parser.error(error_message);

@ -3,13 +3,11 @@ import { walk } from 'estree-walker';
import { Parser } from '../index';
import { Node } from 'estree';
import { Style } from '../../interfaces';
import { css_errors, template_errors } from '../errors';
export default function read_style(parser: Parser, start: number, attributes: Node[]): Style {
const content_start = parser.index;
const error_message = {
code: 'unclosed-style',
message: '<style> must have a closing tag'
};
const error_message = template_errors.unclosed_style();
const styles = parser.read_until(/<\/style\s*>/, error_message);
@ -31,10 +29,7 @@ export default function read_style(parser: Parser, start: number, attributes: No
});
} catch (err) {
if (err.name === 'SyntaxError') {
parser.error({
code: 'css-syntax-error',
message: err.message
}, err.offset);
parser.error(css_errors.syntax_error(err.message), err.offset);
} else {
throw err;
}
@ -52,26 +47,17 @@ export default function read_style(parser: Parser, start: number, attributes: No
const b = node.children[i + 1];
if (is_ref_selector(a, b)) {
parser.error({
code: 'invalid-ref-selector',
message: 'ref selectors are no longer supported'
}, a.loc.start.offset);
parser.error(css_errors.invalid_ref_selector(), a.loc.start.offset);
}
}
}
if (node.type === 'Declaration' && node.value.type === 'Value' && node.value.children.length === 0) {
parser.error({
code: 'invalid-declaration',
message: 'Declaration cannot be empty'
}, node.start);
parser.error(css_errors.invalid_declaration(), node.start);
}
if (node.type === 'PseudoClassSelector' && node.name === 'global' && node.children === null) {
parser.error({
code: 'css-syntax-error',
message: ':global() must contain a selector'
}, node.loc.start.offset);
parser.error(css_errors.empty_global_selector(), node.loc.start.offset);
}
if (node.loc) {

@ -6,6 +6,7 @@ import { trim_start, trim_end } from '../../utils/trim';
import { to_string } from '../utils/node';
import { Parser } from '../index';
import { TemplateNode } from '../../interfaces';
import { template_errors } from '../errors';
function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after: boolean) {
if (!block.children || block.children.length === 0) return; // AwaitBlock
@ -66,10 +67,7 @@ export default function mustache(parser: Parser) {
} else if (block.type === 'KeyBlock') {
expected = 'key';
} else {
parser.error({
code: 'unexpected-block-close',
message: 'Unexpected block closing tag'
});
parser.error(template_errors.unexpected_block_close());
}
parser.eat(expected, true);
@ -98,10 +96,7 @@ export default function mustache(parser: Parser) {
parser.stack.pop();
} else if (parser.eat(':else')) {
if (parser.eat('if')) {
parser.error({
code: 'invalid-elseif',
message: "'elseif' should be 'else if'"
});
parser.error(template_errors.invalid_elseif());
}
parser.allow_whitespace();
@ -110,12 +105,11 @@ export default function mustache(parser: Parser) {
if (parser.eat('if')) {
const block = parser.current();
if (block.type !== 'IfBlock') {
parser.error({
code: 'invalid-elseif-placement',
message: parser.stack.some(block => block.type === 'IfBlock')
? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
: 'Cannot have an {:else if ...} block outside an {#if ...} block'
});
parser.error(
parser.stack.some(block => block.type === 'IfBlock')
? template_errors.else_if_before_block_close(to_string(block))
: template_errors.else_if_without_if()
);
}
parser.require_whitespace();
@ -146,12 +140,11 @@ export default function mustache(parser: Parser) {
// :else
const block = parser.current();
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error({
code: 'invalid-else-placement',
message: parser.stack.some(block => block.type === 'IfBlock' || block.type === 'EachBlock')
? `Expected to close ${to_string(block)} before seeing {:else} block`
: 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block'
});
parser.error(
parser.stack.some(block => block.type === 'IfBlock' || block.type === 'EachBlock')
? template_errors.else_before_block_close(to_string(block))
: template_errors.else_without_if_each()
);
}
parser.allow_whitespace();

Loading…
Cancel
Save