1/* 2 * Copyright (c) 2022-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 { AbstractVisitor } from '../../common/abstract-visitor'; 18import { annotation, collect, filterDefined } from '../../common/arkts-utils'; 19import { ProjectConfig } from '../../common/plugin-context'; 20import { classifyProperty, PropertyTranslator } from '../property-translators'; 21import { 22 addMemoAnnotation, 23 CustomComponentNames, 24 getCustomComponentOptionsName, 25 getTypeNameFromTypeParameter, 26 getTypeParamsFromClassDecl, 27} from '../utils'; 28import { isCustomComponentClass, isKnownMethodDefinition, isEtsGlobalClass, isReourceNode, CustomComponentScopeInfo, findCanAddMemoFromArrowFunction } from './utils'; 29import { factory as uiFactory } from '../ui-factory'; 30import { factory } from './factory'; 31import { isEntryWrapperClass } from '../entry-translators/utils'; 32import { factory as entryFactory } from '../entry-translators/factory'; 33import { DecoratorNames, hasDecorator } from '../property-translators/utils'; 34import { ScopeInfoCollection } from './utils'; 35 36function tranformPropertyMembers( 37 className: string, 38 propertyTranslators: PropertyTranslator[], 39 optionsTypeName: string, 40 isDecl?: boolean, 41 scope?: CustomComponentScopeInfo 42): arkts.AstNode[] { 43 const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); 44 const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); 45 const collections = []; 46 if (!scope?.hasInitializeStruct) { 47 collections.push(factory.createInitializeStruct(currentStructInfo, optionsTypeName, isDecl)); 48 } 49 if (!scope?.hasUpdateStruct) { 50 collections.push(factory.createUpdateStruct(currentStructInfo, optionsTypeName, isDecl)); 51 } 52 if (currentStructInfo.isReusable) { 53 collections.push(factory.toRecord(optionsTypeName, currentStructInfo.toRecordBody)); 54 } 55 return collect(...collections, ...propertyMembers); 56} 57 58function transformEtsGlobalClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { 59 if (!node.definition) { 60 return node; 61 } 62 node.definition.body.map((member: arkts.AstNode) => { 63 if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { 64 member.scriptFunction.setAnnotations([annotation('memo')]); 65 } 66 return member; 67 }); 68 return node; 69} 70 71function transformOtherMembersInClass( 72 member: arkts.AstNode, 73 classTypeName: string | undefined, 74 classOptionsName: string | undefined, 75 className: string, 76 isDecl?: boolean 77): arkts.AstNode { 78 if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { 79 member.scriptFunction.setAnnotations([annotation('memo')]); 80 return member; 81 } 82 if ( 83 arkts.isMethodDefinition(member) && 84 isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && 85 !isDecl 86 ) { 87 return uiFactory.createConstructorMethod(member); 88 } 89 if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { 90 return factory.transformBuildMethodWithOriginBuild( 91 member, 92 classTypeName ?? className, 93 classOptionsName ?? getCustomComponentOptionsName(className), 94 isDecl 95 ); 96 } 97 return member; 98} 99 100function tranformClassMembers( 101 node: arkts.ClassDeclaration, 102 isDecl?: boolean, 103 scope?: CustomComponentScopeInfo 104): arkts.ClassDeclaration { 105 if (!node.definition) { 106 return node; 107 } 108 109 let classTypeName: string | undefined; 110 let classOptionsName: string | undefined; 111 if (isDecl) { 112 const [classType, classOptions] = getTypeParamsFromClassDecl(node); 113 classTypeName = getTypeNameFromTypeParameter(classType); 114 classOptionsName = getTypeNameFromTypeParameter(classOptions); 115 } 116 const definition: arkts.ClassDefinition = node.definition; 117 const className: string | undefined = node.definition.ident?.name; 118 if (!className) { 119 throw new Error('Non Empty className expected for Component'); 120 } 121 122 const propertyTranslators: PropertyTranslator[] = filterDefined( 123 definition.body.map((it) => classifyProperty(it, className)) 124 ); 125 const translatedMembers: arkts.AstNode[] = tranformPropertyMembers( 126 className, 127 propertyTranslators, 128 classOptionsName ?? getCustomComponentOptionsName(className), 129 isDecl, 130 scope 131 ); 132 const updateMembers: arkts.AstNode[] = definition.body 133 .filter((member) => !arkts.isClassProperty(member)) 134 .map((member: arkts.AstNode) => 135 transformOtherMembersInClass(member, classTypeName, classOptionsName, className, isDecl) 136 ); 137 138 const updateClassDef: arkts.ClassDefinition = factory.updateCustomComponentClass(definition, [ 139 ...translatedMembers, 140 ...updateMembers, 141 ]); 142 return arkts.factory.updateClassDeclaration(node, updateClassDef); 143} 144 145function transformResource( 146 resourceNode: arkts.CallExpression, 147 projectConfig: ProjectConfig | undefined 148): arkts.CallExpression { 149 const newArgs: arkts.AstNode[] = [ 150 arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), 151 arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), 152 ...resourceNode.arguments, 153 ]; 154 return factory.generateTransformedResource(resourceNode, newArgs); 155} 156 157export class StructTransformer extends AbstractVisitor { 158 private scopeInfoCollection: ScopeInfoCollection; 159 projectConfig: ProjectConfig | undefined; 160 161 constructor(projectConfig: ProjectConfig | undefined) { 162 super(); 163 this.projectConfig = projectConfig; 164 this.scopeInfoCollection = { customComponents: [] }; 165 } 166 167 reset(): void { 168 super.reset(); 169 this.scopeInfoCollection = { customComponents: [] }; 170 } 171 172 enter(node: arkts.AstNode): void { 173 if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { 174 this.scopeInfoCollection.customComponents.push({ name: node.definition!.ident!.name }); 175 } 176 if (arkts.isMethodDefinition(node) && this.scopeInfoCollection.customComponents.length > 0) { 177 const name = node.name.name; 178 const scopeInfo = this.scopeInfoCollection.customComponents.pop()!; 179 scopeInfo.hasInitializeStruct ||= name === CustomComponentNames.COMPONENT_INITIALIZE_STRUCT; 180 scopeInfo.hasUpdateStruct ||= name === CustomComponentNames.COMPONENT_UPDATE_STRUCT; 181 scopeInfo.hasReusableRebind ||= name === CustomComponentNames.REUSABLE_COMPONENT_REBIND_STATE; 182 this.scopeInfoCollection.customComponents.push(scopeInfo); 183 } 184 } 185 186 exit(node: arkts.AstNode): void { 187 if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { 188 this.scopeInfoCollection.customComponents.pop(); 189 } 190 } 191 192 visitor(beforeChildren: arkts.AstNode): arkts.AstNode { 193 this.enter(beforeChildren); 194 const node = this.visitEachChild(beforeChildren); 195 if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { 196 let scope: CustomComponentScopeInfo | undefined; 197 const scopeInfos: CustomComponentScopeInfo[] = this.scopeInfoCollection.customComponents; 198 if (scopeInfos.length > 0) { 199 scope = scopeInfos[scopeInfos.length - 1]; 200 } 201 const newClass: arkts.ClassDeclaration = tranformClassMembers( 202 node, 203 arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), 204 scope 205 ); 206 this.exit(beforeChildren); 207 return newClass; 208 } else if (isEntryWrapperClass(node)) { 209 entryFactory.addMemoToEntryWrapperClassMethods(node); 210 return node; 211 } else if (arkts.isClassDeclaration(node) && isEtsGlobalClass(node)) { 212 return transformEtsGlobalClassMembers(node); 213 } else if (arkts.isCallExpression(node) && isReourceNode(node)) { 214 return transformResource(node, this.projectConfig); 215 } else if (findCanAddMemoFromArrowFunction(node)) { 216 return addMemoAnnotation(node); 217 } 218 return node; 219 } 220} 221