• 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 ts from "typescript";
17import uiconfig from './arkui_config_util';
18import path from 'path';
19import { ComponentFile } from "./component_file";
20
21export function addImportTransformer(): ts.TransformerFactory<ts.SourceFile> {
22    return (context) => {
23        return (sourceFile) => {
24            const [updatedSource, targetImport] = createTargetImport(sourceFile, context);
25            const insertPosition = findBestInsertPosition(updatedSource);
26
27            const newStatements = [
28                ...updatedSource.statements.slice(0, insertPosition),
29                ...targetImport,
30                ...updatedSource.statements.slice(insertPosition)
31            ];
32
33            return ts.factory.updateSourceFile(
34                updatedSource,
35                newStatements,
36                updatedSource.isDeclarationFile,
37                updatedSource.referencedFiles,
38                updatedSource.typeReferenceDirectives,
39                updatedSource.hasNoDefaultLib,
40                updatedSource.libReferenceDirectives
41            );
42        };
43    };
44}
45
46function findBestInsertPosition(sourceFile: ts.SourceFile): number {
47    let lastImportIndex = -1;
48    sourceFile.statements.forEach((stmt, index) => {
49        if (ts.isImportDeclaration(stmt)) {
50            lastImportIndex = index;
51        }
52    });
53
54    if (lastImportIndex !== -1) {
55        return lastImportIndex + 1;
56    }
57
58    return 0;
59}
60
61function handleImportDeclaration(node: ts.ImportDeclaration): [ts.Node, boolean] {
62    if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
63        const moduleText = node.moduleSpecifier.text;
64        const haveCommon = moduleText.includes("common");
65        if (uiconfig.isComponentFile(moduleText)) {
66            const importClause = node.importClause;
67            const uiprefixImports: Set<string> = new Set
68            if (importClause && ts.isImportClause(importClause) && importClause.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
69                const namedImports = importClause.namedBindings.elements;
70                const existingImports = namedImports.map((element) => element.name.text);
71                if (moduleText.includes("common")) {
72                    uiprefixImports.add('AttributeModifier');
73                    uiprefixImports.add('CommonMethod');
74                }
75
76                const addedImports = Array.from(uiprefixImports).filter((im) => !existingImports.includes(im));
77                const pureModule = path.basename(moduleText)
78                const updatedName = ComponentFile.snake2Camel(pureModule, true)
79                const updatedModuleSpecifier = ts.factory.createStringLiteral(moduleText.replace(pureModule, updatedName));
80                const updatedNode = ts.factory.updateImportDeclaration(
81                    node,
82                    undefined,
83                    ts.factory.updateImportClause(
84                        importClause,
85                        importClause.isTypeOnly,
86                        importClause.name,
87                        ts.factory.updateNamedImports(
88                            importClause.namedBindings,
89                            [
90                                ...namedImports,
91                                ...addedImports.map(im => { return ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(im)) }),
92                            ]
93                        )
94                    ),
95                    updatedModuleSpecifier,
96                    undefined
97                );
98                return [updatedNode, haveCommon]
99            } else {
100                throw new Error("Unexpected import clause structure");
101            }
102        }
103    }
104    return [node, false];
105}
106
107function createTargetImport(sourceFile: ts.SourceFile, context: ts.TransformationContext): [ts.SourceFile, ts.ImportDeclaration[]] {
108    const targetImport: ts.ImportDeclaration[] = []
109
110    const memoImport = ts.factory.createImportDeclaration(
111        undefined,
112        ts.factory.createImportClause(
113            false,
114            undefined,
115            ts.factory.createNamedImports([
116                ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("memo")),
117                ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ComponentBuilder"))
118            ])
119        ),
120        ts.factory.createStringLiteral("./../stateManagement/runtime")
121    );
122    targetImport.push(memoImport)
123
124    let haveCommonImportFlag = false;
125    const newSource = ts.visitEachChild(sourceFile, (node) => {
126        if (ts.isImportDeclaration(node)) {
127            const [transNode, flag] = handleImportDeclaration(node)!;
128            haveCommonImportFlag = flag ? flag : haveCommonImportFlag
129            return transNode
130        }
131        return node;
132    }, context)
133
134    if (!haveCommonImportFlag) {
135        const commonImport = ts.factory.createImportDeclaration(
136            undefined,
137            ts.factory.createImportClause(
138                false,
139                undefined,
140                ts.factory.createNamedImports([
141                    ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("AttributeModifier")),
142                    ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("CommonMethod")),
143                ])
144            ),
145            ts.factory.createStringLiteral("./common"),
146            undefined
147        )
148        targetImport.push(commonImport)
149    }
150    return [newSource, targetImport]
151}
152