• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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