@markuplint/rules 5.0.0-rc.2 → 5.0.0-rc.5

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 (573) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CLAUDE.md +31 -2
  3. package/lib/{wai-aria-deprecated-role → aria-prop-requires-role}/index.d.ts +0 -1
  4. package/lib/{wai-aria-no-global-prop → aria-prop-requires-role}/index.js +0 -1
  5. package/lib/attr-check.d.ts +0 -48
  6. package/lib/attr-check.js +0 -36
  7. package/lib/attr-eligibility.d.ts +41 -0
  8. package/lib/attr-eligibility.js +60 -0
  9. package/lib/attr-value-quotes/index.d.ts +0 -7
  10. package/lib/attr-value-quotes/index.js +0 -7
  11. package/lib/attr-value-quotes/meta.d.ts +0 -1
  12. package/lib/attr-value-quotes/meta.js +0 -1
  13. package/lib/case-sensitive-attr-name/index.d.ts +0 -8
  14. package/lib/case-sensitive-attr-name/index.js +0 -8
  15. package/lib/case-sensitive-attr-name/meta.d.ts +0 -1
  16. package/lib/case-sensitive-attr-name/meta.js +0 -1
  17. package/lib/case-sensitive-tag-name/index.d.ts +0 -8
  18. package/lib/case-sensitive-tag-name/index.js +0 -8
  19. package/lib/case-sensitive-tag-name/meta.d.ts +0 -1
  20. package/lib/case-sensitive-tag-name/meta.js +0 -1
  21. package/lib/class-naming/index.d.ts +0 -8
  22. package/lib/class-naming/index.js +0 -8
  23. package/lib/class-naming/meta.d.ts +1 -2
  24. package/lib/class-naming/meta.js +1 -2
  25. package/lib/consistent-table-row-length/index.d.ts +14 -0
  26. package/lib/consistent-table-row-length/index.js +69 -0
  27. package/lib/consistent-table-row-length/meta.d.ts +4 -0
  28. package/lib/consistent-table-row-length/meta.js +3 -0
  29. package/lib/create-message.d.ts +2 -25
  30. package/lib/create-message.js +2 -53
  31. package/lib/debug.d.ts +1 -9
  32. package/lib/debug.js +1 -5
  33. package/lib/element-supports-aria-prop/index.d.ts +12 -0
  34. package/lib/element-supports-aria-prop/index.js +38 -0
  35. package/lib/form-attr-references-form/index.d.ts +12 -0
  36. package/lib/form-attr-references-form/index.js +64 -0
  37. package/lib/{correct-aspect-ratio → form-attr-references-form}/meta.d.ts +1 -1
  38. package/lib/form-attr-references-form/meta.js +3 -0
  39. package/lib/head-element-order/meta.d.ts +1 -1
  40. package/lib/head-element-order/meta.js +1 -1
  41. package/lib/helpers.d.ts +8 -138
  42. package/lib/helpers.js +24 -165
  43. package/lib/index.d.ts +135 -78
  44. package/lib/index.js +167 -82
  45. package/lib/input-list-references-datalist/index.d.ts +13 -0
  46. package/lib/input-list-references-datalist/index.js +46 -0
  47. package/lib/input-list-references-datalist/meta.d.ts +4 -0
  48. package/lib/input-list-references-datalist/meta.js +3 -0
  49. package/lib/itemprop-requires-itemscope/index.d.ts +21 -0
  50. package/lib/itemprop-requires-itemscope/index.js +53 -0
  51. package/lib/itemprop-requires-itemscope/meta.d.ts +4 -0
  52. package/lib/itemprop-requires-itemscope/meta.js +3 -0
  53. package/lib/label-for-references-labelable/index.d.ts +12 -0
  54. package/lib/label-for-references-labelable/index.js +68 -0
  55. package/lib/label-for-references-labelable/meta.d.ts +4 -0
  56. package/lib/label-for-references-labelable/meta.js +3 -0
  57. package/lib/label-has-control/index.d.ts +5 -5
  58. package/lib/label-has-control/index.js +5 -18
  59. package/lib/label-has-control/meta.d.ts +0 -1
  60. package/lib/label-has-control/meta.js +0 -1
  61. package/lib/label-no-multiple-controls/index.d.ts +31 -0
  62. package/lib/label-no-multiple-controls/index.js +104 -0
  63. package/lib/label-no-multiple-controls/meta.d.ts +4 -0
  64. package/lib/label-no-multiple-controls/meta.js +3 -0
  65. package/lib/landmark-roles/roles.d.ts +32 -0
  66. package/lib/landmark-roles/roles.js +73 -0
  67. package/lib/link-types/index.d.ts +0 -5
  68. package/lib/link-types/index.js +5 -6
  69. package/lib/link-types/meta.d.ts +1 -2
  70. package/lib/link-types/meta.js +1 -2
  71. package/lib/map-id-name-match/index.d.ts +10 -0
  72. package/lib/map-id-name-match/index.js +39 -0
  73. package/lib/map-id-name-match/meta.d.ts +4 -0
  74. package/lib/map-id-name-match/meta.js +3 -0
  75. package/lib/meta-charset-position/index.d.ts +15 -0
  76. package/lib/meta-charset-position/index.js +31 -0
  77. package/lib/meta-charset-position/meta.d.ts +4 -0
  78. package/lib/meta-charset-position/meta.js +3 -0
  79. package/lib/meter-value-bounds/index.d.ts +10 -0
  80. package/lib/meter-value-bounds/index.js +123 -0
  81. package/lib/meter-value-bounds/meta.d.ts +4 -0
  82. package/lib/meter-value-bounds/meta.js +3 -0
  83. package/lib/{wai-aria-required-props → no-abstract-role}/index.d.ts +0 -1
  84. package/lib/{wai-aria-abstract-role → no-abstract-role}/index.js +0 -1
  85. package/lib/no-always-matching-source/index.d.ts +13 -0
  86. package/lib/no-always-matching-source/index.js +100 -0
  87. package/lib/no-always-matching-source/meta.d.ts +4 -0
  88. package/lib/no-always-matching-source/meta.js +3 -0
  89. package/lib/no-ambiguous-navigable-target-names/index.d.ts +0 -8
  90. package/lib/no-ambiguous-navigable-target-names/index.js +0 -8
  91. package/lib/no-ambiguous-navigable-target-names/meta.d.ts +1 -2
  92. package/lib/no-ambiguous-navigable-target-names/meta.js +1 -2
  93. package/lib/{wai-aria-required-owned-elements → no-aria-on-presentational-children}/index.d.ts +0 -1
  94. package/lib/{wai-aria-presentational-children → no-aria-on-presentational-children}/index.js +0 -1
  95. package/lib/no-aria-on-unsupported-element/index.d.ts +11 -0
  96. package/lib/no-aria-on-unsupported-element/index.js +35 -0
  97. package/lib/no-aria-on-unsupported-element/meta.d.ts +9 -0
  98. package/lib/no-aria-on-unsupported-element/meta.js +8 -0
  99. package/lib/no-boolean-attr-value/index.d.ts +0 -7
  100. package/lib/no-boolean-attr-value/index.js +0 -7
  101. package/lib/no-boolean-attr-value/meta.d.ts +0 -1
  102. package/lib/no-boolean-attr-value/meta.js +0 -1
  103. package/lib/no-broken-fragment-link/index.d.ts +15 -0
  104. package/lib/no-broken-fragment-link/index.js +93 -0
  105. package/lib/no-broken-fragment-link/meta.d.ts +4 -0
  106. package/lib/no-broken-fragment-link/meta.js +3 -0
  107. package/lib/no-consecutive-br/index.d.ts +0 -8
  108. package/lib/no-consecutive-br/index.js +0 -8
  109. package/lib/no-consecutive-br/meta.d.ts +0 -1
  110. package/lib/no-consecutive-br/meta.js +0 -1
  111. package/lib/no-content-after-body/index.d.ts +25 -0
  112. package/lib/no-content-after-body/index.js +51 -0
  113. package/lib/no-content-after-body/meta.d.ts +4 -0
  114. package/lib/no-content-after-body/meta.js +3 -0
  115. package/lib/no-contradictory-aria-prop/index.d.ts +10 -0
  116. package/lib/no-contradictory-aria-prop/index.js +34 -0
  117. package/lib/{wai-aria-abstract-role → no-default-aria-value}/index.d.ts +0 -1
  118. package/lib/{wai-aria-default-value → no-default-aria-value}/index.js +0 -1
  119. package/lib/no-default-value/index.d.ts +0 -7
  120. package/lib/no-default-value/index.js +0 -7
  121. package/lib/no-default-value/meta.d.ts +0 -1
  122. package/lib/no-default-value/meta.js +0 -1
  123. package/lib/no-deprecated-aria-prop/index.d.ts +3 -0
  124. package/lib/{wai-aria-deprecated-props → no-deprecated-aria-prop}/index.js +0 -1
  125. package/lib/no-deprecated-attr/index.d.ts +2 -0
  126. package/lib/{deprecated-attr → no-deprecated-attr}/index.js +5 -9
  127. package/lib/no-deprecated-attr/meta.d.ts +4 -0
  128. package/lib/no-deprecated-attr/meta.js +3 -0
  129. package/lib/no-deprecated-element/index.d.ts +2 -0
  130. package/lib/no-deprecated-element/index.js +27 -0
  131. package/lib/no-deprecated-element/meta.d.ts +4 -0
  132. package/lib/no-deprecated-element/meta.js +3 -0
  133. package/lib/no-deprecated-role/index.d.ts +3 -0
  134. package/lib/{wai-aria-deprecated-role → no-deprecated-role}/index.js +0 -1
  135. package/lib/no-disallowed-ancestor/index.d.ts +2 -0
  136. package/lib/no-disallowed-ancestor/index.js +25 -0
  137. package/lib/no-disallowed-ancestor/meta.d.ts +4 -0
  138. package/lib/no-disallowed-ancestor/meta.js +3 -0
  139. package/lib/no-disallowed-attr/index.d.ts +28 -0
  140. package/lib/no-disallowed-attr/index.js +77 -0
  141. package/lib/no-disallowed-attr/meta.d.ts +4 -0
  142. package/lib/no-disallowed-attr/meta.js +3 -0
  143. package/lib/no-duplicate-attr/index.d.ts +2 -0
  144. package/lib/{attr-duplication → no-duplicate-attr}/index.js +0 -7
  145. package/lib/no-duplicate-attr/meta.d.ts +6 -0
  146. package/lib/no-duplicate-attr/meta.js +10 -0
  147. package/lib/no-duplicate-autofocus/index.d.ts +0 -5
  148. package/lib/no-duplicate-autofocus/index.js +20 -6
  149. package/lib/no-duplicate-autofocus/meta.d.ts +1 -1
  150. package/lib/no-duplicate-autofocus/meta.js +1 -1
  151. package/lib/no-duplicate-dt/index.d.ts +0 -6
  152. package/lib/no-duplicate-dt/index.js +1 -6
  153. package/lib/no-duplicate-dt/meta.d.ts +1 -2
  154. package/lib/no-duplicate-dt/meta.js +1 -2
  155. package/lib/no-duplicate-h1/index.d.ts +18 -0
  156. package/lib/no-duplicate-h1/index.js +41 -0
  157. package/lib/no-duplicate-h1/meta.d.ts +4 -0
  158. package/lib/no-duplicate-h1/meta.js +3 -0
  159. package/lib/no-duplicate-id/index.d.ts +2 -0
  160. package/lib/{id-duplication → no-duplicate-id}/index.js +0 -7
  161. package/lib/no-duplicate-id/meta.d.ts +4 -0
  162. package/lib/no-duplicate-id/meta.js +3 -0
  163. package/lib/no-duplicate-sibling-attr/index.d.ts +2 -0
  164. package/lib/no-duplicate-sibling-attr/index.js +35 -0
  165. package/lib/no-duplicate-sibling-attr/meta.d.ts +4 -0
  166. package/lib/no-duplicate-sibling-attr/meta.js +3 -0
  167. package/lib/no-duplicate-visible-main/index.d.ts +0 -1
  168. package/lib/no-duplicate-visible-main/index.js +0 -1
  169. package/lib/no-duplicate-visible-main/meta.d.ts +1 -1
  170. package/lib/no-duplicate-visible-main/meta.js +1 -1
  171. package/lib/no-empty-palpable-content/index.d.ts +0 -8
  172. package/lib/no-empty-palpable-content/index.js +0 -8
  173. package/lib/no-empty-palpable-content/meta.d.ts +1 -2
  174. package/lib/no-empty-palpable-content/meta.js +1 -2
  175. package/lib/no-empty-table-track/index.d.ts +12 -0
  176. package/lib/no-empty-table-track/index.js +42 -0
  177. package/lib/no-empty-table-track/meta.d.ts +4 -0
  178. package/lib/no-empty-table-track/meta.js +3 -0
  179. package/lib/no-event-handler-attr/index.d.ts +7 -0
  180. package/lib/{no-use-event-handler-attr → no-event-handler-attr}/index.js +0 -11
  181. package/lib/no-event-handler-attr/meta.d.ts +4 -0
  182. package/lib/no-event-handler-attr/meta.js +3 -0
  183. package/lib/no-experimental-features/index.d.ts +9 -0
  184. package/lib/no-experimental-features/index.js +53 -0
  185. package/lib/no-experimental-features/meta.d.ts +4 -0
  186. package/lib/no-experimental-features/meta.js +3 -0
  187. package/lib/no-extra-selected-options/index.d.ts +10 -0
  188. package/lib/no-extra-selected-options/index.js +37 -0
  189. package/lib/no-extra-selected-options/meta.d.ts +4 -0
  190. package/lib/no-extra-selected-options/meta.js +3 -0
  191. package/lib/no-focusable-in-aria-hidden/index.d.ts +3 -0
  192. package/lib/{wai-aria-interaction-in-hidden → no-focusable-in-aria-hidden}/index.js +0 -1
  193. package/lib/no-focusable-in-aria-hidden/meta.d.ts +4 -0
  194. package/lib/no-focusable-in-aria-hidden/meta.js +3 -0
  195. package/lib/no-hardcoded-id/index.d.ts +2 -0
  196. package/lib/{no-hard-code-id → no-hardcoded-id}/index.js +0 -8
  197. package/lib/no-hardcoded-id/meta.d.ts +4 -0
  198. package/lib/no-hardcoded-id/meta.js +3 -0
  199. package/lib/no-ineffective-attr/index.d.ts +2 -0
  200. package/lib/{ineffective-attr → no-ineffective-attr}/index.js +0 -7
  201. package/lib/no-ineffective-attr/meta.d.ts +5 -0
  202. package/lib/no-ineffective-attr/meta.js +4 -0
  203. package/lib/no-input-file-value/index.d.ts +10 -0
  204. package/lib/no-input-file-value/index.js +41 -0
  205. package/lib/no-input-file-value/meta.d.ts +4 -0
  206. package/lib/no-input-file-value/meta.js +3 -0
  207. package/lib/no-invalid-aria-prop-value/index.d.ts +3 -0
  208. package/lib/{wai-aria-value → no-invalid-aria-prop-value}/index.js +0 -1
  209. package/lib/no-invalid-aria-prop-value/meta.d.ts +4 -0
  210. package/lib/no-invalid-aria-prop-value/meta.js +3 -0
  211. package/lib/no-invalid-attr-value/index.d.ts +27 -0
  212. package/lib/no-invalid-attr-value/index.js +83 -0
  213. package/lib/no-invalid-attr-value/meta.d.ts +4 -0
  214. package/lib/no-invalid-attr-value/meta.js +3 -0
  215. package/lib/no-malformed-character-reference/index.d.ts +2 -0
  216. package/lib/no-malformed-character-reference/index.js +38 -0
  217. package/lib/no-malformed-character-reference/meta.d.ts +5 -0
  218. package/lib/no-malformed-character-reference/meta.js +20 -0
  219. package/lib/{correct-aspect-ratio → no-mismatched-aspect-ratio}/index.js +13 -3
  220. package/lib/no-mismatched-aspect-ratio/meta.d.ts +4 -0
  221. package/lib/no-mismatched-aspect-ratio/meta.js +3 -0
  222. package/lib/{correct-aspect-ratio → no-mismatched-aspect-ratio}/resolve-image-size.js +8 -2
  223. package/lib/no-mixed-srcset-descriptors/index.d.ts +12 -0
  224. package/lib/no-mixed-srcset-descriptors/index.js +47 -0
  225. package/lib/no-mixed-srcset-descriptors/meta.d.ts +4 -0
  226. package/lib/no-mixed-srcset-descriptors/meta.js +3 -0
  227. package/lib/no-nested-top-level-landmark/index.d.ts +10 -0
  228. package/lib/no-nested-top-level-landmark/index.js +52 -0
  229. package/lib/no-nested-top-level-landmark/meta.d.ts +4 -0
  230. package/lib/no-nested-top-level-landmark/meta.js +3 -0
  231. package/lib/no-nonstandard-features/index.d.ts +9 -0
  232. package/lib/no-nonstandard-features/index.js +53 -0
  233. package/lib/no-nonstandard-features/meta.d.ts +4 -0
  234. package/lib/no-nonstandard-features/meta.js +3 -0
  235. package/lib/no-obsolete-attr/index.d.ts +2 -0
  236. package/lib/no-obsolete-attr/index.js +27 -0
  237. package/lib/no-obsolete-attr/meta.d.ts +4 -0
  238. package/lib/no-obsolete-attr/meta.js +3 -0
  239. package/lib/no-obsolete-doctype/index.d.ts +2 -0
  240. package/lib/{doctype → no-obsolete-doctype}/index.js +9 -16
  241. package/lib/no-obsolete-doctype/meta.d.ts +4 -0
  242. package/lib/no-obsolete-doctype/meta.js +3 -0
  243. package/lib/no-obsolete-element/index.d.ts +2 -0
  244. package/lib/{deprecated-element → no-obsolete-element}/index.js +6 -10
  245. package/lib/no-obsolete-element/meta.d.ts +4 -0
  246. package/lib/no-obsolete-element/meta.js +3 -0
  247. package/lib/no-orphaned-end-tag/index.d.ts +0 -7
  248. package/lib/no-orphaned-end-tag/index.js +0 -7
  249. package/lib/no-orphaned-end-tag/meta.d.ts +2 -2
  250. package/lib/no-orphaned-end-tag/meta.js +5 -2
  251. package/lib/no-prohibited-naming/index.d.ts +8 -0
  252. package/lib/no-prohibited-naming/index.js +36 -0
  253. package/lib/no-prohibited-naming/meta.d.ts +4 -0
  254. package/lib/no-prohibited-naming/meta.js +3 -0
  255. package/lib/{use-list → no-pseudo-list}/index.d.ts +1 -8
  256. package/lib/{use-list → no-pseudo-list}/index.js +0 -7
  257. package/lib/no-pseudo-list/meta.d.ts +4 -0
  258. package/lib/no-pseudo-list/meta.js +3 -0
  259. package/lib/{redundant-accessible-name → no-redundant-accessible-name}/index.d.ts +0 -4
  260. package/lib/{redundant-accessible-name → no-redundant-accessible-name}/index.js +0 -4
  261. package/lib/no-redundant-accessible-name/meta.d.ts +4 -0
  262. package/lib/no-redundant-accessible-name/meta.js +3 -0
  263. package/lib/no-redundant-aria-prop/index.d.ts +10 -0
  264. package/lib/{wai-aria-implicit-props → no-redundant-aria-prop}/index.js +9 -3
  265. package/lib/no-redundant-aria-prop/meta.d.ts +4 -0
  266. package/lib/no-redundant-aria-prop/meta.js +3 -0
  267. package/lib/no-redundant-role/index.d.ts +3 -0
  268. package/lib/{wai-aria-implicit-role → no-redundant-role}/index.js +0 -1
  269. package/lib/no-redundant-role/meta.d.ts +4 -0
  270. package/lib/no-redundant-role/meta.js +3 -0
  271. package/lib/no-refer-to-non-existent-id/index.d.ts +0 -9
  272. package/lib/no-refer-to-non-existent-id/index.js +1 -62
  273. package/lib/no-refer-to-non-existent-id/meta.d.ts +1 -2
  274. package/lib/no-refer-to-non-existent-id/meta.js +1 -2
  275. package/lib/no-restricted-attr/index.d.ts +27 -0
  276. package/lib/no-restricted-attr/index.js +96 -0
  277. package/lib/no-restricted-attr/meta.d.ts +4 -0
  278. package/lib/no-restricted-attr/meta.js +3 -0
  279. package/lib/no-restricted-element/index.d.ts +2 -0
  280. package/lib/{disallowed-element → no-restricted-element}/index.js +7 -8
  281. package/lib/no-restricted-element/meta.d.ts +4 -0
  282. package/lib/no-restricted-element/meta.js +3 -0
  283. package/lib/no-skipped-heading-level/index.d.ts +13 -0
  284. package/lib/{heading-levels → no-skipped-heading-level}/index.js +10 -5
  285. package/lib/no-skipped-heading-level/meta.d.ts +4 -0
  286. package/lib/no-skipped-heading-level/meta.js +3 -0
  287. package/lib/no-stray-head-or-body-tag/index.d.ts +28 -0
  288. package/lib/no-stray-head-or-body-tag/index.js +61 -0
  289. package/lib/no-stray-head-or-body-tag/meta.d.ts +4 -0
  290. package/lib/no-stray-head-or-body-tag/meta.js +3 -0
  291. package/lib/no-table-cell-overlap/index.d.ts +12 -0
  292. package/lib/no-table-cell-overlap/index.js +30 -0
  293. package/lib/no-table-cell-overlap/meta.d.ts +4 -0
  294. package/lib/no-table-cell-overlap/meta.js +3 -0
  295. package/lib/no-table-span-overflow/index.d.ts +12 -0
  296. package/lib/no-table-span-overflow/index.js +37 -0
  297. package/lib/no-table-span-overflow/meta.d.ts +4 -0
  298. package/lib/no-table-span-overflow/meta.js +3 -0
  299. package/lib/no-unclosed-element-at-eof/index.d.ts +2 -0
  300. package/lib/no-unclosed-element-at-eof/index.js +86 -0
  301. package/lib/no-unclosed-element-at-eof/meta.d.ts +4 -0
  302. package/lib/no-unclosed-element-at-eof/meta.js +3 -0
  303. package/lib/no-unescaped-char/index.d.ts +14 -0
  304. package/lib/{character-reference → no-unescaped-char}/index.js +31 -20
  305. package/lib/no-unescaped-char/meta.d.ts +4 -0
  306. package/lib/no-unescaped-char/meta.js +3 -0
  307. package/lib/{invalid-attr → no-unknown-attr}/index.d.ts +4 -19
  308. package/lib/no-unknown-attr/index.js +91 -0
  309. package/lib/no-unknown-attr/meta.d.ts +4 -0
  310. package/lib/no-unknown-attr/meta.js +3 -0
  311. package/lib/no-unknown-role/index.d.ts +3 -0
  312. package/lib/{wai-aria-non-existent-role → no-unknown-role}/index.js +0 -1
  313. package/lib/no-unknown-role/meta.d.ts +4 -0
  314. package/lib/no-unknown-role/meta.js +3 -0
  315. package/lib/no-unpaired-srcset-sizes/index.d.ts +12 -0
  316. package/lib/no-unpaired-srcset-sizes/index.js +77 -0
  317. package/lib/no-unpaired-srcset-sizes/meta.d.ts +4 -0
  318. package/lib/no-unpaired-srcset-sizes/meta.js +3 -0
  319. package/lib/{no-unsupported-features → no-unsupported-browser-features}/index.d.ts +1 -10
  320. package/lib/no-unsupported-browser-features/index.js +81 -0
  321. package/lib/no-unsupported-browser-features/meta.d.ts +4 -0
  322. package/lib/no-unsupported-browser-features/meta.js +3 -0
  323. package/lib/permitted-contents/index.d.ts +0 -18
  324. package/lib/permitted-contents/index.js +0 -78
  325. package/lib/permitted-contents/meta.d.ts +1 -2
  326. package/lib/permitted-contents/meta.js +1 -2
  327. package/lib/permitted-contents/represent-transparent-nodes.js +15 -6
  328. package/lib/permitted-roles/index.d.ts +3 -0
  329. package/lib/{wai-aria-permitted-roles → permitted-roles}/index.js +0 -1
  330. package/lib/permitted-roles/meta.d.ts +4 -0
  331. package/lib/permitted-roles/meta.js +3 -0
  332. package/lib/placeholder-label-option/index.d.ts +10 -4
  333. package/lib/placeholder-label-option/index.js +30 -17
  334. package/lib/placeholder-label-option/meta.d.ts +1 -2
  335. package/lib/placeholder-label-option/meta.js +1 -2
  336. package/lib/progress-value-bounds/index.d.ts +10 -0
  337. package/lib/progress-value-bounds/index.js +59 -0
  338. package/lib/progress-value-bounds/meta.d.ts +4 -0
  339. package/lib/progress-value-bounds/meta.js +3 -0
  340. package/lib/require-accessible-name/index.d.ts +0 -7
  341. package/lib/require-accessible-name/index.js +0 -7
  342. package/lib/require-accessible-name/meta.d.ts +0 -1
  343. package/lib/require-accessible-name/meta.js +0 -1
  344. package/lib/require-adjacent-popover/index.d.ts +2 -0
  345. package/lib/{neighbor-popovers → require-adjacent-popover}/index.js +1 -8
  346. package/lib/require-adjacent-popover/meta.d.ts +4 -0
  347. package/lib/require-adjacent-popover/meta.js +3 -0
  348. package/lib/require-ancestor/index.d.ts +2 -0
  349. package/lib/require-ancestor/index.js +29 -0
  350. package/lib/require-ancestor/meta.d.ts +4 -0
  351. package/lib/require-ancestor/meta.js +3 -0
  352. package/lib/require-aria-prop/index.d.ts +3 -0
  353. package/lib/{wai-aria-required-props → require-aria-prop}/index.js +0 -1
  354. package/lib/require-aria-prop/meta.d.ts +4 -0
  355. package/lib/require-aria-prop/meta.js +3 -0
  356. package/lib/{required-attr → require-attr}/index.d.ts +2 -9
  357. package/lib/{required-attr → require-attr}/index.js +0 -7
  358. package/lib/require-attr/meta.d.ts +4 -0
  359. package/lib/require-attr/meta.js +3 -0
  360. package/lib/require-datetime/index.d.ts +0 -8
  361. package/lib/require-datetime/index.js +0 -8
  362. package/lib/require-datetime/meta.d.ts +1 -2
  363. package/lib/require-datetime/meta.js +1 -2
  364. package/lib/require-dialog-autofocus/index.d.ts +0 -10
  365. package/lib/require-dialog-autofocus/index.js +0 -10
  366. package/lib/require-dialog-autofocus/meta.d.ts +0 -1
  367. package/lib/require-dialog-autofocus/meta.js +0 -1
  368. package/lib/require-doctype/index.d.ts +2 -0
  369. package/lib/require-doctype/index.js +19 -0
  370. package/lib/require-doctype/meta.d.ts +5 -0
  371. package/lib/require-doctype/meta.js +10 -0
  372. package/lib/{required-element → require-element}/index.d.ts +1 -9
  373. package/lib/{required-element → require-element}/index.js +0 -8
  374. package/lib/require-element/meta.d.ts +4 -0
  375. package/lib/require-element/meta.js +3 -0
  376. package/lib/require-end-tag/index.d.ts +2 -0
  377. package/lib/{end-tag → require-end-tag}/index.js +0 -7
  378. package/lib/require-end-tag/meta.d.ts +4 -0
  379. package/lib/require-end-tag/meta.js +3 -0
  380. package/lib/require-h1/index.d.ts +17 -0
  381. package/lib/{required-h1 → require-h1}/index.js +6 -14
  382. package/lib/require-h1/meta.d.ts +4 -0
  383. package/lib/require-h1/meta.js +3 -0
  384. package/lib/require-landmark-label/index.d.ts +11 -0
  385. package/lib/require-landmark-label/index.js +55 -0
  386. package/lib/require-landmark-label/meta.d.ts +4 -0
  387. package/lib/require-landmark-label/meta.js +3 -0
  388. package/lib/require-owned-elements/index.d.ts +3 -0
  389. package/lib/{wai-aria-required-owned-elements → require-owned-elements}/index.js +0 -2
  390. package/lib/require-owned-elements/meta.d.ts +4 -0
  391. package/lib/require-owned-elements/meta.js +3 -0
  392. package/lib/require-parent-role/index.d.ts +3 -0
  393. package/lib/{wai-aria-required-parent-role → require-parent-role}/index.js +0 -1
  394. package/lib/require-parent-role/meta.d.ts +4 -0
  395. package/lib/require-parent-role/meta.js +3 -0
  396. package/lib/role-supports-aria-prop/index.d.ts +8 -0
  397. package/lib/{wai-aria-disallowed-props → role-supports-aria-prop}/index.js +11 -10
  398. package/lib/role-supports-aria-prop/meta.d.ts +4 -0
  399. package/lib/role-supports-aria-prop/meta.js +3 -0
  400. package/lib/rule-aliases.d.ts +2 -0
  401. package/lib/rule-aliases.js +325 -0
  402. package/lib/sizes-auto-requires-lazy-loading/index.d.ts +14 -0
  403. package/lib/sizes-auto-requires-lazy-loading/index.js +68 -0
  404. package/lib/sizes-auto-requires-lazy-loading/meta.d.ts +4 -0
  405. package/lib/sizes-auto-requires-lazy-loading/meta.js +3 -0
  406. package/lib/srcset-sizes-constraint/parse-srcset.d.ts +6 -0
  407. package/lib/srcset-sizes-constraint/parse-srcset.js +6 -0
  408. package/lib/srcset-sizes-constraint/sibling-helpers.d.ts +21 -0
  409. package/lib/srcset-sizes-constraint/sibling-helpers.js +37 -0
  410. package/lib/tab-requires-tabpanel/index.d.ts +3 -0
  411. package/lib/tab-requires-tabpanel/index.js +21 -0
  412. package/lib/tab-requires-tabpanel/meta.d.ts +4 -0
  413. package/lib/tab-requires-tabpanel/meta.js +3 -0
  414. package/lib/table-row-column-alignment/grid.d.ts +108 -52
  415. package/lib/table-row-column-alignment/grid.js +276 -203
  416. package/lib/table-row-column-alignment/types.d.ts +6 -8
  417. package/lib/usemap-references-map/index.d.ts +14 -0
  418. package/lib/usemap-references-map/index.js +46 -0
  419. package/lib/usemap-references-map/meta.d.ts +4 -0
  420. package/lib/usemap-references-map/meta.js +3 -0
  421. package/lib/valid-importmap/index.d.ts +2 -0
  422. package/lib/valid-importmap/index.js +176 -0
  423. package/lib/valid-importmap/meta.d.ts +4 -0
  424. package/lib/valid-importmap/meta.js +3 -0
  425. package/lib/valid-speculation-rules/index.d.ts +2 -0
  426. package/lib/valid-speculation-rules/index.js +292 -0
  427. package/lib/valid-speculation-rules/meta.d.ts +4 -0
  428. package/lib/valid-speculation-rules/meta.js +3 -0
  429. package/lib/wai-aria/checkings/contradictory-aria-prop.d.ts +23 -0
  430. package/lib/wai-aria/checkings/{implicit-props.js → contradictory-aria-prop.js} +22 -14
  431. package/lib/wai-aria/checkings/element-supports-aria-prop.d.ts +20 -0
  432. package/lib/wai-aria/checkings/element-supports-aria-prop.js +83 -0
  433. package/lib/wai-aria/checkings/prohibited-naming.d.ts +35 -0
  434. package/lib/wai-aria/checkings/prohibited-naming.js +64 -0
  435. package/lib/wai-aria/checkings/redundant-aria-prop.d.ts +23 -0
  436. package/lib/wai-aria/checkings/redundant-aria-prop.js +53 -0
  437. package/lib/wai-aria/checkings/required-prop.d.ts +4 -0
  438. package/lib/wai-aria/checkings/required-prop.js +16 -3
  439. package/lib/wai-aria/checkings/role-supports-aria-prop.d.ts +20 -0
  440. package/lib/wai-aria/checkings/role-supports-aria-prop.js +30 -0
  441. package/lib/wai-aria/checkings/tab-requires-tabpanel.d.ts +19 -0
  442. package/lib/wai-aria/checkings/tab-requires-tabpanel.js +56 -0
  443. package/lib/wai-aria/default-options.js +1 -0
  444. package/lib/wai-aria/types.d.ts +2 -0
  445. package/package.json +11 -10
  446. package/schema.json +275 -59
  447. package/SKILL.md +0 -77
  448. package/lib/attr-duplication/index.d.ts +0 -9
  449. package/lib/attr-duplication/meta.d.ts +0 -6
  450. package/lib/attr-duplication/meta.js +0 -5
  451. package/lib/character-reference/index.d.ts +0 -12
  452. package/lib/character-reference/meta.d.ts +0 -5
  453. package/lib/character-reference/meta.js +0 -4
  454. package/lib/correct-aspect-ratio/meta.js +0 -3
  455. package/lib/deprecated-attr/index.d.ts +0 -8
  456. package/lib/deprecated-attr/meta.d.ts +0 -5
  457. package/lib/deprecated-attr/meta.js +0 -4
  458. package/lib/deprecated-element/index.d.ts +0 -8
  459. package/lib/deprecated-element/meta.d.ts +0 -5
  460. package/lib/deprecated-element/meta.js +0 -4
  461. package/lib/disallowed-element/index.d.ts +0 -9
  462. package/lib/disallowed-element/meta.d.ts +0 -5
  463. package/lib/disallowed-element/meta.js +0 -4
  464. package/lib/doctype/index.d.ts +0 -15
  465. package/lib/doctype/meta.d.ts +0 -5
  466. package/lib/doctype/meta.js +0 -4
  467. package/lib/end-tag/index.d.ts +0 -9
  468. package/lib/end-tag/meta.d.ts +0 -5
  469. package/lib/end-tag/meta.js +0 -4
  470. package/lib/heading-levels/index.d.ts +0 -8
  471. package/lib/heading-levels/meta.d.ts +0 -5
  472. package/lib/heading-levels/meta.js +0 -4
  473. package/lib/id-duplication/index.d.ts +0 -9
  474. package/lib/id-duplication/meta.d.ts +0 -5
  475. package/lib/id-duplication/meta.js +0 -4
  476. package/lib/ineffective-attr/index.d.ts +0 -9
  477. package/lib/ineffective-attr/meta.d.ts +0 -6
  478. package/lib/ineffective-attr/meta.js +0 -5
  479. package/lib/invalid-attr/index.js +0 -207
  480. package/lib/invalid-attr/meta.d.ts +0 -5
  481. package/lib/invalid-attr/meta.js +0 -4
  482. package/lib/landmark-roles/index.d.ts +0 -22
  483. package/lib/landmark-roles/index.js +0 -142
  484. package/lib/landmark-roles/meta.d.ts +0 -5
  485. package/lib/landmark-roles/meta.js +0 -4
  486. package/lib/neighbor-popovers/index.d.ts +0 -10
  487. package/lib/neighbor-popovers/meta.d.ts +0 -5
  488. package/lib/neighbor-popovers/meta.js +0 -4
  489. package/lib/no-hard-code-id/index.d.ts +0 -10
  490. package/lib/no-hard-code-id/meta.d.ts +0 -5
  491. package/lib/no-hard-code-id/meta.js +0 -4
  492. package/lib/no-unsupported-features/index.js +0 -139
  493. package/lib/no-unsupported-features/meta.d.ts +0 -5
  494. package/lib/no-unsupported-features/meta.js +0 -4
  495. package/lib/no-use-event-handler-attr/index.d.ts +0 -18
  496. package/lib/no-use-event-handler-attr/meta.d.ts +0 -5
  497. package/lib/no-use-event-handler-attr/meta.js +0 -4
  498. package/lib/redundant-accessible-name/meta.d.ts +0 -5
  499. package/lib/redundant-accessible-name/meta.js +0 -4
  500. package/lib/required-attr/meta.d.ts +0 -5
  501. package/lib/required-attr/meta.js +0 -4
  502. package/lib/required-element/meta.d.ts +0 -5
  503. package/lib/required-element/meta.js +0 -4
  504. package/lib/required-h1/index.d.ts +0 -18
  505. package/lib/required-h1/meta.d.ts +0 -5
  506. package/lib/required-h1/meta.js +0 -4
  507. package/lib/srcset-sizes-constraint/index.d.ts +0 -6
  508. package/lib/srcset-sizes-constraint/index.js +0 -108
  509. package/lib/srcset-sizes-constraint/meta.d.ts +0 -5
  510. package/lib/srcset-sizes-constraint/meta.js +0 -4
  511. package/lib/table-row-column-alignment/index.d.ts +0 -10
  512. package/lib/table-row-column-alignment/index.js +0 -97
  513. package/lib/table-row-column-alignment/meta.d.ts +0 -5
  514. package/lib/table-row-column-alignment/meta.js +0 -4
  515. package/lib/use-list/meta.d.ts +0 -5
  516. package/lib/use-list/meta.js +0 -4
  517. package/lib/wai-aria/checkings/disallowed-prop.d.ts +0 -23
  518. package/lib/wai-aria/checkings/disallowed-prop.js +0 -84
  519. package/lib/wai-aria/checkings/implicit-props.d.ts +0 -20
  520. package/lib/wai-aria/index.d.ts +0 -13
  521. package/lib/wai-aria/index.js +0 -115
  522. package/lib/wai-aria/meta.d.ts +0 -5
  523. package/lib/wai-aria/meta.js +0 -4
  524. package/lib/wai-aria-default-value/index.d.ts +0 -4
  525. package/lib/wai-aria-default-value/meta.d.ts +0 -5
  526. package/lib/wai-aria-default-value/meta.js +0 -4
  527. package/lib/wai-aria-deprecated-props/index.d.ts +0 -4
  528. package/lib/wai-aria-deprecated-props/meta.d.ts +0 -5
  529. package/lib/wai-aria-deprecated-props/meta.js +0 -4
  530. package/lib/wai-aria-disallowed-props/index.d.ts +0 -4
  531. package/lib/wai-aria-disallowed-props/meta.d.ts +0 -5
  532. package/lib/wai-aria-disallowed-props/meta.js +0 -4
  533. package/lib/wai-aria-implicit-props/index.d.ts +0 -4
  534. package/lib/wai-aria-implicit-props/meta.d.ts +0 -5
  535. package/lib/wai-aria-implicit-props/meta.js +0 -4
  536. package/lib/wai-aria-implicit-role/index.d.ts +0 -4
  537. package/lib/wai-aria-interaction-in-hidden/index.d.ts +0 -4
  538. package/lib/wai-aria-interaction-in-hidden/meta.d.ts +0 -5
  539. package/lib/wai-aria-interaction-in-hidden/meta.js +0 -4
  540. package/lib/wai-aria-no-global-prop/index.d.ts +0 -4
  541. package/lib/wai-aria-no-global-prop/meta.d.ts +0 -5
  542. package/lib/wai-aria-no-global-prop/meta.js +0 -4
  543. package/lib/wai-aria-non-existent-role/index.d.ts +0 -4
  544. package/lib/wai-aria-permitted-roles/index.d.ts +0 -4
  545. package/lib/wai-aria-presentational-children/index.d.ts +0 -4
  546. package/lib/wai-aria-presentational-children/meta.d.ts +0 -5
  547. package/lib/wai-aria-presentational-children/meta.js +0 -4
  548. package/lib/wai-aria-required-parent-role/index.d.ts +0 -4
  549. package/lib/wai-aria-value/index.d.ts +0 -4
  550. package/lib/wai-aria-value/meta.d.ts +0 -5
  551. package/lib/wai-aria-value/meta.js +0 -4
  552. /package/lib/{wai-aria-abstract-role → aria-prop-requires-role}/meta.d.ts +0 -0
  553. /package/lib/{wai-aria-abstract-role → aria-prop-requires-role}/meta.js +0 -0
  554. /package/lib/{wai-aria-deprecated-role → element-supports-aria-prop}/meta.d.ts +0 -0
  555. /package/lib/{wai-aria-deprecated-role → element-supports-aria-prop}/meta.js +0 -0
  556. /package/lib/{wai-aria-implicit-role → no-abstract-role}/meta.d.ts +0 -0
  557. /package/lib/{wai-aria-implicit-role → no-abstract-role}/meta.js +0 -0
  558. /package/lib/{wai-aria-non-existent-role → no-aria-on-presentational-children}/meta.d.ts +0 -0
  559. /package/lib/{wai-aria-non-existent-role → no-aria-on-presentational-children}/meta.js +0 -0
  560. /package/lib/{wai-aria-permitted-roles → no-contradictory-aria-prop}/meta.d.ts +0 -0
  561. /package/lib/{wai-aria-permitted-roles → no-contradictory-aria-prop}/meta.js +0 -0
  562. /package/lib/{wai-aria-required-owned-elements → no-default-aria-value}/meta.d.ts +0 -0
  563. /package/lib/{wai-aria-required-owned-elements → no-default-aria-value}/meta.js +0 -0
  564. /package/lib/{wai-aria-required-parent-role → no-deprecated-aria-prop}/meta.d.ts +0 -0
  565. /package/lib/{wai-aria-required-parent-role → no-deprecated-aria-prop}/meta.js +0 -0
  566. /package/lib/{wai-aria-required-props → no-deprecated-role}/meta.d.ts +0 -0
  567. /package/lib/{wai-aria-required-props → no-deprecated-role}/meta.js +0 -0
  568. /package/lib/{correct-aspect-ratio → no-mismatched-aspect-ratio}/index.d.ts +0 -0
  569. /package/lib/{correct-aspect-ratio → no-mismatched-aspect-ratio}/resolve-image-size.d.ts +0 -0
  570. /package/lib/{no-unsupported-features → no-unsupported-browser-features}/compat-data.d.ts +0 -0
  571. /package/lib/{no-unsupported-features → no-unsupported-browser-features}/compat-data.js +0 -0
  572. /package/lib/{no-unsupported-features → no-unsupported-browser-features}/resolve-browsers.d.ts +0 -0
  573. /package/lib/{no-unsupported-features → no-unsupported-browser-features}/resolve-browsers.js +0 -0
@@ -1,24 +1,45 @@
1
1
  import { findChildren } from './find-children.js';
2
2
  /**
3
- * Represents a table as a grid model, splitting it into thead, tbody, and tfoot sections.
3
+ * HTML LS [§4.9.12.1 *Forming a table*](https://html.spec.whatwg.org/multipage/tables.html#forming-a-table)
4
+ * Step 8 clamps `colspan` to the range 1–1000.
5
+ */
6
+ const MAX_COLSPAN = 1000;
7
+ /**
8
+ * HTML LS [§4.9.12.1 *Forming a table*](https://html.spec.whatwg.org/multipage/tables.html#forming-a-table)
9
+ * Step 9 clamps `rowspan` to the range 0–65534.
10
+ */
11
+ const MAX_ROWSPAN = 65_534;
12
+ /**
13
+ * Models a `<table>` as the slot grid of HTML LS
14
+ * [§4.9.12.1 *Forming a table*](https://html.spec.whatwg.org/multipage/tables.html#forming-a-table).
15
+ *
16
+ * Shared by `no-table-cell-overlap`, `no-table-span-overflow`, `no-empty-table-track`, and
17
+ * `consistent-table-row-length` — the four rules split out of the former
18
+ * `table-row-column-alignment` rule, one per table model error / diagnostic this class models.
4
19
  *
5
- * Each section is modeled as a 2D array of `CellType` values that account for
6
- * `colspan` and `rowspan` attributes. Used by the `table-row-column-alignment`
7
- * rule to detect misaligned rows and overlapping cells.
20
+ * The algorithm is followed rather than approximated because every table model error those rules
21
+ * report is defined in terms of it: Step 6 decides which slot a cell is anchored to (a `colspan`
22
+ * after a `rowspan` continuation does *not* start at the cell's ordinal position), Steps 7 and 11
23
+ * decide how wide the table becomes, Step 14 defines cell overlap, and Step 20 defines rows and
24
+ * columns that no cell is anchored to.
25
+ *
26
+ * Two things the spec has no name for are tracked alongside:
27
+ *
28
+ * - **Row width** ({@link Row.slots} length). Rows narrower or wider than the rest of the
29
+ * table are not a table model error — nu-validator reports them as warnings unless column
30
+ * markup fixes the width — but they are `consistent-table-row-length`'s diagnostic and predate
31
+ * the spec-level checks.
32
+ * - **Row group boundaries**. The spec's *ending a row group* algorithm clips a cell to its
33
+ * row group, so slots are covered per group; {@link RowGroup.overflows} keeps the cells that
34
+ * were clipped so `no-table-span-overflow` can report them.
8
35
  */
9
36
  export class Grid {
10
- /** The grid model for the `<tbody>` section. */
11
- tbodyGrid;
12
- /** The grid model for the `<tfoot>` section. */
13
- tfootGrid;
14
- /** The grid model for the `<thead>` section. */
15
- theadGrid;
16
- /** The `<tr>` elements within `<tbody>`. */
17
- #tbodyRowElements;
18
- /** The `<tr>` elements within `<tfoot>`. */
19
- #tfootRowElements;
20
- /** The `<tr>` elements within `<thead>`. */
21
- #theadRowElements;
37
+ /** Column ranges established by `<col>` / `<colgroup>`, in document order. */
38
+ columnMarkup;
39
+ /** The row groups of the table, in document order. */
40
+ rowGroups;
41
+ /** The number of slot columns in the table (spec: xwidth). */
42
+ xwidth;
22
43
  /**
23
44
  * Constructs a grid model from a `<table>` element.
24
45
  *
@@ -27,238 +48,290 @@ export class Grid {
27
48
  constructor(
28
49
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
29
50
  table) {
30
- const thead = findChildren(table, 'thead')[0];
31
- const tbody = findChildren(table, 'tbody')[0];
32
- const tfoot = findChildren(table, 'tfoot')[0];
33
- this.#theadRowElements = thead ? findChildren(thead, 'tr') : [];
34
- this.#tbodyRowElements = tbody ? findChildren(tbody, 'tr') : [];
35
- this.#tfootRowElements = tfoot ? findChildren(tfoot, 'tr') : [];
36
- this.theadGrid = createGrid(this.#theadRowElements);
37
- this.tbodyGrid = createGrid(this.#tbodyRowElements);
38
- this.tfootGrid = createGrid(this.#tfootRowElements);
39
- }
40
- /**
41
- * Returns all `<tr>` elements across all table sections in document order.
42
- *
43
- * @returns Combined array of row elements from thead, tbody, and tfoot.
44
- */
45
- getAllRowElements() {
46
- return [...this.#theadRowElements, ...this.#tbodyRowElements, ...this.#tfootRowElements];
51
+ this.columnMarkup = collectColumnMarkup(table);
52
+ // Column markup establishes the initial xwidth (spec: the *processing column groups* step).
53
+ let xwidth = this.columnMarkup.at(-1)?.right ?? 0;
54
+ const rowGroups = [];
55
+ for (const groupElement of findChildren(table, 'thead, tbody, tfoot')) {
56
+ const group = processRowGroup(groupElement, xwidth);
57
+ xwidth = group.xwidth;
58
+ rowGroups.push(group.rowGroup);
59
+ }
60
+ this.rowGroups = rowGroups;
61
+ this.xwidth = xwidth;
47
62
  }
48
- /**
49
- * Returns all grid rows that contain at least one actual cell element.
50
- *
51
- * @returns Combined array of grid rows from thead, tbody, and tfoot, filtered to rows with real cells.
52
- */
53
- getAllRows() {
54
- return [
55
- ...this.theadGrid.filter(hasElementFilter),
56
- ...this.tbodyGrid.filter(hasElementFilter),
57
- ...this.tfootGrid.filter(hasElementFilter),
58
- ];
63
+ /** @returns Every cell anchored in the table, in document order. */
64
+ getAnchors() {
65
+ return this.rowGroups.flatMap(group => group.anchors);
59
66
  }
60
67
  /**
61
- * Determines the expected (base) column count for the table.
68
+ * Determines the expected (base) column count that rows are compared against.
62
69
  *
63
- * Prefers the thead section if available, then tfoot, then tbody.
70
+ * Rows decide it, preferring `<thead>` then `<tfoot>` since a heading or footing
71
+ * row is the more deliberate declaration. Column markup is deliberately *not*
72
+ * consulted: HTML LS §4.9.12.1 Step 7 grows xwidth to fit a row that is wider
73
+ * than the `<col>` markup, so such a row is not an error (tracked as a
74
+ * nu-validator over-detection in `tests/external/snapshots/excluded-ids.json`).
75
+ * A row narrower than the column markup is caught by the Step 20 column scan
76
+ * instead, which reports the `<col>` that no cell ever starts in.
64
77
  *
65
78
  * @returns The base number of columns that rows should have.
66
79
  */
67
80
  getBaseColLength() {
68
- if (this.theadGrid.length > 0) {
69
- return getBaseColLength(this.theadGrid);
70
- }
71
- if (this.tfootGrid.length > 0) {
72
- return getBaseColLength(this.tfootGrid);
73
- }
74
- return getBaseColLength(this.tbodyGrid);
81
+ const preferred = this.rowGroups.find(group => group.tagName === 'thead' && group.rows.length > 0) ??
82
+ this.rowGroups.find(group => group.tagName === 'tfoot' && group.rows.length > 0) ??
83
+ this.rowGroups.find(group => group.rows.length > 0);
84
+ return preferred ? getBaseColLength(preferred.rows) : 0;
75
85
  }
76
86
  /**
77
- * Returns the grid and row elements for each table section (thead, tbody, tfoot).
87
+ * Collects the columns that no cell is anchored to (spec Step 20), merging
88
+ * neighbouring columns that the same element established into one range so a
89
+ * `colspan` overshooting by four columns is reported once, not four times.
78
90
  *
79
- * @returns An array of objects, each containing the section grid and its row elements.
91
+ * @returns The offending ranges, in column order.
80
92
  */
81
- getSections() {
82
- return [
83
- {
84
- section: this.theadGrid,
85
- elements: this.#theadRowElements,
86
- },
87
- {
88
- section: this.tbodyGrid,
89
- elements: this.#tbodyRowElements,
90
- },
91
- {
92
- section: this.tfootGrid,
93
- elements: this.#tfootRowElements,
94
- },
95
- ];
93
+ getColumnsWithoutAnchor() {
94
+ const anchors = this.getAnchors();
95
+ const anchored = new Set(anchors.map(anchor => anchor.x));
96
+ const ranges = [];
97
+ for (let x = 0; x < this.xwidth; x++) {
98
+ if (anchored.has(x)) {
99
+ continue;
100
+ }
101
+ const source = this.#findColumnSource(x, anchors);
102
+ if (!source) {
103
+ continue;
104
+ }
105
+ const last = ranges.at(-1);
106
+ if (last && last.source === source && last.right === x) {
107
+ ranges[ranges.length - 1] = { source, left: last.left, right: x + 1 };
108
+ continue;
109
+ }
110
+ ranges.push({ source, left: x, right: x + 1 });
111
+ }
112
+ return ranges;
113
+ }
114
+ /** @returns Every row of the table, in document order. */
115
+ getRows() {
116
+ return this.rowGroups.flatMap(group => group.rows);
96
117
  }
97
118
  /**
98
- * Checks whether any cell in the table grid has been marked as overlapping.
119
+ * Checks whether any slot of the table is covered by more than one cell.
99
120
  *
100
- * @returns `true` if any cell has the overlap marker `'x'`.
121
+ * @returns `true` if the table has a Step 14 cell overlap.
101
122
  */
102
123
  hasOverlapped() {
103
- return (this.tbodyGrid.some(row => row.includes('x')) ||
104
- this.tfootGrid.some(row => row.includes('x')) ||
105
- this.theadGrid.some(row => row.includes('x')));
124
+ return this.rowGroups.some(group => group.hasOverlap);
106
125
  }
107
- /** Logs the grid data for all three table sections to the console (for debugging). */
108
- log() {
109
- // eslint-disable-next-line no-console
110
- console.table(this.theadGrid);
111
- // eslint-disable-next-line no-console
112
- console.table(this.tbodyGrid);
113
- // eslint-disable-next-line no-console
114
- console.table(this.tfootGrid);
126
+ /**
127
+ * Resolves which element is answerable for a column: the column markup that
128
+ * declared it, or else the cell whose `colspan` stretched the table over it.
129
+ *
130
+ * @param x - The column index.
131
+ * @param anchors - The anchors of the table.
132
+ * @returns The element, or `null` when nothing covers the column.
133
+ */
134
+ #findColumnSource(x, anchors) {
135
+ const markup = this.columnMarkup.find(range => range.left <= x && x < range.right);
136
+ if (markup) {
137
+ return markup.source;
138
+ }
139
+ const spanning = anchors.find(anchor => anchor.x < x && x < anchor.x + anchor.colspan);
140
+ return spanning?.cell ?? null;
115
141
  }
116
142
  }
117
143
  /**
118
- * Detects a rowspan that extends beyond the available rows in a table section.
144
+ * Maps each slot of a row to the index of the `<th>` / `<td>` anchored to it
145
+ * (counted among the row's own cells), or `null` for a slot the row does not anchor a cell to.
119
146
  *
120
- * When a grid row exists without a corresponding row element, it indicates
121
- * a rowspan overflow. Returns the offending `rowspan` attribute node if found.
147
+ * @param slots - The slots of a single row.
148
+ * @returns An array of cell indices or `null` values, one per slot.
149
+ */
150
+ export function getIndexes(slots) {
151
+ let indexCounter = 0;
152
+ return slots.map(slot => (slot === '●' || slot === '◎' ? indexCounter++ : null));
153
+ }
154
+ /**
155
+ * Collects the column ranges that `<col>` and `<colgroup>` establish.
122
156
  *
123
- * @param rows - The grid rows for a single table section.
124
- * @param rowElements - The `<tr>` elements for the section.
125
- * @returns An object containing the overflowing `rowSpan` attribute, or `null`.
157
+ * A `<colgroup>` contributes its own `span` only when it has no `<col>` children,
158
+ * mirroring HTML LS §4.9.3: the `span` attribute of a `<colgroup>` "must not be
159
+ * specified if the element contains one or more `col` elements".
160
+ *
161
+ * @param table - The table element.
162
+ * @returns The ranges in document order.
126
163
  */
127
- export function getOverflowRowSpan(rows,
164
+ function collectColumnMarkup(
128
165
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
129
- rowElements) {
130
- for (const [rowNum] of rows.entries()) {
131
- const rowEl = rowElements[rowNum];
132
- if (!rowEl) {
133
- const prevRow = rows[rowNum - 1];
134
- const prevRowEl = rowElements[rowNum - 1];
135
- if (!prevRow || !prevRowEl) {
136
- continue;
137
- }
138
- const spanStart = prevRow.indexOf('◎');
139
- if (spanStart === -1) {
140
- continue;
141
- }
142
- const indexes = getIndexes(prevRow);
143
- const index = indexes[spanStart];
144
- if (index == null) {
145
- continue;
146
- }
147
- const cells = findChildren(prevRowEl, 'th, td');
148
- const spanStartCell = cells[index];
149
- if (!spanStartCell) {
150
- continue;
151
- }
152
- const rowSpan = spanStartCell.getAttributeNode('rowspan');
153
- if (!rowSpan) {
154
- continue;
155
- }
156
- return {
157
- rowSpan,
158
- };
166
+ table) {
167
+ const ranges = [];
168
+ let left = 0;
169
+ const append = (
170
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
171
+ source) => {
172
+ const span = parseSpan(source.getAttribute('span'), 1, 1, MAX_COLSPAN);
173
+ ranges.push({ source, left, right: left + span });
174
+ left += span;
175
+ };
176
+ for (const child of findChildren(table, 'colgroup, col')) {
177
+ if (child.localName === 'col') {
178
+ append(child);
179
+ continue;
180
+ }
181
+ const cols = findChildren(child, 'col');
182
+ if (cols.length === 0) {
183
+ append(child);
184
+ continue;
185
+ }
186
+ for (const col of cols) {
187
+ append(col);
159
188
  }
160
189
  }
161
- return null;
190
+ return ranges;
162
191
  }
163
192
  /**
164
- * Maps each cell in a grid row to its source cell index (for cells that correspond
165
- * to actual `<td>`/`<th>` elements), or `null` for spanned cells.
193
+ * Calculates the base (expected) column count from the rows of a row group.
194
+ *
195
+ * For groups with 3 or more rows, uses the row width closest to the average so a
196
+ * single malformed row does not become the baseline. Otherwise uses the first row.
166
197
  *
167
- * @param row - A single row of cell types from the grid model.
168
- * @returns An array of cell indices or `null` values, one per grid column.
198
+ * @param rows - The rows of a row group.
199
+ * @returns The base number of columns.
169
200
  */
170
- export function getIndexes(row) {
171
- let indexCounter = 0;
172
- return row.map(col => (col === '●' || col === '◎' ? indexCounter++ : null));
201
+ function getBaseColLength(rows) {
202
+ if (rows.length < 3) {
203
+ return rows[0]?.slots.length ?? 0;
204
+ }
205
+ let totalCols = 0;
206
+ for (const row of rows) {
207
+ totalCols += row.slots.length;
208
+ }
209
+ const average = Math.round(totalCols / rows.length);
210
+ let closest = rows[0]?.slots.length ?? 0;
211
+ for (const row of rows) {
212
+ if (Math.abs(row.slots.length - average) < Math.abs(closest - average)) {
213
+ closest = row.slots.length;
214
+ }
215
+ }
216
+ return closest;
173
217
  }
174
218
  /**
175
- * Calculates the base (expected) column count from a section grid.
176
- *
177
- * For grids with 3 or more rows, uses the row length closest to the
178
- * average to handle minor variations from spans. Otherwise uses the first row.
219
+ * Parses a `colspan` / `rowspan` / `span` attribute value the way HTML LS does:
220
+ * a value that is missing or fails to parse falls back to the default, and a parsed
221
+ * value is clamped to the range the spec allows.
179
222
  *
180
- * @param grid - The 2D grid for a table section.
181
- * @returns The base number of columns.
223
+ * @param value - The raw attribute value.
224
+ * @param fallback - The value to use when the attribute is absent or fails to parse.
225
+ * @param min - The lower bound of the clamp range.
226
+ * @param max - The upper bound of the clamp range.
227
+ * @returns The effective span.
182
228
  */
183
- function getBaseColLength(grid) {
184
- let baseColLength;
185
- if (grid.length >= 3) {
186
- const totalCols = grid.reduce((acc, row) => {
187
- return acc + row.length;
188
- }, 0);
189
- const average = Math.round(totalCols / grid.length);
190
- baseColLength = grid
191
- // eslint-disable-next-line unicorn/no-array-reduce
192
- .reduce((closest, row) => {
193
- return Math.abs(row.length - average) < Math.abs(closest - average) ? row.length : closest;
194
- }, grid[0]?.length ?? 0);
229
+ function parseSpan(value, fallback, min, max) {
230
+ if (value == null) {
231
+ return fallback;
195
232
  }
196
- else {
197
- baseColLength = grid[0]?.length ?? 0;
233
+ const digits = /^[\t\n\f\r ]*(\d+)/.exec(value);
234
+ if (!digits?.[1]) {
235
+ return fallback;
198
236
  }
199
- return baseColLength;
237
+ return Math.min(Math.max(Number.parseInt(digits[1], 10), min), max);
200
238
  }
201
239
  /**
202
- * Builds a 2D grid model from an array of `<tr>` elements.
203
- *
204
- * Processes each row's `<th>` and `<td>` cells, expanding `colspan` and `rowspan`
205
- * attributes into the grid. Marks cells with appropriate cell types including
206
- * span origins, continuations, and overlaps.
240
+ * Runs the spec's *algorithm for processing rows* over one row group.
207
241
  *
208
- * @param rows - The `<tr>` elements to convert into a grid.
209
- * @returns A 2D array of cell types representing the table section layout.
242
+ * @param groupElement - The `<thead>` / `<tbody>` / `<tfoot>` element.
243
+ * @param initialXwidth - The table's xwidth before this group is processed.
244
+ * @returns The modelled row group and the xwidth the group grew the table to.
210
245
  */
211
- function createGrid(
246
+ function processRowGroup(
212
247
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
213
- rows) {
214
- const rowSpans = new Set();
215
- const matrix = [];
216
- for (const [rowNum, row] of rows.entries()) {
217
- const cols = findChildren(row, 'th, td');
218
- let colNum = 0;
219
- matrix[rowNum] = matrix[rowNum] ?? [];
220
- for (const col of cols) {
221
- const colSpan = Number.parseInt(col.getAttribute('colspan') ?? '1');
222
- const rowSpan = Number.parseInt(col.getAttribute('rowspan') ?? '1');
223
- for (let i = 0; i < colSpan; i++) {
224
- const key = `${rowNum}:${colNum}`;
225
- let spanStart = false;
226
- if (colSpan > 1 && i === 0) {
227
- spanStart = true;
228
- }
229
- if (rowSpan > 1) {
230
- if (i === 0) {
231
- spanStart = true;
232
- }
233
- for (let i = 1; i <= rowSpan - 1; i++) {
234
- matrix[rowNum + i] = matrix[rowNum + i] ?? [];
235
- matrix[rowNum + i][colNum] = '↓';
236
- rowSpans.add(`${rowNum + i}:${colNum}`);
248
+ groupElement, initialXwidth) {
249
+ const rowElements = findChildren(groupElement, 'tr');
250
+ const rowCount = rowElements.length;
251
+ /** The anchor covering each slot, keyed by `"row:column"`. */
252
+ const covered = new Map();
253
+ /** Slots a second cell tried to cover (spec Step 14). */
254
+ const overlapped = new Set();
255
+ const anchors = [];
256
+ const overflows = [];
257
+ const widths = [];
258
+ const anchorCounts = [];
259
+ let xwidth = initialXwidth;
260
+ for (const [y, rowElement] of rowElements.entries()) {
261
+ let xcurrent = 0;
262
+ let anchorCount = 0;
263
+ for (const cell of findChildren(rowElement, 'th, td')) {
264
+ const colspan = parseSpan(cell.getAttribute('colspan'), 1, 1, MAX_COLSPAN);
265
+ const declaredRowspan = parseSpan(cell.getAttribute('rowspan'), 1, 0, MAX_ROWSPAN);
266
+ const growsDownward = declaredRowspan === 0;
267
+ const rowspan = growsDownward ? Math.max(rowCount - y, 1) : declaredRowspan;
268
+ // Step 6
269
+ while (xcurrent < xwidth && covered.has(`${y}:${xcurrent}`)) {
270
+ xcurrent += 1;
271
+ }
272
+ // Step 7
273
+ if (xcurrent === xwidth) {
274
+ xwidth += 1;
275
+ }
276
+ // Step 11
277
+ xwidth = Math.max(xwidth, xcurrent + colspan);
278
+ const anchor = { cell, x: xcurrent, y, colspan, rowspan, growsDownward };
279
+ anchors.push(anchor);
280
+ anchorCount += 1;
281
+ // Steps 13 and 14. The bottom is clipped to the row group per *ending a row group*.
282
+ const bottom = Math.min(y + rowspan, rowCount);
283
+ for (let cy = y; cy < bottom; cy++) {
284
+ for (let cx = xcurrent; cx < xcurrent + colspan; cx++) {
285
+ const key = `${cy}:${cx}`;
286
+ if (covered.has(key)) {
287
+ overlapped.add(key);
288
+ continue;
237
289
  }
290
+ covered.set(key, anchor);
238
291
  }
239
- const addCol = rowSpans.has(key) ? 1 : 0;
240
- if (matrix[rowNum]?.[colNum] === '↓' && !spanStart && colSpan > 1) {
241
- // Overlap
242
- matrix[rowNum][colNum] = 'x';
243
- }
244
- else {
245
- matrix[rowNum][colNum] = spanStart ? '◎' : addCol > 0 ? '↓' : colSpan > 1 ? '→' : '●';
246
- }
247
- if (addCol === 1) {
248
- matrix[rowNum].push('●');
249
- }
250
- colNum += 1;
251
292
  }
293
+ if (!growsDownward && y + rowspan > rowCount) {
294
+ overflows.push(anchor);
295
+ }
296
+ xcurrent += colspan;
297
+ }
298
+ // The row is as wide as its own cells plus the run of continuing spans that follows them.
299
+ while (covered.has(`${y}:${xcurrent}`)) {
300
+ xcurrent += 1;
252
301
  }
302
+ widths[y] = xcurrent;
303
+ anchorCounts[y] = anchorCount;
253
304
  }
254
- return matrix;
255
- }
256
- /**
257
- * Filter predicate that returns `true` if a grid row contains at least one actual cell element.
258
- *
259
- * @param row - A single row of cell types.
260
- * @returns `true` if the row has a regular or span-origin cell.
261
- */
262
- function hasElementFilter(row) {
263
- return row.some(cell => cell === '●' || cell === '◎');
305
+ const rows = rowElements.map((element, y) => {
306
+ const slots = [];
307
+ for (let x = 0; x < (widths[y] ?? 0); x++) {
308
+ const key = `${y}:${x}`;
309
+ if (overlapped.has(key)) {
310
+ slots.push('x');
311
+ continue;
312
+ }
313
+ const anchor = covered.get(key);
314
+ if (!anchor) {
315
+ continue;
316
+ }
317
+ slots.push(anchor.x === x && anchor.y === y
318
+ ? anchor.colspan > 1 || anchor.rowspan > 1
319
+ ? '◎'
320
+ : '●'
321
+ : anchor.y === y
322
+ ? '→'
323
+ : '↓');
324
+ }
325
+ return { element, slots, anchorCount: anchorCounts[y] ?? 0 };
326
+ });
327
+ return {
328
+ rowGroup: {
329
+ tagName: groupElement.localName,
330
+ rows,
331
+ anchors,
332
+ overflows,
333
+ hasOverlap: overlapped.size > 0,
334
+ },
335
+ xwidth,
336
+ };
264
337
  }
@@ -1,12 +1,10 @@
1
- /** A grid cell coordinate key in "row:column" format. */
2
- export type Key = `${number}:${number}`;
3
1
  /**
4
- * Represents the type of a cell in the table grid model.
2
+ * Represents what covers one slot of the table grid.
5
3
  *
6
- * - `'●'` - A regular cell (single-span).
7
- * - `'◎'` - The origin cell of a colspan or rowspan.
8
- * - `'↓'` - A cell occupied by a rowspan from a row above.
9
- * - `'→'` - A cell occupied by a colspan from a column to the left.
10
- * - `'x'` - An overlapping cell caused by conflicting spans.
4
+ * - `'●'` - A cell is anchored here and spans nothing else.
5
+ * - `'◎'` - A cell is anchored here and spans further columns and/or rows.
6
+ * - `'↓'` - Covered by a cell anchored in an earlier row.
7
+ * - `'→'` - Covered by a cell anchored earlier in the same row.
8
+ * - `'x'` - Covered by more than one cell (HTML LS §4.9.12.1 Step 14).
11
9
  */
12
10
  export type CellType = '●' | '◎' | '↓' | '→' | 'x';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Require the `usemap` attribute on an `<img>` element to be a valid
3
+ * hash-name reference to a `<map>` element. Per HTML LS's definition of
4
+ * "valid hash-name reference": a string consisting of "#" followed by a
5
+ * string that exactly matches the `name` attribute of an element of the
6
+ * referenced type in the same tree — matching is by `name`, not `id`, so
7
+ * `no-refer-to-non-existent-id` (which only tracks `id` references) never
8
+ * covers this attribute.
9
+ *
10
+ * @see https://html.spec.whatwg.org/multipage/image-maps.html#attr-hyperlink-usemap
11
+ * @see https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-hash-name-reference
12
+ */
13
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, null>>;
14
+ export default _default;
@@ -0,0 +1,46 @@
1
+ import { createRule } from '@markuplint/ml-core';
2
+ import meta from './meta.js';
3
+ /**
4
+ * Require the `usemap` attribute on an `<img>` element to be a valid
5
+ * hash-name reference to a `<map>` element. Per HTML LS's definition of
6
+ * "valid hash-name reference": a string consisting of "#" followed by a
7
+ * string that exactly matches the `name` attribute of an element of the
8
+ * referenced type in the same tree — matching is by `name`, not `id`, so
9
+ * `no-refer-to-non-existent-id` (which only tracks `id` references) never
10
+ * covers this attribute.
11
+ *
12
+ * @see https://html.spec.whatwg.org/multipage/image-maps.html#attr-hyperlink-usemap
13
+ * @see https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-hash-name-reference
14
+ */
15
+ export default createRule({
16
+ meta: meta,
17
+ defaultValue: true,
18
+ defaultOptions: null,
19
+ async verify({ document, report, t }) {
20
+ await document.walkOn('Element', el => {
21
+ if (el.localName !== 'img')
22
+ return;
23
+ if (el.pretenderContext?.type === 'pretender' && !el.hasAttribute('as'))
24
+ return;
25
+ const usemapAttr = el.getAttributeNode('usemap');
26
+ if (!usemapAttr)
27
+ return;
28
+ if (usemapAttr.isDynamicValue)
29
+ return;
30
+ const value = usemapAttr.value;
31
+ if (!value.startsWith('#') || value.length <= 1)
32
+ return;
33
+ const targetName = value.slice(1);
34
+ const target = [...document.querySelectorAll('map')].find(candidate => candidate.getAttribute('name') === targetName);
35
+ if (!target) {
36
+ report({
37
+ scope: usemapAttr,
38
+ line: usemapAttr.valueNode?.startLine,
39
+ col: usemapAttr.valueNode?.startCol,
40
+ raw: usemapAttr.valueNode?.raw,
41
+ message: t('The "{0}" attribute of the "{1}" element must be a valid hash-name reference to a "{2}" element', 'usemap', 'img', 'map'),
42
+ });
43
+ }
44
+ });
45
+ },
46
+ });
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ readonly category: "references";
3
+ };
4
+ export default _default;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ category: 'references',
3
+ };
@@ -0,0 +1,2 @@
1
+ declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, null>>;
2
+ export default _default;