@markuplint/ml-core 4.13.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.
Files changed (92) hide show
  1. package/ARCHITECTURE.ja.md +524 -0
  2. package/ARCHITECTURE.md +524 -0
  3. package/CHANGELOG.md +52 -2
  4. package/README.md +5 -0
  5. package/SKILL.md +61 -0
  6. package/docs/linting-pipeline.ja.md +307 -0
  7. package/docs/linting-pipeline.md +307 -0
  8. package/docs/maintenance.ja.md +210 -0
  9. package/docs/maintenance.md +210 -0
  10. package/docs/ml-dom/attr.ja.md +103 -0
  11. package/docs/ml-dom/attr.md +103 -0
  12. package/docs/ml-dom/block.ja.md +272 -0
  13. package/docs/ml-dom/block.md +272 -0
  14. package/docs/ml-dom/document.ja.md +141 -0
  15. package/docs/ml-dom/document.md +141 -0
  16. package/docs/ml-dom/element.ja.md +176 -0
  17. package/docs/ml-dom/element.md +176 -0
  18. package/docs/ml-dom/helpers.ja.md +203 -0
  19. package/docs/ml-dom/helpers.md +203 -0
  20. package/docs/ml-dom/node.ja.md +199 -0
  21. package/docs/ml-dom/node.md +199 -0
  22. package/docs/ml-dom/others.ja.md +120 -0
  23. package/docs/ml-dom/others.md +120 -0
  24. package/docs/ml-dom/overview.ja.md +102 -0
  25. package/docs/ml-dom/overview.md +102 -0
  26. package/docs/ml-dom/pretender.ja.md +269 -0
  27. package/docs/ml-dom/pretender.md +269 -0
  28. package/docs/ml-dom/rule-mapping.ja.md +371 -0
  29. package/docs/ml-dom/rule-mapping.md +371 -0
  30. package/docs/ml-dom.ja.md +18 -0
  31. package/docs/ml-dom.md +18 -0
  32. package/docs/rule-system.ja.md +287 -0
  33. package/docs/rule-system.md +287 -0
  34. package/lib/convert-ruleset.d.ts +7 -0
  35. package/lib/convert-ruleset.js +7 -0
  36. package/lib/debug.d.ts +4 -0
  37. package/lib/debug.js +4 -0
  38. package/lib/index.d.ts +4 -3
  39. package/lib/index.js +1 -1
  40. package/lib/ml-core.d.ts +37 -1
  41. package/lib/ml-core.js +171 -82
  42. package/lib/ml-dom/helper/accname.d.ts +8 -0
  43. package/lib/ml-dom/helper/accname.js +71 -55
  44. package/lib/ml-dom/helper/create-node.js +1 -0
  45. package/lib/ml-dom/helper/get-indent.d.ts +4 -1
  46. package/lib/ml-dom/helper/get-indent.js +21 -30
  47. package/lib/ml-dom/node/attr.d.ts +65 -4
  48. package/lib/ml-dom/node/attr.js +151 -53
  49. package/lib/ml-dom/node/block.d.ts +23 -2
  50. package/lib/ml-dom/node/block.js +24 -1
  51. package/lib/ml-dom/node/child-node.d.ts +9 -0
  52. package/lib/ml-dom/node/child-node.js +9 -0
  53. package/lib/ml-dom/node/comment.d.ts +7 -0
  54. package/lib/ml-dom/node/comment.js +7 -0
  55. package/lib/ml-dom/node/document-fragment.d.ts +8 -0
  56. package/lib/ml-dom/node/document-fragment.js +8 -0
  57. package/lib/ml-dom/node/document-type.d.ts +22 -0
  58. package/lib/ml-dom/node/document-type.js +25 -0
  59. package/lib/ml-dom/node/document.d.ts +88 -7
  60. package/lib/ml-dom/node/document.js +128 -32
  61. package/lib/ml-dom/node/dom-token-list.js +17 -30
  62. package/lib/ml-dom/node/element-close-tag.js +1 -0
  63. package/lib/ml-dom/node/element.d.ts +151 -5
  64. package/lib/ml-dom/node/element.js +242 -50
  65. package/lib/ml-dom/node/node-store.js +6 -15
  66. package/lib/ml-dom/node/node.d.ts +19 -1
  67. package/lib/ml-dom/node/node.js +175 -166
  68. package/lib/ml-dom/node/parent-node.js +14 -30
  69. package/lib/ml-dom/node/rule-mapper.js +7 -20
  70. package/lib/ml-dom/node/text.d.ts +19 -0
  71. package/lib/ml-dom/node/text.js +21 -0
  72. package/lib/ml-dom/node/types.d.ts +68 -0
  73. package/lib/ml-dom/token/token.d.ts +42 -0
  74. package/lib/ml-dom/token/token.js +59 -39
  75. package/lib/ml-rule/create-rule.d.ts +17 -1
  76. package/lib/ml-rule/ml-rule-context.js +7 -11
  77. package/lib/ml-rule/ml-rule.d.ts +66 -1
  78. package/lib/ml-rule/ml-rule.js +95 -25
  79. package/lib/ml-rule/types.d.ts +41 -0
  80. package/lib/plugin/plugin.d.ts +8 -0
  81. package/lib/plugin/plugin.js +8 -0
  82. package/lib/plugin/types.d.ts +21 -0
  83. package/lib/ruleset/index.d.ts +10 -0
  84. package/lib/ruleset/index.js +13 -0
  85. package/lib/test/index.d.ts +42 -1
  86. package/lib/test/index.js +39 -2
  87. package/lib/types.d.ts +10 -1
  88. package/lib/violation-collector.d.ts +33 -0
  89. package/lib/violation-collector.js +48 -28
  90. package/lib/virtual-rule.d.ts +72 -0
  91. package/lib/virtual-rule.js +233 -0
  92. package/package.json +16 -13
@@ -0,0 +1,203 @@
1
+ # ヘルパーとユーティリティ
2
+
3
+ ## ヘルパー関数
4
+
5
+ ### createNode()
6
+
7
+ **ソース:** `src/ml-dom/helper/create-node.ts`
8
+
9
+ AST ノード型を対応する MLDOM コンストラクタにディスパッチするファクトリ関数です。
10
+
11
+ | AST `type` フィールド | MLDOM クラス | `nodeType` |
12
+ | --------------------------------- | ----------------------------------------------------------------- | ---------- |
13
+ | `'doctype'` | `MLDocumentType` | `10` |
14
+ | `'starttag'` | `MLElement` | `1` |
15
+ | `'comment'` | `MLComment` | `8` |
16
+ | `'text'` | `MLText` | `3` |
17
+ | `'psblock'` | `MLBlock` | `101` |
18
+ | `'invalid'`(`kind: 'starttag'`) | `MLElement`(`x-invalid`、`elementType: 'web-component'` として) | `1` |
19
+ | `'invalid'`(その他の kind) | `MLText` | `3` |
20
+
21
+ **注:** `'endtag'` は `createNode()` を通過しません -- ドキュメントのコンストラクション中にスキップされます。`MLElement` は `pairNode` 参照から内部的に `MLElementCloseTag` を作成します。`MLASTAttr` は `MLElement` によって処理され、要素の `attributes` 配列から `MLAttr` インスタンスを作成します。
22
+
23
+ ### ウォーカー
24
+
25
+ **ソース:** `src/ml-dom/helper/walkers.ts`
26
+
27
+ #### `syncWalk(nodeList, walker)`
28
+
29
+ 同期的な深さ優先ツリー走査です。`nodeList` の各ノードに対して:
30
+
31
+ - ノードが `ELEMENT_NODE` または `MARKUPLINT_PREPROCESSOR_BLOCK` の場合:まず子を再帰的に走査し、**それから**ノードに対して walker を呼び出す(後順走査)
32
+
33
+ #### `sequentialWalker(list, walker)`
34
+
35
+ 逐次的な非同期走査です。walker が同期か非同期かに関わらず、walker が一度に1つずつ実行されることを保証します。内部のプロミスチェーンを使って逐次実行します。
36
+
37
+ ### accname
38
+
39
+ **ソース:** `src/ml-dom/helper/accname.ts`
40
+
41
+ #### `getAccname(element, version)`
42
+
43
+ WAI-ARIA アルゴリズムに従って要素のアクセシブル名を計算します:
44
+
45
+ 1. `@markuplint/ml-spec` の `get()` 関数で直接計算を試みる(`aria-label`、`aria-labelledby` などを処理)
46
+ 2. 要素が ARIA 設定を持つ pretender コンテキストを持つ場合 → `getAccnameFromPretender()` を使用する:
47
+ - pretender 設定から `aria.name` プロパティを読み取る
48
+ - `name` が `fromAttr` を持つオブジェクトの場合 → 元の要素から指定された属性の値を読み取る
49
+ 3. 要素が `aria-hidden="true"` または `hidden` 属性を持つ場合 → 空文字列を返す
50
+ 4. ロールが `accessibleNameFromContent` をサポートする場合 → 子のテキストコンテンツを再帰的に連結する
51
+ 5. それ以外の場合 → 空文字列を返す
52
+
53
+ ### getIndent
54
+
55
+ **ソース:** `src/ml-dom/helper/get-indent.ts`
56
+
57
+ #### `getIndent(node)`(非推奨)
58
+
59
+ 隣接するテキストノードの空白を調べて、ノードの前のインデントを解析します。
60
+
61
+ `MLDOMIndentation` を返します:
62
+
63
+ | プロパティ/メソッド | 型 | 説明 |
64
+ | ------------------- | --------------------------------------- | ---------------------------------------------------------------------------- |
65
+ | `raw` | `string` | インデント文字列 |
66
+ | `type` | `'tab' \| 'space' \| 'mixed' \| 'none'` | インデントの種類 |
67
+ | `width` | `number` | インデントの文字数 |
68
+ | `line` | `number` | インデントが発生する行番号 |
69
+ | `fix(raw)` | メソッド | ソーステキストノード内の対応する行のインデントを変更してインデントを置換する |
70
+
71
+ `fix()` メソッドはテキストノードの `raw` を改行で分割し、対象行のインデントを置換し、再結合した文字列でテキストノードの `fix()` を呼び出します。
72
+
73
+ ## 補助クラス
74
+
75
+ ### MLNamedNodeMap
76
+
77
+ **ソース:** `src/ml-dom/node/named-node-map.ts`
78
+
79
+ `Array<MLAttr>` を継承し、DOM `NamedNodeMap` インターフェースを実装します。`MLElement.attributes` で使用されます。
80
+
81
+ - **重複排除**: 各属性名の最初の出現のみが保持される
82
+ - **`getNamedItem(qualifiedName)`**: 大文字小文字を区別する名前検索。`MLAttr | null` を返す
83
+ - **`item(index)`**: インデックスベースのアクセス
84
+ - ミューテーションメソッド(`setNamedItem`、`removeNamedItem` など)は `UnexpectedCallError` をスローする
85
+
86
+ #### `toNamedNodeMap(nodes)`
87
+
88
+ `MLAttr` の読み取り専用配列から `MLNamedNodeMap` を作成するヘルパー関数です。
89
+
90
+ ### MLDomTokenList
91
+
92
+ **ソース:** `src/ml-dom/node/dom-token-list.ts`
93
+
94
+ `Array<string>` を継承し、DOM `DOMTokenList` インターフェースを実装します。スペース区切りの属性値(例:`class`)に使用されます。
95
+
96
+ | プロパティ/メソッド | 型 | 説明 |
97
+ | ------------------- | ---------------- | ---------------------------------------------- |
98
+ | `value` | `string` | 元の属性値文字列 |
99
+ | `contains(token)` | `boolean` | セットベースの高速なトークン存在チェック |
100
+ | `allTokens()` | `Scope[]` | 各トークンの位置情報(`Scope`)を返す |
101
+ | `pick(token)` | `Scope \| null` | 特定のトークンの位置情報を返す |
102
+ | `add(...tokens)` | `void` | トークンを追加する(基となる属性値を変更する) |
103
+ | `item(index)` | `string \| null` | インデックスベースのトークンアクセス |
104
+ | `forEach(callback)` | `void` | コールバックでトークンを反復する |
105
+ | `toString()` | `string` | 元の `value` 文字列を返す |
106
+
107
+ ミューテーションメソッド(`remove`、`replace`、`toggle`、`supports`)は `UnexpectedCallError` をスローします。
108
+
109
+ `Scope` オブジェクトには、属性値内の各トークンの正確なソース位置として `startOffset`、`endOffset`、`startLine`、`startCol`、`endLine`、`endCol` が含まれます。
110
+
111
+ ## 型ユーティリティ
112
+
113
+ **ソース:** `src/ml-dom/node/types.ts`
114
+
115
+ ### MappedNode
116
+
117
+ TypeScript の型レベルで AST ノード型を対応する MLDOM ラッパー型にマッピングします:
118
+
119
+ ```typescript
120
+ type MappedNode<N, T, O> = N extends MLASTElement
121
+ ? MLElement<T, O>
122
+ : N extends MLASTComment
123
+ ? MLComment<T, O>
124
+ : N extends MLASTText
125
+ ? MLText<T, O>
126
+ : N extends MLASTDoctype
127
+ ? MLDocumentType<T, O>
128
+ : N extends MLASTPreprocessorSpecificBlock
129
+ ? MLBlock<T, O>
130
+ : N extends MLASTAttr
131
+ ? MLAttr<T, O>
132
+ : N extends MLASTInvalid
133
+ ? MLText<T, O>
134
+ : N extends MLASTToken
135
+ ? MLToken
136
+ : never;
137
+ ```
138
+
139
+ ### NodeTypeOf
140
+
141
+ 数値のノード型定数を MLDOM クラス型に解決し、`is()` 型ガードを有効にします:
142
+
143
+ ```typescript
144
+ type NodeTypeOf<NT, T, O> = NT extends 1
145
+ ? MLElement<T, O> // ELEMENT_NODE
146
+ : NT extends 8
147
+ ? MLComment<T, O> // COMMENT_NODE
148
+ : NT extends 3
149
+ ? MLText<T, O> // TEXT_NODE
150
+ : NT extends 9
151
+ ? MLDocument<T, O> // DOCUMENT_NODE
152
+ : NT extends 10
153
+ ? MLDocumentType<T, O> // DOCUMENT_TYPE_NODE
154
+ : NT extends 11
155
+ ? MLDocumentFragment<T, O> // DOCUMENT_FRAGMENT_NODE
156
+ : NT extends 101
157
+ ? MLBlock<T, O> // MARKUPLINT_PREPROCESSOR_BLOCK
158
+ : NT extends 2
159
+ ? MLAttr<T, O> // ATTRIBUTE_NODE
160
+ : never;
161
+ ```
162
+
163
+ ### AccessibilityProperties
164
+
165
+ `MLDocument.getAccessibilityProp()` が返す計算済みアクセシビリティプロパティ:
166
+
167
+ ```typescript
168
+ type AccessibilityProperties =
169
+ | {
170
+ unknown: false;
171
+ exposedToTree: boolean;
172
+ role?: string;
173
+ name?: string | { unknown: true };
174
+ nameRequired?: boolean;
175
+ nameProhibited?: boolean;
176
+ focusable?: boolean;
177
+ props?: Record<string, { value: string | null; required: boolean }>;
178
+ }
179
+ | {
180
+ unknown: true;
181
+ };
182
+ ```
183
+
184
+ ### PretenderContext
185
+
186
+ pretender システムのコンテキスト:
187
+
188
+ ```typescript
189
+ // 別の要素として振る舞う要素
190
+ type PretenderContextPretender<N, T, O> = {
191
+ readonly type: 'pretender';
192
+ readonly as: N; // 振る舞い先の仮想 MLElement
193
+ readonly aria?: PretenderARIA;
194
+ };
195
+
196
+ // 元の要素を指す仮想要素
197
+ type PretenderContextPretended<N, T, O> = {
198
+ readonly type: 'origin';
199
+ readonly origin: N; // 元の MLElement
200
+ };
201
+
202
+ type PretenderContext<N, T, O> = PretenderContextPretender<N, T, O> | PretenderContextPretended<N, T, O>;
203
+ ```
@@ -0,0 +1,203 @@
1
+ # Helpers & Utilities
2
+
3
+ ## Helper Functions
4
+
5
+ ### createNode()
6
+
7
+ **Source:** `src/ml-dom/helper/create-node.ts`
8
+
9
+ Factory function that dispatches AST node types to their corresponding MLDOM constructors.
10
+
11
+ | AST `type` Field | MLDOM Class | `nodeType` |
12
+ | -------------------------------- | ------------------------------------------------------------ | ---------- |
13
+ | `'doctype'` | `MLDocumentType` | `10` |
14
+ | `'starttag'` | `MLElement` | `1` |
15
+ | `'comment'` | `MLComment` | `8` |
16
+ | `'text'` | `MLText` | `3` |
17
+ | `'psblock'` | `MLBlock` | `101` |
18
+ | `'invalid'` (`kind: 'starttag'`) | `MLElement` (as `x-invalid`, `elementType: 'web-component'`) | `1` |
19
+ | `'invalid'` (other kind) | `MLText` | `3` |
20
+
21
+ **Note:** `'endtag'` is not passed through `createNode()` -- it is skipped during document construction. `MLElement` creates `MLElementCloseTag` internally from its `pairNode` reference. `MLASTAttr` is handled by `MLElement` which creates `MLAttr` instances from the element's `attributes` array.
22
+
23
+ ### Walkers
24
+
25
+ **Source:** `src/ml-dom/helper/walkers.ts`
26
+
27
+ #### `syncWalk(nodeList, walker)`
28
+
29
+ Synchronous depth-first tree walking. For each node in `nodeList`:
30
+
31
+ - If the node is an `ELEMENT_NODE` or `MARKUPLINT_PREPROCESSOR_BLOCK`: recursively walk its children first, **then** call the walker on the node (post-order traversal)
32
+
33
+ #### `sequentialWalker(list, walker)`
34
+
35
+ Sequential async walking. Ensures walkers execute one at a time regardless of whether the walker is sync or async. Uses an internal promise chain for sequential execution.
36
+
37
+ ### accname
38
+
39
+ **Source:** `src/ml-dom/helper/accname.ts`
40
+
41
+ #### `getAccname(element, version)`
42
+
43
+ Computes the accessible name for an element following WAI-ARIA algorithms:
44
+
45
+ 1. Attempt direct computation via `@markuplint/ml-spec`'s `get()` function (handles `aria-label`, `aria-labelledby`, etc.)
46
+ 2. If the element has a pretender context with ARIA settings → use `getAccnameFromPretender()`:
47
+ - Reads the `aria.name` property from the pretender config
48
+ - If `name` is an object with `fromAttr` → reads the specified attribute's value from the original element
49
+ 3. If the element has `aria-hidden="true"` or `hidden` attribute → return empty string
50
+ 4. If the role supports `accessibleNameFromContent` → recursively concatenate child text content
51
+ 5. Otherwise → return empty string
52
+
53
+ ### getIndent
54
+
55
+ **Source:** `src/ml-dom/helper/get-indent.ts`
56
+
57
+ #### `getIndent(node)` (deprecated)
58
+
59
+ Analyzes indentation preceding a node by examining whitespace in adjacent text nodes.
60
+
61
+ Returns `MLDOMIndentation`:
62
+
63
+ | Property/Method | Type | Description |
64
+ | --------------- | --------------------------------------- | ------------------------------------------------------------------------------------ |
65
+ | `raw` | `string` | The indentation string |
66
+ | `type` | `'tab' \| 'space' \| 'mixed' \| 'none'` | Indentation type |
67
+ | `width` | `number` | Character count of indentation |
68
+ | `line` | `number` | Line number where indentation occurs |
69
+ | `fix(raw)` | Method | Replaces the indentation by modifying the corresponding line in the source text node |
70
+
71
+ The `fix()` method splits the text node's `raw` by newlines, replaces the indentation on the target line, and calls `fix()` on the text node with the rejoined string.
72
+
73
+ ## Supplementary Classes
74
+
75
+ ### MLNamedNodeMap
76
+
77
+ **Source:** `src/ml-dom/node/named-node-map.ts`
78
+
79
+ Extends `Array<MLAttr>` and implements the DOM `NamedNodeMap` interface. Used by `MLElement.attributes`.
80
+
81
+ - **Deduplication**: Only the first occurrence of each attribute name is kept
82
+ - **`getNamedItem(qualifiedName)`**: Case-sensitive name lookup, returns `MLAttr | null`
83
+ - **`item(index)`**: Index-based access
84
+ - Mutation methods (`setNamedItem`, `removeNamedItem`, etc.) throw `UnexpectedCallError`
85
+
86
+ #### `toNamedNodeMap(nodes)`
87
+
88
+ Helper function that creates an `MLNamedNodeMap` from a readonly array of `MLAttr` instances.
89
+
90
+ ### MLDomTokenList
91
+
92
+ **Source:** `src/ml-dom/node/dom-token-list.ts`
93
+
94
+ Extends `Array<string>` and implements the DOM `DOMTokenList` interface. Used for space-separated attribute values (e.g., `class`).
95
+
96
+ | Property/Method | Type | Description |
97
+ | ------------------- | ---------------- | ------------------------------------------------------ |
98
+ | `value` | `string` | The original attribute value string |
99
+ | `contains(token)` | `boolean` | Set-based fast token existence check |
100
+ | `allTokens()` | `Scope[]` | Returns position information (`Scope`) for each token |
101
+ | `pick(token)` | `Scope \| null` | Returns position info for a specific token |
102
+ | `add(...tokens)` | `void` | Adds tokens (modifies the underlying attribute values) |
103
+ | `item(index)` | `string \| null` | Index-based token access |
104
+ | `forEach(callback)` | `void` | Iterates tokens with callback |
105
+ | `toString()` | `string` | Returns the original `value` string |
106
+
107
+ Mutation methods (`remove`, `replace`, `toggle`, `supports`) throw `UnexpectedCallError`.
108
+
109
+ The `Scope` object contains `startOffset`, `endOffset`, `startLine`, `startCol`, `endLine`, `endCol` for precise source location of each token within the attribute value.
110
+
111
+ ## Type Utilities
112
+
113
+ **Source:** `src/ml-dom/node/types.ts`
114
+
115
+ ### MappedNode
116
+
117
+ Maps an AST node type to its corresponding MLDOM wrapper type at the TypeScript type level:
118
+
119
+ ```typescript
120
+ type MappedNode<N, T, O> = N extends MLASTElement
121
+ ? MLElement<T, O>
122
+ : N extends MLASTComment
123
+ ? MLComment<T, O>
124
+ : N extends MLASTText
125
+ ? MLText<T, O>
126
+ : N extends MLASTDoctype
127
+ ? MLDocumentType<T, O>
128
+ : N extends MLASTPreprocessorSpecificBlock
129
+ ? MLBlock<T, O>
130
+ : N extends MLASTAttr
131
+ ? MLAttr<T, O>
132
+ : N extends MLASTInvalid
133
+ ? MLText<T, O>
134
+ : N extends MLASTToken
135
+ ? MLToken
136
+ : never;
137
+ ```
138
+
139
+ ### NodeTypeOf
140
+
141
+ Resolves a numeric node type constant to its MLDOM class type, enabling the `is()` type guard:
142
+
143
+ ```typescript
144
+ type NodeTypeOf<NT, T, O> = NT extends 1
145
+ ? MLElement<T, O> // ELEMENT_NODE
146
+ : NT extends 8
147
+ ? MLComment<T, O> // COMMENT_NODE
148
+ : NT extends 3
149
+ ? MLText<T, O> // TEXT_NODE
150
+ : NT extends 9
151
+ ? MLDocument<T, O> // DOCUMENT_NODE
152
+ : NT extends 10
153
+ ? MLDocumentType<T, O> // DOCUMENT_TYPE_NODE
154
+ : NT extends 11
155
+ ? MLDocumentFragment<T, O> // DOCUMENT_FRAGMENT_NODE
156
+ : NT extends 101
157
+ ? MLBlock<T, O> // MARKUPLINT_PREPROCESSOR_BLOCK
158
+ : NT extends 2
159
+ ? MLAttr<T, O> // ATTRIBUTE_NODE
160
+ : never;
161
+ ```
162
+
163
+ ### AccessibilityProperties
164
+
165
+ Computed accessibility properties returned by `MLDocument.getAccessibilityProp()`:
166
+
167
+ ```typescript
168
+ type AccessibilityProperties =
169
+ | {
170
+ unknown: false;
171
+ exposedToTree: boolean;
172
+ role?: string;
173
+ name?: string | { unknown: true };
174
+ nameRequired?: boolean;
175
+ nameProhibited?: boolean;
176
+ focusable?: boolean;
177
+ props?: Record<string, { value: string | null; required: boolean }>;
178
+ }
179
+ | {
180
+ unknown: true;
181
+ };
182
+ ```
183
+
184
+ ### PretenderContext
185
+
186
+ Context for the pretender system:
187
+
188
+ ```typescript
189
+ // Element pretending to be another element
190
+ type PretenderContextPretender<N, T, O> = {
191
+ readonly type: 'pretender';
192
+ readonly as: N; // The virtual MLElement being pretended as
193
+ readonly aria?: PretenderARIA;
194
+ };
195
+
196
+ // Virtual element pointing back to the original
197
+ type PretenderContextPretended<N, T, O> = {
198
+ readonly type: 'origin';
199
+ readonly origin: N; // The original MLElement
200
+ };
201
+
202
+ type PretenderContext<N, T, O> = PretenderContextPretender<N, T, O> | PretenderContextPretended<N, T, O>;
203
+ ```
@@ -0,0 +1,199 @@
1
+ # MLNode / MLParentNode
2
+
3
+ ## MLNode
4
+
5
+ **ソース:** `src/ml-dom/node/node.ts`
6
+
7
+ すべての markuplint DOM ノードラッパーの抽象基底クラスです。`MLToken` を継承し、DOM `Node` インターフェース準拠、ツリー走査、ルール設定アクセス、子ノード管理の機能を追加します。
8
+
9
+ ### ノード型定数
10
+
11
+ | 定数 | 値 | DOM Standard |
12
+ | ------------------------------- | ----- | ------------------------- |
13
+ | `ELEMENT_NODE` | `1` | はい |
14
+ | `ATTRIBUTE_NODE` | `2` | はい |
15
+ | `TEXT_NODE` | `3` | はい |
16
+ | `CDATA_SECTION_NODE` | `4` | はい |
17
+ | `PROCESSING_INSTRUCTION_NODE` | `7` | はい |
18
+ | `COMMENT_NODE` | `8` | はい |
19
+ | `DOCUMENT_NODE` | `9` | はい |
20
+ | `DOCUMENT_TYPE_NODE` | `10` | はい |
21
+ | `DOCUMENT_FRAGMENT_NODE` | `11` | はい |
22
+ | `MARKUPLINT_PREPROCESSOR_BLOCK` | `101` | いいえ(markuplint 拡張) |
23
+
24
+ ### ツリー構造プロパティ
25
+
26
+ | プロパティ | 型 | 説明 |
27
+ | ----------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
28
+ | `parentNode` | `MLDocument \| MLDocumentFragment \| MLElement \| null` | DOM 準拠の親。透過的な `MLBlock` の親はスキップされる |
29
+ | `parentElement` | `MLElement \| null` | 最も近い `MLElement` である祖先 |
30
+ | `syntacticalParentNode` | `MLDocument \| MLDocumentFragment \| MLElement \| MLBlock \| null` | `MLBlock` ノードを含む構文上の親 |
31
+ | `childNodes` | `NodeListOf<MLChildNode>` | 子ノード(Element, Text, Comment, Block)。フラグメントの子はインライン展開される |
32
+ | `firstChild` | `MLChildNode \| null` | 最初の子ノード |
33
+ | `lastChild` | `MLChildNode \| null` | 最後の子ノード |
34
+ | `nextSibling` | `MLChildNode \| null` | 同じ `parentNode` を持つ次の兄弟ノード |
35
+ | `previousSibling` | `MLChildNode \| null` | 同じ `parentNode` を持つ前の兄弟ノード |
36
+ | `nextNode` | `MLNode \| null` | 構文上の兄弟リスト内の次のノード(`syntacticalParentNode.childNodes` から) |
37
+ | `prevNode` | `MLNode \| null` | 構文上の兄弟リスト内の前のノード |
38
+ | `prevToken` | `MLNode \| null` | ドキュメント順 `nodeList` 内の前のノード(省略された要素はスキップ) |
39
+ | `ownerDocument` | `any` | 所属するドキュメント(DOM 互換、`any` 型) |
40
+ | `ownerMLDocument` | `MLDocument<T, O>` | 適切なジェネリクス型を持つ所属ドキュメント |
41
+ | `isFragment` | `boolean` | このノードがフラグメントとして動作するかどうか |
42
+
43
+ #### `nextNode`/`prevNode` と `nextSibling`/`previousSibling` の違い
44
+
45
+ この2組のプロパティは異なる目的に使用されます:
46
+
47
+ - **`nextNode`/`prevNode`**: `syntacticalParentNode.childNodes`(構文上の親がない場合は `nodeList`)の構文上の兄弟リストを走査します。`MLBlock` ノードを含み、AST レベルで動作します。
48
+ - **`nextSibling`/`previousSibling`**: 同じ DOM `parentNode` を共有する兄弟を走査します。`parentNode` が異なるノード(例:非透過ブロック内のノード)はスキップされます。
49
+
50
+ #### `prevToken` と省略された要素
51
+
52
+ `prevToken` はドキュメント順の `nodeList` を走査しますが、**省略された(ゴースト)要素をスキップ**します。省略された要素は対応するソーストークンを持たないため、それらを含めるとオフセット計算が壊れます。これはインデント解析やソース再構築にとって重要です。
53
+
54
+ ### `childNodes` とフラグメント展開
55
+
56
+ 子ノードが `isFragment === true` を持つ場合、その子ノード自身の子が親の `childNodes` にインライン展開されます:
57
+
58
+ ```jsx
59
+ // JSX フラグメント
60
+ <div>
61
+ <>
62
+ {' '}
63
+ {/* isFragment = true */}
64
+ <p>A</p>
65
+ <p>B</p>
66
+ </>
67
+ <p>C</p>
68
+ </div>
69
+ ```
70
+
71
+ `div.childNodes` は `[<p>A</p>, <p>B</p>, <p>C</p>]` を返します -- フラグメントラッパーは透過的です。
72
+
73
+ ### `parentNode` と MLBlock の透過性
74
+
75
+ `parentNode` ゲッターは `MLBlock` の透過性を処理します:
76
+
77
+ 1. `syntacticalParentNode` を取得
78
+ 2. 親が `isTransparent === true` の `MLBlock` の場合:ブロックの `parentNode` を返す(再帰的にスキップ)
79
+ 3. 親が `isTransparent === false` の `MLBlock` の場合:`null` を返す(DOM の観点からノードは「孤立」している)
80
+ 4. 親がフラグメント `MLDocument`(つまり `isFragment === true`)の場合:`null` を返す
81
+ 5. それ以外の場合:親をそのまま返す
82
+
83
+ | シナリオ | `syntacticalParentNode` | `parentNode` |
84
+ | ------------------------------------------- | ----------------------- | --------------------------- |
85
+ | `<body>` 内の `<div>` | `<body>` | `<body>` |
86
+ | `<div>` 内の Pug `if` ブロック内の `<span>` | `#ml-block` | `<div>`(透過的にスキップ) |
87
+ | 非透過ブロック内の `<span>` | `#ml-block` | `null` |
88
+ | フラグメントドキュメントのトップレベル | `#document` | `null` |
89
+
90
+ ```pug
91
+ //- Pug の例
92
+ div
93
+ if foo
94
+ span
95
+ //- syntacticalParentNode: #ml-block
96
+ //- parentNode: <div> (ブロックは透過的、スキップされる)
97
+ ```
98
+
99
+ ### `conditionalChildNodes()` -- 条件分岐パターン生成
100
+
101
+ テンプレートエンジンの条件分岐から可能なすべての子ノードの組み合わせを生成します。これは `permitted-contents` などのルールが、すべてのレンダリングパスに対してコンテンツモデルを検証するために使用されます。
102
+
103
+ #### アルゴリズム
104
+
105
+ 1. `childNodes` を順番に走査する
106
+ 2. `MLBlock` に遭遇した場合、その `blockBehavior?.type` から分岐 `mode` を決定する:
107
+ - `'if'` または `'if:elseif'` → mode `'if'`
108
+ - `'switch:case'` → mode `'switch'`
109
+ - `'if:else'`、`'each'`、`'each:empty'`、`'switch:default'`、`'await'`、`'await:catch'`、`'await:then'` → 現在のモードを継続(新しいモードは設定されない)
110
+ - その他の型 → スキップ(条件分岐ではない)
111
+ 3. ブロックに対して再帰的に `conditionalChildNodes()` を呼び出し、サブパターンを取得する
112
+ 4. 分岐を収集する。非ブロックの子に到達したら、現在の分岐グループを閉じる
113
+ 5. 空白のみのテキストノードはスキップされる
114
+ 6. `'if'`、`'switch'` モードの場合:「空の分岐」(何もレンダリングされないケース)を表す `null` センチネルが追加される
115
+ 7. 収集した分岐を `branchesToPatterns()` に渡し、すべての組み合わせの直積を生成する
116
+
117
+ #### 例
118
+
119
+ ```html
120
+ <ul>
121
+ {% if cond %}
122
+ <li>A</li>
123
+ {% else %}
124
+ <li>B</li>
125
+ {% endif %}
126
+ <li>C</li>
127
+ </ul>
128
+ ```
129
+
130
+ `ul.conditionalChildNodes()` は以下を返します:
131
+
132
+ - パターン 1: `[<li>A</li>, <li>C</li>]`
133
+ - パターン 2: `[<li>B</li>, <li>C</li>]`
134
+
135
+ `permitted-contents` ルールは**すべての**パターンを検査して妥当性を確認します。
136
+
137
+ ### `findSubsequentNodes(selector?)`
138
+
139
+ ドキュメント順でこのノードの後に出現するノードを収集します:
140
+
141
+ 1. `ownerMLDocument.nodeList` を反復する
142
+ 2. `endOffset <= this.endOffset` のノードをスキップする
143
+ 3. 子孫ノードをスキップする(`this.contains(node)` で判定)
144
+ 4. `selector` が指定されている場合:CSS セレクタにマッチする要素のみを含める
145
+ 5. `selector` が未指定の場合:すべての後続 `MLChildNode` インスタンス(Element, Text, Comment, Block)を含める
146
+
147
+ ### ルールプロパティ
148
+
149
+ | プロパティ | 型 | 説明 |
150
+ | ---------- | ------------------------- | ------------------------------------------------------- |
151
+ | `rules` | `Record<string, AnyRule>` | `RuleMapper` によってこのノードにマッピングされたルール |
152
+ | `rule` | `RuleInfo<T, O>` | 現在のルールのこのノードに対する解決済み設定 |
153
+
154
+ `rule` ゲッターは、現在評価中のルール(`document.currentRule.name` 経由)の設定を `rules` レコードから取得し、`optimizeOption()` で解決します。現在評価中のルールがない場合はエラーをスローします。
155
+
156
+ ### `is()` による型の絞り込み
157
+
158
+ `is()` メソッドは `this is NodeTypeOf<NType, T, O>` を返し、TypeScript の型の絞り込みを可能にします:
159
+
160
+ ```typescript
161
+ function processNode(node: MLNode<any, any>) {
162
+ if (node.is(node.ELEMENT_NODE)) {
163
+ // node は MLElement<any, any> に絞り込まれる
164
+ console.log(node.localName);
165
+ } else if (node.is(node.TEXT_NODE)) {
166
+ // node は MLText<any, any> に絞り込まれる
167
+ console.log(node.isWhitespace());
168
+ } else if (node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK)) {
169
+ // node は MLBlock<any, any> に絞り込まれる
170
+ console.log(node.blockBehavior?.type);
171
+ }
172
+ }
173
+ ```
174
+
175
+ ## MLParentNode
176
+
177
+ **ソース:** `src/ml-dom/node/parent-node.ts`
178
+
179
+ 子を持てるノード(`MLElement`、`MLDocument`、`MLDocumentFragment`)の抽象基底クラスです。DOM `ParentNode` ミックスインを実装します。
180
+
181
+ ### プロパティ
182
+
183
+ | プロパティ | 型 | 説明 |
184
+ | ------------------- | ----------------------------- | ------------------------------------ |
185
+ | `children` | `HTMLCollectionOf<MLElement>` | 要素のみの子ノード(キャッシュ済み) |
186
+ | `childElementCount` | `number` | 子要素の数 |
187
+ | `firstElementChild` | `MLElement \| null` | 最初の子要素 |
188
+ | `lastElementChild` | `MLElement \| null` | 最後の子要素 |
189
+
190
+ ### メソッド
191
+
192
+ | メソッド | シグネチャ | 説明 |
193
+ | ------------------ | ------------------------------------------------------------ | -------------------------------------------------------------------------- |
194
+ | `querySelector` | `querySelector(selectors: string): MLElement \| null` | CSS セレクタにマッチする最初の子孫要素 |
195
+ | `querySelectorAll` | `querySelectorAll(selectors: string): NodeListOf<MLElement>` | CSS セレクタにマッチするすべての子孫要素(セレクタ文字列ごとにキャッシュ) |
196
+
197
+ ### `_descendantsToArray(filter?)`
198
+
199
+ `syncWalk` を使ってツリーを再帰的に走査し、フィルタされた子孫の配列を返す protected メソッドです。`querySelectorAll` の内部で使用されます。