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