@markuplint/rules 4.0.0-alpha.1 → 4.0.0-alpha.11

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 (56) hide show
  1. package/.eslintrc +5 -0
  2. package/LICENSE +1 -1
  3. package/lib/attr-check.js +2 -2
  4. package/lib/attr-duplication/index.js +3 -0
  5. package/lib/attr-value-quotes/index.js +3 -1
  6. package/lib/case-sensitive-attr-name/index.js +3 -0
  7. package/lib/case-sensitive-tag-name/index.js +3 -0
  8. package/lib/character-reference/index.js +8 -5
  9. package/lib/class-naming/index.js +5 -2
  10. package/lib/create-message.d.ts +1 -1
  11. package/lib/create-message.js +21 -10
  12. package/lib/deprecated-attr/index.js +3 -0
  13. package/lib/deprecated-element/index.js +4 -1
  14. package/lib/disallowed-element/index.js +7 -4
  15. package/lib/doctype/index.js +3 -0
  16. package/lib/end-tag/index.js +3 -0
  17. package/lib/helpers.js +10 -6
  18. package/lib/id-duplication/index.js +3 -0
  19. package/lib/index.d.ts +6 -2
  20. package/lib/ineffective-attr/index.js +3 -0
  21. package/lib/invalid-attr/index.js +3 -0
  22. package/lib/label-has-control/index.js +3 -0
  23. package/lib/landmark-roles/index.js +13 -12
  24. package/lib/no-boolean-attr-value/index.js +3 -0
  25. package/lib/no-default-value/index.js +3 -0
  26. package/lib/no-empty-palpable-content/index.js +4 -1
  27. package/lib/no-hard-code-id/index.js +3 -0
  28. package/lib/no-refer-to-non-existent-id/index.d.ts +1 -1
  29. package/lib/no-refer-to-non-existent-id/index.js +11 -8
  30. package/lib/no-use-event-handler-attr/index.js +3 -0
  31. package/lib/permitted-contents/choice.js +14 -4
  32. package/lib/permitted-contents/content-model.js +6 -3
  33. package/lib/permitted-contents/count-pattern.js +1 -1
  34. package/lib/permitted-contents/debug.browser.d.ts +2 -2
  35. package/lib/permitted-contents/debug.d.ts +2 -2
  36. package/lib/permitted-contents/index.js +3 -0
  37. package/lib/permitted-contents/matches-selector.js +2 -2
  38. package/lib/permitted-contents/order.js +3 -3
  39. package/lib/permitted-contents/represent-transparent-nodes.js +1 -1
  40. package/lib/permitted-contents/transparent.js +2 -2
  41. package/lib/permitted-contents/utils.js +27 -18
  42. package/lib/placeholder-label-option/index.js +7 -4
  43. package/lib/require-accessible-name/index.js +3 -3
  44. package/lib/require-datetime/index.js +8 -5
  45. package/lib/require-datetime/utils.js +1 -1
  46. package/lib/required-attr/index.js +3 -0
  47. package/lib/required-element/index.js +4 -1
  48. package/lib/required-h1/index.js +3 -0
  49. package/lib/use-list/index.d.ts +4 -0
  50. package/lib/use-list/index.js +47 -27
  51. package/lib/wai-aria/checkings/implicit-props.js +7 -7
  52. package/lib/wai-aria/checkings/presentational-children.js +1 -1
  53. package/lib/wai-aria/checkings/required-owned-elements.js +3 -3
  54. package/lib/wai-aria/checkings/value.js +3 -3
  55. package/lib/wai-aria/index.js +3 -0
  56. package/package.json +11 -12
package/.eslintrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "rules": {
3
+ "import/no-default-export": 0
4
+ }
5
+ }
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017-2019 Yusuke Hirao
3
+ Copyright (c) 2017-2024 Yusuke Hirao
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/lib/attr-check.js CHANGED
@@ -71,10 +71,10 @@ export function attrCheck(t, name, value, isCustomRule, spec) {
71
71
  };
72
72
  }
73
73
  });
74
- if (invalidList.some(i => i === false)) {
74
+ if (invalidList.includes(false)) {
75
75
  return false;
76
76
  }
77
- const invalid = invalidList.find(i => i);
77
+ const invalid = invalidList.find(Boolean);
78
78
  return invalid ?? false;
79
79
  }
80
80
  export function valueCheck(t, name, value, type) {
@@ -1,5 +1,8 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  async verify({ document, report, t }) {
4
7
  const message = t('{0} is {1:c}', t('the {0}', 'attribute name'), 'duplicated');
5
8
  await document.walkOn('Element', node => {
@@ -4,6 +4,9 @@ const quoteList = {
4
4
  single: "'",
5
5
  };
6
6
  export default createRule({
7
+ meta: {
8
+ category: 'style',
9
+ },
7
10
  defaultSeverity: 'warning',
8
11
  defaultValue: 'double',
9
12
  async verify({ document, report, t }) {
@@ -26,7 +29,6 @@ export default createRule({
26
29
  const quote = quoteList[attr.rule.value];
27
30
  if (quote && attr.startQuote && attr.startQuote.raw !== quote) {
28
31
  attr.startQuote.fix(quote);
29
- // TODO: attr.endQuote = new MLToken(quote);
30
32
  attr.endQuote?.fix(quote);
31
33
  }
32
34
  });
@@ -1,5 +1,8 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'style',
5
+ },
3
6
  defaultSeverity: 'warning',
4
7
  defaultValue: 'lower',
5
8
  async verify({ document, report, t }) {
@@ -1,5 +1,8 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'style',
5
+ },
3
6
  defaultSeverity: 'warning',
4
7
  defaultValue: 'lower',
5
8
  async verify({ document, report, t }) {
@@ -1,11 +1,14 @@
1
1
  import { createRule, getLocationFromChars } from '@markuplint/ml-core';
2
2
  const defaultChars = ['"', '&', '<', '>'];
3
- const ignoreParentElement = ['script', 'style'];
3
+ const ignoreParentElement = new Set(['script', 'style']);
4
4
  export default createRule({
5
+ meta: {
6
+ category: 'style',
7
+ },
5
8
  async verify({ document, report, t }) {
6
9
  const targetNodes = [];
7
10
  await document.walkOn('Text', node => {
8
- if (node.parentNode && ignoreParentElement.includes(node.parentNode.nodeName.toLowerCase())) {
11
+ if (node.parentNode && ignoreParentElement.has(node.parentNode.nodeName.toLowerCase())) {
9
12
  return;
10
13
  }
11
14
  const severity = node.rule.severity;
@@ -41,14 +44,14 @@ export default createRule({
41
44
  if (!('scope' in targetNode && 'line' in targetNode && targetNode.line != null)) {
42
45
  continue;
43
46
  }
44
- const escapedText = targetNode.raw.replace(/&(?:[a-z]+|#[0-9]+|#x[0-9a-f]+);/gi, $0 => '*'.repeat($0.length));
45
- getLocationFromChars(defaultChars, escapedText, targetNode.line, targetNode.col).forEach(location => {
47
+ const escapedText = targetNode.raw.replaceAll(/&(?:[a-z]+|#\d+|#x[\da-f]+);/gi, $0 => '*'.repeat($0.length));
48
+ for (const location of getLocationFromChars(defaultChars, escapedText, targetNode.line, targetNode.col)) {
46
49
  report({
47
50
  scope: targetNode.scope,
48
51
  message: targetNode.message,
49
52
  ...location,
50
53
  });
51
- });
54
+ }
52
55
  }
53
56
  },
54
57
  });
@@ -2,6 +2,9 @@ import { createRule } from '@markuplint/ml-core';
2
2
  import { toNoEmptyStringArrayFromStringOrArray } from '@markuplint/shared';
3
3
  import { match } from '../helpers.js';
4
4
  export default createRule({
5
+ meta: {
6
+ category: 'naming-convention',
7
+ },
5
8
  defaultSeverity: 'warning',
6
9
  defaultValue: null,
7
10
  async verify({ document, report, t }) {
@@ -14,9 +17,9 @@ export default createRule({
14
17
  }
15
18
  const classAttr = attr.valueNode;
16
19
  const classList = attr.value
17
- .split(/\s+/g)
20
+ .split(/\s+/)
18
21
  .map(c => c.trim())
19
- .filter(c => c);
22
+ .filter(Boolean);
20
23
  for (const className of classList) {
21
24
  if (!classPatterns.some(pattern => match(className, pattern))) {
22
25
  report({
@@ -3,4 +3,4 @@ import type { AttributeType } from '@markuplint/ml-spec';
3
3
  import type { UnmatchedResult } from '@markuplint/types';
4
4
  import type { ReadonlyDeep } from 'type-fest';
5
5
  export declare function createMessageValueExpected(t: Translator, baseTarget: string, type: ReadonlyDeep<AttributeType>, matches: UnmatchedResult): string;
6
- export declare function __createMessageValueExpected(t: Translator, baseTarget: string, expected: string | null, matches: Pick<UnmatchedResult, 'partName' | 'reason' | 'raw' | 'candidate' | 'ref' | 'extra'>): string;
6
+ export declare function __createMessageValueExpected(t: Translator, baseTarget: string, expected: string | null, matches: Pick<UnmatchedResult, 'partName' | 'reason' | 'raw' | 'candidate' | 'ref' | 'extra' | 'fallbackTo'>): string;
@@ -13,7 +13,7 @@ export function createMessageValueExpected(t, baseTarget, type, matches) {
13
13
  }
14
14
  const expected = createExpectedObject(tokenType, matches, t);
15
15
  const message = [listDescriptionPart, __createMessageValueExpected(t, target, expected, matches)]
16
- .filter(s => s)
16
+ .filter(Boolean)
17
17
  .join(t('. '));
18
18
  return message;
19
19
  }
@@ -23,6 +23,7 @@ export function __createMessageValueExpected(t, baseTarget, expected, matches) {
23
23
  let expectPart;
24
24
  let unnecessaryPart;
25
25
  let candidatePart;
26
+ let fallbackToPart;
26
27
  let expectOrNeed = 'expects';
27
28
  if (matches.partName) {
28
29
  if (matches.partName === 'the content of the list') {
@@ -135,11 +136,11 @@ export function __createMessageValueExpected(t, baseTarget, expected, matches) {
135
136
  if (lte != null && gte === lte) {
136
137
  expectedDigits = t('{0} digits', gte);
137
138
  }
138
- else if (lte != null) {
139
- expectedDigits = t('{0} to {1} digits', gte, lte);
139
+ else if (lte == null) {
140
+ expectedDigits = t('{0} or more digits', gte);
140
141
  }
141
142
  else {
142
- expectedDigits = t('{0} or more digits', gte);
143
+ expectedDigits = t('{0} to {1} digits', gte, lte);
143
144
  }
144
145
  if (!expected) {
145
146
  expected = expectedDigits;
@@ -187,7 +188,12 @@ export function __createMessageValueExpected(t, baseTarget, expected, matches) {
187
188
  if (matches.candidate) {
188
189
  candidatePart = t('Did you mean "{0*}"?', matches.candidate);
189
190
  }
190
- let message = [reasonPart, unnecessaryPart, expectPart, candidatePart].filter(s => s).join(t('. '));
191
+ if (matches.fallbackTo) {
192
+ fallbackToPart = t('The user agent will automatically use "{0*}" instead', matches.fallbackTo);
193
+ }
194
+ let message = [reasonPart, unnecessaryPart, expectPart, candidatePart, fallbackToPart]
195
+ .filter(Boolean)
196
+ .join(t('. '));
191
197
  if (matches.ref) {
192
198
  message += ` (${matches.ref})`;
193
199
  }
@@ -220,19 +226,24 @@ function createExpectedObject(type, matches, t) {
220
226
  }
221
227
  function expectValueToWord(t, expect, type) {
222
228
  switch (expect.type) {
223
- case 'common':
229
+ case 'common': {
224
230
  return expect.value;
225
- case 'const':
231
+ }
232
+ case 'const': {
226
233
  return expect.value ? `%${expect.value}%` : '';
227
- case 'format':
234
+ }
235
+ case 'format': {
228
236
  return t('the {0} format', expect.value);
229
- case 'regexp':
237
+ }
238
+ case 'regexp': {
230
239
  return t('{0} ({1})', 'regular expression', expect.value);
231
- case 'syntax':
240
+ }
241
+ case 'syntax': {
232
242
  if (isKeyword(type) && type[0] === '<') {
233
243
  return t('the CSS Syntax "{0}"', expect.value);
234
244
  }
235
245
  return t('{0} syntax', expect.value);
246
+ }
236
247
  }
237
248
  }
238
249
  function createExpectedNumber(t, type) {
@@ -1,5 +1,8 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  async verify({ document, report, t }) {
4
7
  await document.walkOn('Attr', attr => {
5
8
  const attrSpecs = getAttrSpecs(attr.ownerElement, document.specs);
@@ -1,5 +1,8 @@
1
1
  import { createRule, getSpec } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  async verify({ document, report, t }) {
4
7
  await document.walkOn('Element', el => {
5
8
  if (!(el.namespaceURI === 'http://www.w3.org/1999/xhtml' ||
@@ -9,7 +12,7 @@ export default createRule({
9
12
  }
10
13
  const spec = getSpec(el, document.specs.specs);
11
14
  if (spec && (spec.obsolete != null || spec.deprecated || spec.nonStandard)) {
12
- const message = t('{0} is {1:c}', t('the "{0*}" {1}', el.localName, 'element'), spec.deprecated ? 'deprecated' : spec.obsolete != null ? 'obsolete' : 'non-standard');
15
+ const message = t('{0} is {1:c}', t('the "{0*}" {1}', el.localName, 'element'), spec.deprecated ? 'deprecated' : spec.obsolete == null ? 'non-standard' : 'obsolete');
13
16
  report({
14
17
  scope: el,
15
18
  message,
@@ -1,16 +1,19 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  defaultValue: [],
4
7
  async verify({ document, report, t }) {
5
8
  for (const query of document.rule.value) {
6
9
  const elements = document.querySelectorAll(query);
7
- elements.forEach(el => {
10
+ for (const el of elements) {
8
11
  const message = t('{0} is disallowed', t('the "{0*}" {1}', query, 'element'));
9
12
  report({
10
13
  scope: el,
11
14
  message,
12
15
  });
13
- });
16
+ }
14
17
  }
15
18
  await document.walkOn('Element', el => {
16
19
  if (el.rule.value === document.rule.value) {
@@ -18,13 +21,13 @@ export default createRule({
18
21
  }
19
22
  for (const query of el.rule.value) {
20
23
  const elements = el.querySelectorAll(query);
21
- elements.forEach(el => {
24
+ for (const el of elements) {
22
25
  const message = t('{0} is disallowed', t('the "{0*}" {1}', query, 'element'));
23
26
  report({
24
27
  scope: el,
25
28
  message,
26
29
  });
27
- });
30
+ }
28
31
  }
29
32
  });
30
33
  },
@@ -1,5 +1,8 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  defaultValue: 'always',
4
7
  defaultOptions: {
5
8
  denyObsoleteType: true,
@@ -1,6 +1,9 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import { isVoidElement } from '@markuplint/ml-spec';
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'style',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  async verify({ document, report, t }) {
6
9
  if (document.endTag === 'never') {
package/lib/helpers.js CHANGED
@@ -1,5 +1,9 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
1
6
  var _Collection_items;
2
- import { __classPrivateFieldGet } from "tslib";
3
7
  // @ts-ignore
4
8
  import structuredClone from '@ungap/structured-clone';
5
9
  import { attrCheck } from './attr-check.js';
@@ -13,7 +17,7 @@ node, condition) {
13
17
  return node.matches(condSelector);
14
18
  }
15
19
  export function match(needle, pattern) {
16
- const matches = pattern.match(/^\/(.*)\/(i|g|m)*$/);
20
+ const matches = pattern.match(/^\/(.*)\/([gim])*$/);
17
21
  if (matches && matches[1]) {
18
22
  const re = matches[1];
19
23
  const flag = matches[2];
@@ -87,7 +91,7 @@ export function toNormalizedValue(value, spec) {
87
91
  }
88
92
  if (typeof spec.type === 'string') {
89
93
  if (spec.type[0] === '<') {
90
- normalized = normalized.toLowerCase().trim().replace(/\s+/g, ' ');
94
+ normalized = normalized.toLowerCase().trim().replaceAll(/\s+/g, ' ');
91
95
  }
92
96
  }
93
97
  else {
@@ -99,10 +103,10 @@ export function toNormalizedValue(value, spec) {
99
103
  normalized = normalized.trim();
100
104
  }
101
105
  if (spec.type.separator === 'space') {
102
- normalized = normalized.replace(/\s+/g, ' ');
106
+ normalized = normalized.replaceAll(/\s+/g, ' ');
103
107
  }
104
108
  if (spec.type.separator === 'comma') {
105
- normalized = normalized.replace(/\s*,\s*/g, ',');
109
+ normalized = normalized.replaceAll(/\s*,\s*/g, ',');
106
110
  }
107
111
  }
108
112
  }
@@ -157,7 +161,7 @@ export class Collection {
157
161
  }
158
162
  }
159
163
  toArray() {
160
- return Object.freeze(Array.from(__classPrivateFieldGet(this, _Collection_items, "f")));
164
+ return Object.freeze([...__classPrivateFieldGet(this, _Collection_items, "f")]);
161
165
  }
162
166
  }
163
167
  export function deepCopy(value) {
@@ -1,5 +1,8 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'validation',
5
+ },
3
6
  async verify({ document, report, t }) {
4
7
  const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'value'), t('the "{0*}" {1}', 'id', 'attribute')), 'duplicated');
5
8
  const idStack = [];
package/lib/index.d.ts CHANGED
@@ -72,7 +72,7 @@ declare const rules: {
72
72
  }>>;
73
73
  readonly 'no-hard-code-id': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
74
74
  readonly 'no-refer-to-non-existent-id': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, {
75
- ariaVersion: "1.1" | "1.2" | "1.3";
75
+ ariaVersion: "1.2" | "1.1" | "1.3";
76
76
  fragmentRefersNameAttr: boolean;
77
77
  }>>;
78
78
  readonly 'no-use-event-handler-attr': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
@@ -81,7 +81,7 @@ declare const rules: {
81
81
  readonly 'permitted-contents': Readonly<import("@markuplint/ml-core").RuleSeed<import("./permitted-contents/types.js").TagRule[], import("./permitted-contents/types.js").Options>>;
82
82
  readonly 'placeholder-label-option': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, undefined>>;
83
83
  readonly 'require-accessible-name': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
84
- ariaVersion: "1.1" | "1.2" | "1.3";
84
+ ariaVersion: "1.2" | "1.1" | "1.3";
85
85
  }>>;
86
86
  readonly 'require-datetime': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
87
87
  langs?: import("./require-datetime/types.js").Lang[] | undefined;
@@ -96,6 +96,10 @@ declare const rules: {
96
96
  readonly 'required-h1': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, import("./required-h1/index.js").Options>>;
97
97
  readonly 'use-list': Readonly<import("@markuplint/ml-core").RuleSeed<readonly string[], {
98
98
  spaceNeededBullets?: string[] | undefined;
99
+ noPrev?: boolean | undefined;
100
+ prevElement?: boolean | undefined;
101
+ prevComment?: boolean | undefined;
102
+ prevCodeBlock?: boolean | undefined;
99
103
  }>>;
100
104
  readonly 'wai-aria': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, import("./wai-aria/types.js").Options>>;
101
105
  };
@@ -1,6 +1,9 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  import { toNoEmptyStringArrayFromStringOrArray } from '@markuplint/shared';
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'style',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  async verify({ document, report, t }) {
6
9
  await document.walkOn('Attr', attr => {
@@ -4,6 +4,9 @@ import { log as ruleLog } from '../debug.js';
4
4
  import { isValidAttr, match } from '../helpers.js';
5
5
  const log = ruleLog.extend('invalid-attr');
6
6
  export default createRule({
7
+ meta: {
8
+ category: 'validation',
9
+ },
7
10
  defaultOptions: {},
8
11
  async verify({ document, report, t }) {
9
12
  await document.walkOn('Attr', attr => {
@@ -1,6 +1,9 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  const controlSelector = ["input:not([type='hidden' i])", 'select', 'textarea'].join(',');
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'a11y',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  async verify({ document, report, t }) {
6
9
  await document.walkOn('Element', el => {
@@ -10,6 +10,9 @@ const selectors = {
10
10
  };
11
11
  const topLevelRoles = ['banner', 'main', 'complementary', 'contentinfo'];
12
12
  export default createRule({
13
+ meta: {
14
+ category: 'a11y',
15
+ },
13
16
  defaultSeverity: 'warning',
14
17
  defaultOptions: {
15
18
  ignoreRoles: [],
@@ -20,13 +23,13 @@ export default createRule({
20
23
  return;
21
24
  }
22
25
  const roles = {
23
- complementary: Array.from(document.querySelectorAll(selectors.complementary.join(','))),
24
- contentinfo: Array.from(document.querySelectorAll(selectors.contentinfo.join(','))),
25
- form: Array.from(document.querySelectorAll(selectors.form.join(','))),
26
- banner: Array.from(document.querySelectorAll(selectors.banner.join(','))),
27
- main: Array.from(document.querySelectorAll(selectors.main.join(','))),
28
- navigation: Array.from(document.querySelectorAll(selectors.navigation.join(','))),
29
- region: Array.from(document.querySelectorAll(selectors.region.join(','))),
26
+ complementary: [...document.querySelectorAll(selectors.complementary.join(','))],
27
+ contentinfo: [...document.querySelectorAll(selectors.contentinfo.join(','))],
28
+ form: [...document.querySelectorAll(selectors.form.join(','))],
29
+ banner: [...document.querySelectorAll(selectors.banner.join(','))],
30
+ main: [...document.querySelectorAll(selectors.main.join(','))],
31
+ navigation: [...document.querySelectorAll(selectors.navigation.join(','))],
32
+ region: [...document.querySelectorAll(selectors.region.join(','))],
30
33
  };
31
34
  /**
32
35
  * `<header>`
@@ -40,7 +43,7 @@ export default createRule({
40
43
  * > - nav
41
44
  * > - section
42
45
  */
43
- const headers = Array.from(document.querySelectorAll('header')).filter(header => {
46
+ const headers = [...document.querySelectorAll('header')].filter(header => {
44
47
  return !header.closest('article, aside, main, nav, section');
45
48
  });
46
49
  roles.banner.push(...headers);
@@ -56,7 +59,7 @@ export default createRule({
56
59
  * > - nav
57
60
  * > - section
58
61
  */
59
- const footers = Array.from(document.querySelectorAll('footer')).filter(footer => {
62
+ const footers = [...document.querySelectorAll('footer')].filter(footer => {
60
63
  return !footer.closest('article, aside, main, nav, section');
61
64
  });
62
65
  roles.contentinfo.push(...footers);
@@ -106,9 +109,7 @@ export default createRule({
106
109
  function landmarkRoleElementUUIDList(
107
110
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
108
111
  roleset) {
109
- return Object.values(roleset)
110
- .map(elements => elements.map(element => element.uuid))
111
- .flat();
112
+ return Object.values(roleset).flatMap(elements => elements.map(element => element.uuid));
112
113
  }
113
114
  function hasLabel(
114
115
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -1,5 +1,8 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'style',
5
+ },
3
6
  defaultSeverity: 'warning',
4
7
  async verify({ document, report, t }) {
5
8
  await document.walkOn('Attr', attr => {
@@ -1,6 +1,9 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  import { toNormalizedValue } from '../helpers.js';
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'style',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  async verify({ document, report, t }) {
6
9
  await document.walkOn('Attr', attr => {
@@ -1,6 +1,9 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import { isNothingContentModel, isPalpableElement } from '@markuplint/ml-spec';
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'validation',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  defaultOptions: {
6
9
  extendsExposableElements: true,
@@ -28,7 +31,7 @@ export default createRule({
28
31
  if (el.rule.options.ignoreIfAriaBusy && el.getAttribute('aria-busy') === 'true') {
29
32
  return;
30
33
  }
31
- const isEmpty = Array.from(el.childNodes).every(node => node.is(node.TEXT_NODE) && node.isWhitespace());
34
+ const isEmpty = [...el.childNodes].every(node => node.is(node.TEXT_NODE) && node.isWhitespace());
32
35
  if (isEmpty) {
33
36
  report({
34
37
  scope: el,
@@ -1,5 +1,8 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  export default createRule({
3
+ meta: {
4
+ category: 'maintainability',
5
+ },
3
6
  defaultSeverity: 'warning',
4
7
  async verify({ document, report, t }) {
5
8
  if (!document.isFragment) {
@@ -1,5 +1,5 @@
1
1
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, {
2
- ariaVersion: "1.1" | "1.2" | "1.3";
2
+ ariaVersion: "1.2" | "1.1" | "1.3";
3
3
  fragmentRefersNameAttr: boolean;
4
4
  }>>;
5
5
  export default _default;
@@ -3,6 +3,9 @@ import { ARIA_RECOMMENDED_VERSION } from '@markuplint/ml-spec';
3
3
  import { decodeEntities, decodeHref } from '@markuplint/shared';
4
4
  const HYPERLINK_SELECTOR = 'a[href], area[href]';
5
5
  export default createRule({
6
+ meta: {
7
+ category: 'a11y',
8
+ },
6
9
  defaultOptions: {
7
10
  ariaVersion: ARIA_RECOMMENDED_VERSION,
8
11
  fragmentRefersNameAttr: false,
@@ -16,10 +19,10 @@ export default createRule({
16
19
  if (isMutable) {
17
20
  return;
18
21
  }
19
- document.querySelectorAll('[id]').forEach(el => {
22
+ for (const el of document.querySelectorAll('[id]')) {
20
23
  const attr = el.getAttributeNode('id');
21
24
  if (!attr) {
22
- return;
25
+ continue;
23
26
  }
24
27
  if (attr.isDynamicValue) {
25
28
  hasDynamicId = true;
@@ -27,14 +30,14 @@ export default createRule({
27
30
  if (attr.valueType !== 'code') {
28
31
  idList.add(decodeEntities(attr.value));
29
32
  }
30
- });
33
+ }
31
34
  if (hasDynamicId) {
32
35
  return;
33
36
  }
34
- document.querySelectorAll('[name]').forEach(el => {
37
+ for (const el of document.querySelectorAll('[name]')) {
35
38
  const attr = el.getAttributeNode('name');
36
39
  if (!attr) {
37
- return;
40
+ continue;
38
41
  }
39
42
  if (attr.isDynamicValue) {
40
43
  hasDynamicName = true;
@@ -42,7 +45,7 @@ export default createRule({
42
45
  if (attr.valueType !== 'code') {
43
46
  nameList.add(decodeEntities(attr.value));
44
47
  }
45
- });
48
+ }
46
49
  await document.walkOn('Attr', attr => {
47
50
  const attrSpec = getAttrSpecs(attr.ownerElement, document.specs);
48
51
  if (!attrSpec) {
@@ -75,7 +78,7 @@ export default createRule({
75
78
  const refs = value
76
79
  .split(spec.type.separator === 'space' ? /\s/ : ',')
77
80
  .map(id => id.trim())
78
- .filter(_ => _);
81
+ .filter(Boolean);
79
82
  for (const ref of refs) {
80
83
  if (!idList.has(ref)) {
81
84
  report({
@@ -105,7 +108,7 @@ export default createRule({
105
108
  const refs = value
106
109
  .split(/\s/)
107
110
  .map(id => id.trim())
108
- .filter(_ => _);
111
+ .filter(Boolean);
109
112
  for (const ref of refs) {
110
113
  if (!idList.has(ref)) {
111
114
  report({
@@ -1,6 +1,9 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import { match } from '../helpers.js';
3
3
  export default createRule({
4
+ meta: {
5
+ category: 'maintainability',
6
+ },
4
7
  defaultSeverity: 'warning',
5
8
  defaultOptions: {},
6
9
  async verify({ document, report, t }) {