@double-great/stylelint-a11y 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/.babelrc +3 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.js +27 -0
  4. package/.github/dependabot.yml +10 -0
  5. package/.github/workflows/test.yml +21 -0
  6. package/.husky/pre-commit +4 -0
  7. package/LICENSE +21 -0
  8. package/README.md +68 -0
  9. package/dist/index.js +18 -0
  10. package/dist/rules/content-property-no-static-value/__tests__/index.js +37 -0
  11. package/dist/rules/content-property-no-static-value/index.js +90 -0
  12. package/dist/rules/font-size-is-readable/__tests__/index.js +35 -0
  13. package/dist/rules/font-size-is-readable/index.js +77 -0
  14. package/dist/rules/index.js +48 -0
  15. package/dist/rules/line-height-is-vertical-rhythmed/__tests__/index.js +59 -0
  16. package/dist/rules/line-height-is-vertical-rhythmed/index.js +84 -0
  17. package/dist/rules/media-prefers-color-scheme/__tests__/index.js +47 -0
  18. package/dist/rules/media-prefers-color-scheme/index.js +121 -0
  19. package/dist/rules/media-prefers-reduced-motion/__tests__/index.js +41 -0
  20. package/dist/rules/media-prefers-reduced-motion/index.js +175 -0
  21. package/dist/rules/no-display-none/__tests__/index.js +17 -0
  22. package/dist/rules/no-display-none/index.js +76 -0
  23. package/dist/rules/no-obsolete-attribute/__tests__/index.js +27 -0
  24. package/dist/rules/no-obsolete-attribute/index.js +78 -0
  25. package/dist/rules/no-obsolete-attribute/obsoleteAttributes.js +8 -0
  26. package/dist/rules/no-obsolete-element/__tests__/index.js +27 -0
  27. package/dist/rules/no-obsolete-element/index.js +78 -0
  28. package/dist/rules/no-obsolete-element/obsoleteElements.js +8 -0
  29. package/dist/rules/no-outline-none/__tests__/index.js +40 -0
  30. package/dist/rules/no-outline-none/index.js +88 -0
  31. package/dist/rules/no-spread-text/__tests__/index.js +32 -0
  32. package/dist/rules/no-spread-text/index.js +75 -0
  33. package/dist/rules/no-text-align-justify/__tests__/index.js +32 -0
  34. package/dist/rules/no-text-align-justify/index.js +76 -0
  35. package/dist/rules/selector-pseudo-class-focus/__tests__/index.js +46 -0
  36. package/dist/rules/selector-pseudo-class-focus/index.js +121 -0
  37. package/jest.config.js +20 -0
  38. package/jest.setup.js +5 -0
  39. package/package.json +63 -0
  40. package/recommended.js +8 -0
  41. package/src/index.js +8 -0
  42. package/src/rules/content-property-no-static-value/README.md +34 -0
  43. package/src/rules/content-property-no-static-value/__tests__/index.js +48 -0
  44. package/src/rules/content-property-no-static-value/index.js +72 -0
  45. package/src/rules/font-size-is-readable/README.md +34 -0
  46. package/src/rules/font-size-is-readable/__tests__/index.js +45 -0
  47. package/src/rules/font-size-is-readable/index.js +57 -0
  48. package/src/rules/index.js +27 -0
  49. package/src/rules/line-height-is-vertical-rhythmed/README.md +51 -0
  50. package/src/rules/line-height-is-vertical-rhythmed/__tests__/index.js +75 -0
  51. package/src/rules/line-height-is-vertical-rhythmed/index.js +61 -0
  52. package/src/rules/media-prefers-color-scheme/README.md +72 -0
  53. package/src/rules/media-prefers-color-scheme/__tests__/index.js +60 -0
  54. package/src/rules/media-prefers-color-scheme/index.js +115 -0
  55. package/src/rules/media-prefers-reduced-motion/README.md +60 -0
  56. package/src/rules/media-prefers-reduced-motion/__tests__/index.js +55 -0
  57. package/src/rules/media-prefers-reduced-motion/index.js +164 -0
  58. package/src/rules/no-display-none/README.md +27 -0
  59. package/src/rules/no-display-none/__tests__/index.js +21 -0
  60. package/src/rules/no-display-none/index.js +58 -0
  61. package/src/rules/no-obsolete-attribute/README.md +35 -0
  62. package/src/rules/no-obsolete-attribute/__tests__/index.js +33 -0
  63. package/src/rules/no-obsolete-attribute/index.js +58 -0
  64. package/src/rules/no-obsolete-attribute/obsoleteAttributes.js +204 -0
  65. package/src/rules/no-obsolete-element/README.md +20 -0
  66. package/src/rules/no-obsolete-element/__tests__/index.js +33 -0
  67. package/src/rules/no-obsolete-element/index.js +57 -0
  68. package/src/rules/no-obsolete-element/obsoleteElements.js +32 -0
  69. package/src/rules/no-outline-none/README.md +84 -0
  70. package/src/rules/no-outline-none/__tests__/index.js +51 -0
  71. package/src/rules/no-outline-none/index.js +75 -0
  72. package/src/rules/no-spread-text/README.md +50 -0
  73. package/src/rules/no-spread-text/__tests__/index.js +42 -0
  74. package/src/rules/no-spread-text/index.js +72 -0
  75. package/src/rules/no-text-align-justify/README.md +52 -0
  76. package/src/rules/no-text-align-justify/__tests__/index.js +42 -0
  77. package/src/rules/no-text-align-justify/index.js +60 -0
  78. package/src/rules/selector-pseudo-class-focus/README.md +43 -0
  79. package/src/rules/selector-pseudo-class-focus/__tests__/index.js +51 -0
  80. package/src/rules/selector-pseudo-class-focus/index.js +85 -0
@@ -0,0 +1,115 @@
1
+ import { utils } from 'stylelint';
2
+ import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
3
+ import isStandardSyntaxSelector from 'stylelint/lib/utils/isStandardSyntaxSelector';
4
+ import isStandardSyntaxAtRule from 'stylelint/lib/utils/isStandardSyntaxAtRule';
5
+ import isCustomSelector from 'stylelint/lib/utils/isCustomSelector';
6
+
7
+ export const ruleName = 'a11y/media-prefers-color-scheme';
8
+
9
+ export const messages = utils.ruleMessages(ruleName, {
10
+ expected: (selector) => `Expected ${selector} is used with @media (prefers-color-scheme)`,
11
+ });
12
+ const targetProperties = ['background-color', 'color'];
13
+
14
+ function check(selector, node) {
15
+ const declarations = node.nodes;
16
+ const params = node.parent.params;
17
+ const parentNodes = node.parent.nodes;
18
+
19
+ if (!declarations) return true;
20
+
21
+ if (!isStandardSyntaxSelector(selector)) {
22
+ return true;
23
+ }
24
+
25
+ if (isCustomSelector(selector)) {
26
+ return true;
27
+ }
28
+
29
+ let currentSelector = null;
30
+
31
+ const declarationsIsMatched = declarations.some((declaration) => {
32
+ const noMatchedParams = !params || params.indexOf('prefers-color-scheme') === -1;
33
+ const index = targetProperties.indexOf(declaration.prop);
34
+ currentSelector = targetProperties[index];
35
+
36
+ return index >= 0 && noMatchedParams;
37
+ });
38
+
39
+ if (!declarationsIsMatched) return true;
40
+
41
+ if (declarationsIsMatched) {
42
+ const parentMatchedNode = parentNodes.some((parentNode) => {
43
+ if (!parentNode || !parentNode.nodes) return;
44
+ return parentNode.nodes.some((childrenNode) => {
45
+ const childrenNodes = childrenNode.nodes;
46
+
47
+ if (
48
+ !parentNode.params ||
49
+ !Array.isArray(childrenNodes) ||
50
+ selector !== childrenNode.selector
51
+ )
52
+ return false;
53
+
54
+ const matchedChildrenNodes = childrenNodes.some((declaration) => {
55
+ const index = targetProperties.indexOf(declaration.prop);
56
+ if (currentSelector !== targetProperties[index]) return false;
57
+
58
+ return index >= 0 && parentNode.params.indexOf('prefers-color-scheme') >= 0;
59
+ });
60
+
61
+ return matchedChildrenNodes;
62
+ });
63
+ });
64
+
65
+ if (!parentMatchedNode) return false;
66
+
67
+ return true;
68
+ }
69
+
70
+ return true;
71
+ }
72
+
73
+ export default function (actual) {
74
+ return (root, result) => {
75
+ const validOptions = utils.validateOptions(result, ruleName, { actual });
76
+
77
+ if (!validOptions || !actual) {
78
+ return;
79
+ }
80
+
81
+ root.walk((node) => {
82
+ let selector = null;
83
+
84
+ if (node.type === 'rule') {
85
+ if (!isStandardSyntaxRule(node)) {
86
+ return;
87
+ }
88
+
89
+ selector = node.selector;
90
+ } else if (node.type === 'atrule' && node.name === 'page' && node.params) {
91
+ if (!isStandardSyntaxAtRule(node)) {
92
+ return;
93
+ }
94
+
95
+ selector = node.params;
96
+ }
97
+
98
+ if (!selector) {
99
+ return;
100
+ }
101
+
102
+ const isAccepted = check(selector, node);
103
+
104
+ if (!isAccepted) {
105
+ utils.report({
106
+ index: node.lastEach,
107
+ message: messages.expected(selector),
108
+ node,
109
+ ruleName,
110
+ result,
111
+ });
112
+ }
113
+ });
114
+ };
115
+ }
@@ -0,0 +1,60 @@
1
+ # media-prefers-reduced-motion
2
+
3
+ Require certain styles if the animation or transition in media features.
4
+
5
+ Safari 10.1 [introduced](https://webkit.org/blog/7551/responsive-design-for-motion/) the Reduced Motion Media Query. It is a non-vendor-prefixed declaration that allows developers to "create styles that avoid large areas of motion for users that specify a preference for reduced motion in System Preferences."
6
+
7
+ The `--fix` option on the command line can automatically fix all of the problems reported by this rule.
8
+
9
+ ## Options
10
+
11
+ ### true
12
+
13
+ The following pattern are considered violations:
14
+
15
+ ```css
16
+ .foo {
17
+ animation: 1s ease-in;
18
+ }
19
+ ```
20
+
21
+ ```css
22
+ .bar {
23
+ animation-name: skew;
24
+ }
25
+ @media screen and (prefers-reduced-motion) {
26
+ .bar {
27
+ transition: none;
28
+ }
29
+ }
30
+ ```
31
+
32
+ The following patterns are _not_ considered violations:
33
+
34
+ ```css
35
+ div {
36
+ transition: none;
37
+ }
38
+ ```
39
+
40
+ ```css
41
+ .foo {
42
+ transition: none;
43
+ }
44
+ @media screen and (prefers-reduced-motion: reduce) {
45
+ .foo {
46
+ transition: none;
47
+ }
48
+ }
49
+ ```
50
+
51
+ ```css
52
+ .bar {
53
+ animation: none;
54
+ }
55
+ @media screen and (prefers-reduced-motion) {
56
+ .bar {
57
+ animation: none;
58
+ }
59
+ }
60
+ ```
@@ -0,0 +1,55 @@
1
+ import { messages, ruleName } from '../index';
2
+
3
+ testRule({
4
+ ruleName,
5
+ config: [true],
6
+ fix: true,
7
+
8
+ accept: [
9
+ {
10
+ code: 'a { }',
11
+ },
12
+ {
13
+ code: 'div { transition: none; }',
14
+ },
15
+ {
16
+ code: '.foo { transition: none } @media screen and (prefers-reduced-motion: reduce) { .foo { transition: none } }',
17
+ },
18
+ {
19
+ code: '.bar { animation: none } @media screen and (prefers-reduced-motion) { .bar { animation: none } }',
20
+ },
21
+ {
22
+ code: 'a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { animation: none } }',
23
+ },
24
+ {
25
+ code: '.foo { transition: all; @media (prefers-reduced-motion: reduce) { transition: none; } }',
26
+ },
27
+ ],
28
+
29
+ reject: [
30
+ {
31
+ code: 'a { animation-name: skew; }',
32
+ fixed:
33
+ '@media screen and (prefers-reduced-motion: reduce) {\na { animation: none;\n}\n}\na { animation-name: skew; }',
34
+ message: messages.expected('a'),
35
+ line: 1,
36
+ column: 3,
37
+ },
38
+ {
39
+ code: 'a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { transition: none; } }',
40
+ fixed:
41
+ '@media screen and (prefers-reduced-motion: reduce) {\na { animation: none;\n}\n} a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { transition: none; } }',
42
+ message: messages.expected('a'),
43
+ line: 1,
44
+ column: 3,
45
+ },
46
+ {
47
+ code: '.foo { animation: 1s ease-in; } @media screen and (prefers-reduced-motion) { .foo { animation: 1s ease-in; } }',
48
+ fixed:
49
+ '@media screen and (prefers-reduced-motion: reduce) {\n.foo { animation: none;\n}\n} .foo { animation: 1s ease-in; } @media screen and (prefers-reduced-motion) { .foo { animation: 1s ease-in; } }',
50
+ message: messages.expected('.foo'),
51
+ line: 1,
52
+ column: 3,
53
+ },
54
+ ],
55
+ });
@@ -0,0 +1,164 @@
1
+ import { utils } from 'stylelint';
2
+ import { parse } from 'postcss';
3
+ import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
4
+ import isStandardSyntaxSelector from 'stylelint/lib/utils/isStandardSyntaxSelector';
5
+ import isStandardSyntaxAtRule from 'stylelint/lib/utils/isStandardSyntaxAtRule';
6
+ import isCustomSelector from 'stylelint/lib/utils/isCustomSelector';
7
+
8
+ export const ruleName = 'a11y/media-prefers-reduced-motion';
9
+
10
+ export const messages = utils.ruleMessages(ruleName, {
11
+ expected: (selector) => `Expected ${selector} is used with @media (prefers-reduced-motion)`,
12
+ });
13
+ const targetProperties = ['transition', 'animation', 'animation-name'];
14
+
15
+ function checkChildrenNodes(childrenNodes, currentSelector, parentNode) {
16
+ return childrenNodes.some((declaration) => {
17
+ const index = targetProperties.indexOf(declaration.prop);
18
+ if (currentSelector === 'animation-name' && targetProperties[index] === 'animation')
19
+ return true;
20
+ if (currentSelector !== targetProperties[index]) return false;
21
+ if (declaration.value !== 'none') return false;
22
+
23
+ return index >= 0 && parentNode.params.indexOf('prefers-reduced-motion') >= 0;
24
+ });
25
+ }
26
+
27
+ function check(selector, node) {
28
+ const declarations = node.nodes;
29
+ const params = node.parent.params;
30
+ const parentNodes = node.parent.nodes;
31
+
32
+ if (!declarations) return true;
33
+
34
+ if (!isStandardSyntaxSelector(selector)) {
35
+ return true;
36
+ }
37
+
38
+ if (isCustomSelector(selector)) {
39
+ return true;
40
+ }
41
+
42
+ let currentSelector = null;
43
+
44
+ const declarationsIsMatched = declarations.some((declaration) => {
45
+ const noMatchedParams = !params || params.indexOf('prefers-reduced-motion') === -1;
46
+ const index = targetProperties.indexOf(declaration.prop);
47
+ currentSelector = targetProperties[index];
48
+ if (targetProperties.indexOf(declaration.prop) >= 0 && declaration.value === 'none') {
49
+ return false;
50
+ }
51
+
52
+ return index >= 0 && noMatchedParams;
53
+ });
54
+
55
+ if (!declarationsIsMatched) return true;
56
+
57
+ if (declarationsIsMatched) {
58
+ const parentMatchedNode = parentNodes.some((parentNode) => {
59
+ if (!parentNode || !parentNode.nodes) return;
60
+ return parentNode.nodes.some((childrenNode) => {
61
+ const childrenNodes = childrenNode.nodes;
62
+
63
+ if (
64
+ childrenNode.type === 'atrule' &&
65
+ childrenNode.params.indexOf('prefers-reduced-motion') >= 0
66
+ ) {
67
+ return childrenNodes.some((declaration) => {
68
+ const index = targetProperties.indexOf(declaration.prop);
69
+ if (currentSelector === 'animation-name' && targetProperties[index] === 'animation')
70
+ return true;
71
+ if (currentSelector !== targetProperties[index]) return false;
72
+ if (declaration.value !== 'none') return false;
73
+
74
+ return index >= 0;
75
+ });
76
+ }
77
+
78
+ if (
79
+ !parentNode.params ||
80
+ !Array.isArray(childrenNodes) ||
81
+ selector !== childrenNode.selector
82
+ )
83
+ return false;
84
+
85
+ return checkChildrenNodes(childrenNodes, currentSelector, parentNode);
86
+ });
87
+ });
88
+
89
+ if (!parentMatchedNode) return false;
90
+
91
+ return true;
92
+ }
93
+
94
+ return true;
95
+ }
96
+
97
+ export default function (actual, _, context) {
98
+ return (root, result) => {
99
+ const validOptions = utils.validateOptions(result, ruleName, { actual });
100
+
101
+ if (!validOptions || !actual) {
102
+ return;
103
+ }
104
+
105
+ root.walk((node) => {
106
+ let selector = null;
107
+
108
+ if (node.type === 'rule') {
109
+ if (!isStandardSyntaxRule(node)) {
110
+ return;
111
+ }
112
+
113
+ selector = node.selector;
114
+ } else if (node.type === 'atrule' && node.name === 'page' && node.params) {
115
+ if (!isStandardSyntaxAtRule(node)) {
116
+ return;
117
+ }
118
+
119
+ selector = node.params;
120
+ }
121
+
122
+ if (!selector) {
123
+ return;
124
+ }
125
+
126
+ const isAccepted = check(selector, node);
127
+
128
+ if (context.fix && !isAccepted) {
129
+ const media = parse('@media screen and (prefers-reduced-motion: reduce) {}');
130
+ media.nodes.forEach((o) => {
131
+ o.raws.after = '\n';
132
+ });
133
+ const cloneRule = node.clone();
134
+ cloneRule.raws = {
135
+ ...cloneRule.raws,
136
+ before: '\n',
137
+ after: '\n',
138
+ semicolon: true,
139
+ };
140
+ cloneRule.nodes.forEach((o) => {
141
+ if (o.prop === 'animation-name') {
142
+ o.prop = 'animation';
143
+ }
144
+ if (targetProperties.indexOf(o.prop) >= 0) {
145
+ o.value = 'none';
146
+ }
147
+ });
148
+ media.first.append(cloneRule);
149
+ node.before(media);
150
+ return;
151
+ }
152
+
153
+ if (!isAccepted) {
154
+ utils.report({
155
+ index: node.lastEach,
156
+ message: messages.expected(selector),
157
+ node,
158
+ ruleName,
159
+ result,
160
+ });
161
+ }
162
+ });
163
+ };
164
+ }
@@ -0,0 +1,27 @@
1
+ # no-display-none
2
+
3
+ Sources that will help you do without `{ display: none; }` and hide the content:
4
+
5
+ - [CSS Tricks](https://css-tricks.com/places-its-tempting-to-use-display-none-but-dont/)
6
+ - [A11Y Project](https://a11yproject.com/posts/how-to-hide-content/)
7
+ - [WebAIM](https://webaim.org/techniques/css/invisiblecontent/)
8
+
9
+ ## Options
10
+
11
+ ### true
12
+
13
+ The following pattern are considered violations:
14
+
15
+ ```css
16
+ .foo {
17
+ display: none;
18
+ }
19
+ ```
20
+
21
+ The following patterns are _not_ considered violations:
22
+
23
+ ```css
24
+ .foo {
25
+ display: flex;
26
+ }
27
+ ```
@@ -0,0 +1,21 @@
1
+ import { messages, ruleName } from '../index';
2
+
3
+ testRule({
4
+ ruleName,
5
+ config: [true],
6
+
7
+ accept: [
8
+ {
9
+ code: '.foo { display: flex; }',
10
+ },
11
+ ],
12
+
13
+ reject: [
14
+ {
15
+ code: '.foo { display: none; }',
16
+ message: messages.expected('.foo'),
17
+ line: 1,
18
+ column: 3,
19
+ },
20
+ ],
21
+ });
@@ -0,0 +1,58 @@
1
+ import { utils } from 'stylelint';
2
+ import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
3
+
4
+ export const ruleName = 'a11y/no-display-none';
5
+
6
+ export const messages = utils.ruleMessages(ruleName, {
7
+ expected: (selector) => `Unexpected using "{ display: none; }" in ${selector}`,
8
+ });
9
+
10
+ function check(selector, node) {
11
+ if (node.type !== 'rule') {
12
+ return true;
13
+ }
14
+
15
+ return !node.nodes.some(
16
+ (o) =>
17
+ o.type === 'decl' && o.prop.toLowerCase() === 'display' && o.value.toLowerCase() === 'none'
18
+ );
19
+ }
20
+
21
+ export default function (actual) {
22
+ return (root, result) => {
23
+ const validOptions = utils.validateOptions(result, ruleName, { actual });
24
+
25
+ if (!validOptions || !actual) {
26
+ return;
27
+ }
28
+
29
+ root.walk((node) => {
30
+ let selector = null;
31
+
32
+ if (node.type === 'rule') {
33
+ if (!isStandardSyntaxRule(node)) {
34
+ return;
35
+ }
36
+ selector = node.selector;
37
+ } else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
38
+ selector = node.params;
39
+ }
40
+
41
+ if (!selector) {
42
+ return;
43
+ }
44
+
45
+ const isAccepted = check(selector, node);
46
+
47
+ if (!isAccepted) {
48
+ utils.report({
49
+ index: node.lastEach,
50
+ message: messages.expected(selector),
51
+ node,
52
+ ruleName,
53
+ result,
54
+ });
55
+ }
56
+ });
57
+ };
58
+ }
@@ -0,0 +1,35 @@
1
+ # no-obsolete-attribute
2
+
3
+ Disallow obsolete attribute using.
4
+
5
+ **Sources:**
6
+
7
+ - [W3G Obsolete features](https://www.w3.org/TR/html5/obsolete.html#obsolete)
8
+ - [W3C Non-conforming features](https://w3c.github.io/html/obsolete.html#non-conforming-features)
9
+ - [W3G Features removed](https://www.w3.org/TR/html52/changes.html#features-removed)
10
+
11
+ ## Options
12
+
13
+ ### true
14
+
15
+ The following pattern are considered violations:
16
+
17
+ ```css
18
+ body[link] {
19
+ background-color: pink;
20
+ }
21
+ ```
22
+
23
+ ```css
24
+ a,
25
+ img[datasrc] {
26
+ color: pink;
27
+ }
28
+ ```
29
+
30
+ ```css
31
+ img[align],
32
+ a[name] {
33
+ color: pink;
34
+ }
35
+ ```
@@ -0,0 +1,33 @@
1
+ import { ruleName, messages } from '../index';
2
+
3
+ testRule({
4
+ ruleName,
5
+ config: [true],
6
+
7
+ accept: [
8
+ {
9
+ code: 'a { color: pink; }',
10
+ },
11
+ ],
12
+
13
+ reject: [
14
+ {
15
+ code: 'body[link] { color: pink; }',
16
+ message: messages.expected('body[link]'),
17
+ line: 1,
18
+ column: 3,
19
+ },
20
+ {
21
+ code: 'a, img[datasrc] { color: pink; }',
22
+ message: messages.expected('a, img[datasrc]'),
23
+ line: 1,
24
+ column: 3,
25
+ },
26
+ {
27
+ code: 'img[align], a[name] { color: pink; }',
28
+ message: messages.expected('img[align], a[name]'),
29
+ line: 1,
30
+ column: 3,
31
+ },
32
+ ],
33
+ });
@@ -0,0 +1,58 @@
1
+ import { utils } from 'stylelint';
2
+ import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
3
+ import { obsoleteAttributes } from './obsoleteAttributes';
4
+
5
+ export const ruleName = 'a11y/no-obsolete-attribute';
6
+
7
+ export const messages = utils.ruleMessages(ruleName, {
8
+ expected: (selector) => `Unexpected using obsolete attribute "${selector}"`,
9
+ });
10
+
11
+ function check(selector, node) {
12
+ if (node.type !== 'rule') {
13
+ return true;
14
+ }
15
+
16
+ return !node.selectors.some((sel) => {
17
+ return obsoleteAttributes.has(sel);
18
+ });
19
+ }
20
+
21
+ export default function (actual) {
22
+ return (root, result) => {
23
+ const validOptions = utils.validateOptions(result, ruleName, { actual });
24
+
25
+ if (!validOptions || !actual) {
26
+ return;
27
+ }
28
+
29
+ root.walk((node) => {
30
+ let selector = null;
31
+
32
+ if (node.type === 'rule') {
33
+ if (!isStandardSyntaxRule(node)) {
34
+ return;
35
+ }
36
+ selector = node.selector;
37
+ } else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
38
+ selector = node.params;
39
+ }
40
+
41
+ if (!selector) {
42
+ return;
43
+ }
44
+
45
+ const isAccepted = check(selector, node);
46
+
47
+ if (!isAccepted) {
48
+ utils.report({
49
+ index: node.lastEach,
50
+ message: messages.expected(selector),
51
+ node,
52
+ ruleName,
53
+ result,
54
+ });
55
+ }
56
+ });
57
+ };
58
+ }