fix: error on invalid element name (#13057)

closes #12925
pull/13046/head
Rich Harris 2 months ago committed by GitHub
parent 5facd5b476
commit 77096a1f30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: error on invalid element name

@ -100,10 +100,6 @@
> This type of directive is not valid on components > This type of directive is not valid on components
## component_invalid_name
> Component name must be a valid variable name or dot notation expression
## const_tag_cycle ## const_tag_cycle
> Cyclical dependency detected: %cycle% > Cyclical dependency detected: %cycle%
@ -136,10 +132,6 @@
> `</%name%>` attempted to close element that was already automatically closed by `<%reason%>` (cannot nest `<%reason%>` inside `<%name%>`) > `</%name%>` attempted to close element that was already automatically closed by `<%reason%>` (cannot nest `<%reason%>` inside `<%name%>`)
## element_invalid_tag_name
> Expected valid tag name
## element_unclosed ## element_unclosed
> `<%name%>` was left open > `<%name%>` was left open
@ -358,6 +350,10 @@ HTML restricts where certain elements can appear. In case of a violation the bro
> `<svelte:self>` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components > `<svelte:self>` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components
## tag_invalid_name
> Expected a valid element or component name. Components must have a valid variable name or dot notation expression
## tag_invalid_placement ## tag_invalid_placement
> {@%name% ...} tag cannot be %location% > {@%name% ...} tag cannot be %location%

@ -786,15 +786,6 @@ export function component_invalid_directive(node) {
e(node, "component_invalid_directive", "This type of directive is not valid on components"); e(node, "component_invalid_directive", "This type of directive is not valid on components");
} }
/**
* Component name must be a valid variable name or dot notation expression
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function component_invalid_name(node) {
e(node, "component_invalid_name", "Component name must be a valid variable name or dot notation expression");
}
/** /**
* Cyclical dependency detected: %cycle% * Cyclical dependency detected: %cycle%
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node
@ -872,15 +863,6 @@ export function element_invalid_closing_tag_autoclosed(node, name, reason) {
e(node, "element_invalid_closing_tag_autoclosed", `\`</${name}>\` attempted to close element that was already automatically closed by \`<${reason}>\` (cannot nest \`<${reason}>\` inside \`<${name}>\`)`); e(node, "element_invalid_closing_tag_autoclosed", `\`</${name}>\` attempted to close element that was already automatically closed by \`<${reason}>\` (cannot nest \`<${reason}>\` inside \`<${name}>\`)`);
} }
/**
* Expected valid tag name
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function element_invalid_tag_name(node) {
e(node, "element_invalid_tag_name", "Expected valid tag name");
}
/** /**
* `<%name%>` was left open * `<%name%>` was left open
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node
@ -1378,6 +1360,15 @@ export function svelte_self_invalid_placement(node) {
e(node, "svelte_self_invalid_placement", "`<svelte:self>` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components"); e(node, "svelte_self_invalid_placement", "`<svelte:self>` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components");
} }
/**
* Expected a valid element or component name. Components must have a valid variable name or dot notation expression
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function tag_invalid_name(node) {
e(node, "tag_invalid_name", "Expected a valid element or component name. Components must have a valid variable name or dot notation expression");
}
/** /**
* {@%name% ...} tag cannot be %location% * {@%name% ...} tag cannot be %location%
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node

@ -17,14 +17,14 @@ import { list } from '../../../utils/string.js';
const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/; const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/;
const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i; const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i;
const regex_closing_comment = /-->/; const regex_closing_comment = /-->/;
const regex_component_name = /^(?:[A-Z]|[A-Za-z][A-Za-z0-9_$]*\.)/;
const regex_valid_component_name =
/^(?:[A-Z][A-Za-z0-9_$.]*|[a-z][A-Za-z0-9_$]*\.[A-Za-z0-9_$])[A-Za-z0-9_$.]*$/;
const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/; const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/;
const regex_token_ending_character = /[\s=/>"']/; const regex_token_ending_character = /[\s=/>"']/;
const regex_starts_with_quote_characters = /^["']/; const regex_starts_with_quote_characters = /^["']/;
const regex_attribute_value = /^(?:"([^"]*)"|'([^'])*'|([^>\s]+))/; const regex_attribute_value = /^(?:"([^"]*)"|'([^'])*'|([^>\s]+))/;
const regex_valid_tag_name = /^!?[a-zA-Z]{1,}:?[a-zA-Z0-9-]*/; const regex_valid_element_name =
/^(?:![a-zA-Z]+|[a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|[a-zA-Z][a-zA-Z0-9]*:[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9])$/;
const regex_valid_component_name =
/^(?:[A-Z][A-Za-z0-9_$.]*|[a-z][A-Za-z0-9_$]*(?:\.[A-Za-z0-9_$]+)+)$/;
/** @type {Map<string, Compiler.ElementLike['type']>} */ /** @type {Map<string, Compiler.ElementLike['type']>} */
const root_only_meta_tags = new Map([ const root_only_meta_tags = new Map([
@ -107,9 +107,9 @@ export default function element(parser) {
e.svelte_meta_invalid_tag(bounds, list(Array.from(meta_tags.keys()))); e.svelte_meta_invalid_tag(bounds, list(Array.from(meta_tags.keys())));
} }
if (!regex_valid_tag_name.test(name)) { if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) {
const bounds = { start: start + 1, end: start + 1 + name.length }; const bounds = { start: start + 1, end: start + 1 + name.length };
e.element_invalid_tag_name(bounds); e.tag_invalid_name(bounds);
} }
if (root_only_meta_tags.has(name)) { if (root_only_meta_tags.has(name)) {
@ -126,7 +126,7 @@ export default function element(parser) {
const type = meta_tags.has(name) const type = meta_tags.has(name)
? meta_tags.get(name) ? meta_tags.get(name)
: regex_component_name.test(name) : regex_valid_component_name.test(name)
? 'Component' ? 'Component'
: name === 'title' && parent_is_head(parser.stack) : name === 'title' && parent_is_head(parser.stack)
? 'TitleElement' ? 'TitleElement'
@ -135,10 +135,6 @@ export default function element(parser) {
? 'SlotElement' ? 'SlotElement'
: 'RegularElement'; : 'RegularElement';
if (type === 'Component' && !regex_valid_component_name.test(name)) {
e.component_invalid_name({ start: start + 1, end: start + name.length + 1 });
}
/** @type {Compiler.ElementLike} */ /** @type {Compiler.ElementLike} */
const element = const element =
type === 'RegularElement' type === 'RegularElement'

@ -2,8 +2,9 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'component_invalid_name', code: 'tag_invalid_name',
message: 'Component name must be a valid variable name or dot notation expression', message:
'Expected a valid element or component name. Components must have a valid variable name or dot notation expression',
position: [1, 14] position: [1, 14]
} }
}); });

@ -0,0 +1,10 @@
import { test } from '../../test';
export default test({
error: {
code: 'tag_invalid_name',
message:
'Expected a valid element or component name. Components must have a valid variable name or dot notation expression',
position: [1, 8]
}
});
Loading…
Cancel
Save