Skip to content

Commit 57c0efa

Browse files
authored
feat(camelcase): allow for non-declaration object destructuring and named import when has no alias (#1187)
1 parent cbc93b7 commit 57c0efa

File tree

1 file changed

+92
-87
lines changed

1 file changed

+92
-87
lines changed

src/rules/camelcase.rs

Lines changed: 92 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use super::{Context, LintRule};
33
use crate::handler::{Handler, Traverse};
44
use crate::swc_util::StringRepr;
55

6+
use deno_ast::view::{Node, NodeKind, NodeTrait};
67
use deno_ast::{view as ast_view, SourceRange, SourceRanged};
78
use once_cell::sync::Lazy;
89
use regex::{Captures, Regex};
@@ -115,6 +116,7 @@ enum IdentToCheck {
115116
key_name: String,
116117
value_name: Option<String>,
117118
has_default: bool,
119+
is_destructuring: bool,
118120
},
119121
/// Local name and imported name in named import, for example:
120122
///
@@ -177,6 +179,7 @@ impl IdentToCheck {
177179
key_name: &K,
178180
value_name: Option<&V>,
179181
has_default: bool,
182+
is_destructuring: bool,
180183
) -> Self
181184
where
182185
K: AsRef<str>,
@@ -186,6 +189,7 @@ impl IdentToCheck {
186189
key_name: key_name.as_ref().to_string(),
187190
value_name: value_name.map(|v| v.as_ref().to_string()),
188191
has_default,
192+
is_destructuring,
189193
}
190194
}
191195

@@ -286,12 +290,20 @@ impl IdentToCheck {
286290
key_name,
287291
value_name,
288292
has_default,
293+
is_destructuring: in_var_decl,
289294
} => {
290-
if let Some(value_name) = value_name {
295+
let rename_name = if let Some(value_name) = value_name {
296+
Some(value_name)
297+
} else if *in_var_decl {
298+
None
299+
} else {
300+
Some(key_name)
301+
};
302+
if let Some(name) = rename_name {
291303
return format!(
292304
"Consider renaming `{}` to `{}`",
293-
value_name,
294-
to_camelcase(value_name),
305+
name,
306+
to_camelcase(name),
295307
);
296308
}
297309

@@ -427,6 +439,7 @@ impl CamelcaseHandler {
427439
&key.string_repr().unwrap_or_else(|| "[KEY]".to_string()),
428440
Some(&value_ident.id.inner),
429441
false,
442+
pat_in_var_declarator(pat.into()),
430443
),
431444
);
432445
}
@@ -440,6 +453,7 @@ impl CamelcaseHandler {
440453
&key.string_repr().unwrap_or_else(|| "[KEY]".to_string()),
441454
Some(&value_ident.id.inner),
442455
true,
456+
pat_in_var_declarator(pat.into()),
443457
),
444458
);
445459
}
@@ -453,14 +467,18 @@ impl CamelcaseHandler {
453467
..
454468
}) => {
455469
let has_default = value.is_some();
456-
self.check_ident(
457-
key,
458-
IdentToCheck::object_pat::<&str, &str>(
459-
&key.inner.as_ref(),
460-
None,
461-
has_default,
462-
),
463-
);
470+
let in_var_declarator = pat_in_var_declarator(pat.into());
471+
if !in_var_declarator {
472+
self.check_ident(
473+
key,
474+
IdentToCheck::object_pat::<&str, &str>(
475+
&key.inner.as_ref(),
476+
None,
477+
has_default,
478+
in_var_declarator,
479+
),
480+
);
481+
}
464482
}
465483
ast_view::ObjectPatProp::Rest(ast_view::RestPat {
466484
ref arg,
@@ -587,16 +605,18 @@ impl Handler for CamelcaseHandler {
587605
let ast_view::ImportNamedSpecifier {
588606
local, imported, ..
589607
} = import_named_specifier;
590-
self.check_ident(
591-
local,
592-
IdentToCheck::named_import(
593-
local.inner,
594-
imported.as_ref().map(|i| match i {
595-
ast_view::ModuleExportName::Ident(ident) => ident.sym(),
596-
ast_view::ModuleExportName::Str(str) => str.value(),
597-
}),
598-
),
599-
);
608+
if let Some(imported) = &imported {
609+
self.check_ident(
610+
local,
611+
IdentToCheck::named_import(
612+
local.inner,
613+
Some(match imported {
614+
ast_view::ModuleExportName::Ident(ident) => ident.sym(),
615+
ast_view::ModuleExportName::Str(str) => str.value(),
616+
}),
617+
),
618+
);
619+
}
600620
}
601621

602622
fn import_default_specifier(
@@ -719,6 +739,28 @@ impl Handler for CamelcaseHandler {
719739
}
720740
}
721741

742+
fn pat_in_var_declarator(pat: Node) -> bool {
743+
for ancestor in pat.ancestors() {
744+
match ancestor.kind() {
745+
NodeKind::VarDeclarator => {
746+
return true;
747+
}
748+
NodeKind::ArrayPat
749+
| NodeKind::ObjectPat
750+
| NodeKind::AssignPat
751+
| NodeKind::AssignPatProp
752+
| NodeKind::RestPat
753+
| NodeKind::KeyValuePatProp => {
754+
// keep going
755+
}
756+
_ => {
757+
return false;
758+
}
759+
}
760+
}
761+
false
762+
}
763+
722764
#[cfg(test)]
723765
mod tests {
724766
use super::*;
@@ -788,6 +830,7 @@ mod tests {
788830
key_name: s("foo_bar"),
789831
value_name: None,
790832
has_default: false,
833+
is_destructuring: true,
791834
},
792835
"Consider replacing `{ foo_bar }` with `{ foo_bar: fooBar }`",
793836
),
@@ -796,6 +839,7 @@ mod tests {
796839
key_name: s("foo_bar"),
797840
value_name: Some(s("snake_case")),
798841
has_default: false,
842+
is_destructuring: true,
799843
},
800844
"Consider renaming `snake_case` to `snakeCase`",
801845
),
@@ -804,14 +848,26 @@ mod tests {
804848
key_name: s("foo_bar"),
805849
value_name: None,
806850
has_default: true,
851+
is_destructuring: true,
807852
},
808853
"Consider replacing `{ foo_bar = .. }` with `{ foo_bar: fooBar = .. }`",
809854
),
855+
(
856+
IdentToCheck::ObjectPat {
857+
key_name: s("foo_bar"),
858+
value_name: None,
859+
has_default: true,
860+
is_destructuring: false,
861+
},
862+
// not destructuring, so suggest a rename
863+
"Consider renaming `foo_bar` to `fooBar`",
864+
),
810865
(
811866
IdentToCheck::ObjectPat {
812867
key_name: s("foo_bar"),
813868
value_name: Some(s("snake_case")),
814869
has_default: true,
870+
is_destructuring: true,
815871
},
816872
"Consider renaming `snake_case` to `snakeCase`",
817873
),
@@ -869,9 +925,14 @@ mod tests {
869925
r#"var { category_id: category } = query;"#,
870926
r#"var { _leading } = query;"#,
871927
r#"var { trailing_ } = query;"#,
928+
r#"var { or_middle } = query;"#,
929+
r#"var { category_id = 1 } = query;"#,
930+
r#"var { category_id: { property_test } } = query;"#,
931+
r#"const { no_camelcased = false } = bar;"#,
872932
r#"import { camelCased } from "external module";"#,
873933
r#"import { _leading } from "external module";"#,
874934
r#"import { trailing_ } from "external module";"#,
935+
r#"import { or_middle } from "external module";"#,
875936
r#"import { no_camelcased as camelCased } from "external-module";"#,
876937
r#"import { no_camelcased as _leading } from "external-module";"#,
877938
r#"import { no_camelcased as trailing_ } from "external-module";"#,
@@ -989,48 +1050,20 @@ mod tests {
9891050
hint: "Consider renaming `category_alias` to `categoryAlias`",
9901051
}
9911052
],
992-
r#"var { category_id } = query;"#: [
993-
{
994-
col: 6,
995-
message: "Identifier 'category_id' is not in camel case.",
996-
hint: "Consider replacing `{ category_id }` with `{ category_id: categoryId }`",
997-
}
998-
],
9991053
r#"var { category_id: category_id } = query;"#: [
10001054
{
10011055
col: 19,
10021056
message: "Identifier 'category_id' is not in camel case.",
10031057
hint: "Consider renaming `category_id` to `categoryId`",
10041058
}
10051059
],
1006-
r#"var { category_id = 1 } = query;"#: [
1007-
{
1008-
col: 6,
1009-
message: "Identifier 'category_id' is not in camel case.",
1010-
hint: "Consider replacing `{ category_id = .. }` with `{ category_id: categoryId = .. }`",
1011-
}
1012-
],
1013-
r#"import no_camelcased from "external-module";"#: [
1014-
{
1015-
col: 7,
1016-
message: "Identifier 'no_camelcased' is not in camel case.",
1017-
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
1018-
}
1019-
],
10201060
r#"import * as no_camelcased from "external-module";"#: [
10211061
{
10221062
col: 12,
10231063
message: "Identifier 'no_camelcased' is not in camel case.",
10241064
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
10251065
}
10261066
],
1027-
r#"import { no_camelcased } from "external-module";"#: [
1028-
{
1029-
col: 9,
1030-
message: "Identifier 'no_camelcased' is not in camel case.",
1031-
hint: "Consider replacing `{ no_camelcased }` with `{ no_camelcased as noCamelcased }`",
1032-
}
1033-
],
10341067
r#"import { no_camelcased as no_camel_cased } from "external module";"#: [
10351068
{
10361069
col: 26,
@@ -1045,27 +1078,6 @@ mod tests {
10451078
hint: "Consider renaming `no_camel_cased` to `noCamelCased`",
10461079
}
10471080
],
1048-
r#"import { camelCased, no_camelcased } from "external-module";"#: [
1049-
{
1050-
col: 21,
1051-
message: "Identifier 'no_camelcased' is not in camel case.",
1052-
hint: "Consider replacing `{ no_camelcased }` with `{ no_camelcased as noCamelcased }`",
1053-
}
1054-
],
1055-
r#"import { no_camelcased as camelCased, another_no_camelcased } from "external-module";"#: [
1056-
{
1057-
col: 38,
1058-
message: "Identifier 'another_no_camelcased' is not in camel case.",
1059-
hint: "Consider replacing `{ another_no_camelcased }` with `{ another_no_camelcased as anotherNoCamelcased }`",
1060-
}
1061-
],
1062-
r#"import camelCased, { no_camelcased } from "external-module";"#: [
1063-
{
1064-
col: 21,
1065-
message: "Identifier 'no_camelcased' is not in camel case.",
1066-
hint: "Consider replacing `{ no_camelcased }` with `{ no_camelcased as noCamelcased }`",
1067-
}
1068-
],
10691081
r#"import no_camelcased, { another_no_camelcased as camelCased } from "external-module";"#: [
10701082
{
10711083
col: 7,
@@ -1098,14 +1110,14 @@ mod tests {
10981110
{
10991111
col: 15,
11001112
message: "Identifier 'no_camelcased' is not in camel case.",
1101-
hint: "Consider replacing `{ no_camelcased }` with `{ no_camelcased: noCamelcased }`",
1113+
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
11021114
}
11031115
],
11041116
r#"function foo({ no_camelcased = 'default value' }) {};"#: [
11051117
{
11061118
col: 15,
11071119
message: "Identifier 'no_camelcased' is not in camel case.",
1108-
hint: "Consider replacing `{ no_camelcased = .. }` with `{ no_camelcased: noCamelcased = .. }`",
1120+
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
11091121
}
11101122
],
11111123
r#"const no_camelcased = 0; function foo({ camelcased_value = no_camelcased }) {}"#: [
@@ -1117,7 +1129,7 @@ mod tests {
11171129
{
11181130
col: 40,
11191131
message: "Identifier 'camelcased_value' is not in camel case.",
1120-
hint: "Consider replacing `{ camelcased_value = .. }` with `{ camelcased_value: camelcasedValue = .. }`",
1132+
hint: "Consider renaming `camelcased_value` to `camelcasedValue`",
11211133
}
11221134
],
11231135
r#"const { bar: no_camelcased } = foo;"#: [
@@ -1141,25 +1153,18 @@ mod tests {
11411153
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
11421154
}
11431155
],
1144-
r#"var { foo: bar_baz = 1 } = quz;"#: [
1145-
{
1146-
col: 11,
1147-
message: "Identifier 'bar_baz' is not in camel case.",
1148-
hint: "Consider renaming `bar_baz` to `barBaz`",
1149-
}
1150-
],
1151-
r#"const { no_camelcased = false } = bar;"#: [
1156+
r#"function foo({ isCamelcased: { no_camelcased } }) {};"#: [
11521157
{
1153-
col: 8,
1158+
col: 31,
11541159
message: "Identifier 'no_camelcased' is not in camel case.",
1155-
hint: "Consider replacing `{ no_camelcased = .. }` with `{ no_camelcased: noCamelcased = .. }`",
1160+
hint: "Consider renaming `no_camelcased` to `noCamelcased`",
11561161
}
11571162
],
1158-
r#"const { no_camelcased = foo_bar } = bar;"#: [
1163+
r#"var { foo: bar_baz = 1 } = quz;"#: [
11591164
{
1160-
col: 8,
1161-
message: "Identifier 'no_camelcased' is not in camel case.",
1162-
hint: "Consider replacing `{ no_camelcased = .. }` with `{ no_camelcased: noCamelcased = .. }`",
1165+
col: 11,
1166+
message: "Identifier 'bar_baz' is not in camel case.",
1167+
hint: "Consider renaming `bar_baz` to `barBaz`",
11631168
}
11641169
],
11651170
r#"const f = function no_camelcased() {};"#: [

0 commit comments

Comments
 (0)