Skip to content

Commit 2f9ec5b

Browse files
committed
Extract rule: template-attribute-order
1 parent 5d2ad94 commit 2f9ec5b

4 files changed

Lines changed: 215 additions & 6 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,13 @@ rules in templates can be disabled with eslint directives with mustache or html
315315

316316
### Stylistic Issues
317317

318-
| Name | Description | 💼 | 🔧 | 💡 |
319-
| :--------------------------------------------------------- | :------------------------------------------------ | :- | :- | :- |
320-
| [order-in-components](docs/rules/order-in-components.md) | enforce proper order of properties in components | | 🔧 | |
321-
| [order-in-controllers](docs/rules/order-in-controllers.md) | enforce proper order of properties in controllers | | 🔧 | |
322-
| [order-in-models](docs/rules/order-in-models.md) | enforce proper order of properties in models | | 🔧 | |
323-
| [order-in-routes](docs/rules/order-in-routes.md) | enforce proper order of properties in routes | | 🔧 | |
318+
| Name                     | Description | 💼 | 🔧 | 💡 |
319+
| :----------------------------------------------------------------- | :------------------------------------------------------------- | :- | :- | :- |
320+
| [order-in-components](docs/rules/order-in-components.md) | enforce proper order of properties in components | | 🔧 | |
321+
| [order-in-controllers](docs/rules/order-in-controllers.md) | enforce proper order of properties in controllers | | 🔧 | |
322+
| [order-in-models](docs/rules/order-in-models.md) | enforce proper order of properties in models | | 🔧 | |
323+
| [order-in-routes](docs/rules/order-in-routes.md) | enforce proper order of properties in routes | | 🔧 | |
324+
| [template-attribute-order](docs/rules/template-attribute-order.md) | enforce consistent ordering of attributes in template elements | | | |
324325

325326
### Testing
326327

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# ember/template-attribute-order
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Enforces a consistent ordering of attributes in template elements. This helps improve readability and maintainability of templates.
6+
7+
## Rule Details
8+
9+
This rule enforces a consistent order for attributes on template elements. By default, it follows this order:
10+
11+
1. `class`
12+
2. `id`
13+
3. `role`
14+
4. `aria-*` attributes
15+
5. `data-test-*` attributes
16+
6. `type`
17+
7. `name`
18+
8. `value`
19+
9. `placeholder`
20+
10. `disabled`
21+
22+
## Examples
23+
24+
Examples of **incorrect** code for this rule:
25+
26+
```gjs
27+
<template>
28+
<div id="main" class="container"></div>
29+
</template>
30+
```
31+
32+
```gjs
33+
<template>
34+
<button aria-label="Submit" role="button">Send</button>
35+
</template>
36+
```
37+
38+
Examples of **correct** code for this rule:
39+
40+
```gjs
41+
<template>
42+
<div class="container" id="main"></div>
43+
</template>
44+
```
45+
46+
```gjs
47+
<template>
48+
<button class="btn" role="button" aria-label="Submit">Send</button>
49+
</template>
50+
```
51+
52+
## Configuration
53+
54+
You can customize the order by providing an `order` array:
55+
56+
```js
57+
module.exports = {
58+
rules: {
59+
'ember/template-attribute-order': [
60+
'error',
61+
{
62+
order: ['class', 'id', 'role', 'aria-', 'type'],
63+
},
64+
],
65+
},
66+
};
67+
```
68+
69+
## References
70+
71+
- [ember-template-lint attribute-order](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/attribute-order.md)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'suggestion',
5+
docs: {
6+
description: 'enforce consistent ordering of attributes in template elements',
7+
category: 'Stylistic Issues',
8+
strictGjs: true,
9+
strictGts: true,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-attribute-order.md',
11+
},
12+
fixable: null,
13+
schema: [
14+
{
15+
type: 'object',
16+
properties: {
17+
order: {
18+
type: 'array',
19+
items: {
20+
type: 'string',
21+
},
22+
},
23+
},
24+
additionalProperties: false,
25+
},
26+
],
27+
messages: {
28+
wrongOrder: 'Attribute "{{currentAttr}}" should come {{position}} "{{expectedAttr}}".',
29+
},
30+
},
31+
32+
create(context) {
33+
const options = context.options[0] || {};
34+
const order = options.order || [
35+
'class',
36+
'id',
37+
'role',
38+
'aria-',
39+
'data-test-',
40+
'type',
41+
'name',
42+
'value',
43+
'placeholder',
44+
'disabled',
45+
];
46+
47+
function getAttributeCategory(attrName) {
48+
for (const category of order) {
49+
if (category.endsWith('-')) {
50+
if (attrName.startsWith(category)) {
51+
return category;
52+
}
53+
} else if (attrName === category) {
54+
return category;
55+
}
56+
}
57+
return null;
58+
}
59+
60+
function getExpectedIndex(attrName) {
61+
const category = getAttributeCategory(attrName);
62+
if (category === null) {
63+
return order.length; // Unknown attributes go last
64+
}
65+
return order.indexOf(category);
66+
}
67+
68+
return {
69+
GlimmerElementNode(node) {
70+
if (!node.attributes || node.attributes.length < 2) {
71+
return;
72+
}
73+
74+
const attributes = node.attributes.filter(
75+
(attr) => attr.type === 'GlimmerAttrNode' && attr.name
76+
);
77+
78+
for (let i = 1; i < attributes.length; i++) {
79+
const current = attributes[i];
80+
const currentIndex = getExpectedIndex(current.name);
81+
82+
for (let j = 0; j < i; j++) {
83+
const previous = attributes[j];
84+
const previousIndex = getExpectedIndex(previous.name);
85+
86+
if (currentIndex < previousIndex) {
87+
context.report({
88+
node: current,
89+
messageId: 'wrongOrder',
90+
data: {
91+
currentAttr: current.name,
92+
position: 'before',
93+
expectedAttr: previous.name,
94+
},
95+
});
96+
break;
97+
}
98+
}
99+
}
100+
},
101+
};
102+
},
103+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const rule = require('../../../lib/rules/template-attribute-order');
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+
9+
ruleTester.run('template-attribute-order', rule, {
10+
valid: [
11+
'<template><div class="foo" id="bar"></div></template>',
12+
'<template><button class="btn" role="button" aria-label="Submit"></button></template>',
13+
'<template><input type="text" name="username" value=""></template>',
14+
'<template><div data-test-id="foo"></div></template>',
15+
],
16+
17+
invalid: [
18+
{
19+
code: '<template><div id="bar" class="foo"></div></template>',
20+
output: null,
21+
errors: [{ messageId: 'wrongOrder' }],
22+
},
23+
{
24+
code: '<template><button aria-label="Submit" role="button"></button></template>',
25+
output: null,
26+
errors: [{ messageId: 'wrongOrder' }],
27+
},
28+
{
29+
code: '<template><input name="username" type="text"></template>',
30+
output: null,
31+
errors: [{ messageId: 'wrongOrder' }],
32+
},
33+
],
34+
});

0 commit comments

Comments
 (0)