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 type { SourceFile } from 'typescript';
17import { SyntaxKind } from 'typescript';
18import { firstCharacterToUppercase, getClassNameSet, specialType } from '../common/commonUtils';
19import type { ReturnTypeEntity } from '../common/commonUtils';
20import { getImportDeclarationArray } from '../declaration-node/importAndExportDeclaration';
21import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration';
22import type { MethodEntity } from '../declaration-node/methodDeclaration';
23import type { FunctionEntity } from '../declaration-node/functionDeclaration';
24import type { MethodSignatureEntity } from '../declaration-node/methodSignatureDeclaration';
25
26const repeatReturn = `} else {
27        return {
28          done: true
29        };
30      }`;
31
32const repeatString = `index++;
33        return {
34          value: returnValue,
35          done: false
36        };
37      ${repeatReturn}
38    }
39  };`;
40
41const iteratorEntriesMock = `
42  let index = 0;
43  const IteratorEntriesMock = {
44    *[Symbol.iterator]() {
45      yield ['[PC Preview] unknown paramIterMock_K', '[PC Preview] unknown paramIterMock_V'];
46    },
47    next: () => {
48      if (index < 1) {
49        const returnValue = ['[PC Previwe] unknown paramIterMock_K', '[PC Previwe] unknown paramIterMock_V'];
50        ${repeatString}
51  return IteratorEntriesMock;
52`;
53
54const iteratorStringMock = `
55  let index = 0;
56  const IteratorStringMock = {
57    *[Symbol.iterator]() {
58      yield '[PC Preview] unknown string';
59    },
60    next: () => {
61      if (index < 1) {
62        const returnValue = '[PC Previwe] unknown string';
63        ${repeatString}
64  return IteratorStringMock;
65`;
66
67/**
68 * get warn console template
69 * @param interfaceNameOrClassName
70 * @param functionNameOrPropertyName
71 * @returns
72 */
73export function getWarnConsole(interfaceNameOrClassName: string, functionNameOrPropertyName: string): string {
74  return `console.warn('The ${interfaceNameOrClassName}.${functionNameOrPropertyName} interface in the Previewer is a mocked implementation and may behave differently than on a real device.');\n`;
75}
76
77function handlePromiseParams(returnType: ReturnTypeEntity): string {
78  const returnKindName = returnType.returnKindName.slice(0, returnType.returnKindName.length - 1).slice(8).trim();
79  let returnName = `return new Promise((resolve, reject) => {
80    resolve('[PC Preview] unknown type');
81  })`;
82  Object.keys(paramsTypeStart).forEach(key => {
83    if (returnKindName.startsWith(key)) {
84      const data = paramsTypeStart[key] === '[PC Preview] unknown type' ? `'${paramsTypeStart[key]}'` : `${paramsTypeStart[key]}`;
85      returnName = `return new Promise((resolve, reject) => {
86        resolve(${data});
87      })`;
88    }
89  });
90  return returnName;
91}
92
93/**
94 * generate return statement;
95 * @param returnType
96 * @param sourceFile
97 * @returns
98 */
99export function getReturnStatement(returnType: ReturnTypeEntity, sourceFile: SourceFile): string {
100  if (returnType.returnKind === SyntaxKind.TypeReference) {
101    return handleTypeReferenceReturnBody(returnType, sourceFile);
102  } else if (returnType.returnKind === SyntaxKind.UnionType) {
103    return handleUnionTypeReturnBody(returnType);
104  }
105  let returnName = returnType.returnKindName.trim();
106  let temp = true;
107  if (returnName.endsWith(']')) {
108    returnName = '[]';
109    temp = false;
110  } else {
111    Object.keys(paramsTypeStart).forEach(key => {
112      if (returnType.returnKindName.startsWith(key)) {
113        returnName = paramsTypeStart[key];
114        temp = false;
115      }
116    });
117  }
118  if (temp) {
119    return 'return \'[PC Preview] unknown type\'';
120  }
121  return `return ${returnName};`;
122}
123
124/**
125 * TypeReference return statement;
126 * @param returnType
127 * @param sourceFile
128 * @returns
129 */
130function handleTypeReferenceReturnBody(returnType: ReturnTypeEntity, sourceFile: SourceFile): string {
131  if (returnType.returnKindName.startsWith('Promise')) {
132    return handlePromiseParams(returnType);
133  }
134  const judgmentResult = judgmentReturnBody(returnType);
135  if (judgmentResult !== '') {
136    return judgmentResult;
137  }
138  const substepResult = substepReturnBody(returnType);
139  if (substepResult !== '') {
140    return substepResult;
141  }
142  if (returnType.returnKindName.includes('<')) {
143    return returnType.returnKindName.includes(',')
144      ? 'return {};'
145      : `return new ${returnType.returnKindName.split('<')[0]}()`;
146  } else {
147    return generateReturnType(returnType, sourceFile);
148  }
149}
150
151/**
152 * generate return type
153 * @param returnType
154 * @param sourceFile
155 * @returns
156 */
157function generateReturnType(returnType: ReturnTypeEntity, sourceFile: SourceFile): string {
158  if (getClassNameSet().has(returnType.returnKindName) && !specialType.includes(returnType.returnKindName)) {
159    return returnType.returnKindName === 'Want'
160      ? 'return mockWant().Want'
161      : `return new ${returnType.returnKindName}()`;
162  } else if (getClassNameSet().has(returnType.returnKindName) && specialType.includes(returnType.returnKindName)) {
163    return `return ${returnType.returnKindName}`;
164  } else if (propertyTypeWhiteList(returnType.returnKindName) === returnType.returnKindName) {
165    return `return ${getTheRealReferenceFromImport(sourceFile, returnType.returnKindName)}`;
166  } else {
167    return `return ${propertyTypeWhiteList(returnType.returnKindName)}`;
168  }
169}
170
171function judgmentReturnBody(returnType: ReturnTypeEntity): string {
172  if (returnType.returnKindName === 'T') {
173    return 'return \'[PC Preview] unknown type\'';
174  } else if (returnType.returnKindName === 'object' || returnType.returnKindName === 'Object') {
175    return 'return {}';
176  } else if (returnType.returnKindName === 'Function') {
177    return 'return \'[PC Preview] unknown type\'';
178  } else if (returnType.returnKindName === 'String' || returnType.returnKindName === 'string') {
179    return `return ${returnType.returnKindName}(...args)`;
180  } else if (returnType.returnKindName === 'number' || returnType.returnKindName === 'Number') {
181    return 'return 0';
182  } else if (returnType.returnKindName === 'boolean' || returnType.returnKindName === 'Boolean') {
183    return 'return false';
184  } else if (returnType.returnKindName === 'ArrayBuffer') {
185    return `return new ${returnType.returnKindName}(0)`;
186  } else {
187    return '';
188  }
189}
190
191function substepReturnBody(returnType: ReturnTypeEntity): string {
192  if (returnType.returnKindName.startsWith('Array')) {
193    if (returnType.returnKindName.includes('<') && returnType.returnKindName.includes('>')) {
194      return `return [${generateGenericTypeToMockValue(returnType.returnKindName)}]`;
195    } else {
196      return `return new ${returnType.returnKindName}()`;
197    }
198  } else if (returnType.returnKindName.startsWith('Readonly')) {
199    return `return ${returnType.returnKindName.split('<')[1].split('>')[0]}`;
200  } else if (checkIsGenericSymbol(returnType.returnKindName)) {
201    return `return '[PC Preview] unknown iterableiterator_${returnType.returnKindName}'`;
202  } else if (returnType.returnKindName.startsWith('Uint8Array')) {
203    return `return new ${returnType.returnKindName}()`;
204  } else if (returnType.returnKindName.startsWith('IterableIterator')) {
205    return returnType.returnKindName.includes(',') ? iteratorEntriesMock : iteratorStringMock;
206  } else if (returnType.returnKindName.includes('<T>')) {
207    const tmpReturn = returnType.returnKindName.split('<')[0];
208    return tmpReturn.startsWith('Array') ? 'return []' : 'return {}';
209  } else {
210    return '';
211  }
212}
213
214/**
215 * UnionType return statement;
216 * @param returnType
217 * @returns
218 */
219function handleUnionTypeReturnBody(returnType: ReturnTypeEntity): string {
220  const returnNames = returnType.returnKindName.split('|');
221  let returnName = returnNames[0];
222  for (let i = 0; i < returnNames.length; i++) {
223    if (!returnNames[i].includes('[]') && !returnNames[i].includes('<')) {
224      returnName = returnNames[i];
225      break;
226    }
227  }
228  if (returnName.trim() === 'void') {
229    return '';
230  }
231  if (getClassNameSet().has(returnName)) {
232    return `return new ${returnName}()`;
233  } else {
234    return `return ${getBaseReturnValue(returnName.trim())}`;
235  }
236}
237
238/**
239 * special property whitelist
240 * @param propertyTypeName
241 * @returns
242 */
243export function propertyTypeWhiteList(propertyTypeName: string): boolean | number | string {
244  const whiteList = ['GLboolean', 'GLuint', 'GLenum', 'GLint', 'NotificationFlags'];
245  if (whiteList.includes(propertyTypeName)) {
246    if (propertyTypeName === 'NotificationFlags' || propertyTypeName === 'GLenum') {
247      return `'[PC Preview] unknown ${propertyTypeName}'`;
248    } else if (propertyTypeName === 'GLboolean') {
249      return true;
250    } else {
251      return 0;
252    }
253  } else {
254    return propertyTypeName;
255  }
256}
257
258/**
259 * get basic return value
260 * @param value
261 * @returns
262 */
263export function getBaseReturnValue(value: string): string | number | boolean {
264  if (value === 'string') {
265    return '\'\'';
266  } else if (value === 'number') {
267    return 0;
268  } else if (value === 'boolean') {
269    return true;
270  } else if (value === 'Object' || value === 'object') {
271    return '{}';
272  } else if (checkIsGenericSymbol(value)) {
273    return '\'[PC Preview] unknown type\'';
274  } else if (value === 'WebGLActiveInfo') {
275    return '{size: \'[PC Preview] unknown GLint\', type: 0, name: \'[PC Preview] unknown name\'}';
276  } else {
277    return value;
278  }
279}
280
281/**
282 * get current sourceFile import data
283 * @param sourceFile
284 * @param typeName
285 * @returns
286 */
287export function getTheRealReferenceFromImport(sourceFile: SourceFile, typeName: string): string {
288  const importArray = getImportDeclarationArray(sourceFile);
289  let returnName = '';
290  let isFromImport = false;
291  let isOhos = false;
292  let mockMockName = '';
293  importArray.forEach(value => {
294    if (typeName.includes('.') && typeName.split('.')[0] === value.importElements) {
295      isFromImport = true;
296      if (value.importPath.includes('@ohos')) {
297        isOhos = true;
298      }
299      if (value.importElements.trim() === typeName.split('.')[0]) {
300        const tmpArr = value.importPath.split('.');
301        mockMockName = tmpArr[tmpArr.length - 1].replace(/["']/g, '');
302      }
303    }
304  });
305  if (isFromImport) {
306    const splitReturnKindName = typeName.split('.');
307    let left = '';
308    for (let i = 1; i < splitReturnKindName.length; i++) {
309      left += `.${splitReturnKindName[i]}`;
310    }
311    if (isOhos) {
312      if (mockMockName === 'observer') {
313        returnName = `${mockMockName}()${left}`;
314      } else {
315        returnName = `mock${firstCharacterToUppercase(mockMockName)}()${left}`;
316      }
317    }
318  } else {
319    returnName = getImportTypeAliasNameFromImportElements(importArray, typeName);
320  }
321  return returnName.split('<')[0];
322}
323
324/**
325 * get return type alias, for example: {Context as _Context} return _Context
326 * @param importElementEntity
327 * @param typeName
328 * @returns
329 */
330function getImportTypeAliasNameFromImportElements(
331  importElementEntity: ImportElementEntity[],
332  typeName: string
333): string {
334  for (let i = 0; i < importElementEntity.length; i++) {
335    if (importElementEntity[i].importElements.includes('_')) {
336      const importElements = importElementEntity[i].importElements.replace('{', '').replace('}', '').split(',');
337      typeName = getTypeName(importElements, typeName);
338    }
339  }
340  if (typeName === 'Want') {
341    typeName = 'mockWant().Want';
342  } else if (typeName === 'InputMethodExtensionContext') {
343    typeName = 'mockInputMethodExtensionContext().InputMethodExtensionContext';
344  } else if (typeName.includes('<') && typeName.includes(',')) {
345    typeName = '{}';
346  }
347  return typeName;
348}
349
350/**
351 * get type name , for example: {Context as _Context} return _Context
352 * @param importElements
353 * @param typeName
354 * @returns
355 */
356function getTypeName(importElements: string[], typeName: string): string {
357  for (let j = 0; j < importElements.length; j++) {
358    const element = importElements[j].trim();
359    if (!element) {
360      continue;
361    }
362    if (`_${typeName}` === element.trim()) {
363      return `_${typeName}`;
364    }
365    if (element.includes(' as ') && `_${typeName}` === element.split('as')[1].trim()) {
366      return `_${typeName}`;
367    }
368  }
369  return typeName;
370}
371
372/**
373 * check is generic symbol
374 * @param type
375 * @returns
376 */
377export function checkIsGenericSymbol(type: string): boolean {
378  const words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
379  return words.includes(type);
380}
381
382/**
383 * generate basic type default value
384 * @param kindName
385 * @returns
386 */
387export function generateGenericTypeToMockValue(kindName: string): string | number | boolean {
388  const genericTypeName = kindName.split('<')[1].split('>')[0];
389  if (genericTypeName === 'string') {
390    return '\'\'';
391  } else if (genericTypeName === 'number') {
392    return 0;
393  } else if (genericTypeName === 'boolean') {
394    return true;
395  } else if (genericTypeName === 'Object' || genericTypeName === 'object') {
396    return '{}';
397  } else {
398    return '';
399  }
400}
401
402export const paramsTypeStart = {
403  'void': '\'[PC Preview] unknown type\'',
404  'Array': '[]',
405  'Object': '{}',
406  'object': '{}',
407  '{': '{}',
408  'string': '""',
409  'String': '""',
410  'number': 0,
411  'Number': 0,
412  'boolean': false,
413  'Boolean': false,
414  'ArrayBuffer': 'new ArrayBuffer(0)',
415  'Uint8Array': 'new Uint8Array()',
416  'unknown': '\'[PC Preview] unknown type\''
417};
418
419const removeCallback = (str: string): { type: string; value: string } => {
420  const callbackParams = {
421    type: 'Callback',
422    value: ''
423  };
424  if (str.startsWith('Callback')) {
425    if (str.lastIndexOf('>') < str.lastIndexOf(' | ')) {
426      callbackParams.value = str.slice(0, str.lastIndexOf('>')).slice(9).trim();
427      callbackParams.type = 'Callback';
428    } else {
429      callbackParams.value = str.slice(0, str.length - 1).slice(9).trim();
430      callbackParams.type = 'Callback';
431    }
432  } else if (str.startsWith('AsyncCallback')) {
433    callbackParams.value = str.slice(0, str.length - 1).slice(14).trim();
434    callbackParams.type = 'AsyncCallback';
435  }
436  let isHaveAnglebrackets = false;
437  if (callbackParams.value.includes('<') && callbackParams.value.includes(',')) {
438    isHaveAnglebrackets = true;
439  }
440  if (callbackParams.value.includes(',') && !isHaveAnglebrackets) {
441    callbackParams.value = callbackParams.value.split(',')[0].trim();
442  }
443  return callbackParams;
444};
445
446const isInImportType = (mockApi: string, value: string): string => {
447  let hasDotFirstWorld = '';
448  if (value.includes('.')) {
449    hasDotFirstWorld = value.split('.')[0].trim();
450  }
451  if (hasDotFirstWorld && mockApi.includes(`import { mock${firstLetterWord(hasDotFirstWorld)} `)) {
452    return 'isHasDotImportMock';
453  }
454  if (hasDotFirstWorld && mockApi.includes(`import { ${firstLetterWord(hasDotFirstWorld)} `)) {
455    return 'isNoHasDotImportMock';
456  }
457  if (mockApi.includes(`import { mock${firstLetterWord(value)} `)) {
458    return 'isImportMock';
459  }
460  if (mockApi.includes(`import { ${value} `)) {
461    return 'isImport';
462  }
463  return 'noImport';
464};
465
466const firstLetterWord = (word: string): string => {
467  return word.slice(0, 1).toUpperCase() + word.slice(1);
468};
469
470const hasDotFirstWord = (str: string): string => {
471  return str.includes('.') ? str.split('.')[0] : str;
472};
473
474function callbackHasNoImportType(callbackParams: { type: string; value: string }): string {
475  let callbackData = '';
476  let paramsTypeHasType = true;
477  if (callbackParams.value.endsWith(']')) {
478    callbackData = '[]';
479  } else {
480    Object.keys(paramsTypeStart).forEach(item => {
481      if (callbackParams.value.startsWith(item)) {
482        callbackData = paramsTypeStart[item];
483        paramsTypeHasType = false;
484      }
485    });
486    if (paramsTypeHasType) {
487      callbackData = callbackParams.value;
488      if (callbackParams.value.includes('<')) {
489        callbackData = `${callbackParams.value.split('<')[0]}`;
490      }
491      if (callbackParams.value.includes('<') && callbackParams.value.includes(',')) {
492        callbackData = '{}';
493      }
494    }
495    if (callbackParams.value === 'Date') {
496      callbackData = 'new Date()';
497    }
498    if (callbackParams.value === 'Uint8Array') {
499      callbackData = 'new Uint8Array()';
500    }
501    if (callbackParams.value === 'T') {
502      callbackData = '[PC Preview] unknown type';
503    }
504  }
505  return callbackData;
506}
507
508/**
509 * get callback parameters data
510 * @returns data: parameters data: type: AsyncCallback or Callback
511 */
512const setCallbackData = (mockApi: string, paramTypeString: string): { data: string; type: string } => {
513  const callbackParams = removeCallback(paramTypeString);
514  let callbackData = '';
515  let importType = '';
516  if (callbackParams.value) {
517    importType = isInImportType(mockApi, callbackParams.value);
518  }
519  if (importType === 'isHasDotImportMock') {
520    const upperWord = firstLetterWord(callbackParams.value); // Image.PixelMap
521    const firstWord = hasDotFirstWord(upperWord); // Image
522    callbackData = `mock${firstWord}()${upperWord.slice(firstWord.length)}`;
523  } else if (importType === 'isNoHasDotImportMock') {
524    callbackData = callbackParams.value;
525  } else if (importType === 'isImportMock') {
526    callbackData = `mock${firstLetterWord(callbackParams.value)}()`;
527  } else if (importType === 'isImport') {
528    callbackData = callbackParams.value;
529  } else if (importType === 'noImport') {
530    callbackData = callbackHasNoImportType(callbackParams);
531  } else {
532    callbackData = '[PC Preview] unknown type';
533  }
534  return {
535    data: callbackData,
536    type: callbackParams.type
537  };
538};
539
540/**
541 * get callback statement
542 * @returns callback statement
543 */
544export function getCallbackStatement(mockApi: string, paramTypeString?: string): string {
545  let outPut = `if (args && typeof args[args.length - 1] === 'function') {
546    args[args.length - 1].call(this,`;
547  const callbackError = "{'code': '','data': '','name': '','message': '','stack': ''}";
548  let callbackDataParams = {
549    type: '',
550    data: '[PC Preview] unknown type'
551  };
552  if (paramTypeString) {
553    callbackDataParams = setCallbackData(mockApi, paramTypeString);
554  }
555  if (callbackDataParams?.type === 'AsyncCallback') {
556    outPut += ` ${callbackError},`;
557  }
558  outPut +=
559    callbackDataParams.data === '[PC Preview] unknown type'
560      ? ` '${callbackDataParams.data}');\n}`
561      : ` ${callbackDataParams.data});\n}`;
562  return outPut;
563}
564
565/**
566 * get callback statement
567 * @returns callback statement
568 */
569export function getOverloadedFunctionCallbackStatement(
570  entityArray: Array<FunctionEntity> | Array<MethodEntity> | Array<MethodSignatureEntity>,
571  sourceFile: SourceFile,
572  mockApi: string
573): string {
574  let overloadedCallbackBody = '';
575  entityArray.forEach(functionBody => {
576    let content = '';
577    let firstParamContent = '';
578    let callbackParamContent = '';
579    functionBody.args.forEach(arg => {
580      if (
581        arg.paramTypeString.startsWith("'") && arg.paramTypeString.endsWith("'") ||
582        arg.paramTypeString.startsWith('"') && arg.paramTypeString.endsWith('"')
583      ) {
584        const paramTypeStringArr = arg.paramTypeString.split('|');
585        firstParamContent += `if (args && [${paramTypeStringArr}].includes(args[0])) {\n`;
586      }
587      if (['callback', 'observercallback', 'listener', 'synccallback'].includes(arg.paramName.toLowerCase())) {
588        callbackParamContent += getCallbackBody(mockApi, arg.paramTypeString);
589      }
590    });
591    if (firstParamContent) {
592      content = `${firstParamContent}${callbackParamContent}\n}` + content;
593    } else {
594      content += callbackParamContent;
595    }
596    overloadedCallbackBody += content;
597  });
598  overloadedCallbackBody += '\n';
599  return overloadedCallbackBody;
600}
601
602/**
603 * get callback statement
604 * @returns callback statement
605 */
606function getCallbackBody(mockApi: string, paramString: string): string {
607  let bodyInfo = `if (args && typeof args[args.length - 1] === 'function') {
608    args[args.length - 1].call(this,`;
609  const callbackError = "{'code': '','data': '','name': '','message': '','stack': ''}";
610  if (paramString === 'ErrorCallback') {
611    bodyInfo += callbackError + ');\n}';
612    return bodyInfo;
613  }
614  let callbackDataParams = {
615    type: '',
616    data: '[PC Preview] unknown type'
617  };
618  if (paramString) {
619    callbackDataParams = setCallbackData(mockApi, paramString);
620  }
621  if (callbackDataParams?.type === 'AsyncCallback') {
622    bodyInfo += ` ${callbackError},`;
623  }
624  bodyInfo +=
625    callbackDataParams.data === '[PC Preview] unknown type'
626      ? ` '${callbackDataParams.data}');\n`
627      : ` ${callbackDataParams.data});\n`;
628  bodyInfo += '}';
629  return bodyInfo;
630}
631
632/**
633 * get iterator template string
634 * @param methodEntity
635 * @returns
636 */
637export function generateSymbolIterator(methodEntity: MethodEntity): string {
638  let iteratorMethod = '';
639  if (methodEntity.returnType.returnKindName.includes('<[')) {
640    iteratorMethod += `let index = 0;
641    const IteratorMock = {
642      next: () => {
643        if (index < 1) {
644          const returnValue = ['[PC Previwe] unknown iterableiterator_k', '[PC Previwe] unknown iterableiterator_v'];
645          index++;
646          return {
647            value: returnValue,
648            done: false
649          };
650        } else {
651          return {
652            done: true
653          };
654        }
655      }
656    };
657    return IteratorMock;`;
658  } else {
659    iteratorMethod += `let index = 0;
660    const IteratorMock = {
661      next: () => {
662        if (index < 1) {
663          index++;
664          return {
665            value: '[PC Preview] unknown any',
666            done: false
667          };
668        }
669        return {
670          done: true
671        };
672      }
673    };
674    return IteratorMock;`;
675  }
676
677  return iteratorMethod;
678}
679
680function handleReturnDataNoImportType(returnPromiseParams: string, returnType: ReturnTypeEntity): string {
681  let returnData = '';
682  if (returnPromiseParams.startsWith('[') || returnPromiseParams.endsWith(']')) {
683    returnData = '[]';
684  } else {
685    let paramsTypeHasType = true;
686    Object.keys(paramsTypeStart).forEach(item => {
687      if (returnPromiseParams.startsWith(item)) {
688        returnData = paramsTypeStart[item];
689        paramsTypeHasType = false;
690      }
691    });
692    if (paramsTypeHasType) {
693      returnData = returnPromiseParams;
694      if (returnPromiseParams.includes('<')) {
695        returnData = `${returnPromiseParams.split('<')[0]}`;
696      }
697      if (returnPromiseParams.includes('<') && returnPromiseParams.includes(',')) {
698        returnData = '{}';
699      }
700    }
701    if (returnPromiseParams === 'Date') {
702      returnData = 'new Date()';
703    }
704    if (returnPromiseParams === 'T') {
705      returnData = '"[PC Preview] unknown type"';
706    }
707    if (returnType.returnKindName.startsWith('Readonly')) {
708      returnData = `${returnType.returnKindName.split('<')[1].split('>')[0]}`;
709    }
710    if (checkIsGenericSymbol(returnType.returnKindName)) {
711      returnData = `'[PC Preview] unknown iterableiterator_${returnType.returnKindName}'`;
712    }
713  }
714  return returnData;
715}
716
717/**
718 * generate more function name return statement;
719 * @param isReturnPromise
720 * @param returnType
721 * @param sourceFile
722 * @param mockApi
723 * @returns
724 */
725export function getReturnData(
726  isCallBack: boolean,
727  isReturnPromise: boolean,
728  returnType: ReturnTypeEntity,
729  sourceFile: SourceFile,
730  mockApi: string
731): string {
732  // If the return value is an iterator IterableIterator, then iteratorEntriesMock is directly returned
733  if (returnType.returnKindName.startsWith('IterableIterator')) {
734    return returnType.returnKindName.includes(',') ? iteratorEntriesMock : iteratorStringMock;
735  }
736  // If it is a promise, intercept the content of x in promise<x>, which may have the following formats:
737  // fun(): y | Promise<y>、 fun(): Promise<x | y | z>、 fun(): Promise<x>、 fun(): Promise<x.y>
738  // If it is not a promise, the returned type may be x, x | y | z, x.y
739  let returnPromiseParams = returnType.returnKindName;
740  if (isReturnPromise) {
741    if (returnType.returnKind === SyntaxKind.UnionType) {
742      // fun(): y | Promise<y>
743      const returnNames = returnPromiseParams.split('|');
744      returnPromiseParams = loopReturnNames(returnNames, returnPromiseParams);
745    }
746    // At this point, obtain the values in these formats: Promise<x | y | z>, Promise<y>, Promise<x.y>, Promise<x>
747    const kindName = returnPromiseParams;
748    returnPromiseParams = kindName.slice(0, kindName.length - 1).slice(8).trim();
749  }
750  // At this point, the value type of param in promise<param>may be x, x | y | z, x.y
751  if (returnPromiseParams.includes('|')) {
752    returnPromiseParams = getSeparatorParam(returnPromiseParams);
753  }
754
755  // At this point, the possible types of promiseParam are x, x.y x [] Array<x>
756  // Check if it was imported
757  let returnData = '"[PC Preview] unknown type"';
758  const importType = isInImportType(mockApi, returnPromiseParams);
759  if (importType === 'isHasDotImportMock') {
760    const upperWord = firstLetterWord(returnPromiseParams); // Image.PixelMap
761    const firstWord = hasDotFirstWord(upperWord); // Image
762    returnData = `mock${firstWord}()${upperWord.slice(firstWord.length)}`;
763  } else if (importType === 'isNoHasDotImportMock') {
764    returnData = returnPromiseParams;
765  } else if (importType === 'isImportMock') {
766    returnData = `mock${firstLetterWord(returnPromiseParams)}()`;
767  } else if (importType === 'isImport') {
768    returnData = returnPromiseParams;
769  } else if (importType === 'noImport') {
770    returnData = handleReturnDataNoImportType(returnPromiseParams, returnType);
771  } else {
772    returnData = '"[PC Preview] unknown type"';
773  }
774  const data =
775    typeof returnData === 'string' && returnData.startsWith('[PC Preview] unknown')
776      ? `'${returnData}'`
777      : `${returnData}`;
778  if (isReturnPromise) {
779    return `
780        return new Promise((resolve, reject) => {
781          resolve(${data});
782        })
783      `;
784  } else {
785    return `return ${data}`;
786  }
787}
788
789/**
790 * loop ReturnNames
791 * @param returnNames
792 * @param returnPromiseParams
793 * @returns
794 */
795function loopReturnNames(returnNames: string[], returnPromiseParams: string): string {
796  for (let i = 0; i < returnNames.length; i++) {
797    if (returnNames[i].trim().startsWith('Promise<')) {
798      // Promise<y>
799      returnPromiseParams = returnNames[i].trim();
800      break;
801    }
802  }
803  return returnPromiseParams;
804}
805
806/**
807 *
808 * @param returnPromiseParams
809 * @returns
810 */
811function getSeparatorParam(returnPromiseParams: string): string {
812  let hasObj = '';
813  let hasArr = '';
814  let hasUint8Array = '';
815  let hasArrayBuffer = '';
816  let otherValue = '';
817  const paramsArr = returnPromiseParams.split('|');
818  for (let i = 0; i < paramsArr.length; i++) {
819    const param = paramsArr[i].trim();
820    if (param.startsWith('{') || param.startsWith('Object')) {
821      hasObj = '{}';
822    } else if (param.endsWith(']') || param.startsWith('[') || param.startsWith('Array<')) {
823      hasArr = '[]';
824    } else if (param.startsWith('Uint8Array')) {
825      hasUint8Array = 'Uint8Array';
826    } else if (param.startsWith('ArrayBuffer')) {
827      hasArrayBuffer = 'ArrayBuffer';
828    } else {
829      if (param !== null) {
830        otherValue = param;
831      }
832    }
833  }
834  if (hasObj) {
835    return hasObj;
836  }
837  if (hasArr) {
838    return hasArr;
839  }
840  if (hasUint8Array) {
841    return hasUint8Array;
842  }
843  if (hasArrayBuffer) {
844    return hasArrayBuffer;
845  }
846  return otherValue;
847}
848
849/**
850 *
851 * @param mockName string
852 * @param sourceFile SourceFile
853 * @returns boolean
854 */
855export function hasExportDefaultKeyword(mockName: string, sourceFile: SourceFile): boolean {
856  let fileContent = sourceFile.getText();
857  const removeNoteRegx = /\/\*[\s\S]*?\*\//g;
858  fileContent = fileContent.replace(removeNoteRegx, '');
859  if (
860    fileContent.includes(`export default class ${firstCharacterToUppercase(mockName)} `) ||
861    fileContent.includes(`export default interface ${firstCharacterToUppercase(mockName)} `) ||
862    fileContent.includes(`export default ${firstCharacterToUppercase(mockName)}`)
863  ) {
864    return false;
865  }
866  return true;
867}
868
869export const overloadedFunctionArr = ['on', 'off'];
870
871export const needToAddBrace = ['ViewData', 'AutoFillType'];
872
873export interface MockFunctionElementEntity {
874  elementName: string;
875  type: string;
876}
877export interface ReturnDataParams {
878  mockData: string;
879  mockFunctionElements: Array<MockFunctionElementEntity>;
880}
881