1/* 2 * Copyright (c) 2024 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 const typeScriptLinter = createTypeScriptLinter(program, tscStrictDiagnostics, sdkOptions); 80 LibraryTypeCallDiagnosticChecker.instance.rebuildTscDiagnostics(tscStrictDiagnostics); 81 const interopTypescriptLinter = createInteropTypescriptLinter(program, !!sdkOptions?.isUseRtLogic); 82 processFiles(srcFiles, { 83 incrementalLinterState, 84 typeScriptLinter, 85 interopTypescriptLinter, 86 tscStrictDiagnostics, 87 diagnostics, 88 etsLoaderPath, 89 tsImportSendableEnable 90 }); 91 timePrinterInstance.appendTime(TimePhase.LINT); 92 if (buildInfoWriteFile) { 93 IncrementalLinterState.emitBuildInfo(buildInfoWriteFile, tscDiagnosticsLinter.getBuilderProgram()); 94 timePrinterInstance.appendTime(TimePhase.EMIT_BUILD_INFO); 95 } 96 releaseResources(); 97 return diagnostics; 98} 99 100function processFiles(srcFiles: ts.SourceFile[], lintParameter: LintParameter): void { 101 for (const fileToLint of srcFiles) { 102 const scriptKind = getScriptKind(fileToLint); 103 if (scriptKind !== ts.ScriptKind.ETS && scriptKind !== ts.ScriptKind.TS) { 104 continue; 105 } 106 107 const currentDiagnostics = getDiagnostic(fileToLint, scriptKind, lintParameter); 108 lintParameter.diagnostics.push(...currentDiagnostics); 109 lintParameter.incrementalLinterState.updateDiagnostics(fileToLint, currentDiagnostics); 110 } 111} 112 113function getDiagnostic( 114 fileToLint: ts.SourceFile, 115 scriptKind: ts.ScriptKind, 116 lintParameter: LintParameter 117): ts.Diagnostic[] { 118 let currentDiagnostics: ts.Diagnostic[] = []; 119 if (lintParameter.incrementalLinterState.isFileChanged(fileToLint)) { 120 if (scriptKind === ts.ScriptKind.ETS) { 121 lintParameter.typeScriptLinter.lint(fileToLint); 122 123 // Get list of bad nodes from the current run. 124 currentDiagnostics = lintParameter.tscStrictDiagnostics.get(path.normalize(fileToLint.fileName)) ?? []; 125 lintParameter.typeScriptLinter.problemsInfos.forEach((x) => { 126 return currentDiagnostics.push(translateDiag(fileToLint, x)); 127 }); 128 lintParameter.typeScriptLinter.problemsInfos.length = 0; 129 } else { 130 if ( 131 path.basename(fileToLint.fileName).toLowerCase(). 132 indexOf('@kit.') === 0 || 133 ts.isOHModules(fileToLint.fileName) 134 ) { 135 return currentDiagnostics; 136 } 137 138 const isInSdk = lintParameter.etsLoaderPath ? 139 path. 140 normalize(fileToLint.fileName). 141 // @ts-expect-error : should expect method 142 indexOf(ts.resolvePath(lintParameter.etsLoaderPath, '../..')) === 0 : 143 false; 144 if (!lintParameter.tsImportSendableEnable && !isInSdk) { 145 return currentDiagnostics; 146 } 147 148 lintParameter.interopTypescriptLinter.lint(fileToLint); 149 lintParameter.interopTypescriptLinter.problemsInfos.forEach((x) => { 150 return currentDiagnostics.push(translateDiag(fileToLint, x)); 151 }); 152 lintParameter.interopTypescriptLinter.problemsInfos.length = 0; 153 } 154 } else { 155 // Get diagnostics from old run. 156 currentDiagnostics = lintParameter.incrementalLinterState.getOldDiagnostics(fileToLint); 157 } 158 return currentDiagnostics; 159} 160 161function getSrcFiles(program: ts.Program, srcFile?: ts.SourceFile): ts.SourceFile[] { 162 let srcFiles: ts.SourceFile[] = []; 163 if (srcFile) { 164 srcFiles.push(srcFile); 165 } else { 166 srcFiles = program.getSourceFiles() as ts.SourceFile[]; 167 } 168 return srcFiles; 169} 170 171function createTypeScriptLinter( 172 program: ts.Program, 173 tscStrictDiagnostics: Map<string, ts.Diagnostic[]>, 174 sdkOptions?: SdkOptions 175): TypeScriptLinter { 176 TypeScriptLinter.initGlobals(); 177 return new TypeScriptLinter( 178 program.getLinterTypeChecker(), 179 { 180 ideMode: true, 181 enableAutofix: sdkOptions?.needAutoFix, 182 useRtLogic: sdkOptions?.isUseRtLogic, 183 compatibleSdkVersion: program.getCompilerOptions().compatibleSdkVersion, 184 compatibleSdkVersionStage: program.getCompilerOptions().compatibleSdkVersionStage 185 }, 186 tscStrictDiagnostics 187 ); 188} 189 190function createInteropTypescriptLinter(program: ts.Program, useRtLogic: boolean): InteropTypescriptLinter { 191 InteropTypescriptLinter.initGlobals(); 192 return new InteropTypescriptLinter( 193 program.getLinterTypeChecker(), 194 program.getCompilerOptions(), 195 { 196 ideMode: true, 197 useRtLogic: useRtLogic 198 }, 199 program.getCompilerOptions().etsLoaderPath 200 ); 201} 202 203// Reclaim memory for Hvigor with "no-parallel" and "daemon". 204function releaseResources(): void {} 205