@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
package/CHANGELOG.md CHANGED
@@ -3,13 +3,74 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [4.11.2](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.1...@markuplint/rules@4.11.2) (2025-11-05)
6
+ # [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
7
7
 
8
- **Note:** Version bump only for package @markuplint/rules
8
+ ### Bug Fixes
9
9
 
10
+ - **rules:** address review feedback for require-dialog-autofocus ([6fcadb2](https://github.com/markuplint/markuplint/commit/6fcadb2ab0e6a974e8ba6f644eff914a7840b8ef))
11
+ - treat orphaned end tags as bogus instead of plain text ([#1575](https://github.com/markuplint/markuplint/issues/1575)) ([557199a](https://github.com/markuplint/markuplint/commit/557199a6960ab35573a544f9a33c00e98eb9967e))
10
12
 
13
+ - feat(rules)!: simplify invalid-attr options and remove attrs ([3ced12d](https://github.com/markuplint/markuplint/commit/3ced12d200f12ba4c9e177c9aaca25e7e24a9151))
14
+ - feat(rules)!: add no-unsupported-features rule for browser support checks ([8525a96](https://github.com/markuplint/markuplint/commit/8525a96fb04f634820362a9a73d6541284b53686))
15
+ - feat(rules)!: change ignoreOmittedElements default to true ([5ec04a7](https://github.com/markuplint/markuplint/commit/5ec04a7d63cbf19846b42124c22653197f603a59)), closes [#3136](https://github.com/markuplint/markuplint/issues/3136)
16
+ - refactor(rules)!: replace selfClosingSolidus with tagCloseChar ([f9cd9d8](https://github.com/markuplint/markuplint/commit/f9cd9d81bfb0ba49c2578eace7b04a9a1ebdd12a))
11
17
 
18
+ ### Features
19
+
20
+ - **html-spec:** require href or imagesrcset on link element ([e6a2631](https://github.com/markuplint/markuplint/commit/e6a26318ed8e4be9ba4e81884eb0b879a816efb5)), closes [#717](https://github.com/markuplint/markuplint/issues/717)
21
+ - **ml-spec,rules:** adopt ARIA 1.3 property names with 1.2 compat ([4f7e54d](https://github.com/markuplint/markuplint/commit/4f7e54d21593495d36e48fbe8ad27f8be85ab5ef))
22
+ - **rules:** add correct-aspect-ratio rule ([4068582](https://github.com/markuplint/markuplint/commit/40685828da697dfa8565288438a5d027dd0fae45))
23
+ - **rules:** add ignoreAttrs option to required-attr rule ([#690](https://github.com/markuplint/markuplint/issues/690)) ([fe4ce5e](https://github.com/markuplint/markuplint/commit/fe4ce5e283bc9e5284d209322dc0bbe3cbe07d55))
24
+ - **rules:** add link-types rule for rel attribute validation ([95d22a5](https://github.com/markuplint/markuplint/commit/95d22a596c1f786c385b7e99c3b818434995f4b5))
25
+ - **rules:** add redundant-accessible-name rule for detecting overridden accessible name sources ([63b89d4](https://github.com/markuplint/markuplint/commit/63b89d47bf5056f823d5b27cda4fda2b96419bb3))
26
+ - **rules:** add require-dialog-autofocus rule ([2d8d650](https://github.com/markuplint/markuplint/commit/2d8d650f8bc82e706687d292b27c310f3552b418)), closes [#689](https://github.com/markuplint/markuplint/issues/689)
27
+ - **rules:** add required context role check to wai-aria rule ([#970](https://github.com/markuplint/markuplint/issues/970)) ([1433a5a](https://github.com/markuplint/markuplint/commit/1433a5ae429aca4e7dc0a1a7f514a184eb423c03))
28
+ - **rules:** add srcset-sizes-constraint rule ([#1051](https://github.com/markuplint/markuplint/issues/1051)) ([bcc624a](https://github.com/markuplint/markuplint/commit/bcc624a3c6535ef85ad155b05d7571198983277a))
29
+ - **rules:** recognize DPub ARIA roles in wai-aria rule ([3dd32ff](https://github.com/markuplint/markuplint/commit/3dd32ffa7e5e706bad33bf9b9ff1785de8c8e630))
30
+ - **rules:** suggest similar attribute names for typos in invalid-attr rule ([337c7c5](https://github.com/markuplint/markuplint/commit/337c7c56b792cf7bc72baa3a2528f0c93892d550)), closes [#1487](https://github.com/markuplint/markuplint/issues/1487)
31
+ - **rules:** support ARIA 1.3 generic transparency in owned elements check ([c1d6991](https://github.com/markuplint/markuplint/commit/c1d6991cb197d511c5751bba183fde1208001590))
32
+ - **rules:** support source elements inside picture in correct-aspect-ratio ([3cd3dc9](https://github.com/markuplint/markuplint/commit/3cd3dc9981a04f4ff5b78b97c28cc7852d31886e))
33
+ - **rules:** use ruleCommonSettings.ariaVersion fallback in ARIA rules ([07ccd8a](https://github.com/markuplint/markuplint/commit/07ccd8acd8b9db11ccf473b419a3d01cc782e15c))
34
+
35
+ ### Performance Improvements
36
+
37
+ - **rules:** fix exponential slowdown in transparent element resolution ([f657e6a](https://github.com/markuplint/markuplint/commit/f657e6a0156933c3acafb8e3975fbc31488b7cca)), closes [#3249](https://github.com/markuplint/markuplint/issues/3249)
38
+
39
+ ### BREAKING CHANGES
40
+
41
+ - Remove the deprecated `attrs` option and the
42
+ `{ type: X }` value wrapper from `invalid-attr` rule options.
43
+
44
+ * Remove `attrs` option (deprecated since v3.7.0)
45
+ * Remove `{ type: X }` wrapper; specify type strings directly
46
+ * Deprecate object format for `allowAttrs`/`disallowAttrs`
47
+ * Unify value type validation via the types API
48
+
49
+ - non-standard element/attribute detection moved from
50
+ deprecated-element to no-unsupported-features with checkNonStandard option.
51
+ Users of recommended preset are unaffected (auto-enabled via compat preset).
52
+ - The `ignoreOmittedElements` option in the
53
+ `required-element` rule now defaults to `true` instead of `false`.
54
+ Ghost (omitted) elements implicitly created by the HTML parser
55
+ are no longer counted as satisfying the requirement by default.
56
+ Users who relied on the previous behavior should explicitly set
57
+ `ignoreOmittedElements: false`.
58
+ - The end-tag rule now checks tagCloseChar for
59
+ self-closing detection instead of the removed selfClosingSolidus
60
+ property.
61
+
62
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63
+
64
+ # [4.12.0](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.2...@markuplint/rules@4.12.0) (2026-02-10)
12
65
 
66
+ ### Features
67
+
68
+ - **rules:** add ignoreOmittedElements option to required-element ([0cc879c](https://github.com/markuplint/markuplint/commit/0cc879ca175491da7eda4d6f4d0638b74fe528e5)), closes [#2066](https://github.com/markuplint/markuplint/issues/2066)
69
+ - **rules:** support Invoker Commands API in neighbor-popovers rule ([963853f](https://github.com/markuplint/markuplint/commit/963853f9f3701ab552d7d03fe5712a671c50d444)), closes [#3065](https://github.com/markuplint/markuplint/issues/3065)
70
+
71
+ ## [4.11.2](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.1...@markuplint/rules@4.11.2) (2025-11-05)
72
+
73
+ **Note:** Version bump only for package @markuplint/rules
13
74
 
14
75
  ## [4.11.1](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.0...@markuplint/rules@4.11.1) (2025-08-24)
15
76
 
package/SKILL.md ADDED
@@ -0,0 +1,76 @@
1
+ ---
2
+ description: Perform maintenance tasks for @markuplint/rules
3
+ ---
4
+
5
+ # rules-maintenance
6
+
7
+ Maintain rule test suites, especially when `@markuplint/html-spec` changes.
8
+
9
+ ## Input
10
+
11
+ `$ARGUMENTS` specifies the task:
12
+
13
+ | Task | Description |
14
+ | --------------------------------- | ------------------------------------- |
15
+ | `add-spec-tests <rule> <element>` | Add tests after a spec change |
16
+ | `update-test-expectations` | Fix tests broken by spec data changes |
17
+
18
+ ## Task: add-spec-tests
19
+
20
+ ### Which test file?
21
+
22
+ | Spec change | Test file |
23
+ | ----------------------------------- | -------------------------------------- |
24
+ | ARIA (implicitRole, permittedRoles) | `src/wai-aria/index.spec.ts` |
25
+ | Attributes (new/changed/enum) | `src/invalid-attr/index.spec.ts` |
26
+ | Content model | `src/permitted-contents/index.spec.ts` |
27
+
28
+ ### Test Pattern
29
+
30
+ ```typescript
31
+ import { mlRuleTest } from 'markuplint';
32
+ import rule from './index.js';
33
+
34
+ // Valid case — no violations
35
+ const { violations } = await mlRuleTest(rule, '<img src="x.png" alt="desc" role="math">');
36
+ expect(violations).toStrictEqual([]);
37
+
38
+ // Invalid case — exact violation object
39
+ const { violations } = await mlRuleTest(rule, '<img src="x.png" alt="desc" role="navigation">');
40
+ expect(violations).toStrictEqual([
41
+ {
42
+ severity: 'error',
43
+ line: 1,
44
+ col: 38,
45
+ message: 'Cannot overwrite the "navigation" role to the "img" element according to ARIA in HTML specification',
46
+ raw: 'navigation',
47
+ },
48
+ ]);
49
+
50
+ // ARIA version test
51
+ const { violations } = await mlRuleTest(rule, '<button role="separator" ...>', {
52
+ rule: { options: { version: '1.1' } },
53
+ });
54
+ ```
55
+
56
+ ### Conventions
57
+
58
+ - `toStrictEqual` with exact `{ severity, line, col, message, raw }` — **never** loose assertions
59
+ - Always include both valid and invalid cases
60
+ - Some roles need ARIA attributes in HTML: `separator` → `aria-valuenow`, `meter` → `aria-valuenow`
61
+
62
+ ## Task: update-test-expectations
63
+
64
+ When `html-spec` data changes break existing tests:
65
+
66
+ 1. Run `yarn test` to find failures
67
+ 2. Verify the new behavior is correct (not a regression)
68
+ 3. Update expected violation objects (message strings, etc.)
69
+ 4. Common cause: adding enum values changes error message strings
70
+
71
+ ## Rules
72
+
73
+ 1. **Always run `yarn test` (full suite)**, not just the rules package
74
+ 2. **Use `toStrictEqual` with exact objects** — no loose assertions
75
+ 3. **Check `ml-spec` algorithm tests** when changing ARIA mappings —
76
+ `ml-spec/src/algorithm/aria/get-permitted-roles-spec.spec.ts` has hardcoded role arrays
@@ -1,25 +1,64 @@
1
1
  import type { Translator } from '@markuplint/i18n';
2
2
  import type { Attribute as AttrSpec, AttributeType } from '@markuplint/ml-spec';
3
+ import type { Type } from '@markuplint/types';
3
4
  import type { ReadonlyDeep } from 'type-fest';
5
+ /**
6
+ * Discriminated union tag representing the kind of attribute invalidity.
7
+ */
4
8
  type InvalidTYpe = 'non-existent' | 'invalid-value' | 'disallowed-attr';
9
+ /**
10
+ * Describes a single attribute validation failure, including the kind
11
+ * of invalidity, a human-readable message, and an optional source location.
12
+ *
13
+ * @template T - The specific invalidity tag (defaults to any `InvalidTYpe`)
14
+ */
5
15
  type Invalid<T extends InvalidTYpe = InvalidTYpe> = {
6
16
  invalidType: T;
7
17
  message: string;
8
18
  loc?: Loc;
9
19
  };
20
+ /**
21
+ * Source location information pointing to the invalid portion of an attribute value.
22
+ */
10
23
  type Loc = {
11
24
  raw: string;
12
25
  line: number;
13
26
  col: number;
14
27
  };
15
28
  /**
16
- * Use in rules `invalid-attr` and `wai-aria`
29
+ * Validates an attribute against its specification. Used by the `invalid-attr`
30
+ * and `wai-aria` rules.
31
+ *
32
+ * Performs the following checks in order:
33
+ * 1. Skips `data-*`, `aria-*`/`role`, and `adapt-*` attributes (unless `isCustomRule` is `true`)
34
+ * 2. Verifies the attribute exists in the spec
35
+ * 3. Checks case-sensitive name matching
36
+ * 4. Checks whether the attribute is marked as `noUse` (disallowed)
37
+ * 5. Validates the attribute value against all declared types
38
+ *
39
+ * @param t - The i18n translator for generating localized error messages
40
+ * @param name - The attribute name to check
41
+ * @param value - The attribute value to validate
42
+ * @param isCustomRule - When `true`, skips the built-in bypass for `data-*`, `aria-*`, and `adapt-*` attributes
43
+ * @param spec - The attribute specification to validate against; if absent, the attribute is considered non-existent
44
+ * @param allAttrNames - Optional list of all valid attribute names for the element;
45
+ * used to suggest a similar attribute name via Levenshtein distance when the attribute is non-existent
46
+ * @returns `false` if the attribute is valid, a single `Invalid` object for existence/disallowed errors,
47
+ * or an array of `Invalid<'invalid-value'>` objects for value validation failures
48
+ */
49
+ export declare function attrCheck(t: Translator, name: string, value: string, isCustomRule: boolean, spec?: AttrSpec, allAttrNames?: readonly string[]): Invalid | Invalid<'invalid-value'>[] | false;
50
+ /**
51
+ * Validates an attribute value against a single attribute type definition.
52
+ * Returns `false` if the value is valid, or a tuple of `[message, location]`
53
+ * describing the mismatch.
54
+ *
55
+ * Boolean attributes are always considered valid (their mere presence is sufficient).
17
56
  *
18
- * @param name
19
- * @param value
20
- * @param isCustomRule
21
- * @param spec
57
+ * @param t - The i18n translator for generating localized error messages
58
+ * @param name - The attribute name (used in error messages)
59
+ * @param value - The attribute value to validate
60
+ * @param type - The attribute type definition to validate against
61
+ * @returns `false` if the value is valid, or a `[message, location]` tuple on failure
22
62
  */
23
- export declare function attrCheck(t: Translator, name: string, value: string, isCustomRule: boolean, spec?: AttrSpec): Invalid | Invalid<'invalid-value'>[] | false;
24
- export declare function valueCheck(t: Translator, name: string, value: string, type: ReadonlyDeep<AttributeType>): [string, Loc] | false;
63
+ export declare function valueCheck(t: Translator, name: string, value: string, type: ReadonlyDeep<AttributeType | Type>): [string, Loc] | false;
25
64
  export {};
package/lib/attr-check.js CHANGED
@@ -1,16 +1,29 @@
1
1
  import { toNonNullableArrayFromItemOrArray } from '@markuplint/shared';
2
- import { check } from '@markuplint/types';
2
+ import { check, getCandidate } from '@markuplint/types';
3
3
  import { createMessageValueExpected } from './create-message.js';
4
4
  import { log } from './debug.js';
5
5
  /**
6
- * Use in rules `invalid-attr` and `wai-aria`
6
+ * Validates an attribute against its specification. Used by the `invalid-attr`
7
+ * and `wai-aria` rules.
7
8
  *
8
- * @param name
9
- * @param value
10
- * @param isCustomRule
11
- * @param spec
9
+ * Performs the following checks in order:
10
+ * 1. Skips `data-*`, `aria-*`/`role`, and `adapt-*` attributes (unless `isCustomRule` is `true`)
11
+ * 2. Verifies the attribute exists in the spec
12
+ * 3. Checks case-sensitive name matching
13
+ * 4. Checks whether the attribute is marked as `noUse` (disallowed)
14
+ * 5. Validates the attribute value against all declared types
15
+ *
16
+ * @param t - The i18n translator for generating localized error messages
17
+ * @param name - The attribute name to check
18
+ * @param value - The attribute value to validate
19
+ * @param isCustomRule - When `true`, skips the built-in bypass for `data-*`, `aria-*`, and `adapt-*` attributes
20
+ * @param spec - The attribute specification to validate against; if absent, the attribute is considered non-existent
21
+ * @param allAttrNames - Optional list of all valid attribute names for the element;
22
+ * used to suggest a similar attribute name via Levenshtein distance when the attribute is non-existent
23
+ * @returns `false` if the attribute is valid, a single `Invalid` object for existence/disallowed errors,
24
+ * or an array of `Invalid<'invalid-value'>` objects for value validation failures
12
25
  */
13
- export function attrCheck(t, name, value, isCustomRule, spec) {
26
+ export function attrCheck(t, name, value, isCustomRule, spec, allAttrNames) {
14
27
  if (!isCustomRule) {
15
28
  if (/^data-.+$/.test(name)) {
16
29
  // Ignore checking because "data-*" attribute is any type
@@ -30,9 +43,17 @@ export function attrCheck(t, name, value, isCustomRule, spec) {
30
43
  // Existence
31
44
  if (!spec) {
32
45
  log('The "%s" attribute DOES\'NT EXIST in the spec', name);
46
+ const baseMessage = t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed');
47
+ const candidate = allAttrNames ? getCandidate(name, allAttrNames) : undefined;
48
+ if (candidate) {
49
+ return {
50
+ invalidType: 'non-existent',
51
+ message: baseMessage + t('. ') + t('Did you mean "{0*}"?', candidate),
52
+ };
53
+ }
33
54
  return {
34
55
  invalidType: 'non-existent',
35
- message: t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed'),
56
+ message: baseMessage,
36
57
  };
37
58
  }
38
59
  const nameCaseSensitive = /[A-Z]/.test(spec.name);
@@ -68,6 +89,19 @@ export function attrCheck(t, name, value, isCustomRule, spec) {
68
89
  }
69
90
  return [...invalidMap.values()];
70
91
  }
92
+ /**
93
+ * Validates an attribute value against a single attribute type definition.
94
+ * Returns `false` if the value is valid, or a tuple of `[message, location]`
95
+ * describing the mismatch.
96
+ *
97
+ * Boolean attributes are always considered valid (their mere presence is sufficient).
98
+ *
99
+ * @param t - The i18n translator for generating localized error messages
100
+ * @param name - The attribute name (used in error messages)
101
+ * @param value - The attribute value to validate
102
+ * @param type - The attribute type definition to validate against
103
+ * @returns `false` if the value is valid, or a `[message, location]` tuple on failure
104
+ */
71
105
  export function valueCheck(t, name, value, type) {
72
106
  if (type === 'Boolean') {
73
107
  // Valid because an attribute is exist
@@ -1,2 +1,9 @@
1
+ /**
2
+ * Rule that checks for duplicate attributes on the same element.
3
+ *
4
+ * Walks all elements and reports when two or more attributes share the same
5
+ * name (case-insensitive for HTML elements). Attributes marked as duplicatable
6
+ * (e.g., template-engine directives) are excluded from this check.
7
+ */
1
8
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
2
9
  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 checks for duplicate attributes on the same element.
5
+ *
6
+ * Walks all elements and reports when two or more attributes share the same
7
+ * name (case-insensitive for HTML elements). Attributes marked as duplicatable
8
+ * (e.g., template-engine directives) are excluded from this check.
9
+ */
3
10
  export default createRule({
4
11
  meta: meta,
5
12
  async verify({ document, report, t }) {
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `attr-duplication`: categorized as a validation rule. */
1
2
  declare const _default: {
2
3
  readonly category: "validation";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `attr-duplication`: categorized as a validation rule. */
1
2
  export default {
2
3
  category: 'validation',
3
4
  };
@@ -1,7 +1,17 @@
1
+ /** The accepted quote style: double or single. */
1
2
  export type Type = 'double' | 'single';
3
+ /** A literal quotation mark character. */
2
4
  export type Quote = '"' | "'";
5
+ /** Maps each quote style name to its corresponding character. */
3
6
  export type QuoteMap = {
4
7
  [P in Type]: Quote;
5
8
  };
9
+ /**
10
+ * Rule that enforces consistent quotation marks around attribute values.
11
+ *
12
+ * Verifies that all attribute values use the configured quote style (double
13
+ * or single). Attributes without values, dynamic values, and directives are
14
+ * skipped. Includes an auto-fix that replaces mismatched quotes.
15
+ */
6
16
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<Type, undefined>>;
7
17
  export default _default;
@@ -1,9 +1,19 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Mapping from quote type names to their literal characters.
5
+ */
3
6
  const quoteList = {
4
7
  double: '"',
5
8
  single: "'",
6
9
  };
10
+ /**
11
+ * Rule that enforces consistent quotation marks around attribute values.
12
+ *
13
+ * Verifies that all attribute values use the configured quote style (double
14
+ * or single). Attributes without values, dynamic values, and directives are
15
+ * skipped. Includes an auto-fix that replaces mismatched quotes.
16
+ */
7
17
  export default createRule({
8
18
  meta: meta,
9
19
  defaultSeverity: 'warning',
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `attr-value-quotes`: categorized as a style rule. */
1
2
  declare const _default: {
2
3
  readonly category: "style";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `attr-value-quotes`: categorized as a style rule. */
1
2
  export default {
2
3
  category: 'style',
3
4
  };
@@ -1,3 +1,12 @@
1
+ /** The enforced letter case for attribute names. */
1
2
  export type Value = 'lower' | 'upper';
3
+ /**
4
+ * Rule that enforces consistent letter case for HTML attribute names.
5
+ *
6
+ * Reports attribute names that do not match the configured case (lower or upper)
7
+ * on HTML elements. Foreign elements and case-sensitive attributes (per spec)
8
+ * are excluded. Includes an auto-fix that converts attribute names to the
9
+ * configured case.
10
+ */
2
11
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<Value, undefined>>;
3
12
  export default _default;
@@ -1,5 +1,13 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Rule that enforces consistent letter case for HTML attribute names.
5
+ *
6
+ * Reports attribute names that do not match the configured case (lower or upper)
7
+ * on HTML elements. Foreign elements and case-sensitive attributes (per spec)
8
+ * are excluded. Includes an auto-fix that converts attribute names to the
9
+ * configured case.
10
+ */
3
11
  export default createRule({
4
12
  meta: meta,
5
13
  defaultSeverity: 'warning',
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `case-sensitive-attr-name`: categorized as a style rule. */
1
2
  declare const _default: {
2
3
  readonly category: "style";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `case-sensitive-attr-name`: categorized as a style rule. */
1
2
  export default {
2
3
  category: 'style',
3
4
  };
@@ -1,3 +1,12 @@
1
+ /** The enforced letter case for tag names. */
1
2
  export type Value = 'lower' | 'upper';
3
+ /**
4
+ * Rule that enforces consistent letter case for HTML tag names.
5
+ *
6
+ * Reports opening and closing tag names that do not match the configured
7
+ * case (lower or upper) on HTML elements. Skipped when the document is
8
+ * tag-name-case-sensitive or for foreign elements. Includes an auto-fix
9
+ * that converts tag names to the configured case.
10
+ */
2
11
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<Value, undefined>>;
3
12
  export default _default;
@@ -1,5 +1,13 @@
1
1
  import { createRule } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Rule that enforces consistent letter case for HTML tag names.
5
+ *
6
+ * Reports opening and closing tag names that do not match the configured
7
+ * case (lower or upper) on HTML elements. Skipped when the document is
8
+ * tag-name-case-sensitive or for foreign elements. Includes an auto-fix
9
+ * that converts tag names to the configured case.
10
+ */
3
11
  export default createRule({
4
12
  meta: meta,
5
13
  defaultSeverity: 'warning',
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `case-sensitive-tag-name`: categorized as a style rule. */
1
2
  declare const _default: {
2
3
  readonly category: "style";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `case-sensitive-tag-name`: categorized as a style rule. */
1
2
  export default {
2
3
  category: 'style',
3
4
  };
@@ -1,3 +1,12 @@
1
1
  import type { RuleConfigValue } from '@markuplint/ml-config';
2
+ /**
3
+ * Rule that checks whether special characters are properly escaped as
4
+ * character references.
5
+ *
6
+ * Scans text nodes and attribute values for the characters `"`, `&`, `<`,
7
+ * and `>` that are not already part of a valid character reference. Text
8
+ * inside `<script>` and `<style>` elements is excluded, as are bogus
9
+ * text nodes (e.g., orphaned end tags).
10
+ */
2
11
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RuleConfigValue, undefined>>;
3
12
  export default _default;
@@ -1,12 +1,32 @@
1
1
  import { createRule, getLocationFromChars } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Characters that must be escaped using character references in HTML text
5
+ * and attribute values.
6
+ */
3
7
  const defaultChars = ['"', '&', '<', '>'];
8
+ /**
9
+ * Parent elements whose text content is exempt from character reference checks
10
+ * because their content is not parsed as HTML.
11
+ */
4
12
  const ignoreParentElement = new Set(['script', 'style']);
13
+ /**
14
+ * Rule that checks whether special characters are properly escaped as
15
+ * character references.
16
+ *
17
+ * Scans text nodes and attribute values for the characters `"`, `&`, `<`,
18
+ * and `>` that are not already part of a valid character reference. Text
19
+ * inside `<script>` and `<style>` elements is excluded, as are bogus
20
+ * text nodes (e.g., orphaned end tags).
21
+ */
5
22
  export default createRule({
6
23
  meta: meta,
7
24
  async verify({ document, report, t }) {
8
25
  const targetNodes = [];
9
26
  await document.walkOn('Text', node => {
27
+ if (node.isBogus) {
28
+ return;
29
+ }
10
30
  if (node.parentNode && ignoreParentElement.has(node.parentNode.nodeName.toLowerCase())) {
11
31
  return;
12
32
  }
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `character-reference`: categorized as a style rule. */
1
2
  declare const _default: {
2
3
  readonly category: "style";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `character-reference`: categorized as a style rule. */
1
2
  export default {
2
3
  category: 'style',
3
4
  };
@@ -1,3 +1,12 @@
1
+ /** One or more regex or glob patterns that class names must match, or `null` to disable. */
1
2
  export type Value = string | string[] | null;
3
+ /**
4
+ * Rule that validates class attribute values against configured naming patterns.
5
+ *
6
+ * Splits the `class` attribute into individual class names and checks each
7
+ * one against the provided string or regex patterns. Reports any class name
8
+ * that does not match at least one of the configured patterns. Dynamic
9
+ * values are excluded from the check.
10
+ */
2
11
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<Value, undefined>>;
3
12
  export default _default;
@@ -2,6 +2,14 @@ import { createRule } from '@markuplint/ml-core';
2
2
  import { toNoEmptyStringArrayFromStringOrArray } from '@markuplint/shared';
3
3
  import { match } from '../helpers.js';
4
4
  import meta from './meta.js';
5
+ /**
6
+ * Rule that validates class attribute values against configured naming patterns.
7
+ *
8
+ * Splits the `class` attribute into individual class names and checks each
9
+ * one against the provided string or regex patterns. Reports any class name
10
+ * that does not match at least one of the configured patterns. Dynamic
11
+ * values are excluded from the check.
12
+ */
5
13
  export default createRule({
6
14
  meta: meta,
7
15
  defaultSeverity: 'warning',
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `class-naming`: categorized as a naming-convention rule. */
1
2
  declare const _default: {
2
3
  readonly category: "naming-convention";
3
4
  };
@@ -1,3 +1,4 @@
1
+ /** Rule metadata for `class-naming`: categorized as a naming-convention rule. */
1
2
  export default {
2
3
  category: 'naming-convention',
3
4
  };
@@ -0,0 +1,5 @@
1
+ type Options = {
2
+ readonly documentRoot?: string;
3
+ };
4
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
5
+ export default _default;