From 2bfced0cf1596e1b02757208f8e498041ec1d699 Mon Sep 17 00:00:00 2001 From: Victorien ELVINGER Date: Mon, 5 Dec 2022 11:07:15 +0100 Subject: [PATCH] feat(rome_js_analyze): `useEnumInitializers` (#3839) Co-authored-by: Micha Reiser --- .../src/categories.rs | 1 + .../rome_js_analyze/src/analyzers/nursery.rs | 3 +- .../nursery/use_enum_initializers.rs | 160 +++++++++++++ .../nursery/useEnumInitializers/invalid.ts | 31 +++ .../useEnumInitializers/invalid.ts.snap | 220 ++++++++++++++++++ .../nursery/useEnumInitializers/valid.ts | 15 ++ .../nursery/useEnumInitializers/valid.ts.snap | 25 ++ .../src/configuration/linter/rules.rs | 13 +- editors/vscode/configuration_schema.json | 11 + npm/backend-jsonrpc/src/workspace.ts | 5 + npm/js-api/src/wasm.ts | 6 +- npm/rome/configuration_schema.json | 11 + website/src/pages/lint/rules/index.mdx | 6 + .../pages/lint/rules/useEnumInitializers.md | 113 +++++++++ website/src/playground/types.ts | 6 +- 15 files changed, 615 insertions(+), 11 deletions(-) create mode 100644 crates/rome_js_analyze/src/analyzers/nursery/use_enum_initializers.rs create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts.snap create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts.snap create mode 100644 website/src/pages/lint/rules/useEnumInitializers.md diff --git a/crates/rome_diagnostics_categories/src/categories.rs b/crates/rome_diagnostics_categories/src/categories.rs index 95ffe2e41d6..78a7a28c0aa 100644 --- a/crates/rome_diagnostics_categories/src/categories.rs +++ b/crates/rome_diagnostics_categories/src/categories.rs @@ -98,6 +98,7 @@ define_dategories! { "lint/nursery/useCamelCase": "https://docs.rome.tools/lint/rules/useCamelCase", "lint/nursery/useConst":"https://docs.rome.tools/lint/rules/useConst", "lint/nursery/useDefaultSwitchClauseLast":"https://docs.rome.tools/lint/rules/useDefaultSwitchClauseLast", + "lint/nursery/useEnumInitializers":"https://docs.rome.tools/lint/rules/useEnumInitializers", "lint/nursery/useExhaustiveDependencies": "https://docs.rome.tools/lint/rules/useExhaustiveDependencies", "lint/nursery/useFlatMap": "https://docs.rome.tools/lint/rules/useFlatMap", "lint/nursery/useNumericLiterals": "https://docs.rome.tools/lint/rules/useNumericLiterals", diff --git a/crates/rome_js_analyze/src/analyzers/nursery.rs b/crates/rome_js_analyze/src/analyzers/nursery.rs index 0be2c019938..45abdc87ed7 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery.rs @@ -21,7 +21,8 @@ mod no_string_case_mismatch; mod no_unsafe_finally; mod no_void_type_return; mod use_default_switch_clause_last; +mod use_enum_initializers; mod use_flat_map; mod use_numeric_literals; mod use_valid_for_direction; -declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_banned_types :: NoBannedTypes , self :: no_conditional_assignment :: NoConditionalAssignment , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_dupe_keys :: NoDupeKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_explicit_any :: NoExplicitAny , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_header_scope :: NoHeaderScope , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_precision_loss :: NoPrecisionLoss , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_void_type_return :: NoVoidTypeReturn , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_flat_map :: UseFlatMap , self :: use_numeric_literals :: UseNumericLiterals , self :: use_valid_for_direction :: UseValidForDirection ,] } } +declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_banned_types :: NoBannedTypes , self :: no_conditional_assignment :: NoConditionalAssignment , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_dupe_keys :: NoDupeKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_explicit_any :: NoExplicitAny , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_header_scope :: NoHeaderScope , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_precision_loss :: NoPrecisionLoss , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_void_type_return :: NoVoidTypeReturn , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_enum_initializers :: UseEnumInitializers , self :: use_flat_map :: UseFlatMap , self :: use_numeric_literals :: UseNumericLiterals , self :: use_valid_for_direction :: UseValidForDirection ,] } } diff --git a/crates/rome_js_analyze/src/analyzers/nursery/use_enum_initializers.rs b/crates/rome_js_analyze/src/analyzers/nursery/use_enum_initializers.rs new file mode 100644 index 00000000000..13269d10f3b --- /dev/null +++ b/crates/rome_js_analyze/src/analyzers/nursery/use_enum_initializers.rs @@ -0,0 +1,160 @@ +use crate::JsRuleAction; +use rome_analyze::context::RuleContext; +use rome_analyze::{declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic}; +use rome_console::markup; +use rome_diagnostics::Applicability; +use rome_js_factory::make; +use rome_js_syntax::{AnyJsExpression, AnyJsLiteralExpression, JsSyntaxKind, TsEnumMember}; +use rome_rowan::{AstNode, BatchMutationExt, Direction}; + +declare_rule! { + /// Require that each enum member value be explicitly initialized. + /// + /// TypeScript enums are a practical way to organize semantically related constant values. Members of enums that don't have explicit values are by default given sequentially increasing numbers. + /// + /// When the value of enum members are important, allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + /// + /// Source: https://typescript-eslint.io/rules/prefer-enum-initializers + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```ts,expect_diagnostic + /// enum Version { + /// V1, + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// enum Status { + /// Open = 1, + /// Close, + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// enum Color { + /// Red = "Red", + /// Green = "Green", + /// Blue, + /// } + /// ``` + /// + /// ### Valid + /// + /// ```ts + /// enum Status { + /// Open = 1, + /// Close = 2, + /// } + /// ``` + /// + /// ```ts + /// enum Color { + /// Red = "Red", + /// Green = "Green", + /// Blue = "Blue", + /// } + /// ``` + /// + pub(crate) UseEnumInitializers { + version: "11.0.0", + name: "useEnumInitializers", + recommended: true, + } +} + +impl Rule for UseEnumInitializers { + type Query = Ast; + type State = (); + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let enum_member = ctx.query(); + enum_member.initializer().is_none().then_some(()) + } + + fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { + let enum_member = ctx.query(); + Some(RuleDiagnostic::new( + rule_category!(), + enum_member.range(), + markup! { + "The ""enum member"" should be initialized." + }, + ).note( + "Allowing implicit values for enum members can cause bugs if enum declarations are modified over time." + )) + } + + fn action(ctx: &RuleContext, _: &Self::State) -> Option { + let enum_member = ctx.query(); + let prev_enum_members = enum_member + .syntax() + .siblings(Direction::Prev) + .into_iter() + .skip(1) // consume enum_member + .filter_map(TsEnumMember::cast); + let mut count = 0; + let mut prev_enum_member_info = None; + for prev_enum_member in prev_enum_members { + count += 1; + if let Some(initializer) = prev_enum_member.initializer() { + prev_enum_member_info = Some((initializer, prev_enum_member.name().ok()?)); + break; + } + } + let expr = if let Some((prev_initializer, prev_name)) = prev_enum_member_info { + let expr = prev_initializer.expression().ok()?; + let expr = expr.as_any_js_literal_expression()?; + match expr { + AnyJsLiteralExpression::JsNumberLiteralExpression(expr) => { + Some(AnyJsLiteralExpression::JsNumberLiteralExpression( + make::js_number_literal_expression(make::js_number_literal( + expr.as_number()? + f64::from(count as i32), + )), + )) + } + AnyJsLiteralExpression::JsStringLiteralExpression(expr) => { + let prev_enum_delim_val = expr.value_token().ok()?; + let prev_enum_delim_val = prev_enum_delim_val.text(); + let prev_enum_val = &prev_enum_delim_val[1..(prev_enum_delim_val.len() - 1)]; + if prev_name.text() == prev_enum_val { + let enum_name = enum_member.name().ok()?.text(); + Some(AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal(&enum_name)), + )) + } else { + None + } + } + _ => None, + } + } else { + Some(AnyJsLiteralExpression::JsNumberLiteralExpression( + make::js_number_literal_expression(make::js_number_literal(count)), + )) + }; + if let Some(expr) = expr { + let new_enum_member = + enum_member + .to_owned() + .with_initializer(Some(make::js_initializer_clause( + make::token_decorated_with_space(JsSyntaxKind::EQ), + AnyJsExpression::AnyJsLiteralExpression(expr), + ))); + let mut mutation = ctx.root().begin(); + mutation.replace_node_discard_trivia(enum_member.to_owned(), new_enum_member); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "" }.to_owned(), + mutation, + }) + } else { + None + } + } +} diff --git a/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts new file mode 100644 index 00000000000..f7b57e67667 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts @@ -0,0 +1,31 @@ +export enum Status { + Close, + MidClose = 1, + MidOpen = 10, + /* implicit */ Open /* 11 */, +} + +export enum ComputedFlags { + Flag1 = 1, + Flag2 = 1 << 1, + Flag3, +} + +export enum Direction { + Down, + Left, + Right, + Up, +} + +export enum Color { + Red = "Red", + Green = "Green", + Blue, +} + +export enum IndexedColor { + Red = "0", + Green = "1", + Blue, +} \ No newline at end of file diff --git a/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts.snap b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts.snap new file mode 100644 index 00000000000..8c8944e3ca0 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/invalid.ts.snap @@ -0,0 +1,220 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 74 +expression: invalid.ts +--- +# Input +```js +export enum Status { + Close, + MidClose = 1, + MidOpen = 10, + /* implicit */ Open /* 11 */, +} + +export enum ComputedFlags { + Flag1 = 1, + Flag2 = 1 << 1, + Flag3, +} + +export enum Direction { + Down, + Left, + Right, + Up, +} + +export enum Color { + Red = "Red", + Green = "Green", + Blue, +} + +export enum IndexedColor { + Red = "0", + Green = "1", + Blue, +} +``` + +# Diagnostics +``` +invalid.ts:2:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 1 │ export enum Status { + > 2 │ Close, + │ ^^^^^ + 3 │ MidClose = 1, + 4 │ MidOpen = 10, + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 2 │ → Close·=·0, + │ ++++ + +``` + +``` +invalid.ts:5:17 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 3 │ MidClose = 1, + 4 │ MidOpen = 10, + > 5 │ /* implicit */ Open /* 11 */, + │ ^^^^ + 6 │ } + 7 │ + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 5 │ → /*·implicit·*/·Open·/*·11·*/·=·11, + │ +++++ + +``` + +``` +invalid.ts:11:2 lint/nursery/useEnumInitializers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 9 │ Flag1 = 1, + 10 │ Flag2 = 1 << 1, + > 11 │ Flag3, + │ ^^^^^ + 12 │ } + 13 │ + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + +``` + +``` +invalid.ts:15:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 14 │ export enum Direction { + > 15 │ Down, + │ ^^^^ + 16 │ Left, + 17 │ Right, + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 15 │ → Down·=·0, + │ ++++ + +``` + +``` +invalid.ts:16:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 14 │ export enum Direction { + 15 │ Down, + > 16 │ Left, + │ ^^^^ + 17 │ Right, + 18 │ Up, + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 16 │ → Left·=·1, + │ ++++ + +``` + +``` +invalid.ts:17:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 15 │ Down, + 16 │ Left, + > 17 │ Right, + │ ^^^^^ + 18 │ Up, + 19 │ } + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 17 │ → Right·=·2, + │ ++++ + +``` + +``` +invalid.ts:18:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 16 │ Left, + 17 │ Right, + > 18 │ Up, + │ ^^ + 19 │ } + 20 │ + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 18 │ → Up·=·3, + │ ++++ + +``` + +``` +invalid.ts:24:2 lint/nursery/useEnumInitializers FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 22 │ Red = "Red", + 23 │ Green = "Green", + > 24 │ Blue, + │ ^^^^ + 25 │ } + 26 │ + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + i Suggested fix: + + 24 │ → Blue·=·"Blue", + │ +++++++++ + +``` + +``` +invalid.ts:30:2 lint/nursery/useEnumInitializers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The enum member should be initialized. + + 28 │ Red = "0", + 29 │ Green = "1", + > 30 │ Blue, + │ ^^^^ + 31 │ } + + i Allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts new file mode 100644 index 00000000000..d281ef47ea5 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts @@ -0,0 +1,15 @@ +export enum Status { + Open = 1, + Close = 2, +} + +export enum Color { + Red = "Red", + Green = "Green", + Blue = "Blue", +} + +export enum Mix { + Str = "Str", + Number = 0, +} \ No newline at end of file diff --git a/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts.snap b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts.snap new file mode 100644 index 00000000000..4b2e36d04e5 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useEnumInitializers/valid.ts.snap @@ -0,0 +1,25 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 74 +expression: valid.ts +--- +# Input +```js +export enum Status { + Open = 1, + Close = 2, +} + +export enum Color { + Red = "Red", + Green = "Green", + Blue = "Blue", +} + +export enum Mix { + Str = "Str", + Number = 0, +} +``` + + diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index 9c61f90c000..b00c03e7fa1 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -781,6 +781,8 @@ struct NurserySchema { use_const: Option, #[doc = "Enforce default clauses in switch statements to be last"] use_default_switch_clause_last: Option, + #[doc = "Require that each enum member value be explicitly initialized."] + use_enum_initializers: Option, #[doc = "Enforce all dependencies are correctly specified."] use_exhaustive_dependencies: Option, #[doc = "Promotes the use of .flatMap() when map().flat() are used together."] @@ -792,7 +794,7 @@ struct NurserySchema { } impl Nursery { const CATEGORY_NAME: &'static str = "nursery"; - pub(crate) const CATEGORY_RULES: [&'static str; 30] = [ + pub(crate) const CATEGORY_RULES: [&'static str; 31] = [ "noAccessKey", "noBannedTypes", "noConditionalAssignment", @@ -819,12 +821,13 @@ impl Nursery { "useCamelCase", "useConst", "useDefaultSwitchClauseLast", + "useEnumInitializers", "useExhaustiveDependencies", "useFlatMap", "useNumericLiterals", "useValidForDirection", ]; - const RECOMMENDED_RULES: [&'static str; 25] = [ + const RECOMMENDED_RULES: [&'static str; 26] = [ "noAccessKey", "noBannedTypes", "noConditionalAssignment", @@ -846,12 +849,13 @@ impl Nursery { "useAriaPropsForRole", "useConst", "useDefaultSwitchClauseLast", + "useEnumInitializers", "useExhaustiveDependencies", "useFlatMap", "useNumericLiterals", "useValidForDirection", ]; - const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 25] = [ + const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 26] = [ RuleFilter::Rule("nursery", Self::CATEGORY_RULES[0]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[1]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[2]), @@ -877,6 +881,7 @@ impl Nursery { RuleFilter::Rule("nursery", Self::CATEGORY_RULES[27]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[28]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[29]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[30]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -903,7 +908,7 @@ impl Nursery { pub(crate) fn is_recommended_rule(rule_name: &str) -> bool { Self::RECOMMENDED_RULES.contains(&rule_name) } - pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 25] { + pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 26] { Self::RECOMMENDED_RULES_AS_FILTERS } } diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index 55058a8c16a..7267a325d32 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -1046,6 +1046,17 @@ } ] }, + "useEnumInitializers": { + "description": "Require that each enum member value be explicitly initialized.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, "useExhaustiveDependencies": { "description": "Enforce all dependencies are correctly specified.", "anyOf": [ diff --git a/npm/backend-jsonrpc/src/workspace.ts b/npm/backend-jsonrpc/src/workspace.ts index 221e79e90be..08f26ab6539 100644 --- a/npm/backend-jsonrpc/src/workspace.ts +++ b/npm/backend-jsonrpc/src/workspace.ts @@ -455,6 +455,10 @@ export interface Nursery { * Enforce default clauses in switch statements to be last */ useDefaultSwitchClauseLast?: RuleConfiguration; + /** + * Require that each enum member value be explicitly initialized. + */ + useEnumInitializers?: RuleConfiguration; /** * Enforce all dependencies are correctly specified. */ @@ -698,6 +702,7 @@ export type Category = | "lint/nursery/useCamelCase" | "lint/nursery/useConst" | "lint/nursery/useDefaultSwitchClauseLast" + | "lint/nursery/useEnumInitializers" | "lint/nursery/useExhaustiveDependencies" | "lint/nursery/useFlatMap" | "lint/nursery/useNumericLiterals" diff --git a/npm/js-api/src/wasm.ts b/npm/js-api/src/wasm.ts index 201a85187c1..40da17b57ba 100644 --- a/npm/js-api/src/wasm.ts +++ b/npm/js-api/src/wasm.ts @@ -11,15 +11,15 @@ export enum Distribution { /** * Use this if you want to communicate with the WebAssembly client built for bundlers */ - BUNDLER, + BUNDLER = 0, /** * Use this if you want to communicate with the WebAssembly client built for Node.JS */ - NODE, + NODE = 1, /** * Use this if you want to communicate with the WebAssembly client built for the Web */ - WEB, + WEB = 2, } const isInitialized = { diff --git a/npm/rome/configuration_schema.json b/npm/rome/configuration_schema.json index 55058a8c16a..7267a325d32 100644 --- a/npm/rome/configuration_schema.json +++ b/npm/rome/configuration_schema.json @@ -1046,6 +1046,17 @@ } ] }, + "useEnumInitializers": { + "description": "Require that each enum member value be explicitly initialized.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, "useExhaustiveDependencies": { "description": "Enforce all dependencies are correctly specified.", "anyOf": [ diff --git a/website/src/pages/lint/rules/index.mdx b/website/src/pages/lint/rules/index.mdx index ca0ebe12787..a7f50017938 100644 --- a/website/src/pages/lint/rules/index.mdx +++ b/website/src/pages/lint/rules/index.mdx @@ -602,6 +602,12 @@ Require const declarations for variables that are never reassigned Enforce default clauses in switch statements to be last
+

+ useEnumInitializers +

+Require that each enum member value be explicitly initialized. +
+

useExhaustiveDependencies

diff --git a/website/src/pages/lint/rules/useEnumInitializers.md b/website/src/pages/lint/rules/useEnumInitializers.md new file mode 100644 index 00000000000..9d3eba19eb3 --- /dev/null +++ b/website/src/pages/lint/rules/useEnumInitializers.md @@ -0,0 +1,113 @@ +--- +title: Lint Rule useEnumInitializers +parent: lint/rules/index +--- + +# useEnumInitializers (since v11.0.0) + +Require that each enum member value be explicitly initialized. + +TypeScript enums are a practical way to organize semantically related constant values. Members of enums that don't have explicit values are by default given sequentially increasing numbers. + +When the value of enum members are important, allowing implicit values for enum members can cause bugs if enum declarations are modified over time. + +Source: https://typescript-eslint.io/rules/prefer-enum-initializers + +## Examples + +### Invalid + +```ts +enum Version { + V1, +} +``` + +
nursery/useEnumInitializers.js:2:5 lint/nursery/useEnumInitializers  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━
+
+   The enum member should be initialized.
+  
+    1 │ enum Version {
+  > 2 │     V1,
+       ^^
+    3 │ }
+    4 │ 
+  
+   Allowing implicit values for enum members can cause bugs if enum declarations are modified over time.
+  
+   Suggested fix: 
+  
+    2 │ ····V1·=·0,
+        ++++ 
+
+ +```ts +enum Status { + Open = 1, + Close, +} +``` + +
nursery/useEnumInitializers.js:3:5 lint/nursery/useEnumInitializers  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━
+
+   The enum member should be initialized.
+  
+    1 │ enum Status {
+    2 │     Open = 1,
+  > 3 │     Close,
+       ^^^^^
+    4 │ }
+    5 │ 
+  
+   Allowing implicit values for enum members can cause bugs if enum declarations are modified over time.
+  
+   Suggested fix: 
+  
+    3 │ ····Close·=·2,
+           ++++ 
+
+ +```ts +enum Color { + Red = "Red", + Green = "Green", + Blue, +} +``` + +
nursery/useEnumInitializers.js:4:5 lint/nursery/useEnumInitializers  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━
+
+   The enum member should be initialized.
+  
+    2 │     Red = "Red",
+    3 │     Green = "Green",
+  > 4 │     Blue,
+       ^^^^
+    5 │ }
+    6 │ 
+  
+   Allowing implicit values for enum members can cause bugs if enum declarations are modified over time.
+  
+   Suggested fix: 
+  
+    4 │ ····Blue·=·"Blue",
+          +++++++++ 
+
+ +### Valid + +```ts +enum Status { + Open = 1, + Close = 2, +} +``` + +```ts +enum Color { + Red = "Red", + Green = "Green", + Blue = "Blue", +} +``` + diff --git a/website/src/playground/types.ts b/website/src/playground/types.ts index 10bf009b58d..16989ebfa5a 100644 --- a/website/src/playground/types.ts +++ b/website/src/playground/types.ts @@ -34,9 +34,9 @@ export enum TrailingComma { } export enum LoadingState { - Loading, - Success, - Error, + Loading = 0, + Success = 1, + Error = 2, } export enum Semicolons {