13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ciimport * as path from 'node:path'; 173af6ab5fSopenharmony_ciimport type * as ts from 'typescript'; 183af6ab5fSopenharmony_ciimport type { CommandLineOptions } from './CommandLineOptions'; 193af6ab5fSopenharmony_ciimport { faultsAttrs } from './FaultAttrs'; 203af6ab5fSopenharmony_ciimport { faultDesc } from './FaultDesc'; 213af6ab5fSopenharmony_ciimport { InteropTypescriptLinter } from './InteropTypescriptLinter'; 223af6ab5fSopenharmony_ciimport type { LintOptions } from './LintOptions'; 233af6ab5fSopenharmony_ciimport type { LintRunResult } from './LintRunResult'; 243af6ab5fSopenharmony_ciimport type { ProblemInfo } from './ProblemInfo'; 253af6ab5fSopenharmony_ciimport { ProblemSeverity } from './ProblemSeverity'; 263af6ab5fSopenharmony_ciimport { FaultID } from './Problems'; 273af6ab5fSopenharmony_ciimport { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; 283af6ab5fSopenharmony_ciimport { getTscDiagnostics } from './ts-diagnostics/GetTscDiagnostics'; 293af6ab5fSopenharmony_ciimport { transformTscDiagnostics } from './ts-diagnostics/TransformTscDiagnostics'; 303af6ab5fSopenharmony_ciimport { 313af6ab5fSopenharmony_ci ARKTS_IGNORE_DIRS_NO_OH_MODULES, 323af6ab5fSopenharmony_ci ARKTS_IGNORE_DIRS_OH_MODULES, 333af6ab5fSopenharmony_ci ARKTS_IGNORE_FILES 343af6ab5fSopenharmony_ci} from './utils/consts/ArktsIgnorePaths'; 353af6ab5fSopenharmony_ciimport { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; 363af6ab5fSopenharmony_ciimport { clearPathHelperCache, pathContainsDirectory } from './utils/functions/PathHelper'; 373af6ab5fSopenharmony_ciimport { LibraryTypeCallDiagnosticChecker } from './utils/functions/LibraryTypeCallDiagnosticChecker'; 383af6ab5fSopenharmony_ci 393af6ab5fSopenharmony_cifunction prepareInputFilesList(cmdOptions: CommandLineOptions): string[] { 403af6ab5fSopenharmony_ci let inputFiles = cmdOptions.inputFiles; 413af6ab5fSopenharmony_ci if (!cmdOptions.parsedConfigFile) { 423af6ab5fSopenharmony_ci return inputFiles; 433af6ab5fSopenharmony_ci } 443af6ab5fSopenharmony_ci 453af6ab5fSopenharmony_ci inputFiles = cmdOptions.parsedConfigFile.fileNames; 463af6ab5fSopenharmony_ci if (cmdOptions.inputFiles.length <= 0) { 473af6ab5fSopenharmony_ci return inputFiles; 483af6ab5fSopenharmony_ci } 493af6ab5fSopenharmony_ci 503af6ab5fSopenharmony_ci /* 513af6ab5fSopenharmony_ci * Apply linter only to the project source files that are specified 523af6ab5fSopenharmony_ci * as a command-line arguments. Other source files will be discarded. 533af6ab5fSopenharmony_ci */ 543af6ab5fSopenharmony_ci const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => { 553af6ab5fSopenharmony_ci return path.resolve(x); 563af6ab5fSopenharmony_ci }); 573af6ab5fSopenharmony_ci const configInputsResolvedPaths = inputFiles.map((x) => { 583af6ab5fSopenharmony_ci return path.resolve(x); 593af6ab5fSopenharmony_ci }); 603af6ab5fSopenharmony_ci inputFiles = configInputsResolvedPaths.filter((x) => { 613af6ab5fSopenharmony_ci return cmdInputsResolvedPaths.some((y) => { 623af6ab5fSopenharmony_ci return x === y; 633af6ab5fSopenharmony_ci }); 643af6ab5fSopenharmony_ci }); 653af6ab5fSopenharmony_ci 663af6ab5fSopenharmony_ci return inputFiles; 673af6ab5fSopenharmony_ci} 683af6ab5fSopenharmony_ci 693af6ab5fSopenharmony_cifunction countProblems(linter: TypeScriptLinter | InteropTypescriptLinter): [number, number] { 703af6ab5fSopenharmony_ci let errorNodesTotal = 0; 713af6ab5fSopenharmony_ci let warningNodes = 0; 723af6ab5fSopenharmony_ci for (let i = 0; i < FaultID.LAST_ID; i++) { 733af6ab5fSopenharmony_ci switch (faultsAttrs[i].severity) { 743af6ab5fSopenharmony_ci case ProblemSeverity.ERROR: 753af6ab5fSopenharmony_ci errorNodesTotal += linter.nodeCounters[i]; 763af6ab5fSopenharmony_ci break; 773af6ab5fSopenharmony_ci case ProblemSeverity.WARNING: 783af6ab5fSopenharmony_ci warningNodes += linter.nodeCounters[i]; 793af6ab5fSopenharmony_ci break; 803af6ab5fSopenharmony_ci default: 813af6ab5fSopenharmony_ci } 823af6ab5fSopenharmony_ci } 833af6ab5fSopenharmony_ci 843af6ab5fSopenharmony_ci return [errorNodesTotal, warningNodes]; 853af6ab5fSopenharmony_ci} 863af6ab5fSopenharmony_ci 873af6ab5fSopenharmony_ci// eslint-disable-next-line max-lines-per-function 883af6ab5fSopenharmony_ciexport function lint(options: LintOptions, etsLoaderPath: string | undefined): LintRunResult { 893af6ab5fSopenharmony_ci const cmdOptions = options.cmdOptions; 903af6ab5fSopenharmony_ci const tscCompiledProgram = options.tscCompiledProgram; 913af6ab5fSopenharmony_ci const tsProgram = tscCompiledProgram.getProgram(); 923af6ab5fSopenharmony_ci 933af6ab5fSopenharmony_ci // Prepare list of input files for linter and retrieve AST for those files. 943af6ab5fSopenharmony_ci let inputFiles = prepareInputFilesList(cmdOptions); 953af6ab5fSopenharmony_ci inputFiles = inputFiles.filter((input) => { 963af6ab5fSopenharmony_ci return shouldProcessFile(options, input); 973af6ab5fSopenharmony_ci }); 983af6ab5fSopenharmony_ci const srcFiles: ts.SourceFile[] = []; 993af6ab5fSopenharmony_ci for (const inputFile of inputFiles) { 1003af6ab5fSopenharmony_ci const srcFile = tsProgram.getSourceFile(inputFile); 1013af6ab5fSopenharmony_ci if (srcFile) { 1023af6ab5fSopenharmony_ci srcFiles.push(srcFile); 1033af6ab5fSopenharmony_ci } 1043af6ab5fSopenharmony_ci } 1053af6ab5fSopenharmony_ci 1063af6ab5fSopenharmony_ci const tscStrictDiagnostics = getTscDiagnostics(tscCompiledProgram, srcFiles); 1073af6ab5fSopenharmony_ci LibraryTypeCallDiagnosticChecker.rebuildTscDiagnostics(tscStrictDiagnostics); 1083af6ab5fSopenharmony_ci const linter = options.isEtsFile ? 1093af6ab5fSopenharmony_ci new TypeScriptLinter( 1103af6ab5fSopenharmony_ci tsProgram.getTypeChecker(), 1113af6ab5fSopenharmony_ci cmdOptions.enableAutofix, 1123af6ab5fSopenharmony_ci cmdOptions.arkts2, 1133af6ab5fSopenharmony_ci options.cancellationToken, 1143af6ab5fSopenharmony_ci options.incrementalLintInfo, 1153af6ab5fSopenharmony_ci tscStrictDiagnostics, 1163af6ab5fSopenharmony_ci options.reportAutofixCb, 1173af6ab5fSopenharmony_ci options.isEtsFileCb, 1183af6ab5fSopenharmony_ci options.compatibleSdkVersion, 1193af6ab5fSopenharmony_ci options.compatibleSdkVersionStage 1203af6ab5fSopenharmony_ci ) : 1213af6ab5fSopenharmony_ci new InteropTypescriptLinter( 1223af6ab5fSopenharmony_ci tsProgram.getTypeChecker(), 1233af6ab5fSopenharmony_ci tsProgram.getCompilerOptions(), 1243af6ab5fSopenharmony_ci cmdOptions.arkts2, 1253af6ab5fSopenharmony_ci etsLoaderPath 1263af6ab5fSopenharmony_ci ); 1273af6ab5fSopenharmony_ci const { errorNodes, problemsInfos } = lintFiles(srcFiles, linter); 1283af6ab5fSopenharmony_ci consoleLog('\n\n\nFiles scanned: ', srcFiles.length); 1293af6ab5fSopenharmony_ci consoleLog('\nFiles with problems: ', errorNodes); 1303af6ab5fSopenharmony_ci 1313af6ab5fSopenharmony_ci const [errorNodesTotal, warningNodes] = countProblems(linter); 1323af6ab5fSopenharmony_ci logTotalProblemsInfo(errorNodesTotal, warningNodes, linter); 1333af6ab5fSopenharmony_ci logProblemsPercentageByFeatures(linter); 1343af6ab5fSopenharmony_ci 1353af6ab5fSopenharmony_ci freeMemory(); 1363af6ab5fSopenharmony_ci 1373af6ab5fSopenharmony_ci return { 1383af6ab5fSopenharmony_ci errorNodes: errorNodesTotal, 1393af6ab5fSopenharmony_ci problemsInfos: mergeArrayMaps(problemsInfos, transformTscDiagnostics(tscStrictDiagnostics)) 1403af6ab5fSopenharmony_ci }; 1413af6ab5fSopenharmony_ci} 1423af6ab5fSopenharmony_ci 1433af6ab5fSopenharmony_cifunction lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter | InteropTypescriptLinter): LintRunResult { 1443af6ab5fSopenharmony_ci let problemFiles = 0; 1453af6ab5fSopenharmony_ci const problemsInfos: Map<string, ProblemInfo[]> = new Map(); 1463af6ab5fSopenharmony_ci 1473af6ab5fSopenharmony_ci for (const srcFile of srcFiles) { 1483af6ab5fSopenharmony_ci const prevVisitedNodes = linter.totalVisitedNodes; 1493af6ab5fSopenharmony_ci const prevErrorLines = linter.totalErrorLines; 1503af6ab5fSopenharmony_ci const prevWarningLines = linter.totalWarningLines; 1513af6ab5fSopenharmony_ci linter.errorLineNumbersString = ''; 1523af6ab5fSopenharmony_ci linter.warningLineNumbersString = ''; 1533af6ab5fSopenharmony_ci const nodeCounters: number[] = []; 1543af6ab5fSopenharmony_ci 1553af6ab5fSopenharmony_ci for (let i = 0; i < FaultID.LAST_ID; i++) { 1563af6ab5fSopenharmony_ci nodeCounters[i] = linter.nodeCounters[i]; 1573af6ab5fSopenharmony_ci } 1583af6ab5fSopenharmony_ci 1593af6ab5fSopenharmony_ci linter.lint(srcFile); 1603af6ab5fSopenharmony_ci // save results and clear problems array 1613af6ab5fSopenharmony_ci problemsInfos.set(path.normalize(srcFile.fileName), [...linter.problemsInfos]); 1623af6ab5fSopenharmony_ci linter.problemsInfos.length = 0; 1633af6ab5fSopenharmony_ci 1643af6ab5fSopenharmony_ci // print results for current file 1653af6ab5fSopenharmony_ci const fileVisitedNodes = linter.totalVisitedNodes - prevVisitedNodes; 1663af6ab5fSopenharmony_ci const fileErrorLines = linter.totalErrorLines - prevErrorLines; 1673af6ab5fSopenharmony_ci const fileWarningLines = linter.totalWarningLines - prevWarningLines; 1683af6ab5fSopenharmony_ci 1693af6ab5fSopenharmony_ci problemFiles = countProblemFiles( 1703af6ab5fSopenharmony_ci nodeCounters, 1713af6ab5fSopenharmony_ci problemFiles, 1723af6ab5fSopenharmony_ci srcFile, 1733af6ab5fSopenharmony_ci fileVisitedNodes, 1743af6ab5fSopenharmony_ci fileErrorLines, 1753af6ab5fSopenharmony_ci fileWarningLines, 1763af6ab5fSopenharmony_ci linter 1773af6ab5fSopenharmony_ci ); 1783af6ab5fSopenharmony_ci } 1793af6ab5fSopenharmony_ci 1803af6ab5fSopenharmony_ci return { 1813af6ab5fSopenharmony_ci errorNodes: problemFiles, 1823af6ab5fSopenharmony_ci problemsInfos: problemsInfos 1833af6ab5fSopenharmony_ci }; 1843af6ab5fSopenharmony_ci} 1853af6ab5fSopenharmony_ci 1863af6ab5fSopenharmony_ci// eslint-disable-next-line max-lines-per-function, max-params 1873af6ab5fSopenharmony_cifunction countProblemFiles( 1883af6ab5fSopenharmony_ci nodeCounters: number[], 1893af6ab5fSopenharmony_ci filesNumber: number, 1903af6ab5fSopenharmony_ci tsSrcFile: ts.SourceFile, 1913af6ab5fSopenharmony_ci fileNodes: number, 1923af6ab5fSopenharmony_ci fileErrorLines: number, 1933af6ab5fSopenharmony_ci fileWarningLines: number, 1943af6ab5fSopenharmony_ci linter: TypeScriptLinter | InteropTypescriptLinter 1953af6ab5fSopenharmony_ci): number { 1963af6ab5fSopenharmony_ci let errorNodes = 0; 1973af6ab5fSopenharmony_ci let warningNodes = 0; 1983af6ab5fSopenharmony_ci for (let i = 0; i < FaultID.LAST_ID; i++) { 1993af6ab5fSopenharmony_ci const nodeCounterDiff = linter.nodeCounters[i] - nodeCounters[i]; 2003af6ab5fSopenharmony_ci switch (faultsAttrs[i].severity) { 2013af6ab5fSopenharmony_ci case ProblemSeverity.ERROR: 2023af6ab5fSopenharmony_ci errorNodes += nodeCounterDiff; 2033af6ab5fSopenharmony_ci break; 2043af6ab5fSopenharmony_ci case ProblemSeverity.WARNING: 2053af6ab5fSopenharmony_ci warningNodes += nodeCounterDiff; 2063af6ab5fSopenharmony_ci break; 2073af6ab5fSopenharmony_ci default: 2083af6ab5fSopenharmony_ci } 2093af6ab5fSopenharmony_ci } 2103af6ab5fSopenharmony_ci if (errorNodes > 0) { 2113af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 2123af6ab5fSopenharmony_ci filesNumber++; 2133af6ab5fSopenharmony_ci const errorRate = (errorNodes / fileNodes * 100).toFixed(2); 2143af6ab5fSopenharmony_ci const warningRate = (warningNodes / fileNodes * 100).toFixed(2); 2153af6ab5fSopenharmony_ci consoleLog(tsSrcFile.fileName, ': ', '\n\tError lines: ', linter.errorLineNumbersString); 2163af6ab5fSopenharmony_ci consoleLog(tsSrcFile.fileName, ': ', '\n\tWarning lines: ', linter.warningLineNumbersString); 2173af6ab5fSopenharmony_ci consoleLog( 2183af6ab5fSopenharmony_ci '\n\tError constructs (%): ', 2193af6ab5fSopenharmony_ci errorRate, 2203af6ab5fSopenharmony_ci '\t[ of ', 2213af6ab5fSopenharmony_ci fileNodes, 2223af6ab5fSopenharmony_ci ' constructs ], \t', 2233af6ab5fSopenharmony_ci fileErrorLines, 2243af6ab5fSopenharmony_ci ' lines' 2253af6ab5fSopenharmony_ci ); 2263af6ab5fSopenharmony_ci consoleLog( 2273af6ab5fSopenharmony_ci '\n\tWarning constructs (%): ', 2283af6ab5fSopenharmony_ci warningRate, 2293af6ab5fSopenharmony_ci '\t[ of ', 2303af6ab5fSopenharmony_ci fileNodes, 2313af6ab5fSopenharmony_ci ' constructs ], \t', 2323af6ab5fSopenharmony_ci fileWarningLines, 2333af6ab5fSopenharmony_ci ' lines' 2343af6ab5fSopenharmony_ci ); 2353af6ab5fSopenharmony_ci } 2363af6ab5fSopenharmony_ci 2373af6ab5fSopenharmony_ci return filesNumber; 2383af6ab5fSopenharmony_ci} 2393af6ab5fSopenharmony_ci 2403af6ab5fSopenharmony_cifunction logTotalProblemsInfo( 2413af6ab5fSopenharmony_ci errorNodes: number, 2423af6ab5fSopenharmony_ci warningNodes: number, 2433af6ab5fSopenharmony_ci linter: TypeScriptLinter | InteropTypescriptLinter 2443af6ab5fSopenharmony_ci): void { 2453af6ab5fSopenharmony_ci const errorRate = (errorNodes / linter.totalVisitedNodes * 100).toFixed(2); 2463af6ab5fSopenharmony_ci const warningRate = (warningNodes / linter.totalVisitedNodes * 100).toFixed(2); 2473af6ab5fSopenharmony_ci consoleLog('\nTotal error constructs (%): ', errorRate); 2483af6ab5fSopenharmony_ci consoleLog('\nTotal warning constructs (%): ', warningRate); 2493af6ab5fSopenharmony_ci consoleLog('\nTotal error lines:', linter.totalErrorLines, ' lines\n'); 2503af6ab5fSopenharmony_ci consoleLog('\nTotal warning lines:', linter.totalWarningLines, ' lines\n'); 2513af6ab5fSopenharmony_ci} 2523af6ab5fSopenharmony_ci 2533af6ab5fSopenharmony_cifunction logProblemsPercentageByFeatures(linter: TypeScriptLinter | InteropTypescriptLinter): void { 2543af6ab5fSopenharmony_ci consoleLog('\nPercent by features: '); 2553af6ab5fSopenharmony_ci for (let i = 0; i < FaultID.LAST_ID; i++) { 2563af6ab5fSopenharmony_ci const nodes = linter.nodeCounters[i]; 2573af6ab5fSopenharmony_ci const lines = linter.lineCounters[i]; 2583af6ab5fSopenharmony_ci const pecentage = (nodes / linter.totalVisitedNodes * 100).toFixed(2).padEnd(7, ' '); 2593af6ab5fSopenharmony_ci 2603af6ab5fSopenharmony_ci consoleLog(faultDesc[i].padEnd(55, ' '), pecentage, '[', nodes, ' constructs / ', lines, ' lines]'); 2613af6ab5fSopenharmony_ci } 2623af6ab5fSopenharmony_ci} 2633af6ab5fSopenharmony_ci 2643af6ab5fSopenharmony_cifunction shouldProcessFile(options: LintOptions, fileFsPath: string): boolean { 2653af6ab5fSopenharmony_ci if ( 2663af6ab5fSopenharmony_ci ARKTS_IGNORE_FILES.some((ignore) => { 2673af6ab5fSopenharmony_ci return path.basename(fileFsPath) === ignore; 2683af6ab5fSopenharmony_ci }) 2693af6ab5fSopenharmony_ci ) { 2703af6ab5fSopenharmony_ci return false; 2713af6ab5fSopenharmony_ci } 2723af6ab5fSopenharmony_ci 2733af6ab5fSopenharmony_ci if ( 2743af6ab5fSopenharmony_ci ARKTS_IGNORE_DIRS_NO_OH_MODULES.some((ignore) => { 2753af6ab5fSopenharmony_ci return pathContainsDirectory(path.resolve(fileFsPath), ignore); 2763af6ab5fSopenharmony_ci }) 2773af6ab5fSopenharmony_ci ) { 2783af6ab5fSopenharmony_ci return false; 2793af6ab5fSopenharmony_ci } 2803af6ab5fSopenharmony_ci 2813af6ab5fSopenharmony_ci return ( 2823af6ab5fSopenharmony_ci !pathContainsDirectory(path.resolve(fileFsPath), ARKTS_IGNORE_DIRS_OH_MODULES) || 2833af6ab5fSopenharmony_ci !!options.isFileFromModuleCb?.(fileFsPath) 2843af6ab5fSopenharmony_ci ); 2853af6ab5fSopenharmony_ci} 2863af6ab5fSopenharmony_ci 2873af6ab5fSopenharmony_cifunction freeMemory(): void { 2883af6ab5fSopenharmony_ci clearPathHelperCache(); 2893af6ab5fSopenharmony_ci} 290