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 { annotation } from '../common/arkts-utils'; 18import { DecoratorNames } from './property-translators/utils'; 19 20export enum CustomComponentNames { 21 ENTRY_ANNOTATION_NAME = 'Entry', 22 COMPONENT_ANNOTATION_NAME = 'Component', 23 RESUABLE_ANNOTATION_NAME = 'Reusable', 24 COMPONENT_BUILD_ORI = 'build', 25 COMPONENT_CONSTRUCTOR_ORI = 'constructor', 26 COMPONENT_DEFAULT_IMPORT = 'arkui.component.customComponent', 27 COMPONENT_CLASS_NAME = 'CustomComponent', 28 COMPONENT_INTERFACE_PREFIX = '__Options_', 29 COMPONENT_INITIALIZE_STRUCT = '__initializeStruct', 30 COMPONENT_UPDATE_STRUCT = '__updateStruct', 31 COMPONENT_BUILD = '_build', 32 REUSABLE_COMPONENT_REBIND_STATE = '__rebindStates', 33 COMPONENT_INITIALIZERS_NAME = 'initializers', 34 BUILDCOMPATIBLENODE = '_buildCompatibleNode', 35 OPTIONS = 'options', 36} 37 38export enum BuilderLambdaNames { 39 ANNOTATION_NAME = 'ComponentBuilder', 40 ORIGIN_METHOD_NAME = '$_instantiate', 41 TRANSFORM_METHOD_NAME = '_instantiateImpl', 42 STYLE_PARAM_NAME = 'style', 43 STYLE_ARROW_PARAM_NAME = 'instance', 44 CONTENT_PARAM_NAME = 'content', 45 ANIMATION_NAME = 'animation', 46 ANIMATION_START = 'animationStart', 47 ANIMATION_STOP = 'animationStop', 48} 49 50export enum Dollars { 51 DOLLAR_RESOURCE = '$r', 52 DOLLAR_RAWFILE = '$rawfile', 53 DOLLAR_DOLLAR = '$$', 54} 55 56export function findLocalImport( 57 node: arkts.ETSImportDeclaration, 58 sourceName: string, 59 importedName: string 60): arkts.Identifier | undefined { 61 const isFromSource = !!node.source && node.source.str === sourceName; 62 if (!isFromSource) return undefined; 63 64 const importSpecifier = node.specifiers.find( 65 (spec) => arkts.isImportSpecifier(spec) && !!spec.imported && spec.imported.name === importedName 66 ) as arkts.ImportSpecifier | undefined; 67 return importSpecifier?.local ?? importSpecifier?.imported; 68} 69 70// TODO: currently, we forcely assume initializerOptions is named in pattern __Options_xxx 71export function getCustomComponentNameFromInitializerOptions(name: string): string | undefined { 72 const prefix: string = CustomComponentNames.COMPONENT_INTERFACE_PREFIX; 73 if (name.startsWith(prefix)) { 74 return name.substring(prefix.length); 75 } 76} 77 78export function getCustomComponentOptionsName(className: string): string { 79 return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; 80} 81 82export function isStatic(node: arkts.AstNode): boolean { 83 return node.isStatic; 84} 85 86export function getTypeParamsFromClassDecl(node: arkts.ClassDeclaration | undefined): readonly arkts.TSTypeParameter[] { 87 return node?.definition?.typeParams?.params ?? []; 88} 89 90export function getTypeNameFromTypeParameter(node: arkts.TSTypeParameter | undefined): string | undefined { 91 return node?.name?.name; 92} 93 94export function createOptionalClassProperty( 95 name: string, 96 property: arkts.ClassProperty, 97 stageManagementIdent: string, 98 modifiers: arkts.Es2pandaModifierFlags, 99 needMemo: boolean = false 100): arkts.ClassProperty { 101 const newType: arkts.TypeNode | undefined = property.typeAnnotation?.clone(); 102 if (needMemo) { 103 newType?.setAnnotations([annotation('memo')]); 104 } 105 const newProperty = arkts.factory.createClassProperty( 106 arkts.factory.createIdentifier(name), 107 undefined, 108 stageManagementIdent.length ? createStageManagementType(stageManagementIdent, property) : newType, 109 modifiers, 110 false 111 ); 112 return arkts.classPropertySetOptional(newProperty, true); 113} 114 115export function createStageManagementType( 116 stageManagementIdent: string, 117 property: arkts.ClassProperty 118): arkts.ETSTypeReference { 119 return arkts.factory.createTypeReference( 120 arkts.factory.createTypeReferencePart( 121 arkts.factory.createIdentifier(stageManagementIdent), 122 arkts.factory.createTSTypeParameterInstantiation([ 123 property.typeAnnotation ? property.typeAnnotation.clone() : arkts.factory.createETSUndefinedType(), 124 ]) 125 ) 126 ); 127} 128 129export function getGettersFromClassDecl(definition: arkts.ClassDefinition): arkts.MethodDefinition[] { 130 return definition.body.filter( 131 (member) => 132 arkts.isMethodDefinition(member) && 133 arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) 134 ) as arkts.MethodDefinition[]; 135} 136 137export type MemoAstNode = 138 | arkts.ScriptFunction 139 | arkts.ETSParameterExpression 140 | arkts.ClassProperty 141 | arkts.TSTypeAliasDeclaration 142 | arkts.ETSFunctionType 143 | arkts.ArrowFunctionExpression 144 | arkts.ETSUnionType; 145 146export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: string): boolean { 147 if (!(node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName)) { 148 return false; 149 } 150 return true; 151} 152 153export function addMemoAnnotation<T extends MemoAstNode>(node: T, memoName: string = 'memo'): T { 154 if (arkts.isETSUnionType(node)) { 155 const functionType = node.types.find((type) => arkts.isETSFunctionType(type)); 156 if (!functionType) { 157 return node; 158 } 159 addMemoAnnotation(functionType, memoName); 160 return node; 161 } 162 const newAnnotations: arkts.AnnotationUsage[] = [ 163 ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), 164 annotation(memoName), 165 ]; 166 if (arkts.isEtsParameterExpression(node)) { 167 node.annotations = newAnnotations; 168 return node; 169 } 170 return node.setAnnotations(newAnnotations) as T; 171} 172 173export function hasPropertyInAnnotation(annotation: arkts.AnnotationUsage, propertyName: string): boolean { 174 return !!annotation.properties.find( 175 (annoProp: arkts.AstNode) => 176 arkts.isClassProperty(annoProp) && 177 annoProp.key && 178 arkts.isIdentifier(annoProp.key) && 179 annoProp.key.name === propertyName 180 ); 181} 182 183/** 184 * Determine whether the type node includes null or undefined type. 185 * 186 * @param type type node 187 */ 188export function hasNullOrUndefinedType(type: arkts.TypeNode): boolean { 189 let res: boolean = false; 190 if (arkts.isETSUnionType(type)) { 191 type.types.forEach((item: arkts.TypeNode) => { 192 res = res || hasNullOrUndefinedType(item); 193 }); 194 } 195 if (arkts.isETSUndefinedType(type) || arkts.isETSNullType(type)) { 196 res = true; 197 } 198 return res; 199}