1
- /** @import { AssignmentExpression, BinaryOperator, CallExpression, Expression, ExpressionStatement, MethodDefinition, Pattern, Program, Property, PropertyDefinition, Statement, VariableDeclarator } from 'estree' */
1
+ /** @import { Expression, ExpressionStatement, MethodDefinition, Pattern, Program, Property, PropertyDefinition, Statement, VariableDeclarator } from 'estree' */
2
2
/** @import { Binding, Namespace, SvelteNode, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
3
3
/** @import { ComponentServerTransformState, ComponentVisitors, ServerTransformState, Visitors } from './types.js' */
4
4
/** @import { Analysis, ComponentAnalysis } from '../../types.js' */
@@ -8,10 +8,12 @@ import { walk } from 'zimmerframe';
8
8
import { set_scope , get_rune } from '../../scope.js' ;
9
9
import { extract_identifiers , extract_paths , is_expression_async } from '../../../utils/ast.js' ;
10
10
import * as b from '../../../utils/builders.js' ;
11
- import { transform_inspect_rune } from '../utils.js' ;
12
11
import { filename } from '../../../state.js' ;
13
12
import { render_stylesheet } from '../css/index.js' ;
13
+ import { AssignmentExpression } from './visitors/javascript/AssignmentExpression.js' ;
14
+ import { CallExpression } from './visitors/javascript/CallExpression.js' ;
14
15
import { Identifier } from './visitors/javascript/Identifier.js' ;
16
+ import { UpdateExpression } from './visitors/javascript/UpdateExpression.js' ;
15
17
import { AwaitBlock } from './visitors/template/AwaitBlock.js' ;
16
18
import { Component } from './visitors/template/Component.js' ;
17
19
import { ConstTag } from './visitors/template/ConstTag.js' ;
@@ -33,7 +35,6 @@ import { SvelteFragment } from './visitors/template/SvelteFragment.js';
33
35
import { SvelteHead } from './visitors/template/SvelteHead.js' ;
34
36
import { SvelteSelf } from './visitors/template/SvelteSelf.js' ;
35
37
import { TitleElement } from './visitors/template/TitleElement.js' ;
36
- import { serialize_get_binding } from './visitors/javascript/shared/utils.js' ;
37
38
38
39
/**
39
40
* @param {VariableDeclarator } declarator
@@ -57,193 +58,12 @@ function create_state_declarators(declarator, scope, value) {
57
58
] ;
58
59
}
59
60
60
- /**
61
- * @param {AssignmentExpression } node
62
- * @param {Pick<import('zimmerframe').Context<SvelteNode, ServerTransformState>, 'visit' | 'state'> } context
63
- */
64
- function get_assignment_value ( node , { state, visit } ) {
65
- if ( node . left . type === 'Identifier' ) {
66
- const operator = node . operator ;
67
- return operator === '='
68
- ? /** @type {Expression } */ ( visit ( node . right ) )
69
- : // turn something like x += 1 into x = x + 1
70
- b . binary (
71
- /** @type {BinaryOperator } */ ( operator . slice ( 0 , - 1 ) ) ,
72
- serialize_get_binding ( node . left , state ) ,
73
- /** @type {Expression } */ ( visit ( node . right ) )
74
- ) ;
75
- }
76
-
77
- return /** @type {Expression } */ ( visit ( node . right ) ) ;
78
- }
79
-
80
- /**
81
- * @param {string } name
82
- */
83
- function is_store_name ( name ) {
84
- return name [ 0 ] === '$' && / [ A - Z a - z _ ] / . test ( name [ 1 ] ) ;
85
- }
86
-
87
- /**
88
- * @param {AssignmentExpression } node
89
- * @param {import('zimmerframe').Context<SvelteNode, ServerTransformState> } context
90
- * @param {() => any } fallback
91
- * @returns {Expression }
92
- */
93
- function serialize_set_binding ( node , context , fallback ) {
94
- const { state, visit } = context ;
95
-
96
- if (
97
- node . left . type === 'ArrayPattern' ||
98
- node . left . type === 'ObjectPattern' ||
99
- node . left . type === 'RestElement'
100
- ) {
101
- // Turn assignment into an IIFE, so that `$.set` calls etc don't produce invalid code
102
- const tmp_id = context . state . scope . generate ( 'tmp' ) ;
103
-
104
- /** @type {AssignmentExpression[] } */
105
- const original_assignments = [ ] ;
106
-
107
- /** @type {Expression[] } */
108
- const assignments = [ ] ;
109
-
110
- const paths = extract_paths ( node . left ) ;
111
-
112
- for ( const path of paths ) {
113
- const value = path . expression ?. ( b . id ( tmp_id ) ) ;
114
- const assignment = b . assignment ( '=' , path . node , value ) ;
115
- original_assignments . push ( assignment ) ;
116
- assignments . push ( serialize_set_binding ( assignment , context , ( ) => assignment ) ) ;
117
- }
118
-
119
- if ( assignments . every ( ( assignment , i ) => assignment === original_assignments [ i ] ) ) {
120
- // No change to output -> nothing to transform -> we can keep the original assignment
121
- return fallback ( ) ;
122
- }
123
-
124
- return b . call (
125
- b . thunk (
126
- b . block ( [
127
- b . const ( tmp_id , /** @type {Expression } */ ( visit ( node . right ) ) ) ,
128
- b . stmt ( b . sequence ( assignments ) ) ,
129
- b . return ( b . id ( tmp_id ) )
130
- ] )
131
- )
132
- ) ;
133
- }
134
-
135
- if ( node . left . type !== 'Identifier' && node . left . type !== 'MemberExpression' ) {
136
- throw new Error ( `Unexpected assignment type ${ node . left . type } ` ) ;
137
- }
138
-
139
- let left = node . left ;
140
-
141
- while ( left . type === 'MemberExpression' ) {
142
- // @ts -expect-error
143
- left = left . object ;
144
- }
145
-
146
- if ( left . type !== 'Identifier' ) {
147
- return fallback ( ) ;
148
- }
149
-
150
- const is_store = is_store_name ( left . name ) ;
151
- const left_name = is_store ? left . name . slice ( 1 ) : left . name ;
152
- const binding = state . scope . get ( left_name ) ;
153
-
154
- if ( ! binding ) return fallback ( ) ;
155
-
156
- if ( binding . mutation !== null ) {
157
- return binding . mutation ( node , context ) ;
158
- }
159
-
160
- if (
161
- binding . kind !== 'state' &&
162
- binding . kind !== 'frozen_state' &&
163
- binding . kind !== 'prop' &&
164
- binding . kind !== 'bindable_prop' &&
165
- binding . kind !== 'each' &&
166
- binding . kind !== 'legacy_reactive' &&
167
- ! is_store
168
- ) {
169
- // TODO error if it's a computed (or rest prop)? or does that already happen elsewhere?
170
- return fallback ( ) ;
171
- }
172
-
173
- const value = get_assignment_value ( node , { state, visit } ) ;
174
- if ( left === node . left ) {
175
- if ( is_store ) {
176
- return b . call ( '$.store_set' , b . id ( left_name ) , /** @type {Expression } */ ( visit ( node . right ) ) ) ;
177
- }
178
- return fallback ( ) ;
179
- } else if ( is_store ) {
180
- return b . call (
181
- '$.mutate_store' ,
182
- b . assignment ( '??=' , b . id ( '$$store_subs' ) , b . object ( [ ] ) ) ,
183
- b . literal ( left . name ) ,
184
- b . id ( left_name ) ,
185
- b . assignment ( node . operator , /** @type {Pattern } */ ( visit ( node . left ) ) , value )
186
- ) ;
187
- }
188
- return fallback ( ) ;
189
- }
190
-
191
61
/** @type {Visitors } */
192
62
const global_visitors = {
63
+ AssignmentExpression,
193
64
Identifier,
194
- AssignmentExpression ( node , context ) {
195
- return serialize_set_binding ( node , context , context . next ) ;
196
- } ,
197
- UpdateExpression ( node , context ) {
198
- const { state, next } = context ;
199
- const argument = node . argument ;
200
-
201
- if ( argument . type === 'Identifier' && state . scope . get ( argument . name ) ?. kind === 'store_sub' ) {
202
- return b . call (
203
- node . prefix ? '$.update_store_pre' : '$.update_store' ,
204
- b . assignment ( '??=' , b . id ( '$$store_subs' ) , b . object ( [ ] ) ) ,
205
- b . literal ( argument . name ) ,
206
- b . id ( argument . name . slice ( 1 ) ) ,
207
- node . operator === '--' && b . literal ( - 1 )
208
- ) ;
209
- }
210
-
211
- return next ( ) ;
212
- } ,
213
- CallExpression ( node , context ) {
214
- const rune = get_rune ( node , context . state . scope ) ;
215
-
216
- if ( rune === '$host' ) {
217
- return b . id ( 'undefined' ) ;
218
- }
219
-
220
- if ( rune === '$effect.tracking' ) {
221
- return b . literal ( false ) ;
222
- }
223
-
224
- if ( rune === '$effect.root' ) {
225
- // ignore $effect.root() calls, just return a noop which mimics the cleanup function
226
- return b . arrow ( [ ] , b . block ( [ ] ) ) ;
227
- }
228
-
229
- if ( rune === '$state.snapshot' ) {
230
- return b . call ( '$.snapshot' , /** @type {Expression } */ ( context . visit ( node . arguments [ 0 ] ) ) ) ;
231
- }
232
-
233
- if ( rune === '$state.is' ) {
234
- return b . call (
235
- 'Object.is' ,
236
- /** @type {Expression } */ ( context . visit ( node . arguments [ 0 ] ) ) ,
237
- /** @type {Expression } */ ( context . visit ( node . arguments [ 1 ] ) )
238
- ) ;
239
- }
240
-
241
- if ( rune === '$inspect' || rune === '$inspect().with' ) {
242
- return transform_inspect_rune ( node , context ) ;
243
- }
244
-
245
- context . next ( ) ;
246
- }
65
+ UpdateExpression,
66
+ CallExpression
247
67
} ;
248
68
249
69
/** @type {Visitors } */
0 commit comments