fix: better handle hydration of script/style elements (alternative) (#14683)

* alternative approach to #14624

* changeset

* fix

* lint
pull/13429/merge
Rich Harris 2 weeks ago committed by GitHub
parent 8ba1b9ddd0
commit ef8bd6adeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: better handle hydration of script/style elements

@ -21,6 +21,7 @@ import { component_context, active_effect } from '../../runtime.js';
import { DEV } from 'esm-env';
import { EFFECT_TRANSPARENT } from '../../constants.js';
import { assign_nodes } from '../template.js';
import { is_raw_text_element } from '../../../../utils.js';
/**
* @param {Comment | Element} node
@ -116,6 +117,11 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
assign_nodes(element, element);
if (render_fn) {
if (hydrating && is_raw_text_element(next_tag)) {
// prevent hydration glitches
element.append(document.createComment(''));
}
// If hydrating, use the existing ssr comment as the anchor so that the
// inner open and close methods can pick up the existing nodes correctly
var child_anchor = /** @type {TemplateNode} */ (

@ -16,7 +16,7 @@ import { DEV } from 'esm-env';
import { current_component, pop, push } from './context.js';
import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
import { validate_store } from '../shared/validate.js';
import { is_boolean_attribute, is_void } from '../../utils.js';
import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js';
import { reset_elements } from './dev.js';
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
@ -24,9 +24,6 @@ import { reset_elements } from './dev.js';
const INVALID_ATTR_NAME_CHAR_REGEX =
/[\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;
/** List of elements that require raw contents and should not have SSR comments put in them */
const RAW_TEXT_ELEMENTS = ['textarea', 'script', 'style', 'title'];
/**
* @param {Payload} to_copy
* @returns {Payload}
@ -64,13 +61,13 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop)
payload.out += '<!---->';
if (tag) {
payload.out += `<${tag} `;
payload.out += `<${tag}`;
attributes_fn();
payload.out += `>`;
if (!is_void(tag)) {
children_fn();
if (!RAW_TEXT_ELEMENTS.includes(tag)) {
if (!is_raw_text_element(tag)) {
payload.out += EMPTY_COMMENT;
}
payload.out += `</${tag}>`;

@ -441,3 +441,11 @@ const RUNES = /** @type {const} */ ([
export function is_rune(name) {
return RUNES.includes(/** @type {RUNES[number]} */ (name));
}
/** List of elements that require raw contents and should not have SSR comments put in them */
const RAW_TEXT_ELEMENTS = /** @type {const} */ (['textarea', 'script', 'style', 'title']);
/** @param {string} name */
export function is_raw_text_element(name) {
return RAW_TEXT_ELEMENTS.includes(/** @type {RAW_TEXT_ELEMENTS[number]} */ (name));
}

@ -0,0 +1,11 @@
import { test } from '../../test';
export default test({
snapshot(target) {
const script = target.querySelector('script');
return {
script
};
}
});

@ -0,0 +1 @@
<!--[--><!----><script>{}<!----></script><!----><!--]-->

@ -0,0 +1 @@
<svelte:element this={"script"}>{"{}"}</svelte:element>
Loading…
Cancel
Save