diff --git a/src/compiler/parse/state/tag.js b/src/compiler/parse/state/tag.js index 8fde01f952..78e0979982 100644 --- a/src/compiler/parse/state/tag.js +++ b/src/compiler/parse/state/tag.js @@ -11,43 +11,46 @@ const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; /** Invalid attribute characters if the attribute is not surrounded by quotes */ const regex_starts_with_invalid_attr_value = /^(\/>|[\s"'=<>`])/; const meta_tags = new Map([ - ['svelte:head', 'Head'], - ['svelte:options', 'Options'], - ['svelte:window', 'Window'], - ['svelte:document', 'Document'], - ['svelte:body', 'Body'] + ['svelte:head', 'Head'], + ['svelte:options', 'Options'], + ['svelte:window', 'Window'], + ['svelte:document', 'Document'], + ['svelte:body', 'Body'] ]); -const valid_meta_tags = Array.from(meta_tags.keys()).concat('svelte:self', 'svelte:component', 'svelte:fragment', 'svelte:element'); +const valid_meta_tags = Array.from(meta_tags.keys()).concat( + 'svelte:self', + 'svelte:component', + 'svelte:fragment', + 'svelte:element' +); const specials = new Map([ - [ - 'script', - { - read: read_script, - property: 'js' - } - ], - [ - 'style', - { - read: read_style, - property: 'css' - } - ] + [ + 'script', + { + read: read_script, + property: 'js' + } + ], + [ + 'style', + { + read: read_style, + property: 'css' + } + ] ]); const SELF = /^svelte:self(?=[\s/>])/; const COMPONENT = /^svelte:component(?=[\s/>])/; const SLOT = /^svelte:fragment(?=[\s/>])/; const ELEMENT = /^svelte:element(?=[\s/>])/; function parent_is_head(stack) { - let i = stack.length; - while (i--) { - const { type } = stack[i]; - if (type === 'Head') - return true; - if (type === 'Element' || type === 'InlineComponent') - return false; - } - return false; + let i = stack.length; + while (i--) { + const { type } = stack[i]; + if (type === 'Head') return true; + if (type === 'Element' || type === 'InlineComponent') return false; + } + return false; } const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i; const regex_closing_comment = /-->/; @@ -57,165 +60,175 @@ const regex_capital_letter = /[A-Z]/; * @param {Parser} parser */ export default function tag(parser) { - const start = parser.index++; - let parent = parser.current(); - if (parser.eat('!--')) { - const data = parser.read_until(regex_closing_comment); - parser.eat('-->', true, parser_errors.unclosed_comment); - parser.current().children.push({ - start, - end: parser.index, - type: 'Comment', - data, - ignores: extract_svelte_ignore(data) - }); - return; - } - const is_closing_tag = parser.eat('/'); - const name = read_tag_name(parser); - if (meta_tags.has(name)) { - const slug = meta_tags.get(name).toLowerCase(); - if (is_closing_tag) { - if ((name === 'svelte:window' || name === 'svelte:body') && - parser.current().children.length) { - parser.error(parser_errors.invalid_element_content(slug, name), parser.current().children[0].start); - } - } - else { - if (name in parser.meta_tags) { - parser.error(parser_errors.duplicate_element(slug, name), start); - } - if (parser.stack.length > 1) { - parser.error(parser_errors.invalid_element_placement(slug, name), start); - } - parser.meta_tags[name] = true; - } - } - const type = meta_tags.has(name) - ? meta_tags.get(name) - : regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' - ? 'InlineComponent' - : name === 'svelte:fragment' - ? 'SlotTemplate' - : name === 'title' && parent_is_head(parser.stack) - ? 'Title' - : name === 'slot' - ? 'Slot' - : 'Element'; + const start = parser.index++; + let parent = parser.current(); + if (parser.eat('!--')) { + const data = parser.read_until(regex_closing_comment); + parser.eat('-->', true, parser_errors.unclosed_comment); + parser.current().children.push({ + start, + end: parser.index, + type: 'Comment', + data, + ignores: extract_svelte_ignore(data) + }); + return; + } + const is_closing_tag = parser.eat('/'); + const name = read_tag_name(parser); + if (meta_tags.has(name)) { + const slug = meta_tags.get(name).toLowerCase(); + if (is_closing_tag) { + if ( + (name === 'svelte:window' || name === 'svelte:body') && + parser.current().children.length + ) { + parser.error( + parser_errors.invalid_element_content(slug, name), + parser.current().children[0].start + ); + } + } else { + if (name in parser.meta_tags) { + parser.error(parser_errors.duplicate_element(slug, name), start); + } + if (parser.stack.length > 1) { + parser.error(parser_errors.invalid_element_placement(slug, name), start); + } + parser.meta_tags[name] = true; + } + } + const type = meta_tags.has(name) + ? meta_tags.get(name) + : regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' + ? 'InlineComponent' + : name === 'svelte:fragment' + ? 'SlotTemplate' + : name === 'title' && parent_is_head(parser.stack) + ? 'Title' + : name === 'slot' + ? 'Slot' + : 'Element'; - /** - * @type {TemplateNode} - */ - const element = { - start, - end: null, - type, - name, - attributes: [], - children: [] - }; - parser.allow_whitespace(); - if (is_closing_tag) { - if (is_void(name)) { - parser.error(parser_errors.invalid_void_content(name), start); - } - parser.eat('>', true); - // close any elements that don't have their own closing tags, e.g.

- while (parent.name !== name) { - if (parent.type !== 'Element') { - const error = parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name - ? parser_errors.invalid_closing_tag_autoclosed(name, parser.last_auto_closed_tag.reason) - : parser_errors.invalid_closing_tag_unopened(name); - parser.error(error, start); - } - parent.end = start; - parser.stack.pop(); - parent = parser.current(); - } - parent.end = parser.index; - parser.stack.pop(); - if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) { - parser.last_auto_closed_tag = null; - } - return; - } - else if (closing_tag_omitted(parent.name, name)) { - parent.end = start; - parser.stack.pop(); - parser.last_auto_closed_tag = { - tag: parent.name, - reason: name, - depth: parser.stack.length - }; - } + /** + * @type {TemplateNode} + */ + const element = { + start, + end: null, + type, + name, + attributes: [], + children: [] + }; + parser.allow_whitespace(); + if (is_closing_tag) { + if (is_void(name)) { + parser.error(parser_errors.invalid_void_content(name), start); + } + parser.eat('>', true); + // close any elements that don't have their own closing tags, e.g.

+ while (parent.name !== name) { + if (parent.type !== 'Element') { + const error = + parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name + ? parser_errors.invalid_closing_tag_autoclosed(name, parser.last_auto_closed_tag.reason) + : parser_errors.invalid_closing_tag_unopened(name); + parser.error(error, start); + } + parent.end = start; + parser.stack.pop(); + parent = parser.current(); + } + parent.end = parser.index; + parser.stack.pop(); + if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) { + parser.last_auto_closed_tag = null; + } + return; + } else if (closing_tag_omitted(parent.name, name)) { + parent.end = start; + parser.stack.pop(); + parser.last_auto_closed_tag = { + tag: parent.name, + reason: name, + depth: parser.stack.length + }; + } - /** - * @type {Set} - */ - const unique_names = new Set(); - let attribute; - while ((attribute = read_attribute(parser, unique_names))) { - element.attributes.push(attribute); - parser.allow_whitespace(); - } - if (name === 'svelte:component') { - const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this'); - if (index === -1) { - parser.error(parser_errors.missing_component_definition, start); - } - const definition = element.attributes.splice(index, 1)[0]; - if (definition.value === true || - definition.value.length !== 1 || - definition.value[0].type === 'Text') { - parser.error(parser_errors.invalid_component_definition, definition.start); - } - element.expression = definition.value[0].expression; - } - if (name === 'svelte:element') { - const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this'); - if (index === -1) { - parser.error(parser_errors.missing_element_definition, start); - } - const definition = element.attributes.splice(index, 1)[0]; - if (definition.value === true) { - parser.error(parser_errors.invalid_element_definition, definition.start); - } - element.tag = definition.value[0].data || definition.value[0].expression; - } - // special cases – top-level