@markuplint/rules 1.10.4 → 2.0.0-dev.20211115.1

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 (57) hide show
  1. package/README.md +21 -20
  2. package/lib/attr-check.d.ts +16 -0
  3. package/lib/attr-check.js +498 -0
  4. package/lib/attr-duplication/index.d.ts +1 -1
  5. package/lib/attr-duplication/index.js +5 -8
  6. package/lib/attr-equal-space-after/index.d.ts +1 -1
  7. package/lib/attr-equal-space-after/index.js +8 -11
  8. package/lib/attr-equal-space-before/index.d.ts +1 -1
  9. package/lib/attr-equal-space-before/index.js +8 -11
  10. package/lib/attr-spacing/index.d.ts +3 -3
  11. package/lib/attr-spacing/index.js +19 -21
  12. package/lib/attr-value-quotes/index.d.ts +1 -1
  13. package/lib/attr-value-quotes/index.js +9 -12
  14. package/lib/case-sensitive-attr-name/index.d.ts +1 -1
  15. package/lib/case-sensitive-attr-name/index.js +11 -14
  16. package/lib/case-sensitive-tag-name/index.d.ts +1 -1
  17. package/lib/case-sensitive-tag-name/index.js +8 -11
  18. package/lib/character-reference/index.d.ts +1 -1
  19. package/lib/character-reference/index.js +15 -11
  20. package/lib/class-naming/index.d.ts +1 -1
  21. package/lib/class-naming/index.js +7 -10
  22. package/lib/deprecated-attr/index.d.ts +1 -1
  23. package/lib/deprecated-attr/index.js +6 -9
  24. package/lib/deprecated-element/index.d.ts +1 -1
  25. package/lib/deprecated-element/index.js +10 -15
  26. package/lib/doctype/index.d.ts +1 -1
  27. package/lib/doctype/index.js +16 -25
  28. package/lib/helpers.d.ts +12 -11
  29. package/lib/helpers.js +38 -25
  30. package/lib/id-duplication/index.d.ts +1 -1
  31. package/lib/id-duplication/index.js +5 -8
  32. package/lib/indentation/index.d.ts +3 -3
  33. package/lib/indentation/index.js +81 -116
  34. package/lib/index.d.ts +45 -24
  35. package/lib/index.js +42 -42
  36. package/lib/invalid-attr/index.d.ts +2 -2
  37. package/lib/invalid-attr/index.js +23 -25
  38. package/lib/landmark-roles/index.d.ts +3 -3
  39. package/lib/landmark-roles/index.js +12 -20
  40. package/lib/permitted-contents/index.d.ts +2 -2
  41. package/lib/permitted-contents/index.js +63 -45
  42. package/lib/permitted-contents/permitted-content.spec-to-regexp.d.ts +2 -2
  43. package/lib/permitted-contents/permitted-content.spec-to-regexp.js +49 -30
  44. package/lib/permitted-contents/unfold-content-models-to-tags.d.ts +1 -1
  45. package/lib/permitted-contents/unfold-content-models-to-tags.js +2 -3
  46. package/lib/{type-check.d.ts → primitive-check.d.ts} +24 -7
  47. package/lib/primitive-check.js +109 -0
  48. package/lib/required-attr/index.d.ts +1 -1
  49. package/lib/required-attr/index.js +12 -16
  50. package/lib/required-h1/index.d.ts +3 -3
  51. package/lib/required-h1/index.js +8 -13
  52. package/lib/wai-aria/index.d.ts +1 -1
  53. package/lib/wai-aria/index.js +57 -64
  54. package/package.json +6 -6
  55. package/tsconfig.test.json +1 -24
  56. package/tsconfig.tsbuildinfo +1 -1
  57. package/lib/type-check.js +0 -321
package/README.md CHANGED
@@ -12,26 +12,27 @@
12
12
  | -------- | ---- | -------- | ------------------------------------------------- | ------- |
13
13
  | ✅ | ✅ | ✅ | [attr-duplication](./src/attr-duplication/) | 🚧 |
14
14
  | ✅ | ✅ | ✅ | [character-reference](./src/character-reference/) | 🚧 |
15
- | 🚧 | 🚧 | 🚧 | [comment](./src/comment/) |
16
- | ✅ | ✅ | ✅ | [deprecated-attr](./src/deprecated-attr/) |
17
- | ✅ | ✅ | ✅ | [deprecated-element](./src/deprecated-element/) |
15
+ | 🚧 | 🚧 | 🚧 | [comment](./src/comment/) | |
16
+ | ✅ | ✅ | ✅ | [deprecated-attr](./src/deprecated-attr/) | |
17
+ | ✅ | ✅ | ✅ | [deprecated-element](./src/deprecated-element/) | |
18
18
  | ✅ | ✅ | ✅ | [doctype](./src/doctype/) | 🚧 |
19
- | ✅ | ✅ | ✅ | [id-duplication](./src/id-duplication/) |
20
- | ✅ | ✅ | ✅ | [permitted-contents](./src/permitted-contents/) |
21
- | ✅ | ✅ | ✅ | [required-attr](./src/required-attr/) |
19
+ | ✅ | ✅ | ✅ | [id-duplication](./src/id-duplication/) | |
20
+ | ✅ | ✅ | ✅ | [permitted-contents](./src/permitted-contents/) | |
21
+ | ✅ | ✅ | ✅ | [required-attr](./src/required-attr/) | |
22
+ | ✅ | ✅ | ✅ | [invalid-attr](./src/invalid-attr/) | |
22
23
 
23
24
  ### Accessibility
24
25
 
25
26
  | Avaiable | Docs | Docs(ja) | Rule | Fixable |
26
27
  | -------- | ---- | -------- | --------------------------------------------------------------------- | ------- |
27
- | 🚧 | 🚧 | 🚧 | [accessible-text](./src/accessible-text/) |
28
- | 🚧 | 🚧 | 🚧 | [attr-role](./src/permitted-role/) |
29
- | 🚧 | 🚧 | 🚧 | [heading-in-sectioning-content](./src/heading-in-sectioning-content/) |
30
- | 🚧 | 🚧 | 🚧 | [heading-in-sectioning-root](./src/heading-in-sectioning-root/) |
31
- | 🚧 | 🚧 | 🚧 | [heading-levels-skipping](./src/heading-levels-skipping/) |
32
- | 🚧 | 🚧 | 🚧 | [labeling-controls](./src/labeling-controls/) |
33
- | ✅ | ✅ | ✅ | [landmark-roles](./src/landmark-roles/) |
34
- | ✅ | ✅ | ✅ | [required-h1](./src/required-h1/) |
28
+ | 🚧 | 🚧 | 🚧 | [accessible-text](./src/accessible-text/) | |
29
+ | 🚧 | 🚧 | 🚧 | [attr-role](./src/permitted-role/) | |
30
+ | 🚧 | 🚧 | 🚧 | [heading-in-sectioning-content](./src/heading-in-sectioning-content/) | |
31
+ | 🚧 | 🚧 | 🚧 | [heading-in-sectioning-root](./src/heading-in-sectioning-root/) | |
32
+ | 🚧 | 🚧 | 🚧 | [heading-levels-skipping](./src/heading-levels-skipping/) | |
33
+ | 🚧 | 🚧 | 🚧 | [labeling-controls](./src/labeling-controls/) | |
34
+ | ✅ | ✅ | ✅ | [landmark-roles](./src/landmark-roles/) | |
35
+ | ✅ | ✅ | ✅ | [required-h1](./src/required-h1/) | |
35
36
 
36
37
  ### Usability
37
38
 
@@ -43,10 +44,10 @@
43
44
 
44
45
  | Avaiable | Docs | Docs(ja) | Rule | Fixable |
45
46
  | -------- | ---- | -------- | ----------------------------------------------------- | ------- |
46
- | ✅ | ✅ | ✅ | [class-naming](./src/class-naming/) |
47
- | 🚧 | 🚧 | 🚧 | [custom-element-naming](./src/custom-element-naming/) |
48
- | 🚧 | 🚧 | 🚧 | [data-attr-naming](./src/data-attr-naming/) |
49
- | 🚧 | 🚧 | 🚧 | [required-element](./src/required-element/) |
47
+ | ✅ | ✅ | ✅ | [class-naming](./src/class-naming/) | |
48
+ | 🚧 | 🚧 | 🚧 | [custom-element-naming](./src/custom-element-naming/) | |
49
+ | 🚧 | 🚧 | 🚧 | [data-attr-naming](./src/data-attr-naming/) | |
50
+ | 🚧 | 🚧 | 🚧 | [required-element](./src/required-element/) | |
50
51
 
51
52
  ### Style
52
53
 
@@ -59,10 +60,10 @@
59
60
  | ✅ | ✅ | ✅ | [case-sensitive-attr-name](./src/case-sensitive-attr-name/) | ✅ |
60
61
  | ✅ | ✅ | ✅ | [case-sensitive-tag-name](./src/case-sensitive-tag-name/) | ✅ |
61
62
  | 🚧 | 🚧 | 🚧 | [comment-spasing](./src/comment-spasing/) | 🚧 |
62
- | 🚧 | 🚧 | 🚧 | [event-attr](./src/event-attr/) |
63
+ | 🚧 | 🚧 | 🚧 | [event-attr](./src/event-attr/) | |
63
64
  | ✅ | ✅ | ✅ | [indentation](./src/indentation/) | ✅ |
64
65
  | 🚧 | 🚧 | 🚧 | [indentation-attr](./src/indentation-attr/) | 🚧 |
65
- | 🚧 | 🚧 | 🚧 | [path](./src/path/) |
66
+ | 🚧 | 🚧 | 🚧 | [path](./src/path/) | |
66
67
  | 🚧 | 🚧 | ✅ | [self-closing-tag](./src/self-closing-tag/) | 🚧 |
67
68
  | 🚧 | 🚧 | 🚧 | [tag-omission](./src/tag-omission/) | 🚧 |
68
69
 
@@ -0,0 +1,16 @@
1
+ import type { Translator } from '@markuplint/i18n';
2
+ import type { Attribute as AttrSpec } from '@markuplint/ml-spec';
3
+ declare type Invalid = {
4
+ invalidType: 'non-existent' | 'invalid-value';
5
+ message: string;
6
+ };
7
+ /**
8
+ * Use in rules `invalid-attr` and `wai-aria`
9
+ *
10
+ * @param name
11
+ * @param value
12
+ * @param isCustomRule
13
+ * @param spec
14
+ */
15
+ export declare function attrCheck(t: Translator, name: string, value: string, isCustomRule: boolean, spec?: AttrSpec): Invalid | false;
16
+ export {};
@@ -0,0 +1,498 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attrCheck = void 0;
4
+ const primitive_check_1 = require("./primitive-check");
5
+ /**
6
+ * Use in rules `invalid-attr` and `wai-aria`
7
+ *
8
+ * @param name
9
+ * @param value
10
+ * @param isCustomRule
11
+ * @param spec
12
+ */
13
+ function attrCheck(t, name, value, isCustomRule, spec) {
14
+ if (!isCustomRule) {
15
+ if (/^data-.+$/.test(name)) {
16
+ // Ignore checking because "data-*" attribute is any type
17
+ return false;
18
+ }
19
+ if (/^aria-.+$|^role$/.test(name)) {
20
+ // Ignore checking because ARIA attributes are check on another rule
21
+ return false;
22
+ }
23
+ }
24
+ // Existance
25
+ if (!spec) {
26
+ return {
27
+ invalidType: 'non-existent',
28
+ message: t('{0} is {1:c}', t('the "{0}" {1}', name, 'attribute'), 'disallowed'),
29
+ };
30
+ }
31
+ const invalid = valueCheck(t, name, value, spec);
32
+ if (invalid) {
33
+ return {
34
+ invalidType: 'invalid-value',
35
+ message: invalid,
36
+ };
37
+ }
38
+ return false;
39
+ }
40
+ exports.attrCheck = attrCheck;
41
+ function valueCheck(t, name, value, spec) {
42
+ // Valid because any string value is acceptable
43
+ if (typeof spec.type !== 'string' && 'enum' in spec.type) {
44
+ // has "enum"
45
+ return includesEnum(t, name, value, spec.type.enum);
46
+ }
47
+ const t_theNameAttribute = t('the "{0}" {1}', name, 'attribute');
48
+ if (spec.type === 'NonEmptyString' && value === '') {
49
+ return t('{0} must not be {1}', t('{0} of {1}', t('the {0}', 'value'), t_theNameAttribute), 'empty string');
50
+ }
51
+ if (spec.type === 'Boolean') {
52
+ // Valid because an attribute is exist
53
+ return false;
54
+ }
55
+ if (spec.type === 'Function') {
56
+ // TODO: To eval function-body string
57
+ return false;
58
+ }
59
+ if (spec.type === 'Date') {
60
+ // TODO: https://html.spec.whatwg.org/multipage/text-level-semantics.html#datetime-value
61
+ return false;
62
+ }
63
+ if (spec.type === 'Int') {
64
+ if ((0, primitive_check_1.intCheck)(value)) {
65
+ return false;
66
+ }
67
+ return t('{0} expects {1}', t_theNameAttribute, 'integer');
68
+ }
69
+ if (spec.type === 'Uint') {
70
+ if ((0, primitive_check_1.uintCheck)(value)) {
71
+ return false;
72
+ }
73
+ return t('{0} expects {1}', t_theNameAttribute, 'non-negative integer');
74
+ }
75
+ if (spec.type === 'Float') {
76
+ if ((0, primitive_check_1.floatCheck)(value)) {
77
+ return false;
78
+ }
79
+ return t('{0} expects {1}', t_theNameAttribute, 'floating-point number');
80
+ }
81
+ if (spec.type === 'NonZeroUint') {
82
+ if ((0, primitive_check_1.nonZeroUintCheck)(value)) {
83
+ return false;
84
+ }
85
+ return t('{0} expects {1}', t_theNameAttribute, t('{0} greater than {1}', 'floating-point number', 'zero'));
86
+ }
87
+ if (spec.type === 'ZeroToOne') {
88
+ if ((0, primitive_check_1.range)(value, 0, 1)) {
89
+ return false;
90
+ }
91
+ return t('{0} expects {1:c}', t_theNameAttribute, t('in the range between {0} and {1}', 'zero', 'one'));
92
+ }
93
+ if (spec.type === 'AcceptList') {
94
+ // TODO: https://html.spec.whatwg.org/multipage/input.html#attr-input-accept
95
+ return false;
96
+ }
97
+ if (spec.type === 'AutoComplete') {
98
+ // TODO: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-detail-tokens
99
+ return false;
100
+ }
101
+ if (spec.type === 'BCP47') {
102
+ // TODO: https://tools.ietf.org/html/bcp47
103
+ return false;
104
+ }
105
+ if (spec.type === 'Color') {
106
+ // TODO: https://drafts.csswg.org/css-color/#typedef-color
107
+ return false;
108
+ }
109
+ if (spec.type === 'ColSpan') {
110
+ /**
111
+ * > value must be a valid non-negative integer greater than zero and less than or equal to 1000.
112
+ *
113
+ * @see https://html.spec.whatwg.org/multipage/tables.html#attr-tdth-colspan
114
+ */
115
+ if ((0, primitive_check_1.intCheck)(value) && (0, primitive_check_1.range)(value, 0, 1000)) {
116
+ return false;
117
+ }
118
+ return t('{0} expects {1:c}', t_theNameAttribute, t('{0:c} and {1:c}', t('{0} greater than {1}', 'non-negative integer', 'zero'), t('less than or equal to {0}', 1000)));
119
+ }
120
+ if (spec.type === 'Coords') {
121
+ // TODO: https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-coords
122
+ return false;
123
+ }
124
+ if (spec.type === 'Crossorigin') {
125
+ // TODO: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
126
+ return false;
127
+ }
128
+ if (spec.type === 'DateTime') {
129
+ // TODO: https://html.spec.whatwg.org/multipage/text-level-semantics.html#datetime-value
130
+ return false;
131
+ }
132
+ if (spec.type === 'Destination') {
133
+ // TODO: https://html.spec.whatwg.org/multipage/semantics.html#attr-link-as
134
+ return false;
135
+ }
136
+ if (spec.type === 'DOMID') {
137
+ // TODO: Searching ID in Document
138
+ return false;
139
+ }
140
+ if (spec.type === 'DOMIDList') {
141
+ // TODO: Searching ID in Document
142
+ return false;
143
+ }
144
+ if (spec.type === 'IRI') {
145
+ // TODO: https://developer.mozilla.org/ja/docs/Web/SVG/Content_type#iri
146
+ return false;
147
+ }
148
+ if (spec.type === 'ItemType') {
149
+ // TODO: https://html.spec.whatwg.org/multipage/microdata.html#attr-itemtype
150
+ return false;
151
+ }
152
+ if (spec.type === 'LinkSizes') {
153
+ // TODO: https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
154
+ return false;
155
+ }
156
+ if (spec.type === 'LinkType') {
157
+ // TODO: https://html.spec.whatwg.org/multipage/links.html#linkTypes
158
+ return false;
159
+ }
160
+ if (spec.type === 'LinkTypeList') {
161
+ // TODO: https://html.spec.whatwg.org/multipage/links.html#linkTypes
162
+ return false;
163
+ }
164
+ if (spec.type === 'MediaQuery') {
165
+ // TODO: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-media-query-list
166
+ return false;
167
+ }
168
+ if (spec.type === 'MediaQueryList') {
169
+ // TODO: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-media-query-list
170
+ return false;
171
+ }
172
+ if (spec.type === 'MIMEType') {
173
+ // TODO: https://mimesniff.spec.whatwg.org/#valid-mime-type
174
+ return false;
175
+ }
176
+ if (spec.type === 'ReferrerPolicy') {
177
+ /**
178
+ * @see https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attributes
179
+ */
180
+ return includesEnum(t, name, value, [
181
+ '',
182
+ 'no-referrer',
183
+ 'no-referrer-when-downgrade',
184
+ 'same-origin',
185
+ 'origin',
186
+ 'strict-origin',
187
+ 'origin-when-cross-origin',
188
+ 'strict-origin-when-cross-origin',
189
+ 'unsafe-url',
190
+ ]);
191
+ }
192
+ if (spec.type === 'RowSpan') {
193
+ /**
194
+ * > value must be a valid non-negative integer less than or equal to 65534.
195
+ *
196
+ * @see https://html.spec.whatwg.org/multipage/tables.html#attr-tdth-rowspan
197
+ */
198
+ if ((0, primitive_check_1.intCheck)(value) && (0, primitive_check_1.range)(value, 0, 65534)) {
199
+ return false;
200
+ }
201
+ return t('{0} expects {1:c}', t_theNameAttribute, t('{0:c} and {1:c}', t('{0} greater than {1}', 'non-negative integer', 'zero'), t('less than or equal to {0}', 65534)));
202
+ }
203
+ if (spec.type === 'SourceSizeList') {
204
+ // TODO: https://html.spec.whatwg.org/multipage/images.html#sizes-attributes
205
+ return false;
206
+ }
207
+ if (spec.type === 'SrcSet') {
208
+ // TODO: https://html.spec.whatwg.org/multipage/images.html#srcset-attribute
209
+ return false;
210
+ }
211
+ if (spec.type === 'TabIndex') {
212
+ /**
213
+ * > ## omitted (or non-integer values)
214
+ * >
215
+ * > The user agent will decide whether the element is focusable, and if it is, whether it is sequentially focusable or click focusable (or both).
216
+ * >
217
+ * > ## −1 (or other negative integer values)
218
+ * >
219
+ * > Causes the element to be focusable, and indicates that the author would prefer the element to be click focusable but not sequentially focusable. The user agent might ignore this preference for click and sequential focusability, e.g., for specific element types according to platform conventions, or for keyboard-only users.
220
+ * >
221
+ * > ## 0
222
+ * >
223
+ * > Causes the element to be focusable, and indicates that the author would prefer the element to be both click focusable and sequentially focusable. The user agent might ignore this preference for click and sequential focusabiity.
224
+ * >
225
+ * > ## positive integer values
226
+ * >
227
+ * > Behaves the same as 0, but in addition creates a relative ordering within a tabindex-ordered focus navigation scope, so that elements with higher tabindex attribute value come later.
228
+ *
229
+ * @see https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
230
+ */
231
+ if ((0, primitive_check_1.intCheck)(value)) {
232
+ const i = parseInt(value);
233
+ if (-1 <= i) {
234
+ return false;
235
+ }
236
+ if (-1 > i) {
237
+ return t('{0} behaves the same as {1} if {2}', t_theNameAttribute, '-1', t('less than {0}', '-1'));
238
+ }
239
+ }
240
+ return t('{0} expects {1:c}', t_theNameAttribute, t('either {0}', t(['-1', 'zero', 'non-negative integer'])));
241
+ }
242
+ if (spec.type === 'Target') {
243
+ // TODO: https://html.spec.whatwg.org/multipage/links.html#attr-hyperlink-target
244
+ return false;
245
+ }
246
+ if (spec.type === 'URL') {
247
+ // TODO: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#valid-url-potentially-surrounded-by-spaces
248
+ return false;
249
+ }
250
+ if (spec.type === 'URLHash') {
251
+ /**
252
+ * https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-hash-name-reference
253
+ */
254
+ const valid = value[0] === '#';
255
+ if (valid) {
256
+ return false;
257
+ }
258
+ return t('{0} expects {1:c}', t_theNameAttribute, t('valid {0}', 'hash-name reference'));
259
+ }
260
+ if (spec.type === 'URLList') {
261
+ // TODO: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#valid-url-potentially-surrounded-by-spaces
262
+ return false;
263
+ }
264
+ if (spec.type === 'CSSAngle') {
265
+ /**
266
+ * @see https://drafts.csswg.org/css-values/#angles
267
+ */
268
+ const valid = (0, primitive_check_1.numberCheckWithUnit)(value, ['deg', 'grad', 'rad', 'turn']);
269
+ if (valid) {
270
+ return false;
271
+ }
272
+ return t('{0} expects {1}', t_theNameAttribute, 'angle');
273
+ }
274
+ if (spec.type === 'CSSBlendMode') {
275
+ /**
276
+ * @see https://drafts.fxtf.org/compositing/#ltblendmodegt
277
+ */
278
+ return includesEnum(t, name, value, [
279
+ 'normal',
280
+ 'multiply',
281
+ 'screen',
282
+ 'overlay',
283
+ 'darken',
284
+ 'lighten',
285
+ 'color-dodge',
286
+ 'color-burn',
287
+ 'hard-light',
288
+ 'soft-light',
289
+ 'difference',
290
+ 'exclusion',
291
+ 'hue',
292
+ 'saturation',
293
+ 'color',
294
+ 'luminosity',
295
+ ]);
296
+ }
297
+ if (spec.type === 'CSSClipPath') {
298
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path
299
+ return false;
300
+ }
301
+ if (spec.type === 'CSSCustomIdent') {
302
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident
303
+ return false;
304
+ }
305
+ if (spec.type === 'CSSDisplay') {
306
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/display
307
+ return false;
308
+ }
309
+ if (spec.type === 'CSSFilter') {
310
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/filter
311
+ return false;
312
+ }
313
+ if (spec.type === 'CSSFontFamily') {
314
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family
315
+ return false;
316
+ }
317
+ if (spec.type === 'CSSFontSize') {
318
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size
319
+ return false;
320
+ }
321
+ if (spec.type === 'CSSFontVariant') {
322
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant
323
+ return false;
324
+ }
325
+ if (spec.type === 'CSSFontWeight') {
326
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
327
+ return false;
328
+ }
329
+ if (spec.type === 'CSSMask') {
330
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/mask
331
+ return false;
332
+ }
333
+ if (spec.type === 'CSSOpacity') {
334
+ /**
335
+ * @see https://drafts.csswg.org/css-color/#typedef-alpha-value
336
+ */
337
+ const { num, unit } = (0, primitive_check_1.splitUnit)(value);
338
+ if (unit === '%') {
339
+ const vaild = (0, primitive_check_1.range)(num, 0, 100);
340
+ if (vaild) {
341
+ return false;
342
+ }
343
+ return t('{0} expects {1:c}', t_theNameAttribute, t('{0} as {1}', t('{0} to {1}', '0%', '100%'), 'alpha channel value'));
344
+ }
345
+ const vaild = (0, primitive_check_1.range)(num, 0, 1);
346
+ if (vaild) {
347
+ return false;
348
+ }
349
+ return t('{0} expects {1:c}', t_theNameAttribute, t('{0} as {1}', t('{0} to {1}', '0', '1'), 'alpha channel value'));
350
+ }
351
+ if (spec.type === 'CSSTextDecoration') {
352
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration
353
+ return false;
354
+ }
355
+ if (spec.type === 'CSSTransformList') {
356
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function
357
+ return false;
358
+ }
359
+ if (spec.type === 'CSSTransformOrigin') {
360
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin
361
+ return false;
362
+ }
363
+ if (spec.type === 'CSSURL') {
364
+ // TODO: https://www.w3.org/TR/css3-values/#url-value
365
+ return false;
366
+ }
367
+ if (spec.type === 'SVGAnimatableValue') {
368
+ // TODO
369
+ /**
370
+ * The exact value type for this attribute depends on the value of the attribute
371
+ * that will be animated.
372
+ *
373
+ * ```svg
374
+ * <rect x="10" y="10" height="100">
375
+ * <animate attributeName="width" fill="freeze" from="100" to="150" dur="3s"/>
376
+ * </rect>
377
+ * ```
378
+ *
379
+ * The `from` and `to` attributes of the `animate` element are SVGAnimatableValue.
380
+ */
381
+ return false;
382
+ }
383
+ if (spec.type === 'SVGBeginValueList') {
384
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/begin
385
+ return false;
386
+ }
387
+ if (spec.type === 'SVGClockValue') {
388
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#clock-value
389
+ return false;
390
+ }
391
+ if (spec.type === 'SVGColorMatrix') {
392
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix
393
+ return false;
394
+ }
395
+ if (spec.type === 'SVGDashArray') {
396
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
397
+ return false;
398
+ }
399
+ if (spec.type === 'SVGEndValueList') {
400
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/end
401
+ return false;
402
+ }
403
+ if (spec.type === 'SVGFilterPrimitiveReference') {
404
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/result#usage_notes
405
+ return false;
406
+ }
407
+ if (spec.type === 'SVGKernelMatrix') {
408
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/kernelMatrix
409
+ return false;
410
+ }
411
+ if (spec.type === 'SVGKeyPoints') {
412
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/keyPoints
413
+ return false;
414
+ }
415
+ if (spec.type === 'SVGKeySplines') {
416
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/keySplines
417
+ return false;
418
+ }
419
+ if (spec.type === 'SVGKeyTimes') {
420
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/keyTimes
421
+ return false;
422
+ }
423
+ if (spec.type === 'SVGLanguageTags') {
424
+ /**
425
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/systemLanguage
426
+ */
427
+ // TODO: List of BCP47
428
+ return false;
429
+ }
430
+ if (spec.type === 'SVGLength') {
431
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length
432
+ return false;
433
+ }
434
+ if (spec.type === 'SVGLengthList') {
435
+ /**
436
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length
437
+ */
438
+ // TODO: List of SVGLength
439
+ return false;
440
+ }
441
+ if (spec.type === 'SVGNumberList') {
442
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/tableValues#usage_notes
443
+ return false;
444
+ }
445
+ if (spec.type === 'SVGNumberOptionalNumber') {
446
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#number-optional-number
447
+ return false;
448
+ }
449
+ if (spec.type === 'SVGOrigin') {
450
+ // TODO: https://www.w3.org/TR/2001/REC-smil-animation-20010904/#MotionOriginAttribute
451
+ return false;
452
+ }
453
+ if (spec.type === 'SVGPaint') {
454
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#paint
455
+ return false;
456
+ }
457
+ if (spec.type === 'SVGPathCommands') {
458
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#path_commands
459
+ return false;
460
+ }
461
+ if (spec.type === 'SVGPercentage') {
462
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#percentage
463
+ return false;
464
+ }
465
+ if (spec.type === 'SVGPercentageList') {
466
+ /**
467
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#percentage
468
+ */
469
+ // TODO: List of SVGPercentage
470
+ return false;
471
+ }
472
+ if (spec.type === 'SVGPoints') {
473
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/points
474
+ return false;
475
+ }
476
+ if (spec.type === 'SVGPreserveAspectRatio') {
477
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio
478
+ return false;
479
+ }
480
+ if (spec.type === 'SVGViewBox') {
481
+ // TODO: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox
482
+ return false;
483
+ }
484
+ return false;
485
+ }
486
+ /**
487
+ *
488
+ * @param name
489
+ * @param value
490
+ * @param enumValues
491
+ */
492
+ function includesEnum(t, name, value, enumValues) {
493
+ const valid = enumValues.includes(value.toLowerCase());
494
+ if (valid) {
495
+ return false;
496
+ }
497
+ return t('{0} expects {1:c}', t('the "{0}" {1}', name, 'attribute'), t('either {0}', t(enumValues)));
498
+ }
@@ -1,2 +1,2 @@
1
- declare const _default: import("@markuplint/ml-core").MLRule<null, null>;
1
+ declare const _default: import("@markuplint/ml-core").RuleSeed<null, null>;
2
2
  export default _default;
@@ -1,13 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const ml_core_1 = require("@markuplint/ml-core");
4
- exports.default = ml_core_1.createRule({
5
- name: 'attr-duplication',
4
+ exports.default = (0, ml_core_1.createRule)({
6
5
  defaultValue: null,
7
6
  defaultOptions: null,
8
- async verify(document, translate) {
9
- const reports = [];
10
- const message = translate('Duplicate {0}', 'attribute name');
7
+ async verify({ document, report, t }) {
8
+ const message = t('{0} is {1:c}', t('the {0}', 'attribute name'), 'duplicated');
11
9
  await document.walkOn('Element', async (node) => {
12
10
  const attrNameStack = [];
13
11
  for (const attr of node.attributes) {
@@ -17,8 +15,8 @@ exports.default = ml_core_1.createRule({
17
15
  const attrName = attr.getName();
18
16
  const name = node.isCustomElement ? attrName.potential : attrName.potential.toLowerCase();
19
17
  if (attrNameStack.includes(name)) {
20
- reports.push({
21
- severity: node.rule.severity,
18
+ report({
19
+ scope: node,
22
20
  message,
23
21
  line: attrName.line,
24
22
  col: attrName.col,
@@ -30,6 +28,5 @@ exports.default = ml_core_1.createRule({
30
28
  }
31
29
  }
32
30
  });
33
- return reports;
34
31
  },
35
32
  });
@@ -1,3 +1,3 @@
1
1
  declare type Value = 'always' | 'never' | 'always-single-line' | 'never-single-line';
2
- declare const _default: import("@markuplint/ml-core").MLRule<Value, null>;
2
+ declare const _default: import("@markuplint/ml-core").RuleSeed<Value, null>;
3
3
  export default _default;