mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(linter): make overrides globs relative to config path (#7407)
- fixes https://github.com/oxc-project/oxc/issues/7365 currently, we are matching globs on the absolute path to the file, which means that in order to properly match, all globs would need to start with `**/`. this is not consistent with the behavior in ESLint, which is defined here: https://eslint.org/docs/v8.x/use/configure/configuration-files#how-do-overrides-work > **The patterns are applied against the file path relative to the directory of the config file.** For example, if your config file has the path `/Users/john/workspace/any-project/.eslintrc.js` and the file you want to lint has the path `/Users/john/workspace/any-project/lib/util.js`, then the pattern provided in `.eslintrc.js` is executed against the relative path `lib/util.js`. This PR adds the ability to store the path to the configuration file along with the configuration data, so that we can later use it to resolve the relative paths of the files in the overrides section. I'm not exactly sure if this will still make sense with nested configuration files, but I think it makes sense to store a path to each configuration file, alongside where the overrides are stored.
This commit is contained in:
parent
52784d2aea
commit
e88cf1bfd6
11 changed files with 89 additions and 4 deletions
19
apps/oxlint/fixtures/overrides/directories-config.json
Normal file
19
apps/oxlint/fixtures/overrides/directories-config.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"rules": {
|
||||
"no-debugger": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["lib/*.{js,ts}", "src/*"],
|
||||
"rules": {
|
||||
"no-debugger": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["**/tests/*"],
|
||||
"rules": {
|
||||
"no-debugger": "warn"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
apps/oxlint/fixtures/overrides/lib/index.ts
Normal file
1
apps/oxlint/fixtures/overrides/lib/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
debugger;
|
||||
1
apps/oxlint/fixtures/overrides/lib/tests/index.js
Normal file
1
apps/oxlint/fixtures/overrides/lib/tests/index.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
debugger;
|
||||
1
apps/oxlint/fixtures/overrides/src/oxlint.js
Normal file
1
apps/oxlint/fixtures/overrides/src/oxlint.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
debugger;
|
||||
1
apps/oxlint/fixtures/overrides/src/tests/index.js
Normal file
1
apps/oxlint/fixtures/overrides/src/tests/index.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
debugger;
|
||||
|
|
@ -695,4 +695,13 @@ mod test {
|
|||
assert_eq!(result.number_of_warnings, 0);
|
||||
assert_eq!(result.number_of_errors, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overrides_directories() {
|
||||
let result =
|
||||
test(&["-c", "fixtures/overrides/directories-config.json", "fixtures/overrides"]);
|
||||
assert_eq!(result.number_of_files, 7);
|
||||
assert_eq!(result.number_of_warnings, 2);
|
||||
assert_eq!(result.number_of_errors, 2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,9 +94,10 @@ impl LinterBuilder {
|
|||
categories,
|
||||
rules: oxlintrc_rules,
|
||||
overrides,
|
||||
path,
|
||||
} = oxlintrc;
|
||||
|
||||
let config = LintConfig { plugins, settings, env, globals };
|
||||
let config = LintConfig { plugins, settings, env, globals, path: Some(path) };
|
||||
let options = LintOptions::default();
|
||||
let rules =
|
||||
if start_empty { FxHashSet::default() } else { Self::warn_correctness(plugins) };
|
||||
|
|
|
|||
|
|
@ -92,8 +92,24 @@ impl ConfigStore {
|
|||
let mut overrides_to_apply: Vec<OverrideId> = Vec::new();
|
||||
let mut hasher = FxBuildHasher.build_hasher();
|
||||
|
||||
// Compute the path of the file relative to the configuration file for glob matching. Globs should match
|
||||
// relative to the location of the configuration file.
|
||||
// - path: /some/path/like/this/to/file.js
|
||||
// - config_path: /some/path/like/.oxlintrc.json
|
||||
// => relative_path: this/to/file.js
|
||||
// TODO: Handle nested configuration file paths.
|
||||
let relative_path = if let Some(config_path) = &self.base.config.path {
|
||||
if let Some(parent) = config_path.parent() {
|
||||
path.strip_prefix(parent).unwrap_or(path)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
for (id, override_config) in self.overrides.iter_enumerated() {
|
||||
if override_config.files.is_match(path) {
|
||||
if override_config.files.is_match(relative_path) {
|
||||
overrides_to_apply.push(id);
|
||||
id.hash(&mut hasher);
|
||||
}
|
||||
|
|
@ -285,6 +301,7 @@ mod test {
|
|||
env: OxlintEnv::default(),
|
||||
settings: OxlintSettings::default(),
|
||||
globals: OxlintGlobals::default(),
|
||||
path: None,
|
||||
};
|
||||
let overrides = from_json!([{
|
||||
"files": ["*.jsx", "*.tsx"],
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ mod plugins;
|
|||
mod rules;
|
||||
mod settings;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) use self::flat::ResolvedLinterState;
|
||||
pub use self::{
|
||||
env::OxlintEnv,
|
||||
|
|
@ -29,6 +31,8 @@ pub(crate) struct LintConfig {
|
|||
pub(crate) env: OxlintEnv,
|
||||
/// Enabled or disabled specific global variables.
|
||||
pub(crate) globals: OxlintGlobals,
|
||||
/// Absolute path to the configuration file (may be `None` if there is no file).
|
||||
pub(crate) path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl From<Oxlintrc> for LintConfig {
|
||||
|
|
@ -38,6 +42,7 @@ impl From<Oxlintrc> for LintConfig {
|
|||
settings: config.settings,
|
||||
env: config.env,
|
||||
globals: config.globals,
|
||||
path: Some(config.path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +63,7 @@ mod test {
|
|||
let fixture_path = env::current_dir().unwrap().join("fixtures/eslint_config.json");
|
||||
let config = Oxlintrc::from_file(&fixture_path).unwrap();
|
||||
assert!(!config.rules.is_empty());
|
||||
assert!(config.path.ends_with("fixtures/eslint_config.json"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -153,6 +153,26 @@ impl JsonSchema for GlobSet {
|
|||
}
|
||||
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_globset() {
|
||||
use super::*;
|
||||
use serde_json::{from_value, json};
|
||||
|
||||
let config: OxlintOverride = from_value(json!({
|
||||
"files": ["*.tsx",],
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(config.files.globs.is_match("/some/path/foo.tsx"));
|
||||
assert!(!config.files.globs.is_match("/some/path/foo.ts"));
|
||||
|
||||
let config: OxlintOverride = from_value(json!({
|
||||
"files": ["lib/*.ts",],
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(config.files.globs.is_match("lib/foo.ts"));
|
||||
assert!(!config.files.globs.is_match("src/foo.ts"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsing_plugins() {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use schemars::JsonSchema;
|
||||
|
|
@ -85,6 +85,9 @@ pub struct Oxlintrc {
|
|||
/// Add, remove, or otherwise reconfigure rules for specific files or groups of files.
|
||||
#[serde(skip_serializing_if = "OxlintOverrides::is_empty")]
|
||||
pub overrides: OxlintOverrides,
|
||||
/// Absolute path to the configuration file.
|
||||
#[serde(skip)]
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Oxlintrc {
|
||||
|
|
@ -116,10 +119,15 @@ impl Oxlintrc {
|
|||
OxcDiagnostic::error(format!("Failed to parse eslint config {path:?}.\n{err}"))
|
||||
})?;
|
||||
|
||||
let config = Self::deserialize(&json).map_err(|err| {
|
||||
let mut config = Self::deserialize(&json).map_err(|err| {
|
||||
OxcDiagnostic::error(format!("Failed to parse config with error {err:?}"))
|
||||
})?;
|
||||
|
||||
// Get absolute path from `path`
|
||||
let absolute_path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
|
||||
|
||||
config.path = absolute_path;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
|
@ -137,6 +145,7 @@ mod test {
|
|||
assert!(config.rules.is_empty());
|
||||
assert_eq!(config.settings, OxlintSettings::default());
|
||||
assert_eq!(config.env, OxlintEnv::default());
|
||||
assert_eq!(config.path, PathBuf::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue