diff --git a/apps/oxlint/src/lib.rs b/apps/oxlint/src/lib.rs index a98163f4db127..75a5315df3067 100644 --- a/apps/oxlint/src/lib.rs +++ b/apps/oxlint/src/lib.rs @@ -4,9 +4,12 @@ mod result; mod runner; mod walk; -pub use crate::{ - command::*, - lint::LintRunner, - result::{CliRunResult, LintResult}, - runner::Runner, -}; +pub mod cli { + + pub use crate::{ + command::*, + lint::LintRunner, + result::{CliRunResult, LintResult}, + runner::Runner, + }; +} diff --git a/apps/oxlint/src/lint/mod.rs b/apps/oxlint/src/lint/mod.rs index e19d40bbbac73..3668e0a24da7a 100644 --- a/apps/oxlint/src/lint/mod.rs +++ b/apps/oxlint/src/lint/mod.rs @@ -3,14 +3,16 @@ use std::{env, io::BufWriter, time::Instant}; use ignore::gitignore::Gitignore; use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler}; use oxc_linter::{ - partial_loader::LINT_PARTIAL_LOADER_EXT, LintOptions, LintService, LintServiceOptions, Linter, + partial_loader::LINT_PARTIAL_LOADER_EXT, LintService, LintServiceOptions, Linter, OxlintOptions, }; use oxc_span::VALID_EXTENSIONS; use crate::{ - command::{LintCommand, OutputFormat, OutputOptions, WarningOptions}, + cli::{ + CliRunResult, LintCommand, LintResult, MiscOptions, OutputFormat, OutputOptions, Runner, + WarningOptions, + }, walk::{Extensions, Walk}, - CliRunResult, LintResult, MiscOptions, Runner, }; pub struct LintRunner { @@ -90,7 +92,7 @@ impl Runner for LintRunner { let number_of_files = paths.len(); let cwd = std::env::current_dir().unwrap().into_boxed_path(); - let lint_options = LintOptions::default() + let lint_options = OxlintOptions::default() .with_filter(filter) .with_config_path(basic_options.config) .with_fix(fix_options.fix_kind()) @@ -184,7 +186,7 @@ impl LintRunner { #[cfg(all(test, not(target_os = "windows")))] mod test { use super::LintRunner; - use crate::{lint_command, CliRunResult, LintResult, Runner}; + use crate::cli::{lint_command, CliRunResult, LintResult, Runner}; fn test(args: &[&str]) -> LintResult { let mut new_args = vec!["--silent"]; diff --git a/apps/oxlint/src/main.rs b/apps/oxlint/src/main.rs index c5f94bfc0623e..867f1e1aa247f 100644 --- a/apps/oxlint/src/main.rs +++ b/apps/oxlint/src/main.rs @@ -7,13 +7,13 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -use oxlint::{CliRunResult, LintRunner, Runner}; +use oxlint::cli::{CliRunResult, LintRunner, Runner}; fn main() -> CliRunResult { init_tracing(); init_miette(); - let command = oxlint::lint_command().run(); + let command = oxlint::cli::lint_command().run(); command.handle_threads(); LintRunner::new(command).run() } diff --git a/apps/oxlint/src/runner.rs b/apps/oxlint/src/runner.rs index a757fe3efec81..46fdd11b514a3 100644 --- a/apps/oxlint/src/runner.rs +++ b/apps/oxlint/src/runner.rs @@ -1,4 +1,4 @@ -use crate::CliRunResult; +use crate::cli::CliRunResult; /// A trait for exposing functionality to the CLI. pub trait Runner { diff --git a/apps/oxlint/src/walk.rs b/apps/oxlint/src/walk.rs index e22e07a9d9466..7c10bacd0d3f6 100644 --- a/apps/oxlint/src/walk.rs +++ b/apps/oxlint/src/walk.rs @@ -6,7 +6,7 @@ use std::{ use ignore::{overrides::OverrideBuilder, DirEntry}; use oxc_span::VALID_EXTENSIONS; -use crate::IgnoreOptions; +use crate::cli::IgnoreOptions; #[derive(Clone)] pub struct Extensions(pub Vec<&'static str>); @@ -142,7 +142,7 @@ mod test { use std::{env, ffi::OsString}; use super::{Extensions, Walk}; - use crate::IgnoreOptions; + use crate::cli::IgnoreOptions; #[test] fn test_walk_with_extensions() { diff --git a/crates/oxc_language_server/src/main.rs b/crates/oxc_language_server/src/main.rs index 4a830be3a9806..28bf03829494b 100644 --- a/crates/oxc_language_server/src/main.rs +++ b/crates/oxc_language_server/src/main.rs @@ -7,7 +7,7 @@ use futures::future::join_all; use globset::Glob; use ignore::gitignore::Gitignore; use log::{debug, error, info}; -use oxc_linter::{FixKind, LintOptions, Linter}; +use oxc_linter::{FixKind, Linter, OxlintOptions}; use serde::{Deserialize, Serialize}; use tokio::sync::{Mutex, OnceCell, RwLock, SetError}; use tower_lsp::{ @@ -345,7 +345,7 @@ impl Backend { let mut linter = self.server_linter.write().await; *linter = ServerLinter::new_with_linter( Linter::from_options( - LintOptions::default() + OxlintOptions::default() .with_fix(FixKind::SafeFix) .with_config_path(Some(config_path)), ) diff --git a/crates/oxc_linter/src/config/mod.rs b/crates/oxc_linter/src/config/mod.rs index 816fe04a87814..d717bf8006bc5 100644 --- a/crates/oxc_linter/src/config/mod.rs +++ b/crates/oxc_linter/src/config/mod.rs @@ -57,7 +57,21 @@ use crate::{ #[serde(default)] pub struct OxlintConfig { /// See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html). - pub(crate) rules: OxlintRules, + pub rules: OxlintRules, + pub settings: OxlintSettings, + /// Environments enable and disable collections of global variables. + pub env: OxlintEnv, + /// Enabled or disabled specific global variables. + pub globals: OxlintGlobals, +} + +/// Configuration used by the linter, fixer, and rules. +/// +/// This is a mapping from the public [`OxlintConfig`] API to a trimmed down +/// version that is also better suited for internal use. Do not expose this +/// struct outside this crate. +#[derive(Debug, Default)] +pub(crate) struct LintConfig { pub(crate) settings: OxlintSettings, /// Environments enable and disable collections of global variables. pub(crate) env: OxlintEnv, @@ -65,6 +79,12 @@ pub struct OxlintConfig { pub(crate) globals: OxlintGlobals, } +impl From for LintConfig { + fn from(config: OxlintConfig) -> Self { + Self { settings: config.settings, env: config.env, globals: config.globals } + } +} + impl OxlintConfig { /// # Errors /// diff --git a/crates/oxc_linter/src/context.rs b/crates/oxc_linter/src/context.rs index 941066f0eb0fc..bef2d143bc308 100644 --- a/crates/oxc_linter/src/context.rs +++ b/crates/oxc_linter/src/context.rs @@ -10,11 +10,11 @@ use oxc_syntax::module_record::ModuleRecord; #[cfg(debug_assertions)] use crate::rule::RuleFixMeta; use crate::{ - config::OxlintRules, + config::LintConfig, disable_directives::{DisableDirectives, DisableDirectivesBuilder}, fixer::{FixKind, Message, RuleFix, RuleFixer}, javascript_globals::GLOBALS, - AllowWarnDeny, FrameworkFlags, OxlintConfig, OxlintEnv, OxlintGlobals, OxlintSettings, + AllowWarnDeny, FrameworkFlags, OxlintEnv, OxlintGlobals, OxlintSettings, }; #[derive(Clone)] @@ -38,7 +38,7 @@ pub struct LintContext<'a> { file_path: Rc, - eslint_config: Arc, + config: Arc, // states current_plugin_name: &'static str, @@ -82,7 +82,7 @@ impl<'a> LintContext<'a> { disable_directives: Rc::new(disable_directives), fix: FixKind::None, file_path: file_path.into(), - eslint_config: Arc::new(OxlintConfig::default()), + config: Arc::new(LintConfig::default()), current_plugin_name: "eslint", current_plugin_prefix: "eslint", current_rule_name: "", @@ -99,8 +99,8 @@ impl<'a> LintContext<'a> { self } - pub fn with_eslint_config(mut self, eslint_config: &Arc) -> Self { - self.eslint_config = Arc::clone(eslint_config); + pub(crate) fn with_config(mut self, config: &Arc) -> Self { + self.config = Arc::clone(config); self } @@ -175,22 +175,18 @@ impl<'a> LintContext<'a> { /// Plugin settings pub fn settings(&self) -> &OxlintSettings { - &self.eslint_config.settings + &self.config.settings } pub fn globals(&self) -> &OxlintGlobals { - &self.eslint_config.globals + &self.config.globals } /// Runtime environments turned on/off by the user. /// /// Examples of environments are `builtin`, `browser`, `node`, etc. pub fn env(&self) -> &OxlintEnv { - &self.eslint_config.env - } - - pub fn rules(&self) -> &OxlintRules { - &self.eslint_config.rules + &self.config.env } pub fn env_contains_var(&self, var: &str) -> bool { diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 3eb3382a1345c..520fc2c70e3fb 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -22,6 +22,8 @@ pub mod table; use std::{io::Write, path::Path, rc::Rc, sync::Arc}; +use config::LintConfig; +use options::LintOptions; use oxc_diagnostics::Error; use oxc_semantic::{AstNode, Semantic}; @@ -30,7 +32,7 @@ pub use crate::{ context::LintContext, fixer::FixKind, frameworks::FrameworkFlags, - options::{AllowWarnDeny, LintOptions}, + options::{AllowWarnDeny, OxlintOptions}, rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity}, service::{LintService, LintServiceOptions}, }; @@ -54,12 +56,12 @@ fn size_asserts() { pub struct Linter { rules: Vec, options: LintOptions, - eslint_config: Arc, + config: Arc, } impl Default for Linter { fn default() -> Self { - Self::from_options(LintOptions::default()).unwrap() + Self::from_options(OxlintOptions::default()).unwrap() } } @@ -67,9 +69,9 @@ impl Linter { /// # Errors /// /// Returns `Err` if there are any errors parsing the configuration file. - pub fn from_options(options: LintOptions) -> Result { - let (rules, eslint_config) = options.derive_rules_and_config()?; - Ok(Self { rules, options, eslint_config: Arc::new(eslint_config) }) + pub fn from_options(options: OxlintOptions) -> Result { + let (rules, config) = options.derive_rules_and_config()?; + Ok(Self { rules, options: options.into(), config: Arc::new(config) }) } #[cfg(test)] @@ -79,9 +81,11 @@ impl Linter { self } + /// Used for testing + #[cfg(test)] #[must_use] - pub fn with_eslint_config(mut self, eslint_config: OxlintConfig) -> Self { - self.eslint_config = Arc::new(eslint_config); + pub(crate) fn with_eslint_config(mut self, config: LintConfig) -> Self { + self.config = Arc::new(config); self } @@ -101,7 +105,7 @@ impl Linter { self } - pub fn options(&self) -> &LintOptions { + pub(crate) fn options(&self) -> &LintOptions { &self.options } @@ -153,7 +157,7 @@ impl Linter { fn create_ctx<'a>(&self, path: &Path, semantic: Rc>) -> LintContext<'a> { let mut ctx = LintContext::new(path.to_path_buf().into_boxed_path(), semantic) .with_fix(self.options.fix) - .with_eslint_config(&self.eslint_config) + .with_config(&self.config) .with_frameworks(self.options.framework_hints); // set file-specific jest/vitest flags diff --git a/crates/oxc_linter/src/options/mod.rs b/crates/oxc_linter/src/options/mod.rs index e2d6e72ec4a37..aa2ce91222c41 100644 --- a/crates/oxc_linter/src/options/mod.rs +++ b/crates/oxc_linter/src/options/mod.rs @@ -7,7 +7,10 @@ use oxc_diagnostics::Error; use rustc_hash::FxHashSet; use crate::{ - config::OxlintConfig, fixer::FixKind, rules::RULES, utils::is_jest_rule_adapted_to_vitest, + config::{LintConfig, OxlintConfig}, + fixer::FixKind, + rules::RULES, + utils::is_jest_rule_adapted_to_vitest, FrameworkFlags, RuleCategory, RuleEnum, RuleWithSeverity, }; @@ -15,7 +18,7 @@ pub use allow_warn_deny::AllowWarnDeny; pub use plugins::LintPluginOptions; #[derive(Debug)] -pub struct LintOptions { +pub struct OxlintOptions { /// Allow / Deny rules in order. [("allow" / "deny", rule name)] /// Defaults to [("deny", "correctness")] pub filter: Vec<(AllowWarnDeny, String)>, @@ -30,7 +33,15 @@ pub struct LintOptions { pub framework_hints: FrameworkFlags, } -impl Default for LintOptions { +#[derive(Debug, Default)] +#[cfg_attr(test, derive(PartialEq))] +pub(crate) struct LintOptions { + pub fix: FixKind, + pub framework_hints: FrameworkFlags, + pub plugins: LintPluginOptions, +} + +impl Default for OxlintOptions { fn default() -> Self { Self { filter: vec![(AllowWarnDeny::Warn, String::from("correctness"))], @@ -41,8 +52,22 @@ impl Default for LintOptions { } } } +/// Subset of options used directly by the [`Linter`]. Derived from +/// [`OxlintOptions`], which is the public-facing API. Do not expose this +/// outside of this crate. +/// +/// [`Linter`]: crate::Linter +impl From for LintOptions { + fn from(options: OxlintOptions) -> Self { + Self { + fix: options.fix, + framework_hints: options.framework_hints, + plugins: options.plugins, + } + } +} -impl LintOptions { +impl OxlintOptions { #[must_use] pub fn with_filter(mut self, filter: Vec<(AllowWarnDeny, String)>) -> Self { if !filter.is_empty() { @@ -146,11 +171,13 @@ impl LintOptions { } } -impl LintOptions { +impl OxlintOptions { /// # Errors /// /// * Returns `Err` if there are any errors parsing the configuration file. - pub fn derive_rules_and_config(&self) -> Result<(Vec, OxlintConfig), Error> { + pub(crate) fn derive_rules_and_config( + &self, + ) -> Result<(Vec, LintConfig), Error> { let config = self.config_path.as_ref().map(|path| OxlintConfig::from_file(path)).transpose()?; @@ -211,7 +238,7 @@ impl LintOptions { // for stable diagnostics output ordering rules.sort_unstable_by_key(|rule| rule.id()); - Ok((rules, config.unwrap_or_default())) + Ok((rules, config.map(Into::into).unwrap_or_default())) } /// Get final filtered rules by reading `self.xxx_plugin` @@ -246,3 +273,15 @@ impl LintOptions { .collect::>() } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_default_conversion() { + let lint_options = LintOptions::default(); + let oxlint_options = OxlintOptions::default(); + assert_eq!(lint_options, oxlint_options.into()); + } +} diff --git a/crates/oxc_linter/src/options/plugins.rs b/crates/oxc_linter/src/options/plugins.rs index 3c4cab9ab855b..d0821fcb7b3f9 100644 --- a/crates/oxc_linter/src/options/plugins.rs +++ b/crates/oxc_linter/src/options/plugins.rs @@ -1,4 +1,5 @@ #[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] #[non_exhaustive] pub struct LintPluginOptions { /// On by default. @@ -115,22 +116,6 @@ impl<'s> FromIterator<&'s str> for LintPluginOptions { #[cfg(test)] mod test { use super::*; - impl PartialEq for LintPluginOptions { - fn eq(&self, other: &Self) -> bool { - self.react == other.react - && self.unicorn == other.unicorn - && self.typescript == other.typescript - && self.oxc == other.oxc - && self.import == other.import - && self.jsdoc == other.jsdoc - && self.jest == other.jest - && self.vitest == other.vitest - && self.jsx_a11y == other.jsx_a11y - && self.nextjs == other.nextjs - && self.react_perf == other.react_perf - && self.promise == other.promise - } - } #[test] fn test_collect_empty() { diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index eab1e29ab95da..5381ace2eacba 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -9,8 +9,8 @@ use serde::Deserialize; use serde_json::Value; use crate::{ - fixer::FixKind, options::LintPluginOptions, rules::RULES, AllowWarnDeny, Fixer, LintOptions, - LintService, LintServiceOptions, Linter, OxlintConfig, RuleEnum, RuleWithSeverity, + fixer::FixKind, options::LintPluginOptions, rules::RULES, AllowWarnDeny, Fixer, LintService, + LintServiceOptions, Linter, OxlintConfig, OxlintOptions, RuleEnum, RuleWithSeverity, }; #[derive(Eq, PartialEq)] @@ -344,7 +344,7 @@ impl Tester { ) -> TestResult { let allocator = Allocator::default(); let rule = self.find_rule().read_json(rule_config.unwrap_or_default()); - let options = LintOptions::default() + let options = OxlintOptions::default() .with_fix(fix.into()) .with_import_plugin(self.plugins.import) .with_jest_plugin(self.plugins.jest) @@ -358,7 +358,7 @@ impl Tester { let linter = Linter::from_options(options) .unwrap() .with_rules(vec![RuleWithSeverity::new(rule, AllowWarnDeny::Warn)]) - .with_eslint_config(eslint_config); + .with_eslint_config(eslint_config.into()); let path_to_lint = if self.plugins.import { assert!(path.is_none(), "import plugin does not support path"); self.current_working_directory.join(&self.rule_path) diff --git a/tasks/benchmark/benches/linter.rs b/tasks/benchmark/benches/linter.rs index 3848dabeb686d..5cccf206ad9e5 100644 --- a/tasks/benchmark/benches/linter.rs +++ b/tasks/benchmark/benches/linter.rs @@ -6,7 +6,7 @@ use std::{ use oxc_allocator::Allocator; use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use oxc_linter::{AllowWarnDeny, FixKind, LintOptions, Linter}; +use oxc_linter::{AllowWarnDeny, FixKind, Linter, OxlintOptions}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; @@ -41,7 +41,7 @@ fn bench_linter(criterion: &mut Criterion) { (AllowWarnDeny::Deny, "all".into()), (AllowWarnDeny::Deny, "nursery".into()), ]; - let lint_options = LintOptions::default() + let lint_options = OxlintOptions::default() .with_filter(filter) .with_fix(FixKind::All) .with_import_plugin(true) diff --git a/tasks/website/src/linter/cli.rs b/tasks/website/src/linter/cli.rs index e017847b467a5..67d1209614919 100644 --- a/tasks/website/src/linter/cli.rs +++ b/tasks/website/src/linter/cli.rs @@ -1,4 +1,4 @@ -use oxlint::lint_command; +use oxlint::cli::lint_command; #[test] fn test_cli() { @@ -10,7 +10,7 @@ fn test_cli() { #[test] fn test_cli_terminal() { - let snapshot = oxlint::lint_command().run_inner(&["--help"]).unwrap_err().unwrap_stdout(); + let snapshot = oxlint::cli::lint_command().run_inner(&["--help"]).unwrap_err().unwrap_stdout(); insta::with_settings!({ prepend_module_to_snapshot => false }, { insta::assert_snapshot!(snapshot); });