diff --git a/crates/oxc_linter/src/config/config_store.rs b/crates/oxc_linter/src/config/config_store.rs index b4e37e03cf815..c688556c1d955 100644 --- a/crates/oxc_linter/src/config/config_store.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -1077,7 +1077,7 @@ mod test { // Base config has external rule with options A, severity warn let base_external_rule_id = store.lookup_rule_id("custom", "my-rule").unwrap(); let base_options_id = - store.add_options(ExternalRuleId::DUMMY, serde_json::json!([{ "opt": "A" }])); + store.add_options(ExternalRuleId::DUMMY, vec![serde_json::json!({ "opt": "A" })]); let base = Config::new( vec![], @@ -1096,7 +1096,7 @@ mod test { base_external_rule_id, store.add_options( ExternalRuleId::DUMMY, - serde_json::json!([{ "opt": "B" }]), + vec![serde_json::json!({ "opt": "B" })], ), AllowWarnDeny::Deny, )], diff --git a/crates/oxc_linter/src/config/rules.rs b/crates/oxc_linter/src/config/rules.rs index dd799e61ebf15..c4ba7659a8910 100644 --- a/crates/oxc_linter/src/config/rules.rs +++ b/crates/oxc_linter/src/config/rules.rs @@ -56,7 +56,8 @@ pub struct ESLintRule { /// Severity of the rule: `off`, `warn`, `error`, etc. pub severity: AllowWarnDeny, /// JSON configuration for the rule, if any. - pub config: Option, + /// If is `Some`, the `Vec` must not be empty. + pub config: Option>, } impl OxlintRules { @@ -95,7 +96,15 @@ impl OxlintRules { .find(|r| r.name() == rule_name && r.plugin_name() == plugin_name) }); if let Some(rule) = rule { - let config = rule_config.config.clone().unwrap_or_default(); + // Configs are stored as `Option>`, but `from_configuration` expects + // a single `Value` with `Value::Null` being the equivalent of `None` + let config = match &rule_config.config { + Some(config) => { + debug_assert!(!config.is_empty()); + serde_json::Value::Array(config.clone()) + } + None => serde_json::Value::Null, + }; rules_to_replace.push((rule.from_configuration(config), severity)); } } else { @@ -190,7 +199,7 @@ impl Serialize for OxlintRules { let key = rule.full_name(); match rule.config.as_ref() { // e.g. unicorn/some-rule: ["warn", { foo: "bar" }] - Some(config) if !config.is_null() => { + Some(config) => { let value = (rule.severity.as_str(), config); rules.serialize_entry(&key, &value)?; } @@ -283,7 +292,7 @@ pub(super) fn unalias_plugin_name(plugin_name: &str, rule_name: &str) -> (String fn parse_rule_value( value: serde_json::Value, -) -> Result<(AllowWarnDeny, Option), Error> { +) -> Result<(AllowWarnDeny, Option>), Error> { match value { serde_json::Value::String(_) | serde_json::Value::Number(_) => { let severity = AllowWarnDeny::try_from(&value)?; @@ -307,7 +316,7 @@ fn parse_rule_value( } else { // e.g. ["error", "args", { type: "whatever" }, ["len", "also"]] v.remove(0); - Some(serde_json::Value::Array(v)) + Some(v) }; Ok((severity, config)) @@ -382,7 +391,7 @@ mod test { assert_eq!(r3.rule_name, "dummy"); assert_eq!(r3.plugin_name, "eslint"); assert!(r3.severity.is_warn_deny()); - assert_eq!(r3.config, Some(serde_json::json!(["arg1", "args2"]))); + assert_eq!(r3.config, Some(vec![serde_json::json!("arg1"), serde_json::json!("args2")])); let r4 = rules.next().unwrap(); assert_eq!(r4.rule_name, "noop"); diff --git a/crates/oxc_linter/src/external_plugin_store.rs b/crates/oxc_linter/src/external_plugin_store.rs index f8025be51e47b..34bb14547bee5 100644 --- a/crates/oxc_linter/src/external_plugin_store.rs +++ b/crates/oxc_linter/src/external_plugin_store.rs @@ -145,19 +145,11 @@ impl ExternalPluginStore { } /// Add options to the store and return its [`ExternalOptionsId`]. - /// - /// `options` must be a `serde_json::Value::Array`. - /// - /// # Panics - /// Panics if `options` is not an array. pub fn add_options( &mut self, rule_id: ExternalRuleId, - options: serde_json::Value, + options: Vec, ) -> ExternalOptionsId { - let serde_json::Value::Array(options) = options else { - panic!("`options` must be an array"); - }; debug_assert!(!options.is_empty(), "`options` should never be an empty `Vec`"); self.options.push((rule_id, options)) }