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 16import fs from 'fs'; 17import path from 'path'; 18import * as ts from 'typescript'; 19import { 20 globalProgram, 21 projectConfig 22} from '../main'; 23import { 24 toUnixPath, 25 getRollupCache, 26 setRollupCache 27} from './utils'; 28import { 29 resolveModuleNames, 30 resolveTypeReferenceDirectives, 31 fileHashScriptVersion, 32 LanguageServiceCache, 33} from './ets_checker'; 34import { ARKTS_LINTER_BUILD_INFO_SUFFIX } from './pre_define'; 35 36const arkTSDir: string = 'ArkTS'; 37const arkTSLinterOutputFileName: string = 'ArkTSLinter_output.json'; 38const spaceNumBeforeJsonLine = 2; 39 40interface OutputInfo { 41 categoryInfo: string | undefined; 42 fileName: string | undefined; 43 line: number | undefined; 44 character: number | undefined; 45 messageText: string | ts.DiagnosticMessageChain; 46} 47 48export enum ArkTSLinterMode { 49 NOT_USE = 0, 50 COMPATIBLE_MODE = 1, 51 STANDARD_MODE = 2 52} 53 54export enum ArkTSVersion { 55 ArkTS_1_0, 56 ArkTS_1_1, 57} 58 59export type ProcessDiagnosticsFunc = (diagnostics: ts.Diagnostic) => void; 60 61function getArkTSVersionString(arkTSVersion: ArkTSVersion): string { 62 return arkTSVersion === ArkTSVersion.ArkTS_1_0 ? 'ArkTS_1_0' : 'ArkTS_1_1'; 63} 64 65export function doArkTSLinter(arkTSVersion: ArkTSVersion, arkTSMode: ArkTSLinterMode, 66 builderProgram: ts.BuilderProgram, printDiagnostic: ProcessDiagnosticsFunc, shouldWriteFile: boolean = true, 67 buildInfoWriteFile?: ts.WriteFileCallback): ts.Diagnostic[] { 68 if (arkTSMode === ArkTSLinterMode.NOT_USE) { 69 return []; 70 } 71 72 let diagnostics: ts.Diagnostic[] = []; 73 74 if (arkTSVersion === ArkTSVersion.ArkTS_1_0) { 75 diagnostics = ts.ArkTSLinter_1_0.runArkTSLinter(builderProgram, /*srcFile*/ undefined, buildInfoWriteFile, 76 getArkTSVersionString(arkTSVersion)); 77 } else { 78 diagnostics = ts.ArkTSLinter_1_1.runArkTSLinter(builderProgram, /*srcFile*/ undefined, buildInfoWriteFile, 79 getArkTSVersionString(arkTSVersion)); 80 } 81 82 removeOutputFile(); 83 if (diagnostics.length === 0) { 84 return []; 85 } 86 87 if (arkTSMode === ArkTSLinterMode.COMPATIBLE_MODE) { 88 processArkTSLinterReportAsWarning(diagnostics, printDiagnostic, shouldWriteFile); 89 } else { 90 processArkTSLinterReportAsError(diagnostics, printDiagnostic); 91 } 92 93 return diagnostics; 94} 95 96function processArkTSLinterReportAsError(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void { 97 diagnostics.forEach((diagnostic: ts.Diagnostic) => { 98 printDiagnostic(diagnostic); 99 }); 100 printArkTSLinterFAQ(diagnostics, printDiagnostic); 101} 102 103function processArkTSLinterReportAsWarning(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc, 104 shouldWriteFile: boolean): void { 105 const filePath = shouldWriteFile ? writeOutputFile(diagnostics) : undefined; 106 if (filePath === undefined) { 107 diagnostics.forEach((diagnostic: ts.Diagnostic) => { 108 const originalCategory = diagnostic.category; 109 diagnostic.category = ts.DiagnosticCategory.Warning; 110 printDiagnostic(diagnostic); 111 diagnostic.category = originalCategory; 112 }); 113 printArkTSLinterFAQ(diagnostics, printDiagnostic); 114 return; 115 } 116 const logMessage = `Has ${diagnostics.length} ArkTS Linter Error. You can get the output in ${filePath}`; 117 const arkTSDiagnostic: ts.Diagnostic = { 118 file: undefined, 119 start: undefined, 120 length: undefined, 121 messageText: logMessage, 122 category: ts.DiagnosticCategory.Warning, 123 code: -1, 124 reportsUnnecessary: undefined, 125 reportsDeprecated: undefined 126 }; 127 printDiagnostic(arkTSDiagnostic); 128 129 printArkTSLinterFAQ(diagnostics, printDiagnostic); 130} 131 132function writeOutputFile(diagnostics: ts.Diagnostic[]): string | undefined { 133 let filePath: string = toUnixPath(projectConfig.cachePath); 134 if (!fs.existsSync(filePath)) { 135 return undefined; 136 } 137 filePath = toUnixPath(path.join(filePath, arkTSDir)); 138 if (!fs.existsSync(filePath)) { 139 fs.mkdirSync(filePath); 140 } 141 filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName))); 142 const outputInfo: OutputInfo[] = []; 143 diagnostics.forEach((diagnostic: ts.Diagnostic) => { 144 const { line, character }: ts.LineAndCharacter = 145 diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); 146 outputInfo.push({ 147 categoryInfo: diagnostic.category === ts.DiagnosticCategory.Error ? 'Error' : 'Warning', 148 fileName: diagnostic.file?.fileName, 149 line: line + 1, 150 character: character + 1, 151 messageText: diagnostic.messageText 152 }); 153 }); 154 let output: string | undefined = filePath; 155 try { 156 fs.writeFileSync(filePath, JSON.stringify(outputInfo, undefined, spaceNumBeforeJsonLine)); 157 } catch { 158 output = undefined; 159 } 160 return output; 161} 162 163function removeOutputFile(): void { 164 let filePath: string = toUnixPath(projectConfig.cachePath); 165 if (!fs.existsSync(filePath)) { 166 return; 167 } 168 filePath = toUnixPath(path.join(filePath, arkTSDir)); 169 if (!fs.existsSync(filePath)) { 170 return; 171 } 172 filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName))); 173 if (fs.existsSync(filePath)) { 174 fs.rmSync(filePath); 175 } 176} 177 178function printArkTSLinterFAQ(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void { 179 if (diagnostics === undefined || diagnostics.length === undefined || diagnostics.length <= 0) { 180 return; 181 } 182 183 const logMessageFAQ = 'For details about ArkTS syntax errors, see FAQs'; 184 const arkTSFAQDiagnostic: ts.Diagnostic = { 185 file: undefined, 186 start: undefined, 187 length: undefined, 188 messageText: logMessageFAQ, 189 category: ts.DiagnosticCategory.Warning, 190 code: -1, 191 reportsUnnecessary: undefined, 192 reportsDeprecated: undefined 193 }; 194 printDiagnostic(arkTSFAQDiagnostic); 195} 196