1- /** @import  { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Node, Identifier, PrivateIdentifier,  Statement } from 'estree' */ 
1+ /** @import  { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Statement } from 'estree' */ 
22/** @import  { Context } from '../types' */ 
33import  *  as  b  from  '../../../../utils/builders.js' ; 
44import  {  get_rune  }  from  '../../../scope.js' ; 
55import  {  should_proxy  }  from  '../utils.js' ; 
6- import  {  walk  }  from  'zimmerframe' ; 
76
87/** 
98 * @param  {ObjectExpression } node 
109 * @param  {Context } context 
1110 */ 
1211export  function  ObjectExpression ( node ,  context )  { 
1312	/** 
14- 	 * @typedef  {[string, NonNullable<ReturnType<typeof get_rune>>, '$.state' | '$.derived', Expression, boolean ] } ReactiveProperty 
13+ 	 * @typedef  {[string, NonNullable<ReturnType<typeof get_rune>>] } ReactiveProperty 
1514	 */ 
1615	let  has_runes  =  false ; 
17- 	/** 
18- 	 * @type  {Array<{rune: NonNullable<ReturnType<typeof get_rune>>, property: Property & {value: CallExpression}}> } 
19- 	 */ 
20- 	const  reactive_properties  =  [ ] ; 
2116	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 ; 
2222	for  ( let  property  of  node . properties )  { 
2323		if  ( property . type  !==  'Property' )  continue ; 
2424		const  rune  =  get_rune ( property . value ,  context . state . scope ) ; 
2525		if  ( rune  &&  valid_property_runes . includes ( rune ) )  { 
2626			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 ) ) ) ; 
3143		} 
3244	} 
3345	if  ( ! has_runes )  { 
3446		context . next ( ) ; 
3547		return ; 
3648	} 
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- 	} 
17149	/** @type  {(Property | SpreadElement)[] } */ 
17250	const  properties  =  [ ] ; 
17351	for  ( let  property  of  node . properties )  { 
@@ -176,17 +54,12 @@ export function ObjectExpression(node, context) {
17654			continue ; 
17755		} 
17856		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 ) ) ; 
18558			properties . push ( 
18659				b . prop ( 
18760					'get' , 
18861					/** @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 ) ) ) ] ) ) , 
19063					property . computed 
19164				) , 
19265				b . prop ( 
@@ -197,12 +70,7 @@ export function ObjectExpression(node, context) {
19770						[ b . id ( '$$value' ) ] , 
19871						b . block ( [ 
19972							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 ) 
20674							) 
20775						] ) 
20876					) , 
@@ -213,14 +81,6 @@ export function ObjectExpression(node, context) {
21381			properties . push ( /** @type  {Property } */  ( context . visit ( property ) ) ) ; 
21482		} 
21583	} 
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 ) ) ) ; 
22585	return  b . call ( b . arrow ( [ ] ,  b . block ( body ) ) ) ; 
22686} 
0 commit comments