From e3ad1acc9000995316c42b4c249d7eca5c206690 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Sun, 15 Sep 2024 19:39:13 -0400 Subject: [PATCH] fix: follow spec for `customElement` option (#13247) * fix: follow spec for `customElement` option * tweak messages, add link to details that will be included on future docs site --------- Co-authored-by: Rich Harris --- .changeset/stale-rats-check.md | 5 ++++ .../messages/compile-errors/template.md | 10 +++++++- packages/svelte/src/compiler/errors.js | 13 ++++++++-- .../compiler/phases/1-parse/read/options.js | 25 ++++++++++++++++--- .../validator/samples/tag-emoji/_config.js | 7 ++++++ .../validator/samples/tag-emoji/input.svelte | 1 + .../validator/samples/tag-hyphen/_config.js | 7 ++++++ .../validator/samples/tag-hyphen/input.svelte | 1 + .../validator/samples/tag-invalid/errors.json | 2 +- .../samples/tag-reserved/errors.json | 14 +++++++++++ .../samples/tag-reserved/input.svelte | 1 + 11 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 .changeset/stale-rats-check.md create mode 100644 packages/svelte/tests/validator/samples/tag-emoji/_config.js create mode 100644 packages/svelte/tests/validator/samples/tag-emoji/input.svelte create mode 100644 packages/svelte/tests/validator/samples/tag-hyphen/_config.js create mode 100644 packages/svelte/tests/validator/samples/tag-hyphen/input.svelte create mode 100644 packages/svelte/tests/validator/samples/tag-reserved/errors.json create mode 100644 packages/svelte/tests/validator/samples/tag-reserved/input.svelte diff --git a/.changeset/stale-rats-check.md b/.changeset/stale-rats-check.md new file mode 100644 index 0000000000..61f1ba746e --- /dev/null +++ b/.changeset/stale-rats-check.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: follow spec for `customElement` option diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index a71716b06d..d606e4fd84 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -344,7 +344,15 @@ HTML restricts where certain elements can appear. In case of a violation the bro ## svelte_options_invalid_tagname -> Tag name must be two or more words joined by the "-" character +> Tag name must be lowercase and hyphenated + +See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name for more information on valid tag names + +## svelte_options_reserved_tagname + +> Tag name is reserved + +See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name for more information on valid tag names ## svelte_options_unknown_attribute diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 3facf74282..299154c988 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -1352,12 +1352,21 @@ export function svelte_options_invalid_customelement_shadow(node) { } /** - * Tag name must be two or more words joined by the "-" character + * Tag name must be lowercase and hyphenated * @param {null | number | NodeLike} node * @returns {never} */ export function svelte_options_invalid_tagname(node) { - e(node, "svelte_options_invalid_tagname", "Tag name must be two or more words joined by the \"-\" character"); + e(node, "svelte_options_invalid_tagname", "Tag name must be lowercase and hyphenated"); +} + +/** + * Tag name is reserved + * @param {null | number | NodeLike} node + * @returns {never} + */ +export function svelte_options_reserved_tagname(node) { + e(node, "svelte_options_reserved_tagname", "Tag name is reserved"); } /** diff --git a/packages/svelte/src/compiler/phases/1-parse/read/options.js b/packages/svelte/src/compiler/phases/1-parse/read/options.js index 6ea069e875..a36e101468 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/options.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/options.js @@ -3,8 +3,6 @@ import { NAMESPACE_MATHML, NAMESPACE_SVG } from '../../../../constants.js'; import * as e from '../../../errors.js'; -const regex_valid_tag_name = /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/; - /** * @param {AST.SvelteOptionsRaw} node * @returns {AST.Root['options']} @@ -229,6 +227,21 @@ function get_boolean_value(attribute) { return value; } +// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name +const tag_name_char = + '[a-z0-9_.\xB7\xC0-\xD6\xD8-\xF6\xF8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}-]'; +const regex_valid_tag_name = new RegExp(`^[a-z]${tag_name_char}*-${tag_name_char}*$`, 'u'); +const reserved_tag_names = [ + 'annotation-xml', + 'color-profile', + 'font-face', + 'font-face-src', + 'font-face-uri', + 'font-face-format', + 'font-face-name', + 'missing-glyph' +]; + /** * @param {any} attribute * @param {string | null} tag @@ -238,7 +251,11 @@ function validate_tag(attribute, tag) { if (typeof tag !== 'string') { e.svelte_options_invalid_tagname(attribute); } - if (tag && !regex_valid_tag_name.test(tag)) { - e.svelte_options_invalid_tagname(attribute); + if (tag) { + if (!regex_valid_tag_name.test(tag)) { + e.svelte_options_invalid_tagname(attribute); + } else if (reserved_tag_names.includes(tag)) { + e.svelte_options_reserved_tagname(attribute); + } } } diff --git a/packages/svelte/tests/validator/samples/tag-emoji/_config.js b/packages/svelte/tests/validator/samples/tag-emoji/_config.js new file mode 100644 index 0000000000..307385cb57 --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-emoji/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + customElement: true + } +}); diff --git a/packages/svelte/tests/validator/samples/tag-emoji/input.svelte b/packages/svelte/tests/validator/samples/tag-emoji/input.svelte new file mode 100644 index 0000000000..3684c143a7 --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-emoji/input.svelte @@ -0,0 +1 @@ + diff --git a/packages/svelte/tests/validator/samples/tag-hyphen/_config.js b/packages/svelte/tests/validator/samples/tag-hyphen/_config.js new file mode 100644 index 0000000000..307385cb57 --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-hyphen/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + customElement: true + } +}); diff --git a/packages/svelte/tests/validator/samples/tag-hyphen/input.svelte b/packages/svelte/tests/validator/samples/tag-hyphen/input.svelte new file mode 100644 index 0000000000..222ae2930e --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-hyphen/input.svelte @@ -0,0 +1 @@ + diff --git a/packages/svelte/tests/validator/samples/tag-invalid/errors.json b/packages/svelte/tests/validator/samples/tag-invalid/errors.json index 848f51af98..0ebbfd8fe0 100644 --- a/packages/svelte/tests/validator/samples/tag-invalid/errors.json +++ b/packages/svelte/tests/validator/samples/tag-invalid/errors.json @@ -1,7 +1,7 @@ [ { "code": "svelte_options_invalid_tagname", - "message": "Tag name must be two or more words joined by the \"-\" character", + "message": "Tag name must be lowercase and hyphenated", "start": { "line": 1, "column": 16 diff --git a/packages/svelte/tests/validator/samples/tag-reserved/errors.json b/packages/svelte/tests/validator/samples/tag-reserved/errors.json new file mode 100644 index 0000000000..29b75b0cbd --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-reserved/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "svelte_options_reserved_tagname", + "message": "Tag name is reserved", + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 41 + } + } +] diff --git a/packages/svelte/tests/validator/samples/tag-reserved/input.svelte b/packages/svelte/tests/validator/samples/tag-reserved/input.svelte new file mode 100644 index 0000000000..0326794519 --- /dev/null +++ b/packages/svelte/tests/validator/samples/tag-reserved/input.svelte @@ -0,0 +1 @@ +