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