Skip to content

Commit 86f931d

Browse files
committed
WIP simplify props
1 parent fd4a52c commit 86f931d

File tree

64 files changed

+137
-993
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+137
-993
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -795,31 +795,51 @@ function serialize_inline_component(node, component_name, context) {
795795
push_prop(
796796
b.get(attribute.name, [
797797
b.return(
798-
b.call(
799-
'$.exposable',
800-
b.thunk(
801-
/** @type {import('estree').Expression} */ (context.visit(attribute.expression))
802-
)
803-
)
798+
context.state.analysis.immutable
799+
? /** @type {import('estree').Expression} */ (context.visit(attribute.expression))
800+
: b.call(
801+
'$.exposable',
802+
b.thunk(
803+
/** @type {import('estree').Expression} */ (
804+
context.visit(attribute.expression)
805+
)
806+
)
807+
)
804808
)
805809
])
806810
);
807-
// If the binding is just a reference to a top level state variable
808-
// we don't need a setter as the inner component can write to the signal directly
809-
const binding =
810-
attribute.expression.type !== 'Identifier'
811-
? null
812-
: context.state.scope.get(attribute.expression.name);
813-
if (
814-
binding === null ||
815-
(binding.kind !== 'state' && binding.kind !== 'prop' && binding.kind !== 'rest_prop')
816-
) {
811+
812+
if (attribute.expression.type === 'Identifier') {
817813
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
818814
push_prop(
819815
b.set(attribute.name, [
820816
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
821817
])
822818
);
819+
} else {
820+
if (context.state.analysis.immutable) {
821+
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
822+
push_prop(
823+
b.set(attribute.name, [
824+
b.stmt(
825+
b.assignment(
826+
'=',
827+
/** @type {import('estree').MemberExpression} */ (
828+
context.visit(attribute.expression)
829+
),
830+
b.id('$$value')
831+
)
832+
)
833+
])
834+
);
835+
} else {
836+
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
837+
push_prop(
838+
b.set(attribute.name, [
839+
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
840+
])
841+
);
842+
}
823843
}
824844
}
825845
}

packages/svelte/src/internal/client/runtime.js

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DEV } from 'esm-env';
22
import { subscribe_to_store } from '../../store/utils.js';
33
import { EMPTY_FUNC, run_all } from '../common.js';
4-
import { get_descriptors, is_array } from './utils.js';
4+
import { get_descriptor, get_descriptors, is_array } from './utils.js';
55

66
export const SOURCE = 1;
77
export const DERIVED = 1 << 1;
@@ -1470,88 +1470,82 @@ export function is_store(val) {
14701470
*/
14711471
export function prop_source(props_obj, key, immutable, default_value, call_default_value) {
14721472
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
1473-
const possible_signal = /** @type {import('./types.js').MaybeSignal<V>} */ (
1474-
expose(() => props[key])
1475-
);
1476-
const update_bound_prop = Object.getOwnPropertyDescriptor(props, key)?.set;
1473+
const desc = get_descriptor(props, key);
1474+
const setter = desc && desc.set;
1475+
14771476
let value = props[key];
1478-
const should_set_default_value = value === undefined && default_value !== undefined;
1477+
let was_defined = value !== undefined;
14791478

1480-
if (
1481-
is_signal(possible_signal) &&
1482-
possible_signal.v === value &&
1483-
update_bound_prop === undefined
1484-
) {
1485-
if (should_set_default_value) {
1486-
set(
1487-
possible_signal,
1488-
// @ts-expect-error would need a cumbersome method overload to type this
1489-
call_default_value ? default_value() : default_value
1490-
);
1479+
/** @type {import('./types.js').SourceSignal | undefined} */
1480+
let exposed = undefined;
1481+
1482+
if (setter !== undefined) {
1483+
if (default_value !== undefined) {
1484+
// TODO consolidate all these random runtime errors
1485+
throw new Error('Cannot use fallback values with bind:');
14911486
}
1492-
return possible_signal;
1487+
1488+
exposed = expose(() => props[key]);
14931489
}
14941490

1495-
if (should_set_default_value) {
1496-
value =
1497-
// @ts-expect-error would need a cumbersome method overload to type this
1498-
call_default_value ? default_value() : default_value;
1491+
if (!was_defined && default_value !== undefined) {
1492+
value = call_default_value ? /** @type {() => V} */ (default_value)() : default_value;
14991493
}
15001494

15011495
const source_signal = immutable ? source(value) : mutable_source(value);
15021496

1503-
// Synchronize prop changes with source signal.
1504-
// Needs special equality checking because the prop in the
1505-
// parent could be changed through `foo.bar = 'new value'`.
1506-
let ignore_next1 = false;
1507-
let ignore_next2 = false;
1508-
let did_update_to_defined = !should_set_default_value;
1497+
let updating = false;
1498+
let initial = true;
15091499

1510-
let mount = true;
1500+
// sync parent to child
15111501
sync_effect(() => {
15121502
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
1513-
// Before if to ensure signal dependency is registered
1514-
const propagating_value = props[key];
1515-
if (mount) {
1516-
mount = false;
1503+
const value = props[key];
1504+
1505+
// console.log('ptc', { key, value, initial, updating });
1506+
1507+
if (initial) {
1508+
initial = false;
15171509
return;
15181510
}
1519-
if (ignore_next1) {
1520-
ignore_next1 = false;
1521-
return;
1511+
1512+
if (updating) return;
1513+
updating = true;
1514+
1515+
if (value !== undefined) {
1516+
was_defined = true;
15221517
}
15231518

1524-
if (
1525-
// Ensure that updates from undefined to undefined are ignored
1526-
(did_update_to_defined || propagating_value !== undefined) &&
1527-
not_equal(immutable, propagating_value, source_signal.v)
1528-
) {
1529-
ignore_next2 = true;
1530-
did_update_to_defined = true;
1531-
// TODO figure out why we need it this way and the explain in a comment;
1532-
// some tests fail is we just do set_signal_value(source_signal, propagating_value)
1533-
untrack(() => set_signal_value(source_signal, propagating_value));
1519+
if (was_defined) {
1520+
set(source_signal, value);
15341521
}
1522+
1523+
updating = false;
15351524
});
15361525

1537-
if (is_signal(possible_signal) && update_bound_prop !== undefined) {
1538-
let ignore_first = !should_set_default_value;
1526+
// sync child to parent
1527+
// TODO get rid of the props signal, so we can just write these as `$$props.foo = ...`
1528+
// instead of mucking about with effects (we can probably delete sync_effect)
1529+
if (setter) {
1530+
let initial = true;
1531+
15391532
sync_effect(() => {
1540-
// Before if to ensure signal dependency is registered
1541-
const propagating_value = get(source_signal);
1542-
if (ignore_first) {
1543-
ignore_first = false;
1544-
return;
1545-
}
1546-
if (ignore_next2) {
1547-
ignore_next2 = false;
1533+
const value = get(source_signal);
1534+
1535+
// console.log('ctp', { key, value, initial });
1536+
1537+
if (initial) {
1538+
initial = false;
15481539
return;
15491540
}
15501541

1551-
if (not_equal(immutable, propagating_value, possible_signal.v)) {
1552-
ignore_next1 = true;
1553-
did_update_to_defined = true;
1554-
untrack(() => update_bound_prop(propagating_value));
1542+
if (updating) return;
1543+
1544+
setter(value);
1545+
1546+
// legacy
1547+
if (exposed) {
1548+
set(exposed, exposed.v);
15551549
}
15561550
});
15571551
}

packages/svelte/tests/runtime-browser/custom-elements-samples/nested/Counter.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<script>
44
import { getContext } from "svelte";
55
6-
export let count = 0;
6+
export let count;
77
88
const context = getContext("context");
99
</script>

packages/svelte/tests/runtime-browser/custom-elements-samples/nested/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { setContext } from "svelte";
55
import Counter from "./Counter.svelte";
66
7-
export let count;
7+
export let count = 0;
88
export let counter;
99
1010
setContext("context", "works");

packages/svelte/tests/runtime-legacy/samples/binding-backflow/Child.svelte

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/binding-backflow/Parent.svelte

Lines changed: 0 additions & 11 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/binding-backflow/_config.js

Lines changed: 0 additions & 47 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/binding-backflow/main.svelte

Lines changed: 0 additions & 9 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/Foo.svelte

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/main.svelte

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/component-binding-aliased/Widget.svelte

Lines changed: 0 additions & 4 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/component-binding-aliased/_config.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/svelte/tests/runtime-legacy/samples/component-binding-aliased/main.svelte

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)