diff --git a/.gitignore b/.gitignore
index 329472f10601..d134f6249cef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ coverage
coverage.lcov
test/sourcemaps/*/output.js
test/sourcemaps/*/output.js.map
+_actual.json
\ No newline at end of file
diff --git a/src/parse/state/tag.js b/src/parse/state/tag.js
index 72f73f06513a..3b79ad84060f 100644
--- a/src/parse/state/tag.js
+++ b/src/parse/state/tag.js
@@ -21,9 +21,46 @@ const specials = {
}
};
+// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
+const disallowedContents = {
+ li: [ 'li' ],
+ dt: [ 'dt', 'dd' ],
+ dd: [ 'dt', 'dd' ],
+ p: 'address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split( ' ' ),
+ rt: [ 'rt', 'rp' ],
+ rp: [ 'rt', 'rp' ],
+ optgroup: [ 'optgroup' ],
+ option: [ 'option', 'optgroup' ],
+ thead: [ 'tbody', 'tfoot' ],
+ tbody: [ 'tbody', 'tfoot' ],
+ tfoot: [ 'tbody' ],
+ tr: [ 'tr', 'tbody' ],
+ td: [ 'td', 'th', 'tr' ],
+ th: [ 'td', 'th', 'tr' ]
+};
+
+function stripWhitespace ( element ) {
+ if ( element.children.length ) {
+ const firstChild = element.children[0];
+ const lastChild = element.children[ element.children.length - 1 ];
+
+ if ( firstChild.type === 'Text' ) {
+ firstChild.data = trimStart( firstChild.data );
+ if ( !firstChild.data ) element.children.shift();
+ }
+
+ if ( lastChild.type === 'Text' ) {
+ lastChild.data = trimEnd( lastChild.data );
+ if ( !lastChild.data ) element.children.pop();
+ }
+ }
+}
+
export default function tag ( parser ) {
const start = parser.index++;
+ let parent = parser.current();
+
if ( parser.eat( '!--' ) ) {
const data = parser.readUntil( /-->/ );
parser.eat( '-->' );
@@ -40,8 +77,6 @@ export default function tag ( parser ) {
const isClosingTag = parser.eat( '/' );
- // TODO handle cases like
onetwo
-
const name = readTagName( parser );
parser.allowWhitespace();
@@ -53,28 +88,31 @@ export default function tag ( parser ) {
if ( !parser.eat( '>' ) ) parser.error( `Expected '>'` );
- const element = parser.current();
-
- // strip leading/trailing whitespace as necessary
- if ( element.children.length ) {
- const firstChild = element.children[0];
- const lastChild = element.children[ element.children.length - 1 ];
-
- if ( firstChild.type === 'Text' ) {
- firstChild.data = trimStart( firstChild.data );
- if ( !firstChild.data ) element.children.shift();
- }
+ // close any elements that don't have their own closing tags, e.g.
+ while ( parent.name !== name ) {
+ parent.end = start;
+ parser.stack.pop();
- if ( lastChild.type === 'Text' ) {
- lastChild.data = trimEnd( lastChild.data );
- if ( !lastChild.data ) element.children.pop();
- }
+ parent = parser.current();
}
- element.end = parser.index;
+ // strip leading/trailing whitespace as necessary
+ stripWhitespace( parent );
+
+ parent.end = parser.index;
parser.stack.pop();
return null;
+ } else if ( parent.name in disallowedContents ) {
+ // can this be a child of the parent element, or does it implicitly
+ // close it, like `onetwo`?
+ const disallowed = disallowedContents[ parent.name ];
+ if ( ~disallowed.indexOf( name ) ) {
+ stripWhitespace( parent );
+
+ parent.end = start;
+ parser.stack.pop();
+ }
}
const attributes = [];
diff --git a/test/parse.js b/test/parse.js
index fa8cf85d8dc4..caa132bbdf65 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -16,7 +16,8 @@ describe( 'parse', () => {
const input = fs.readFileSync( `test/parser/${dir}/input.html`, 'utf-8' ).replace( /\s+$/, '' );
try {
- const actual = JSON.parse( JSON.stringify( svelte.parse( input ) ) );
+ const actual = svelte.parse( input );
+ fs.writeFileSync( `test/parser/${dir}/_actual.json`, JSON.stringify( actual, null, '\t' ) );
const expected = require( `./parser/${dir}/output.json` );
assert.deepEqual( actual.html, expected.html );
diff --git a/test/parser/implicitly-closed-li/input.html b/test/parser/implicitly-closed-li/input.html
new file mode 100644
index 000000000000..14fc1168baae
--- /dev/null
+++ b/test/parser/implicitly-closed-li/input.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/test/parser/implicitly-closed-li/output.json b/test/parser/implicitly-closed-li/output.json
new file mode 100644
index 000000000000..3263fa8e403b
--- /dev/null
+++ b/test/parser/implicitly-closed-li/output.json
@@ -0,0 +1,66 @@
+{
+ "hash": 3806276940,
+ "html": {
+ "start": 0,
+ "end": 31,
+ "type": "Fragment",
+ "children": [
+ {
+ "start": 0,
+ "end": 31,
+ "type": "Element",
+ "name": "ul",
+ "attributes": [],
+ "children": [
+ {
+ "start": 6,
+ "end": 13,
+ "type": "Element",
+ "name": "li",
+ "attributes": [],
+ "children": [
+ {
+ "start": 10,
+ "end": 13,
+ "type": "Text",
+ "data": "a"
+ }
+ ]
+ },
+ {
+ "start": 13,
+ "end": 20,
+ "type": "Element",
+ "name": "li",
+ "attributes": [],
+ "children": [
+ {
+ "start": 17,
+ "end": 20,
+ "type": "Text",
+ "data": "b"
+ }
+ ]
+ },
+ {
+ "start": 20,
+ "end": 26,
+ "type": "Element",
+ "name": "li",
+ "attributes": [],
+ "children": [
+ {
+ "start": 24,
+ "end": 26,
+ "type": "Text",
+ "data": "c\n"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "css": null,
+ "js": null
+}
\ No newline at end of file