• 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 { getInteropPath } from '../../path';
18const interop = require(getInteropPath());
19const nullptr = interop.nullptr;
20import { EntryWrapperNames } from './utils';
21import { annotation } from '../../common/arkts-utils';
22import { factory as uiFactory } from '../ui-factory';
23
24export class factory {
25    /**
26     * insert an 'entry' method to an entry wrapper class.
27     *
28     * @param node entry wrapper class declaration node.
29     */
30    static registerEntryFunction(node: arkts.ClassDeclaration): arkts.AstNode {
31        const definition: arkts.ClassDefinition | undefined = node.definition;
32        const classname = node?.definition?.ident?.name;
33        if (!definition || !classname) {
34            throw new Error('Node definition is undefined');
35        }
36        const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition(
37            definition,
38            definition.ident,
39            definition.typeParams,
40            definition.superTypeParams,
41            definition.implements,
42            undefined,
43            definition.super,
44            [...definition.body, factory.generateEntryFunction(classname)],
45            definition.modifiers,
46            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE
47        );
48        return arkts.factory.updateClassDeclaration(node, updateClassDef);
49    }
50
51    /**
52     * insert an 'entry' property to an entry wrapper class.
53     *
54     * @param node entry wrapper class declaration node.
55     * @deprecated
56     */
57    static registerEntryProperty(node: arkts.ClassDeclaration): arkts.AstNode {
58        const definition: arkts.ClassDefinition | undefined = node.definition;
59        const classname = node?.definition?.ident?.name;
60        if (!definition || !classname) {
61            throw new Error('Node definition is undefined');
62        }
63        const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition(
64            definition,
65            definition.ident,
66            definition.typeParams,
67            definition.superTypeParams,
68            definition.implements,
69            undefined,
70            definition.super,
71            [...definition.body, factory.generateEntryProperty(classname)],
72            definition.modifiers,
73            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE
74        );
75        return arkts.factory.updateClassDeclaration(node, updateClassDef);
76    }
77
78    /**
79     * create `entry(): void { <name>(); }` class method for the entry wrapper class,
80     * which calls the struct within the method.
81     *
82     * @param name class/struct name that has `@Entry` annotation.
83     */
84    static generateEntryFunction(name: string): arkts.MethodDefinition {
85        const exp = arkts.factory.createExpressionStatement(
86            arkts.factory.createCallExpression(
87                arkts.factory.createIdentifier(name),
88                undefined,
89                [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later
90            )
91        );
92        const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC);
93        const block = arkts.factory.createBlock([exp]);
94        const entryScript = arkts.factory
95            .createScriptFunction(
96                block,
97                arkts.FunctionSignature.createFunctionSignature(
98                    undefined,
99                    [],
100                    arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID),
101                    false
102                ),
103                arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD,
104                arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC
105            )
106            .setIdent(key);
107
108        const def = arkts.factory.createMethodDefinition(
109            arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD,
110            key,
111            entryScript,
112            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC,
113            false
114        );
115
116        return def;
117    }
118
119    static generateConstructor(): arkts.MethodDefinition {
120        const key: arkts.Identifier = arkts.factory.createIdentifier('constructor');
121        const block = arkts.factory.createBlock([]);
122        const entryScript = arkts.factory
123            .createScriptFunction(
124                block,
125                arkts.FunctionSignature.createFunctionSignature(undefined, [], undefined, false),
126                arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR |
127                    arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_IMPLICIT_SUPER_CALL_NEEDED,
128                arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC |
129                    arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR
130            )
131            .setIdent(key);
132        const def = arkts.factory.createMethodDefinition(
133            arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR,
134            key,
135            entryScript,
136            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE,
137            false
138        );
139        return def;
140    }
141
142    /**
143     * create `entry = (): void => { <name>(); }` class property for the entry wrapper class,
144     * which calls the struct within the arrow function.
145     *
146     * @param name class/struct name that has `@Entry` annotation.
147     * @deprecated
148     */
149    static generateEntryProperty(name: string): arkts.ClassProperty {
150        const exp = arkts.factory.createExpressionStatement(
151            arkts.factory.createCallExpression(
152                arkts.factory.createIdentifier(name),
153                undefined,
154                [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later
155            )
156        );
157        const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC);
158        const block: arkts.BlockStatement = arkts.factory.createBlock([exp]);
159        const signature: arkts.FunctionSignature = arkts.FunctionSignature.createFunctionSignature(
160            undefined,
161            [],
162            arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID),
163            false
164        );
165        const entryScript: arkts.ScriptFunction = arkts.factory
166            .createScriptFunction(
167                block,
168                signature,
169                arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW,
170                arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC
171            )
172            .setIdent(key);
173
174        const def = arkts.factory.createClassProperty(
175            key,
176            arkts.factory.createArrowFunction(entryScript),
177            arkts.factory.createFunctionType(signature, arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW),
178            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC,
179            false
180        );
181
182        return def;
183    }
184
185    /**
186     * create `__EntryWrapper_Entry` entry wrapper class that contains an 'entry' method that
187     * calls the struct within the method.
188     *
189     * @param name class/struct name that has `@Entry` annotation.
190     */
191    static generateEntryWrapper(name: string): arkts.ClassDeclaration {
192        const ctor = factory.generateConstructor();
193        const definition: arkts.ClassDefinition = arkts.factory
194            .createClassDefinition(
195                arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME),
196                undefined,
197                undefined,
198                [],
199                undefined,
200                arkts.factory.createTypeReference(
201                    arkts.factory.createTypeReferencePart(
202                        arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME)
203                    )
204                ),
205                [factory.generateEntryFunction(name), ctor],
206                arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_CLASS_DECL |
207                    arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_DECLARATION |
208                    arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_ID_REQUIRED,
209                arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE
210            )
211            .setCtor(ctor as any);
212        const newClass = arkts.factory.createClassDeclaration(definition);
213        newClass.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE;
214        return newClass;
215    }
216
217    /**
218     * add `@memo` to all class methods that are named 'entry'.
219     *
220     * @param node class declaration node
221     */
222    static addMemoToEntryWrapperClassMethods(node: arkts.ClassDeclaration): void {
223        node.definition?.body.forEach((member) => {
224            if (
225                arkts.isMethodDefinition(member) &&
226                !!member.scriptFunction.id &&
227                member.scriptFunction.id.name === EntryWrapperNames.ENTRY_FUNC
228            ) {
229                member.scriptFunction.setAnnotations([annotation('memo')]);
230            }
231        });
232    }
233
234    /**
235     * add `@memo` to the class property's value (expecting an arrow function), where the property is named 'entry'.
236     *
237     * @param node class declaration node
238     * @deprecated
239     */
240    static addMemoToEntryWrapperPropertyValue(node: arkts.ClassDeclaration): void {
241        node.definition?.body.forEach((member) => {
242            if (
243                arkts.isClassProperty(member) &&
244                !!member.value &&
245                arkts.isArrowFunctionExpression(member.value) &&
246                !!member.key &&
247                arkts.isIdentifier(member.key) &&
248                member.key.name === EntryWrapperNames.ENTRY_FUNC
249            ) {
250                member.setAnnotations([annotation('memo')]);
251            }
252        });
253    }
254
255    /**
256     * create `private _entry_local_storage_ = <name>;` class property
257     * from `{storage: "<name>"}` in `@Entry`'s properties.
258     *
259     * @param annotation `@Entry` annotation.
260     */
261    static createEntryLocalStorageInClass(property: arkts.ClassProperty) {
262        const value = property.value as arkts.StringLiteral;
263        return arkts.factory.createClassProperty(
264            arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME),
265            arkts.factory.createIdentifier(value.str),
266            undefined,
267            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE,
268            false
269        );
270    }
271
272    /**
273     * create and insert `import { EntryPoint as EntryPoint } from "@ohos.arkui.UserView";`
274     * to the top of script's statements.
275     */
276    static createAndInsertEntryPointImport(program?: arkts.Program) {
277        const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(EntryWrapperNames.ENTRY_DEFAULT_IMPORT);
278        const imported: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME);
279        // Insert this import at the top of the script's statements.
280        if (!program) {
281            throw Error('Failed to insert import: Transformer has no program');
282        }
283        uiFactory.createAndInsertImportDeclaration(
284            source,
285            imported,
286            imported,
287            arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE,
288            program
289        );
290    }
291}
292