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 { getInteropPath } from '../path'; 18const interop = require(getInteropPath()); 19const nullptr = interop.nullptr; 20import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; 21import { InteroperAbilityNames } from '../common/predefines'; 22import { getCustomComponentOptionsName } from './utils'; 23 24interface LegacyTransformerOptions extends VisitorOptions { 25 structList?: string[] 26} 27 28export class LegacyTransformer extends AbstractVisitor { 29 private structList: string[] = []; 30 private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = []; 31 32 constructor(options?: LegacyTransformerOptions) { 33 const _options: LegacyTransformerOptions = options ?? {}; 34 super(_options); 35 this.structList = _options.structList ?? []; 36 } 37 38 reset(): void { 39 super.reset(); 40 this.componentInterfaceCollection = []; 41 } 42 43 getList(): string[] { 44 return this.structList; 45 } 46 47 createParam(name: string, type: string): arkts.ETSParameterExpression { 48 return arkts.factory.createParameterDeclaration( 49 arkts.factory.createIdentifier( 50 name, 51 arkts.factory.createTypeReference( 52 arkts.factory.createTypeReferencePart( 53 arkts.factory.createIdentifier(type) 54 ) 55 ) 56 ), 57 undefined 58 ); 59 } 60 61 createInteropMethod(name: string): arkts.MethodDefinition { 62 const path = this.createParam('path', 'string'); 63 const line = this.createParam('line', 'number'); 64 line.setOptional(true); 65 const col = this.createParam('col', 'number'); 66 col.setOptional(true); 67 const options = this.createParam('options', getCustomComponentOptionsName(name)); 68 options.setOptional(true); 69 70 const script = arkts.factory.createScriptFunction( 71 arkts.factory.createBlock([]), 72 arkts.FunctionSignature.createFunctionSignature( 73 undefined, 74 [path, line, col, options], 75 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 76 false 77 ), 78 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 79 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC 80 ); 81 82 return arkts.factory.createMethodDefinition( 83 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 84 arkts.factory.createIdentifier('instantiate_Interop'), 85 script, 86 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, 87 false 88 ); 89 } 90 91 generateMember(map: Map<string, arkts.TypeNode>): arkts.ClassProperty[] { 92 const properties: arkts.ClassProperty[] = []; 93 94 map.forEach((value, key) => { 95 const property = arkts.factory.createClassProperty( 96 arkts.factory.createIdentifier(key), 97 undefined, 98 value, 99 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | 100 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL, 101 false 102 ); 103 104 properties.push(property); 105 }); 106 107 return properties; 108 } 109 110 generateComponentInterface(name: string, modifiers: number, map: Map<string, arkts.TypeNode>): arkts.TSInterfaceDeclaration { 111 const interfaceNode = arkts.factory.createInterfaceDeclaration( 112 [], 113 arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), 114 nullptr, // TODO: wtf 115 arkts.factory.createInterfaceBody([...(this.generateMember(map) || [])]), 116 false, 117 false 118 ); 119 interfaceNode.modifiers = modifiers; 120 return interfaceNode; 121 } 122 123 processComponent(node: arkts.StructDeclaration): arkts.StructDeclaration | arkts.ClassDeclaration { 124 const definition: arkts.ClassDefinition = node.definition!; 125 const ident = definition.ident!; 126 const hasExportFlag = 127 (node.modifiers & arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT) === 128 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; 129 if (hasExportFlag) { 130 this.structList.push(ident.name); 131 } 132 133 const instantiate_Interop: arkts.MethodDefinition = this.createInteropMethod(ident.name); 134 135 const newDefinition = arkts.factory.updateClassDefinition( 136 definition, 137 definition.ident, 138 definition.typeParams, 139 definition.superTypeParams, 140 definition.implements, 141 undefined, 142 definition.super, 143 [...definition.body, instantiate_Interop], 144 definition.modifiers, 145 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE 146 ); 147 148 console.log('print legacyclass definition' + newDefinition.dumpSrc()); 149 150 151 //TODO: need check 152 if (arkts.isStructDeclaration(node)) { 153 const _node = arkts.factory.createClassDeclaration(newDefinition); 154 _node.modifiers = node.modifiers; 155 return _node; 156 } else { 157 return arkts.factory.updateClassDeclaration(node, newDefinition); 158 } 159 } 160 161 processConstructor(node: arkts.MethodDefinition): arkts.MethodDefinition { 162 const esvalue = arkts.factory.createTypeReference( 163 arkts.factory.createTypeReferencePart( 164 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) 165 ) 166 ); 167 const script = arkts.factory.createScriptFunction( 168 arkts.factory.createBlock([]), 169 arkts.factory.createFunctionSignature( 170 undefined, 171 [ 172 arkts.factory.createParameterDeclaration( 173 arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, esvalue), 174 undefined, 175 ), 176 arkts.factory.createParameterDeclaration( 177 arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, esvalue), 178 undefined, 179 ), 180 arkts.factory.createParameterDeclaration( 181 arkts.factory.createIdentifier('localStorage', esvalue), 182 undefined, 183 ), 184 arkts.factory.createParameterDeclaration( 185 arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, esvalue), 186 undefined, 187 ), 188 arkts.factory.createParameterDeclaration( 189 arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, esvalue), 190 undefined, 191 ), 192 arkts.factory.createParameterDeclaration( 193 arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, esvalue), 194 undefined, 195 ) 196 ], undefined, false), 197 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, 198 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, 199 ); 200 return arkts.factory.updateMethodDefinition( 201 node, 202 node.kind, 203 node.name, 204 script, 205 node.modifiers, 206 false 207 ); 208 } 209 210 collectComponentMembers(node: arkts.StructDeclaration, className: string): Map<string, arkts.TypeNode> { 211 const result: Map<string, arkts.TypeNode> = new Map(); 212 node.definition.body.map((it) => { 213 if (arkts.isClassProperty(it)) { 214 const name = (it.key as arkts.Identifier).name; 215 const type = it.typeAnnotation!; 216 result.set(name, type); 217 } 218 }); 219 return result; 220 } 221 222 processEtsScript(node: arkts.EtsScript): arkts.EtsScript { 223 let updateStatements: arkts.AstNode[] = []; 224 if (this.componentInterfaceCollection.length > 0) { 225 updateStatements.push(...this.componentInterfaceCollection); 226 } 227 if (updateStatements.length > 0) { 228 return arkts.factory.updateEtsScript(node, [...node.statements, ...updateStatements]); 229 } 230 return node; 231 } 232 233 visitor(node: arkts.AstNode): arkts.AstNode { 234 const newNode = this.visitEachChild(node); 235 if (arkts.isEtsScript(newNode)) { 236 return this.processEtsScript(newNode); 237 } 238 if (arkts.isStructDeclaration(newNode)) { 239 const className = node.definition?.ident?.name; 240 const memberMap = this.collectComponentMembers(node as arkts.StructDeclaration, className); 241 this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers, memberMap)); 242 const updateNode = this.processComponent(newNode); 243 return updateNode; 244 } 245 if (arkts.isMethodDefinition(newNode)) { 246 const kind = newNode.kind; 247 if (kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR) { 248 const updateNode = this.processConstructor(newNode); 249 return updateNode; 250 } 251 } 252 return newNode; 253 } 254}