@@ -6,8 +6,9 @@ import { string_literal } from '../../../utils/stringify';
6
6
import { b , x } from 'code-red' ;
7
7
import Expression from '../../../nodes/shared/Expression' ;
8
8
import Text from '../../../nodes/Text' ;
9
+ import { Identifier , Node } from 'estree' ;
9
10
10
- export default class AttributeWrapper {
11
+ export class BaseAttributeWrapper {
11
12
node : Attribute ;
12
13
parent : ElementWrapper ;
13
14
@@ -20,7 +21,29 @@ export default class AttributeWrapper {
20
21
parent . not_static_content ( ) ;
21
22
22
23
block . add_dependencies ( node . dependencies ) ;
24
+ }
25
+ }
26
+
27
+ render ( _block : Block ) { }
28
+ }
23
29
30
+ export default class AttributeWrapper extends BaseAttributeWrapper {
31
+ node : Attribute ;
32
+ parent : ElementWrapper ;
33
+ metadata : any ;
34
+ name : string ;
35
+ property_name : string ;
36
+ is_indirectly_bound_value : boolean ;
37
+ is_src : boolean ;
38
+ is_select_value_attribute : boolean ;
39
+ is_input_value : boolean ;
40
+ should_cache : boolean ;
41
+ last : Identifier ;
42
+
43
+ constructor ( parent : ElementWrapper , block : Block , node : Attribute ) {
44
+ super ( parent , block , node ) ;
45
+
46
+ if ( node . dependencies . size > 0 ) {
24
47
// special case — <option value={foo}> — see below
25
48
if ( this . parent . node . name === 'option' && node . name === 'value' ) {
26
49
let select : ElementWrapper = this . parent ;
@@ -37,31 +60,22 @@ export default class AttributeWrapper {
37
60
}
38
61
}
39
62
}
40
- }
41
63
42
- is_indirectly_bound_value ( ) {
43
- const element = this . parent ;
44
- const name = fix_attribute_casing ( this . node . name ) ;
45
- return name === 'value' &&
46
- ( element . node . name === 'option' || // TODO check it's actually bound
47
- ( element . node . name === 'input' &&
48
- element . node . bindings . some (
49
- ( binding ) =>
50
- / c h e c k e d | g r o u p / . test ( binding . name )
51
- ) ) ) ;
64
+ this . name = fix_attribute_casing ( this . node . name ) ;
65
+ this . metadata = this . get_metadata ( ) ;
66
+ this . is_indirectly_bound_value = is_indirectly_bound_value ( this ) ;
67
+ this . property_name = this . is_indirectly_bound_value
68
+ ? '__value'
69
+ : this . metadata && this . metadata . property_name ;
70
+ this . is_src = this . name === 'src' ; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
71
+ this . is_select_value_attribute = this . name === 'value' && this . parent . node . name === 'select' ;
72
+ this . is_input_value = this . name === 'value' && this . parent . node . name === 'input' ;
73
+ this . should_cache = should_cache ( this ) ;
52
74
}
53
75
54
76
render ( block : Block ) {
55
77
const element = this . parent ;
56
- const name = fix_attribute_casing ( this . node . name ) ;
57
-
58
- const metadata = this . get_metadata ( ) ;
59
-
60
- const is_indirectly_bound_value = this . is_indirectly_bound_value ( ) ;
61
-
62
- const property_name = is_indirectly_bound_value
63
- ? '__value'
64
- : metadata && metadata . property_name ;
78
+ const { name, property_name, should_cache, is_indirectly_bound_value } = this ;
65
79
66
80
// xlink is a special case... we could maybe extend this to generic
67
81
// namespaced attributes but I'm not sure that's applicable in
@@ -77,81 +91,59 @@ export default class AttributeWrapper {
77
91
const dependencies = this . node . get_dependencies ( ) ;
78
92
const value = this . get_value ( block ) ;
79
93
80
- const is_src = this . node . name === 'src' ; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
81
- const is_select_value_attribute =
82
- name === 'value' && element . node . name === 'select' ;
83
-
84
- const is_input_value = name === 'value' && element . node . name === 'input' ;
85
-
86
- const should_cache = is_src || this . node . should_cache ( ) || is_select_value_attribute ; // TODO is this necessary?
87
-
88
- const last = should_cache && block . get_unique_name (
89
- `${ element . var . name } _${ name . replace ( / [ ^ a - z A - Z _ $ ] / g, '_' ) } _value`
90
- ) ;
91
-
92
- if ( should_cache ) block . add_variable ( last ) ;
93
-
94
94
let updater ;
95
- const init = should_cache ? x ` ${ last } = ${ value } ` : value ;
95
+ const init = this . get_init ( block , value ) ;
96
96
97
97
if ( is_legacy_input_type ) {
98
98
block . chunks . hydrate . push (
99
99
b `@set_input_type(${ element . var } , ${ init } );`
100
100
) ;
101
- updater = b `@set_input_type(${ element . var } , ${ should_cache ? last : value } );` ;
102
- } else if ( is_select_value_attribute ) {
101
+ updater = b `@set_input_type(${ element . var } , ${ should_cache ? this . last : value } );` ;
102
+ } else if ( this . is_select_value_attribute ) {
103
103
// annoying special case
104
104
const is_multiple_select = element . node . get_static_attribute_value ( 'multiple' ) ;
105
105
106
106
if ( is_multiple_select ) {
107
- updater = b `@select_options(${ element . var } , ${ last } );` ;
107
+ updater = b `@select_options(${ element . var } , ${ this . last } );` ;
108
108
} else {
109
- updater = b `@select_option(${ element . var } , ${ last } );` ;
109
+ updater = b `@select_option(${ element . var } , ${ this . last } );` ;
110
110
}
111
111
112
112
block . chunks . mount . push ( b `
113
- ${ last } = ${ value } ;
113
+ ${ this . last } = ${ value } ;
114
114
${ updater }
115
115
` ) ;
116
- } else if ( is_src ) {
116
+ } else if ( this . is_src ) {
117
117
block . chunks . hydrate . push (
118
- b `if (${ element . var } .src !== ${ init } ) ${ method } (${ element . var } , "${ name } ", ${ last } );`
118
+ b `if (${ element . var } .src !== ${ init } ) ${ method } (${ element . var } , "${ name } ", ${ this . last } );`
119
119
) ;
120
- updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? last : value } );` ;
120
+ updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? this . last : value } );` ;
121
121
} else if ( property_name ) {
122
122
block . chunks . hydrate . push (
123
123
b `${ element . var } .${ property_name } = ${ init } ;`
124
124
) ;
125
125
updater = block . renderer . options . dev
126
- ? b `@prop_dev(${ element . var } , "${ property_name } ", ${ should_cache ? last : value } );`
127
- : b `${ element . var } .${ property_name } = ${ should_cache ? last : value } ;` ;
126
+ ? b `@prop_dev(${ element . var } , "${ property_name } ", ${ should_cache ? this . last : value } );`
127
+ : b `${ element . var } .${ property_name } = ${ should_cache ? this . last : value } ;` ;
128
128
} else {
129
129
block . chunks . hydrate . push (
130
130
b `${ method } (${ element . var } , "${ name } ", ${ init } );`
131
131
) ;
132
- updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? last : value } );` ;
132
+ updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? this . last : value } );` ;
133
133
}
134
134
135
- if ( dependencies . length > 0 ) {
136
- let condition = block . renderer . dirty ( dependencies ) ;
137
-
138
- if ( should_cache ) {
139
- condition = is_src
140
- ? x `${ condition } && (${ element . var } .src !== (${ last } = ${ value } ))`
141
- : x `${ condition } && (${ last } !== (${ last } = ${ value } ))` ;
142
- }
143
-
144
- if ( is_input_value ) {
145
- const type = element . node . get_static_attribute_value ( 'type' ) ;
135
+ if ( is_indirectly_bound_value ) {
136
+ const update_value = b `${ element . var } .value = ${ element . var } .__value;` ;
137
+ block . chunks . hydrate . push ( update_value ) ;
146
138
147
- if ( type === null || type === "" || type === "text" || type === "email" || type === "password" ) {
148
- condition = x `${ condition } && ${ element . var } .${ property_name } !== ${ should_cache ? last : value } ` ;
149
- }
150
- }
139
+ updater = b `
140
+ ${ updater }
141
+ ${ update_value } ;
142
+ ` ;
143
+ }
151
144
152
- if ( block . has_outros ) {
153
- condition = x `!#current || ${ condition } ` ;
154
- }
145
+ if ( dependencies . length > 0 ) {
146
+ const condition = this . get_dom_update_conditions ( block , block . renderer . dirty ( dependencies ) ) ;
155
147
156
148
block . chunks . update . push ( b `
157
149
if (${ condition } ) {
@@ -163,26 +155,56 @@ export default class AttributeWrapper {
163
155
if ( this . node . is_true && name === 'autofocus' ) {
164
156
block . autofocus = element . var ;
165
157
}
158
+ }
166
159
167
- if ( is_indirectly_bound_value ) {
168
- const update_value = b `${ element . var } .value = ${ element . var } .__value;` ;
160
+ get_init ( block : Block , value : Node ) {
161
+ this . last = this . should_cache && block . get_unique_name (
162
+ `${ this . parent . var . name } _${ this . name . replace ( / [ ^ a - z A - Z _ $ ] / g, '_' ) } _value`
163
+ ) ;
169
164
170
- block . chunks . hydrate . push ( update_value ) ;
171
- if ( this . node . get_dependencies ( ) . length > 0 ) block . chunks . update . push ( update_value ) ;
165
+ if ( this . should_cache ) block . add_variable ( this . last ) ;
166
+
167
+ return this . should_cache ? x `${ this . last } = ${ value } ` : value ;
168
+ }
169
+
170
+ get_dom_update_conditions ( block : Block , dependency_condition : Node ) {
171
+ const { property_name, should_cache, last } = this ;
172
+ const element = this . parent ;
173
+ const value = this . get_value ( block ) ;
174
+
175
+ let condition = dependency_condition ;
176
+
177
+ if ( should_cache ) {
178
+ condition = this . is_src
179
+ ? x `${ condition } && (${ element . var } .src !== (${ last } = ${ value } ))`
180
+ : x `${ condition } && (${ last } !== (${ last } = ${ value } ))` ;
181
+ }
182
+
183
+ if ( this . is_input_value ) {
184
+ const type = element . node . get_static_attribute_value ( 'type' ) ;
185
+
186
+ if ( type === null || type === "" || type === "text" || type === "email" || type === "password" ) {
187
+ condition = x `${ condition } && ${ element . var } .${ property_name } !== ${ should_cache ? last : value } ` ;
188
+ }
189
+ }
190
+
191
+ if ( block . has_outros ) {
192
+ condition = x `!#current || ${ condition } ` ;
172
193
}
194
+
195
+ return condition ;
173
196
}
174
197
175
198
get_metadata ( ) {
176
199
if ( this . parent . node . namespace ) return null ;
177
- const metadata = attribute_lookup [ fix_attribute_casing ( this . node . name ) ] ;
200
+ const metadata = attribute_lookup [ this . name ] ;
178
201
if ( metadata && metadata . applies_to && ! metadata . applies_to . includes ( this . parent . node . name ) ) return null ;
179
202
return metadata ;
180
203
}
181
204
182
205
get_value ( block ) {
183
206
if ( this . node . is_true ) {
184
- const metadata = this . get_metadata ( ) ;
185
- if ( metadata && boolean_attribute . has ( metadata . property_name . toLowerCase ( ) ) ) {
207
+ if ( this . metadata && boolean_attribute . has ( this . metadata . property_name . toLowerCase ( ) ) ) {
186
208
return x `true` ;
187
209
}
188
210
return x `""` ;
@@ -330,4 +352,19 @@ const boolean_attribute = new Set([
330
352
'required' ,
331
353
'reversed' ,
332
354
'selected'
333
- ] ) ;
355
+ ] ) ;
356
+
357
+ function should_cache ( attribute : AttributeWrapper ) {
358
+ return attribute . is_src || attribute . node . should_cache ( ) || attribute . is_select_value_attribute ; // TODO is this necessary?
359
+ }
360
+
361
+ function is_indirectly_bound_value ( attribute : AttributeWrapper ) {
362
+ const element = attribute . parent ;
363
+ return attribute . name === 'value' &&
364
+ ( element . node . name === 'option' || // TODO check it's actually bound
365
+ ( element . node . name === 'input' &&
366
+ element . node . bindings . some (
367
+ ( binding ) =>
368
+ / c h e c k e d | g r o u p / . test ( binding . name )
369
+ ) ) ) ;
370
+ }
0 commit comments