Skip to content

Commit ef8bd6a

Browse files
authored
fix: better handle hydration of script/style elements (alternative) (#14683)
* alternative approach to #14624 * changeset * fix * lint
1 parent 8ba1b9d commit ef8bd6a

File tree

7 files changed

+35
-6
lines changed

7 files changed

+35
-6
lines changed

.changeset/rotten-yaks-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: better handle hydration of script/style elements

packages/svelte/src/internal/client/dom/blocks/svelte-element.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { component_context, active_effect } from '../../runtime.js';
2121
import { DEV } from 'esm-env';
2222
import { EFFECT_TRANSPARENT } from '../../constants.js';
2323
import { assign_nodes } from '../template.js';
24+
import { is_raw_text_element } from '../../../../utils.js';
2425

2526
/**
2627
* @param {Comment | Element} node
@@ -116,6 +117,11 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
116117
assign_nodes(element, element);
117118

118119
if (render_fn) {
120+
if (hydrating && is_raw_text_element(next_tag)) {
121+
// prevent hydration glitches
122+
element.append(document.createComment(''));
123+
}
124+
119125
// If hydrating, use the existing ssr comment as the anchor so that the
120126
// inner open and close methods can pick up the existing nodes correctly
121127
var child_anchor = /** @type {TemplateNode} */ (

packages/svelte/src/internal/server/index.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,14 @@ import { DEV } from 'esm-env';
1616
import { current_component, pop, push } from './context.js';
1717
import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
1818
import { validate_store } from '../shared/validate.js';
19-
import { is_boolean_attribute, is_void } from '../../utils.js';
19+
import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js';
2020
import { reset_elements } from './dev.js';
2121

2222
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
2323
// https://infra.spec.whatwg.org/#noncharacter
2424
const INVALID_ATTR_NAME_CHAR_REGEX =
2525
/[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
2626

27-
/** List of elements that require raw contents and should not have SSR comments put in them */
28-
const RAW_TEXT_ELEMENTS = ['textarea', 'script', 'style', 'title'];
29-
3027
/**
3128
* @param {Payload} to_copy
3229
* @returns {Payload}
@@ -64,13 +61,13 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop)
6461
payload.out += '<!---->';
6562

6663
if (tag) {
67-
payload.out += `<${tag} `;
64+
payload.out += `<${tag}`;
6865
attributes_fn();
6966
payload.out += `>`;
7067

7168
if (!is_void(tag)) {
7269
children_fn();
73-
if (!RAW_TEXT_ELEMENTS.includes(tag)) {
70+
if (!is_raw_text_element(tag)) {
7471
payload.out += EMPTY_COMMENT;
7572
}
7673
payload.out += `</${tag}>`;

packages/svelte/src/utils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,11 @@ const RUNES = /** @type {const} */ ([
441441
export function is_rune(name) {
442442
return RUNES.includes(/** @type {RUNES[number]} */ (name));
443443
}
444+
445+
/** List of elements that require raw contents and should not have SSR comments put in them */
446+
const RAW_TEXT_ELEMENTS = /** @type {const} */ (['textarea', 'script', 'style', 'title']);
447+
448+
/** @param {string} name */
449+
export function is_raw_text_element(name) {
450+
return RAW_TEXT_ELEMENTS.includes(/** @type {RAW_TEXT_ELEMENTS[number]} */ (name));
451+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
snapshot(target) {
5+
const script = target.querySelector('script');
6+
7+
return {
8+
script
9+
};
10+
}
11+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!--[--><!----><script>{}<!----></script><!----><!--]-->
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<svelte:element this={"script"}>{"{}"}</svelte:element>

0 commit comments

Comments
 (0)