Skip to content

Commit 90fe51f

Browse files
committed
fix(linter): more invalid docs
1 parent 48031ad commit 90fe51f

16 files changed

+362
-57
lines changed

crates/oxc_linter/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub use crate::{
3131
fixer::FixKind,
3232
frameworks::FrameworkFlags,
3333
options::{AllowWarnDeny, LintOptions},
34-
rule::{RuleCategory, RuleMeta, RuleWithSeverity},
34+
rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity},
3535
service::{LintService, LintServiceOptions},
3636
};
3737
use crate::{
@@ -146,7 +146,7 @@ impl Linter {
146146
pub fn print_rules<W: Write>(writer: &mut W) {
147147
let table = RuleTable::new();
148148
for section in table.sections {
149-
writeln!(writer, "{}", section.render_markdown_table()).unwrap();
149+
writeln!(writer, "{}", section.render_markdown_table(None)).unwrap();
150150
}
151151
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
152152
writeln!(writer, "Total: {}", table.total).unwrap();

crates/oxc_linter/src/rule.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl fmt::Display for RuleCategory {
117117

118118
// NOTE: this could be packed into a single byte if we wanted. I don't think
119119
// this is needed, but we could do it if it would have a performance impact.
120-
/// Describes the auto-fixing capabilities of a [`Rule`].
120+
/// Describes the auto-fixing capabilities of a `Rule`.
121121
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
122122
pub enum RuleFixMeta {
123123
/// An auto-fix is not available.
@@ -132,14 +132,24 @@ pub enum RuleFixMeta {
132132
}
133133

134134
impl RuleFixMeta {
135-
/// Does this [`Rule`] have some kind of auto-fix available?
135+
#[inline]
136+
pub fn is_none(self) -> bool {
137+
matches!(self, Self::None)
138+
}
139+
140+
/// Does this `Rule` have some kind of auto-fix available?
136141
///
137142
/// Also returns `true` for suggestions.
138143
#[inline]
139144
pub fn has_fix(self) -> bool {
140145
matches!(self, Self::Fixable(_) | Self::Conditional(_))
141146
}
142147

148+
#[inline]
149+
pub fn is_pending(self) -> bool {
150+
matches!(self, Self::FixPending)
151+
}
152+
143153
pub fn supports_fix(self, kind: FixKind) -> bool {
144154
matches!(self, Self::Fixable(fix_kind) | Self::Conditional(fix_kind) if fix_kind.can_apply(kind))
145155
}
@@ -163,9 +173,10 @@ impl RuleFixMeta {
163173
let mut message =
164174
if kind.is_dangerous() { format!("dangerous {noun}") } else { noun.into() };
165175

166-
let article = match message.chars().next().unwrap() {
167-
'a' | 'e' | 'i' | 'o' | 'u' => "An",
168-
_ => "A",
176+
let article = match message.chars().next() {
177+
Some('a' | 'e' | 'i' | 'o' | 'u') => "An",
178+
Some(_) => "A",
179+
None => unreachable!(),
169180
};
170181

171182
if matches!(self, Self::Conditional(_)) {

crates/oxc_linter/src/rules/import/namespace.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ pub struct Namespace {
4040

4141
declare_oxc_lint!(
4242
/// ### What it does
43-
/// Enforces names exist at the time they are dereferenced, when imported as a full namespace (i.e. import * as foo from './foo'; foo.bar(); will report if bar is not exported by ./foo.).
44-
/// Will report at the import declaration if there are no exported names found.
45-
/// Also, will report for computed references (i.e. foo["bar"]()).
46-
/// Reports on assignment to a member of an imported namespace.
43+
/// Enforces names exist at the time they are dereferenced, when imported as
44+
/// a full namespace (i.e. `import * as foo from './foo'; foo.bar();` will
45+
/// report if bar is not exported by `./foo.`). Will report at the import
46+
/// declaration if there are no exported names found. Also, will report for
47+
/// computed references (i.e. `foo["bar"]()`). Reports on assignment to a
48+
/// member of an imported namespace.
4749
Namespace,
4850
correctness
4951
);

crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs

+4-18
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ declare_oxc_lint!(
5656
///
5757
/// Consider the following:
5858
///
59-
/// ```javascript
59+
/// ```jsx
6060
/// <a href="javascript:void(0)" onClick={foo}>Perform action</a>
6161
/// <a href="#" onClick={foo}>Perform action</a>
6262
/// <a onClick={foo}>Perform action</a>
6363
/// ````
6464
///
6565
/// All these anchor implementations indicate that the element is only used to execute JavaScript code. All the above should be replaced with:
6666
///
67-
/// ```javascript
67+
/// ```jsx
6868
/// <button onClick={foo}>Perform action</button>
6969
/// ```
7070
/// `
@@ -78,33 +78,19 @@ declare_oxc_lint!(
7878
///
7979
/// #### Valid
8080
///
81-
/// ```javascript
81+
/// ```jsx
8282
/// <a href={`https://www.javascript.com`}>navigate here</a>
83-
/// ```
84-
///
85-
/// ```javascript
8683
/// <a href={somewhere}>navigate here</a>
87-
/// ```
88-
///
89-
/// ```javascript
9084
/// <a {...spread}>navigate here</a>
9185
/// ```
9286
///
9387
/// #### Invalid
9488
///
95-
/// ```javascript
89+
/// ```jsx
9690
/// <a href={null}>navigate here</a>
97-
/// ```
98-
/// ```javascript
9991
/// <a href={undefined}>navigate here</a>
100-
/// ```
101-
/// ```javascript
10292
/// <a href>navigate here</a>
103-
/// ```
104-
/// ```javascript
10593
/// <a href="javascript:void(0)">navigate here</a>
106-
/// ```
107-
/// ```javascript
10894
/// <a href="https://example.com" onClick={something}>navigate here</a>
10995
/// ```
11096
///

crates/oxc_linter/src/rules/jsx_a11y/lang.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct Lang;
2626
declare_oxc_lint!(
2727
/// ### What it does
2828
///
29-
/// The lang prop on the <html> element must be a valid IETF's BCP 47 language tag.
29+
/// The lang prop on the `<html>` element must be a valid IETF's BCP 47 language tag.
3030
///
3131
/// ### Why is this bad?
3232
///
@@ -39,13 +39,13 @@ declare_oxc_lint!(
3939
/// ### Example
4040
///
4141
/// // good
42-
/// ```javascript
42+
/// ```jsx
4343
/// <html lang="en">
4444
/// <html lang="en-US">
4545
/// ```
4646
///
4747
/// // bad
48-
/// ```javascript
48+
/// ```jsx
4949
/// <html>
5050
/// <html lang="foo">
5151
/// ````

crates/oxc_linter/src/rules/jsx_a11y/no_distracting_elements.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ declare_oxc_lint!(
2222
///
2323
/// ### Why is this necessary?
2424
///
25-
/// Elements that can be visually distracting can cause accessibility issues with visually impaired users.
26-
/// Such elements are most likely deprecated, and should be avoided. By default, <marquee> and <blink> elements are visually distracting.
25+
/// Elements that can be visually distracting can cause accessibility issues
26+
/// with visually impaired users. Such elements are most likely deprecated,
27+
/// and should be avoided. By default, `<marquee>` and `<blink>` elements
28+
/// are visually distracting.
2729
///
2830
/// ### What it checks
2931
///
3032
/// This rule checks for marquee and blink element.
3133
///
3234
/// ### Example
33-
/// ```javascript
35+
/// ```jsx
3436
/// // Bad
3537
/// <marquee />
3638
/// <marquee {...props} />

crates/oxc_linter/src/rules/jsx_a11y/scope.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ pub struct Scope;
2323
declare_oxc_lint!(
2424
/// ### What it does
2525
///
26-
/// The scope prop should be used only on <th> elements.
26+
/// The scope prop should be used only on `<th>` elements.
2727
///
2828
/// ### Why is this bad?
2929
/// The scope attribute makes table navigation much easier for screen reader users, provided that it is used correctly.
3030
/// Incorrectly used, scope can make table navigation much harder and less efficient.
3131
/// A screen reader operates under the assumption that a table has a header and that this header specifies a scope. Because of the way screen readers function, having an accurate header makes viewing a table far more accessible and more efficient for people who use the device.
3232
///
3333
/// ### Example
34-
/// ```javascript
34+
/// ```jsx
3535
/// // Bad
3636
/// <div scope />
3737
///

crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ pub struct NoDuplicateHead;
1010

1111
declare_oxc_lint!(
1212
/// ### What it does
13-
/// Prevent duplicate usage of <Head> in pages/_document.js.
13+
/// Prevent duplicate usage of `<Head>` in `pages/_document.js``.
1414
///
1515
/// ### Why is this bad?
1616
/// This can cause unexpected behavior in your application.
1717
///
1818
/// ### Example
19-
/// ```javascript
19+
/// ```jsx
2020
/// import Document, { Html, Head, Main, NextScript } from 'next/document'
2121
/// class MyDocument extends Document {
2222
/// static async getInitialProps(ctx) {

crates/oxc_linter/src/rules/typescript/no_this_alias.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ declare_oxc_lint!(
5959
///
6060
/// ### Why is this bad?
6161
///
62-
/// Generic type parameters (<T>) in TypeScript may be "constrained" with an extends keyword.
63-
/// When no extends is provided, type parameters default a constraint to unknown. It is therefore redundant to extend from any or unknown.
62+
/// Generic type parameters (`<T>`) in TypeScript may be "constrained" with
63+
/// an extends keyword. When no extends is provided, type parameters
64+
/// default a constraint to unknown. It is therefore redundant to extend
65+
/// from any or unknown.
6466
///
65-
/// the rule doesn't allow const {allowedName} = this
67+
/// the rule doesn't allow `const {allowedName} = this`
6668
/// this is to keep 1:1 with eslint implementation
67-
/// sampe with obj.<allowedName> = this
69+
/// sampe with `obj.<allowedName> = this`
6870
/// ```
6971
NoThisAlias,
7072
correctness

crates/oxc_linter/src/rules/typescript/no_unnecessary_type_constraint.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ declare_oxc_lint!(
2828
///
2929
/// ### Why is this bad?
3030
///
31-
/// Generic type parameters (<T>) in TypeScript may be "constrained" with an extends keyword.
31+
/// Generic type parameters (`<T>`) in TypeScript may be "constrained" with an extends keyword.
3232
/// When no extends is provided, type parameters default a constraint to unknown. It is therefore redundant to extend from any or unknown.
3333
///
3434
/// ### Example
35-
/// ```javascript
35+
/// ```typescript
3636
/// interface FooAny<T extends any> {}
3737
/// interface FooUnknown<T extends unknown> {}
3838
/// type BarAny<T extends any> = {};

crates/oxc_linter/src/table.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use std::fmt::Write;
1+
use std::{borrow::Cow, fmt::Write};
22

33
use rustc_hash::{FxHashMap, FxHashSet};
44

5-
use crate::{rules::RULES, Linter, RuleCategory};
5+
use crate::{rules::RULES, Linter, RuleCategory, RuleFixMeta};
66

77
pub struct RuleTable {
88
pub sections: Vec<RuleTableSection>,
@@ -23,6 +23,7 @@ pub struct RuleTableRow {
2323
pub category: RuleCategory,
2424
pub documentation: Option<&'static str>,
2525
pub turned_on_by_default: bool,
26+
pub autofix: RuleFixMeta,
2627
}
2728

2829
impl Default for RuleTable {
@@ -49,6 +50,7 @@ impl RuleTable {
4950
plugin: rule.plugin_name().to_string(),
5051
category: rule.category(),
5152
turned_on_by_default: default_rules.contains(name),
53+
autofix: rule.fix(),
5254
}
5355
})
5456
.collect::<Vec<_>>();
@@ -88,7 +90,11 @@ impl RuleTable {
8890
}
8991

9092
impl RuleTableSection {
91-
pub fn render_markdown_table(&self) -> String {
93+
/// Renders all the rules in this section as a markdown table.
94+
///
95+
/// Provide [`Some`] prefix to render the rule name as a link. Provide
96+
/// [`None`] to just display the rule name as text.
97+
pub fn render_markdown_table(&self, link_prefix: Option<&str>) -> String {
9298
let mut s = String::new();
9399
let category = &self.category;
94100
let rows = &self.rows;
@@ -108,7 +114,12 @@ impl RuleTableSection {
108114
let plugin_name = &row.plugin;
109115
let (default, default_width) =
110116
if row.turned_on_by_default { ("✅", 6) } else { ("", 7) };
111-
writeln!(s, "| {rule_name:<rule_width$} | {plugin_name:<plugin_width$} | {default:<default_width$} |").unwrap();
117+
let rendered_name = if let Some(prefix) = link_prefix {
118+
Cow::Owned(format!("[{rule_name}]({prefix}/{plugin_name}/{rule_name}.html)"))
119+
} else {
120+
Cow::Borrowed(rule_name)
121+
};
122+
writeln!(s, "| {rendered_name:<rule_width$} | {plugin_name:<plugin_width$} | {default:<default_width$} |").unwrap();
112123
}
113124

114125
s
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! Create documentation pages for each rule. Pages are printed as Markdown and
2+
//! get added to the website.
3+
4+
use oxc_linter::{table::RuleTableRow, RuleFixMeta};
5+
use std::fmt::{self, Write};
6+
7+
use crate::linter::rules::html::HtmlWriter;
8+
9+
pub fn render_rule_docs_page(rule: &RuleTableRow) -> Result<String, fmt::Error> {
10+
const APPROX_FIX_CATEGORY_AND_PLUGIN_LEN: usize = 512;
11+
let RuleTableRow { name, documentation, plugin, turned_on_by_default, autofix, .. } = rule;
12+
13+
let mut page = HtmlWriter::with_capacity(
14+
documentation.map_or(0, str::len) + name.len() + APPROX_FIX_CATEGORY_AND_PLUGIN_LEN,
15+
);
16+
17+
writeln!(
18+
page,
19+
"<!-- This file is auto-generated by {}. Do not edit it manually. -->\n",
20+
file!()
21+
)?;
22+
writeln!(page, "# {plugin}/{name}\n")?;
23+
24+
// rule metadata
25+
page.div(r#"class="rule-meta""#, |p| {
26+
if *turned_on_by_default {
27+
p.span(r#"class="default-on""#, |p| {
28+
p.writeln("✅ This rule is turned on by default.")
29+
})?;
30+
}
31+
32+
if let Some(emoji) = fix_emoji(*autofix) {
33+
p.span(r#"class="fix""#, |p| {
34+
p.writeln(format!("{} {}", emoji, autofix.description()))
35+
})?;
36+
}
37+
38+
Ok(())
39+
})?;
40+
41+
// rule documentation
42+
if let Some(docs) = documentation {
43+
writeln!(page, "\n{}", *docs)?;
44+
}
45+
46+
// TODO: link to rule source
47+
48+
Ok(page.into())
49+
}
50+
51+
fn fix_emoji(fix: RuleFixMeta) -> Option<&'static str> {
52+
match fix {
53+
RuleFixMeta::None => None,
54+
RuleFixMeta::FixPending => Some("🚧"),
55+
RuleFixMeta::Conditional(_) | RuleFixMeta::Fixable(_) => Some("🛠️"),
56+
}
57+
}

0 commit comments

Comments
 (0)