From 93bf429570e19dcff4f7024f135228e35b7f2546 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 30 Aug 2024 20:40:30 +0100 Subject: [PATCH 1/7] fix: prevent nullish snippet for rendering empty content --- .changeset/tame-frogs-shave.md | 5 +++++ packages/svelte/messages/client-errors/errors.md | 4 ++++ .../3-transform/client/visitors/RenderTag.js | 10 +++++++++- .../src/internal/client/dom/blocks/snippet.js | 11 ++++++++--- packages/svelte/src/internal/client/errors.js | 16 ++++++++++++++++ .../samples/snippet-undefined/_config.js | 13 +++++++++++++ .../samples/snippet-undefined/main.svelte | 13 +++++++++++++ 7 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 .changeset/tame-frogs-shave.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-undefined/main.svelte diff --git a/.changeset/tame-frogs-shave.md b/.changeset/tame-frogs-shave.md new file mode 100644 index 000000000000..af0728a4a979 --- /dev/null +++ b/.changeset/tame-frogs-shave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent nullish snippet for rendering empty content diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 4e8af2e6b404..db0e772a46b0 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -48,6 +48,10 @@ > Failed to hydrate the application +## invalid_snippet + +> Could not `{@render}` snippet due to the expression being null or undefined. Consider using optional chaining `{@render snippet?.()}` + ## lifecycle_legacy_only > `%name%(...)` cannot be used in runes mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index 6da1f87c0d39..acdad095b7fd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -31,7 +31,15 @@ export function RenderTag(node, context) { if (node.metadata.dynamic) { context.state.init.push( - b.stmt(b.call('$.snippet', context.state.node, b.thunk(snippet_function), ...args)) + b.stmt( + b.call( + '$.snippet', + context.state.node, + b.thunk(snippet_function), + b.literal(node.expression.type === 'ChainExpression'), + ...args, + ) + ) ); } else { context.state.init.push( diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index c184f72ddccf..37600c2a77ab 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -11,21 +11,24 @@ 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 w from '../../warnings.js'; +import * as e from '../../errors.js'; import { DEV } from 'esm-env'; import { get_first_child, get_next_sibling } from '../operations.js'; +import { UNINITIALIZED } from '../../../../constants.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn * @param {TemplateNode} node * @param {() => SnippetFn | null | undefined} get_snippet + * @param {boolean} is_chain * @param {(() => any)[]} args * @returns {void} */ -export function snippet(node, get_snippet, ...args) { +export function snippet(node, get_snippet, is_chain, ...args) { var anchor = node; - /** @type {SnippetFn | null | undefined} */ - var snippet; + /** @type {SnippetFn | null | undefined | typeof UNINITIALIZED} */ + var snippet = UNINITIALIZED; /** @type {Effect | null} */ var snippet_effect; @@ -40,6 +43,8 @@ export function snippet(node, get_snippet, ...args) { if (snippet) { snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); + } else if (!is_chain) { + e.invalid_snippet(); } }, EFFECT_TRANSPARENT); diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 8b537ed7ba5d..70bef2367445 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -210,6 +210,22 @@ export function hydration_failed() { } } +/** + * Could not `{@render}` snippet due to the expression being null or undefined. Consider using optional chaining `{@render snippet?.()}` + * @returns {never} + */ +export function invalid_snippet() { + if (DEV) { + const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being null or undefined. Consider using optional chaining \`{@render snippet?.()}\``); + + error.name = 'Svelte error'; + throw error; + } else { + // TODO print a link to the documentation + throw new Error("invalid_snippet"); + } +} + /** * `%name%(...)` cannot be used in runes mode * @param {string} name diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js new file mode 100644 index 000000000000..4a13e208b543 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + const btn = target.querySelector('button'); + + assert.throws(() => { + btn?.click(); + flushSync(); + }, /invalid_snippet/); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/main.svelte new file mode 100644 index 000000000000..09607fec0d5c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/main.svelte @@ -0,0 +1,13 @@ + + +{#snippet counter()} + Test +{/snippet} + +{@render state.value()} + + From 042c165fb8465966f10ae843bbfe01add63c20f0 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 30 Aug 2024 20:41:39 +0100 Subject: [PATCH 2/7] fix: prevent nullish snippet for rendering empty content --- packages/svelte/src/internal/client/dom/blocks/snippet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index 37600c2a77ab..a6c4a60dc740 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -41,7 +41,7 @@ export function snippet(node, get_snippet, is_chain, ...args) { snippet_effect = null; } - if (snippet) { + if (snippet != null) { snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); } else if (!is_chain) { e.invalid_snippet(); From ade048870e91f77e223d2e30d8ac9cc562be624d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 30 Aug 2024 20:52:01 +0100 Subject: [PATCH 3/7] lint --- .../compiler/phases/3-transform/client/visitors/RenderTag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index acdad095b7fd..74042f46532b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -37,7 +37,7 @@ export function RenderTag(node, context) { context.state.node, b.thunk(snippet_function), b.literal(node.expression.type === 'ChainExpression'), - ...args, + ...args ) ) ); From 0dc49964a419d79a8f27f9ea9b262dd7ea10dad3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 4 Sep 2024 09:09:09 +0100 Subject: [PATCH 4/7] fix message --- packages/svelte/messages/client-errors/errors.md | 2 +- packages/svelte/src/internal/client/errors.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index db0e772a46b0..9bd7e8a654a6 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -50,7 +50,7 @@ ## invalid_snippet -> Could not `{@render}` snippet due to the expression being null or undefined. Consider using optional chaining `{@render snippet?.()}` +> Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` ## lifecycle_legacy_only diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 70bef2367445..26d38f0aba91 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -211,12 +211,12 @@ export function hydration_failed() { } /** - * Could not `{@render}` snippet due to the expression being null or undefined. Consider using optional chaining `{@render snippet?.()}` + * Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` * @returns {never} */ export function invalid_snippet() { if (DEV) { - const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being null or undefined. Consider using optional chaining \`{@render snippet?.()}\``); + const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being \`null\` or \`undefined\`. Consider using optional chaining \`{@render snippet?.()}\``); error.name = 'Svelte error'; throw error; From b888dce79eaccd0b25ba47a45b3a60872973d4c6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 5 Sep 2024 15:18:10 +0100 Subject: [PATCH 5/7] alternative approach --- .../svelte/messages/client-errors/errors.md | 4 ---- .../3-transform/client/visitors/RenderTag.js | 15 ++++++--------- .../src/internal/client/dom/blocks/snippet.js | 19 ++++++++----------- packages/svelte/src/internal/client/errors.js | 16 ---------------- packages/svelte/src/internal/client/index.js | 2 +- 5 files changed, 15 insertions(+), 41 deletions(-) diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 9bd7e8a654a6..4e8af2e6b404 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -48,10 +48,6 @@ > Failed to hydrate the application -## invalid_snippet - -> Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` - ## lifecycle_legacy_only > `%name%(...)` cannot be used in runes mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index 74042f46532b..92f750ada52e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -30,16 +30,13 @@ export function RenderTag(node, context) { let snippet_function = /** @type {Expression} */ (context.visit(callee)); if (node.metadata.dynamic) { + // If we have a chain expression then ensure a nullish snippet function gets turned into an empty one + if (b.literal(node.expression.type === 'ChainExpression')) { + snippet_function = b.logical('??', snippet_function, b.id('$.empty_snippet')); + } + context.state.init.push( - b.stmt( - b.call( - '$.snippet', - context.state.node, - b.thunk(snippet_function), - b.literal(node.expression.type === 'ChainExpression'), - ...args - ) - ) + b.stmt(b.call('$.snippet', context.state.node, b.thunk(snippet_function), ...args)) ); } else { context.state.init.push( diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index a6c4a60dc740..8a312afebcdc 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -11,24 +11,21 @@ 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 w from '../../warnings.js'; -import * as e from '../../errors.js'; import { DEV } from 'esm-env'; import { get_first_child, get_next_sibling } from '../operations.js'; -import { UNINITIALIZED } from '../../../../constants.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn * @param {TemplateNode} node * @param {() => SnippetFn | null | undefined} get_snippet - * @param {boolean} is_chain * @param {(() => any)[]} args * @returns {void} */ -export function snippet(node, get_snippet, is_chain, ...args) { +export function snippet(node, get_snippet, ...args) { var anchor = node; - /** @type {SnippetFn | null | undefined | typeof UNINITIALIZED} */ - var snippet = UNINITIALIZED; + /** @type {SnippetFn | null | undefined} */ + var snippet = /** @type {SnippetFn} */ (empty_snippet); /** @type {Effect | null} */ var snippet_effect; @@ -41,11 +38,7 @@ export function snippet(node, get_snippet, is_chain, ...args) { snippet_effect = null; } - if (snippet != null) { - snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); - } else if (!is_chain) { - e.invalid_snippet(); - } + snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); }, EFFECT_TRANSPARENT); if (hydrating) { @@ -53,6 +46,10 @@ export function snippet(node, get_snippet, is_chain, ...args) { } } +// Just an empty function +/** @type {(...args: any[]) => void} */ +export const empty_snippet = () => {}; + /** * In development, wrap the snippet function so that it passes validation, and so that the * correct component context is set for ownership checks diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 26d38f0aba91..8b537ed7ba5d 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -210,22 +210,6 @@ export function hydration_failed() { } } -/** - * Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` - * @returns {never} - */ -export function invalid_snippet() { - if (DEV) { - const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being \`null\` or \`undefined\`. Consider using optional chaining \`{@render snippet?.()}\``); - - error.name = 'Svelte error'; - throw error; - } else { - // TODO print a link to the documentation - throw new Error("invalid_snippet"); - } -} - /** * `%name%(...)` cannot be used in runes mode * @param {string} name diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 519a41248685..407b4ff5b0f6 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -18,7 +18,7 @@ export { css_props } from './dom/blocks/css-props.js'; export { index, each } from './dom/blocks/each.js'; export { html } from './dom/blocks/html.js'; export { sanitize_slots, slot } from './dom/blocks/slot.js'; -export { snippet, wrap_snippet } from './dom/blocks/snippet.js'; +export { snippet, wrap_snippet, empty_snippet } from './dom/blocks/snippet.js'; export { component } from './dom/blocks/svelte-component.js'; export { element } from './dom/blocks/svelte-element.js'; export { head } from './dom/blocks/svelte-head.js'; From 8ace4d02cb7b839ce438c6f3c6220021abbdb8d6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 5 Sep 2024 15:21:49 +0100 Subject: [PATCH 6/7] tweak --- .../compiler/phases/3-transform/client/visitors/RenderTag.js | 2 +- .../tests/runtime-runes/samples/snippet-undefined/_config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index 92f750ada52e..d782c8c2aae6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -31,7 +31,7 @@ export function RenderTag(node, context) { if (node.metadata.dynamic) { // If we have a chain expression then ensure a nullish snippet function gets turned into an empty one - if (b.literal(node.expression.type === 'ChainExpression')) { + if (node.expression.type === 'ChainExpression') { snippet_function = b.logical('??', snippet_function, b.id('$.empty_snippet')); } diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js index 4a13e208b543..b5e2e50cc90c 100644 --- a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js @@ -8,6 +8,6 @@ export default test({ assert.throws(() => { btn?.click(); flushSync(); - }, /invalid_snippet/); + }, /snippet is not a function/); } }); From a241a5a5b6388761a37f3427ab1042653bd79299 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 5 Sep 2024 15:38:14 +0100 Subject: [PATCH 7/7] feedback --- packages/svelte/messages/client-errors/errors.md | 4 ++++ .../3-transform/client/visitors/RenderTag.js | 2 +- .../src/internal/client/dom/blocks/snippet.js | 13 ++++++++----- packages/svelte/src/internal/client/errors.js | 16 ++++++++++++++++ packages/svelte/src/internal/client/index.js | 2 +- .../samples/snippet-undefined/_config.js | 2 +- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 4e8af2e6b404..9bd7e8a654a6 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -48,6 +48,10 @@ > Failed to hydrate the application +## invalid_snippet + +> Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` + ## lifecycle_legacy_only > `%name%(...)` cannot be used in runes mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index d782c8c2aae6..ad3fa6d240e8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -32,7 +32,7 @@ export function RenderTag(node, context) { if (node.metadata.dynamic) { // If we have a chain expression then ensure a nullish snippet function gets turned into an empty one if (node.expression.type === 'ChainExpression') { - snippet_function = b.logical('??', snippet_function, b.id('$.empty_snippet')); + snippet_function = b.logical('??', snippet_function, b.id('$.noop')); } context.state.init.push( diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index 8a312afebcdc..cec57f83b7ba 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -11,8 +11,10 @@ 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 w from '../../warnings.js'; +import * as e from '../../errors.js'; import { DEV } from 'esm-env'; import { get_first_child, get_next_sibling } from '../operations.js'; +import { noop } from '../../../shared/utils.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn @@ -25,7 +27,8 @@ export function snippet(node, get_snippet, ...args) { var anchor = node; /** @type {SnippetFn | null | undefined} */ - var snippet = /** @type {SnippetFn} */ (empty_snippet); + // @ts-ignore + var snippet = noop; /** @type {Effect | null} */ var snippet_effect; @@ -38,6 +41,10 @@ export function snippet(node, get_snippet, ...args) { snippet_effect = null; } + if (DEV && snippet == null) { + e.invalid_snippet(); + } + snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); }, EFFECT_TRANSPARENT); @@ -46,10 +53,6 @@ export function snippet(node, get_snippet, ...args) { } } -// Just an empty function -/** @type {(...args: any[]) => void} */ -export const empty_snippet = () => {}; - /** * In development, wrap the snippet function so that it passes validation, and so that the * correct component context is set for ownership checks diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 8b537ed7ba5d..26d38f0aba91 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -210,6 +210,22 @@ export function hydration_failed() { } } +/** + * Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}` + * @returns {never} + */ +export function invalid_snippet() { + if (DEV) { + const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being \`null\` or \`undefined\`. Consider using optional chaining \`{@render snippet?.()}\``); + + error.name = 'Svelte error'; + throw error; + } else { + // TODO print a link to the documentation + throw new Error("invalid_snippet"); + } +} + /** * `%name%(...)` cannot be used in runes mode * @param {string} name diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 407b4ff5b0f6..519a41248685 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -18,7 +18,7 @@ export { css_props } from './dom/blocks/css-props.js'; export { index, each } from './dom/blocks/each.js'; export { html } from './dom/blocks/html.js'; export { sanitize_slots, slot } from './dom/blocks/slot.js'; -export { snippet, wrap_snippet, empty_snippet } from './dom/blocks/snippet.js'; +export { snippet, wrap_snippet } from './dom/blocks/snippet.js'; export { component } from './dom/blocks/svelte-component.js'; export { element } from './dom/blocks/svelte-element.js'; export { head } from './dom/blocks/svelte-head.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js index b5e2e50cc90c..4a13e208b543 100644 --- a/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/snippet-undefined/_config.js @@ -8,6 +8,6 @@ export default test({ assert.throws(() => { btn?.click(); flushSync(); - }, /snippet is not a function/); + }, /invalid_snippet/); } });