Skip to content

Commit

Permalink
Merge branch 'main' into feat/vitest-expect-expect
Browse files Browse the repository at this point in the history
  • Loading branch information
mysteryven committed Jul 18, 2024
2 parents e1be9d7 + 9df7b56 commit 75f0797
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 13 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ ignored = ["napi", "oxc_transform_napi", "prettyplease"]
# and we don't rely on it for debugging that much.
debug = false

[profile.dev.package]
# Compile macros with some optimizations to make consuming crates build faster
oxc_macros.opt-level = 1
oxc_ast_macros.opt-level = 1

[profile.release.package.oxc_wasm]
opt-level = 'z'

Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ use walk::*;

/// Syntax tree traversal
pub trait Visit<'a>: Sized {
#[inline]
fn enter_node(&mut self, kind: AstKind<'a>) {}
#[inline]
fn leave_node(&mut self, kind: AstKind<'a>) {}

#[inline]
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {}
#[inline]
fn leave_scope(&mut self) {}

#[inline]
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ use walk_mut::*;

/// Syntax tree traversal
pub trait VisitMut<'a>: Sized {
#[inline]
fn enter_node(&mut self, kind: AstType) {}
#[inline]
fn leave_node(&mut self, kind: AstType) {}

#[inline]
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {}
#[inline]
fn leave_scope(&mut self) {}

#[inline]
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod import {
pub mod no_named_as_default_member;
pub mod no_self_import;
// pub mod no_unused_modules;
pub mod no_webpack_loader_syntax;
}

mod eslint {
Expand Down Expand Up @@ -741,6 +742,7 @@ oxc_macros::declare_all_lint_rules! {
// import::no_unused_modules,
import::no_duplicates,
import::no_default_export,
import::no_webpack_loader_syntax,
jsx_a11y::alt_text,
jsx_a11y::anchor_has_content,
jsx_a11y::anchor_is_valid,
Expand Down
118 changes: 118 additions & 0 deletions crates/oxc_linter/src/rules/import/no_webpack_loader_syntax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use oxc_ast::{
ast::{Argument, Expression},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::AstNode;
use oxc_span::Span;

use crate::{context::LintContext, rule::Rule};

fn no_named_as_default_diagnostic(x0: &str, span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"eslint-plugin-import(no-webpack-loader-syntax): Unexpected `!` in `{x0}`."
))
.with_help("Do not use import syntax to configure webpack loaders")
.with_label(span0)
}

#[derive(Debug, Default, Clone)]
pub struct NoWebpackLoaderSyntax;

declare_oxc_lint!(
/// ### What it does
///
/// Forbid Webpack loader syntax in imports.
///
/// ### Why is this bad?
///
/// This loader syntax is non-standard, so it couples the code to Webpack. The recommended way to
/// specify Webpack loader configuration is in a [Webpack configuration file](https://webpack.js.org/concepts/loaders/#configuration).
///
/// ### Example
/// ```javascript
/// import myModule from 'my-loader!my-module';
/// import theme from 'style!css!./theme.css';
///
/// var myModule = require('my-loader!./my-module');
/// var theme = require('style!css!./theme.css');
/// ```
NoWebpackLoaderSyntax,
restriction,
);

impl Rule for NoWebpackLoaderSyntax {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
// not in top level
if node.scope_id() != ctx.scopes().root_scope_id() {
return;
}

match node.kind() {
AstKind::CallExpression(call_expr) => {
if let Expression::Identifier(identifier) = &call_expr.callee {
if identifier.name != "require" {
return;
}

if call_expr.arguments.len() != 1 {
return;
}

let Argument::StringLiteral(ident) = &call_expr.arguments[0] else {
return;
};

if ident.value.contains('!') {
ctx.diagnostic(no_named_as_default_diagnostic(
ident.value.as_str(),
ident.span,
));
}
}
}
AstKind::ImportDeclaration(import_decl) => {
if import_decl.source.value.contains('!') {
ctx.diagnostic(no_named_as_default_diagnostic(
&import_decl.source.value,
import_decl.source.span,
));
}
}
_ => {}
}
}
}

#[test]
fn test() {
use crate::tester::Tester;

let pass = vec![
"import _ from 'lodash'",
"import find from 'lodash.find'",
"import foo from './foo.css'",
"import data from '@scope/my-package/data.json'",
"var _ = require('lodash')",
"var find = require('lodash.find')",
"var foo = require('./foo')",
"var foo = require('../foo')",
"var foo = require('foo')",
"var foo = require('./')",
"var foo = require('@scope/foo')",
];

let fail = vec![
"import _ from 'babel!lodash'",
"import find from '-babel-loader!lodash.find'",
"import foo from 'style!css!./foo.css'",
"import data from 'json!@scope/my-package/data.json'",
"var _ = require('babel!lodash')",
"var find = require('-babel-loader!lodash.find')",
"var foo = require('style!css!./foo.css')",
"var data = require('json!@scope/my-package/data.json')",
];

Tester::new(NoWebpackLoaderSyntax::NAME, pass, fail).test_and_snapshot();
}
72 changes: 63 additions & 9 deletions crates/oxc_linter/src/rules/jest/no_alias_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode},
utils::{
collect_possible_jest_call_node, get_test_plugin_name, parse_expect_jest_fn_call,
PossibleJestNode, TestPluginName,
},
};

fn no_alias_methods_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("eslint-plugin-jest(no-alias-methods): Unexpected alias {x0:?}"))
.with_help(format!("Replace {x0:?} with its canonical name of {x1:?}"))
.with_label(span2)
fn no_alias_methods_diagnostic(
x0: TestPluginName,
x1: &str,
x2: &str,
span3: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("{x0}(no-alias-methods): Unexpected alias {x1:?}"))
.with_help(format!("Replace {x1:?} with its canonical name of {x2:?}"))
.with_label(span3)
}

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -42,6 +50,17 @@ declare_oxc_lint!(
/// expect(a).nthReturnedWith();
/// expect(a).toThrowError();
/// ```
///
/// This rule is compatible with [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/no-alias-methods.md),
/// to use it, add the following configuration to your `.eslintrc.json`:
///
/// ```json
/// {
/// "rules": {
/// "vitest/no-alias-methods": "error"
/// }
/// }
/// ```
NoAliasMethods,
style
);
Expand Down Expand Up @@ -77,8 +96,10 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
span.end -= 1;
}

let plugin_name = get_test_plugin_name(ctx);

ctx.diagnostic_with_fix(
no_alias_methods_diagnostic(name, canonical_name, matcher.span),
no_alias_methods_diagnostic(plugin_name, name, canonical_name, matcher.span),
// || Fix::new(canonical_name, Span::new(start, end)),
|fixer| fixer.replace(span, canonical_name),
);
Expand Down Expand Up @@ -140,7 +161,7 @@ impl BadAliasMethodName {
fn test() {
use crate::tester::Tester;

let pass = vec![
let mut pass = vec![
("expect(a).toHaveBeenCalled()", None),
("expect(a).toHaveBeenCalledTimes()", None),
("expect(a).toHaveBeenCalledWith()", None),
Expand All @@ -156,7 +177,7 @@ fn test() {
("expect(a);", None),
];

let fail = vec![
let mut fail = vec![
("expect(a).toBeCalled()", None),
("expect(a).toBeCalledTimes()", None),
("expect(a).toBeCalledWith()", None),
Expand All @@ -174,14 +195,47 @@ fn test() {
("expect(a).not['toThrowError']()", None),
];

let fix = vec![
let mut fix = vec![
("expect(a).toBeCalled()", "expect(a).toHaveBeenCalled()", None),
("expect(a).not['toThrowError']()", "expect(a).not['toThrow']()", None),
("expect(a).not[`toThrowError`]()", "expect(a).not[`toThrow`]()", None),
];

let pass_vitest = vec![
"expect(a).toHaveBeenCalled()",
"expect(a).toHaveBeenCalledTimes()",
"expect(a).toHaveBeenCalledWith()",
"expect(a).toHaveBeenLastCalledWith()",
"expect(a).toHaveBeenNthCalledWith()",
"expect(a).toHaveReturned()",
"expect(a).toHaveReturnedTimes()",
"expect(a).toHaveReturnedWith()",
"expect(a).toHaveLastReturnedWith()",
"expect(a).toHaveNthReturnedWith()",
"expect(a).toThrow()",
"expect(a).rejects;",
"expect(a);",
];

let fail_vitest = vec![
"expect(a).toBeCalled()",
"expect(a).toBeCalledTimes()",
r#"expect(a).not["toThrowError"]()"#,
];

let fix_vitest = vec![
("expect(a).toBeCalled()", "expect(a).toHaveBeenCalled()", None),
("expect(a).toBeCalledTimes()", "expect(a).toHaveBeenCalledTimes()", None),
("expect(a).not['toThrowError']()", "expect(a).not['toThrow']()", None),
];

pass.extend(pass_vitest.into_iter().map(|x| (x, None)));
fail.extend(fail_vitest.into_iter().map(|x| (x, None)));
fix.extend(fix_vitest);

Tester::new(NoAliasMethods::NAME, pass, fail)
.with_jest_plugin(true)
.with_vitest_plugin(true)
.expect_fix(fix)
.test_and_snapshot();
}
20 changes: 17 additions & 3 deletions crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,18 @@ impl Rule for NoAutofocus {
if self.ignore_non_dom {
if HTML_TAG.contains(&element_type) {
if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus {
ctx.diagnostic(no_autofocus_diagnostic(attr.span));
ctx.diagnostic_with_fix(no_autofocus_diagnostic(attr.span), |fixer| {
fixer.delete(&attr.span)
});
}
}
return;
}

if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus {
ctx.diagnostic(no_autofocus_diagnostic(attr.span));
ctx.diagnostic_with_fix(no_autofocus_diagnostic(attr.span), |fixer| {
fixer.delete(&attr.span)
});
}
}
}
Expand Down Expand Up @@ -154,5 +158,15 @@ fn test() {
("<Button autoFocus />", Some(config()), Some(settings()), None),
];

Tester::new(NoAutofocus::NAME, pass, fail).test_and_snapshot();
let fix = vec![
("<div autoFocus />", "<div />", None),
("<div autoFocus={true} />", "<div />", None),
("<div autoFocus='true' />", "<div />", None),
("<Button autoFocus='true' />", "<Button />", None),
("<input autoFocus />", "<input />", None),
("<div autoFocus>foo</div>", "<div >foo</div>", None),
("<div autoFocus id='lol'>foo</div>", "<div id='lol'>foo</div>", None),
];

Tester::new(NoAutofocus::NAME, pass, fail).expect_fix(fix).test_and_snapshot();
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,13 @@ fn test() {
("for (const [{innerText}] of elements);", None),
];

Tester::new(PreferDomNodeTextContent::NAME, pass, fail).test_and_snapshot();
// TODO: implement a fixer for destructuring assignment cases
let fix = vec![
("node.innerText;", "node.textContent;"),
("node?.innerText;", "node?.textContent;"),
("node.innerText = 'foo';", "node.textContent = 'foo';"),
("innerText.innerText = 'foo';", "innerText.textContent = 'foo';"),
];

Tester::new(PreferDomNodeTextContent::NAME, pass, fail).expect_fix(fix).test_and_snapshot();
}
22 changes: 22 additions & 0 deletions crates/oxc_linter/src/snapshots/no_alias_methods.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 216
---
eslint-plugin-jest(no-alias-methods): Unexpected alias "toBeCalled"
╭─[no_alias_methods.tsx:1:11]
Expand Down Expand Up @@ -105,3 +106,24 @@ source: crates/oxc_linter/src/tester.rs
· ──────────────
╰────
help: Replace "toThrowError" with its canonical name of "toThrow"

eslint-plugin-jest(no-alias-methods): Unexpected alias "toBeCalled"
╭─[no_alias_methods.tsx:1:11]
1expect(a).toBeCalled()
· ──────────
╰────
help: Replace "toBeCalled" with its canonical name of "toHaveBeenCalled"

eslint-plugin-jest(no-alias-methods): Unexpected alias "toBeCalledTimes"
╭─[no_alias_methods.tsx:1:11]
1expect(a).toBeCalledTimes()
· ───────────────
╰────
help: Replace "toBeCalledTimes" with its canonical name of "toHaveBeenCalledTimes"

eslint-plugin-jest(no-alias-methods): Unexpected alias "toThrowError"
╭─[no_alias_methods.tsx:1:15]
1expect(a).not["toThrowError"]()
· ──────────────
╰────
help: Replace "toThrowError" with its canonical name of "toThrow"
Loading

0 comments on commit 75f0797

Please sign in to comment.