Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-domain-group-enables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@biomejs/biome": patch
---

Fixed a bug where enabling the rules of a whole group, would enable rules that belonged to a domain under the same group.

For example, `linter.rules.correctness = "error"` no longer enables React- or Qwik-specific correctness rules unless `linter.domains.react`, `linter.domains.qwik`, or an explicit rule config also enables them, or their relative dependencies are installed.
57 changes: 57 additions & 0 deletions crates/biome_cli/tests/cases/linter_domains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,60 @@ describe("foo", () => {
result,
));
}

/// Verifies that explicit domain enables are additive with whole-group enables:
/// the plain group contributes only non-domain rules, and the domain adds its
/// domain-tagged rules back explicitly.
#[test]
fn group_enable_and_domain_enable_are_additive() {
let mut console = BufferConsole::default();
let fs = MemoryFileSystem::default();
let config = Utf8Path::new("biome.json");
fs.insert(
config.into(),
br#"{
"linter": {
"rules": {
"recommended": false,
"correctness": "error"
},
"domains": {
"react": "all"
}
}
}
"#,
);
let test1 = Utf8Path::new("test1.jsx");
fs.insert(
test1.into(),
br#"import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}
"#,
);

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(["lint", test1.as_str()].as_slice()),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"group_enable_and_domain_enable_are_additive",
fs,
console,
result,
));
}
108 changes: 108 additions & 0 deletions crates/biome_cli/tests/cases/linter_groups_plain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,111 @@ fn disable_all_rules_when_group_is_off() {
result,
));
}

/// Verifies that enabling the whole `correctness` group does not implicitly
/// enable React-domain rules such as `useExhaustiveDependencies`.
#[test]
fn group_enable_does_not_enable_domain_rules() {
let mut console = BufferConsole::default();
let fs = MemoryFileSystem::default();
let config = Utf8Path::new("biome.json");
fs.insert(
config.into(),
br#"{
"linter": {
"rules": {
"recommended": false,
"correctness": "error"
}
}
}
"#,
);
let test1 = Utf8Path::new("test1.jsx");
fs.insert(
test1.into(),
br#"import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}
"#,
);

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(["lint", test1.as_str()].as_slice()),
);

assert!(result.is_ok(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"group_enable_does_not_enable_domain_rules",
fs,
console,
result,
));
}

/// Verifies that a domain-tagged rule can still be enabled explicitly by name
/// even though plain group enables skip domain rules.
#[test]
fn explicit_domain_rule_enable_still_works() {
let mut console = BufferConsole::default();
let fs = MemoryFileSystem::default();
let config = Utf8Path::new("biome.json");
fs.insert(
config.into(),
br#"{
"linter": {
"rules": {
"recommended": false,
"correctness": {
"useExhaustiveDependencies": "error"
}
}
}
}
"#,
);
let test1 = Utf8Path::new("test1.jsx");
fs.insert(
test1.into(),
br#"import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}
"#,
);

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(["lint", test1.as_str()].as_slice()),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"explicit_domain_rule_enable_still_works",
fs,
console,
result,
));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: redactor(content)
---
## `biome.json`

```json
{
"linter": {
"rules": {
"recommended": false,
"correctness": "error"
},
"domains": {
"react": "all"
}
}
}
```

## `test1.jsx`

```jsx
import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}

```

# Termination Message

```block
lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Some errors were emitted while running checks.



```

# Emitted Messages

```block
test1.jsx:6:5 lint/correctness/useExhaustiveDependencies FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× This hook does not specify its dependency on local.

4 │ const [local, setLocal] = useState(0);
5 │
> 6 │ useEffect(() => {
│ ^^^^^^^^^
7 │ console.log(local);
8 │ }, []);

i This dependency is being used here, but is not specified in the hook dependency list.

6 │ useEffect(() => {
> 7 │ console.log(local);
│ ^^^^^
8 │ }, []);
9 │

i React relies on hook dependencies to determine when to re-compute Effects.
Failing to specify dependencies can result in Effects not updating correctly when state changes.
These "stale closures" are a common source of surprising bugs.

i Either include it or remove the dependency array.

i Unsafe fix: Add the missing dependency local to the list.

8 │ ····},·[local]);
│ +++++

```

```block
Checked 1 file in <TIME>. No fixes applied.
Found 1 error.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: redactor(content)
---
## `biome.json`

```json
{
"linter": {
"rules": {
"recommended": false,
"correctness": {
"useExhaustiveDependencies": "error"
}
}
}
}
```

## `test1.jsx`

```jsx
import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}

```

# Termination Message

```block
lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Some errors were emitted while running checks.



```

# Emitted Messages

```block
test1.jsx:6:5 lint/correctness/useExhaustiveDependencies FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× This hook does not specify its dependency on local.

4 │ const [local, setLocal] = useState(0);
5 │
> 6 │ useEffect(() => {
│ ^^^^^^^^^
7 │ console.log(local);
8 │ }, []);

i This dependency is being used here, but is not specified in the hook dependency list.

6 │ useEffect(() => {
> 7 │ console.log(local);
│ ^^^^^
8 │ }, []);
9 │

i React relies on hook dependencies to determine when to re-compute Effects.
Failing to specify dependencies can result in Effects not updating correctly when state changes.
These "stale closures" are a common source of surprising bugs.

i Either include it or remove the dependency array.

i Unsafe fix: Add the missing dependency local to the list.

8 │ ····},·[local]);
│ +++++

```

```block
Checked 1 file in <TIME>. No fixes applied.
Found 1 error.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: redactor(content)
---
## `biome.json`

```json
{
"linter": {
"rules": {
"recommended": false,
"correctness": "error"
}
}
}
```

## `test1.jsx`

```jsx
import { useEffect, useState } from "react";

export function Component() {
const [local, setLocal] = useState(0);

useEffect(() => {
console.log(local);
}, []);

return <button onClick={() => setLocal(1)}>increment</button>;
}

```

# Emitted Messages

```block
Checked 1 file in <TIME>. No fixes applied.
```
Loading