Skip to content

Commit

Permalink
fix: improve hydration of svelte head blocks (#11099)
Browse files Browse the repository at this point in the history
* fix: improve hydration of svelte head blocks

* revert sandbox

* simplify
  • Loading branch information
trueadm authored Apr 9, 2024
1 parent 48549f7 commit 3c2f4d2
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-queens-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

fix: improve hydration of svelte head blocks
27 changes: 22 additions & 5 deletions packages/svelte/src/internal/client/dom/blocks/svelte-head.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { hydrate_anchor, hydrate_nodes, hydrating, set_hydrate_nodes } from '../hydration.js';
import { empty } from '../operations.js';
import { block } from '../../reactivity/effects.js';
import { HYDRATION_START } from '../../../../constants.js';
import { HYDRATION_END, HYDRATION_START } from '../../../../constants.js';

/**
* @type {Node | undefined}
*/
let head_anchor;

export function reset_head_anchor() {
head_anchor = undefined;
}

/**
* @param {(anchor: Node) => import('#client').Dom | void} render_fn
Expand All @@ -19,12 +28,20 @@ export function head(render_fn) {
if (hydrating) {
previous_hydrate_nodes = hydrate_nodes;

let anchor = /** @type {import('#client').TemplateNode} */ (document.head.firstChild);
while (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) {
anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling);
// There might be multiple head blocks in our app, so we need to account for each one needing independent hydration.
if (head_anchor === undefined) {
head_anchor = /** @type {import('#client').TemplateNode} */ (document.head.firstChild);
}

while (
head_anchor.nodeType !== 8 ||
/** @type {Comment} */ (head_anchor).data !== HYDRATION_START
) {
head_anchor = /** @type {import('#client').TemplateNode} */ (head_anchor.nextSibling);
}

anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(anchor));
head_anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(head_anchor));
head_anchor = /** @type {import('#client').TemplateNode} */ (head_anchor.nextSibling);
} else {
anchor = document.head.appendChild(empty());
}
Expand Down
2 changes: 2 additions & 0 deletions packages/svelte/src/internal/client/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from './dom/hydration.js';
import { array_from } from './utils.js';
import { handle_event_propagation } from './dom/elements/events.js';
import { reset_head_anchor } from './dom/blocks/svelte-head.js';

/** @type {Set<string>} */
export const all_registered_events = new Set();
Expand Down Expand Up @@ -175,6 +176,7 @@ export function hydrate(component, options) {
} finally {
set_hydrating(!!previous_hydrate_nodes);
set_hydrate_nodes(previous_hydrate_nodes);
reset_head_anchor();
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/svelte/src/internal/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,7 @@ export function render(component, options) {
on_destroy = prev_on_destroy;

return {
head:
payload.head.out || payload.head.title
? payload.head.title + BLOCK_OPEN + payload.head.out + BLOCK_CLOSE
: '',
head: payload.head.out || payload.head.title ? payload.head.out + payload.head.title : '',
html: payload.out
};
}
Expand Down Expand Up @@ -247,7 +244,9 @@ export function escape(value, is_attr = false) {
*/
export function head(payload, fn) {
const head_payload = payload.head;
payload.head.out += BLOCK_OPEN;
fn(head_payload);
payload.head.out += BLOCK_CLOSE;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
let title = $state('Hello world');
let desc = $state('Some description');
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={desc}>
<meta name="author" content="@svelteawesome">
</svelte:head>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { test } from '../../test';

export default test({
html: `<div>Hello</div>`,

async test({ assert, target }) {
assert.htmlEqual(
target.ownerDocument.head.innerHTML,
`<script async="" src="https://www.googletagmanager.com/gtag/js?id=12345"></script><meta content="Some description" name="description"><meta content="@svelteawesome" name="author"><title>Hello world</title>`
);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
import MetaTag from './MetaTag.svelte'
</script>
<svelte:head>
<script async src="https://www.googletagmanager.com/gtag/js?id=12345"></script>
</svelte:head>
<MetaTag />

<div>Hello</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!ssr:0>
<title>Some Title</title>
<link rel="canonical" href="/">
<meta name="description" content="some description">
<meta name="keywords" content="some keywords">
<title>Some Title</title>
<!ssr:0>

0 comments on commit 3c2f4d2

Please sign in to comment.