Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(js_analyze): add lint/noEnum #3653

Merged
merged 15 commits into from
Aug 27, 2024
Merged

Conversation

nickfla1
Copy link
Contributor

@nickfla1 nickfla1 commented Aug 13, 2024

Summary

Closes #3651

Added the noEnum TypeScript rule to disallow of enums altogether.

Test Plan

Run cargo test.

quick_test changes to test this rule:

diff --git a/crates/biome_js_analyze/src/lib.rs b/crates/biome_js_analyze/src/lib.rs
index 5883fe6caa..029fa40916 100644
--- a/crates/biome_js_analyze/src/lib.rs
+++ b/crates/biome_js_analyze/src/lib.rs
@@ -180,7 +180,7 @@ mod tests {
     use crate::react::hooks::StableHookResult;
     use crate::{analyze, AnalysisFilter, ControlFlow};
 
-    #[ignore]
+    // #[ignore]
     #[test]
     fn quick_test() {
         fn markup_to_string(markup: Markup) -> String {
@@ -192,7 +192,7 @@ mod tests {
             String::from_utf8(buffer).unwrap()
         }
 
-        const SOURCE: &str = r#"import buffer from "buffer"; "#;
+        const SOURCE: &str = r#"enum Foo { Bar = 'bar' }; "#;
 
         let parsed = parse(SOURCE, JsFileSource::tsx(), JsParserOptions::default());
 
@@ -204,7 +204,7 @@ mod tests {
             dependencies_index: Some(1),
             stable_result: StableHookResult::None,
         };
-        let rule_filter = RuleFilter::Rule("style", "useNodejsImportProtocol");
+        let rule_filter = RuleFilter::Rule("nursery", "noEnum");
 
         options.configuration.rules.push_rule(
             RuleKey::new("nursery", "useHookAtTopLevel"),

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Aug 13, 2024
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used noConsole as inspiration to write diagnostic and documentation, but I understand it can feel a little lacking.

I'm open to suggestions!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be helpful to suggest an alternative in this case. For example, recommending a TypeScript object as a replacement for enums

Copy link
Member

@chansuke chansuke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution and a good start! I've added a few comments. :)

/// BAR = 'bar'
/// }
/// ```
///
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add a valid case like below?

    /// ### Valid
    ///
    /// ```js
    /// const Foo = {
    ///     BAR: 'bar',
    /// };
    /// ```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be helpful to suggest an alternative in this case. For example, recommending a TypeScript object as a replacement for enums

Copy link

codspeed-hq bot commented Aug 13, 2024

CodSpeed Performance Report

Merging #3653 will not alter performance

Comparing nickfla1:feat/no-enum (8c1f632) with main (b16def6)

Summary

✅ 101 untouched benchmarks

Comment on lines 58 to 63
"Don't use "<Emphasis>"enum"</Emphasis>
},
)
.note(markup! {
"Prefer using JavaScript objects or TypeScript unions over enums."
}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diagnostics are good, however we miss the explanation of the error. Here, we need to explain why a user shouldn't use enums.

Please refer to our rule pillars: https://biomejs.dev/linter/#rule-pillars

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @nickfla1, I noticed 5694370 (#3653). The reason should be added to the diagnostic too :)

@Conaclos Conaclos changed the title feat(biome_js_analyze): noEnum feat(js_analyze): add lint/noEnum Aug 18, 2024
Copy link
Member

@Conaclos Conaclos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions / suggestions:

  • We already have a rule that bans const enum. I tis a recommended rule.
    Should we restrict noEnum to regular enums?
    This could avoid double diagnostics and more flexibility: a user could allow const enum and disallow enum.
  • I think we could allow declare enum and enums in TS declaration files (See rules such as noEvolvingTypes that checks if a file is a declaration/definition file) and in any ambient context (we have a is_ambient method that can be applied on a TsEnumdeclaration node)

@nickfla1
Copy link
Contributor Author

Some questions / suggestions:

  • We already have a rule that bans const enum. I tis a recommended rule.
    Should we restrict noEnum to regular enums?
    This could avoid double diagnostics and more flexibility: a user could allow const enum and disallow enum.
  • I think we could allow declare enum and enums in TS declaration files (See rules such as noEvolvingTypes that checks if a file is a declaration/definition file) and in any ambient context (we have a is_ambient method that can be applied on a TsEnumdeclaration node)

I think I agree on everything you've said! I'll wait for confirmation from @ematipico before implementing it in the PR

@ematipico
Copy link
Member

Sure go ahead

Comment on lines 67 to 72
let source_type = ctx.source_type::<JsFileSource>().language();
let is_declaration = source_type.is_definition_file();

if is_declaration {
return None
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check it before checking for const/ambient enums.

Comment on lines 61 to 63
let is_ambient = enum_decl.is_ambient();

if is_const_decl || is_ambient {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let is_ambient = enum_decl.is_ambient();
if is_const_decl || is_ambient {
if is_const_decl || enum_decl.is_ambient() {

@@ -23,7 +23,7 @@ invalid.ts:1:1 lint/nursery/noEnum ━━━━━━━━━━━━━━━
│ ^
4 │

i Prefer using JavaScript objects or TypeScript unions over enums.
i Prefer using JavaScript objects, TypeScript unions or const enums over plain enums.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const enum have some important caveat (cf noConstEnum, docs). Moreover, we recommend noConstEnum.
Thus, I think it is not a good idea to suggest using them here.

Suggested change
i Prefer using JavaScript objects, TypeScript unions or const enums over plain enums.
i Prefer using JavaScript objects or TypeScript unions over plain enums.

Copy link
Member

@Conaclos Conaclos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I left my last suggestions :)

Comment on lines 95 to 97
.note(markup! {
"Enums will get compiled into JavaScript code, which can increase bundle size and cause side-effects in the codebase."
}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not always true because some bundlers such as ESBuild or TSC are able to inline some enums. Thus, this can even be the reverse: using TS enum can improve code size and performance.

I suggest removing this note.

Comment on lines 89 to 91
.note(markup! {
"Prefer using JavaScript objects or TypeScript unions over plain enums."
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose this rephrasing:

Suggested change
.note(markup! {
"Prefer using JavaScript objects or TypeScript unions over plain enums."
})
.note(markup! {
"Use JavaScript objects or TypeScript unions instead."
})

Also, to follow the order of our rule pillars, I could put this note after other notes.

crates/biome_js_analyze/src/lint/nursery/no_enum.rs Outdated Show resolved Hide resolved
@nickfla1
Copy link
Contributor Author

I have updated the docs and notes accordingly.

I am wondering what's the best approach to solve the conflict on that file since it's autogenerated.

@ematipico
Copy link
Member

I have updated the docs and notes accordingly.

I am wondering what's the best approach to solve the conflict on that file since it's autogenerated.

Merge from main and accept the changes. Then run just gen-lint and push the new changes.

Copy link
Member

@chansuke chansuke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Member

@Conaclos Conaclos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me :) Thanks for your contribution!

@Conaclos Conaclos merged commit 2d590ea into biomejs:main Aug 27, 2024
12 checks passed
@nickfla1
Copy link
Contributor Author

Happy to help! Thank you for your support and suggestions :)

@nickfla1 nickfla1 deleted the feat/no-enum branch August 28, 2024 07:54
@Conaclos Conaclos added the A-Changelog Area: changelog label Sep 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Changelog Area: changelog A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

📎 Implement noEnum
4 participants