• 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 path from 'path';
17import { SyntaxKind } from 'typescript';
18import type { SourceFile } from 'typescript';
19import { firstCharacterToUppercase } from '../common/commonUtils';
20import type { ModuleBlockEntity } from '../declaration-node/moduleDeclaration';
21import {
22  getDefaultExportClassDeclaration, getSourceFileFunctions,
23  getSourceFileVariableStatements
24} from '../declaration-node/sourceFileElementsAssemply';
25import { generateClassDeclaration } from './generateClassDeclaration';
26import { generateCommonFunction } from './generateCommonFunction';
27import { generateEnumDeclaration } from './generateEnumDeclaration';
28import { generateImportEqual } from './generateImportEqual';
29import { addToIndexArray } from './generateIndex';
30import { generateInterfaceDeclaration } from './generateInterfaceDeclaration';
31import { generateStaticFunction } from './generateStaticFunction';
32import { addToSystemIndexArray } from './generateSystemIndex';
33import { generateTypeAliasDeclaration } from './generateTypeAlias';
34import { generateVariableStatementDelcatation } from './generateVariableStatementDeclaration';
35
36/**
37 * generate declare
38 * @param rootName
39 * @param moduleEntity
40 * @param sourceFile
41 * @param filename
42 * @param extraImport
43 * @returns
44 */
45export function generateModuleDeclaration(rootName: string, moduleEntity: ModuleBlockEntity, sourceFile: SourceFile,
46  filename: string, mockApi: string, extraImport: string[]): string {
47  let moduleName = moduleEntity.moduleName.replace(/["']/g, '');
48  let moduleBody = `export function mock${firstCharacterToUppercase(moduleName)}() {\n`;
49  if (!(moduleEntity.exportModifiers.includes(SyntaxKind.DeclareKeyword) &&
50    (moduleEntity.moduleName.startsWith('"') || moduleEntity.moduleName.startsWith('\''))) &&
51    path.basename(sourceFile.fileName).startsWith('@ohos')
52  ) {
53    addToIndexArray({ fileName: filename, mockFunctionName: `mock${firstCharacterToUppercase(moduleName)}` });
54  }
55  let outBody = '';
56  const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
57
58  if (defaultExportClass.length > 0) {
59    defaultExportClass.forEach(value => {
60      if (value.exportModifiers.includes(SyntaxKind.DefaultKeyword) && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) {
61        if (filename.startsWith('system_')) {
62          const mockNameArr = filename.split('_');
63          const mockName = mockNameArr[mockNameArr.length - 1];
64          addToSystemIndexArray({
65            filename: filename,
66            mockFunctionName: `mock${firstCharacterToUppercase(mockName)}`
67          });
68
69          moduleBody += `global.systemplugin.${mockName} = {`;
70          if (value.staticMethods.length > 0) {
71            let staticMethodBody = '';
72            value.staticMethods.forEach(val => {
73              staticMethodBody += generateStaticFunction(val, true, sourceFile, mockApi) + '\n';
74            });
75            moduleBody += staticMethodBody;
76          }
77          moduleBody += '}';
78        } else {
79          outBody += generateClassDeclaration('', value, false, '', filename, sourceFile, false, mockApi);
80        }
81      }
82    });
83  }
84
85  if (moduleEntity.typeAliasDeclarations.length > 0) {
86    moduleEntity.typeAliasDeclarations.forEach(value => {
87      outBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport) + '\n';
88    });
89  }
90
91  if (moduleEntity.moduleImportEquaqls.length > 0) {
92    moduleEntity.moduleImportEquaqls.forEach(value => {
93      outBody += generateImportEqual(value) + '\n';
94    });
95  }
96
97  if (moduleEntity.classDeclarations.length > 0) {
98    moduleEntity.classDeclarations.forEach(value => {
99      if (value.exportModifiers.length > 0 && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) {
100        outBody += generateClassDeclaration(moduleName, value, false, '', '', sourceFile, false, mockApi) + '\n';
101      } else {
102        moduleBody += '\t' + generateClassDeclaration(moduleName, value, false, '', '', sourceFile, true, mockApi) + '\n';
103      }
104    });
105  }
106
107  if (moduleEntity.interfaceDeclarations.length > 0) {
108    moduleEntity.interfaceDeclarations.forEach(value => {
109      if (value.exportModifiers.length > 0) {
110        outBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations) + ';\n';
111      } else {
112        moduleBody += '\t' + generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations) + ';\n';
113      }
114    });
115  }
116
117  if (moduleEntity.enumDeclarations.length > 0) {
118    moduleEntity.enumDeclarations.forEach(value => {
119      if (value.exportModifiers.length > 0) {
120        outBody += generateEnumDeclaration(moduleName, value) + '\n';
121      } else {
122        moduleBody += '\t' + generateEnumDeclaration(moduleName, value) + '\n';
123      }
124    });
125  }
126
127  let functionBody = '';
128  if (moduleEntity.functionDeclarations.size > 0) {
129    moduleEntity.functionDeclarations.forEach(value => {
130      functionBody += '\t' + generateCommonFunction(moduleName, value, sourceFile, mockApi) + '\n';
131    });
132  }
133
134  moduleBody += '\t' + `const ${moduleName} = {`;
135  if (moduleEntity.variableStatements.length > 0) {
136    moduleEntity.variableStatements.forEach(value => {
137      value.forEach(val => {
138        moduleBody += generateVariableStatementDelcatation(val, false) + '\n';
139      });
140    });
141  }
142
143  const sourceFileFunctions = getSourceFileFunctions(sourceFile);
144  let sourceFileFunctionBody = '';
145  if (sourceFileFunctions.size > 0) {
146    sourceFileFunctions.forEach(value => {
147      sourceFileFunctionBody += generateCommonFunction(moduleName, value, sourceFile, mockApi);
148    });
149  }
150
151  const sourceFileVariableStatements = getSourceFileVariableStatements(sourceFile);
152  let sourceFileStatementBody = '';
153  if (sourceFileVariableStatements.length > 0) {
154    sourceFileVariableStatements.forEach(value => {
155      value.forEach(val => {
156        sourceFileStatementBody += generateVariableStatementDelcatation(val, false);
157      });
158    });
159  }
160
161  moduleBody += sourceFileFunctionBody + '\n';
162  moduleBody += sourceFileStatementBody + '\n';
163  moduleBody += functionBody + '\n';
164
165  const exports = getModuleExportElements(moduleEntity);
166  let exportString = '';
167  exports.forEach(value => {
168    exportString += `${value.name}: ${value.name},\n`;
169  });
170  if (exportString !== '') {
171    moduleBody += '\t' + exportString;
172  }
173
174  moduleBody += '\t};';
175  moduleBody += `\n\treturn ${moduleName};}\n`;
176  moduleBody += outBody;
177  return moduleBody;
178}
179
180/**
181 * generate inner module for declare module
182 * @param moduleEntity
183 * @returns
184 */
185function generateInnerDeclareModule(moduleEntity: ModuleBlockEntity): string {
186  let moduleName = '$' + moduleEntity.moduleName.replace(/["']/g, '');
187  let module = `\n\texport const ${moduleName} = `;
188  if (moduleEntity.exportDeclarations.length > 0) {
189    moduleEntity.exportDeclarations.forEach(value => {
190      module += value.match(/{[^{}]*}/g)[0] + '\n';
191    });
192  }
193  return module;
194}
195
196/**
197 * generate inner module
198 * @param moduleEntity
199 * @param sourceFile
200 * @param extraImport
201 * @returns
202 */
203function generateInnerModule(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, extraImport: string[]): string {
204  const moduleName = moduleEntity.moduleName;
205  let innerModuleBody = `const ${moduleName} = (()=> {`;
206
207  if (moduleEntity.enumDeclarations.length > 0) {
208    moduleEntity.enumDeclarations.forEach(value => {
209      innerModuleBody += generateEnumDeclaration(moduleName, value) + '\n';
210    });
211  }
212
213  if (moduleEntity.typeAliasDeclarations.length > 0) {
214    moduleEntity.typeAliasDeclarations.forEach(value => {
215      innerModuleBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport) + '\n';
216    });
217  }
218
219  if (moduleEntity.moduleImportEquaqls.length > 0) {
220    moduleEntity.moduleImportEquaqls.forEach(value => {
221      innerModuleBody += generateImportEqual(value) + '\n';
222    });
223  }
224
225  if (moduleEntity.interfaceDeclarations.length > 0) {
226    moduleEntity.interfaceDeclarations.forEach(value => {
227      innerModuleBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, '', moduleEntity.interfaceDeclarations) + '\n';
228    });
229  }
230
231  let functionBody = 'return {';
232  if (moduleEntity.functionDeclarations.size > 0) {
233    moduleEntity.functionDeclarations.forEach(value => {
234      functionBody += generateCommonFunction(moduleName, value, sourceFile, '') + '\n';
235    });
236  }
237
238  if (moduleEntity.variableStatements.length > 0) {
239    moduleEntity.variableStatements.forEach(value => {
240      value.forEach(val => {
241        innerModuleBody += generateVariableStatementDelcatation(val, true) + '\n';
242      });
243    });
244  }
245  innerModuleBody += functionBody + '\n';
246
247  const exports = getModuleExportElements(moduleEntity);
248  let exportString = '';
249  exports.forEach(value => {
250    exportString += `${value.name}: ${value.name},\n`;
251  });
252  if (exportString !== '') {
253    innerModuleBody += '\t' + exportString;
254  }
255  innerModuleBody += '\t};})();';
256  return innerModuleBody;
257}
258
259/**
260 * get all export elements
261 * @param moduleEntity
262 * @returns
263 */
264function getModuleExportElements(moduleEntity: ModuleBlockEntity): Array<ModuleExportEntity> {
265  const exportElements: Array<ModuleExportEntity> = [];
266  if (moduleEntity.moduleName.startsWith('"') && moduleEntity.moduleName.endsWith('"')) {
267    return exportElements;
268  }
269  if (moduleEntity.classDeclarations.length > 0) {
270    moduleEntity.classDeclarations.forEach(value => {
271      exportElements.push({ name: firstCharacterToUppercase(value.className), type: 'class' });
272    });
273  }
274
275  if (moduleEntity.interfaceDeclarations.length > 0) {
276    moduleEntity.interfaceDeclarations.forEach(value => {
277      exportElements.push({ name: value.interfaceName, type: 'interface' });
278    });
279  }
280
281  if (moduleEntity.enumDeclarations.length > 0) {
282    moduleEntity.enumDeclarations.forEach(value => {
283      exportElements.push({ name: value.enumName, type: 'enum' });
284    });
285  }
286
287  if (moduleEntity.moduleDeclarations.length > 0) {
288    moduleEntity.moduleDeclarations.forEach(value => {
289      exportElements.push({ name: value.moduleName, type: 'module' });
290    });
291  }
292
293  if (moduleEntity.typeAliasDeclarations.length > 0) {
294    moduleEntity.typeAliasDeclarations.forEach(value => {
295      exportElements.push({ name: value.typeAliasName, type: 'type' });
296    });
297  }
298  return exportElements;
299}
300
301interface ModuleExportEntity {
302  type: string,
303  name: string
304}
305