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