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[], mockApi: 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, mockApi); 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[], mockApi: 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) { 108 continue; 109 } 110 if (!typeName?.trim().startsWith('import(')) { 111 let name = typeName.trim(); 112 if (name.includes('.')) { 113 name = name.trim().split('.')[0]; 114 } 115 if (mockApi.includes(`import { ${name} `) || mockApi.includes(` as ${name.trim()} `) || mockApi.includes(`import ${name} `)) { 116 return typeName.trim(); 117 } 118 continue; 119 } 120 const splitTypeName = typeName.split(')'); 121 const propertiesIndex = 1; 122 const properties = splitTypeName[propertiesIndex]; 123 const importPath = getImportFileFullPath(typeName); 124 const realImportPath = pathToImportPath(sourceFile.fileName, importPath); 125 if (!importPath) { 126 continue; 127 } 128 const importFileContent = fs.readFileSync(importPath, 'utf-8'); 129 if (properties.startsWith('.default')) { 130 let result = importFileContent.match(/export\sdefault\sclass\s[a-zA-Z]+/); 131 if (result) { 132 const defaultModuleName = '_' + result[0].replace(/export\sdefault\sclass\s/, ''); 133 const importStr = `import ${defaultModuleName} from '${realImportPath}';\n`; 134 !extraImport.includes(importStr) && extraImport.push(importStr); 135 return `${defaultModuleName}${properties.replace('.default', '')}`; 136 } 137 result = importFileContent.match(/export\sdefault\s[a-zA-Z]+;/); 138 if (result) { 139 const moduleName = result[0] 140 .replace(/export\sdefault\s/, '') 141 .replace(';', ''); 142 const mockFunctionName = `mock${firstCharacterToUppercase(moduleName)}`; 143 const importStr = `import {${mockFunctionName}} from '${realImportPath}';\n`; 144 !extraImport.includes(importStr) && extraImport.push(importStr); 145 return `${mockFunctionName}()${properties.replace('.default', '')}`; 146 } 147 } else { 148 const moduleName = properties.replace('.', '').split('.')[0]; 149 const importStr = `import {${moduleName} as _${moduleName}} from '${realImportPath}';\n`; 150 !extraImport.includes(importStr) && extraImport.push(importStr); 151 return `_${properties.replace('.', '')}`; 152 } 153 } 154 return ''; 155} 156