Skip to content

Commit f90cc11

Browse files
committed
Extract rule: template-no-unnecessary-curly-strings
1 parent a0c4d1a commit f90cc11

4 files changed

Lines changed: 190 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,12 @@ rules in templates can be disabled with eslint directives with mustache or html
431431
| [no-unnecessary-service-injection-argument](docs/rules/no-unnecessary-service-injection-argument.md) | disallow unnecessary argument when injecting services | | 🔧 | |
432432
| [no-unused-services](docs/rules/no-unused-services.md) | disallow unused service injections (see rule doc for limitations) | | | 💡 |
433433

434+
### Style
435+
436+
| Name | Description | 💼 | 🔧 | 💡 |
437+
| :------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- | :- | :- |
438+
| [template-no-unnecessary-curly-strings](docs/rules/template-no-unnecessary-curly-strings.md) | disallow unnecessary curly braces in string interpolations | | 🔧 | |
439+
434440
### Stylistic Issues
435441

436442
| Name                           | Description | 💼 | 🔧 | 💡 |
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# ember/template-no-unnecessary-curly-strings
2+
3+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
4+
5+
<!-- end auto-generated rule header -->
6+
7+
Strings need not be wrapped in curly braces (mustache expressions).
8+
9+
## Rule Details
10+
11+
This rule detects unnecessary mustache expressions wrapping simple string literals. It checks both **attribute values** and **text content**.
12+
13+
## Examples
14+
15+
This rule **forbids** the following:
16+
17+
```gjs
18+
<template><FooBar class={{'btn'}} /></template>
19+
```
20+
21+
```gjs
22+
<template><FooBar class="btn">{{"Hello"}}</FooBar></template>
23+
```
24+
25+
This rule **allows** the following:
26+
27+
```gjs
28+
<template><FooBar class='btn' /></template>
29+
```
30+
31+
```gjs
32+
<template><FooBar class='btn'>Hello</FooBar></template>
33+
```
34+
35+
## References
36+
37+
- [Handlebars expressions](https://handlebarsjs.com/guide/expressions.html)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'suggestion',
5+
docs: {
6+
description: 'disallow unnecessary curly braces in string interpolations',
7+
category: 'Style',
8+
recommendedGjs: false,
9+
recommendedGts: false,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-unnecessary-curly-strings.md',
11+
templateMode: 'both',
12+
},
13+
fixable: 'code',
14+
schema: [],
15+
messages: { unnecessary: 'Unnecessary curly braces in string.' },
16+
originallyFrom: {
17+
name: 'ember-template-lint',
18+
rule: 'lib/rules/no-unnecessary-curly-strings.js',
19+
docs: 'docs/rule/no-unnecessary-curly-strings.md',
20+
tests: 'test/unit/rules/no-unnecessary-curly-strings-test.js',
21+
},
22+
},
23+
create(context) {
24+
const sourceCode = context.sourceCode || context.getSourceCode();
25+
26+
return {
27+
GlimmerAttrNode(node) {
28+
if (
29+
node.value?.type === 'GlimmerMustacheStatement' &&
30+
node.value.path?.type === 'GlimmerStringLiteral'
31+
) {
32+
const strNode = node.value.path;
33+
const strValue = strNode.value || strNode.original;
34+
context.report({
35+
node: node.value,
36+
messageId: 'unnecessary',
37+
fix(fixer) {
38+
const strSource = sourceCode.getText(strNode);
39+
const quoteChar = strSource[0] === "'" ? "'" : '"';
40+
return fixer.replaceText(node.value, `${quoteChar}${strValue}${quoteChar}`);
41+
},
42+
});
43+
}
44+
},
45+
GlimmerMustacheStatement(node) {
46+
if (node.path?.type === 'GlimmerStringLiteral' && node.parent?.type !== 'GlimmerAttrNode') {
47+
const strValue = node.path.value || node.path.original;
48+
context.report({
49+
node,
50+
messageId: 'unnecessary',
51+
fix(fixer) {
52+
return fixer.replaceText(node, strValue);
53+
},
54+
});
55+
}
56+
},
57+
};
58+
},
59+
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const rule = require('../../../lib/rules/template-no-unnecessary-curly-strings');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const ruleTester = new RuleTester({
5+
parser: require.resolve('ember-eslint-parser'),
6+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
7+
});
8+
ruleTester.run('template-no-unnecessary-curly-strings', rule, {
9+
valid: [
10+
'<template><div class="foo"></div></template>',
11+
'<template><FooBar class="btn" /></template>',
12+
'<template>{{foo}}</template>',
13+
'<template>{{(foo)}}</template>',
14+
'<template>{{this.calculate 1 2 op="add"}}</template>',
15+
'<template>{{get address part}}</template>',
16+
'<template>foo</template>',
17+
'<template>"foo"</template>',
18+
'<template><FooBar value=12345 /></template>',
19+
'<template><FooBar value=null /></template>',
20+
'<template><FooBar value=true /></template>',
21+
'<template><FooBar value=undefined /></template>',
22+
'<template><FooBar value={{12345}} /></template>',
23+
'<template><FooBar value={{null}} /></template>',
24+
'<template><FooBar value={{true}} /></template>',
25+
'<template><FooBar value={{undefined}} /></template>',
26+
],
27+
invalid: [
28+
{
29+
code: '<template><div class={{"foo"}}></div></template>',
30+
output: '<template><div class="foo"></div></template>',
31+
errors: [{ messageId: 'unnecessary' }],
32+
},
33+
34+
{
35+
code: '<template><FooBar class={{"btn"}} @fooArg={{\'barbaz\'}} /></template>',
36+
output: '<template><FooBar class="btn" @fooArg=\'barbaz\' /></template>',
37+
errors: [{ messageId: 'unnecessary' }, { messageId: 'unnecessary' }],
38+
},
39+
{
40+
code: '<template><FooBar class="btn">{{"Foo"}}</FooBar></template>',
41+
output: '<template><FooBar class="btn">Foo</FooBar></template>',
42+
errors: [{ messageId: 'unnecessary' }],
43+
},
44+
],
45+
});
46+
47+
const hbsRuleTester = new RuleTester({
48+
parser: require.resolve('ember-eslint-parser/hbs'),
49+
parserOptions: {
50+
ecmaVersion: 2022,
51+
sourceType: 'module',
52+
},
53+
});
54+
55+
hbsRuleTester.run('template-no-unnecessary-curly-strings', rule, {
56+
valid: [
57+
'<FooBar class="btn" />',
58+
'{{foo}}',
59+
'{{(foo)}}',
60+
'{{this.calculate 1 2 op="add"}}',
61+
'{{get address part}}',
62+
'foo',
63+
'"foo"',
64+
'<FooBar value=12345 />',
65+
'<FooBar value=null />',
66+
'<FooBar value=true />',
67+
'<FooBar value=undefined />',
68+
'<FooBar value={{12345}} />',
69+
'<FooBar value={{null}} />',
70+
'<FooBar value={{true}} />',
71+
'<FooBar value={{undefined}} />',
72+
],
73+
invalid: [
74+
{
75+
code: '<FooBar class={{"btn"}} @fooArg={{\'barbaz\'}} />',
76+
output: '<FooBar class="btn" @fooArg=\'barbaz\' />',
77+
errors: [
78+
{ message: 'Unnecessary curly braces in string.' },
79+
{ message: 'Unnecessary curly braces in string.' },
80+
],
81+
},
82+
{
83+
code: '<FooBar class="btn">{{"Foo"}}</FooBar>',
84+
output: '<FooBar class="btn">Foo</FooBar>',
85+
errors: [{ message: 'Unnecessary curly braces in string.' }],
86+
},
87+
],
88+
});

0 commit comments

Comments
 (0)