diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 66dff0fde2..1211670f94 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -39,10 +39,7 @@ function print_error(renderer, message) { // eslint-disable-next-line no-console console.error(message); - renderer.child( - (renderer) => renderer.push(``), - 'head' - ); + renderer.head((r) => r.push(``)); } /** diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 618e75f932..1e2d75d6dc 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -69,11 +69,11 @@ export function render(component, options = {}) { * @returns {void} */ export function head(renderer, fn) { - renderer.child((renderer) => { + renderer.head((renderer) => { renderer.push(BLOCK_OPEN); renderer.child(fn); renderer.push(BLOCK_CLOSE); - }, 'head'); + }); } /** diff --git a/packages/svelte/src/internal/server/renderer.js b/packages/svelte/src/internal/server/renderer.js index 26f7ac2b3c..21ae91fbb7 100644 --- a/packages/svelte/src/internal/server/renderer.js +++ b/packages/svelte/src/internal/server/renderer.js @@ -81,23 +81,33 @@ export class Renderer { /** * @param {SSRState} global * @param {Renderer | undefined} [parent] - * @param {RendererType} [type] */ - constructor(global, parent, type) { + constructor(global, parent) { + this.#parent = parent; + this.global = global; this.local = parent ? { ...parent.local } : { select_value: undefined }; - this.#parent = parent; - this.type = type ?? parent?.type ?? 'body'; + this.type = parent ? parent.type : 'body'; + } + + /** + * @param {(renderer: Renderer) => void} fn + */ + head(fn) { + const head = new Renderer(this.global, this); + head.type = 'head'; + + this.#out.push(head); + head.child(fn); } /** * Create a child renderer. The child renderer inherits the state from the parent, * but has its own content. * @param {(renderer: Renderer) => MaybePromise} fn - * @param {RendererType} [type] */ - child(fn, type) { - const child = new Renderer(this.global, this, type); + child(fn) { + const child = new Renderer(this.global, this); this.#out.push(child); const parent = ssr_context; @@ -184,7 +194,7 @@ export class Renderer { * @deprecated this is needed for legacy component bindings */ copy() { - const copy = new Renderer(this.global, this.#parent, this.type); + const copy = new Renderer(this.global, this.#parent); copy.#out = this.#out.map((item) => (item instanceof Renderer ? item.copy() : item)); copy.promises = this.promises; return copy; @@ -457,7 +467,8 @@ export class Renderer { static #push_accumulated_content(tree, accumulated_content) { for (const [type, content] of Object.entries(accumulated_content)) { if (!content) continue; - const child = new Renderer(tree.global, tree, /** @type {RendererType} */ (type)); + const child = new Renderer(tree.global, tree); + child.type = /** @type {RendererType} */ (type); child.push(content); tree.#out.push(child); } diff --git a/packages/svelte/src/internal/server/renderer.test.ts b/packages/svelte/src/internal/server/renderer.test.ts index fb14654d5d..2b2bf14cf7 100644 --- a/packages/svelte/src/internal/server/renderer.test.ts +++ b/packages/svelte/src/internal/server/renderer.test.ts @@ -20,9 +20,9 @@ test('collects synchronous body content by default', () => { test('child type switches content area (head vs body)', () => { const component = (renderer: Renderer) => { renderer.push('a'); - renderer.child(($$renderer) => { + renderer.head(($$renderer) => { $$renderer.push('T'); - }, 'head'); + }); renderer.push('b'); }; @@ -33,12 +33,12 @@ test('child type switches content area (head vs body)', () => { test('child inherits parent type when not specified', () => { const component = (renderer: Renderer) => { - renderer.child((renderer) => { + renderer.head((renderer) => { renderer.push(''); renderer.child((renderer) => { renderer.push(''); }); - }, 'head'); + }); }; const { head, body } = Renderer.render(component as unknown as Component); @@ -118,7 +118,9 @@ test('local state is shallow-copied to children', () => { }); test('subsume replaces tree content and state from other', () => { - const a = new Renderer(new SSRState('async'), undefined, 'head'); + const a = new Renderer(new SSRState('async')); + a.type = 'head'; + a.push(''); a.local.select_value = 'A'; @@ -140,7 +142,9 @@ test('subsume replaces tree content and state from other', () => { }); test('subsume refuses to switch modes', () => { - const a = new Renderer(new SSRState('sync'), undefined, 'head'); + const a = new Renderer(new SSRState('sync')); + a.type = 'head'; + a.push(''); a.local.select_value = 'A'; @@ -273,12 +277,12 @@ describe('async', () => { test('push async functions work with head content type', async () => { const component = (renderer: Renderer) => { - renderer.child(($$renderer) => { + renderer.head(($$renderer) => { $$renderer.push(async () => { await Promise.resolve(); return 'Async Title'; }); - }, 'head'); + }); }; const { head, body } = await Renderer.render(component as unknown as Component); diff --git a/packages/svelte/tests/server-side-rendering/samples/head-multiple-title/_config.js b/packages/svelte/tests/server-side-rendering/samples/head-multiple-title/_config.js index 45054bbada..f47bee71df 100644 --- a/packages/svelte/tests/server-side-rendering/samples/head-multiple-title/_config.js +++ b/packages/svelte/tests/server-side-rendering/samples/head-multiple-title/_config.js @@ -1,5 +1,3 @@ import { test } from '../../test'; -export default test({ - props: { adjective: 'custom' } -}); +export default test({});