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