diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index db95432b858919afb42776ef74cdfd05635ce867..c8dc9e711ea97b3528a6d10212124380e88e84d2 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -127,6 +127,10 @@ export enum InnerComponentNames { FOR_EACH = 'ForEach', } +export enum InnerComponentAttributes { + COMMON_METHOD = 'CommonMethod', +} + export enum DecoratorNames { STATE = 'State', STORAGE_LINK = 'StorageLink', diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 7ff48d4bb7f4605166b1e5575956470c99dea0fc..07dbb0cc88764af1b76268463fc093fcc73ea870 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -476,7 +476,7 @@ export class factory { lambdaBodyInfo: BuilderLambdaStyleBodyInfo, declInfo: BuilderLambdaDeclInfo ): (arkts.AstNode | undefined)[] { - const { isFunctionCall, params, returnType, moduleName } = declInfo; + const { isFunctionCall, params, returnType, moduleName, isFromCommonMethod } = declInfo; const type: arkts.Identifier | undefined = builderLambdaType(leaf); const args: (arkts.AstNode | undefined)[] = []; const modifiedArgs: (arkts.AstNode | undefined)[] = []; @@ -507,7 +507,12 @@ export class factory { }, { isTrailingCall } ); - const lambdaBody = this.addOptionsArgsToLambdaBodyInStyleArg(lambdaBodyInfo, modifiedArgs, typeArguments); + const lambdaBody = this.addOptionsArgsToLambdaBodyInStyleArg( + lambdaBodyInfo, + modifiedArgs, + typeArguments, + isFromCommonMethod + ); const typeNode = !isFunctionCall && !!type ? UIFactory.createTypeReferenceFromString(type.name) : returnType; const styleArg = this.createStyleArgInBuilderLambda(lambdaBody, typeNode, moduleName); args.unshift(styleArg); @@ -520,14 +525,15 @@ export class factory { static addOptionsArgsToLambdaBodyInStyleArg( lambdaBodyInfo: BuilderLambdaStyleBodyInfo, args: (arkts.AstNode | undefined)[], - typeArguments: readonly arkts.TypeNode[] | undefined + typeArguments: readonly arkts.TypeNode[] | undefined, + shouldApplyAttribute: boolean = true ): arkts.CallExpression | arkts.Identifier | undefined { const { lambdaBody, initCallPtr } = lambdaBodyInfo; if (!lambdaBody) { return undefined; } if (!initCallPtr || arkts.isIdentifier(lambdaBody)) { - return this.addApplyAttributesFinishToLambdaBodyEnd(lambdaBody); + return this.addApplyAttributesFinishToLambdaBodyEnd(lambdaBody, shouldApplyAttribute); } const styleInternalsVisitor = new StyleInternalsVisitor(); const newLambdaBody = styleInternalsVisitor @@ -535,15 +541,19 @@ export class factory { .registerInitCallArgs(filterDefined(args)) .registerInitCallTypeArguments(typeArguments) .visitor(lambdaBody) as arkts.CallExpression | arkts.Identifier; - return this.addApplyAttributesFinishToLambdaBodyEnd(newLambdaBody); + return this.addApplyAttributesFinishToLambdaBodyEnd(newLambdaBody, shouldApplyAttribute); } /** * add `.applyAttributesFinish()` at the end of style argument body. */ static addApplyAttributesFinishToLambdaBodyEnd( - lambdaBody: arkts.CallExpression | arkts.Identifier + lambdaBody: arkts.CallExpression | arkts.Identifier, + shouldApplyAttribute: boolean = true ): arkts.CallExpression | arkts.Identifier { + if (!shouldApplyAttribute) { + return lambdaBody; + } return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( lambdaBody, @@ -747,8 +757,9 @@ export class factory { * transform `@ComponentBuilder` in declared methods. */ static transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.MethodDefinition { + const nameNode: arkts.Identifier | undefined = node.name; const func: arkts.ScriptFunction = node.scriptFunction; - const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); + const isFunctionCall: boolean = isBuilderLambdaFunctionCall(nameNode); if (isFunctionCall) { ComponentAttributeCache.getInstance().collect(node); } @@ -760,7 +771,7 @@ export class factory { node, [this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall)], removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), - replaceBuilderLambdaDeclMethodName(node.name.name) + replaceBuilderLambdaDeclMethodName(nameNode.name) ).setOverloads(newOverloads); arkts.NodeCache.getInstance().collect(newNode); return newNode; diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index 04ba680cafe41fc5caff25aa3470230c8827216b..f0e59026c5273a44f15870a5340c096372314f55 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -15,9 +15,15 @@ import * as arkts from '@koalaui/libarkts'; import { isAnnotation, matchPrefix } from '../../common/arkts-utils'; -import { BuilderLambdaNames, isCustomComponentAnnotation } from '../utils'; +import { BuilderLambdaNames, expectNameInTypeReference, isCustomComponentAnnotation } from '../utils'; import { DeclarationCollector } from '../../common/declaration-collector'; -import { ARKUI_IMPORT_PREFIX_NAMES, BindableDecl, Dollars, StructDecoratorNames } from '../../common/predefines'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + BindableDecl, + Dollars, + InnerComponentAttributes, + StructDecoratorNames, +} from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; import { collectTypeRecordFromParameter, @@ -37,6 +43,7 @@ export type BuilderLambdaDeclInfo = { returnType: arkts.TypeNode | undefined; moduleName: string; hasReceiver?: boolean; + isFromCommonMethod?: boolean; }; export type BuilderLambdaStyleBodyInfo = { @@ -368,29 +375,58 @@ export function findBuilderLambdaDeclInfo(decl: arkts.AstNode | undefined): Buil if (!moduleName) { return undefined; } - if (arkts.isMethodDefinition(decl)) { - const name = decl.name.name; - const params = decl.scriptFunction.params.map((p) => p.clone()); - const returnType = decl.scriptFunction.returnTypeAnnotation?.clone(); - const isFunctionCall = isBuilderLambdaFunctionCall(decl); - const hasReceiver = decl.scriptFunction.hasReceiver; - return { name, isFunctionCall, params, returnType, moduleName, hasReceiver }; + if (!arkts.isMethodDefinition(decl)) { + return undefined; } - - return undefined; + const func = decl.scriptFunction; + const nameNode = decl.name; + const originType = func.returnTypeAnnotation; + const params = func.params.map((p) => p.clone()); + const isFunctionCall = isBuilderLambdaFunctionCall(nameNode); + const hasReceiver = func.hasReceiver; + const isFromCommonMethod = isFunctionCall && findComponentAttributeFromCommonMethod(originType); + const returnType = originType?.clone(); + const name = nameNode.name; + return { name, isFunctionCall, params, returnType, moduleName, hasReceiver, isFromCommonMethod }; +} + +export function findComponentAttributeFromCommonMethod(attrType: arkts.TypeNode | undefined): boolean { + const nameNode = expectNameInTypeReference(attrType); + if (!nameNode) { + return false; + } + const decl = arkts.getDecl(nameNode); + return findCommonMethodInterfaceInExtends(decl); } -export function isBuilderLambdaFunctionCall(decl: arkts.AstNode | undefined): boolean { - if (!decl) { +export function findCommonMethodInterfaceInExtends(interfaceNode: arkts.AstNode | undefined): boolean { + if (!interfaceNode || !arkts.isTSInterfaceDeclaration(interfaceNode)) { return false; } - if (arkts.isMethodDefinition(decl)) { - return ( - decl.name.name !== BuilderLambdaNames.ORIGIN_METHOD_NAME && - decl.name.name !== BuilderLambdaNames.TRANSFORM_METHOD_NAME - ); + const nameNode = interfaceNode.id; + if (!nameNode) { + return false; } - return false; + const extendNodes = interfaceNode.extends; + if (extendNodes.length === 0) { + return nameNode.name === InnerComponentAttributes.COMMON_METHOD; + } + return extendNodes.some((node) => { + const name = expectNameInTypeReference(node.expr); + const decl = !!name ? arkts.getDecl(name) : undefined; + return findCommonMethodInterfaceInExtends(decl); + }); +} + +export function isBuilderLambdaFunctionCall(nameNode: arkts.Identifier | undefined): boolean { + if (!nameNode) { + return false; + } + const name = nameNode.name; + return ( + name !== BuilderLambdaNames.ORIGIN_METHOD_NAME && + name !== BuilderLambdaNames.TRANSFORM_METHOD_NAME + ); } export function callIsGoodForBuilderLambda(leaf: arkts.CallExpression): boolean { diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index d5382b035cd5ad224c28fc17bddeb571da819037..51a95a5cfd95d7a3c8d56f286aa8839ce5e644f2 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -387,3 +387,18 @@ export function getClassPropertyType(property: arkts.ClassProperty): arkts.TypeN } return undefined; } + +export function expectNameInTypeReference(node: arkts.TypeNode | undefined): arkts.Identifier | undefined { + if (!node || !arkts.isETSTypeReference(node)) { + return undefined; + } + const part = node.part; + if (!part || !arkts.isETSTypeReferencePart(part)) { + return undefined; + } + const nameNode = part.name; + if (!nameNode || !arkts.isIdentifier(nameNode)) { + return undefined; + } + return nameNode; +}