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 path = require('path'); 17const fs = require('fs'); 18const ts = require('typescript'); 19const ExcelJS = require('exceljs'); 20const compressing = require('compressing'); 21const { readFile, getSubsystemBySyscap, getApiInfoWithFlag, getApiInfoDeleteDtsPath } = require('./util'); 22const { parse } = require('./collect_base_api'); 23const { allMergeData } = require('./format_data'); 24let methodType = ''; 25 26function getSubsystemJson(jsonDir) { 27 let fileContent = fs.readFileSync(jsonDir, 'utf-8'); 28 let subsystems = JSON.parse(fileContent); 29 const subsystemMap = new Map(); 30 const fileNameMap = new Map(); 31 subsystems.forEach(element => { 32 subsystemMap.set(element.syscap, element.subsystem); 33 fileNameMap.set(element.syscap, element.fileName); 34 }); 35 return { subsystemMap, fileNameMap }; 36} 37 38// 对比新旧版本文件列表 39function compileFiles(newFiles, oldFiles) { 40 const newFileNames = []; 41 const oldFileNames = []; 42 newFiles.forEach(file => { 43 const fileName = path.basename(file).replace(/.d.ts$/g, '.ts'); 44 newFileNames.push(fileName); 45 }); 46 oldFiles.forEach(file => { 47 const fileName = path.basename(file).replace(/.d.ts$/g, '.ts'); 48 oldFileNames.push(fileName); 49 }); 50 51 //保存有差异的信息 52 const compileApiInfos = []; 53 // 旧版本不存在新版本中的文件,表示新增 54 newFileNames.forEach(fileName => { 55 let index = oldFileNames.indexOf(fileName); 56 if (index < 0) { 57 compileApiInfos.push({ 58 flag: '新版本新增', 59 fileName: fileName, 60 filePath: newFiles[newFileNames.indexOf(fileName)], 61 }); 62 } 63 }); 64 // 新版本不在在旧版本中的文件,表示删除 65 oldFileNames.forEach(fileName => { 66 let index = newFileNames.indexOf(fileName); 67 if (index < 0) { 68 compileApiInfos.push({ 69 flag: '新版本删除', 70 fileName: fileName, 71 filePath: oldFiles[oldFileNames.indexOf(fileName)], 72 }); 73 } 74 }); 75 76 return compileApiInfos; 77} 78 79// 根据文件列表获取api信息数组 80function getFilesApi(files) { 81 const fileContentList = []; 82 files.forEach(file => { 83 let fileContent = fs.readFileSync(file, 'utf-8'); 84 fileContentList.push({ 85 fileName: path.basename(file).replace(/.d.ts$/g, '.ts'), 86 fileContent: fileContent, 87 fileRoot: file, 88 }); 89 90 }); 91 const api = parse(fileContentList); 92 return api; 93} 94 95function getDIffApisWithCompile(newFiles, oldFiles) { 96 const newFilesApi = getFilesApi(newFiles); 97 const oldFilesApi = getFilesApi(oldFiles); 98 // 去掉dtspath后的 99 const tmpNewApis = []; 100 newFilesApi.forEach(newApi => { 101 tmpNewApis.push(JSON.stringify(getApiInfoDeleteDtsPath(newApi))); 102 }); 103 const tmpOldApis = []; 104 oldFilesApi.forEach(oldApi => { 105 tmpOldApis.push(JSON.stringify(getApiInfoDeleteDtsPath(oldApi))); 106 }); 107 108 let hasFunTypeNewApis = []; 109 let hasFunTypeOldApis = []; 110 hasFunTypeNewApis = deleteRepeatApi(tmpOldApis, tmpNewApis, newFilesApi, oldFilesApi).newDiffApiArr; 111 hasFunTypeOldApis = deleteRepeatApi(tmpOldApis, tmpNewApis, newFilesApi, oldFilesApi).oldDiffApiArr; 112 113 let newDiffApis = []; 114 let oldDiffApis = []; 115 newDiffApis = collectSameTypeFun(hasFunTypeNewApis); 116 oldDiffApis = collectSameTypeFun(hasFunTypeOldApis); 117 const subsystemMap = getSubsystemJson(subsystemDir).subsystemMap; 118 const fileNameMap = getSubsystemJson(subsystemDir).fileNameMap; 119 const diffApis = []; 120 getDiffApisBaseNew(newDiffApis, oldDiffApis, diffApis, subsystemMap, fileNameMap); 121 getDiffApisBaseOld(newDiffApis, oldDiffApis, diffApis, subsystemMap, fileNameMap); 122 return diffApis; 123} 124 125function deleteRepeatApi(oldApis, newApis, newFilesApi, oldFilesApi) { 126 let newDiffApiArr = []; 127 let oldDiffApiArr = []; 128 for (let i = 0; i < newApis.length; i++) { 129 const newApi = newApis[i]; 130 let index = oldApis.indexOf(newApi); 131 if (index < 0) { 132 let includeTypeApi = addMethodType(newFilesApi[i]); 133 newDiffApiArr.push(includeTypeApi); 134 } 135 } 136 137 for (let i = 0; i < oldApis.length; i++) { 138 const oldApi = oldApis[i]; 139 let index = newApis.indexOf(oldApi); 140 if (index < 0) { 141 let includeTypeApi = addMethodType(oldFilesApi[i]); 142 oldDiffApiArr.push(includeTypeApi); 143 } 144 } 145 return { newDiffApiArr, oldDiffApiArr }; 146} 147 148function collectSameTypeFun(apiArr) { 149 apiArr.forEach(api => { 150 let sameNameFun = ''; 151 // 标记既不是Promise也不是callback的同名函数 152 let number = 0; 153 let sameNamePromiseText = ''; 154 apiArr.forEach(newApi => { 155 if (api.dtsPath.replace(newDir, '') === newApi.dtsPath.replace(newDir, '') && api.className === newApi.className && 156 api.methodName === newApi.methodName && api.apiType === 'Method' && api.funType === newApi.funType) { 157 if (sameNameFun.indexOf(newApi.methodText) < 0 && api.funType === 'callback') { 158 sameNameFun += `\n${newApi.methodText}`; 159 api.callbackMethodText = sameNameFun; 160 } else if (sameNamePromiseText.indexOf(newApi.methodText) < 0 && api.funType === 'Promise') { 161 sameNamePromiseText += `\n${newApi.methodText}`; 162 api.promiseMethodText = sameNamePromiseText; 163 } else if (!api.funType) { 164 number++; 165 api.note = number; 166 } 167 } 168 }); 169 }); 170 return apiArr; 171} 172 173function addMethodType(baseApi) { 174 let methodContent = `interface test{ 175 ${baseApi.methodText} 176 }`; 177 getMethodType(methodContent, filterType); 178 if (methodType === 'callback' || methodType === 'Promise') { 179 baseApi.funType = methodType; 180 } 181 return baseApi; 182} 183 184function getMethodType(content, callback) { 185 ts.transpileModule(content, { 186 compilerOptions: { 187 'target': ts.ScriptTarget.ES2017, 188 }, 189 fileName: 'index.ets', 190 transformers: { before: [callback()] }, 191 }); 192} 193 194function filterType() { 195 return (context) => { 196 methodType = ''; 197 return (node) => { 198 getType(node); 199 return node; 200 }; 201 function getType(node) { 202 // add function type(callback or Promise) 203 if (ts.isFunctionDeclaration(node) || ts.isMethodSignature(node)) { 204 getFunType(node); 205 } 206 return ts.visitEachChild(node, getType, context); 207 } 208 }; 209} 210 211function getFunType(node) { 212 if (node.type && ts.isTypeReferenceNode(node.type)) { 213 methodType = node.type.typeName.escapedText; 214 } else if (node.parameters.length > 0) { 215 const parameter = node.parameters[node.parameters.length - 1]; 216 if (parameter.name.escapedText === 'callback') { 217 methodType = parameter.name.escapedText; 218 } 219 } else { 220 methodType = ''; 221 } 222} 223 224function collectDeprecatedDiff(newDeprecated, oldDeprecated, startDiffNew, startDiffOld, diffApis, subsystemMap, 225 notes, fileNameMap, newApi) { 226 flag = '废弃版本有变化'; 227 diffOld = startDiffOld + '废弃版本:' + oldDeprecated; 228 const useinsteadInfo = newApi.useinsteadInfo instanceof Object ? newApi.useinsteadInfo.new : newApi.useinsteadInfo; 229 if (newApi.useinsteadInfo) { 230 diffNew = `${startDiffNew}废弃版本:${newDeprecated}\n代替接口:${useinsteadInfo}`; 231 } else { 232 diffNew = startDiffNew + '废弃版本:' + newDeprecated; 233 } 234 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 235 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 236} 237 238function getDiffApisBaseNew(newDiffApis, oldDiffApis, diffApis, subsystemMap, fileNameMap) { 239 const notes = ''; 240 const diffMethodTextSet = new Set(); 241 newDiffApis.forEach(newApi => { 242 let diffOld = 'NA'; 243 let diffNew = 'NA'; 244 const tmpApis = []; 245 246 oldDiffApis.forEach(oldApi => { 247 if (newApi.dtsPath.replace(newDir, '') === oldApi.dtsPath.replace(oldDir, '')) { 248 tmpApis.push(oldApi); 249 } 250 }); 251 if (tmpApis.length === 0) { 252 collectNewFileApi(newApi, subsystemMap, notes, fileNameMap, diffApis, diffOld); 253 } else { 254 if (newApi.apiType === 'import') { 255 collectImportDiff(tmpApis, newApi, diffApis, subsystemMap, notes, fileNameMap); 256 } else if (newApi.apiType === 'export') { 257 collectExportDiff(tmpApis, newApi, diffApis, subsystemMap, notes, fileNameMap); 258 } else { 259 let oldSameClassApis = handleApiByClassName(tmpApis, newApi, newDiffApis).oldSameClassApi; 260 let oldMethodTexts = handleApiByClassName(tmpApis, newApi, newDiffApis).oldMethodTexts; 261 collectSameClassDiff(oldSameClassApis, newApi, diffNew, diffOld, diffApis, subsystemMap, fileNameMap, diffMethodTextSet); 262 getNewAddApi(oldMethodTexts, diffApis, subsystemMap, fileNameMap, diffMethodTextSet, newApi); 263 } 264 } 265 }); 266} 267function collectSameClassDiff(oldSameClassApis, newApi, diffNew, diffOld, diffApis, subsystemMap, fileNameMap, diffMethodTextSet) { 268 const notes = ''; 269 if (oldSameClassApis.length === 0) { 270 flag = '新增'; 271 if (newApi.methodName === '') { 272 diffNew = '模块名: ' + newApi.packageName + '\n类名: ' + newApi.className; 273 } else { 274 diffNew = '模块名: ' + newApi.packageName + '\n类名: ' + newApi.className + 275 '\n方法 or 属性:' + newApi.methodText; 276 } 277 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 278 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 279 } else { 280 oldSameClassApis.forEach(oldApi => { 281 if (newApi.methodName === oldApi.methodName && newApi.apiType.toString() === oldApi.apiType.toString()) { 282 collectChangePart(newApi, oldApi, diffApis, subsystemMap, fileNameMap, diffMethodTextSet); 283 } 284 }); 285 286 } 287} 288function getNewAddApi(oldMethodTexts, diffApis, subsystemMap, fileNameMap, diffMethodTextSet, newApi) { 289 if (!oldMethodTexts.has(newApi.methodText.replace(/\r|\n|\s+|\,|\;/g, '')) && 290 !diffMethodTextSet.has(newApi.methodText)) { 291 const notes = ''; 292 const flag = '新增'; 293 const diffNew = '类名:' + newApi.className + '\n方法or属性:' + newApi.methodText; 294 const diffOld = 'NA'; 295 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 296 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 297 } 298} 299function collectChangePart(newApi, oldApi, diffApis, subsystemMap, fileNameMap, diffMethodTextSet) { 300 let startDiffOld = ''; 301 let startDiffNew = ''; 302 const NOTE_NUMBER = 2; 303 if (newApi.methodName === '') { 304 startDiffOld = '类名:' + oldApi.className + '\n'; 305 startDiffNew = '类名:' + newApi.className + '\n'; 306 } else { 307 startDiffOld = '类名:' + oldApi.className + '\n方法 or 属性:' + oldApi.methodText + '\n'; 308 startDiffNew = '类名:' + newApi.className + '\n方法 or 属性:' + newApi.methodText + '\n'; 309 } 310 if (isCallbackTypeFun(newApi, oldApi, 'callback')) { 311 diffMethodTextSet.add(newApi.methodText); 312 collectMethodTextDiff(oldApi, newApi, startDiffNew, startDiffOld, diffApis, newApi.callbackMethodText, 313 oldApi.callbackMethodText, subsystemMap, fileNameMap); 314 } 315 if (isPromiseTypeFun(newApi, oldApi, 'Promise')) { 316 diffMethodTextSet.add(newApi.methodText); 317 collectMethodTextDiff(oldApi, newApi, startDiffNew, startDiffOld, diffApis, newApi.promiseMethodText, 318 oldApi.promiseMethodText, subsystemMap, fileNameMap); 319 } 320 if (!newApi.funType && !oldApi.funType && newApi.methodText !== oldApi.methodText && newApi.note < NOTE_NUMBER) { 321 diffMethodTextSet.add(newApi.methodText); 322 collectMethodTextDiff(oldApi, newApi, startDiffNew, startDiffOld, diffApis, newApi.methodText, 323 oldApi.methodText, subsystemMap, fileNameMap); 324 } 325 if (newApi.methodText === oldApi.methodText) { 326 collectNoteDiff(newApi.version, oldApi.version, newApi.deprecated, oldApi.deprecated, newApi.errorCode, 327 oldApi.errorCode, newApi.sysCap, oldApi.sysCap, newApi.isSystemApi, oldApi.isSystemApi, newApi.permission, 328 oldApi.permission, newApi.model, oldApi.model, newApi.typeInfo, oldApi.typeInfo, startDiffNew, startDiffOld, 329 diffApis, subsystemMap, fileNameMap, newApi); 330 } 331 332} 333function isCallbackTypeFun(newApi, oldApi, funType) { 334 if (newApi.funType === funType && oldApi.funType === funType && 335 newApi.callbackMethodText !== oldApi.callbackMethodText) { 336 return true; 337 } else { 338 return false; 339 } 340} 341function isPromiseTypeFun(newApi, oldApi, funType) { 342 if (newApi.funType === funType && oldApi.funType === funType && 343 newApi.promiseMethodText !== oldApi.promiseMethodText) { 344 return true; 345 } else { 346 return false; 347 } 348} 349 350function collectNoteDiff(newVersion, oldVersion, newDeprecated, oldDeprecated, newErrorCode, oldErrorCode, newSyscap, 351 oldSyscap, newIsSystemApi, oldIsSystemApi, newPermission, oldPermission, newModel, oldModel, newTypeInfo, 352 oldTypeInfo, startDiffNew, startDiffOld, diffApis, subsystemMap, fileNameMap, newApi) { 353 const notes = ''; 354 if (newVersion !== oldVersion) { 355 collectVersionDiff(newVersion, oldVersion, startDiffNew, startDiffOld, 356 diffApis, subsystemMap, notes, fileNameMap, newApi); 357 } 358 if (newDeprecated !== oldDeprecated) { 359 collectDeprecatedDiff(newDeprecated, oldDeprecated, startDiffNew, startDiffOld, 360 diffApis, subsystemMap, notes, fileNameMap, newApi); 361 } 362 if (newErrorCode !== oldErrorCode) { 363 collectErrorCodeDiff(newErrorCode, oldErrorCode, startDiffNew, startDiffOld, 364 diffApis, subsystemMap, notes, fileNameMap, newApi); 365 } 366 if (newSyscap !== oldSyscap) { 367 collectSyscapDiff(newSyscap, oldSyscap, startDiffNew, startDiffOld, 368 diffApis, subsystemMap, notes, fileNameMap, newApi); 369 } 370 if (newIsSystemApi !== oldIsSystemApi) { 371 collectSystemApiDiff(newIsSystemApi, oldIsSystemApi, startDiffNew, startDiffOld, 372 diffApis, subsystemMap, notes, fileNameMap, newApi); 373 } 374 if (newPermission !== oldPermission) { 375 collectPermissionDiff(newPermission, oldPermission, startDiffNew, startDiffOld, 376 diffApis, subsystemMap, notes, fileNameMap, newApi); 377 } 378 if (newModel !== oldModel) { 379 collectModelDiff(newModel, oldModel, startDiffNew, startDiffOld, 380 diffApis, subsystemMap, notes, fileNameMap, newApi); 381 } 382 if (newTypeInfo !== oldTypeInfo) { 383 collectTypeDiff(newTypeInfo, oldTypeInfo, startDiffNew, startDiffOld, 384 diffApis, subsystemMap, notes, fileNameMap, newApi); 385 } 386} 387 388function collectTypeDiff(newType, oldType, startDiffNew, startDiffOld, diffApis, subsystemMap, notes, fileNameMap, newApi) { 389 let flag = ''; 390 if (oldType !== newType) { 391 flag = 'type有变化'; 392 } 393 if (flag) { 394 diffOld = startDiffOld + 'type:' + oldType; 395 diffNew = startDiffNew + 'type:' + newType; 396 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 397 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 398 } 399} 400 401function collectNewFileApi(newApi, subsystemMap, notes, fileNameMap, diffApis, diffOld) { 402 const flag = '新增'; 403 let diffNew = ''; 404 if (newApi.apiType === 'import') { 405 diffNew = '模块名: ' + newApi.packageName + '\nimport 信息: ' + newApi.headimport; 406 } else if (newApi.apiType === 'export') { 407 diffNew = '模块名: ' + newApi.packageName + '\nexport 信息: ' + newApi.endexport; 408 } else if (newApi.methodName === '') { 409 diffNew = '模块名: ' + newApi.packageName + '\n类名: ' + newApi.className; 410 } else { 411 diffNew = '模块名: ' + newApi.packageName + '\n类名: ' + newApi.className + '\n方法 or 属性: ' + newApi.methodText; 412 } 413 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 414 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 415} 416 417function collectImportDiff(tmpApis, newApi, diffApis, subsystemMap, notes, fileNameMap) { 418 const tmpApis2 = []; 419 tmpApis.forEach(oldApi => { 420 if (oldApi.apiType === 'import' && newApi.headimport === oldApi.headimport) { 421 tmpApis2.push(oldApi); 422 } 423 }); 424 425 if (tmpApis2.length === 0) { 426 flag = '新增'; 427 diffNew = '模块名: ' + newApi.packageName + '\nimport 信息: ' + newApi.headimport; 428 } 429 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 430 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 431} 432 433function collectExportDiff(tmpApis, newApi, diffApis, subsystemMap, notes, fileNameMap) { 434 const tmpApis3 = []; 435 tmpApis.forEach(oldApi => { 436 if (oldApi.apiType === 'export' && newApi.endexport === oldApi.endexport) { 437 tmpApis3.push(oldApi); 438 } 439 }); 440 if (tmpApis3.length === 0) { 441 flag = '新增'; 442 diffNew = '模块名: ' + newApi.packageName + '\nexport 信息: ' + newApi.endexport; 443 } 444 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 445 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 446} 447 448function handleApiByClassName(tmpApis, newApi, newDiffApis) { 449 const oldSameClassApi = []; 450 const newSameClassApi = []; 451 const oldMethodTexts = new Set([]); 452 const newMethodTexts = new Set([]); 453 let equalClassName = 'N/A'; 454 tmpApis.forEach(oldApi => { 455 if (newApi.className === oldApi.className) { 456 oldSameClassApi.push(oldApi); 457 equalClassName = oldApi.className; 458 oldMethodTexts.add(oldApi.methodText.replace(/\r|\n|\s+|\,|\;/g, '')); 459 } 460 }); 461 newDiffApis.forEach(apiText => { 462 if (apiText.className === equalClassName) { 463 newMethodTexts.add(apiText.methodText.replace(/\r|\n|\s+|\,|\;/g, '')); 464 newSameClassApi.push(apiText); 465 } 466 }); 467 return { oldSameClassApi, oldMethodTexts, newMethodTexts }; 468} 469 470function collectVersionDiff(newVersion, oldVersion, startDiffNew, startDiffOld, diffApis, 471 subsystemMap, notes, fileNameMap, newApi) { 472 flag = '起始版本有变化'; 473 diffOld = startDiffOld + '起始版本:' + oldVersion; 474 diffNew = startDiffNew + '起始版本:' + newVersion; 475 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 476 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 477} 478 479function collectModelDiff(newModel, oldModel, startDiffNew, startDiffOld, diffApis, 480 subsystemMap, notes, fileNameMap, newApi) { 481 flag = 'model有变化'; 482 diffOld = startDiffOld + 'model:' + oldModel; 483 diffNew = startDiffNew + 'model:' + newModel; 484 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 485 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 486} 487 488function collectPermissionDiff(newPermission, oldPermission, startDiffNew, startDiffOld, diffApis, 489 subsystemMap, notes, fileNameMap, newApi) { 490 flag = getPermissionFlag(oldPermission, newPermission); 491 if (flag !== '格式变化') { 492 diffOld = startDiffOld + '权限:' + oldPermission; 493 diffNew = startDiffNew + '权限:' + newPermission; 494 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 495 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 496 } 497} 498 499function collectMethodTextDiff(oldApi, newApi, startDiffNew, startDiffOld, diffApis, newMethodText, oldMethodText, 500 subsystemMap, fileNameMap) { 501 flag = getMethodFlag(oldMethodText, newMethodText); 502 const note = '去重'; 503 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 504 if (flag === '函数有变化') { 505 diffOld = startDiffOld; 506 diffNew = startDiffNew; 507 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, note, fileNameMap)); 508 } 509} 510 511function collectErrorCodeDiff(newErrorCode, oldErrorCode, startDiffNew, startDiffOld, diffApis, subsystemMap, notes, 512 fileNameMap, newApi) { 513 flag = oldErrorCode === 'N/A' ? '新增(错误码)' : 'API修改(错误码)'; 514 if (flag === '新增(错误码)') { 515 diffOld = 'NA'; 516 } else if (flag === 'API修改(错误码)') { 517 diffOld = startDiffOld + '错误码内容:' + oldErrorCode; 518 } 519 diffNew = startDiffNew + '错误码内容:' + newErrorCode; 520 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 521 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 522} 523 524function collectSyscapDiff(newSyscap, oldSyscap, startDiffNew, startDiffOld, diffApis, subsystemMap, notes, 525 fileNameMap, newApi) { 526 flag = 'SysCap有变化'; 527 diffOld = startDiffOld + 'SysCap:' + oldSyscap; 528 diffNew = startDiffNew + 'SysCap:' + newSyscap; 529 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 530 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 531} 532 533function collectSystemApiDiff(newIsSystemApi, oldIsSystemApi, startDiffNew, startDiffOld, diffApis, subsystemMap, 534 notes, fileNameMap, newApi) { 535 flag = '访问级别有变化'; 536 diffOld = startDiffOld + '访问级别:' + oldIsSystemApi; 537 diffNew = startDiffNew + '访问级别:' + newIsSystemApi; 538 let sysCapInfo = getSubsystemBySyscap(newApi, newApi.sysCap); 539 diffApis.push(getApiInfoWithFlag(newApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 540} 541 542function getMethodFlag(methodTextOld, methodTextNew) { 543 let methodTextOlds = methodTextOld.replace(/\r|\n|\s+|\,|\;/g, ''); 544 let methodTextNews = methodTextNew.replace(/\r|\n|\s+|\,|\;/g, ''); 545 if (methodTextOlds === methodTextNews) { 546 return '函数格式变化'; 547 } else { 548 return '函数有变化'; 549 } 550} 551 552function getPermissionFlag(permissionOld, permissionNew) { 553 let permissionOlds = permissionOld.replace(/\r|\n|\s+|\.|\//g, ''); 554 let permissionNews = permissionNew.replace(/\r|\n|\s+|\.|\//g, ''); 555 if (permissionOlds === permissionNews) { 556 return '格式变化'; 557 } else if (permissionOld === 'N/A') { 558 return '新增(权限)'; 559 } else if (permissionNew === 'N/A') { 560 return '删除(权限)'; 561 } else { 562 return '权限有变化'; 563 } 564} 565 566function getDiffApisBaseOld(newDiffApis, oldDiffApis, diffApis, subsystemMap, fileNameMap) { 567 const notes = ''; 568 oldDiffApis.forEach(oldApi => { 569 let flag = ''; 570 let diffOld = ''; 571 let diffNew = 'NA'; 572 const tmpApis = []; 573 newDiffApis.forEach(newApi => { 574 if (oldApi.dtsPath.replace(oldDir, '') === newApi.dtsPath.replace(newDir, '')) { 575 tmpApis.push(newApi); 576 } 577 }); 578 if (tmpApis.length === 0) { 579 flag = deleteFileApi(oldApi, flag, diffOld).flag; 580 diffOld = deleteFileApi(oldApi, flag, diffOld).diffOld; 581 } else { 582 if (oldApi.apiType === 'import') { 583 flag = deleteImportApi(flag, diffOld, tmpApis, oldApi).flag; 584 diffOld = deleteImportApi(flag, diffOld, tmpApis, oldApi).diffOld; 585 } else if (oldApi.apiType === 'export') { 586 flag = deleteExportApi(flag, diffOld, tmpApis, oldApi).flag; 587 diffOld = deleteExportApi(flag, diffOld, tmpApis, oldApi).diffOld; 588 } else { 589 flag = collectDeleteApis(tmpApis, oldApi, flag, diffOld).flag; 590 diffOld = collectDeleteApis(tmpApis, oldApi, flag, diffOld).diffOld; 591 } 592 } 593 if (flag !== '') { 594 let sysCapInfo = getSubsystemBySyscap(oldApi, oldApi.sysCap); 595 diffApis.push(getApiInfoWithFlag(oldApi, flag, diffOld, diffNew, subsystemMap, sysCapInfo, notes, fileNameMap)); 596 } 597 }); 598} 599 600function collectDeleteApis(tmpApis, oldApi, flag, diffOld) { 601 const newSameClassApis = []; 602 tmpApis.forEach(newApi => { 603 if (oldApi.className === newApi.className) { 604 newSameClassApis.push(newApi); 605 } 606 }); 607 if (newSameClassApis.length === 0) { 608 flag = '删除'; 609 if (oldApi.methodName === '') { 610 diffOld = '模块名: ' + oldApi.packageName + '\n类名: ' + oldApi.className; 611 } else { 612 diffOld = '模块名: ' + oldApi.packageName + '\n类名: ' + oldApi.className + '\n方法 or 属性:' + oldApi.methodText; 613 } 614 } else { 615 let count = 0; 616 newSameClassApis.forEach(newApi => { 617 if (oldApi.methodName === newApi.methodName && oldApi.apiType.toString() === newApi.apiType.toString()) { 618 count++; 619 } 620 }); 621 622 if (count === 0) { 623 flag = '删除'; 624 if (oldApi.methodName === '') { 625 diffOld = '模块名:' + oldApi.packageName + '\n类名:' + oldApi.className; 626 } else { 627 diffOld = '模块名:' + oldApi.packageName + '\n类名:' + oldApi.className + '\n方法 or 属性:' + oldApi.methodText; 628 } 629 } 630 } 631 return { flag, diffOld }; 632} 633 634function deleteImportApi(flag, diffOld, tmpApis, oldApi) { 635 const tmpApis2 = []; 636 tmpApis.forEach(newApi => { 637 if (newApi.apiType === 'import' && newApi.headimport === oldApi.headimport) { 638 tmpApis2.push(newApi); 639 } 640 }); 641 642 if (tmpApis2.length === 0) { 643 flag = '删除'; 644 diffOld = '模块名: ' + oldApi.packageName + '\nimport 信息: ' + oldApi.headimport; 645 } 646 return { flag, diffOld }; 647} 648 649function deleteExportApi(flag, diffOld, tmpApis, oldApi) { 650 const tmpApis3 = []; 651 tmpApis.forEach(newApi => { 652 if (newApi.apiType === 'export' && newApi.endexport === oldApi.endexport) { 653 tmpApis3.push(newApi); 654 } 655 }); 656 if (tmpApis3.length === 0) { 657 flag = '删除'; 658 diffOld = '模块名: ' + oldApi.packageName + '\nexport 信息: ' + oldApi.endexport; 659 } 660 return { flag, diffOld }; 661} 662 663function deleteFileApi(oldApi, flag, diffOld) { 664 flag = '删除'; 665 if (oldApi.apiType === 'import') { 666 diffOld = '模块名: ' + oldApi.packageName + '\nimport 信息: ' + oldApi.headimport; 667 } else if (oldApi.apiType === 'export') { 668 diffOld = '模块名: ' + oldApi.packageName + '\nexport 信息: ' + oldApi.endexport; 669 } else if (oldApi.methodName === '') { 670 diffOld = '模块名: ' + oldApi.packageName + '\n类名: ' + oldApi.className; 671 } else { 672 diffOld = '模块名: ' + oldApi.packageName + '\n类名: ' + oldApi.className + '\n方法 or 属性:' + oldApi.methodText; 673 } 674 return { flag, diffOld }; 675} 676 677// 输出新旧版本所有api对比结果 678async function exportDiffApiInfo(exportType, compileApiInfos, exportFileName) { 679 if (exportType === 'excel') { 680 const workbook = new ExcelJS.Workbook(); 681 const sheet = workbook.addWorksheet('api差异', { viers: [{ xSplit: 1 }] }); 682 sheet.getRow(1).values = ['操作标记*', '差异项-旧版本', '差异项-新版本', 'd.ts文件', '归属子系统']; 683 for (let i = 0; i < compileApiInfos.length; i++) { 684 let apiData = compileApiInfos[i]; 685 let fileName = apiData.dtsPath ? path.basename(apiData.dtsPath) : apiData.packageName; 686 const ADD_NUMBER = 2; 687 sheet.getRow(i + ADD_NUMBER).values = [apiData.flag, apiData.diffOld, apiData.diffNew, fileName, 688 apiData.subsystem]; 689 } 690 const buffer = await workbook.xlsx.writeBuffer(); 691 fs.writeFile(exportFileName, buffer, function (err) { 692 if (err) { 693 return; 694 } 695 }); 696 } else if (exportType === 'json') { 697 fs.writeFile(exportFileName, JSON.stringify(compileApiInfos), function (err) { 698 if (err) { 699 console.log('Write failed:' + err); 700 return; 701 } 702 console.log(exportFileName + 'Write completed.'); 703 }); 704 } 705} 706 707function exportDiffMd(subsystem, diffInfos) { 708 let finalContent = '| 操作 | 旧版本 | 新版本 | d.ts文件 |\n' + '| ---- | ------ | ------ | -------- |\n'; 709 for (let i = 0; i < diffInfos.length; i++) { 710 let apiData = diffInfos[i]; 711 let fileName = apiData.dtsPath ? path.basename(apiData.dtsPath) : apiData.packageName; 712 const oldData = apiData.diffOld.replace(/\r|\n/g, '<br>').replace(/\|/g, '\\|').replace(/\<(?!br>)/g, '\\<'); 713 const newData = apiData.diffNew.replace(/\r|\n/g, '<br>').replace(/\|/g, '\\|').replace(/\<(?!br>)/g, '\\<'); 714 finalContent += `|${apiData.flag}|${oldData}|${newData}|${fileName}|\n`; 715 } 716 const mdFilesDir = `${__dirname.replace('src', '')}diff合集`; 717 if (!fs.existsSync(mdFilesDir)) { 718 fs.mkdirSync('../diff合集'); 719 } 720 721 fs.writeFile(`../diff合集/js-apidiff-${subsystem}.md`, finalContent, function (err) { 722 if (err) { 723 console.log('WRITE FAILED:::', err); 724 return; 725 } 726 console.log('WRITE SUCCESSED'); 727 }); 728 729} 730 731function diffBySbusystem(compileApiInfos) { 732 let filePath = __dirname.replace('\\src', '') + '\\subsystem.json'; 733 let subsystemArr = JSON.parse(fs.readFileSync(filePath, 'utf-8')); 734 let flagArr = ['新增', '删除', 'model有变化', '访问级别有变化', '废弃版本有变化', '起始版本有变化', '权限有变化', 735 '删除(权限)', '新增(错误码)', '新增(权限)', '错误码有变化', 'SysCap有变化', '函数有变化']; 736 subsystemArr.forEach(subsystem => { 737 let diffInfos = []; 738 compileApiInfos.forEach(api => { 739 if (subsystem.subsystem === api.subsystem) { 740 diffInfos.push(api); 741 } 742 }); 743 if (diffInfos.length !== 0) { 744 const sortDiffInfos = []; 745 flagArr.forEach(flag => { 746 diffInfos.forEach(diffInfo => { 747 if (diffInfo.flag === flag) { 748 sortDiffInfos.push(diffInfo); 749 } 750 }); 751 }); 752 exportDiffMd(subsystem.fileName, sortDiffInfos); 753 } 754 755 }); 756} 757// 读取路径下所有的zip文件路径 758function readZipFile(dir, zipFiles) { 759 try { 760 const files = fs.readdirSync(dir); 761 files.forEach(element => { 762 const filePath = path.join(dir, element); 763 const status = fs.statSync(filePath); 764 if (status.isDirectory()) { 765 readZipFile(filePath, zipFiles); 766 } else { 767 if (filePath.endsWith('.zip')) { 768 zipFiles.push(filePath); 769 } 770 } 771 }); 772 } catch (error) { 773 console.error('ETS ERROR:' + error); 774 } 775} 776 777function unCompressAllZipFilesAndCompile(newDir, oldDir) { 778 const zipFiles = []; 779 readZipFile(newDir, zipFiles); 780 readZipFile(oldDir, zipFiles); 781 let count = 0; 782 let countfile = 0; 783 zipFiles.forEach(async element => { 784 let filePath = element.replace(path.basename(element), ''); 785 compressing.zip.uncompress(element, filePath).then((data) => { 786 console.log('uncompress success:', data, '--' + element); 787 count++; 788 if ((count + countfile) === zipFiles.length) { 789 compileAndExportToFile(); 790 } 791 }).catch(error => { 792 console.log(('uncompress error:', error)); 793 }); 794 }); 795} 796 797function compileAndExportToFile() { 798 const newFiles = []; 799 const oldFiles = []; 800 readFile(oldDir, oldFiles); 801 readFile(newDir, newFiles); 802 let diffApis = getDIffApisWithCompile(newFiles, oldFiles); 803 let noRepeatDiffApi = []; 804 let diffApisSet = new Set(); 805 diffApis.map(api => diffApisSet.add(JSON.stringify(api))); 806 diffApisSet.forEach(item => { 807 noRepeatDiffApi.push(JSON.parse(item)); 808 }); 809 for (let i = 0; i < allMergeData.length; i++) { 810 for (let j = 0; j < noRepeatDiffApi.length; j++) { 811 if (allMergeData[i].packageName === noRepeatDiffApi[j].packageName && noRepeatDiffApi[j].flag === '删除' && 812 allMergeData[i].diffOld === noRepeatDiffApi[j].methodText) { 813 noRepeatDiffApi.splice(j, 1); 814 j--; 815 } else if (allMergeData[i].packageName === noRepeatDiffApi[j].packageName && 816 noRepeatDiffApi[j].flag === '新增' && allMergeData[i].diffNew === noRepeatDiffApi[j].methodText) { 817 noRepeatDiffApi.splice(j, 1); 818 j--; 819 } 820 821 } 822 823 } 824 let afterMergeData = noRepeatDiffApi.concat(allMergeData); 825 exportDiffApiInfo('excel', afterMergeData, 'Js_Api_Diff.xlsx'); 826 diffBySbusystem(afterMergeData); 827} 828 829const subsystemDir = __dirname.replace('\\src', '') + '\\subsystem.json'; 830const urlObject = fs.readFileSync(__dirname.replace('\\src', '') + '\\url.json', 'utf-8'); 831const oldDir = JSON.parse(urlObject).oldDir; 832const newDir = JSON.parse(urlObject).newDir; 833unCompressAllZipFilesAndCompile(newDir, oldDir); 834