1/* 2 * Copyright (c) 2022-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, 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 os from 'node:os'; 18import * as path from 'node:path'; 19import * as readline from 'node:readline'; 20import type { CommandLineOptions } from '../lib/CommandLineOptions'; 21import { lint } from '../lib/LinterRunner'; 22import { Logger } from '../lib/Logger'; 23import type { ProblemInfo } from '../lib/ProblemInfo'; 24import { TypeScriptLinter } from '../lib/TypeScriptLinter'; 25import { parseCommandLine } from './CommandLineParser'; 26import { compileLintOptions } from './Compiler'; 27import type { LintOptions } from '../lib/LintOptions'; 28 29export function run(): void { 30 const commandLineArgs = process.argv.slice(2); 31 if (commandLineArgs.length === 0) { 32 Logger.info('Command line error: no arguments'); 33 process.exit(-1); 34 } 35 36 const cmdOptions = parseCommandLine(commandLineArgs); 37 38 if (cmdOptions.testMode) { 39 TypeScriptLinter.testMode = true; 40 } 41 42 TypeScriptLinter.initGlobals(); 43 44 if (!cmdOptions.ideMode) { 45 const compileOptions = compileLintOptions(cmdOptions); 46 const result = lint(compileOptions, getEtsLoaderPath(compileOptions)); 47 process.exit(result.errorNodes > 0 ? 1 : 0); 48 } else { 49 runIDEMode(cmdOptions); 50 } 51} 52 53function getTempFileName(): string { 54 return path.join(os.tmpdir(), Math.floor(Math.random() * 10000000).toString() + '_linter_tmp_file.ts'); 55} 56 57function showJSONMessage(problems: ProblemInfo[][]): void { 58 const jsonMessage = problems[0].map((x) => { 59 return { 60 line: x.line, 61 column: x.column, 62 start: x.start, 63 end: x.end, 64 type: x.type, 65 suggest: x.suggest, 66 rule: x.rule, 67 severity: x.severity, 68 autofix: x.autofix 69 }; 70 }); 71 Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); 72} 73 74function runIDEMode(cmdOptions: CommandLineOptions): void { 75 TypeScriptLinter.ideMode = true; 76 const tmpFileName = getTempFileName(); 77 // read data from stdin 78 const writeStream = fs.createWriteStream(tmpFileName, { flags: 'w' }); 79 const rl = readline.createInterface({ 80 input: process.stdin, 81 output: writeStream, 82 terminal: false 83 }); 84 85 rl.on('line', (line: string) => { 86 fs.appendFileSync(tmpFileName, line + '\n'); 87 }); 88 rl.once('close', () => { 89 // end of input 90 writeStream.close(); 91 cmdOptions.inputFiles = [tmpFileName]; 92 if (cmdOptions.parsedConfigFile) { 93 cmdOptions.parsedConfigFile.fileNames.push(tmpFileName); 94 } 95 const compileOptions = compileLintOptions(cmdOptions); 96 const result = lint(compileOptions, getEtsLoaderPath(compileOptions)); 97 const problems = Array.from(result.problemsInfos.values()); 98 if (problems.length === 1) { 99 showJSONMessage(problems); 100 } else { 101 Logger.error('Unexpected error: could not lint file'); 102 } 103 fs.unlinkSync(tmpFileName); 104 }); 105} 106 107export function getEtsLoaderPath(compileOptions: LintOptions): string | undefined { 108 const tsProgram = compileOptions.tscCompiledProgram.getProgram(); 109 return tsProgram.getCompilerOptions().etsLoaderPath; 110} 111