From bd74929920191601e899eb9d1067fca8946456b0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Mar 2018 14:37:42 -0400 Subject: [PATCH 1/3] dont treat :foo as a directive --- src/parse/read/directives.ts | 77 +++++++++++++++++------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts index a3ff94c68c..f197c8aac9 100644 --- a/src/parse/read/directives.ts +++ b/src/parse/read/directives.ts @@ -4,53 +4,47 @@ import { Parser } from '../index'; const DIRECTIVES = { Ref: { - names: [ 'ref' ], + names: ['ref'], attribute(start, end, type, name) { return { start, end, type, name }; } }, EventHandler: { - names: [ 'on' ], - allowedExpressionTypes: [ 'CallExpression' ], + names: ['on'], + allowedExpressionTypes: ['CallExpression'], + attribute(start, end, type, name, expression) { + return { start, end, type, name, expression }; + } }, Binding: { - names: [ '', 'bind' ], - allowedExpressionTypes: [ 'Identifier', 'MemberExpression' ], - attribute(start, end, type, name, expression, directiveName) { - let value; - - // :foo is shorthand for foo='{{foo}}' - if (!directiveName) { - const valueStart = start + 1; - const valueEnd = start + name.length; - type = 'Attribute'; - value = getShorthandValue(start + 1, name); - } else { - value = expression || { + names: ['bind'], + allowedExpressionTypes: ['Identifier', 'MemberExpression'], + attribute(start, end, type, name, expression) { + return { + start, end, type, name, + value: expression || { type: 'Identifier', start: start + 5, end, name, - }; - } - - return { start, end, type, name, value }; - }, + } + }; + } }, Transition: { - names: [ 'in', 'out', 'transition' ], - allowedExpressionTypes: [ 'ObjectExpression' ], + names: ['in', 'out', 'transition'], + allowedExpressionTypes: ['ObjectExpression'], attribute(start, end, type, name, expression, directiveName) { return { start, end, type, name, expression, intro: directiveName === 'in' || directiveName === 'transition', outro: directiveName === 'out' || directiveName === 'transition', - } - }, - }, + }; + } + } }; @@ -83,7 +77,7 @@ function readExpression(parser: Parser, start: number, quoteMark: string|null) { } else { str += char; } - } else if (/[\s\r\n\/>]/.test(char)) { + } else if (/[\s\/>]/.test(char)) { break; } else { str += char; @@ -106,12 +100,23 @@ export function readDirective( start: number, attrName: string ) { - const [ directiveName, name ] = attrName.split(':'); + const [directiveName, name] = attrName.split(':'); if (name === undefined) return; // No colon in the name - + + if (directiveName === '') { + // not a directive — :foo is short for foo={{foo}} + return { + start: start, + end: start + name.length + 1, + type: 'Attribute', + name, + value: getShorthandValue(start + 1, name) + }; + } + const type = lookupByName[directiveName]; if (!type) return; // not a registered directive - + const directive = DIRECTIVES[type]; let expression = null; @@ -137,17 +142,7 @@ export function readDirective( } } - if (directive.attribute) { - return directive.attribute(start, parser.index, type, name, expression, directiveName); - } else { - return { - start, - end: parser.index, - type: type, - name, - expression, - }; - } + return directive.attribute(start, parser.index, type, name, expression, directiveName); } From f97036dd391bb49bd3f5570fad40adfe1b913279 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Mar 2018 14:49:37 -0400 Subject: [PATCH 2/3] error if ref:foo has a value --- src/parse/read/directives.ts | 9 +++++++-- test/parser/samples/error-ref-value/error.json | 8 ++++++++ test/parser/samples/error-ref-value/input.html | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/parser/samples/error-ref-value/error.json create mode 100644 test/parser/samples/error-ref-value/input.html diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts index f197c8aac9..4c8bcd3ea7 100644 --- a/src/parse/read/directives.ts +++ b/src/parse/read/directives.ts @@ -1,5 +1,6 @@ import { parseExpressionAt } from 'acorn'; import repeat from '../../utils/repeat'; +import list from '../../utils/list'; import { Parser } from '../index'; const DIRECTIVES = { @@ -137,8 +138,12 @@ export function readDirective( } expression = readExpression(parser, expressionStart, quoteMark); - if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) { - parser.error(`Expected ${directive.allowedExpressionTypes.join(' or ')}`, expressionStart); + if (directive.allowedExpressionTypes) { + if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) { + parser.error(`Expected ${list(directive.allowedExpressionTypes)}`, expressionStart); + } + } else { + parser.error(`${directiveName} directives cannot have a value`, expressionStart); } } diff --git a/test/parser/samples/error-ref-value/error.json b/test/parser/samples/error-ref-value/error.json new file mode 100644 index 0000000000..963c4656eb --- /dev/null +++ b/test/parser/samples/error-ref-value/error.json @@ -0,0 +1,8 @@ +{ + "message": "ref directives cannot have a value", + "loc": { + "line": 1, + "column": 14 + }, + "pos": 14 +} \ No newline at end of file diff --git a/test/parser/samples/error-ref-value/input.html b/test/parser/samples/error-ref-value/input.html new file mode 100644 index 0000000000..8ad19958e3 --- /dev/null +++ b/test/parser/samples/error-ref-value/input.html @@ -0,0 +1 @@ +
\ No newline at end of file From 39694c31c3efe3db36d7ac9ce241ca897d4d8394 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Mar 2018 17:03:44 -0400 Subject: [PATCH 3/3] nicer error messages --- src/parse/read/directives.ts | 28 +++++++++---------- .../samples/error-binding-rvalue/error.json | 2 +- .../samples/error-event-handler/error.json | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts index 4c8bcd3ea7..c9c79a85c3 100644 --- a/src/parse/read/directives.ts +++ b/src/parse/read/directives.ts @@ -1,6 +1,5 @@ import { parseExpressionAt } from 'acorn'; import repeat from '../../utils/repeat'; -import list from '../../utils/list'; import { Parser } from '../index'; const DIRECTIVES = { @@ -8,20 +7,22 @@ const DIRECTIVES = { names: ['ref'], attribute(start, end, type, name) { return { start, end, type, name }; - } + }, + allowedExpressionTypes: [], + error: 'ref directives cannot have a value' }, EventHandler: { names: ['on'], - allowedExpressionTypes: ['CallExpression'], attribute(start, end, type, name, expression) { return { start, end, type, name, expression }; - } + }, + allowedExpressionTypes: ['CallExpression'], + error: 'Expected a method call' }, Binding: { names: ['bind'], - allowedExpressionTypes: ['Identifier', 'MemberExpression'], attribute(start, end, type, name, expression) { return { start, end, type, name, @@ -32,19 +33,22 @@ const DIRECTIVES = { name, } }; - } + }, + allowedExpressionTypes: ['Identifier', 'MemberExpression'], + error: 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)' }, Transition: { names: ['in', 'out', 'transition'], - allowedExpressionTypes: ['ObjectExpression'], attribute(start, end, type, name, expression, directiveName) { return { start, end, type, name, expression, intro: directiveName === 'in' || directiveName === 'transition', outro: directiveName === 'out' || directiveName === 'transition', }; - } + }, + allowedExpressionTypes: ['ObjectExpression'], + error: 'Transition argument must be an object literal, e.g. `{ duration: 400 }`' } }; @@ -138,12 +142,8 @@ export function readDirective( } expression = readExpression(parser, expressionStart, quoteMark); - if (directive.allowedExpressionTypes) { - if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) { - parser.error(`Expected ${list(directive.allowedExpressionTypes)}`, expressionStart); - } - } else { - parser.error(`${directiveName} directives cannot have a value`, expressionStart); + if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) { + parser.error(directive.error, expressionStart); } } diff --git a/test/parser/samples/error-binding-rvalue/error.json b/test/parser/samples/error-binding-rvalue/error.json index 2f25dbef0d..63f6711cd1 100644 --- a/test/parser/samples/error-binding-rvalue/error.json +++ b/test/parser/samples/error-binding-rvalue/error.json @@ -1,5 +1,5 @@ { - "message": "Expected Identifier or MemberExpression", + "message": "Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)", "pos": 19, "loc": { "line": 1, diff --git a/test/parser/samples/error-event-handler/error.json b/test/parser/samples/error-event-handler/error.json index 45032c2707..f5e603b06a 100644 --- a/test/parser/samples/error-event-handler/error.json +++ b/test/parser/samples/error-event-handler/error.json @@ -1,5 +1,5 @@ { - "message": "Expected CallExpression", + "message": "Expected a method call", "loc": { "line": 1, "column": 15