start moving errors into a single location

pull/5328/head
pngwn 5 years ago
parent cb24094963
commit ae3a00259a

6
package-lock.json generated

@ -4329,9 +4329,9 @@
"dev": true
},
"typescript": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
"integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
"dev": true
},
"uri-js": {

@ -91,7 +91,7 @@
"source-map-support": "^0.5.13",
"tiny-glob": "^0.2.6",
"tslib": "^1.10.0",
"typescript": "^3.5.3"
"typescript": "^4.0.2"
},
"nyc": {
"include": [

@ -0,0 +1,109 @@
interface ErroMessage {
/**
* The short code that references this error
*/
code: string;
/**
* A friendly error message that will be displayed to the user
*/
message: string;
}
/**
* Generates an error message for a given parsing error.
*
* Returns an object containing the error code and an actionable message
*/
type error_generator = (...args: string[]) => ErroMessage;
export const template_errors: Record<string, error_generator> = {
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-elseif-placement`,
message: `Cannot have an {:else} block outside an {#if ...} or {#each ...} block`
}),
else_before_block_close: (block) => ({
code: `invalid-elseif-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: Record<string, error_generator> = {
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: Record<string, error_generator> = {
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 errors from './errors';
type ParserState = (parser: Parser) => (ParserState | void);
@ -217,27 +218,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(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(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(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 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(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(errors.fixed_context_attribute(), context.start);
}
return value;
@ -28,13 +23,10 @@ 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 [data, matched] = parser.read_until(/<\/script\s*>/, error_message);
if (!matched) parser.error(error_message);
const [data, matched] = parser.read_until(/<\/script\s*>/, errors.unclosed_script());
if (!matched) parser.error(errors.unclosed_script());
const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') + data;

@ -3,17 +3,15 @@ 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 [styles, matched] = parser.read_until(/<\/style\s*>/, error_message);
const [styles, matched] = parser.read_until(/<\/style\s*>/, template_errors.unclosed_style());
const content_end = parser.index;
if (!matched) parser.error(error_message);
if (!matched) parser.error(template_errors.unclosed_style());
let ast;
@ -24,10 +22,7 @@ export default function read_style(parser: Parser, start: number, attributes: No
});
} catch (err) {
if (err.name === 'CssSyntaxError') {
parser.error({
code: `css-syntax-error`,
message: err.message
}, err.offset);
parser.error(css_errors.syntax_error(err.message), err.offset);
} else {
throw err;
}
@ -45,26 +40,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
@ -64,10 +65,7 @@ export default function mustache(parser: Parser) {
} else if (block.type === 'AwaitBlock') {
expected = 'await';
} else {
parser.error({
code: `unexpected-block-close`,
message: `Unexpected block closing tag`
});
parser.error(template_errors.unexpected_block_close());
}
parser.eat(expected, true);
@ -96,10 +94,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();
@ -108,12 +103,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()
);
}
parser.allow_whitespace();

Loading…
Cancel
Save