@markuplint/ml-spec 4.10.2 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +24 -10
- package/ARCHITECTURE.md +24 -10
- package/CHANGELOG.md +18 -0
- package/README.md +2 -2
- package/docs/aria-algorithms.ja.md +180 -33
- package/docs/aria-algorithms.md +183 -34
- package/docs/html-algorithms.ja.md +2 -2
- package/docs/html-algorithms.md +2 -2
- package/docs/maintenance.ja.md +13 -22
- package/docs/maintenance.md +13 -22
- package/docs/spec-resolution.ja.md +37 -9
- package/docs/spec-resolution.md +45 -16
- package/docs/type-definitions.ja.md +8 -4
- package/docs/type-definitions.md +8 -4
- package/lib/algorithm/aria/accname/__tests__/test-helpers.d.ts +45 -0
- package/lib/algorithm/aria/accname/__tests__/test-helpers.js +120 -0
- package/lib/algorithm/aria/accname/aria-steps.d.ts +51 -0
- package/lib/algorithm/aria/accname/aria-steps.js +104 -0
- package/lib/algorithm/aria/accname/compute.d.ts +51 -0
- package/lib/algorithm/aria/accname/compute.js +101 -0
- package/lib/algorithm/aria/accname/element-names.d.ts +36 -0
- package/lib/algorithm/aria/accname/element-names.js +342 -0
- package/lib/algorithm/aria/accname/helpers.d.ts +98 -0
- package/lib/algorithm/aria/accname/helpers.js +330 -0
- package/lib/algorithm/aria/accname/index.d.ts +4 -0
- package/lib/algorithm/aria/accname/index.js +3 -0
- package/lib/algorithm/aria/accname/label-steps.d.ts +25 -0
- package/lib/algorithm/aria/accname/label-steps.js +66 -0
- package/lib/algorithm/aria/accname/svg-helpers.d.ts +17 -0
- package/lib/algorithm/aria/accname/svg-helpers.js +30 -0
- package/lib/algorithm/aria/accname/types.d.ts +70 -0
- package/lib/algorithm/aria/accname/types.js +2 -0
- package/lib/algorithm/aria/accname-computation.d.ts +14 -3
- package/lib/algorithm/aria/accname-computation.js +131 -8
- package/lib/algorithm/aria/aria-specs.d.ts +1 -0
- package/lib/algorithm/aria/get-aria.js +30 -4
- package/lib/algorithm/aria/get-computed-role.js +106 -26
- package/lib/algorithm/aria/get-explicit-role.d.ts +12 -0
- package/lib/algorithm/aria/get-explicit-role.js +12 -0
- package/lib/algorithm/aria/get-non-presentational-ancestor.d.ts +12 -0
- package/lib/algorithm/aria/get-non-presentational-ancestor.js +14 -2
- package/lib/algorithm/aria/get-permitted-roles-spec.d.ts +8 -4
- package/lib/algorithm/aria/get-permitted-roles-spec.js +19 -6
- package/lib/algorithm/aria/get-role-spec.js +10 -3
- package/lib/algorithm/aria/has-required-owned-elements.d.ts +2 -1
- package/lib/algorithm/aria/has-required-owned-elements.js +18 -15
- package/lib/algorithm/aria/is-presentational.d.ts +24 -0
- package/lib/algorithm/aria/is-presentational.js +31 -0
- package/lib/algorithm/aria/matches-context-role.d.ts +12 -4
- package/lib/algorithm/aria/matches-context-role.js +39 -6
- package/lib/algorithm/html/content-model-category-to-tag-names.js +1 -1
- package/lib/algorithm/html/get-content-model.d.ts +4 -2
- package/lib/algorithm/html/get-content-model.js +6 -7
- package/lib/const/accname.d.ts +29 -0
- package/lib/const/accname.js +76 -0
- package/lib/const/dom.d.ts +8 -0
- package/lib/const/dom.js +8 -0
- package/lib/const/index.d.ts +2 -0
- package/lib/const/index.js +2 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -0
- package/lib/types/index.d.ts +65 -0
- package/lib/utils/directive-resolver.d.ts +23 -0
- package/lib/utils/directive-resolver.js +50 -0
- package/lib/utils/get-attr-specs-spec.js +4 -3
- package/lib/utils/get-ns.d.ts +7 -0
- package/lib/utils/get-ns.js +7 -0
- package/lib/utils/get-spec-by-tag-name.d.ts +1 -1
- package/lib/utils/merge-array.d.ts +10 -0
- package/lib/utils/merge-array.js +10 -0
- package/lib/utils/resolve-version.d.ts +11 -0
- package/lib/utils/resolve-version.js +11 -0
- package/lib/utils/schema-to-spec.d.ts +2 -0
- package/lib/utils/schema-to-spec.js +15 -4
- package/package.json +9 -7
package/docs/aria-algorithms.md
CHANGED
|
@@ -9,7 +9,7 @@ The implementation covers algorithms from the following specifications:
|
|
|
9
9
|
- **WAI-ARIA 1.1 / 1.2 / 1.3** -- Role definitions, states, and properties
|
|
10
10
|
- **HTML-AAM** (HTML Accessibility API Mappings) -- Implicit role mappings for HTML elements
|
|
11
11
|
- **SVG-AAM** (SVG Accessibility API Mappings) -- Accessibility tree inclusion rules for SVG
|
|
12
|
-
- **AccName 1.
|
|
12
|
+
- **AccName 1.2** (Accessible Name and Description Computation) -- Accessible name computation
|
|
13
13
|
- **ARIA in HTML** -- Permitted roles and ARIA attribute constraints per element
|
|
14
14
|
|
|
15
15
|
### Design Principles
|
|
@@ -19,7 +19,7 @@ All ARIA algorithm functions share a consistent design:
|
|
|
19
19
|
- They operate on the standard DOM `Element` interface, requiring no markuplint-specific node types.
|
|
20
20
|
- They accept an `MLMLSpec` parameter containing the full markup language specification data.
|
|
21
21
|
- They accept an `ARIAVersion` parameter (`'1.1'`, `'1.2'`, or `'1.3'`) to select version-specific behavior.
|
|
22
|
-
- They are pure functions with no side effects (aside from internal caching in `getARIA`).
|
|
22
|
+
- They are pure functions with no side effects (aside from internal caching in `getARIA` and a reentrant guard in `getAccname`).
|
|
23
23
|
|
|
24
24
|
## Role Computation Pipeline
|
|
25
25
|
|
|
@@ -39,7 +39,7 @@ flowchart TB
|
|
|
39
39
|
GCR --> CR{Computed Role}
|
|
40
40
|
CR -->|presentational| PCR["Presentational Roles<br/>Conflict Resolution"]
|
|
41
41
|
|
|
42
|
-
PCR --> Check1["1. Required
|
|
42
|
+
PCR --> Check1["1. Required Accessibility<br/>Parent Role validation"]
|
|
43
43
|
PCR --> Check2["2. SVG accessibility<br/>tree inclusion"]
|
|
44
44
|
PCR --> Check3["3. Interactive element<br/>protection"]
|
|
45
45
|
PCR --> Check4["4. Required owned element<br/>check"]
|
|
@@ -86,13 +86,13 @@ The core function of the ARIA algorithm suite. It computes the final ARIA role f
|
|
|
86
86
|
|
|
87
87
|
**Conflict Resolution checks (in order):**
|
|
88
88
|
|
|
89
|
-
1. **Required context role validation** -- If the role has `requiredContextRole`
|
|
89
|
+
1. **Required context role validation** -- If the role has `requiredAccessibilityParentRole` entries (called `requiredContextRole` in ARIA 1.2), the function checks the parent hierarchy. If no parent element exists, returns `NO_OWNER`. If the parent hierarchy does not satisfy the context role conditions (via `matchesContextRole()`), returns `INVALID_REQUIRED_CONTEXT_ROLE`. Presentational ancestors are traversed transparently via `getNonPresentationalAncestor()`.
|
|
90
90
|
|
|
91
|
-
2. **SVG accessibility tree inclusion** -- For SVG namespace elements without a valid explicit role, the function checks whether the element has an accessible name (via `
|
|
91
|
+
2. **SVG accessibility tree inclusion** -- For SVG namespace elements without a valid explicit role, the function checks whether the element has an accessible name source (via `hasSvgAccessibleNameSource()`) — i.e., `aria-label`, `aria-labelledby`, or a `<title>`/`<desc>` child element. If none exists, the SVG element is excluded from the accessibility tree (returns `role: null`). This implements the SVG-AAM rules for including normally-omitted SVG elements.
|
|
92
92
|
|
|
93
93
|
3. **Interactive element protection** -- Focusable elements cannot be presentational. The function checks `mayBeFocusable()` and ensures the element is not `disabled`, `inert`, or `hidden` (traversing ancestors for each attribute). If the element is interactive and not disabled/inert/hidden, the presentational role is overridden with the implicit role and `INTERACTIVE_ELEMENT_MUST_NOT_BE_PRESENTATIONAL` error.
|
|
94
94
|
|
|
95
|
-
4. **Required owned element check** -- If a non-presentational ancestor has `requiredOwnedElements` and the current element's implicit role matches one of those required owned elements, the presentational role is overridden. Returns `REQUIRED_OWNED_ELEMENT_MUST_NOT_BE_PRESENTATIONAL`.
|
|
95
|
+
4. **Required owned element check** -- If a non-presentational ancestor has `allowedAccessibilityChildRoles` (called `requiredOwnedElements` in ARIA 1.2) and the current element's implicit role matches one of those required owned elements, the presentational role is overridden. Returns `REQUIRED_OWNED_ELEMENT_MUST_NOT_BE_PRESENTATIONAL`.
|
|
96
96
|
|
|
97
97
|
5. **Global ARIA property check** -- If the element has any global ARIA properties (e.g., `aria-label`, `aria-describedby`), the presentational role is overridden with the implicit role. Returns `GLOBAL_PROP_MUST_NOT_BE_PRESENTATIONAL`.
|
|
98
98
|
|
|
@@ -242,12 +242,12 @@ The spec-level implementation for computing permitted ARIA roles. Operates on ta
|
|
|
242
242
|
|
|
243
243
|
| `permittedRoles` value | Behavior |
|
|
244
244
|
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
245
|
-
| `true` | All non-abstract roles from the ARIA spec are permitted.
|
|
245
|
+
| `true` | All non-abstract roles from the ARIA spec (including DPub roles) are permitted. |
|
|
246
246
|
| `PermittedARIAAAMInfo` object | If `core-aam` is `true`, adds all non-abstract roles. If `graphics-aam` is `true`, adds all non-abstract graphics roles. |
|
|
247
247
|
| Array of strings/objects | The specific listed roles are permitted. |
|
|
248
248
|
| `false` | No roles are permitted (empty list before implicit role). |
|
|
249
249
|
|
|
250
|
-
4. Always includes the implicit role in the result. If the implicit role is `"presentation"` or `"none"`, both equivalents are included.
|
|
250
|
+
4. Always includes the implicit role in the result. If the implicit role is `"presentation"` or `"none"`, both equivalents are included. In ARIA 1.3, if the implicit role is `"img"` or `"image"`, both synonyms are included.
|
|
251
251
|
5. Returns the merged, deduplicated list.
|
|
252
252
|
|
|
253
253
|
---
|
|
@@ -273,9 +273,10 @@ Retrieves the full ARIA role specification for a given role name, including the
|
|
|
273
273
|
|
|
274
274
|
1. Searches for the role by name in the ARIA roles list for the given version.
|
|
275
275
|
2. For SVG namespace (`http://www.w3.org/2000/svg`), also searches `graphicsRoles` if not found in core roles.
|
|
276
|
-
3.
|
|
277
|
-
4.
|
|
278
|
-
5.
|
|
276
|
+
3. If still not found, searches `dpubRoles` (DPub ARIA roles are accepted for all elements regardless of namespace).
|
|
277
|
+
4. Recursively traverses super-class roles via the `generalization` property, building the complete inheritance chain.
|
|
278
|
+
5. Normalizes all optional fields to non-undefined defaults (e.g., `!!role.isAbstract`). Resolves `requiredAccessibilityParentRole` from the schema's `requiredContextRole` (ARIA 1.2 name) or `requiredAccessibilityParentRole` (ARIA 1.3 name), and similarly `allowedAccessibilityChildRoles` from `requiredOwnedElements` or `allowedAccessibilityChildRoles`. Both the new and deprecated property names are populated with the same values.
|
|
279
|
+
6. Returns `null` if the role name does not exist in the spec.
|
|
279
280
|
|
|
280
281
|
**Normalized fields in the return value:**
|
|
281
282
|
|
|
@@ -284,8 +285,10 @@ Retrieves the full ARIA role specification for a given role name, including the
|
|
|
284
285
|
name: string;
|
|
285
286
|
isAbstract: boolean; // default: false
|
|
286
287
|
deprecated: boolean; // default: false
|
|
287
|
-
|
|
288
|
-
|
|
288
|
+
requiredAccessibilityParentRole: string[]; // default: []
|
|
289
|
+
allowedAccessibilityChildRoles: string[]; // default: []
|
|
290
|
+
requiredContextRole: string[]; // @deprecated alias — same as requiredAccessibilityParentRole
|
|
291
|
+
requiredOwnedElements: string[]; // @deprecated alias — same as allowedAccessibilityChildRoles
|
|
289
292
|
accessibleNameRequired: boolean; // default: false
|
|
290
293
|
accessibleNameFromAuthor: boolean; // default: false
|
|
291
294
|
accessibleNameFromContent: boolean;// default: false
|
|
@@ -322,7 +325,7 @@ Gets the version-resolved ARIA specification for an element, evaluating conditio
|
|
|
322
325
|
1. Calls `getVersionResolvedARIA()` which:
|
|
323
326
|
- Looks up the element spec by tag name and namespace.
|
|
324
327
|
- Applies `resolveVersion()` to merge version-specific overrides on top of the base ARIA spec.
|
|
325
|
-
- Optimizes permitted roles: if `"presentation"` is in the permitted roles array, `"none"` is added, and vice versa (per WAI-ARIA 1.2 note on the `none` role).
|
|
328
|
+
- Optimizes permitted roles: if `"presentation"` is in the permitted roles array, `"none"` is added, and vice versa (per WAI-ARIA 1.2 note on the `none` role). In ARIA 1.3, if `"image"` is present, `"img"` is added, and vice versa (per the ARIA 1.3 `image`/`img` synonym).
|
|
326
329
|
- Caches results by `localName + namespace + version`.
|
|
327
330
|
|
|
328
331
|
2. Evaluates conditional overrides (the `conditions` block in the ARIA spec):
|
|
@@ -386,25 +389,140 @@ Returns an empty record if the element has no computed role.
|
|
|
386
389
|
|
|
387
390
|
---
|
|
388
391
|
|
|
389
|
-
### 10. `getAccname(el): string`
|
|
392
|
+
### 10. `getAccname(el, specs, version): string`
|
|
390
393
|
|
|
391
394
|
**Source:** `src/algorithm/aria/accname-computation.ts`
|
|
392
395
|
|
|
393
|
-
Computes the accessible name for
|
|
396
|
+
Computes the accessible name for a DOM element using the HTML-AAM §4.1 algorithm. This is the public facade that creates a DOM-based resolver and delegates to the pure `computeAccessibleName()` function.
|
|
394
397
|
|
|
395
398
|
**Parameters:**
|
|
396
399
|
|
|
397
|
-
| Parameter | Type
|
|
398
|
-
| --------- |
|
|
399
|
-
| `el` | `Element`
|
|
400
|
+
| Parameter | Type | Description |
|
|
401
|
+
| --------- | ------------- | ----------------------------------------------- |
|
|
402
|
+
| `el` | `Element` | The DOM element |
|
|
403
|
+
| `specs` | `MLMLSpec` | The full markup language specification |
|
|
404
|
+
| `version` | `ARIAVersion` | The ARIA specification version for role queries |
|
|
400
405
|
|
|
401
406
|
**Returns:** `string` -- the computed accessible name, or an empty string if none is found.
|
|
402
407
|
|
|
403
|
-
**
|
|
408
|
+
**Architecture:**
|
|
409
|
+
|
|
410
|
+
The implementation is split into two layers:
|
|
411
|
+
|
|
412
|
+
- **`accname-computation.ts`** (facade): Creates a DOM-based `AccnameResolver`, handles reentrant guard for `:aria(has name)` selector cycles, and delegates to the pure algorithm.
|
|
413
|
+
- **`accname/compute.ts`** (pure algorithm): Implements Steps 2A–2I of AccName 1.2 §4.3.2 using only the `AccnameElement` and `AccnameResolver` interfaces, with no dependency on the DOM or markuplint types.
|
|
414
|
+
|
|
415
|
+
**Algorithm (AccName 1.2 §4.3.2):**
|
|
416
|
+
|
|
417
|
+
1. **Step 2A — Hidden check:** If the element is hidden and not referenced by `aria-labelledby`, return empty.
|
|
418
|
+
2. **Step 2B – `aria-labelledby`:** Resolve referenced elements, compute their names recursively (with cycle prevention via visited set).
|
|
419
|
+
3. **Step 2D – `aria-label`:** Use the `aria-label` attribute value if non-empty.
|
|
420
|
+
4. **Step 2E – Element-specific name:** Apply HTML-AAM §4.1 rules (label association, `<img alt>`, `<input value>`, `<fieldset>` legend, `<table>` caption, SVG `<title>`, etc.).
|
|
421
|
+
5. **Step 2F – Name from content:** If the element's role allows name-from-content, or the element is referenced by `aria-labelledby`, recursively collect text from child nodes (with embedded control value extraction per Step 2C).
|
|
422
|
+
6. **Step 2I – Title fallback:** Use the `title` attribute value.
|
|
423
|
+
|
|
424
|
+
**Reentrant guard:** `getAccname` uses a `WeakSet<Element>` to prevent infinite recursion when `getComputedRole` evaluates `:aria(has name)` selectors that call back into `getAccname`.
|
|
425
|
+
|
|
426
|
+
#### AccName Algorithm Control Flow
|
|
427
|
+
|
|
428
|
+
The following diagram shows the full control flow of the accessible name computation algorithm, mapping each step to its implementation file.
|
|
429
|
+
|
|
430
|
+
```mermaid
|
|
431
|
+
flowchart TB
|
|
432
|
+
Start([Element]) --> Facade["getAccname()\n<i>accname-computation.ts</i>"]
|
|
433
|
+
Facade --> Guard{"Reentrant\nguard?"}
|
|
434
|
+
Guard -->|"Already computing"| Empty(["Return ''"])
|
|
435
|
+
Guard -->|"First call"| Resolver["createDomResolver()\n<i>accname-computation.ts</i>"]
|
|
436
|
+
Resolver --> Compute["computeAccessibleName()\n<i>compute.ts</i>"]
|
|
437
|
+
|
|
438
|
+
Compute --> Hidden{"Hidden &\nnot in\nlabelledby?"}
|
|
439
|
+
Hidden -->|Yes| EmptyResult(["Return empty"])
|
|
440
|
+
Hidden -->|No| Precomp{"getPrecomputedName?\n<i>[Implementation-specific]</i>"}
|
|
441
|
+
|
|
442
|
+
Precomp -->|"Has value"| PrecompResult(["Return precomputed name"])
|
|
443
|
+
Precomp -->|"No"| Step2B
|
|
444
|
+
|
|
445
|
+
subgraph step2b ["Step 2B — aria-labelledby (aria-steps.ts)"]
|
|
446
|
+
Step2B{"aria-labelledby\npresent &\nnot in labelledby\ntraversal?"}
|
|
447
|
+
Step2B -->|Yes| SplitIDs["Split IDREFs by whitespace"]
|
|
448
|
+
SplitIDs --> ForEachID["For each IDREF:\n• Skip visited (allow self-ref)\n• getElementById()\n• Recurse with inLabelledby=true"]
|
|
449
|
+
ForEachID --> JoinParts["Join parts with space"]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
Step2B -->|"No / empty"| Step2D
|
|
453
|
+
JoinParts -->|"Has name"| LabelledbyResult(["Return name\nsource: aria-labelledby"])
|
|
454
|
+
|
|
455
|
+
subgraph step2d ["Step 2D — aria-label (aria-steps.ts)"]
|
|
456
|
+
Step2D{"aria-label\nnon-empty?"}
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
Step2D -->|Yes| AriaLabelResult(["Return name\nsource: aria-label"])
|
|
460
|
+
Step2D -->|No| Step2E
|
|
461
|
+
|
|
462
|
+
subgraph step2e ["Step 2E — Element-specific (element-names.ts)"]
|
|
463
|
+
Step2E["getElementSpecificName()"]
|
|
464
|
+
Step2E --> Dispatch{"Element\ntype?"}
|
|
465
|
+
Dispatch -->|"SVG"| SVGTitle["SVG: title child\n<i>SVG-AAM §8.1</i>"]
|
|
466
|
+
Dispatch -->|"input"| InputType["Input: branch by type\n<i>HTML-AAM §4.1</i>"]
|
|
467
|
+
Dispatch -->|"button"| BtnLabel["Label → content\n<i>HTML-AAM §4.1</i>"]
|
|
468
|
+
Dispatch -->|"fieldset"| Legend["Legend content\n<i>HTML-AAM §4.1</i>"]
|
|
469
|
+
Dispatch -->|"table"| Caption["Caption content\n<i>HTML-AAM §4.1</i>"]
|
|
470
|
+
Dispatch -->|"img"| ImgAlt["alt attr\n<i>HTML-AAM §4.1</i>"]
|
|
471
|
+
Dispatch -->|"Other"| LabelAssoc["label-steps.ts:\nLabel association\n<i>for= / ancestor</i>"]
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
Step2E -->|"Has name"| ElementResult(["Return name\nsource: element-specific"])
|
|
475
|
+
Step2E -->|"null"| Step2F
|
|
476
|
+
|
|
477
|
+
subgraph step2f ["Step 2F/2C — Name from content (helpers.ts)"]
|
|
478
|
+
Step2F{"Role allows\nnameFrom: content\nOR in labelledby\ntraversal?"}
|
|
479
|
+
Step2F -->|Yes| WalkChildren["resolveNameFromContent():\nFor each child node:"]
|
|
480
|
+
WalkChildren --> ChildType{"Node\ntype?"}
|
|
481
|
+
ChildType -->|"Text"| TextContent["Use textContent"]
|
|
482
|
+
ChildType -->|"Embedded\ncontrol"| EmbedValue["getEmbeddedControlValue()\n<i>AccName §4.3.2 Step 2C</i>"]
|
|
483
|
+
ChildType -->|"Element"| RecurseChild["Recurse computeFn();\nif no name → collectTextContent()\n<i>[Implementation-specific]</i>"]
|
|
484
|
+
TextContent --> JoinSpaces["Join with space"]
|
|
485
|
+
EmbedValue --> JoinSpaces
|
|
486
|
+
RecurseChild --> JoinSpaces
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
Step2F -->|"No"| Step2I
|
|
490
|
+
JoinSpaces -->|"Has name"| ContentResult(["Return name\nsource: content"])
|
|
491
|
+
JoinSpaces -->|"Empty"| Step2I
|
|
492
|
+
|
|
493
|
+
subgraph step2i ["Step 2I — Title fallback"]
|
|
494
|
+
Step2I{"title attr\nnon-empty?"}
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
Step2I -->|Yes| TitleResult(["Return name\nsource: title"])
|
|
498
|
+
Step2I -->|No| FinalEmpty(["Return empty"])
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### W3C Specification References
|
|
502
|
+
|
|
503
|
+
| Spec | Section | Description |
|
|
504
|
+
| --------------------------------------------------- | ------------------ | ------------------------------------------------- |
|
|
505
|
+
| [AccName 1.2](https://www.w3.org/TR/accname-1.2/) | §4.3.2 | Computation Steps (Steps 2A–2I) |
|
|
506
|
+
| [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) | §4.1 | Accessible Name and Description Computation |
|
|
507
|
+
| [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) | §4.1 (per element) | Element-specific name computation rules |
|
|
508
|
+
| [SVG-AAM 1.0](https://www.w3.org/TR/svg-aam-1.0/) | §5.1.1, §8.1 | SVG accessibility tree inclusion and name mapping |
|
|
509
|
+
|
|
510
|
+
#### Implementation-Specific Extensions
|
|
511
|
+
|
|
512
|
+
The following behaviors extend beyond the strict AccName specification:
|
|
404
513
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
514
|
+
| Extension | Location | Rationale |
|
|
515
|
+
| ---------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
516
|
+
| Pre-computed name (`getPrecomputedName`) | `compute.ts` | Supports ml-core's Pretender integration for framework components |
|
|
517
|
+
| Transparent text collection (`collectTextContent`) | `helpers.ts` | Collects text from intermediate elements (e.g., `<span>` inside `<button>`) whose roles do not include `nameFrom: ["content"]` |
|
|
518
|
+
| `<select>` selected option (`getSelectedOptionText`) | `helpers.ts` | Static approximation of selected option text via `selected` attribute. Customizable `<select>` ([#2069](https://github.com/markuplint/markuplint/issues/2069)) will require: (1) `collectOptions` to skip new child types (`<button>`, `<datalist>`, `<selectedcontent>`), (2) evaluate whether `<selectedcontent>` affects name computation |
|
|
519
|
+
| Reentrant guard (`computingElements` WeakSet) | `accname-computation.ts` | Prevents infinite recursion from `:aria(has name)` pseudo-class in `getComputedRole` → `getARIA` → `matches` chain |
|
|
520
|
+
|
|
521
|
+
#### Known Limitations
|
|
522
|
+
|
|
523
|
+
| Limitation | AccName Reference | Description |
|
|
524
|
+
| -------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
525
|
+
| CSS-generated content (`::before`/`::after`) | §4.3.2 Step 2G | The AccName spec requires CSS-generated textual content (via the `content` property on `::before`/`::after` pseudo-elements) to be included in the accumulated text. Markuplint performs static HTML analysis without CSS processing, so this content is unavailable at lint time. Elements relying solely on CSS-generated content for their accessible name will not be detected. |
|
|
408
526
|
|
|
409
527
|
---
|
|
410
528
|
|
|
@@ -448,19 +566,19 @@ Determines whether an element is included in (exposed to) the Accessibility Tree
|
|
|
448
566
|
|
|
449
567
|
**Source:** `src/algorithm/aria/has-required-owned-elements.ts`
|
|
450
568
|
|
|
451
|
-
Two related functions for validating the "Required Owned Elements"
|
|
569
|
+
Two related functions for validating the "Allowed Accessibility Child Roles" constraint (called "Required Owned Elements" in ARIA 1.2).
|
|
452
570
|
|
|
453
571
|
#### `hasRequiredOwnedElement`
|
|
454
572
|
|
|
455
|
-
Checks whether an element satisfies the
|
|
573
|
+
Checks whether an element satisfies the "Allowed Accessibility Child Roles" constraint defined by its computed role.
|
|
456
574
|
|
|
457
575
|
**Algorithm:**
|
|
458
576
|
|
|
459
577
|
1. If the element has an `aria-owns` attribute, returns `true` (partial support -- the referenced elements are not validated).
|
|
460
578
|
2. Computes the element's role via `getComputedRole()`.
|
|
461
|
-
3. If the role has no `
|
|
462
|
-
4. Traverses the element's closest non-presentational descendants (children, transparently passing through
|
|
463
|
-
5. For each
|
|
579
|
+
3. If the role has no `allowedAccessibilityChildRoles`, returns `true`.
|
|
580
|
+
4. Traverses the element's closest non-presentational descendants (children, transparently passing through elements with ownership-transparent roles via `isTransparentForOwnership()`). In ARIA 1.3, `generic` elements are additionally transparent.
|
|
581
|
+
5. For each allowed accessibility child role pattern, checks if any descendant matches via `isRequiredOwnedElement()`.
|
|
464
582
|
|
|
465
583
|
#### `isRequiredOwnedElement`
|
|
466
584
|
|
|
@@ -478,7 +596,7 @@ Determines whether an element matches a required owned element query.
|
|
|
478
596
|
|
|
479
597
|
**Query syntax:** Supports chains with `>` notation. For example, `"group > listitem"` means the element must have role `"group"` and contain a descendant with role `"listitem"`.
|
|
480
598
|
|
|
481
|
-
**
|
|
599
|
+
**Ownership traversal:** In ARIA 1.1/1.2, the spec had not decided whether "owned" means child or descendant. This implementation interprets "owned" as **child** (not descendant), to align with HTML semantics. `presentation`/`none` children are traversed transparently. ARIA 1.3 formally resolves this with the definitions of "accessibility child" and "accessibility parent", and additionally makes `generic` elements transparent (via `isTransparentForOwnership()`).
|
|
482
600
|
|
|
483
601
|
---
|
|
484
602
|
|
|
@@ -507,6 +625,8 @@ Validates whether an element's parent hierarchy satisfies at least one of the re
|
|
|
507
625
|
4. For each level, calls `getComputedRole()` with `assumeSingleNode = true` to get the parent's role independently.
|
|
508
626
|
5. Returns `true` if any condition string fully matches the ancestor chain.
|
|
509
627
|
|
|
628
|
+
**ARIA 1.3 transparency:** In ARIA 1.3+, parent elements with `generic` or `none` roles are transparently skipped during matching (via `isTransparentForOwnership()`). In ARIA 1.1/1.2, parents are matched strictly without skipping.
|
|
629
|
+
|
|
510
630
|
**Example:** For a `listitem` role with `requiredContextRole: ["list", "list > group"]`:
|
|
511
631
|
|
|
512
632
|
- `"list"` matches if the parent has the `list` role.
|
|
@@ -557,13 +677,39 @@ Traverses the parent element chain to find the nearest ancestor with a non-prese
|
|
|
557
677
|
|
|
558
678
|
1. Starts from `el.parentElement`.
|
|
559
679
|
2. For each ancestor, computes the role via `getComputedRole()`.
|
|
560
|
-
3. If the ancestor's role is not
|
|
680
|
+
3. If the ancestor's role is not transparent for ownership traversal (via `isTransparentForOwnership()`), returns that ancestor's computed role.
|
|
561
681
|
4. Otherwise, continues to the next parent.
|
|
562
682
|
5. If no non-presentational ancestor is found, returns `{ el: null, role: null }`.
|
|
563
683
|
|
|
684
|
+
**ARIA 1.3 transparency:** In ARIA 1.3, `generic` role elements are additionally skipped (alongside `presentation`/`none`), per the WAI-ARIA definitions of "accessibility child" and "accessibility parent".
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
688
|
+
### 16. `isTransparentForOwnership(roleName, version): boolean`
|
|
689
|
+
|
|
690
|
+
**Source:** `src/algorithm/aria/is-presentational.ts`
|
|
691
|
+
|
|
692
|
+
Determines whether a given role is transparent for ownership traversal. Used by `getNonPresentationalAncestor`, `getClosestNonPresentationalDescendants`, and `matchesContextRole` to decide which elements to skip.
|
|
693
|
+
|
|
694
|
+
**Parameters:**
|
|
695
|
+
|
|
696
|
+
| Parameter | Type | Description |
|
|
697
|
+
| ---------- | --------------------- | ------------------------------ |
|
|
698
|
+
| `roleName` | `string \| undefined` | The ARIA role name to check |
|
|
699
|
+
| `version` | `ARIAVersion` | The ARIA specification version |
|
|
700
|
+
|
|
701
|
+
**Returns:** `true` if the role should be skipped during ownership traversal.
|
|
702
|
+
|
|
703
|
+
**Version behavior:**
|
|
704
|
+
|
|
705
|
+
| ARIA Version | Transparent roles |
|
|
706
|
+
| ---------------- | --------------------------------- |
|
|
707
|
+
| `'1.1'`, `'1.2'` | `presentation`, `none` |
|
|
708
|
+
| `'1.3'` | `presentation`, `none`, `generic` |
|
|
709
|
+
|
|
564
710
|
---
|
|
565
711
|
|
|
566
|
-
###
|
|
712
|
+
### 17. `ariaSpecs(specs, version)`
|
|
567
713
|
|
|
568
714
|
**Source:** `src/algorithm/aria/aria-specs.ts`
|
|
569
715
|
|
|
@@ -576,9 +722,9 @@ A simple accessor function that retrieves the ARIA specification data for a spec
|
|
|
576
722
|
| `specs` | `MLMLSpec` | The full markup language specification |
|
|
577
723
|
| `version` | `ARIAVersion` | The ARIA specification version |
|
|
578
724
|
|
|
579
|
-
**Returns:** `{ roles: ARIARoleInSchema[], graphicsRoles: ARIARoleInSchema[], props: ARIAProperty[] }`
|
|
725
|
+
**Returns:** `{ roles: ARIARoleInSchema[], graphicsRoles: ARIARoleInSchema[], dpubRoles: ARIARoleInSchema[], props: ARIAProperty[] }`
|
|
580
726
|
|
|
581
|
-
**Implementation:** Returns `specs.def['#aria'][version]`, providing direct access to the roles, graphics roles, and properties defined for the requested ARIA version.
|
|
727
|
+
**Implementation:** Returns `specs.def['#aria'][version]`, providing direct access to the roles, graphics roles, DPub roles, and properties defined for the requested ARIA version.
|
|
582
728
|
|
|
583
729
|
## RoleComputationError Reference
|
|
584
730
|
|
|
@@ -636,6 +782,8 @@ This design allows the schema to define a base ARIA spec that works across versi
|
|
|
636
782
|
### Version Impact on Behavior
|
|
637
783
|
|
|
638
784
|
- **`getNonPresentationalAncestor`**: In ARIA 1.1/1.2, ancestor role computation uses full context (`assumeSingleNode = false`). In ARIA 1.3+, each ancestor is computed independently (`assumeSingleNode = true`).
|
|
785
|
+
- **`isTransparentForOwnership`**: In ARIA 1.1/1.2, only `presentation`/`none` are transparent. In ARIA 1.3, `generic` is additionally transparent per the WAI-ARIA definitions of "accessibility child" and "accessibility parent".
|
|
786
|
+
- **`matchesContextRole`**: In ARIA 1.1/1.2, parent elements are matched strictly. In ARIA 1.3+, `generic`/`none` parents are transparently skipped.
|
|
639
787
|
- **`namingProhibited`**: Only applicable in ARIA 1.2 and later. Version 1.1 always uses the base value.
|
|
640
788
|
- **Role and property definitions**: The available roles and their properties may differ across versions (e.g., new roles added in 1.2 or 1.3).
|
|
641
789
|
|
|
@@ -646,6 +794,7 @@ The ARIA algorithms implement behavior defined in the following W3C specificatio
|
|
|
646
794
|
- [WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-1.2/) -- Accessible Rich Internet Applications, primary reference
|
|
647
795
|
- [WAI-ARIA 1.1](https://www.w3.org/TR/wai-aria-1.1/) -- Previous version, still supported
|
|
648
796
|
- [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) -- HTML Accessibility API Mappings (implicit role mappings)
|
|
649
|
-
- [AccName 1.
|
|
797
|
+
- [AccName 1.2](https://www.w3.org/TR/accname-1.2/) -- Accessible Name and Description Computation
|
|
650
798
|
- [SVG-AAM 1.0](https://www.w3.org/TR/svg-aam-1.0/) -- SVG Accessibility API Mappings
|
|
799
|
+
- [DPub-ARIA 1.1](https://w3c.github.io/dpub-aria/) -- Digital Publishing WAI-ARIA Module (DPub roles)
|
|
651
800
|
- [ARIA in HTML](https://www.w3.org/TR/html-aria/) -- Permitted roles and ARIA attribute constraints per HTML element
|
|
@@ -79,7 +79,7 @@ function getContentModel(
|
|
|
79
79
|
|
|
80
80
|
**動作:**
|
|
81
81
|
|
|
82
|
-
1.
|
|
82
|
+
1. `WeakMap<Element, result>` キャッシュを確認します。指定された要素インスタンスに対するキャッシュ結果が存在すれば、即座に返します。
|
|
83
83
|
2. `getSpec()` を使用して要素の仕様を検索します。見つからない場合、`null` をキャッシュして返します。
|
|
84
84
|
3. `contentModel.conditional[]`(存在する場合)を走査します。各条件に対して `el.matches(cond.condition)` を呼び出します。
|
|
85
85
|
4. 最初にマッチした条件の `contents` を返します。どの条件にもマッチしない場合、デフォルトの `contentModel.contents` を返します。
|
|
@@ -87,7 +87,7 @@ function getContentModel(
|
|
|
87
87
|
|
|
88
88
|
**キャッシュ戦略:**
|
|
89
89
|
|
|
90
|
-
キャッシュは
|
|
90
|
+
キャッシュは `Element` インスタンスをキーとする `WeakMap` です。同じ要素に対する冗長な計算を回避しつつ、要素が参照されなくなった場合(例: 再パース後)にキャッシュエントリが自動的にガベージコレクションされます。
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
package/docs/html-algorithms.md
CHANGED
|
@@ -79,7 +79,7 @@ function getContentModel(
|
|
|
79
79
|
|
|
80
80
|
**Behavior:**
|
|
81
81
|
|
|
82
|
-
1. Checks the
|
|
82
|
+
1. Checks the `WeakMap<Element, result>` cache. If a cached result exists for the given element instance, returns it immediately.
|
|
83
83
|
2. Looks up the element's spec using `getSpec()`. If not found, caches and returns `null`.
|
|
84
84
|
3. Iterates over `contentModel.conditional[]` (if present). For each condition, calls `el.matches(cond.condition)`.
|
|
85
85
|
4. Returns the first matching condition's `contents`. If no condition matches, returns the default `contentModel.contents`.
|
|
@@ -87,7 +87,7 @@ function getContentModel(
|
|
|
87
87
|
|
|
88
88
|
**Caching strategy:**
|
|
89
89
|
|
|
90
|
-
The cache is a
|
|
90
|
+
The cache is a `WeakMap` keyed by the `Element` instance. This avoids redundant computation for the same element while ensuring cache entries are automatically garbage-collected when elements are no longer referenced (e.g., after a re-parse).
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
package/docs/maintenance.ja.md
CHANGED
|
@@ -128,12 +128,13 @@ schema:json → schema:content-models → schema:attributes → schema:aria →
|
|
|
128
128
|
|
|
129
129
|
### テストファイル
|
|
130
130
|
|
|
131
|
-
パッケージには vitest を使用する
|
|
131
|
+
パッケージには vitest を使用する 18 のテストファイルがあります:
|
|
132
132
|
|
|
133
|
-
| ディレクトリ
|
|
134
|
-
|
|
|
135
|
-
| `src/algorithm/aria/`
|
|
136
|
-
| `src/
|
|
133
|
+
| ディレクトリ | テストファイル数 | カバレッジ |
|
|
134
|
+
| ----------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
135
|
+
| `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
|
|
136
|
+
| `src/algorithm/aria/accname/` | 3 | `aria-steps`, `compute`, `element-names`, `label-steps` |
|
|
137
|
+
| `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
|
|
137
138
|
|
|
138
139
|
### テストの実行
|
|
139
140
|
|
|
@@ -154,13 +155,12 @@ yarn test packages/@markuplint/ml-spec/src/algorithm/aria/get-computed-role.spec
|
|
|
154
155
|
|
|
155
156
|
### ランタイム依存
|
|
156
157
|
|
|
157
|
-
| パッケージ
|
|
158
|
-
|
|
|
159
|
-
| `@markuplint/ml-ast`
|
|
160
|
-
| `@markuplint/types`
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `type-fest` | 4.41.0 | `ReadonlyDeep` ユーティリティ型 | 低(型のみ) |
|
|
158
|
+
| パッケージ | バージョン | 目的 | 更新リスク |
|
|
159
|
+
| -------------------- | ---------- | ---------------------------------- | ------------------ |
|
|
160
|
+
| `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` 型 | 低(内部) |
|
|
161
|
+
| `@markuplint/types` | 4.8.1 | 属性値型の `Type` ユニオン | 中(スキーマ参照) |
|
|
162
|
+
| `is-plain-object` | 5.0.0 | AAM 情報のプレーンオブジェクト検出 | 低(安定 API) |
|
|
163
|
+
| `type-fest` | 4.41.0 | `ReadonlyDeep` ユーティリティ型 | 低(型のみ) |
|
|
164
164
|
|
|
165
165
|
### 開発依存
|
|
166
166
|
|
|
@@ -171,7 +171,6 @@ yarn test packages/@markuplint/ml-spec/src/algorithm/aria/get-computed-role.spec
|
|
|
171
171
|
|
|
172
172
|
### 依存関係の更新
|
|
173
173
|
|
|
174
|
-
- **`dom-accessibility-api`**: 更新により AccName 計算の動作が変わる場合があります。更新後は `accname-computation.spec.ts` テストを実行してください。
|
|
175
174
|
- **`json-schema-to-typescript`**: メジャーバージョン更新により、生成される型の出力が変わる場合があります(フォーマット、optional の扱い)。更新後は `yarn workspace @markuplint/ml-spec run schema` を実行し、`src/types/*.ts` の差分を確認してください。
|
|
176
175
|
- **`@markuplint/types`**: 更新後は必ず `yarn up:schema` を実行して、スキーマ参照の一貫性を確保してください。
|
|
177
176
|
- **`type-fest`**: 型のみの依存です。自由に更新できますが、ビルドが成功することを確認してください(`yarn build --scope @markuplint/ml-spec`)。
|
|
@@ -278,7 +277,7 @@ W3C 仕様が更新された場合(例: WAI-ARIA 1.3 が勧告になった場
|
|
|
278
277
|
| --------------------------------------------- | ----------------------------------------------- | -------------------------- |
|
|
279
278
|
| `getARIA()` 内部キャッシュ | `Map`(`localName + namespace + version` キー) | プロセス再起動時のみクリア |
|
|
280
279
|
| `getSpecByTagName()` キャッシュ | `Map`(`namespace:localName` キー) | specs インスタンスごと |
|
|
281
|
-
| `getContentModel()` キャッシュ | `
|
|
280
|
+
| `getContentModel()` キャッシュ | `WeakMap<Element, ...>` | 要素ごと、GC セーフ |
|
|
282
281
|
| `contentModelCategoryToTagNames()` キャッシュ | モジュールレベル `Map<Category, string[]>` | グローバル、無効化なし |
|
|
283
282
|
| `getAttrSpecs()` キャッシュ | `WeakSet` + `Map`(スキーマごと) | 新しいスキーマでリセット |
|
|
284
283
|
| `resolveNamespace()` キャッシュ | モジュールレベル `Map` | グローバル、無効化なし |
|
|
@@ -310,14 +309,6 @@ W3C 仕様が更新された場合(例: WAI-ARIA 1.3 が勧告になった場
|
|
|
310
309
|
|
|
311
310
|
**対処:** 差分を注意深く確認してください。型が意味的に同等であれば、変更をコミットしてください。動作が変わった場合(例: 以前 optional だったフィールドが required になった)、`json-schema-to-typescript` のチェンジログを調査してください。
|
|
312
311
|
|
|
313
|
-
### `dom-accessibility-api` 更新後のテスト失敗
|
|
314
|
-
|
|
315
|
-
**症状:** `dom-accessibility-api` の更新後、`accname-computation.spec.ts` が失敗する。
|
|
316
|
-
|
|
317
|
-
**原因:** ライブラリが AccName アルゴリズムの実装を更新し、計算されるアクセシブル名が変わった。
|
|
318
|
-
|
|
319
|
-
**対処:** [AccName 1.1 仕様](https://www.w3.org/TR/accname-1.1/)に照らして新しい動作を確認してください。ライブラリがより仕様準拠になった場合は、テストの期待値を更新してください。
|
|
320
|
-
|
|
321
312
|
### ビルドエラー: 生成型の不一致
|
|
322
313
|
|
|
323
314
|
**症状:** `src/types/aria.ts`、`attributes.ts`、`permitted-structures.ts` の型を参照する TypeScript ビルドエラー。
|
package/docs/maintenance.md
CHANGED
|
@@ -128,12 +128,13 @@ These files carry "DO NOT MODIFY" headers and are overwritten by the generation
|
|
|
128
128
|
|
|
129
129
|
### Test files
|
|
130
130
|
|
|
131
|
-
The package has
|
|
131
|
+
The package has 18 test files using vitest:
|
|
132
132
|
|
|
133
|
-
| Directory
|
|
134
|
-
|
|
|
135
|
-
| `src/algorithm/aria/`
|
|
136
|
-
| `src/
|
|
133
|
+
| Directory | Test files | Coverage |
|
|
134
|
+
| ----------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
135
|
+
| `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
|
|
136
|
+
| `src/algorithm/aria/accname/` | 3 | `aria-steps`, `compute`, `element-names`, `label-steps` |
|
|
137
|
+
| `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
|
|
137
138
|
|
|
138
139
|
### Running tests
|
|
139
140
|
|
|
@@ -154,13 +155,12 @@ Tests depend on `@markuplint/test-tools` (devDependency) which provides HTML par
|
|
|
154
155
|
|
|
155
156
|
### Runtime dependencies
|
|
156
157
|
|
|
157
|
-
| Package
|
|
158
|
-
|
|
|
159
|
-
| `@markuplint/ml-ast`
|
|
160
|
-
| `@markuplint/types`
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `type-fest` | 4.41.0 | `ReadonlyDeep` utility type | Low (types only) |
|
|
158
|
+
| Package | Version | Purpose | Update risk |
|
|
159
|
+
| -------------------- | ------- | -------------------------------------- | ------------------------- |
|
|
160
|
+
| `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` type | Low (internal) |
|
|
161
|
+
| `@markuplint/types` | 4.8.1 | `Type` union for attribute value types | Medium (schema reference) |
|
|
162
|
+
| `is-plain-object` | 5.0.0 | Plain object detection for AAM info | Low (stable API) |
|
|
163
|
+
| `type-fest` | 4.41.0 | `ReadonlyDeep` utility type | Low (types only) |
|
|
164
164
|
|
|
165
165
|
### Dev dependencies
|
|
166
166
|
|
|
@@ -171,7 +171,6 @@ Tests depend on `@markuplint/test-tools` (devDependency) which provides HTML par
|
|
|
171
171
|
|
|
172
172
|
### Updating dependencies
|
|
173
173
|
|
|
174
|
-
- **`dom-accessibility-api`**: Updates may change AccName computation behavior. Run `accname-computation.spec.ts` tests after updating.
|
|
175
174
|
- **`json-schema-to-typescript`**: Major version updates may change generated type output (formatting, optional handling). After updating, run `yarn workspace @markuplint/ml-spec run schema` and review diffs in `src/types/*.ts`.
|
|
176
175
|
- **`@markuplint/types`**: Always run `yarn up:schema` after updating to ensure schema references stay consistent.
|
|
177
176
|
- **`type-fest`**: Type-only dependency. Update freely, but verify the build succeeds (`yarn build --scope @markuplint/ml-spec`).
|
|
@@ -278,7 +277,7 @@ The package uses several runtime caches that are **never invalidated during a pr
|
|
|
278
277
|
| ---------------------------------------- | ------------------------------------------------ | ------------------------------- |
|
|
279
278
|
| `getARIA()` internal cache | `Map` keyed by `localName + namespace + version` | Cleared only on process restart |
|
|
280
279
|
| `getSpecByTagName()` cache | `Map` keyed by `namespace:localName` | Per-specs instance |
|
|
281
|
-
| `getContentModel()` cache | `
|
|
280
|
+
| `getContentModel()` cache | `WeakMap<Element, ...>` | Per-element, GC-safe |
|
|
282
281
|
| `contentModelCategoryToTagNames()` cache | Module-level `Map<Category, string[]>` | Global, never invalidated |
|
|
283
282
|
| `getAttrSpecs()` cache | `WeakSet` + `Map` per schema | New schema resets cache |
|
|
284
283
|
| `resolveNamespace()` cache | Module-level `Map` | Global, never invalidated |
|
|
@@ -310,14 +309,6 @@ If you add a new algorithm function that computes expensive results, consider ad
|
|
|
310
309
|
|
|
311
310
|
**Fix:** Review the diff carefully. If the types are semantically equivalent, commit the changes. If behavior changed (e.g., previously optional fields became required), investigate the `json-schema-to-typescript` changelog.
|
|
312
311
|
|
|
313
|
-
### Test failures after `dom-accessibility-api` update
|
|
314
|
-
|
|
315
|
-
**Symptom:** `accname-computation.spec.ts` fails after updating `dom-accessibility-api`.
|
|
316
|
-
|
|
317
|
-
**Cause:** The library updated its AccName algorithm implementation, changing computed accessible names.
|
|
318
|
-
|
|
319
|
-
**Fix:** Verify the new behavior against the [AccName 1.1 specification](https://www.w3.org/TR/accname-1.1/). If the library is now more spec-compliant, update the test expectations.
|
|
320
|
-
|
|
321
312
|
### Build error: generated type mismatch
|
|
322
313
|
|
|
323
314
|
**Symptom:** TypeScript build errors referencing types in `src/types/aria.ts`, `attributes.ts`, or `permitted-structures.ts`.
|