mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
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">
This commit is contained in:
parent
4436774e8c
commit
a0effab160
7 changed files with 211 additions and 23 deletions
|
|
@ -4,8 +4,11 @@ use serde::Deserialize;
|
|||
use std::{borrow::Borrow, hash::Hash};
|
||||
|
||||
/// Predefine global variables.
|
||||
// TODO: list the keys we support
|
||||
// <https://eslint.org/docs/v8.x/use/configure/language-options#specifying-environments>
|
||||
///
|
||||
/// Environments 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.
|
||||
#[derive(Debug, Clone, Deserialize, JsonSchema)]
|
||||
pub struct OxlintEnv(FxHashMap<String, bool>);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,46 @@
|
|||
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,
|
||||
|
|
@ -16,8 +48,118 @@ pub enum GlobalValue {
|
|||
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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,10 +54,12 @@ use crate::{
|
|||
#[derive(Debug, Default, Deserialize, JsonSchema)]
|
||||
#[serde(default)]
|
||||
pub struct OxlintConfig {
|
||||
/// See [Oxlint Rules](./rules)
|
||||
/// See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html).
|
||||
pub(crate) rules: OxlintRules,
|
||||
pub(crate) settings: OxlintSettings,
|
||||
/// Environments enable and disable collections of global variables.
|
||||
pub(crate) env: OxlintEnv,
|
||||
/// Enabled or disabled specific global variables.
|
||||
pub(crate) globals: OxlintGlobals,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ impl JsonSchema for OxlintRules {
|
|||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, JsonSchema)]
|
||||
#[schemars(description = "See [Oxlint Rules](./rules)")]
|
||||
#[schemars(
|
||||
description = "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html)"
|
||||
)]
|
||||
struct DummyRuleMap(pub FxHashMap<String, DummyRule>);
|
||||
|
||||
gen.subschema_for::<DummyRuleMap>()
|
||||
|
|
|
|||
|
|
@ -9,13 +9,23 @@ expression: json
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"$ref": "#/definitions/OxlintEnv"
|
||||
"description": "Environments enable and disable collections of global variables.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintEnv"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globals": {
|
||||
"$ref": "#/definitions/OxlintGlobals"
|
||||
"description": "Enabled or disabled specific global variables.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintGlobals"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"description": "See [Oxlint Rules](./rules)",
|
||||
"description": "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html).",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintRules"
|
||||
|
|
@ -101,7 +111,7 @@ expression: json
|
|||
]
|
||||
},
|
||||
"DummyRuleMap": {
|
||||
"description": "See [Oxlint Rules](./rules)",
|
||||
"description": "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DummyRule"
|
||||
|
|
@ -201,14 +211,14 @@ expression: json
|
|||
]
|
||||
},
|
||||
"OxlintEnv": {
|
||||
"description": "Predefine global variables.",
|
||||
"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",
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"OxlintGlobals": {
|
||||
"description": "Add or remove global variables.",
|
||||
"description": "Add or remove global variables.\n\nFor each global variable, set the corresponding value equal to `\"writable\"` to allow the variable to be overwritten or `\"readonly\"` to disallow overwriting.\n\nGlobals 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:\n\n```json\n\n{ \"env\": { \"es6\": true }, \"globals\": { \"Promise\": \"off\" } }\n\n```\n\nYou may also use `\"readable\"` or `false` to represent `\"readonly\"`, and `\"writeable\"` or `true` to represent `\"writable\"`.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/GlobalValue"
|
||||
|
|
|
|||
|
|
@ -5,13 +5,23 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"$ref": "#/definitions/OxlintEnv"
|
||||
"description": "Environments enable and disable collections of global variables.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintEnv"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globals": {
|
||||
"$ref": "#/definitions/OxlintGlobals"
|
||||
"description": "Enabled or disabled specific global variables.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintGlobals"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"description": "See [Oxlint Rules](./rules)",
|
||||
"description": "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html).",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OxlintRules"
|
||||
|
|
@ -97,7 +107,7 @@
|
|||
]
|
||||
},
|
||||
"DummyRuleMap": {
|
||||
"description": "See [Oxlint Rules](./rules)",
|
||||
"description": "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DummyRule"
|
||||
|
|
@ -197,14 +207,14 @@
|
|||
]
|
||||
},
|
||||
"OxlintEnv": {
|
||||
"description": "Predefine global variables.",
|
||||
"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",
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"OxlintGlobals": {
|
||||
"description": "Add or remove global variables.",
|
||||
"description": "Add or remove global variables.\n\nFor each global variable, set the corresponding value equal to `\"writable\"` to allow the variable to be overwritten or `\"readonly\"` to disallow overwriting.\n\nGlobals 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:\n\n```json\n\n{ \"env\": { \"es6\": true }, \"globals\": { \"Promise\": \"off\" } }\n\n```\n\nYou may also use `\"readable\"` or `false` to represent `\"readonly\"`, and `\"writeable\"` or `true` to represent `\"writable\"`.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/GlobalValue"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ type: `object`
|
|||
|
||||
Predefine global variables.
|
||||
|
||||
Environments 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.
|
||||
|
||||
|
||||
|
||||
## globals
|
||||
|
|
@ -48,13 +50,30 @@ type: `object`
|
|||
|
||||
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"`.
|
||||
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
type: `object`
|
||||
|
||||
See [Oxlint Rules](./rules)
|
||||
See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue