1/* 2 * Copyright (c) 2025 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 * as fs from 'node:fs'; 17import * as path from 'node:path'; 18import type { LintRunResult } from '../../LintRunResult'; 19import type { ProblemInfo } from '../../ProblemInfo'; 20import * as mk from '../../utils/consts/MapKeyConst'; 21import { countFiles } from './CountFile'; 22import { countNapiFiles } from './CountNapiFile'; 23import type { ProblemNumbersInfo } from './ProblemNumbersInfo'; 24import type { ProblemStatisticsInfo } from './ProblemStatisticsInfo'; 25import type { RuleDetailedErrorInfo } from './RuleDetailedErrorInfo'; 26import type { StatisticsReportInPutInfo } from './StatisticsReportInPutInfo'; 27import type { TimeRecorder } from './TimeRecorder'; 28import { WorkLoadInfo } from './WorkLoadInfo'; 29 30export function getProblemStatisticsInfo( 31 problemNumbers: ProblemNumbersInfo, 32 ruleToNumbersMap: Map<string, number>, 33 ruleToAutoFixedNumbersMap: Map<string, number>, 34 timeRecorder: TimeRecorder, 35 WorkLoadInfo?: WorkLoadInfo 36): ProblemStatisticsInfo { 37 const problemNumberMap: Map<string, number> = new Map(); 38 problemNumberMap.set(mk.TOTAL_PROBLEMS, problemNumbers.totalProblemNumbers); 39 problemNumberMap.set(mk.ONE_POINT_ONE_PROBLEMS, problemNumbers.arkOnePointOneProblemNumbers); 40 problemNumberMap.set(mk.ONE_POINT_TWO_PROBLEMS, problemNumbers.arkOnePointTWOProblemNumbers); 41 problemNumberMap.set(mk.CAN_BE_AUTO_FIXED_PROBLEMS_NUMBERS, problemNumbers.canBeAutoFixedproblemNumbers); 42 problemNumberMap.set(mk.NEED_TO_NAMUAL_FIX_PROBLEM_NUMBERS, problemNumbers.needToManualFixproblemNumbers); 43 44 const scanTime = timeRecorder.getScanTime(); 45 const migrationTime = timeRecorder.getMigrationTime(); 46 const usedTimeMap: Map<string, string> = new Map(); 47 usedTimeMap.set(mk.SCAN_TIME, scanTime); 48 usedTimeMap.set(mk.MIGRATION_TIME, migrationTime); 49 50 const detailRuleProblemsMap: Map<string, RuleDetailedErrorInfo> = new Map(); 51 ruleToNumbersMap.forEach((value, key) => { 52 const ruleDetailedErrorInfo: RuleDetailedErrorInfo = { 53 rule: key, 54 problemNumbers: value, 55 canBeAutoFixedMumbers: ruleToAutoFixedNumbersMap.get(key) ?? 0 56 }; 57 detailRuleProblemsMap.set(key, ruleDetailedErrorInfo); 58 }); 59 60 const problemStatisticsInfo: ProblemStatisticsInfo = { 61 problems: Object.fromEntries(problemNumberMap), 62 usedTime: Object.fromEntries(usedTimeMap), 63 WorkLoadInfo: WorkLoadInfo, 64 eachRuleProblemsDetail: Array.from(detailRuleProblemsMap.values()) 65 }; 66 return problemStatisticsInfo; 67} 68 69export function getArktsOnePointOneProlemNumbers(problems: ProblemInfo[]): number { 70 let problemNumbersForATSOnePointOne: number = 0; 71 const signForOne: string = 's2d'; 72 problems.forEach((problem) => { 73 if (problem.rule !== undefined) { 74 if (problem.rule.includes(signForOne)) { 75 problemNumbersForATSOnePointOne = problemNumbersForATSOnePointOne + 1; 76 } 77 } 78 }); 79 return problemNumbersForATSOnePointOne; 80} 81 82export function accumulateRuleNumbers( 83 problems: ProblemInfo[], 84 ruleToNumbersMap: Map<string, number>, 85 ruleToAutoFixedNumbersMap: Map<string, number> 86): void { 87 problems.forEach((problem) => { 88 if (problem.rule !== undefined) { 89 if (problem.autofix) { 90 const currentNumber = ruleToAutoFixedNumbersMap.get(problem.rule) || 0; 91 ruleToAutoFixedNumbersMap.set(problem.rule, currentNumber + 1); 92 } 93 const currentNumber = ruleToNumbersMap.get(problem.rule) || 0; 94 ruleToNumbersMap.set(problem.rule, currentNumber + 1); 95 } 96 }); 97} 98 99export async function generateReportFile(reportName: string, reportData, reportPath?: string): Promise<void> { 100 let reportFilePath = path.join(reportName); 101 if (reportPath !== undefined) { 102 reportFilePath = path.join(path.normalize(reportPath), reportName); 103 } 104 try { 105 await fs.promises.mkdir(path.dirname(reportFilePath), { recursive: true }); 106 await fs.promises.writeFile(reportFilePath, JSON.stringify(reportData, null, 2)); 107 } catch (error) { 108 console.error('Error generating report file:', error); 109 } 110} 111 112export function generateReportFileSync(reportName: string, reportData: any, reportPath?: string): void { 113 let reportFilePath = reportName; 114 if (reportPath !== undefined) { 115 reportFilePath = path.join(path.normalize(reportPath), reportName); 116 } 117 try { 118 fs.mkdirSync(path.dirname(reportFilePath), { recursive: true }); 119 fs.writeFileSync(reportFilePath, JSON.stringify(reportData, null, 2)); 120 } catch (error) { 121 console.error('Error generating report file:', error); 122 } 123} 124 125export function getMapValueSum(map: Map<any, number>): number { 126 let result = 0; 127 for (const value of map.values()) { 128 result += value; 129 } 130 return result; 131} 132 133export async function generateScanProbelemStatisticsReport( 134 statisticsReportInPutInfo: StatisticsReportInPutInfo 135): Promise<void> { 136 const canBeAutoFixedproblemNumbers = getMapValueSum(statisticsReportInPutInfo.ruleToAutoFixedNumbersMap); 137 const problemNumbers: ProblemNumbersInfo = { 138 totalProblemNumbers: statisticsReportInPutInfo.totalProblemNumbers, 139 arkOnePointOneProblemNumbers: statisticsReportInPutInfo.arkOnePointOneProblemNumbers, 140 arkOnePointTWOProblemNumbers: 141 statisticsReportInPutInfo.totalProblemNumbers - statisticsReportInPutInfo.arkOnePointOneProblemNumbers, 142 canBeAutoFixedproblemNumbers: canBeAutoFixedproblemNumbers, 143 needToManualFixproblemNumbers: statisticsReportInPutInfo.totalProblemNumbers - canBeAutoFixedproblemNumbers 144 }; 145 const projectFolderList = statisticsReportInPutInfo.cmdOptions.linterOptions.projectFolderList!; 146 const WorkLoadInfo = await getWorkLoadInfo(projectFolderList); 147 const statisticsReportData = getProblemStatisticsInfo( 148 problemNumbers, 149 statisticsReportInPutInfo.ruleToNumbersMap, 150 statisticsReportInPutInfo.ruleToAutoFixedNumbersMap, 151 statisticsReportInPutInfo.timeRecorder, 152 WorkLoadInfo 153 ); 154 await generateReportFile( 155 statisticsReportInPutInfo.statisticsReportName, 156 statisticsReportData, 157 statisticsReportInPutInfo.cmdOptions.outputFilePath 158 ); 159} 160 161export function generateMigrationStatisicsReport( 162 lintResult: LintRunResult, 163 timeRecorder: TimeRecorder, 164 outputFilePath?: string 165): void { 166 const ruleToNumbersMap: Map<string, number> = new Map(); 167 const ruleToAutoFixedNumbersMap: Map<string, number> = new Map(); 168 let arkOnePointOneProblemNumbers: number = 0; 169 170 const problemsInfoAfterAutofixed = lintResult.problemsInfos; 171 for (const problems of problemsInfoAfterAutofixed.values()) { 172 accumulateRuleNumbers(problems, ruleToNumbersMap, ruleToAutoFixedNumbersMap); 173 arkOnePointOneProblemNumbers = arkOnePointOneProblemNumbers + getArktsOnePointOneProlemNumbers(problems); 174 } 175 176 let totalProblemNumbers: number = 0; 177 for (const problems of problemsInfoAfterAutofixed.values()) { 178 totalProblemNumbers += problems.length; 179 } 180 181 const canBeAutoFixedproblemNumbers = getMapValueSum(ruleToAutoFixedNumbersMap); 182 183 const problemNumbers: ProblemNumbersInfo = { 184 totalProblemNumbers: totalProblemNumbers, 185 arkOnePointOneProblemNumbers: arkOnePointOneProblemNumbers, 186 arkOnePointTWOProblemNumbers: totalProblemNumbers - arkOnePointOneProblemNumbers, 187 canBeAutoFixedproblemNumbers: canBeAutoFixedproblemNumbers, 188 needToManualFixproblemNumbers: totalProblemNumbers - canBeAutoFixedproblemNumbers 189 }; 190 191 const statisticsReportData = getProblemStatisticsInfo( 192 problemNumbers, 193 ruleToNumbersMap, 194 ruleToAutoFixedNumbersMap, 195 timeRecorder 196 ); 197 const statisticsReportName: string = 'migration-results-statistics.json'; 198 generateReportFileSync(statisticsReportName, statisticsReportData, outputFilePath); 199} 200 201export async function getWorkLoadInfo(projectPathList: string[]): Promise<WorkLoadInfo> { 202 const workloadInfo = new WorkLoadInfo(); 203 const projectResults = await Promise.all( 204 projectPathList.map(async(projectPath) => { 205 const normalizedPath = path.normalize(projectPath); 206 const [countFilesResults, countNapiFileResults] = await Promise.all([ 207 countFiles(normalizedPath), 208 countNapiFiles(normalizedPath) 209 ]); 210 211 const getLines = (lang: keyof typeof countFilesResults): number => { 212 return countFilesResults[lang]?.lineCount || 0; 213 }; 214 215 return { 216 normalizedPath, 217 arkTSCodeLines: getLines('ArkTS') + getLines('ArkTS Test'), 218 cAndCPPCodeLines: getLines('C/C++'), 219 napiCodeLines: countNapiFileResults.napiLines, 220 jsCodeLines: getLines('JavaScript'), 221 tsCodeLines: getLines('TypeScript'), 222 jsonCodeLines: getLines('JSON'), 223 xmlCodeLines: getLines('XML') 224 }; 225 }) 226 ); 227 228 projectResults.forEach((result) => { 229 workloadInfo.addFloderResult(result); 230 }); 231 return workloadInfo; 232} 233