@@ -546,11 +546,88 @@ const javascript_visitors = {
546
546
547
547
/** @type {import('./types').Visitors } */
548
548
const javascript_visitors_runes = {
549
+ ClassBody ( node , { state, visit, next } ) {
550
+ if ( ! state . analysis . runes ) {
551
+ next ( ) ;
552
+ }
553
+ /** @type {import('estree').PropertyDefinition[] } */
554
+ const deriveds = [ ] ;
555
+ /** @type {import('estree').MethodDefinition | null } */
556
+ let constructor = null ;
557
+ // Get the constructor
558
+ for ( const definition of node . body ) {
559
+ if ( definition . type === 'MethodDefinition' && definition . kind === 'constructor' ) {
560
+ constructor = /** @type {import('estree').MethodDefinition } */ ( visit ( definition ) ) ;
561
+ }
562
+ }
563
+ // Move $derived() runes to the end of the body if there is a constructor
564
+ if ( constructor !== null ) {
565
+ const body = [ ] ;
566
+ for ( const definition of node . body ) {
567
+ if (
568
+ definition . type === 'PropertyDefinition' &&
569
+ ( definition . key . type === 'Identifier' || definition . key . type === 'PrivateIdentifier' )
570
+ ) {
571
+ const is_private = definition . key . type === 'PrivateIdentifier' ;
572
+
573
+ if ( definition . value ?. type === 'CallExpression' ) {
574
+ const rune = get_rune ( definition . value , state . scope ) ;
575
+
576
+ if ( rune === '$derived' ) {
577
+ deriveds . push ( /** @type {import('estree').PropertyDefinition } */ ( visit ( definition ) ) ) ;
578
+ if ( is_private ) {
579
+ // Keep the private #name initializer if private, but remove initial value
580
+ body . push ( {
581
+ ...definition ,
582
+ value : null
583
+ } ) ;
584
+ }
585
+ continue ;
586
+ }
587
+ }
588
+ }
589
+ if ( definition . type !== 'MethodDefinition' || definition . kind !== 'constructor' ) {
590
+ body . push (
591
+ /** @type {import('estree').PropertyDefinition | import('estree').MethodDefinition | import('estree').StaticBlock } */ (
592
+ visit ( definition )
593
+ )
594
+ ) ;
595
+ }
596
+ }
597
+ if ( deriveds . length > 0 ) {
598
+ body . push ( {
599
+ ...constructor ,
600
+ value : {
601
+ ...constructor . value ,
602
+ body : b . block ( [
603
+ ...constructor . value . body . body ,
604
+ ...deriveds . map ( ( d ) => {
605
+ return b . stmt (
606
+ b . assignment (
607
+ '=' ,
608
+ b . member ( b . this , d . key ) ,
609
+ /** @type {import('estree').Expression } */ ( d . value )
610
+ )
611
+ ) ;
612
+ } )
613
+ ] )
614
+ }
615
+ } ) ;
616
+ } else {
617
+ body . push ( constructor ) ;
618
+ }
619
+ return {
620
+ ...node ,
621
+ body
622
+ } ;
623
+ }
624
+ next ( ) ;
625
+ } ,
549
626
PropertyDefinition ( node , { state, next, visit } ) {
550
627
if ( node . value != null && node . value . type === 'CallExpression' ) {
551
628
const rune = get_rune ( node . value , state . scope ) ;
552
629
553
- if ( rune === '$state' || rune === '$state.frozen' ) {
630
+ if ( rune === '$state' || rune === '$state.frozen' || rune === '$derived' ) {
554
631
return {
555
632
...node ,
556
633
value :
@@ -559,26 +636,6 @@ const javascript_visitors_runes = {
559
636
: /** @type {import('estree').Expression } */ ( visit ( node . value . arguments [ 0 ] ) )
560
637
} ;
561
638
}
562
- if ( rune === '$derived' ) {
563
- return {
564
- type : 'MethodDefinition' ,
565
- kind : 'get' ,
566
- key : node . key ,
567
- computed : false ,
568
- static : false ,
569
- value : b . function (
570
- null ,
571
- [ ] ,
572
- b . block ( [
573
- b . return (
574
- node . value . arguments . length === 0
575
- ? null
576
- : /** @type {import('estree').Expression } */ ( visit ( node . value . arguments [ 0 ] ) )
577
- )
578
- ] )
579
- )
580
- } ;
581
- }
582
639
if ( rune === '$derived.by' ) {
583
640
return {
584
641
...node ,
0 commit comments