Skip to content

Commit 4b103e6

Browse files
Merge pull request #2585 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-unnecessary-concat
Extract rule: template-no-unnecessary-concat
2 parents 3142567 + e0654c0 commit 4b103e6

4 files changed

Lines changed: 153 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ rules in templates can be disabled with eslint directives with mustache or html
256256
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
257257
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
258258
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
259+
| [template-no-unnecessary-concat](docs/rules/template-no-unnecessary-concat.md) | disallow unnecessary string concatenation | | 🔧 | |
259260
| [template-no-unnecessary-curly-parens](docs/rules/template-no-unnecessary-curly-parens.md) | disallow unnecessary parentheses enclosing statements in curlies | | 🔧 | |
260261
| [template-no-unused-block-params](docs/rules/template-no-unused-block-params.md) | disallow unused block parameters in templates | | | |
261262
| [template-no-valueless-arguments](docs/rules/template-no-valueless-arguments.md) | disallow valueless named arguments | | | |
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# ember/template-no-unnecessary-concat
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+
💅 The `extends: 'stylistic'` property in a configuration file enables this rule.
8+
9+
This rule forbids unnecessary use of quotes (`""`) around expressions like `{{myValue}}`.
10+
11+
## Examples
12+
13+
This rule **forbids** the following:
14+
15+
```gjs
16+
<template>
17+
<span class="{{if errors.length 'text-danger' 'text-grey'}}">
18+
<img src="{{customSrc}}" alt="{{customAlt}}">
19+
<label for="{{concat elementId "-date"}}">
20+
</template>
21+
```
22+
23+
This rule **allows** the following:
24+
25+
```gjs
26+
<template>
27+
<span class={{if errors.length 'text-danger' 'text-grey'}}>
28+
<img src={{customSrc}} alt={{customAlt}}>
29+
<label for={{concat elementId "-date"}}>
30+
</template>
31+
```
32+
33+
## Migration
34+
35+
Use regexp find-and-replace to fix existing violations of this rule:
36+
37+
| Before | After |
38+
| ---------------- | --------- |
39+
| `="{{([^}]+)}}"` | `={{$1}}` |
40+
41+
## References
42+
43+
- [Handlebars docs/expressions](https://handlebarsjs.com/guide/expressions.html)
44+
- [Ember api/concat helper](https://api.emberjs.com/ember/release/classes/Ember.Templates.helpers/methods/concat?anchor=concat)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'suggestion',
5+
docs: {
6+
description: 'disallow unnecessary string concatenation',
7+
category: 'Best Practices',
8+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-unnecessary-concat.md',
9+
templateMode: 'both',
10+
},
11+
fixable: 'code',
12+
schema: [],
13+
messages: {
14+
unnecessary: 'Unnecessary string concatenation. Use {{inner}} instead of {{outer}}.',
15+
},
16+
originallyFrom: {
17+
name: 'ember-template-lint',
18+
rule: 'lib/rules/no-unnecessary-concat.js',
19+
docs: 'docs/rule/no-unnecessary-concat.md',
20+
tests: 'test/unit/rules/no-unnecessary-concat-test.js',
21+
},
22+
},
23+
create(context) {
24+
return {
25+
GlimmerConcatStatement(node) {
26+
if (node.parts?.length === 1) {
27+
const sourceCode = context.sourceCode;
28+
context.report({
29+
node,
30+
messageId: 'unnecessary',
31+
data: {
32+
inner: sourceCode.getText(node.parts[0]),
33+
outer: sourceCode.getText(node),
34+
},
35+
fix(fixer) {
36+
return fixer.replaceText(node, sourceCode.getText(node.parts[0]));
37+
},
38+
});
39+
}
40+
},
41+
};
42+
},
43+
};
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const rule = require('../../../lib/rules/template-no-unnecessary-concat');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const validHbs = [
5+
'<div class={{clazz}}></div>',
6+
'<div class="first {{second}}"></div>',
7+
'"{{foo}}"',
8+
];
9+
10+
const invalidHbs = [
11+
{
12+
code: '<div class="{{clazz}}"></div>',
13+
output: '<div class={{clazz}}></div>',
14+
errors: [
15+
{ message: 'Unnecessary string concatenation. Use {{clazz}} instead of "{{clazz}}".' },
16+
],
17+
},
18+
{
19+
code: '<img src="{{url}}" alt="{{t "alternate-text"}}">',
20+
output: '<img src={{url}} alt={{t "alternate-text"}}>',
21+
errors: [
22+
{ message: 'Unnecessary string concatenation. Use {{url}} instead of "{{url}}".' },
23+
{
24+
message:
25+
'Unnecessary string concatenation. Use {{t "alternate-text"}} instead of "{{t "alternate-text"}}".',
26+
},
27+
],
28+
},
29+
];
30+
31+
function wrapTemplate(entry) {
32+
if (typeof entry === 'string') {
33+
return `<template>${entry}</template>`;
34+
}
35+
36+
return {
37+
...entry,
38+
code: `<template>${entry.code}</template>`,
39+
output: entry.output ? `<template>${entry.output}</template>` : entry.output,
40+
errors: entry.errors.map(() => ({ messageId: 'unnecessary' })),
41+
};
42+
}
43+
44+
const gjsRuleTester = new RuleTester({
45+
parser: require.resolve('ember-eslint-parser'),
46+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
47+
});
48+
49+
gjsRuleTester.run('template-no-unnecessary-concat', rule, {
50+
valid: validHbs.map(wrapTemplate),
51+
invalid: invalidHbs.map(wrapTemplate),
52+
});
53+
54+
const hbsRuleTester = new RuleTester({
55+
parser: require.resolve('ember-eslint-parser/hbs'),
56+
parserOptions: {
57+
ecmaVersion: 2022,
58+
sourceType: 'module',
59+
},
60+
});
61+
62+
hbsRuleTester.run('template-no-unnecessary-concat', rule, {
63+
valid: validHbs,
64+
invalid: invalidHbs,
65+
});

0 commit comments

Comments
 (0)