WIP simplify props

pull/9767/head
Rich Harris 3 years ago
parent fd4a52c894
commit 86f931d4e0

@ -795,31 +795,51 @@ function serialize_inline_component(node, component_name, context) {
push_prop(
b.get(attribute.name, [
b.return(
b.call(
'$.exposable',
b.thunk(
/** @type {import('estree').Expression} */ (context.visit(attribute.expression))
)
)
context.state.analysis.immutable
? /** @type {import('estree').Expression} */ (context.visit(attribute.expression))
: b.call(
'$.exposable',
b.thunk(
/** @type {import('estree').Expression} */ (
context.visit(attribute.expression)
)
)
)
)
])
);
// If the binding is just a reference to a top level state variable
// we don't need a setter as the inner component can write to the signal directly
const binding =
attribute.expression.type !== 'Identifier'
? null
: context.state.scope.get(attribute.expression.name);
if (
binding === null ||
(binding.kind !== 'state' && binding.kind !== 'prop' && binding.kind !== 'rest_prop')
) {
if (attribute.expression.type === 'Identifier') {
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
])
);
} else {
if (context.state.analysis.immutable) {
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(
b.assignment(
'=',
/** @type {import('estree').MemberExpression} */ (
context.visit(attribute.expression)
),
b.id('$$value')
)
)
])
);
} else {
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
])
);
}
}
}
}

@ -1,7 +1,7 @@
import { DEV } from 'esm-env';
import { subscribe_to_store } from '../../store/utils.js';
import { EMPTY_FUNC, run_all } from '../common.js';
import { get_descriptors, is_array } from './utils.js';
import { get_descriptor, get_descriptors, is_array } from './utils.js';
export const SOURCE = 1;
export const DERIVED = 1 << 1;
@ -1470,88 +1470,82 @@ export function is_store(val) {
*/
export function prop_source(props_obj, key, immutable, default_value, call_default_value) {
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
const possible_signal = /** @type {import('./types.js').MaybeSignal<V>} */ (
expose(() => props[key])
);
const update_bound_prop = Object.getOwnPropertyDescriptor(props, key)?.set;
const desc = get_descriptor(props, key);
const setter = desc && desc.set;
let value = props[key];
const should_set_default_value = value === undefined && default_value !== undefined;
let was_defined = value !== undefined;
if (
is_signal(possible_signal) &&
possible_signal.v === value &&
update_bound_prop === undefined
) {
if (should_set_default_value) {
set(
possible_signal,
// @ts-expect-error would need a cumbersome method overload to type this
call_default_value ? default_value() : default_value
);
/** @type {import('./types.js').SourceSignal | undefined} */
let exposed = undefined;
if (setter !== undefined) {
if (default_value !== undefined) {
// TODO consolidate all these random runtime errors
throw new Error('Cannot use fallback values with bind:');
}
return possible_signal;
exposed = expose(() => props[key]);
}
if (should_set_default_value) {
value =
// @ts-expect-error would need a cumbersome method overload to type this
call_default_value ? default_value() : default_value;
if (!was_defined && default_value !== undefined) {
value = call_default_value ? /** @type {() => V} */ (default_value)() : default_value;
}
const source_signal = immutable ? source(value) : mutable_source(value);
// Synchronize prop changes with source signal.
// Needs special equality checking because the prop in the
// parent could be changed through `foo.bar = 'new value'`.
let ignore_next1 = false;
let ignore_next2 = false;
let did_update_to_defined = !should_set_default_value;
let updating = false;
let initial = true;
let mount = true;
// sync parent to child
sync_effect(() => {
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
// Before if to ensure signal dependency is registered
const propagating_value = props[key];
if (mount) {
mount = false;
const value = props[key];
// console.log('ptc', { key, value, initial, updating });
if (initial) {
initial = false;
return;
}
if (ignore_next1) {
ignore_next1 = false;
return;
if (updating) return;
updating = true;
if (value !== undefined) {
was_defined = true;
}
if (
// Ensure that updates from undefined to undefined are ignored
(did_update_to_defined || propagating_value !== undefined) &&
not_equal(immutable, propagating_value, source_signal.v)
) {
ignore_next2 = true;
did_update_to_defined = true;
// TODO figure out why we need it this way and the explain in a comment;
// some tests fail is we just do set_signal_value(source_signal, propagating_value)
untrack(() => set_signal_value(source_signal, propagating_value));
if (was_defined) {
set(source_signal, value);
}
updating = false;
});
if (is_signal(possible_signal) && update_bound_prop !== undefined) {
let ignore_first = !should_set_default_value;
// sync child to parent
// TODO get rid of the props signal, so we can just write these as `$$props.foo = ...`
// instead of mucking about with effects (we can probably delete sync_effect)
if (setter) {
let initial = true;
sync_effect(() => {
// Before if to ensure signal dependency is registered
const propagating_value = get(source_signal);
if (ignore_first) {
ignore_first = false;
return;
}
if (ignore_next2) {
ignore_next2 = false;
const value = get(source_signal);
// console.log('ctp', { key, value, initial });
if (initial) {
initial = false;
return;
}
if (not_equal(immutable, propagating_value, possible_signal.v)) {
ignore_next1 = true;
did_update_to_defined = true;
untrack(() => update_bound_prop(propagating_value));
if (updating) return;
setter(value);
// legacy
if (exposed) {
set(exposed, exposed.v);
}
});
}

@ -3,7 +3,7 @@
<script>
import { getContext } from "svelte";
export let count = 0;
export let count;
const context = getContext("context");
</script>

@ -4,7 +4,7 @@
import { setContext } from "svelte";
import Counter from "./Counter.svelte";
export let count;
export let count = 0;
export let counter;
setContext("context", "works");

@ -1,20 +0,0 @@
<script>
export let testcase;
export let value = { foo: 'kid' };
if (testcase === 'init_update') {
value = { foo: 'kid' }
}
if (testcase === 'init_mutate') {
value.foo = 'kid'
}
$: if (testcase === 'reactive_update') {
value = { foo: 'kid' }
}
$: if (testcase === 'reactive_mutate') {
value.foo = 'kid'
}
export let updates = [];
$: updates = [...updates, value];
</script>
<div>child: {value?.foo} | updates: {updates.length}</div>

@ -1,11 +0,0 @@
<script>
import Child from './Child.svelte';
export let child;
export let testcase;
export let value;
export let updates = [];
$: updates = [...updates, value];
</script>
<div>parent: {value?.foo} | updates: {updates.length}</div>
<Child bind:value bind:this={child} {testcase} />

@ -1,47 +0,0 @@
import { test } from '../../test';
export default test({
get props() {
return {
configs: [
{ testcase: 'parent_override_child_default', value: { foo: 'mon' } },
{ testcase: 'child_default_populate_parent', value: undefined },
{ testcase: 'reactive_update', value: { foo: 'mon' } },
{ testcase: 'reactive_mutate', value: { foo: 'mon' } },
{ testcase: 'init_update', value: { foo: 'mon' } },
{ testcase: 'init_mutate', value: { foo: 'mon' } }
]
};
},
async test({ assert, component }) {
const parents = component.parents;
// first testcase should update once
// the rest should update twice
let p;
p = parents['parent_override_child_default'];
assert.deepEqual(p.value, { foo: 'mon' });
assert.equal(p.updates.length, 1);
p = parents['child_default_populate_parent'];
assert.deepEqual(p.value, { foo: 'kid' });
assert.equal(p.updates.length, 2);
p = parents['reactive_update'];
assert.deepEqual(p.value, { foo: 'kid' });
assert.equal(p.updates.length, 2);
p = parents['reactive_mutate'];
assert.deepEqual(p.value, { foo: 'kid' });
assert.equal(p.updates.length, 2);
p = parents['init_update'];
assert.deepEqual(p.value, { foo: 'kid' });
assert.equal(p.updates.length, 2);
p = parents['init_mutate'];
assert.deepEqual(p.value, { foo: 'kid' });
assert.equal(p.updates.length, 2);
}
});

@ -1,9 +0,0 @@
<script>
import Parent from './Parent.svelte';
export let configs = [];
export let parents = {};
</script>
{#each configs as config}
<Parent bind:this={parents[config.testcase]} value={config.value} testcase={config.testcase} />
{/each}

@ -1,12 +0,0 @@
<script>
export let bar = 1;
export let baz = 2;
export function double() {
bar = bar * 2;
baz = baz * 2;
}
</script>
<p>bar in Foo: {bar}</p>
<p>baz in Foo: {baz}</p>

@ -1,20 +0,0 @@
import { test } from '../../test';
export default test({
async test({ assert, component }) {
const { foo, p } = component;
/** @type {string[]} */
const values = [];
Object.defineProperty(p.childNodes[0], 'nodeValue', {
set(value) {
values.push(value);
}
});
await foo.double();
assert.deepEqual(values, ['6']);
}
});

@ -1,12 +0,0 @@
<script>
import Foo from './Foo.svelte';
export let p;
export let foo;
export let bar;
export let baz;
</script>
<Foo bind:this={foo} bind:bar bind:baz/>
<p bind:this={p}>{bar + baz}</p>

@ -1,4 +0,0 @@
<script>
let foo = 42;
export { foo as bar };
</script>

@ -1,7 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<div>42</div>
`
});

@ -1,8 +0,0 @@
<script>
import Widget from './Widget.svelte';
let bar;
</script>
<Widget bind:bar/>
<div>{bar}</div>

@ -1,18 +0,0 @@
<script>
import Two from './Two.svelte';
export let list;
export let i;
function handle_click() {
list = [...list, {}];
}
</script>
{#each list as item, j}
<Two bind:value={item.value} {i} {j}/>
{/each}
<button on:click={handle_click}>
click me
</button>

@ -1,4 +0,0 @@
<script>
export let i, j;
export let value = `${i}:${j}`;
</script>

@ -1,29 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<button>click me</button>
<button>click me</button>
<p>{"value":"0:0"}</p>
<p></p>
`,
async test({ assert, target, window }) {
const button = target.querySelectorAll('button')[1];
await button.dispatchEvent(new window.Event('click', { bubbles: true }));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`
<button>click me</button>
<button>click me</button>
<p>{"value":"0:0"}</p>
<p>{"value":"1:0"}</p>
`
);
}
});

@ -1,14 +0,0 @@
<script>
import One from "./One.svelte";
const obj = {
a: [{}],
b: []
};
</script>
<One bind:list={obj.a} i={0}/>
<One bind:list={obj.b} i={1}/>
<p>{obj.a.map(JSON.stringify)}</p>
<p>{obj.b.map(JSON.stringify)}</p>

@ -1,18 +0,0 @@
<script>
import Two from './Two.svelte';
export let list;
export let i;
function handle_click() {
list = [...list, {}];
}
</script>
{#each list as item, j}
<Two bind:value={item.value} {i} {j}/>
{/each}
<button on:click={handle_click}>
click me
</button>

@ -1,4 +0,0 @@
<script>
export let i, j;
export let value = { i, j };
</script>

@ -1,29 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<button>click me</button>
<button>click me</button>
<p>{"value":{"i":0,"j":0}}</p>
<p></p>
`,
async test({ assert, target, window }) {
const button = target.querySelectorAll('button')[1];
await button.dispatchEvent(new window.Event('click', { bubbles: true }));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`
<button>click me</button>
<button>click me</button>
<p>{"value":{"i":0,"j":0}}</p>
<p>{"value":{"i":1,"j":0}}</p>
`
);
}
});

@ -1,14 +0,0 @@
<script>
import One from "./One.svelte";
const obj = {
a: [{}],
b: []
};
</script>
<One bind:list={obj.a} i={0}/>
<One bind:list={obj.b} i={1}/>
<p>{obj.a.map(JSON.stringify)}</p>
<p>{obj.b.map(JSON.stringify)}</p>

@ -1,5 +0,0 @@
<script>
export let y = 'bar';
</script>
<p>y: {y}</p>

@ -1,5 +0,0 @@
<script>
export let y = 'foo';
</script>
<p>y: {y}</p>

@ -1,26 +0,0 @@
import { test } from '../../test';
export default test({
// This test failed in Svelte 4, because the Bar y binding is activated before the
// Baz x binding, meaning that by the time Foo is created, we already
// have a value for y which Foo won't override. Easily worked around,
// probably impossible to 'fix', so this test is left here for info
// purposes but will probably remain skipped indefinitely - or rather,
// it's okay if it needs to be skipped again sometime in the future.
html: `
<p>y: foo</p>
<p>y: foo</p>
`,
test({ assert, component, target }) {
component.x = false;
assert.htmlEqual(
target.innerHTML,
`
<p>y: foo</p>
<p>y: foo</p>
`
);
}
});

@ -1,18 +0,0 @@
<script>
import Foo from './Foo.svelte';
import Bar from './Bar.svelte';
import Baz from './Baz.svelte';
export let y;
export let x;
</script>
<p>y: {y}</p>
<Baz bind:x/>
{#if x}
<Foo bind:y/>
{:else}
<Bar bind:y/>
{/if}

@ -1,5 +0,0 @@
<script>
export let y = 'bar';
</script>
<p>y: {y}</p>

@ -1,5 +0,0 @@
<script>
export let y = 'foo';
</script>
<p>y: {y}</p>

@ -1,20 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<p>y: bar</p>
<p>y: bar</p>
`,
test({ assert, component, target }) {
component.x = false;
assert.htmlEqual(
target.innerHTML,
`
<p>y: bar</p>
<p>y: bar</p>
`
);
}
});

@ -1,18 +0,0 @@
<script>
import Foo from './Foo.svelte';
import Bar from './Bar.svelte';
import Baz from './Baz.svelte';
export let y;
export let x;
</script>
<p>y: {y}</p>
{#if x}
<Foo bind:y/>
{:else}
<Bar bind:y/>
{/if}
<Baz bind:x/>

@ -1,42 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<p>Foo: yes</p>
<p>x in parent: yes</p>
`,
async test({ assert, component, target }) {
component.a = false;
assert.htmlEqual(
target.innerHTML,
`
<p>Bar: yes</p>
<p>x in parent: yes</p>
`
);
component.a = true;
assert.equal(component.x, 'yes');
component.x = undefined;
assert.htmlEqual(
target.innerHTML,
`
<p>Foo: </p>
<p>x in parent: </p>
`
);
component.a = false;
assert.htmlEqual(
target.innerHTML,
`
<p>Bar: no</p>
<p>x in parent: no</p>
`
);
}
});

@ -1,15 +0,0 @@
<script>
import Foo from './Foo.svelte';
import Bar from './Bar.svelte';
export let a = true;
export let x;
</script>
{#if a}
<Foo bind:x/>
{:else}
<Bar bind:x/>
{/if}
<p>x in parent: {x}</p>

@ -1,42 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<p>Foo: yes</p>
<p>x in parent: yes</p>
`,
async test({ assert, component, target }) {
component.a = false;
assert.htmlEqual(
target.innerHTML,
`
<p>Bar: yes</p>
<p>x in parent: yes</p>
`
);
component.a = true;
assert.equal(component.x, 'yes');
component.x = undefined;
assert.htmlEqual(
target.innerHTML,
`
<p>Foo: </p>
<p>x in parent: </p>
`
);
component.a = false;
assert.htmlEqual(
target.innerHTML,
`
<p>Bar: no</p>
<p>x in parent: no</p>
`
);
}
});

@ -1,11 +0,0 @@
<script>
import Foo from './Foo.svelte';
import Bar from './Bar.svelte';
export let a = true;
export let x;
</script>
<svelte:component this="{a ? Foo : Bar}" bind:x/>
<p>x in parent: {x}</p>

@ -1,5 +0,0 @@
<script>
export let count = 0;
</script>
<button on:click='{() => count += 1}'>+1</button>

@ -1,36 +0,0 @@
import { ok, test } from '../../test';
export default test({
html: `
<button>+1</button>
<p>count: 10</p>
`,
async test({ assert, component, target, window }) {
const click = new window.MouseEvent('click', { bubbles: true });
const button = target.querySelector('button');
ok(button);
await button.dispatchEvent(click);
assert.equal(component.x, 11);
assert.htmlEqual(
target.innerHTML,
`
<button>+1</button>
<p>count: 11</p>
`
);
await button.dispatchEvent(click);
assert.equal(component.x, 12);
assert.htmlEqual(
target.innerHTML,
`
<button>+1</button>
<p>count: 12</p>
`
);
}
});

@ -1,8 +0,0 @@
<script>
import Counter from './Counter.svelte';
export let x = 10;
</script>
<Counter bind:count={x}/>
<p>count: {x}</p>

@ -1,5 +0,0 @@
<script>
export let value = '';
</script>
<input bind:value />

@ -1,71 +0,0 @@
import { test } from '../../test';
export default test({
html: `
<input />
<input />
<div></div>
`,
ssrHtml: `
<input value=""/>
<input value=""/>
<div></div>
`,
async test({ assert, component, target, window }) {
let count = 0;
component.callback = () => {
count++;
};
const [input1, input2] = target.querySelectorAll('input');
input1.value = '1';
await input1.dispatchEvent(new window.Event('input'));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>1</div>
`
);
assert.equal(input1.value, '1');
assert.equal(input2.value, '1');
assert.equal(count, 1);
input2.value = '123';
await input2.dispatchEvent(new window.Event('input'));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>123</div>
`
);
assert.equal(input1.value, '123');
assert.equal(input2.value, '123');
assert.equal(count, 2);
input1.value = '456';
await input1.dispatchEvent(new window.Event('input'));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>456</div>
`
);
assert.equal(input1.value, '456');
assert.equal(input2.value, '456');
assert.equal(count, 3);
}
});

@ -1,18 +0,0 @@
<script>
import { writable } from 'svelte/store';
import Input from './Input.svelte';
let value = writable({ value: '' });
export let callback = () => {};
value.subscribe(() => {
callback();
})
</script>
<input bind:value={$value.value} />
<Input bind:value={$value.value}/>
<div>{$value.value}</div>

@ -1,5 +0,0 @@
<script>
export let count = 0;
</script>
<button on:click='{() => count += 1}'>+1</button>

@ -1,36 +0,0 @@
import { ok, test } from '../../test';
export default test({
html: `
<button>+1</button>
<p>count: 0</p>
`,
async test({ assert, component, target, window }) {
const click = new window.MouseEvent('click', { bubbles: true });
const button = target.querySelector('button');
ok(button);
await button.dispatchEvent(click);
assert.equal(component.x, 1);
assert.htmlEqual(
target.innerHTML,
`
<button>+1</button>
<p>count: 1</p>
`
);
await button.dispatchEvent(click);
assert.equal(component.x, 2);
assert.htmlEqual(
target.innerHTML,
`
<button>+1</button>
<p>count: 2</p>
`
);
}
});

@ -1,8 +0,0 @@
<script>
import Counter from './Counter.svelte';
export let x;
</script>
<Counter bind:count={x}/>
<p>count: {x}</p>

@ -1,3 +0,0 @@
<script>
export let value = 'foo';
</script>

@ -1,10 +0,0 @@
import { test } from '../../test';
export default test({
skip_if_ssr: 'permanent',
html: `
<p>Reactive: foo</p>
<p>Value: foo</p>
`
});

@ -1,9 +0,0 @@
<script>
import Widget from './Widget.svelte';
let value;
$: reactive = value;
</script>
<Widget bind:value/>
<p>Reactive: {reactive}</p>
<p>Value: {value}</p>

@ -1,5 +0,0 @@
<script>
export let foo = 'green';
</script>
<p>green {foo}</p>

@ -1,5 +0,0 @@
<script>
export let foo = 'red';
</script>
<p>red {foo}</p>

@ -1,36 +0,0 @@
import { test } from '../../test';
export default test({
get props() {
return { x: true };
},
html: `
<p>parent green</p>
<p>green green</p>
`,
test({ assert, component, target }) {
component.foo = undefined;
component.x = false;
assert.htmlEqual(
target.innerHTML,
`
<p>parent red</p>
<p>red red</p>
`
);
component.foo = undefined;
component.x = true;
assert.htmlEqual(
target.innerHTML,
`
<p>parent green</p>
<p>green green</p>
`
);
}
});

@ -1,10 +0,0 @@
<script>
import Green from './Green.svelte';
import Red from './Red.svelte';
export let foo;
export let x;
</script>
<p>parent {foo}</p>
<svelte:component this="{x ? Green : Red}" bind:foo />

@ -1,7 +1,7 @@
<script>
export let options = [];
export let value = "";
export let label = "";
export let value;
export let label;
</script>
<select bind:value {...$$restProps}>

@ -92,7 +92,8 @@ afterAll(() => {
export function runtime_suite(runes: boolean) {
return suite_with_variants<RuntimeTest, 'hydrate' | 'ssr' | 'dom', CompileOptions>(
['dom', 'hydrate', 'ssr'],
// ['dom', 'hydrate', 'ssr'],
['dom'],
(variant, config) => {
if (variant === 'hydrate') {
if (config.skip_if_hydrate === 'permanent') return 'no-test';

@ -1,11 +1,11 @@
<script context="module">
class SomeLogic {
someValue = $state(0);
isAboveThree = $derived(this.someValue > 3);
trigger() {
this.someValue++;
}
class SomeLogic {
someValue = $state(0);
isAboveThree = $derived(this.someValue > 3);
trigger() {
this.someValue++;
}
}
const someLogic = new SomeLogic();
</script>

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@ -6,36 +7,31 @@ export default test({
async test({ assert, target }) {
const [b1, b2, b3] = target.querySelectorAll('button');
b1?.click();
await Promise.resolve();
flushSync(() => b1?.click());
assert.htmlEqual(
target.innerHTML,
'<button>1</button><button>1</button><button>change handler</button>'
);
b2?.click();
await Promise.resolve();
flushSync(() => b2?.click());
assert.htmlEqual(
target.innerHTML,
'<button>2</button><button>2</button><button>change handler</button>'
);
b3?.click();
await Promise.resolve();
flushSync(() => b3?.click());
assert.htmlEqual(
target.innerHTML,
'<button>2</button><button>2</button><button>change handler</button>'
);
b1?.click();
await Promise.resolve();
flushSync(() => b1?.click());
assert.htmlEqual(
target.innerHTML,
'<button>1</button><button>1</button><button>change handler</button>'
);
b2?.click();
await Promise.resolve();
flushSync(() => b2?.click());
assert.htmlEqual(
target.innerHTML,
'<button>0</button><button>0</button><button>change handler</button>'

@ -12,5 +12,7 @@ export default test({
await btn?.click();
assert.htmlEqual(target.innerHTML, `<button>1</button><span>1</span>`);
}
},
error: `Cannot use fallback values with bind:`
});

@ -10,7 +10,6 @@ export default test({
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault: 1
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
@ -20,27 +19,24 @@ export default test({
readonly: 0
readonlyWithDefault: 0
binding: 0
bindingWithDefault: 0
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault: 1
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 0
bindingWithDefault: 0
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
@ -50,19 +46,13 @@ export default test({
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined:
bind_bindingWithDefault_undefined: 1
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined: 0
bind_bindingWithDefault_defined: 0
</p>
<button>set everything to 10</button>
@ -93,64 +83,54 @@ export default test({
`
<p>props undefined:</p>
<p>
readonly:
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined:
bind_bindingWithDefault_undefined:
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined:
bind_bindingWithDefault_defined:
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -169,61 +149,51 @@ export default test({
readonly:
readonlyWithDefault: 1
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined: 5
bind_bindingWithDefault_undefined: 5
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined: 5
bind_bindingWithDefault_defined: 5
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -239,61 +209,51 @@ export default test({
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined: 10
readonlyWithDefault_undefined: 10
binding_undefined: 10
bindingWithDefault_undefined: 10
readonly_defined: 10
readonlyWithDefault_defined: 10
binding_defined: 10
bindingWithDefault_defined: 10
bind_readonly_undefined: 10
bind_readonlyWithDefault_undefined: 10
bind_binding_undefined: 10
bind_bindingWithDefault_undefined: 10
bind_readonly_defined: 10
bind_readonlyWithDefault_defined: 10
bind_binding_defined: 10
bind_bindingWithDefault_defined: 10
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -309,61 +269,51 @@ export default test({
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined:
readonlyWithDefault_defined:
binding_defined:
bindingWithDefault_defined:
bind_readonly_undefined:
bind_readonlyWithDefault_undefined:
bind_binding_undefined:
bind_bindingWithDefault_undefined:
bind_readonly_defined:
bind_readonlyWithDefault_defined:
bind_binding_defined:
bind_bindingWithDefault_defined:
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`

@ -1,20 +1,17 @@
<script>
let { readonly, readonlyWithDefault = 1, binding, bindingWithDefault = 1 } = $props();
let { readonly, readonlyWithDefault = 1, binding } = $props();
</script>
<p>
readonly: {readonly}
readonlyWithDefault: {readonlyWithDefault}
binding: {binding}
bindingWithDefault: {bindingWithDefault}
</p>
<button on:click={() => {
binding = 5;
bindingWithDefault = 5;
}}>set bindings to 5</button>
<button on:click={() => {
binding = undefined;
bindingWithDefault = undefined;
}}>set bindings to undefined</button>

@ -4,19 +4,13 @@
let readonly_undefined = $state();
let readonlyWithDefault_undefined = $state();
let binding_undefined = $state();
let bindingWithDefault_undefined = $state();
let readonly_defined = $state(0);
let readonlyWithDefault_defined = $state(0);
let binding_defined = $state(0);
let bindingWithDefault_defined = $state(0);
let bind_readonly_undefined = $state();
let bind_readonlyWithDefault_undefined = $state();
let bind_binding_undefined = $state();
let bind_bindingWithDefault_undefined = $state();
let bind_readonly_defined = $state(0);
let bind_readonlyWithDefault_defined = $state(0);
let bind_binding_defined = $state(0);
let bind_bindingWithDefault_defined = $state(0);
</script>
<p>props undefined:</p>
@ -24,7 +18,6 @@
readonly={readonly_undefined}
readonlyWithDefault={readonlyWithDefault_undefined}
binding={binding_undefined}
bindingWithDefault={bindingWithDefault_undefined}
/>
<p>props defined:</p>
@ -32,23 +25,20 @@
readonly={readonly_defined}
readonlyWithDefault={readonlyWithDefault_defined}
binding={binding_defined}
bindingWithDefault={bindingWithDefault_defined}
/>
<p>bindings undefined:</p>
<Inner
bind:readonly={bind_readonly_undefined}
bind:readonlyWithDefault={bind_readonlyWithDefault_undefined}
readonlyWithDefault={readonlyWithDefault_undefined}
bind:binding={bind_binding_undefined}
bind:bindingWithDefault={bind_bindingWithDefault_undefined}
/>
<p>bindings defined:</p>
<Inner
bind:readonly={bind_readonly_defined}
bind:readonlyWithDefault={bind_readonlyWithDefault_defined}
readonlyWithDefault={readonlyWithDefault_defined}
bind:binding={bind_binding_defined}
bind:bindingWithDefault={bind_bindingWithDefault_defined}
/>
<p>
@ -56,55 +46,37 @@
readonly_undefined: {readonly_undefined}
readonlyWithDefault_undefined: {readonlyWithDefault_undefined}
binding_undefined: {binding_undefined}
bindingWithDefault_undefined: {bindingWithDefault_undefined}
readonly_defined: {readonly_defined}
readonlyWithDefault_defined: {readonlyWithDefault_defined}
binding_defined: {binding_defined}
bindingWithDefault_defined: {bindingWithDefault_defined}
bind_readonly_undefined: {bind_readonly_undefined}
bind_readonlyWithDefault_undefined: {bind_readonlyWithDefault_undefined}
bind_binding_undefined: {bind_binding_undefined}
bind_bindingWithDefault_undefined: {bind_bindingWithDefault_undefined}
bind_readonly_defined: {bind_readonly_defined}
bind_readonlyWithDefault_defined: {bind_readonlyWithDefault_defined}
bind_binding_defined: {bind_binding_defined}
bind_bindingWithDefault_defined: {bind_bindingWithDefault_defined}
</p>
<button on:click={() => {
readonly_undefined = 10;
readonlyWithDefault_undefined = 10;
binding_undefined = 10;
bindingWithDefault_undefined = 10;
readonly_defined = 10;
readonlyWithDefault_defined = 10;
binding_defined = 10;
bindingWithDefault_defined = 10;
bind_readonly_undefined = 10;
bind_readonlyWithDefault_undefined = 10;
bind_binding_undefined = 10;
bind_bindingWithDefault_undefined = 10;
bind_readonly_defined = 10;
bind_readonlyWithDefault_defined = 10;
bind_binding_defined = 10;
bind_bindingWithDefault_defined = 10;
}}>set everything to 10</button>
<button on:click={() => {
readonly_undefined = undefined;
readonlyWithDefault_undefined = undefined;
binding_undefined = undefined;
bindingWithDefault_undefined = undefined;
readonly_defined = undefined;
readonlyWithDefault_defined = undefined;
binding_defined = undefined;
bindingWithDefault_defined = undefined;
bind_readonly_undefined = undefined;
bind_readonlyWithDefault_undefined = undefined;
bind_binding_undefined = undefined;
bind_bindingWithDefault_undefined = undefined;
bind_readonly_defined = undefined;
bind_readonlyWithDefault_defined = undefined;
bind_binding_defined = undefined;
bind_bindingWithDefault_defined = undefined;
}}>set everything to undefined</button>

Loading…
Cancel
Save