fix: ensure head effects are kept in the effect tree (#17746)

Fixes #17726

The problem was that the head effect had no "keep me around"-flag, which
it needs because its children are not guaranteed to be present
immediately - as shown in the related issue, where the child effect is
only created once async work has completed.

### Before submitting the PR, please make sure you do the following

- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).

### Tests and linting

- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
pull/17747/head
Simon H 6 days ago committed by GitHub
parent f8bf9bb461
commit 582e4443dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure head effects are kept in the effect tree

@ -2,7 +2,7 @@
import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js';
import { create_text, get_first_child, get_next_sibling } from '../operations.js';
import { block } from '../../reactivity/effects.js';
import { COMMENT_NODE, HEAD_EFFECT } from '#client/constants';
import { COMMENT_NODE, EFFECT_PRESERVED, HEAD_EFFECT } from '#client/constants';
/**
* @param {string} hash
@ -49,7 +49,9 @@ export function head(hash, render_fn) {
}
try {
block(() => render_fn(anchor), HEAD_EFFECT);
// normally a branch is the child of a block and would have the EFFECT_PRESERVED flag,
// but since head blocks don't necessarily only have direct branch children we add it on the block itself
block(() => render_fn(anchor), HEAD_EFFECT | EFFECT_PRESERVED);
} finally {
if (was_hydrating) {
set_hydrating(true);

@ -0,0 +1,12 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const p = target.querySelector('p');
assert.equal(p?.innerHTML, 'hello');
assert.equal(window.document.title, 'hello');
}
});

@ -0,0 +1,10 @@
<script>
let promise = Promise.resolve('hello');
const value = $derived(await promise);
</script>
<svelte:head>
<title>{value}</title>
</svelte:head>
<p>{value}</p>
Loading…
Cancel
Save