1/* 2 * Copyright (c) 2024-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, softwareP 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 ts from 'typescript'; 17import * as path from 'node:path'; 18import type { ProblemInfo } from '../../lib/ProblemInfo'; 19import { ProblemSeverity } from '../../lib/ProblemSeverity'; 20import { TypeScriptLinter } from '../../lib/TypeScriptLinter'; 21import { getTscDiagnostics } from '../../lib/ts-diagnostics/GetTscDiagnostics'; 22import { ArkTSLinterTimePrinter, TimePhase } from '../ArkTSTimePrinter'; 23import { getScriptKind } from '../../lib/utils/functions/GetScriptKind'; 24import { SdkTSCCompiledProgram } from './SdkTSCCompiledProgram'; 25import { IncrementalLinterState } from './IncrementalLinter'; 26import { InteropTypescriptLinter } from '../../lib/InteropTypescriptLinter'; 27import type { SdkOptions } from './SdkOptions'; 28import type { LintParameter } from './LintParameter'; 29import { LibraryTypeCallDiagnosticChecker } from '../../lib/utils/functions/LibraryTypeCallDiagnosticChecker'; 30 31function makeDiag( 32 category: ts.DiagnosticCategory, 33 file: ts.SourceFile, 34 length: number, 35 problemInfo: ProblemInfo 36): ts.Diagnostic { 37 const code = problemInfo.ruleTag; 38 const start = problemInfo.start; 39 const messageText = problemInfo.rule; 40 return { category, code, file, start, length, messageText }; 41} 42 43export function translateDiag(srcFile: ts.SourceFile, problemInfo: ProblemInfo): ts.Diagnostic { 44 const severity = 45 problemInfo.severity === ProblemSeverity.ERROR ? ts.DiagnosticCategory.Error : ts.DiagnosticCategory.Warning; 46 const length = problemInfo.end - problemInfo.start + 1; 47 return makeDiag(severity, srcFile, length, problemInfo); 48} 49 50export function runArkTSLinter( 51 tsBuilderProgram: ts.BuilderProgram, 52 srcFile?: ts.SourceFile, 53 buildInfoWriteFile?: ts.WriteFileCallback, 54 arkTSVersion?: string, 55 sdkOptions?: SdkOptions 56): ts.Diagnostic[] { 57 const diagnostics: ts.Diagnostic[] = []; 58 const tscDiagnosticsLinter = new SdkTSCCompiledProgram(tsBuilderProgram); 59 const program = tscDiagnosticsLinter.getProgram(); 60 const incrementalLinterState = new IncrementalLinterState( 61 tsBuilderProgram, 62 arkTSVersion, 63 program.getCompilerOptions() 64 ); 65 incrementalLinterState.updateProgramStateArkTSVersion(arkTSVersion); 66 const timePrinterInstance = ArkTSLinterTimePrinter.getInstance(); 67 timePrinterInstance.appendTime(TimePhase.INIT); 68 tscDiagnosticsLinter.updateCompilationDiagnostics(); 69 const srcFiles: ts.SourceFile[] = getSrcFiles(program, srcFile); 70 const tscStrictDiagnostics = getTscDiagnostics( 71 tscDiagnosticsLinter, 72 srcFiles.filter((file) => { 73 return incrementalLinterState.isFileChanged(file); 74 }) 75 ); 76 timePrinterInstance.appendTime(TimePhase.GET_TSC_DIAGNOSTICS); 77 const etsLoaderPath = program.getCompilerOptions().etsLoaderPath; 78 const tsImportSendableEnable = program.getCompilerOptions().tsImportSendableEnable; 79 LibraryTypeCallDiagnosticChecker.instance.rebuildTscDiagnostics(tscStrictDiagnostics); 80 const lintParam: LintParameter = { 81 incrementalLinterState, 82 tscStrictDiagnostics, 83 diagnostics, 84 etsLoaderPath, 85 tsImportSendableEnable, 86 program, 87 sdkOptions 88 }; 89 processFiles(srcFiles, lintParam, tscStrictDiagnostics); 90 timePrinterInstance.appendTime(TimePhase.LINT); 91 if (buildInfoWriteFile) { 92 IncrementalLinterState.emitBuildInfo(buildInfoWriteFile, tscDiagnosticsLinter.getBuilderProgram()); 93 timePrinterInstance.appendTime(TimePhase.EMIT_BUILD_INFO); 94 } 95 releaseResources(); 96 return diagnostics; 97} 98 99function processFiles( 100 srcFiles: ts.SourceFile[], 101 lintParameter: LintParameter, 102 tscStrictDiagnostics: Map<string, ts.Diagnostic[]> 103): void { 104 for (const fileToLint of srcFiles) { 105 const scriptKind = getScriptKind(fileToLint); 106 if (scriptKind !== ts.ScriptKind.ETS && scriptKind !== ts.ScriptKind.TS) { 107 continue; 108 } 109 110 const currentDiagnostics = getDiagnostic(fileToLint, scriptKind, lintParameter, tscStrictDiagnostics); 111 lintParameter.diagnostics.push(...currentDiagnostics); 112 lintParameter.incrementalLinterState.updateDiagnostics(fileToLint, currentDiagnostics); 113 } 114} 115 116function getDiagnostic( 117 fileToLint: ts.SourceFile, 118 scriptKind: ts.ScriptKind, 119 lintParameter: LintParameter, 120 tscStrictDiagnostics: Map<string, ts.Diagnostic[]> 121): ts.Diagnostic[] { 122 let currentDiagnostics: ts.Diagnostic[] = []; 123 if (lintParameter.incrementalLinterState.isFileChanged(fileToLint)) { 124 if (scriptKind === ts.ScriptKind.ETS) { 125 const typeScriptLinter = createTypeScriptLinter(fileToLint, lintParameter, tscStrictDiagnostics); 126 typeScriptLinter.lint(); 127 128 // Get list of bad nodes from the current run. 129 currentDiagnostics = lintParameter.tscStrictDiagnostics.get(path.normalize(fileToLint.fileName)) ?? []; 130 typeScriptLinter.problemsInfos.forEach((x) => { 131 return currentDiagnostics.push(translateDiag(fileToLint, x)); 132 }); 133 } else { 134 if ( 135 path.basename(fileToLint.fileName).toLowerCase(). 136 indexOf('@kit.') === 0 || 137 ts.isOHModules(fileToLint.fileName) 138 ) { 139 return currentDiagnostics; 140 } 141 142 const isInSdk = lintParameter.etsLoaderPath ? 143 path. 144 normalize(fileToLint.fileName). 145 // @ts-expect-error : should expect method 146 indexOf(ts.resolvePath(lintParameter.etsLoaderPath, '../..')) === 0 : 147 false; 148 if (!lintParameter.tsImportSendableEnable && !isInSdk) { 149 return currentDiagnostics; 150 } 151 152 const interopTypescriptLinter = createInteropTypescriptLinter(lintParameter.program, fileToLint, lintParameter); 153 interopTypescriptLinter.lint(); 154 interopTypescriptLinter.problemsInfos.forEach((x) => { 155 return currentDiagnostics.push(translateDiag(fileToLint, x)); 156 }); 157 } 158 } else { 159 // Get diagnostics from old run. 160 currentDiagnostics = lintParameter.incrementalLinterState.getOldDiagnostics(fileToLint); 161 } 162 return currentDiagnostics; 163} 164 165function getSrcFiles(program: ts.Program, srcFile?: ts.SourceFile): ts.SourceFile[] { 166 let srcFiles: ts.SourceFile[] = []; 167 if (srcFile) { 168 srcFiles.push(srcFile); 169 } else { 170 srcFiles = program.getSourceFiles() as ts.SourceFile[]; 171 } 172 return srcFiles; 173} 174 175function createTypeScriptLinter( 176 sourceFile: ts.SourceFile, 177 lintParameter: LintParameter, 178 tscStrictDiagnostics: Map<string, ts.Diagnostic[]> 179): TypeScriptLinter { 180 TypeScriptLinter.initGlobals(); 181 return new TypeScriptLinter( 182 lintParameter.program.getLinterTypeChecker(), 183 { 184 enableAutofix: lintParameter.sdkOptions?.needAutoFix, 185 useRtLogic: lintParameter.sdkOptions?.isUseRtLogic, 186 compatibleSdkVersion: lintParameter.program.getCompilerOptions().compatibleSdkVersion, 187 compatibleSdkVersionStage: lintParameter.program.getCompilerOptions().compatibleSdkVersionStage 188 }, 189 sourceFile, 190 tscStrictDiagnostics 191 ); 192} 193 194function createInteropTypescriptLinter( 195 program: ts.Program, 196 sourceFile: ts.SourceFile, 197 lintParameter: LintParameter 198): InteropTypescriptLinter { 199 InteropTypescriptLinter.initGlobals(); 200 return new InteropTypescriptLinter( 201 program.getLinterTypeChecker(), 202 program.getCompilerOptions(), 203 { 204 etsLoaderPath: lintParameter.etsLoaderPath, 205 useRtLogic: lintParameter.sdkOptions?.isUseRtLogic 206 }, 207 sourceFile 208 ); 209} 210 211// Reclaim memory for Hvigor with "no-parallel" and "daemon". 212function releaseResources(): void {} 213