From 4ce0f2fd581cc15158077ee7c86593d99869c5b7 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Mon, 21 Nov 2016 08:23:48 -0500 Subject: [PATCH] handle unquoted attribute values --- compiler/parse/state/tag.js | 18 +++++++----- test/parser/attribute-unquoted/input.html | 1 + test/parser/attribute-unquoted/output.json | 34 ++++++++++++++++++++++ test/test.js | 14 +++++---- 4 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 test/parser/attribute-unquoted/input.html create mode 100644 test/parser/attribute-unquoted/output.json diff --git a/compiler/parse/state/tag.js b/compiler/parse/state/tag.js index 1ac9ca6b62..e9775b54d8 100644 --- a/compiler/parse/state/tag.js +++ b/compiler/parse/state/tag.js @@ -6,6 +6,7 @@ import { trimStart, trimEnd } from '../utils/trim.js'; const validTagName = /^[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const voidElementNames = /^(?:area|base|br|col|command|doctype|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i; +const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/; const specials = { script: { @@ -170,13 +171,11 @@ function readAttribute ( parser ) { } function readAttributeValue ( parser ) { - if ( parser.eat( `'` ) ) return readQuotedAttributeValue( parser, `'` ); - if ( parser.eat( `"` ) ) return readQuotedAttributeValue( parser, `"` ); + let quoteMark; - parser.error( `TODO unquoted attribute values` ); -} + if ( parser.eat( `'` ) ) quoteMark = `'`; + if ( parser.eat( `"` ) ) quoteMark = `"`; -function readQuotedAttributeValue ( parser, quoteMark ) { let currentChunk = { start: parser.index, end: null, @@ -186,6 +185,10 @@ function readQuotedAttributeValue ( parser, quoteMark ) { let escaped = false; + const done = quoteMark ? + char => char === quoteMark : + char => invalidUnquotedAttributeCharacters.test( char ); + const chunks = []; while ( parser.index < parser.template.length ) { @@ -228,8 +231,9 @@ function readQuotedAttributeValue ( parser, quoteMark ) { escaped = true; } - else if ( parser.match( quoteMark ) ) { - currentChunk.end = parser.index++; + else if ( done( parser.template[ parser.index ] ) ) { + currentChunk.end = parser.index; + if ( quoteMark ) parser.index += 1; if ( currentChunk.data ) chunks.push( currentChunk ); return chunks; diff --git a/test/parser/attribute-unquoted/input.html b/test/parser/attribute-unquoted/input.html new file mode 100644 index 0000000000..2d388456d0 --- /dev/null +++ b/test/parser/attribute-unquoted/input.html @@ -0,0 +1 @@ +
diff --git a/test/parser/attribute-unquoted/output.json b/test/parser/attribute-unquoted/output.json new file mode 100644 index 0000000000..c2a788b0a7 --- /dev/null +++ b/test/parser/attribute-unquoted/output.json @@ -0,0 +1,34 @@ +{ + "html": { + "start": 0, + "end": 21, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 21, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 14, + "type": "Attribute", + "name": "class", + "value": [ + { + "start": 11, + "end": 14, + "type": "Text", + "data": "foo" + } + ] + } + ], + "children": [] + } + ] + }, + "css": null, + "js": null +} diff --git a/test/test.js b/test/test.js index efd690d92f..4785e5edf5 100644 --- a/test/test.js +++ b/test/test.js @@ -41,11 +41,15 @@ describe( 'svelte', () => { } catch ( err ) { if ( err.name !== 'ParseError' ) throw err; - const expected = require( `./parser/${dir}/error.json` ); - - assert.equal( err.shortMessage, expected.message ); - assert.deepEqual( err.loc, expected.loc ); - assert.equal( err.pos, expected.pos ); + try { + const expected = require( `./parser/${dir}/error.json` ); + + assert.equal( err.shortMessage, expected.message ); + assert.deepEqual( err.loc, expected.loc ); + assert.equal( err.pos, expected.pos ); + } catch ( err2 ) { + throw err2.code === 'MODULE_NOT_FOUND' ? err : err2; + } } }); });