Skip to content

Commit

Permalink
test(linter): ensure rule docs have valid syntax (#4644)
Browse files Browse the repository at this point in the history
Adds tests for rule documentation by
1. Compiling doc markdown into HTML, which ensures docs use valid markdown syntax
2. Converts the generated HTML into JSX and parses the results with the parser, ensuring the generated HTML is valid

Has the added benefit of adding a lot of JSX test cases to the parser. I've also fixed all violations for these tests in this PR.
  • Loading branch information
DonIsaac committed Aug 10, 2024
1 parent b3c3125 commit 8f2a566
Show file tree
Hide file tree
Showing 112 changed files with 1,192 additions and 342 deletions.
342 changes: 333 additions & 9 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
12 changes: 11 additions & 1 deletion crates/oxc_linter/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/for_direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/guard_for_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ declare_oxc_lint!(
/// ### Example
/// ```javascript
/// for (key in foo) {
// doSomething(key);
// }
/// doSomething(key);
/// }
/// ```
GuardForIn,
style
Expand Down
13 changes: 6 additions & 7 deletions crates/oxc_linter/src/rules/eslint/max_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down
12 changes: 8 additions & 4 deletions crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?
///
Expand All @@ -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
Expand Down
26 changes: 13 additions & 13 deletions crates/oxc_linter/src/rules/eslint/no_case_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion crates/oxc_linter/src/rules/eslint/no_cond_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions crates/oxc_linter/src/rules/eslint/no_continue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 6 additions & 3 deletions crates/oxc_linter/src/rules/eslint/no_debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_linter/src/rules/eslint/no_ex_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
// try {
// // code
// } catch (e) {
// e = 10;
// }
/// try {
/// // code
/// } catch (e) {
/// e = 10;
/// }
/// ```
NoExAssign,
correctness
Expand Down
7 changes: 3 additions & 4 deletions crates/oxc_linter/src/rules/eslint/no_undefined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ declare_oxc_lint!(
/// var undefined = "foo";
///
/// if (foo === undefined) {
/// ...
/// // ...
/// }
///
/// function baz(undefined) {
/// ...
/// // ...
/// }
///
/// bar(undefined, "lorem");
///
/// ```
///
/// ### Example of good code
Expand All @@ -47,7 +46,7 @@ declare_oxc_lint!(
/// var Undefined = "foo";
///
/// if (typeof foo === "undefined") {
/// ...
/// // ...
/// }
///
/// global.undefined = "foo";
Expand Down
38 changes: 38 additions & 0 deletions crates/oxc_linter/src/rules/eslint/no_useless_escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_linter/src/rules/eslint/no_void.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/unicode_bom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ declare_oxc_lint!(
///
/// ### Example
/// ```javascript
/// var a = 123;"
/// var a = 123;
/// ```
UnicodeBom,
restriction,
Expand Down
47 changes: 46 additions & 1 deletion crates/oxc_linter/src/rules/import/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/jest/no_conditional_expect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Loading

0 comments on commit 8f2a566

Please sign in to comment.