13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ciimport { Logger } from '../lib/Logger'; 173af6ab5fSopenharmony_ciimport { logTscDiagnostic } from '../lib/utils/functions/LogTscDiagnostic'; 183af6ab5fSopenharmony_ciimport type { CommandLineOptions } from '../lib/CommandLineOptions'; 193af6ab5fSopenharmony_ciimport { Command, Option } from 'commander'; 203af6ab5fSopenharmony_ciimport * as ts from 'typescript'; 213af6ab5fSopenharmony_ciimport * as fs from 'node:fs'; 223af6ab5fSopenharmony_ciimport * as path from 'node:path'; 233af6ab5fSopenharmony_ci 243af6ab5fSopenharmony_ciconst TS_EXT = '.ts'; 253af6ab5fSopenharmony_ciconst TSX_EXT = '.tsx'; 263af6ab5fSopenharmony_ciconst ETS_EXT = '.ets'; 273af6ab5fSopenharmony_ci 283af6ab5fSopenharmony_cilet inputFiles: string[]; 293af6ab5fSopenharmony_cilet responseFile = ''; 303af6ab5fSopenharmony_cifunction addSrcFile(value: string): void { 313af6ab5fSopenharmony_ci if (value.startsWith('@')) { 323af6ab5fSopenharmony_ci responseFile = value; 333af6ab5fSopenharmony_ci } else { 343af6ab5fSopenharmony_ci inputFiles.push(value); 353af6ab5fSopenharmony_ci } 363af6ab5fSopenharmony_ci} 373af6ab5fSopenharmony_ci 383af6ab5fSopenharmony_ciconst getFiles = (dir: string): string[] => { 393af6ab5fSopenharmony_ci const resultFiles: string[] = []; 403af6ab5fSopenharmony_ci 413af6ab5fSopenharmony_ci const files = fs.readdirSync(dir); 423af6ab5fSopenharmony_ci for (let i = 0; i < files.length; ++i) { 433af6ab5fSopenharmony_ci const name = path.join(dir, files[i]); 443af6ab5fSopenharmony_ci if (fs.statSync(name).isDirectory()) { 453af6ab5fSopenharmony_ci resultFiles.push(...getFiles(name)); 463af6ab5fSopenharmony_ci } else { 473af6ab5fSopenharmony_ci const extension = path.extname(name); 483af6ab5fSopenharmony_ci if (extension === TS_EXT || extension === TSX_EXT || extension === ETS_EXT) { 493af6ab5fSopenharmony_ci resultFiles.push(name); 503af6ab5fSopenharmony_ci } 513af6ab5fSopenharmony_ci } 523af6ab5fSopenharmony_ci } 533af6ab5fSopenharmony_ci 543af6ab5fSopenharmony_ci return resultFiles; 553af6ab5fSopenharmony_ci}; 563af6ab5fSopenharmony_ci 573af6ab5fSopenharmony_cifunction addProjectFolder(projectFolder: string, previous: string[]): string[] { 583af6ab5fSopenharmony_ci return previous.concat([projectFolder]); 593af6ab5fSopenharmony_ci} 603af6ab5fSopenharmony_ci 613af6ab5fSopenharmony_cifunction formCommandLineOptions(program: Command): CommandLineOptions { 623af6ab5fSopenharmony_ci const opts: CommandLineOptions = { 633af6ab5fSopenharmony_ci inputFiles: inputFiles, 643af6ab5fSopenharmony_ci warningsAsErrors: false, 653af6ab5fSopenharmony_ci enableAutofix: false, 663af6ab5fSopenharmony_ci arkts2: false 673af6ab5fSopenharmony_ci }; 683af6ab5fSopenharmony_ci const options = program.opts(); 693af6ab5fSopenharmony_ci if (options.TSC_Errors) { 703af6ab5fSopenharmony_ci opts.logTscErrors = true; 713af6ab5fSopenharmony_ci } 723af6ab5fSopenharmony_ci if (options.devecoPluginMode) { 733af6ab5fSopenharmony_ci opts.ideMode = true; 743af6ab5fSopenharmony_ci } 753af6ab5fSopenharmony_ci if (options.testMode) { 763af6ab5fSopenharmony_ci opts.testMode = true; 773af6ab5fSopenharmony_ci } 783af6ab5fSopenharmony_ci if (options.projectFolder) { 793af6ab5fSopenharmony_ci doProjectFolderArg(options.projectFolder, opts); 803af6ab5fSopenharmony_ci } 813af6ab5fSopenharmony_ci if (options.project) { 823af6ab5fSopenharmony_ci doProjectArg(options.project, opts); 833af6ab5fSopenharmony_ci } 843af6ab5fSopenharmony_ci if (options.autofix) { 853af6ab5fSopenharmony_ci opts.enableAutofix = true; 863af6ab5fSopenharmony_ci } 873af6ab5fSopenharmony_ci if (options.arkts2) { 883af6ab5fSopenharmony_ci opts.arkts2 = true; 893af6ab5fSopenharmony_ci } 903af6ab5fSopenharmony_ci if (options.warningsAsErrors) { 913af6ab5fSopenharmony_ci opts.warningsAsErrors = true; 923af6ab5fSopenharmony_ci } 933af6ab5fSopenharmony_ci return opts; 943af6ab5fSopenharmony_ci} 953af6ab5fSopenharmony_ci 963af6ab5fSopenharmony_ciexport function parseCommandLine(commandLineArgs: string[]): CommandLineOptions { 973af6ab5fSopenharmony_ci const program = new Command(); 983af6ab5fSopenharmony_ci program.name('tslinter').description('Linter for TypeScript sources'). 993af6ab5fSopenharmony_ci version('0.0.1'); 1003af6ab5fSopenharmony_ci program. 1013af6ab5fSopenharmony_ci option('-E, --TSC_Errors', 'show error messages from Tsc'). 1023af6ab5fSopenharmony_ci option('--test-mode', 'run linter as if running TS test files'). 1033af6ab5fSopenharmony_ci option('--deveco-plugin-mode', 'run as IDE plugin'). 1043af6ab5fSopenharmony_ci option('-p, --project <project_file>', 'path to TS project config file'). 1053af6ab5fSopenharmony_ci option('--project-folder <project_folder>', 'path to folder containig TS files to verify', addProjectFolder, []). 1063af6ab5fSopenharmony_ci option('--autofix', 'automatically fix problems found by linter'). 1073af6ab5fSopenharmony_ci option('--arkts-2', 'enable ArkTS 2.0 mode'). 1083af6ab5fSopenharmony_ci addOption(new Option('--warnings-as-errors', 'treat warnings as errors').hideHelp(true)); 1093af6ab5fSopenharmony_ci program.argument('[srcFile...]', 'files to be verified', addSrcFile); 1103af6ab5fSopenharmony_ci 1113af6ab5fSopenharmony_ci inputFiles = []; 1123af6ab5fSopenharmony_ci // method parse() eats two first args, so make them dummy 1133af6ab5fSopenharmony_ci let cmdArgs: string[] = ['dummy', 'dummy']; 1143af6ab5fSopenharmony_ci cmdArgs.push(...commandLineArgs); 1153af6ab5fSopenharmony_ci program.parse(cmdArgs); 1163af6ab5fSopenharmony_ci if (responseFile !== '') { 1173af6ab5fSopenharmony_ci try { 1183af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 1193af6ab5fSopenharmony_ci commandLineArgs = fs. 1203af6ab5fSopenharmony_ci readFileSync(responseFile.slice(1)). 1213af6ab5fSopenharmony_ci toString(). 1223af6ab5fSopenharmony_ci split('\n'). 1233af6ab5fSopenharmony_ci filter((e) => { 1243af6ab5fSopenharmony_ci return e.trimEnd(); 1253af6ab5fSopenharmony_ci }); 1263af6ab5fSopenharmony_ci cmdArgs = ['dummy', 'dummy']; 1273af6ab5fSopenharmony_ci cmdArgs.push(...commandLineArgs); 1283af6ab5fSopenharmony_ci program.parse(cmdArgs); 1293af6ab5fSopenharmony_ci } catch (error) { 1303af6ab5fSopenharmony_ci Logger.error('Failed to read response file: ' + error); 1313af6ab5fSopenharmony_ci process.exit(-1); 1323af6ab5fSopenharmony_ci } 1333af6ab5fSopenharmony_ci } 1343af6ab5fSopenharmony_ci 1353af6ab5fSopenharmony_ci return formCommandLineOptions(program); 1363af6ab5fSopenharmony_ci} 1373af6ab5fSopenharmony_ci 1383af6ab5fSopenharmony_cifunction doProjectFolderArg(prjFolders: string[], opts: CommandLineOptions): void { 1393af6ab5fSopenharmony_ci for (let i = 0; i < prjFolders.length; i++) { 1403af6ab5fSopenharmony_ci const prjFolderPath = prjFolders[i]; 1413af6ab5fSopenharmony_ci try { 1423af6ab5fSopenharmony_ci opts.inputFiles.push(...getFiles(prjFolderPath)); 1433af6ab5fSopenharmony_ci } catch (error) { 1443af6ab5fSopenharmony_ci Logger.error('Failed to read folder: ' + error); 1453af6ab5fSopenharmony_ci process.exit(-1); 1463af6ab5fSopenharmony_ci } 1473af6ab5fSopenharmony_ci } 1483af6ab5fSopenharmony_ci} 1493af6ab5fSopenharmony_ci 1503af6ab5fSopenharmony_cifunction doProjectArg(cfgPath: string, opts: CommandLineOptions): void { 1513af6ab5fSopenharmony_ci // Process project file (tsconfig.json) and retrieve config arguments. 1523af6ab5fSopenharmony_ci const configFile = cfgPath; 1533af6ab5fSopenharmony_ci 1543af6ab5fSopenharmony_ci const host: ts.ParseConfigFileHost = ts.sys as ts.System & ts.ParseConfigFileHost; 1553af6ab5fSopenharmony_ci 1563af6ab5fSopenharmony_ci const diagnostics: ts.Diagnostic[] = []; 1573af6ab5fSopenharmony_ci 1583af6ab5fSopenharmony_ci try { 1593af6ab5fSopenharmony_ci const oldUnrecoverableDiagnostic = host.onUnRecoverableConfigFileDiagnostic; 1603af6ab5fSopenharmony_ci host.onUnRecoverableConfigFileDiagnostic = (diagnostic: ts.Diagnostic): void => { 1613af6ab5fSopenharmony_ci diagnostics.push(diagnostic); 1623af6ab5fSopenharmony_ci }; 1633af6ab5fSopenharmony_ci opts.parsedConfigFile = ts.getParsedCommandLineOfConfigFile(configFile, {}, host); 1643af6ab5fSopenharmony_ci host.onUnRecoverableConfigFileDiagnostic = oldUnrecoverableDiagnostic; 1653af6ab5fSopenharmony_ci 1663af6ab5fSopenharmony_ci if (opts.parsedConfigFile) { 1673af6ab5fSopenharmony_ci diagnostics.push(...ts.getConfigFileParsingDiagnostics(opts.parsedConfigFile)); 1683af6ab5fSopenharmony_ci } 1693af6ab5fSopenharmony_ci 1703af6ab5fSopenharmony_ci if (diagnostics.length > 0) { 1713af6ab5fSopenharmony_ci // Log all diagnostic messages and exit program. 1723af6ab5fSopenharmony_ci Logger.error('Failed to read config file.'); 1733af6ab5fSopenharmony_ci logTscDiagnostic(diagnostics, Logger.info); 1743af6ab5fSopenharmony_ci process.exit(-1); 1753af6ab5fSopenharmony_ci } 1763af6ab5fSopenharmony_ci } catch (error) { 1773af6ab5fSopenharmony_ci Logger.error('Failed to read config file: ' + error); 1783af6ab5fSopenharmony_ci process.exit(-1); 1793af6ab5fSopenharmony_ci } 1803af6ab5fSopenharmony_ci} 181