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 fs from 'fs'; 17import path from 'path'; 18import type { SourceFile } from 'typescript'; 19import { SyntaxKind } from 'typescript'; 20import type { InterfaceEntity } from '../declaration-node/interfaceDeclaration'; 21import { generateCommonMethodSignature } from './generateCommonMethodSignature'; 22import { generateIndexSignature } from './generateIndexSignature'; 23import { generatePropertySignatureDeclaration } from './generatePropertySignatureDeclaration'; 24import { dtsFileList, getApiInputPath, hasBeenImported, specialFiles } from '../common/commonUtils'; 25import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration'; 26import type { PropertySignatureEntity } from '../declaration-node/propertySignatureDeclaration'; 27 28/** 29 * generate interface 30 * @param rootName 31 * @param interfaceEntity 32 * @param sourceFile 33 * @param isSourceFile 34 * @returns 35 */ 36export function generateInterfaceDeclaration(rootName: string, interfaceEntity: InterfaceEntity, sourceFile: SourceFile, isSourceFile: boolean, 37 mockApi: string, currentSourceInterfaceArray: InterfaceEntity[], importDeclarations?: ImportElementEntity[], extraImport?: string[]): string { 38 const interfaceName = interfaceEntity.interfaceName; 39 let interfaceBody = ''; 40 const interfaceElementSet = new Set<string>(); 41 if (interfaceEntity.exportModifiers.length > 0 || isSourceFile) { 42 interfaceBody += `export const ${interfaceName} = { \n`; 43 } else { 44 interfaceBody += `const ${interfaceName} = { \n`; 45 } 46 47 if (interfaceEntity.interfacePropertySignatures.length > 0) { 48 interfaceEntity.interfacePropertySignatures.forEach(value => { 49 interfaceBody += generatePropertySignatureDeclaration(interfaceName, value, sourceFile, mockApi) + '\n'; 50 interfaceElementSet.add(value.propertyName); 51 addExtraImport(extraImport, importDeclarations, sourceFile, value); 52 }); 53 } 54 55 if (interfaceEntity.interfaceMethodSignature.size > 0) { 56 interfaceEntity.interfaceMethodSignature.forEach(value => { 57 interfaceBody += generateCommonMethodSignature(interfaceName, value, sourceFile, mockApi) + '\n'; 58 interfaceElementSet.add(value[0].functionName); 59 }); 60 } 61 62 if (interfaceEntity.indexSignature.length > 0) { 63 interfaceEntity.indexSignature.forEach(value => { 64 interfaceBody += generateIndexSignature(value) + '\n'; 65 interfaceElementSet.add(value.indexSignatureKey); 66 }); 67 } 68 69 if (interfaceEntity.heritageClauses.length > 0) { 70 interfaceEntity.heritageClauses.forEach(value => { 71 currentSourceInterfaceArray.forEach(currentInterface => { 72 if (value.types.includes(currentInterface.interfaceName)) { 73 interfaceBody += generateHeritageInterface(currentInterface, sourceFile, interfaceElementSet, mockApi); 74 } 75 }); 76 }); 77 } 78 79 interfaceBody += '}\n'; 80 if (interfaceEntity.exportModifiers.includes(SyntaxKind.DeclareKeyword)) { 81 interfaceBody += ` 82 if (!global.${interfaceName}) { 83 global.${interfaceName} = ${interfaceName};\n 84 } 85 `; 86 } 87 return interfaceBody; 88} 89 90function generateHeritageInterface(interfaceEntity: InterfaceEntity, sourceFile: SourceFile, elements: Set<string>, mockApi: string): string { 91 const interfaceName = interfaceEntity.interfaceName; 92 let interfaceBody = ''; 93 if (interfaceEntity.interfacePropertySignatures.length > 0) { 94 interfaceEntity.interfacePropertySignatures.forEach(value => { 95 if (!elements.has(value.propertyName)) { 96 interfaceBody += generatePropertySignatureDeclaration(interfaceName, value, sourceFile, mockApi) + '\n'; 97 } 98 }); 99 } 100 101 if (interfaceEntity.interfaceMethodSignature.size > 0) { 102 interfaceEntity.interfaceMethodSignature.forEach(value => { 103 if (!elements.has(value[0].functionName)) { 104 interfaceBody += generateCommonMethodSignature(interfaceName, value, sourceFile, mockApi) + '\n'; 105 } 106 }); 107 } 108 109 if (interfaceEntity.indexSignature.length > 0) { 110 interfaceEntity.indexSignature.forEach(value => { 111 if (elements.has(value.indexSignatureKey)) { 112 interfaceBody += generateIndexSignature(value) + '\n'; 113 } 114 }); 115 } 116 return interfaceBody; 117} 118 119/** 120 * 121 * @param extraImport 122 * @param importDeclarations 123 * @param sourceFile 124 * @param value 125 * @returns 126 */ 127function addExtraImport( 128 extraImport: string[], 129 importDeclarations: ImportElementEntity[], 130 sourceFile: SourceFile, 131 value: PropertySignatureEntity): void { 132 if (extraImport && importDeclarations) { 133 const propertyTypeName = value.propertyTypeName.split('.')[0].split('|')[0].split('&')[0].replace(/"'/g, '').trim(); 134 if (propertyTypeName.includes('/')) { 135 return; 136 } 137 if (hasBeenImported(importDeclarations, propertyTypeName)) { 138 return; 139 } 140 const specialFilesList = [...specialFiles.map(specialFile => path.join(getApiInputPath(), ...specialFile.split('/')))]; 141 if (!specialFilesList.includes(sourceFile.fileName)) { 142 specialFilesList.unshift(sourceFile.fileName); 143 } 144 for (let i = 0; i < specialFilesList.length; i++) { 145 const specialFilePath = specialFilesList[i]; 146 let specialFileContent = fs.readFileSync(specialFilePath, 'utf-8'); 147 const removeNoteRegx = /\/\*[\s\S]*?\*\//g; 148 specialFileContent = specialFileContent.replace(removeNoteRegx, ''); 149 const regex = new RegExp(`\\s${propertyTypeName}\\s({|=|extends)`); 150 const results = specialFileContent.match(regex); 151 if (!results) { 152 continue; 153 } 154 if (sourceFile.fileName === specialFilePath) { 155 return; 156 } 157 let specialFileRelatePath = path.relative(path.dirname(sourceFile.fileName), path.dirname(specialFilePath)); 158 if (!specialFileRelatePath.startsWith('./') && !specialFileRelatePath.startsWith('../')) { 159 specialFileRelatePath = './' + specialFileRelatePath; 160 } 161 if (!dtsFileList.includes(specialFilePath)) { 162 dtsFileList.push(specialFilePath); 163 } 164 specialFileRelatePath = specialFileRelatePath.split(path.sep).join('/'); 165 const importStr = `import {${propertyTypeName}} from '${ 166 specialFileRelatePath}${ 167 specialFileRelatePath.endsWith('/') ? '' : '/'}${ 168 path.basename(specialFilePath).replace('.d.ts', '').replace('.d.ets', '')}'\n`; 169 if (extraImport.includes(importStr)) { 170 return; 171 } 172 extraImport.push(importStr); 173 return; 174 } 175 if (propertyTypeName.includes('<') || propertyTypeName.includes('[')) { 176 return; 177 } 178 console.log(sourceFile.fileName, 'propertyTypeName', propertyTypeName); 179 return; 180 } 181} 182