• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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