mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): configure by category in config files (#6120)
> closes #5454 Adds a `categories` property to config files, where each key is a `RuleCategory` and each value is `"allow"/"off"`, `"warn"`, or `"deny"/"error"`. Note that this change won't come into effect until after #6088 is merged.
This commit is contained in:
parent
d5cf1f823b
commit
6e3224d5fa
8 changed files with 313 additions and 2 deletions
|
|
@ -70,7 +70,8 @@ impl LinterBuilder {
|
|||
/// ```
|
||||
pub fn from_oxlintrc(start_empty: bool, oxlintrc: Oxlintrc) -> Self {
|
||||
// TODO: monorepo config merging, plugin-based extends, etc.
|
||||
let Oxlintrc { plugins, settings, env, globals, rules: oxlintrc_rules } = oxlintrc;
|
||||
let Oxlintrc { plugins, settings, env, globals, categories, rules: oxlintrc_rules } =
|
||||
oxlintrc;
|
||||
|
||||
let config = LintConfig { settings, env, globals };
|
||||
let options = LintOptions { plugins, ..Default::default() };
|
||||
|
|
@ -79,6 +80,10 @@ impl LinterBuilder {
|
|||
let cache = RulesCache::new(options.plugins);
|
||||
let mut builder = Self { rules, options, config, cache };
|
||||
|
||||
if !categories.is_empty() {
|
||||
builder = builder.with_filters(categories.filters());
|
||||
}
|
||||
|
||||
{
|
||||
let all_rules = builder.cache.borrow();
|
||||
oxlintrc_rules.override_rules(&mut builder.rules, all_rules.as_slice());
|
||||
|
|
@ -536,4 +541,55 @@ mod test {
|
|||
let builder = builder.with_plugins(expected_plugins);
|
||||
assert_eq!(expected_plugins, builder.plugins());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_categories() {
|
||||
let oxlintrc: Oxlintrc = serde_json::from_str(
|
||||
r#"
|
||||
{
|
||||
"categories": {
|
||||
"correctness": "warn",
|
||||
"suspicious": "deny"
|
||||
},
|
||||
"rules": {
|
||||
"no-const-assign": "error"
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let builder = LinterBuilder::from_oxlintrc(false, oxlintrc);
|
||||
for rule in &builder.rules {
|
||||
let name = rule.name();
|
||||
let plugin = rule.plugin_name();
|
||||
let category = rule.category();
|
||||
match category {
|
||||
RuleCategory::Correctness => {
|
||||
if name == "no-const-assign" {
|
||||
assert_eq!(
|
||||
rule.severity,
|
||||
AllowWarnDeny::Deny,
|
||||
"no-const-assign should be denied",
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
rule.severity,
|
||||
AllowWarnDeny::Warn,
|
||||
"{plugin}/{name} should be a warning"
|
||||
);
|
||||
}
|
||||
}
|
||||
RuleCategory::Suspicious => {
|
||||
assert_eq!(
|
||||
rule.severity,
|
||||
AllowWarnDeny::Deny,
|
||||
"{plugin}/{name} should be denied"
|
||||
);
|
||||
}
|
||||
invalid => {
|
||||
panic!("Found rule {plugin}/{name} with an unexpected category {invalid:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
85
crates/oxc_linter/src/config/categories.rs
Normal file
85
crates/oxc_linter/src/config/categories.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AllowWarnDeny, LintFilter, RuleCategory};
|
||||
|
||||
/// Configure an entire category of rules all at once.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct OxlintCategories(FxHashMap<RuleCategory, AllowWarnDeny>);
|
||||
|
||||
impl Deref for OxlintCategories {
|
||||
type Target = FxHashMap<RuleCategory, AllowWarnDeny>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl OxlintCategories {
|
||||
pub fn filters(&self) -> impl Iterator<Item = LintFilter> + '_ {
|
||||
self.iter().map(|(category, severity)| LintFilter::new(*severity, *category).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonSchema for OxlintCategories {
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("OxlintCategories")
|
||||
}
|
||||
|
||||
fn schema_name() -> String {
|
||||
"OxlintCategories".to_string()
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
let severity = gen.subschema_for::<AllowWarnDeny>();
|
||||
let mut schema =
|
||||
gen.subschema_for::<FxHashMap<RuleCategory, AllowWarnDeny>>().into_object();
|
||||
|
||||
{
|
||||
schema.object().additional_properties = None;
|
||||
let properties = &mut schema.object().properties;
|
||||
|
||||
properties.insert(RuleCategory::Correctness.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Suspicious.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Pedantic.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Perf.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Style.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Restriction.as_str().to_string(), severity.clone());
|
||||
properties.insert(RuleCategory::Nursery.as_str().to_string(), severity.clone());
|
||||
}
|
||||
|
||||
{
|
||||
let metadata = schema.metadata();
|
||||
metadata.title = Some("Rule Categories".to_string());
|
||||
|
||||
metadata.description = Some(
|
||||
r#"
|
||||
Configure an entire category of rules all at once.
|
||||
|
||||
Rules enabled or disabled this way will be overwritten by individual rules in the `rules` field.
|
||||
|
||||
# Example
|
||||
```json
|
||||
{
|
||||
"categories": {
|
||||
"correctness": "warn"
|
||||
},
|
||||
"rules": {
|
||||
"eslint/no-unused-vars": "error"
|
||||
}
|
||||
}
|
||||
```
|
||||
"#
|
||||
.trim()
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
metadata.examples = vec![serde_json::json!({ "correctness": "warn" })];
|
||||
}
|
||||
|
||||
schema.into()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
mod categories;
|
||||
mod env;
|
||||
mod globals;
|
||||
mod oxlintrc;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{env::OxlintEnv, globals::OxlintGlobals, rules::OxlintRules, settings::OxlintSettings};
|
||||
use super::{
|
||||
categories::OxlintCategories, env::OxlintEnv, globals::OxlintGlobals, rules::OxlintRules,
|
||||
settings::OxlintSettings,
|
||||
};
|
||||
|
||||
use crate::{options::LintPlugins, utils::read_to_string};
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ use crate::{options::LintPlugins, utils::read_to_string};
|
|||
#[non_exhaustive]
|
||||
pub struct Oxlintrc {
|
||||
pub plugins: LintPlugins,
|
||||
pub categories: OxlintCategories,
|
||||
/// See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html).
|
||||
pub rules: OxlintRules,
|
||||
pub settings: OxlintSettings,
|
||||
|
|
|
|||
|
|
@ -100,6 +100,18 @@ impl RuleCategory {
|
|||
Self::Nursery => "New lints that are still under development.",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Correctness => "correctness",
|
||||
Self::Suspicious => "suspicious",
|
||||
Self::Pedantic => "pedantic",
|
||||
Self::Perf => "perf",
|
||||
Self::Style => "style",
|
||||
Self::Restriction => "restriction",
|
||||
Self::Nursery => "nursery",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for RuleCategory {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ expression: json
|
|||
"description": "Oxlint Configuration File\n\nThis configuration is aligned with ESLint v8's configuration schema (`eslintrc.json`).\n\nUsage: `oxlint -c oxlintrc.json --import-plugin`\n\n::: danger NOTE\n\nOnly the `.json` format is supported. You can use comments in configuration files.\n\n:::\n\nExample\n\n`.oxlintrc.json`\n\n```json { \"env\": { \"browser\": true }, \"globals\": { \"foo\": \"readonly\" }, \"settings\": { }, \"rules\": { \"eqeqeq\": \"warn\", \"import/no-cycle\": \"error\" } } ```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"categories": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintCategories"
|
||||
}
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"description": "Environments enable and disable collections of global variables.",
|
||||
"default": {
|
||||
|
|
@ -267,6 +275,39 @@ expression: json
|
|||
}
|
||||
]
|
||||
},
|
||||
"OxlintCategories": {
|
||||
"title": "Rule Categories",
|
||||
"description": "Configure an entire category of rules all at once.\n\nRules enabled or disabled this way will be overwritten by individual rules in the `rules` field.\n\n# Example\n```json\n{\n \"categories\": {\n \"correctness\": \"warn\"\n },\n \"rules\": {\n \"eslint/no-unused-vars\": \"error\"\n }\n}\n```",
|
||||
"examples": [
|
||||
{
|
||||
"correctness": "warn"
|
||||
}
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"correctness": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"nursery": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"pedantic": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"perf": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"restriction": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"style": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"suspicious": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OxlintEnv": {
|
||||
"description": "Predefine global variables.\n\nEnvironments specify what global variables are predefined. See [ESLint's list of environments](https://eslint.org/docs/v8.x/use/configure/language-options#specifying-environments) for what environments are available and what each one provides.",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@
|
|||
"description": "Oxlint Configuration File\n\nThis configuration is aligned with ESLint v8's configuration schema (`eslintrc.json`).\n\nUsage: `oxlint -c oxlintrc.json --import-plugin`\n\n::: danger NOTE\n\nOnly the `.json` format is supported. You can use comments in configuration files.\n\n:::\n\nExample\n\n`.oxlintrc.json`\n\n```json { \"env\": { \"browser\": true }, \"globals\": { \"foo\": \"readonly\" }, \"settings\": { }, \"rules\": { \"eqeqeq\": \"warn\", \"import/no-cycle\": \"error\" } } ```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"categories": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintCategories"
|
||||
}
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"description": "Environments enable and disable collections of global variables.",
|
||||
"default": {
|
||||
|
|
@ -263,6 +271,39 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"OxlintCategories": {
|
||||
"title": "Rule Categories",
|
||||
"description": "Configure an entire category of rules all at once.\n\nRules enabled or disabled this way will be overwritten by individual rules in the `rules` field.\n\n# Example\n```json\n{\n \"categories\": {\n \"correctness\": \"warn\"\n },\n \"rules\": {\n \"eslint/no-unused-vars\": \"error\"\n }\n}\n```",
|
||||
"examples": [
|
||||
{
|
||||
"correctness": "warn"
|
||||
}
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"correctness": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"nursery": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"pedantic": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"perf": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"restriction": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"style": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
},
|
||||
"suspicious": {
|
||||
"$ref": "#/definitions/AllowWarnDeny"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OxlintEnv": {
|
||||
"description": "Predefine global variables.\n\nEnvironments specify what global variables are predefined. See [ESLint's list of environments](https://eslint.org/docs/v8.x/use/configure/language-options#specifying-environments) for what environments are available and what each one provides.",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,77 @@ Example
|
|||
```
|
||||
|
||||
|
||||
## categories
|
||||
|
||||
type: `object`
|
||||
|
||||
Configure an entire category of rules all at once.
|
||||
|
||||
Rules enabled or disabled this way will be overwritten by individual rules in the `rules` field.
|
||||
|
||||
# Example
|
||||
```json
|
||||
{
|
||||
"categories": {
|
||||
"correctness": "warn"
|
||||
},
|
||||
"rules": {
|
||||
"eslint/no-unused-vars": "error"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### categories.correctness
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.nursery
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.pedantic
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.perf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.restriction
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.style
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### categories.suspicious
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## env
|
||||
|
||||
type: `object`
|
||||
|
|
|
|||
Loading…
Reference in a new issue