Skip to content

Commit cc4c867

Browse files
authored
feat(linter): implement noTemplateCurlyInString (#4069)
1 parent 73ae493 commit cc4c867

File tree

13 files changed

+354
-36
lines changed

13 files changed

+354
-36
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
2929

3030
### Linter
3131

32+
#### New features
33+
34+
- Add [noTemplateCurlyInString](https://biomejs.dev/linter/rules/no-template-curly-in-string/). Contributed by @fireairforce
35+
- Add [NoOctalEscape](https://biomejs.dev/linter/rules/no-octal-escape/). Contributed by @fireairforce
36+
3237
#### Bug fixes
3338

3439
- [noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/) no longer panics when it encounters malformed code. Contributed by @Conaclos
@@ -72,6 +77,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
7277

7378
- [useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) now ignores `alert` and `alertdialog` roles ([#3858](https://github.com/biomejs/biome/issues/3858)). Contributed by @Conaclos
7479

80+
- [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) don't create invaild JSX code when Fragments children contains JSX Expression and in a LogicalExpression. Contributed by @fireairforce
81+
7582
### Parser
7683

7784
## v1.9.2 (2024-09-19)

crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_configuration/src/analyzer/linter/rules.rs

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,6 +3332,10 @@ pub struct Nursery {
33323332
#[doc = "Enforce the use of String.slice() over String.substr() and String.substring()."]
33333333
#[serde(skip_serializing_if = "Option::is_none")]
33343334
pub no_substr: Option<RuleFixConfiguration<biome_js_analyze::options::NoSubstr>>,
3335+
#[doc = "Disallow template literal placeholder syntax in regular strings."]
3336+
#[serde(skip_serializing_if = "Option::is_none")]
3337+
pub no_template_curly_in_string:
3338+
Option<RuleConfiguration<biome_js_analyze::options::NoTemplateCurlyInString>>,
33353339
#[doc = "Disallow unknown pseudo-class selectors."]
33363340
#[serde(skip_serializing_if = "Option::is_none")]
33373341
pub no_unknown_pseudo_class:
@@ -3429,6 +3433,7 @@ impl Nursery {
34293433
"noSecrets",
34303434
"noStaticElementInteractions",
34313435
"noSubstr",
3436+
"noTemplateCurlyInString",
34323437
"noUnknownPseudoClass",
34333438
"noUnknownPseudoElement",
34343439
"noUselessEscapeInRegex",
@@ -3464,13 +3469,13 @@ impl Nursery {
34643469
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]),
34653470
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]),
34663471
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]),
3467-
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]),
34683472
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]),
34693473
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]),
3470-
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]),
3471-
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]),
3474+
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]),
3475+
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]),
34723476
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]),
3473-
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]),
3477+
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]),
3478+
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]),
34743479
];
34753480
const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[
34763481
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]),
@@ -3506,6 +3511,7 @@ impl Nursery {
35063511
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]),
35073512
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]),
35083513
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]),
3514+
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]),
35093515
];
35103516
#[doc = r" Retrieves the recommended rules"]
35113517
pub(crate) fn is_recommended_true(&self) -> bool {
@@ -3607,86 +3613,91 @@ impl Nursery {
36073613
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
36083614
}
36093615
}
3610-
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
3616+
if let Some(rule) = self.no_template_curly_in_string.as_ref() {
36113617
if rule.is_enabled() {
36123618
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
36133619
}
36143620
}
3615-
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
3621+
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
36163622
if rule.is_enabled() {
36173623
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
36183624
}
36193625
}
3620-
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
3626+
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
36213627
if rule.is_enabled() {
36223628
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
36233629
}
36243630
}
3625-
if let Some(rule) = self.no_value_at_rule.as_ref() {
3631+
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
36263632
if rule.is_enabled() {
36273633
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
36283634
}
36293635
}
3630-
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
3636+
if let Some(rule) = self.no_value_at_rule.as_ref() {
36313637
if rule.is_enabled() {
36323638
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]));
36333639
}
36343640
}
3635-
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
3641+
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
36363642
if rule.is_enabled() {
36373643
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]));
36383644
}
36393645
}
3640-
if let Some(rule) = self.use_component_export_only_modules.as_ref() {
3646+
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
36413647
if rule.is_enabled() {
36423648
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]));
36433649
}
36443650
}
3645-
if let Some(rule) = self.use_consistent_curly_braces.as_ref() {
3651+
if let Some(rule) = self.use_component_export_only_modules.as_ref() {
36463652
if rule.is_enabled() {
36473653
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]));
36483654
}
36493655
}
3650-
if let Some(rule) = self.use_consistent_member_accessibility.as_ref() {
3656+
if let Some(rule) = self.use_consistent_curly_braces.as_ref() {
36513657
if rule.is_enabled() {
36523658
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]));
36533659
}
36543660
}
3655-
if let Some(rule) = self.use_deprecated_reason.as_ref() {
3661+
if let Some(rule) = self.use_consistent_member_accessibility.as_ref() {
36563662
if rule.is_enabled() {
36573663
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]));
36583664
}
36593665
}
3660-
if let Some(rule) = self.use_explicit_function_return_type.as_ref() {
3666+
if let Some(rule) = self.use_deprecated_reason.as_ref() {
36613667
if rule.is_enabled() {
36623668
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]));
36633669
}
36643670
}
3665-
if let Some(rule) = self.use_import_restrictions.as_ref() {
3671+
if let Some(rule) = self.use_explicit_function_return_type.as_ref() {
36663672
if rule.is_enabled() {
36673673
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]));
36683674
}
36693675
}
3670-
if let Some(rule) = self.use_sorted_classes.as_ref() {
3676+
if let Some(rule) = self.use_import_restrictions.as_ref() {
36713677
if rule.is_enabled() {
36723678
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]));
36733679
}
36743680
}
3675-
if let Some(rule) = self.use_strict_mode.as_ref() {
3681+
if let Some(rule) = self.use_sorted_classes.as_ref() {
36763682
if rule.is_enabled() {
36773683
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]));
36783684
}
36793685
}
3680-
if let Some(rule) = self.use_trim_start_end.as_ref() {
3686+
if let Some(rule) = self.use_strict_mode.as_ref() {
36813687
if rule.is_enabled() {
36823688
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]));
36833689
}
36843690
}
3685-
if let Some(rule) = self.use_valid_autocomplete.as_ref() {
3691+
if let Some(rule) = self.use_trim_start_end.as_ref() {
36863692
if rule.is_enabled() {
36873693
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]));
36883694
}
36893695
}
3696+
if let Some(rule) = self.use_valid_autocomplete.as_ref() {
3697+
if rule.is_enabled() {
3698+
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]));
3699+
}
3700+
}
36903701
index_set
36913702
}
36923703
pub(crate) fn get_disabled_rules(&self) -> FxHashSet<RuleFilter<'static>> {
@@ -3776,86 +3787,91 @@ impl Nursery {
37763787
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
37773788
}
37783789
}
3779-
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
3790+
if let Some(rule) = self.no_template_curly_in_string.as_ref() {
37803791
if rule.is_disabled() {
37813792
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
37823793
}
37833794
}
3784-
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
3795+
if let Some(rule) = self.no_unknown_pseudo_class.as_ref() {
37853796
if rule.is_disabled() {
37863797
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
37873798
}
37883799
}
3789-
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
3800+
if let Some(rule) = self.no_unknown_pseudo_element.as_ref() {
37903801
if rule.is_disabled() {
37913802
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
37923803
}
37933804
}
3794-
if let Some(rule) = self.no_value_at_rule.as_ref() {
3805+
if let Some(rule) = self.no_useless_escape_in_regex.as_ref() {
37953806
if rule.is_disabled() {
37963807
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
37973808
}
37983809
}
3799-
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
3810+
if let Some(rule) = self.no_value_at_rule.as_ref() {
38003811
if rule.is_disabled() {
38013812
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]));
38023813
}
38033814
}
3804-
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
3815+
if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() {
38053816
if rule.is_disabled() {
38063817
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]));
38073818
}
38083819
}
3809-
if let Some(rule) = self.use_component_export_only_modules.as_ref() {
3820+
if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() {
38103821
if rule.is_disabled() {
38113822
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]));
38123823
}
38133824
}
3814-
if let Some(rule) = self.use_consistent_curly_braces.as_ref() {
3825+
if let Some(rule) = self.use_component_export_only_modules.as_ref() {
38153826
if rule.is_disabled() {
38163827
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]));
38173828
}
38183829
}
3819-
if let Some(rule) = self.use_consistent_member_accessibility.as_ref() {
3830+
if let Some(rule) = self.use_consistent_curly_braces.as_ref() {
38203831
if rule.is_disabled() {
38213832
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]));
38223833
}
38233834
}
3824-
if let Some(rule) = self.use_deprecated_reason.as_ref() {
3835+
if let Some(rule) = self.use_consistent_member_accessibility.as_ref() {
38253836
if rule.is_disabled() {
38263837
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]));
38273838
}
38283839
}
3829-
if let Some(rule) = self.use_explicit_function_return_type.as_ref() {
3840+
if let Some(rule) = self.use_deprecated_reason.as_ref() {
38303841
if rule.is_disabled() {
38313842
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]));
38323843
}
38333844
}
3834-
if let Some(rule) = self.use_import_restrictions.as_ref() {
3845+
if let Some(rule) = self.use_explicit_function_return_type.as_ref() {
38353846
if rule.is_disabled() {
38363847
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]));
38373848
}
38383849
}
3839-
if let Some(rule) = self.use_sorted_classes.as_ref() {
3850+
if let Some(rule) = self.use_import_restrictions.as_ref() {
38403851
if rule.is_disabled() {
38413852
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]));
38423853
}
38433854
}
3844-
if let Some(rule) = self.use_strict_mode.as_ref() {
3855+
if let Some(rule) = self.use_sorted_classes.as_ref() {
38453856
if rule.is_disabled() {
38463857
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]));
38473858
}
38483859
}
3849-
if let Some(rule) = self.use_trim_start_end.as_ref() {
3860+
if let Some(rule) = self.use_strict_mode.as_ref() {
38503861
if rule.is_disabled() {
38513862
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]));
38523863
}
38533864
}
3854-
if let Some(rule) = self.use_valid_autocomplete.as_ref() {
3865+
if let Some(rule) = self.use_trim_start_end.as_ref() {
38553866
if rule.is_disabled() {
38563867
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]));
38573868
}
38583869
}
3870+
if let Some(rule) = self.use_valid_autocomplete.as_ref() {
3871+
if rule.is_disabled() {
3872+
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]));
3873+
}
3874+
}
38593875
index_set
38603876
}
38613877
#[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"]
@@ -3960,6 +3976,10 @@ impl Nursery {
39603976
.no_substr
39613977
.as_ref()
39623978
.map(|conf| (conf.level(), conf.get_options())),
3979+
"noTemplateCurlyInString" => self
3980+
.no_template_curly_in_string
3981+
.as_ref()
3982+
.map(|conf| (conf.level(), conf.get_options())),
39633983
"noUnknownPseudoClass" => self
39643984
.no_unknown_pseudo_class
39653985
.as_ref()

crates/biome_diagnostics_categories/src/categories.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ define_categories! {
161161
"lint/nursery/noShorthandPropertyOverrides": "https://biomejs.dev/linter/rules/no-shorthand-property-overrides",
162162
"lint/nursery/noStaticElementInteractions": "https://biomejs.dev/linter/rules/no-static-element-interactions",
163163
"lint/nursery/noSubstr": "https://biomejs.dev/linter/rules/no-substr",
164+
"lint/nursery/noTemplateCurlyInString": "https://biomejs.dev/linter/rules/no-template-curly-in-string",
164165
"lint/nursery/noUndeclaredDependencies": "https://biomejs.dev/linter/rules/no-undeclared-dependencies",
165166
"lint/nursery/noUnknownFunction": "https://biomejs.dev/linter/rules/no-unknown-function",
166167
"lint/nursery/noUnknownMediaFeatureName": "https://biomejs.dev/linter/rules/no-unknown-media-feature-name",

crates/biome_js_analyze/src/lint/nursery.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod no_restricted_types;
1616
pub mod no_secrets;
1717
pub mod no_static_element_interactions;
1818
pub mod no_substr;
19+
pub mod no_template_curly_in_string;
1920
pub mod no_useless_escape_in_regex;
2021
pub mod use_adjacent_overload_signatures;
2122
pub mod use_aria_props_supported_by_role;
@@ -47,6 +48,7 @@ declare_lint_group! {
4748
self :: no_secrets :: NoSecrets ,
4849
self :: no_static_element_interactions :: NoStaticElementInteractions ,
4950
self :: no_substr :: NoSubstr ,
51+
self :: no_template_curly_in_string :: NoTemplateCurlyInString ,
5052
self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex ,
5153
self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures ,
5254
self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole ,

0 commit comments

Comments
 (0)