Skip to content

Commit 6220d10

Browse files
Adds a new no-class-in-container rule (#111)
* Adds a new no-class-in-container rule * Adds a description of the rule * Adds a rule to the list * Update CHANGELOG.md
1 parent 0f2963f commit 6220d10

File tree

4 files changed

+195
-3
lines changed

4 files changed

+195
-3
lines changed

CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@
44
- Adds a `req-tags-presence` rule that requires the specified tags on the page.
55
- Adds a `req-preload-font` rule that requires the `preload` value for the font.
66
- Adds a `req-webp-in-picture` rule that requires `webp` in `<picture>`
7-
7+
- Adds a `no-class-in-container` rule that checks the `class` attribute for child elements inside the specified container.
88

99
```json
1010
{
1111
"htmlacademy/req-tags-presence": [ true, ["header", "nav", "main", "section", "h1", "footer"]],
1212
"htmlacademy/req-preload-font": true,
13-
"htmlacademy/req-webp-in-picture": true
13+
"htmlacademy/req-webp-in-picture": true,
14+
"htmlacademy/no-class-in-container": [true, {
15+
"containers": ["content"],
16+
"ignore": {
17+
"tags": ["h1", "p"],
18+
"classes": ["content__title"]
19+
}
20+
}]
1421
}
1522
```
1623

docs/list-of-rules.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| [htmlacademy/input-req-label](../rules/input-req-label/README.md) | Требует наличие метки для поля ввода, и позволяет указать метку в `aria-label` |
1818
| [htmlacademy/link-req-content](../rules/link-req-content/README.md) | Проверяет наличие текстового содержания у `<a>` |
1919
| [htmlacademy/no-blocking-script](../rules/no-blocking-script/README.md) | Проверяет расположение скриптов в разметке |
20+
| [htmlacademy/no-class-in-container](../rules/no-class-in-container/README.md) | Проверяет атрибут `class` у дочерних элементов внутри указанного контейнера |
2021
| [htmlacademy/no-double-br](../rules/no-double-br/README.md) | Запрещает идущие подряд двойной `<br>` |
2122
| [htmlacademy/no-px-size](../rules/no-px-size/README.md) | Атрибуты `width` и `height` содержат только цифры, без единиц измерения |
2223
| [htmlacademy/req-charset-utf](../rules/req-charset-utf/README.md) | Требует `UTF-8` для `<meta charset="">` |
@@ -28,7 +29,7 @@
2829
| [htmlacademy/req-source-width-height](../rules/req-source-width-height/README.md) | Требует `width` и `height` у `<source>` внутри `<picture>` |
2930
| [htmlacademy/req-stylesheet-link](../rules/req-stylesheet-link/README.md) | Проверяет наличие `<link rel="stylesheet" href="">` с непустым `href` |
3031
| [htmlacademy/req-tags-presence](../rules/req-tags-presence/README.md) | Требует указанные теги на странице |
31-
| [htmlacademy/req-webp-in-picture](../rules/req-webp-in-picture/README.md) | Требует `webp` в `<picture>` |
32+
| [htmlacademy/req-webp-in-picture](../rules/req-webp-in-picture/README.md) | Требует `webp` в `<picture>` |
3233
| [htmlacademy/section-has-heading](../rules/section-has-heading/README.md) | Требует добавление заголовка любого уровня в `<section>` |
3334
| [htmlacademy/space-between-comments](../rules/space-between-comments/README.md) | Проверят пробелы у комментария `<!-- Это комментарий -->` |
3435
| [htmlacademy/tag-forbid-attr](../rules/tag-forbid-attr/README.md) | Указанные атрибуты должны отсутствовать в указанном теге |

rules/no-class-in-container/README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# htmlacademy/no-class-in-container
2+
3+
Правило проверяет наличие атрибута `class` у дочерних элементов внутри указанного контейнера.
4+
5+
```json
6+
{
7+
"htmlacademy/no-class-in-container": [true, {
8+
"containers": ["content", "wrapper"],
9+
"ignore": {
10+
"tags": ["h1", "h2"],
11+
"class": ["content__title", "content__text"]
12+
}
13+
}]
14+
}
15+
```
16+
17+
## true
18+
Если указано `true`, то необходимо передать значение `class` контейнера. В `containers` можно передавать несколько классов, тогда будут проверяться несколько контейнеров.
19+
20+
```json
21+
{
22+
"htmlacademy/no-class-in-container": [true, {
23+
"containers": ["content", "wysiwg"]
24+
}]
25+
}
26+
```
27+
28+
Проблемными считаются следующие шаблоны:
29+
```html
30+
<div class="content">
31+
<h1 class="content__title">title</h1>
32+
</div>
33+
```
34+
35+
Так как на дочернем элементе `<h1>` есть `class`.
36+
37+
Следующие шаблоны **не** считаются проблемами:
38+
```html
39+
<div class="content">
40+
<h1>title</h1>
41+
</div>
42+
43+
<div class="wysiwg">
44+
<h1>title</h1>
45+
<p>text</p>
46+
</div>
47+
```
48+
49+
### ignore
50+
`ignore` принимает теги и классы, которые нужно игнорировать внутри контейнера.
51+
52+
#### ignore.tags
53+
Игнорирует указанные теги внутри контейнера.
54+
55+
```json
56+
{
57+
"htmlacademy/no-class-in-container": [true, {
58+
"containers": ["content"],
59+
"ignore": {
60+
"tags": ["h1"]
61+
}
62+
}]
63+
}
64+
```
65+
66+
Проблемными считаются следующие шаблоны:
67+
```html
68+
<div class="content">
69+
<h1 class="content__title">title</h1>
70+
<p class="content__text">text</p>
71+
</div>
72+
```
73+
74+
так как у `<p>` есть атрибут `class`.
75+
76+
Следующие шаблоны **не** считаются проблемами:
77+
78+
```html
79+
<div class="content">
80+
<h1 class="content__title">title</h1>
81+
</div>
82+
```
83+
84+
```html
85+
<div class="content">
86+
<h1 class="content__title">title</h1>
87+
<p>text</p>
88+
</div>
89+
```
90+
91+
#### ignore.classes
92+
Игнорирует элементы с указанными классами внутри контейнера.
93+
94+
```json
95+
{
96+
"htmlacademy/no-class-in-container": [true, {
97+
"containers": ["content"],
98+
"ignore": {
99+
"classes": [ "content__title", "content__text"]
100+
}
101+
}]
102+
}
103+
```
104+
105+
Проблемными считаются следующие шаблоны:
106+
```html
107+
<div class="content">
108+
<h1 class="content__title">title</h1>
109+
<h2 class="content__subtitle">subtitle</h2>
110+
<p class="content__text">text</p>
111+
</div>
112+
```
113+
114+
Так как есть `content__subtitle`.
115+
116+
Следующие шаблоны **не** считаются проблемами:
117+
118+
```html
119+
<div class="content">
120+
<h1 class="content__title">title</h1>
121+
</div>
122+
```
123+
124+
```html
125+
<div class="content">
126+
<h1 class="content__title">title</h1>
127+
<p class="content__text">text</p>
128+
</div>
129+
```
130+
131+
```html
132+
<div class="content">
133+
<h1 class="content__title">title</h1>
134+
<p class="content__text">text</p>
135+
<svg width="20" height="20"></svg>
136+
</div>
137+
```

rules/no-class-in-container/index.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
const { is_tag_node, has_attribute } = require('@linthtml/dom-utils');
3+
4+
const checkChildNodes = (node, report, container, ignore) => {
5+
node.children.forEach((child) => {
6+
if (is_tag_node(child)) {
7+
if (ignore?.tags?.includes(child.name)) {
8+
return;
9+
}
10+
if (has_attribute(child, 'class')) {
11+
let hasIgnoredClass = false;
12+
child.attributes.forEach((attribute) => {
13+
const classList = attribute.value.chars.split(' ');
14+
if (classList.some((className) => ignore?.classes?.includes(className))) {
15+
hasIgnoredClass = true;
16+
}
17+
});
18+
if (hasIgnoredClass) {
19+
return;
20+
}
21+
report({
22+
position: child.loc,
23+
message: `Element inside the specified container '${container}' should not have a class attribute`
24+
});
25+
}
26+
}
27+
if (child.children) {
28+
checkChildNodes(child, report, container, ignore);
29+
}
30+
});
31+
};
32+
33+
module.exports = {
34+
name: 'htmlacademy/no-class-in-container',
35+
lint(node, { containers, ignore = {} }, { report }) {
36+
if (is_tag_node(node) && has_attribute(node, 'class')) {
37+
node.attributes.forEach((attribute) => {
38+
const classList = attribute.value.chars.split(' ');
39+
containers.forEach((container) => {
40+
if (classList.includes(container)) {
41+
checkChildNodes(node, report, container, ignore);
42+
}
43+
});
44+
});
45+
}
46+
}
47+
};

0 commit comments

Comments
 (0)