@markuplint/ml-spec 5.0.0-rc.1 → 5.0.0-rc.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.
- package/ARCHITECTURE.ja.md +6 -6
- package/ARCHITECTURE.md +6 -6
- package/CHANGELOG.md +16 -0
- package/README.md +1 -1
- package/docs/aria-algorithms.ja.md +14 -10
- package/docs/aria-algorithms.md +14 -10
- package/docs/maintenance.ja.md +28 -0
- package/docs/maintenance.md +28 -0
- package/docs/type-definitions.ja.md +21 -2
- package/docs/type-definitions.md +21 -2
- package/lib/algorithm/aria/accname/aria-steps.js +1 -2
- package/lib/algorithm/aria/get-aria.js +9 -8
- package/lib/algorithm/aria/get-permitted-roles-spec.js +7 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/types/aria.d.ts +27 -60
- package/lib/types/aria.js +1 -0
- package/lib/types/attributes.d.ts +38 -25
- package/lib/types/attributes.js +1 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/permitted-structures.d.ts +9 -1
- package/lib/types/permitted-structures.js +1 -0
- package/lib/utils/is-conditional-attribute-type.d.ts +30 -0
- package/lib/utils/is-conditional-attribute-type.js +29 -0
- package/package.json +13 -11
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -146,12 +146,12 @@ flowchart TD
|
|
|
146
146
|
|
|
147
147
|
型システムは、マークアップ言語仕様・要素仕様・ARIA ロール・属性の構造を定義します。
|
|
148
148
|
|
|
149
|
-
| ファイル | 役割
|
|
150
|
-
| ------------------------------- |
|
|
151
|
-
| `types/index.ts` | 手書き型: `MLMLSpec`, `ElementSpec`, `ExtendedSpec`, `ARIARole`, `ComputedRole` 等
|
|
152
|
-
| `types/aria.ts` | 生成型: `ARIA`, `PermittedRoles`, `ImplicitRole`, `PermittedARIAProperties`, `ImplicitProperties`
|
|
153
|
-
| `types/attributes.ts` | 生成型: `AttributeType`, `GlobalAttributes`, `AttributeJSON`, `List`, `Enum`, `Number`
|
|
154
|
-
| `types/permitted-structures.ts` | 生成型: `PermittedContentPattern`, `ContentModel`, `Category`(HTML 13 + SVG 19 + MathML 3 カテゴリ)
|
|
149
|
+
| ファイル | 役割 |
|
|
150
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
151
|
+
| `types/index.ts` | 手書き型: `MLMLSpec`, `ElementSpec`, `ExtendedSpec`, `ARIARole`, `ComputedRole` 等 |
|
|
152
|
+
| `types/aria.ts` | 生成型: `ARIA`, `PermittedRoles`, `ImplicitRole`, `PermittedARIAProperties`, `ImplicitProperties` |
|
|
153
|
+
| `types/attributes.ts` | 生成型: `AttributeType`, `ConditionalAttributeType`, `GlobalAttributes`, `AttributeJSON`, `List`, `Enum`, `Number`, `Directive` |
|
|
154
|
+
| `types/permitted-structures.ts` | 生成型: `PermittedContentPattern`, `ContentModel`, `Category`(HTML 13 + SVG 19 + MathML 3 カテゴリ) |
|
|
155
155
|
|
|
156
156
|
### 2. ARIA アルゴリズム
|
|
157
157
|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -146,12 +146,12 @@ flowchart TD
|
|
|
146
146
|
|
|
147
147
|
The type system defines the structure of markup language specifications, element specs, ARIA roles, and attributes.
|
|
148
148
|
|
|
149
|
-
| File | Purpose
|
|
150
|
-
| ------------------------------- |
|
|
151
|
-
| `types/index.ts` | Hand-written types: `MLMLSpec`, `ElementSpec`, `ExtendedSpec`, `ARIARole`, `ComputedRole`, etc.
|
|
152
|
-
| `types/aria.ts` | Generated: `ARIA`, `PermittedRoles`, `ImplicitRole`, `PermittedARIAProperties`, `ImplicitProperties`
|
|
153
|
-
| `types/attributes.ts` | Generated: `AttributeType`, `GlobalAttributes`, `AttributeJSON`, `List`, `Enum`, `Number`
|
|
154
|
-
| `types/permitted-structures.ts` | Generated: `PermittedContentPattern`, `ContentModel`, `Category` (HTML 13 + SVG 19 + MathML 3 categories)
|
|
149
|
+
| File | Purpose |
|
|
150
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
151
|
+
| `types/index.ts` | Hand-written types: `MLMLSpec`, `ElementSpec`, `ExtendedSpec`, `ARIARole`, `ComputedRole`, etc. |
|
|
152
|
+
| `types/aria.ts` | Generated: `ARIA`, `PermittedRoles`, `ImplicitRole`, `PermittedARIAProperties`, `ImplicitProperties` |
|
|
153
|
+
| `types/attributes.ts` | Generated: `AttributeType`, `ConditionalAttributeType`, `GlobalAttributes`, `AttributeJSON`, `List`, `Enum`, `Number`, `Directive` |
|
|
154
|
+
| `types/permitted-structures.ts` | Generated: `PermittedContentPattern`, `ContentModel`, `Category` (HTML 13 + SVG 19 + MathML 3 categories) |
|
|
155
155
|
|
|
156
156
|
### 2. ARIA Algorithms
|
|
157
157
|
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.0.0-rc.2](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.1...v5.0.0-rc.2) (2026-04-15)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **ml-spec:** apply optimizePermittedRoles to condition-specific overrides ([f6ad782](https://github.com/markuplint/markuplint/commit/f6ad7823c298011fb690cd3360392bc2893c05f2)), closes [#3724](https://github.com/markuplint/markuplint/issues/3724)
|
|
11
|
+
- **ml-spec:** back-port content-model fields to schema source ([835d4d8](https://github.com/markuplint/markuplint/commit/835d4d8e4aa6bc2329b087194748159a8c1f86f6))
|
|
12
|
+
- **ml-spec:** treat permittedRoles:false as forbidding all explicit roles ([098cc41](https://github.com/markuplint/markuplint/commit/098cc4167297599e7721bf88119af7d8f3c2df08)), closes [#3641](https://github.com/markuplint/markuplint/issues/3641)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- **html-spec:** add conditional value types for input element ([#3598](https://github.com/markuplint/markuplint/issues/3598)) ([290be1c](https://github.com/markuplint/markuplint/commit/290be1cde53165f1f6165731bf2ec184b0c46b54))
|
|
17
|
+
- **ml-spec:** add #nonEmptyText to Category type ([d60be3f](https://github.com/markuplint/markuplint/commit/d60be3f43bff6cc5d9c1cfa9dcdbe5d69f8697b8))
|
|
18
|
+
- **ml-spec:** add ConditionalAttributeType to Attribute.type union ([#3685](https://github.com/markuplint/markuplint/issues/3685)) ([a619a07](https://github.com/markuplint/markuplint/commit/a619a071d93566dd8aa7ab8dee2ed751c2e8756c)), closes [#3598](https://github.com/markuplint/markuplint/issues/3598) [#3189](https://github.com/markuplint/markuplint/issues/3189)
|
|
19
|
+
- **ml-spec:** add forbiddenAncestors field to ContentModel type ([5b32af5](https://github.com/markuplint/markuplint/commit/5b32af5ee7041843b63ca1542cb811d3192e8527))
|
|
20
|
+
- **ml-spec:** add uniqueAttrs field to ContentModel type ([d9e6f16](https://github.com/markuplint/markuplint/commit/d9e6f169888e92c9e8c85de884775cf026c2d1bf))
|
|
21
|
+
|
|
6
22
|
# [5.0.0-rc.1](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-03-27)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @markuplint/ml-spec
|
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ Generated TypeScript types (do not edit directly):
|
|
|
96
96
|
|
|
97
97
|
- **Built output**: `index.json` (48K+ lines, consolidated specification data)
|
|
98
98
|
- **Sources**: `src/spec-*.json` (individual element specifications)
|
|
99
|
-
- **Build process**: `build.
|
|
99
|
+
- **Build process**: `build.ts` → `generator/` scripts → enriched with MDN/W3C data
|
|
100
100
|
|
|
101
101
|
**@markuplint/ml-spec** (this package) provides:
|
|
102
102
|
|
|
@@ -240,16 +240,19 @@ if (result.role) {
|
|
|
240
240
|
2. 仕様から `implicitRole` と `permittedRoles` を読み取ります。
|
|
241
241
|
3. `permittedRoles` の値に基づいて許可ロールリストを構築します:
|
|
242
242
|
|
|
243
|
-
| `permittedRoles` の値 | 動作
|
|
244
|
-
| ----------------------------------- |
|
|
245
|
-
| `true` | ARIA 仕様のすべての非抽象ロール(DPub ロールを含む)が許可されます。
|
|
246
|
-
| `PermittedARIAAAMInfo` オブジェクト | `core-aam` が `true` の場合、すべての非抽象ロールを追加。`graphics-aam` が `true` の場合、すべての非抽象グラフィックスロールを追加。
|
|
247
|
-
| 文字列/オブジェクトの配列 | リストに記載された特定のロールが許可されます。
|
|
248
|
-
| `false` |
|
|
249
|
-
|
|
250
|
-
4.
|
|
243
|
+
| `permittedRoles` の値 | 動作 |
|
|
244
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
245
|
+
| `true` | ARIA 仕様のすべての非抽象ロール(DPub ロールを含む)が許可されます。 |
|
|
246
|
+
| `PermittedARIAAAMInfo` オブジェクト | `core-aam` が `true` の場合、すべての非抽象ロールを追加。`graphics-aam` が `true` の場合、すべての非抽象グラフィックスロールを追加。 |
|
|
247
|
+
| 文字列/オブジェクトの配列 | リストに記載された特定のロールが許可されます。 |
|
|
248
|
+
| `false` | **明示的な `role` 属性値は一切許可されません** — 空リストを返し、暗黙ロールのマージ(手順4)をスキップします。ARIA in HTML の "No role permitted" 指示を厳密に反映する挙動です。 |
|
|
249
|
+
|
|
250
|
+
4. `permittedRoles` が `false` でない場合、結果に暗黙のロールを含めます。暗黙のロールが `"presentation"` または `"none"` の場合、両方の等価ロールが含まれます。ARIA 1.3 では、暗黙のロールが `"img"` または `"image"` の場合、両方のシノニムが含まれます。
|
|
251
251
|
5. マージおよび重複排除されたリストを返します。
|
|
252
252
|
|
|
253
|
+
> [!NOTE]
|
|
254
|
+
> `permittedRoles: false` は厳密な挙動です。暗黙ロールに一致する値(例: `<img alt="" role="presentation">`)も拒否します。これは [ARIA in HTML](https://w3c.github.io/html-aria/) の「No role permitted」行が `role` 属性を一切許可しない仕様を反映したものです。背景は Issue #3641 を参照してください。
|
|
255
|
+
|
|
253
256
|
---
|
|
254
257
|
|
|
255
258
|
### 7. `getRoleSpec(specs, roleName, namespace, version)`
|
|
@@ -325,7 +328,6 @@ if (result.role) {
|
|
|
325
328
|
1. `getVersionResolvedARIA()` を呼び出します。この関数は:
|
|
326
329
|
- タグ名と名前空間で要素仕様を検索します。
|
|
327
330
|
- `resolveVersion()` を適用して、ベース ARIA 仕様の上にバージョン固有のオーバーライドをマージします。
|
|
328
|
-
- 許可ロールの最適化:許可ロール配列に `"presentation"` がある場合は `"none"` を追加し、逆も同様です(WAI-ARIA 1.2 の `none` ロールに関する注記に準拠)。ARIA 1.3 では、`"image"` がある場合は `"img"` を追加し、逆も同様です(ARIA 1.3 の `image`/`img` シノニムに準拠)。
|
|
329
331
|
- 結果を `localName + namespace + version` をキーとしてキャッシュします。
|
|
330
332
|
|
|
331
333
|
2. 条件付きオーバーライド(ARIA 仕様の `conditions` ブロック)を評価します:
|
|
@@ -333,7 +335,9 @@ if (result.role) {
|
|
|
333
335
|
- マッチする条件ごとに、`implicitRole`、`permittedRoles`、`implicitProperties`、`properties`、`namingProhibited` を上書きします。
|
|
334
336
|
- 後の条件は前の条件よりも優先されます。
|
|
335
337
|
|
|
336
|
-
3.
|
|
338
|
+
3. 許可ロールの最適化(ベース値と条件オーバーライド値の両方に適用):許可ロール配列に `"presentation"` がある場合は `"none"` を追加し、逆も同様です(WAI-ARIA 1.2 の `none` ロールに関する注記に準拠)。ARIA 1.3 では、`"image"` がある場合は `"img"` を追加し、逆も同様です(ARIA 1.3 の `image`/`img` シノニムに準拠)。
|
|
339
|
+
|
|
340
|
+
4. 最終的に解決された ARIA 仕様を返します。要素の仕様が存在しない場合は `null` を返します。
|
|
337
341
|
|
|
338
342
|
**例:** `<input>` の場合、ベース仕様は汎用的な暗黙のロールを定義しますが、条件 `[type=checkbox]` がそれを `"checkbox"` ロールに上書きします。
|
|
339
343
|
|
package/docs/aria-algorithms.md
CHANGED
|
@@ -240,16 +240,19 @@ The spec-level implementation for computing permitted ARIA roles. Operates on ta
|
|
|
240
240
|
2. Reads `implicitRole` and `permittedRoles` from the spec.
|
|
241
241
|
3. Builds the permitted role list based on the `permittedRoles` value:
|
|
242
242
|
|
|
243
|
-
| `permittedRoles` value | Behavior
|
|
244
|
-
| ----------------------------- |
|
|
245
|
-
| `true` | All non-abstract roles from the ARIA spec (including DPub roles) are permitted.
|
|
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
|
-
| Array of strings/objects | The specific listed roles are permitted.
|
|
248
|
-
| `false` | No
|
|
249
|
-
|
|
250
|
-
4.
|
|
243
|
+
| `permittedRoles` value | Behavior |
|
|
244
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
245
|
+
| `true` | All non-abstract roles from the ARIA spec (including DPub roles) are permitted. |
|
|
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
|
+
| Array of strings/objects | The specific listed roles are permitted. |
|
|
248
|
+
| `false` | **No explicit role is permitted** — returns an empty list and skips the implicit-role merge below. This models ARIA in HTML's "No role permitted" directive. |
|
|
249
|
+
|
|
250
|
+
4. If `permittedRoles` was not `false`, 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
|
+
> [!NOTE]
|
|
254
|
+
> `permittedRoles: false` is strict: even a value matching the implicit role (e.g. `<img alt="" role="presentation">`) is rejected. This mirrors the [ARIA in HTML](https://w3c.github.io/html-aria/) spec, where rows marked "No role permitted" forbid _any_ explicit `role` attribute. See Issue #3641 for background.
|
|
255
|
+
|
|
253
256
|
---
|
|
254
257
|
|
|
255
258
|
### 7. `getRoleSpec(specs, roleName, namespace, version)`
|
|
@@ -325,7 +328,6 @@ Gets the version-resolved ARIA specification for an element, evaluating conditio
|
|
|
325
328
|
1. Calls `getVersionResolvedARIA()` which:
|
|
326
329
|
- Looks up the element spec by tag name and namespace.
|
|
327
330
|
- Applies `resolveVersion()` to merge version-specific overrides on top of the base ARIA spec.
|
|
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).
|
|
329
331
|
- Caches results by `localName + namespace + version`.
|
|
330
332
|
|
|
331
333
|
2. Evaluates conditional overrides (the `conditions` block in the ARIA spec):
|
|
@@ -333,7 +335,9 @@ Gets the version-resolved ARIA specification for an element, evaluating conditio
|
|
|
333
335
|
- For each matching condition, overrides `implicitRole`, `permittedRoles`, `implicitProperties`, `properties`, and `namingProhibited`.
|
|
334
336
|
- Later conditions take precedence over earlier ones.
|
|
335
337
|
|
|
336
|
-
3.
|
|
338
|
+
3. Optimizes permitted roles (applied to both base and condition-overridden values): 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).
|
|
339
|
+
|
|
340
|
+
4. Returns the final resolved ARIA spec, or `null` if no spec exists for the element.
|
|
337
341
|
|
|
338
342
|
**Example:** For `<input>`, the base spec may define a generic implicit role, but the condition `[type=checkbox]` overrides it to `"checkbox"`.
|
|
339
343
|
|
package/docs/maintenance.ja.md
CHANGED
|
@@ -230,6 +230,34 @@ AttributeJSON: {
|
|
|
230
230
|
|
|
231
231
|
再生成すると、新しいフィールドが `src/types/attributes.ts` にオプショナルプロパティとして反映されます。
|
|
232
232
|
|
|
233
|
+
> **警告:** `schemas/attributes.schema.json` と `schemas/global-attributes.schema.json` は `gen/gen.ts` によって _上書き_ されます。**これらのファイルを直接編集してはいけません** — 次に `schema:json`(または `yarn up:schema`)が走った瞬間に変更が消えます。すべての編集は `gen/gen.ts` に対して行ってください。
|
|
234
|
+
|
|
235
|
+
### 3b. `attributes.schema.json` に新しい definition を追加する(新しいスキーマ型)
|
|
236
|
+
|
|
237
|
+
`AttributeJSON` のフィールドではなく、まったく新しい definition を追加したい場合 — 例えば #3685 の `ConditionalAttributeType` のような新しい構造化型バリアント — は、`gen/gen.ts` の 2 つめの `fs.writeFileSync` 呼び出し内の `definitions` オブジェクトに、`AttributeJSON` の隣に配置してください。
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
fs.writeFileSync(
|
|
241
|
+
path.resolve(import.meta.dirname, '..', 'schemas', 'attributes.schema.json'),
|
|
242
|
+
JSON.stringify({
|
|
243
|
+
definitions: {
|
|
244
|
+
// ...既存の definitions...
|
|
245
|
+
NewType: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
additionalProperties: false,
|
|
248
|
+
required: ['foo'],
|
|
249
|
+
properties: { foo: { type: 'string' } },
|
|
250
|
+
},
|
|
251
|
+
AttributeJSON: {
|
|
252
|
+
/* ... */
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
}),
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
再生成すると、新しい definition は TypeScript の `interface` として `src/types/attributes.ts` から export されます。下流パッケージ(例: `@markuplint/rules`)で narrowing が必要な場合は、`src/utils/` に型ガードを追加し `src/index.ts` から再 export してください — #3685 で確立したパターンは `is-conditional-attribute-type.ts` を参照してください。
|
|
260
|
+
|
|
233
261
|
### 4. ARIA ロール/プロパティスキーマを変更する
|
|
234
262
|
|
|
235
263
|
`schemas/aria.schema.json` を直接編集します。例えば、ロール定義に新しいフィールドを追加する場合:
|
package/docs/maintenance.md
CHANGED
|
@@ -230,6 +230,34 @@ AttributeJSON: {
|
|
|
230
230
|
|
|
231
231
|
Then regenerate. The new field will appear in `src/types/attributes.ts` as an optional property.
|
|
232
232
|
|
|
233
|
+
> **Warning:** `schemas/attributes.schema.json` and `schemas/global-attributes.schema.json` are both _overwritten_ by `gen/gen.ts`. **Never hand-edit those files** — your changes will be blown away the next time `schema:json` (or `yarn up:schema`) runs. All edits must live in `gen/gen.ts`.
|
|
234
|
+
|
|
235
|
+
### 3b. Add a new definition to `attributes.schema.json` (new schema type)
|
|
236
|
+
|
|
237
|
+
When you need to add a brand-new definition (not just a field on `AttributeJSON`) — for example, adding a new structured type variant like `ConditionalAttributeType` (#3685) — edit `gen/gen.ts` and place the new definition alongside `AttributeJSON` inside the `definitions` object of the second `fs.writeFileSync` call.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
fs.writeFileSync(
|
|
241
|
+
path.resolve(import.meta.dirname, '..', 'schemas', 'attributes.schema.json'),
|
|
242
|
+
JSON.stringify({
|
|
243
|
+
definitions: {
|
|
244
|
+
// ...existing definitions...
|
|
245
|
+
NewType: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
additionalProperties: false,
|
|
248
|
+
required: ['foo'],
|
|
249
|
+
properties: { foo: { type: 'string' } },
|
|
250
|
+
},
|
|
251
|
+
AttributeJSON: {
|
|
252
|
+
/* ... */
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
}),
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
After regeneration, the new definition is exported from `src/types/attributes.ts` as a TypeScript `interface`. If downstream packages (e.g. `@markuplint/rules`) need to narrow against it, add a type guard in `src/utils/` and re-export from `src/index.ts` — see `is-conditional-attribute-type.ts` for the pattern established by #3685.
|
|
260
|
+
|
|
233
261
|
### 4. Modify ARIA role/property schema
|
|
234
262
|
|
|
235
263
|
Edit `schemas/aria.schema.json` directly. For example, to add a new field to the role definition:
|
|
@@ -113,7 +113,10 @@ type ElementSpec = {
|
|
|
113
113
|
```ts
|
|
114
114
|
type Attribute = {
|
|
115
115
|
readonly name: string;
|
|
116
|
-
readonly type:
|
|
116
|
+
readonly type:
|
|
117
|
+
| ReadonlyDeep<AttributeType>
|
|
118
|
+
| readonly ReadonlyDeep<AttributeType>[]
|
|
119
|
+
| readonly ReadonlyDeep<ConditionalAttributeType>[];
|
|
117
120
|
readonly description?: string;
|
|
118
121
|
readonly caseSensitive?: true;
|
|
119
122
|
readonly experimental?: boolean;
|
|
@@ -125,6 +128,8 @@ type Attribute = {
|
|
|
125
128
|
|
|
126
129
|
`& ExtendableAttributeSpec` の交差型により、生成された `AttributeJSON` 型のフィールド(`type` を除く。`ReadonlyDeep` ラッパーでオーバーライドされるため)が追加されます: `defaultValue`、`required`、`requiredEither`、`noUse`、`condition`、`ineffective`、`animatable`。
|
|
127
130
|
|
|
131
|
+
`type` の共用体には、別の属性の値によって値型が切り替わる属性のために `readonly ReadonlyDeep<ConditionalAttributeType>[]` が含まれます(下記 [`ConditionalAttributeType`](#attributesschemajson-から----srctypesattributests) を参照)。このバリアントは v5.0 では型定義のみで先行投入されており(#3685)、実ロジックは follow-up Issue #3598 / #3189 で実装されます。
|
|
132
|
+
|
|
128
133
|
## ARIA 型
|
|
129
134
|
|
|
130
135
|
### `ARIARole`
|
|
@@ -419,7 +424,10 @@ interface GlobalAttributes {
|
|
|
419
424
|
|
|
420
425
|
```ts
|
|
421
426
|
interface AttributeJSON {
|
|
422
|
-
type?:
|
|
427
|
+
type?:
|
|
428
|
+
| AttributeType
|
|
429
|
+
| [AttributeType, ...AttributeType[]]
|
|
430
|
+
| [ConditionalAttributeType, ...ConditionalAttributeType[]];
|
|
423
431
|
defaultValue?: string;
|
|
424
432
|
deprecated?: boolean;
|
|
425
433
|
required?: boolean | AttributeCondition;
|
|
@@ -432,6 +440,17 @@ interface AttributeJSON {
|
|
|
432
440
|
}
|
|
433
441
|
```
|
|
434
442
|
|
|
443
|
+
**`ConditionalAttributeType`** -- 条件付き型切り替え: ある属性の期待する値型が、同じ要素上の _別の_ 属性の値によって切り替わることを宣言します。例えば `<input value>` は `type=color` のとき valid simple color、`type=url` のとき valid URL でなければならない、あるいは `<link as>` は `rel` が `preload` か `modulepreload` かによって異なる destination キーワードを受け入れる、といった仕様表現に使われます。
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
interface ConditionalAttributeType {
|
|
447
|
+
condition: AttributeCondition; // 所属要素に対する CSS セレクター
|
|
448
|
+
type: AttributeType | [AttributeType, ...AttributeType[]];
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
> **ステータス:** 型定義は v5.0 で投入済み (#3685)。条件解決は完全に実装済み: `type` による `input[value]` は [#3598](https://github.com/markuplint/markuplint/issues/3598)、`rel` による `link[as]` は [#3189](https://github.com/markuplint/markuplint/issues/3189)。`helpers.ts` の `isValidAttr()` が要素を各条件にマッチさせ、解決された型でバリデーションを行います。条件に一致しない場合は `Any` にフォールバックします。直接呼び出し元向けに、`attrCheck()` に安全ネットガードが残っており、未解決の `ConditionalAttributeType[]` は有効として扱います。
|
|
453
|
+
|
|
435
454
|
**`List`** -- スペース区切りまたはカンマ区切りのトークンリスト用の構造化型です。
|
|
436
455
|
|
|
437
456
|
```ts
|
package/docs/type-definitions.md
CHANGED
|
@@ -113,7 +113,10 @@ Describes a single HTML/SVG attribute with its type, description, and status fla
|
|
|
113
113
|
```ts
|
|
114
114
|
type Attribute = {
|
|
115
115
|
readonly name: string;
|
|
116
|
-
readonly type:
|
|
116
|
+
readonly type:
|
|
117
|
+
| ReadonlyDeep<AttributeType>
|
|
118
|
+
| readonly ReadonlyDeep<AttributeType>[]
|
|
119
|
+
| readonly ReadonlyDeep<ConditionalAttributeType>[];
|
|
117
120
|
readonly description?: string;
|
|
118
121
|
readonly caseSensitive?: true;
|
|
119
122
|
readonly experimental?: boolean;
|
|
@@ -125,6 +128,8 @@ type Attribute = {
|
|
|
125
128
|
|
|
126
129
|
The `& ExtendableAttributeSpec` intersection adds fields from the generated `AttributeJSON` type (excluding `type`, which is overridden with the `ReadonlyDeep` wrapper): `defaultValue`, `required`, `requiredEither`, `noUse`, `condition`, `ineffective`, `animatable`.
|
|
127
130
|
|
|
131
|
+
The `type` union includes `readonly ReadonlyDeep<ConditionalAttributeType>[]` for attributes whose value type depends on another attribute's value (see [`ConditionalAttributeType`](#from-attributesschemajson----srctypesattributests) below). This variant is shipped type-only in v5.0 (#3685); follow-ups #3598 and #3189 implement the runtime resolution.
|
|
132
|
+
|
|
128
133
|
## ARIA Types
|
|
129
134
|
|
|
130
135
|
### `ARIARole`
|
|
@@ -419,7 +424,10 @@ interface GlobalAttributes {
|
|
|
419
424
|
|
|
420
425
|
```ts
|
|
421
426
|
interface AttributeJSON {
|
|
422
|
-
type?:
|
|
427
|
+
type?:
|
|
428
|
+
| AttributeType
|
|
429
|
+
| [AttributeType, ...AttributeType[]]
|
|
430
|
+
| [ConditionalAttributeType, ...ConditionalAttributeType[]];
|
|
423
431
|
defaultValue?: string;
|
|
424
432
|
deprecated?: boolean;
|
|
425
433
|
required?: boolean | AttributeCondition;
|
|
@@ -432,6 +440,17 @@ interface AttributeJSON {
|
|
|
432
440
|
}
|
|
433
441
|
```
|
|
434
442
|
|
|
443
|
+
**`ConditionalAttributeType`** -- Conditional type switching: declares that an attribute's expected value type depends on the value of _another_ attribute on the same element. Used when the spec states, for example, that `<input value>` must be a valid simple color when `type=color` but a valid URL when `type=url`, or that `<link as>` accepts different destination keywords depending on whether `rel` is `preload` or `modulepreload`.
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
interface ConditionalAttributeType {
|
|
447
|
+
condition: AttributeCondition; // CSS selector against the owning element
|
|
448
|
+
type: AttributeType | [AttributeType, ...AttributeType[]];
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
> **Status:** The type definition shipped in v5.0 (#3685). Conditional resolution is fully implemented: `input[value]` by `type` in [#3598](https://github.com/markuplint/markuplint/issues/3598), `link[as]` by `rel` in [#3189](https://github.com/markuplint/markuplint/issues/3189). `isValidAttr()` in `helpers.ts` matches the element against each condition and validates against the resolved type; unmatched conditions fall back to `Any`. A safety-net guard in `attrCheck()` treats any remaining unresolved `ConditionalAttributeType[]` as valid for direct callers.
|
|
453
|
+
|
|
435
454
|
**`List`** -- Structured type for space-separated or comma-separated token lists.
|
|
436
455
|
|
|
437
456
|
```ts
|
|
@@ -71,8 +71,7 @@ export function resolveAriaLabelledby(el, resolver, visited, computeFn) {
|
|
|
71
71
|
}
|
|
72
72
|
// Each branch gets its own visited set so that one IDREF's
|
|
73
73
|
// traversal doesn't block resolution of a later IDREF.
|
|
74
|
-
const innerVisited = new Set(newVisited);
|
|
75
|
-
innerVisited.add(id);
|
|
74
|
+
const innerVisited = new Set([...newVisited, id]);
|
|
76
75
|
const result = computeFn(referenced, resolver, true, innerVisited);
|
|
77
76
|
if (result.name) {
|
|
78
77
|
parts.push(result.name);
|
|
@@ -20,7 +20,10 @@ export function getARIA(specs, localName, namespace, version, matches) {
|
|
|
20
20
|
}
|
|
21
21
|
const conditions = aria.conditions;
|
|
22
22
|
if (!conditions) {
|
|
23
|
-
return
|
|
23
|
+
return {
|
|
24
|
+
...aria,
|
|
25
|
+
permittedRoles: optimizePermittedRoles(aria.permittedRoles, version),
|
|
26
|
+
};
|
|
24
27
|
}
|
|
25
28
|
const conditionKeys = Object.keys(conditions);
|
|
26
29
|
let { implicitRole, permittedRoles, implicitProperties, properties, namingProhibited } = aria;
|
|
@@ -56,12 +59,16 @@ export function getARIA(specs, localName, namespace, version, matches) {
|
|
|
56
59
|
}
|
|
57
60
|
return {
|
|
58
61
|
implicitRole,
|
|
59
|
-
permittedRoles,
|
|
62
|
+
permittedRoles: optimizePermittedRoles(permittedRoles, version),
|
|
60
63
|
implicitProperties,
|
|
61
64
|
properties,
|
|
62
65
|
namingProhibited,
|
|
63
66
|
};
|
|
64
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns raw ARIA spec without permittedRoles optimization.
|
|
70
|
+
* Callers must apply optimizePermittedRoles() to the result.
|
|
71
|
+
*/
|
|
65
72
|
function getVersionResolvedARIA(specs, localName, namespace, version) {
|
|
66
73
|
const key = localName + namespace + version;
|
|
67
74
|
let aria = cache.get(key);
|
|
@@ -74,12 +81,6 @@ function getVersionResolvedARIA(specs, localName, namespace, version) {
|
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
83
|
aria = resolveVersion(spec, version);
|
|
77
|
-
if (aria.permittedRoles !== false) {
|
|
78
|
-
aria = {
|
|
79
|
-
...aria,
|
|
80
|
-
permittedRoles: optimizePermittedRoles(aria.permittedRoles, version),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
84
|
cache.set(key, aria);
|
|
84
85
|
return aria;
|
|
85
86
|
}
|
|
@@ -60,6 +60,13 @@ export function getPermittedRoles(specs, localName, namespace, version, matches)
|
|
|
60
60
|
return role;
|
|
61
61
|
}));
|
|
62
62
|
}
|
|
63
|
+
// When `permittedRoles` is explicitly `false`, no explicit role attribute is
|
|
64
|
+
// allowed at all per ARIA in HTML — even a value matching the implicit role.
|
|
65
|
+
// e.g. `<img alt="">`: implicit role is `presentation`, but "No role permitted"
|
|
66
|
+
// means `role="presentation"` and `role="none"` are both disallowed.
|
|
67
|
+
if (permittedRoles === false) {
|
|
68
|
+
return permittedRoleList;
|
|
69
|
+
}
|
|
63
70
|
if (implicitRole === false) {
|
|
64
71
|
return permittedRoleList;
|
|
65
72
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export * from './utils/schema-to-spec.js';
|
|
|
27
27
|
export * from './utils/resolve-namespace.js';
|
|
28
28
|
export * from './utils/validate-aria-version.js';
|
|
29
29
|
export * from './utils/directive-resolver.js';
|
|
30
|
+
export * from './utils/is-conditional-attribute-type.js';
|
|
30
31
|
export * from './types/index.js';
|
|
31
32
|
export * from './types/aria.js';
|
|
32
33
|
export * from './types/attributes.js';
|
package/lib/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export * from './utils/schema-to-spec.js';
|
|
|
31
31
|
export * from './utils/resolve-namespace.js';
|
|
32
32
|
export * from './utils/validate-aria-version.js';
|
|
33
33
|
export * from './utils/directive-resolver.js';
|
|
34
|
+
export * from './utils/is-conditional-attribute-type.js';
|
|
34
35
|
// Type definitions
|
|
35
36
|
export * from './types/index.js';
|
|
36
37
|
export * from './types/aria.js';
|
package/lib/types/aria.d.ts
CHANGED
|
@@ -43,25 +43,26 @@ export type PermittedARIAProperties = false | {
|
|
|
43
43
|
*/
|
|
44
44
|
without?: [
|
|
45
45
|
{
|
|
46
|
-
type:
|
|
46
|
+
type: "not-recommended" | "should-not" | "must-not";
|
|
47
47
|
name: string;
|
|
48
48
|
value?: string;
|
|
49
49
|
alt?: {
|
|
50
|
-
method:
|
|
50
|
+
method: "remove-attr" | "set-attr";
|
|
51
51
|
target: string;
|
|
52
52
|
};
|
|
53
53
|
},
|
|
54
54
|
...{
|
|
55
|
-
type:
|
|
55
|
+
type: "not-recommended" | "should-not" | "must-not";
|
|
56
56
|
name: string;
|
|
57
57
|
value?: string;
|
|
58
58
|
alt?: {
|
|
59
|
-
method:
|
|
59
|
+
method: "remove-attr" | "set-attr";
|
|
60
60
|
target: string;
|
|
61
61
|
};
|
|
62
62
|
}[]
|
|
63
63
|
];
|
|
64
64
|
};
|
|
65
|
+
export type ConditionsOrFalse = false | Conditions;
|
|
65
66
|
export interface AriaSchema {
|
|
66
67
|
_?: ARIA;
|
|
67
68
|
[k: string]: unknown;
|
|
@@ -72,81 +73,34 @@ export interface ARIA {
|
|
|
72
73
|
namingProhibited?: true;
|
|
73
74
|
implicitProperties?: ImplicitProperties;
|
|
74
75
|
properties?: PermittedARIAProperties;
|
|
75
|
-
conditions?:
|
|
76
|
-
|
|
77
|
-
* This interface was referenced by `undefined`'s JSON-Schema definition
|
|
78
|
-
* via the `patternProperty` ".+".
|
|
79
|
-
*/
|
|
80
|
-
[k: string]: {
|
|
81
|
-
implicitRole?: ImplicitRole;
|
|
82
|
-
permittedRoles?: PermittedRoles;
|
|
83
|
-
namingProhibited?: true;
|
|
84
|
-
implicitProperties?: ImplicitProperties;
|
|
85
|
-
properties?: PermittedARIAProperties;
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
'1.3'?: {
|
|
76
|
+
conditions?: Conditions;
|
|
77
|
+
"1.3"?: {
|
|
89
78
|
implicitRole?: ImplicitRole;
|
|
90
79
|
permittedRoles?: PermittedRoles;
|
|
91
80
|
namingProhibited?: true;
|
|
92
81
|
implicitProperties?: ImplicitProperties;
|
|
93
82
|
properties?: PermittedARIAProperties;
|
|
94
|
-
conditions?:
|
|
95
|
-
/**
|
|
96
|
-
* This interface was referenced by `undefined`'s JSON-Schema definition
|
|
97
|
-
* via the `patternProperty` ".+".
|
|
98
|
-
*/
|
|
99
|
-
[k: string]: {
|
|
100
|
-
implicitRole?: ImplicitRole;
|
|
101
|
-
permittedRoles?: PermittedRoles;
|
|
102
|
-
namingProhibited?: true;
|
|
103
|
-
implicitProperties?: ImplicitProperties;
|
|
104
|
-
properties?: PermittedARIAProperties;
|
|
105
|
-
};
|
|
106
|
-
};
|
|
83
|
+
conditions?: ConditionsOrFalse;
|
|
107
84
|
};
|
|
108
|
-
|
|
85
|
+
"1.2"?: {
|
|
109
86
|
implicitRole?: ImplicitRole;
|
|
110
87
|
permittedRoles?: PermittedRoles;
|
|
111
88
|
namingProhibited?: true;
|
|
112
89
|
implicitProperties?: ImplicitProperties;
|
|
113
90
|
properties?: PermittedARIAProperties;
|
|
114
|
-
conditions?:
|
|
115
|
-
/**
|
|
116
|
-
* This interface was referenced by `undefined`'s JSON-Schema definition
|
|
117
|
-
* via the `patternProperty` ".+".
|
|
118
|
-
*/
|
|
119
|
-
[k: string]: {
|
|
120
|
-
implicitRole?: ImplicitRole;
|
|
121
|
-
permittedRoles?: PermittedRoles;
|
|
122
|
-
namingProhibited?: true;
|
|
123
|
-
implicitProperties?: ImplicitProperties;
|
|
124
|
-
properties?: PermittedARIAProperties;
|
|
125
|
-
};
|
|
126
|
-
};
|
|
91
|
+
conditions?: ConditionsOrFalse;
|
|
127
92
|
};
|
|
128
|
-
|
|
93
|
+
"1.1"?: {
|
|
129
94
|
implicitRole?: ImplicitRole;
|
|
130
95
|
permittedRoles?: PermittedRoles;
|
|
131
96
|
implicitProperties?: ImplicitProperties;
|
|
132
97
|
properties?: PermittedARIAProperties;
|
|
133
|
-
conditions?:
|
|
134
|
-
/**
|
|
135
|
-
* This interface was referenced by `undefined`'s JSON-Schema definition
|
|
136
|
-
* via the `patternProperty` ".+".
|
|
137
|
-
*/
|
|
138
|
-
[k: string]: {
|
|
139
|
-
implicitRole?: ImplicitRole;
|
|
140
|
-
permittedRoles?: PermittedRoles;
|
|
141
|
-
implicitProperties?: ImplicitProperties;
|
|
142
|
-
properties?: PermittedARIAProperties;
|
|
143
|
-
};
|
|
144
|
-
};
|
|
98
|
+
conditions?: ConditionsOrFalse;
|
|
145
99
|
};
|
|
146
100
|
}
|
|
147
101
|
export interface PermittedARIAAAMInfo {
|
|
148
|
-
|
|
149
|
-
|
|
102
|
+
"core-aam"?: true;
|
|
103
|
+
"graphics-aam"?: true;
|
|
150
104
|
}
|
|
151
105
|
export interface ImplicitProperties {
|
|
152
106
|
/**
|
|
@@ -155,3 +109,16 @@ export interface ImplicitProperties {
|
|
|
155
109
|
*/
|
|
156
110
|
[k: string]: string;
|
|
157
111
|
}
|
|
112
|
+
export interface Conditions {
|
|
113
|
+
/**
|
|
114
|
+
* This interface was referenced by `Conditions`'s JSON-Schema definition
|
|
115
|
+
* via the `patternProperty` ".+".
|
|
116
|
+
*/
|
|
117
|
+
[k: string]: {
|
|
118
|
+
implicitRole?: ImplicitRole;
|
|
119
|
+
permittedRoles?: PermittedRoles;
|
|
120
|
+
namingProhibited?: true;
|
|
121
|
+
implicitProperties?: ImplicitProperties;
|
|
122
|
+
properties?: PermittedARIAProperties;
|
|
123
|
+
};
|
|
124
|
+
}
|
package/lib/types/aria.js
CHANGED