@@ -7,7 +7,7 @@ import { Cache } from '../../Cache';
77import * as path from 'path' ;
88import { util } from '../../util' ;
99import type { ProvideFileEvent } from '../../interfaces' ;
10- import { isDottedGetExpression , isFieldStatement , isMethodStatement , isVariableExpression } from '../../astUtils/reflection' ;
10+ import { isDottedGetExpression , isFieldStatement , isMethodStatement , isVariableExpression , isLiteralExpression , isTemplateStringExpression } from '../../astUtils/reflection' ;
1111import { createFunctionStatement , createFunctionExpression , createDottedSetStatement , createVariableExpression } from '../../astUtils/creators' ;
1212import type { Statement } from '../../parser/AstNode' ;
1313import { TokenKind } from '../../lexer/TokenKind' ;
@@ -27,7 +27,7 @@ export class ComponentStatementProvider {
2727 const cache = new Cache < string , string > ( ) ;
2828 file . ast . walk ( createVisitor ( {
2929 ComponentStatement : ( node ) => {
30- //force the desetPath for this component to be within the `pkg:/components` folder
30+ //force the destPath for this component to be within the `pkg:/components` folder
3131 const destDir = cache . getOrAdd ( file . srcPath , ( ) => {
3232 return path . dirname ( file . destPath ) . replace ( / ^ ( .+ ?) (? = [ \/ \\ ] | $ ) / , ( match : string , firstDirName : string ) => {
3333 return 'components' ;
@@ -56,26 +56,64 @@ export class ComponentStatementProvider {
5656
5757 //declare interface field
5858 } else if ( isFieldStatement ( member ) && member . accessModifier ?. text . toLowerCase ( ) === 'public' ) {
59- return `<field id="${ member . name . text } " type="${ member . typeExpression . getName ( ) } " />` ;
59+ return `<field
60+ id="${ member . name . text } "
61+ type="${ member . typeExpression . getName ( ) } "
62+ alias="${ this . getAnnotationValue ( member . annotations ?. filter ( x => x . name . toLowerCase ( ) === 'alias' ) ) } "
63+ onChange="${ this . getAnnotationValue ( member . annotations ?. filter ( x => x . name . toLowerCase ( ) === 'onchange' ) ) } "
64+ alwaysNotify="${ this . getAnnotationValue ( member . annotations ?. filter ( x => x . name . toLowerCase ( ) === 'alwaysnotify' ) ) === 'true' } "
65+ />` ;
6066 } else {
6167 return '' ;
6268 }
6369 } ) . filter ( x => ! ! x ) ;
6470
71+ let componentChildren = '' ;
72+ const template = statement . annotations ?. find ( x => x . name . toLowerCase ( ) === 'template' ) ;
73+ if ( template ) {
74+ componentChildren = `<children>${ this . getAnnotationValue ( [ template ] ) } </children>` ;
75+ }
76+
6577 xmlFile . parse ( undent `
6678 <component name="${ name } " extends="${ statement . getParentName ( ParseMode . BrightScript ) ?? 'Group' } ">
6779 <script uri="${ util . sanitizePkgPath ( file . destPath ) } " />
6880 <script uri="${ util . sanitizePkgPath ( codebehindFile . destPath ) } " />
6981 ${ interfaceMembers . length > 0 ? '<interface>' : '' }
7082 ${ interfaceMembers . join ( '\n ' ) }
7183 ${ interfaceMembers . length > 0 ? '</interface>' : '' }
84+ ${ componentChildren }
7285 </component>
7386 ` ) ;
7487
75-
7688 this . event . files . push ( xmlFile ) ;
7789 }
7890
91+ private getAnnotationValue ( annotations : any ) {
92+ let response = [ ] ;
93+ annotations . forEach ( a => {
94+ let args = a ?. call ?. args [ 0 ] ;
95+ if ( isVariableExpression ( args ) || isDottedGetExpression ( args ) ) {
96+ response . push ( args . name . text ) ;
97+ } else if ( isLiteralExpression ( args ) ) {
98+ let values = args ?. token ?. text . replaceAll ( '\"' , '' ) . replaceAll ( ' ' , '' ) . split ( ',' ) ;
99+ response = response . concat ( values ) ;
100+ } else if ( isTemplateStringExpression ( args ) ) {
101+ let textOutput = '' ;
102+ args . quasis [ 0 ] ?. expressions ?. forEach ( ( a : { token : { text : string } } ) => {
103+ if ( ! a . token . text . includes ( 'component>' ) && ! a . token . text . includes ( 'children>' ) ) {
104+ textOutput += a . token . text ;
105+ }
106+ } ) ;
107+ response . push ( textOutput ) ;
108+ }
109+ } ) ;
110+
111+ response = response . filter ( ( item , index ) => {
112+ return response . indexOf ( item ) === index ;
113+ } ) ;
114+ return response . join ( ', ' ) ;
115+ }
116+
79117 private registerCodebehind ( name : string , statement : ComponentStatement , destDir : string ) {
80118 //create the codebehind file
81119 const file = this . event . fileFactory . BrsFile ( {
@@ -120,7 +158,7 @@ export class ComponentStatementProvider {
120158 initFunc . func . body . statements . unshift ( ...initStatements ) ;
121159 }
122160
123- //TODO these are hacks that we need until scope has been refactored to leverate the AST directly
161+ //TODO these are hacks that we need until scope has been refactored to leverage the AST directly
124162 file . parser . invalidateReferences ( ) ;
125163 // eslint-disable-next-line @typescript-eslint/dot-notation
126164 file [ 'findCallables' ] ( ) ;
0 commit comments