diff --git a/Cargo.lock b/Cargo.lock
index fdbf6286dd174..4e5d5417394a7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,6 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
+ "getrandom",
"once_cell",
"version_check",
"zerocopy",
@@ -192,6 +193,12 @@ dependencies = [
"allocator-api2",
]
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
[[package]]
name = "bytes"
version = "1.6.0"
@@ -388,6 +395,29 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "cssparser"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa",
+ "phf 0.11.2",
+ "smallvec",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "ctor"
version = "0.2.8"
@@ -440,6 +470,17 @@ dependencies = [
"powerfmt",
]
+[[package]]
+name = "derive_more"
+version = "0.99.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "digest"
version = "0.10.7"
@@ -450,6 +491,21 @@ dependencies = [
"crypto-common",
]
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
[[package]]
name = "dunce"
version = "1.0.4"
@@ -462,6 +518,12 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+[[package]]
+name = "ego-tree"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
+
[[package]]
name = "either"
version = "1.13.0"
@@ -559,6 +621,16 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
[[package]]
name = "futures"
version = "0.3.30"
@@ -648,6 +720,15 @@ dependencies = [
"slab",
]
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -658,6 +739,15 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -762,6 +852,20 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+[[package]]
+name = "html5ever"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "httparse"
version = "1.9.4"
@@ -937,7 +1041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -997,6 +1101,35 @@ dependencies = [
"url",
]
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "markdown"
+version = "1.0.0-alpha.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e61c5c85b392273c4d4ea546e6399ace3e3db172ab01b6de8f3d398d1dbd2ec"
+dependencies = [
+ "unicode-id",
+]
+
+[[package]]
+name = "markup5ever"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
+dependencies = [
+ "log",
+ "phf 0.11.2",
+ "phf_codegen 0.11.2",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
[[package]]
name = "memchr"
version = "2.7.4"
@@ -1147,6 +1280,12 @@ dependencies = [
"libloading",
]
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -1437,7 +1576,7 @@ dependencies = [
"oxc_span",
"oxc_tasks_common",
"oxc_transformer",
- "phf",
+ "phf 0.11.2",
"pico-args",
"project-root",
"rayon",
@@ -1525,6 +1664,7 @@ dependencies = [
"json-strip-comments",
"language-tags",
"lazy_static",
+ "markdown",
"memchr",
"mime_guess",
"once_cell",
@@ -1540,7 +1680,7 @@ dependencies = [
"oxc_semantic",
"oxc_span",
"oxc_syntax",
- "phf",
+ "phf 0.11.2",
"project-root",
"rayon",
"regex",
@@ -1728,7 +1868,7 @@ dependencies = [
"oxc_parser",
"oxc_span",
"oxc_syntax",
- "phf",
+ "phf 0.11.2",
"rustc-hash",
"serde",
"serde_json",
@@ -1773,7 +1913,7 @@ dependencies = [
"oxc_ast_macros",
"oxc_index",
"oxc_span",
- "phf",
+ "phf 0.11.2",
"rustc-hash",
"ryu-js",
"serde",
@@ -1980,6 +2120,15 @@ dependencies = [
"indexmap",
]
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_shared 0.10.0",
+]
+
[[package]]
name = "phf"
version = "0.11.2"
@@ -1987,7 +2136,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
- "phf_shared",
+ "phf_shared 0.11.2",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
+dependencies = [
+ "phf_generator 0.11.2",
+ "phf_shared 0.11.2",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand",
]
[[package]]
@@ -1996,7 +2175,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
- "phf_shared",
+ "phf_shared 0.11.2",
"rand",
]
@@ -2006,13 +2185,22 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
- "phf_generator",
- "phf_shared",
+ "phf_generator 0.11.2",
+ "phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn",
]
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "phf_shared"
version = "0.11.2"
@@ -2066,6 +2254,21 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
[[package]]
name = "prettyplease"
version = "0.2.20"
@@ -2119,6 +2322,18 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
"rand_core",
]
@@ -2127,6 +2342,9 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
[[package]]
name = "rayon"
@@ -2374,6 +2592,41 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "scraper"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0"
+dependencies = [
+ "ahash",
+ "cssparser",
+ "ego-tree",
+ "getopts",
+ "html5ever",
+ "once_cell",
+ "selectors",
+ "tendril",
+]
+
+[[package]]
+name = "selectors"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
+dependencies = [
+ "bitflags 2.6.0",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "new_debug_unreachable",
+ "phf 0.10.1",
+ "phf_codegen 0.10.0",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+]
+
[[package]]
name = "semver"
version = "1.0.23"
@@ -2463,6 +2716,15 @@ dependencies = [
"syn",
]
+[[package]]
+name = "servo_arc"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44"
+dependencies = [
+ "stable_deref_trait",
+]
+
[[package]]
name = "sha2"
version = "0.10.8"
@@ -2541,6 +2803,12 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -2553,6 +2821,32 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c"
+[[package]]
+name = "string_cache"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
+dependencies = [
+ "new_debug_unreachable",
+ "once_cell",
+ "parking_lot",
+ "phf_shared 0.10.0",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "subtle"
version = "2.6.1"
@@ -2592,6 +2886,17 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
[[package]]
name = "textwrap"
version = "0.16.1"
@@ -2879,6 +3184,12 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+[[package]]
+name = "unicode-id"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f"
+
[[package]]
name = "unicode-id-start"
version = "1.2.0"
@@ -2953,6 +3264,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
[[package]]
name = "valuable"
version = "0.1.0"
@@ -3067,10 +3384,16 @@ dependencies = [
"bpaf",
"handlebars",
"insta",
+ "markdown",
+ "oxc_allocator",
+ "oxc_diagnostics",
"oxc_linter",
+ "oxc_parser",
+ "oxc_span",
"oxlint",
"pico-args",
"schemars",
+ "scraper",
"serde",
"serde_json",
]
@@ -3257,6 +3580,7 @@ version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
+ "byteorder",
"zerocopy-derive",
]
diff --git a/crates/oxc_linter/Cargo.toml b/crates/oxc_linter/Cargo.toml
index fd82b48b5e359..e25ac5120ec00 100644
--- a/crates/oxc_linter/Cargo.toml
+++ b/crates/oxc_linter/Cargo.toml
@@ -57,3 +57,4 @@ schemars = { workspace = true, features = ["indexmap2"] }
static_assertions = { workspace = true }
insta = { workspace = true }
project-root = { workspace = true }
+markdown = { version = "1.0.0-alpha.18" }
diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs
index 3fb081fb1315d..287bd40a9bfe3 100644
--- a/crates/oxc_linter/src/rule.rs
+++ b/crates/oxc_linter/src/rule.rs
@@ -235,12 +235,22 @@ impl RuleWithSeverity {
#[cfg(test)]
mod test {
use crate::rules::RULES;
+ use markdown::{to_html_with_options, Options};
#[test]
fn ensure_documentation() {
assert!(!RULES.is_empty());
+ let options = Options::gfm();
+
for rule in RULES.iter() {
- assert!(rule.documentation().is_some_and(|s| !s.is_empty()), "{}", rule.name());
+ let name = rule.name();
+ assert!(
+ rule.documentation().is_some_and(|s| !s.is_empty()),
+ "Rule '{name}' is missing documentation."
+ );
+ // will panic if provided invalid markdown
+ let html = to_html_with_options(rule.documentation().unwrap(), &options).unwrap();
+ assert!(!html.is_empty());
}
}
}
diff --git a/crates/oxc_linter/src/rules/eslint/for_direction.rs b/crates/oxc_linter/src/rules/eslint/for_direction.rs
index 13dd1219f629a..b227ed8bc716c 100644
--- a/crates/oxc_linter/src/rules/eslint/for_direction.rs
+++ b/crates/oxc_linter/src/rules/eslint/for_direction.rs
@@ -35,7 +35,7 @@ declare_oxc_lint!(
/// ```javascript
/// for (var i = 0; i < 10; i--) {}
///
- /// for (var = 10; i >= 0; i++) {}
+ /// for (var i = 10; i >= 0; i++) {}
/// ```
ForDirection,
correctness,
diff --git a/crates/oxc_linter/src/rules/eslint/guard_for_in.rs b/crates/oxc_linter/src/rules/eslint/guard_for_in.rs
index 71d33322ee07e..0a487c4c0d233 100644
--- a/crates/oxc_linter/src/rules/eslint/guard_for_in.rs
+++ b/crates/oxc_linter/src/rules/eslint/guard_for_in.rs
@@ -24,8 +24,8 @@ declare_oxc_lint!(
/// ### Example
/// ```javascript
/// for (key in foo) {
- // doSomething(key);
- // }
+ /// doSomething(key);
+ /// }
/// ```
GuardForIn,
style
diff --git a/crates/oxc_linter/src/rules/eslint/max_lines.rs b/crates/oxc_linter/src/rules/eslint/max_lines.rs
index 3159d077ed00c..9aae4a5925380 100644
--- a/crates/oxc_linter/src/rules/eslint/max_lines.rs
+++ b/crates/oxc_linter/src/rules/eslint/max_lines.rs
@@ -37,16 +37,15 @@ impl Default for MaxLinesConfig {
declare_oxc_lint!(
/// ### What it does
- /// Enforce a maximum number of lines per file
+ /// Enforce a maximum number of lines per file.
///
/// ### Why is this bad?
///
- /// Some people consider large files a code smell. Large files tend to do a lot of things and can make it hard following what’s going.
- /// While there is not an objective maximum number of lines considered acceptable in a file, most people would agree it should not be in the thousands. Recommendations usually range from 100 to 500 lines.
- ///
- /// ### Example
- /// ```javascript
- /// ```
+ /// Some people consider large files a code smell. Large files tend to do a
+ /// lot of things and can make it hard following what’s going. While there
+ /// is not an objective maximum number of lines considered acceptable in a
+ /// file, most people would agree it should not be in the thousands.
+ /// Recommendations usually range from 100 to 500 lines.
MaxLines,
pedantic
);
diff --git a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs
index e2622c5c0195b..b142dd6ab966b 100644
--- a/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs
@@ -18,7 +18,7 @@ pub struct NoAwaitInLoop;
declare_oxc_lint!(
/// ### What it does
///
- /// This rule disallows the use of await within loop bodies. (for, for-in, for-of, while, do-while).
+ /// This rule disallows the use of `await` within loop bodies. (for, for-in, for-of, while, do-while).
///
/// ### Why is this bad?
///
@@ -28,14 +28,18 @@ declare_oxc_lint!(
/// ### Example
/// Bad:
/// ```javascript
- /// for (const user of users) {
- /// const userRecord = await getUserRecord(user);
+ /// async function bad() {
+ /// for (const user of users) {
+ /// const userRecord = await getUserRecord(user);
+ /// }
/// }
/// ```
///
/// Good:
/// ```javascript
- /// await Promise.all(users.map(user => getUserRecord(user)));
+ /// async function good() {
+ /// await Promise.all(users.map(user => getUserRecord(user)));
+ /// }
/// ```
NoAwaitInLoop,
perf
diff --git a/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs b/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs
index d654c7f24f8e7..b2ad9e9792746 100644
--- a/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_case_declarations.rs
@@ -26,19 +26,19 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
- // switch (foo) {
- // case 1:
- // let x = 1;
- // break;
- // case 2:
- // const y = 2;
- // break;
- // case 3:
- // function f() {}
- // break;
- // default:
- // class C {}
- // }
+ /// switch (foo) {
+ /// case 1:
+ /// let x = 1;
+ /// break;
+ /// case 2:
+ /// const y = 2;
+ /// break;
+ /// case 3:
+ /// function f() {}
+ /// break;
+ /// default:
+ /// class C {}
+ /// }
/// ```
NoCaseDeclarations,
pedantic
diff --git a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs
index 8a649b3d638cc..ea35fa436ad9d 100644
--- a/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs
@@ -29,12 +29,24 @@ enum NoCondAssignConfig {
declare_oxc_lint!(
/// ### What it does
///
+ /// Disallow assignment operators in conditional expressions
///
/// ### Why is this bad?
///
+ /// In conditional statements, it is very easy to mistype a comparison
+ /// operator (such as `==`) as an assignment operator (such as `=`).
+ ///
+ /// There are valid reasons to use assignment operators in conditional
+ /// statements. However, it can be difficult to tell whether a specific
+ /// assignment was intentional.
///
/// ### Example
- /// ```javascript
+ ///
+ /// ```js
+ /// // Check the user's job title
+ /// if (user.jobTitle = "manager") {
+ /// // user.jobTitle is now incorrect
+ /// }
/// ```
NoCondAssign,
correctness
diff --git a/crates/oxc_linter/src/rules/eslint/no_continue.rs b/crates/oxc_linter/src/rules/eslint/no_continue.rs
index dcca60785e98d..4c41048972703 100644
--- a/crates/oxc_linter/src/rules/eslint/no_continue.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_continue.rs
@@ -24,15 +24,15 @@ declare_oxc_lint!(
/// ### Example
/// ```javascript
/// var sum = 0,
- // i;
- //
- // for(i = 0; i < 10; i++) {
- // if(i >= 5) {
- // continue;
- // }
- //
- // sum += i;
- // }
+ /// i;
+ ///
+ /// for(i = 0; i < 10; i++) {
+ /// if(i >= 5) {
+ /// continue;
+ /// }
+ ///
+ /// sum += i;
+ /// }
/// ```
NoContinue,
style
diff --git a/crates/oxc_linter/src/rules/eslint/no_debugger.rs b/crates/oxc_linter/src/rules/eslint/no_debugger.rs
index af14da3d0fe3c..d61b7a5f24be2 100644
--- a/crates/oxc_linter/src/rules/eslint/no_debugger.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_debugger.rs
@@ -21,10 +21,13 @@ declare_oxc_lint!(
/// They're most commonly an accidental debugging leftover.
///
/// ### Example
+ ///
/// ```javascript
- /// const data = await getData();
- /// const result = complexCalculation(data);
- /// debugger;
+ /// async function main() {
+ /// const data = await getData();
+ /// const result = complexCalculation(data);
+ /// debugger;
+ /// }
/// ```
NoDebugger,
correctness,
diff --git a/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs b/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs
index 8ed18e65692c6..5629ceaeb2389 100644
--- a/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_ex_assign.rs
@@ -25,11 +25,11 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
- // try {
- // // code
- // } catch (e) {
- // e = 10;
- // }
+ /// try {
+ /// // code
+ /// } catch (e) {
+ /// e = 10;
+ /// }
/// ```
NoExAssign,
correctness
diff --git a/crates/oxc_linter/src/rules/eslint/no_undefined.rs b/crates/oxc_linter/src/rules/eslint/no_undefined.rs
index 213fcd6021ddd..ea860a512d45d 100644
--- a/crates/oxc_linter/src/rules/eslint/no_undefined.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_undefined.rs
@@ -29,15 +29,14 @@ declare_oxc_lint!(
/// var undefined = "foo";
///
/// if (foo === undefined) {
- /// ...
+ /// // ...
/// }
///
/// function baz(undefined) {
- /// ...
+ /// // ...
/// }
///
/// bar(undefined, "lorem");
- ///
/// ```
///
/// ### Example of good code
@@ -47,7 +46,7 @@ declare_oxc_lint!(
/// var Undefined = "foo";
///
/// if (typeof foo === "undefined") {
- /// ...
+ /// // ...
/// }
///
/// global.undefined = "foo";
diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs
index ffda849250af2..1d9bc5a978b3d 100644
--- a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs
@@ -23,7 +23,45 @@ declare_oxc_lint!(
///
///
/// ### Example
+ ///
+ /// Examples of **incorrect** code for this rule:
+ ///
+ /// ```javascript
+ /// /*eslint no-useless-escape: "error"*/
+ ///
+ /// "\'";
+ /// '\"';
+ /// "\#";
+ /// "\e";
+ /// `\"`;
+ /// `\"${foo}\"`;
+ /// `\#{foo}`;
+ /// /\!/;
+ /// /\@/;
+ /// /[\[]/;
+ /// /[a-z\-]/;
+ /// ```
+ ///
+ /// Examples of **correct** code for this rule:
+ ///
/// ```javascript
+ /// /*eslint no-useless-escape: "error"*/
+ ///
+ /// "\"";
+ /// '\'';
+ /// "\x12";
+ /// "\u00a9";
+ /// "\371";
+ /// "xs\u2111";
+ /// `\``;
+ /// `\${${foo}}`;
+ /// `$\{${foo}}`;
+ /// /\\/g;
+ /// /\t/g;
+ /// /\w\$\*\^\./;
+ /// /[[]/;
+ /// /[\]]/;
+ /// /[a-z-]/;
/// ```
NoUselessEscape,
correctness,
diff --git a/crates/oxc_linter/src/rules/eslint/no_void.rs b/crates/oxc_linter/src/rules/eslint/no_void.rs
index 0785114107417..931c20891dab7 100644
--- a/crates/oxc_linter/src/rules/eslint/no_void.rs
+++ b/crates/oxc_linter/src/rules/eslint/no_void.rs
@@ -30,9 +30,9 @@ declare_oxc_lint!(
/// var foo = void 0;
///
/// // success
- /// "var foo = bar()",
- /// "foo.void()",
- /// "foo.void = bar",
+ /// "var foo = bar()";
+ /// "foo.void()";
+ /// "foo.void = bar";
/// ```
NoVoid,
restriction,
diff --git a/crates/oxc_linter/src/rules/eslint/unicode_bom.rs b/crates/oxc_linter/src/rules/eslint/unicode_bom.rs
index d0d7596f361bf..a8591fef24750 100644
--- a/crates/oxc_linter/src/rules/eslint/unicode_bom.rs
+++ b/crates/oxc_linter/src/rules/eslint/unicode_bom.rs
@@ -36,7 +36,7 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
- /// var a = 123;"
+ /// var a = 123;
/// ```
UnicodeBom,
restriction,
diff --git a/crates/oxc_linter/src/rules/import/named.rs b/crates/oxc_linter/src/rules/import/named.rs
index 6142745b4497c..9ae1dd0c19134 100644
--- a/crates/oxc_linter/src/rules/import/named.rs
+++ b/crates/oxc_linter/src/rules/import/named.rs
@@ -18,10 +18,55 @@ pub struct Named;
declare_oxc_lint!(
/// ### What it does
///
+ /// Verifies that all named imports are part of the set of named exports in
+ /// the referenced module.
+ ///
+ /// For `export`, verifies that all named exports exist in the referenced
+ /// module.
+ ///
+ /// Note: for packages, the plugin will find exported names from
+ /// `jsnext:main` (deprecated) or `module`, if present in `package.json`.
+ /// Redux's npm module includes this key, and thereby is lintable, for
+ /// example.
+ ///
+ /// A module path that is ignored or not unambiguously an ES module will not
+ /// be reported when imported. Note that type imports and exports, as used
+ /// by Flow, are always ignored.
+ ///
/// ### Why is this bad?
///
/// ### Example
- /// ```javascript
+ /// Given
+ /// ```js
+ /// // ./foo.js
+ /// export const foo = "I'm so foo"
+ /// ```
+ ///
+ /// The following is considered valid:
+ ///
+ /// ```js
+ /// // ./bar.js
+ /// import { foo } from './foo'
+ ///
+ /// // ES7 proposal
+ /// export { foo as bar } from './foo'
+ ///
+ /// // node_modules without jsnext:main are not analyzed by default
+ /// // (import/ignore setting)
+ /// import { SomeNonsenseThatDoesntExist } from 'react'
+ /// ```
+ ///
+ /// ...and the following are reported:
+ ///
+ /// ```js
+ /// // ./baz.js
+ /// import { notFoo } from './foo'
+ ///
+ /// // ES7 proposal
+ /// export { notFoo as defNotBar } from './foo'
+ ///
+ /// // will follow 'jsnext:main', if available
+ /// import { dontCreateStore } from 'redux'
/// ```
Named,
correctness
diff --git a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs
index 7d4427f4900b4..e6e028b883569 100644
--- a/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs
+++ b/crates/oxc_linter/src/rules/jest/no_conditional_expect.rs
@@ -47,8 +47,8 @@ declare_oxc_lint!(
/// });
///
/// it('throws an error', async () => {
- // await foo().catch(error => expect(error).toBeInstanceOf(error));
- // });
+ /// await foo().catch(error => expect(error).toBeInstanceOf(error));
+ /// });
/// ```
///
/// This rule is compatible with [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/no-conditional-expect.md),
diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs
index 893478c7c40d7..a66aac45882fb 100644
--- a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs
+++ b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs
@@ -53,7 +53,7 @@ declare_oxc_lint!(
/// ```javascript
///
/// it('is false', () => {
- /// if this has a modifier (i.e. `not.toBeFalsy`), it would be considered fine
+ /// // if this has a modifier (i.e. `not.toBeFalsy`), it would be considered fine
/// expect(a).toBeFalsy();
/// });
///
@@ -68,6 +68,7 @@ declare_oxc_lint!(
/// expect(uploadFileMock).not.toHaveBeenCalledWith('file.name');
/// });
/// });
+ /// ```
///
NoRestrictedMatchers,
style,
diff --git a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs
index e76130faf9235..b9ea8f912b683 100644
--- a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs
+++ b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs
@@ -34,6 +34,9 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
+ /// test('one', () => {
+ /// return expect(1).toBe(1);
+ /// });
/// ```
NoTestReturnStatement,
style,
diff --git a/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs b/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs
index a80c2b6cc1d91..432baf458d3b2 100644
--- a/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs
+++ b/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs
@@ -57,20 +57,24 @@ declare_oxc_lint!(
///
/// ```javascript
/// // invalid
- /// Above a describe block
+ ///
+ /// // Above a describe block
/// test('my test', () => {});
/// describe('test suite', () => {
/// it('test', () => {});
/// });
+ ///
/// // Below a describe block
/// describe('test suite', () => {});
/// test('my test', () => {});
+ ///
/// // Same for hooks
/// beforeAll('my beforeAll', () => {});
/// describe('test suite', () => {});
/// afterEach('my afterEach', () => {});
///
/// //valid
+ ///
/// // Above a describe block
/// // In a describe block
/// describe('test suite', () => {
@@ -80,9 +84,9 @@ declare_oxc_lint!(
/// // In a nested describe block
/// describe('test suite', () => {
/// test('my test', () => {});
- /// describe('another test suite', () => {
- /// test('my other test', () => {});
- /// });
+ /// describe('another test suite', () => {
+ /// test('my other test', () => {});
+ /// });
/// });
/// ```
///
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
index 27b6544d85d12..6d65d68b917a9 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
@@ -119,12 +119,15 @@ declare_oxc_lint!(
/// text that describes the element's content or purpose.
///
/// ### Example
- /// ```javascript
- /// // Bad
- ///
///
- /// // Good
- ///
+ /// Examples of **incorrect** code for this rule:
+ /// ```jsx
+ ///
+ /// ```
+ ///
+ /// Examples of **correct** code for this rule:
+ /// ```jsx
+ ///
/// ```
AltText,
correctness
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs
index 5818637998967..bad859069fb99 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs
@@ -57,9 +57,11 @@ declare_oxc_lint!(
/// Consider the following:
///
/// ```jsx
- /// Perform action
- /// Perform action
- /// Perform action
+ /// <>
+ /// Perform action
+ /// Perform action
+ /// Perform action
+ /// >
/// ````
///
/// All these anchor implementations indicate that the element is only used to execute JavaScript code. All the above should be replaced with:
@@ -79,19 +81,23 @@ declare_oxc_lint!(
/// #### Valid
///
/// ```jsx
- /// navigate here
- /// navigate here
- /// navigate here
+ /// <>
+ /// navigate here
+ /// navigate here
+ /// navigate here
+ /// >
/// ```
///
/// #### Invalid
///
/// ```jsx
- /// navigate here
- /// navigate here
- /// navigate here
- /// navigate here
- /// navigate here
+ /// <>
+ /// navigate here
+ /// navigate here
+ /// navigate here
+ /// navigate here
+ /// navigate here
+ /// >
/// ```
///
/// ### Reference
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
index 5d7962c3d8f62..0ccaef174e63a 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
@@ -32,25 +32,25 @@ declare_oxc_lint!(
///
/// ### Example
/// ```jsx
- /// // Good
- ///
/// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs index 23833b414cde9..3564d49898bb1 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs @@ -37,7 +37,7 @@ declare_oxc_lint!( /// This rule checks for title property on iframe element. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad /// /// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs index e8ed401761666..9be533c2b9dbd 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs @@ -79,7 +79,7 @@ declare_oxc_lint!( /// `` and the components which you define in options.components with the exception of components which is hidden from screen reader. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad /// /// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs index eeab4ae0caa4e..ead7ecfa5f882 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs @@ -26,7 +26,7 @@ declare_oxc_lint!( /// Inconsistencies between keyboard shortcuts and keyboard commands used by screenreaders and keyboard-only users create accessibility complications so to avoid complications, access keys should not be used. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad ///
/// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs index bc21518695080..df0bd33e0796c 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs @@ -30,7 +30,7 @@ declare_oxc_lint!( /// `aria-hidden="true"` on focusable elements can lead to confusion or unexpected behavior for screen reader users. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad ///
/// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs index 4a8303f840213..25da21bb7afbd 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs @@ -24,12 +24,16 @@ pub struct NoAutofocus { declare_oxc_lint!( /// ### What it does - /// Enforce that autoFocus prop is not used on elements. Autofocusing elements can cause usability issues for sighted and non-sighted users, alike. + /// + /// Enforce that `autoFocus` prop is not used on elements. Autofocusing + /// elements can cause usability issues for sighted and non-sighted users, + /// alike. /// /// ### Rule Option + /// /// This rule takes one optional object argument of type object: /// - /// ``` + /// ```json /// { /// "rules": { /// "jsx-a11y/no-autofocus": [ 2, { @@ -39,24 +43,25 @@ declare_oxc_lint!( /// } /// ``` /// - /// For the `ignoreNonDOM` option, this determines if developer created components are checked. + /// For the `ignoreNonDOM` option, this determines if developer created + /// components are checked. /// /// ### Example - /// // good - /// - /// ```javascript - ///
- /// ``` /// - /// // bad + /// Examples of **incorrect** code for this rule: /// - /// ``` + /// ```jsx ///
///
///
///
/// ``` /// + /// Examples of **correct** code for this rule: + /// + /// ```jsx + ///
+ /// ``` NoAutofocus, correctness, fix diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs index a4542a8391ef8..77670164dbc21 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs @@ -27,13 +27,15 @@ pub struct NoRedundantRoles; declare_oxc_lint!( /// ### What it does - /// Enforces that the explicit role property is not the same as implicit/default role property on element. + /// + /// Enforces that the explicit `role` property is not the same as + /// implicit/default role property on element. /// /// ### Why is this bad? /// Redundant roles can lead to confusion and verbosity in the codebase. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad /// /// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs b/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs index 0e43aae3130b1..e6bee0de19088 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs @@ -32,7 +32,7 @@ declare_oxc_lint!( /// Using semantic HTML tags can improve accessibility and readability of the code. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad ///
/// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/role_has_required_aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/role_has_required_aria_props.rs index d96c715ce8aaf..75730319bfa69 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/role_has_required_aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/role_has_required_aria_props.rs @@ -19,13 +19,17 @@ fn role_has_required_aria_props_diagnostic(span: Span, role: &str, props: &str) pub struct RoleHasRequiredAriaProps; declare_oxc_lint!( /// ### What it does - /// Enforces that elements with ARIA roles must have all required attributes for that role. + /// + /// Enforces that elements with ARIA roles must have all required attributes + /// for that role. /// /// ### Why is this bad? - /// Certain ARIA roles require specific attributes to express necessary semantics for assistive technology. + /// + /// Certain ARIA roles require specific attributes to express necessary + /// semantics for assistive technology. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad ///
/// diff --git a/crates/oxc_linter/src/rules/jsx_a11y/tabindex_no_positive.rs b/crates/oxc_linter/src/rules/jsx_a11y/tabindex_no_positive.rs index 16dc7f9ab2018..6db1afc40c8bb 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/tabindex_no_positive.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/tabindex_no_positive.rs @@ -21,13 +21,19 @@ pub struct TabindexNoPositive; declare_oxc_lint!( /// ### What it does - /// Enforces that positive values for the tabIndex attribute are not used in JSX. + /// + /// Enforces that positive values for the `tabIndex` attribute are not used + /// in JSX. /// /// ### Why is this bad? - /// Using tabIndex values greater than 0 can make navigation and interaction difficult for keyboard and assistive technology users, disrupting the logical order of content. + /// + /// Using `tabIndex` values greater than `0` can make navigation and + /// interaction difficult for keyboard and assistive technology users, + /// disrupting the logical order of content. /// /// ### Example - /// ```javascript + /// + /// ```jsx /// // Bad /// foo /// diff --git a/crates/oxc_linter/src/rules/nextjs/google_font_display.rs b/crates/oxc_linter/src/rules/nextjs/google_font_display.rs index 967c0befb1aa5..1270c7b96728f 100644 --- a/crates/oxc_linter/src/rules/nextjs/google_font_display.rs +++ b/crates/oxc_linter/src/rules/nextjs/google_font_display.rs @@ -30,13 +30,50 @@ pub struct GoogleFontDisplay; declare_oxc_lint!( /// ### What it does /// + /// Enforce font-display behavior with Google Fonts. /// /// ### Why is this bad? /// + /// Specifying display=optional minimizes the risk of invisible text or + /// layout shift. If swapping to the custom font after it has loaded is + /// important to you, then use `display=swap`` instead. /// /// ### Example - /// ```javascript + /// + /// Examples of **incorrect** code for this rule: + /// + /// ```jsx + /// import Head from "next/head"; + /// + /// export default Test = () => { + /// return ( + ///
+ /// + /// + /// ); + /// }; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// + /// ```jsx + /// import Head from "next/head"; + /// + /// export default Test = () => { + /// return ( + ///
+ /// + /// + /// ); + /// }; /// ``` + /// GoogleFontDisplay, correctness ); @@ -153,18 +190,19 @@ fn test() { ]; let fail = vec![ - r#"import Head from "next/head"; - - export default Test = () => { - return ( -
- - - ); - }; + r#" + import Head from "next/head"; + + export default Test = () => { + return ( +
+
+
+ );
+ };
"#,
r#"import Head from "next/head";
diff --git a/crates/oxc_linter/src/rules/oxc/approx_constant.rs b/crates/oxc_linter/src/rules/oxc/approx_constant.rs
index 6480a0901c8b5..913827a3f0ffa 100644
--- a/crates/oxc_linter/src/rules/oxc/approx_constant.rs
+++ b/crates/oxc_linter/src/rules/oxc/approx_constant.rs
@@ -21,14 +21,21 @@ pub struct ApproxConstant;
declare_oxc_lint!(
/// ### What it does
///
- /// Disallows the use of approximate constants, instead preferring the use of the constants in the `Math` object.
+ /// Disallows the use of approximate constants, instead preferring the use
+ /// of the constants in the `Math` object.
///
/// ### Why is this bad?
///
/// Approximate constants are not as accurate as the constants in the `Math` object.
///
/// ### Example
+ ///
/// ```javascript
+ /// // Bad
+ /// let log10e = 0.434294
+ ///
+ /// // Good
+ /// let log10e = Math.LOG10E
/// ```
ApproxConstant,
suspicious
diff --git a/crates/oxc_linter/src/rules/oxc/no_const_enum.rs b/crates/oxc_linter/src/rules/oxc/no_const_enum.rs
index 3cee8b2df9d41..9bf485f24a88e 100644
--- a/crates/oxc_linter/src/rules/oxc/no_const_enum.rs
+++ b/crates/oxc_linter/src/rules/oxc/no_const_enum.rs
@@ -27,11 +27,11 @@ declare_oxc_lint!(
/// Their use can lead to import nonexistent values (because const enums are erased).
///
/// ### Example
- /// ```javascript
+ /// ```ts
/// const enum Color {
- /// Red,
- /// Green,
- /// Blue
+ /// Red,
+ /// Green,
+ /// Blue
/// }
/// ```
NoConstEnum,
diff --git a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs
index b2e7e3f86b16b..edc321c1314cf 100644
--- a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs
+++ b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs
@@ -35,10 +35,10 @@ declare_oxc_lint!(
/// It increase cognitive complexity and may impact performance.
///
/// ### Example
- /// ```javascript
+ /// ```ts
/// // Bad - the argument `b` is only used in recursive calls
/// function f(a: number, b: number): number {
- /// if a == 0 {
+ /// if (a == 0) {
/// return 1
/// } else {
/// return f(a - 1, b + 1)
@@ -47,7 +47,7 @@ declare_oxc_lint!(
///
/// // Good - the argument `b` is omitted
/// function f(a: number): number {
- /// if a == 0 {
+ /// if (a == 0) {
/// return 1
/// } else {
/// return f(a - 1)
diff --git a/crates/oxc_linter/src/rules/promise/avoid_new.rs b/crates/oxc_linter/src/rules/promise/avoid_new.rs
index fd4045749f89e..7f457353e19df 100644
--- a/crates/oxc_linter/src/rules/promise/avoid_new.rs
+++ b/crates/oxc_linter/src/rules/promise/avoid_new.rs
@@ -23,7 +23,7 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
- /// new Promise((resolve, reject) => { ... });
+ /// new Promise((resolve, reject) => { /* ... */ });
/// ```
AvoidNew,
restriction,
diff --git a/crates/oxc_linter/src/rules/promise/param_names.rs b/crates/oxc_linter/src/rules/promise/param_names.rs
index a8ed194541561..ca883b081adff 100644
--- a/crates/oxc_linter/src/rules/promise/param_names.rs
+++ b/crates/oxc_linter/src/rules/promise/param_names.rs
@@ -50,8 +50,8 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
- /// new Promise(function (reject, resolve) { ... }) // incorrect order
- /// new Promise(function (ok, fail) { ... }) // non-standard parameter names
+ /// new Promise(function (reject, resolve) { /* ... */ }) // incorrect order
+ /// new Promise(function (ok, fail) { /* ... */ }) // non-standard parameter names
/// ```
ParamNames,
style,
diff --git a/crates/oxc_linter/src/rules/react/button_has_type.rs b/crates/oxc_linter/src/rules/react/button_has_type.rs
index 5fd224a820d9c..cad562ed7e8c1 100644
--- a/crates/oxc_linter/src/rules/react/button_has_type.rs
+++ b/crates/oxc_linter/src/rules/react/button_has_type.rs
@@ -48,10 +48,12 @@ declare_oxc_lint!(
///
/// ### Why is this bad?
///
- /// The default value of `type` attribute for `button` HTML element is `"submit"` which is often not the desired behavior and may lead to unexpected page reloads.
+ /// The default value of `type` attribute for `button` HTML element is
+ /// `"submit"` which is often not the desired behavior and may lead to
+ /// unexpected page reloads.
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // Bad
///
///
diff --git a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
index 03f7b7607730d..9f4eaec14d89f 100644
--- a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
+++ b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
@@ -37,7 +37,7 @@ declare_oxc_lint!(
/// It also warns when checked and defaultChecked properties are used together.
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // Bad
///
///
diff --git a/crates/oxc_linter/src/rules/react/jsx_boolean_value.rs b/crates/oxc_linter/src/rules/react/jsx_boolean_value.rs
index 49f3d0982c915..00adb59a9c463 100644
--- a/crates/oxc_linter/src/rules/react/jsx_boolean_value.rs
+++ b/crates/oxc_linter/src/rules/react/jsx_boolean_value.rs
@@ -55,7 +55,7 @@ declare_oxc_lint!(
/// Enforce a consistent boolean attribute style in your code.
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// const Hello =
/// var Hello = diff --git a/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs b/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs index e9f171742de6c..4a7f64415ef83 100644 --- a/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs +++ b/crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs @@ -36,7 +36,7 @@ declare_oxc_lint!( /// Fragments are a useful tool when you need to group multiple children without adding a node to the DOM tree. However, sometimes you might end up with a fragment with a single child. When this child is an element, string, or expression, it's not necessary to use a fragment. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad /// <>foo> ///
diff --git a/crates/oxc_linter/src/rules/react/no_children_prop.rs b/crates/oxc_linter/src/rules/react/no_children_prop.rs index d2b89b472b63d..dd57de36a6fd3 100644 --- a/crates/oxc_linter/src/rules/react/no_children_prop.rs +++ b/crates/oxc_linter/src/rules/react/no_children_prop.rs @@ -27,7 +27,7 @@ declare_oxc_lint!( /// When not using JSX, the children should be passed as additional arguments to `React.createElement`. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad ///
/// diff --git a/crates/oxc_linter/src/rules/react/no_danger.rs b/crates/oxc_linter/src/rules/react/no_danger.rs index 2727739128176..bfeccc2c0f1ab 100644 --- a/crates/oxc_linter/src/rules/react/no_danger.rs +++ b/crates/oxc_linter/src/rules/react/no_danger.rs @@ -29,10 +29,26 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// `dangerouslySetInnerHTML` is a way to inject HTML into your React component. This is dangerous because it can easily lead to XSS vulnerabilities. + /// `dangerouslySetInnerHTML` is a way to inject HTML into your React + /// component. This is dangerous because it can easily lead to XSS + /// vulnerabilities. /// /// ### Example - /// ```javascript + /// + /// Examples of **incorrect** code for this rule: + /// + /// ```jsx + /// import React from "react"; + /// + /// const Hello =
; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// + /// ```jsx + /// import React from "react"; + /// + /// const Hello =
;
/// ```
NoDanger,
restriction
diff --git a/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs b/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs
index 360995e09525e..0cca06116d6f1 100644
--- a/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs
+++ b/crates/oxc_linter/src/rules/react/no_direct_mutation_state.rs
@@ -34,7 +34,7 @@ declare_oxc_lint!(
/// calling setState() afterwards may replace the mutation you made
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // error
/// var Hello = createReactClass({
/// componentDidMount: function() {
diff --git a/crates/oxc_linter/src/rules/react/no_find_dom_node.rs b/crates/oxc_linter/src/rules/react/no_find_dom_node.rs
index c81c173abcbf5..ca328c0710fea 100644
--- a/crates/oxc_linter/src/rules/react/no_find_dom_node.rs
+++ b/crates/oxc_linter/src/rules/react/no_find_dom_node.rs
@@ -24,7 +24,7 @@ declare_oxc_lint!(
/// [It has been deprecated in `StrictMode`.](https://legacy.reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage)
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// class MyComponent extends Component {
/// componentDidMount() {
/// findDOMNode(this).scrollIntoView();
diff --git a/crates/oxc_linter/src/rules/react/no_is_mounted.rs b/crates/oxc_linter/src/rules/react/no_is_mounted.rs
index f69eeea35544c..7e2fdd2b7c957 100644
--- a/crates/oxc_linter/src/rules/react/no_is_mounted.rs
+++ b/crates/oxc_linter/src/rules/react/no_is_mounted.rs
@@ -25,7 +25,7 @@ declare_oxc_lint!(
/// and it is on its way to being officially deprecated.///
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// class Hello extends React.Component {
/// someMethod() {
/// if (!this.isMounted()) {
diff --git a/crates/oxc_linter/src/rules/react/no_render_return_value.rs b/crates/oxc_linter/src/rules/react/no_render_return_value.rs
index c27f860c4245a..3cb324983400c 100644
--- a/crates/oxc_linter/src/rules/react/no_render_return_value.rs
+++ b/crates/oxc_linter/src/rules/react/no_render_return_value.rs
@@ -20,7 +20,7 @@ declare_oxc_lint!(
/// This rule will warn you if you try to use the ReactDOM.render() return value.
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // Bad
/// vaa inst =ReactDOM.render(
; diff --git a/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs b/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs index 1ea93d1362d8e..70967d8f77f0a 100644 --- a/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs +++ b/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs @@ -21,10 +21,11 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// When using JSX, `` expands to `React.createElement("a")`. Therefore the `React` variable must be in scope. + /// When using JSX, `` expands to `React.createElement("a")`. Therefore + /// the `React` variable must be in scope. /// /// ### Example - /// ```javascript + /// ```jsx /// // Bad /// var a = ; /// diff --git a/crates/oxc_linter/src/rules/react/require_render_return.rs b/crates/oxc_linter/src/rules/react/require_render_return.rs index 32ba615b19307..1cf13ba358654 100644 --- a/crates/oxc_linter/src/rules/react/require_render_return.rs +++ b/crates/oxc_linter/src/rules/react/require_render_return.rs @@ -31,7 +31,7 @@ declare_oxc_lint!( /// When writing the `render` method in a component it is easy to forget to return the JSX content. This rule will warn if the return statement is missing. /// /// ### Example - /// ```javascript + /// ```jsx /// var Hello = createReactClass({ /// render() { ///
;
diff --git a/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs b/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs
index 6860b588850ae..5c2de109f93bd 100644
--- a/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs
+++ b/crates/oxc_linter/src/rules/react/void_dom_elements_no_children.rs
@@ -29,7 +29,7 @@ declare_oxc_lint!(
/// This rule checks that children are not passed to void DOM elements.
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // Bad
///
Children
///
diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs
index 968a4efc243b9..fe70fe4810f09 100644
--- a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs
+++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs
@@ -13,7 +13,7 @@ declare_oxc_lint!(
/// Prevent JSX that are local to the current method from being used as values of JSX props
///
/// ### Example
- /// ```javascript
+ /// ```jsx
/// // Bad
/// "));
+ }
+ }
+
+ #[test]
+ fn test_table_with_links() {
+ const PREFIX: &str = "/foo/bar";
+ const PREFIX_WITH_SLASH: &str = "/foo/bar/";
+
+ let options = Options::gfm();
+
+ for section in &table().sections {
+ let rendered_table = section.render_markdown_table(Some(PREFIX));
+ assert!(!rendered_table.is_empty());
+ assert_eq!(rendered_table.split("\n").count(), 5 + section.rows.len());
+
+ let html = to_html_with_options(&rendered_table, &options).unwrap();
+ assert!(!html.is_empty());
+ assert!(html.contains("
"));
+ assert!(html.contains(PREFIX_WITH_SLASH));
+ }
+ }
+}
diff --git a/tasks/website/Cargo.toml b/tasks/website/Cargo.toml
index 934333cbf7222..c35428112dd35 100644
--- a/tasks/website/Cargo.toml
+++ b/tasks/website/Cargo.toml
@@ -27,7 +27,14 @@ serde = { workspace = true }
bpaf = { workspace = true, features = ["docgen"] }
[dev-dependencies]
-insta = { workspace = true }
+oxc_allocator = { workspace = true }
+oxc_diagnostics = { workspace = true }
+oxc_parser = { workspace = true }
+oxc_span = { workspace = true }
+
+insta = { workspace = true }
+markdown = { version = "1.0.0-alpha.18" }
+scraper = { version = "0.20.0" }
[package.metadata.cargo-shear]
ignored = ["bpaf"]
diff --git a/tasks/website/src/linter/rules/mod.rs b/tasks/website/src/linter/rules/mod.rs
index 151a658d8c0a8..88cafd8dfd6eb 100644
--- a/tasks/website/src/linter/rules/mod.rs
+++ b/tasks/website/src/linter/rules/mod.rs
@@ -1,6 +1,8 @@
mod doc_page;
mod html;
mod table;
+#[cfg(test)]
+mod test;
use std::{
borrow::Cow,
@@ -95,6 +97,9 @@ fn write_rule_doc_pages(table: &RuleTable, outdir: &Path) {
let plugin_path = outdir.join(&rule.plugin);
fs::create_dir_all(&plugin_path).unwrap();
let page_path = plugin_path.join(format!("{}.md", rule.name));
+ if page_path.exists() {
+ fs::remove_file(&page_path).unwrap();
+ }
println!("{}", page_path.display());
let docs = render_rule_docs_page(rule).unwrap();
fs::write(&page_path, docs).unwrap();
diff --git a/tasks/website/src/linter/rules/test.rs b/tasks/website/src/linter/rules/test.rs
new file mode 100644
index 0000000000000..e074d39b21863
--- /dev/null
+++ b/tasks/website/src/linter/rules/test.rs
@@ -0,0 +1,119 @@
+use markdown::{to_html, to_html_with_options, Options};
+use oxc_diagnostics::NamedSource;
+use scraper::{ElementRef, Html, Selector};
+use std::sync::{Arc, OnceLock};
+
+use oxc_allocator::Allocator;
+use oxc_linter::table::RuleTable;
+use oxc_parser::Parser;
+use oxc_span::SourceType;
+
+use super::{render_rule_docs_page, render_rules_table};
+
+static TABLE: OnceLock