-
-
Notifications
You must be signed in to change notification settings - Fork 466
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): support more flexible config.globals values (#4990)
Support `"readable", `"writable"`, and boolean values for `GlobalValue`. I also enhanced the documentation for `OxcGlobals` ## Screenshot <img width="797" alt="image" src="https://github.com/user-attachments/assets/8f76de4c-4ae8-44d1-9be1-720fc3c7e0ec">
- Loading branch information
Showing
7 changed files
with
211 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,165 @@ | ||
use rustc_hash::FxHashMap; | ||
use schemars::JsonSchema; | ||
use serde::Deserialize; | ||
use serde::{de::Visitor, Deserialize}; | ||
use std::{borrow, fmt, hash}; | ||
|
||
/// Add or remove global variables. | ||
/// | ||
/// For each global variable, set the corresponding value equal to `"writable"` | ||
/// to allow the variable to be overwritten or `"readonly"` to disallow overwriting. | ||
/// | ||
/// Globals can be disabled by setting their value to `"off"`. For example, in | ||
/// an environment where most Es2015 globals are available but `Promise` is unavailable, | ||
/// you might use this config: | ||
/// | ||
/// ```json | ||
/// | ||
/// { | ||
/// "env": { | ||
/// "es6": true | ||
/// }, | ||
/// "globals": { | ||
/// "Promise": "off" | ||
/// } | ||
/// } | ||
/// | ||
/// ``` | ||
/// | ||
/// 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, JsonSchema)] | ||
pub struct OxlintGlobals(FxHashMap<String, GlobalValue>); | ||
impl OxlintGlobals { | ||
pub fn is_enabled<Q>(&self, name: &Q) -> bool | ||
where | ||
String: borrow::Borrow<Q>, | ||
Q: ?Sized + Eq + hash::Hash, | ||
{ | ||
self.0.get(name).is_some_and(|value| *value != GlobalValue::Off) | ||
} | ||
} | ||
|
||
// TODO: support deprecated `false` | ||
#[derive(Debug, Eq, PartialEq, Deserialize, JsonSchema)] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, JsonSchema)] | ||
#[serde(rename_all = "lowercase")] | ||
pub enum GlobalValue { | ||
Readonly, | ||
Writeable, | ||
Off, | ||
} | ||
|
||
impl OxlintGlobals { | ||
pub fn is_enabled(&self, name: &str) -> bool { | ||
self.0.get(name).is_some_and(|value| *value != GlobalValue::Off) | ||
impl GlobalValue { | ||
pub const fn as_str(self) -> &'static str { | ||
match self { | ||
Self::Readonly => "readonly", | ||
Self::Writeable => "writeable", | ||
Self::Off => "off", | ||
} | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for GlobalValue { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: serde::Deserializer<'de>, | ||
{ | ||
deserializer.deserialize_any(GlobalValueVisitor) | ||
} | ||
} | ||
|
||
impl From<bool> for GlobalValue { | ||
#[inline] | ||
fn from(value: bool) -> Self { | ||
if value { | ||
GlobalValue::Writeable | ||
} else { | ||
GlobalValue::Readonly | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<&str> for GlobalValue { | ||
type Error = &'static str; | ||
|
||
fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
match value { | ||
"readonly" | "readable" => Ok(GlobalValue::Readonly), | ||
"writable" | "writeable" => Ok(GlobalValue::Writeable), | ||
"off" => Ok(GlobalValue::Off), | ||
_ => Err("Invalid global value"), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for GlobalValue { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
self.as_str().fmt(f) | ||
} | ||
} | ||
|
||
struct GlobalValueVisitor; | ||
impl<'de> Visitor<'de> for GlobalValueVisitor { | ||
type Value = GlobalValue; | ||
|
||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
write!(formatter, "'readonly', 'writable', 'off', or a boolean") | ||
} | ||
|
||
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> | ||
where | ||
E: serde::de::Error, | ||
{ | ||
Ok(v.into()) | ||
} | ||
|
||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> | ||
where | ||
E: serde::de::Error, | ||
{ | ||
v.try_into().map_err(E::custom) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
use serde_json::json; | ||
|
||
macro_rules! globals { | ||
($($json:tt)+) => { | ||
OxlintGlobals::deserialize(&json!($($json)+)).unwrap() | ||
}; | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_normal() { | ||
let globals = globals!({ | ||
"foo": "readonly", | ||
"bar": "writable", | ||
"baz": "off", | ||
}); | ||
assert!(globals.is_enabled("foo")); | ||
assert!(globals.is_enabled("bar")); | ||
assert!(!globals.is_enabled("baz")); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_legacy_spelling() { | ||
let globals = globals!({ | ||
"foo": "readable", | ||
"bar": "writeable", | ||
}); | ||
assert!(globals.is_enabled("foo")); | ||
assert!(globals.is_enabled("bar")); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_bool() { | ||
let globals = globals!({ | ||
"foo": true, | ||
"bar": false, | ||
}); | ||
assert!(globals.is_enabled("foo")); | ||
assert!(globals.is_enabled("bar")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters