diff --git a/.changeset/dirty-zebras-do.md b/.changeset/dirty-zebras-do.md new file mode 100644 index 000000000000..f0b266b09caa --- /dev/null +++ b/.changeset/dirty-zebras-do.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly validate head snippets on the server diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 34849196b7b6..157f22f929c9 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -6,7 +6,7 @@ import { } from '../../html-tree-validation.js'; import { current_component } from './context.js'; import { invalid_snippet_arguments } from '../shared/errors.js'; -import { Payload } from './payload.js'; +import { HeadPayload, Payload } from './payload.js'; /** * @typedef {{ @@ -105,7 +105,11 @@ export function pop_element() { * @param {Payload} payload */ export function validate_snippet_args(payload) { - if (typeof payload !== 'object' || !(payload instanceof Payload)) { + if ( + typeof payload !== 'object' || + // for some reason typescript consider the type of payload as never after the first instanceof + !(payload instanceof Payload || /** @type {any} */ (payload) instanceof HeadPayload) + ) { invalid_snippet_arguments(); } } diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index 03bcaf492ea9..8df5787ba4a7 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -1,16 +1,25 @@ +export class HeadPayload { + /** @type {Set<{ hash: string; code: string }>} */ + css = new Set(); + out = ''; + uid = () => ''; + title = ''; + + constructor(css = new Set(), out = '', title = '', uid = () => '') { + this.css = css; + this.out = out; + this.title = title; + this.uid = uid; + } +} + export class Payload { /** @type {Set<{ hash: string; code: string }>} */ css = new Set(); out = ''; uid = () => ''; - head = { - /** @type {Set<{ hash: string; code: string }>} */ - css: new Set(), - title: '', - out: '', - uid: () => '' - }; + head = new HeadPayload(); constructor(id_prefix = '') { this.uid = props_id_generator(id_prefix); @@ -30,12 +39,11 @@ export function copy_payload({ out, css, head, uid }) { payload.css = new Set(css); payload.uid = uid; - payload.head = { - title: head.title, - out: head.out, - css: new Set(head.css), - uid: head.uid - }; + payload.head = new HeadPayload(); + payload.head.out = head.out; + payload.head.css = new Set(head.css); + payload.head.title = head.title; + payload.head.uid = head.uid; return payload; } diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js new file mode 100644 index 000000000000..7c609205df9b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + mode: ['server'], + async test({ errors, assert }) { + assert.equal(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte new file mode 100644 index 000000000000..7eb31d3a9ee9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte @@ -0,0 +1,7 @@ +{#snippet head()} +