1/* 2 * Copyright (c) 2023 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 type { TypeAliasEntity, TypeAliasTypeEntity } from '../declaration-node/typeAliasDeclaration'; 17import { firstCharacterToUppercase, getOhosInterfacesDir } from '../common/commonUtils'; 18import path from 'path'; 19import fs from 'fs'; 20import type { SourceFile } from 'typescript'; 21 22const interceptIndex = 2; 23 24/** 25 * generate type alias 26 * @param typeAliasEntity 27 * @param isInner 28 * @param sourceFile 29 * @param extraImport 30 * @returns 31 */ 32export function generateTypeAliasDeclaration( 33 typeAliasEntity: TypeAliasEntity, isInner: boolean, sourceFile: SourceFile, extraImport: string[] 34): string { 35 let typeAliasName = ''; 36 if (!isInner) { 37 typeAliasName += `export const ${typeAliasEntity.typeAliasName} = `; 38 } else { 39 typeAliasName += `const ${typeAliasEntity.typeAliasName} = `; 40 } 41 42 let typeAliasValue = ''; 43 44 const typeAliasTypeElements = typeAliasEntity.typeAliasTypeElements; 45 46 if (typeAliasTypeElements) { 47 typeAliasValue += parseImportExpression(typeAliasTypeElements, sourceFile, extraImport); 48 } 49 50 if (!typeAliasValue) { 51 typeAliasValue += `'[PC Preview] unknown ${typeAliasEntity.typeAliasName}'`; 52 } 53 return typeAliasName + typeAliasValue + ';'; 54} 55 56function getImportFileFullPath(typeName: string): string { 57 const importRelatePathTmp = typeName.match(/\('[^'()]+'\)/); 58 if (!importRelatePathTmp) { 59 return ''; 60 } 61 const importRelatePath = importRelatePathTmp[0].substring(interceptIndex, importRelatePathTmp[0].length - interceptIndex); 62 const tmpRealPath = getOhosInterfacesDir() + importRelatePath.replace('../api', '').replace(/\//g, path.sep); 63 if (fs.existsSync(tmpRealPath + '.d.ts')) { 64 return tmpRealPath + '.d.ts'; 65 } 66 67 if (fs.existsSync(tmpRealPath + '.d.ets')) { 68 return tmpRealPath + '.d.ets'; 69 } 70 console.warn(`Can not find import \'${importRelatePath}\'`); 71 return ''; 72} 73 74function pathToImportPath(currentFilePath: string, importFilePath: string): string { 75 const currentFilePathSteps = currentFilePath.replace(/.d.e?ts/, '').split('/'); 76 const importFilePathSteps = importFilePath.replace(/.d.e?ts/, '').split(path.sep); 77 const importFilePathStepsLength = importFilePathSteps.length; 78 importFilePathSteps[importFilePathStepsLength - 1] = importFilePathSteps[importFilePathStepsLength - 1] 79 .replace('@', '').replace(/\./g, '_'); 80 let differStepIndex: number; 81 for (differStepIndex = 0; differStepIndex < currentFilePathSteps.length; differStepIndex++) { 82 if (currentFilePathSteps[differStepIndex] !== importFilePathSteps[differStepIndex]) { 83 break; 84 } 85 } 86 const currentFileDifferPathSteps = currentFilePathSteps.slice(differStepIndex); 87 const importFileDifferPathSteps = importFilePathSteps.slice(differStepIndex); 88 if (currentFileDifferPathSteps.length === importFileDifferPathSteps.length 89 && currentFileDifferPathSteps.length === 1) { 90 return `./${path.basename(importFilePath)}`; 91 } else { 92 const steps = []; 93 for (let i = 0; i < currentFileDifferPathSteps.length - 1; i++) { 94 steps.push('..'); 95 } 96 const fullSteps = steps.concat(importFileDifferPathSteps); 97 return fullSteps.join('/'); 98 } 99} 100 101function parseImportExpression( 102 typeAliasTypeElements: TypeAliasTypeEntity[], sourceFile: SourceFile, extraImport: string[] 103): string { 104 for (let i = 0; i < typeAliasTypeElements.length; i++) { 105 const typeAliasTypeElement = typeAliasTypeElements[i]; 106 const typeName = typeAliasTypeElement.typeName; 107 if (!typeName?.trim().startsWith('import(')) { 108 continue; 109 } 110 const splitTypeName = typeName.split(')'); 111 const propertiesIndex = 1; 112 const properties = splitTypeName[propertiesIndex]; 113 const importPath = getImportFileFullPath(typeName); 114 const realImportPath = pathToImportPath(sourceFile.fileName, importPath); 115 if (!importPath) { 116 continue; 117 } 118 const importFileContent = fs.readFileSync(importPath, 'utf-8'); 119 if (properties.startsWith('.default')) { 120 let result = importFileContent.match(/export\sdefault\sclass\s[a-zA-Z]+/); 121 if (result) { 122 const defaultModuleName = '_' + result[0].replace(/export\sdefault\sclass\s/, ''); 123 const importStr = `import ${defaultModuleName} from '${realImportPath}';\n`; 124 !extraImport.includes(importStr) && extraImport.push(importStr); 125 return `${defaultModuleName}${properties.replace('.default', '')}`; 126 } 127 result = importFileContent.match(/export\sdefault\s[a-zA-Z]+;/); 128 if (result) { 129 const moduleName = result[0] 130 .replace(/export\sdefault\s/, '') 131 .replace(';', ''); 132 const mockFunctionName = `mock${firstCharacterToUppercase(moduleName)}`; 133 const importStr = `import {${mockFunctionName}} from '${realImportPath}';\n`; 134 !extraImport.includes(importStr) && extraImport.push(importStr); 135 return `${mockFunctionName}()${properties.replace('.default', '')}`; 136 } 137 } else { 138 const moduleName = properties.replace('.', '').split('.')[0]; 139 const importStr = `import {${moduleName} as _${moduleName}} from '${realImportPath}';\n`; 140 !extraImport.includes(importStr) && extraImport.push(importStr); 141 return `_${properties.replace('.', '')}`; 142 } 143 } 144 return ''; 145} 146