• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript';
17import { collectAllLegalImports, firstCharacterToUppercase, getAllFileNameList } from '../common/commonUtils';
18import { ImportElementEntity } from '../declaration-node/importAndExportDeclaration';
19import { getDefaultExportClassDeclaration, getSourceFileFunctions, getSourceFileVariableStatements, SourceFileEntity } from '../declaration-node/sourceFileElementsAssemply';
20import { generateClassDeclaration } from './generateClassDeclaration';
21import { generateCommonFunction } from './generateCommonFunction';
22import { generateEnumDeclaration } from './generateEnumDeclaration';
23import { addToIndexArray } from './generateIndex';
24import { generateInterfaceDeclaration } from './generateInterfaceDeclaration';
25import { generateModuleDeclaration } from './generateModuleDeclaration';
26import { generateStaticFunction } from './generateStaticFunction';
27import { addToSystemIndexArray } from './generateSystemIndex';
28import { generateTypeAliasDeclaration } from './generateTypeAlias';
29import { generateVariableStatementDelcatation } from './generateVariableStatementDeclaration';
30
31/**
32 * generate mock file string
33 * @param rootName
34 * @param sourceFileEntity
35 * @param sourceFile
36 * @param fileName
37 * @returns
38 */
39export function generateSourceFileElements(rootName: string, sourceFileEntity: SourceFileEntity, sourceFile: SourceFile, fileName: string): string {
40  let mockApi = '';
41  const mockFunctionElements: Array<MockFunctionElementEntity> = [];
42  const heritageClausesArray = getCurrentApiHeritageArray(sourceFileEntity, sourceFile);
43  if (sourceFileEntity.importDeclarations.length > 0) {
44    sourceFileEntity.importDeclarations.forEach(value => {
45      mockApi += generateImportDeclaration(value, fileName, heritageClausesArray);
46    });
47  }
48
49  if (sourceFileEntity.moduleDeclarations.length > 0) {
50    sourceFileEntity.moduleDeclarations.forEach(value => {
51      mockApi += generateModuleDeclaration('', value, sourceFile, fileName) + '\n';
52    });
53  }
54
55  if (sourceFileEntity.classDeclarations.length > 0) {
56    sourceFileEntity.classDeclarations.forEach(value => {
57      if (!fileName.startsWith('system_') && !value.exportModifiers.includes(SyntaxKind.DefaultKeyword)) {
58        mockApi += generateClassDeclaration('', value, false, '', fileName, sourceFile, false) + '\n';
59        mockFunctionElements.push({ elementName: value.className, type: 'class' });
60      }
61    });
62  }
63
64  if (sourceFileEntity.interfaceDeclarations.length > 0) {
65    sourceFileEntity.interfaceDeclarations.forEach(value => {
66      mockApi += generateInterfaceDeclaration('', value, sourceFile, true, sourceFileEntity.interfaceDeclarations) + '\n';
67      mockFunctionElements.push({ elementName: value.interfaceName, type: 'interface' });
68    });
69  }
70
71  if (sourceFileEntity.enumDeclarations.length > 0) {
72    sourceFileEntity.enumDeclarations.forEach(value => {
73      mockApi += generateEnumDeclaration('', value) + '\n';
74      mockFunctionElements.push({ elementName: value.enumName, type: 'enum' });
75    });
76  }
77
78  if (sourceFileEntity.typeAliasDeclarations.length > 0) {
79    sourceFileEntity.typeAliasDeclarations.forEach(value => {
80      mockApi += generateTypeAliasDeclaration(value, false) + '\n';
81      mockFunctionElements.push({ elementName: value.typeAliasName, type: 'typeAlias' });
82    });
83  }
84
85  if (sourceFileEntity.moduleDeclarations.length === 0 && (fileName.startsWith('ohos_') || fileName.startsWith('system_') || fileName.startsWith('webgl'))) {
86    const mockNameArr = fileName.split('_');
87    const mockName = mockNameArr[mockNameArr.length - 1];
88    const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
89    if (defaultExportClass.length > 0) {
90      defaultExportClass.forEach(value => {
91        mockApi += generateClassDeclaration(rootName, value, false, mockName, '', sourceFile, false) + '\n';
92        mockFunctionElements.push({ elementName: value.className, type: 'class' });
93      });
94    }
95    mockApi += `export function mock${firstCharacterToUppercase(mockName)}() {\n`;
96    if (fileName.startsWith('system_')) {
97      addToSystemIndexArray({
98        filename: fileName,
99        mockFunctionName: `mock${firstCharacterToUppercase(mockName)}`
100      });
101      mockApi += `global.systemplugin.${mockName} = {`;
102      const defaultClass = getDefaultExportClassDeclaration(sourceFile);
103      let staticMethodBody = '';
104      if (defaultClass.length > 0) {
105        defaultClass.forEach(value => {
106          value.staticMethods.forEach(val => {
107            staticMethodBody += generateStaticFunction(val, true, sourceFile);
108          });
109        });
110      }
111      mockApi += staticMethodBody;
112      mockApi += '}';
113    } else {
114      if (!fileName.startsWith('webgl')) {
115        addToIndexArray({ fileName: fileName, mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` });
116      }
117    }
118    mockApi += `\nconst mockModule${firstCharacterToUppercase(mockName)} = {`;
119    mockFunctionElements.forEach(val => {
120      mockApi += `${val.elementName}: ${val.elementName},`;
121    });
122    mockApi += '}\n';
123    mockApi += `return mockModule${firstCharacterToUppercase(mockName)}.${firstCharacterToUppercase(mockName)}\n`;
124    mockApi += '}';
125  } else {
126    const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
127    if (defaultExportClass.length > 0) {
128      const mockNameArr = fileName.split('_');
129      const mockName = mockNameArr[mockNameArr.length - 1];
130      defaultExportClass.forEach(value => {
131        mockApi += generateClassDeclaration(rootName, value, false, mockName, '', sourceFile, false) + '\n';
132      });
133    }
134  }
135  if (sourceFileEntity.exportDeclarations.length > 0) {
136    sourceFileEntity.exportDeclarations.forEach(value => {
137      if (!value.includes('export {')) {
138        mockApi += `${value}\n`;
139      }
140    });
141  }
142  return mockApi;
143}
144
145/**
146 * generate import definition
147 * @param importEntity
148 * @param sourceFileName
149 * @returns
150 */
151export function generateImportDeclaration(importEntity: ImportElementEntity, sourceFileName: string, heritageClausesArray: string[]): string {
152  let importPathName = '';
153  const importPathSplit = importEntity.importPath.split('/');
154  let fileName = importPathSplit[importPathSplit.length - 1];
155  if (fileName.endsWith('.d.ts')) {
156    fileName = fileName.split('.d.')[0];
157  }
158  if (fileName.includes('@')) {
159    importPathName = fileName.replace('@', '').replace(/\./g, '_');
160  } else {
161    importPathName = fileName.replace(/\./g, '_');
162  }
163  let importPath = '';
164  for (let i = 0; i < importPathSplit.length - 1; i++) {
165    importPath += importPathSplit[i] + '/';
166  }
167  importPath += importPathName;
168  let importElements = importEntity.importElements;
169  if (!importElements.includes('{') && !importElements.includes('* as') && !heritageClausesArray.includes(importElements)) {
170    if (importEntity.importPath.includes('@ohos')) {
171      const tmpArr = importEntity.importPath.split('.');
172      importElements = `{ mock${firstCharacterToUppercase(tmpArr[tmpArr.length - 1].replace('"', '').replace('\'', ''))} }`;
173    } else {
174      importElements = `{ ${importElements} }`;
175    }
176  }
177  if (checIsDefaultExportClass(importEntity.importElements)) {
178    importElements = `{ ${importEntity.importElements} }`;
179  }
180  const testPath = importPath.replace(/"/g, '').replace(/'/g, '').split('/');
181  if (getAllFileNameList().has(testPath[testPath.length - 1]) || testPath[testPath.length - 1] === 'ohos_application_want') {
182    const tmpImportPath = importPath.replace(/'/g, '').replace(/"/g, '');
183    if (!tmpImportPath.startsWith('./') && !tmpImportPath.startsWith('../')) {
184      importPath = `'./${tmpImportPath}'`;
185    }
186    if (sourceFileName === 'tagSession' && importPath === `'./basic'` || sourceFileName === 'notificationContent' && importPath === `'./ohos_multimedia_image'`) {
187      importPath = `'.${importPath.replace(/'/g, '')}'`;
188    }
189
190    // adapt no rules .d.ts
191    if (importElements.trimRight().trimEnd() === 'AccessibilityExtensionContext, { AccessibilityElement }') {
192      importElements = '{ AccessibilityExtensionContext, AccessibilityElement }';
193    }
194    if (importElements.trimRight().trimEnd() === '{ image }') {
195      importElements = '{ mockImage as image }';
196    }
197    if (sourceFileName === 'AbilityContext' && importPath === `'../ohos_application_Ability'` ||
198      sourceFileName === 'Context' && importPath === `"./ApplicationContext"`) {
199      return '';
200    }
201    collectAllLegalImports(importElements);
202    return `import ${importElements} from ${importPath}\n`;
203  } else {
204    return '';
205  }
206}
207
208/**
209 * adapter default export
210 * @param importName
211 * @returns
212 */
213 function checIsDefaultExportClass(importName: string): boolean {
214  const defaultExportClass = ['Context', 'BaseContext', 'ExtensionContext', 'ApplicationContext', 'ExtensionAbility', 'Ability'];
215  return defaultExportClass.includes(importName);
216}
217
218/**
219 * get heritage elements
220 * @param sourceFileEntity
221 * @param sourceFile
222 * @returns
223 */
224function getCurrentApiHeritageArray(sourceFileEntity: SourceFileEntity, sourceFile: SourceFile): string[] {
225  const heritageClausesArray = [];
226  const defaultClassArray = getDefaultExportClassDeclaration(sourceFile);
227  sourceFileEntity.classDeclarations.forEach(value => {
228    value.heritageClauses.forEach(val => {
229      val.types.forEach(v => {
230        heritageClausesArray.push(v);
231      });
232    });
233  });
234  defaultClassArray.forEach(value => {
235    value.heritageClauses.forEach(val => {
236      val.types.forEach(v => {
237        heritageClausesArray.push(v);
238      });
239    });
240  });
241  return heritageClausesArray;
242}
243
244interface MockFunctionElementEntity {
245  elementName: string,
246  type: string
247}
248