diff --git a/Cargo.lock b/Cargo.lock index 29c1400efda1a..539bdd724a8f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1639,6 +1639,7 @@ dependencies = [ "oxc_span", "oxc_tasks_common", "oxc_transformer", + "rustc-hash", "serde", "serde_json", ] @@ -2018,6 +2019,7 @@ dependencies = [ "oxc_index", "oxc_linter", "oxc_napi", + "rustc-hash", "serde", "serde_json", ] diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 8ad8d08152ea5..3fea36e452f3e 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -12,8 +12,8 @@ use cow_utils::CowUtils; use ignore::{gitignore::Gitignore, overrides::OverrideBuilder}; use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, OxcDiagnostic}; use oxc_linter::{ - AllowWarnDeny, ConfigStore, ConfigStoreBuilder, InvalidFilterKind, LintFilter, LintOptions, - LintService, LintServiceOptions, Linter, Oxlintrc, + AllowWarnDeny, Config, ConfigStore, ConfigStoreBuilder, InvalidFilterKind, LintFilter, + LintOptions, LintService, LintServiceOptions, Linter, Oxlintrc, }; use rustc_hash::{FxHashMap, FxHashSet}; use serde_json::Value; @@ -269,15 +269,10 @@ impl Runner for LintRunner { _ => None, }; - let linter = if nested_configs.is_empty() { - Linter::new(LintOptions::default(), lint_config) + let linter = + Linter::new(LintOptions::default(), ConfigStore::new(lint_config, nested_configs)) .with_fix(fix_options.fix_kind()) - .with_report_unused_directives(report_unused_directives) - } else { - Linter::new_with_nested_configs(LintOptions::default(), lint_config, nested_configs) - .with_fix(fix_options.fix_kind()) - .with_report_unused_directives(report_unused_directives) - }; + .with_report_unused_directives(report_unused_directives); let tsconfig = basic_options.tsconfig; if let Some(path) = tsconfig.as_ref() { @@ -400,11 +395,11 @@ impl LintRunner { handler: &GraphicalReportHandler, filters: &Vec, paths: &Vec>, - ) -> Result, CliRunResult> { + ) -> Result, CliRunResult> { // TODO(perf): benchmark whether or not it is worth it to store the configurations on a // per-file or per-directory basis, to avoid calling `.parent()` on every path. let mut nested_oxlintrc = FxHashMap::<&Path, Oxlintrc>::default(); - let mut nested_configs = FxHashMap::::default(); + let mut nested_configs = FxHashMap::::default(); // get all of the unique directories among the paths to use for search for // oxlint config files in those directories and their ancestors // e.g. `/some/file.js` will check `/some` and `/` diff --git a/crates/oxc_language_server/src/linter/server_linter.rs b/crates/oxc_language_server/src/linter/server_linter.rs index 3965a22379bb7..8e984e4c3f34f 100644 --- a/crates/oxc_language_server/src/linter/server_linter.rs +++ b/crates/oxc_language_server/src/linter/server_linter.rs @@ -7,7 +7,7 @@ use log::{debug, warn}; use rustc_hash::{FxBuildHasher, FxHashMap}; use tower_lsp_server::lsp_types::Uri; -use oxc_linter::{ConfigStore, ConfigStoreBuilder, LintOptions, Linter, Oxlintrc}; +use oxc_linter::{Config, ConfigStore, ConfigStoreBuilder, LintOptions, Linter, Oxlintrc}; use tower_lsp_server::UriExt; use crate::linter::{ @@ -61,21 +61,24 @@ impl ServerLinter { config_builder.plugins().has_import() }; - let config_store = config_builder.build().expect("Failed to build config store"); + let base_config = config_builder.build().expect("Failed to build config store"); let lint_options = LintOptions { fix: options.fix_kind(), ..Default::default() }; - let linter = if use_nested_config { - let nested_configs = nested_configs.pin(); - let nested_configs_copy: FxHashMap = nested_configs - .iter() - .map(|(key, value)| (key.clone(), value.clone())) - .collect::>(); + let config_store = ConfigStore::new( + base_config, + if use_nested_config { + let nested_configs = nested_configs.pin(); + nested_configs + .iter() + .map(|(key, value)| (key.clone(), value.clone())) + .collect::>() + } else { + FxHashMap::default() + }, + ); - Linter::new_with_nested_configs(lint_options, config_store, nested_configs_copy) - } else { - Linter::new(lint_options, config_store) - }; + let linter = Linter::new(lint_options, config_store); let isolated_linter = IsolatedLintHandler::new( linter, @@ -93,7 +96,7 @@ impl ServerLinter { fn create_nested_configs( root_uri: &Uri, options: &Options, - ) -> ConcurrentHashMap { + ) -> ConcurrentHashMap { // nested config is disabled, no need to search for configs if !options.use_nested_configs() { return ConcurrentHashMap::default(); diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 7fe3a90d54f3f..af048f17bb023 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -12,13 +12,12 @@ use oxc_span::{CompactStr, format_compact_str}; use crate::{ AllowWarnDeny, LintConfig, LintFilter, LintFilterKind, Oxlintrc, RuleCategory, RuleEnum, RuleWithSeverity, - config::{ - ConfigStore, ESLintRule, LintPlugins, OxlintOverrides, OxlintRules, - overrides::OxlintOverride, - }, + config::{ESLintRule, LintPlugins, OxlintOverrides, OxlintRules, overrides::OxlintOverride}, rules::RULES, }; +use super::Config; + #[must_use = "You dropped your builder without building a Linter! Did you mean to call .build()?"] pub struct ConfigStoreBuilder { pub(super) rules: FxHashSet, @@ -72,7 +71,7 @@ impl ConfigStoreBuilder { /// will be applied on top of a default [`Oxlintrc`]. /// /// # Example - /// Here's how to create a [`ConfigStore`] from a `.oxlintrc.json` file. + /// Here's how to create a [`Config`] from a `.oxlintrc.json` file. /// ```ignore /// use oxc_linter::{ConfigBuilder, Oxlintrc}; /// let oxlintrc = Oxlintrc::from_file("path/to/.oxlintrc.json").unwrap(); @@ -301,7 +300,7 @@ impl ConfigStoreBuilder { } /// # Errors - pub fn build(self) -> Result { + pub fn build(self) -> Result { // When a plugin gets disabled before build(), rules for that plugin aren't removed until // with_filters() gets called. If the user never calls it, those now-undesired rules need // to be taken out. @@ -312,7 +311,8 @@ impl ConfigStoreBuilder { self.rules.into_iter().collect::>() }; rules.sort_unstable_by_key(|r| r.id()); - Ok(ConfigStore::new(rules, self.config, self.overrides)) + + Ok(Config::new(rules, self.config, self.overrides)) } /// Warn for all correctness rules in the given set of plugins. @@ -386,7 +386,7 @@ impl Debug for ConfigStoreBuilder { } } -/// An error that can occur while building a [`ConfigStore`] from an [`Oxlintrc`]. +/// An error that can occur while building a [`Config`] from an [`Oxlintrc`]. #[derive(Eq, PartialEq, Debug, Clone)] pub enum ConfigBuilderError { /// There were unknown rules that could not be matched to any known plugins/rules. @@ -656,7 +656,7 @@ mod test { desired_plugins.set(LintPlugins::TYPESCRIPT, false); let linter = ConfigStoreBuilder::default().with_plugins(desired_plugins).build().unwrap(); - for rule in linter.rules().iter() { + for rule in linter.base.rules.iter() { let name = rule.name(); let plugin = rule.plugin_name(); assert_ne!( @@ -1030,14 +1030,14 @@ mod test { assert!(config.rules().is_empty()); } - fn config_store_from_path(path: &str) -> ConfigStore { + fn config_store_from_path(path: &str) -> Config { ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::from_file(&PathBuf::from(path)).unwrap()) .unwrap() .build() .unwrap() } - fn config_store_from_str(s: &str) -> ConfigStore { + fn config_store_from_str(s: &str) -> Config { ConfigStoreBuilder::from_oxlintrc(true, serde_json::from_str(s).unwrap()) .unwrap() .build() diff --git a/crates/oxc_linter/src/config/config_store.rs b/crates/oxc_linter/src/config/config_store.rs index f006d4376e514..111aebc7a47f5 100644 --- a/crates/oxc_linter/src/config/config_store.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -1,6 +1,9 @@ -use std::{path::Path, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use super::{LintConfig, LintPlugins, overrides::OxlintOverrides}; use crate::{RuleWithSeverity, rules::RULES}; @@ -20,57 +23,47 @@ impl Clone for ResolvedLinterState { } #[derive(Debug, Clone)] -struct Config { +pub struct Config { /// The basic linter state for this configuration. - base: ResolvedLinterState, + pub(crate) base: ResolvedLinterState, /// An optional set of overrides to apply to the base state depending on the file being linted. - overrides: OxlintOverrides, + pub(crate) overrides: OxlintOverrides, } -/// Resolves a lint configuration for a given file, by applying overrides based on the file's path. -#[derive(Debug, Clone)] -pub struct ConfigStore { - base: Config, -} - -impl ConfigStore { +impl Config { pub fn new( - base_rules: Vec, - base_config: LintConfig, + rules: Vec, + config: LintConfig, overrides: OxlintOverrides, ) -> Self { - let base = ResolvedLinterState { - rules: Arc::from(base_rules.into_boxed_slice()), - config: Arc::new(base_config), - }; - Self { base: Config { base, overrides } } + Config { + base: ResolvedLinterState { + rules: Arc::from(rules.into_boxed_slice()), + config: Arc::new(config), + }, + overrides, + } } - pub fn number_of_rules(&self) -> usize { - self.base.base.rules.len() + pub fn plugins(&self) -> LintPlugins { + self.base.config.plugins } pub fn rules(&self) -> &Arc<[RuleWithSeverity]> { - &self.base.base.rules - } - - pub fn plugins(&self) -> LintPlugins { - self.base.base.config.plugins + &self.base.rules } - pub(crate) fn resolve(&self, path: &Path) -> ResolvedLinterState { - // TODO: based on the `path` provided, resolve the configuration file to use. - let resolved_config = &self.base; - Self::apply_overrides(resolved_config, path) + pub fn number_of_rules(&self) -> usize { + self.base.rules.len() } - fn apply_overrides(config: &Config, path: &Path) -> ResolvedLinterState { - if config.overrides.is_empty() { - return config.base.clone(); + pub fn apply_overrides(&self, path: &Path) -> ResolvedLinterState { + if self.overrides.is_empty() { + return self.base.clone(); } - let relative_path = config + let relative_path = self .base .config .path @@ -81,18 +74,18 @@ impl ConfigStore { .unwrap_or(path); let overrides_to_apply = - config.overrides.iter().filter(|config| config.files.is_match(relative_path)); + self.overrides.iter().filter(|config| config.files.is_match(relative_path)); let mut overrides_to_apply = overrides_to_apply.peekable(); if overrides_to_apply.peek().is_none() { - return config.base.clone(); + return self.base.clone(); } - let mut env = config.base.config.env.clone(); - let mut globals = config.base.config.globals.clone(); - let mut plugins = config.base.config.plugins; - let mut rules = config + let mut env = self.base.config.env.clone(); + let mut globals = self.base.config.globals.clone(); + let mut plugins = self.base.config.plugins; + let mut rules = self .base .rules .iter() @@ -124,13 +117,13 @@ impl ConfigStore { } } - let config = if plugins == config.base.config.plugins - && env == config.base.config.env - && globals == config.base.config.globals + let config: Arc = if plugins == self.base.config.plugins + && env == self.base.config.env + && globals == self.base.config.globals { - Arc::clone(&config.base.config) + Arc::clone(&self.base.config) } else { - let mut config = (*config.base.config).clone(); + let mut config = (*self.base.config).clone(); config.plugins = plugins; config.env = env; @@ -143,12 +136,65 @@ impl ConfigStore { } } +/// Resolves a lint configuration for a given file, by applying overrides based on the file's path. +#[derive(Debug, Clone)] +pub struct ConfigStore { + base: Config, + + nested_configs: FxHashMap, +} + +impl ConfigStore { + pub fn new(base_config: Config, nested_configs: FxHashMap) -> Self { + Self { base: base_config, nested_configs } + } + + pub fn number_of_rules(&self) -> Option { + self.nested_configs.is_empty().then_some(self.base.base.rules.len()) + } + + pub fn rules(&self) -> &Arc<[RuleWithSeverity]> { + &self.base.base.rules + } + + pub fn plugins(&self) -> LintPlugins { + self.base.base.config.plugins + } + + pub(crate) fn resolve(&self, path: &Path) -> ResolvedLinterState { + let resolved_config = if self.nested_configs.is_empty() { + &self.base + } else if let Some(config) = self.get_nearest_config(path) { + config + } else { + &self.base + }; + + Config::apply_overrides(resolved_config, path) + } + + fn get_nearest_config(&self, path: &Path) -> Option<&Config> { + // TODO(perf): should we cache the computed nearest config for every directory, + // so we don't have to recompute it for every file? + let mut current = path.parent(); + while let Some(dir) = current { + if let Some(config) = self.nested_configs.get(dir) { + return Some(config); + } + current = dir.parent(); + } + None + } +} + #[cfg(test)] mod test { + use rustc_hash::FxHashMap; + use super::{ConfigStore, OxlintOverrides}; use crate::{ AllowWarnDeny, LintPlugins, RuleEnum, RuleWithSeverity, - config::{LintConfig, OxlintEnv, OxlintGlobals, OxlintSettings}, + config::{LintConfig, OxlintEnv, OxlintGlobals, OxlintSettings, config_store::Config}, }; macro_rules! from_json { @@ -173,7 +219,10 @@ mod test { "files": ["*.test.{ts,tsx}"], "rules": {} }]); - let store = ConfigStore::new(base_rules, LintConfig::default(), overrides); + let store = ConfigStore::new( + Config::new(base_rules, LintConfig::default(), overrides), + FxHashMap::default(), + ); let rules_for_source_file = store.resolve("App.tsx".as_ref()); let rules_for_test_file = store.resolve("App.test.tsx".as_ref()); @@ -195,7 +244,10 @@ mod test { "plugins": ["react", "typescript", "unicorn", "oxc", "jsx-a11y"], "rules": {} }]); - let store = ConfigStore::new(base_rules, LintConfig::default(), overrides); + let store = ConfigStore::new( + Config::new(base_rules, LintConfig::default(), overrides), + FxHashMap::default(), + ); let rules_for_source_file = store.resolve("App.tsx".as_ref()); let rules_for_test_file = store.resolve("App.test.tsx".as_ref()); @@ -218,8 +270,11 @@ mod test { } }]); - let store = ConfigStore::new(base_rules, LintConfig::default(), overrides); - assert_eq!(store.number_of_rules(), 1); + let store = ConfigStore::new( + Config::new(base_rules, LintConfig::default(), overrides), + FxHashMap::default(), + ); + assert_eq!(store.number_of_rules(), Some(1)); let rules_for_source_file = store.resolve("App.tsx".as_ref()); assert_eq!(rules_for_source_file.rules.len(), 1); @@ -238,8 +293,11 @@ mod test { } }]); - let store = ConfigStore::new(base_rules, LintConfig::default(), overrides); - assert_eq!(store.number_of_rules(), 1); + let store = ConfigStore::new( + Config::new(base_rules, LintConfig::default(), overrides), + FxHashMap::default(), + ); + assert_eq!(store.number_of_rules(), Some(1)); assert_eq!(store.resolve("App.tsx".as_ref()).rules.len(), 1); assert_eq!(store.resolve("src/App.tsx".as_ref()).rules.len(), 2); @@ -258,8 +316,11 @@ mod test { } }]); - let store = ConfigStore::new(base_rules, LintConfig::default(), overrides); - assert_eq!(store.number_of_rules(), 1); + let store = ConfigStore::new( + Config::new(base_rules, LintConfig::default(), overrides), + FxHashMap::default(), + ); + assert_eq!(store.number_of_rules(), Some(1)); let app = store.resolve("App.tsx".as_ref()).rules; assert_eq!(app.len(), 1); @@ -287,7 +348,8 @@ mod test { "plugins": ["typescript"], }]); - let store = ConfigStore::new(vec![], base_config, overrides); + let store = + ConfigStore::new(Config::new(vec![], base_config, overrides), FxHashMap::default()); assert_eq!(store.base.base.config.plugins, LintPlugins::IMPORT); let app = store.resolve("other.mjs".as_ref()).config; @@ -320,7 +382,8 @@ mod test { }, }]); - let store = ConfigStore::new(vec![], base_config, overrides); + let store = + ConfigStore::new(Config::new(vec![], base_config, overrides), FxHashMap::default()); assert!(!store.base.base.config.env.contains("React")); let app = store.resolve("App.tsx".as_ref()).config; @@ -344,7 +407,8 @@ mod test { }, }]); - let store = ConfigStore::new(vec![], base_config, overrides); + let store = + ConfigStore::new(Config::new(vec![], base_config, overrides), FxHashMap::default()); assert!(store.base.base.config.env.contains("es2024")); let app = store.resolve("App.tsx".as_ref()).config; @@ -369,7 +433,8 @@ mod test { }, }]); - let store = ConfigStore::new(vec![], base_config, overrides); + let store = + ConfigStore::new(Config::new(vec![], base_config, overrides), FxHashMap::default()); assert!(!store.base.base.config.globals.is_enabled("React")); assert!(!store.base.base.config.globals.is_enabled("Secret")); @@ -399,7 +464,8 @@ mod test { }, }]); - let store = ConfigStore::new(vec![], base_config, overrides); + let store = + ConfigStore::new(Config::new(vec![], base_config, overrides), FxHashMap::default()); assert!(store.base.base.config.globals.is_enabled("React")); assert!(store.base.base.config.globals.is_enabled("Secret")); diff --git a/crates/oxc_linter/src/config/mod.rs b/crates/oxc_linter/src/config/mod.rs index d766225e6ac45..0b0314c9d4bf8 100644 --- a/crates/oxc_linter/src/config/mod.rs +++ b/crates/oxc_linter/src/config/mod.rs @@ -11,8 +11,8 @@ mod plugins; mod rules; mod settings; pub use config_builder::{ConfigBuilderError, ConfigStoreBuilder}; -pub use config_store::ConfigStore; pub use config_store::ResolvedLinterState; +pub use config_store::{Config, ConfigStore}; pub use env::OxlintEnv; pub use globals::{GlobalValue, OxlintGlobals}; pub use overrides::OxlintOverrides; diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index c5d68d3ae8940..8030e83fe8d7d 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -22,18 +22,14 @@ pub mod loader; pub mod rules; pub mod table; -use std::{ - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; +use std::{path::Path, rc::Rc, sync::Arc}; use oxc_semantic::{AstNode, Semantic}; -use rustc_hash::FxHashMap; pub use crate::{ config::{ - ConfigBuilderError, ConfigStore, ConfigStoreBuilder, ESLintRule, LintPlugins, Oxlintrc, + Config, ConfigBuilderError, ConfigStore, ConfigStoreBuilder, ESLintRule, LintPlugins, + Oxlintrc, }, context::LintContext, fixer::FixKind, @@ -72,23 +68,11 @@ pub struct Linter { options: LintOptions, // config: Arc, config: ConfigStore, - // TODO(refactor): remove duplication with `config` field when nested config is - // standardized, as we do not need to pass both at that point - nested_configs: FxHashMap, } impl Linter { pub fn new(options: LintOptions, config: ConfigStore) -> Self { - Self { options, config, nested_configs: FxHashMap::default() } - } - - // TODO(refactor); remove this when nested config is standardized - pub fn new_with_nested_configs( - options: LintOptions, - config: ConfigStore, - nested_configs: FxHashMap, - ) -> Self { - Self { options, config, nested_configs } + Self { options, config } } /// Set the kind of auto fixes to apply. @@ -112,7 +96,7 @@ impl Linter { /// nested configurations in use, in which case it returns `None` since the /// number of rules depends on which file is being linted. pub fn number_of_rules(&self) -> Option { - self.nested_configs.is_empty().then_some(self.config.number_of_rules()) + self.config.number_of_rules() } pub fn run<'a>( @@ -121,15 +105,8 @@ impl Linter { semantic: Rc>, module_record: Arc, ) -> Vec> { - // TODO(refactor): remove branch when nested config is standardized - let ResolvedLinterState { rules, config } = if self.nested_configs.is_empty() { - // Get config + rules for this file. Takes base rules and applies glob-based overrides. - self.config.resolve(path) - } else if let Some(nearest_config) = self.get_nearest_config(path) { - nearest_config.resolve(path) - } else { - self.config.resolve(path) - }; + let ResolvedLinterState { rules, config } = self.config.resolve(path); + let ctx_host = Rc::new(ContextHost::new(path, semantic, module_record, self.options, config)); @@ -216,22 +193,6 @@ impl Linter { ctx_host.take_diagnostics() } - - /// Get the nearest config for the given path, in the following priority order: - /// 1. config file in the same directory as the path - /// 2. config file in the closest parent directory - fn get_nearest_config(&self, path: &Path) -> Option<&ConfigStore> { - // TODO(perf): should we cache the computed nearest config for every directory, - // so we don't have to recompute it for every file? - let mut current = path.parent(); - while let Some(dir) = current { - if let Some(config_store) = self.nested_configs.get(dir) { - return Some(config_store); - } - current = dir.parent(); - } - None - } } #[cfg(test)] diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index 83275a921e94a..c0dbc2c19474b 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -9,12 +9,13 @@ use std::{ use cow_utils::CowUtils; use oxc_allocator::Allocator; use oxc_diagnostics::{GraphicalReportHandler, GraphicalTheme, NamedSource}; +use rustc_hash::FxHashMap; use serde::Deserialize; use serde_json::Value; use crate::{ - AllowWarnDeny, ConfigStoreBuilder, LintPlugins, LintService, LintServiceOptions, Linter, - Oxlintrc, RuleEnum, RuleWithSeverity, + AllowWarnDeny, ConfigStore, ConfigStoreBuilder, LintPlugins, LintService, LintServiceOptions, + Linter, Oxlintrc, RuleEnum, RuleWithSeverity, fixer::{FixKind, Fixer}, options::LintOptions, read_to_string, @@ -483,16 +484,19 @@ impl Tester { let rule = self.find_rule().read_json(rule_config.unwrap_or_default()); let linter = Linter::new( self.lint_options, - eslint_config - .as_ref() - .map_or_else(ConfigStoreBuilder::empty, |v| { - ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::deserialize(v).unwrap()) - .unwrap() - }) - .with_plugins(self.plugins) - .with_rule(RuleWithSeverity::new(rule, AllowWarnDeny::Warn)) - .build() - .unwrap(), + ConfigStore::new( + eslint_config + .as_ref() + .map_or_else(ConfigStoreBuilder::empty, |v| { + ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::deserialize(v).unwrap()) + .unwrap() + }) + .with_plugins(self.plugins) + .with_rule(RuleWithSeverity::new(rule, AllowWarnDeny::Warn)) + .build() + .unwrap(), + FxHashMap::default(), + ), ) .with_fix(fix.into()); diff --git a/napi/playground/Cargo.toml b/napi/playground/Cargo.toml index 1fe44c281946a..e4014c699b1d5 100644 --- a/napi/playground/Cargo.toml +++ b/napi/playground/Cargo.toml @@ -30,6 +30,7 @@ oxc_napi = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } +rustc-hash = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/napi/playground/src/lib.rs b/napi/playground/src/lib.rs index 4524a215879b1..f56e980fbcb78 100644 --- a/napi/playground/src/lib.rs +++ b/napi/playground/src/lib.rs @@ -5,6 +5,8 @@ use std::{ sync::Arc, }; +use rustc_hash::FxHashMap; + use napi_derive::napi; use serde::Serialize; @@ -27,7 +29,7 @@ use oxc::{ }; use oxc_formatter::{FormatOptions, Formatter}; use oxc_index::Idx; -use oxc_linter::{ConfigStoreBuilder, LintOptions, Linter, ModuleRecord}; +use oxc_linter::{ConfigStore, ConfigStoreBuilder, LintOptions, Linter, ModuleRecord}; use oxc_napi::{Comment, OxcError, convert_utf8_to_utf16}; use crate::options::{OxcOptions, OxcRunOptions}; @@ -284,11 +286,11 @@ impl Oxc { let semantic = Rc::new(semantic_ret.semantic); let lint_config = ConfigStoreBuilder::default().build().expect("Failed to build config store"); - let linter_ret = Linter::new(LintOptions::default(), lint_config).run( - path, - Rc::clone(&semantic), - Arc::clone(module_record), - ); + let linter_ret = Linter::new( + LintOptions::default(), + ConfigStore::new(lint_config, FxHashMap::default()), + ) + .run(path, Rc::clone(&semantic), Arc::clone(module_record)); self.diagnostics.extend(linter_ret.into_iter().map(|e| e.error)); } } diff --git a/tasks/benchmark/Cargo.toml b/tasks/benchmark/Cargo.toml index db698e126d420..69b97511a69cb 100644 --- a/tasks/benchmark/Cargo.toml +++ b/tasks/benchmark/Cargo.toml @@ -80,6 +80,8 @@ oxc_transformer = { workspace = true, optional = true } criterion2 = { workspace = true } +rustc-hash = { workspace = true } + # Only for NAPI benchmark serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } diff --git a/tasks/benchmark/benches/linter.rs b/tasks/benchmark/benches/linter.rs index 470043522466e..ff859ea51ad57 100644 --- a/tasks/benchmark/benches/linter.rs +++ b/tasks/benchmark/benches/linter.rs @@ -1,8 +1,10 @@ use std::{env, path::Path, rc::Rc, sync::Arc}; +use rustc_hash::FxHashMap; + use oxc_allocator::Allocator; use oxc_benchmark::{BenchmarkId, Criterion, criterion_group, criterion_main}; -use oxc_linter::{ConfigStoreBuilder, FixKind, LintOptions, Linter, ModuleRecord}; +use oxc_linter::{ConfigStore, ConfigStoreBuilder, FixKind, LintOptions, Linter, ModuleRecord}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; @@ -41,7 +43,11 @@ fn bench_linter(criterion: &mut Criterion) { let semantic = Rc::new(semantic); let lint_config = ConfigStoreBuilder::all().build().expect("Failed to build config store"); - let linter = Linter::new(LintOptions::default(), lint_config).with_fix(FixKind::All); + let linter = Linter::new( + LintOptions::default(), + ConfigStore::new(lint_config, FxHashMap::default()), + ) + .with_fix(FixKind::All); b.iter(|| linter.run(path, Rc::clone(&semantic), Arc::clone(&module_record))); }); }