diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 896c762bb3..e51242d8f6 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -60,10 +60,6 @@ > The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files -## snippet_missing_mount - -> Snippets created with `createRawSnippet(...)` and used on the client must specify a `mount` function - ## state_prototype_fixed > Cannot set prototype of `$state` object diff --git a/packages/svelte/messages/server-errors/lifecycle.md b/packages/svelte/messages/server-errors/lifecycle.md index d0e28050a1..80830f7903 100644 --- a/packages/svelte/messages/server-errors/lifecycle.md +++ b/packages/svelte/messages/server-errors/lifecycle.md @@ -1,7 +1,3 @@ ## lifecycle_function_unavailable > `%name%(...)` is not available on the server - -## snippet_missing_render - -> Snippets created with `createRawSnippet(...)` and used on the server must specify a `render` function diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index ddd6e99d67..f4a3bf7db6 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -1,5 +1,6 @@ /** @import { Effect, TemplateNode } from '#client' */ /** @import { Getters } from '#shared' */ +import { run } from '../../../shared/utils.js'; import { add_snippet_symbol } from '../../../shared/validate.js'; import { EFFECT_TRANSPARENT } from '../../constants.js'; import { branch, block, destroy_effect } from '../../reactivity/effects.js'; @@ -8,8 +9,8 @@ import { set_dev_current_component_function } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; +import { create_fragment_from_html } from '../reconciler.js'; import { assign_nodes } from '../template.js'; -import * as e from '../../errors.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn @@ -65,20 +66,15 @@ export function wrap_snippet(component, fn) { } /** - * Create a snippet imperatively using mount, hydrate and render functions. + * Create a snippet programmatically * @template {unknown[]} Params * @param {{ - * mount?: (...params: Getters) => Element, - * hydrate?: (element: Element, ...params: Getters) => void, - * render?: (...params: Params) => string + * render: (...params: Params) => string + * update?: (element: Element, ...params: Getters) => void, * }} options * @returns {import('svelte').Snippet} */ -export function createRawSnippet({ mount, hydrate }) { - if (mount === undefined) { - e.snippet_missing_mount(); - } - +export function createRawSnippet({ render, update }) { return add_snippet_symbol( (/** @type {TemplateNode} */ anchor, /** @type {Getters} */ ...params) => { /** @type {Element} */ @@ -87,17 +83,14 @@ export function createRawSnippet({ mount, hydrate }) { if (hydrating) { element = /** @type {Element} */ (hydrate_node); hydrate_next(); - - if (hydrate === undefined) { - element.replaceWith((element = mount(...params))); - } else { - hydrate(element, ...params); - } } else { - element = mount(...params); + var html = render(.../** @type {Params} */ (params.map(run))); + var fragment = create_fragment_from_html(html); + element = /** @type {Element} */ (fragment.firstChild); anchor.before(element); } + update?.(element, ...params); assign_nodes(element, element); } ); diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 5ff07dfbea..f7ab6597af 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -262,22 +262,6 @@ export function rune_outside_svelte(rune) { } } -/** - * Snippets created with `createRawSnippet(...)` and used on the client must specify a `mount` function - * @returns {never} - */ -export function snippet_missing_mount() { - if (DEV) { - const error = new Error(`snippet_missing_mount\nSnippets created with \`createRawSnippet(...)\` and used on the client must specify a \`mount\` function`); - - error.name = 'Svelte error'; - throw error; - } else { - // TODO print a link to the documentation - throw new Error("snippet_missing_mount"); - } -} - /** * Cannot set prototype of `$state` object * @returns {never} diff --git a/packages/svelte/src/internal/server/blocks/snippet.js b/packages/svelte/src/internal/server/blocks/snippet.js index 7b9805ce7b..f9856f07ea 100644 --- a/packages/svelte/src/internal/server/blocks/snippet.js +++ b/packages/svelte/src/internal/server/blocks/snippet.js @@ -1,23 +1,21 @@ +/** @import { Snippet } from 'svelte' */ /** @import { Payload } from '#server' */ +/** @import { Getters } from '#shared' */ import { add_snippet_symbol } from '../../shared/validate.js'; -import * as e from '../errors.js'; /** - * Create a snippet imperatively using mount, hyrdate and render functions. + * Create a snippet programmatically + * @template {unknown[]} Params * @param {{ - * mount?: (...params: any[]) => Element, - * hydrate?: (element: Element, ...params: any[]) => void, - * render?: (...params: any[]) => string + * render: (...params: Params) => string + * update?: (element: Element, ...params: Getters) => void, * }} options + * @returns {Snippet} */ export function createRawSnippet({ render }) { - if (render === undefined) { - e.snippet_missing_render(); - } - - const snippet_fn = (/** @type {Payload} */ payload, /** @type {any[]} */ ...args) => { + const snippet_fn = (/** @type {Payload} */ payload, /** @type {Params} */ ...args) => { payload.out += render(...args); }; add_snippet_symbol(snippet_fn); - return snippet_fn; + return /** @type {Snippet} */ (snippet_fn); } diff --git a/packages/svelte/src/internal/server/errors.js b/packages/svelte/src/internal/server/errors.js index 8afa903540..67f4a2dfc6 100644 --- a/packages/svelte/src/internal/server/errors.js +++ b/packages/svelte/src/internal/server/errors.js @@ -8,17 +8,6 @@ export function lifecycle_function_unavailable(name) { const error = new Error(`lifecycle_function_unavailable\n\`${name}(...)\` is not available on the server`); - error.name = 'Svelte error'; - throw error; -} - -/** - * Snippets created with `createRawSnippet(...)` and used on the server must specify a `render` function - * @returns {never} - */ -export function snippet_missing_render() { - const error = new Error(`snippet_missing_render\nSnippets created with \`createRawSnippet(...)\` and used on the server must specify a \`render\` function`); - error.name = 'Svelte error'; throw error; } \ No newline at end of file diff --git a/packages/svelte/tests/hydration/samples/snippet-raw-hydrate/main.svelte b/packages/svelte/tests/hydration/samples/snippet-raw-hydrate/main.svelte index 51a1cab1b2..52bfd8f1ed 100644 --- a/packages/svelte/tests/hydration/samples/snippet-raw-hydrate/main.svelte +++ b/packages/svelte/tests/hydration/samples/snippet-raw-hydrate/main.svelte @@ -2,16 +2,11 @@ import { createRawSnippet } from 'svelte'; const snippet = createRawSnippet({ - mount() { - const p = document.createElement('p'); - p.textContent = 'mounted'; - return p; - }, - hydrate(p) { - p.textContent = 'hydrated'; - }, render() { return `

rendered

`; + }, + update(p) { + p.textContent = 'hydrated'; } }); diff --git a/packages/svelte/tests/hydration/samples/snippet-raw-mount/_config.js b/packages/svelte/tests/hydration/samples/snippet-raw-mount/_config.js deleted file mode 100644 index f47bee71df..0000000000 --- a/packages/svelte/tests/hydration/samples/snippet-raw-mount/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from '../../test'; - -export default test({}); diff --git a/packages/svelte/tests/hydration/samples/snippet-raw-mount/_expected.html b/packages/svelte/tests/hydration/samples/snippet-raw-mount/_expected.html deleted file mode 100644 index f93cda1078..0000000000 --- a/packages/svelte/tests/hydration/samples/snippet-raw-mount/_expected.html +++ /dev/null @@ -1 +0,0 @@ -

mounted

diff --git a/packages/svelte/tests/hydration/samples/snippet-raw-mount/main.svelte b/packages/svelte/tests/hydration/samples/snippet-raw-mount/main.svelte deleted file mode 100644 index 0677a0cf4f..0000000000 --- a/packages/svelte/tests/hydration/samples/snippet-raw-mount/main.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - -{@render snippet()} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/_config.js deleted file mode 100644 index a22776d7e7..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/_config.js +++ /dev/null @@ -1,17 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true // Render in dev mode to check that the validation error is not thrown - }, - html: `
0
`, - - test({ assert, target }) { - const [b1] = target.querySelectorAll('button'); - - b1?.click(); - flushSync(); - assert.htmlEqual(target.innerHTML, `
1
`); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/main.svelte deleted file mode 100644 index 6568abd987..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/snippet-raw-args/main.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - -
- {@render snippet(count)} -
- diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-raw/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-raw/main.svelte index 0f65e0f89a..88290285d2 100644 --- a/packages/svelte/tests/runtime-runes/samples/snippet-raw/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/snippet-raw/main.svelte @@ -4,17 +4,13 @@ let count = $state(0); const hello = createRawSnippet({ - mount(count) { - const p = document.createElement('p') - + render(count) { + return `

clicks: ${count}

`; + }, + update(p, count) { $effect(() => { p.textContent = `clicks: ${count()}` }); - - return p; - }, - render(count) { - return `

clicks: ${count}

`; } });