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 { ScriptTarget, SyntaxKind, createSourceFile } from 'typescript';
19import type { SourceFile } from 'typescript';
20import {
21  collectAllLegalImports,
22  dtsFileList,
23  firstCharacterToUppercase,
24  getAllFileNameList,
25  getApiInputPath
26} from '../common/commonUtils';
27import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration';
28import { getDefaultExportClassDeclaration } from '../declaration-node/sourceFileElementsAssemply';
29import type { SourceFileEntity } from '../declaration-node/sourceFileElementsAssemply';
30import { generateClassDeclaration } from './generateClassDeclaration';
31import { generateEnumDeclaration } from './generateEnumDeclaration';
32import { addToIndexArray } from './generateIndex';
33
34import { generateInterfaceDeclaration } from './generateInterfaceDeclaration';
35import { generateModuleDeclaration } from './generateModuleDeclaration';
36import { generateStaticFunction } from './generateStaticFunction';
37import { addToSystemIndexArray } from './generateSystemIndex';
38import { generateTypeAliasDeclaration } from './generateTypeAlias';
39import { generateCommonFunction } from './generateCommonFunction';
40import {
41  needToAddBrace,
42  hasExportDefaultKeyword,
43  MockFunctionElementEntity,
44  ReturnDataParams
45} from './generateCommonUtil';
46import { handleImportKit } from '../common/kitUtils';
47
48/**
49 * generate mock file string
50 * @param rootName absolute path to the mock file to be generated
51 * @param sourceFileEntity all node information in the file
52 * @param sourceFile file Text Information
53 * @param fileName file name
54 * @returns file mock text
55 */
56export function generateSourceFileElements(
57  rootName: string,
58  sourceFileEntity: SourceFileEntity,
59  sourceFile: SourceFile,
60  fileName: string
61): string {
62  let mockApi = '';
63  let mockFunctionElements: Array<MockFunctionElementEntity> = [];
64  const dependsSourceFileList = collectReferenceFiles(sourceFile);
65  const heritageClausesArray = getCurrentApiHeritageArray(sourceFileEntity, sourceFile);
66  const extraImport = [];
67
68  mockApi += importDeclarationsGenerate(sourceFileEntity, sourceFile, fileName, heritageClausesArray, dependsSourceFileList);
69  const enumDeclarationsData = enumDeclarationsGenerate(sourceFileEntity, mockFunctionElements);
70  mockApi += enumDeclarationsData.mockData;
71  mockFunctionElements = enumDeclarationsData.mockFunctionElements;
72
73  const typeAliasDeclarationsData = typeAliasDeclarationsGenerate(sourceFileEntity, mockFunctionElements, sourceFile, extraImport, mockApi);
74  mockApi += typeAliasDeclarationsData.mockData;
75  mockFunctionElements = typeAliasDeclarationsData.mockFunctionElements;
76
77  const interfaceDeclarationsData = interfaceDeclarationsGenerate(sourceFileEntity, mockFunctionElements, sourceFile, extraImport, mockApi);
78  mockApi += interfaceDeclarationsData.mockData;
79  mockFunctionElements = interfaceDeclarationsData.mockFunctionElements;
80
81  const classDeclarationsData = classDeclarationsGenerate(sourceFileEntity, mockFunctionElements, sourceFile, mockApi, fileName);
82  mockApi += classDeclarationsData.mockData;
83  mockFunctionElements = classDeclarationsData.mockFunctionElements;
84
85  mockApi += moduleDeclarationsGenerate(sourceFileEntity, sourceFile, mockApi, fileName, extraImport);
86
87  mockApi += functionDeclarationsGenerate(sourceFileEntity, sourceFile, mockApi);
88
89  mockApi += otherDeclarationsGenerate(rootName, sourceFileEntity, mockFunctionElements, sourceFile, mockApi, fileName, extraImport).mockData;
90
91  mockApi += handleExportDeclarations(sourceFileEntity);
92
93  mockApi = extraImport.join('') + mockApi;
94
95  mockApi = addExportDefaultExpression(mockApi);
96
97  return mockApi;
98}
99
100/**
101 * get import declarations generate
102 * @param sourceFileEntity all node information in the file
103 * @param sourceFile file Text Information
104 * @param fileName file name
105 * @param heritageClausesArray  heritage elements array data
106 * @param dependsSourceFileList reference Files data
107 * @returns string
108 */
109function importDeclarationsGenerate(
110  sourceFileEntity: SourceFileEntity,
111  sourceFile: SourceFile,
112  fileName: string,
113  heritageClausesArray: string[],
114  dependsSourceFileList: SourceFile[]
115): string {
116  let mockData = '';
117  if (sourceFileEntity.importDeclarations.length > 0) {
118    sourceFileEntity.importDeclarations.forEach(value => {
119      if (
120        sourceFile.fileName.endsWith('@ohos.arkui.UIContext.d.ts') &&
121        ['\'DatePickerDialogParam\'', '\'TimePickerDialogParam\'',
122          '\'textPickerDialogParam\'', '\'./@ohos.app.ability.common\''].includes(value.importPath)
123      ) {
124        mockData += '';
125      } else {
126        mockData += generateImportDeclaration(value, fileName, heritageClausesArray, sourceFile.fileName, dependsSourceFileList);
127      }
128    });
129  }
130  if (fileName === 'ohos_arkui_observer') {
131    mockData += 'const NavigationOperation = { PUSH: 1, POP: 2, REPLACE: 3 }\n';
132  }
133  if (fileName === 'navigation') {
134    mockData += 'const TextModifier = {}\n';
135  }
136  return mockData;
137}
138
139/**
140 * get enum declarations generate
141 * @param sourceFileEntity all node information in the file
142 * @param mockFunctionElements all function element entity
143 * @returns ReturnDataParams
144 */
145function enumDeclarationsGenerate(
146  sourceFileEntity: SourceFileEntity,
147  mockFunctionElements: Array<MockFunctionElementEntity>
148): ReturnDataParams {
149  const data: ReturnDataParams = {
150    mockData: '',
151    mockFunctionElements: mockFunctionElements
152  };
153  if (sourceFileEntity.enumDeclarations.length > 0) {
154    sourceFileEntity.enumDeclarations.forEach(value => {
155      data.mockData += generateEnumDeclaration('', value) + '\n';
156      data.mockFunctionElements.push({ elementName: value.enumName, type: 'enum' });
157    });
158  }
159  return data;
160}
161
162/**
163 * get typeAlias declarations generate
164 * @param sourceFileEntity all node information in the file
165 * @param mockFunctionElements all function element entity
166 * @param sourceFile file Text Information
167 * @param extraImport extra import data
168 * @param mockApi file mock text
169 * @returns ReturnDataParams
170 */
171function typeAliasDeclarationsGenerate(
172  sourceFileEntity: SourceFileEntity,
173  mockFunctionElements: Array<MockFunctionElementEntity>,
174  sourceFile: SourceFile,
175  extraImport: string[],
176  mockApi: string
177): ReturnDataParams {
178  const data: ReturnDataParams = {
179    mockData: '',
180    mockFunctionElements: mockFunctionElements
181  };
182  if (sourceFileEntity.typeAliasDeclarations.length > 0) {
183    sourceFileEntity.typeAliasDeclarations.forEach(value => {
184      data.mockData += generateTypeAliasDeclaration(value, false, sourceFile, extraImport, mockApi) + '\n';
185      data.mockFunctionElements.push({ elementName: value.typeAliasName, type: 'typeAlias' });
186    });
187  }
188  return data;
189}
190
191/**
192 * get interface declarations generate
193 * @param sourceFileEntity all node information in the file
194 * @param mockFunctionElements all function element entity
195 * @param sourceFile file Text Information
196 * @param extraImport Additional exported data
197 * @param mockApi  file mock into text
198 * @returns ReturnDataParams
199 */
200function interfaceDeclarationsGenerate(
201  entity: SourceFileEntity,
202  mockFunctionElements: Array<MockFunctionElementEntity>,
203  sourceFile: SourceFile,
204  extraImport: string[],
205  mockApi: string
206): ReturnDataParams {
207  const data: ReturnDataParams = {
208    mockData: '',
209    mockFunctionElements: mockFunctionElements
210  };
211  if (entity.interfaceDeclarations.length > 0) {
212    entity.interfaceDeclarations.forEach(value => {
213      data.mockData += generateInterfaceDeclaration(
214        value, sourceFile, true, mockApi, entity.interfaceDeclarations,
215        entity.importDeclarations, extraImport
216      ) + '\n';
217      data.mockFunctionElements.push({ elementName: value.interfaceName, type: 'interface' });
218    });
219  }
220  return data;
221}
222
223/**
224 * get class declarations generate
225 * @param sourceFileEntity all node information in the file
226 * @param mockFunctionElements all function element entity
227 * @param sourceFile file Text Information
228 * @param mockApi file mock text
229 * @param fileName file name
230 * @returns ReturnDataParams
231 */
232function classDeclarationsGenerate(
233  sourceFileEntity: SourceFileEntity,
234  mockFunctionElements: Array<MockFunctionElementEntity>,
235  sourceFile: SourceFile,
236  mockApi: string,
237  fileName: string
238): ReturnDataParams {
239  const data: ReturnDataParams = {
240    mockData: '',
241    mockFunctionElements: mockFunctionElements
242  };
243  if (sourceFileEntity.classDeclarations.length > 0) {
244    sourceFileEntity.classDeclarations.forEach(value => {
245      if (!fileName.startsWith('system_') && !value.exportModifiers.includes(SyntaxKind.DefaultKeyword)) {
246        data.mockData += generateClassDeclaration('', value, false, '', fileName, sourceFile, false, mockApi) + '\n';
247        data.mockFunctionElements.push({ elementName: value.className, type: 'class' });
248      }
249    });
250  }
251  return data;
252}
253
254/**
255 * get module declarations generate
256 * @param sourceFileEntity all node information in the file
257 * @param sourceFile file Text Information
258 * @param mockApi file mock text
259 * @param fileName  file name
260 * @param extraImport  extra import data
261 * @returns string
262 */
263function moduleDeclarationsGenerate(
264  sourceFileEntity: SourceFileEntity,
265  sourceFile: SourceFile,
266  mockApi: string,
267  fileName: string,
268  extraImport: string[]
269): string {
270  let mockData = '';
271  if (sourceFileEntity.moduleDeclarations.length > 0) {
272    sourceFileEntity.moduleDeclarations.forEach(value => {
273      mockData +=
274        generateModuleDeclaration(
275          value,
276          sourceFile,
277          fileName,
278          mockApi,
279          extraImport,
280          sourceFileEntity.importDeclarations
281        ) + '\n';
282    });
283  }
284  return mockData;
285}
286
287/**
288 * get function declarations generate
289 * @param sourceFileEntity  all node information in the file
290 * @param sourceFile file Text Information
291 * @param mockApi file mock text
292 * @returns string
293 */
294function functionDeclarationsGenerate(
295  sourceFileEntity: SourceFileEntity,
296  sourceFile: SourceFile,
297  mockApi: string
298): string {
299  let mockData = '';
300  if (sourceFileEntity.functionDeclarations.size > 0) {
301    Array.from(sourceFileEntity.functionDeclarations.keys()).forEach(key => {
302      mockData += generateCommonFunction(key, sourceFileEntity.functionDeclarations.get(key), sourceFile, mockApi, true) + '\n';
303    });
304  }
305  return mockData;
306}
307
308/**
309 * get other declarations generate
310 * @param rootName  absolute path to the mock file to be generated
311 * @param sourceFileEntity  all node information in the file
312 * @param mockFunctionElements  all function element entity
313 * @param sourceFile  file Text Information
314 * @param mockApi  file mock text
315 * @param fileName file name
316 * @returns ReturnDataParams
317 */
318function otherDeclarationsGenerate(
319  rootName: string,
320  sourceFileEntity: SourceFileEntity,
321  mockFunctionElements: Array<MockFunctionElementEntity>,
322  sourceFile: SourceFile,
323  mockApi: string,
324  fileName: string,
325  extraImport: string[]
326): ReturnDataParams {
327  const data: ReturnDataParams = {
328    mockData: '',
329    mockFunctionElements: []
330  };
331  if (
332    sourceFileEntity.moduleDeclarations.length === 0 &&
333    (fileName.startsWith('ohos_') || fileName.startsWith('system_') || fileName.startsWith('webgl'))
334  ) {
335    const moduleDeclarationsData = handleModuleDeclarationsNotExist(
336      rootName,
337      fileName,
338      sourceFile,
339      mockApi,
340      mockFunctionElements
341    );
342    data.mockData = moduleDeclarationsData.mockData;
343    data.mockFunctionElements = moduleDeclarationsData.mockFunctionElements;
344  } else {
345    const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
346    if (defaultExportClass.length > 0) {
347      const mockNameArr = fileName.split('_');
348      const mockName = mockNameArr[mockNameArr.length - 1];
349      defaultExportClass.forEach(value => {
350        data.mockData +=
351          generateClassDeclaration(
352            rootName,
353            value,
354            false,
355            mockName,
356            '',
357            sourceFile,
358            false,
359            mockApi,
360            extraImport,
361            sourceFileEntity.importDeclarations
362          ) + '\n';
363      });
364    }
365  }
366  return data;
367}
368
369/**
370 * handle Export Declarations
371 * @param sourceFileEntity all node information in the file
372 * @returns export text info
373 */
374function handleExportDeclarations(sourceFileEntity: SourceFileEntity): string {
375  let mockApi = '';
376  if (sourceFileEntity.exportDeclarations.length > 0) {
377    sourceFileEntity.exportDeclarations.forEach(value => {
378      const removeNoteRegx = /\/\*[\s\S]*?\*\//g;
379      const flieText = value.replace(removeNoteRegx, '').replace(/\n/g, '');
380      if (flieText.includes('export type {')) {
381        return;
382      }
383      if (flieText.includes('export {') && value.includes(' from ')) {
384        mockApi += `${flieText}\n`;
385      }
386      if (flieText.includes('export *')) {
387        mockApi += `${flieText}\n`;
388      }
389    });
390  }
391  return mockApi;
392}
393
394/**
395 * add extra export default expression
396 * @param mockApi file mock text
397 * @returns export text info
398 */
399function addExportDefaultExpression(mockApi: string): string {
400  const paramIndex = 2;
401  const reg = /export\sconst\s.*\s=/g;
402  const regDefault = /export\sdefault\s/g;
403  const regFunc = /export\sfunction\s/g;
404  const results = mockApi.match(reg);
405  const resultDefaults = mockApi.match(regDefault);
406  const resultFuncs = mockApi.match(regFunc);
407  if (results && results.length === 1 && !resultDefaults && !resultFuncs) {
408    const arr = results[0].split(' ');
409    const moduleName = arr[arr.length - paramIndex];
410    mockApi += `\nexport default ${moduleName};`;
411  }
412  return mockApi;
413}
414
415/**
416 * generate import definition
417 * @param importEntity import entity data
418 * @param sourceFileName file name
419 * @param heritageClausesArray heritage elements array data
420 * @param currentFilePath current file path
421 * @param dependsSourceFileList reference Files data
422 * @returns string
423 */
424export function generateImportDeclaration(
425  importEntity: ImportElementEntity,
426  sourceFileName: string,
427  heritageClausesArray: string[],
428  currentFilePath: string,
429  dependsSourceFileList: SourceFile[]
430): string {
431  const importEntities = handleImportKit(importEntity);
432  if (importEntities.length) {
433    return importEntities
434      .map(entity =>
435        generateImportDeclaration(entity, sourceFileName, heritageClausesArray, currentFilePath, dependsSourceFileList)
436      )
437      .join('\n');
438  }
439
440  const importDeclaration = referenctImport2ModuleImport(importEntity, currentFilePath, dependsSourceFileList);
441  if (importDeclaration) {
442    return importDeclaration;
443  }
444
445  const importPathSplit = importEntity.importPath.split('/');
446
447  let importPath = importPathSplit.slice(0, -1).join('/') + '/';
448  importPath += getImportPathName(importPathSplit);
449
450  let importElements = generateImportElements(importEntity, heritageClausesArray);
451  if (importElements === '{ mockWantAgent }' && importPath.includes('ohos_app_ability_wantAgent')) {
452    importElements = '{ mockWantAgent as mockAbilityWantAgent }';
453  }
454  const testPath = importPath.replace(/"/g, '').replace(/'/g, '').split('/');
455  if (!getAllFileNameList().has(testPath[testPath.length - 1]) && testPath[testPath.length - 1] !== 'ohos_application_want') {
456    return '';
457  }
458
459  const tmpImportPath = importPath.replace(/["']/g, '');
460  if (!tmpImportPath.startsWith('./') && !tmpImportPath.startsWith('../')) {
461    importPath = `'./${tmpImportPath}'`;
462  }
463  if (sourceFileName === 'tagSession' && tmpImportPath === './basic' || sourceFileName === 'notificationContent' &&
464    tmpImportPath === './ohos_multimedia_image') {
465    importPath = `'.${importPath.replace(/'/g, '')}'`;
466  }
467
468  if (sourceFileName === 'AbilityContext' && tmpImportPath === '../ohos_application_Ability') {
469    return '';
470  }
471  if (sourceFileName === 'Context' && tmpImportPath === './ApplicationContext') {
472    return 'import { mockEnvironmentCallback } from \'../ohos_app_ability_EnvironmentCallback\'\n';
473  }
474  if (!importElements.includes('{') && !importElements.includes('}') && needToAddBrace.includes(importElements)) {
475    importElements = `{ ${importElements} }`;
476  }
477  collectAllLegalImports(importElements);
478  return `import ${importElements} from ${importPath}\n`;
479}
480
481/**
482 * handle module declarations does it exist
483 * @param rootName absolute path to the mock file to be generated
484 * @param fileName file name
485 * @param sourceFile file Text Information
486 * @param mockApi file mock text
487 * @param mockFunctionElements all function element entity
488 * @returns ReturnDataParams
489 */
490function handleModuleDeclarationsNotExist(
491  rootName: string,
492  fileName: string,
493  sourceFile: SourceFile,
494  mockApi: string,
495  mockFunctionElements: Array<MockFunctionElementEntity>
496): ReturnDataParams {
497  const data: ReturnDataParams = {
498    mockData: '',
499    mockFunctionElements: mockFunctionElements
500  };
501  const mockNameArr = fileName.split('_');
502  const mockName = mockNameArr[mockNameArr.length - 1];
503  const defaultExportClass = getDefaultExportClassDeclaration(sourceFile);
504  defaultExportClass.forEach(value => {
505    data.mockData += generateClassDeclaration(rootName, value, false, mockName, '', sourceFile, false, mockApi) + '\n';
506    data.mockFunctionElements.push({ elementName: value.className, type: 'class' });
507  });
508  data.mockData += `export function mock${firstCharacterToUppercase(mockName)}() {\n`;
509  if (fileName.startsWith('system_')) {
510    addToSystemIndexArray({
511      filename: fileName,
512      mockFunctionName: `mock${firstCharacterToUppercase(mockName)}`
513    });
514    data.mockData += `global.systemplugin.${mockName} = {`;
515    const defaultClass = getDefaultExportClassDeclaration(sourceFile);
516    let staticMethodBody = '';
517    defaultClass.forEach(value => {
518      value.staticMethods.forEach(val => {
519        staticMethodBody += generateStaticFunction(val, true, sourceFile, mockApi);
520      });
521    });
522    data.mockData += staticMethodBody;
523    data.mockData += '}';
524  } else {
525    if (!fileName.startsWith('webgl')) {
526      addToIndexArray({ fileName: fileName, mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` });
527    }
528  }
529  data.mockData += `\nconst mockModule${firstCharacterToUppercase(mockName)} = {`;
530  data.mockFunctionElements.forEach(val => {
531    data.mockData += `${val.elementName}: ${val.elementName},`;
532  });
533  data.mockData += '}\n';
534  const isHaveExportDefault = hasExportDefaultKeyword(mockName, sourceFile);
535  const mockNameUppercase = firstCharacterToUppercase(mockName);
536  data.mockData += isHaveExportDefault
537    ? `return mockModule${mockNameUppercase}\n`
538    : `return mockModule${mockNameUppercase}.${mockNameUppercase}\n`;
539  data.mockData += '}';
540  return data;
541}
542
543/**
544 * adapter default export
545 * @param importName
546 * @returns boolean
547 */
548function checIsDefaultExportClass(importName: string): boolean {
549  const defaultExportClass = [
550    'Context',
551    'BaseContext',
552    'ExtensionContext',
553    'ApplicationContext',
554    'ExtensionAbility',
555    'Ability',
556    'UIExtensionAbility',
557    'UIExtensionContext'
558  ];
559  return defaultExportClass.includes(importName);
560}
561
562/**
563 * get heritage elements
564 * @param sourceFileEntity all node information in the file
565 * @param sourceFile file Text Information
566 * @returns string[]
567 */
568function getCurrentApiHeritageArray(sourceFileEntity: SourceFileEntity, sourceFile: SourceFile): string[] {
569  const heritageClausesArray = [];
570  const defaultClassArray = getDefaultExportClassDeclaration(sourceFile);
571  sourceFileEntity.classDeclarations.forEach(value => {
572    value.heritageClauses.forEach(val => {
573      val.types.forEach(v => {
574        heritageClausesArray.push(v);
575      });
576    });
577  });
578  defaultClassArray.forEach(value => {
579    value.heritageClauses.forEach(val => {
580      val.types.forEach(v => {
581        heritageClausesArray.push(v);
582      });
583    });
584  });
585  return heritageClausesArray;
586}
587
588/**
589 * collect reference Files
590 * @param sourceFile file Text Information
591 * @returns SourceFile[]
592 */
593function collectReferenceFiles(sourceFile: SourceFile): SourceFile[] {
594  const referenceElementTemplate = /\/\/\/\s*<reference\s+path="[^'"\[\]]+/g;
595  const referenceFiles: SourceFile[] = [];
596  const text = sourceFile.text;
597  const referenceElement = text.match(referenceElementTemplate);
598
599  referenceElement &&
600    referenceElement.forEach(element => {
601      const referenceRelatePath = element.split(/path=["']/g)[1];
602      const realReferenceFilePath = contentRelatePath2RealRelatePath(sourceFile.fileName, referenceRelatePath);
603      if (!realReferenceFilePath) {
604        return;
605      }
606
607      if (!fs.existsSync(realReferenceFilePath)) {
608        console.error(`Can not resolve file: ${realReferenceFilePath}`);
609        return;
610      }
611      const code = fs.readFileSync(realReferenceFilePath);
612      referenceFiles.push(createSourceFile(realReferenceFilePath, code.toString(), ScriptTarget.Latest));
613      !dtsFileList.includes(realReferenceFilePath) && dtsFileList.push(realReferenceFilePath);
614    });
615  return referenceFiles;
616}
617
618/**
619 * content relatePath to real relatePath
620 * @param currentFilePath file name
621 * @param contentReferenceRelatePath reference relate Path
622 * @returns string
623 */
624function contentRelatePath2RealRelatePath(currentFilePath: string, contentReferenceRelatePath: string): string {
625  const conmponentSourceFileTemplate = /component\/[^'"\/]+\.d\.ts/;
626  const currentFolderSourceFileTemplate = /\.\/[^\/]+\.d\.ts/;
627  const baseFileNameTemplate = /[^\/]+\.d\.ts/;
628
629  let realReferenceFilePath: string;
630  if (conmponentSourceFileTemplate.test(contentReferenceRelatePath)) {
631    const newRelateReferencePath = contentReferenceRelatePath.match(conmponentSourceFileTemplate)[0];
632    const referenceFileName = path.basename(newRelateReferencePath);
633    realReferenceFilePath = path.join(getApiInputPath(), '@internal', 'component', 'ets', referenceFileName);
634  } else if (currentFolderSourceFileTemplate.test(contentReferenceRelatePath)) {
635    const referenceFileName = path.basename(contentReferenceRelatePath);
636    realReferenceFilePath = currentFilePath.replace(baseFileNameTemplate, referenceFileName).replace(/\//g, path.sep);
637  } else {
638    console.error(`Can not find reference ${contentReferenceRelatePath} from ${currentFilePath}`);
639    return '';
640  }
641  return realReferenceFilePath;
642}
643
644/**
645 * referenct import to module import
646 * @param importEntity import entity data
647 * @param currentFilePath current file path data
648 * @param dependsSourceFileList reference Files data
649 * @returns string
650 */
651export function referenctImport2ModuleImport(
652  importEntity: ImportElementEntity,
653  currentFilePath: string,
654  dependsSourceFileList: SourceFile[]
655): string {
656  if (dependsSourceFileList.length && !importEntity.importPath.includes('.')) {
657    for (let i = 0; i < dependsSourceFileList.length; i++) {
658      if (dependsSourceFileList[i].text.includes(`declare module ${importEntity.importPath.replace(/'/g, '"')}`)) {
659        let relatePath = path
660          .relative(path.dirname(currentFilePath), dependsSourceFileList[i].fileName)
661          .replace(/\\/g, '/')
662          .replace(/.d.ts/g, '')
663          .replace(/.d.ets/g, '');
664        relatePath = (relatePath.startsWith('@internal/component') ? './' : '') + relatePath;
665        return `import ${importEntity.importElements} from '${relatePath}'\n`;
666      }
667    }
668  }
669  return '';
670}
671
672/**
673 * get import pathName
674 * @param importPathSplit import path split to array data
675 * @returns string
676 */
677function getImportPathName(importPathSplit: string[]): string {
678  let importPathName: string;
679  let fileName = importPathSplit[importPathSplit.length - 1];
680  if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ets')) {
681    fileName = fileName.split(/\.d\.e?ts/)[0];
682  }
683  if (fileName.includes('@')) {
684    importPathName = fileName.replace('@', '').replace(/\./g, '_');
685  } else {
686    importPathName = fileName.replace(/\./g, '_');
687  }
688  return importPathName;
689}
690
691/**
692 * get import pathName
693 * @param importEntity import entity data
694 * @param heritageClausesArray heritage elements array data
695 * @returns string
696 */
697function generateImportElements(importEntity: ImportElementEntity, heritageClausesArray: string[]): string {
698  let importElements = importEntity.importElements;
699  if (
700    !importElements.includes('{') &&
701    !importElements.includes('* as') &&
702    !heritageClausesArray.includes(importElements) &&
703    importEntity.importPath.includes('@ohos')
704  ) {
705    const tmpArr = importEntity.importPath.split('.');
706    const mockModuleName = firstCharacterToUppercase(tmpArr[tmpArr.length - 1].replace('"', '').replace('\'', ''));
707    if (importElements === 'observer' && importEntity.importPath.includes('@ohos.arkui.observer')) {
708      return `{ mockUiObserver as ${importElements}}`;
709    }
710    importElements = `{ mock${mockModuleName} }`;
711  } else {
712    // adapt no rules .d.ts
713    if (importElements.trim() === 'AccessibilityExtensionContext, { AccessibilityElement }') {
714      importElements = '{ AccessibilityExtensionContext, AccessibilityElement }';
715    } else if (importElements.trim() === '{ image }') {
716      importElements = '{ mockImage as image }';
717    }
718  }
719  return importElements;
720}
721