Skip to content

Commit

Permalink
Merge pull request #318 from sveltejs/implicitly-closed-elements
Browse files Browse the repository at this point in the history
handle implicitly closed elements
  • Loading branch information
Rich-Harris authored Mar 1, 2017
2 parents 1c289ad + 0f2f309 commit c2275f3
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ coverage
coverage.lcov
test/sourcemaps/*/output.js
test/sourcemaps/*/output.js.map
_actual.json
74 changes: 56 additions & 18 deletions src/parse/state/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -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( '-->' );
Expand All @@ -40,8 +77,6 @@ export default function tag ( parser ) {

const isClosingTag = parser.eat( '/' );

// TODO handle cases like <li>one<li>two

const name = readTagName( parser );

parser.allowWhitespace();
Expand All @@ -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. <div><p></div>
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 `<li>one<li>two`?
const disallowed = disallowedContents[ parent.name ];
if ( ~disallowed.indexOf( name ) ) {
stripWhitespace( parent );

parent.end = start;
parser.stack.pop();
}
}

const attributes = [];
Expand Down
3 changes: 2 additions & 1 deletion test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
5 changes: 5 additions & 0 deletions test/parser/implicitly-closed-li/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ul>
<li>a
<li>b
<li>c
</ul>
66 changes: 66 additions & 0 deletions test/parser/implicitly-closed-li/output.json
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit c2275f3

Please sign in to comment.