@markuplint/ml-spec 4.10.1 → 4.10.2

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 (71) hide show
  1. package/ARCHITECTURE.ja.md +253 -0
  2. package/ARCHITECTURE.md +253 -0
  3. package/CHANGELOG.md +3 -3
  4. package/README.md +4 -186
  5. package/SKILL.md +116 -0
  6. package/docs/aria-algorithms.ja.md +651 -0
  7. package/docs/aria-algorithms.md +651 -0
  8. package/docs/html-algorithms.ja.md +469 -0
  9. package/docs/html-algorithms.md +469 -0
  10. package/docs/maintenance.ja.md +340 -0
  11. package/docs/maintenance.md +340 -0
  12. package/docs/spec-resolution.ja.md +540 -0
  13. package/docs/spec-resolution.md +551 -0
  14. package/docs/type-definitions.ja.md +561 -0
  15. package/docs/type-definitions.md +561 -0
  16. package/lib/algorithm/aria/accname-computation.d.ts +7 -0
  17. package/lib/algorithm/aria/accname-computation.js +7 -0
  18. package/lib/algorithm/aria/aria-specs.d.ts +7 -0
  19. package/lib/algorithm/aria/aria-specs.js +7 -0
  20. package/lib/algorithm/aria/get-aria.d.ts +12 -0
  21. package/lib/algorithm/aria/get-aria.js +12 -0
  22. package/lib/algorithm/aria/get-computed-aria-props.d.ts +22 -0
  23. package/lib/algorithm/aria/get-computed-aria-props.js +10 -0
  24. package/lib/algorithm/aria/get-computed-role.d.ts +12 -0
  25. package/lib/algorithm/aria/get-computed-role.js +12 -0
  26. package/lib/algorithm/aria/get-implicit-role.d.ts +18 -0
  27. package/lib/algorithm/aria/get-implicit-role.js +18 -0
  28. package/lib/algorithm/aria/get-permitted-roles.d.ts +9 -0
  29. package/lib/algorithm/aria/get-permitted-roles.js +9 -0
  30. package/lib/algorithm/aria/get-role-spec.d.ts +11 -0
  31. package/lib/algorithm/aria/get-role-spec.js +11 -0
  32. package/lib/algorithm/aria/has-required-owned-elements.d.ts +23 -0
  33. package/lib/algorithm/aria/has-required-owned-elements.js +23 -0
  34. package/lib/algorithm/aria/is-exposed.d.ts +7 -4
  35. package/lib/algorithm/aria/is-exposed.js +7 -4
  36. package/lib/algorithm/aria/is-presentational.d.ts +8 -0
  37. package/lib/algorithm/aria/is-presentational.js +8 -0
  38. package/lib/algorithm/aria/matches-context-role.d.ts +11 -0
  39. package/lib/algorithm/aria/matches-context-role.js +11 -0
  40. package/lib/algorithm/html/content-model-category-to-tag-names.d.ts +9 -0
  41. package/lib/algorithm/html/content-model-category-to-tag-names.js +9 -0
  42. package/lib/algorithm/html/get-content-model.d.ts +9 -0
  43. package/lib/algorithm/html/get-content-model.js +9 -0
  44. package/lib/algorithm/html/get-selectors-by-content-model-category.d.ts +8 -0
  45. package/lib/algorithm/html/get-selectors-by-content-model-category.js +8 -0
  46. package/lib/algorithm/html/is-nothing-content-model.d.ts +7 -0
  47. package/lib/algorithm/html/is-nothing-content-model.js +7 -0
  48. package/lib/algorithm/html/is-palpable-elements.d.ts +13 -0
  49. package/lib/algorithm/html/is-palpable-elements.js +13 -0
  50. package/lib/algorithm/html/is-void-element.d.ts +9 -0
  51. package/lib/algorithm/html/is-void-element.js +9 -0
  52. package/lib/algorithm/html/may-be-focusable.d.ts +10 -0
  53. package/lib/algorithm/html/may-be-focusable.js +10 -0
  54. package/lib/types/index.d.ts +54 -0
  55. package/lib/utils/aria-version.d.ts +6 -0
  56. package/lib/utils/aria-version.js +6 -0
  57. package/lib/utils/get-attr-specs-spec.d.ts +18 -0
  58. package/lib/utils/get-attr-specs-spec.js +18 -0
  59. package/lib/utils/get-attr-specs.d.ts +9 -0
  60. package/lib/utils/get-attr-specs.js +9 -0
  61. package/lib/utils/get-spec-by-tag-name.d.ts +11 -0
  62. package/lib/utils/get-spec-by-tag-name.js +11 -0
  63. package/lib/utils/get-spec.d.ts +11 -1
  64. package/lib/utils/get-spec.js +10 -0
  65. package/lib/utils/resolve-namespace.d.ts +13 -0
  66. package/lib/utils/resolve-namespace.js +10 -0
  67. package/lib/utils/schema-to-spec.d.ts +5 -2
  68. package/lib/utils/schema-to-spec.js +5 -2
  69. package/lib/utils/validate-aria-version.d.ts +7 -0
  70. package/lib/utils/validate-aria-version.js +7 -0
  71. package/package.json +6 -6
@@ -0,0 +1,340 @@
1
+ # Maintenance Guide
2
+
3
+ ## Overview
4
+
5
+ This guide covers the day-to-day operational and maintenance tasks for `@markuplint/ml-spec`. It focuses on schema generation, dependency management, testing, common recipes, and troubleshooting.
6
+
7
+ ## Build and Development Commands
8
+
9
+ | Command | Scope | Description |
10
+ | ----------------------------------------------- | -------- | ---------------------------------------------------- |
11
+ | `yarn build --scope @markuplint/ml-spec` | Package | Compile TypeScript to `lib/` |
12
+ | `yarn workspace @markuplint/ml-spec run dev` | Package | Watch mode compilation |
13
+ | `yarn workspace @markuplint/ml-spec run clean` | Package | Remove `lib/` output |
14
+ | `yarn workspace @markuplint/ml-spec run schema` | Package | Regenerate schemas and types |
15
+ | `yarn up:schema` | Monorepo | Regenerate schemas across all packages (recommended) |
16
+ | `yarn test` | Monorepo | Run all tests via vitest |
17
+
18
+ ## Schema Generation Pipeline
19
+
20
+ ### Why schema generation exists
21
+
22
+ The package uses JSON Schema files as a single source of truth for complex type structures (ARIA definitions, attribute types, content models, global attributes). TypeScript types are auto-generated from these schemas via `json-schema-to-typescript` (`json2ts`). This ensures that:
23
+
24
+ - JSON data files consumed at runtime are validated against the same structure as TypeScript types.
25
+ - Schema changes automatically propagate to type definitions.
26
+ - The `@markuplint/html-spec` JSON data stays consistent with the type system.
27
+
28
+ ### Generation flow
29
+
30
+ ```
31
+ gen/global-attribute.data.ts schemas/aria.schema.json
32
+ │ schemas/content-models.schema.json
33
+ ▼ │
34
+ gen/gen.ts │
35
+ │ │
36
+ ▼ ▼
37
+ schemas/global-attributes.schema.json │
38
+ schemas/attributes.schema.json │
39
+ │ │
40
+ └──────────┬───────────────────┘
41
+
42
+ json-schema-to-typescript
43
+
44
+ ┌──────────┼──────────────┐
45
+ ▼ ▼ ▼
46
+ types/aria.ts types/ types/permitted-
47
+ attributes.ts structures.ts
48
+
49
+
50
+ prettier + eslint
51
+ ```
52
+
53
+ ### Script breakdown (`yarn workspace @markuplint/ml-spec run schema`)
54
+
55
+ The `schema` script is a sequential pipeline using `run-s`:
56
+
57
+ ```
58
+ schema:json → schema:content-models → schema:attributes → schema:aria → schema:prettier → schema:eslint → schema:prettier
59
+ ```
60
+
61
+ | Step | Command | Input | Output |
62
+ | ----------------------- | ---------------------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------- |
63
+ | `schema:json` | `tsx ./gen/gen.ts` | `gen/global-attribute.data.ts` | `schemas/global-attributes.schema.json`, `schemas/attributes.schema.json` |
64
+ | `schema:content-models` | `json2ts ./schemas/content-models.schema.json` | `schemas/content-models.schema.json` | `src/types/permitted-structures.ts` |
65
+ | `schema:attributes` | `json2ts ./schemas/attributes.schema.json --cwd ./schemas` | `schemas/attributes.schema.json` | `src/types/attributes.ts` |
66
+ | `schema:aria` | `json2ts ./schemas/aria.schema.json --cwd ./schemas` | `schemas/aria.schema.json` | `src/types/aria.ts` |
67
+ | `schema:prettier` | `prettier --write` | `schemas/*.json`, `src/types/*.ts` | Formatted files |
68
+ | `schema:eslint` | `eslint --fix` | `src/types/*.ts` | Lint-fixed files |
69
+
70
+ Note: `schema:prettier` runs **twice** -- once after `schema:aria` to format generated files, then again after `schema:eslint` to clean up any eslint auto-fix formatting changes.
71
+
72
+ ### Cross-package dependency: `@markuplint/types`
73
+
74
+ `schemas/attributes.schema.json` contains a `$ref` to `@markuplint/types`:
75
+
76
+ ```json
77
+ {
78
+ "AttributeType": {
79
+ "$ref": "../../types/types.schema.json#/definitions/type"
80
+ }
81
+ }
82
+ ```
83
+
84
+ This means `@markuplint/types` must regenerate its schema **before** `@markuplint/ml-spec`. The monorepo-level `yarn up:schema` handles this order automatically:
85
+
86
+ 1. `@markuplint/types` -- generates `types.schema.json`, builds the package
87
+ 2. `@markuplint/ml-spec` -- generates schemas referencing `types.schema.json`
88
+ 3. Other packages with `schema` scripts
89
+
90
+ **Always prefer `yarn up:schema` from the repository root** when updating schemas that may involve cross-package references.
91
+
92
+ ## File Classification: Editable vs Generated
93
+
94
+ ### Files you MUST NOT edit directly
95
+
96
+ These files carry "DO NOT MODIFY" headers and are overwritten by the generation pipeline:
97
+
98
+ | File | Generated from |
99
+ | --------------------------------------- | --------------------------------------------- |
100
+ | `src/types/aria.ts` | `schemas/aria.schema.json` |
101
+ | `src/types/attributes.ts` | `schemas/attributes.schema.json` |
102
+ | `src/types/permitted-structures.ts` | `schemas/content-models.schema.json` |
103
+ | `schemas/global-attributes.schema.json` | `gen/gen.ts` + `gen/global-attribute.data.ts` |
104
+ | `schemas/attributes.schema.json` | `gen/gen.ts` + `gen/global-attribute.data.ts` |
105
+
106
+ ### Files you edit to change generated output
107
+
108
+ | To change... | Edit this file | Then run |
109
+ | ------------------------------------------ | --------------------------------------------- | ----------------------------------------------- |
110
+ | Global attribute categories/items | `gen/global-attribute.data.ts` | `yarn workspace @markuplint/ml-spec run schema` |
111
+ | `AttributeJSON` shape (add field) | `gen/gen.ts` (the `AttributeJSON` definition) | `yarn workspace @markuplint/ml-spec run schema` |
112
+ | ARIA role/property structure | `schemas/aria.schema.json` | `yarn workspace @markuplint/ml-spec run schema` |
113
+ | Content model patterns | `schemas/content-models.schema.json` | `yarn workspace @markuplint/ml-spec run schema` |
114
+ | Attribute value types (CSS keywords, etc.) | `@markuplint/types` package | `yarn up:schema` (from root) |
115
+
116
+ ### Files you edit directly (hand-written)
117
+
118
+ | File | Purpose |
119
+ | ----------------------------- | --------------------------------------------------------------------- |
120
+ | `src/types/index.ts` | Core hand-written types (`MLMLSpec`, `ElementSpec`, `ARIARole`, etc.) |
121
+ | `src/algorithm/aria/*.ts` | ARIA algorithm implementations |
122
+ | `src/algorithm/html/*.ts` | HTML algorithm implementations |
123
+ | `src/utils/*.ts` | Utility functions |
124
+ | `src/index.ts` | Public API exports |
125
+ | `schemas/element.schema.json` | Top-level element schema (manual, 11 lines) |
126
+
127
+ ## Testing
128
+
129
+ ### Test files
130
+
131
+ The package has 15 test files using vitest:
132
+
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/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
137
+
138
+ ### Running tests
139
+
140
+ ```bash
141
+ # All tests in the monorepo
142
+ yarn test
143
+
144
+ # Only ml-spec tests
145
+ yarn test packages/@markuplint/ml-spec
146
+
147
+ # Specific test file
148
+ yarn test packages/@markuplint/ml-spec/src/algorithm/aria/get-computed-role.spec.ts
149
+ ```
150
+
151
+ Tests depend on `@markuplint/test-tools` (devDependency) which provides HTML parsing utilities for creating DOM elements in test environments.
152
+
153
+ ## Dependency Management
154
+
155
+ ### Runtime dependencies
156
+
157
+ | Package | Version | Purpose | Update risk |
158
+ | ----------------------- | ------- | -------------------------------------- | ------------------------- |
159
+ | `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` type | Low (internal) |
160
+ | `@markuplint/types` | 4.8.1 | `Type` union for attribute value types | Medium (schema reference) |
161
+ | `dom-accessibility-api` | 0.7.1 | AccName computation | Medium (spec compliance) |
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
+
165
+ ### Dev dependencies
166
+
167
+ | Package | Version | Purpose |
168
+ | --------------------------- | ------- | --------------------------------------- |
169
+ | `@markuplint/test-tools` | 4.5.22 | Test utilities for DOM element creation |
170
+ | `json-schema-to-typescript` | 15.0.4 | Schema → TypeScript type generation |
171
+
172
+ ### Updating dependencies
173
+
174
+ - **`dom-accessibility-api`**: Updates may change AccName computation behavior. Run `accname-computation.spec.ts` tests after updating.
175
+ - **`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
+ - **`@markuplint/types`**: Always run `yarn up:schema` after updating to ensure schema references stay consistent.
177
+ - **`type-fest`**: Type-only dependency. Update freely, but verify the build succeeds (`yarn build --scope @markuplint/ml-spec`).
178
+
179
+ ## Common Maintenance Recipes
180
+
181
+ ### 1. Add a new global attribute
182
+
183
+ Edit `gen/global-attribute.data.ts`, add the attribute name to the appropriate category array:
184
+
185
+ ```ts
186
+ '#HTMLGlobalAttrs': {
187
+ attrs: [
188
+ // ...existing attributes...
189
+ 'newattribute', // ← add here
190
+ ],
191
+ },
192
+ ```
193
+
194
+ Then regenerate:
195
+
196
+ ```bash
197
+ yarn workspace @markuplint/ml-spec run schema
198
+ ```
199
+
200
+ ### 2. Add a new global attribute category
201
+
202
+ Edit `gen/global-attribute.data.ts`, add a new entry:
203
+
204
+ ```ts
205
+ '#NewCategoryAttrs': {
206
+ description: 'Description with spec link',
207
+ attrs: ['attr1', 'attr2'],
208
+ },
209
+ ```
210
+
211
+ The key must match the pattern `#${string}Attrs`. The generator (`gen/gen.ts`) automatically handles the new category in both `global-attributes.schema.json` and `attributes.schema.json`.
212
+
213
+ Then regenerate:
214
+
215
+ ```bash
216
+ yarn workspace @markuplint/ml-spec run schema
217
+ ```
218
+
219
+ ### 3. Add a new field to `AttributeJSON`
220
+
221
+ Edit `gen/gen.ts`, add the property inside the `AttributeJSON` definition object:
222
+
223
+ ```ts
224
+ AttributeJSON: {
225
+ properties: {
226
+ // ...existing properties...
227
+ newField: { type: 'boolean' }, // ← add here
228
+ },
229
+ },
230
+ ```
231
+
232
+ Then regenerate. The new field will appear in `src/types/attributes.ts` as an optional property.
233
+
234
+ ### 4. Modify ARIA role/property schema
235
+
236
+ Edit `schemas/aria.schema.json` directly. For example, to add a new field to the role definition:
237
+
238
+ ```json
239
+ {
240
+ "definitions": {
241
+ "role": {
242
+ "properties": {
243
+ "newField": { "type": "boolean" }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ ```
249
+
250
+ Then regenerate. The new field will appear in `src/types/aria.ts`.
251
+
252
+ ### 5. Add a new content model category
253
+
254
+ Edit `schemas/content-models.schema.json`, add the category to the `Category` enum and define its pattern.
255
+
256
+ ### 6. Add a new ARIA algorithm function
257
+
258
+ 1. Create the implementation file in `src/algorithm/aria/`.
259
+ 2. Create a corresponding `.spec.ts` test file.
260
+ 3. Export the function from `src/index.ts`.
261
+ 4. Update `docs/aria-algorithms.md` and `docs/aria-algorithms.ja.md`.
262
+
263
+ ### 7. Update W3C specification compliance
264
+
265
+ When a W3C specification updates (e.g., WAI-ARIA 1.3 becomes a Recommendation):
266
+
267
+ 1. Update `@markuplint/html-spec` data if element-level ARIA mappings changed.
268
+ 2. Update algorithm implementations in `src/algorithm/aria/*.ts` if algorithm behavior changed.
269
+ 3. Update `src/utils/aria-version.ts` if a new ARIA version is added.
270
+ 4. Run `yarn up:schema` to regenerate types.
271
+ 5. Run tests to verify compliance.
272
+
273
+ ## Caching Considerations
274
+
275
+ The package uses several runtime caches that are **never invalidated during a process lifetime**. This is safe for markuplint's single-run lint model, but be aware of it when:
276
+
277
+ | Cache location | Scope | Notes |
278
+ | ---------------------------------------- | ------------------------------------------------ | ------------------------------- |
279
+ | `getARIA()` internal cache | `Map` keyed by `localName + namespace + version` | Cleared only on process restart |
280
+ | `getSpecByTagName()` cache | `Map` keyed by `namespace:localName` | Per-specs instance |
281
+ | `getContentModel()` cache | `Map<Specs, Map<Element, ...>>` | Nested, per-specs + per-element |
282
+ | `contentModelCategoryToTagNames()` cache | Module-level `Map<Category, string[]>` | Global, never invalidated |
283
+ | `getAttrSpecs()` cache | `WeakSet` + `Map` per schema | New schema resets cache |
284
+ | `resolveNamespace()` cache | Module-level `Map` | Global, never invalidated |
285
+
286
+ If you add a new algorithm function that computes expensive results, consider adding a similar caching strategy keyed by element + specs + version.
287
+
288
+ ## Versioning Policy
289
+
290
+ - HTML Schema/Specs are **not** part of the public API surface of markuplint.
291
+ - Changes to schemas, generated types, or algorithm behavior are treated as a **minor release**.
292
+ - Publishing is handled by Lerna during the normal release process.
293
+ - The `version` field in `package.json` is managed by Lerna -- do not manually update it.
294
+
295
+ ## Troubleshooting
296
+
297
+ ### Schema generation fails with `$ref` error
298
+
299
+ **Symptom:** `json2ts` reports an unresolved `$ref` during `schema:attributes`.
300
+
301
+ **Cause:** The `$ref` to `../../types/types.schema.json` requires `@markuplint/types` to have generated its schema first.
302
+
303
+ **Fix:** Run `yarn up:schema` from the repository root instead of the package-level `schema` script.
304
+
305
+ ### Generated types differ after `json-schema-to-typescript` update
306
+
307
+ **Symptom:** After updating `json-schema-to-typescript`, `src/types/*.ts` has unexpected changes.
308
+
309
+ **Cause:** New versions may change formatting, optional handling, or type generation strategy.
310
+
311
+ **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
+
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
+ ### Build error: generated type mismatch
322
+
323
+ **Symptom:** TypeScript build errors referencing types in `src/types/aria.ts`, `attributes.ts`, or `permitted-structures.ts`.
324
+
325
+ **Cause:** The JSON schema was edited but types were not regenerated, or a cross-package schema reference is stale.
326
+
327
+ **Fix:**
328
+
329
+ ```bash
330
+ yarn up:schema
331
+ yarn build --scope @markuplint/ml-spec
332
+ ```
333
+
334
+ ### Cache-related issues in long-running processes
335
+
336
+ **Symptom:** Stale data when reusing the same Node.js process across multiple lint runs with different configurations.
337
+
338
+ **Cause:** Module-level caches (`contentModelCategoryToTagNames`, `resolveNamespace`) are never invalidated.
339
+
340
+ **Fix:** This is by design for markuplint's single-run model. If you embed markuplint in a long-running process (e.g., a language server), be aware that spec data is cached at first access. Restarting the process clears all caches.