diff --git a/internal/config/config.go b/internal/config/config.go index 93f305e6..bfde3da4 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -96,6 +96,7 @@ import ( "github.com/web-infra-dev/rslint/internal/rules/no_constant_condition" "github.com/web-infra-dev/rslint/internal/rules/no_constructor_return" "github.com/web-infra-dev/rslint/internal/rules/no_debugger" + "github.com/web-infra-dev/rslint/internal/rules/no_template_curly_in_string" "github.com/web-infra-dev/rslint/internal/rules/no_sparse_arrays" ) @@ -462,6 +463,7 @@ func registerAllCoreEslintRules() { GlobalRuleRegistry.Register("no-constant-condition", no_constant_condition.NoConstantConditionRule) GlobalRuleRegistry.Register("no-constructor-return", no_constructor_return.NoConstructorReturnRule) GlobalRuleRegistry.Register("no-debugger", no_debugger.NoDebuggerRule) + GlobalRuleRegistry.Register("no-template-curly-in-string", no_template_curly_in_string.NoTemplateCurlyInString) GlobalRuleRegistry.Register("no-sparse-arrays", no_sparse_arrays.NoSparseArraysRule) } diff --git a/internal/rules/no_template_curly_in_string/no_template_curly_in_string.go b/internal/rules/no_template_curly_in_string/no_template_curly_in_string.go new file mode 100644 index 00000000..7cca40e3 --- /dev/null +++ b/internal/rules/no_template_curly_in_string/no_template_curly_in_string.go @@ -0,0 +1,27 @@ +package no_template_curly_in_string + +import ( + "regexp" + + "github.com/microsoft/typescript-go/shim/ast" + "github.com/web-infra-dev/rslint/internal/rule" +) + +// https://eslint.org/docs/latest/rules/no-template-curly-in-string +var NoTemplateCurlyInString = rule.Rule{ + Name: "no-template-curly-in-string", + Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { + regex := regexp.MustCompile(`\$\{[^}]+\}`) + return rule.RuleListeners{ + ast.KindStringLiteral: func(node *ast.Node) { + expr := node.AsStringLiteral() + if regex.MatchString(expr.Text) { + ctx.ReportNode(node, rule.RuleMessage{ + Id: "unexpectedTemplateExpression", + Description: "Template literal placeholder syntax in regular strings is not allowed.", + }) + } + }, + } + }, +} diff --git a/internal/rules/no_template_curly_in_string/no_template_curly_in_string_test.go b/internal/rules/no_template_curly_in_string/no_template_curly_in_string_test.go new file mode 100644 index 00000000..02855724 --- /dev/null +++ b/internal/rules/no_template_curly_in_string/no_template_curly_in_string_test.go @@ -0,0 +1,74 @@ +package no_template_curly_in_string + +import ( + "testing" + + "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/fixtures" + "github.com/web-infra-dev/rslint/internal/rule_tester" +) + +func TestNoTemplateCurlyInString(t *testing.T) { + rule_tester.RunRuleTester( + fixtures.GetRootDir(), + "tsconfig.json", + t, + &NoTemplateCurlyInString, + // Valid cases - ported from ESLint + []rule_tester.ValidTestCase{ + {Code: "`Hello, ${name}`;"}, + {Code: "templateFunction`Hello, ${name}`;"}, + {Code: "`Hello, name`;"}, + {Code: "'Hello, name';"}, + {Code: "'Hello, ' + name;"}, + {Code: "`Hello, ${index + 1}`"}, + {Code: "`Hello, ${name + \" foo\"}`"}, + {Code: "`Hello, ${name || \"foo\"}`"}, + {Code: "`Hello, ${{foo: \"bar\"}.foo}`"}, + {Code: "'$2'"}, + {Code: "'${'"}, + {Code: "'$}'"}, + {Code: "'{foo}'"}, + {Code: `'{foo: \"bar\"}'`}, + {Code: "const number = 3"}, + }, + // Invalid cases - ported from ESLint + []rule_tester.InvalidTestCase{ + { + Code: "'Hello, ${name}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + { + Code: "'${greeting}, ${name}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + { + Code: "'Hello, ${index + 1}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + { + Code: "'Hello, ${name + \\\" foo\\\"}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + { + Code: "'Hello, ${name || \\\"foo\\\"}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + { + Code: "'Hello, ${{foo: \\\"bar\\\"}.foo}'", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedTemplateExpression", Line: 1, Column: 1}, + }, + }, + }, + ) +}