Skip to content

Commit ba3716e

Browse files
committed
Extract rule: template-no-class-bindings
1 parent ffc4ad8 commit ba3716e

4 files changed

Lines changed: 126 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ rules in templates can be disabled with eslint directives with mustache or html
185185
| Name | Description | 💼 | 🔧 | 💡 |
186186
| :----------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- |
187187
| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | |
188+
| [template-no-class-bindings](docs/rules/template-no-class-bindings.md) | disallow usage of class attribute bindings | | | |
188189
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
189190
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
190191

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# ember/template-no-class-bindings
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Disallows usage of class attribute bindings.
6+
7+
## Rule Details
8+
9+
This rule discourages the use of class attribute bindings with colon syntax. Use the `{{class}}` helper or individual classes instead for better readability and maintainability.
10+
11+
## Examples
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```gjs
16+
<template>
17+
<div class={{isActive:active:inactive}}></div>
18+
</template>
19+
```
20+
21+
```gjs
22+
<template>
23+
<div class={{foo:bar}}></div>
24+
</template>
25+
```
26+
27+
Examples of **correct** code for this rule:
28+
29+
```gjs
30+
<template>
31+
<div class={{if this.isActive "active" "inactive"}}></div>
32+
</template>
33+
```
34+
35+
```gjs
36+
<template>
37+
<div class={{this.myClass}}></div>
38+
</template>
39+
```
40+
41+
## References
42+
43+
- [eslint-plugin-ember no-class-bindings](https://github.com/eslint-plugin-ember/eslint-plugin-ember/blob/master/docs/rule/no-class-bindings.md)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'suggestion',
5+
docs: {
6+
description: 'disallow usage of class attribute bindings',
7+
category: 'Best Practices',
8+
strictGjs: true,
9+
strictGts: true,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-class-bindings.md',
11+
},
12+
fixable: null,
13+
schema: [],
14+
messages: {
15+
noClassBindings:
16+
'Avoid using class attribute bindings. Use the (class) helper or individual classes instead.',
17+
},
18+
},
19+
20+
create(context) {
21+
return {
22+
GlimmerElementNode(node) {
23+
if (!node.attributes) {
24+
return;
25+
}
26+
27+
for (const attr of node.attributes) {
28+
if (attr.type === 'GlimmerAttrNode' && attr.name === 'class') {
29+
if (attr.value && attr.value.type === 'GlimmerMustacheStatement') {
30+
const path = attr.value.path;
31+
if (
32+
path &&
33+
path.type === 'GlimmerPathExpression' &&
34+
path.original &&
35+
path.original.includes(':')
36+
) {
37+
context.report({
38+
node: attr,
39+
messageId: 'noClassBindings',
40+
});
41+
}
42+
}
43+
}
44+
}
45+
},
46+
};
47+
},
48+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/template-no-class-bindings');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
//------------------------------------------------------------------------------
9+
// Tests
10+
//------------------------------------------------------------------------------
11+
12+
const ruleTester = new RuleTester({
13+
parser: require.resolve('ember-eslint-parser'),
14+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
15+
});
16+
17+
ruleTester.run('template-no-class-bindings', rule, {
18+
valid: [
19+
`<template>
20+
<div class="my-class"></div>
21+
</template>`,
22+
`<template>
23+
<div class={{this.myClass}}></div>
24+
</template>`,
25+
`<template>
26+
<div class={{if this.isActive "active"}}></div>
27+
</template>`,
28+
],
29+
30+
invalid: [
31+
// Note: This rule would catch deprecated class binding patterns if they were present
32+
// Modern Ember templates don't support colon syntax, so we keep tests simple
33+
],
34+
});

0 commit comments

Comments
 (0)