@markuplint/rules 4.11.2 → 4.12.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 (231) hide show
  1. package/CHANGELOG.md +6 -3
  2. package/SKILL.md +76 -0
  3. package/lib/attr-check.d.ts +41 -5
  4. package/lib/attr-check.js +29 -5
  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 +8 -0
  22. package/lib/character-reference/index.js +16 -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/create-message.d.ts +27 -0
  30. package/lib/create-message.js +56 -1
  31. package/lib/debug.d.ts +9 -0
  32. package/lib/debug.js +5 -0
  33. package/lib/deprecated-attr/index.d.ts +6 -0
  34. package/lib/deprecated-attr/index.js +6 -0
  35. package/lib/deprecated-attr/meta.d.ts +1 -0
  36. package/lib/deprecated-attr/meta.js +1 -0
  37. package/lib/deprecated-element/index.d.ts +8 -0
  38. package/lib/deprecated-element/index.js +8 -0
  39. package/lib/deprecated-element/meta.d.ts +1 -0
  40. package/lib/deprecated-element/meta.js +1 -0
  41. package/lib/disallowed-element/index.d.ts +7 -0
  42. package/lib/disallowed-element/index.js +7 -0
  43. package/lib/disallowed-element/meta.d.ts +1 -0
  44. package/lib/disallowed-element/meta.js +1 -0
  45. package/lib/doctype/index.d.ts +10 -0
  46. package/lib/doctype/index.js +8 -0
  47. package/lib/doctype/meta.d.ts +1 -0
  48. package/lib/doctype/meta.js +1 -0
  49. package/lib/end-tag/index.d.ts +7 -0
  50. package/lib/end-tag/index.js +7 -0
  51. package/lib/end-tag/meta.d.ts +1 -0
  52. package/lib/end-tag/meta.js +1 -0
  53. package/lib/heading-levels/index.d.ts +6 -0
  54. package/lib/heading-levels/index.js +6 -0
  55. package/lib/heading-levels/meta.d.ts +1 -0
  56. package/lib/heading-levels/meta.js +1 -0
  57. package/lib/helpers.d.ts +104 -0
  58. package/lib/helpers.js +105 -2
  59. package/lib/id-duplication/index.d.ts +7 -0
  60. package/lib/id-duplication/index.js +7 -0
  61. package/lib/id-duplication/meta.d.ts +1 -0
  62. package/lib/id-duplication/meta.js +1 -0
  63. package/lib/index.d.ts +13 -0
  64. package/lib/index.js +12 -0
  65. package/lib/ineffective-attr/index.d.ts +7 -0
  66. package/lib/ineffective-attr/index.js +7 -0
  67. package/lib/ineffective-attr/meta.d.ts +1 -0
  68. package/lib/ineffective-attr/meta.js +1 -0
  69. package/lib/invalid-attr/index.d.ts +24 -0
  70. package/lib/invalid-attr/index.js +17 -0
  71. package/lib/invalid-attr/meta.d.ts +1 -0
  72. package/lib/invalid-attr/meta.js +1 -0
  73. package/lib/label-has-control/index.d.ts +7 -0
  74. package/lib/label-has-control/index.js +11 -0
  75. package/lib/label-has-control/meta.d.ts +1 -0
  76. package/lib/label-has-control/meta.js +1 -0
  77. package/lib/landmark-roles/index.d.ts +14 -0
  78. package/lib/landmark-roles/index.js +21 -0
  79. package/lib/landmark-roles/meta.d.ts +1 -0
  80. package/lib/landmark-roles/meta.js +1 -0
  81. package/lib/neighbor-popovers/index.d.ts +8 -0
  82. package/lib/neighbor-popovers/index.js +26 -2
  83. package/lib/neighbor-popovers/meta.d.ts +1 -0
  84. package/lib/neighbor-popovers/meta.js +1 -0
  85. package/lib/no-ambiguous-navigable-target-names/index.d.ts +8 -0
  86. package/lib/no-ambiguous-navigable-target-names/index.js +8 -0
  87. package/lib/no-ambiguous-navigable-target-names/meta.d.ts +1 -0
  88. package/lib/no-ambiguous-navigable-target-names/meta.js +1 -0
  89. package/lib/no-boolean-attr-value/index.d.ts +7 -0
  90. package/lib/no-boolean-attr-value/index.js +7 -0
  91. package/lib/no-boolean-attr-value/meta.d.ts +1 -0
  92. package/lib/no-boolean-attr-value/meta.js +1 -0
  93. package/lib/no-consecutive-br/index.d.ts +8 -0
  94. package/lib/no-consecutive-br/index.js +8 -0
  95. package/lib/no-consecutive-br/meta.d.ts +1 -0
  96. package/lib/no-consecutive-br/meta.js +1 -0
  97. package/lib/no-default-value/index.d.ts +7 -0
  98. package/lib/no-default-value/index.js +7 -0
  99. package/lib/no-default-value/meta.d.ts +1 -0
  100. package/lib/no-default-value/meta.js +1 -0
  101. package/lib/no-duplicate-dt/index.d.ts +6 -0
  102. package/lib/no-duplicate-dt/index.js +6 -0
  103. package/lib/no-duplicate-dt/meta.d.ts +1 -0
  104. package/lib/no-duplicate-dt/meta.js +1 -0
  105. package/lib/no-empty-palpable-content/index.d.ts +13 -0
  106. package/lib/no-empty-palpable-content/index.js +12 -0
  107. package/lib/no-empty-palpable-content/meta.d.ts +1 -0
  108. package/lib/no-empty-palpable-content/meta.js +1 -0
  109. package/lib/no-hard-code-id/index.d.ts +8 -0
  110. package/lib/no-hard-code-id/index.js +8 -0
  111. package/lib/no-hard-code-id/meta.d.ts +1 -0
  112. package/lib/no-hard-code-id/meta.js +1 -0
  113. package/lib/no-orphaned-end-tag/index.d.ts +7 -0
  114. package/lib/no-orphaned-end-tag/index.js +7 -0
  115. package/lib/no-orphaned-end-tag/meta.d.ts +1 -0
  116. package/lib/no-orphaned-end-tag/meta.js +1 -0
  117. package/lib/no-refer-to-non-existent-id/index.d.ts +8 -0
  118. package/lib/no-refer-to-non-existent-id/index.js +9 -0
  119. package/lib/no-refer-to-non-existent-id/meta.d.ts +1 -0
  120. package/lib/no-refer-to-non-existent-id/meta.js +1 -0
  121. package/lib/no-use-event-handler-attr/index.d.ts +10 -0
  122. package/lib/no-use-event-handler-attr/index.js +8 -0
  123. package/lib/no-use-event-handler-attr/meta.d.ts +1 -0
  124. package/lib/no-use-event-handler-attr/meta.js +1 -0
  125. package/lib/permitted-contents/choice.d.ts +17 -0
  126. package/lib/permitted-contents/choice.js +30 -0
  127. package/lib/permitted-contents/complex-branch.d.ts +11 -7
  128. package/lib/permitted-contents/complex-branch.js +11 -7
  129. package/lib/permitted-contents/content-model.d.ts +11 -0
  130. package/lib/permitted-contents/content-model.js +32 -0
  131. package/lib/permitted-contents/count-pattern.d.ts +13 -7
  132. package/lib/permitted-contents/count-pattern.js +23 -7
  133. package/lib/permitted-contents/debug.browser.d.ts +12 -0
  134. package/lib/permitted-contents/debug.browser.js +12 -0
  135. package/lib/permitted-contents/debug.d.ts +12 -0
  136. package/lib/permitted-contents/debug.js +12 -0
  137. package/lib/permitted-contents/index.d.ts +12 -0
  138. package/lib/permitted-contents/index.js +21 -0
  139. package/lib/permitted-contents/matches-selector.d.ts +19 -0
  140. package/lib/permitted-contents/matches-selector.js +33 -0
  141. package/lib/permitted-contents/meta.d.ts +1 -0
  142. package/lib/permitted-contents/meta.js +1 -0
  143. package/lib/permitted-contents/order.d.ts +14 -7
  144. package/lib/permitted-contents/order.js +14 -7
  145. package/lib/permitted-contents/recursive-branch.d.ts +17 -0
  146. package/lib/permitted-contents/recursive-branch.js +17 -0
  147. package/lib/permitted-contents/represent-transparent-nodes.d.ts +29 -0
  148. package/lib/permitted-contents/represent-transparent-nodes.js +24 -0
  149. package/lib/permitted-contents/start.d.ts +12 -6
  150. package/lib/permitted-contents/start.js +12 -6
  151. package/lib/permitted-contents/transparent.d.ts +12 -0
  152. package/lib/permitted-contents/transparent.js +12 -0
  153. package/lib/permitted-contents/types.d.ts +67 -0
  154. package/lib/permitted-contents/utils.d.ts +161 -1
  155. package/lib/permitted-contents/utils.js +194 -0
  156. package/lib/placeholder-label-option/index.d.ts +7 -0
  157. package/lib/placeholder-label-option/index.js +19 -15
  158. package/lib/placeholder-label-option/meta.d.ts +1 -0
  159. package/lib/placeholder-label-option/meta.js +1 -0
  160. package/lib/require-accessible-name/index.d.ts +11 -0
  161. package/lib/require-accessible-name/index.js +7 -0
  162. package/lib/require-accessible-name/meta.d.ts +1 -0
  163. package/lib/require-accessible-name/meta.js +1 -0
  164. package/lib/require-datetime/index.d.ts +12 -0
  165. package/lib/require-datetime/index.js +8 -0
  166. package/lib/require-datetime/meta.d.ts +1 -0
  167. package/lib/require-datetime/meta.js +1 -0
  168. package/lib/require-datetime/types.d.ts +7 -0
  169. package/lib/require-datetime/utils.d.ts +18 -5
  170. package/lib/require-datetime/utils.js +53 -9
  171. package/lib/required-attr/index.d.ts +13 -0
  172. package/lib/required-attr/index.js +7 -0
  173. package/lib/required-attr/meta.d.ts +1 -0
  174. package/lib/required-attr/meta.js +1 -0
  175. package/lib/required-element/index.d.ts +14 -0
  176. package/lib/required-element/index.js +14 -2
  177. package/lib/required-element/meta.d.ts +1 -0
  178. package/lib/required-element/meta.js +1 -0
  179. package/lib/required-h1/index.d.ts +12 -0
  180. package/lib/required-h1/index.js +7 -0
  181. package/lib/required-h1/meta.d.ts +1 -0
  182. package/lib/required-h1/meta.js +1 -0
  183. package/lib/table-row-column-alignment/find-children.d.ts +10 -0
  184. package/lib/table-row-column-alignment/find-children.js +11 -0
  185. package/lib/table-row-column-alignment/grid.d.ts +60 -0
  186. package/lib/table-row-column-alignment/grid.js +85 -0
  187. package/lib/table-row-column-alignment/index.d.ts +8 -0
  188. package/lib/table-row-column-alignment/index.js +8 -0
  189. package/lib/table-row-column-alignment/meta.d.ts +1 -0
  190. package/lib/table-row-column-alignment/meta.js +1 -0
  191. package/lib/table-row-column-alignment/types.d.ts +10 -0
  192. package/lib/use-list/index.d.ts +16 -0
  193. package/lib/use-list/index.js +20 -0
  194. package/lib/use-list/meta.d.ts +1 -0
  195. package/lib/use-list/meta.js +1 -0
  196. package/lib/wai-aria/checkings/abstract-role.d.ts +9 -0
  197. package/lib/wai-aria/checkings/abstract-role.js +9 -0
  198. package/lib/wai-aria/checkings/default-value.d.ts +11 -0
  199. package/lib/wai-aria/checkings/default-value.js +11 -0
  200. package/lib/wai-aria/checkings/deprecated-props.d.ts +11 -0
  201. package/lib/wai-aria/checkings/deprecated-props.js +11 -0
  202. package/lib/wai-aria/checkings/deprecated-role.d.ts +10 -0
  203. package/lib/wai-aria/checkings/deprecated-role.js +10 -0
  204. package/lib/wai-aria/checkings/disallowed-prop.d.ts +14 -0
  205. package/lib/wai-aria/checkings/disallowed-prop.js +14 -0
  206. package/lib/wai-aria/checkings/implicit-props.d.ts +13 -0
  207. package/lib/wai-aria/checkings/implicit-props.js +13 -0
  208. package/lib/wai-aria/checkings/implicit-role.d.ts +9 -0
  209. package/lib/wai-aria/checkings/implicit-role.js +9 -0
  210. package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +9 -0
  211. package/lib/wai-aria/checkings/interaction-in-hidden.js +15 -4
  212. package/lib/wai-aria/checkings/no-global-prop.d.ts +10 -0
  213. package/lib/wai-aria/checkings/no-global-prop.js +10 -0
  214. package/lib/wai-aria/checkings/non-existent-role.d.ts +10 -0
  215. package/lib/wai-aria/checkings/non-existent-role.js +10 -0
  216. package/lib/wai-aria/checkings/permitted-roles.d.ts +10 -0
  217. package/lib/wai-aria/checkings/permitted-roles.js +10 -0
  218. package/lib/wai-aria/checkings/presentational-children.d.ts +7 -1
  219. package/lib/wai-aria/checkings/presentational-children.js +14 -1
  220. package/lib/wai-aria/checkings/required-owned-elements.d.ts +9 -1
  221. package/lib/wai-aria/checkings/required-owned-elements.js +16 -1
  222. package/lib/wai-aria/checkings/required-prop.d.ts +13 -0
  223. package/lib/wai-aria/checkings/required-prop.js +13 -0
  224. package/lib/wai-aria/checkings/value.d.ts +23 -0
  225. package/lib/wai-aria/checkings/value.js +32 -0
  226. package/lib/wai-aria/index.d.ts +10 -0
  227. package/lib/wai-aria/index.js +10 -0
  228. package/lib/wai-aria/meta.d.ts +1 -0
  229. package/lib/wai-aria/meta.js +1 -0
  230. package/lib/wai-aria/types.d.ts +17 -0
  231. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -3,13 +3,16 @@
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)
7
-
8
- **Note:** Version bump only for package @markuplint/rules
6
+ # [4.12.0](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.2...@markuplint/rules@4.12.0) (2026-02-10)
9
7
 
8
+ ### Features
10
9
 
10
+ - **rules:** add ignoreOmittedElements option to required-element ([0cc879c](https://github.com/markuplint/markuplint/commit/0cc879ca175491da7eda4d6f4d0638b74fe528e5)), closes [#2066](https://github.com/markuplint/markuplint/issues/2066)
11
+ - **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)
11
12
 
13
+ ## [4.11.2](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.1...@markuplint/rules@4.11.2) (2025-11-05)
12
14
 
15
+ **Note:** Version bump only for package @markuplint/rules
13
16
 
14
17
  ## [4.11.1](https://github.com/markuplint/markuplint/compare/@markuplint/rules@4.11.0...@markuplint/rules@4.11.1) (2025-08-24)
15
18
 
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,61 @@
1
1
  import type { Translator } from '@markuplint/i18n';
2
2
  import type { Attribute as AttrSpec, AttributeType } from '@markuplint/ml-spec';
3
3
  import type { ReadonlyDeep } from 'type-fest';
4
+ /**
5
+ * Discriminated union tag representing the kind of attribute invalidity.
6
+ */
4
7
  type InvalidTYpe = 'non-existent' | 'invalid-value' | 'disallowed-attr';
8
+ /**
9
+ * Describes a single attribute validation failure, including the kind
10
+ * of invalidity, a human-readable message, and an optional source location.
11
+ *
12
+ * @template T - The specific invalidity tag (defaults to any `InvalidTYpe`)
13
+ */
5
14
  type Invalid<T extends InvalidTYpe = InvalidTYpe> = {
6
15
  invalidType: T;
7
16
  message: string;
8
17
  loc?: Loc;
9
18
  };
19
+ /**
20
+ * Source location information pointing to the invalid portion of an attribute value.
21
+ */
10
22
  type Loc = {
11
23
  raw: string;
12
24
  line: number;
13
25
  col: number;
14
26
  };
15
27
  /**
16
- * Use in rules `invalid-attr` and `wai-aria`
28
+ * Validates an attribute against its specification. Used by the `invalid-attr`
29
+ * and `wai-aria` rules.
30
+ *
31
+ * Performs the following checks in order:
32
+ * 1. Skips `data-*`, `aria-*`/`role`, and `adapt-*` attributes (unless `isCustomRule` is `true`)
33
+ * 2. Verifies the attribute exists in the spec
34
+ * 3. Checks case-sensitive name matching
35
+ * 4. Checks whether the attribute is marked as `noUse` (disallowed)
36
+ * 5. Validates the attribute value against all declared types
17
37
  *
18
- * @param name
19
- * @param value
20
- * @param isCustomRule
21
- * @param spec
38
+ * @param t - The i18n translator for generating localized error messages
39
+ * @param name - The attribute name to check
40
+ * @param value - The attribute value to validate
41
+ * @param isCustomRule - When `true`, skips the built-in bypass for `data-*`, `aria-*`, and `adapt-*` attributes
42
+ * @param spec - The attribute specification to validate against; if absent, the attribute is considered non-existent
43
+ * @returns `false` if the attribute is valid, a single `Invalid` object for existence/disallowed errors,
44
+ * or an array of `Invalid<'invalid-value'>` objects for value validation failures
22
45
  */
23
46
  export declare function attrCheck(t: Translator, name: string, value: string, isCustomRule: boolean, spec?: AttrSpec): Invalid | Invalid<'invalid-value'>[] | false;
47
+ /**
48
+ * Validates an attribute value against a single attribute type definition.
49
+ * Returns `false` if the value is valid, or a tuple of `[message, location]`
50
+ * describing the mismatch.
51
+ *
52
+ * Boolean attributes are always considered valid (their mere presence is sufficient).
53
+ *
54
+ * @param t - The i18n translator for generating localized error messages
55
+ * @param name - The attribute name (used in error messages)
56
+ * @param value - The attribute value to validate
57
+ * @param type - The attribute type definition to validate against
58
+ * @returns `false` if the value is valid, or a `[message, location]` tuple on failure
59
+ */
24
60
  export declare function valueCheck(t: Translator, name: string, value: string, type: ReadonlyDeep<AttributeType>): [string, Loc] | false;
25
61
  export {};
package/lib/attr-check.js CHANGED
@@ -3,12 +3,23 @@ import { check } 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
+ * @returns `false` if the attribute is valid, a single `Invalid` object for existence/disallowed errors,
22
+ * or an array of `Invalid<'invalid-value'>` objects for value validation failures
12
23
  */
13
24
  export function attrCheck(t, name, value, isCustomRule, spec) {
14
25
  if (!isCustomRule) {
@@ -68,6 +79,19 @@ export function attrCheck(t, name, value, isCustomRule, spec) {
68
79
  }
69
80
  return [...invalidMap.values()];
70
81
  }
82
+ /**
83
+ * Validates an attribute value against a single attribute type definition.
84
+ * Returns `false` if the value is valid, or a tuple of `[message, location]`
85
+ * describing the mismatch.
86
+ *
87
+ * Boolean attributes are always considered valid (their mere presence is sufficient).
88
+ *
89
+ * @param t - The i18n translator for generating localized error messages
90
+ * @param name - The attribute name (used in error messages)
91
+ * @param value - The attribute value to validate
92
+ * @param type - The attribute type definition to validate against
93
+ * @returns `false` if the value is valid, or a `[message, location]` tuple on failure
94
+ */
71
95
  export function valueCheck(t, name, value, type) {
72
96
  if (type === 'Boolean') {
73
97
  // 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,11 @@
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.
9
+ */
2
10
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RuleConfigValue, undefined>>;
3
11
  export default _default;
@@ -1,7 +1,23 @@
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.
20
+ */
5
21
  export default createRule({
6
22
  meta: meta,
7
23
  async verify({ document, report, t }) {
@@ -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
  };
@@ -2,5 +2,32 @@ import type { Translator } from '@markuplint/i18n';
2
2
  import type { AttributeType } from '@markuplint/ml-spec';
3
3
  import type { UnmatchedResult } from '@markuplint/types';
4
4
  import type { ReadonlyDeep } from 'type-fest';
5
+ /**
6
+ * Builds a human-readable error message explaining why an attribute value
7
+ * does not match its expected type. Handles both plain types and list types
8
+ * (space-separated or comma-separated).
9
+ *
10
+ * @param t - The i18n translator for generating localized messages
11
+ * @param baseTarget - A human-readable description of the attribute being validated
12
+ * (e.g. `'the "href" attribute'`)
13
+ * @param type - The attribute type definition that the value was validated against
14
+ * @param matches - The result object from a failed type check, containing
15
+ * details about the mismatch (reason, candidate, part name, etc.)
16
+ * @returns A localized error message describing the expected value
17
+ */
5
18
  export declare function createMessageValueExpected(t: Translator, baseTarget: string, type: ReadonlyDeep<AttributeType>, matches: UnmatchedResult): string;
19
+ /**
20
+ * Assembles the detailed portion of a value-expected error message by
21
+ * inspecting the mismatch reason and composing reason, expectation,
22
+ * candidate suggestion, and fallback parts into a single string.
23
+ *
24
+ * This is an internal helper exposed for testing and advanced use.
25
+ * The double-underscore prefix indicates it is not part of the stable public API.
26
+ *
27
+ * @param t - The i18n translator for generating localized messages
28
+ * @param baseTarget - A human-readable description of the validation target
29
+ * @param expected - A description of the expected value(s), or `null` if unknown
30
+ * @param matches - Partial match result containing reason, candidate, and other details
31
+ * @returns A localized error message string
32
+ */
6
33
  export declare function __createMessageValueExpected(t: Translator, baseTarget: string, expected: string | null, matches: Pick<UnmatchedResult, 'partName' | 'reason' | 'raw' | 'candidate' | 'ref' | 'extra' | 'fallbackTo'>): string;
@@ -1,4 +1,17 @@
1
1
  import { isList, isKeyword, isEnum, isNumber, isDirective } from '@markuplint/types';
2
+ /**
3
+ * Builds a human-readable error message explaining why an attribute value
4
+ * does not match its expected type. Handles both plain types and list types
5
+ * (space-separated or comma-separated).
6
+ *
7
+ * @param t - The i18n translator for generating localized messages
8
+ * @param baseTarget - A human-readable description of the attribute being validated
9
+ * (e.g. `'the "href" attribute'`)
10
+ * @param type - The attribute type definition that the value was validated against
11
+ * @param matches - The result object from a failed type check, containing
12
+ * details about the mismatch (reason, candidate, part name, etc.)
13
+ * @returns A localized error message describing the expected value
14
+ */
2
15
  export function createMessageValueExpected(t, baseTarget, type, matches) {
3
16
  let target = baseTarget;
4
17
  let listDescriptionPart;
@@ -17,6 +30,20 @@ export function createMessageValueExpected(t, baseTarget, type, matches) {
17
30
  .join(t('. '));
18
31
  return message;
19
32
  }
33
+ /**
34
+ * Assembles the detailed portion of a value-expected error message by
35
+ * inspecting the mismatch reason and composing reason, expectation,
36
+ * candidate suggestion, and fallback parts into a single string.
37
+ *
38
+ * This is an internal helper exposed for testing and advanced use.
39
+ * The double-underscore prefix indicates it is not part of the stable public API.
40
+ *
41
+ * @param t - The i18n translator for generating localized messages
42
+ * @param baseTarget - A human-readable description of the validation target
43
+ * @param expected - A description of the expected value(s), or `null` if unknown
44
+ * @param matches - Partial match result containing reason, candidate, and other details
45
+ * @returns A localized error message string
46
+ */
20
47
  export function __createMessageValueExpected(t, baseTarget, expected, matches) {
21
48
  let target = baseTarget;
22
49
  let reasonPart;
@@ -132,7 +159,7 @@ export function __createMessageValueExpected(t, baseTarget, expected, matches) {
132
159
  switch (matches.reason.type) {
133
160
  case 'out-of-range-length-digit': {
134
161
  const { gte, lte } = matches.reason;
135
- let expectedDigits = null;
162
+ let expectedDigits;
136
163
  if (lte != null && gte === lte) {
137
164
  expectedDigits = t('{0} digits', gte);
138
165
  }
@@ -199,6 +226,17 @@ export function __createMessageValueExpected(t, baseTarget, expected, matches) {
199
226
  }
200
227
  return message;
201
228
  }
229
+ /**
230
+ * Derives a human-readable description of the expected value from an attribute
231
+ * type definition and the unmatched result. Inspects keywords, enums, numbers,
232
+ * directives, and explicit `expects` entries from the match result.
233
+ *
234
+ * @param type - The attribute type (excluding list wrappers)
235
+ * @param matches - The unmatched result containing optional `expects` hints
236
+ * @param t - The i18n translator
237
+ * @returns A localized string describing the expected value(s), or `null` if
238
+ * no expectation can be determined
239
+ */
202
240
  function createExpectedObject(type, matches, t) {
203
241
  const expectedObject = [];
204
242
  if (matches.expects && matches.expects.length > 0) {
@@ -227,6 +265,14 @@ function createExpectedObject(type, matches, t) {
227
265
  : (expectedObject[0] ?? null);
228
266
  return expects;
229
267
  }
268
+ /**
269
+ * Converts a single `Expect` descriptor into a human-readable word or phrase.
270
+ *
271
+ * @param t - The i18n translator
272
+ * @param expect - The expectation descriptor from a type-check result
273
+ * @param type - The attribute type context for determining phrasing (e.g. CSS syntax)
274
+ * @returns A localized word or phrase describing the expected value
275
+ */
230
276
  function expectValueToWord(t, expect, type) {
231
277
  switch (expect.type) {
232
278
  case 'common': {
@@ -249,6 +295,15 @@ function expectValueToWord(t, expect, type) {
249
295
  }
250
296
  }
251
297
  }
298
+ /**
299
+ * Builds a human-readable description of an expected numeric range
300
+ * from a `Number` type definition (e.g. "integer greater than or equal to 0").
301
+ *
302
+ * @param t - The i18n translator
303
+ * @param type - The number type definition containing range constraints
304
+ * (`gt`, `gte`, `lt`, `lte`) and the numeric type label
305
+ * @returns A localized description of the expected numeric range
306
+ */
252
307
  function createExpectedNumber(t, type) {
253
308
  if (type.gt != null) {
254
309
  if (type.lt != null) {
package/lib/debug.d.ts CHANGED
@@ -1,3 +1,12 @@
1
1
  import debug from 'debug';
2
+ /**
3
+ * Type alias for the debug logger function provided by the `debug` package.
4
+ * Used throughout the rules package for conditional diagnostic logging.
5
+ */
2
6
  export type Log = debug.Debugger;
7
+ /**
8
+ * Shared debug logger instance for the `@markuplint/rules` package.
9
+ * Enable by setting the `DEBUG` environment variable to include `ml-rules`
10
+ * (e.g. `DEBUG=ml-rules`).
11
+ */
3
12
  export declare const log: debug.Debugger;
package/lib/debug.js CHANGED
@@ -1,2 +1,7 @@
1
1
  import debug from 'debug';
2
+ /**
3
+ * Shared debug logger instance for the `@markuplint/rules` package.
4
+ * Enable by setting the `DEBUG` environment variable to include `ml-rules`
5
+ * (e.g. `DEBUG=ml-rules`).
6
+ */
2
7
  export const log = debug('ml-rules');
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Rule that reports the use of deprecated or obsolete HTML attributes.
3
+ *
4
+ * Checks each attribute against the HTML spec and reports any that are
5
+ * marked as deprecated or obsolete.
6
+ */
1
7
  declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
2
8
  export default _default;
@@ -1,5 +1,11 @@
1
1
  import { createRule, getAttrSpecs } from '@markuplint/ml-core';
2
2
  import meta from './meta.js';
3
+ /**
4
+ * Rule that reports the use of deprecated or obsolete HTML attributes.
5
+ *
6
+ * Checks each attribute against the HTML spec and reports any that are
7
+ * marked as deprecated or obsolete.
8
+ */
3
9
  export default createRule({
4
10
  meta: meta,
5
11
  async verify({ document, report, t }) {