Skip to content

Commit d563884

Browse files
committed
add changeset and tests
1 parent f75a781 commit d563884

File tree

9 files changed

+99
-11
lines changed

9 files changed

+99
-11
lines changed

.changeset/slimy-donkeys-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
feat: add support for bind getters/setters

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function build_component(node, component_name, context, anchor = context.
4242
/** @type {Property[]} */
4343
const custom_css_props = [];
4444

45-
/** @type {Identifier | MemberExpression | null} */
45+
/** @type {Identifier | MemberExpression | [Expression, Expression] | null} */
4646
let bind_this = null;
4747

4848
/** @type {ExpressionStatement[]} */
@@ -162,17 +162,21 @@ export function build_component(node, component_name, context, anchor = context.
162162
}
163163
} else if (attribute.type === 'BindDirective') {
164164
if (Array.isArray(attribute.expression)) {
165-
const [get_expression, set_expression] = attribute.expression;
166-
const get = /** @type {Expression} */ (context.visit(get_expression));
167-
const set = /** @type {Expression} */ (context.visit(set_expression));
168-
const get_id = b.id(context.state.scope.generate('bind_get'));
169-
const set_id = b.id(context.state.scope.generate('bind_set'));
165+
if (attribute.name === 'this') {
166+
bind_this = attribute.expression;
167+
} else {
168+
const [get_expression, set_expression] = attribute.expression;
169+
const get = /** @type {Expression} */ (context.visit(get_expression));
170+
const set = /** @type {Expression} */ (context.visit(set_expression));
171+
const get_id = b.id(context.state.scope.generate('bind_get'));
172+
const set_id = b.id(context.state.scope.generate('bind_set'));
170173

171-
context.state.init.push(b.var(get_id, get));
172-
context.state.init.push(b.var(set_id, set));
174+
context.state.init.push(b.var(get_id, get));
175+
context.state.init.push(b.var(set_id, set));
173176

174-
push_prop(b.get(attribute.name, [b.return(b.call(get_id))]));
175-
push_prop(b.set(attribute.name, [b.stmt(b.call(set_id, b.id('$$value')))]));
177+
push_prop(b.get(attribute.name, [b.return(b.call(get_id))]));
178+
push_prop(b.set(attribute.name, [b.stmt(b.call(set_id, b.id('$$value')))]));
179+
}
176180
} else {
177181
const expression = /** @type {Expression} */ (context.visit(attribute.expression));
178182

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export function build_bind_this(expression, value, { state, visit }) {
171171
const get = /** @type {Expression} */ (visit(get_expression));
172172
const set = /** @type {Expression} */ (visit(set_expression));
173173

174-
return b.call('$.bind_this', value, get, set);
174+
return b.call('$.bind_this', value, set, get);
175175
}
176176

177177
/** @type {Identifier[]} */
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
let div = $state();
3+
4+
$effect(() => {
5+
console.log(div?.textContent);
6+
})
7+
8+
export const someData = '123';
9+
</script>
10+
11+
<div bind:this={() => div, v => div = v}>123</div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
async test({ assert, target, logs }) {
5+
assert.htmlEqual(target.innerHTML, `<div>123</div>`);
6+
7+
assert.deepEqual(logs, ['123', '123']);
8+
}
9+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
let child = $state();
5+
6+
$effect(() => {
7+
console.log(child.someData);
8+
});
9+
</script>
10+
11+
<Child bind:this={() => child, v => child = v} />
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
let { a = $bindable() } = $props();
3+
</script>
4+
5+
<input
6+
type="value"
7+
bind:value={() => a,
8+
(v) => {
9+
console.log('b', v);
10+
a = v;
11+
}}
12+
/>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
import { assert_ok } from '../../../suite';
4+
5+
export default test({
6+
async test({ assert, target, logs }) {
7+
const input = target.querySelector('input');
8+
9+
assert_ok(input);
10+
11+
input.value = '2';
12+
input.dispatchEvent(new window.Event('input'));
13+
14+
flushSync();
15+
16+
assert.htmlEqual(target.innerHTML, `<button>a: 2</button><input type="value">`);
17+
18+
assert.deepEqual(logs, ['b', '2', 'a', '2']);
19+
}
20+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
let a = $state(0);
5+
</script>
6+
7+
<button onclick={() => a++}>a: {a}</button>
8+
9+
<Child
10+
bind:a={() => a,
11+
(v) => {
12+
console.log('a', v);
13+
a = v;
14+
}}
15+
/>
16+

0 commit comments

Comments
 (0)