diff --git a/crates/oxc_cli/src/command/lint.rs b/crates/oxc_cli/src/command/lint.rs index b30132658..c1aa8dc33 100644 --- a/crates/oxc_cli/src/command/lint.rs +++ b/crates/oxc_cli/src/command/lint.rs @@ -57,6 +57,10 @@ pub struct LintOptions { #[bpaf(long, short, argument("PATH"))] pub config: Option, + /// TypeScript `tsconfig.json` path for reading path alias and project references for import plugin + #[bpaf(argument("PATH"))] + pub tsconfig: Option, + /// Single file, single path or list of paths #[bpaf(positional("PATH"), many, guard(validate_paths, PATHS_ERROR_MESSAGE))] pub paths: Vec, diff --git a/crates/oxc_cli/src/lint/mod.rs b/crates/oxc_cli/src/lint/mod.rs index c883eaa9a..11795cde9 100644 --- a/crates/oxc_cli/src/lint/mod.rs +++ b/crates/oxc_cli/src/lint/mod.rs @@ -2,7 +2,9 @@ use ignore::gitignore::Gitignore; use std::{env, io::BufWriter, time::Instant, vec::Vec}; use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler}; -use oxc_linter::{partial_loader::LINT_PARTIAL_LOADER_EXT, LintOptions, LintService, Linter}; +use oxc_linter::{ + partial_loader::LINT_PARTIAL_LOADER_EXT, LintOptions, LintService, LintServiceOptions, Linter, +}; use oxc_span::VALID_EXTENSIONS; use crate::{ @@ -37,6 +39,7 @@ impl Runner for LintRunner { fix_options, enable_plugins, config, + tsconfig, output_options, .. } = self.options; @@ -110,7 +113,8 @@ impl Runner for LintRunner { } }; - let lint_service = LintService::new(cwd, &paths, linter); + let options = LintServiceOptions { cwd, paths, tsconfig }; + let lint_service = LintService::new(linter, options); let mut diagnostic_service = Self::get_diagnostic_service(&warning_options, &output_options); diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 32d90bdbc..f216bd59c 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -33,7 +33,7 @@ use crate::{ pub use crate::{ context::LintContext, options::{AllowWarnDeny, LintOptions}, - service::LintService, + service::{LintService, LintServiceOptions}, }; use oxc_semantic::AstNode; diff --git a/crates/oxc_linter/src/service.rs b/crates/oxc_linter/src/service.rs index 420654ce5..fcd00b5f4 100644 --- a/crates/oxc_linter/src/service.rs +++ b/crates/oxc_linter/src/service.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, ffi::OsStr, fs, - path::Path, + path::{Path, PathBuf}, rc::Rc, sync::{Arc, Condvar, Mutex}, }; @@ -23,20 +23,31 @@ use crate::{ Fixer, LintContext, Linter, Message, }; +pub struct LintServiceOptions { + /// Current working directory + pub cwd: Box, + + /// All paths to lint + pub paths: Vec>, + + /// TypeScript `tsconfig.json` path for reading path alias and project references + pub tsconfig: Option, +} + #[derive(Clone)] pub struct LintService { runtime: Arc, } impl LintService { - pub fn new(cwd: Box, paths: &[Box], linter: Linter) -> Self { - let runtime = Arc::new(Runtime::new(cwd, paths, linter)); + pub fn new(linter: Linter, options: LintServiceOptions) -> Self { + let runtime = Arc::new(Runtime::new(linter, options)); Self { runtime } } #[cfg(test)] - pub(crate) fn from_linter(cwd: Box, paths: &[Box], linter: Linter) -> Self { - let runtime = Arc::new(Runtime::new(cwd, paths, linter)); + pub(crate) fn from_linter(linter: Linter, options: LintServiceOptions) -> Self { + let runtime = Arc::new(Runtime::new(linter, options)); Self { runtime } } @@ -123,11 +134,11 @@ pub struct Runtime { } impl Runtime { - fn new(cwd: Box, paths: &[Box], linter: Linter) -> Self { - let resolver = linter.options().import_plugin.then(Self::get_resolver); + fn new(linter: Linter, options: LintServiceOptions) -> Self { + let resolver = linter.options().import_plugin.then(|| Self::get_resolver(options.tsconfig)); Self { - cwd, - paths: paths.iter().cloned().collect(), + cwd: options.cwd, + paths: options.paths.iter().cloned().collect(), linter, resolver, module_map: ModuleMap::default(), @@ -135,12 +146,19 @@ impl Runtime { } } - fn get_resolver() -> Resolver { + fn get_resolver(tsconfig: Option) -> Resolver { use oxc_resolver::{ResolveOptions, TsconfigOptions, TsconfigReferences}; - let tsconfig_path = std::env::current_dir().map(|p| p.join("tsconfig.json")).ok(); - let tsconfig = tsconfig_path - .filter(|p| p.exists()) - .map(|p| TsconfigOptions { config_file: p, references: TsconfigReferences::Auto }); + let tsconfig = if let Some(path) = tsconfig { + if path.is_file() { + Some(TsconfigOptions { config_file: path, references: TsconfigReferences::Auto }) + } else { + // TODO: crates/oxc_cli/src/lint/mod.rs + eprintln!("Tsconfig {path:?} is not a file"); + None + } + } else { + None + }; Resolver::new(ResolveOptions { extensions: VALID_EXTENSIONS.iter().map(|ext| format!(".{ext}")).collect(), condition_names: vec!["module".into(), "require".into()], diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index 8f90a8c78..c2453a33e 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -9,7 +9,10 @@ use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, GraphicalTheme} use serde::Deserialize; use serde_json::Value; -use crate::{rules::RULES, ESLintSettings, Fixer, LintOptions, LintService, Linter, RuleEnum}; +use crate::{ + rules::RULES, ESLintSettings, Fixer, LintOptions, LintService, LintServiceOptions, Linter, + RuleEnum, +}; #[derive(Eq, PartialEq)] enum TestResult { @@ -209,11 +212,10 @@ impl Tester { self.rule_path.clone() }; - let lint_service = LintService::from_linter( - self.current_working_directory.clone(), - &[path_to_lint.into_boxed_path()], - linter, - ); + let cwd = self.current_working_directory.clone(); + let paths = vec![path_to_lint.into_boxed_path()]; + let options = LintServiceOptions { cwd, paths, tsconfig: None }; + let lint_service = LintService::from_linter(linter, options); let diagnostic_service = DiagnosticService::default(); let tx_error = diagnostic_service.sender(); let result = lint_service.run_source(&allocator, source_text, false, tx_error);