diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 4abd506984..8de35cee48 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -1,5 +1,6 @@ import read_context from '../read/context'; import read_expression from '../read/expression'; +import { closing_tag_omitted } from '../utils/html'; import { whitespace } from '../../utils/patterns'; import { trim_start, trim_end } from '../../utils/trim'; import { Parser } from '../index'; @@ -41,6 +42,12 @@ export default function mustache(parser: Parser) { let block = parser.current(); let expected; + if (closing_tag_omitted(block.name)) { + block.end = start; + parser.stack.pop(); + block = parser.current(); + } + if (block.type === 'ElseBlock' || block.type === 'PendingBlock' || block.type === 'ThenBlock' || block.type === 'CatchBlock') { block.end = start; parser.stack.pop(); diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index 03735f67be..21a7b3caea 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -1,7 +1,7 @@ import read_expression from '../read/expression'; import read_script from '../read/script'; import read_style from '../read/style'; -import { decode_character_references } from '../utils/html'; +import { decode_character_references, closing_tag_omitted } from '../utils/html'; import { is_void } from '../../utils/names'; import { Parser } from '../index'; import { Directive, DirectiveType, Node, Text } from '../../interfaces'; @@ -42,31 +42,6 @@ const SELF = /^svelte:self(?=[\s\/>])/; // eslint-disable-next-line no-useless-escape const COMPONENT = /^svelte:component(?=[\s\/>])/; -// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission -const disallowed_contents = new Map([ - ['li', new Set(['li'])], - ['dt', new Set(['dt', 'dd'])], - ['dd', new Set(['dt', 'dd'])], - [ - 'p', - new Set( - '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', new Set(['rt', 'rp'])], - ['rp', new Set(['rt', 'rp'])], - ['optgroup', new Set(['optgroup'])], - ['option', new Set(['option', 'optgroup'])], - ['thead', new Set(['tbody', 'tfoot'])], - ['tbody', new Set(['tbody', 'tfoot'])], - ['tfoot', new Set(['tbody'])], - ['tr', new Set(['tr', 'tbody'])], - ['td', new Set(['td', 'th', 'tr'])], - ['th', new Set(['td', 'th', 'tr'])], -]); - function parent_is_head(stack) { let i = stack.length; while (i--) { @@ -176,13 +151,9 @@ export default function tag(parser: Parser) { parser.stack.pop(); return; - } else if (disallowed_contents.has(parent.name)) { - // can this be a child of the parent element, or does it implicitly - // close it, like `
  • one
  • two`? - if (disallowed_contents.get(parent.name).has(name)) { - parent.end = start; - parser.stack.pop(); - } + } else if (closing_tag_omitted(parent.name, name)) { + parent.end = start; + parser.stack.pop(); } const unique_names: Set = new Set(); diff --git a/src/compiler/parse/utils/html.ts b/src/compiler/parse/utils/html.ts index 13ff553a3f..2b8c4213b7 100644 --- a/src/compiler/parse/utils/html.ts +++ b/src/compiler/parse/utils/html.ts @@ -112,3 +112,40 @@ function validate_code(code: number) { return NUL; } + +// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission +const disallowed_contents = new Map([ + ['li', new Set(['li'])], + ['dt', new Set(['dt', 'dd'])], + ['dd', new Set(['dt', 'dd'])], + [ + 'p', + new Set( + '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', new Set(['rt', 'rp'])], + ['rp', new Set(['rt', 'rp'])], + ['optgroup', new Set(['optgroup'])], + ['option', new Set(['option', 'optgroup'])], + ['thead', new Set(['tbody', 'tfoot'])], + ['tbody', new Set(['tbody', 'tfoot'])], + ['tfoot', new Set(['tbody'])], + ['tr', new Set(['tr', 'tbody'])], + ['td', new Set(['td', 'th', 'tr'])], + ['th', new Set(['td', 'th', 'tr'])], +]); + +// can this be a child of the parent element, or does it implicitly +// close it, like `
  • one
  • two`? +export function closing_tag_omitted(current: string, next?: string) { + if (disallowed_contents.has(current)) { + if (!next || disallowed_contents.get(current).has(next)) { + return true; + } + } + + return false; +} diff --git a/test/parser/samples/implicitly-closed-li-block/input.svelte b/test/parser/samples/implicitly-closed-li-block/input.svelte new file mode 100644 index 0000000000..05fc778999 --- /dev/null +++ b/test/parser/samples/implicitly-closed-li-block/input.svelte @@ -0,0 +1,7 @@ + diff --git a/test/parser/samples/implicitly-closed-li-block/output.json b/test/parser/samples/implicitly-closed-li-block/output.json new file mode 100644 index 0000000000..4a75a34223 --- /dev/null +++ b/test/parser/samples/implicitly-closed-li-block/output.json @@ -0,0 +1,94 @@ +{ + "html": { + "start": 0, + "end": 51, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 51, + "type": "Element", + "name": "ul", + "attributes": [], + "children": [ + { + "start": 4, + "end": 6, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + }, + { + "start": 6, + "end": 40, + "type": "Element", + "name": "li", + "attributes": [], + "children": [ + { + "start": 10, + "end": 13, + "type": "Text", + "raw": "a\n\t", + "data": "a\n\t" + }, + { + "start": 13, + "end": 38, + "type": "IfBlock", + "expression": { + "type": "Literal", + "start": 18, + "end": 22, + "value": true, + "raw": "true" + }, + "children": [ + { + "start": 26, + "end": 33, + "type": "Element", + "name": "li", + "attributes": [], + "children": [ + { + "start": 30, + "end": 33, + "type": "Text", + "raw": "b\n\t", + "data": "b\n\t" + } + ] + } + ] + }, + { + "start": 38, + "end": 40, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + } + ] + }, + { + "start": 40, + "end": 46, + "type": "Element", + "name": "li", + "attributes": [], + "children": [ + { + "start": 44, + "end": 46, + "type": "Text", + "raw": "c\n", + "data": "c\n" + } + ] + } + ] + } + ] + } +} \ No newline at end of file