mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): support --print-config all to print config file for project (#6579)
Continue work on #4742. Only `oxlint --print-config all` is supported. It's useful to migrate from command-line interface to config file. The `--print-config PATH` looks not really useful for us now, I will add it after config file overrides supported.
This commit is contained in:
parent
ffa3945465
commit
0acca58581
18 changed files with 208 additions and 10 deletions
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"rules": {
|
||||
"no-debugger": "warn",
|
||||
"eqeqeq": [
|
||||
"warn",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
apps/oxlint/fixtures/print_config/ban_rules/expect.json
Normal file
44
apps/oxlint/fixtures/print_config/ban_rules/expect.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"plugins": [
|
||||
"react",
|
||||
"unicorn",
|
||||
"typescript",
|
||||
"oxc"
|
||||
],
|
||||
"categories": {},
|
||||
"rules": {
|
||||
"eqeqeq": [
|
||||
"deny",
|
||||
[
|
||||
"always"
|
||||
]
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"jsx-a11y": {
|
||||
"polymorphicPropName": null,
|
||||
"components": {}
|
||||
},
|
||||
"next": {
|
||||
"rootDir": []
|
||||
},
|
||||
"react": {
|
||||
"formComponents": [],
|
||||
"linkComponents": []
|
||||
},
|
||||
"jsdoc": {
|
||||
"ignorePrivate": false,
|
||||
"ignoreInternal": false,
|
||||
"ignoreReplacesDocs": true,
|
||||
"overrideReplacesDocs": true,
|
||||
"augmentsExtendsReplacesDocs": false,
|
||||
"implementsReplacesDocs": false,
|
||||
"exemptDestructuredRootsFromChecks": false,
|
||||
"tagNamePreference": {}
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"builtin": true
|
||||
},
|
||||
"globals": {}
|
||||
}
|
||||
37
apps/oxlint/fixtures/print_config/normal/expect.json
Normal file
37
apps/oxlint/fixtures/print_config/normal/expect.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"plugins": [
|
||||
"react",
|
||||
"unicorn",
|
||||
"typescript",
|
||||
"oxc"
|
||||
],
|
||||
"categories": {},
|
||||
"rules": {},
|
||||
"settings": {
|
||||
"jsx-a11y": {
|
||||
"polymorphicPropName": null,
|
||||
"components": {}
|
||||
},
|
||||
"next": {
|
||||
"rootDir": []
|
||||
},
|
||||
"react": {
|
||||
"formComponents": [],
|
||||
"linkComponents": []
|
||||
},
|
||||
"jsdoc": {
|
||||
"ignorePrivate": false,
|
||||
"ignoreInternal": false,
|
||||
"ignoreReplacesDocs": true,
|
||||
"overrideReplacesDocs": true,
|
||||
"augmentsExtendsReplacesDocs": false,
|
||||
"implementsReplacesDocs": false,
|
||||
"exemptDestructuredRootsFromChecks": false,
|
||||
"tagNamePreference": {}
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"builtin": true
|
||||
},
|
||||
"globals": {}
|
||||
}
|
||||
|
|
@ -25,6 +25,11 @@ pub struct MiscOptions {
|
|||
/// Number of threads to use. Set to 1 for using only 1 CPU core
|
||||
#[bpaf(argument("INT"), hide_usage)]
|
||||
pub threads: Option<usize>,
|
||||
|
||||
/// This option outputs the configuration to be used.
|
||||
/// When present, no linting is performed and only config-related options are valid.
|
||||
#[bpaf(switch, hide_usage)]
|
||||
pub print_config: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::ptr_arg)]
|
||||
|
|
|
|||
|
|
@ -116,10 +116,19 @@ impl Runner for LintRunner {
|
|||
};
|
||||
|
||||
enable_plugins.apply_overrides(&mut oxlintrc.plugins);
|
||||
|
||||
let oxlintrc_for_print =
|
||||
if misc_options.print_config { Some(oxlintrc.clone()) } else { None };
|
||||
let builder = LinterBuilder::from_oxlintrc(false, oxlintrc)
|
||||
.with_filters(filter)
|
||||
.with_fix(fix_options.fix_kind());
|
||||
|
||||
if let Some(basic_config_file) = oxlintrc_for_print {
|
||||
return CliRunResult::PrintConfigResult {
|
||||
config_file: builder.resolve_final_config_file(basic_config_file),
|
||||
};
|
||||
}
|
||||
|
||||
let mut options =
|
||||
LintServiceOptions::new(cwd, paths).with_cross_module(builder.plugins().has_import());
|
||||
let linter = builder.build();
|
||||
|
|
@ -586,4 +595,40 @@ mod test {
|
|||
// Write the file back.
|
||||
fs::write(file, content).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_print_config_ban_all_rules() {
|
||||
let args = &["-A", "all", "--print-config"];
|
||||
let options = lint_command().run_inner(args).unwrap();
|
||||
let ret = LintRunner::new(options).run();
|
||||
let CliRunResult::PrintConfigResult { config_file: config } = ret else {
|
||||
panic!("Expected PrintConfigResult, got {ret:?}")
|
||||
};
|
||||
|
||||
let expect_json =
|
||||
std::fs::read_to_string("fixtures/print_config/normal/expect.json").unwrap();
|
||||
assert_eq!(config, expect_json.trim());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_print_config_ban_rules() {
|
||||
let args = &[
|
||||
"-c",
|
||||
"fixtures/print_config/ban_rules/eslintrc.json",
|
||||
"-A",
|
||||
"all",
|
||||
"-D",
|
||||
"eqeqeq",
|
||||
"--print-config",
|
||||
];
|
||||
let options = lint_command().run_inner(args).unwrap();
|
||||
let ret = LintRunner::new(options).run();
|
||||
let CliRunResult::PrintConfigResult { config_file: config } = ret else {
|
||||
panic!("Expected PrintConfigResult, got {ret:?}")
|
||||
};
|
||||
|
||||
let expect_json =
|
||||
std::fs::read_to_string("fixtures/print_config/ban_rules/expect.json").unwrap();
|
||||
assert_eq!(config, expect_json.trim());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub enum CliRunResult {
|
|||
LintResult(LintResult),
|
||||
FormatResult(FormatResult),
|
||||
TypeCheckResult { duration: Duration, number_of_diagnostics: usize },
|
||||
PrintConfigResult { config_file: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -107,6 +108,10 @@ impl Termination for CliRunResult {
|
|||
|
||||
ExitCode::from(0)
|
||||
}
|
||||
Self::PrintConfigResult { config_file } => {
|
||||
println!("{config_file}");
|
||||
ExitCode::from(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@ use std::{
|
|||
fmt,
|
||||
};
|
||||
|
||||
use oxc_span::CompactStr;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
options::LintPlugins, rules::RULES, AllowWarnDeny, FixKind, FrameworkFlags, LintConfig,
|
||||
LintFilter, LintFilterKind, LintOptions, Linter, Oxlintrc, RuleCategory, RuleEnum,
|
||||
RuleWithSeverity,
|
||||
config::{ESLintRule, OxlintRules},
|
||||
options::LintPlugins,
|
||||
rules::RULES,
|
||||
AllowWarnDeny, FixKind, FrameworkFlags, LintConfig, LintFilter, LintFilterKind, LintOptions,
|
||||
Linter, Oxlintrc, RuleCategory, RuleEnum, RuleWithSeverity,
|
||||
};
|
||||
|
||||
#[must_use = "You dropped your builder without building a Linter! Did you mean to call .build()?"]
|
||||
|
|
@ -244,6 +247,44 @@ impl LinterBuilder {
|
|||
.map(|rule| RuleWithSeverity { rule: rule.clone(), severity: AllowWarnDeny::Warn })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// This function will panic if the `oxlintrc` is not valid JSON.
|
||||
pub fn resolve_final_config_file(&self, oxlintrc: Oxlintrc) -> String {
|
||||
let mut oxlintrc = oxlintrc;
|
||||
let previous_rules = std::mem::take(&mut oxlintrc.rules);
|
||||
|
||||
let rule_name_to_rule = previous_rules
|
||||
.into_iter()
|
||||
.map(|r| (get_name(&r.plugin_name, &r.rule_name), r))
|
||||
.collect::<rustc_hash::FxHashMap<_, _>>();
|
||||
|
||||
let new_rules = self
|
||||
.rules
|
||||
.iter()
|
||||
.map(|r: &RuleWithSeverity| {
|
||||
return ESLintRule {
|
||||
plugin_name: r.plugin_name().to_string(),
|
||||
rule_name: r.rule.name().to_string(),
|
||||
severity: r.severity,
|
||||
config: rule_name_to_rule
|
||||
.get(&get_name(r.plugin_name(), r.rule.name()))
|
||||
.and_then(|r| r.config.clone()),
|
||||
};
|
||||
})
|
||||
.collect();
|
||||
|
||||
oxlintrc.rules = OxlintRules::new(new_rules);
|
||||
serde_json::to_string_pretty(&oxlintrc).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_name(plugin_name: &str, rule_name: &str) -> CompactStr {
|
||||
if plugin_name == "eslint" {
|
||||
CompactStr::from(rule_name)
|
||||
} else {
|
||||
CompactStr::from(format!("{plugin_name}/{rule_name}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Oxlintrc> for LinterBuilder {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use serde::{de::Visitor, Deserialize, Serialize};
|
|||
/// You may also use `"readable"` or `false` to represent `"readonly"`, and
|
||||
/// `"writeable"` or `true` to represent `"writable"`.
|
||||
// <https://eslint.org/docs/v8.x/use/configure/language-options#using-configuration-files-1>
|
||||
#[derive(Debug, Default, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize, JsonSchema, Clone)]
|
||||
pub struct OxlintGlobals(FxHashMap<String, GlobalValue>);
|
||||
impl OxlintGlobals {
|
||||
pub fn is_enabled<Q>(&self, name: &Q) -> bool
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ pub use self::{
|
|||
env::OxlintEnv,
|
||||
globals::OxlintGlobals,
|
||||
oxlintrc::Oxlintrc,
|
||||
rules::ESLintRule,
|
||||
rules::OxlintRules,
|
||||
settings::{jsdoc::JSDocPluginSettings, OxlintSettings},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use crate::{options::LintPlugins, utils::read_to_string};
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Default, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema)]
|
||||
#[serde(default)]
|
||||
#[non_exhaustive]
|
||||
pub struct Oxlintrc {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ type RuleSet = FxHashSet<RuleWithSeverity>;
|
|||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct OxlintRules(Vec<ESLintRule>);
|
||||
|
||||
impl OxlintRules {
|
||||
pub fn new(rules: Vec<ESLintRule>) -> Self {
|
||||
Self(rules)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct ESLintRule {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::utils::default_true;
|
||||
|
||||
// <https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/settings.md>
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct JSDocPluginSettings {
|
||||
/// For all rules but NOT apply to `check-access` and `empty-tags` rule
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use schemars::JsonSchema;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// <https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#configurations>
|
||||
#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct JSXA11yPluginSettings {
|
||||
#[serde(rename = "polymorphicPropName")]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use self::{
|
|||
};
|
||||
|
||||
/// Shared settings for plugins
|
||||
#[derive(Debug, Deserialize, Serialize, Default, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default, JsonSchema)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct OxlintSettings {
|
||||
#[serde(default)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct NextPluginSettings {
|
||||
#[serde(default)]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use schemars::JsonSchema;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// <https://github.com/jsx-eslint/eslint-plugin-react#configuration-legacy-eslintrc->
|
||||
#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Default, Serialize, JsonSchema)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct ReactPluginSettings {
|
||||
#[serde(default)]
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ Arguments:
|
|||
Do not display any diagnostics
|
||||
- **` --threads`**=_`INT`_ —
|
||||
Number of threads to use. Set to 1 for using only 1 CPU core
|
||||
- **` --print-config`** —
|
||||
This option outputs the configuration to be used. When present, no linting is performed and only config-related options are valid.
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ Output
|
|||
Miscellaneous
|
||||
--silent Do not display any diagnostics
|
||||
--threads=INT Number of threads to use. Set to 1 for using only 1 CPU core
|
||||
--print-config This option outputs the configuration to be used. When present, no
|
||||
linting is performed and only config-related options are valid.
|
||||
|
||||
Available positional items:
|
||||
PATH Single file, single path or list of paths
|
||||
|
|
|
|||
Loading…
Reference in a new issue