@markuplint/rules 4.11.2 → 5.0.0-alpha.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 (266) hide show
  1. package/CHANGELOG.md +63 -2
  2. package/SKILL.md +76 -0
  3. package/lib/attr-check.d.ts +46 -7
  4. package/lib/attr-check.js +42 -8
  5. package/lib/attr-duplication/index.d.ts +7 -0
  6. package/lib/attr-duplication/index.js +7 -0
  7. package/lib/attr-duplication/meta.d.ts +1 -0
  8. package/lib/attr-duplication/meta.js +1 -0
  9. package/lib/attr-value-quotes/index.d.ts +10 -0
  10. package/lib/attr-value-quotes/index.js +10 -0
  11. package/lib/attr-value-quotes/meta.d.ts +1 -0
  12. package/lib/attr-value-quotes/meta.js +1 -0
  13. package/lib/case-sensitive-attr-name/index.d.ts +9 -0
  14. package/lib/case-sensitive-attr-name/index.js +8 -0
  15. package/lib/case-sensitive-attr-name/meta.d.ts +1 -0
  16. package/lib/case-sensitive-attr-name/meta.js +1 -0
  17. package/lib/case-sensitive-tag-name/index.d.ts +9 -0
  18. package/lib/case-sensitive-tag-name/index.js +8 -0
  19. package/lib/case-sensitive-tag-name/meta.d.ts +1 -0
  20. package/lib/case-sensitive-tag-name/meta.js +1 -0
  21. package/lib/character-reference/index.d.ts +9 -0
  22. package/lib/character-reference/index.js +20 -0
  23. package/lib/character-reference/meta.d.ts +1 -0
  24. package/lib/character-reference/meta.js +1 -0
  25. package/lib/class-naming/index.d.ts +9 -0
  26. package/lib/class-naming/index.js +8 -0
  27. package/lib/class-naming/meta.d.ts +1 -0
  28. package/lib/class-naming/meta.js +1 -0
  29. package/lib/correct-aspect-ratio/index.d.ts +5 -0
  30. package/lib/correct-aspect-ratio/index.js +80 -0
  31. package/lib/correct-aspect-ratio/meta.d.ts +4 -0
  32. package/lib/correct-aspect-ratio/meta.js +3 -0
  33. package/lib/correct-aspect-ratio/resolve-image-size.d.ts +24 -0
  34. package/lib/correct-aspect-ratio/resolve-image-size.js +165 -0
  35. package/lib/create-message.d.ts +29 -2
  36. package/lib/create-message.js +60 -2
  37. package/lib/debug.d.ts +9 -0
  38. package/lib/debug.js +5 -0
  39. package/lib/deprecated-attr/index.d.ts +6 -0
  40. package/lib/deprecated-attr/index.js +6 -0
  41. package/lib/deprecated-attr/meta.d.ts +1 -0
  42. package/lib/deprecated-attr/meta.js +1 -0
  43. package/lib/deprecated-element/index.d.ts +6 -0
  44. package/lib/deprecated-element/index.js +8 -2
  45. package/lib/deprecated-element/meta.d.ts +1 -0
  46. package/lib/deprecated-element/meta.js +1 -0
  47. package/lib/disallowed-element/index.d.ts +7 -0
  48. package/lib/disallowed-element/index.js +7 -0
  49. package/lib/disallowed-element/meta.d.ts +1 -0
  50. package/lib/disallowed-element/meta.js +1 -0
  51. package/lib/doctype/index.d.ts +10 -0
  52. package/lib/doctype/index.js +8 -0
  53. package/lib/doctype/meta.d.ts +1 -0
  54. package/lib/doctype/meta.js +1 -0
  55. package/lib/end-tag/index.d.ts +7 -0
  56. package/lib/end-tag/index.js +8 -1
  57. package/lib/end-tag/meta.d.ts +1 -0
  58. package/lib/end-tag/meta.js +1 -0
  59. package/lib/heading-levels/index.d.ts +6 -0
  60. package/lib/heading-levels/index.js +6 -1
  61. package/lib/heading-levels/meta.d.ts +1 -0
  62. package/lib/heading-levels/meta.js +1 -0
  63. package/lib/helpers.d.ts +105 -0
  64. package/lib/helpers.js +112 -16
  65. package/lib/id-duplication/index.d.ts +7 -0
  66. package/lib/id-duplication/index.js +7 -0
  67. package/lib/id-duplication/meta.d.ts +1 -0
  68. package/lib/id-duplication/meta.js +1 -0
  69. package/lib/index.d.ts +43 -41
  70. package/lib/index.js +24 -0
  71. package/lib/ineffective-attr/index.d.ts +7 -0
  72. package/lib/ineffective-attr/index.js +7 -0
  73. package/lib/ineffective-attr/meta.d.ts +1 -0
  74. package/lib/ineffective-attr/meta.js +1 -0
  75. package/lib/invalid-attr/index.d.ts +29 -21
  76. package/lib/invalid-attr/index.js +60 -109
  77. package/lib/invalid-attr/meta.d.ts +1 -0
  78. package/lib/invalid-attr/meta.js +1 -0
  79. package/lib/label-has-control/index.d.ts +7 -0
  80. package/lib/label-has-control/index.js +11 -0
  81. package/lib/label-has-control/meta.d.ts +1 -0
  82. package/lib/label-has-control/meta.js +1 -0
  83. package/lib/landmark-roles/index.d.ts +14 -0
  84. package/lib/landmark-roles/index.js +21 -0
  85. package/lib/landmark-roles/meta.d.ts +1 -0
  86. package/lib/landmark-roles/meta.js +1 -0
  87. package/lib/link-types/index.d.ts +10 -0
  88. package/lib/link-types/index.js +180 -0
  89. package/lib/link-types/meta.d.ts +5 -0
  90. package/lib/link-types/meta.js +4 -0
  91. package/lib/neighbor-popovers/index.d.ts +8 -0
  92. package/lib/neighbor-popovers/index.js +28 -6
  93. package/lib/neighbor-popovers/meta.d.ts +1 -0
  94. package/lib/neighbor-popovers/meta.js +1 -0
  95. package/lib/no-ambiguous-navigable-target-names/index.d.ts +8 -0
  96. package/lib/no-ambiguous-navigable-target-names/index.js +8 -0
  97. package/lib/no-ambiguous-navigable-target-names/meta.d.ts +1 -0
  98. package/lib/no-ambiguous-navigable-target-names/meta.js +1 -0
  99. package/lib/no-boolean-attr-value/index.d.ts +7 -0
  100. package/lib/no-boolean-attr-value/index.js +7 -0
  101. package/lib/no-boolean-attr-value/meta.d.ts +1 -0
  102. package/lib/no-boolean-attr-value/meta.js +1 -0
  103. package/lib/no-consecutive-br/index.d.ts +8 -0
  104. package/lib/no-consecutive-br/index.js +8 -0
  105. package/lib/no-consecutive-br/meta.d.ts +1 -0
  106. package/lib/no-consecutive-br/meta.js +1 -0
  107. package/lib/no-default-value/index.d.ts +7 -0
  108. package/lib/no-default-value/index.js +7 -0
  109. package/lib/no-default-value/meta.d.ts +1 -0
  110. package/lib/no-default-value/meta.js +1 -0
  111. package/lib/no-duplicate-dt/index.d.ts +6 -0
  112. package/lib/no-duplicate-dt/index.js +6 -0
  113. package/lib/no-duplicate-dt/meta.d.ts +1 -0
  114. package/lib/no-duplicate-dt/meta.js +1 -0
  115. package/lib/no-empty-palpable-content/index.d.ts +13 -0
  116. package/lib/no-empty-palpable-content/index.js +12 -0
  117. package/lib/no-empty-palpable-content/meta.d.ts +1 -0
  118. package/lib/no-empty-palpable-content/meta.js +1 -0
  119. package/lib/no-hard-code-id/index.d.ts +8 -0
  120. package/lib/no-hard-code-id/index.js +8 -0
  121. package/lib/no-hard-code-id/meta.d.ts +1 -0
  122. package/lib/no-hard-code-id/meta.js +1 -0
  123. package/lib/no-orphaned-end-tag/index.d.ts +7 -0
  124. package/lib/no-orphaned-end-tag/index.js +8 -2
  125. package/lib/no-orphaned-end-tag/meta.d.ts +1 -0
  126. package/lib/no-orphaned-end-tag/meta.js +1 -0
  127. package/lib/no-refer-to-non-existent-id/index.d.ts +9 -1
  128. package/lib/no-refer-to-non-existent-id/index.js +11 -2
  129. package/lib/no-refer-to-non-existent-id/meta.d.ts +1 -0
  130. package/lib/no-refer-to-non-existent-id/meta.js +1 -0
  131. package/lib/no-unsupported-features/compat-data.d.ts +71 -0
  132. package/lib/no-unsupported-features/compat-data.js +204 -0
  133. package/lib/no-unsupported-features/index.d.ts +24 -0
  134. package/lib/no-unsupported-features/index.js +139 -0
  135. package/lib/no-unsupported-features/meta.d.ts +5 -0
  136. package/lib/no-unsupported-features/meta.js +4 -0
  137. package/lib/no-unsupported-features/resolve-browsers.d.ts +30 -0
  138. package/lib/no-unsupported-features/resolve-browsers.js +125 -0
  139. package/lib/no-use-event-handler-attr/index.d.ts +10 -0
  140. package/lib/no-use-event-handler-attr/index.js +8 -0
  141. package/lib/no-use-event-handler-attr/meta.d.ts +1 -0
  142. package/lib/no-use-event-handler-attr/meta.js +1 -0
  143. package/lib/permitted-contents/choice.d.ts +17 -0
  144. package/lib/permitted-contents/choice.js +31 -1
  145. package/lib/permitted-contents/complex-branch.d.ts +11 -7
  146. package/lib/permitted-contents/complex-branch.js +11 -7
  147. package/lib/permitted-contents/content-model.d.ts +11 -0
  148. package/lib/permitted-contents/content-model.js +32 -0
  149. package/lib/permitted-contents/count-pattern.d.ts +13 -7
  150. package/lib/permitted-contents/count-pattern.js +24 -8
  151. package/lib/permitted-contents/debug.browser.d.ts +12 -0
  152. package/lib/permitted-contents/debug.browser.js +12 -0
  153. package/lib/permitted-contents/debug.d.ts +12 -0
  154. package/lib/permitted-contents/debug.js +12 -0
  155. package/lib/permitted-contents/index.d.ts +12 -0
  156. package/lib/permitted-contents/index.js +21 -0
  157. package/lib/permitted-contents/matches-selector.d.ts +19 -0
  158. package/lib/permitted-contents/matches-selector.js +33 -0
  159. package/lib/permitted-contents/meta.d.ts +1 -0
  160. package/lib/permitted-contents/meta.js +1 -0
  161. package/lib/permitted-contents/order.d.ts +14 -7
  162. package/lib/permitted-contents/order.js +15 -8
  163. package/lib/permitted-contents/recursive-branch.d.ts +17 -0
  164. package/lib/permitted-contents/recursive-branch.js +17 -0
  165. package/lib/permitted-contents/represent-transparent-nodes.d.ts +54 -0
  166. package/lib/permitted-contents/represent-transparent-nodes.js +119 -12
  167. package/lib/permitted-contents/start.d.ts +12 -6
  168. package/lib/permitted-contents/start.js +12 -6
  169. package/lib/permitted-contents/transparent.d.ts +12 -0
  170. package/lib/permitted-contents/transparent.js +12 -0
  171. package/lib/permitted-contents/types.d.ts +67 -0
  172. package/lib/permitted-contents/utils.d.ts +160 -0
  173. package/lib/permitted-contents/utils.js +216 -35
  174. package/lib/placeholder-label-option/index.d.ts +7 -0
  175. package/lib/placeholder-label-option/index.js +19 -15
  176. package/lib/placeholder-label-option/meta.d.ts +1 -0
  177. package/lib/placeholder-label-option/meta.js +1 -0
  178. package/lib/redundant-accessible-name/index.d.ts +10 -0
  179. package/lib/redundant-accessible-name/index.js +123 -0
  180. package/lib/redundant-accessible-name/meta.d.ts +5 -0
  181. package/lib/redundant-accessible-name/meta.js +4 -0
  182. package/lib/require-accessible-name/index.d.ts +10 -4
  183. package/lib/require-accessible-name/index.js +13 -5
  184. package/lib/require-accessible-name/meta.d.ts +1 -0
  185. package/lib/require-accessible-name/meta.js +1 -0
  186. package/lib/require-datetime/index.d.ts +12 -0
  187. package/lib/require-datetime/index.js +8 -0
  188. package/lib/require-datetime/meta.d.ts +1 -0
  189. package/lib/require-datetime/meta.js +1 -0
  190. package/lib/require-datetime/types.d.ts +7 -0
  191. package/lib/require-datetime/utils.d.ts +18 -5
  192. package/lib/require-datetime/utils.js +53 -9
  193. package/lib/require-dialog-autofocus/index.d.ts +22 -0
  194. package/lib/require-dialog-autofocus/index.js +62 -0
  195. package/lib/require-dialog-autofocus/meta.d.ts +5 -0
  196. package/lib/require-dialog-autofocus/meta.js +4 -0
  197. package/lib/required-attr/index.d.ts +21 -1
  198. package/lib/required-attr/index.js +34 -4
  199. package/lib/required-attr/meta.d.ts +1 -0
  200. package/lib/required-attr/meta.js +1 -0
  201. package/lib/required-element/index.d.ts +14 -0
  202. package/lib/required-element/index.js +14 -2
  203. package/lib/required-element/meta.d.ts +1 -0
  204. package/lib/required-element/meta.js +1 -0
  205. package/lib/required-h1/index.d.ts +12 -0
  206. package/lib/required-h1/index.js +7 -0
  207. package/lib/required-h1/meta.d.ts +1 -0
  208. package/lib/required-h1/meta.js +1 -0
  209. package/lib/srcset-sizes-constraint/index.d.ts +6 -0
  210. package/lib/srcset-sizes-constraint/index.js +108 -0
  211. package/lib/srcset-sizes-constraint/meta.d.ts +5 -0
  212. package/lib/srcset-sizes-constraint/meta.js +4 -0
  213. package/lib/srcset-sizes-constraint/parse-srcset.d.ts +40 -0
  214. package/lib/srcset-sizes-constraint/parse-srcset.js +56 -0
  215. package/lib/table-row-column-alignment/find-children.d.ts +10 -0
  216. package/lib/table-row-column-alignment/find-children.js +11 -0
  217. package/lib/table-row-column-alignment/grid.d.ts +60 -0
  218. package/lib/table-row-column-alignment/grid.js +104 -26
  219. package/lib/table-row-column-alignment/index.d.ts +8 -0
  220. package/lib/table-row-column-alignment/index.js +8 -0
  221. package/lib/table-row-column-alignment/meta.d.ts +1 -0
  222. package/lib/table-row-column-alignment/meta.js +1 -0
  223. package/lib/table-row-column-alignment/types.d.ts +10 -0
  224. package/lib/use-list/index.d.ts +16 -0
  225. package/lib/use-list/index.js +20 -0
  226. package/lib/use-list/meta.d.ts +1 -0
  227. package/lib/use-list/meta.js +1 -0
  228. package/lib/wai-aria/checkings/abstract-role.d.ts +9 -0
  229. package/lib/wai-aria/checkings/abstract-role.js +14 -2
  230. package/lib/wai-aria/checkings/default-value.d.ts +11 -0
  231. package/lib/wai-aria/checkings/default-value.js +11 -0
  232. package/lib/wai-aria/checkings/deprecated-props.d.ts +11 -0
  233. package/lib/wai-aria/checkings/deprecated-props.js +11 -0
  234. package/lib/wai-aria/checkings/deprecated-role.d.ts +10 -0
  235. package/lib/wai-aria/checkings/deprecated-role.js +10 -0
  236. package/lib/wai-aria/checkings/disallowed-prop.d.ts +14 -0
  237. package/lib/wai-aria/checkings/disallowed-prop.js +19 -2
  238. package/lib/wai-aria/checkings/implicit-props.d.ts +13 -0
  239. package/lib/wai-aria/checkings/implicit-props.js +13 -0
  240. package/lib/wai-aria/checkings/implicit-role.d.ts +9 -0
  241. package/lib/wai-aria/checkings/implicit-role.js +14 -2
  242. package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +9 -0
  243. package/lib/wai-aria/checkings/interaction-in-hidden.js +15 -4
  244. package/lib/wai-aria/checkings/no-global-prop.d.ts +10 -0
  245. package/lib/wai-aria/checkings/no-global-prop.js +10 -0
  246. package/lib/wai-aria/checkings/non-existent-role.d.ts +11 -0
  247. package/lib/wai-aria/checkings/non-existent-role.js +19 -2
  248. package/lib/wai-aria/checkings/permitted-roles.d.ts +10 -0
  249. package/lib/wai-aria/checkings/permitted-roles.js +15 -2
  250. package/lib/wai-aria/checkings/presentational-children.d.ts +7 -1
  251. package/lib/wai-aria/checkings/presentational-children.js +17 -3
  252. package/lib/wai-aria/checkings/required-accessibility-parent-role.d.ts +25 -0
  253. package/lib/wai-aria/checkings/required-accessibility-parent-role.js +56 -0
  254. package/lib/wai-aria/checkings/required-owned-elements.d.ts +10 -1
  255. package/lib/wai-aria/checkings/required-owned-elements.js +69 -25
  256. package/lib/wai-aria/checkings/required-prop.d.ts +13 -0
  257. package/lib/wai-aria/checkings/required-prop.js +18 -2
  258. package/lib/wai-aria/checkings/value.d.ts +23 -0
  259. package/lib/wai-aria/checkings/value.js +32 -0
  260. package/lib/wai-aria/index.d.ts +10 -0
  261. package/lib/wai-aria/index.js +22 -4
  262. package/lib/wai-aria/meta.d.ts +1 -0
  263. package/lib/wai-aria/meta.js +1 -0
  264. package/lib/wai-aria/types.d.ts +25 -1
  265. package/package.json +16 -11
  266. package/schema.json +18 -0
@@ -2,28 +2,36 @@ import { createRule, getRoleSpec, getComputedRole } from '@markuplint/ml-core';
2
2
  import { ARIA_RECOMMENDED_VERSION, isExposed } from '@markuplint/ml-spec';
3
3
  import { accnameMayBeMutable } from '../helpers.js';
4
4
  import meta from './meta.js';
5
+ /**
6
+ * Rule that requires elements with roles that need accessible names to have one.
7
+ *
8
+ * For each element exposed to the accessibility tree whose computed ARIA role
9
+ * has `accessibleNameRequired` set to `true`, this rule verifies that the element
10
+ * provides a non-empty accessible name.
11
+ */
5
12
  export default createRule({
6
13
  meta: meta,
7
14
  defaultOptions: {
8
- ariaVersion: ARIA_RECOMMENDED_VERSION,
15
+ ariaVersion: undefined,
9
16
  },
10
17
  async verify({ document, report, t }) {
11
18
  await document.walkOn('Element', el => {
19
+ const ariaVersion = el.rule.options?.ariaVersion ?? document.ruleCommonSettings?.ariaVersion ?? ARIA_RECOMMENDED_VERSION;
12
20
  if (accnameMayBeMutable(el, document)) {
13
21
  return;
14
22
  }
15
- if (!isExposed(el, document.specs, el.rule.options.ariaVersion)) {
23
+ if (!isExposed(el, document.specs, ariaVersion)) {
16
24
  return;
17
25
  }
18
- const computed = getComputedRole(document.specs, el, el.rule.options.ariaVersion);
26
+ const computed = getComputedRole(document.specs, el, ariaVersion);
19
27
  if (!computed.role) {
20
28
  return;
21
29
  }
22
- const roleSpec = getRoleSpec(document.specs, computed.role.name, el.namespaceURI, el.rule.options.ariaVersion);
30
+ const roleSpec = getRoleSpec(document.specs, computed.role.name, el.namespaceURI, ariaVersion);
23
31
  if (!roleSpec || !roleSpec.accessibleNameRequired) {
24
32
  return;
25
33
  }
26
- const hasAccessibleName = !!el.getAccessibleName(el.rule.options.ariaVersion).trim();
34
+ const hasAccessibleName = !!el.getAccessibleName(ariaVersion).trim();
27
35
  if (!hasAccessibleName) {
28
36
  report({ scope: el, message: t('Require {0}', 'accessible name') });
29
37
  }
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `require-accessible-name` rule, categorized as accessibility. */
1
2
  declare const _default: {
2
3
  readonly category: "a11y";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `require-accessible-name` rule, categorized as accessibility. */
1
2
  export default {
2
3
  category: 'a11y',
3
4
  };
@@ -1,6 +1,18 @@
1
1
  import type { Lang } from './types.js';
2
+ /**
3
+ * Configuration options for the require-datetime rule.
4
+ */
2
5
  type Options = {
6
+ /** Languages to use when parsing natural language date/time text content. */
3
7
  langs?: Lang[];
4
8
  };
9
+ /**
10
+ * Rule that validates `<time>` elements without a `datetime` attribute have
11
+ * machine-readable date/time content.
12
+ *
13
+ * When a `<time>` element lacks the `datetime` attribute, this rule checks
14
+ * whether the text content is a valid datetime string. If not, it attempts
15
+ * to parse the content as natural language and suggests a `datetime` value.
16
+ */
5
17
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
6
18
  export default _default;
@@ -2,6 +2,14 @@ import { createRule } from '@markuplint/ml-core';
2
2
  import { check } from '@markuplint/types';
3
3
  import meta from './meta.js';
4
4
  import { getCandidateDatetimeString } from './utils.js';
5
+ /**
6
+ * Rule that validates `<time>` elements without a `datetime` attribute have
7
+ * machine-readable date/time content.
8
+ *
9
+ * When a `<time>` element lacks the `datetime` attribute, this rule checks
10
+ * whether the text content is a valid datetime string. If not, it attempts
11
+ * to parse the content as natural language and suggests a `datetime` value.
12
+ */
5
13
  export default createRule({
6
14
  meta: meta,
7
15
  defaultOptions: {
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `require-datetime` rule, categorized as validation. */
1
2
  declare const _default: {
2
3
  readonly category: "validation";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `require-datetime` rule, categorized as validation. */
1
2
  export default {
2
3
  category: 'validation',
3
4
  };
@@ -1,10 +1,17 @@
1
+ /** Union of possible date/time component keys used in parsed datetime data. */
1
2
  export type DateTimeKey = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'ms';
3
+ /** A partial record mapping date/time component keys to their numeric values. */
2
4
  export type DateTimeData = Partial<Record<DateTimeKey, number>>;
5
+ /** Represents a parsed date/time value with optional timezone offset. */
3
6
  export type DateTime = {
7
+ /** The parsed date/time component values. */
4
8
  datetime: DateTimeData;
9
+ /** Timezone offset in minutes from UTC (e.g., -300 for UTC-5). */
5
10
  zone?: number;
6
11
  };
7
12
  /**
13
+ * Supported locale codes for natural language date/time parsing.
14
+ *
8
15
  * @see https://github.com/wanasit/chrono#locales
9
16
  */
10
17
  export type Lang = 'en' | 'ja' | 'fr' | 'nl' | 'ru' | 'de' | 'pt' | 'zh';
@@ -1,11 +1,24 @@
1
1
  import type { DateTime, Lang } from './types.js';
2
2
  /**
3
- * Datetime-ish text to a datetime data
3
+ * Parses natural language datetime text into structured datetime data.
4
4
  *
5
- * @param content
6
- * @param langs
7
- * @param base Reference date for a test
8
- * @returns
5
+ * Tries multiple locale parsers and returns the first successful parse result
6
+ * with only the certain (non-implied) date/time components included.
7
+ *
8
+ * @param content - The text content to parse as a datetime.
9
+ * @param langs - Locale codes to attempt parsing with.
10
+ * @param base - Optional reference date for relative date parsing (e.g., "tomorrow").
11
+ * @returns Parsed datetime data with timezone, or `null` if parsing fails.
9
12
  */
10
13
  export declare function parseADatetime(content: string, langs: readonly Lang[], base?: Readonly<Date>): DateTime | null;
14
+ /**
15
+ * Generates an HTML `datetime` attribute value from natural language text.
16
+ *
17
+ * Parses the text content using chrono-node and formats the result as an
18
+ * ISO 8601-like datetime string suitable for the `datetime` attribute.
19
+ *
20
+ * @param content - The text content to parse.
21
+ * @param langs - Locale codes to use for parsing. Defaults to all supported languages.
22
+ * @returns A formatted datetime string, or `null` if the content cannot be parsed.
23
+ */
11
24
  export declare function getCandidateDatetimeString(content: string, langs?: Lang[]): string | null;
@@ -1,12 +1,16 @@
1
1
  import * as chrono from 'chrono-node';
2
+ /** Default set of languages to try when parsing natural language datetime text. */
2
3
  const defaultLangs = ['en', 'ja', 'fr', 'nl', 'ru', 'de', 'pt', 'zh'];
3
4
  /**
4
- * Datetime-ish text to a datetime data
5
+ * Parses natural language datetime text into structured datetime data.
5
6
  *
6
- * @param content
7
- * @param langs
8
- * @param base Reference date for a test
9
- * @returns
7
+ * Tries multiple locale parsers and returns the first successful parse result
8
+ * with only the certain (non-implied) date/time components included.
9
+ *
10
+ * @param content - The text content to parse as a datetime.
11
+ * @param langs - Locale codes to attempt parsing with.
12
+ * @param base - Optional reference date for relative date parsing (e.g., "tomorrow").
13
+ * @returns Parsed datetime data with timezone, or `null` if parsing fails.
10
14
  */
11
15
  export function parseADatetime(content, langs, base) {
12
16
  const date = parseTryMultipleLangs(content, langs, base);
@@ -43,6 +47,16 @@ export function parseADatetime(content, langs, base) {
43
47
  }
44
48
  return datetime;
45
49
  }
50
+ /**
51
+ * Generates an HTML `datetime` attribute value from natural language text.
52
+ *
53
+ * Parses the text content using chrono-node and formats the result as an
54
+ * ISO 8601-like datetime string suitable for the `datetime` attribute.
55
+ *
56
+ * @param content - The text content to parse.
57
+ * @param langs - Locale codes to use for parsing. Defaults to all supported languages.
58
+ * @returns A formatted datetime string, or `null` if the content cannot be parsed.
59
+ */
46
60
  export function getCandidateDatetimeString(content, langs = defaultLangs) {
47
61
  const date = parseADatetime(content, langs);
48
62
  if (!date) {
@@ -60,6 +74,15 @@ export function getCandidateDatetimeString(content, langs = defaultLangs) {
60
74
  }
61
75
  return datetimeStr;
62
76
  }
77
+ /**
78
+ * Converts parsed datetime data into an ISO 8601-like string.
79
+ *
80
+ * Produces different formats depending on which components are present
81
+ * (e.g., date-only, time-only, or combined date-time).
82
+ *
83
+ * @param date - The parsed date/time component data.
84
+ * @returns A formatted datetime string, or `null` if the components do not match any known format.
85
+ */
63
86
  function toDatetimeString(date) {
64
87
  if (only(date, ['year', 'month'])) {
65
88
  return `${f(date.year, 4)}-${f(date.month, 2)}`;
@@ -90,6 +113,17 @@ function toDatetimeString(date) {
90
113
  }
91
114
  return null;
92
115
  }
116
+ /**
117
+ * Attempts to parse datetime text using multiple locale parsers sequentially.
118
+ *
119
+ * Returns the parsed start component from the first locale that produces a
120
+ * valid result (non-range, single datetime).
121
+ *
122
+ * @param content - The text content to parse.
123
+ * @param langs - Locale codes to try in order.
124
+ * @param base - Optional reference date for relative date parsing.
125
+ * @returns The parsed start component, or `null` if no locale succeeds.
126
+ */
93
127
  function parseTryMultipleLangs(content, langs, base) {
94
128
  for (const lang of langs) {
95
129
  const results =
@@ -111,6 +145,16 @@ function parseTryMultipleLangs(content, langs, base) {
111
145
  }
112
146
  return null;
113
147
  }
148
+ /**
149
+ * Type guard that checks if the given datetime data contains only the specified keys.
150
+ *
151
+ * @template K - The array of datetime keys to check for.
152
+ * @template U - Union of the key types.
153
+ * @template R - The resulting narrowed type with required properties.
154
+ * @param date - The datetime data to check.
155
+ * @param keys - The keys that should be the only ones present.
156
+ * @returns `true` if the date contains only the specified keys (and narrows the type).
157
+ */
114
158
  function only(date, keys) {
115
159
  const list = Object.keys(date);
116
160
  for (const exists of list) {
@@ -121,11 +165,11 @@ function only(date, keys) {
121
165
  return true;
122
166
  }
123
167
  /**
124
- * Formatter
168
+ * Formats a number with zero-padding to the specified width.
125
169
  *
126
- * @param n
127
- * @param pad zero padding
128
- * @returns
170
+ * @param n - The number to format.
171
+ * @param pad - The minimum number of digits in the output string.
172
+ * @returns The zero-padded string representation.
129
173
  */
130
174
  function f(n, pad) {
131
175
  return n.toString(10).padStart(pad, '0');
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Rule that requires a `<dialog>` element referenced by a `show-modal` command
3
+ * to contain a descendant (or itself) with the `autofocus` attribute.
4
+ *
5
+ * Without `autofocus`, the browser's dialog focusing steps fall back to the
6
+ * `<dialog>` element itself, which is not ideal for accessibility — screen
7
+ * reader users may miss the dialog content, and keyboard users may need
8
+ * extra tab presses to reach interactive elements.
9
+ *
10
+ * Detection algorithm:
11
+ * 1. Find all `<button>` elements with both `command` and `commandfor` attributes
12
+ * 2. Filter to triggers whose `command` value is `show-modal` (case-insensitive)
13
+ * 3. Resolve the referenced `<dialog>` via `document.getElementById(commandfor)`
14
+ * 4. Skip non-dialog targets and already-reported dialogs (deduplication)
15
+ * 5. Report if neither the dialog nor any descendant has the `autofocus` attribute
16
+ *
17
+ * @see https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps
18
+ * @see https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
19
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
20
+ */
21
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
22
+ export default _default;
@@ -0,0 +1,62 @@
1
+ import { createRule } from '@markuplint/ml-core';
2
+ import meta from './meta.js';
3
+ /**
4
+ * Rule that requires a `<dialog>` element referenced by a `show-modal` command
5
+ * to contain a descendant (or itself) with the `autofocus` attribute.
6
+ *
7
+ * Without `autofocus`, the browser's dialog focusing steps fall back to the
8
+ * `<dialog>` element itself, which is not ideal for accessibility — screen
9
+ * reader users may miss the dialog content, and keyboard users may need
10
+ * extra tab presses to reach interactive elements.
11
+ *
12
+ * Detection algorithm:
13
+ * 1. Find all `<button>` elements with both `command` and `commandfor` attributes
14
+ * 2. Filter to triggers whose `command` value is `show-modal` (case-insensitive)
15
+ * 3. Resolve the referenced `<dialog>` via `document.getElementById(commandfor)`
16
+ * 4. Skip non-dialog targets and already-reported dialogs (deduplication)
17
+ * 5. Report if neither the dialog nor any descendant has the `autofocus` attribute
18
+ *
19
+ * @see https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps
20
+ * @see https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
21
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
22
+ */
23
+ export default createRule({
24
+ defaultSeverity: 'warning',
25
+ meta: meta,
26
+ verify({ document, report, t }) {
27
+ const triggers = document.querySelectorAll('button[command][commandfor]');
28
+ const reportedDialogIds = new Set();
29
+ for (const trigger of triggers) {
30
+ const command = trigger.getAttribute('command');
31
+ if (!command || command.toLowerCase() !== 'show-modal') {
32
+ continue;
33
+ }
34
+ const targetId = trigger.getAttribute('commandfor');
35
+ if (!targetId) {
36
+ continue;
37
+ }
38
+ if (reportedDialogIds.has(targetId)) {
39
+ continue;
40
+ }
41
+ const target = document.getElementById(targetId);
42
+ if (!target) {
43
+ continue;
44
+ }
45
+ if (target.localName !== 'dialog') {
46
+ continue;
47
+ }
48
+ if (target.hasAttribute('autofocus')) {
49
+ continue;
50
+ }
51
+ const autofocusDescendants = target.querySelectorAll('[autofocus]');
52
+ if (autofocusDescendants.length > 0) {
53
+ continue;
54
+ }
55
+ reportedDialogIds.add(targetId);
56
+ report({
57
+ scope: target,
58
+ message: t('The "{0*}" element referenced by a "{1*}" command requires an element with the "{2*}" attribute', 'dialog', 'show-modal', 'autofocus'),
59
+ });
60
+ }
61
+ },
62
+ });
@@ -0,0 +1,5 @@
1
+ /** Rule metadata for the `require-dialog-autofocus` rule, categorized as accessibility. */
2
+ declare const _default: {
3
+ readonly category: "a11y";
4
+ };
5
+ export default _default;
@@ -0,0 +1,4 @@
1
+ /** Rule metadata for the `require-dialog-autofocus` rule, categorized as accessibility. */
2
+ export default {
3
+ category: 'a11y',
4
+ };
@@ -1,7 +1,27 @@
1
+ /** The rule value: either a single attribute name or an array of required attribute descriptors. */
1
2
  type RequiredAttributes = string | (string | Attr)[];
3
+ /**
4
+ * Descriptor for a required attribute with an optional set of allowed values.
5
+ */
2
6
  type Attr = {
7
+ /** The attribute name that is required. */
3
8
  name: string;
9
+ /** One or more allowed values for the attribute, if constrained. */
4
10
  value?: string | string[];
5
11
  };
6
- declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RequiredAttributes, undefined>>;
12
+ /**
13
+ * Configuration options for the `required-attr` rule.
14
+ */
15
+ type Options = {
16
+ /** Attribute names to exclude from required-attribute checks. */
17
+ readonly ignoreAttrs?: readonly string[];
18
+ };
19
+ /**
20
+ * Rule that validates elements have all required attributes.
21
+ *
22
+ * Checks both HTML-spec-defined required attributes and custom required attributes
23
+ * specified in the rule configuration. Also validates that required attribute values
24
+ * match expected patterns when value constraints are provided.
25
+ */
26
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RequiredAttributes, Options>>;
7
27
  export default _default;
@@ -1,9 +1,17 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  import { attrMatches, match } from '../helpers.js';
3
3
  import meta from './meta.js';
4
+ /**
5
+ * Rule that validates elements have all required attributes.
6
+ *
7
+ * Checks both HTML-spec-defined required attributes and custom required attributes
8
+ * specified in the rule configuration. Also validates that required attribute values
9
+ * match expected patterns when value constraints are provided.
10
+ */
4
11
  export default createRule({
5
12
  meta: meta,
6
13
  defaultValue: [],
14
+ defaultOptions: {},
7
15
  async verify({ document, report, t }) {
8
16
  await document.walkOn('Element', el => {
9
17
  if (el.hasSpreadAttr) {
@@ -11,6 +19,7 @@ export default createRule({
11
19
  }
12
20
  const customRequiredAttrs = typeof el.rule.value === 'string' ? [el.rule.value] : el.rule.value;
13
21
  const attrSpec = getAttrSpecs(el, document.specs);
22
+ const ignoreAttrs = new Set(el.rule.options.ignoreAttrs);
14
23
  const attributeSpecs = {};
15
24
  if (attrSpec && el.elementType === 'html') {
16
25
  for (const spec of attrSpec) {
@@ -44,12 +53,33 @@ export default createRule({
44
53
  };
45
54
  }
46
55
  for (const spec of Object.values(attributeSpecs)) {
56
+ if (ignoreAttrs.has(spec.name)) {
57
+ if (spec.requiredEither) {
58
+ const activeCandidates = spec.requiredEither.filter(n => !ignoreAttrs.has(n));
59
+ if (activeCandidates.length > 0) {
60
+ const invalid = !activeCandidates.some(attrName => el.hasAttribute(attrName));
61
+ if (invalid) {
62
+ const sortedCandidate = activeCandidates.toSorted();
63
+ const expects = sortedCandidate.length === 1
64
+ ? t('the "{0*}" {1}', sortedCandidate[0], 'attribute')
65
+ : t('{0} {1}', sortedCandidate
66
+ .map(attrName => t('the "{0*}"', attrName))
67
+ // eslint-disable-next-line unicorn/no-array-reduce
68
+ .reduce((a, b) => t('{0} or {1}', a, b)), t('attribute'));
69
+ const message = t('{0} expects {1}', t('the "{0*}" {1}', el.localName, 'element'), expects);
70
+ report({ scope: el, message });
71
+ }
72
+ }
73
+ }
74
+ continue;
75
+ }
47
76
  const didntHave = !el.hasAttribute(spec.name);
48
77
  const candidate = [spec.name];
49
78
  let invalid = false;
50
79
  if (spec.requiredEither) {
51
80
  candidate.push(...spec.requiredEither);
52
- invalid = !candidate.some(attrName => el.hasAttribute(attrName));
81
+ const activeCandidates = candidate.filter(n => !ignoreAttrs.has(n));
82
+ invalid = !activeCandidates.some(attrName => el.hasAttribute(attrName));
53
83
  }
54
84
  else if (spec.required === true) {
55
85
  invalid = attrMatches(el, spec.condition) && didntHave;
@@ -58,11 +88,11 @@ export default createRule({
58
88
  const selector = typeof spec.required === 'string' ? spec.required : spec.required.join(',');
59
89
  invalid = el.matches(selector) && didntHave;
60
90
  }
61
- candidate.sort();
91
+ const sortedCandidate = candidate.filter(n => !ignoreAttrs.has(n)).toSorted();
62
92
  if (invalid) {
63
- const expects = candidate.length === 1
93
+ const expects = sortedCandidate.length === 1
64
94
  ? t('the "{0*}" {1}', spec.name, 'attribute')
65
- : t('{0} {1}', candidate
95
+ : t('{0} {1}', sortedCandidate
66
96
  .map(attrName => t('the "{0*}"', attrName))
67
97
  // eslint-disable-next-line unicorn/no-array-reduce
68
98
  .reduce((a, b) => t('{0} or {1}', a, b)), t('attribute'));
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-attr` rule, categorized as validation. */
1
2
  declare const _default: {
2
3
  readonly category: "validation";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-attr` rule, categorized as validation. */
1
2
  export default {
2
3
  category: 'validation',
3
4
  };
@@ -1,5 +1,19 @@
1
+ /**
2
+ * Configuration options for the required-element rule.
3
+ */
1
4
  type Options = {
5
+ /** Whether to skip validation for elements that contain mutable (dynamic) content. */
2
6
  ignoreHasMutableContents: boolean;
7
+ /** Whether to ignore omitted (ghost) elements that are implicitly created by the HTML parser. */
8
+ ignoreOmittedElements: boolean;
3
9
  };
10
+ /**
11
+ * Rule that validates required child elements are present.
12
+ *
13
+ * Accepts an array of CSS selectors as the rule value. For the document level,
14
+ * reports if any matching element is missing from the entire document. For
15
+ * element-level overrides, checks that the specified child elements exist
16
+ * among the element's direct children.
17
+ */
4
18
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<string[], Options>>;
5
19
  export default _default;
@@ -1,16 +1,28 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Rule that validates required child elements are present.
5
+ *
6
+ * Accepts an array of CSS selectors as the rule value. For the document level,
7
+ * reports if any matching element is missing from the entire document. For
8
+ * element-level overrides, checks that the specified child elements exist
9
+ * among the element's direct children.
10
+ */
3
11
  export default createRule({
4
12
  meta: meta,
5
13
  defaultValue: [],
6
14
  defaultOptions: {
7
15
  ignoreHasMutableContents: true,
16
+ ignoreOmittedElements: true,
8
17
  },
9
18
  async verify({ document, report, t }) {
10
19
  const hasMutableContent = document.nodeList.some(n => n.is(n.ELEMENT_NODE) && n.hasMutableChildren());
11
20
  if (!hasMutableContent) {
12
21
  for (const query of document.rule.value) {
13
- const exists = document.querySelectorAll(query);
22
+ const matched = document.querySelectorAll(query);
23
+ const exists = document.rule.options.ignoreOmittedElements
24
+ ? [...matched].filter(el => !el.isOmitted)
25
+ : [...matched];
14
26
  if (exists.length === 0) {
15
27
  const message = t('Require {0}', t('the "{0*}" {1}', query, 'element'));
16
28
  report({
@@ -30,7 +42,7 @@ export default createRule({
30
42
  return;
31
43
  }
32
44
  for (const query of el.rule.value) {
33
- const exists = [...el.children].find(child => child.matches(query));
45
+ const exists = [...el.children].find(child => !(el.rule.options.ignoreOmittedElements && child.isOmitted) && child.matches(query));
34
46
  if (!exists) {
35
47
  const message = t('Require {0}', t('the "{0*}" {1}', query, 'element'));
36
48
  report({
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-element` rule, categorized as validation. */
1
2
  declare const _default: {
2
3
  readonly category: "validation";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-element` rule, categorized as validation. */
1
2
  export default {
2
3
  category: 'validation',
3
4
  };
@@ -1,6 +1,18 @@
1
+ /**
2
+ * Configuration options for the required-h1 rule.
3
+ */
1
4
  export interface Options {
5
+ /** Whether to report a violation when more than one `<h1>` element is found. */
2
6
  'expected-once': boolean;
7
+ /** Whether to apply this rule in document fragments (components, partials). */
3
8
  'in-document-fragment': boolean;
4
9
  }
10
+ /**
11
+ * Rule that requires exactly one `<h1>` element in the document.
12
+ *
13
+ * Reports a violation when no `<h1>` is present. Optionally reports when
14
+ * multiple `<h1>` elements exist (controlled by `expected-once`). Can be
15
+ * configured to skip document fragments via the `in-document-fragment` option.
16
+ */
5
17
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
6
18
  export default _default;
@@ -1,5 +1,12 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Rule that requires exactly one `<h1>` element in the document.
5
+ *
6
+ * Reports a violation when no `<h1>` is present. Optionally reports when
7
+ * multiple `<h1>` elements exist (controlled by `expected-once`). Can be
8
+ * configured to skip document fragments via the `in-document-fragment` option.
9
+ */
3
10
  export default createRule({
4
11
  meta: meta,
5
12
  defaultOptions: {
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-h1` rule, categorized as accessibility. */
1
2
  declare const _default: {
2
3
  readonly category: "a11y";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for the `required-h1` rule, categorized as accessibility. */
1
2
  export default {
2
3
  category: 'a11y',
3
4
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Rule that enforces WHATWG constraints between the `srcset`, `sizes`,
3
+ * and `loading` attributes on `<img>` and `<source>` elements.
4
+ */
5
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, undefined>>;
6
+ export default _default;