mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): no_template_curly_in_string (#2763)
Rule detail: https://eslint.org/docs/latest/rules/no-template-curly-in-string At first I implemented it with regex, but I think you are trying to avoid it for performance reasons, so I did it by searching for the chars. I had some problems showing the span in the case of 'Hello, ${{foo: "bar"}.foo}' I think it turned out more or less well, however, if you think it can be done in another way I am willing to do it . --------- Co-authored-by: j.buendia <j.buendia>
This commit is contained in:
parent
134e15e657
commit
ac813a60f6
3 changed files with 164 additions and 0 deletions
|
|
@ -96,6 +96,7 @@ mod eslint {
|
|||
pub mod no_setter_return;
|
||||
pub mod no_shadow_restricted_names;
|
||||
pub mod no_sparse_arrays;
|
||||
pub mod no_template_curly_in_string;
|
||||
pub mod no_ternary;
|
||||
pub mod no_this_before_super;
|
||||
pub mod no_undef;
|
||||
|
|
@ -369,6 +370,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::max_params,
|
||||
eslint::no_ternary,
|
||||
eslint::no_this_before_super,
|
||||
eslint::no_template_curly_in_string,
|
||||
eslint::no_array_constructor,
|
||||
eslint::no_async_promise_executor,
|
||||
eslint::no_bitwise,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
use oxc_ast::AstKind;
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::Error,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("eslint(no-template-curly-in-string): Unexpected template string expression")]
|
||||
#[diagnostic(
|
||||
severity(warning),
|
||||
help("Disallow template literal placeholder syntax in regular strings")
|
||||
)]
|
||||
struct NoTemplateCurlyInStringDiagnostic(#[label] pub Span);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoTemplateCurlyInString;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
/// Disallow template literal placeholder syntax in regular strings
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// ECMAScript 6 allows programmers to create strings containing variable or expressions using template literals, instead of string concatenation, by writing expressions like ${variable} between two backtick quotes (`). It can be easy to use the wrong quotes when wanting to use template literals, by writing "${variable}", and end up with the literal value "${variable}" instead of a string containing the value of the injected expressions.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// /*eslint no-template-curly-in-string: "error"*/
|
||||
/// "Hello ${name}!";
|
||||
/// 'Hello ${name}!';
|
||||
/// "Time: ${12 * 60 * 60 * 1000}";
|
||||
/// ```
|
||||
NoTemplateCurlyInString,
|
||||
style
|
||||
);
|
||||
|
||||
impl Rule for NoTemplateCurlyInString {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::StringLiteral(literal) = node.kind() {
|
||||
let text = literal.value.as_str();
|
||||
if let Some(start_index) = text.find("${") {
|
||||
let mut open_braces_count = 0;
|
||||
let mut end_index = None;
|
||||
|
||||
for (i, c) in text[start_index..].char_indices() {
|
||||
let real_index = start_index + i;
|
||||
if c == '{' {
|
||||
open_braces_count += 1;
|
||||
} else if c == '}' && open_braces_count > 0 {
|
||||
open_braces_count -= 1;
|
||||
if open_braces_count == 0 {
|
||||
end_index = Some(real_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(end_index) = end_index {
|
||||
let literal_span_start = literal.span.start + 1;
|
||||
let match_start = u32::try_from(start_index)
|
||||
.expect("Conversion from usize to u32 failed for match_start");
|
||||
let match_end = u32::try_from(end_index + 1)
|
||||
.expect("Conversion from usize to u32 failed for match_end");
|
||||
ctx.diagnostic(NoTemplateCurlyInStringDiagnostic(Span::new(
|
||||
literal_span_start + match_start,
|
||||
literal_span_start + match_end,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
"`Hello, ${name}`;",
|
||||
"templateFunction`Hello, ${name}`;",
|
||||
"`Hello, name`;",
|
||||
"'Hello, name';",
|
||||
"'Hello, ' + name;",
|
||||
"`Hello, ${index + 1}`",
|
||||
r#"`Hello, ${name + " foo"}`"#,
|
||||
r#"`Hello, ${name || "foo"}`"#,
|
||||
r#"`Hello, ${{foo: "bar"}.foo}`"#,
|
||||
"'$2'",
|
||||
"'${'",
|
||||
"'$}'",
|
||||
"'{foo}'",
|
||||
r#"'{foo: "bar"}'"#,
|
||||
"const number = 3",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
"'Hello, ${name}'",
|
||||
r#""Hello, ${name}""#,
|
||||
"'${greeting}, ${name}'",
|
||||
"'Hello, ${index + 1}'",
|
||||
r#"'Hello, ${name + " foo"}'"#,
|
||||
r#"'Hello, ${name || "foo"}'"#,
|
||||
r#"'Hello, ${{foo: "bar"}.foo}'"#,
|
||||
];
|
||||
|
||||
Tester::new(NoTemplateCurlyInString::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: no_template_curly_in_string
|
||||
---
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ 'Hello, ${name}'
|
||||
· ───────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ "Hello, ${name}"
|
||||
· ───────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:2]
|
||||
1 │ '${greeting}, ${name}'
|
||||
· ───────────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ 'Hello, ${index + 1}'
|
||||
· ────────────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ 'Hello, ${name + " foo"}'
|
||||
· ────────────────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ 'Hello, ${name || "foo"}'
|
||||
· ────────────────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
|
||||
⚠ eslint(no-template-curly-in-string): Unexpected template string expression
|
||||
╭─[no_template_curly_in_string.tsx:1:9]
|
||||
1 │ 'Hello, ${{foo: "bar"}.foo}'
|
||||
· ───────────────────
|
||||
╰────
|
||||
help: Disallow template literal placeholder syntax in regular strings
|
||||
Loading…
Reference in a new issue