@@ -9,6 +9,7 @@ import { BaseDirective, BaseNode } from '../../interfaces';
99import { Element } from './Element' ;
1010import { InlineComponent } from './InlineComponent' ;
1111import { surroundWithIgnoreComments } from '../../utils/ignore' ;
12+ import { SequenceExpression } from 'estree' ;
1213
1314/**
1415 * List of binding names that are transformed to sth like `binding = variable`.
@@ -58,47 +59,54 @@ export function handleBinding(
5859 preserveBind : boolean ,
5960 isSvelte5Plus : boolean
6061) : void {
61- // bind group on input
62- if ( element instanceof Element && attr . name == 'group' && parent . name == 'input' ) {
63- // add reassignment to force TS to widen the type of the declaration (in case it's never reassigned anywhere else)
64- appendOneWayBinding ( attr , ' = __sveltets_2_any(null)' , element ) ;
65- return ;
66- }
62+ const isGetSetBinding = attr . expression . type === 'SequenceExpression' ;
6763
68- // bind this
69- if ( attr . name === 'this' && supportsBindThis . includes ( parent . type ) ) {
70- // bind:this is effectively only works bottom up - the variable is updated by the element, not
71- // the other way round. So we check if the instance is assignable to the variable.
72- // Note: If the component unmounts (it's inside an if block, or svelte:component this={null},
73- // the value becomes null, but we don't add it to the clause because it would introduce
74- // worse DX for the 99% use case, and because null !== undefined which others might use to type the declaration.
75- appendOneWayBinding ( attr , ` = ${ element . name } ` , element ) ;
76- return ;
77- }
64+ if ( ! isGetSetBinding ) {
65+ // bind group on input
66+ if ( element instanceof Element && attr . name == 'group' && parent . name == 'input' ) {
67+ // add reassignment to force TS to widen the type of the declaration (in case it's never reassigned anywhere else)
68+ appendOneWayBinding ( attr , ' = __sveltets_2_any(null)' , element ) ;
69+ return ;
70+ }
7871
79- // one way binding
80- if ( oneWayBindingAttributes . has ( attr . name ) && element instanceof Element ) {
81- appendOneWayBinding ( attr , `= ${ element . name } .${ attr . name } ` , element ) ;
82- return ;
83- }
72+ // bind this
73+ if ( attr . name === 'this' && supportsBindThis . includes ( parent . type ) ) {
74+ // bind:this is effectively only works bottom up - the variable is updated by the element, not
75+ // the other way round. So we check if the instance is assignable to the variable.
76+ // Note: If the component unmounts (it's inside an if block, or svelte:component this={null},
77+ // the value becomes null, but we don't add it to the clause because it would introduce
78+ // worse DX for the 99% use case, and because null !== undefined which others might use to type the declaration.
79+ appendOneWayBinding ( attr , ` = ${ element . name } ` , element ) ;
80+ return ;
81+ }
8482
85- // one way binding whose property is not on the element
86- if ( oneWayBindingAttributesNotOnElement . has ( attr . name ) && element instanceof Element ) {
83+ // one way binding
84+ if ( oneWayBindingAttributes . has ( attr . name ) && element instanceof Element ) {
85+ appendOneWayBinding ( attr , `= ${ element . name } .${ attr . name } ` , element ) ;
86+ return ;
87+ }
88+
89+ // one way binding whose property is not on the element
90+ if ( oneWayBindingAttributesNotOnElement . has ( attr . name ) && element instanceof Element ) {
91+ element . appendToStartEnd ( [
92+ [ attr . expression . start , getEnd ( attr . expression ) ] ,
93+ `= ${ surroundWithIgnoreComments (
94+ `null as ${ oneWayBindingAttributesNotOnElement . get ( attr . name ) } `
95+ ) } ;`
96+ ] ) ;
97+ return ;
98+ }
99+
100+ // add reassignment to force TS to widen the type of the declaration (in case it's never reassigned anywhere else)
101+ const expressionStr = str . original . substring (
102+ attr . expression . start ,
103+ getEnd ( attr . expression )
104+ ) ;
87105 element . appendToStartEnd ( [
88- [ attr . expression . start , getEnd ( attr . expression ) ] ,
89- `= ${ surroundWithIgnoreComments (
90- `null as ${ oneWayBindingAttributesNotOnElement . get ( attr . name ) } `
91- ) } ;`
106+ surroundWithIgnoreComments ( `() => ${ expressionStr } = __sveltets_2_any(null);` )
92107 ] ) ;
93- return ;
94108 }
95109
96- // add reassignment to force TS to widen the type of the declaration (in case it's never reassigned anywhere else)
97- const expressionStr = str . original . substring ( attr . expression . start , getEnd ( attr . expression ) ) ;
98- element . appendToStartEnd ( [
99- surroundWithIgnoreComments ( `() => ${ expressionStr } = __sveltets_2_any(null);` )
100- ] ) ;
101-
102110 // other bindings which are transformed to normal attributes/props
103111 const isShorthand = attr . expression . start === attr . start + 'bind:' . length ;
104112 const name : TransformationArray =
@@ -122,11 +130,20 @@ export function handleBinding(
122130 ]
123131 ] ;
124132
133+ const [ get , set ] = isGetSetBinding ? ( attr . expression as SequenceExpression ) . expressions : [ ] ;
125134 const value : TransformationArray | undefined = isShorthand
126135 ? preserveBind && element instanceof Element
127136 ? [ rangeWithTrailingPropertyAccess ( str . original , attr . expression ) ]
128137 : undefined
129- : [ rangeWithTrailingPropertyAccess ( str . original , attr . expression ) ] ;
138+ : isGetSetBinding
139+ ? [
140+ '__sveltets_2_get_set_binding(' ,
141+ [ get . start , get . end ] ,
142+ ',' ,
143+ rangeWithTrailingPropertyAccess ( str . original , set ) ,
144+ ')'
145+ ]
146+ : [ rangeWithTrailingPropertyAccess ( str . original , attr . expression ) ] ;
130147
131148 if ( isSvelte5Plus && element instanceof InlineComponent ) {
132149 // To check if property is actually bindable
0 commit comments