@markuplint/ml-spec 5.0.0-rc.4 → 5.0.0-rc.6

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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +0 -8
  3. package/lib/algorithm/aria/accname/aria-steps.d.ts +0 -24
  4. package/lib/algorithm/aria/accname/aria-steps.js +0 -24
  5. package/lib/algorithm/aria/accname/compute.d.ts +0 -10
  6. package/lib/algorithm/aria/accname/compute.js +0 -10
  7. package/lib/algorithm/aria/accname/element-names.d.ts +0 -23
  8. package/lib/algorithm/aria/accname/element-names.js +0 -23
  9. package/lib/algorithm/aria/accname/helpers.d.ts +2 -64
  10. package/lib/algorithm/aria/accname/helpers.js +2 -72
  11. package/lib/algorithm/aria/accname/label-steps.d.ts +2 -18
  12. package/lib/algorithm/aria/accname/label-steps.js +5 -21
  13. package/lib/algorithm/aria/accname/types.d.ts +0 -3
  14. package/lib/algorithm/aria/get-explicit-role.d.ts +1 -8
  15. package/lib/algorithm/aria/get-explicit-role.js +1 -8
  16. package/lib/algorithm/aria/get-non-presentational-ancestor.d.ts +2 -10
  17. package/lib/algorithm/aria/get-non-presentational-ancestor.js +2 -10
  18. package/lib/algorithm/aria/get-permitted-roles-spec.js +1 -0
  19. package/lib/algorithm/aria/matches-context-role.d.ts +3 -10
  20. package/lib/algorithm/aria/matches-context-role.js +3 -10
  21. package/lib/algorithm/html/content-model-category-to-tag-names.d.ts +5 -0
  22. package/lib/algorithm/html/content-model-category-to-tag-names.js +5 -0
  23. package/lib/index.d.ts +28 -0
  24. package/lib/index.js +28 -0
  25. package/lib/types/index.d.ts +35 -4
  26. package/lib/utils/schema-to-spec.d.ts +10 -0
  27. package/lib/utils/schema-to-spec.js +10 -0
  28. package/package.json +6 -6
  29. package/ARCHITECTURE.ja.md +0 -267
  30. package/ARCHITECTURE.md +0 -267
  31. package/SKILL.md +0 -116
  32. package/docs/aria-algorithms.ja.md +0 -802
  33. package/docs/aria-algorithms.md +0 -804
  34. package/docs/html-algorithms.ja.md +0 -469
  35. package/docs/html-algorithms.md +0 -469
  36. package/docs/maintenance.ja.md +0 -359
  37. package/docs/maintenance.md +0 -359
  38. package/docs/spec-resolution.ja.md +0 -575
  39. package/docs/spec-resolution.md +0 -588
  40. package/docs/type-definitions.ja.md +0 -584
  41. package/docs/type-definitions.md +0 -584
@@ -1,16 +1,8 @@
1
1
  import { isTransparentForOwnership } from './is-presentational.js';
2
2
  import { getComputedRole } from './get-computed-role.js';
3
3
  /**
4
- * Traverses the parent element chain to find the nearest ancestor with a
5
- * non-presentational role, skipping elements that are transparent for
6
- * ownership traversal (via `isTransparentForOwnership`).
7
- *
8
- * In ARIA 1.3, `generic` role elements are additionally transparent.
9
- *
10
- * @param el - The DOM element whose ancestors to traverse
11
- * @param specs - The full markup language specification
12
- * @param version - The ARIA specification version to use
13
- * @returns The nearest non-presentational ancestor's `ComputedRole`, or `{ el: null, role: null }` if none exists
4
+ * In ARIA 1.3, `generic` role elements are additionally transparent for
5
+ * ownership traversal.
14
6
  */
15
7
  export function getNonPresentationalAncestor(
16
8
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -64,6 +64,7 @@ export function getPermittedRoles(specs, localName, namespace, version, matches)
64
64
  // allowed at all per ARIA in HTML — even a value matching the implicit role.
65
65
  // e.g. `<img alt="">`: implicit role is `presentation`, but "No role permitted"
66
66
  // means `role="presentation"` and `role="none"` are both disallowed.
67
+ // See https://github.com/markuplint/markuplint/issues/3641 for background.
67
68
  if (permittedRoles === false) {
68
69
  return permittedRoleList;
69
70
  }
@@ -1,9 +1,8 @@
1
1
  import type { ARIAVersion, MLMLSpec } from '../../types/index.js';
2
2
  /**
3
- * Checks whether an element's parent hierarchy satisfies the
4
- * "Required Accessibility Parent Role" (called "Required Context Role" in ARIA 1.2).
5
- * Each condition string may describe a chain of ancestor roles separated by
6
- * ` > ` (e.g., `"list > group"`).
3
+ * Implements the ARIA "Required Accessibility Parent Role" (called "Required
4
+ * Context Role" in ARIA 1.2). Each condition string describes a chain of
5
+ * ancestor roles separated by ` > ` (e.g., `"list > group"`).
7
6
  *
8
7
  * TODO: This function only walks the DOM `parentElement` chain and does not
9
8
  * consider `aria-owns` relationships. An element referenced by `aria-owns` on
@@ -11,11 +10,5 @@ import type { ARIAVersion, MLMLSpec } from '../../types/index.js';
11
10
  * that ancestor. Implementing this requires a document-wide reverse lookup of
12
11
  * `aria-owns` attributes, which is a separate architectural concern.
13
12
  * See also: `has-required-owned-elements.ts` has a similar limitation.
14
- *
15
- * @param conditions - An array of required accessibility parent role condition strings to match against
16
- * @param ownedEl - The owned DOM element whose parent context is being validated
17
- * @param specs - The full markup language specification
18
- * @param version - The ARIA specification version to use
19
- * @returns `true` if any of the context role conditions are satisfied by the element's ancestors
20
13
  */
21
14
  export declare function matchesContextRole(conditions: readonly string[], ownedEl: Element, specs: MLMLSpec, version: ARIAVersion): boolean;
@@ -1,10 +1,9 @@
1
1
  import { isTransparentForOwnership } from './is-presentational.js';
2
2
  import { getComputedRole } from './get-computed-role.js';
3
3
  /**
4
- * Checks whether an element's parent hierarchy satisfies the
5
- * "Required Accessibility Parent Role" (called "Required Context Role" in ARIA 1.2).
6
- * Each condition string may describe a chain of ancestor roles separated by
7
- * ` > ` (e.g., `"list > group"`).
4
+ * Implements the ARIA "Required Accessibility Parent Role" (called "Required
5
+ * Context Role" in ARIA 1.2). Each condition string describes a chain of
6
+ * ancestor roles separated by ` > ` (e.g., `"list > group"`).
8
7
  *
9
8
  * TODO: This function only walks the DOM `parentElement` chain and does not
10
9
  * consider `aria-owns` relationships. An element referenced by `aria-owns` on
@@ -12,12 +11,6 @@ import { getComputedRole } from './get-computed-role.js';
12
11
  * that ancestor. Implementing this requires a document-wide reverse lookup of
13
12
  * `aria-owns` attributes, which is a separate architectural concern.
14
13
  * See also: `has-required-owned-elements.ts` has a similar limitation.
15
- *
16
- * @param conditions - An array of required accessibility parent role condition strings to match against
17
- * @param ownedEl - The owned DOM element whose parent context is being validated
18
- * @param specs - The full markup language specification
19
- * @param version - The ARIA specification version to use
20
- * @returns `true` if any of the context role conditions are satisfied by the element's ancestors
21
14
  */
22
15
  export function matchesContextRole(conditions,
23
16
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -5,6 +5,11 @@ import type { Category } from '../../types/permitted-structures.js';
5
5
  * frozen array of HTML/SVG tag names that belong to that category.
6
6
  * Results are cached for repeated lookups.
7
7
  *
8
+ * Note: entries are returned as stored in the spec's `#contentModels` map
9
+ * without CSS selector parsing — they are expected to be bare tag names or
10
+ * tag-with-attribute selectors (e.g. `a[href]`); complex selectors are not
11
+ * decomposed into tag names.
12
+ *
8
13
  * @param contentModel - The content model category identifier
9
14
  * @param def - The specification definitions containing content model mappings
10
15
  * @returns A frozen, sorted array of tag name strings belonging to the category
@@ -4,6 +4,11 @@ const cache = new Map();
4
4
  * frozen array of HTML/SVG tag names that belong to that category.
5
5
  * Results are cached for repeated lookups.
6
6
  *
7
+ * Note: entries are returned as stored in the spec's `#contentModels` map
8
+ * without CSS selector parsing — they are expected to be bare tag names or
9
+ * tag-with-attribute selectors (e.g. `a[href]`); complex selectors are not
10
+ * decomposed into tag names.
11
+ *
7
12
  * @param contentModel - The content model category identifier
8
13
  * @param def - The specification definitions containing content model mappings
9
14
  * @returns A frozen, sorted array of tag name strings belonging to the category
package/lib/index.d.ts CHANGED
@@ -1,3 +1,31 @@
1
+ /**
2
+ * Specification foundation layer for markuplint.
3
+ *
4
+ * Type definitions, W3C specification algorithms (WAI-ARIA, HTML-AAM, AccName,
5
+ * HTML content models), and JSON schemas live together in this single package
6
+ * by design. Separating the "static schemas" from the "computing algorithms"
7
+ * was considered and rejected: the algorithms are integral parts of the
8
+ * specifications themselves — HTML elements define implicit ARIA roles, the
9
+ * ARIA algorithms reference HTML semantics, and test cases validate
10
+ * cross-specification behavior — so a split would create an artificial
11
+ * boundary with no runtime benefit.
12
+ *
13
+ * The accessible name computation (`getAccname`) is implemented in-house with
14
+ * no external dependency; this is intentional.
15
+ *
16
+ * Versioning policy: the schemas and spec data structures are not part of
17
+ * markuplint's public API surface. Changes to schemas, generated types, or
18
+ * algorithm behavior are released as minor versions.
19
+ *
20
+ * Several module-level caches (element spec lookup, namespace resolution,
21
+ * version-resolved ARIA, content-model category tables) are intentionally
22
+ * never invalidated: spec data is immutable after merging and a lint run uses
23
+ * a single configuration. Long-running embedders (e.g. language servers)
24
+ * should be aware that spec data is cached at first access for the process
25
+ * lifetime.
26
+ *
27
+ * @module @markuplint/ml-spec
28
+ */
1
29
  export { getAttrSpecs as getAttrSpecsByNames } from './utils/get-attr-specs-spec.js';
2
30
  export * from './utils/aria-version.js';
3
31
  export { EMBEDDED_CONTROL_ROLES, isNativeEmbeddedControl } from './const/index.js';
package/lib/index.js CHANGED
@@ -1,3 +1,31 @@
1
+ /**
2
+ * Specification foundation layer for markuplint.
3
+ *
4
+ * Type definitions, W3C specification algorithms (WAI-ARIA, HTML-AAM, AccName,
5
+ * HTML content models), and JSON schemas live together in this single package
6
+ * by design. Separating the "static schemas" from the "computing algorithms"
7
+ * was considered and rejected: the algorithms are integral parts of the
8
+ * specifications themselves — HTML elements define implicit ARIA roles, the
9
+ * ARIA algorithms reference HTML semantics, and test cases validate
10
+ * cross-specification behavior — so a split would create an artificial
11
+ * boundary with no runtime benefit.
12
+ *
13
+ * The accessible name computation (`getAccname`) is implemented in-house with
14
+ * no external dependency; this is intentional.
15
+ *
16
+ * Versioning policy: the schemas and spec data structures are not part of
17
+ * markuplint's public API surface. Changes to schemas, generated types, or
18
+ * algorithm behavior are released as minor versions.
19
+ *
20
+ * Several module-level caches (element spec lookup, namespace resolution,
21
+ * version-resolved ARIA, content-model category tables) are intentionally
22
+ * never invalidated: spec data is immutable after merging and a lint run uses
23
+ * a single configuration. Long-running embedders (e.g. language servers)
24
+ * should be aware that spec data is cached at first access for the process
25
+ * lifetime.
26
+ *
27
+ * @module @markuplint/ml-spec
28
+ */
1
29
  export { getAttrSpecs as getAttrSpecsByNames } from './utils/get-attr-specs-spec.js';
2
30
  export * from './utils/aria-version.js';
3
31
  // Constants
@@ -12,6 +12,16 @@ export interface MLMLSpec {
12
12
  readonly def: SpecDefs;
13
13
  readonly specs: readonly ElementSpec[];
14
14
  readonly directivePatterns?: readonly DirectivePattern[];
15
+ /**
16
+ * Controls how `@markuplint/ml-core`'s `MLAttr` resolves attribute names.
17
+ *
18
+ * - `'idl'`: enables IDL-to-content attribute name resolution (e.g.
19
+ * `className` → `class`) and suggests the IDL name as a candidate when the
20
+ * content attribute name is used (e.g. `tabindex` → "Did you mean
21
+ * `tabIndex`?"). Used by React, which accepts only IDL property names.
22
+ * - `'both'`: enables the same resolution but accepts both content attribute
23
+ * names and IDL property names without suggesting either. Used by Svelte.
24
+ */
15
25
  readonly acceptedAttrNames?: 'idl' | 'both';
16
26
  }
17
27
  /**
@@ -32,6 +42,10 @@ export type ExtendedSpec = {
32
42
  readonly def?: Partial<SpecDefs>;
33
43
  readonly specs?: readonly ExtendedElementSpec[];
34
44
  readonly directivePatterns?: readonly DirectivePattern[];
45
+ /**
46
+ * See {@link MLMLSpec.acceptedAttrNames}. Last-write-wins across merged
47
+ * specs; omitting the property does not reset a previously set value.
48
+ */
35
49
  readonly acceptedAttrNames?: 'idl' | 'both';
36
50
  };
37
51
  /**
@@ -41,6 +55,10 @@ export type ExtendedSpec = {
41
55
  *
42
56
  * The `pattern` is a regex string matched against the raw attribute name.
43
57
  * Capture groups can be referenced in `potentialName` using `$1`, `$2`, etc.
58
+ *
59
+ * Patterns are evaluated in order and the first match wins. Merging specs
60
+ * (`schemaToSpec`) concatenates pattern arrays, so spec authors should define
61
+ * patterns from most specific to most general.
44
62
  */
45
63
  export type DirectivePattern = {
46
64
  /**
@@ -131,21 +149,26 @@ export type ElementSpec = {
131
149
  */
132
150
  readonly description?: string;
133
151
  /**
134
- * Experimental technology
152
+ * Experimental technology: not yet standardized (proposed or draft;
153
+ * subject to change or removal).
135
154
  */
136
155
  readonly experimental?: true;
137
156
  /**
138
- * Obsolete or alternative elements
157
+ * Obsolete or alternative elements: removed from the spec entirely
158
+ * (browsers may no longer recognize it). Optionally points to an
159
+ * alternative element via `alt`.
139
160
  */
140
161
  readonly obsolete?: true | {
141
162
  readonly alt: string;
142
163
  };
143
164
  /**
144
- * Deprecated
165
+ * Deprecated: still defined by the spec but its use is discouraged
166
+ * (usually still works in browsers).
145
167
  */
146
168
  readonly deprecated?: true;
147
169
  /**
148
- * Non-standard
170
+ * Non-standard: never part of any standard (vendor-specific or
171
+ * proprietary).
149
172
  */
150
173
  readonly nonStandard?: true;
151
174
  /**
@@ -246,11 +269,19 @@ export type ARIARoleInSchema = Partial<ARIARole & {
246
269
  /**
247
270
  * Describes a property owned by an ARIA role, including whether it is inherited,
248
271
  * required, or deprecated.
272
+ *
273
+ * `requiredCondition` qualifies `required: true` so that the property is only
274
+ * required when the condition holds. The W3C ARIA source markup does not encode
275
+ * such conditions; values are populated by manual overrides in
276
+ * `@markuplint/html-spec/generator/aria.ts`. Currently the only value is
277
+ * `'focusable'` (applied to `separator`'s `aria-valuenow`); the `wai-aria`
278
+ * checker resolves this through `mayBeFocusable` from `@markuplint/ml-spec`.
249
279
  */
250
280
  export type ARIARoleOwnedProperties = {
251
281
  readonly name: string;
252
282
  readonly inherited?: true;
253
283
  readonly required?: true;
284
+ readonly requiredCondition?: 'focusable';
254
285
  readonly deprecated?: true;
255
286
  };
256
287
  /**
@@ -6,6 +6,16 @@ import type { ElementSpec, ExtendedSpec, MLMLSpec } from '../types/index.js';
6
6
  *
7
7
  * Ex: `@markuplint/html-spec` + `{ specs: { "\\.vue$": "@markuplint/vue-spec" } }` in configure files.
8
8
  *
9
+ * The merge is an additive overlay with silent, unconditional override: later
10
+ * specs win, and there is deliberately no conflict detection and no provenance
11
+ * tracking in the merged result. Framework specs exist precisely to relax or
12
+ * extend base HTML constraints (e.g. React's `dangerouslySetInnerHTML`, Vue's
13
+ * `v-if`), so every key collision with the base spec is treated as a
14
+ * deliberate decision by the `ExtendedSpec` author — an extension that
15
+ * unintentionally weakens a base constraint is not flagged anywhere.
16
+ *
17
+ * @see https://github.com/markuplint/markuplint/issues/3893
18
+ *
9
19
  * @param schemas - A tuple where the first element is the base `MLMLSpec` and subsequent elements are extended specs to merge
10
20
  * @returns The merged specification combining the base spec with all extensions
11
21
  */
@@ -6,6 +6,16 @@ import { mergeArray } from './merge-array.js';
6
6
  *
7
7
  * Ex: `@markuplint/html-spec` + `{ specs: { "\\.vue$": "@markuplint/vue-spec" } }` in configure files.
8
8
  *
9
+ * The merge is an additive overlay with silent, unconditional override: later
10
+ * specs win, and there is deliberately no conflict detection and no provenance
11
+ * tracking in the merged result. Framework specs exist precisely to relax or
12
+ * extend base HTML constraints (e.g. React's `dangerouslySetInnerHTML`, Vue's
13
+ * `v-if`), so every key collision with the base spec is treated as a
14
+ * deliberate decision by the `ExtendedSpec` author — an extension that
15
+ * unintentionally weakens a base constraint is not flagged anywhere.
16
+ *
17
+ * @see https://github.com/markuplint/markuplint/issues/3893
18
+ *
9
19
  * @param schemas - A tuple where the first element is the base `MLMLSpec` and subsequent elements are extended specs to merge
10
20
  * @returns The merged specification combining the base spec with all extensions
11
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-spec",
3
- "version": "5.0.0-rc.4",
3
+ "version": "5.0.0-rc.6",
4
4
  "description": "Types and schema that specs of the Markup languages for markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "author": "Yusuke Hirao <yusukehirao@me.com>",
11
11
  "license": "MIT",
12
12
  "engines": {
13
- "node": ">=22"
13
+ "node": ">=24"
14
14
  },
15
15
  "type": "module",
16
16
  "exports": {
@@ -36,14 +36,14 @@
36
36
  "schema:aria": "npx json2ts ./schemas/aria.schema.json --cwd ./schemas > ./src/types/aria.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@markuplint/ml-ast": "5.0.0-rc.4",
40
- "@markuplint/types": "5.0.0-rc.4",
39
+ "@markuplint/ml-ast": "5.0.0-rc.6",
40
+ "@markuplint/types": "5.0.0-rc.6",
41
41
  "is-plain-object": "5.0.0",
42
42
  "type-fest": "5.6.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@markuplint/test-tools": "5.0.0-rc.4",
45
+ "@markuplint/test-tools": "5.0.0-rc.6",
46
46
  "json-schema-to-typescript": "15.0.4"
47
47
  },
48
- "gitHead": "97a6339bbae23f556de5d307b3ce2ef7cfd9402d"
48
+ "gitHead": "c02c3a0783eac6b2fb4707be2dc00b88f6219641"
49
49
  }
@@ -1,267 +0,0 @@
1
- # @markuplint/ml-spec
2
-
3
- ## 概要
4
-
5
- `@markuplint/ml-spec` は markuplint の仕様基盤レイヤーです。型定義、W3C 仕様アルゴリズム(ARIA/HTML)、JSON スキーマ、ランタイムユーティリティを提供し、Web 標準の生データと markuplint のリントルールを橋渡しします。
6
-
7
- `@markuplint/html-spec`(およびフレームワーク固有の仕様パッケージ)から要素仕様・ARIA ロール/プロパティ定義・コンテンツモデルデータを読み込み、ARIA ロール計算・要素仕様の解決・コンテンツモデル評価・アクセシビリティツリー包含判定のアルゴリズムを公開します。15 以上の下流パッケージが依存しています。
8
-
9
- ## ディレクトリ構成
10
-
11
- ```
12
- src/
13
- ├── index.ts # エントリーポイント。全公開 API を再エクスポート
14
- ├── types/
15
- │ ├── index.ts # 手書きのコア型(MLMLSpec, ElementSpec, ARIARole 等)
16
- │ ├── aria.ts # aria.schema.json から生成(ARIA, PermittedRoles, ImplicitRole)
17
- │ ├── attributes.ts # attributes.schema.json から生成(AttributeType, GlobalAttributes)
18
- │ └── permitted-structures.ts # content-models.schema.json から生成(ContentModel, Category)
19
- ├── algorithm/
20
- │ ├── aria/
21
- │ │ ├── accname-computation.ts # アクセシブルネーム計算ファサード(DOM リゾルバ + 再入防止ガード)
22
- │ │ ├── accname/ # AccName 純粋アルゴリズム(HTML-AAM §4.1)
23
- │ │ │ ├── types.ts # AccnameElement/AccnameResolver インターフェース
24
- │ │ │ ├── compute.ts # コアアルゴリズム: Steps 2A-2I
25
- │ │ │ ├── aria-steps.ts # Steps 2B (aria-labelledby), 2D (aria-label)
26
- │ │ │ ├── element-names.ts # Step 2E: 要素固有の名前(HTML-AAM §4.1)
27
- │ │ │ ├── helpers.ts # 共有ユーティリティ(埋め込みコントロール、name-from-content)
28
- │ │ │ ├── label-steps.ts # Step 2E: ラベル関連付け(labelable 要素)
29
- │ │ │ ├── svg-helpers.ts # SVG アクセシブル名ソース判定
30
- │ │ │ └── index.ts # 公開 API 再エクスポート
31
- │ │ ├── aria-specs.ts # バージョン別 ARIA 仕様データの取得
32
- │ │ ├── get-aria.ts # 要素レベルの ARIA 仕様解決(条件付き)
33
- │ │ ├── get-computed-aria-props.ts # ARIA プロパティ解決(明示 → HTML → デフォルト)
34
- │ │ ├── get-computed-role.ts # 中核:最終ロール計算と競合解決
35
- │ │ ├── get-explicit-role.ts # role 属性からの明示ロール(著者エラー処理)
36
- │ │ ├── get-implicit-role.ts # HTML-AAM による暗黙ロール
37
- │ │ ├── get-implicit-role-spec.ts # 暗黙ロール名の低レベル検索
38
- │ │ ├── get-non-presentational-ancestor.ts # プレゼンテーショナルロールをスキップする祖先探索
39
- │ │ ├── get-permitted-roles.ts # DOM 要素の許可ロール
40
- │ │ ├── get-permitted-roles-spec.ts # タグ名/名前空間による許可ロール(低レベル)
41
- │ │ ├── get-role-spec.ts # スーパークラスチェーン付きロール仕様
42
- │ │ ├── has-required-owned-elements.ts # 必須所有要素の検証
43
- │ │ ├── is-exposed.ts # アクセシビリティツリー包含/除外
44
- │ │ ├── is-presentational.ts # プレゼンテーショナルロール判定(presentation/none)
45
- │ │ └── matches-context-role.ts # 必須コンテキストロールの検証
46
- │ └── html/
47
- │ ├── content-model-category-to-tag-names.ts # カテゴリ → タグ名配列(キャッシュ付き)
48
- │ ├── get-content-model.ts # 条件付きコンテンツモデル評価
49
- │ ├── get-selectors-by-content-model-category.ts # カテゴリ → CSS セレクタ配列
50
- │ ├── is-nothing-content-model.ts # 「Nothing」コンテンツモデル判定
51
- │ ├── is-palpable-elements.ts # パルパブルコンテンツ検出
52
- │ ├── is-void-element.ts # ボイド要素判定(13 要素)
53
- │ └── may-be-focusable.ts # フォーカス可能性ヒューリスティック
54
- ├── const/
55
- │ ├── index.ts # 全定数の再エクスポート
56
- │ ├── dom.ts # DOM 定数(ELEMENT_NODE, TEXT_NODE, 名前空間 URI)
57
- │ └── accname.ts # AccName 定数(埋め込みコントロールロール、入力型、デフォルト値)
58
- └── utils/
59
- ├── aria-version.ts # ARIA バージョン定数('1.1', '1.2', '1.3')
60
- ├── get-attr-specs.ts # DOM 要素の属性仕様(ラッパー)
61
- ├── get-attr-specs-spec.ts # タグ名/名前空間による属性仕様(コア)
62
- ├── get-ns.ts # 名前空間 URI → 短縮名マッピング
63
- ├── get-spec.ts # DOM 要素の要素仕様(ラッパー)
64
- ├── get-spec-by-tag-name.ts # タグ名/名前空間による要素仕様(キャッシュ付き)
65
- ├── merge-array.ts # 名前ベースの配列マージユーティリティ
66
- ├── resolve-namespace.ts # 名前空間解決とプレフィックス正規化
67
- ├── resolve-version.ts # ARIA バージョン固有プロパティの解決
68
- ├── schema-to-spec.ts # スキーママージパイプライン(ベース + 拡張)
69
- └── validate-aria-version.ts # ARIA バージョン文字列の型ガード
70
-
71
- schemas/
72
- ├── element.schema.json # トップレベル要素仕様スキーマ(11 行)
73
- ├── aria.schema.json # ARIA ロール/プロパティスキーマ(291 行)
74
- ├── attributes.schema.json # 属性型スキーマ(190 行)
75
- ├── content-models.schema.json # コンテンツモデルパターンスキーマ(215 行)
76
- └── global-attributes.schema.json # グローバル属性カテゴリスキーマ(787 行)
77
-
78
- gen/
79
- ├── gen.ts # global-attributes.schema.json のスキーマジェネレータ
80
- └── global-attribute.data.ts # グローバル属性カテゴリ定義
81
- ```
82
-
83
- ## アーキテクチャ図
84
-
85
- ```mermaid
86
- flowchart TD
87
- subgraph input ["入力レイヤー"]
88
- htmlSpec["@markuplint/html-spec\n(MLMLSpec JSON)"]
89
- fwSpec["フレームワーク仕様\n(ExtendedSpec)"]
90
- end
91
-
92
- subgraph merge ["仕様解決"]
93
- schemaToSpec["schemaToSpec()"]
94
- getSpec["getSpec() / getSpecByTagName()"]
95
- getAttrSpecs["getAttrSpecs()"]
96
- resolveNS["resolveNamespace()"]
97
- end
98
-
99
- subgraph aria ["ARIA アルゴリズム"]
100
- getComputedRole["getComputedRole()"]
101
- getExplicitRole["getExplicitRole()"]
102
- getImplicitRole["getImplicitRole()"]
103
- getPermittedRoles["getPermittedRoles()"]
104
- getRoleSpec["getRoleSpec()"]
105
- getARIA["getARIA()"]
106
- getComputedAriaProps["getComputedAriaProps()"]
107
- isExposed["isExposed()"]
108
- getAccname["getAccname()"]
109
- end
110
-
111
- subgraph html ["HTML アルゴリズム"]
112
- getContentModel["getContentModel()"]
113
- isPalpable["isPalpableElement()"]
114
- isVoid["isVoidElement()"]
115
- mayBeFocusable["mayBeFocusable()"]
116
- end
117
-
118
- subgraph types ["型定義"]
119
- handWritten["MLMLSpec, ElementSpec\nARIARole, ComputedRole"]
120
- generated["ARIA, PermittedRoles\nContentModel, Category"]
121
- end
122
-
123
- htmlSpec --> schemaToSpec
124
- fwSpec --> schemaToSpec
125
- schemaToSpec --> getSpec
126
- getSpec --> getAttrSpecs
127
- getSpec --> getARIA
128
- resolveNS --> getSpec
129
-
130
- getARIA --> getComputedRole
131
- getExplicitRole --> getComputedRole
132
- getImplicitRole --> getComputedRole
133
- getPermittedRoles --> getExplicitRole
134
- getRoleSpec --> getImplicitRole
135
- getRoleSpec --> getExplicitRole
136
- getComputedRole --> getComputedAriaProps
137
- getComputedRole --> isExposed
138
-
139
- getSpec --> getContentModel
140
- getSpec --> isPalpable
141
- ```
142
-
143
- ## 主要コンポーネント
144
-
145
- ### 1. 型定義
146
-
147
- 型システムは、マークアップ言語仕様・要素仕様・ARIA ロール・属性の構造を定義します。
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`, `ConditionalAttributeType`, `GlobalAttributes`, `AttributeJSON`, `List`, `Enum`, `Number`, `Directive` |
154
- | `types/permitted-structures.ts` | 生成型: `PermittedContentPattern`, `ContentModel`, `Category`(HTML 13 + SVG 19 + MathML 3 カテゴリ) |
155
-
156
- ### 2. ARIA アルゴリズム
157
-
158
- ARIA アルゴリズムは WAI-ARIA, HTML-AAM, SVG-AAM, MathML-AAM, AccName 1.2 仕様に基づくロール計算とアクセシビリティツリー管理を実装します。
159
-
160
- | ファイル | 役割 |
161
- | -------------------------------- | ------------------------------------------------------------------------------- |
162
- | `get-computed-role.ts` | 中核アルゴリズム: Presentational Roles Conflict Resolution による最終ロール計算 |
163
- | `get-explicit-role.ts` | `role` 属性からの明示ロール解決(著者エラー処理付き) |
164
- | `get-implicit-role.ts` | HTML-AAM に基づく暗黙(ネイティブ)ARIA ロールの決定 |
165
- | `get-computed-aria-props.ts` | ARIA プロパティ解決: 明示 `aria-*` → HTML 等価属性 → 仕様デフォルト |
166
- | `is-exposed.ts` | WAI-ARIA ルールに基づくアクセシビリティツリーの包含/除外判定 |
167
- | `get-permitted-roles.ts` | 要素の許可ロール一覧(Any/No/具体的リスト) |
168
- | `get-role-spec.ts` | スーパークラスロールチェーン付き完全ロール仕様の取得 |
169
- | `has-required-owned-elements.ts` | 必須所有要素制約の検証 |
170
- | `matches-context-role.ts` | 祖先チェーンにおける必須コンテキストロール条件の検証 |
171
- | `accname-computation.ts` | アクセシブルネーム計算ファサード(DOM リゾルバ + 再入防止ガード) |
172
- | `accname/compute.ts` | 純粋 AccName アルゴリズム: HTML-AAM §4.1 の Steps 2A-2I |
173
- | `accname/element-names.ts` | Step 2E: 要素固有の名前計算(HTML-AAM §4.1) |
174
- | `get-aria.ts` | バージョンと条件の解決を含む要素レベル ARIA 仕様 |
175
- | `is-presentational.ts` | ロールが `presentation` または `none` かどうかの判定 |
176
-
177
- ### 3. HTML アルゴリズム
178
-
179
- HTML アルゴリズムは HTML Living Standard に基づくコンテンツモデル評価と要素分類を実装します。
180
-
181
- | ファイル | 役割 |
182
- | -------------------------------------------- | ------------------------------------------------------------------------------ |
183
- | `get-content-model.ts` | 条件付きパターン評価を含むコンテンツモデルの取得 |
184
- | `content-model-category-to-tag-names.ts` | コンテンツモデルカテゴリをソート済みタグ名配列に変換 |
185
- | `get-selectors-by-content-model-category.ts` | コンテンツモデルカテゴリを CSS セレクタにマッピング |
186
- | `is-palpable-elements.ts` | SVG/MathML/露出可能要素拡張付きパルパブルコンテンツ検出 |
187
- | `is-void-element.ts` | ボイド要素判定(13 の HTML ボイド要素) |
188
- | `is-nothing-content-model.ts` | 「Nothing」コンテンツモデル判定(void + iframe + template) |
189
- | `may-be-focusable.ts` | フォーカス可能性ヒューリスティック(interactive + tabindex + contenteditable) |
190
-
191
- ### 4. 仕様解決ユーティリティ
192
-
193
- 仕様のマージ・解決・キャッシュのためのユーティリティです。
194
-
195
- | ファイル | 役割 |
196
- | -------------------------- | ---------------------------------------------------------------------------- |
197
- | `schema-to-spec.ts` | ベース `MLMLSpec` と `ExtendedSpec[]` のマージ(グローバル属性, ARIA, 要素) |
198
- | `get-spec-by-tag-name.ts` | タグ名 + 名前空間による要素仕様の検索(キャッシュ付き) |
199
- | `get-attr-specs-spec.ts` | マージ済み属性仕様の取得(グローバル + 要素固有) |
200
- | `resolve-namespace.ts` | 名前空間プレフィックスを含む要素名の正規化 |
201
- | `resolve-version.ts` | ARIA バージョン固有のオーバーライドをフォールバック付きで解決 |
202
- | `merge-array.ts` | 名前ベースの配列マージ(`name` プロパティによる追加/上書き) |
203
- | `validate-aria-version.ts` | 有効な ARIA バージョン文字列の型ガード |
204
-
205
- ## 外部依存パッケージ
206
-
207
- | パッケージ | 用途 | 使用箇所 |
208
- | -------------------- | -------------------------------------------------- | ----------------------------- |
209
- | `@markuplint/ml-ast` | XML 名前空間処理のための `NamespaceURI` 型 | `types/index.ts`, utils |
210
- | `@markuplint/types` | 属性値の型定義のための `Type` 共用体 | `types/attributes.ts` 経由 |
211
- | `is-plain-object` | AAM 情報のプレーンオブジェクト検出 | `get-permitted-roles-spec.ts` |
212
- | `type-fest` | 深い不変性のための `ReadonlyDeep` ユーティリティ型 | 複数ファイル |
213
-
214
- ## 他パッケージとの連携
215
-
216
- ```mermaid
217
- flowchart LR
218
- subgraph upstream ["上流パッケージ"]
219
- htmlSpec["@markuplint/html-spec"]
220
- vueSpec["@markuplint/vue-spec"]
221
- reactSpec["@markuplint/react-spec"]
222
- otherSpec["その他フレームワーク仕様"]
223
- end
224
-
225
- subgraph pkg ["@markuplint/ml-spec"]
226
- types["型定義"]
227
- ariaAlgo["ARIA アルゴリズム"]
228
- htmlAlgo["HTML アルゴリズム"]
229
- specRes["仕様解決"]
230
- end
231
-
232
- subgraph downstream ["下流パッケージ"]
233
- mlCore["@markuplint/ml-core"]
234
- rules["@markuplint/rules"]
235
- selector["@markuplint/selector"]
236
- end
237
-
238
- htmlSpec -->|"MLMLSpec JSON"| specRes
239
- vueSpec -->|"ExtendedSpec"| specRes
240
- reactSpec -->|"ExtendedSpec"| specRes
241
- otherSpec -->|"ExtendedSpec"| specRes
242
-
243
- specRes --> ariaAlgo
244
- specRes --> htmlAlgo
245
- types --> mlCore
246
- ariaAlgo -->|"ロール計算\nアクセシビリティツリー"| rules
247
- htmlAlgo -->|"コンテンツモデル\n要素分類"| rules
248
- types --> selector
249
- ```
250
-
251
- ### 上流
252
-
253
- `@markuplint/html-spec` は、全 HTML 要素仕様・ARIA 定義・コンテンツモデルデータを含むベース `MLMLSpec` JSON を提供します。フレームワーク固有パッケージ(`@markuplint/vue-spec`, `@markuplint/react-spec` 等)は、要素・属性・ARIA マッピングを追加またはオーバーライドする `ExtendedSpec` オブジェクトを提供します。
254
-
255
- ### 下流
256
-
257
- - **`@markuplint/ml-core`** は型定義を使用して、仕様認識を持つパース済みドキュメント要素を表現します。
258
- - **`@markuplint/rules`** は ARIA および HTML アルゴリズムを呼び出して、リントルール(ロール検証、コンテンツモデルチェック、アクセシビリティチェック)を実装します。
259
- - **`@markuplint/selector`** は要素マッチングのために型定義を使用します。
260
-
261
- ## ドキュメントマップ
262
-
263
- - [ARIA アルゴリズム](docs/aria-algorithms.ja.md) -- ロール計算、アクセシビリティツリー、ARIA プロパティ解決
264
- - [HTML アルゴリズム](docs/html-algorithms.ja.md) -- コンテンツモデル、要素分類、ボイド要素
265
- - [型定義](docs/type-definitions.ja.md) -- コア型、生成型、JSON スキーマ
266
- - [仕様解決](docs/spec-resolution.ja.md) -- スキーママージ、名前空間解決、キャッシュ
267
- - [メンテナンスガイド](docs/maintenance.ja.md) -- スキーマ生成、依存関係管理、レシピ、トラブルシューティング