-
-
Notifications
You must be signed in to change notification settings - Fork 540
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5fa96f0
commit dc4ae2c
Showing
13 changed files
with
471 additions
and
92 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
191 changes: 105 additions & 86 deletions
191
crates/biome_configuration/src/analyzer/linter/rules.rs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
crates/biome_js_analyze/src/lint/nursery/no_react_deps.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
use biome_analyze::{ | ||
context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, | ||
RuleSourceKind, | ||
}; | ||
use biome_console::markup; | ||
use biome_js_syntax::{AnyJsExpression, JsCallExpression}; | ||
use biome_rowan::{AstNode, AstSeparatedList, TextRange}; | ||
|
||
declare_lint_rule! { | ||
/// Disallow usage of dependency arrays in `createEffect` and `createMemo`. | ||
/// | ||
/// In Solid, `createEffect` and `createMemo` track dependencies automatically, it's no need to add dependency arrays. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect(() => { | ||
/// console.log(signal()); | ||
/// }, [signal()]); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect(() => { | ||
/// console.log(signal()); | ||
/// }, [signal]); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createEffect } from "solid-js"; | ||
/// const deps = [signal]; | ||
/// createEffect(() => { | ||
/// console.log(signal()); | ||
/// }, deps) | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createMemo } from "solid-js"; | ||
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a(), b()]); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createMemo } from "solid-js"; | ||
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b]); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createMemo } from "solid-js"; | ||
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b()]); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createMemo } from "solid-js"; | ||
/// const deps = [a, b]; | ||
/// const value = createMemo(() => computeExpensiveValue(a(), b()), deps); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// import { createMemo } from "solid-js"; | ||
/// const deps = [a, b]; | ||
/// const memoFn = () => computeExpensiveValue(a(), b()); | ||
/// const value = createMemo(memoFn, deps); | ||
/// ``` | ||
/// | ||
/// ### Valid | ||
/// | ||
/// ```js | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect(() => { | ||
/// console.log(signal()); | ||
/// }); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect((prev) => { | ||
/// console.log(signal()); | ||
/// return prev + 1; | ||
/// }, 0); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect((prev) => { | ||
/// console.log(signal()); | ||
/// return (prev || 0) + 1; | ||
/// }); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createEffect } from "solid-js"; | ||
/// createEffect((prev) => { | ||
/// console.log(signal()); | ||
/// return prev ? prev + 1 : 1; | ||
/// }, undefined); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createMemo } from "solid-js"; | ||
/// const value = createMemo(() => computeExpensiveValue(a(), b())); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createMemo } from "solid-js"; | ||
/// const sum = createMemo((prev) => input() + prev, 0); | ||
/// ``` | ||
/// | ||
/// ```js | ||
/// import { createEffect } from "solid-js"; | ||
/// const args = [ | ||
/// () => { | ||
/// console.log(signal()); | ||
/// }, | ||
/// [signal()], | ||
/// ]; | ||
/// createEffect(...args); | ||
/// ``` | ||
pub NoReactDeps { | ||
version: "next", | ||
name: "noReactDeps", | ||
language: "js", | ||
domains: &[RuleDomain::Solid], | ||
recommended: false, | ||
sources: &[RuleSource::EslintSolid("no-react-deps")], | ||
source_kind: RuleSourceKind::Inspired, | ||
} | ||
} | ||
|
||
impl Rule for NoReactDeps { | ||
type Query = Ast<JsCallExpression>; | ||
type State = (String, TextRange); | ||
type Signals = Option<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
let callee = node.callee().ok()?; | ||
let ident = callee.as_js_identifier_expression()?.name().ok()?; | ||
let callee_name = ident.value_token().ok()?; | ||
let callee_name = callee_name.text_trimmed(); | ||
|
||
if callee_name != "createEffect" && callee_name != "createMemo" { | ||
return None; | ||
} | ||
|
||
let arguments = node.arguments().ok()?.args(); | ||
let len = arguments.len(); | ||
let mut iter = arguments.into_iter(); | ||
|
||
let has_spread = iter.all(|arg| arg.is_ok_and(|arg| arg.as_js_spread().is_some())); | ||
|
||
if len == 2 && !has_spread { | ||
let first_argument = iter.next()?.ok()?; | ||
let first_argument = first_argument.as_any_js_expression()?; | ||
|
||
let is_first_arg_function_type = | ||
first_argument.as_js_arrow_function_expression().is_some() | ||
|| first_argument.as_js_function_expression().is_some(); | ||
|
||
let first_arg_parameter_len = match first_argument { | ||
AnyJsExpression::JsArrowFunctionExpression(node) => node.parameters().ok()?.len(), | ||
AnyJsExpression::JsFunctionExpression(node) => { | ||
node.parameters().ok()?.items().len() | ||
} | ||
_ => 0, | ||
}; | ||
|
||
let second_argument = iter.next()?.ok()?; | ||
let second_argument = second_argument.as_any_js_expression()?; | ||
let is_second_arg_array_type = second_argument.as_js_array_expression().is_some(); | ||
|
||
if is_first_arg_function_type | ||
&& first_arg_parameter_len == 0 | ||
&& is_second_arg_array_type | ||
{ | ||
return Some((callee_name.into(), second_argument.range())); | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
fn diagnostic(_ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
let (callee_name, range) = state; | ||
Some( | ||
RuleDiagnostic::new( | ||
rule_category!(), | ||
range, | ||
markup! { | ||
"In Solid, "<Emphasis>{callee_name}</Emphasis>" doesn't accept a dependency array because it automatically tracks its dependencies." | ||
}, | ||
) | ||
.note(markup! { | ||
"Please just remove the dependency array parameter here." | ||
}) | ||
.note(markup! { | ||
"If you really need to override the list of dependencies, use \ | ||
"<Hyperlink href="https://docs.solidjs.com/reference/reactive-utilities/on-util#on">"on"</Hyperlink>"." | ||
}), | ||
) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
27 changes: 27 additions & 0 deletions
27
crates/biome_js_analyze/tests/specs/nursery/noReactDeps/invalid.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { createEffect, createMemo } from "solid-js"; | ||
|
||
createEffect(() => { | ||
console.log(signal()); | ||
}, [signal()]); | ||
|
||
createEffect(() => { | ||
console.log(signal()); | ||
}, [signal]); | ||
|
||
const deps = [signal]; | ||
createEffect(() => { | ||
console.log(signal()); | ||
}, deps); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a(), b()]); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b]); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b()]); | ||
|
||
const deps = [a, b]; | ||
const value = createMemo(() => computeExpensiveValue(a(), b()), deps); | ||
|
||
const deps = [a, b]; | ||
const memoFn = () => computeExpensiveValue(a(), b()); | ||
const value = createMemo(memoFn, deps); |
35 changes: 35 additions & 0 deletions
35
crates/biome_js_analyze/tests/specs/nursery/noReactDeps/invalid.tsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
source: crates/biome_js_analyze/tests/spec_tests.rs | ||
expression: invalid.tsx | ||
snapshot_kind: text | ||
--- | ||
# Input | ||
```tsx | ||
import { createEffect, createMemo } from "solid-js"; | ||
|
||
createEffect(() => { | ||
console.log(signal()); | ||
}, [signal()]); | ||
|
||
createEffect(() => { | ||
console.log(signal()); | ||
}, [signal]); | ||
|
||
const deps = [signal]; | ||
createEffect(() => { | ||
console.log(signal()); | ||
}, deps); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a(), b()]); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b]); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b()]); | ||
|
||
const deps = [a, b]; | ||
const value = createMemo(() => computeExpensiveValue(a(), b()), deps); | ||
|
||
const deps = [a, b]; | ||
const memoFn = () => computeExpensiveValue(a(), b()); | ||
const value = createMemo(memoFn, deps); | ||
``` |
27 changes: 27 additions & 0 deletions
27
crates/biome_js_analyze/tests/specs/nursery/noReactDeps/valid.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { createEffect, createMemo } from "solid-js"; | ||
|
||
createEffect(() => { | ||
console.log(signal()); | ||
}); | ||
|
||
createEffect((prev) => { | ||
console.log(signal()); | ||
return prev + 1; | ||
}, 0); | ||
|
||
createEffect((prev) => { | ||
console.log(signal()); | ||
return (prev || 0) + 1; | ||
}); | ||
|
||
createEffect((prev) => { | ||
console.log(signal()); | ||
return prev ? prev + 1 : 1; | ||
}, undefined); | ||
|
||
const value = createMemo(() => computeExpensiveValue(a(), b())); | ||
|
||
const sum = createMemo((prev) => input() + prev, 0); | ||
|
||
const args = [() => { console.log(signal()); }, [signal()]]; | ||
createEffect(...args); |
Oops, something went wrong.