@@ -7,7 +7,8 @@ use biome_diagnostics::Severity;
77use biome_js_factory:: make;
88use biome_js_syntax:: {
99 AnyJsAssignment , AnyJsComputedMember , AnyJsMemberExpression , AnyJsName , AnyJsObjectMemberName ,
10- AnyTsEnumMemberName , JsComputedMemberName , JsSyntaxKind , T ,
10+ AnyTsEnumMemberName , JsComputedMemberName , JsSyntaxKind , JsSyntaxToken , T , inner_string_text,
11+ static_value:: StaticValue ,
1112} ;
1213use biome_rowan:: { AstNode , BatchMutationExt , SyntaxNodeOptionExt , TextRange , declare_node_union} ;
1314use biome_unicode_table:: is_js_ident;
@@ -50,6 +51,7 @@ declare_lint_rule! {
5051 language: "js" ,
5152 sources: & [
5253 RuleSource :: Eslint ( "dot-notation" ) ,
54+ RuleSource :: Eslint ( "no-useless-computed-key" ) ,
5355 RuleSource :: EslintTypeScript ( "dot-notation" )
5456 ] ,
5557 recommended: true ,
@@ -60,7 +62,7 @@ declare_lint_rule! {
6062
6163impl Rule for UseLiteralKeys {
6264 type Query = Ast < AnyJsMember > ;
63- type State = ( TextRange , String , bool ) ;
65+ type State = ( TextRange , JsSyntaxToken , bool ) ;
6466 type Signals = Option < Self :: State > ;
6567 type Options = ( ) ;
6668
@@ -75,29 +77,38 @@ impl Rule for UseLiteralKeys {
7577 }
7678 } ;
7779 let value = inner_expression. as_static_value ( ) ?;
78- let value = value. as_string_constant ( ) ?;
79- // `{["__proto__"]: null }` and `{"__proto__": null}`/`{"__proto__": null}`
80- // have different semantic.
81- // The first is a regular property.
82- // The second is a special property that changes the object prototype.
83- // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
84- if is_computed_member_name && value == "__proto__" {
85- return None ;
86- }
87- // A computed property `["something"]` can always be simplified to a string literal "something",
88- // unless it is a template literal inside that contains unescaped new line characters:
89- //
90- // const a = {
91- // [`line1
92- // line2`]: true
93- // }
94- //
95- if ( is_computed_member_name && !has_unescaped_new_line ( value) ) || is_js_ident ( value) {
96- return Some ( (
97- inner_expression. range ( ) ,
98- value. to_string ( ) ,
99- is_computed_member_name,
100- ) ) ;
80+ //let value = value.as_string_constant()?;
81+ match value {
82+ StaticValue :: Number ( token) => {
83+ if is_computed_member_name {
84+ return Some ( ( inner_expression. range ( ) , token, is_computed_member_name) ) ;
85+ }
86+ }
87+ StaticValue :: String ( token) => {
88+ let value = inner_string_text ( & token) ;
89+ // `{["__proto__"]: null }` and `{"__proto__": null}`/`{"__proto__": null}`
90+ // have different semantic.
91+ // The first is a regular property.
92+ // The second is a special property that changes the object prototype.
93+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
94+ if is_computed_member_name && value == "__proto__" {
95+ return None ;
96+ }
97+ // A computed property `["something"]` can always be simplified to a string literal "something",
98+ // unless it is a template literal inside that contains unescaped new line characters:
99+ //
100+ // const a = {
101+ // [`line1
102+ // line2`]: true
103+ // }
104+ //
105+ if ( is_computed_member_name && !has_unescaped_new_line ( & value) )
106+ || is_js_ident ( & value)
107+ {
108+ return Some ( ( inner_expression. range ( ) , token, is_computed_member_name) ) ;
109+ }
110+ }
111+ _ => { }
101112 }
102113 None
103114 }
@@ -121,13 +132,14 @@ impl Rule for UseLiteralKeys {
121132 ) )
122133 }
123134
124- fn action ( ctx : & RuleContext < Self > , ( _, identifier , _) : & Self :: State ) -> Option < JsRuleAction > {
135+ fn action ( ctx : & RuleContext < Self > , ( _, token , _) : & Self :: State ) -> Option < JsRuleAction > {
125136 let node = ctx. query ( ) ;
126137 let mut mutation = ctx. root ( ) . begin ( ) ;
127138 match node {
128139 AnyJsMember :: AnyJsComputedMember ( node) => {
140+ let identifier = inner_string_text ( token) ;
129141 let object = node. object ( ) . ok ( ) ?;
130- let member = make:: js_name ( make:: ident ( identifier) ) ;
142+ let member = make:: js_name ( make:: ident ( & identifier) ) ;
131143 let dot_token = node
132144 . optional_chain_token ( )
133145 . unwrap_or_else ( || make:: token ( T ! [ . ] ) ) ;
@@ -158,10 +170,15 @@ impl Rule for UseLiteralKeys {
158170 }
159171 }
160172 AnyJsMember :: JsComputedMemberName ( member) => {
161- let name_token = if ctx . as_preferred_quote ( ) . is_double ( ) {
162- make :: js_string_literal ( identifier )
173+ let name_token = if token . kind ( ) == JsSyntaxKind :: JS_NUMBER_LITERAL {
174+ token . clone ( )
163175 } else {
164- make:: js_string_literal_single_quotes ( identifier)
176+ let identifier = inner_string_text ( token) ;
177+ if ctx. as_preferred_quote ( ) . is_double ( ) {
178+ make:: js_string_literal ( & identifier)
179+ } else {
180+ make:: js_string_literal_single_quotes ( & identifier)
181+ }
165182 } ;
166183 if member. syntax ( ) . parent ( ) . kind ( ) == Some ( JsSyntaxKind :: TS_ENUM_MEMBER ) {
167184 let literal_enum_member_name = make:: ts_literal_enum_member_name ( name_token) ;
0 commit comments