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