• 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';
35import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration';
36
37/**
38 * generate declare
39 * @param rootName
40 * @param moduleEntity
41 * @param sourceFile
42 * @param filename
43 * @param extraImport
44 * @returns
45 */
46export function generateModuleDeclaration(rootName: string, moduleEntity: ModuleBlockEntity, sourceFile: SourceFile,
47  filename: string, mockApi: string, extraImport: string[], importDeclarations: ImportElementEntity[]): string {
48  let innerModuleBody = '';
49  const moduleName = moduleEntity.moduleName.replace(/["']/g, '');
50  let moduleBody = `export function mock${firstCharacterToUppercase(moduleName)}() {\n`;
51  let enumBody = '';
52  if (!(moduleEntity.exportModifiers.includes(SyntaxKind.DeclareKeyword) &&
53    (moduleEntity.moduleName.startsWith('"') || moduleEntity.moduleName.startsWith('\''))) &&
54    path.basename(sourceFile.fileName).startsWith('@ohos')
55  ) {
56    addToIndexArray({ fileName: filename, mockFunctionName: `mock${firstCharacterToUppercase(moduleName)}` });
57  }
58  let outBody = '';
59  const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
60
61  if (defaultExportClass.length > 0) {
62    defaultExportClass.forEach(value => {
63      if (value.exportModifiers.includes(SyntaxKind.DefaultKeyword) && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) {
64        if (filename.startsWith('system_')) {
65          const mockNameArr = filename.split('_');
66          const mockName = mockNameArr[mockNameArr.length - 1];
67          addToSystemIndexArray({
68            filename: filename,
69            mockFunctionName: `mock${firstCharacterToUppercase(mockName)}`
70          });
71
72          moduleBody += `global.systemplugin.${mockName} = {`;
73          if (value.staticMethods.length > 0) {
74            let staticMethodBody = '';
75            value.staticMethods.forEach(val => {
76              staticMethodBody += generateStaticFunction(val, true, sourceFile, mockApi) + '\n';
77            });
78            moduleBody += staticMethodBody;
79          }
80          moduleBody += '}';
81        } else {
82          outBody += generateClassDeclaration('', value, false, '', filename, sourceFile, false, mockApi);
83        }
84      }
85    });
86  }
87
88  if (moduleEntity.typeAliasDeclarations.length > 0) {
89    moduleEntity.typeAliasDeclarations.forEach(value => {
90      outBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n';
91    });
92  }
93
94  if (moduleEntity.moduleImportEquaqls.length > 0) {
95    moduleEntity.moduleImportEquaqls.forEach(value => {
96      outBody += generateImportEqual(value) + '\n';
97    });
98  }
99
100  if (moduleEntity.classDeclarations.length > 0) {
101    moduleEntity.classDeclarations.forEach(value => {
102      if (value.exportModifiers.length > 0 && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) {
103        outBody += generateClassDeclaration(moduleName, value, false, '', '', sourceFile, false, mockApi) + '\n';
104      } else {
105        moduleBody += '\t' + generateClassDeclaration(moduleName, value, false, '', '', sourceFile, true, mockApi) + '\n';
106      }
107    });
108  }
109
110  if (moduleEntity.interfaceDeclarations.length > 0) {
111    moduleEntity.interfaceDeclarations.forEach(value => {
112      if (value.exportModifiers.length > 0) {
113        outBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n';
114      } else {
115        moduleBody += '\t' + generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n';
116      }
117    });
118  }
119
120  if (moduleEntity.enumDeclarations.length > 0) {
121    moduleEntity.enumDeclarations.forEach(value => {
122      if (value.exportModifiers.length > 0) {
123        outBody += generateEnumDeclaration(moduleName, value) + '\n';
124      } else {
125        enumBody += generateEnumDeclaration(moduleName, value);
126      }
127    });
128  }
129
130  let functionBody = '';
131  if (moduleEntity.functionDeclarations.size > 0) {
132    moduleEntity.functionDeclarations.forEach(value => {
133      functionBody += '\t' + generateCommonFunction(moduleName, value, sourceFile, mockApi, false) + '\n';
134    });
135  }
136
137  if (moduleEntity.moduleDeclarations.length > 0) {
138    moduleEntity.moduleDeclarations.forEach(value => {
139      if (!value.moduleName.startsWith("'") && !value.moduleName.startsWith('"')) {
140        innerModuleBody += generateInnerModuleDeclaration(value, sourceFile, filename, mockApi, extraImport, importDeclarations);
141      }
142    });
143  }
144  if (innerModuleBody) {
145    moduleBody += innerModuleBody + '\n';
146  }
147
148  moduleBody += '\t' + `const ${moduleName} = {`;
149  if (moduleEntity.variableStatements.length > 0) {
150    moduleEntity.variableStatements.forEach(value => {
151      value.forEach(val => {
152        moduleBody += generateVariableStatementDelcatation(val, false) + '\n';
153      });
154    });
155  }
156
157  const sourceFileFunctions = getSourceFileFunctions(sourceFile);
158  let sourceFileFunctionBody = '';
159  if (sourceFileFunctions.size > 0) {
160    sourceFileFunctions.forEach(value => {
161      sourceFileFunctionBody += '\n' + generateCommonFunction(moduleName, value, sourceFile, mockApi, false);
162    });
163  }
164
165  const sourceFileVariableStatements = getSourceFileVariableStatements(sourceFile);
166  let sourceFileStatementBody = '';
167  if (sourceFileVariableStatements.length > 0) {
168    sourceFileVariableStatements.forEach(value => {
169      value.forEach(val => {
170        sourceFileStatementBody += '\n' + generateVariableStatementDelcatation(val, false);
171      });
172    });
173  }
174
175  moduleBody += sourceFileFunctionBody + '\n';
176  moduleBody += sourceFileStatementBody + '\n';
177  moduleBody += functionBody + '\n';
178
179  const exports = getModuleExportElements(moduleEntity);
180  let exportString = '';
181  exports.forEach(value => {
182    if (value.type === 'module' && !value.name.startsWith("'") && !value.name.startsWith('"')) {
183      exportString += `${value.name}: mock${value.name}(),\n`;
184    } else {
185      exportString += `${value.name}: ${value.name},\n`;
186    }
187  });
188  if (exportString !== '') {
189    moduleBody += '\t' + exportString;
190  }
191
192  moduleBody += '\t};';
193  moduleBody += `\n\treturn ${moduleName};}\n`;
194  moduleBody += outBody;
195  moduleBody = enumBody + moduleBody;
196  return moduleBody;
197}
198
199function generateInnerModuleDeclaration(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile,
200  filename: string, mockApi: string, extraImport: string[], importDeclarations: ImportElementEntity[]): string {
201  let innerModuleBody = '';
202  const innerModuleName = moduleEntity.moduleName.replace(/["']/g, '');
203  let moduleBody = `function mock${innerModuleName}() {\n`;
204  let innerOutBody = '';
205  let innerFunctionBody = '';
206
207  if (moduleEntity.typeAliasDeclarations.length) {
208    moduleEntity.typeAliasDeclarations.forEach(value => {
209      innerOutBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n';
210    });
211  }
212
213  if (moduleEntity.moduleImportEquaqls.length) {
214    moduleEntity.moduleImportEquaqls.forEach(value => {
215      innerOutBody += generateImportEqual(value) + '\n';
216    });
217  }
218
219  if (moduleEntity.classDeclarations.length) {
220    moduleEntity.classDeclarations.forEach(value => {
221      if (value.exportModifiers.length && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) {
222        innerOutBody += generateClassDeclaration(innerModuleName, value, false, '', '', sourceFile, false, mockApi) + '\n';
223      } else {
224        moduleBody += '\t' + generateClassDeclaration(innerModuleName, value, false, '', '', sourceFile, true, mockApi) + '\n';
225      }
226    });
227  }
228
229  if (moduleEntity.interfaceDeclarations.length) {
230    moduleEntity.interfaceDeclarations.forEach(value => {
231      if (value.exportModifiers.length) {
232        innerOutBody += generateInterfaceDeclaration(innerModuleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n';
233      } else {
234        moduleBody += '\t' + generateInterfaceDeclaration(innerModuleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n';
235      }
236    });
237  }
238
239  if (moduleEntity.enumDeclarations.length) {
240    moduleEntity.enumDeclarations.forEach(value => {
241      if (value.exportModifiers.length) {
242        innerOutBody += generateEnumDeclaration(innerModuleName, value) + '\n';
243      } else {
244        moduleBody += generateEnumDeclaration(innerModuleName, value);
245      }
246    });
247  }
248
249  if (moduleEntity.functionDeclarations.size) {
250    moduleEntity.functionDeclarations.forEach(value => {
251      innerFunctionBody += '\n' + generateCommonFunction(innerModuleName, value, sourceFile, mockApi, false) + '\n';
252    });
253  }
254
255  if (moduleEntity.moduleDeclarations.length) {
256    moduleEntity.moduleDeclarations.forEach(value => {
257      if (!value.moduleName.startsWith("'") && !value.moduleName.startsWith('"')) {
258        innerModuleBody += generateInnerModuleDeclaration(value, sourceFile, filename, mockApi, extraImport, importDeclarations);
259      }
260    });
261  }
262  if (innerModuleBody) {
263    moduleBody += innerModuleBody + '\n';
264  }
265
266  moduleBody += `const ${innerModuleName} = {\n`;
267  if (moduleEntity.variableStatements.length) {
268    moduleEntity.variableStatements.forEach(value => {
269      value.forEach(val => {
270        moduleBody += generateVariableStatementDelcatation(val, false) + '\n';
271      });
272    });
273  }
274
275  moduleBody += innerFunctionBody + '\n';
276
277  const exportArr = getModuleExportElements(moduleEntity);
278  let innerExportString = '';
279  exportArr.forEach(value => {
280    if (value.type === 'module' && !value.name.startsWith("'") && !value.name.startsWith('"')) {
281      innerExportString += `${value.name}: mock${value.name}(),\n`;
282    } else {
283      innerExportString += `${value.name}: ${value.name},\n`;
284    }
285  });
286  if (innerExportString !== '') {
287    moduleBody += '\t' + innerExportString;
288  }
289
290  moduleBody += '\t};';
291  moduleBody += `\n\treturn ${innerModuleName};}\n`;
292  moduleBody += innerOutBody;
293  return moduleBody;
294}
295
296/**
297 * generate inner module for declare module
298 * @param moduleEntity
299 * @returns
300 */
301function generateInnerDeclareModule(moduleEntity: ModuleBlockEntity): string {
302  let moduleName = '$' + moduleEntity.moduleName.replace(/["']/g, '');
303  let module = `\n\texport const ${moduleName} = `;
304  if (moduleEntity.exportDeclarations.length > 0) {
305    moduleEntity.exportDeclarations.forEach(value => {
306      module += value.match(/{[^{}]*}/g)[0] + '\n';
307    });
308  }
309  return module;
310}
311
312/**
313 * generate inner module
314 * @param moduleEntity
315 * @param sourceFile
316 * @param extraImport
317 * @returns
318 */
319function generateInnerModule(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, extraImport: string[], mockApi: string): string {
320  const moduleName = moduleEntity.moduleName;
321  let innerModuleBody = `const ${moduleName} = (()=> {`;
322
323  if (moduleEntity.enumDeclarations.length > 0) {
324    moduleEntity.enumDeclarations.forEach(value => {
325      innerModuleBody += generateEnumDeclaration(moduleName, value) + '\n';
326    });
327  }
328
329  if (moduleEntity.typeAliasDeclarations.length > 0) {
330    moduleEntity.typeAliasDeclarations.forEach(value => {
331      innerModuleBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n';
332    });
333  }
334
335  if (moduleEntity.moduleImportEquaqls.length > 0) {
336    moduleEntity.moduleImportEquaqls.forEach(value => {
337      innerModuleBody += generateImportEqual(value) + '\n';
338    });
339  }
340
341  if (moduleEntity.interfaceDeclarations.length > 0) {
342    moduleEntity.interfaceDeclarations.forEach(value => {
343      innerModuleBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, '', moduleEntity.interfaceDeclarations) + '\n';
344    });
345  }
346
347  let functionBody = 'return {';
348  if (moduleEntity.functionDeclarations.size > 0) {
349    moduleEntity.functionDeclarations.forEach(value => {
350      functionBody += generateCommonFunction(moduleName, value, sourceFile, '', false) + '\n';
351    });
352  }
353
354  if (moduleEntity.variableStatements.length > 0) {
355    moduleEntity.variableStatements.forEach(value => {
356      value.forEach(val => {
357        innerModuleBody += generateVariableStatementDelcatation(val, true) + '\n';
358      });
359    });
360  }
361  innerModuleBody += functionBody + '\n';
362
363  const exports = getModuleExportElements(moduleEntity);
364  let exportString = '';
365  exports.forEach(value => {
366    exportString += `${value.name}: ${value.name},\n`;
367  });
368  if (exportString !== '') {
369    innerModuleBody += '\t' + exportString;
370  }
371  innerModuleBody += '\t};})();';
372  return innerModuleBody;
373}
374
375/**
376 * get all export elements
377 * @param moduleEntity
378 * @returns
379 */
380function getModuleExportElements(moduleEntity: ModuleBlockEntity): Array<ModuleExportEntity> {
381  const exportElements: Array<ModuleExportEntity> = [];
382  if (moduleEntity.moduleName.startsWith('"') && moduleEntity.moduleName.endsWith('"')) {
383    return exportElements;
384  }
385  if (moduleEntity.classDeclarations.length > 0) {
386    moduleEntity.classDeclarations.forEach(value => {
387      exportElements.push({ name: firstCharacterToUppercase(value.className), type: 'class' });
388    });
389  }
390
391  if (moduleEntity.interfaceDeclarations.length > 0) {
392    moduleEntity.interfaceDeclarations.forEach(value => {
393      exportElements.push({ name: value.interfaceName, type: 'interface' });
394    });
395  }
396
397  if (moduleEntity.enumDeclarations.length > 0) {
398    moduleEntity.enumDeclarations.forEach(value => {
399      exportElements.push({ name: value.enumName, type: 'enum' });
400    });
401  }
402
403  if (moduleEntity.moduleDeclarations.length > 0) {
404    moduleEntity.moduleDeclarations.forEach(value => {
405      exportElements.push({ name: value.moduleName, type: 'module' });
406    });
407  }
408
409  if (moduleEntity.typeAliasDeclarations.length > 0) {
410    moduleEntity.typeAliasDeclarations.forEach(value => {
411      exportElements.push({ name: value.typeAliasName, type: 'type' });
412    });
413  }
414  return exportElements;
415}
416
417interface ModuleExportEntity {
418  type: string,
419  name: string
420}
421