@@ -50,6 +50,8 @@ export const javascript_visitors_runes = {
50
50
: rune === '$derived.by'
51
51
? 'derived_call'
52
52
: 'derived' ,
53
+ public_id : definition . key . type === 'PrivateIdentifier' ? null : definition . key ,
54
+ declared_in_constructor : false ,
53
55
// @ts -expect-error this is set in the next pass
54
56
id : is_private ? definition . key : null
55
57
} ;
@@ -61,6 +63,61 @@ export const javascript_visitors_runes = {
61
63
}
62
64
}
63
65
}
66
+ } else if ( definition . type === 'MethodDefinition' && definition . kind === 'constructor' ) {
67
+ for ( const entry of definition . value . body . body ) {
68
+ if (
69
+ entry . type === 'ExpressionStatement' &&
70
+ entry . expression . type === 'AssignmentExpression'
71
+ ) {
72
+ let { left, right } = entry . expression ;
73
+ if (
74
+ left . type !== 'MemberExpression' ||
75
+ left . object . type !== 'ThisExpression' ||
76
+ ( left . property . type !== 'Identifier' && left . property . type !== 'PrivateIdentifier' )
77
+ ) {
78
+ continue ;
79
+ }
80
+
81
+ const id = left . property ;
82
+ const name = get_name ( id ) ;
83
+ if ( ! name ) continue ;
84
+
85
+ const is_private = id . type === 'PrivateIdentifier' ;
86
+ if ( is_private ) private_ids . push ( name ) ;
87
+
88
+ if ( right . type === 'CallExpression' ) {
89
+ const rune = get_rune ( right , state . scope ) ;
90
+ if (
91
+ rune === '$state' ||
92
+ rune === '$state.frozen' ||
93
+ rune === '$derived' ||
94
+ rune === '$derived.by'
95
+ ) {
96
+ /** @type {import('../types.js').StateField } */
97
+ const field = {
98
+ kind :
99
+ rune === '$state'
100
+ ? 'state'
101
+ : rune === '$state.frozen'
102
+ ? 'frozen_state'
103
+ : rune === '$derived.by'
104
+ ? 'derived_call'
105
+ : 'derived' ,
106
+ public_id : id . type === 'PrivateIdentifier' ? null : id ,
107
+ declared_in_constructor : true ,
108
+ // @ts -expect-error this is set in the next pass
109
+ id : is_private ? id : null
110
+ } ;
111
+
112
+ if ( is_private ) {
113
+ private_state . set ( name , field ) ;
114
+ } else {
115
+ public_state . set ( name , field ) ;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
64
121
}
65
122
}
66
123
@@ -78,6 +135,53 @@ export const javascript_visitors_runes = {
78
135
/** @type {Array<import('estree').MethodDefinition | import('estree').PropertyDefinition> } */
79
136
const body = [ ] ;
80
137
138
+ // create getters and setters for public fields
139
+ for ( const [ name , field ] of public_state ) {
140
+ const public_id = /** @type {import('estree').Identifier | import('estree').Literal } */ (
141
+ field . public_id
142
+ ) ;
143
+ const member = b . member ( b . this , field . id ) ;
144
+
145
+ if ( field . declared_in_constructor ) {
146
+ // #foo;
147
+ body . push ( b . prop_def ( field . id , undefined ) ) ;
148
+ }
149
+
150
+ // get foo() { return this.#foo; }
151
+ body . push ( b . method ( 'get' , public_id , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] ) ) ;
152
+
153
+ if ( field . kind === 'state' || field . kind === 'frozen_state' ) {
154
+ // set foo(value) { this.#foo = value; }
155
+ const value = b . id ( 'value' ) ;
156
+ body . push (
157
+ b . method (
158
+ 'set' ,
159
+ public_id ,
160
+ [ value ] ,
161
+ [
162
+ b . stmt (
163
+ b . call (
164
+ '$.set' ,
165
+ member ,
166
+ b . call ( field . kind === 'state' ? '$.proxy' : '$.freeze' , value )
167
+ )
168
+ )
169
+ ]
170
+ )
171
+ ) ;
172
+ } else if ( state . options . dev ) {
173
+ body . push (
174
+ b . method (
175
+ 'set' ,
176
+ public_id ,
177
+ [ b . id ( '_' ) ] ,
178
+ [ b . throw_error ( `Cannot update a derived property ('${ name } ')` ) ]
179
+ )
180
+ ) ;
181
+ }
182
+ }
183
+
184
+ /** @type {import('../types.js').ComponentClientTransformState } */
81
185
const child_state = { ...state , public_state, private_state } ;
82
186
83
187
// Replace parts of the class body
@@ -121,53 +225,8 @@ export const javascript_visitors_runes = {
121
225
value = b . call ( '$.source' ) ;
122
226
}
123
227
124
- if ( is_private ) {
125
- body . push ( b . prop_def ( field . id , value ) ) ;
126
- } else {
127
- // #foo;
128
- const member = b . member ( b . this , field . id ) ;
129
- body . push ( b . prop_def ( field . id , value ) ) ;
130
-
131
- // get foo() { return this.#foo; }
132
- body . push ( b . method ( 'get' , definition . key , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] ) ) ;
133
-
134
- if ( field . kind === 'state' ) {
135
- // set foo(value) { this.#foo = value; }
136
- const value = b . id ( 'value' ) ;
137
- body . push (
138
- b . method (
139
- 'set' ,
140
- definition . key ,
141
- [ value ] ,
142
- [ b . stmt ( b . call ( '$.set' , member , b . call ( '$.proxy' , value ) ) ) ]
143
- )
144
- ) ;
145
- }
146
-
147
- if ( field . kind === 'frozen_state' ) {
148
- // set foo(value) { this.#foo = value; }
149
- const value = b . id ( 'value' ) ;
150
- body . push (
151
- b . method (
152
- 'set' ,
153
- definition . key ,
154
- [ value ] ,
155
- [ b . stmt ( b . call ( '$.set' , member , b . call ( '$.freeze' , value ) ) ) ]
156
- )
157
- ) ;
158
- }
159
-
160
- if ( ( field . kind === 'derived' || field . kind === 'derived_call' ) && state . options . dev ) {
161
- body . push (
162
- b . method (
163
- 'set' ,
164
- definition . key ,
165
- [ b . id ( '_' ) ] ,
166
- [ b . throw_error ( `Cannot update a derived property ('${ name } ')` ) ]
167
- )
168
- ) ;
169
- }
170
- }
228
+ // #foo = $state/$derived();
229
+ body . push ( b . prop_def ( field . id , value ) ) ;
171
230
continue ;
172
231
}
173
232
}
0 commit comments