From ead637bf5085a07f3fc6a20d3925199128a59ae2 Mon Sep 17 00:00:00 2001 From: Boshen Date: Thu, 23 May 2024 01:15:36 +0800 Subject: [PATCH] feat(website): generate linter configuration page --- Cargo.lock | 137 ++++++++++--------- Cargo.toml | 2 +- crates/oxc_linter/src/config/env.rs | 3 +- crates/oxc_linter/src/config/globals.rs | 3 +- crates/oxc_linter/src/config/mod.rs | 34 ++++- crates/oxc_linter/src/config/rules.rs | 5 +- crates/oxc_linter/src/config/settings/mod.rs | 7 +- tasks/website/src/linter/json_schema.rs | 67 ++++++--- 8 files changed, 160 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2c1a507c..afdcf8529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,7 +76,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -87,7 +87,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -170,7 +170,7 @@ checksum = "9a8d5b11f7fa1068e5bbac8ab6c8c2c6940047f69185987446b60c995d4bf89c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -252,6 +252,31 @@ dependencies = [ "half", ] +[[package]] +name = "ckb_schemars" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21f99fca82a4eb8708e406e99246987b087ecc1e1babeece1a0b1d5238b1750" +dependencies = [ + "ckb_schemars_derive", + "dyn-clone", + "indexmap", + "serde", + "serde_json", +] + +[[package]] +name = "ckb_schemars_derive" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c813b4fadbdd9f33b1cf02a1ddfa9537d955c8d2fbe150d1fc1684dbf78e73" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals 0.26.0", + "syn 1.0.109", +] + [[package]] name = "clean-path" version = "0.2.1" @@ -403,7 +428,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -594,7 +619,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1010,7 +1035,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1089,7 +1114,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1104,7 +1129,7 @@ dependencies = [ "quote", "regex", "semver", - "syn", + "syn 2.0.63", ] [[package]] @@ -1207,7 +1232,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1418,6 +1443,7 @@ dependencies = [ name = "oxc_linter" version = "0.0.0" dependencies = [ + "ckb_schemars", "convert_case", "dashmap", "insta", @@ -1443,7 +1469,6 @@ dependencies = [ "regex", "rust-lapper", "rustc-hash", - "schemars", "serde", "serde_json", "static_assertions", @@ -1458,7 +1483,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1793,7 +1818,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1847,7 +1872,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1882,7 +1907,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -1914,7 +1939,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", "version_check", "yansi", ] @@ -2178,31 +2203,6 @@ dependencies = [ "hashlink", ] -[[package]] -name = "schemars" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29" -dependencies = [ - "dyn-clone", - "indexmap", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals 0.29.1", - "syn", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -2249,7 +2249,18 @@ checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -2260,18 +2271,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2294,7 +2294,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2411,6 +2411,17 @@ dependencies = [ "is_ci", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.63" @@ -2471,7 +2482,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2536,7 +2547,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2637,7 +2648,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2665,7 +2676,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] @@ -2743,7 +2754,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals 0.28.0", - "syn", + "syn 2.0.63", ] [[package]] @@ -2916,7 +2927,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -2938,7 +2949,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2973,11 +2984,11 @@ name = "website" version = "0.0.0" dependencies = [ "bpaf", + "ckb_schemars", "handlebars", "oxc_cli", "oxc_linter", "pico-args", - "schemars", "serde", "serde_json", ] @@ -3184,7 +3195,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.63", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4cbb8bf38..e269fcea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -173,7 +173,7 @@ unicode-width = "0.1.12" saphyr = "0.0.1" base64-simd = "0.8" cfg-if = "1.0.0" -schemars = "0.8.20" +schemars = { version = "0.8.16", package = "ckb_schemars" } # contains fix for stripping newlines in description [workspace.metadata.cargo-shear] ignored = ["napi", "oxc_traverse"] diff --git a/crates/oxc_linter/src/config/env.rs b/crates/oxc_linter/src/config/env.rs index ef214dac5..2e9a682d4 100644 --- a/crates/oxc_linter/src/config/env.rs +++ b/crates/oxc_linter/src/config/env.rs @@ -2,8 +2,9 @@ use rustc_hash::FxHashMap; use schemars::JsonSchema; use serde::Deserialize; -/// Env +/// Predefine global variables. // TODO: list the keys we support +// #[derive(Debug, Clone, Deserialize, JsonSchema)] pub struct ESLintEnv(FxHashMap); diff --git a/crates/oxc_linter/src/config/globals.rs b/crates/oxc_linter/src/config/globals.rs index d958e593a..d9f246e06 100644 --- a/crates/oxc_linter/src/config/globals.rs +++ b/crates/oxc_linter/src/config/globals.rs @@ -3,7 +3,8 @@ use serde::Deserialize; use rustc_hash::FxHashMap; -/// +/// Add or remove global variables. +// #[derive(Debug, Default, Deserialize, JsonSchema)] pub struct ESLintGlobals(FxHashMap); diff --git a/crates/oxc_linter/src/config/mod.rs b/crates/oxc_linter/src/config/mod.rs index 1c838789a..6717fe67a 100644 --- a/crates/oxc_linter/src/config/mod.rs +++ b/crates/oxc_linter/src/config/mod.rs @@ -17,11 +17,41 @@ pub use self::{ settings::jsdoc::JSDocPluginSettings, settings::ESLintSettings, }; -/// ESLint Config -// +/// Oxlint Configuration File +/// +/// This configuration is aligned with ESLint v8's configuration schema (`eslintrc.json`). +/// +/// Usage: `oxlint -c oxlintrc.json` +/// +/// ::: danger NOTE +/// +/// Only the `.json` format is supported. +/// +/// ::: +/// +/// Example +/// +/// ```json +/// // oxlintrc.json +/// { +/// // Comments are supported. +/// "env": { +/// "browser": true +/// }, +/// "globals": { +/// "foo": "readonly", +/// }, +/// "settings": { +/// }, +/// "rules": { +/// "eqeqeq": "warn", +/// }, +/// } +/// ``` #[derive(Debug, Default, Deserialize, JsonSchema)] #[serde(default)] pub struct ESLintConfig { + /// See [Oxlint Rules](./rules) pub(crate) rules: ESLintRules, pub(crate) settings: ESLintSettings, pub(crate) env: ESLintEnv, diff --git a/crates/oxc_linter/src/config/rules.rs b/crates/oxc_linter/src/config/rules.rs index 66abe5b32..bf690f193 100644 --- a/crates/oxc_linter/src/config/rules.rs +++ b/crates/oxc_linter/src/config/rules.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, fmt, ops::Deref}; use oxc_diagnostics::{Error, OxcDiagnostic}; +use rustc_hash::FxHashMap; use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; use serde::{ de::{self, Deserializer, Visitor}, @@ -9,8 +10,6 @@ use serde::{ use crate::AllowWarnDeny; -// The `rules` field from ESLint config -// // TS type is `Record` // - type SeverityConf = 0 | 1 | 2 | "off" | "warn" | "error"; // - type RuleConf = SeverityConf | [SeverityConf, ...any[]]; @@ -45,7 +44,7 @@ impl JsonSchema for ESLintRules { String(String), Array(Vec), } - DummyRule::json_schema(gen) + gen.subschema_for::>() } } diff --git a/crates/oxc_linter/src/config/settings/mod.rs b/crates/oxc_linter/src/config/settings/mod.rs index 0dc735482..ae4400093 100644 --- a/crates/oxc_linter/src/config/settings/mod.rs +++ b/crates/oxc_linter/src/config/settings/mod.rs @@ -11,12 +11,7 @@ use self::{ react::ReactPluginSettings, }; -// The `settings` field from ESLint config -// An object containing name-value pairs of information that should be available to all rules -// -// TS type is `Object` -// -// But each plugin extends this with their own properties. +/// Shared settings for plugins #[derive(Debug, Deserialize, Default, JsonSchema)] pub struct ESLintSettings { #[serde(default)] diff --git a/tasks/website/src/linter/json_schema.rs b/tasks/website/src/linter/json_schema.rs index 485a7a68c..f88fa0bb2 100644 --- a/tasks/website/src/linter/json_schema.rs +++ b/tasks/website/src/linter/json_schema.rs @@ -1,6 +1,6 @@ use handlebars::Handlebars; use schemars::{ - schema::{RootSchema, Schema, SchemaObject}, + schema::{RootSchema, Schema, SchemaObject, SingleOrVec}, schema_for, }; use serde::Serialize; @@ -13,7 +13,8 @@ pub fn generate_schema_json() { } pub fn generate_schema_markdown() { - let rendered = Renderer::new().render(); + let root_schema = schema_for!(ESLintConfig); + let rendered = Renderer::new(root_schema).render(); println!("{rendered}"); } @@ -25,9 +26,11 @@ const ROOT: &str = " const SECTION: &str = " {{#each sections}} -{{level}} `{{title}}` +{{level}} {{title}} +{{#if instance_type}} type: `{{instance_type}}` +{{/if}} {{description}} @@ -46,7 +49,7 @@ struct Root { struct Section { level: String, title: String, - instance_type: String, + instance_type: Option, description: String, sections: Vec
, } @@ -57,12 +60,11 @@ struct Renderer { } impl Renderer { - fn new() -> Self { + fn new(root_schema: RootSchema) -> Self { let mut handlebars = Handlebars::new(); handlebars.register_escape_fn(handlebars::no_escape); assert!(handlebars.register_template_string("root", ROOT).is_ok()); assert!(handlebars.register_template_string("section", SECTION).is_ok()); - let root_schema = schema_for!(ESLintConfig); Self { handlebars, root_schema } } @@ -78,10 +80,14 @@ impl Renderer { } } - fn get_referenced_schema(&self, reference: &str) -> &SchemaObject { - let definitions = &self.root_schema.definitions; - let definition = definitions.get(reference.trim_start_matches("#/definitions/")); - definition.map(Self::get_schema_object).unwrap() + fn get_referenced_schema<'a>(&'a self, object: &'a SchemaObject) -> &'a SchemaObject { + if let Some(reference) = &object.reference { + let definitions = &self.root_schema.definitions; + let definition = definitions.get(reference.trim_start_matches("#/definitions/")); + definition.map(Self::get_schema_object).unwrap() + } else { + object + } } fn render_root_schema(&self, root_schema: &RootSchema) -> Root { @@ -101,23 +107,42 @@ impl Renderer { parent_key: Option<&str>, schema: &SchemaObject, ) -> Vec
{ - let Some(object) = &schema.object else { return vec![] }; - object - .properties - .iter() - .map(|(key, schema)| { - let key = parent_key.map_or_else(|| key.clone(), |k| format!("{k}.{key}")); - self.render_schema(depth + 1, &key, Self::get_schema_object(schema)) - }) - .collect::>() + if let Some(array) = &schema.array { + return array + .items + .iter() + .map(|item| match item { + SingleOrVec::Single(schema) => { + let schema_object = Self::get_schema_object(schema); + let key = parent_key.map_or_else(String::new, |k| format!("{k}[n]")); + self.render_schema(depth + 1, &key, schema_object) + } + SingleOrVec::Vec(_) => panic!(), + }) + .collect(); + } + if let Some(object) = &schema.object { + return object + .properties + .iter() + .map(|(key, schema)| { + let key = parent_key.map_or_else(|| key.clone(), |k| format!("{k}.{key}")); + self.render_schema(depth + 1, &key, Self::get_schema_object(schema)) + }) + .collect::>(); + } + vec![] } fn render_schema(&self, depth: usize, key: &str, schema: &SchemaObject) -> Section { - let schema = schema.reference.as_ref().map_or(schema, |r| self.get_referenced_schema(r)); + let schema = self.get_referenced_schema(schema); Section { level: "#".repeat(depth), title: key.into(), - instance_type: serde_json::to_string(&schema.instance_type).unwrap().replace('"', ""), + instance_type: schema + .instance_type + .as_ref() + .map(|t| serde_json::to_string_pretty(t).unwrap().replace('"', "")), description: schema .metadata .as_ref()