From 27ee8fd26df6d1d0fa41f2d960e61697aabbeea5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 27 Aug 2024 13:49:06 -0400 Subject: [PATCH] simplify some parser logic, improve some compiler errors --- .../compiler/phases/1-parse/state/element.js | 59 ++++--------------- .../phases/2-analyze/visitors/SvelteSelf.js | 13 ++++ .../samples/self-reference/_config.js | 2 +- .../samples/svelte-selfdestructive/_config.js | 2 +- 4 files changed, 26 insertions(+), 50 deletions(-) diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 99d3aeff10..f7784aea90 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -12,6 +12,7 @@ import { create_fragment } from '../utils/create.js'; import { create_attribute, create_expression_metadata } from '../../nodes.js'; import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js'; import { closing_tag_omitted } from '../../../../html-tree-validation.js'; +import { list } from '../../../utils/string.js'; // eslint-disable-next-line no-useless-escape const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; @@ -20,7 +21,7 @@ const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const regex_starts_with_invalid_attr_value = /^(\/>|[\s"'=<>`])/; /** @type {Map} */ -const root_only_meta_tags = new Map([ +const root_only_special_elements = new Map([ ['svelte:head', 'SvelteHead'], ['svelte:options', 'SvelteOptions'], ['svelte:window', 'SvelteWindow'], @@ -29,21 +30,14 @@ const root_only_meta_tags = new Map([ ]); /** @type {Map} */ -const meta_tags = new Map([ - ...root_only_meta_tags, +const special_elements = new Map([ + ...root_only_special_elements, ['svelte:element', 'SvelteElement'], ['svelte:component', 'SvelteComponent'], ['svelte:self', 'SvelteSelf'], ['svelte:fragment', 'SvelteFragment'] ]); -const valid_meta_tags = Array.from(meta_tags.keys()); - -const SELF = /^svelte:self(?=[\s/>])/; -const COMPONENT = /^svelte:component(?=[\s/>])/; -const SLOT = /^svelte:fragment(?=[\s/>])/; -const ELEMENT = /^svelte:element(?=[\s/>])/; - /** @param {Compiler.TemplateNode[]} stack */ function parent_is_head(stack) { let i = stack.length; @@ -103,7 +97,7 @@ export default function element(parser) { const name = read_tag_name(parser); - if (root_only_meta_tags.has(name)) { + if (root_only_special_elements.has(name)) { if (is_closing_tag) { if ( ['svelte:options', 'svelte:window', 'svelte:body', 'svelte:document'].includes(name) && @@ -127,8 +121,8 @@ export default function element(parser) { } } - const type = meta_tags.has(name) - ? meta_tags.get(name) + const type = special_elements.has(name) + ? special_elements.get(name) : regex_component_name.test(name) ? 'Component' : name === 'title' && parent_is_head(parser.stack) @@ -392,48 +386,17 @@ const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/; function read_tag_name(parser) { const start = parser.index; - if (parser.read(SELF)) { - // check we're inside a block, otherwise this - // will cause infinite recursion - let i = parser.stack.length; - let legal = false; - - while (i--) { - const fragment = parser.stack[i]; - if ( - fragment.type === 'IfBlock' || - fragment.type === 'EachBlock' || - fragment.type === 'Component' || - fragment.type === 'SnippetBlock' - ) { - legal = true; - break; - } - } - - if (!legal) { - e.svelte_self_invalid_placement(start); - } - - return 'svelte:self'; - } - - if (parser.read(COMPONENT)) return 'svelte:component'; - if (parser.read(ELEMENT)) return 'svelte:element'; - - if (parser.read(SLOT)) return 'svelte:fragment'; - const name = parser.read_until(regex_whitespace_or_slash_or_closing_tag); + if (special_elements.has(name)) return name; - if (meta_tags.has(name)) return name; + const end = start + name.length; if (name.startsWith('svelte:')) { - const list = `${valid_meta_tags.slice(0, -1).join(', ')} or ${valid_meta_tags[valid_meta_tags.length - 1]}`; - e.svelte_meta_invalid_tag(start, list); + e.svelte_meta_invalid_tag({ start, end }, list(Array.from(special_elements.keys()))); } if (!valid_tag_name.test(name)) { - e.element_invalid_tag_name(start); + e.element_invalid_tag_name({ start, end }); } return name; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js index 5b259bb636..7406ad31dc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js @@ -1,11 +1,24 @@ /** @import { SvelteSelf } from '#compiler' */ /** @import { Context } from '../types' */ import { visit_component } from './shared/component.js'; +import * as e from '../../../errors.js'; /** * @param {SvelteSelf} node * @param {Context} context */ export function SvelteSelf(node, context) { + const valid = context.path.some( + (node) => + node.type === 'IfBlock' || + node.type === 'EachBlock' || + node.type === 'Component' || + node.type === 'SnippetBlock' + ); + + if (!valid) { + e.svelte_self_invalid_placement(node); + } + visit_component(node, context); } diff --git a/packages/svelte/tests/compiler-errors/samples/self-reference/_config.js b/packages/svelte/tests/compiler-errors/samples/self-reference/_config.js index 74e7e4cbe5..55edfec975 100644 --- a/packages/svelte/tests/compiler-errors/samples/self-reference/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/self-reference/_config.js @@ -5,6 +5,6 @@ export default test({ code: 'svelte_self_invalid_placement', message: '`` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components', - position: [1, 1] + position: [0, 14] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/svelte-selfdestructive/_config.js b/packages/svelte/tests/compiler-errors/samples/svelte-selfdestructive/_config.js index bc995db523..371ddb81db 100644 --- a/packages/svelte/tests/compiler-errors/samples/svelte-selfdestructive/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/svelte-selfdestructive/_config.js @@ -5,6 +5,6 @@ export default test({ code: 'svelte_meta_invalid_tag', message: 'Valid `` tag names are svelte:head, svelte:options, svelte:window, svelte:document, svelte:body, svelte:element, svelte:component, svelte:self or svelte:fragment', - position: [10, 10] + position: [10, 32] } });