@ -1,10 +1,11 @@
/** @import { CallExpression, ClassBody, ClassDeclaration, ClassExpression, MethodDefinition, PropertyDefinition, StaticBlock } from 'estree' */
/** @import { CallExpression, ClassBody, ClassDeclaration, ClassExpression, Expression, MethodDefinition, PropertyDefinition, StaticBlock, VariableDeclaration } from 'estree' */
/** @import { StateField } from '#compiler' */
/** @import { Context } from '../types' */
import * as b from '#compiler/builders' ;
import { dev } from '../../../../state.js' ;
import { get _parent } from '../../../../utils/ast.js' ;
import { get _name } from '../../../nodes.js' ;
import { is _constant } from '../../../scope.js' ;
/ * *
* @ param { ClassBody } node
@ -23,33 +24,94 @@ export function ClassBody(node, context) {
const body = [ ] ;
const child _state = { ... context . state , state _fields } ;
const computed _field _declarations = /** @type {VariableDeclaration[]} */ (
context . state . computed _field _declarations
) ;
// insert backing fields for stuff declared in the constructor
for ( const [ name , field ] of state _fields ) {
if ( name [ 0 ] === '#' ) {
if (
( typeof name === 'string' && name [ 0 ] === '#' ) ||
field . node . type !== 'AssignmentExpression'
) {
continue ;
}
// insert backing fields for stuff declared in the constructor
if ( field . node . type === 'AssignmentExpression' ) {
const member = b . member ( b . this , field . key ) ;
const should _proxy = field . type === '$state' && true ; // TODO
const key = b . key ( name ) ;
const member = b . member ( b . this , field . key ) ;
const should _proxy = field . type === '$state' && true ; // TODO
if ( typeof name === 'number' && field . computed _key ) {
const computed _key = /** @type {Expression} */ ( context . visit ( field . computed _key ) ) ;
const evaluation = context . state . scope . evaluate ( computed _key ) ;
if ( evaluation . is _known ) {
body . push (
b . prop _def ( field . key , null ) ,
b . method (
'get' ,
b . literal ( evaluation . value ) ,
[ ] ,
[ b . return ( b . call ( '$.get' , member ) ) ] ,
true
) ,
b . method (
'set' ,
b . literal ( evaluation . value ) ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
continue ;
}
if ( is _constant ( computed _key , context . state . scope ) ) {
body . push (
b . prop _def ( field . key , null ) ,
b . method ( 'get' , computed _key , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] , true ) ,
b . method (
'set' ,
computed _key ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
continue ;
}
const key = context . state . scope . generate ( 'key' ) ;
computed _field _declarations . push ( b . let ( key ) ) ;
body . push (
b . prop _def ( field . key , null ) ,
b . method ( 'get' , key , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] ) ,
b . method (
'get' ,
b . assignment ( '=' , b . id ( key ) , computed _key ) ,
[ ] ,
[ b . return ( b . call ( '$.get' , member ) ) ] ,
true
) ,
b . method (
'set' ,
key ,
b. id ( key) ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ]
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
continue ;
}
const key = b . key ( /** @type {string} */ ( name ) ) ;
body . push (
b . prop _def ( field . key , null ) ,
b . method ( 'get' , key , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] ) ,
b . method (
'set' ,
key ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ]
)
) ;
}
const declaration = /** @type {ClassDeclaration | ClassExpression} */ (
@ -65,15 +127,20 @@ export function ClassBody(node, context) {
continue ;
}
const name = get _name ( definition . key ) ;
const field = name && /** @type {StateField} */ ( state _fields . get ( name ) ) ;
const name = definition . computed
? [ ... state _fields . entries ( ) ] . find ( ( [ , field ] ) => field . node === definition ) ? . [ 0 ] ? ? null
: get _name ( definition . key ) ;
const field = name !== null && /** @type {StateField} */ ( state _fields . get ( name ) ) ;
if ( ! field ) {
body . push ( /** @type {PropertyDefinition} */ ( context . visit ( definition , child _state ) ) ) ;
continue ;
}
const member = b . member ( b . this , field . key ) ;
const should _proxy = field . type === '$state' && true ; // TODO
if ( name [ 0 ] === '#' ) {
if ( typeof name === 'string' && name [ 0 ] === '#' ) {
let value = definition . value
? /** @type {CallExpression} */ ( context . visit ( definition . value , child _state ) )
: undefined ;
@ -83,14 +150,12 @@ export function ClassBody(node, context) {
}
body . push ( b . prop _def ( definition . key , value ) ) ;
} else if ( field . node === definition ) {
} else if ( field . node === definition && typeof name === 'string' ) {
let call = /** @type {CallExpression} */ ( context . visit ( field . value , child _state ) ) ;
if ( dev ) {
call = b . call ( '$.tag' , call , b . literal ( ` ${ declaration . id ? . name ? ? '[class]' } . ${ name } ` ) ) ;
}
const member = b . member ( b . this , field . key ) ;
const should _proxy = field . type === '$state' && true ; // TODO
body . push (
b . prop _def ( field . key , call ) ,
@ -104,6 +169,71 @@ export function ClassBody(node, context) {
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ]
)
) ;
} else if ( field . computed _key ) {
let call = /** @type {CallExpression} */ ( context . visit ( field . value , child _state ) ) ;
if ( dev ) {
call = b . call (
'$.tag' ,
call ,
b . literal ( ` ${ declaration . id ? . name ? ? '[class]' } [computed key] ` )
) ;
}
const computed _key = /** @type {Expression} */ ( context . visit ( field . computed _key ) ) ;
const evaluation = context . state . scope . evaluate ( computed _key ) ;
if ( evaluation . is _known ) {
body . push (
b . prop _def ( field . key , call ) ,
b . method (
'get' ,
b . literal ( evaluation . value ) ,
[ ] ,
[ b . return ( b . call ( '$.get' , member ) ) ] ,
true
) ,
b . method (
'set' ,
b . literal ( evaluation . value ) ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
continue ;
}
if ( is _constant ( computed _key , context . state . scope ) ) {
body . push (
b . prop _def ( field . key , call ) ,
b . method ( 'get' , computed _key , [ ] , [ b . return ( b . call ( '$.get' , member ) ) ] , true ) ,
b . method (
'set' ,
computed _key ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
continue ;
}
const key = context . state . scope . generate ( 'key' ) ;
computed _field _declarations . push ( b . let ( key ) ) ;
body . push (
b . prop _def ( field . key , call ) ,
b . method (
'get' ,
b . assignment ( '=' , b . id ( key ) , computed _key ) ,
[ ] ,
[ b . return ( b . call ( '$.get' , member ) ) ] ,
true
) ,
b . method (
'set' ,
b . id ( key ) ,
[ b . id ( 'value' ) ] ,
[ b . stmt ( b . call ( '$.set' , member , b . id ( 'value' ) , should _proxy && b . true ) ) ] ,
true
)
) ;
}
}