feat: The Final Solution

pull/10320/head
S. Elliott Johnson 8 months ago
parent 106514514d
commit d7dac4eac2

@ -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))
)
)
)

@ -1993,6 +1993,18 @@ export function proxy_rest_array(items) {
});
}
/**
* @template {Function | undefined} T
* @param {T} fn
* @returns {ReturnType<T> | 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

@ -39,6 +39,7 @@ export {
unwrap,
proxy_rest_array,
thunkspread,
maybe_call,
freeze
} from './client/runtime.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');
}
});

@ -0,0 +1,28 @@
<script>
function box(value) {
let state = $state(value);
return {
get value() {
return state
},
set value(v) {
state = v;
}
}
}
let count = box(0);
</script>
{#snippet counter(c)}
{#if c}
<button on:click={() => (c.value += 1)}>{c.value}</button>
{:else}
<p>fallback</p>
{/if}
{/snippet}
{@render counter()}
{@render counter(count)}
Loading…
Cancel
Save