diff --git a/src/parse/index.ts b/src/parse/index.ts index b7656a426a..9b3994c831 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -71,11 +71,19 @@ export class Parser { const current = this.current(); const type = current.type === 'Element' ? `<${current.name}>` : 'Block'; - this.error(`${type} was left open`, current.start); + const slug = current.type === 'Element' ? 'element' : 'block'; + + this.error({ + code: `unclosed-${slug}`, + message: `${type} was left open` + }, current.start); } if (state !== fragment) { - this.error('Unexpected end of input'); + this.error({ + code: `unexpected-eof`, + message: 'Unexpected end of input' + }); } if (this.html.children.length) { @@ -97,12 +105,16 @@ export class Parser { } acornError(err: any) { - this.error(err.message.replace(/ \(\d+:\d+\)$/, ''), err.pos); + this.error({ + code: `parse-error`, + message: err.message.replace(/ \(\d+:\d+\)$/, '') + }, err.pos); } - error(message: string, index = this.index) { + error({ code, message }: { code: string, message: string }, index = this.index) { error(message, { name: 'ParseError', + code, source: this.template, start: index, filename: this.filename @@ -116,7 +128,10 @@ export class Parser { } if (required) { - this.error(message || `Expected ${str}`); + this.error({ + code: `unexpected-${this.index === this.template.length ? 'eof' : 'token'}`, + message: message || `Expected ${str}` + }); } return false; @@ -164,7 +179,10 @@ export class Parser { const identifier = this.template.slice(this.index, this.index = i); if (reservedNames.has(identifier)) { - this.error(`'${identifier}' is a reserved word in JavaScript and cannot be used here`, start); + this.error({ + code: `unexpected-reserved-word`, + message: `'${identifier}' is a reserved word in JavaScript and cannot be used here` + }, start); } return identifier; @@ -172,7 +190,10 @@ export class Parser { readUntil(pattern: RegExp) { if (this.index >= this.template.length) - this.error('Unexpected end of input'); + this.error({ + code: `unexpected-eof`, + message: 'Unexpected end of input' + }); const start = this.index; const match = pattern.exec(this.template.slice(start)); @@ -192,7 +213,10 @@ export class Parser { requireWhitespace() { if (!whitespace.test(this.template[this.index])) { - this.error(`Expected whitespace`); + this.error({ + code: `missing-whitespace`, + message: `Expected whitespace` + }); } this.allowWhitespace(); diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts index 77c0ffe47a..6d8fa70fc5 100644 --- a/src/parse/read/directives.ts +++ b/src/parse/read/directives.ts @@ -2,7 +2,19 @@ import { parseExpressionAt } from 'acorn'; import repeat from '../../utils/repeat'; import { Parser } from '../index'; -const DIRECTIVES = { +const DIRECTIVES: Record { start: number, end: number, type: string, name: string, value?: any, expression?: any }; + allowedExpressionTypes: string[]; + error: string; +}> = { Ref: { names: ['ref'], attribute(start, end, type, name) { @@ -143,7 +155,10 @@ export function readDirective( try { expression = readExpression(parser, expressionStart, quoteMark); if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) { - parser.error(directive.error, expressionStart); + parser.error({ + code: `invalid-directive-value`, + message: directive.error + }, expressionStart); } } catch (err) { if (parser.template[expressionStart] === '{') { @@ -155,7 +170,10 @@ export function readDirective( const value = parser.template.slice(expressionStart + (parser.v2 ? 1 : 2), expressionEnd); message += ` — use '${value}', not '${parser.v2 ? `{${value}}` : `{{${value}}}`}'`; } - parser.error(message, expressionStart); + parser.error({ + code: `invalid-directive-value`, + message + }, expressionStart); } throw err; diff --git a/src/parse/read/script.ts b/src/parse/read/script.ts index 4f28200b43..0518bc51a6 100644 --- a/src/parse/read/script.ts +++ b/src/parse/read/script.ts @@ -12,7 +12,10 @@ export default function readScript(parser: Parser, start: number, attributes: No const scriptStart = parser.index; const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart); - if (scriptEnd === -1) parser.error(`