@@ -546,11 +546,88 @@ const javascript_visitors = {
546546
547547/** @type {import('./types').Visitors } */
548548const 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+ } ,
549626 PropertyDefinition ( node , { state, next, visit } ) {
550627 if ( node . value != null && node . value . type === 'CallExpression' ) {
551628 const rune = get_rune ( node . value , state . scope ) ;
552629
553- if ( rune === '$state' || rune === '$state.frozen' ) {
630+ if ( rune === '$state' || rune === '$state.frozen' || rune === '$derived' ) {
554631 return {
555632 ...node ,
556633 value :
@@ -559,26 +636,6 @@ const javascript_visitors_runes = {
559636 : /** @type {import('estree').Expression } */ ( visit ( node . value . arguments [ 0 ] ) )
560637 } ;
561638 }
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- }
582639 if ( rune === '$derived.by' ) {
583640 return {
584641 ...node ,
0 commit comments