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