• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}