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