mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +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
|
/// Number of threads to use. Set to 1 for using only 1 CPU core
|
||||||
#[bpaf(argument("INT"), hide_usage)]
|
#[bpaf(argument("INT"), hide_usage)]
|
||||||
pub threads: Option<usize>,
|
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)]
|
#[allow(clippy::ptr_arg)]
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,19 @@ impl Runner for LintRunner {
|
||||||
};
|
};
|
||||||
|
|
||||||
enable_plugins.apply_overrides(&mut oxlintrc.plugins);
|
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)
|
let builder = LinterBuilder::from_oxlintrc(false, oxlintrc)
|
||||||
.with_filters(filter)
|
.with_filters(filter)
|
||||||
.with_fix(fix_options.fix_kind());
|
.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 =
|
let mut options =
|
||||||
LintServiceOptions::new(cwd, paths).with_cross_module(builder.plugins().has_import());
|
LintServiceOptions::new(cwd, paths).with_cross_module(builder.plugins().has_import());
|
||||||
let linter = builder.build();
|
let linter = builder.build();
|
||||||
|
|
@ -586,4 +595,40 @@ mod test {
|
||||||
// Write the file back.
|
// Write the file back.
|
||||||
fs::write(file, content).unwrap();
|
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),
|
LintResult(LintResult),
|
||||||
FormatResult(FormatResult),
|
FormatResult(FormatResult),
|
||||||
TypeCheckResult { duration: Duration, number_of_diagnostics: usize },
|
TypeCheckResult { duration: Duration, number_of_diagnostics: usize },
|
||||||
|
PrintConfigResult { config_file: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|
@ -107,6 +108,10 @@ impl Termination for CliRunResult {
|
||||||
|
|
||||||
ExitCode::from(0)
|
ExitCode::from(0)
|
||||||
}
|
}
|
||||||
|
Self::PrintConfigResult { config_file } => {
|
||||||
|
println!("{config_file}");
|
||||||
|
ExitCode::from(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@ use std::{
|
||||||
fmt,
|
fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use oxc_span::CompactStr;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
options::LintPlugins, rules::RULES, AllowWarnDeny, FixKind, FrameworkFlags, LintConfig,
|
config::{ESLintRule, OxlintRules},
|
||||||
LintFilter, LintFilterKind, LintOptions, Linter, Oxlintrc, RuleCategory, RuleEnum,
|
options::LintPlugins,
|
||||||
RuleWithSeverity,
|
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()?"]
|
#[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 })
|
.map(|rule| RuleWithSeverity { rule: rule.clone(), severity: AllowWarnDeny::Warn })
|
||||||
.collect()
|
.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 {
|
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
|
/// You may also use `"readable"` or `false` to represent `"readonly"`, and
|
||||||
/// `"writeable"` or `true` to represent `"writable"`.
|
/// `"writeable"` or `true` to represent `"writable"`.
|
||||||
// <https://eslint.org/docs/v8.x/use/configure/language-options#using-configuration-files-1>
|
// <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>);
|
pub struct OxlintGlobals(FxHashMap<String, GlobalValue>);
|
||||||
impl OxlintGlobals {
|
impl OxlintGlobals {
|
||||||
pub fn is_enabled<Q>(&self, name: &Q) -> bool
|
pub fn is_enabled<Q>(&self, name: &Q) -> bool
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ pub use self::{
|
||||||
env::OxlintEnv,
|
env::OxlintEnv,
|
||||||
globals::OxlintGlobals,
|
globals::OxlintGlobals,
|
||||||
oxlintrc::Oxlintrc,
|
oxlintrc::Oxlintrc,
|
||||||
|
rules::ESLintRule,
|
||||||
|
rules::OxlintRules,
|
||||||
settings::{jsdoc::JSDocPluginSettings, OxlintSettings},
|
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)]
|
#[serde(default)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Oxlintrc {
|
pub struct Oxlintrc {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ type RuleSet = FxHashSet<RuleWithSeverity>;
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct OxlintRules(Vec<ESLintRule>);
|
pub struct OxlintRules(Vec<ESLintRule>);
|
||||||
|
|
||||||
|
impl OxlintRules {
|
||||||
|
pub fn new(rules: Vec<ESLintRule>) -> Self {
|
||||||
|
Self(rules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct ESLintRule {
|
pub struct ESLintRule {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::utils::default_true;
|
use crate::utils::default_true;
|
||||||
|
|
||||||
// <https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/settings.md>
|
// <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))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct JSDocPluginSettings {
|
pub struct JSDocPluginSettings {
|
||||||
/// For all rules but NOT apply to `check-access` and `empty-tags` rule
|
/// For all rules but NOT apply to `check-access` and `empty-tags` rule
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
// <https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#configurations>
|
// <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))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct JSXA11yPluginSettings {
|
pub struct JSXA11yPluginSettings {
|
||||||
#[serde(rename = "polymorphicPropName")]
|
#[serde(rename = "polymorphicPropName")]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use self::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Shared settings for plugins
|
/// Shared settings for plugins
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Default, JsonSchema)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct OxlintSettings {
|
pub struct OxlintSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Default, Serialize, JsonSchema)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct NextPluginSettings {
|
pub struct NextPluginSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
// <https://github.com/jsx-eslint/eslint-plugin-react#configuration-legacy-eslintrc->
|
// <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))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub struct ReactPluginSettings {
|
pub struct ReactPluginSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,8 @@ Arguments:
|
||||||
Do not display any diagnostics
|
Do not display any diagnostics
|
||||||
- **` --threads`**=_`INT`_ —
|
- **` --threads`**=_`INT`_ —
|
||||||
Number of threads to use. Set to 1 for using only 1 CPU core
|
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
|
Miscellaneous
|
||||||
--silent Do not display any diagnostics
|
--silent Do not display any diagnostics
|
||||||
--threads=INT Number of threads to use. Set to 1 for using only 1 CPU core
|
--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:
|
Available positional items:
|
||||||
PATH Single file, single path or list of paths
|
PATH Single file, single path or list of paths
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue