1
- /** @import { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Node, Identifier, PrivateIdentifier, Statement } from 'estree' */
1
+ /** @import { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Statement } from 'estree' */
2
2
/** @import { Context } from '../types' */
3
3
import * as b from '../../../../utils/builders.js' ;
4
4
import { get_rune } from '../../../scope.js' ;
5
5
import { should_proxy } from '../utils.js' ;
6
- import { walk } from 'zimmerframe' ;
7
6
8
7
/**
9
8
* @param {ObjectExpression } node
10
9
* @param {Context } context
11
10
*/
12
11
export function ObjectExpression ( node , context ) {
13
12
/**
14
- * @typedef {[string, NonNullable<ReturnType<typeof get_rune>>, '$.state' | '$.derived', Expression, boolean ] } ReactiveProperty
13
+ * @typedef {[string, NonNullable<ReturnType<typeof get_rune>>] } ReactiveProperty
15
14
*/
16
15
let has_runes = false ;
17
- /**
18
- * @type {Array<{rune: NonNullable<ReturnType<typeof get_rune>>, property: Property & {value: CallExpression}}> }
19
- */
20
- const reactive_properties = [ ] ;
21
16
const valid_property_runes = [ '$state' , '$derived' , '$state.raw' , '$derived.by' ] ;
17
+ /** @type {Statement[] } */
18
+ const body = [ ] ;
19
+ /** @type {Map<Property, ReactiveProperty> } */
20
+ const sources = new Map ( ) ;
21
+ let counter = 0 ;
22
22
for ( let property of node . properties ) {
23
23
if ( property . type !== 'Property' ) continue ;
24
24
const rune = get_rune ( property . value , context . state . scope ) ;
25
25
if ( rune && valid_property_runes . includes ( rune ) ) {
26
26
has_runes = true ;
27
- reactive_properties . push ( {
28
- rune,
29
- property : /**@type {Property & {value: CallExpression} } */ ( property )
30
- } ) ;
27
+ const name = context . state . scope . generate ( `$$${ ++ counter } ` ) ;
28
+ const call = rune . match ( / ^ \$ s t a t e / ) ? '$.state' : '$.derived' ;
29
+ /** @type {Expression } */
30
+ let value = /** @type {Expression } */ (
31
+ context . visit ( /** @type {CallExpression } */ ( property . value ) . arguments [ 0 ] ?? b . void0 )
32
+ ) ;
33
+ value =
34
+ rune === '$derived'
35
+ ? b . thunk ( value )
36
+ : rune === '$state' && should_proxy ( value , context . state . scope )
37
+ ? b . call ( '$.proxy' , value )
38
+ : value ;
39
+ /** @type {ReactiveProperty } */
40
+ const source = [ name , rune ] ;
41
+ sources . set ( property , source ) ;
42
+ body . push ( b . let ( name , b . call ( call , value ) ) ) ;
31
43
}
32
44
}
33
45
if ( ! has_runes ) {
34
46
context . next ( ) ;
35
47
return ;
36
48
}
37
- /** @type {Statement[] } */
38
- const body = [ ] ;
39
- /** @type {Map<Property, ReactiveProperty> } */
40
- const sources = new Map ( ) ;
41
- let has_this_reference = false ;
42
- let counter = 0 ;
43
- /** @type {Statement[] } */
44
- const before = [ ] ;
45
- /** @type {Statement[] } */
46
- const after = [ ] ;
47
- /** @type {string[] } */
48
- const declarations = [ ] ;
49
- /** @type {Map<string, Expression | undefined> } */
50
- const initial_declarations = new Map ( ) ;
51
- // if a computed property is accessed, we treat it as if all of the object's properties have been accessed
52
- let all_are_referenced = false ;
53
- /** @type {Set<any> } */
54
- const is_referenced = new Set ( ) ;
55
- for ( let property of node . properties ) {
56
- walk ( property , null , {
57
- //@ts -ignore
58
- FunctionExpression ( ) {
59
- return ;
60
- } ,
61
- //@ts -ignore
62
- FunctionDeclaration ( ) {
63
- return ;
64
- } ,
65
- ObjectExpression ( ) {
66
- return ;
67
- } ,
68
- /**
69
- *
70
- * @param {Node } node
71
- * @param {import('zimmerframe').Context<Node, null> } context
72
- */
73
- ThisExpression ( node , context ) {
74
- const parent = context . path . at ( - 1 ) ;
75
- if ( parent ?. type === 'MemberExpression' ) {
76
- if ( parent . computed ) {
77
- all_are_referenced = true ;
78
- } else {
79
- is_referenced . add ( /** @type {Identifier | PrivateIdentifier } */ ( parent . property ) . name ) ;
80
- }
81
- }
82
- } ,
83
- ClassBody ( ) {
84
- return ;
85
- }
86
- } ) ;
87
- }
88
- for ( let { rune, property } of reactive_properties ) {
89
- const name = context . state . scope . generate ( `$$${ ++ counter } ` ) ;
90
- const call = rune . match ( / ^ \$ s t a t e / ) ? '$.state' : '$.derived' ;
91
- let references_this = false ;
92
- /** @type {Expression } */
93
- let value = /** @type {Expression } */ ( context . visit ( property . value . arguments [ 0 ] ?? b . void0 ) ) ;
94
- value = walk ( value , null , {
95
- FunctionExpression ( ) {
96
- return ;
97
- } ,
98
- //@ts -ignore
99
- FunctionDeclaration ( ) {
100
- return ;
101
- } ,
102
- ObjectExpression ( ) {
103
- return ;
104
- } ,
105
- ThisExpression ( ) {
106
- has_this_reference = true ;
107
- references_this = true ;
108
- return b . id ( '$$object' ) ;
109
- } ,
110
- ClassBody ( ) {
111
- return ;
112
- }
113
- } ) ;
114
- value =
115
- rune === '$derived'
116
- ? b . thunk ( value )
117
- : rune === '$state' && should_proxy ( value , context . state . scope )
118
- ? b . call ( '$.proxy' , value )
119
- : value ;
120
- let key = property . computed
121
- ? Symbol ( )
122
- : property . key . type === 'Literal'
123
- ? property . key . value
124
- : /** @type {Identifier } */ ( property . key ) . name ;
125
- if ( rune . match ( / ^ \$ s t a t e / ) && ! ( all_are_referenced || is_referenced . has ( key ) ) ) {
126
- let should_be_declared = false ;
127
- walk ( value , null , {
128
- CallExpression ( node , context ) {
129
- should_be_declared = true ;
130
- context . stop ( ) ;
131
- } ,
132
- MemberExpression ( node , context ) {
133
- should_be_declared = true ;
134
- context . stop ( ) ;
135
- }
136
- } ) ;
137
- if ( should_be_declared ) {
138
- const value_name = context . state . scope . generate ( '$$initial' ) ;
139
- initial_declarations . set ( value_name , value ) ;
140
- value = b . id ( value_name ) ;
141
- }
142
- }
143
- /** @type {ReactiveProperty } */
144
- const source = [
145
- name ,
146
- rune ,
147
- call ,
148
- value ,
149
- ( value . type === 'Identifier' && initial_declarations . has ( value . name ) ) || references_this
150
- ] ;
151
- sources . set ( property , source ) ;
152
- if ( references_this ) {
153
- declarations . push ( name ) ;
154
- } else if ( source [ 4 ] ) {
155
- before . push ( b . let ( name , value ) ) ;
156
- } else {
157
- before . push ( b . let ( name , b . call ( call , value ) ) ) ;
158
- }
159
- }
160
- if ( declarations . length ) {
161
- before . push (
162
- b . declaration (
163
- 'let' ,
164
- declarations . map ( ( name ) => b . declarator ( name ) )
165
- )
166
- ) ;
167
- }
168
- for ( let [ name , value ] of initial_declarations ) {
169
- after . push ( b . let ( name , value ) ) ;
170
- }
171
49
/** @type {(Property | SpreadElement)[] } */
172
50
const properties = [ ] ;
173
51
for ( let property of node . properties ) {
@@ -176,17 +54,12 @@ export function ObjectExpression(node, context) {
176
54
continue ;
177
55
}
178
56
if ( sources . has ( property ) ) {
179
- const [ name , rune , call , value , initially_declared ] = /** @type {ReactiveProperty } */ (
180
- sources . get ( property )
181
- ) ;
182
- let maybe_assign = initially_declared
183
- ? b . assignment ( '??=' , b . id ( name ) , b . call ( call , value ) )
184
- : b . id ( name ) ;
57
+ const [ name , rune ] = /** @type {ReactiveProperty } */ ( sources . get ( property ) ) ;
185
58
properties . push (
186
59
b . prop (
187
60
'get' ,
188
61
/** @type {Expression } */ ( context . visit ( /**@type {Expression } */ ( property . key ) ) ) ,
189
- b . function ( null , [ ] , b . block ( [ b . return ( b . call ( '$.get' , maybe_assign ) ) ] ) ) ,
62
+ b . function ( null , [ ] , b . block ( [ b . return ( b . call ( '$.get' , b . id ( name ) ) ) ] ) ) ,
190
63
property . computed
191
64
) ,
192
65
b . prop (
@@ -197,12 +70,7 @@ export function ObjectExpression(node, context) {
197
70
[ b . id ( '$$value' ) ] ,
198
71
b . block ( [
199
72
b . stmt (
200
- b . call (
201
- '$.set' ,
202
- maybe_assign ,
203
- b . id ( '$$value' ) ,
204
- rune === '$state' ? b . true : undefined
205
- )
73
+ b . call ( '$.set' , b . id ( name ) , b . id ( '$$value' ) , rune === '$state' ? b . true : undefined )
206
74
)
207
75
] )
208
76
) ,
@@ -213,14 +81,6 @@ export function ObjectExpression(node, context) {
213
81
properties . push ( /** @type {Property } */ ( context . visit ( property ) ) ) ;
214
82
}
215
83
}
216
- if ( has_this_reference ) {
217
- body . push ( ...before ) ;
218
- body . push ( b . let ( '$$object' , b . object ( properties ) ) ) ;
219
- body . push ( ...after ) ;
220
- body . push ( b . return ( b . id ( '$$object' ) ) ) ;
221
- } else {
222
- body . push ( ...before , ...after ) ;
223
- body . push ( b . return ( b . object ( properties ) ) ) ;
224
- }
84
+ body . push ( b . return ( b . object ( properties ) ) ) ;
225
85
return b . call ( b . arrow ( [ ] , b . block ( body ) ) ) ;
226
86
}
0 commit comments