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