Skip to content

Commit f3d4c00

Browse files
Moktoematipico
andauthored
feat(js/nursery): add noSvelteNoUnnecessaryStateWrap rule (#10545)
Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
1 parent 03c4767 commit f3d4c00

27 files changed

Lines changed: 895 additions & 0 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Added the new nursery rule [`noSvelteUnnecessaryStateWrap`](https://biomejs.dev/linter/rules/no-svelte-unnecessary-state-wrap/), which reports unnecessary `$state()` wrapping of classes from `svelte/reactivity` that are already reactive.
6+
7+
```svelte
8+
<script>
9+
import { SvelteMap } from "svelte/reactivity";
10+
const map = $state(new SvelteMap()); // redundant
11+
</script>
12+
```

crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_cli/src/execute/migrate/eslint_eslint.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,11 @@ impl Deserializable for Rules {
601601
result.insert(Rule::TypeScriptNoBaseToString(conf));
602602
}
603603
}
604+
"svelte/no-unnecessary-state-wrap" => {
605+
if let Some(conf) = RuleConf::deserialize(ctx, &value, name) {
606+
result.insert(Rule::SvelteNoUnnecessaryStateWrap(conf));
607+
}
608+
}
604609
"unicorn/filename-case" => {
605610
if let Some(conf) = RuleConf::deserialize(ctx, &value, name) {
606611
result.insert(Rule::UnicornFilenameCase(conf));
@@ -824,6 +829,7 @@ pub(crate) enum Rule {
824829
TypeScriptNoBaseToString(RuleConf<eslint_typescript::NoBaseToStringOptions>),
825830
TypeScriptNamingConvention(RuleConf<Box<eslint_typescript::NamingConventionSelection>>),
826831
TypeScriptNoShadow(RuleConf<eslint_typescript::NoShadowOptions>),
832+
SvelteNoUnnecessaryStateWrap(RuleConf<SvelteNoUnnecessaryStateWrapOptions>),
827833
UnicornFilenameCase(RuleConf<eslint_unicorn::FilenameCaseOptions>),
828834
UnicornNumericSeparatorsStyle(RuleConf<eslint_unicorn::NumericSeparatorsStyleOptions>),
829835
// If you add new variants, don't forget to update [Rules::deserialize].
@@ -853,6 +859,9 @@ impl Rule {
853859
Cow::Borrowed("@typescript-eslint/naming-convention")
854860
}
855861
Self::TypeScriptNoShadow(_) => Cow::Borrowed("@typescript-eslint/no-shadow"),
862+
Self::SvelteNoUnnecessaryStateWrap(_) => {
863+
Cow::Borrowed("svelte/no-unnecessary-state-wrap")
864+
}
856865
Self::UnicornFilenameCase(_) => Cow::Borrowed("unicorn/filename-case"),
857866
Self::UnicornNumericSeparatorsStyle(_) => {
858867
Cow::Borrowed("unicorn/numeric-separators-style")
@@ -871,3 +880,23 @@ impl Hash for Rule {
871880
self.name().hash(state);
872881
}
873882
}
883+
884+
#[derive(Debug, Default, Deserializable)]
885+
pub(crate) struct SvelteNoUnnecessaryStateWrapOptions {
886+
#[deserializable(rename = "additionalReactiveClasses")]
887+
pub(crate) additional_reactive_classes: Box<[Box<str>]>,
888+
#[deserializable(rename = "allowReassign")]
889+
pub(crate) allow_reassign: Option<bool>,
890+
}
891+
892+
impl From<SvelteNoUnnecessaryStateWrapOptions>
893+
for biome_rule_options::no_svelte_unnecessary_state_wrap::NoSvelteUnnecessaryStateWrapOptions
894+
{
895+
fn from(value: SvelteNoUnnecessaryStateWrapOptions) -> Self {
896+
Self {
897+
additional_reactive_classes: (!value.additional_reactive_classes.is_empty())
898+
.then_some(value.additional_reactive_classes),
899+
allow_reassign: value.allow_reassign,
900+
}
901+
}
902+
}

crates/biome_cli/src/execute/migrate/eslint_to_biome.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,21 @@ fn migrate_eslint_rule(
910910
}
911911
}
912912
}
913+
eslint_eslint::Rule::SvelteNoUnnecessaryStateWrap(conf) => {
914+
if migrate_eslint_any_rule(rules, &name, conf.severity(), opts, results) {
915+
let group = rules.nursery.get_or_insert_with(Default::default);
916+
if let SeverityOrGroup::Group(group) = group {
917+
group.no_svelte_unnecessary_state_wrap =
918+
Some(biome_config::RuleFixConfiguration::WithOptions(
919+
biome_config::RuleWithFixOptions {
920+
level: conf.severity().into(),
921+
fix: None,
922+
options: conf.option_or_default().into(),
923+
},
924+
));
925+
}
926+
}
927+
}
913928
}
914929
}
915930

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"biome": {},
3+
"eslint": {
4+
"rules": {
5+
"svelte/no-unnecessary-state-wrap": "error"
6+
}
7+
}
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
source: crates/biome_cli/src/execute/migrate/eslint_to_biome.rs
3+
expression: severity_only.jsonc
4+
---
5+
## ESLint Config
6+
7+
```json
8+
{ "rules": { "svelte/no-unnecessary-state-wrap": "error" } }
9+
10+
```
11+
12+
## Biome Config Before Migration
13+
14+
```json
15+
{}
16+
17+
```
18+
19+
## Biome Config After Migration
20+
21+
```json
22+
{
23+
"linter": {
24+
"rules": {
25+
"preset": "none",
26+
"nursery": {
27+
"noSvelteUnnecessaryStateWrap": { "level": "error", "options": {} }
28+
}
29+
}
30+
}
31+
}
32+
33+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"biome": {},
3+
"eslint": {
4+
"rules": {
5+
"svelte/no-unnecessary-state-wrap": [
6+
"error",
7+
{
8+
"additionalReactiveClasses": ["MyStore"],
9+
"allowReassign": true
10+
}
11+
]
12+
}
13+
}
14+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
source: crates/biome_cli/src/execute/migrate/eslint_to_biome.rs
3+
expression: with_options.jsonc
4+
---
5+
## ESLint Config
6+
7+
```json
8+
{
9+
"rules": {
10+
"svelte/no-unnecessary-state-wrap": [
11+
"error",
12+
{ "additionalReactiveClasses": ["MyStore"], "allowReassign": true }
13+
]
14+
}
15+
}
16+
17+
```
18+
19+
## Biome Config Before Migration
20+
21+
```json
22+
{}
23+
24+
```
25+
26+
## Biome Config After Migration
27+
28+
```json
29+
{
30+
"linter": {
31+
"rules": {
32+
"preset": "none",
33+
"nursery": {
34+
"noSvelteUnnecessaryStateWrap": {
35+
"level": "error",
36+
"options": {
37+
"additionalReactiveClasses": ["MyStore"],
38+
"allowReassign": true
39+
}
40+
}
41+
}
42+
}
43+
}
44+
}
45+
46+
```

crates/biome_configuration/src/analyzer/linter/rules.rs

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_configuration/src/generated/domain_selector.rs

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)