1/* 2 * Copyright (C) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as arkts from '@koalaui/libarkts'; 17import { BuilderLambdaNames } from '../utils'; 18import { annotation, backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; 19import { 20 BuilderLambdaDeclInfo, 21 builderLambdaFunctionName, 22 builderLambdaMethodDeclType, 23 builderLambdaTypeName, 24 callIsGoodForBuilderLambda, 25 findBuilderLambdaDecl, 26 findBuilderLambdaDeclInfo, 27 isBuilderLambda, 28 isBuilderLambdaFunctionCall, 29 isSafeType, 30 replaceBuilderLambdaDeclMethodName, 31 BindableDecl, 32 getDecalTypeFromValue, 33 hasBindableProperty, 34 isDoubleDollarCall, 35 InstanceCallInfo, 36 isStyleChainedCall, 37 isStyleWithReceiverCall, 38} from './utils'; 39import { DecoratorNames } from '../property-translators/utils'; 40import { factory as PropertyFactory } from '../property-translators/factory'; 41import { ProjectConfig } from '../../common/plugin-context'; 42 43export class factory { 44 /* 45 * update @ComponentBuilder decorated method. 46 */ 47 static updateBuilderLambdaMethodDecl( 48 node: arkts.MethodDefinition, 49 styleArg: arkts.ETSParameterExpression, 50 newAnno: arkts.AnnotationUsage[], 51 newName: string | undefined 52 ): arkts.MethodDefinition { 53 const func: arkts.ScriptFunction = node.scriptFunction; 54 const updateFunc = arkts.factory 55 .updateScriptFunction( 56 func, 57 func.body, 58 arkts.FunctionSignature.createFunctionSignature( 59 func.typeParams, 60 [styleArg, ...func.params], 61 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 62 false 63 ), 64 func.flags, 65 func.modifiers 66 ) 67 .setAnnotations(newAnno); 68 69 return arkts.factory.updateMethodDefinition( 70 node, 71 node.kind, 72 arkts.factory.updateIdentifier(node.name, newName ?? node.name.name), 73 updateFunc, 74 node.modifiers, 75 false // TODO: how do I get it? 76 ); 77 } 78 79 /* 80 * transform arguments in style node. 81 */ 82 static getTransformedStyle(call: arkts.CallExpression): arkts.Expression[] { 83 const decl = arkts.getDecl(call.expression); 84 if (!decl || !arkts.isMethodDefinition(decl)) { 85 return [...call.arguments]; 86 } 87 const type: arkts.AstNode | undefined = arkts.isEtsParameterExpression(decl.scriptFunction.params[0]) 88 ? decl.scriptFunction.params[0].type 89 : undefined; 90 if ( 91 type && 92 arkts.isTypeNode(type) && 93 hasBindableProperty(type, BindableDecl.BINDABLE) && 94 isDoubleDollarCall(call.arguments[0]) 95 ) { 96 const bindableArg: arkts.Expression = (call.arguments[0] as arkts.CallExpression).arguments[0]; 97 return [factory.updateBindableStyleArguments(bindableArg), ...call.arguments.slice(1)]; 98 } 99 return [...call.arguments]; 100 } 101 102 /* 103 * transform bundable arguments in style node, e.g. `Radio().checked($$(this.checked))` => `Radio().checked({value: xxx, onChange: xxx})`. 104 */ 105 static updateBindableStyleArguments(bindableArg: arkts.Expression): arkts.Expression { 106 const valueType: arkts.TypeNode = getDecalTypeFromValue(bindableArg); 107 const objExp: arkts.ObjectExpression = arkts.factory.createObjectExpression( 108 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 109 [factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType)], 110 false 111 ); 112 return arkts.factory.createTSAsExpression(objExp, factory.createBindableType(valueType), false); 113 } 114 115 /* 116 * create style instance call, e.g. `instance.margin(10)`. 117 */ 118 static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo, projectConfig: ProjectConfig | undefined): arkts.CallExpression { 119 if (!callInfo.isReceiver) { 120 const newArgs: arkts.Expression[] = factory.getTransformedStyle(callInfo.call); 121 return arkts.factory.createCallExpression( 122 arkts.factory.createMemberExpression( 123 lambdaBody, 124 callInfo.call.expression, 125 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 126 false, 127 false 128 ), 129 undefined, 130 newArgs.map((arg) => { 131 if (arkts.isArrowFunctionExpression(arg)) { 132 return this.processArgArrowFunction(arg, projectConfig); 133 } 134 return arg; 135 }) 136 ); 137 } else { 138 return arkts.factory.createCallExpression(callInfo.call.expression, callInfo.call.typeArguments, [ 139 lambdaBody, 140 ...callInfo.call.arguments.slice(1), 141 ]); 142 } 143 } 144 145 /* 146 * update parameter passing, e.g. `<val.object>: __backing_<originName>`. 147 */ 148 static updateBackingMember(val: arkts.MemberExpression, originName: string): arkts.MemberExpression { 149 return arkts.factory.updateMemberExpression( 150 val, 151 val.object, 152 arkts.factory.createIdentifier(backingField(originName)), 153 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 154 false, 155 false 156 ); 157 } 158 159 /* 160 * create style arguments in builder lambda. 161 */ 162 static createStyleArgInBuilderLambda( 163 lambdaBody: arkts.Expression | undefined, 164 typeNode: arkts.TypeNode | undefined 165 ): arkts.UndefinedLiteral | arkts.ArrowFunctionExpression { 166 if (!lambdaBody) { 167 return arkts.factory.createUndefinedLiteral(); 168 } 169 const safeType: arkts.TypeNode | undefined = isSafeType(typeNode) ? typeNode : undefined; 170 const styleLambdaParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( 171 arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME, safeType), 172 undefined 173 ); 174 175 const body: arkts.BlockStatement = arkts.factory.createBlock([ 176 arkts.factory.createExpressionStatement(lambdaBody), 177 arkts.factory.createReturnStatement(), 178 ]); 179 180 const func = arkts.factory.createScriptFunction( 181 body, 182 arkts.FunctionSignature.createFunctionSignature( 183 undefined, 184 [styleLambdaParam], 185 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 186 false 187 ), 188 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 189 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC 190 ); 191 192 return arkts.factory.createArrowFunction(func); 193 } 194 195 /* 196 * create style arguments in builder lambda declaration. 197 */ 198 static createStyleArgInBuilderLambdaDecl( 199 typeNode: arkts.TypeNode | undefined, 200 isFunctionCall: boolean 201 ): arkts.ETSParameterExpression { 202 const styleLambdaParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( 203 arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME, typeNode), 204 undefined 205 ); 206 const funcType = arkts.factory.createFunctionType( 207 arkts.FunctionSignature.createFunctionSignature( 208 undefined, 209 [styleLambdaParam], 210 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 211 false 212 ), 213 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW 214 ); 215 216 let parameter: arkts.ETSParameterExpression; 217 if (isFunctionCall) { 218 parameter = arkts.factory 219 .createParameterDeclaration( 220 arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, funcType), 221 undefined 222 ) 223 .setOptional(true); 224 } else { 225 const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); 226 parameter = arkts.factory.createParameterDeclaration( 227 arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), 228 undefined 229 ); 230 } 231 parameter.annotations = [annotation('memo')]; 232 return parameter; 233 } 234 235 /** 236 * If a builder lambda's argument is an arrow function, 237 * then transform any builder lambda in the function body. 238 */ 239 static processArgArrowFunction(arg: arkts.ArrowFunctionExpression, projectConfig: ProjectConfig | undefined): arkts.ArrowFunctionExpression { 240 const func: arkts.ScriptFunction = arg.scriptFunction; 241 const updateFunc = arkts.factory.updateScriptFunction( 242 func, 243 !!func.body && arkts.isBlockStatement(func.body) 244 ? arkts.factory.updateBlock( 245 func.body, 246 func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) 247 ) 248 : undefined, 249 arkts.FunctionSignature.createFunctionSignature( 250 func.typeParams, 251 func.params, 252 func.returnTypeAnnotation, 253 false 254 ), 255 func.flags, 256 func.modifiers 257 ); 258 return arkts.factory.updateArrowFunction(arg, updateFunc); 259 } 260 261 /** 262 * transform options argument in a builder lambda call. 263 */ 264 static processOptionsArg<T extends arkts.TSAsExpression | arkts.ObjectExpression>(arg: T, typeName: string): T { 265 let expr: arkts.ObjectExpression | undefined; 266 if (arkts.isTSAsExpression(arg) && !!arg.expr && arkts.isObjectExpression(arg.expr)) { 267 expr = arg.expr; 268 } else if (arkts.isObjectExpression(arg)) { 269 expr = arg; 270 } 271 272 if (!expr) { 273 return arg; 274 } 275 276 const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName!); 277 const properties = expr.properties as arkts.Property[]; 278 properties.forEach((prop, index) => { 279 this.updateParameterPassingInLinkedProperties(prop, index, currentStructInfo, properties); 280 }); 281 const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( 282 expr, 283 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 284 properties, 285 false 286 ); 287 if (arkts.isTSAsExpression(arg)) { 288 return arkts.TSAsExpression.updateTSAsExpression(arg, updatedExpr, arg.typeAnnotation, arg.isConst) as T; 289 } 290 return updatedExpr as T; 291 } 292 293 /** 294 * update any `@Link` parameter passing to the custom-component child. 295 */ 296 static updateParameterPassingInLinkedProperties( 297 prop: arkts.Property, 298 index: number, 299 currentStructInfo: arkts.StructInfo, 300 properties: arkts.Property[] 301 ): void { 302 const decl = prop.key ? arkts.getDecl(prop.key) : undefined; 303 if (decl && arkts.isMethodDefinition(decl)) { 304 const type: arkts.TypeNode | undefined = decl.scriptFunction.returnTypeAnnotation; 305 if ( 306 type && 307 hasBindableProperty(type, BindableDecl.BINDABLE) && 308 arkts.isProperty(prop) && 309 prop.value && 310 isDoubleDollarCall(prop.value) 311 ) { 312 properties[index] = factory.updateBindableProperty(prop); 313 } 314 } 315 if ( 316 !!prop.key && 317 !!prop.value && 318 arkts.isIdentifier(prop.key) && 319 arkts.isMemberExpression(prop.value) && 320 arkts.isThisExpression(prop.value.object) && 321 arkts.isIdentifier(prop.value.property) 322 ) { 323 const structVariableMetadata = currentStructInfo.metadata[prop.key.name]; 324 if ( 325 structVariableMetadata && 326 structVariableMetadata.properties.length && 327 structVariableMetadata.properties.includes(DecoratorNames.LINK) 328 ) { 329 properties[index] = arkts.Property.updateProperty( 330 prop, 331 arkts.factory.createIdentifier(backingField(prop.key.name)), 332 this.updateBackingMember(prop.value, prop.value.property.name) 333 ); 334 } 335 } 336 } 337 338 /** 339 * create or update arguments in a builder lambda call. 340 * If the corresponding argument is not provided, fill-in an `undefined` to it. 341 */ 342 static createOrUpdateArgInBuilderLambda( 343 arg: arkts.Expression | undefined, 344 projectConfig: ProjectConfig | undefined, 345 typeName?: string, 346 ): arkts.AstNode { 347 if (!arg) { 348 return arkts.factory.createUndefinedLiteral(); 349 } 350 if (arkts.isArrowFunctionExpression(arg)) { 351 return this.processArgArrowFunction(arg, projectConfig); 352 } 353 // this is too optimistic to check if this is an options argument... 354 if (arkts.isTSAsExpression(arg) || arkts.isObjectExpression(arg)) { 355 return this.processOptionsArg(arg, typeName!); 356 } 357 return arg; 358 } 359 360 /** 361 * transform arguments in a builder lambda call. 362 */ 363 static generateArgsInBuilderLambda( 364 leaf: arkts.CallExpression, 365 lambdaBody: arkts.Identifier | arkts.CallExpression, 366 declInfo: BuilderLambdaDeclInfo, 367 projectConfig: ProjectConfig | undefined 368 ): (arkts.AstNode | undefined)[] { 369 const { params, returnType } = declInfo; 370 const typeName: string | undefined = builderLambdaTypeName(leaf); 371 const args: (arkts.AstNode | undefined)[] = [this.createStyleArgInBuilderLambda(lambdaBody, returnType)]; 372 let index = 0; 373 while (index < params.length) { 374 args.push(this.createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), projectConfig, typeName)); 375 index++; 376 } 377 const isReusable: boolean = typeName 378 ? arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName).isReusable : false; 379 if (isReusable) { 380 args.splice(-1, 1, arkts.factory.createStringLiteral(typeName!)); 381 } 382 else if (typeName === 'XComponent') { 383 let packageInfo: string = ''; 384 if (projectConfig?.bundleName && projectConfig?.moduleName) { 385 packageInfo = projectConfig?.bundleName + '/' + projectConfig?.moduleName; 386 } 387 args.splice(args.length - 1, 0, arkts.factory.createStringLiteral(packageInfo)); 388 } 389 return args; 390 } 391 392 /** 393 * update if-else in trailing lambda contents in a builder lambda call. 394 */ 395 static updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode, projectConfig: ProjectConfig | undefined): arkts.AstNode { 396 if (arkts.isIfStatement(statement)) { 397 const alternate = !!statement.alternate 398 ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, projectConfig) 399 : statement.alternate; 400 const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent, projectConfig); 401 return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); 402 } 403 if (arkts.isBlockStatement(statement)) { 404 return arkts.factory.updateBlock( 405 statement, 406 statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) 407 ); 408 } 409 return statement; 410 } 411 412 /** 413 * update trailing lambda contents in a builder lambda call. 414 */ 415 static updateContentBodyInBuilderLambda(statement: arkts.Statement, projectConfig: ProjectConfig | undefined): arkts.Statement { 416 if ( 417 arkts.isExpressionStatement(statement) && 418 arkts.isCallExpression(statement.expression) && 419 isBuilderLambda(statement.expression) 420 ) { 421 return arkts.factory.updateExpressionStatement( 422 statement, 423 this.transformBuilderLambda(statement.expression, projectConfig) 424 ); 425 } 426 if (arkts.isIfStatement(statement)) { 427 return this.updateIfElseContentBodyInBuilderLambda(statement, projectConfig); 428 } 429 430 return statement; 431 } 432 433 /** 434 * replace function call's name to the corresponding transformed name. 435 */ 436 static builderLambdaReplace(leaf: arkts.CallExpression): arkts.Identifier | arkts.MemberExpression | undefined { 437 if (!callIsGoodForBuilderLambda(leaf)) { 438 return undefined; 439 } 440 const node = leaf.expression; 441 const funcName = builderLambdaFunctionName(leaf); 442 if (!funcName) { 443 return undefined; 444 } 445 if (arkts.isIdentifier(node)) { 446 return arkts.factory.createIdentifier(funcName); 447 } 448 if (arkts.isMemberExpression(node)) { 449 return arkts.factory.createMemberExpression( 450 node.object, 451 arkts.factory.createIdentifier(funcName), 452 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 453 node.computed, 454 node.optional 455 ); 456 } 457 return undefined; 458 } 459 460 /** 461 * transform `@ComponentBuilder` in declared methods. 462 */ 463 static transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.MethodDefinition { 464 const func: arkts.ScriptFunction = node.scriptFunction; 465 const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); 466 const typeNode: arkts.TypeNode | undefined = builderLambdaMethodDeclType(node); 467 const styleArg: arkts.ETSParameterExpression = this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall); 468 const newOverloads: arkts.MethodDefinition[] = node.overloads.map((method) => 469 factory.transformBuilderLambdaMethodDecl(method) 470 ); 471 472 return this.updateBuilderLambdaMethodDecl( 473 node, 474 styleArg, 475 removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), 476 replaceBuilderLambdaDeclMethodName(node.name.name) 477 ).setOverloads(newOverloads); 478 } 479 480 /** 481 * transform `.animation(...)` to `.animationStart(...) and .animationStop(...)` 482 */ 483 static updateAnimation(instanceCalls: InstanceCallInfo[]): void { 484 let lastAniIdx = 0; 485 let curIdx = 0; 486 487 while (curIdx < instanceCalls.length) { 488 if (instanceCalls[curIdx].isReceiver) { 489 curIdx++; 490 continue; 491 } 492 const property: arkts.Identifier = instanceCalls[curIdx].call.expression as arkts.Identifier; 493 if (property.name === BuilderLambdaNames.ANIMATION_NAME) { 494 const aniStart: arkts.CallExpression = arkts.factory.createCallExpression( 495 arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_START), 496 undefined, 497 instanceCalls[curIdx].call.arguments 498 ); 499 const aniStop: arkts.CallExpression = arkts.factory.createCallExpression( 500 arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_STOP), 501 undefined, 502 instanceCalls[curIdx].call.arguments.map((arg) => arg.clone()) 503 ); 504 instanceCalls.splice(lastAniIdx, 0, { isReceiver: false, call: aniStart }); 505 instanceCalls[curIdx + 1] = { isReceiver: false, call: aniStop }; 506 curIdx += 2; 507 lastAniIdx = curIdx; 508 } else { 509 curIdx++; 510 } 511 } 512 } 513 514 /** 515 * transform `@ComponentBuilder` in non-declared calls. 516 */ 517 static transformBuilderLambda(node: arkts.CallExpression, projectConfig: ProjectConfig | undefined): arkts.AstNode { 518 let instanceCalls: InstanceCallInfo[] = []; 519 let leaf: arkts.CallExpression = node; 520 521 while (isStyleChainedCall(leaf) || isStyleWithReceiverCall(leaf)) { 522 if (isStyleChainedCall(leaf)) { 523 instanceCalls.push({ 524 isReceiver: false, 525 call: arkts.factory.createCallExpression( 526 (leaf.expression as arkts.MemberExpression).property, 527 leaf.typeArguments, 528 leaf.arguments 529 ), 530 }); 531 leaf = (leaf.expression as arkts.MemberExpression).object as arkts.CallExpression; 532 } 533 534 if (isStyleWithReceiverCall(leaf)) { 535 instanceCalls.push({ 536 isReceiver: true, 537 call: arkts.factory.createCallExpression(leaf.expression, leaf.typeArguments, leaf.arguments), 538 }); 539 leaf = leaf.arguments[0] as arkts.CallExpression; 540 } 541 } 542 543 const decl: arkts.AstNode | undefined = findBuilderLambdaDecl(leaf); 544 if (!decl) { 545 return node; 546 } 547 548 const replace: arkts.Identifier | arkts.MemberExpression | undefined = this.builderLambdaReplace(leaf); 549 const declInfo: BuilderLambdaDeclInfo | undefined = findBuilderLambdaDeclInfo(decl); 550 if (!replace || !declInfo) { 551 return node; 552 } 553 554 let lambdaBody: arkts.Identifier | arkts.CallExpression | undefined; 555 if (instanceCalls.length > 0) { 556 instanceCalls = instanceCalls.reverse(); 557 this.updateAnimation(instanceCalls); 558 lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); 559 instanceCalls.forEach((callInfo) => { 560 lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo, projectConfig); 561 }); 562 } 563 564 const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo, projectConfig); 565 return arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); 566 } 567 568 /* 569 * update bindableProperty, e.g. `text: $$(this.text)` => `text: { value: xxx , onChange: xxx }`. 570 */ 571 static updateBindableProperty(prop: arkts.Property, type?: arkts.TypeNode): arkts.Property { 572 let res: arkts.Property[] = []; 573 let valueType: arkts.TypeNode; 574 if ( 575 prop.value && 576 arkts.isCallExpression(prop.value) && 577 prop.value.arguments && 578 prop.value.arguments.length === 1 579 ) { 580 let bindableArg = prop.value.arguments[0]; 581 valueType = getDecalTypeFromValue(bindableArg); 582 res.push( 583 factory.generateValueProperty(bindableArg), 584 factory.generateOnChangeArrowFunc(bindableArg, valueType) 585 ); 586 } else { 587 return prop; 588 } 589 const asObjProp: arkts.TSAsExpression = arkts.factory.createTSAsExpression( 590 arkts.ObjectExpression.createObjectExpression( 591 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 592 res, 593 false 594 ), 595 factory.createBindableType(valueType), 596 false 597 ); 598 return arkts.factory.updateProperty(prop, prop.key, asObjProp); 599 } 600 601 /* 602 * generate `value: <bindableArg>` in object. 603 */ 604 static generateValueProperty(bindableArg: arkts.Expression): arkts.Property { 605 return arkts.factory.createProperty(arkts.factory.createIdentifier('value'), bindableArg.clone()); 606 } 607 608 /* 609 * generate `onChange: (value) => <bindableArg> = value` in object. 610 */ 611 static generateOnChangeArrowFunc(bindableArg: arkts.Expression, valueType: arkts.TypeNode): arkts.Property { 612 return arkts.factory.createProperty( 613 arkts.factory.createIdentifier('onChange'), 614 PropertyFactory.createArrowFunctionWithParamsAndBody( 615 undefined, 616 [ 617 arkts.factory.createParameterDeclaration( 618 arkts.factory.createIdentifier('value', valueType.clone()), 619 undefined 620 ), 621 ], 622 undefined, 623 false, 624 [ 625 arkts.factory.createExpressionStatement( 626 arkts.factory.createAssignmentExpression( 627 bindableArg.clone(), 628 arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, 629 arkts.factory.createIdentifier('value') 630 ) 631 ), 632 ] 633 ) 634 ); 635 } 636 637 /* 638 * generate `Bindable<valueType>`. 639 */ 640 static createBindableType(valueType: arkts.TypeNode): arkts.ETSTypeReference { 641 return arkts.factory.createTypeReference( 642 arkts.factory.createTypeReferencePart( 643 arkts.factory.createIdentifier(BindableDecl.BINDABLE), 644 arkts.factory.createTSTypeParameterInstantiation([valueType.clone()]) 645 ) 646 ); 647 } 648} 649