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, CustomComponentNames, Dollars } from '../utils'; 18import { factory as uiFactory } from '../ui-factory'; 19import { annotation } from '../../common/arkts-utils'; 20 21export class factory { 22 /* 23 * create `constructor() {}`. 24 */ 25 static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { 26 return arkts.factory.createMethodDefinition( 27 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, 28 member.name, 29 member.scriptFunction, 30 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, 31 false 32 ); 33 } 34 35 /* 36 * create _build method. 37 */ 38 static transformBuildMethodWithOriginBuild( 39 method: arkts.MethodDefinition, 40 typeName: string, 41 optionsName: string, 42 isDecl?: boolean 43 ): arkts.MethodDefinition { 44 const updateKey: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_BUILD); 45 46 const scriptFunction: arkts.ScriptFunction = method.scriptFunction; 47 const updateScriptFunction = arkts.factory 48 .createScriptFunction( 49 scriptFunction.body, 50 arkts.FunctionSignature.createFunctionSignature( 51 scriptFunction.typeParams, 52 [ 53 uiFactory.createStyleParameter(typeName), 54 uiFactory.createContentParameter(), 55 uiFactory.createInitializersOptionsParameter(optionsName), 56 ], 57 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 58 false 59 ), 60 scriptFunction.flags, 61 scriptFunction.modifiers 62 ) 63 .setAnnotations([annotation('memo')]); 64 65 const modifiers: arkts.Es2pandaModifierFlags = isDecl 66 ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT 67 : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; 68 return arkts.factory.createMethodDefinition( 69 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 70 updateKey, 71 updateScriptFunction, 72 modifiers, 73 false 74 ); 75 } 76 77 /* 78 * generate _r(<newArgs>) or _rawfile(<newArgs>). 79 */ 80 static generateTransformedResource( 81 resourceNode: arkts.CallExpression, 82 newArgs: arkts.AstNode[] 83 ): arkts.CallExpression { 84 const transformedKey: string = 85 resourceNode.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE ? '_r' : '_rawfile'; 86 return arkts.factory.updateCallExpression( 87 resourceNode, 88 arkts.factory.createIdentifier(transformedKey), 89 resourceNode.typeArguments, 90 newArgs 91 ); 92 } 93 94 /* 95 * create __initializeStruct method. 96 */ 97 static createInitializeStruct( 98 structInfo: arkts.StructInfo, 99 optionsTypeName: string, 100 isDecl?: boolean 101 ): arkts.MethodDefinition { 102 const updateKey: arkts.Identifier = arkts.factory.createIdentifier( 103 CustomComponentNames.COMPONENT_INITIALIZE_STRUCT 104 ); 105 106 let body: arkts.BlockStatement | undefined; 107 let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; 108 if (!isDecl) { 109 body = arkts.factory.createBlock(structInfo.initializeBody); 110 modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; 111 } 112 const scriptFunction: arkts.ScriptFunction = arkts.factory 113 .createScriptFunction( 114 body, 115 arkts.FunctionSignature.createFunctionSignature( 116 undefined, 117 [uiFactory.createInitializersOptionsParameter(optionsTypeName), uiFactory.createContentParameter()], 118 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 119 false 120 ), 121 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 122 modifiers 123 ) 124 .setIdent(updateKey); 125 126 return arkts.factory.createMethodDefinition( 127 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 128 updateKey, 129 scriptFunction, 130 modifiers, 131 false 132 ); 133 } 134 135 /* 136 * create __updateStruct method. 137 */ 138 static createUpdateStruct( 139 structInfo: arkts.StructInfo, 140 optionsTypeName: string, 141 isDecl?: boolean 142 ): arkts.MethodDefinition { 143 const updateKey: arkts.Identifier = arkts.factory.createIdentifier( 144 CustomComponentNames.COMPONENT_UPDATE_STRUCT 145 ); 146 147 let body: arkts.BlockStatement | undefined; 148 let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; 149 if (!isDecl) { 150 body = arkts.factory.createBlock(structInfo.updateBody); 151 modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; 152 } 153 154 const scriptFunction: arkts.ScriptFunction = arkts.factory 155 .createScriptFunction( 156 body, 157 arkts.FunctionSignature.createFunctionSignature( 158 undefined, 159 [uiFactory.createInitializersOptionsParameter(optionsTypeName)], 160 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 161 false 162 ), 163 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 164 modifiers 165 ) 166 .setIdent(updateKey); 167 168 return arkts.factory.createMethodDefinition( 169 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 170 updateKey, 171 scriptFunction, 172 modifiers, 173 false 174 ); 175 } 176 177 /* 178 * create __toRecord method when the component is decorated with @Reusable. 179 */ 180 static toRecord(optionsTypeName: string, toRecordBody: arkts.Property[]): arkts.MethodDefinition { 181 const paramsCasted = factory.generateParamsCasted(optionsTypeName); 182 const returnRecord = arkts.factory.createReturnStatement( 183 arkts.ObjectExpression.createObjectExpression( 184 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 185 toRecordBody, 186 false 187 ) 188 ); 189 const body: arkts.BlockStatement = arkts.factory.createBlock([paramsCasted, returnRecord]); 190 191 const params = arkts.ETSParameterExpression.create( 192 arkts.factory.createIdentifier('params', factory.generateTypeReferenceWithTypeName('Object')), 193 undefined 194 ); 195 196 const toRecordScriptFunction = arkts.factory.createScriptFunction( 197 body, 198 arkts.FunctionSignature.createFunctionSignature(undefined, [params], factory.generateTypeRecord(), false), 199 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 200 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC 201 ); 202 203 return arkts.factory.createMethodDefinition( 204 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, 205 arkts.factory.createIdentifier('__toRecord'), 206 toRecordScriptFunction, 207 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OVERRIDE, 208 false 209 ); 210 } 211 212 /* 213 * generate `const paramsCasted = (params as <optionsTypeName>)`. 214 */ 215 static generateParamsCasted(optionsTypeName: string): arkts.VariableDeclaration { 216 return arkts.factory.createVariableDeclaration( 217 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 218 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, 219 [ 220 arkts.factory.createVariableDeclarator( 221 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, 222 arkts.factory.createIdentifier('paramsCasted'), 223 arkts.TSAsExpression.createTSAsExpression( 224 arkts.factory.createIdentifier('params'), 225 factory.generateTypeReferenceWithTypeName(optionsTypeName), 226 false 227 ) 228 ), 229 ] 230 ); 231 } 232 233 /* 234 * generate Record<string, Object> type. 235 */ 236 static generateTypeRecord(): arkts.ETSTypeReference { 237 return arkts.factory.createTypeReference( 238 arkts.factory.createTypeReferencePart( 239 arkts.factory.createIdentifier('Record'), 240 arkts.factory.createTSTypeParameterInstantiation([ 241 factory.generateTypeReferenceWithTypeName('string'), 242 factory.generateTypeReferenceWithTypeName('Object'), 243 ]) 244 ) 245 ); 246 } 247 248 /* 249 * create type reference with type name, e.g. number. 250 */ 251 static generateTypeReferenceWithTypeName(typeName: string): arkts.ETSTypeReference { 252 return arkts.factory.createTypeReference( 253 arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(typeName)) 254 ); 255 } 256 257 /* 258 * create type reference with type name, e.g. number. 259 */ 260 static updateCustomComponentClass( 261 definition: arkts.ClassDefinition, 262 members: arkts.AstNode[] 263 ): arkts.ClassDefinition { 264 return arkts.factory.updateClassDefinition( 265 definition, 266 definition.ident, 267 definition.typeParams, 268 definition.superTypeParams, 269 definition.implements, 270 undefined, 271 definition.super, 272 members, 273 definition.modifiers, 274 arkts.classDefinitionFlags(definition) 275 ); 276 } 277 278 /* 279 * add headers for animation in CommonMethod 280 */ 281 static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.AstNode { 282 const animationStart = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_START); 283 const animationStop = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_STOP); 284 const updatedBody = arkts.factory.updateInterfaceBody(node.body!, [ 285 animationStart, 286 animationStop, 287 ...node.body!.body, 288 ]); 289 return arkts.factory.updateInterfaceDeclaration( 290 node, 291 node.extends, 292 node.id, 293 node.typeParams, 294 updatedBody, 295 node.isStatic, 296 node.isFromExternal 297 ); 298 } 299 300 /* 301 * generate animationStart(...) and animationStop(...) 302 */ 303 static createAnimationMethod(key: string): arkts.MethodDefinition { 304 const aniparams: arkts.Expression[] = [ 305 arkts.factory.createParameterDeclaration( 306 arkts.factory.createIdentifier( 307 'value', 308 arkts.factory.createUnionType( 309 [ 310 arkts.factory.createTypeReference( 311 arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) 312 ), 313 arkts.factory.createETSUndefinedType() 314 ] 315 ) 316 ), 317 undefined 318 ), 319 ]; 320 const aniFuncExpr = arkts.factory.createScriptFunction( 321 undefined, 322 arkts.factory.createFunctionSignature(undefined, aniparams, arkts.TSThisType.createTSThisType(), false), 323 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 324 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC 325 ); 326 return arkts.factory.createMethodDefinition( 327 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 328 arkts.factory.createIdentifier(key), 329 aniFuncExpr, 330 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, 331 false 332 ); 333 } 334 335 /* 336 * generate XComponent(..., packageInfo: string, ...) 337 */ 338 static modifyXcomponent(node: arkts.ScriptFunction): arkts.ScriptFunction { 339 const info = arkts.factory.createParameterDeclaration( 340 arkts.factory.createIdentifier( 341 'packageInfo', 342 arkts.factory.createTypeReference( 343 arkts.factory.createTypeReferencePart( 344 arkts.factory.createIdentifier('string') 345 ) 346 ) 347 ), 348 undefined 349 ); 350 return arkts.factory.updateScriptFunction( 351 node, 352 node.body, 353 arkts.factory.createFunctionSignature( 354 node.typeParams, 355 [...node.params.slice(0, 2), info, ...node.params.slice(2)], 356 node.returnTypeAnnotation, 357 false 358 ), 359 node.flags, 360 node.modifiers 361 ); 362 } 363 364 /* 365 * transform ExternalSource headers 366 */ 367 static transformExternalSource(externalSourceName: string, node: arkts.AstNode): arkts.AstNode { 368 if ( 369 externalSourceName === 'arkui.component.common' && 370 arkts.isTSInterfaceDeclaration(node) && 371 !!node.id && 372 node.id.name === 'CommonMethod' 373 ) { 374 return factory.modifyExternalComponentCommon(node); 375 } else if ( 376 externalSourceName === 'arkui.component.xcomponent' && 377 arkts.isScriptFunction(node) && 378 !!node.id && 379 node.id.name === 'XComponent' 380 ) { 381 return factory.modifyXcomponent(node); 382 } 383 return node; 384 } 385} 386