1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const ts = require('typescript'); 17const { ApiDigestInfo } = require('./api_data'); 18const { DiffReporter } = require('./reporter'); 19const { StatusCode } = require('./reporter'); 20const { getCheckApiVersion } = require('../../api_check_plugin/src/utils'); 21 22class TagItem { 23 constructor() { } 24 25 addSinceVersion(version) { 26 this.setProperty('sinceVersion', version); 27 } 28 29 getSinceVersion() { 30 return this.sinceVersion; 31 } 32 33 addSyscap(sysCap) { 34 this.setProperty('sysCap', sysCap); 35 } 36 37 getSyscap() { 38 return this.sysCap; 39 } 40 41 addAppModel(model) { 42 this.setProperty('appModel', model); 43 } 44 45 getAppModel() { 46 return this.appModel; 47 } 48 49 addDeprecated(deprecated) { 50 this.setProperty('deprecated', deprecated); 51 } 52 53 getDeprecated() { 54 return this.deprecated; 55 } 56 57 addErrorCode(errorCode) { 58 this.setProperty('errorCode', errorCode); 59 } 60 61 getErrorCode() { 62 return this.errorCode; 63 } 64 65 addApiLevel(level) { 66 this.setProperty('apiLevel', level); 67 } 68 69 getApiLevel() { 70 return this.apiLevel; 71 } 72 73 addTypeTag(type) { 74 this.setProperty('type', type); 75 } 76 77 getTypeTag() { 78 return this.type; 79 } 80 81 addUseInstead(useinstead) { 82 this.setProperty('useinstead', useinstead); 83 } 84 85 getUseInstead() { 86 return this.useinstead; 87 } 88 89 addPermission(permission) { 90 this.setProperty('permission', permission); 91 } 92 93 getPermission() { 94 return this.permission; 95 } 96 97 addForm(form) { 98 this.setProperty('form', form); 99 } 100 101 getForm() { 102 return this.form; 103 } 104 105 addCrossplatform(crossplatform) { 106 this.setProperty('crossplatform', crossplatform); 107 } 108 109 getCrossplatform() { 110 return this.crossplatform; 111 } 112 113 setProperty(name, value) { 114 if (this[name]) { 115 this[name].push(value); 116 } else { 117 this[name] = [value]; 118 } 119 } 120} 121 122function isArrayEquals(first, second) { 123 if (!first && !second) { 124 return true; 125 } 126 const ret = first && second && first.length === second.length; 127 if (ret) { 128 first.sort(); 129 second.sort(); 130 for (let index = 0; index < first.length; index++) { 131 if (first[index] !== second[index]) { 132 return false; 133 } 134 } 135 } 136 return ret; 137} 138 139function arrayToString(array) { 140 if (!array || array.length === 0) { 141 return ''; 142 } 143 return array.join(); 144} 145 146/** 147 * 获取API @deprecated 标签信息, 继承父类 148 * 149 * @param {ApiDigestInfo} api 150 * @returns {Array} 151 */ 152function getApiDeprecated(api) { 153 let curApi = api; 154 while (curApi) { 155 const jsdocTagItem = getTagItemFromJSDoc(curApi); 156 if (jsdocTagItem.getDeprecated()) { 157 return jsdocTagItem.getDeprecated(); 158 } 159 curApi = curApi.getParent(); 160 } 161 return []; 162} 163 164function matchSyscapInFile(api) { 165 let syscap = getApiSyscap(api)[0]; 166 let curApi = api; 167 while (!syscap && !ts.isSourceFile(curApi.node)) { 168 curApi = curApi.getParent(); 169 } 170 if (!syscap && ts.isSourceFile(curApi.node)) { 171 const fileContent = curApi.node.getFullText(); 172 if (/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(fileContent)) { 173 fileContent.replace(/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g, sysCapInfo => { 174 syscap = sysCapInfo.replace(/\@syscap/g, '').trim(); 175 }); 176 } 177 } 178 return syscap; 179} 180 181/** 182 * 获取 API @syscap 标签信息,继承父类 183 * 184 * @param {ApiDigestInfo} api 185 * @returns {Array} 186 */ 187function getApiSyscap(api) { 188 let curApi = api; 189 while (curApi && !ts.isSourceFile(curApi.node)) { 190 const jsdocTagItem = getTagItemFromJSDoc(curApi); 191 if (jsdocTagItem.getSyscap()) { 192 return jsdocTagItem.getSyscap(); 193 } 194 curApi = curApi.getParent(); 195 } 196 return []; 197} 198 199/** 200 * 获取API @useinstead,继承父类 201 * 202 * @param {ApiDigestInfo} api 203 * @returns {Array} 204 */ 205function getApiUseInstead(api) { 206 let curApi = api; 207 while (curApi) { 208 const jsdocTagItem = getTagItemFromJSDoc(curApi); 209 if (jsdocTagItem.getUseInstead()) { 210 return jsdocTagItem.getUseInstead(); 211 } 212 curApi = curApi.getParent(); 213 } 214 return []; 215} 216 217/** 218 * 从 JSDoc 对象获取所有Tag标签信息 219 * 220 * @param {ApiDigestInfo} api 221 * @returns {Object} 222 */ 223function getTagItemFromJSDoc(api) { 224 let jsdocTagItem = api.getJSDocTagItem(); 225 if (!jsdocTagItem) { 226 jsdocTagItem = createTagItemFromJSDoc(api.jsdoc); 227 api.setJSDocTagItem(jsdocTagItem); 228 } 229 return jsdocTagItem; 230} 231 232function wrapApiChanges(api, statusCode, oldMessage, newMessage, hint, oldNode, newNode, syscap) { 233 return { 234 api: api, 235 statusCode: statusCode, 236 oldMessage: oldMessage, 237 newMessage: newMessage, 238 hint: hint, 239 oldNode: oldNode, 240 newNode: newNode, 241 syscap: syscap, 242 }; 243} 244 245/** 246 * 比较JSDoc的差异 247 * 248 * @param {ApiDigestInfo} oldApi 249 * @param {ApiDigestInfo} newApi 250 * @param {DiffReporter} diffReporter 251 */ 252function compareJSDocs(oldApi, newApi, diffReporter) { 253 const oldTagItem = getTagItemFromJSDoc(oldApi); 254 const newTagItem = getTagItemFromJSDoc(newApi); 255 const useinstead = getApiUseInstead(newApi); 256 const hint = useinstead.length > 0 ? `useinstead: ${useinstead[0]}` : ''; 257 diffHistoricalJsDoc(oldApi, newApi, diffReporter, hint); 258 diffErrorCode(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 259 diffPermission(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 260 diffForm(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 261 diffCrossplatform(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 262 diffDeprecated(diffReporter, oldApi, newApi, hint); 263 diffApiLevel(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 264 diffAppModel(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 265 diffType(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint); 266} 267 268function getLatestVersion(jsdocs) { 269 let apiLatestVersion = ''; 270 if (!jsdocs || jsdocs.length === 0) { 271 return apiLatestVersion; 272 } 273 const jsdoc = jsdocs[jsdocs.length - 1]; 274 jsdoc.tags?.forEach(tagObject => { 275 if (tagObject.tag === 'since') { 276 apiLatestVersion = tagObject.name; 277 } 278 }); 279 return apiLatestVersion; 280} 281 282function diffHistoricalJsDoc(oldApi, newApi, diffReporter, hint) { 283 const currentVersion = getCheckApiVersion(); 284 const oldJsDocTextArr = oldApi.getAstNode().getFullText().replace(oldApi.getAstNode().getText(), '').split('*/'); 285 const newJsDocTextArr = newApi.getAstNode().getFullText().replace(oldApi.getAstNode().getText(), '').split('*/'); 286 if (!oldApi.jsdoc) { 287 return; 288 } 289 const oldLatestVersion = getLatestVersion(oldApi.jsdoc); 290 const newLatestVersion = getLatestVersion(newApi.jsdoc); 291 if (oldLatestVersion === currentVersion) { 292 oldJsDocTextArr.splice(-2); 293 } else { 294 oldJsDocTextArr.splice(-1); 295 } 296 297 if (newLatestVersion === currentVersion) { 298 newJsDocTextArr.splice(-2); 299 } else { 300 newJsDocTextArr.splice(-1); 301 } 302 303 if (oldJsDocTextArr.length !== newJsDocTextArr.length) { 304 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.HOSTORICAL_JSDOC_CHANGE, 305 '', 306 '', 307 hint, 308 oldApi.node, 309 newApi.node 310 )); 311 return; 312 } 313 for (let i = 0; i < oldJsDocTextArr.length; i++) { 314 if (oldJsDocTextArr[i].replace(/\r|\n|\s+/g, '') !== newJsDocTextArr[i].replace(/\r|\n|\s+/g, '')) { 315 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.HOSTORICAL_JSDOC_CHANGE, 316 '', 317 '', 318 hint, 319 oldApi.node, 320 newApi.node 321 )); 322 } 323 } 324} 325 326/** 327 * 比较@type 328 * 329 * @param {DiffReporter} diffReporter 330 * @param {TagItem} oldTagItem 331 * @param {TagItem} newTagItem 332 * @param {ApiDigestInfo} oldApi 333 * @param {ApiDigestInfo} newApi 334 * @param {string} hint 335 */ 336function diffType(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 337 const oldType = oldTagItem.getTypeTag(); 338 const newType = newTagItem.getTypeTag(); 339 if (!isArrayEquals(oldType, newType)) { 340 diffReporter.addChangedApi(wrapApiChanges(newApi, StatusCode.TYPE_CHNAGES, 341 arrayToString(oldType), arrayToString(newType), 342 hint, '', '', matchSyscapInFile(newApi) 343 )); 344 345 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.TYPE_CHNAGES, 346 arrayToString(oldType), 347 arrayToString(newType), 348 hint, 349 oldApi.node, 350 newApi.node 351 )); 352 } 353} 354 355/** 356 * 比较@FAModelOnly @StageModelOnly 357 * 358 * @param {DiffReporter} diffReporter 359 * @param {TagItem} oldTagItem 360 * @param {TagItem} newTagItem 361 * @param {ApiDigestInfo} oldApi 362 * @param {ApiDigestInfo} newApi 363 * @param {string} hint 364 */ 365function diffAppModel(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 366 const oldAppModel = oldTagItem.getAppModel(); 367 const newAppModel = newTagItem.getAppModel(); 368 if (!isArrayEquals(oldAppModel, newTagItem.getAppModel())) { 369 diffReporter.addChangedApi(wrapApiChanges(newApi, StatusCode.MODEL_CHNAGES, 370 arrayToString(oldAppModel), arrayToString(newAppModel), 371 hint, '', '', matchSyscapInFile(newApi) 372 )); 373 374 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.MODEL_CHNAGES, 375 arrayToString(oldAppModel), 376 arrayToString(newAppModel), 377 hint, 378 oldApi.node, 379 newApi.node 380 )); 381 } 382} 383 384/** 385 * 比较@systemapi 386 * 387 * @param {DiffReporter} diffReporter 388 * @param {TagItem} oldTagItem 389 * @param {TagItem} newTagItem 390 * @param {ApiDigestInfo} oldApi 391 * @param {ApiDigestInfo} newApi 392 * @param {string} hint 393 */ 394function diffApiLevel(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 395 const oldApiLevel = oldTagItem.getApiLevel(); 396 const newApiLevel = newTagItem.getApiLevel(); 397 if (!isArrayEquals(oldApiLevel, newApiLevel)) { 398 diffReporter.addChangedApi(wrapApiChanges( 399 newApi, StatusCode.SYSTEM_API_CHNAGES, 400 arrayToString(oldApiLevel), 401 arrayToString(newApiLevel), 402 hint, '', '', matchSyscapInFile(newApi) 403 )); 404 405 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.SYSTEM_API_CHNAGES, 406 arrayToString(oldApiLevel), 407 arrayToString(newApiLevel), 408 hint, 409 oldApi.node, 410 newApi.node 411 )); 412 } 413} 414 415/** 416 * 比较@crossplatform 417 * 418 * @param {DiffReporter} diffReporter 419 * @param {TagItem} oldTagItem 420 * @param {TagItem} newTagItem 421 * @param {ApiDigestInfo} oldApi 422 * @param {ApiDigestInfo} newApi 423 * @param {string} hint 424 */ 425function diffCrossplatform(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 426 if (!isArrayEquals(oldTagItem.getCrossplatform(), newTagItem.getCrossplatform())) { 427 diffReporter.addChangedApi(wrapApiChanges( 428 newApi, StatusCode.CROSSPLATFORM_CHANGED, arrayToString(oldTagItem.getCrossplatform()), 429 arrayToString(newTagItem.getCrossplatform()), 430 hint, '', '', matchSyscapInFile(newApi) 431 )); 432 433 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.CROSSPLATFORM_CHANGED, 434 arrayToString(oldTagItem.getCrossplatform()), 435 arrayToString(newTagItem.getCrossplatform()), 436 hint, 437 oldApi.node, 438 newApi.node 439 )); 440 } 441} 442 443/** 444 * 比较@form 445 * 446 * @param {DiffReporter} diffReporter 447 * @param {TagItem} oldTagItem 448 * @param {TagItem} newTagItem 449 * @param {ApiDigestInfo} oldApi 450 * @param {ApiDigestInfo} newApi 451 * @param {string} hint 452 */ 453function diffForm(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 454 if (!isArrayEquals(oldTagItem.getForm(), newTagItem.getForm())) { 455 diffReporter.addChangedApi(wrapApiChanges( 456 newApi, StatusCode.FORM_CHANGED, arrayToString(oldTagItem.getForm()), 457 arrayToString(newTagItem.getForm()), 458 hint, '', '', matchSyscapInFile(newApi) 459 )); 460 461 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.FORM_CHANGED, 462 arrayToString(oldTagItem.getForm()), 463 arrayToString(newTagItem.getForm()), 464 hint, 465 oldApi.node, 466 newApi.node 467 )); 468 } 469} 470 471/** 472 * 比较@syscap 473 * 474 * @param {DiffReporter} diffReporter 475 * @param {ApiDigestInfo} oldApi 476 * @param {ApiDigestInfo} newApi 477 * @param {string} hint 478 */ 479function diffSyscap(diffReporter, oldApi, newApi, hint) { 480 const oldSyscap = getApiSyscap(oldApi); 481 const newSyscap = getApiSyscap(newApi); 482 if (!isArrayEquals(oldSyscap, newSyscap)) { 483 diffReporter.addChangedApi(wrapApiChanges( 484 newApi, StatusCode.SYSCAP_CHANGES, arrayToString(oldSyscap), 485 arrayToString(newSyscap), 486 hint, '', '', matchSyscapInFile(newApi) 487 )); 488 diffReporter.addDiffInfo(wrapApiChanges( 489 newApi, StatusCode.SYSCAP_CHANGES, arrayToString(oldSyscap), 490 arrayToString(newSyscap), 491 hint, 492 oldApi.node, 493 newApi.node 494 )); 495 } 496} 497 498/** 499 * 比较@since 500 * 501 * @param {DiffReporter} diffReporter 502 * @param {TagItem} oldTagItem 503 * @param {TagItem} newTagItem 504 * @param {ApiDigestInfo} oldApi 505 * @param {ApiDigestInfo} newApi 506 * @param {string} hint 507 */ 508function diffSinceVersion(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 509 const oldVersion = oldTagItem.getSinceVersion(); 510 const newVersion = newTagItem.getSinceVersion(); 511 if (!isArrayEquals(oldVersion, newVersion)) { 512 diffReporter.addChangedApi(wrapApiChanges( 513 newApi, StatusCode.VERSION_CHNAGES, arrayToString(oldVersion), 514 arrayToString(newVersion), 515 hint, '', '', matchSyscapInFile(newApi) 516 )); 517 diffReporter.addDiffInfo(wrapApiChanges( 518 newApi, StatusCode.VERSION_CHNAGES, arrayToString(oldVersion), 519 arrayToString(newVersion), 520 hint, 521 oldApi.node, 522 newApi.node 523 )); 524 } 525} 526 527/** 528 * 比较@deprecated 529 * 530 * @param {DiffReporter} diffReporter 531 * @param {ApiDigestInfo} oldApi 532 * @param {ApiDigestInfo} newApi 533 * @param {string} hint 534 */ 535function diffDeprecated(diffReporter, oldApi, newApi, hint) { 536 const oldDeprecated = getApiDeprecated(oldApi); 537 const newDeprecated = getApiDeprecated(newApi); 538 if (!isArrayEquals(oldDeprecated, newDeprecated)) { 539 diffReporter.addChangedApi(wrapApiChanges( 540 newApi, StatusCode.DEPRECATED_CHNAGES, 541 arrayToString(oldDeprecated), arrayToString(newDeprecated), 542 hint, '', '', matchSyscapInFile(newApi) 543 )); 544 diffReporter.addDiffInfo(wrapApiChanges( 545 newApi, StatusCode.DEPRECATED_CHNAGES, arrayToString(oldDeprecated), 546 arrayToString(newDeprecated), 547 hint, 548 oldApi.node, 549 newApi.node 550 )); 551 } 552} 553 554/** 555 * 比较权限的差异@permission 556 * 557 * @param {DiffReporter} diffReporter 558 * @param {TagItem} oldTagItem 559 * @param {TagItem} newTagItem 560 * @param {ApiDigestInfo} oldApi 561 * @param {ApiDigestInfo} newApi 562 * @param {string} hint 563 */ 564function diffPermission(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 565 if (!isArrayEquals(oldTagItem.getPermission(), newTagItem.getPermission())) { 566 diffReporter.addChangedApi(wrapApiChanges(newApi, StatusCode.PERMISSION_CHANGES, 567 arrayToString(oldTagItem.getPermission()), 568 arrayToString(newTagItem.getPermission()), 569 hint, 570 oldApi.node, 571 newApi.node, 572 matchSyscapInFile(newApi) 573 )); 574 575 diffReporter.addDiffInfo(wrapApiChanges(newApi, StatusCode.PERMISSION_CHANGES, 576 arrayToString(oldTagItem.getPermission()), 577 arrayToString(newTagItem.getPermission()), 578 hint, 579 oldApi.node, 580 newApi.node 581 )); 582 } 583} 584 585/** 586 * 比较错误码的差异@throws 587 * 588 * @param {DiffReporter} diffReporter 589 * @param {TagItem} oldTagItem 590 * @param {TagItem} newTagItem 591 * @param {ApiDigestInfo} oldApi 592 * @param {ApiDigestInfo} newApi 593 * @param {string} hint 594 */ 595function diffErrorCode(diffReporter, oldTagItem, newTagItem, oldApi, newApi, hint) { 596 if (!isArrayEquals(oldTagItem.getErrorCode(), newTagItem.getErrorCode())) { 597 let statusCode = ''; 598 if (oldTagItem.getErrorCode() === undefined) { 599 statusCode = StatusCode.NEW_ERRORCODE; 600 } else { 601 statusCode = StatusCode.ERRORCODE_CHANGES; 602 } 603 diffReporter.addChangedApi(wrapApiChanges( 604 newApi, statusCode, arrayToString(oldTagItem.getErrorCode()), 605 arrayToString(newTagItem.getErrorCode()), 606 hint, 607 oldApi.node, 608 newApi.node, 609 matchSyscapInFile(newApi) 610 )); 611 diffReporter.addDiffInfo(wrapApiChanges( 612 newApi, statusCode, arrayToString(oldTagItem.getErrorCode()), 613 arrayToString(newTagItem.getErrorCode()), 614 hint, 615 oldApi.node, 616 newApi.node 617 )); 618 } 619} 620 621/** 622 * 解析 @since 7 623 * 624 * @param {Object} tagObject 625 * @param {TagItem} tagItem 626 */ 627function appendSinceTag(tagObject, tagItem) { 628 tagItem.addSinceVersion(tagObject.name ? tagObject.name : ''); 629} 630 631/** 632 * 解析 @syscap 633 * 634 * @param {Object} tagObject 635 * @param {TagItem} tagItem 636 */ 637function appendSyscapTag(tagObject, tagItem) { 638 tagItem.addSyscap(tagObject.name ? tagObject.name : ''); 639} 640 641/** 642 * 解析 @permission 643 * 644 * @param {Object} tagObject 645 * @param {TagItem} tagItem 646 */ 647function appendPermissionTag(tagObject, tagItem) { 648 const permissionRegExp = RegExp(/ohos\.permission\.\w+/g); 649 let sourceText = ''; 650 tagObject.source.forEach((src) => { 651 sourceText += src.source; 652 }); 653 const permissionArray = sourceText.match(permissionRegExp); 654 if (permissionArray) { 655 permissionArray.forEach((permission) => { 656 tagItem.addPermission(permission); 657 }); 658 } 659} 660 661/** 662 * 解析 @deprecated 标签 663 * 664 * @param {Object} tagObject 665 * @param {TagItem} tagItem 666 */ 667function appendDeprecatedTag(tagObject, tagItem) { 668 tagItem.addDeprecated(tagObject.description); 669} 670 671/** 672 * 解析 @FAModelOnly @StageModelOnly 673 * 674 * @param {Object} tagObject 675 * @param {TagItem} tagItem 676 */ 677function appendModelTag(tagObject, tagItem) { 678 tagItem.addAppModel(tagObject.tag); 679} 680 681/** 682 * 解析 @systemapi 683 * 684 * @param {Object} tagObject 685 * @param {TagItem} tagItem 686 */ 687function appendApiLevelTag(tagObject, tagItem) { 688 tagItem.addApiLevel(tagObject.tag); 689} 690 691/** 692 * 解析 @type {string} 693 * 694 * @param {Object} tagObject 695 * @param {TagItem} tagItem 696 */ 697function appendTypeTag(tagObject, tagItem) { 698 tagItem.addTypeTag(tagObject.type); 699} 700 701/** 702 * 解析 @useinstedad 标签 703 * 704 * @param {Object} tagObject 705 * @param {TagItem} tagItem 706 */ 707function appendUseInsteadTag(tagObject, tagItem) { 708 tagItem.addUseInstead(tagObject.name); 709} 710 711/** 712 * 解析 @throws { BusinessError } 201 - 标签 713 * 714 * @param {Object} tagObject 715 * @param {TagItem} tagItem 716 */ 717function appendErrorCodeTag(tagObject, tagItem) { 718 tagItem.addErrorCode(tagObject.name); 719} 720 721/** 722 * 解析 @form - 标签 723 * 724 * @param {Object} tagObject 725 * @param {TagItem} tagItem 726 */ 727function appendForm(tagObject, tagItem) { 728 tagItem.addForm(tagObject.tag); 729} 730 731function appendCrossplatform(tagObject, tagItem) { 732 tagItem.addCrossplatform(tagObject.tag); 733} 734 735const tagHandlerMap = new Map([ 736 ['syscap', appendSyscapTag], 737 ['permission', appendPermissionTag], 738 ['deprecated', appendDeprecatedTag], 739 ['famodelonly', appendModelTag], 740 ['stagemodelonly', appendModelTag], 741 ['systemapi', appendApiLevelTag], 742 ['type', appendTypeTag], 743 ['useinstead', appendUseInsteadTag], 744 ['throws', appendErrorCodeTag], 745 ['form', appendForm], 746 ['crossplatform', appendCrossplatform] 747]); 748 749 750 751/** 752 * 从 comment-parser 库解析的JSDoc对象中提取标签信息 753 * 754 * @param {Object} jsdocs 755 */ 756function createTagItemFromJSDoc(jsdocs) { 757 const tagItem = new TagItem(); 758 if (!jsdocs || jsdocs.length === 0) { 759 return tagItem; 760 } 761 const singleJSDoc = jsdocs[jsdocs.length - 1]; 762 const firstJsDoc = jsdocs[0]; 763 if (singleJSDoc.tags) { 764 singleJSDoc.tags.forEach((tagObject) => { 765 const handler = tagHandlerMap.get(tagObject.tag.toLowerCase()); 766 if (handler) { 767 handler(tagObject, tagItem); 768 } 769 }); 770 } 771 772 if (firstJsDoc.tags) { 773 firstJsDoc.tags.forEach((tagObject) => { 774 if (tagObject.tag.toLowerCase() === 'since') { 775 appendSinceTag(tagObject, tagItem); 776 } 777 }); 778 } 779 return tagItem; 780} 781 782exports.JSDocDiffer = { 783 collectJSDocDiffs: compareJSDocs, 784};