From e5d0cd2eb47834bf7584a250e749a564c73a8f1d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 31 May 2025 15:38:49 -0400
Subject: [PATCH 1/5] Version Packages (#16045)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.changeset/orange-tips-pull.md | 5 -----
packages/svelte/CHANGELOG.md | 6 ++++++
packages/svelte/package.json | 2 +-
packages/svelte/src/version.js | 2 +-
4 files changed, 8 insertions(+), 7 deletions(-)
delete mode 100644 .changeset/orange-tips-pull.md
diff --git a/.changeset/orange-tips-pull.md b/.changeset/orange-tips-pull.md
deleted file mode 100644
index 4d474dafaf..0000000000
--- a/.changeset/orange-tips-pull.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'svelte': patch
----
-
-fix: treat transitive dependencies of each blocks as mutable in legacy mode if item is mutated
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index da35e7cd68..4623f64766 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
# svelte
+## 5.33.11
+
+### Patch Changes
+
+- fix: treat transitive dependencies of each blocks as mutable in legacy mode if item is mutated ([#16038](https://github.com/sveltejs/svelte/pull/16038))
+
## 5.33.10
### Patch Changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index d1cf6c428b..2ee065730c 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
- "version": "5.33.10",
+ "version": "5.33.11",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js
index eef48b0cbb..4cbf73e7f9 100644
--- a/packages/svelte/src/version.js
+++ b/packages/svelte/src/version.js
@@ -4,5 +4,5 @@
* The current version, as set in package.json.
* @type {string}
*/
-export const VERSION = '5.33.10';
+export const VERSION = '5.33.11';
export const PUBLIC_VERSION = '5';
From b5fcd112c60012276e3416e65feae3ac16bfdaf1 Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Sun, 1 Jun 2025 00:53:48 -0700
Subject: [PATCH 2/5] fix: correctly transform reassignments to class fields in
SSR mode (#16051)
* fix: correctly transform reassignments to class fields in SSR mode
* add test, fix more stuff
* fix
---
.changeset/rude-drinks-relate.md | 5 ++++
.../server/visitors/AssignmentExpression.js | 13 +++++++++--
.../_expected/client/index.svelte.js | 23 ++++++++++++++++++-
.../_expected/server/index.svelte.js | 23 ++++++++++++++++++-
.../index.svelte | 7 ++++--
5 files changed, 65 insertions(+), 6 deletions(-)
create mode 100644 .changeset/rude-drinks-relate.md
diff --git a/.changeset/rude-drinks-relate.md b/.changeset/rude-drinks-relate.md
new file mode 100644
index 0000000000..d0eab6ba11
--- /dev/null
+++ b/.changeset/rude-drinks-relate.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: correctly transform reassignments to class fields in SSR mode
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js
index 466682fb82..49a284d12b 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js
@@ -24,7 +24,12 @@ export function AssignmentExpression(node, context) {
* @returns {Expression | null}
*/
function build_assignment(operator, left, right, context) {
- if (context.state.analysis.runes && left.type === 'MemberExpression') {
+ if (
+ context.state.analysis.runes &&
+ left.type === 'MemberExpression' &&
+ left.object.type === 'ThisExpression' &&
+ !left.computed
+ ) {
const name = get_name(left.property);
const field = name && context.state.state_fields.get(name);
@@ -44,7 +49,11 @@ function build_assignment(operator, left, right, context) {
/** @type {Expression} */ (context.visit(right))
);
}
- } else if (field && (field.type === '$derived' || field.type === '$derived.by')) {
+ } else if (
+ field &&
+ (field.type === '$derived' || field.type === '$derived.by') &&
+ left.property.type === 'PrivateIdentifier'
+ ) {
let value = /** @type {Expression} */ (
context.visit(build_assignment_value(operator, left, right))
);
diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
index 2133974176..c9725d6718 100644
--- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
@@ -5,7 +5,7 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
$.push($$props, true);
class Foo {
- #a = $.state();
+ #a = $.state(0);
get a() {
return $.get(this.#a);
@@ -16,10 +16,31 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
}
#b = $.state();
+ #foo = $.derived(() => ({ bar: this.a * 2 }));
+
+ get foo() {
+ return $.get(this.#foo);
+ }
+
+ set foo(value) {
+ $.set(this.#foo, value);
+ }
+
+ #bar = $.derived(() => ({ baz: this.foo }));
+
+ get bar() {
+ return $.get(this.#bar);
+ }
+
+ set bar(value) {
+ $.set(this.#bar, value);
+ }
constructor() {
this.a = 1;
$.set(this.#b, 2);
+ this.foo.bar = 3;
+ this.bar = 4;
}
}
diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
index 2a115a4983..abfc264fea 100644
--- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
@@ -4,12 +4,33 @@ export default function Class_state_field_constructor_assignment($$payload, $$pr
$.push();
class Foo {
- a;
+ a = 0;
#b;
+ #foo = $.derived(() => ({ bar: this.a * 2 }));
+
+ get foo() {
+ return this.#foo();
+ }
+
+ set foo($$value) {
+ return this.#foo($$value);
+ }
+
+ #bar = $.derived(() => ({ baz: this.foo }));
+
+ get bar() {
+ return this.#bar();
+ }
+
+ set bar($$value) {
+ return this.#bar($$value);
+ }
constructor() {
this.a = 1;
this.#b = 2;
+ this.foo.bar = 3;
+ this.bar = 4;
}
}
diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/index.svelte b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/index.svelte
index a3ff5917e7..127cfd4d2a 100644
--- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/index.svelte
+++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/index.svelte
@@ -1,11 +1,14 @@
From 03273b78791c3359865040081fc58b53de782db8 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Sun, 1 Jun 2025 16:13:30 -0400
Subject: [PATCH 3/5] chore: better HTML normalization test helper
---
packages/svelte/tests/html_equal.js | 96 +++++++++++--------
.../samples/binding-select/_config.js | 4 +-
.../samples/input-list/_config.js | 4 +-
.../samples/namespace-html/_config.js | 2 +-
.../samples/select-in-each/_config.js | 4 +-
.../svelte/tests/runtime-legacy/shared.ts | 4 +-
6 files changed, 66 insertions(+), 48 deletions(-)
diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js
index 4c9e2a7253..f3ab5c54ca 100644
--- a/packages/svelte/tests/html_equal.js
+++ b/packages/svelte/tests/html_equal.js
@@ -3,6 +3,15 @@ import { assert } from 'vitest';
/** @param {Element} node */
function clean_children(node) {
let previous = null;
+ let has_element_children = false;
+ let template =
+ node.nodeName === 'TEMPLATE' ? /** @type {HTMLTemplateElement} */ (node) : undefined;
+
+ if (template) {
+ const div = document.createElement('div');
+ div.append(template.content);
+ node = div;
+ }
// sort attributes
const attributes = Array.from(node.attributes).sort((a, b) => {
@@ -14,6 +23,10 @@ function clean_children(node) {
});
attributes.forEach((attr) => {
+ if ((attr.name === 'onload' || attr.name === 'onerror') && attr.value === 'this.__e=event') {
+ return;
+ }
+
node.setAttribute(attr.name, attr.value);
});
@@ -27,23 +40,35 @@ function clean_children(node) {
node.tagName !== 'tspan'
) {
node.removeChild(child);
+ continue;
}
- text.data = text.data.replace(/[ \t\n\r\f]+/g, '\n');
+ text.data = text.data.replace(/[^\S]+/g, ' ');
if (previous && previous.nodeType === 3) {
const prev = /** @type {Text} */ (previous);
prev.data += text.data;
- prev.data = prev.data.replace(/[ \t\n\r\f]+/g, '\n');
-
node.removeChild(text);
+
text = prev;
+ text.data = text.data.replace(/[^\S]+/g, ' ');
+
+ continue;
}
} else if (child.nodeType === 8) {
// comment
- // do nothing
- } else {
+ child.remove();
+ continue;
+ } else if (child.nodeType === 1) {
+ if (previous?.nodeType === 3) {
+ const prev = /** @type {Text} */ (previous);
+ prev.data = prev.data.replace(/^[^\S]+$/, '\n');
+ } else if (previous?.nodeType === 1) {
+ node.insertBefore(document.createTextNode('\n'), child);
+ }
+
+ has_element_children = true;
clean_children(/** @type {Element} */ (child));
}
@@ -53,37 +78,35 @@ function clean_children(node) {
// collapse whitespace
if (node.firstChild && node.firstChild.nodeType === 3) {
const text = /** @type {Text} */ (node.firstChild);
- text.data = text.data.replace(/^[ \t\n\r\f]+/, '');
- if (!text.data.length) node.removeChild(text);
+ text.data = text.data.trimStart();
}
if (node.lastChild && node.lastChild.nodeType === 3) {
const text = /** @type {Text} */ (node.lastChild);
- text.data = text.data.replace(/[ \t\n\r\f]+$/, '');
- if (!text.data.length) node.removeChild(text);
+ text.data = text.data.trimEnd();
+ }
+
+ if (has_element_children && node.parentNode) {
+ node.innerHTML = `\n\t${node.innerHTML.replace(/\n/g, '\n\t')}\n`;
+ }
+
+ if (template) {
+ template.innerHTML = node.innerHTML;
}
}
/**
* @param {Window} window
* @param {string} html
- * @param {{ removeDataSvelte?: boolean, preserveComments?: boolean }} param2
+ * @param {{ preserveComments?: boolean }} opts
*/
-export function normalize_html(
- window,
- html,
- { removeDataSvelte = false, preserveComments = false }
-) {
+export function normalize_html(window, html, { preserveComments = false } = {}) {
try {
const node = window.document.createElement('div');
- node.innerHTML = html
- .replace(/()/g, preserveComments ? '$1' : '')
- .replace(/(data-svelte-h="[^"]+")/g, removeDataSvelte ? '' : '$1')
- .replace(/>[ \t\n\r\f]+<')
- // Strip out the special onload/onerror hydration events from the test output
- .replace(/\s?onerror="this.__e=event"|\s?onload="this.__e=event"/g, '')
- .trim();
+ node.innerHTML = html.replace(/()/g, preserveComments ? '$1' : '').trim();
+
clean_children(node);
+
return node.innerHTML;
} catch (err) {
throw new Error(`Failed to normalize HTML:\n${html}\nCause: ${err}`);
@@ -98,10 +121,7 @@ export function normalize_new_line(html) {
return html.replace(/\r\n/g, '\n');
}
-/**
- * @param {{ removeDataSvelte?: boolean }} options
- */
-export function setup_html_equal(options = {}) {
+export function setup_html_equal() {
/**
* @param {string} actual
* @param {string} expected
@@ -109,11 +129,7 @@ export function setup_html_equal(options = {}) {
*/
const assert_html_equal = (actual, expected, message) => {
try {
- assert.deepEqual(
- normalize_html(window, actual, options),
- normalize_html(window, expected, options),
- message
- );
+ assert.deepEqual(normalize_html(window, actual), normalize_html(window, expected), message);
} catch (e) {
if (Error.captureStackTrace)
Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal);
@@ -137,15 +153,17 @@ export function setup_html_equal(options = {}) {
try {
assert.deepEqual(
withoutNormalizeHtml
- ? normalize_new_line(actual.trim())
- .replace(/(\sdata-svelte-h="[^"]+")/g, options.removeDataSvelte ? '' : '$1')
- .replace(/()/g, preserveComments !== false ? '$1' : '')
- : normalize_html(window, actual.trim(), { ...options, preserveComments }),
+ ? normalize_new_line(actual.trim()).replace(
+ /()/g,
+ preserveComments !== false ? '$1' : ''
+ )
+ : normalize_html(window, actual.trim(), { preserveComments }),
withoutNormalizeHtml
- ? normalize_new_line(expected.trim())
- .replace(/(\sdata-svelte-h="[^"]+")/g, options.removeDataSvelte ? '' : '$1')
- .replace(/()/g, preserveComments !== false ? '$1' : '')
- : normalize_html(window, expected.trim(), { ...options, preserveComments }),
+ ? normalize_new_line(expected.trim()).replace(
+ /()/g,
+ preserveComments !== false ? '$1' : ''
+ )
+ : normalize_html(window, expected.trim(), { preserveComments }),
message
);
} catch (e) {
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js
index 2507f5fc83..996f68e39f 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js
@@ -25,7 +25,7 @@ export default test({
selected: one
@@ -54,7 +54,7 @@ export default test({
selected: two
diff --git a/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js b/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js
index fe6a29207d..1e95aaafa6 100644
--- a/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js
@@ -4,7 +4,9 @@ export default test({
html: `
`
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js b/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js
index 3be9f0e925..b7ecd04def 100644
--- a/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js
@@ -9,7 +9,7 @@ export default test({
hi
`,
diff --git a/packages/svelte/tests/runtime-legacy/samples/select-in-each/_config.js b/packages/svelte/tests/runtime-legacy/samples/select-in-each/_config.js
index 4c94ea1e01..df03b7a053 100644
--- a/packages/svelte/tests/runtime-legacy/samples/select-in-each/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/select-in-each/_config.js
@@ -7,7 +7,7 @@ export default test({
target.innerHTML,
`
selected: a
@@ -23,7 +23,7 @@ export default test({
target.innerHTML,
`
selected: b
diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts
index 690a7e3d98..c94c4ed422 100644
--- a/packages/svelte/tests/runtime-legacy/shared.ts
+++ b/packages/svelte/tests/runtime-legacy/shared.ts
@@ -86,9 +86,7 @@ function unhandled_rejection_handler(err: Error) {
const listeners = process.rawListeners('unhandledRejection');
-const { assert_html_equal, assert_html_equal_with_options } = setup_html_equal({
- removeDataSvelte: true
-});
+const { assert_html_equal, assert_html_equal_with_options } = setup_html_equal();
beforeAll(() => {
// @ts-expect-error TODO huh?
From 7c10b237d15c563e5a9160f19fad04be8cc9c77f Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Sun, 1 Jun 2025 16:20:36 -0400
Subject: [PATCH 4/5] simplify
---
packages/svelte/tests/html_equal.js | 108 ++++++++----------
.../svelte/tests/runtime-legacy/shared.ts | 4 +-
2 files changed, 50 insertions(+), 62 deletions(-)
diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js
index f3ab5c54ca..22e52417a2 100644
--- a/packages/svelte/tests/html_equal.js
+++ b/packages/svelte/tests/html_equal.js
@@ -121,63 +121,53 @@ export function normalize_new_line(html) {
return html.replace(/\r\n/g, '\n');
}
-export function setup_html_equal() {
- /**
- * @param {string} actual
- * @param {string} expected
- * @param {string} [message]
- */
- const assert_html_equal = (actual, expected, message) => {
- try {
- assert.deepEqual(normalize_html(window, actual), normalize_html(window, expected), message);
- } catch (e) {
- if (Error.captureStackTrace)
- Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal);
- throw e;
- }
- };
-
- /**
- *
- * @param {string} actual
- * @param {string} expected
- * @param {{ preserveComments?: boolean, withoutNormalizeHtml?: boolean }} param2
- * @param {string} [message]
- */
- const assert_html_equal_with_options = (
- actual,
- expected,
- { preserveComments, withoutNormalizeHtml },
- message
- ) => {
- try {
- assert.deepEqual(
- withoutNormalizeHtml
- ? normalize_new_line(actual.trim()).replace(
- /()/g,
- preserveComments !== false ? '$1' : ''
- )
- : normalize_html(window, actual.trim(), { preserveComments }),
- withoutNormalizeHtml
- ? normalize_new_line(expected.trim()).replace(
- /()/g,
- preserveComments !== false ? '$1' : ''
- )
- : normalize_html(window, expected.trim(), { preserveComments }),
- message
- );
- } catch (e) {
- if (Error.captureStackTrace)
- Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal_with_options);
- throw e;
- }
- };
-
- return {
- assert_html_equal,
- assert_html_equal_with_options
- };
-}
+/**
+ * @param {string} actual
+ * @param {string} expected
+ * @param {string} [message]
+ */
+export const assert_html_equal = (actual, expected, message) => {
+ try {
+ assert.deepEqual(normalize_html(window, actual), normalize_html(window, expected), message);
+ } catch (e) {
+ if (Error.captureStackTrace)
+ Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal);
+ throw e;
+ }
+};
-// Common case without options
-export const { assert_html_equal, assert_html_equal_with_options } = setup_html_equal();
+/**
+ *
+ * @param {string} actual
+ * @param {string} expected
+ * @param {{ preserveComments?: boolean, withoutNormalizeHtml?: boolean }} param2
+ * @param {string} [message]
+ */
+export const assert_html_equal_with_options = (
+ actual,
+ expected,
+ { preserveComments, withoutNormalizeHtml },
+ message
+) => {
+ try {
+ assert.deepEqual(
+ withoutNormalizeHtml
+ ? normalize_new_line(actual.trim()).replace(
+ /()/g,
+ preserveComments !== false ? '$1' : ''
+ )
+ : normalize_html(window, actual.trim(), { preserveComments }),
+ withoutNormalizeHtml
+ ? normalize_new_line(expected.trim()).replace(
+ /()/g,
+ preserveComments !== false ? '$1' : ''
+ )
+ : normalize_html(window, expected.trim(), { preserveComments }),
+ message
+ );
+ } catch (e) {
+ if (Error.captureStackTrace)
+ Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal_with_options);
+ throw e;
+ }
+};
diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts
index c94c4ed422..c0d1177a82 100644
--- a/packages/svelte/tests/runtime-legacy/shared.ts
+++ b/packages/svelte/tests/runtime-legacy/shared.ts
@@ -7,7 +7,7 @@ import { flushSync, hydrate, mount, unmount } from 'svelte';
import { render } from 'svelte/server';
import { afterAll, assert, beforeAll } from 'vitest';
import { compile_directory, fragments } from '../helpers.js';
-import { setup_html_equal } from '../html_equal.js';
+import { assert_html_equal, assert_html_equal_with_options } from '../html_equal.js';
import { raf } from '../animation-helpers.js';
import type { CompileOptions } from '#compiler';
import { suite_with_variants, type BaseTest } from '../suite.js';
@@ -86,8 +86,6 @@ function unhandled_rejection_handler(err: Error) {
const listeners = process.rawListeners('unhandledRejection');
-const { assert_html_equal, assert_html_equal_with_options } = setup_html_equal();
-
beforeAll(() => {
// @ts-expect-error TODO huh?
process.prependListener('unhandledRejection', unhandled_rejection_handler);
From 73796c49b05b2d2eb8c5120a78f599ebaaade377 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Sun, 1 Jun 2025 16:45:38 -0400
Subject: [PATCH 5/5] simplify/robustify
---
packages/svelte/tests/html_equal.js | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js
index 22e52417a2..b5e18fdd42 100644
--- a/packages/svelte/tests/html_equal.js
+++ b/packages/svelte/tests/html_equal.js
@@ -1,7 +1,10 @@
import { assert } from 'vitest';
-/** @param {Element} node */
-function clean_children(node) {
+/**
+ * @param {Element} node
+ * @param {{ preserveComments: boolean }} opts
+ */
+function clean_children(node, opts) {
let previous = null;
let has_element_children = false;
let template =
@@ -56,20 +59,26 @@ function clean_children(node) {
continue;
}
- } else if (child.nodeType === 8) {
+ }
+
+ if (child.nodeType === 8 && !opts.preserveComments) {
// comment
child.remove();
continue;
- } else if (child.nodeType === 1) {
+ }
+
+ if (child.nodeType === 1 || child.nodeType === 8) {
if (previous?.nodeType === 3) {
const prev = /** @type {Text} */ (previous);
prev.data = prev.data.replace(/^[^\S]+$/, '\n');
- } else if (previous?.nodeType === 1) {
+ } else if (previous?.nodeType === 1 || previous?.nodeType === 8) {
node.insertBefore(document.createTextNode('\n'), child);
}
- has_element_children = true;
- clean_children(/** @type {Element} */ (child));
+ if (child.nodeType === 1) {
+ has_element_children = true;
+ clean_children(/** @type {Element} */ (child), opts);
+ }
}
previous = child;
@@ -103,9 +112,9 @@ function clean_children(node) {
export function normalize_html(window, html, { preserveComments = false } = {}) {
try {
const node = window.document.createElement('div');
- node.innerHTML = html.replace(/()/g, preserveComments ? '$1' : '').trim();
- clean_children(node);
+ node.innerHTML = html.trim();
+ clean_children(node, { preserveComments });
return node.innerHTML;
} catch (err) {