feat: it's... alive?

pull/10320/head
S. Elliott Johnson 2 years ago
parent 6d364aaade
commit 3da243f5ab

@ -1758,6 +1758,7 @@ export const template_visitors = {
context.visit(b.spread(b.call('$.shallow_thunk', arg.argument))) context.visit(b.spread(b.call('$.shallow_thunk', arg.argument)))
) )
); );
return;
} }
args.push(b.thunk(/** @type {import('estree').Expression} */ (context.visit(arg)))); args.push(b.thunk(/** @type {import('estree').Expression} */ (context.visit(arg))));
}); });
@ -2454,7 +2455,7 @@ export const template_visitors = {
node.context.elements.forEach((argument, i) => { node.context.elements.forEach((argument, i) => {
if (!argument) return; if (!argument) return;
const arg_alias = `$$arg${i}`; let arg_alias = `$$arg${i}`;
/** @type {import('estree').Identifier | undefined} */ /** @type {import('estree').Identifier | undefined} */
let identifier; let identifier;
/** @type {import('estree').Identifier | import('estree').RestElement | string} */ /** @type {import('estree').Identifier | import('estree').RestElement | string} */
@ -2478,16 +2479,24 @@ export const template_visitors = {
} }
if (argument.type === 'RestElement' && argument.argument.type === 'Identifier') { if (argument.type === 'RestElement' && argument.argument.type === 'Identifier') {
// this is a "simple" rest argument (not destructured), so we just need to thunkify it // this is a "simple" rest argument (not destructured), so we just need to proxy it
const binding = /** @type {import('#compiler').Binding} */ ( // const binding = /** @type {import('#compiler').Binding} */ (
context.state.scope.get(argument.argument.name) // context.state.scope.get(argument.argument.name)
); // );
binding.expression = b.call(argument.argument.name); // binding.expression = b.call(argument.argument.name);
// TODO: where does `context.visit` go here? does it matter? i don't understand what it's for :thinkies: // TODO: where does `context.visit` go here? does it matter? i don't understand what it's for :thinkies:
declarations.push(b.let(argument.argument.name, b.thunk(b.id(`$$arg${i}`)))); declarations.push(
b.let(argument.argument.name, b.call('$.proxy_rest_array', b.id(arg_alias)))
);
return; return;
} }
if (argument.type === 'RestElement') {
const new_arg_alias = `$$proxied_arg${i}`;
declarations.push(b.let(new_arg_alias, b.call('$.proxy_rest_array', b.id(arg_alias))));
arg_alias = new_arg_alias;
}
// If, on the other hand, it's a destructuring expression, which could be // If, on the other hand, it's a destructuring expression, which could be
// a rest destructure (eg. `...[foo, bar, { baz }, ...rest]`, which, absurdly, is all valid syntax), // a rest destructure (eg. `...[foo, bar, { baz }, ...rest]`, which, absurdly, is all valid syntax),
// we need to follow the destructuring expression to figure out what variables are being extracted. // we need to follow the destructuring expression to figure out what variables are being extracted.
@ -2501,7 +2510,11 @@ export const template_visitors = {
path.node, path.node,
b.thunk( b.thunk(
/** @type {import('estree').Expression} */ ( /** @type {import('estree').Expression} */ (
context.visit(path.expression?.(b.call(`$$arg${i}`))) context.visit(
path.expression?.(
argument.type === 'RestElement' ? b.id(arg_alias) : b.call(arg_alias)
)
)
) )
) )
) )

@ -1899,3 +1899,37 @@ if (DEV) {
throw_rune_error('$inspect'); throw_rune_error('$inspect');
throw_rune_error('$props'); throw_rune_error('$props');
} }
/**
* @template {Iterable<unknown>} T
* @param {T} iterable
* @returns {{ [P in keyof T]: () => T[P] }}
*/
export function shallow_thunk(iterable) {
const thunks = [];
for (const item of iterable) {
thunks.push(() => item);
}
// @ts-expect-error -- for obvious reasons
return thunks;
}
/**
* @template {unknown[]} T
* @param {T} items
* @returns {T}
*/
export function proxy_rest_array(items) {
return new Proxy(items, {
get(target, property) {
// @ts-expect-error -- It thinks arrays can't have properties that aren't numeric
if (typeof property === 'symbol') return target[property];
if (!isNaN(parseInt(property))) {
// @ts-expect-error -- It thinks arrays can't have properties that aren't numeric
return target[property]();
}
// @ts-expect-error -- It thinks arrays can't have properties that aren't numeric
return target[property];
}
});
}

@ -16,18 +16,3 @@ export var get_descriptors = Object.getOwnPropertyDescriptors;
export function is_function(thing) { export function is_function(thing) {
return typeof thing === 'function'; return typeof thing === 'function';
} }
/**
* TODO: Do the types matter on this? If so, can we improve them so that
* `shallow_thunk(['one', 2])` returns a tuple type instead of a union?
* @template {unknown} T
* @param {Iterable<T>} iterable
* @returns {(() => T)[]}
*/
function shallow_thunk(iterable) {
const thunks = [];
for (const item of iterable) {
thunks.push(() => item);
}
return thunks;
}

@ -36,7 +36,9 @@ export {
effect_active, effect_active,
user_root_effect, user_root_effect,
inspect, inspect,
unwrap unwrap,
proxy_rest_array,
shallow_thunk
} from './client/runtime.js'; } from './client/runtime.js';
export * from './client/each.js'; export * from './client/each.js';

@ -0,0 +1,21 @@
import { test } from '../../test';
export default test({
html: `
<p>clicks: 0, doubled: 0, tripled: 0, quadrupled: 0, something else: 0</p>
<button>click me</button>
`,
async test({ assert, target }) {
const btn = target.querySelector('button');
await btn?.click();
assert.htmlEqual(
target.innerHTML,
`
<p>clicks: 1, doubled: 2, tripled: 3, quadrupled: 4, something else: 5</p>
<button>click me</button>
`
);
}
});

@ -0,0 +1,38 @@
<script lang="ts">
function box(v) {
let state = $state(v)
return {
get value() {
return state;
},
set value(v) {
state = v;
}
}
}
function derivedBox(v, multiplier) {
let state = $derived(v.value * multiplier);
return {
get value() {
return state;
}
}
}
let count = box(0);
let doubled = derivedBox(count, 2);
let tripled = derivedBox(count, 3);
let quadrupled = derivedBox(count, 4);
let whatever_comes_after_that = derivedBox(count, 5);
</script>
{#snippet foo(n: number, ...[doubled, { tripled }, ...rest]: number[])}
<p>clicks: {n.value}, doubled: {doubled.value}, tripled: {tripled.value}, quadrupled: {rest[0].value}, something else: {rest[1].value}</p>
{/snippet}
{@render foo(...[count, doubled, {tripled}, quadrupled, whatever_comes_after_that])}
<button on:click={() => count.value += 1}>
click me
</button>
Loading…
Cancel
Save