Skip to content

Commit

Permalink
refactor(linter): allow multiple rule sources (#2336)
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos authored Apr 8, 2024
1 parent 9e35d5b commit a8726d0
Show file tree
Hide file tree
Showing 217 changed files with 541 additions and 235 deletions.
10 changes: 5 additions & 5 deletions crates/biome_analyze/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct RuleMetadata {
/// The kind of fix
pub fix_kind: Option<FixKind>,
/// The source URL of the rule
pub source: Option<RuleSource>,
pub sources: &'static [RuleSource],
/// The source kind of the rule
pub source_kind: Option<RuleSourceKind>,
}
Expand Down Expand Up @@ -210,7 +210,7 @@ impl RuleSource {
}
}

#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, Copy)]
pub enum RuleSourceKind {
/// The rule implements the same logic of the source
#[default]
Expand All @@ -234,7 +234,7 @@ impl RuleMetadata {
docs,
recommended: false,
fix_kind: None,
source: None,
sources: &[],
source_kind: None,
}
}
Expand All @@ -254,8 +254,8 @@ impl RuleMetadata {
self
}

pub const fn source(mut self, source: RuleSource) -> Self {
self.source = Some(source);
pub const fn sources(mut self, sources: &'static [RuleSource]) -> Self {
self.sources = sources;
//if self.source_kind.is_none() {
// self.source_kind = Some(RuleSourceKind::SameLogic);
//}
Expand Down
94 changes: 94 additions & 0 deletions crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ pub(crate) fn migrate_eslint_any_rule(
let rule = group.use_import_type.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/default-param-last" => {
let group = rules.style.get_or_insert_with(Default::default);
let rule = group
.use_default_parameter_last
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/dot-notation" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group.use_literal_keys.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/naming-convention" => {
if !options.include_inspired {
results.has_inspired_rules = true;
Expand All @@ -62,6 +74,20 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-dupe-class-members" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
.no_duplicate_class_members
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-empty-function" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
.no_empty_block_statements
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-empty-interface" => {
if !options.include_inspired {
results.has_inspired_rules = true;
Expand Down Expand Up @@ -100,6 +126,11 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-loss-of-precision" => {
let group = rules.correctness.get_or_insert_with(Default::default);
let rule = group.no_precision_loss.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-misused-new" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
Expand All @@ -124,6 +155,16 @@ pub(crate) fn migrate_eslint_any_rule(
let rule = group.no_redeclare.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-restricted-imports" => {
if !options.include_nursery {
return false;
}
let group = rules.nursery.get_or_insert_with(Default::default);
let rule = group
.no_restricted_imports
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-this-alias" => {
if !options.include_inspired {
results.has_inspired_rules = true;
Expand All @@ -149,6 +190,11 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-unused-vars" => {
let group = rules.correctness.get_or_insert_with(Default::default);
let rule = group.no_unused_variables.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/no-use-before-define" => {
let group = rules.correctness.get_or_insert_with(Default::default);
let rule = group
Expand Down Expand Up @@ -233,6 +279,11 @@ pub(crate) fn migrate_eslint_any_rule(
let rule = group.use_optional_chain.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"@typescript-eslint/require-await" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group.use_await.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"barrel-files/avoid-namespace-import" => {
if !options.include_nursery {
return false;
Expand Down Expand Up @@ -690,11 +741,25 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-empty-function" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
.no_empty_block_statements
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-empty-pattern" => {
let group = rules.correctness.get_or_insert_with(Default::default);
let rule = group.no_empty_pattern.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-empty-static-block" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
.no_empty_block_statements
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-eval" => {
let group = rules.security.get_or_insert_with(Default::default);
let rule = group.no_global_eval.get_or_insert(Default::default());
Expand Down Expand Up @@ -829,6 +894,11 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-redeclare" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group.no_redeclare.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-regex-spaces" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group
Expand Down Expand Up @@ -944,11 +1014,25 @@ pub(crate) fn migrate_eslint_any_rule(
let rule = group.no_unused_variables.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-use-before-define" => {
let group = rules.correctness.get_or_insert_with(Default::default);
let rule = group
.no_invalid_use_before_declaration
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-useless-catch" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group.no_useless_catch.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-useless-constructor" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group
.no_useless_constructor
.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"no-useless-rename" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group.no_useless_rename.get_or_insert(Default::default());
Expand Down Expand Up @@ -1147,11 +1231,21 @@ pub(crate) fn migrate_eslint_any_rule(
let rule = group.no_for_each.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"unicorn/no-for-loop" => {
let group = rules.style.get_or_insert_with(Default::default);
let rule = group.use_for_of.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"unicorn/no-instanceof-array" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group.use_is_array.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"unicorn/no-static-only-class" => {
let group = rules.complexity.get_or_insert_with(Default::default);
let rule = group.no_static_only_class.get_or_insert(Default::default());
rule.set_level(rule_severity.into());
}
"unicorn/no-thenable" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group.no_then_property.get_or_insert(Default::default());
Expand Down
22 changes: 10 additions & 12 deletions crates/biome_cli/src/execute/migrate/eslint_eslint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ use biome_deserialize::{
};
use biome_deserialize_macros::Deserializable;
use biome_rowan::TextRange;
use indexmap::IndexSet;
use rustc_hash::FxHashMap;
use rustc_hash::FxHashSet;
use std::borrow::Cow;
use std::collections::hash_set;
use std::hash::{Hash, Hasher};
use std::ops::DerefMut;
use std::vec;
Expand Down Expand Up @@ -406,21 +405,20 @@ impl Deserializable for NumberOrString {
}

#[derive(Debug, Default)]
pub(crate) struct Rules(pub(crate) FxHashSet<Rule>);
pub(crate) struct Rules(
// We use `IndexSet` instead of `HashSet` to preserve the order.
// Keeping the order is important because several ESLint rules can have
// the same equivalent Biome rule.
// The severity level of the last one is thus used.
pub(crate) IndexSet<Rule>,
);
impl Merge for Rules {
fn merge_with(&mut self, other: Self) {
self.0.extend(other.0);
}
}
impl IntoIterator for Rules {
type Item = Rule;
type IntoIter = hash_set::IntoIter<Rule>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Deref for Rules {
type Target = FxHashSet<Rule>;
type Target = IndexSet<Rule>;
fn deref(&self) -> &Self::Target {
&self.0
}
Expand Down Expand Up @@ -448,7 +446,7 @@ impl Deserializable for Rules {
diagnostics: &mut Vec<biome_deserialize::DeserializationDiagnostic>,
) -> Option<Self::Output> {
use biome_deserialize::Text;
let mut result = FxHashSet::default();
let mut result = IndexSet::default();
for (key, value) in members.flatten() {
let Some(rule_name) = Text::deserialize(&key, "", diagnostics) else {
continue;
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_cli/src/execute/migrate/eslint_to_biome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl eslint_eslint::Rules {
results: &mut MigrationResults,
) -> biome_config::Rules {
let mut rules = biome_config::Rules::default();
for eslint_rule in self {
for eslint_rule in self.0 {
migrate_eslint_rule(&mut rules, eslint_rule, options, results);
}
rules
Expand Down
33 changes: 33 additions & 0 deletions crates/biome_cli/tests/commands/migrate_eslint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,36 @@ a/**
result,
));
}

#[test]
fn migrate_eslintrcjson_extended_rules() {
let biomejson = r#"{ "linter": { "enabled": true } }"#;
let eslintrc = r#"{
"rules": {
"dot-notation": 0,
"@typescript-eslint/dot-notation": 2,
"@typescript-eslint/no-dupe-class-members": 2,
"no-dupe-class-members": 0
}
}"#;

let mut fs = MemoryFileSystem::default();
fs.insert(Path::new("biome.json").into(), biomejson.as_bytes());
fs.insert(Path::new(".eslintrc.json").into(), eslintrc.as_bytes());

let mut console = BufferConsole::default();
let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from(["migrate", "eslint"].as_slice()),
);

assert!(result.is_ok(), "run_cli returned {result:?}");
assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"migrate_eslintrcjson_extended_rules",
fs,
console,
result,
));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: content
---
## `biome.json`

```json
{ "linter": { "enabled": true } }
```

## `.eslintrc.json`

```json
{
"rules": {
"dot-notation": 0,
"@typescript-eslint/dot-notation": 2,
"@typescript-eslint/no-dupe-class-members": 2,
"no-dupe-class-members": 0
}
}
```

# Emitted Messages

```block
biome.json migrate ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Configuration file can be updated.
1 │ - {·"linter":·{·"enabled":·true·}·}
1 │ + {
2 │ + → "linter":·{
3 │ + → → "enabled":·true,
4 │ + → → "rules":·{
5 │ + → → → "recommended":·false,
6 │ + → → → "complexity":·{·"useLiteralKeys":·"error"·},
7 │ + → → → "suspicious":·{·"noDuplicateClassMembers":·"off"·}
8 │ + → → }
9 │ + → }
10 │ + }
11 │ +
```

```block
Run the command with the option --write to apply the changes.
```
2 changes: 1 addition & 1 deletion crates/biome_js_analyze/src/lint/a11y/no_access_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ declare_rule! {
pub NoAccessKey {
version: "1.0.0",
name: "noAccessKey",
source: RuleSource::EslintJsxA11y("no-access-key"),
sources: &[RuleSource::EslintJsxA11y("no-access-key")],
recommended: true,
fix_kind: FixKind::Unsafe,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ declare_rule! {
pub NoAriaHiddenOnFocusable {
version: "1.4.0",
name: "noAriaHiddenOnFocusable",
source: RuleSource::EslintJsxA11y("no-aria-hidden-on-focusable"),
sources: &[RuleSource::EslintJsxA11y("no-aria-hidden-on-focusable")],
recommended: true,
fix_kind: FixKind::Unsafe,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ declare_rule! {
pub NoAriaUnsupportedElements {
version: "1.0.0",
name: "noAriaUnsupportedElements",
source: RuleSource::EslintJsxA11y("aria-unsupported-elements"),
sources: &[RuleSource::EslintJsxA11y("aria-unsupported-elements")],
recommended: true,
fix_kind: FixKind::Unsafe,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_js_analyze/src/lint/a11y/no_autofocus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ declare_rule! {
pub NoAutofocus {
version: "1.0.0",
name: "noAutofocus",
source: RuleSource::EslintJsxA11y("no-autofocus"),
sources: &[RuleSource::EslintJsxA11y("no-autofocus")],
recommended: true,
fix_kind: FixKind::Unsafe,
}
Expand Down
Loading

0 comments on commit a8726d0

Please sign in to comment.