@markuplint/ml-spec 4.10.1 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/ARCHITECTURE.ja.md +267 -0
  2. package/ARCHITECTURE.md +267 -0
  3. package/CHANGELOG.md +20 -2
  4. package/README.md +6 -188
  5. package/SKILL.md +116 -0
  6. package/docs/aria-algorithms.ja.md +798 -0
  7. package/docs/aria-algorithms.md +800 -0
  8. package/docs/html-algorithms.ja.md +469 -0
  9. package/docs/html-algorithms.md +469 -0
  10. package/docs/maintenance.ja.md +331 -0
  11. package/docs/maintenance.md +331 -0
  12. package/docs/spec-resolution.ja.md +568 -0
  13. package/docs/spec-resolution.md +580 -0
  14. package/docs/type-definitions.ja.md +565 -0
  15. package/docs/type-definitions.md +565 -0
  16. package/lib/algorithm/aria/accname/__tests__/test-helpers.d.ts +45 -0
  17. package/lib/algorithm/aria/accname/__tests__/test-helpers.js +120 -0
  18. package/lib/algorithm/aria/accname/aria-steps.d.ts +51 -0
  19. package/lib/algorithm/aria/accname/aria-steps.js +104 -0
  20. package/lib/algorithm/aria/accname/compute.d.ts +51 -0
  21. package/lib/algorithm/aria/accname/compute.js +101 -0
  22. package/lib/algorithm/aria/accname/element-names.d.ts +36 -0
  23. package/lib/algorithm/aria/accname/element-names.js +342 -0
  24. package/lib/algorithm/aria/accname/helpers.d.ts +98 -0
  25. package/lib/algorithm/aria/accname/helpers.js +330 -0
  26. package/lib/algorithm/aria/accname/index.d.ts +4 -0
  27. package/lib/algorithm/aria/accname/index.js +3 -0
  28. package/lib/algorithm/aria/accname/label-steps.d.ts +25 -0
  29. package/lib/algorithm/aria/accname/label-steps.js +66 -0
  30. package/lib/algorithm/aria/accname/svg-helpers.d.ts +17 -0
  31. package/lib/algorithm/aria/accname/svg-helpers.js +30 -0
  32. package/lib/algorithm/aria/accname/types.d.ts +70 -0
  33. package/lib/algorithm/aria/accname/types.js +2 -0
  34. package/lib/algorithm/aria/accname-computation.d.ts +19 -1
  35. package/lib/algorithm/aria/accname-computation.js +136 -6
  36. package/lib/algorithm/aria/aria-specs.d.ts +8 -0
  37. package/lib/algorithm/aria/aria-specs.js +7 -0
  38. package/lib/algorithm/aria/get-aria.d.ts +12 -0
  39. package/lib/algorithm/aria/get-aria.js +42 -4
  40. package/lib/algorithm/aria/get-computed-aria-props.d.ts +22 -0
  41. package/lib/algorithm/aria/get-computed-aria-props.js +10 -0
  42. package/lib/algorithm/aria/get-computed-role.d.ts +12 -0
  43. package/lib/algorithm/aria/get-computed-role.js +118 -26
  44. package/lib/algorithm/aria/get-explicit-role.d.ts +12 -0
  45. package/lib/algorithm/aria/get-explicit-role.js +12 -0
  46. package/lib/algorithm/aria/get-implicit-role.d.ts +18 -0
  47. package/lib/algorithm/aria/get-implicit-role.js +18 -0
  48. package/lib/algorithm/aria/get-non-presentational-ancestor.d.ts +12 -0
  49. package/lib/algorithm/aria/get-non-presentational-ancestor.js +14 -2
  50. package/lib/algorithm/aria/get-permitted-roles-spec.d.ts +8 -4
  51. package/lib/algorithm/aria/get-permitted-roles-spec.js +19 -6
  52. package/lib/algorithm/aria/get-permitted-roles.d.ts +9 -0
  53. package/lib/algorithm/aria/get-permitted-roles.js +9 -0
  54. package/lib/algorithm/aria/get-role-spec.d.ts +11 -0
  55. package/lib/algorithm/aria/get-role-spec.js +21 -3
  56. package/lib/algorithm/aria/has-required-owned-elements.d.ts +24 -0
  57. package/lib/algorithm/aria/has-required-owned-elements.js +40 -14
  58. package/lib/algorithm/aria/is-exposed.d.ts +7 -4
  59. package/lib/algorithm/aria/is-exposed.js +7 -4
  60. package/lib/algorithm/aria/is-presentational.d.ts +32 -0
  61. package/lib/algorithm/aria/is-presentational.js +39 -0
  62. package/lib/algorithm/aria/matches-context-role.d.ts +19 -0
  63. package/lib/algorithm/aria/matches-context-role.js +46 -2
  64. package/lib/algorithm/html/content-model-category-to-tag-names.d.ts +9 -0
  65. package/lib/algorithm/html/content-model-category-to-tag-names.js +10 -1
  66. package/lib/algorithm/html/get-content-model.d.ts +12 -1
  67. package/lib/algorithm/html/get-content-model.js +14 -6
  68. package/lib/algorithm/html/get-selectors-by-content-model-category.d.ts +8 -0
  69. package/lib/algorithm/html/get-selectors-by-content-model-category.js +8 -0
  70. package/lib/algorithm/html/is-nothing-content-model.d.ts +7 -0
  71. package/lib/algorithm/html/is-nothing-content-model.js +7 -0
  72. package/lib/algorithm/html/is-palpable-elements.d.ts +13 -0
  73. package/lib/algorithm/html/is-palpable-elements.js +13 -0
  74. package/lib/algorithm/html/is-void-element.d.ts +9 -0
  75. package/lib/algorithm/html/is-void-element.js +9 -0
  76. package/lib/algorithm/html/may-be-focusable.d.ts +10 -0
  77. package/lib/algorithm/html/may-be-focusable.js +10 -0
  78. package/lib/const/accname.d.ts +29 -0
  79. package/lib/const/accname.js +76 -0
  80. package/lib/const/dom.d.ts +8 -0
  81. package/lib/const/dom.js +8 -0
  82. package/lib/const/index.d.ts +2 -0
  83. package/lib/const/index.js +2 -0
  84. package/lib/index.d.ts +3 -0
  85. package/lib/index.js +4 -0
  86. package/lib/types/index.d.ts +119 -0
  87. package/lib/utils/aria-version.d.ts +6 -0
  88. package/lib/utils/aria-version.js +6 -0
  89. package/lib/utils/directive-resolver.d.ts +23 -0
  90. package/lib/utils/directive-resolver.js +50 -0
  91. package/lib/utils/get-attr-specs-spec.d.ts +18 -0
  92. package/lib/utils/get-attr-specs-spec.js +22 -3
  93. package/lib/utils/get-attr-specs.d.ts +9 -0
  94. package/lib/utils/get-attr-specs.js +9 -0
  95. package/lib/utils/get-ns.d.ts +7 -0
  96. package/lib/utils/get-ns.js +7 -0
  97. package/lib/utils/get-spec-by-tag-name.d.ts +12 -1
  98. package/lib/utils/get-spec-by-tag-name.js +11 -0
  99. package/lib/utils/get-spec.d.ts +11 -1
  100. package/lib/utils/get-spec.js +10 -0
  101. package/lib/utils/merge-array.d.ts +10 -0
  102. package/lib/utils/merge-array.js +10 -0
  103. package/lib/utils/resolve-namespace.d.ts +13 -0
  104. package/lib/utils/resolve-namespace.js +10 -0
  105. package/lib/utils/resolve-version.d.ts +11 -0
  106. package/lib/utils/resolve-version.js +11 -0
  107. package/lib/utils/schema-to-spec.d.ts +7 -2
  108. package/lib/utils/schema-to-spec.js +20 -6
  109. package/lib/utils/validate-aria-version.d.ts +7 -0
  110. package/lib/utils/validate-aria-version.js +7 -0
  111. package/package.json +9 -7
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Retrieves the CSS selectors associated with a content model category from the spec definitions.
3
+ * These selectors can be used to match elements that belong to the given category.
4
+ *
5
+ * @param specs - The full markup language specification
6
+ * @param category - The content model category identifier (e.g., `#flow`, `#interactive`)
7
+ * @returns A readonly array of CSS selector strings for the category, or an empty array if the category is not defined
8
+ */
1
9
  export function getSelectorsByContentModelCategory(specs, category) {
2
10
  const selectors = specs.def['#contentModels'][category];
3
11
  return selectors ?? [];
@@ -1,5 +1,12 @@
1
1
  /**
2
+ * Determines whether an element uses the "nothing" content model, meaning it
3
+ * must not contain any content. This includes void elements as well as
4
+ * `<iframe>` and `<template>` elements.
5
+ *
2
6
  * @see https://html.spec.whatwg.org/multipage/dom.html#the-nothing-content-model
3
7
  * @see https://html.spec.whatwg.org/multipage/indices.html#elements-3
8
+ *
9
+ * @param el - The DOM element to check
10
+ * @returns `true` if the element has the "nothing" content model
4
11
  */
5
12
  export declare function isNothingContentModel(el: Element): boolean;
@@ -1,7 +1,14 @@
1
1
  import { isVoidElement } from './is-void-element.js';
2
2
  /**
3
+ * Determines whether an element uses the "nothing" content model, meaning it
4
+ * must not contain any content. This includes void elements as well as
5
+ * `<iframe>` and `<template>` elements.
6
+ *
3
7
  * @see https://html.spec.whatwg.org/multipage/dom.html#the-nothing-content-model
4
8
  * @see https://html.spec.whatwg.org/multipage/indices.html#elements-3
9
+ *
10
+ * @param el - The DOM element to check
11
+ * @returns `true` if the element has the "nothing" content model
5
12
  */
6
13
  export function isNothingContentModel(
7
14
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -1,4 +1,17 @@
1
1
  import type { MLMLSpec } from '../../types/index.js';
2
+ /**
3
+ * Determines whether an element is considered palpable content. Palpable elements
4
+ * are those that render something visible or meaningful to the user. Optionally
5
+ * extends the check to include SVG renderable elements and other exposable elements
6
+ * that do not belong to any standard content model category.
7
+ *
8
+ * @param el - The DOM element to check
9
+ * @param specs - The full markup language specification
10
+ * @param options - Optional flags to extend the palpable check
11
+ * @param options.extendsSvg - Whether to include SVG renderable elements (defaults to true)
12
+ * @param options.extendsExposableElements - Whether to include additional exposable elements like `<li>`, `<td>`, etc. (defaults to false)
13
+ * @returns `true` if the element is considered palpable content
14
+ */
2
15
  export declare function isPalpableElement(el: Element, specs: MLMLSpec, options?: {
3
16
  readonly extendsSvg?: boolean;
4
17
  readonly extendsExposableElements?: boolean;
@@ -28,6 +28,19 @@ const exposableElementsThatAreNoBelongingAModel = [
28
28
  'thead',
29
29
  'tr',
30
30
  ];
31
+ /**
32
+ * Determines whether an element is considered palpable content. Palpable elements
33
+ * are those that render something visible or meaningful to the user. Optionally
34
+ * extends the check to include SVG renderable elements and other exposable elements
35
+ * that do not belong to any standard content model category.
36
+ *
37
+ * @param el - The DOM element to check
38
+ * @param specs - The full markup language specification
39
+ * @param options - Optional flags to extend the palpable check
40
+ * @param options.extendsSvg - Whether to include SVG renderable elements (defaults to true)
41
+ * @param options.extendsExposableElements - Whether to include additional exposable elements like `<li>`, `<td>`, etc. (defaults to false)
42
+ * @returns `true` if the element is considered palpable content
43
+ */
31
44
  export function isPalpableElement(
32
45
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
33
46
  el, specs, options) {
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Determines whether an element is a void element as defined by the HTML specification.
3
+ * Void elements cannot have any contents (e.g., `<br>`, `<img>`, `<input>`).
4
+ *
5
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#void-elements
6
+ *
7
+ * @param el - An object with a `localName` property representing the element's tag name
8
+ * @returns `true` if the element is a void element
9
+ */
1
10
  export declare function isVoidElement(el: {
2
11
  readonly localName: string;
3
12
  }): boolean;
@@ -16,6 +16,15 @@ const voidElements = new Set([
16
16
  'track',
17
17
  'wbr',
18
18
  ]);
19
+ /**
20
+ * Determines whether an element is a void element as defined by the HTML specification.
21
+ * Void elements cannot have any contents (e.g., `<br>`, `<img>`, `<input>`).
22
+ *
23
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#void-elements
24
+ *
25
+ * @param el - An object with a `localName` property representing the element's tag name
26
+ * @returns `true` if the element is a void element
27
+ */
19
28
  export function isVoidElement(el) {
20
29
  return voidElements.has(el.localName);
21
30
  }
@@ -1,2 +1,12 @@
1
1
  import type { MLMLSpec } from '../../types/index.js';
2
+ /**
3
+ * Determines whether an element may potentially be focusable, based on whether it matches
4
+ * interactive content selectors, has a `tabindex` attribute, or has a `contenteditable`
5
+ * attribute. This is a heuristic check that does not account for runtime state such as
6
+ * `disabled` or `inert` attributes.
7
+ *
8
+ * @param el - The DOM element to check
9
+ * @param specs - The full markup language specification containing interactive content definitions
10
+ * @returns `true` if the element may be focusable
11
+ */
2
12
  export declare function mayBeFocusable(el: Element, specs: MLMLSpec): boolean;
@@ -1,4 +1,14 @@
1
1
  import { getSelectorsByContentModelCategory } from './get-selectors-by-content-model-category.js';
2
+ /**
3
+ * Determines whether an element may potentially be focusable, based on whether it matches
4
+ * interactive content selectors, has a `tabindex` attribute, or has a `contenteditable`
5
+ * attribute. This is a heuristic check that does not account for runtime state such as
6
+ * `disabled` or `inert` attributes.
7
+ *
8
+ * @param el - The DOM element to check
9
+ * @param specs - The full markup language specification containing interactive content definitions
10
+ * @returns `true` if the element may be focusable
11
+ */
2
12
  export function mayBeFocusable(
3
13
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
4
14
  el, specs) {
@@ -0,0 +1,29 @@
1
+ import type { AccnameElement } from '../algorithm/aria/accname/types.js';
2
+ /**
3
+ * ARIA roles that are embedded controls per AccName Step 2C.
4
+ * @see https://www.w3.org/TR/accname-1.2/#comp_embedded_control
5
+ */
6
+ export declare const EMBEDDED_CONTROL_ROLES: ReadonlySet<string>;
7
+ /**
8
+ * Input types using label → title → placeholder name computation (HTML-AAM §4.1).
9
+ */
10
+ export declare const TEXT_INPUT_TYPES: ReadonlySet<string>;
11
+ /**
12
+ * Input types that behave as text-like controls (value attr → textContent).
13
+ * Subset of TEXT_INPUT_TYPES used for embedded control value extraction.
14
+ */
15
+ export declare const TEXT_LIKE_INPUT_TYPES: ReadonlySet<string>;
16
+ /** Default accessible name for input[type=submit] per HTML-AAM §4.1 */
17
+ export declare const DEFAULT_SUBMIT_LABEL = "Submit";
18
+ /** Default accessible name for input[type=reset] per HTML-AAM §4.1 */
19
+ export declare const DEFAULT_RESET_LABEL = "Reset";
20
+ /** Default accessible name for input[type=image] per HTML-AAM §4.1 */
21
+ export declare const DEFAULT_IMAGE_LABEL = "Submit Query";
22
+ /**
23
+ * Checks if a native HTML element is an embedded control (without role resolution).
24
+ * Centralizes the logic duplicated across accname-computation.ts, ml-core/accname.ts, test-helpers.ts.
25
+ *
26
+ * @param el - The element to check
27
+ * @returns True if the element is a native embedded control (textarea, select, or input with a value-producing type)
28
+ */
29
+ export declare function isNativeEmbeddedControl(el: AccnameElement): boolean;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * ARIA roles that are embedded controls per AccName Step 2C.
3
+ * @see https://www.w3.org/TR/accname-1.2/#comp_embedded_control
4
+ */
5
+ export const EMBEDDED_CONTROL_ROLES = new Set([
6
+ 'textbox',
7
+ 'combobox',
8
+ 'listbox',
9
+ 'spinbutton',
10
+ 'slider',
11
+ 'searchbox',
12
+ ]);
13
+ /**
14
+ * Input types using label → title → placeholder name computation (HTML-AAM §4.1).
15
+ */
16
+ export const TEXT_INPUT_TYPES = new Set([
17
+ 'text',
18
+ 'password',
19
+ 'search',
20
+ 'tel',
21
+ 'url',
22
+ 'email',
23
+ 'number',
24
+ 'date',
25
+ 'month',
26
+ 'week',
27
+ 'time',
28
+ 'datetime-local',
29
+ 'color',
30
+ 'range',
31
+ 'file',
32
+ ]);
33
+ /**
34
+ * Input types that behave as text-like controls (value attr → textContent).
35
+ * Subset of TEXT_INPUT_TYPES used for embedded control value extraction.
36
+ */
37
+ export const TEXT_LIKE_INPUT_TYPES = new Set([
38
+ 'text',
39
+ 'search',
40
+ 'tel',
41
+ 'url',
42
+ 'email',
43
+ 'password',
44
+ 'number',
45
+ ]);
46
+ /** Default accessible name for input[type=submit] per HTML-AAM §4.1 */
47
+ export const DEFAULT_SUBMIT_LABEL = 'Submit';
48
+ /** Default accessible name for input[type=reset] per HTML-AAM §4.1 */
49
+ export const DEFAULT_RESET_LABEL = 'Reset';
50
+ /** Default accessible name for input[type=image] per HTML-AAM §4.1 */
51
+ export const DEFAULT_IMAGE_LABEL = 'Submit Query';
52
+ /**
53
+ * Checks if a native HTML element is an embedded control (without role resolution).
54
+ * Centralizes the logic duplicated across accname-computation.ts, ml-core/accname.ts, test-helpers.ts.
55
+ *
56
+ * @param el - The element to check
57
+ * @returns True if the element is a native embedded control (textarea, select, or input with a value-producing type)
58
+ */
59
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
60
+ export function isNativeEmbeddedControl(el) {
61
+ const { localName } = el;
62
+ if (localName === 'textarea' || localName === 'select') {
63
+ return true;
64
+ }
65
+ if (localName === 'input') {
66
+ const type = (el.getAttribute('type') ?? 'text').toLowerCase();
67
+ return (type !== 'hidden' &&
68
+ type !== 'button' &&
69
+ type !== 'submit' &&
70
+ type !== 'reset' &&
71
+ type !== 'image' &&
72
+ type !== 'checkbox' &&
73
+ type !== 'radio');
74
+ }
75
+ return false;
76
+ }
@@ -0,0 +1,8 @@
1
+ /** DOM Node.ELEMENT_NODE */
2
+ export declare const ELEMENT_NODE = 1;
3
+ /** DOM Node.TEXT_NODE */
4
+ export declare const TEXT_NODE = 3;
5
+ /** SVG namespace URI */
6
+ export declare const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
7
+ /** XHTML namespace URI */
8
+ export declare const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
@@ -0,0 +1,8 @@
1
+ /** DOM Node.ELEMENT_NODE */
2
+ export const ELEMENT_NODE = 1;
3
+ /** DOM Node.TEXT_NODE */
4
+ export const TEXT_NODE = 3;
5
+ /** SVG namespace URI */
6
+ export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
7
+ /** XHTML namespace URI */
8
+ export const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
@@ -0,0 +1,2 @@
1
+ export { ELEMENT_NODE, TEXT_NODE, SVG_NAMESPACE, XHTML_NAMESPACE } from './dom.js';
2
+ export { EMBEDDED_CONTROL_ROLES, TEXT_INPUT_TYPES, TEXT_LIKE_INPUT_TYPES, DEFAULT_SUBMIT_LABEL, DEFAULT_RESET_LABEL, DEFAULT_IMAGE_LABEL, isNativeEmbeddedControl, } from './accname.js';
@@ -0,0 +1,2 @@
1
+ export { ELEMENT_NODE, TEXT_NODE, SVG_NAMESPACE, XHTML_NAMESPACE } from './dom.js';
2
+ export { EMBEDDED_CONTROL_ROLES, TEXT_INPUT_TYPES, TEXT_LIKE_INPUT_TYPES, DEFAULT_SUBMIT_LABEL, DEFAULT_RESET_LABEL, DEFAULT_IMAGE_LABEL, isNativeEmbeddedControl, } from './accname.js';
package/lib/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { getAttrSpecs as getAttrSpecsByNames } from './utils/get-attr-specs-spec.js';
2
2
  export * from './utils/aria-version.js';
3
+ export { EMBEDDED_CONTROL_ROLES, isNativeEmbeddedControl } from './const/index.js';
4
+ export * from './algorithm/aria/accname/index.js';
3
5
  export * from './algorithm/aria/accname-computation.js';
4
6
  export * from './algorithm/aria/get-computed-aria-props.js';
5
7
  export * from './algorithm/aria/get-computed-role.js';
@@ -24,6 +26,7 @@ export * from './utils/get-spec-by-tag-name.js';
24
26
  export * from './utils/schema-to-spec.js';
25
27
  export * from './utils/resolve-namespace.js';
26
28
  export * from './utils/validate-aria-version.js';
29
+ export * from './utils/directive-resolver.js';
27
30
  export * from './types/index.js';
28
31
  export * from './types/aria.js';
29
32
  export * from './types/attributes.js';
package/lib/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  export { getAttrSpecs as getAttrSpecsByNames } from './utils/get-attr-specs-spec.js';
2
2
  export * from './utils/aria-version.js';
3
+ // Constants
4
+ export { EMBEDDED_CONTROL_ROLES, isNativeEmbeddedControl } from './const/index.js';
3
5
  // ARIA algorithms
6
+ export * from './algorithm/aria/accname/index.js';
4
7
  export * from './algorithm/aria/accname-computation.js';
5
8
  export * from './algorithm/aria/get-computed-aria-props.js';
6
9
  export * from './algorithm/aria/get-computed-role.js';
@@ -27,6 +30,7 @@ export * from './utils/get-spec-by-tag-name.js';
27
30
  export * from './utils/schema-to-spec.js';
28
31
  export * from './utils/resolve-namespace.js';
29
32
  export * from './utils/validate-aria-version.js';
33
+ export * from './utils/directive-resolver.js';
30
34
  // Type definitions
31
35
  export * from './types/index.js';
32
36
  export * from './types/aria.js';
@@ -11,20 +11,85 @@ export interface MLMLSpec {
11
11
  readonly cites: Cites;
12
12
  readonly def: SpecDefs;
13
13
  readonly specs: readonly ElementSpec[];
14
+ readonly directivePatterns?: readonly DirectivePattern[];
15
+ readonly useIDLAttributeNames?: boolean;
14
16
  }
17
+ /**
18
+ * An element specification for extending or overriding parts of the base spec.
19
+ * Only the `name` property is required; all other properties are optional partial overrides.
20
+ * Attributes use `Partial<Attribute>` to allow specifying only changed fields.
21
+ */
15
22
  export type ExtendedElementSpec = Partial<Omit<ElementSpec, 'name' | 'attributes'>> & {
16
23
  readonly name: ElementSpec['name'];
17
24
  readonly attributes?: Readonly<Record<string, Partial<Attribute>>>;
18
25
  };
26
+ /**
27
+ * A partial specification used for extending or customizing the base markup language spec.
28
+ * Typically provided by parser plugins or framework-specific spec packages (e.g., `@markuplint/vue-spec`).
29
+ */
19
30
  export type ExtendedSpec = {
20
31
  readonly cites?: Cites;
21
32
  readonly def?: Partial<SpecDefs>;
22
33
  readonly specs?: readonly ExtendedElementSpec[];
34
+ readonly directivePatterns?: readonly DirectivePattern[];
35
+ readonly useIDLAttributeNames?: boolean;
36
+ };
37
+ /**
38
+ * A declarative pattern for resolving framework-specific directive
39
+ * attributes to their canonical names and metadata. Stored as part
40
+ * of the spec, enabling "parser-less" framework support.
41
+ *
42
+ * The `pattern` is a regex string matched against the raw attribute name.
43
+ * Capture groups can be referenced in `potentialName` using `$1`, `$2`, etc.
44
+ */
45
+ export type DirectivePattern = {
46
+ /**
47
+ * A regex string (without delimiters) matched against the raw attribute name.
48
+ * Must be a valid JavaScript RegExp pattern. Capture groups can be used
49
+ * for potentialName templates.
50
+ * @example "^(?:x-bind:|:)([^.]+)(?:\\.[^.]+)?$"
51
+ */
52
+ readonly pattern: string;
53
+ /**
54
+ * Optional regex flags (e.g., "i" for case-insensitive).
55
+ * @default "i"
56
+ */
57
+ readonly flags?: string;
58
+ /**
59
+ * Template for the resolved potentialName. References capture groups
60
+ * with $1, $2, etc. If omitted, the attribute is treated as a directive
61
+ * with no potentialName change.
62
+ * @example "on$1" -- for `@click` -> `onclick`
63
+ * @example "$1" -- for `:href` -> `href`
64
+ */
65
+ readonly potentialName?: string;
66
+ /**
67
+ * Whether this attribute is a framework directive.
68
+ */
69
+ readonly isDirective?: true;
70
+ /**
71
+ * Whether this attribute has a dynamic value.
72
+ */
73
+ readonly isDynamicValue?: true;
74
+ /**
75
+ * The semantic value type for matched attributes.
76
+ */
77
+ readonly valueType?: 'string' | 'number' | 'boolean' | 'code';
78
+ /**
79
+ * If true, the attribute can appear multiple times on the same element.
80
+ * Can also be a string array of potentialName values for which
81
+ * duplication is allowed (e.g., ['class', 'style']).
82
+ */
83
+ readonly isDuplicatable?: true | readonly string[];
23
84
  };
24
85
  /**
25
86
  * Reference URLs
26
87
  */
27
88
  export type Cites = readonly string[];
89
+ /**
90
+ * Internal definition data within a markup language spec, containing global attributes,
91
+ * ARIA role/property definitions for each version, and content model category mappings.
92
+ */
28
93
  export type SpecDefs = {
29
94
  readonly '#globalAttrs': {
30
95
  readonly [category: string]: Readonly<Record<string, Partial<Attribute>>>;
@@ -41,6 +106,7 @@ export type SpecDefs = {
41
106
  type ARIASpec = {
42
107
  readonly roles: readonly ARIARoleInSchema[];
43
108
  readonly graphicsRoles: readonly ARIARoleInSchema[];
109
+ readonly dpubRoles: readonly ARIARoleInSchema[];
44
110
  readonly props: readonly ARIAProperty[];
45
111
  };
46
112
  /**
@@ -124,6 +190,9 @@ type ElementSpecOmissionTags = {
124
190
  type ElementCondition = {
125
191
  readonly __WIP__: 'WORK_IN_PROGRESS';
126
192
  };
193
+ /**
194
+ * Describes a single HTML/SVG attribute with its type, description, and status flags.
195
+ */
127
196
  export type Attribute = {
128
197
  readonly name: string;
129
198
  readonly type: ReadonlyDeep<AttributeType> | readonly ReadonlyDeep<AttributeType>[];
@@ -135,11 +204,26 @@ export type Attribute = {
135
204
  readonly nonStandard?: true;
136
205
  } & ExtendableAttributeSpec;
137
206
  type ExtendableAttributeSpec = Omit<ReadonlyDeep<AttributeJSON>, 'type'>;
207
+ /**
208
+ * A fully resolved ARIA role with all its properties, requirements, and naming constraints.
209
+ */
138
210
  export type ARIARole = {
139
211
  readonly name: string;
140
212
  readonly isAbstract: boolean;
141
213
  readonly deprecated: boolean;
214
+ /** Required Accessibility Parent Role (ARIA 1.3 name) */
215
+ readonly requiredAccessibilityParentRole: readonly string[];
216
+ /** Allowed Accessibility Child Roles (ARIA 1.3 name) */
217
+ readonly allowedAccessibilityChildRoles: readonly string[];
218
+ /**
219
+ * @deprecated Use {@link ARIARole.requiredAccessibilityParentRole} instead.
220
+ * Retained for ARIA 1.2 backward compatibility.
221
+ */
142
222
  readonly requiredContextRole: readonly string[];
223
+ /**
224
+ * @deprecated Use {@link ARIARole.allowedAccessibilityChildRoles} instead.
225
+ * Retained for ARIA 1.2 backward compatibility.
226
+ */
143
227
  readonly requiredOwnedElements: readonly string[];
144
228
  readonly accessibleNameRequired: boolean;
145
229
  readonly accessibleNameFromAuthor: boolean;
@@ -149,18 +233,30 @@ export type ARIARole = {
149
233
  readonly ownedProperties: readonly ARIARoleOwnedProperties[];
150
234
  readonly prohibitedProperties: readonly string[];
151
235
  };
236
+ /**
237
+ * An ARIA role as defined in the raw schema data. All properties are optional
238
+ * except `name`, since the schema may provide only partial role information.
239
+ */
152
240
  export type ARIARoleInSchema = Partial<ARIARole & {
153
241
  readonly description: string;
154
242
  readonly generalization: readonly string[];
155
243
  }> & {
156
244
  readonly name: string;
157
245
  };
246
+ /**
247
+ * Describes a property owned by an ARIA role, including whether it is inherited,
248
+ * required, or deprecated.
249
+ */
158
250
  export type ARIARoleOwnedProperties = {
159
251
  readonly name: string;
160
252
  readonly inherited?: true;
161
253
  readonly required?: true;
162
254
  readonly deprecated?: true;
163
255
  };
256
+ /**
257
+ * Describes an ARIA property or state, including its value type, enumeration values,
258
+ * global status, default value, conditional values per role, and equivalent HTML attributes.
259
+ */
164
260
  export type ARIAProperty = {
165
261
  readonly name: string;
166
262
  readonly type: 'property' | 'state';
@@ -176,14 +272,33 @@ export type ARIAProperty = {
176
272
  readonly equivalentHtmlAttrs?: readonly EquivalentHtmlAttr[];
177
273
  readonly valueDescriptions?: Readonly<Record<string, string>>;
178
274
  };
275
+ /**
276
+ * The possible value types for ARIA attributes, as defined by the WAI-ARIA specification.
277
+ */
179
278
  export type ARIAAttributeValue = 'true/false' | 'tristate' | 'true/false/undefined' | 'ID reference' | 'ID reference list' | 'integer' | 'number' | 'string' | 'token' | 'token list' | 'URI';
279
+ /**
280
+ * A union type of supported ARIA specification version strings, derived from the `ariaVersions` tuple.
281
+ */
180
282
  export type ARIAVersion = (typeof ariaVersions)[number];
283
+ /**
284
+ * Describes an HTML attribute that is semantically equivalent to an ARIA property,
285
+ * enabling automatic mapping from HTML attributes to ARIA states/properties.
286
+ */
181
287
  export type EquivalentHtmlAttr = {
182
288
  readonly htmlAttrName: string;
183
289
  readonly isNotStrictEquivalent?: true;
184
290
  readonly value: string | null;
185
291
  };
292
+ /**
293
+ * A function that tests whether an element matches a given CSS selector string.
294
+ * Typically bound to `Element.prototype.matches`.
295
+ */
186
296
  export type Matches = (selector: string) => boolean;
297
+ /**
298
+ * The result of computing an element's ARIA role, containing the element reference,
299
+ * the resolved role specification (or null if no role applies), and an optional
300
+ * error type indicating issues during role computation.
301
+ */
187
302
  export type ComputedRole = {
188
303
  readonly el: Element;
189
304
  readonly role: (ARIARole & {
@@ -192,5 +307,9 @@ export type ComputedRole = {
192
307
  }) | null;
193
308
  readonly errorType?: RoleComputationError;
194
309
  };
310
+ /**
311
+ * Error codes that may arise during ARIA role computation, indicating specific
312
+ * issues such as abstract roles, invalid context, or presentational conflicts.
313
+ */
195
314
  export type RoleComputationError = 'ABSTRACT' | 'GLOBAL_PROP_MUST_NOT_BE_PRESENTATIONAL' | 'IMPLICIT_ROLE_NAMESPACE_ERROR' | 'INTERACTIVE_ELEMENT_MUST_NOT_BE_PRESENTATIONAL' | 'INVALID_LANDMARK' | 'INVALID_REQUIRED_CONTEXT_ROLE' | 'NO_EXPLICIT' | 'NO_OWNER' | 'NO_PERMITTED' | 'REQUIRED_OWNED_ELEMENT_MUST_NOT_BE_PRESENTATIONAL' | 'ROLE_NO_EXISTS';
196
315
  export {};
@@ -1,2 +1,8 @@
1
+ /**
2
+ * The supported ARIA specification versions as a readonly tuple.
3
+ */
1
4
  export declare const ariaVersions: readonly ["1.1", "1.2", "1.3"];
5
+ /**
6
+ * The recommended default ARIA specification version to use when none is explicitly specified.
7
+ */
2
8
  export declare const ARIA_RECOMMENDED_VERSION = "1.2";
@@ -1,2 +1,8 @@
1
+ /**
2
+ * The supported ARIA specification versions as a readonly tuple.
3
+ */
1
4
  export const ariaVersions = ['1.1', '1.2', '1.3'];
5
+ /**
6
+ * The recommended default ARIA specification version to use when none is explicitly specified.
7
+ */
2
8
  export const ARIA_RECOMMENDED_VERSION = '1.2';
@@ -0,0 +1,23 @@
1
+ import type { DirectivePattern } from '../types/index.js';
2
+ type CompiledDirectivePattern = {
3
+ readonly regex: RegExp;
4
+ readonly pattern: DirectivePattern;
5
+ };
6
+ /**
7
+ * Compiles directive patterns into RegExp objects, caching them
8
+ * by the patterns array reference for efficiency.
9
+ */
10
+ export declare function compileDirectivePatterns(patterns: readonly DirectivePattern[]): readonly CompiledDirectivePattern[];
11
+ export type DirectiveResolution = {
12
+ readonly potentialName?: string;
13
+ readonly isDirective?: true;
14
+ readonly isDynamicValue?: true;
15
+ readonly valueType?: 'string' | 'number' | 'boolean' | 'code';
16
+ readonly isDuplicatable?: boolean;
17
+ };
18
+ /**
19
+ * Resolves an attribute name against a list of compiled directive patterns.
20
+ * Returns the first match's resolution, or null if no pattern matches.
21
+ */
22
+ export declare function resolveDirective(attrName: string, compiledPatterns: readonly CompiledDirectivePattern[]): DirectiveResolution | null;
23
+ export {};
@@ -0,0 +1,50 @@
1
+ const cache = new WeakMap();
2
+ /**
3
+ * Compiles directive patterns into RegExp objects, caching them
4
+ * by the patterns array reference for efficiency.
5
+ */
6
+ export function compileDirectivePatterns(patterns) {
7
+ let compiled = cache.get(patterns);
8
+ if (!compiled) {
9
+ compiled = patterns.map(p => ({
10
+ regex: new RegExp(p.pattern, p.flags ?? 'i'),
11
+ pattern: p,
12
+ }));
13
+ cache.set(patterns, compiled);
14
+ }
15
+ return compiled;
16
+ }
17
+ /**
18
+ * Resolves an attribute name against a list of compiled directive patterns.
19
+ * Returns the first match's resolution, or null if no pattern matches.
20
+ */
21
+ export function resolveDirective(attrName,
22
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
23
+ compiledPatterns) {
24
+ for (const { regex, pattern } of compiledPatterns) {
25
+ regex.lastIndex = 0;
26
+ const match = regex.exec(attrName);
27
+ if (!match) {
28
+ continue;
29
+ }
30
+ let potentialName;
31
+ if (pattern.potentialName != null) {
32
+ potentialName = pattern.potentialName.replaceAll(/\$(\d+)/g, (_, idx) => (match[Number(idx)] ?? '').toLowerCase());
33
+ }
34
+ let isDuplicatable;
35
+ if (pattern.isDuplicatable === true) {
36
+ isDuplicatable = true;
37
+ }
38
+ else if (Array.isArray(pattern.isDuplicatable) && potentialName) {
39
+ isDuplicatable = pattern.isDuplicatable.includes(potentialName);
40
+ }
41
+ return {
42
+ potentialName,
43
+ isDirective: pattern.isDirective,
44
+ isDynamicValue: pattern.isDynamicValue,
45
+ valueType: pattern.valueType,
46
+ isDuplicatable,
47
+ };
48
+ }
49
+ return null;
50
+ }
@@ -1,8 +1,26 @@
1
1
  import type { MLMLSpec, Attribute } from '../types/index.js';
2
2
  import type { NamespaceURI } from '@markuplint/ml-ast';
3
+ /**
4
+ * Retrieves the merged attribute specifications for an element identified by its local name
5
+ * and namespace. Combines global attributes from the relevant categories with the element's
6
+ * own attribute definitions. Results are cached by the namespace-qualified element name.
7
+ *
8
+ * @param localName - The local tag name of the element
9
+ * @param namespace - The namespace URI of the element, or null for HTML namespace
10
+ * @param schema - The full markup language specification containing attribute definitions
11
+ * @returns A sorted array of attribute specifications for the element, or null if the element is not found in the schema
12
+ */
3
13
  export declare function getAttrSpecs(localName: string, namespace: NamespaceURI | null, schema: MLMLSpec): readonly Attribute[] | null;
4
14
  type HasName = {
5
15
  readonly name: string;
6
16
  };
17
+ /**
18
+ * Compares two items by name in a case-insensitive manner, suitable for use as
19
+ * a sort comparator. Accepts either objects with a `name` property or plain strings.
20
+ *
21
+ * @param a - The first item to compare (object with `name` property or a string)
22
+ * @param b - The second item to compare (object with `name` property or a string)
23
+ * @returns A negative number if `a` comes before `b`, positive if after, or 0 if equal
24
+ */
7
25
  export declare function nameCompare(a: HasName | string, b: HasName | string): 1 | -1 | 0;
8
26
  export {};