1/* 2 * Copyright (c) 2024 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 { 19 DECLARES, 20 IGNORE_REFERENCES, 21 importDeclarationFiles, 22 KeyValueTypes, 23 mockBufferMap, 24 MockedFileMap, 25 TSTypes, 26 specialOverloadedFunctionArr, 27 callbackError, ClassNotInEts, 28 undefinedTypes 29} from '../common/constants'; 30import { 31 Declare, 32 KeyValue, 33 ReferenceFindResult, 34 Members, 35 MockBuffer, 36 OverloadedFunctionType, 37 CallbackParamMockData 38} from '../types'; 39import { generateKeyValue } from '../common/commonUtils'; 40 41/** 42 * 生成文件内容 43 * @param mockBuffer mock信息 44 * @param members 文件根节点的成员 45 * @returns 46 */ 47export function generateContent(mockBuffer: MockBuffer, members: Members): string { 48 const membersContent: string[] = []; 49 Object.keys(members).forEach(memberKey => { 50 if (memberKey === 'default') { 51 return; 52 } 53 const keyValue = members[memberKey]; 54 if (!keyValue.isNeedMock) { 55 return; 56 } 57 if (keyValue.type === KeyValueTypes.IMPORT) { 58 return; 59 } 60 if (keyValue.isGlobalDeclare) { 61 membersContent.push(`export const ${memberKey}=global.${memberKey};`); 62 } else { 63 const memberBody = handleKeyValue(memberKey, keyValue, mockBuffer, [], keyValue, keyValue.property); 64 membersContent.push(`export const ${memberKey} = ${memberBody};`); 65 } 66 if (keyValue.isDefault) { 67 const exportDefaultStr = `export default ${keyValue.key}`; 68 membersContent.push(exportDefaultStr); 69 } 70 }); 71 return membersContent.join('\n'); 72} 73 74/** 75 * 处理KV节点 76 * @param key KV节点key值 77 * @param keyValue KV节点 78 * @param mockBuffer KV节点所在文件的mock信息 79 * @param kvPath KV节点路径 80 * @param rootKeyValue 仅次于FILE节点的根节点 81 * @param property KV节点的调用属性节点,如A.b, b节点为property 82 * @returns 83 */ 84function handleKeyValue( 85 key: string, 86 keyValue: KeyValue, 87 mockBuffer: MockBuffer, 88 kvPath: KeyValue[], 89 rootKeyValue: KeyValue, 90 property: KeyValue 91): string { 92 if (keyValue.value !== undefined) { 93 return keyValue.value; 94 } 95 if (new Set<KeyValueTypes>([ 96 KeyValueTypes.CLASS, 97 KeyValueTypes.MODULE, 98 KeyValueTypes.INTERFACE 99 ]).has(keyValue.type) && kvPath.includes(keyValue)) { 100 if (keyValue.isGlobalDeclare) { 101 return `global.${keyValue.key}`; 102 } 103 if (keyValue.parent.isGlobalDeclare) { 104 return `global.${keyValue.parent.key}_temp.${keyValue.key}`; 105 } 106 return 'this'; 107 } else { 108 kvPath = kvPath.concat([keyValue]); 109 } 110 111 return mockKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue, property); 112} 113 114/** 115 * 根据KV节点生成文件内容 116 * @param key KV节点key值 117 * @param keyValue KV节点 118 * @param mockBuffer KV节点所在文件的mock信息 119 * @param kvPath KV节点路径 120 * @param rootKeyValue 仅次于FILE节点的根节点 121 * @param property KV节点的调用属性节点,如A.b, b节点为property 122 * @returns 123 */ 124function mockKeyValue( 125 key: string, 126 keyValue: KeyValue, 127 mockBuffer: MockBuffer, 128 kvPath: KeyValue[], 129 rootKeyValue: KeyValue, 130 property: KeyValue 131): string { 132 let value: string; 133 switch (keyValue.type) { 134 case KeyValueTypes.CLASS: { 135 value = handleClassKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 136 break; 137 } 138 case KeyValueTypes.EXPORT: { 139 value = handleExportKeyValue(keyValue, mockBuffer); 140 break; 141 } 142 case KeyValueTypes.FILE: { 143 value = handleFileKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue, property); 144 break; 145 } 146 case KeyValueTypes.FUNCTION: { 147 value = handleFunctionKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue); 148 break; 149 } 150 case KeyValueTypes.IMPORT: { 151 value = handleImportKeyValue(keyValue, kvPath, rootKeyValue, property); 152 break; 153 } 154 case KeyValueTypes.INTERSECTION: { 155 value = handleIntersectionKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue); 156 break; 157 } 158 case KeyValueTypes.MODULE: { 159 value = handleModuleKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue, property); 160 break; 161 } 162 case KeyValueTypes.INTERFACE: { 163 value = handleInterfaceKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 164 break; 165 } 166 case KeyValueTypes.VALUE: { 167 value = handleValueKeyValue(keyValue); 168 break; 169 } 170 case KeyValueTypes.VARIABLE: { 171 value = handleVariableKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 172 break; 173 } 174 case KeyValueTypes.PROPERTY: { 175 value = handlePropertyKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 176 break; 177 } 178 case KeyValueTypes.REFERENCE: { 179 value = handleReferenceKeyValue(key, keyValue, mockBuffer, kvPath, rootKeyValue, property); 180 break; 181 } 182 case KeyValueTypes.ENUM: { 183 value = handleEnumKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 184 break; 185 } 186 case KeyValueTypes.EXPRESSION: { 187 value = handleExpressionKeyValue(keyValue, mockBuffer, kvPath, rootKeyValue); 188 break; 189 } 190 } 191 keyValue.value = value; 192 return value; 193} 194 195/** 196 * 处理class KV节点 197 * @param keyValue KV节点 198 * @param mockBuffer KV节点所在文件的mock信息 199 * @param kvPath KV节点路径 200 * @param rootKeyValue 仅次于FILE节点的根节点 201 * @returns 202 */ 203function handleClassKeyValue( 204 keyValue: KeyValue, 205 mockBuffer: MockBuffer, 206 kvPath: KeyValue[], 207 rootKeyValue: KeyValue 208): string { 209 const memberLines: string[] = []; 210 const dynamicProperties: string[] = ['this.isAutoMock=true']; 211 212 if (keyValue.heritage) { 213 handleHeritage(keyValue, mockBuffer, kvPath.concat([keyValue.heritage]), rootKeyValue); 214 } 215 216 Object.keys(keyValue.members).forEach(memberKey => { 217 const memberKeyValue = keyValue.members[memberKey]; 218 let elementName = memberKey; 219 220 if (memberKeyValue.type === KeyValueTypes.EXPRESSION) { 221 memberKeyValue.key = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 222 memberKeyValue.type = KeyValueTypes.FUNCTION; 223 memberKeyValue.value = undefined; 224 elementName = memberKeyValue.key; 225 } 226 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property) 227 .replace(/^function\*?/, ''); 228 229 if (memberKeyValue.type === KeyValueTypes.FUNCTION) { 230 if (memberKeyValue.members.IterableIterator) { 231 memberLines.push(`*${elementName}${value}`); 232 } else { 233 memberLines.push(`${memberKeyValue.isStatic ? 'static ' : ''}${elementName}${value}`); 234 } 235 } else { 236 if (memberKeyValue.isStatic) { 237 memberLines.push(`static get ${elementName}(){return ${value}}`); 238 } else { 239 dynamicProperties.push(`this.${elementName} = ${value}`); 240 } 241 } 242 }); 243 244 return `class {constructor() {\n${dynamicProperties.join(';\n')}\n}\n${memberLines.join(';\n')}\n}`; 245} 246 247/** 248 * 处理继承 249 * @param keyValue KV节点 250 * @param mockBuffer KV节点所在文件的mock信息 251 * @param kvPath KV节点路径 252 * @param rootKeyValue 仅次于FILE节点的根节点 253 * @returns 254 */ 255function handleHeritage( 256 keyValue: KeyValue, 257 mockBuffer: MockBuffer, 258 kvPath: KeyValue[], 259 rootKeyValue: KeyValue 260): void { 261 const keyValueInfo = findKeyValueDefined(keyValue.heritage.key, keyValue.heritage, mockBuffer, kvPath, rootKeyValue, keyValue.heritage.property); 262 const defKeyValue = keyValueInfo.keyValue; 263 const defMockBuffer = keyValueInfo.mockBuffer; 264 handleKeyValue(defKeyValue.key, defKeyValue, defMockBuffer, kvPath, rootKeyValue, defKeyValue.property); 265 266 Object.keys(defKeyValue.members).forEach(memberKey => { 267 const memberKeyValue = Object.assign({}, defKeyValue.members[memberKey]); 268 memberKeyValue.isMocked = false; 269 if (memberKeyValue.type === KeyValueTypes.EXPRESSION) { 270 memberKeyValue.key = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 271 memberKeyValue.type = KeyValueTypes.PROPERTY; 272 } 273 if (keyValue.members[memberKeyValue.key]) { 274 return; 275 } 276 keyValue.members[memberKeyValue.key] = memberKeyValue; 277 }); 278} 279 280/** 281 * 处理export KV节点 282 * @param keyValue KV节点 283 * @param mockBuffer KV节点所在文件的mock信息 284 * @returns 285 */ 286function handleExportKeyValue( 287 keyValue: KeyValue, 288 mockBuffer: MockBuffer 289): string { 290 return `export * from './${path.relative(path.dirname(mockBuffer.mockedFilePath), keyValue.key)}';`; 291} 292 293/** 294 * 处理file KV节点 295 * @param key KV节点key值 296 * @param keyValue KV节点 297 * @param mockBuffer KV节点所在文件的mock信息 298 * @param kvPath KV节点路径 299 * @param rootKeyValue 仅次于FILE节点的根节点 300 * @param property KV节点的调用属性节点,如A.b, b节点为property 301 * @returns 302 */ 303function handleFileKeyValue( 304 key: string, 305 keyValue: KeyValue, 306 mockBuffer: MockBuffer, 307 kvPath: KeyValue[], 308 rootKeyValue: KeyValue, 309 property: KeyValue 310): string { 311 if (property) { 312 const propertyKeyValue = keyValue.members[property.key]; 313 if (propertyKeyValue) { 314 return handleKeyValue(property.key, propertyKeyValue, mockBuffer, kvPath, rootKeyValue, property); 315 } else { 316 console.warn(`Not found ${property.key} from ${key} in file ${mockBuffer.rawFilePath}`); 317 } 318 } 319 return '\'\''; 320} 321 322/** 323 * 处理function KV节点 324 * @param key 325 * @param keyValue KV节点 326 * @param mockBuffer KV节点所在文件的mock信息 327 * @param kvPath KV节点路径 328 * @param rootKeyValue 仅次于FILE节点的根节点 329 * @returns 330 */ 331function handleFunctionKeyValue( 332 key: string, 333 keyValue: KeyValue, 334 mockBuffer: MockBuffer, 335 kvPath: KeyValue[], 336 rootKeyValue: KeyValue 337): string { 338 const memberKey = 'IterableIterator'; 339 const memberKeyValue = keyValue.members[memberKey]; 340 if (memberKeyValue) { 341 return handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 342 } 343 344 const sameFuncList: KeyValue[] = []; 345 sameFuncList.push(keyValue); 346 347 keyValue.sameName.forEach(sameFunction => { 348 sameFuncList.push(sameFunction); 349 }); 350 return handleSameFunctions(key, sameFuncList, mockBuffer, kvPath, rootKeyValue); 351} 352 353/** 354 * 处理import KV节点 355 * @param keyValue KV节点 356 * @param kvPath KV节点路径 357 * @param rootKeyValue 仅次于FILE节点的根节点 358 * @param property KV节点的调用属性节点,如A.b, b节点为property 359 * @returns 360 */ 361function handleImportKeyValue( 362 keyValue: KeyValue, 363 kvPath: KeyValue[], 364 rootKeyValue: KeyValue, 365 property: KeyValue 366): string { 367 const importedMockBuffer = mockBufferMap.get(MockedFileMap.get(keyValue.importedModulePath)); 368 const importedRootKeyValue = importedMockBuffer.contents; 369 if (keyValue.isImportDefault) { 370 const defaultKeyValue = importedRootKeyValue.members.default; 371 if (!defaultKeyValue) { 372 console.warn(`The file: ${importedMockBuffer.rawFilePath} does not contain the default export.`); 373 return `'The file: ${importedMockBuffer.rawFilePath} does not contain the default export.'`; 374 } 375 return handleKeyValue(defaultKeyValue.key, defaultKeyValue, importedMockBuffer, kvPath, rootKeyValue, property); 376 } 377 378 const targetKeyValue = importedRootKeyValue.members[keyValue.rawName ?? keyValue.key]; 379 if (!targetKeyValue) { 380 console.warn(`The ${keyValue.rawName ?? keyValue.key} does not export from ${MockedFileMap.get(keyValue.importedModulePath)}.`); 381 return '"Unknown type"'; 382 } 383 return handleKeyValue(targetKeyValue.key, targetKeyValue, importedMockBuffer, kvPath, rootKeyValue, property); 384} 385 386/** 387 * 处理intersection KV节点 388 * @param key KV节点key值 389 * @param keyValue KV节点 390 * @param mockBuffer KV节点所在文件的mock信息 391 * @param kvPath KV节点路径 392 * @param rootKeyValue 仅次于FILE节点的根节点 393 * @returns 394 */ 395function handleIntersectionKeyValue( 396 key: string, 397 keyValue: KeyValue, 398 mockBuffer: MockBuffer, 399 kvPath: KeyValue[], 400 rootKeyValue: KeyValue 401): string { 402 const params: string[] = []; 403 Object.keys(keyValue.methodParams).forEach(param => { 404 const paramKeyValue = keyValue.methodParams[param]; 405 const value = handleKeyValue(param, paramKeyValue, mockBuffer, kvPath, rootKeyValue, paramKeyValue.property); 406 // 因为rollup在编译时,会将this编译成undefined,导致有运行时报错,因此需要打个补丁 407 params.push(`(${value}) || {}`); 408 }); 409 return `${key}(${params.join(', ')})`; 410} 411 412/** 413 * 处理module KV节点 414 * @param key KV节点key值 415 * @param keyValue KV节点 416 * @param mockBuffer KV节点所在文件的mock信息 417 * @param kvPath KV节点路径 418 * @param rootKeyValue 仅次于FILE节点的根节点 419 * @param property KV节点的调用属性节点,如A.b, b节点为property 420 * @returns 421 */ 422function handleModuleKeyValue( 423 key: string, 424 keyValue: KeyValue, 425 mockBuffer: MockBuffer, 426 kvPath: KeyValue[], 427 rootKeyValue: KeyValue, 428 property?: KeyValue 429): string { 430 const memberLines: string[] = []; 431 if (property) { 432 const propertyKeyValue = keyValue.members[property.key]; 433 if (propertyKeyValue) { 434 return handleKeyValue(property.key, propertyKeyValue, mockBuffer, kvPath, rootKeyValue, propertyKeyValue.property); 435 } else { 436 console.warn(`Not found ${property.key} from ${key} in file ${mockBuffer.rawFilePath}`); 437 } 438 } 439 Object.keys(keyValue.members).forEach(memberKey => { 440 const memberKeyValue = keyValue.members[memberKey]; 441 if (!keyValue.isGlobalDeclare && !memberKeyValue.isNeedMock) { 442 return; 443 } 444 const value = `${memberKeyValue.key}: ${handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property)}`; 445 memberLines.push(value); 446 }); 447 return `{\n${memberLines.join(',\n')}\n}`; 448} 449 450/** 451 * 处理interface KV节点 452 * @param keyValue KV节点 453 * @param mockBuffer KV节点所在文件的mock信息 454 * @param kvPath KV节点路径 455 * @param rootKeyValue 仅次于FILE节点的根节点 456 * @returns 457 */ 458function handleInterfaceKeyValue( 459 keyValue: KeyValue, 460 mockBuffer: MockBuffer, 461 kvPath: KeyValue[], 462 rootKeyValue: KeyValue 463): string { 464 const memberLines: string[] = ['isAutoMock: true']; 465 466 if (keyValue.heritage) { 467 handleHeritage(keyValue, mockBuffer, kvPath.concat([keyValue.heritage]), rootKeyValue); 468 } 469 Object.keys(keyValue.members).forEach(memberKey => { 470 const memberKeyValue = keyValue.members[memberKey]; 471 const value = `${memberKeyValue.key}: ${handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property)}`; 472 memberLines.push(value); 473 }); 474 return `{\n${memberLines.join(',\n')}\n}`; 475} 476 477/** 478 * 处理value KV节点 479 * @param keyValue KV节点 480 * @returns 481 */ 482function handleValueKeyValue( 483 keyValue: KeyValue 484): string { 485 return keyValue.key; 486} 487 488/** 489 * 处理variable KV节点 490 * @param keyValue KV节点 491 * @param mockBuffer KV节点所在文件的mock信息 492 * @param kvPath KV节点路径 493 * @param rootKeyValue 仅次于FILE节点的根节点 494 * @returns 495 */ 496function handleVariableKeyValue( 497 keyValue: KeyValue, 498 mockBuffer: MockBuffer, 499 kvPath: KeyValue[], 500 rootKeyValue: KeyValue 501): string { 502 const memberLines: string[] = []; 503 Object.keys(keyValue.members).forEach(memberKey => { 504 const memberKeyValue = keyValue.members[memberKey]; 505 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 506 memberLines.push(value); 507 }); 508 return memberLines.join(',\n'); 509} 510 511/** 512 * 查找reference KV节点定义位置 513 * @param key KV节点key值 514 * @param targetKeyValue 需要查找的reference KV节点 515 * @param mockBuffer KV节点所在文件的mock信息 516 * @param kvPath KV节点路径 517 * @param rootKeyValue 仅次于FILE节点的根节点 518 * @param property KV节点的调用属性节点,如A.b, b节点为property 519 * @returns 520 */ 521function findKeyValueDefined( 522 key: string, 523 targetKeyValue: KeyValue, 524 mockBuffer: MockBuffer, 525 kvPath: KeyValue[], 526 rootKeyValue: KeyValue, 527 property: KeyValue 528): ReferenceFindResult { 529 let keyValueInfo: ReferenceFindResult; 530 531 // 在js库中找 532 keyValueInfo = findInLibs(key, targetKeyValue, mockBuffer, kvPath, rootKeyValue); 533 if (keyValueInfo) { 534 return keyValueInfo; 535 } 536 537 // 在当前文件中找 538 keyValueInfo = findInCurrentFile(key, targetKeyValue, targetKeyValue.parent, mockBuffer, kvPath, rootKeyValue, property); 539 if (keyValueInfo) { 540 return keyValueInfo; 541 } 542 543 // 在全局定义中找 544 keyValueInfo = findInDeclares(key); 545 if (keyValueInfo) { 546 return keyValueInfo; 547 } 548 549 // 在TS内置类型中找 550 keyValueInfo = findTSTypes(key, targetKeyValue, mockBuffer, kvPath, rootKeyValue); 551 if (keyValueInfo) { 552 return keyValueInfo; 553 } 554 555 // 在所有文件中找 556 keyValueInfo = findInAllFiles(key, property); 557 if (keyValueInfo) { 558 return keyValueInfo; 559 } 560 561 // 全局都未找到定义的类型,做特殊处理 562 if (Object.keys(undefinedTypes).includes(key)) { 563 const keyValue: KeyValue = generateKeyValue(key, KeyValueTypes.VALUE); 564 keyValue.value = undefinedTypes[key]; 565 return { keyValue, mockBuffer }; 566 } 567 568 const keyValuePath: string[] = getKeyValuePath(targetKeyValue); 569 const fromFilePath = MockedFileMap.get(keyValuePath[0]).replace(/\\/, '/'); 570 const value: string = `'Cannot find type definition for ${keyValuePath.slice(1).join('->')} from file ${fromFilePath}'`; 571 console.warn(value); 572 const keyValue: KeyValue = generateKeyValue(key, KeyValueTypes.VALUE); 573 keyValue.value = value; 574 return { keyValue, mockBuffer }; 575} 576 577/** 578 * 在 typescript 内置类型库中查找类型定义 579 * @param key KV节点key值 580 * @param targetKeyValue 需要查找的reference KV节点 581 * @param mockBuffer KV节点所在文件的mock信息 582 * @param kvPath KV节点路径 583 * @param rootKeyValue 仅次于FILE节点的根节点 584 * @returns 585 */ 586function findTSTypes( 587 key: string, 588 targetKeyValue: KeyValue, 589 mockBuffer: MockBuffer, 590 kvPath: KeyValue[], 591 rootKeyValue: KeyValue 592): ReferenceFindResult { 593 const paramsContent: string[] = []; 594 Object.keys(targetKeyValue.typeParameters).forEach(typeParameter => { 595 const typeParameterKeyValue: KeyValue = targetKeyValue.typeParameters[typeParameter]; 596 const paramContent: string = handleKeyValue(typeParameter, typeParameterKeyValue, mockBuffer, kvPath, rootKeyValue, typeParameterKeyValue.property); 597 paramsContent.push(paramContent); 598 }); 599 if (!TSTypes[key]) { 600 return undefined; 601 } 602 const keyValue: KeyValue = generateKeyValue(key, KeyValueTypes.VALUE); 603 keyValue.value = TSTypes[key](paramsContent.join(', ')); 604 return { keyValue, mockBuffer }; 605} 606 607/** 608 * 处理函数参数 609 * @param params 参数成员 610 * @param mockBuffer 当前文件mock信息 611 * @param kvPath KV节点路径 612 * @param rootKeyValue 仅次于FILE节点的根节点 613 * @returns 614 */ 615function handleParams( 616 params: Members, 617 mockBuffer: MockBuffer, 618 kvPath: KeyValue[], 619 rootKeyValue: KeyValue 620): string { 621 const contents: string[] = []; 622 Object.keys(params).forEach(key => { 623 const paramKeyValue = params[key]; 624 contents.push(handleKeyValue(paramKeyValue.key, paramKeyValue, mockBuffer, kvPath, rootKeyValue, paramKeyValue.property)); 625 }); 626 return contents.join(', '); 627} 628 629/** 630 * 在 typescript 内置类型库中查找类型定义 631 * @param key KV节点key值 632 * @param targetKeyValue 需要查找的reference KV节点 633 * @param mockBuffer KV节点所在文件的mock信息 634 * @param kvPath KV节点路径 635 * @param rootKeyValue 仅次于FILE节点的根节点 636 * @returns 637 */ 638function findInLibs( 639 key: string, 640 targetKeyValue: KeyValue, 641 mockBuffer: MockBuffer, 642 kvPath: KeyValue[], 643 rootKeyValue: KeyValue 644): ReferenceFindResult { 645 if (key === 'globalThis') { 646 const globalThisKeyValue = generateKeyValue(key, KeyValueTypes.VALUE); 647 return { keyValue: globalThisKeyValue, mockBuffer }; 648 } 649 if (ClassNotInEts.has(key) || !global[key]) { 650 return undefined; 651 } 652 if (key === 'Symbol') { 653 return { 654 keyValue: generateKeyValue('[Symbol.iterator]', KeyValueTypes.VALUE), 655 mockBuffer 656 }; 657 } 658 switch (typeof global[key]) { 659 case 'bigint': { 660 break; 661 } 662 case 'boolean': { 663 break; 664 } 665 case 'function': { 666 return findInLibFunction(key, targetKeyValue, mockBuffer, kvPath, rootKeyValue); 667 } 668 case 'number': { 669 break; 670 } 671 case 'object': { 672 break; 673 } 674 case 'string': { 675 break; 676 } 677 case 'symbol': { 678 break; 679 } 680 case 'undefined': { 681 break; 682 } 683 } 684 return undefined; 685} 686 687/** 688 * 在库函数中查找类型定义 689 * @param key KV节点key值 690 * @param targetKeyValue 需要查找的reference KV节点 691 * @param mockBuffer KV节点所在文件的mock信息 692 * @param kvPath KV节点路径 693 * @param rootKeyValue 仅次于FILE节点的根节点 694 * @returns 695 */ 696function findInLibFunction( 697 key: string, 698 targetKeyValue: KeyValue, 699 mockBuffer: MockBuffer, 700 kvPath: KeyValue[], 701 rootKeyValue: KeyValue 702): ReferenceFindResult { 703 const params = handleParams(targetKeyValue.methodParams, mockBuffer, kvPath, rootKeyValue); 704 let value: string; 705 // 判断是否是函数 706 if (typeof global[key].constructor === 'function') { 707 value = `new ${key}(${params})`; 708 } else { 709 value = `${key}(${params})`; 710 } 711 return { 712 keyValue: generateKeyValue(value, KeyValueTypes.VALUE), 713 mockBuffer 714 }; 715} 716 717/** 718 * 在当前文件中查找类型定义 719 * @param key KV节点key值 720 * @param targetKeyValue 需要查找的reference KV节点 721 * @param parent 父节点 722 * @param mockBuffer KV节点所在文件的mock信息 723 * @param kvPath KV节点路径 724 * @param rootKeyValue 仅次于FILE节点的根节点 725 * @param property KV节点的调用属性节点,如A.b, b节点为property 726 * @returns 727 */ 728function findInCurrentFile( 729 key: string, 730 targetKeyValue: KeyValue, 731 parent: KeyValue, 732 mockBuffer: MockBuffer, 733 kvPath: KeyValue[], 734 rootKeyValue: KeyValue, 735 property: KeyValue 736): ReferenceFindResult { 737 if (!parent) { 738 return undefined as ReferenceFindResult; 739 } 740 if (parent.typeParameters[key] && parent.typeParameters[key] !== targetKeyValue) { 741 return { keyValue: parent.typeParameters[key], mockBuffer } as ReferenceFindResult; 742 } 743 const foundKeyValue = parent.members[key]; 744 if ( 745 foundKeyValue && 746 foundKeyValue !== targetKeyValue && 747 ( 748 parent.type === KeyValueTypes.ENUM || foundKeyValue.type !== KeyValueTypes.PROPERTY 749 ) 750 ) { 751 if (foundKeyValue.type === KeyValueTypes.IMPORT) { 752 return findDefFromImport(foundKeyValue, mockBuffer, rootKeyValue, property) as ReferenceFindResult; 753 } 754 const defKeyValue = findProperty(foundKeyValue, property); 755 if (defKeyValue) { 756 return {keyValue: defKeyValue, mockBuffer} as ReferenceFindResult; 757 } 758 } 759 return findInCurrentFile(key, targetKeyValue, parent.parent, mockBuffer, kvPath, rootKeyValue, property) as ReferenceFindResult; 760} 761 762/** 763 * 在全局声明中查找类型定义 764 * @param key KV节点key值 765 * @returns 766 */ 767function findInDeclares( 768 key: string 769): ReferenceFindResult { 770 if (DECLARES[key]) { 771 const mockBuffer = mockBufferMap.get(MockedFileMap.get(DECLARES[key].from)); 772 return { 773 keyValue: DECLARES[key].keyValue, 774 mockBuffer, 775 isGlobalDeclaration: path.basename(mockBuffer.rawFilePath).startsWith('@') 776 } as ReferenceFindResult; 777 } else { 778 return undefined as ReferenceFindResult; 779 } 780} 781 782/** 783 * 在所有文件中查找类型定义 784 * @param key KV节点key值 785 * @param property KV节点的调用属性节点,如A.b, b节点为property 786 * @returns 787 */ 788function findInAllFiles( 789 key: string, 790 property?: KeyValue 791): ReferenceFindResult { 792 for (const definedMockBuffer of mockBufferMap.values()) { 793 const members = definedMockBuffer.contents.members; 794 if (members[key]) { 795 const defKeyValue = findProperty(members[key], property); 796 return { keyValue: defKeyValue, mockBuffer: definedMockBuffer } as ReferenceFindResult; 797 } 798 } 799 return undefined as ReferenceFindResult; 800} 801 802/** 803 * 获取节点在当前文件的路径 804 * 以递归的方式逐级向上获取所有祖先节点的key 805 * @param keyValue KV节点 806 * @param paths 节点路径 807 * @returns 808 */ 809function getKeyValuePath(keyValue: KeyValue, paths = []): string[] { 810 if (!keyValue) { 811 return paths; 812 } 813 paths.unshift(keyValue.key); 814 return getKeyValuePath(keyValue.parent, paths); 815} 816 817/** 818 * 处理同名函数 819 * @param key 820 * @param sameFuncList 同名函数列表 821 * @param mockBuffer 当前文件的mock信息 822 * @param kvPath KV节点路径 823 * @param rootKeyValue 仅次于FILE节点的根节点 824 * @returns 825 */ 826function handleSameFunctions( 827 key: string, 828 sameFuncList: KeyValue[], 829 mockBuffer: MockBuffer, 830 kvPath: KeyValue[], 831 rootKeyValue: KeyValue 832): string { 833 const functionName = sameFuncList[0].key; 834 if (sameFuncList.length >= 2) { 835 return handleOverloadedFunction(key, sameFuncList, mockBuffer, kvPath, rootKeyValue, functionName); 836 } else { 837 return handleSingleFunction(key, sameFuncList, mockBuffer, kvPath, rootKeyValue, functionName); 838 } 839} 840 841/** 842 * 处理重载函数 843 * @param key 844 * @param sameFuncList 同名函数列表 845 * @param mockBuffer 当前文件的mock信息 846 * @param kvPath KV节点路径 847 * @param rootKeyValue 仅次于FILE节点的根节点 848 * @param functionName 849 * @returns 850 */ 851function handleOverloadedFunction( 852 key: string, 853 sameFuncList: KeyValue[], 854 mockBuffer: MockBuffer, 855 kvPath: KeyValue[], 856 rootKeyValue: KeyValue, 857 functionName: string 858): string { 859 const func = sameFuncList.find(func => func.members.Promise); 860 if (!func) { 861 return handleSingleFunction(key, sameFuncList, mockBuffer, kvPath, rootKeyValue, functionName); 862 } 863 const promiseTypes = func.members.Promise; 864 const memberLines: string[] = []; 865 const returnData: string[] = []; 866 const paramIndex: number = 1; 867 Object.keys(promiseTypes.typeParameters).forEach(memberKey => { 868 const memberKeyValue = promiseTypes.typeParameters[memberKey]; 869 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 870 memberLines.push(`const p${paramIndex} = ${value}`); 871 returnData.push(`p${paramIndex}`); 872 }); 873 const isSpecial = specialOverloadedFunctionArr.includes(functionName); 874 const paramMockData = handleFunParamMockData(sameFuncList, mockBuffer, kvPath, rootKeyValue, isSpecial, 'multiple', returnData); 875 const isGetAccessor = key.startsWith('get ' + functionName); 876 const isSetAccessor = key.startsWith('set ' + functionName); 877 return `function (${isGetAccessor ? '' : `${isSetAccessor ? 'args' : '...args'}`}) { 878 ${isGetAccessor || isSetAccessor ? '' : `console.warn(ts.replace('{{}}', '${func.key}'));`} 879 ${memberLines.join(';\n')}${paramMockData ? '\n' + paramMockData : ''} 880 return new Promise(function (resolve, reject) { 881 resolve(${returnData.join(', ')}); 882 }); 883 }`; 884} 885 886function handleFunParamMockData( 887 funcList: KeyValue[], 888 mockBuffer: MockBuffer, 889 kvPath: KeyValue[], 890 rootKeyValue: KeyValue, 891 isSpecial: boolean, 892 funType: OverloadedFunctionType, 893 returnData?: string[] 894): string { 895 let paramMockData = ''; 896 for (let i = 0; i < funcList.length; i++) { 897 const funInfo = funcList[i]; 898 const { paramName, callBackParams, isAsyncCallback } = getCallbackMockData(funInfo, mockBuffer, kvPath, rootKeyValue, isSpecial); 899 if (!callBackParams.length) { 900 continue; 901 } 902 const returnInfo = isSpecial 903 ? callBackParams.join(', ') 904 : funType === 'single' 905 ? callBackParams.join(', ') 906 : returnData?.join(', '); 907 const data = `if (args && typeof args[args.length - 1] === 'function') { 908 args[args.length - 1].call(this, ${isAsyncCallback ? callbackError : ''}${returnInfo}); 909 }`; 910 const info = (paramName ? `if(args && ['${paramName}'].includes(args[0])){\n` : '') + data + (paramName ? '}\n' : ''); 911 if (funType === 'single' || isSpecial || !isSpecial && !paramMockData) { 912 paramMockData += info; 913 } 914 } 915 return paramMockData; 916} 917 918function getCallbackMockData( 919 funInfo: KeyValue, 920 mockBuffer: MockBuffer, 921 kvPath: KeyValue[], 922 rootKeyValue: KeyValue, 923 isSpecial: boolean 924): CallbackParamMockData { 925 let paramName = ''; 926 let isAsyncCallback = true; 927 let callBackParams: string[] = []; 928 Object.keys(funInfo.methodParams).forEach(key => { 929 const paramInfo = funInfo.methodParams[key]; 930 const callbackData = handleCallbackParamMockData(key, paramInfo, callBackParams, paramName, isAsyncCallback, mockBuffer, kvPath, rootKeyValue, isSpecial); 931 paramName = callbackData.paramName; 932 isAsyncCallback = callbackData.isAsyncCallback; 933 callBackParams = callbackData.callBackParams; 934 }); 935 return { 936 paramName, 937 isAsyncCallback, 938 callBackParams 939 }; 940} 941 942function handleCallbackParamMockData( 943 key: string, 944 paramInfo: KeyValue, 945 callBackParams: string[], 946 paramName: string, 947 isAsyncCallback: boolean, 948 mockBuffer: MockBuffer, 949 kvPath: KeyValue[], 950 rootKeyValue: KeyValue, 951 isSpecial: boolean 952): CallbackParamMockData { 953 if (key === 'callback') { 954 let callbackInfo: KeyValue; 955 if (paramInfo.members.Callback) { 956 isAsyncCallback = false; 957 callbackInfo = paramInfo.members.Callback; 958 } 959 if (paramInfo.members.AsyncCallback) { 960 isAsyncCallback = true; 961 callbackInfo = paramInfo.members.AsyncCallback; 962 } 963 callbackInfo && Object.keys(callbackInfo.typeParameters).forEach(memberKey => { 964 const memberKeyValue = callbackInfo.typeParameters[memberKey]; 965 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 966 callBackParams.push(value); 967 }); 968 } 969 if (key === 'type' && isSpecial) { 970 Object.keys(paramInfo.members).forEach(memberKey => { 971 if (!paramName) { 972 paramName = memberKey; 973 } 974 }); 975 } 976 977 return { 978 isAsyncCallback, 979 callBackParams, 980 paramName 981 }; 982} 983 984/** 985 * 拼接property 986 * 通过递归方式,逐层将调用的属性拼接起来 987 * 如:将{A:{key:A, property: {key:b, property: {key: c}}}} 988 * 拼接成:A.b.c 989 * @param property 调用的属性 990 * @returns 991 */ 992function concatProperty(property?: KeyValue): string { 993 if (!property) { 994 return ''; 995 } 996 return `.${property.key}${concatProperty(property.property)}`; 997} 998 999/** 1000 * 获取获取定义KV节点的最后一个property 1001 * 1002 * @param keyValue 定义的KV节点 1003 * @param property 调用KV节点的属性 1004 * @returns 1005 */ 1006function findProperty(keyValue: KeyValue, property?: KeyValue): KeyValue { 1007 const keyValueKey = keyValue.key; 1008 while (property && keyValue) { 1009 keyValue = keyValue.members[property.key]; 1010 property = property.property; 1011 } 1012 if (!keyValue && property) { 1013 throw new Error(`未能在${keyValueKey}下找到${property.key}子孙属性`); 1014 } 1015 return keyValue; 1016} 1017 1018/** 1019 * 处理property KV节点 1020 * @param keyValue KV节点 1021 * @param mockBuffer KV节点所在文件的mock信息 1022 * @param kvPath KV节点路径 1023 * @param rootKeyValue 仅次于FILE节点的根节点 1024 * @returns 1025 */ 1026function handlePropertyKeyValue( 1027 keyValue: KeyValue, 1028 mockBuffer: MockBuffer, 1029 kvPath: KeyValue[], 1030 rootKeyValue: KeyValue 1031): string { 1032 const memberLines: string[] = []; 1033 Object.keys(keyValue.members).forEach(memberKey => { 1034 const memberKeyValue = keyValue.members[memberKey]; 1035 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1036 memberLines.push(value); 1037 }); 1038 return memberLines.join(','); 1039} 1040 1041/** 1042 * 处理reference KV节点 1043 * @param key KV节点key值 1044 * @param keyValue KV节点 1045 * @param mockBuffer KV节点所在文件的mock信息 1046 * @param kvPath KV节点路径 1047 * @param rootKeyValue 仅次于FILE节点的根节点 1048 * @param property KV节点的调用属性节点,如A.b, b节点为property 1049 * @returns 1050 */ 1051function handleReferenceKeyValue( 1052 key: string, 1053 keyValue: KeyValue, 1054 mockBuffer: MockBuffer, 1055 kvPath: KeyValue[], 1056 rootKeyValue: KeyValue, 1057 property: KeyValue 1058): string { 1059 if (IGNORE_REFERENCES.has(key)) { 1060 const memberLines: string[] = []; 1061 Object.keys(keyValue.typeParameters).forEach(memberKey => { 1062 const memberKeyValue = keyValue.typeParameters[memberKey]; 1063 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1064 memberLines.push(value); 1065 }); 1066 return memberLines.join(',\n'); 1067 } 1068 const keyValueInfo = findKeyValueDefined(key, keyValue, mockBuffer, kvPath, rootKeyValue, keyValue.property); 1069 let value: string; 1070 1071 if (keyValueInfo.isGlobalDeclaration) { 1072 const properties = concatProperty(keyValue.property); 1073 value = `global.${keyValueInfo.keyValue.key}${properties}`; 1074 const dependKeyValue = property ? keyValueInfo.keyValue.members[property.key] : keyValueInfo.keyValue; 1075 !dependKeyValue.isMocked && rootKeyValue.dependOnGlobals.add(dependKeyValue); 1076 } else { 1077 value = handleKeyValue(keyValueInfo.keyValue.key, keyValueInfo.keyValue, keyValueInfo.mockBuffer, kvPath, rootKeyValue, property); 1078 } 1079 1080 if (value !== 'this') { 1081 switch (keyValueInfo.keyValue.type) { 1082 case KeyValueTypes.CLASS: { 1083 value = value.startsWith('global.') ? value : `new (${value})()`; 1084 break; 1085 } 1086 case KeyValueTypes.ENUM: { 1087 if (keyValue.parent.type !== KeyValueTypes.VARIABLE) { 1088 const firstMemberKey = Object.keys(keyValueInfo.keyValue.members)[0]; 1089 const firstMemberKeyValue = keyValueInfo.keyValue.members[firstMemberKey]; 1090 value = handleKeyValue(firstMemberKey, firstMemberKeyValue, keyValueInfo.mockBuffer, kvPath, rootKeyValue, firstMemberKeyValue.property); 1091 } 1092 break; 1093 } 1094 } 1095 } 1096 1097 return value; 1098} 1099 1100/** 1101 * 处理enum KV节点 1102 * @param keyValue KV节点 1103 * @param mockBuffer KV节点所在文件的mock信息 1104 * @param kvPath KV节点路径 1105 * @param rootKeyValue 仅次于FILE节点的根节点 1106 * @returns 1107 */ 1108function handleEnumKeyValue( 1109 keyValue: KeyValue, 1110 mockBuffer: MockBuffer, 1111 kvPath: KeyValue[], 1112 rootKeyValue: KeyValue 1113): string { 1114 const memberLines: string[] = ['isAutoMock: 0']; 1115 Object.keys(keyValue.members).forEach(memberKey => { 1116 const memberKeyValue = keyValue.members[memberKey]; 1117 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1118 memberLines.push(`${memberKey}: ${value}`); 1119 }); 1120 return `{${memberLines.join(',\n')}}`; 1121} 1122 1123/** 1124 * 处理expression KV节点 1125 * @param keyValue KV节点 1126 * @param mockBuffer KV节点所在文件的mock信息 1127 * @param kvPath KV节点路径 1128 * @param rootKeyValue 仅次于FILE节点的根节点 1129 * @returns 1130 */ 1131function handleExpressionKeyValue( 1132 keyValue: KeyValue, 1133 mockBuffer: MockBuffer, 1134 kvPath: KeyValue[], 1135 rootKeyValue: KeyValue 1136): string { 1137 const elements = keyValue.operateElements; 1138 return elements.map(element => { 1139 return handleKeyValue(element.key, element, mockBuffer, kvPath, rootKeyValue, element.property); 1140 }).join(' '); 1141} 1142 1143/** 1144 * 处理非重载函数 1145 * @param key 1146 * @param sameFuncList 1147 * @param mockBuffer KV节点所在文件的mock信息 1148 * @param kvPath KV节点路径 1149 * @param rootKeyValue 仅次于FILE节点的根节点 1150 * @param functionName 1151 * @returns 1152 */ 1153function handleSingleFunction( 1154 key: string, 1155 sameFuncList: KeyValue[], 1156 mockBuffer: MockBuffer, 1157 kvPath: KeyValue[], 1158 rootKeyValue: KeyValue, 1159 functionName: string 1160): string { 1161 const funcKeyValue = sameFuncList[0]; 1162 const memberLines: string[] = []; 1163 Object.keys(funcKeyValue.members).forEach(memberKey => { 1164 const memberKeyValue = funcKeyValue.members[memberKey]; 1165 const value = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1166 memberLines.push(value); 1167 }); 1168 const isSpecial = specialOverloadedFunctionArr.includes(functionName); 1169 const funcList = isSpecial ? sameFuncList : [sameFuncList[0]]; 1170 const paramMockData = handleFunParamMockData(funcList, mockBuffer, kvPath, rootKeyValue, isSpecial, 'single'); 1171 1172 const isGetAccessor = key.startsWith('get ' + functionName); 1173 const isSetAccessor = key.startsWith('set ' + functionName); 1174 const returnStr = funcKeyValue.members.Promise && funcKeyValue.members.Promise.type === KeyValueTypes.REFERENCE ? 1175 `return new Promise(function (resolve, reject) { 1176 resolve(${memberLines.join(',')}); 1177 })` : 1178 `return ${memberLines.join(',')}`; 1179 1180 return `function (${isGetAccessor ? '' : `${isSetAccessor ? 'args' : '...args'}`}) { 1181 ${isGetAccessor || isSetAccessor ? '' : `console.warn(ts.replace('{{}}', '${funcKeyValue.key}'));`} 1182 ${paramMockData ?? ''}${returnStr} 1183 }`; 1184} 1185 1186/** 1187 * 获取KV节点的最后一个property的类型 1188 * @param keyValue KV节点 1189 * @param property 调用的属性 1190 * @returns 1191 */ 1192function getKeyValueType(keyValue: KeyValue, property?: KeyValue): KeyValueTypes { 1193 while (property) { 1194 keyValue = keyValue.members[property.key]; 1195 property = property.property; 1196 } 1197 return keyValue.type; 1198} 1199 1200/** 1201 * 处理所有全局声明的KV节点 1202 * @param outMockJsFileDir mock文件输出路径 1203 */ 1204export function handleDeclares(outMockJsFileDir: string): void { 1205 const declarations: string[] = []; 1206 const mockedDeclarations: Set<string> = new Set(); 1207 Object.keys(DECLARES).forEach(key => { 1208 const keyValue = DECLARES[key].keyValue; 1209 switch (keyValue.type) { 1210 case KeyValueTypes.CLASS: { 1211 declarations.push(`global.${key}_temp = class {constructor(){this.isAutoMock=true}};\nglobal.${key} = global.${key} || global.${key}_temp;`); 1212 break; 1213 } 1214 case KeyValueTypes.INTERFACE: 1215 case KeyValueTypes.MODULE: { 1216 declarations.push(`global.${key}_temp = {isAutoMock: true};\nglobal.${key} = global.${key} || global.${key}_temp;`); 1217 } 1218 } 1219 }); 1220 1221 Object.keys(DECLARES).forEach(key => { 1222 handleDeclare(DECLARES[key], declarations, mockedDeclarations); 1223 }); 1224 1225 const INTERVAL = 100; 1226 for (let counter = 0; counter < declarations.length; counter += INTERVAL) { 1227 const index = Math.floor(counter / INTERVAL) + 1; 1228 const filePath = path.join(outMockJsFileDir, `globalDeclarations${index}.js`); 1229 importDeclarationFiles.push(`import * as globalDeclarations${index} from './globalDeclarations${index}';`); 1230 const content = declarations.slice(counter, counter + INTERVAL).join('\n'); 1231 fs.writeFileSync(filePath, content); 1232 } 1233} 1234 1235/** 1236 * 处理全局声明的KV节点 1237 * @param declaration 全局声明的KV节点 1238 * @param declarations 所有全局声明的KV节点 1239 * @param mockedDeclarations 已mock的全局声明的KV节点的集合,避免重复mock 1240 * @param member 不为undefined时,只mock这个member节点 1241 * @returns 1242 */ 1243function handleDeclare( 1244 declaration: Declare, 1245 declarations: string[], 1246 mockedDeclarations: Set<string>, 1247 member?: KeyValue 1248): void { 1249 if (member?.isMocked) { 1250 return; 1251 } 1252 const keyValue = declaration.keyValue; 1253 const key = keyValue.key; 1254 const mockBuffer = mockBufferMap.get(MockedFileMap.get(declaration.from)); 1255 1256 const values: string[] = []; 1257 switch (keyValue.type) { 1258 case KeyValueTypes.FUNCTION: { 1259 if (!mockedDeclarations.has(key)) { 1260 const functionBody = handleKeyValue(key, keyValue, mockBuffer, [], keyValue, keyValue.property); 1261 const value = `global.${key} = global.${key} || (${functionBody});`; 1262 values.push(value); 1263 mockedDeclarations.add(key); 1264 } 1265 break; 1266 } 1267 case KeyValueTypes.CLASS: { 1268 handleGlobalClass(keyValue, mockBuffer, values, [keyValue], member); 1269 break; 1270 } 1271 case KeyValueTypes.MODULE: { 1272 handleGlobalModule(keyValue, mockBuffer, values, [keyValue], member); 1273 break; 1274 } 1275 case KeyValueTypes.INTERFACE: { 1276 handleGlobalInterface(keyValue, mockBuffer, values, [keyValue], member); 1277 break; 1278 } 1279 default: { 1280 if (!mockedDeclarations.has(key)) { 1281 const value = `global.${key} = global.${key} || (${handleKeyValue(key, keyValue, mockBuffer, [], keyValue, keyValue.property)});`; 1282 values.push(value); 1283 mockedDeclarations.add(key); 1284 } 1285 break; 1286 } 1287 } 1288 handleDependOnGlobals(keyValue, declarations, mockedDeclarations); 1289 Array.prototype.push.apply(declarations, values); 1290} 1291 1292/** 1293 * 处理KV节点用到的全局节点 1294 * @param keyValue KV节点 1295 * @param declarations 已mock的文本内容 1296 * @param mockedDeclarations 已mock的全局节点的集合 1297 * @returns 1298 */ 1299function handleDependOnGlobals( 1300 keyValue: KeyValue, 1301 declarations: string[], 1302 mockedDeclarations: Set<string> 1303): void { 1304 if (keyValue.type === KeyValueTypes.FUNCTION) { 1305 return; 1306 } 1307 keyValue.dependOnGlobals.forEach(dependKeyValue => { 1308 if (dependKeyValue.isGlobalDeclare) { 1309 handleDeclare(DECLARES[dependKeyValue.key], declarations, mockedDeclarations); 1310 } else if (dependKeyValue.parent.isGlobalDeclare) { 1311 handleDeclare(DECLARES[dependKeyValue.parent.key], declarations, mockedDeclarations, dependKeyValue); 1312 } else { 1313 throw new Error(`${keyValue.key}非全局节点。`); 1314 } 1315 }); 1316} 1317 1318/** 1319 * 处理全局class KV节点 1320 * @param keyValue class类型的KV节点 1321 * @param mockBuffer mock信息 1322 * @param declarations 已mock的文本内容 1323 * @param kvPath KV节点路径 1324 * @param member 不为undefined时,只mock这个member节点 1325 * @returns 1326 */ 1327function handleGlobalClass( 1328 keyValue: KeyValue, 1329 mockBuffer: MockBuffer, 1330 declarations: string[], 1331 kvPath: KeyValue[], 1332 member?: KeyValue 1333): void { 1334 if (member) { 1335 if (!member.isMocked) { 1336 const memberValue = handleKeyValue(member.key, member, mockBuffer, kvPath, keyValue, member.property); 1337 const functionKeyword = member.type === KeyValueTypes.FUNCTION ? 'function' : ''; 1338 const prototypeStr = member.isStatic ? '' : '.prototype'; 1339 const value = `global.${keyValue.key}_temp${prototypeStr}.${member.key} = ${functionKeyword}${memberValue};`; 1340 member.isMocked = true; 1341 declarations.push(value); 1342 } 1343 return; 1344 } 1345 if (keyValue.heritage) { 1346 handleHeritage(keyValue, mockBuffer, kvPath.concat(keyValue), keyValue); 1347 } 1348 1349 Object.keys(keyValue.members).forEach( 1350 memberKey => handleClassMembers(memberKey, keyValue, mockBuffer, kvPath, declarations) 1351 ); 1352 // 处理同名declare 1353 keyValue.sameDeclares.forEach(sameDeclare => { 1354 const sameKeyValue = sameDeclare.keyValue; 1355 const sameMockBuffer = mockBufferMap.get(MockedFileMap.get(sameDeclare.from)); 1356 Object.keys(sameKeyValue.members).forEach( 1357 memberKey => handleClassMembers(memberKey, sameKeyValue, sameMockBuffer, kvPath, declarations) 1358 ); 1359 }); 1360} 1361 1362/** 1363 * 处理class KV节点的属性和方法 1364 * @param memberKey 属性或方法的名称 1365 * @param parent 父级class KV节点 1366 * @param mockBuffer 所属文件的mock信息 1367 * @param kvPath KV节点路径 1368 * @param declarations 已mock的文本内容 1369 * @returns 1370 */ 1371function handleClassMembers( 1372 memberKey: string, 1373 parent: KeyValue, 1374 mockBuffer: MockBuffer, 1375 kvPath: KeyValue[], 1376 declarations: string[] 1377): void { 1378 const memberKeyValue = parent.members[memberKey]; 1379 if (memberKeyValue.isMocked) { 1380 return; 1381 } 1382 let elementName = `.${memberKey}`; 1383 if (memberKeyValue.type === KeyValueTypes.EXPRESSION) { 1384 memberKeyValue.key = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, parent, memberKeyValue.property); 1385 memberKeyValue.type = KeyValueTypes.FUNCTION; 1386 memberKeyValue.value = undefined; 1387 elementName = memberKeyValue.key; 1388 } 1389 1390 const memberValue = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, parent, memberKeyValue.property); 1391 let value: string; 1392 if (memberKeyValue.type === KeyValueTypes.FUNCTION) { 1393 value = handleClassMethod(memberKey, memberKeyValue, parent, mockBuffer, kvPath, elementName, memberValue); 1394 } else { 1395 value = `global.${parent.key}_temp${memberKeyValue.isStatic ? '' : '.prototype'}${elementName} = ${memberValue};`; 1396 } 1397 if (!memberKeyValue.isStatic && memberKeyValue.sameName.some(sameKeyValue => sameKeyValue.isStatic)) { 1398 value += `\nglobal.${parent.key}_temp${elementName} = global.${parent.key}_temp.prototype${elementName}`; 1399 } 1400 memberKeyValue.isMocked = true; 1401 declarations.push(value); 1402} 1403 1404/** 1405 * 处理全局module KV节点 1406 * @param keyValue KV节点 1407 * @param mockBuffer 所属文件的mock信息 1408 * @param declarations 已mock的文本内容 1409 * @param kvPath KV节点路径 1410 * @param member 不为undefined时,只mock这个member节点 1411 * @returns 1412 */ 1413function handleGlobalModule( 1414 keyValue: KeyValue, 1415 mockBuffer: MockBuffer, 1416 declarations: string[], 1417 kvPath: KeyValue[], 1418 member?: KeyValue 1419): void { 1420 return handleGlobalModuleOrInterface(keyValue, mockBuffer, declarations, kvPath, member); 1421} 1422 1423/** 1424 * 处理全局module或interface KV节点 1425 * @param keyValue KV节点 1426 * @param mockBuffer 所属文件的mock信息 1427 * @param declarations 已mock的文本内容 1428 * @param kvPath KV节点路径 1429 * @param member 不为undefined时,只mock这个member节点 1430 * @returns 1431 */ 1432function handleGlobalModuleOrInterface( 1433 keyValue: KeyValue, 1434 mockBuffer: MockBuffer, 1435 declarations: string[], 1436 kvPath: KeyValue[], 1437 member?: KeyValue 1438): void { 1439 if (member) { 1440 if (!member.isMocked) { 1441 const memberKey = member.key; 1442 const memberValue = handleKeyValue(memberKey, member, mockBuffer, kvPath, keyValue, member.property); 1443 const value = `global.${keyValue.key}_temp.${memberKey} = ${memberValue};`; 1444 member.isMocked = true; 1445 declarations.push(value); 1446 } 1447 return; 1448 } 1449 Object.keys(keyValue.members).forEach(memberKey => handleModuleOrInterfaceMember(memberKey, keyValue, mockBuffer, kvPath, declarations, keyValue)); 1450 // 处理同名declare 1451 keyValue.sameDeclares.forEach(sameDeclare => { 1452 const sameKeyValue = sameDeclare.keyValue; 1453 const sameMockBuffer = mockBufferMap.get(MockedFileMap.get(sameDeclare.from)); 1454 const needHandleTypes = new Set([KeyValueTypes.CLASS, KeyValueTypes.INTERFACE, KeyValueTypes.MODULE]); 1455 if (!needHandleTypes.has(sameKeyValue.type) && path.basename(sameMockBuffer.rawFilePath).startsWith('@')) { 1456 return; 1457 } 1458 Object.keys(sameKeyValue.members).forEach( 1459 memberKey => handleModuleOrInterfaceMember(memberKey, sameKeyValue, sameMockBuffer, kvPath, declarations, keyValue) 1460 ); 1461 }); 1462} 1463 1464/** 1465 * 处理module和interface的成员 1466 * @param memberKey 成员名称 1467 * @param parent 成员父节点 1468 * @param mockBuffer 当前文件mock信息 1469 * @param kvPath KV节点路径 1470 * @param declarations 已mock的全局接口 1471 * @param rootKeyValue 1472 */ 1473function handleModuleOrInterfaceMember( 1474 memberKey: string, 1475 parent: KeyValue, 1476 mockBuffer: MockBuffer, 1477 kvPath: KeyValue[], 1478 declarations: string[], 1479 rootKeyValue: KeyValue 1480): void { 1481 const memberKeyValue = parent.members[memberKey]; 1482 if (memberKeyValue.isMocked) { 1483 return; 1484 } 1485 let elementName = `.${memberKey}`; 1486 if (memberKeyValue.type === KeyValueTypes.EXPRESSION) { 1487 memberKeyValue.key = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1488 memberKeyValue.type = KeyValueTypes.PROPERTY; 1489 memberKeyValue.value = undefined; 1490 elementName = memberKeyValue.key; 1491 } 1492 const memberValue = handleKeyValue(memberKey, memberKeyValue, mockBuffer, kvPath, rootKeyValue, memberKeyValue.property); 1493 const value = `global.${parent.key}_temp${elementName} = ${memberValue};`; 1494 memberKeyValue.isMocked = true; 1495 declarations.push(value); 1496} 1497 1498/** 1499 * 处理全局interface KV节点 1500 * @param keyValue KV节点 1501 * @param mockBuffer 所属文件的mock信息 1502 * @param declarations 已mock的文本内容 1503 * @param kvPath KV节点路径 1504 * @param member 不为undefined时,只mock这个member节点 1505 * @returns 1506 */ 1507function handleGlobalInterface( 1508 keyValue: KeyValue, 1509 mockBuffer: MockBuffer, 1510 declarations: string[], 1511 kvPath: KeyValue[], 1512 member?: KeyValue 1513): void { 1514 if (keyValue.heritage) { 1515 handleHeritage(keyValue, mockBuffer, kvPath.concat(keyValue), keyValue); 1516 } 1517 return handleGlobalModuleOrInterface(keyValue, mockBuffer, declarations, kvPath, member); 1518} 1519 1520/** 1521 * 从导入节点向上查找类型定义 1522 * @param importKeyValue 导入的KV节点 1523 * @param mockBuffer 当前文件的mock信息 1524 * @param rootKeyValue 仅次于FILE节点的根节点 1525 * @param property KV节点的调用属性节点,如A.b, b节点为property 1526 * @returns 1527 */ 1528function findDefFromImport( 1529 importKeyValue: KeyValue, 1530 mockBuffer: MockBuffer, 1531 rootKeyValue: KeyValue, 1532 property?: KeyValue 1533): ReferenceFindResult { 1534 const importedMockBuffer = mockBufferMap.get(MockedFileMap.get(importKeyValue.importedModulePath)); 1535 if (!importedMockBuffer) { 1536 throw new Error('未找到foundKeyValue.importedModulePath对应的mockBuffer'); 1537 } 1538 let defKeyValue: KeyValue; 1539 if (importKeyValue.isImportDefault) { 1540 defKeyValue = importedMockBuffer.contents.members.default; 1541 } else if (importKeyValue.isNamespaceImport) { 1542 defKeyValue = importedMockBuffer.contents; 1543 } else { 1544 defKeyValue = importedMockBuffer.contents.members[importKeyValue.rawName ?? importKeyValue.key]; 1545 } 1546 if (defKeyValue.isGlobalDeclare) { 1547 const dependKeyValue = property ? defKeyValue.members[property.key] : defKeyValue; 1548 if (dependKeyValue.type === KeyValueTypes.ENUM) { 1549 defKeyValue = dependKeyValue; 1550 } else { 1551 !dependKeyValue.isMocked && rootKeyValue.dependOnGlobals.add(dependKeyValue); 1552 const keyValueType = getKeyValueType(defKeyValue, property); 1553 const newKey = `global.${defKeyValue.key}${concatProperty(property)}`; 1554 defKeyValue = generateKeyValue(newKey, keyValueType); 1555 defKeyValue.value = newKey; 1556 !dependKeyValue.isMocked && defKeyValue.dependOnGlobals.add(dependKeyValue); 1557 } 1558 } else { 1559 defKeyValue = findProperty(defKeyValue, property); 1560 } 1561 1562 if (!defKeyValue) { 1563 const value = `Not exported ${importKeyValue.rawName ?? importKeyValue.key} from ${importedMockBuffer.rawFilePath} in ${mockBuffer.rawFilePath}`.replace(/\\/g, '/'); 1564 console.error(value); 1565 defKeyValue = generateKeyValue(value, KeyValueTypes.VALUE, importedMockBuffer.contents); 1566 defKeyValue.value = `'${value}'`; 1567 } 1568 return { keyValue: defKeyValue, mockBuffer: importedMockBuffer }; 1569} 1570 1571/** 1572 * mock类方法 1573 * @param memberKey 方法原名 1574 * @param memberKeyValue 方法KV节点 1575 * @param parent 负极节点 1576 * @param mockBuffer 当前文件的mock信息 1577 * @param kvPath KV检点路径 1578 * @param elementName 方法名转换后方法名 1579 * @param memberValue 类方法的mock内容 1580 */ 1581function handleClassMethod( 1582 memberKey: string, 1583 memberKeyValue: KeyValue, 1584 parent: KeyValue, 1585 mockBuffer: MockBuffer, 1586 kvPath: KeyValue[], 1587 elementName: string, 1588 memberValue: string 1589): string { 1590 let value:string; 1591 if (memberKey.startsWith('get ') || memberKey.startsWith('set ')) { 1592 value = handleClassGetterOrSetterMethod(memberKeyValue, parent, mockBuffer, kvPath); 1593 } else { 1594 value = `global.${parent.key}_temp${memberKeyValue.isStatic ? '' : '.prototype'}${elementName} = ${memberValue};`; 1595 } 1596 return value; 1597} 1598 1599/** 1600 * mock 类中带get和set关键字的方法 1601 * @param memberKeyValue 类方法KV节点 1602 * @param parent 父级KV节点 1603 * @param mockBuffer 当前文件的mock信息 1604 * @param kvPath KV节点路径 1605 */ 1606function handleClassGetterOrSetterMethod( 1607 memberKeyValue: KeyValue, 1608 parent: KeyValue, 1609 mockBuffer: MockBuffer, 1610 kvPath: KeyValue[] 1611): string { 1612 const getKey = `get ${memberKeyValue.key}`; 1613 let getMethodValue: string = ''; 1614 if (parent.members[getKey]) { 1615 const getFunctionBody = handleKeyValue(getKey, parent.members[getKey], mockBuffer, kvPath, parent, memberKeyValue.property); 1616 getMethodValue = `get: ${getFunctionBody},`; 1617 } 1618 1619 let setMethodValue: string = ''; 1620 const setKey = `set ${memberKeyValue.key}`; 1621 if (parent.members[setKey]) { 1622 const setFunctionBody = handleKeyValue(setKey, parent.members[setKey], mockBuffer, kvPath, parent, memberKeyValue.property); 1623 setMethodValue = `set: ${setFunctionBody},`; 1624 } 1625 1626 if (parent.members[getKey]) { 1627 parent.members[getKey].isMocked = true; 1628 } 1629 if (parent.members[setKey]) { 1630 parent.members[setKey].isMocked = true; 1631 } 1632 1633 return `Object.defineProperty(global.${parent.key}_temp, '${memberKeyValue.key}', { 1634 ${getMethodValue} 1635 ${setMethodValue} 1636});`; 1637} 1638