From d7dac4eac2181b4598c4565326bc4366d5e06f11 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Fri, 5 Jan 2024 17:12:45 -0700 Subject: [PATCH] feat: The Final Solution --- .../3-transform/client/visitors/template.js | 9 ++++-- .../svelte/src/internal/client/runtime.js | 12 ++++++++ packages/svelte/src/internal/index.js | 1 + .../snippet-optional-arguments/_config.js | 15 ++++++++++ .../snippet-optional-arguments/main.svelte | 28 +++++++++++++++++++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/main.svelte diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index a526fd1c2e..d544e9d399 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2491,7 +2491,10 @@ export const template_visitors = { const binding = /** @type {import('#compiler').Binding} */ ( context.state.scope.get(argument.name) ); - binding.expression = b.maybe_call(argument); + // we can't use `b.maybe_call` because it can result in invalid javascript if + // this expression appears on the left side of an assignment somewhere. For example: + // `$.maybe_call(myArg).value = 1` is valid JavaScript, but `$.myArg?.().value = 1` is not + binding.expression = b.call('$.maybe_call', argument); return; } @@ -2526,7 +2529,9 @@ export const template_visitors = { /** @type {import('estree').Expression} */ ( context.visit( path.expression?.( - argument.type === 'RestElement' ? b.id(arg_alias) : b.maybe_call(arg_alias) + argument.type === 'RestElement' + ? b.id(arg_alias) + : b.call('$.maybe_call', b.id(arg_alias)) ) ) ) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a95253c731..4bf8d7806f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1993,6 +1993,18 @@ export function proxy_rest_array(items) { }); } +/** + * @template {Function | undefined} T + * @param {T} fn + * @returns {ReturnType | undefined} + */ +export function maybe_call(fn) { + if (fn === undefined) { + return undefined; + } + return fn(); +} + /** * Expects a value that was wrapped with `freeze` and makes it frozen. * @template {import('./proxy/proxy.js').StateObject} T diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index eaeb746b0a..a5f789ee71 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -39,6 +39,7 @@ export { unwrap, proxy_rest_array, thunkspread, + maybe_call, freeze } from './client/runtime.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/_config.js new file mode 100644 index 0000000000..203cb1c432 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/_config.js @@ -0,0 +1,15 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const count = target.querySelector('button'); + const fallback = target.querySelector('p'); + + assert.htmlEqual(count?.innerHTML ?? '', '0'); + assert.htmlEqual(fallback?.innerHTML ?? '', 'fallback'); + + await count?.click(); + assert.htmlEqual(count?.innerHTML ?? '', '1'); + assert.htmlEqual(fallback?.innerHTML ?? '', 'fallback'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/main.svelte new file mode 100644 index 0000000000..03d3c29c45 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-optional-arguments/main.svelte @@ -0,0 +1,28 @@ + + +{#snippet counter(c)} + {#if c} + + {:else} +

fallback

+ {/if} +{/snippet} + +{@render counter()} +{@render counter(count)} +