From a86e52b17466ff8c5bd6268932da6954d08a87d6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 22 May 2025 09:58:14 -0400 Subject: [PATCH] XHTML compliance --- .changeset/forty-llamas-unite.md | 5 +++++ .../3-transform/client/transform-template/template.js | 9 +++++---- .../phases/3-transform/server/visitors/RegularElement.js | 6 ++++-- packages/svelte/src/internal/client/dom/reconciler.js | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 .changeset/forty-llamas-unite.md diff --git a/.changeset/forty-llamas-unite.md b/.changeset/forty-llamas-unite.md new file mode 100644 index 0000000000..933cece1c6 --- /dev/null +++ b/.changeset/forty-llamas-unite.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: XHTML compliance diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js index bb9a653f8c..8f7f8a1f43 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js @@ -104,10 +104,11 @@ function stringify(item) { if (value !== undefined) str += `="${escape_html(value, true)}"`; } - str += `>`; - str += item.children.map(stringify).join(''); - - if (!is_void(item.name)) { + if (is_void(item.name)) { + str += '/>'; // XHTML compliance + } else { + str += `>`; + str += item.children.map(stringify).join(''); str += ``; } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js index 5901cb4c50..2928ca9173 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js @@ -24,9 +24,11 @@ export function RegularElement(node, context) { context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea' }; + const node_is_void = is_void(node.name); + context.state.template.push(b.literal(`<${node.name}`)); const body = build_element_attributes(node, { ...context, state }); - context.state.template.push(b.literal('>')); + context.state.template.push(b.literal(node_is_void ? '/>' : '>')); // add `/>` for XHTML compliance if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { context.state.template.push( @@ -94,7 +96,7 @@ export function RegularElement(node, context) { ); } - if (!is_void(node.name)) { + if (!node_is_void) { state.template.push(b.literal(``)); } diff --git a/packages/svelte/src/internal/client/dom/reconciler.js b/packages/svelte/src/internal/client/dom/reconciler.js index 9897e08d53..8d3b5c0495 100644 --- a/packages/svelte/src/internal/client/dom/reconciler.js +++ b/packages/svelte/src/internal/client/dom/reconciler.js @@ -1,6 +1,6 @@ /** @param {string} html */ export function create_fragment_from_html(html) { var elem = document.createElement('template'); - elem.innerHTML = html; + elem.innerHTML = html.replaceAll('', ''); // XHTML compliance return elem.content; }