@markuplint/ml-core 5.0.0-rc.2 → 5.0.0-rc.5

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 (67) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +0 -5
  3. package/lib/cursor-offset.js +0 -3
  4. package/lib/fix-applier.js +3 -9
  5. package/lib/ml-core.d.ts +18 -2
  6. package/lib/ml-core.js +223 -61
  7. package/lib/ml-dom/helper/accname.d.ts +0 -8
  8. package/lib/ml-dom/helper/accname.js +7 -10
  9. package/lib/ml-dom/node/attr.js +3 -1
  10. package/lib/ml-dom/node/block.d.ts +6 -0
  11. package/lib/ml-dom/node/block.js +6 -0
  12. package/lib/ml-dom/node/child-node.d.ts +0 -9
  13. package/lib/ml-dom/node/child-node.js +0 -9
  14. package/lib/ml-dom/node/document.d.ts +20 -1
  15. package/lib/ml-dom/node/document.js +24 -13
  16. package/lib/ml-dom/node/element-close-tag.d.ts +12 -0
  17. package/lib/ml-dom/node/element-close-tag.js +12 -0
  18. package/lib/ml-dom/node/element.d.ts +22 -0
  19. package/lib/ml-dom/node/element.js +37 -11
  20. package/lib/ml-dom/node/node-store.d.ts +0 -3
  21. package/lib/ml-dom/node/node-store.js +0 -3
  22. package/lib/ml-dom/node/node.d.ts +34 -1
  23. package/lib/ml-dom/node/node.js +34 -16
  24. package/lib/ml-dom/node/parent-node.js +0 -6
  25. package/lib/ml-dom/node/rule-mapper.d.ts +8 -0
  26. package/lib/ml-dom/node/rule-mapper.js +8 -0
  27. package/lib/ml-rule/ml-rule.d.ts +19 -0
  28. package/lib/ml-rule/ml-rule.js +38 -7
  29. package/lib/ml-rule/types.d.ts +110 -1
  30. package/lib/ml-rule/types.js +28 -1
  31. package/lib/ruleset/index.d.ts +2 -1
  32. package/lib/ruleset/index.js +2 -1
  33. package/lib/test/index.js +1 -1
  34. package/lib/virtual-rule.d.ts +10 -0
  35. package/lib/virtual-rule.js +1 -24
  36. package/package.json +14 -14
  37. package/ARCHITECTURE.ja.md +0 -676
  38. package/ARCHITECTURE.md +0 -726
  39. package/SKILL.md +0 -61
  40. package/docs/linting-pipeline.ja.md +0 -307
  41. package/docs/linting-pipeline.md +0 -307
  42. package/docs/maintenance.ja.md +0 -210
  43. package/docs/maintenance.md +0 -210
  44. package/docs/ml-dom/attr.ja.md +0 -103
  45. package/docs/ml-dom/attr.md +0 -103
  46. package/docs/ml-dom/block.ja.md +0 -272
  47. package/docs/ml-dom/block.md +0 -272
  48. package/docs/ml-dom/document.ja.md +0 -134
  49. package/docs/ml-dom/document.md +0 -134
  50. package/docs/ml-dom/element.ja.md +0 -161
  51. package/docs/ml-dom/element.md +0 -161
  52. package/docs/ml-dom/helpers.ja.md +0 -203
  53. package/docs/ml-dom/helpers.md +0 -203
  54. package/docs/ml-dom/node.ja.md +0 -199
  55. package/docs/ml-dom/node.md +0 -199
  56. package/docs/ml-dom/others.ja.md +0 -120
  57. package/docs/ml-dom/others.md +0 -120
  58. package/docs/ml-dom/overview.ja.md +0 -102
  59. package/docs/ml-dom/overview.md +0 -102
  60. package/docs/ml-dom/pretender.ja.md +0 -269
  61. package/docs/ml-dom/pretender.md +0 -269
  62. package/docs/ml-dom/rule-mapping.ja.md +0 -371
  63. package/docs/ml-dom/rule-mapping.md +0 -371
  64. package/docs/ml-dom.ja.md +0 -18
  65. package/docs/ml-dom.md +0 -18
  66. package/docs/rule-system.ja.md +0 -287
  67. package/docs/rule-system.md +0 -287
@@ -1,676 +0,0 @@
1
- # @markuplint/ml-core
2
-
3
- ## 概要
4
-
5
- `@markuplint/ml-core` は markuplint のコアリンティングエンジンです。パースされた AST(`MLASTDocument`)を DOM ツリー(`MLDOM`)に変換し、設定されたルールをノードに適用して違反を収集します。パッケージは 3 つのサブシステムで構成されます:**MLDOM**(DOM 抽象化レイヤー)、**MLRule**(ルール実行フレームワーク)、**MLCore**(オーケストレーションエンジン)。
6
-
7
- ## ディレクトリ構造
8
-
9
- ```
10
- src/
11
- ├── index.ts — 公開 API の再エクスポート
12
- ├── ml-core.ts — MLCore エンジンクラス
13
- ├── types.ts — MLFabric, MLSchema 型定義
14
- ├── convert-ruleset.ts — Config → Ruleset 変換
15
- ├── debug.ts — デバッグログユーティリティ
16
- ├── violation-collector.ts — 複数ファイルの違反集約
17
- ├── ml-dom/
18
- │ ├── index.ts — MLDOM 公開エクスポート
19
- │ ├── node/
20
- │ │ ├── document.ts — MLDocument(ルートノード、ルールマッピング、pretender 初期化)
21
- │ │ ├── element.ts — MLElement(属性、セレクタ、名前空間)
22
- │ │ ├── node.ts — MLNode(全ノードの抽象基底クラス)
23
- │ │ ├── parent-node.ts — MLParentNode(querySelector, children)
24
- │ │ ├── character-data.ts — MLCharacterData(テキスト系の抽象基底)
25
- │ │ ├── text.ts — MLText
26
- │ │ ├── comment.ts — MLComment
27
- │ │ ├── attr.ts — MLAttr(属性トークン)
28
- │ │ ├── block.ts — MLBlock(プリプロセッサブロック)
29
- │ │ ├── document-fragment.ts — MLDocumentFragment
30
- │ │ ├── document-type.ts — MLDocumentType
31
- │ │ ├── element-close-tag.ts — MLElementCloseTag
32
- │ │ ├── rule-mapper.ts — RuleMapper(ルールセット → ノードマッピング)
33
- │ │ ├── types.ts — ノード型定数、AccessibilityProperties
34
- │ │ └── node-list.ts — NodeList/HTMLCollection ユーティリティ
35
- │ ├── token/
36
- │ │ └── token.ts — MLToken(位置情報付き基底トークン)
37
- │ ├── helper/
38
- │ │ ├── accname.ts — アクセシブル名の計算
39
- │ │ ├── create-node.ts — AST → MLDOM ノードファクトリ
40
- │ │ ├── walkers.ts — ツリー走査(同期/非同期ウォーカー)
41
- │ │ ├── get-indent.ts — インデント解析
42
- │ │ └── debug.ts — デバッグマップ生成
43
- │ └── manipulations/
44
- │ ├── child-node-methods.ts — ChildNode インターフェーススタブ
45
- │ └── get-children.ts — 要素の子要素抽出
46
- ├── virtual-rule.ts — Named nodeRule の展開(expandNamedNodeRules)
47
- ├── virtual-rule.spec.ts — 仮想ルールのユニットテスト
48
- ├── ml-rule/
49
- │ ├── ml-rule.ts — MLRule クラス(ルール実行)
50
- │ ├── ml-rule-context.ts — MLRuleContext(レポート収集)
51
- │ ├── rule-fixer.ts — RuleFixer(fix コールバック用の TextEdit ビルダー)
52
- │ ├── create-rule.ts — createRule ファクトリ
53
- │ ├── create-test-rule.ts — テスト用ルールファクトリ
54
- │ └── types.ts — RuleSeed, Checker 型
55
- ├── fix-applier.ts — applyFixes(重複検出付き TextEdit 適用エンジン)
56
- ├── ruleset/
57
- │ └── index.ts — Ruleset クラス(rules + nodeRules + childNodeRules)
58
- ├── plugin/
59
- │ ├── plugin.ts — createPlugin ファクトリ
60
- │ ├── types.ts — Plugin, PluginCreator 型
61
- │ └── index.ts — Plugin エクスポート
62
- ├── test/
63
- │ └── index.ts — createTestDocument, createTestElement, dummySchemas
64
- └── utils/
65
- ├── index.ts — ユーティリティエクスポート
66
- ├── get-location-from-chars.ts — 文字位置解決
67
- └── string-splice.ts — 文字列スプライスヘルパー
68
- ```
69
-
70
- ## アーキテクチャ図
71
-
72
- ```mermaid
73
- flowchart TD
74
- subgraph upstream ["上流依存パッケージ"]
75
- mlAst["@markuplint/ml-ast\n(AST 型)"]
76
- mlConfig["@markuplint/ml-config\n(Config, RuleConfigValue)"]
77
- mlSpec["@markuplint/ml-spec\n(HTML/ARIA 仕様)"]
78
- htmlSpec["@markuplint/html-spec\n(デフォルト仕様データ)"]
79
- htmlParser["@markuplint/html-parser\n(デフォルトパーサー)"]
80
- parserUtils["@markuplint/parser-utils\n(ParserOptions)"]
81
- selector["@markuplint/selector\n(CSS セレクタマッチング)"]
82
- i18n["@markuplint/i18n\n(ロケール、翻訳)"]
83
- shared["@markuplint/shared\n(ユーティリティ)"]
84
- configPresets["@markuplint/config-presets\n(組み込みプリセット)"]
85
- end
86
-
87
- subgraph pkg ["@markuplint/ml-core"]
88
- subgraph mldom ["MLDOM"]
89
- document["MLDocument"]
90
- element["MLElement"]
91
- node["MLNode / MLToken"]
92
- ruleMapper["RuleMapper"]
93
- end
94
-
95
- subgraph mlRule ["MLRule"]
96
- rule["MLRule"]
97
- ruleContext["MLRuleContext"]
98
- createRule["createRule()"]
99
- end
100
-
101
- subgraph engine ["エンジン"]
102
- core["MLCore"]
103
- ruleset["Ruleset"]
104
- convertRuleset["convertRuleset()"]
105
- end
106
-
107
- subgraph extras ["その他"]
108
- plugin["Plugin / createPlugin()"]
109
- testUtils["テストユーティリティ"]
110
- end
111
- end
112
-
113
- subgraph downstream ["下流"]
114
- rules["@markuplint/rules\n(組み込みルール)"]
115
- markuplint["markuplint\n(CLI & API)"]
116
- end
117
-
118
- upstream -->|"型、パース、仕様"| pkg
119
- core --> document
120
- core --> rule
121
- document --> ruleMapper
122
- rule --> ruleContext
123
- pkg -->|"MLDOM, MLRule, MLCore"| downstream
124
- ```
125
-
126
- ## リンティングパイプライン
127
-
128
- `MLCore.verify()` メソッドがリンティング全体を制御します:
129
-
130
- ```mermaid
131
- flowchart LR
132
- A["MLCore\nコンストラクタ"]
133
- B["_parse()\nParser → MLASTDocument"]
134
- C["_createDocument()\nMLASTDocument → MLDocument"]
135
- D["verify(fix?)\n各ルールに対して:"]
136
- E["document.setRule(rule)\nRuleMapper で設定をノードにマッピング"]
137
- F["rule.verify(document)\nMLRuleContext で報告を収集"]
138
- G["Violations[]"]
139
-
140
- A --> B --> C --> D --> E --> F --> G
141
- ```
142
-
143
- ### ステップごとの説明
144
-
145
- 1. **パース**: `MLCore` は設定されたパーサー(`MLParser`)を呼び出し、`MLASTDocument` を生成
146
- 2. **ドキュメント作成**: AST を `MLDocument` でラップし、`createNode()` ファクトリで MLDOM ツリー全体を構築。`RuleMapper` が各ノードのルール設定を解決
147
- 3. **検証**: 各 `MLRule` に対して、`document.setRule(rule)` を呼び出した後 `rule.verify(document)` を実行。ルールは `document.walkOn()` で対象ノードを走査し、`MLRuleContext` を通じて違反を報告。ルールは report にインライン `fix` コールバックを付与して `TextEdit` オブジェクトを返す
148
- 4. **修正**(オプション): `fix=true` の場合、report の fix コールバックが `RuleFixer` を使って `TextEdit[]` を生成。`FixApplier.applyFixes(sourceCode, fixes)` が全編集をソーステキストに一括適用(重複検出付き)
149
-
150
- ## MLDOM クラス階層
151
-
152
- ```
153
- MLToken<A extends MLASTToken>
154
- └── MLNode<T, O, A extends MLASTNode>
155
- ├── MLAttr<T, O>
156
- ├── MLCharacterData<T, O, A> (abstract)
157
- │ ├── MLText<T, O>
158
- │ └── MLComment<T, O>
159
- ├── MLDocumentType<T, O>
160
- ├── MLBlock<T, O>
161
- ├── MLElementCloseTag<T, O>
162
- └── MLParentNode<T, O, A> (abstract)
163
- ├── MLElement<T, O>
164
- ├── MLDocumentFragment<T, O>
165
- └── MLDocument<T, O>
166
- ```
167
-
168
- ### クラスの責務
169
-
170
- | クラス | DOM インターフェース | 主な責務 |
171
- | -------------------- | -------------------- | ------------------------------------------------------------------------------------ |
172
- | `MLToken` | — | 位置情報付き基底トークン(`startLine`, `endCol`, `raw`, `fixed`)、`fix()` メソッド |
173
- | `MLNode` | `Node` | ツリー構造(`parentNode`, `childNodes`, `nextSibling`)、ルール格納、`is()` 型ガード |
174
- | `MLAttr` | `Attr` | 属性名・値トークン、`isDynamicValue`, `isDirective`, `valueType`, `tokenList` |
175
- | `MLCharacterData` | `CharacterData` | テキスト内容ノードの抽象基底(`data`, `nodeValue`) |
176
- | `MLText` | `Text` | テキストノード、`isWhitespace()`, `isRawTextElementContent()` |
177
- | `MLComment` | `Comment` | コメントノード(`textContent`) |
178
- | `MLDocumentType` | `DocumentType` | `<!DOCTYPE>`(`name`, `publicId`, `systemId`) |
179
- | `MLBlock` | — | プリプロセッサ固有ブロック(if/each/switch)、`blockBehavior`, `isTransparent` |
180
- | `MLElementCloseTag` | — | 開始タグ要素とペアになる閉じタグ |
181
- | `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
182
- | `MLElement` | `Element` | 属性、セレクタ、名前空間、pretender コンテキスト、`elementType`, `closeTag` |
183
- | `MLDocumentFragment` | `DocumentFragment` | フラグメントルートノード |
184
- | `MLDocument` | `Document` | ルートノード、`nodeList`, `walkOn()`, `setRule()`, ルールマッピング、仕様アクセス |
185
-
186
- ## MLDocument
187
-
188
- `MLDocument` は MLDOM ツリーのルートであり、ルール実行の主要インターフェースです。
189
-
190
- ### コンストラクション
191
-
192
- コンストラクタは `MLASTDocument`、`Ruleset`、`MLSchema` タプルを受け取ります。処理内容:
193
-
194
- 1. AST を走査し、各 AST ノードに対して `createNode()` を呼び出してフラットな `nodeList` を構築
195
- 2. `RuleMapper` を初期化して各ノードにルール設定を配布
196
- 3. pretender 定義が提供されている場合、pretender コンテキストをセットアップ
197
-
198
- ### 主要プロパティ
199
-
200
- | プロパティ | 型 | 説明 |
201
- | ------------- | ----------------------- | ------------------------------------------------------- |
202
- | `nodeList` | `ReadonlyArray<MLNode>` | ドキュメント順の全ノードのフラットリスト |
203
- | `specs` | `MLMLSpec` | HTML/ARIA 仕様データ |
204
- | `isFragment` | `boolean` | ドキュメントがフラグメントかどうか |
205
- | `currentRule` | `MLRule \| null` | 現在評価中のルール |
206
- | `endTag` | `EndTagType` | 終了タグ処理モード(`'xml'`, `'omittable'`, `'never'`) |
207
-
208
- ### 主要メソッド
209
-
210
- | メソッド | 説明 |
211
- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
212
- | `walkOn(type, walker)` | 指定した型(`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`)のノードを走査 |
213
- | `setRule(rule)` | 現在のルールを設定(検証時に `MLCore` が使用) |
214
- | `searchNodeByLocation(line, col)` | 指定したソース位置のノードを検索 |
215
- | `getAccessibilityProp(node)` | ARIA アクセシビリティプロパティを計算(`MLElement.getAccessibleName()` のキャッシュ経由でアクセシブルネームを取得) |
216
- | `toString()` | ドキュメントの生のソースコードを返す |
217
-
218
- ## MLElement
219
-
220
- `MLElement` は HTML/SVG/MathML 要素を表し、属性アクセスとセレクタマッチングを完全にサポートします。
221
-
222
- ### 主要プロパティ
223
-
224
- | プロパティ | 型 | 説明 |
225
- | ------------------ | --------------------------- | ------------------------------------------------ |
226
- | `localName` | `string` | 小文字のタグ名(HTML の場合) |
227
- | `namespaceURI` | `NamespaceURI` | 要素の名前空間(HTML, SVG, MathML) |
228
- | `attributes` | `MLNamedNodeMap` | 名前付き属性コレクション |
229
- | `elementType` | `ElementType` | `'html'`, `'web-component'`, または `'authored'` |
230
- | `closeTag` | `MLElementCloseTag \| null` | ペアの閉じタグ |
231
- | `pretenderContext` | `PretenderContext \| null` | pretender マッピングコンテキスト |
232
- | `isForeignElement` | `boolean` | SVG/MathML 要素の場合 `true` |
233
- | `isOmitted` | `boolean` | 暗黙的に挿入された要素の場合 `true` |
234
-
235
- ### 主要メソッド
236
-
237
- | メソッド | 説明 |
238
- | ---------------------------- | ------------------------------------------------------------------- |
239
- | `getAttribute(name)` | 属性値または `null` を返す |
240
- | `getAttributeToken(name)` | 名前付き属性の `MLAttr[]` を返す |
241
- | `hasAttribute(name)` | 属性の存在を確認 |
242
- | `getAccessibleName(version)` | キャッシュ付きアクセシブルネーム計算(ARIA バージョンごとにメモ化) |
243
- | `matches(selector)` | CSS セレクタマッチング |
244
- | `matchMLSelector(selector)` | 拡張 markuplint セレクタマッチング(`RegexSelector` サポート) |
245
- | `querySelector(selector)` | 最初にマッチする子孫を検索 |
246
- | `querySelectorAll(selector)` | マッチするすべての子孫を検索 |
247
-
248
- ## ルールシステム
249
-
250
- ### MLRule
251
-
252
- `MLRule<T, O>` はリンティングルールを検証およびオプションの修正ロジックとともにカプセル化します。
253
-
254
- | プロパティ/メソッド | 説明 |
255
- | --------------------------------- | ------------------------------------------------------------------------------- |
256
- | `name` | ルール識別子(例:`"attr-duplication"`) |
257
- | `defaultSeverity` | デフォルトの重大度レベル |
258
- | `defaultValue` / `defaultOptions` | デフォルト設定 |
259
- | `baseRuleId` | 仮想ルールの場合: ベースルール名(例:`"required-attr"`) |
260
- | `groupName` | 複数エントリ仮想ルールの場合: 一括無効化用のグループ名 |
261
- | `specConformance` | 仮想ルールの場合: `'normative'` または `'non-normative'`(named nodeRule 由来) |
262
- | `verify(document, locale, fix)` | ルールを実行して違反を返す |
263
- | `createAlias(name, options?)` | このルールの verify/fix ロジックを再利用する仮想ルールを作成 |
264
- | `optimizeOption(settings)` | 生のルール設定を `RuleInfo` に正規化 |
265
-
266
- ### RuleSeed
267
-
268
- `RuleSeed<T, O>` 型はルールの実装を定義します:
269
-
270
- ```typescript
271
- type RuleSeed<T, O> = {
272
- meta?: {
273
- category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
274
- };
275
- defaultSeverity?: Severity;
276
- defaultValue?: T;
277
- defaultOptions?: O;
278
- verify(context): void | Promise<void>;
279
- fix?(context): void | Promise<void>;
280
- };
281
- ```
282
-
283
- ### createRule
284
-
285
- `createRule(seed)` は型安全なルールシード作成のためのファクトリ関数です。シードをそのまま返し、主に型ヘルパーとして機能します。
286
-
287
- ### MLRuleContext
288
-
289
- `MLRuleContext<T, O>` はルールの実行コンテキストを提供します:
290
-
291
- - `document` — 現在の `MLDocument`
292
- - `translate` / `t` — ロケール対応のメッセージ翻訳
293
- - `report(report)` — ノード、メッセージ、オプションの修正とともに違反を報告
294
-
295
- `provide()` メソッドは `RuleSeed.verify()` に渡されるコンテキストオブジェクトを返します。自動修正ロジックは、個々の `report()` 呼び出しのインライン `fix` コールバックとして提供され、独立したライフサイクルメソッドではありません。
296
-
297
- ### ルール設定の解決
298
-
299
- ルールは `RuleMapper` によって 3 つのレベルで設定されます:
300
-
301
- 1. **グローバルルール**(`rules`)— すべてのノードに適用。最低優先度
302
- 2. **ノードルール**(`nodeRules`)— セレクタにマッチするノードに適用。中優先度
303
- 3. **子ノードルール**(`childNodeRules`)— セレクタにマッチするノードの子に適用。最高優先度
304
-
305
- 複数のルールがマッチする場合、`RuleMapper` は CSS セレクタの詳細度を使って競合を解決します。マッピングは `MLDocument` の構築時に一度計算され、各 `MLNode.rules` に格納されます。
306
-
307
- ### ルール実行フロー
308
-
309
- ```mermaid
310
- flowchart TD
311
- A["MLCore.verify()"] --> B["各 MLRule に対して"]
312
- B --> C["document.setRule(rule)"]
313
- C --> D["rule.verify(document, locale, fix)"]
314
- D --> E["rule.getRuleInfo(ruleset)\nグローバル設定を解決"]
315
- E --> F["document.walkOn(type, walker)\nマッチするノードを走査"]
316
- F --> G["context.report()\nノードごとに違反を収集"]
317
- G --> H["Violation[] を返す"]
318
- ```
319
-
320
- ### 仮想ルールシステム
321
-
322
- ソース: `src/virtual-rule.ts`
323
-
324
- > **用語ポリシー**: 「仮想ルール (virtual rule)」は**コントリビューター向けの内部実装用語**です。ユーザー向けドキュメント(ウェブサイト、移行ガイド、README)では**「named rule」**を使用すること。設定ユーザーの視点では、**ベースルール**(例: `required-attr`)と **named rule**(例: `a11y/html-lang`)の2つの概念だけで十分です。`MLRule` のエイリアス機構という内部メカニズムを公開してはなりません。
325
-
326
- 仮想ルールは、**名前付き nodeRules** — `/` を含む `name` プロパティを持つ nodeRule エントリ(例: `"a11y/html-lang"`)— から作成される独立した `MLRule` インスタンスです。これによりチェック単位の制御が可能になります: 各仮想ルールは `rules["alias/name"]: false` で個別に有効/無効化できます。
327
-
328
- #### Named NodeRule の展開
329
-
330
- `expandNamedNodeRules()` は `MLCore` の構築時に named nodeRules(および childNodeRules)を仮想ルールに変換します:
331
-
332
- ```
333
- Named nodeRule(設定) 仮想 MLRule(ランタイム)
334
- ┌─────────────────────────┐ ┌──────────────────────────┐
335
- │ name: "a11y/html-lang" │ │ name: "a11y/html-lang" │
336
- │ specConformance: "norm."│ ──────► │ baseRuleId: "required-attr" │
337
- │ selector: ":where(html)"│ │ specConformance: メタデータ│
338
- │ rules: │ │ verify/fix: ベースから │
339
- │ required-attr: [lang] │ └──────────────────────────┘
340
- └─────────────────────────┘
341
- ```
342
-
343
- 主要な動作:
344
-
345
- - **false エントリの分離**: `rules` 内の `false` エントリは自動的に無名 nodeRule に分離され、ベースルールの specificity override としてのセマンティクスが維持される
346
- - **複数エントリサポート**: 非 false エントリが 2 つ以上の named nodeRule は派生名(`name/baseRuleName`)と `groupName` で作成され、グループ一括無効化が可能
347
- - **メタデータ**: `specConformance` は下流ツールやレポート向けのメタデータとして仮想ルールに付与される
348
- - **ホットリロード**: 展開前の nodeRules は `#originalNodeRules` / `#originalChildNodeRules` に保持され、`update()` で再展開可能
349
-
350
- #### なぜ `specConformance` は Named NodeRule 専用なのか
351
-
352
- `specConformance` は意図的に **named nodeRule(プリセット内)でのみ**使用可能であり、通常の組み込みルールでは使用できません。設計根拠は以下の通りです:
353
-
354
- 1. **組み込みルールは既に正しいデフォルト重大度を持っている。** `permitted-contents` や `required-attr` のようなルールは本質的に normative(WHATWG の MUST 要件を強制する)であり、`defaultSeverity` は既に `'error'` に設定されています。別途 `specConformance` フラグは不要です — 重大度は組み込み済みです。
355
-
356
- 2. **Named nodeRules はプリセット作成者による仕様解釈である。** `preset.html-standard.jsonc` のようなプリセットが `"html-standard/head-charset-utf8"` という named nodeRule を作成する場合、プリセット作成者は特定の仕様要件をチェックとして表現しています。`specConformance` により、その要件の RFC 2119 キーワード強度を宣言でき、下流のツールやレポートが違反の仕様由来の分類を識別できます。
357
-
358
- 3. **ユーザーは自分のルールに `specConformance` を設定すべきではない。** カスタムコンポーネント(例: `<MyComponent>` の props 検証)に対するユーザー定義の nodeRule は仕様準拠チェックではなく、プロジェクトの規約です。任意のユーザー設定で `specConformance` を許可すると、「HTML 仕様がこれを要求している」と「チームがこれを好む」の区別が曖昧になります。`name` プロパティ(`/` を含む必要あり)がゲートキーパーとして機能します:named nodeRule のみが `specConformance` を持てる設計であり、named nodeRule は仕様を理解するプリセット作成者向けに設計されています。
359
-
360
- まとめ: `specConformance` は仕様由来のチェックを識別するメタデータを提供する**プリセットレベルのアノテーション**です。組み込みルールは `defaultSeverity` で独自に重大度を管理します。ユーザー定義ルールはルール設定の `severity` フィールドで直接重大度を表現します。
361
-
362
- #### 仮想ルールの無効化
363
-
364
- 仮想ルールは `rules` 設定で3つのレベルで無効化できます:
365
-
366
- 1. **完全一致**: `rules["a11y/html-lang"]: false`
367
- 2. **グループ無効化**: `rules["custom/multi"]: false`(複数エントリの named nodeRule 用)
368
- 3. **名前空間ワイルドカード**: `rules["a11y/*"]: false`(`a11y/` で始まるすべての仮想ルールを無効化)
369
-
370
- ## 自動修正(Autofix)システム
371
-
372
- 自動修正システムは、ルールが違反に対する自動修正を提供する仕組みです。**RuleFixer**(TextEdit ビルダー)、**fix コールバック**(ルール作者が記述するロジック)、**FixApplier**(編集適用エンジン)の 3 つのコンポーネントで構成されます。
373
-
374
- ### 自動修正のデータフロー
375
-
376
- ```mermaid
377
- flowchart LR
378
- subgraph RulePhase ["ルールフェーズ"]
379
- report["context.report({\n message,\n scope,\n fix: コールバック\n})"]
380
- end
381
-
382
- subgraph FixPhase ["Fix コールバック実行"]
383
- callback["fix(fixer) → TextEdit[]"]
384
- fixer["RuleFixer\n(共有インスタンス)"]
385
- callback --> fixer
386
- end
387
-
388
- subgraph ApplyPhase ["適用フェーズ"]
389
- fixdata["FixData\n{ edits: TextEdit[] }"]
390
- applier["applyFixes(\n sourceCode,\n allFixes\n)"]
391
- output["fixedCode"]
392
- fixdata --> applier --> output
393
- end
394
-
395
- report --> callback
396
- fixer --> fixdata
397
- ```
398
-
399
- ### Fix コールバックの動作
400
-
401
- ルールは各 `report()` 呼び出しにオプションの `fix` コールバックを付与します。このコールバックはルール検証中には**実行されず**、保存されるだけです。`MLCore.verify()` に `fix=true` が渡された場合にのみ呼び出されます。
402
-
403
- ```mermaid
404
- sequenceDiagram
405
- participant Rule as ルール (verify)
406
- participant Ctx as MLRuleContext
407
- participant MLR as MLRule.verify()
408
- participant Fixer as RuleFixer
409
- participant Core as MLCore.verify()
410
- participant FA as applyFixes()
411
-
412
- Rule->>Ctx: report({ scope, message, fix })
413
- Note over Ctx: fix コールバック付きレポートを格納
414
-
415
- MLR->>Ctx: context.reports
416
- loop fix コールバックを持つ各レポート
417
- MLR->>Fixer: report.fix(sharedFixer)
418
- Fixer-->>MLR: TextEdit | TextEdit[]
419
- MLR->>MLR: FixData { edits } としてラップ
420
- end
421
- MLR-->>Core: Violation[](FixData 付き)
422
-
423
- Core->>Core: 全 Violation から FixData を収集
424
- Core->>FA: applyFixes(sourceCode, allFixes)
425
- FA-->>Core: FixResult { output, applied, skipped }
426
- ```
427
-
428
- ### RuleFixer API
429
-
430
- `RuleFixer` は `IRuleFixer`(`@markuplint/ml-config` で定義)を実装します。**ステートレス**なヘルパーであり、全ルールで 1 つのインスタンスを共有します。各メソッドはソースコードのレンジ置換を記述する `TextEdit` オブジェクトを生成します。
431
-
432
- | メソッド | 入力 | 生成される TextEdit |
433
- | --------------------------- | ------------------------------------ | --------------------------------------- |
434
- | `replaceText(token, text)` | `startOffset` + `raw` を持つトークン | `range: [start, start+len], text` |
435
- | `replaceRange(range, text)` | 明示的な `[start, end)` レンジ | `range: [start, end], text` |
436
- | `insertBefore(token, text)` | `startOffset` を持つトークン | `range: [start, start], text`(ゼロ幅) |
437
- | `insertAfter(token, text)` | `startOffset` + `raw` を持つトークン | `range: [end, end], text`(ゼロ幅) |
438
- | `remove(token)` | `startOffset` + `raw` を持つトークン | `range: [start, start+len], text: ""` |
439
- | `removeRange(range)` | 明示的な `[start, end)` レンジ | `range: [start, end], text: ""` |
440
-
441
- `token` パラメータは `FixToken` 型(`@markuplint/ml-config` で定義)を満たす任意のオブジェクト — つまり `{ startOffset: number; raw: string }` — を受け付けます。MLDOM トークン(`MLToken`, `MLAttr` 等)は自然にこの要件を満たします。
442
-
443
- ### FixApplier アルゴリズム
444
-
445
- `applyFixes()`(`fix-applier.ts`)は全ルールの `FixData` をマージし、1 パスで適用します:
446
-
447
- ```mermaid
448
- flowchart TD
449
- A["展開: FixData[] → タグ付き TextEdit[]"]
450
- B["ソート: range start 昇順、\nthen range end 降順"]
451
- C["逐次適用:\n各 edit の重複をチェック"]
452
- D{{"edit.start < lastAppliedEnd?"}}
453
- E["スキップ\n(親 FixData をスキップとしてマーク)"]
454
- F["適用\n(出力にスプライス)"]
455
- G["分類: 各 FixData を\napplied または skipped に"]
456
- H["FixResult を返す\n{ output, applied, skipped }"]
457
-
458
- A --> B --> C --> D
459
- D -- Yes --> E --> C
460
- D -- No --> F --> C
461
- C -. "全 edit 処理完了" .-> G --> H
462
- ```
463
-
464
- 主要な制約:
465
-
466
- - 1 つの `FixData` 内の edit は互いに重複してはならない
467
- - `FixData` 間の重複はスキップ機構で処理される
468
- - `FixData` 内のいずれかの edit がスキップされると、その `FixData` 全体がスキップとして分類される
469
-
470
- ### マルチパス Fix ループ
471
-
472
- `applyFixes()` がレンジの重複により一部の fix をスキップした場合、エンジンはマルチパスループ(`_multiPassFix()`)に入り、再パース・再検証を繰り返して修正可能な違反をすべて解決します:
473
-
474
- ```mermaid
475
- flowchart TD
476
- A["violations から fix を抽出"] --> B["applyFixes(code, fixes)"]
477
- B --> C{"applied.length === 0?"}
478
- C -- Yes --> Z["現在のコードを返す"]
479
- C -- No --> D{"output === currentCode?"}
480
- D -- Yes --> Z
481
- D -- No --> E{"サイクル検出?\n(2パス前の出力と一致)"}
482
- E -- Yes --> Z
483
- E -- No --> F{"skipped.length === 0?"}
484
- F -- Yes --> Z["修正済みコードを返す\n(全 fix 適用完了)"]
485
- F -- No --> G["再パース + 再検証"]
486
- G --> H{"ParserError?"}
487
- H -- Yes --> Z["最後の正常なコードに戻す"]
488
- H -- No --> I{"新たな修正可能な違反?"}
489
- I -- No --> Z
490
- I -- Yes --> B
491
- ```
492
-
493
- 主な設計ポイント:
494
-
495
- - **ゼロコストパス**: fix を持つ violation がなければ、マルチパスループは完全にスキップされる
496
- - **シングルパス高速パス**: `skipped.length === 0` のとき即座にループを抜ける(Phase 1 と同等の動作)
497
- - **サイクル検出**: 2パス前の出力と比較し、A→B→A の振動パターンを検出
498
- - **安全上限**: 最大10パス(ESLint の `SourceCodeFixer` と同じ)
499
- - **状態復元**: `verify()` は `try/finally` で `#sourceCode`、`#ast`、`#document` を保存・復元
500
-
501
- **重要**: `VerifyResult` の `violations` 配列は初回パスの結果のみを反映し、`fixedCode` は複数パスの結果である場合があります。修正後コードの正確な違反リストが必要な場合は、出力を再検証してください。
502
-
503
- ### 実例: ルールの Fix 実装
504
-
505
- ```typescript
506
- // ルールの verify 関数内:
507
- context.report({
508
- scope: node,
509
- message: '属性値にはダブルクォートを使用してください',
510
- fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
511
- });
512
- ```
513
-
514
- これにより以下の処理が行われます:
515
-
516
- 1. **レポート** → `MLRuleContext` に格納
517
- 2. **Fix コールバック** → `(fixer) => fixer.replaceText(token, text)`(まだ呼び出されない)
518
- 3. **`fix=true` の場合** → 共有 `RuleFixer` でコールバック実行 → `TextEdit` を返す
519
- 4. **TextEdit** → `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }` としてラップ
520
- 5. **applyFixes** → ソースコードに置換を適用
521
-
522
- ## Pretender システム
523
-
524
- pretender システムにより、コンポーネントをリンティング時にセマンティック HTML 要素として扱うことができます。これにより、ルールがカスタムコンポーネント(例:`<MyButton>`)を標準要素(例:`<button>`)として検証できます。
525
-
526
- ### 設定
527
-
528
- pretender は markuplint 設定で `Pretender` オブジェクトの配列として定義されます:
529
-
530
- ```typescript
531
- type Pretender = {
532
- selector: string; // コンポーネントにマッチする CSS セレクタ
533
- as: string; // 偽装する HTML 要素
534
- aria?: PretenderARIA; // オプションの ARIA オーバーライド
535
- };
536
- ```
537
-
538
- ### 動作の仕組み
539
-
540
- 1. `MLDocument` の構築時に pretender 定義が処理される
541
- 2. pretender セレクタにマッチする各 `MLElement` は `type: 'pretender'` の `pretenderContext` を取得
542
- 3. 対象の HTML 要素は `type: 'origin'` の `pretenderContext` を取得
543
- 4. ルールは `element.pretenderContext` にアクセスしてセマンティックマッピングを確認可能
544
- 5. アクセシビリティ計算はロール/名前の解決に pretender コンテキストを使用
545
-
546
- ## 条件付き子ノード
547
-
548
- テンプレートエンジン(Pug, EJS, Nunjucks など)はプリプロセッサ固有のブロックを生成し、`MLBlock` ノードで表現されます。これらのブロックは子ノードを条件付きでラップできます:
549
-
550
- | `blockBehavior.type` | テンプレート構文 | 説明 |
551
- | -------------------- | ----------------- | ------------------ |
552
- | `'if'` | `{% if %}` | 条件ブロックの開始 |
553
- | `'if:else'` | `{% else %}` | 代替分岐 |
554
- | `'end'` | `{% endif %}` | 条件ブロックの終了 |
555
- | `'each'` | `{% for %}` | ループの開始 |
556
- | `'end'` | `{% endfor %}` | ループの終了 |
557
- | `'switch:case'` | `{% switch %}` | switch の開始 |
558
- | `'switch:default'` | `{% case %}` | switch ケース |
559
- | `'end'` | `{% endswitch %}` | switch の終了 |
560
-
561
- `MLNode.conditionalChildNodes()` は `NodeListOf` 配列の配列を返します(条件分岐ごとに 1 つ)。これにより、ルールは各分岐を独立して分析できます。
562
-
563
- ## プラグインシステム
564
-
565
- プラグインはカスタムルールと共有設定で markuplint を拡張します。
566
-
567
- ### Plugin 型
568
-
569
- ```typescript
570
- type Plugin = {
571
- readonly name: string;
572
- readonly rules?: Record<string, RuleSeed<any, any>>;
573
- readonly configs?: Record<string, Config>;
574
- };
575
- ```
576
-
577
- ### PluginCreator
578
-
579
- 設定を受け付けるプラグイン用:
580
-
581
- ```typescript
582
- type PluginCreator<S> = {
583
- readonly name: string;
584
- create(setting: S): Omit<Plugin, 'name'>;
585
- };
586
- ```
587
-
588
- `createPlugin(creator)` は型安全なプラグインクリエーター定義のためのファクトリ関数です。
589
-
590
- ## テストユーティリティ
591
-
592
- `test/` モジュールはルールテスト用のヘルパーを提供します:
593
-
594
- | 関数 | 説明 |
595
- | ------------------------------------------- | ------------------------------------------------ |
596
- | `createTestDocument(sourceCode, options?)` | ソースをテスト用 `MLDocument` にパース |
597
- | `createTestElement(sourceCode, options?)` | ソースをパースして最初の `MLElement` を返す |
598
- | `createTestNodeList(sourceCode, options?)` | パースされたソースのフラットノードリストを返す |
599
- | `createTestTokenList(sourceCode, options?)` | パースされたソースのフラットトークンリストを返す |
600
- | `dummySchemas()` | デフォルト HTML 仕様をスキーマタプルとして返す |
601
-
602
- `CreateTestOptions` は `config`, `parser`, `specs`, `pretenders` のオーバーライドを受け付けます。
603
-
604
- ## 外部依存パッケージ
605
-
606
- | 依存パッケージ | 用途 |
607
- | ---------------------------- | ------------------------------------------------------ |
608
- | `@markuplint/ml-ast` | AST 型定義(`MLASTDocument`, `MLASTNode` など) |
609
- | `@markuplint/ml-config` | 設定型(`Config`, `RuleConfigValue`, `Pretender`) |
610
- | `@markuplint/ml-spec` | HTML/ARIA 仕様アクセス(`MLMLSpec`, ロール/属性仕様) |
611
- | `@markuplint/html-spec` | デフォルト HTML 仕様データ |
612
- | `@markuplint/html-parser` | デフォルト HTML パーサー(テストユーティリティで使用) |
613
- | `@markuplint/parser-utils` | パーサーオプションと型 |
614
- | `@markuplint/selector` | CSS および拡張セレクタマッチング |
615
- | `@markuplint/i18n` | 国際化(`LocaleSet`, `Translator`) |
616
- | `@markuplint/shared` | 共有ユーティリティ |
617
- | `@markuplint/config-presets` | 組み込み設定プリセット |
618
- | `debug` | デバッグログ |
619
- | `is-plain-object` | プレーンオブジェクト型チェック |
620
- | `type-fest` | TypeScript ユーティリティ型 |
621
-
622
- ## 統合ポイント
623
-
624
- ```mermaid
625
- flowchart TD
626
- subgraph upstream ["上流"]
627
- mlAst["@markuplint/ml-ast"]
628
- mlConfig["@markuplint/ml-config"]
629
- mlSpec["@markuplint/ml-spec"]
630
- htmlSpec["@markuplint/html-spec"]
631
- htmlParser["@markuplint/html-parser"]
632
- parserUtils["@markuplint/parser-utils"]
633
- selector["@markuplint/selector"]
634
- i18n["@markuplint/i18n"]
635
- shared["@markuplint/shared"]
636
- configPresets["@markuplint/config-presets"]
637
- end
638
-
639
- subgraph pkg ["@markuplint/ml-core"]
640
- core["MLCore エンジン"]
641
- end
642
-
643
- subgraph downstream ["下流"]
644
- rules["@markuplint/rules\n(組み込みルール実装)"]
645
- markuplint["markuplint\n(CLI, API, MLEngine)"]
646
- end
647
-
648
- upstream -->|"型、パース、仕様、i18n"| core
649
- core -->|"MLDOM クラス, MLRule,\ncreateRule, テストユーティリティ"| rules
650
- core -->|"MLCore, ViolationCollector,\nconvertRuleset, Plugin 型"| markuplint
651
- ```
652
-
653
- ### 上流
654
-
655
- - **`@markuplint/ml-ast`** — MLDOM ツリー構築に使用される AST 型
656
- - **`@markuplint/ml-config`** — 設定およびルール設定型
657
- - **`@markuplint/ml-spec`** — 要素検証、ロール計算用の HTML/ARIA 仕様
658
- - **`@markuplint/html-spec`** — デフォルト仕様データバンドル
659
- - **`@markuplint/html-parser`** — テストユーティリティで使用されるデフォルトパーサー
660
- - **`@markuplint/parser-utils`** — パーサーオプション型
661
- - **`@markuplint/selector`** — `querySelector`, `matches`, `RegexSelector` 用の CSS セレクタエンジン
662
- - **`@markuplint/i18n`** — ルールメッセージ用のロケールセットと翻訳
663
- - **`@markuplint/shared`** — 共有ユーティリティ関数
664
- - **`@markuplint/config-presets`** — 組み込み設定プリセット
665
-
666
- ### 下流
667
-
668
- - **`@markuplint/rules`** — 組み込みルール実装のために MLDOM クラス、`createRule`, `MLRuleContext`, テストユーティリティをインポート
669
- - **`markuplint`** — CLI と API を提供するために `MLCore`, `ViolationCollector`, `convertRuleset`, プラグイン型をインポート
670
-
671
- ## ドキュメントマップ
672
-
673
- - [MLDOM リファレンス](docs/ml-dom.ja.md) ([English](docs/ml-dom.md)) — クラス階層、ノードプロパティ、ツリー走査
674
- - [ルールシステム](docs/rule-system.ja.md) ([English](docs/rule-system.md)) — MLRule、RuleSeed、MLRuleContext、設定解決
675
- - [リンティングパイプライン](docs/linting-pipeline.ja.md) ([English](docs/linting-pipeline.md)) — MLCore エンジン、verify フロー、pretender、プラグインシステム
676
- - [メンテナンスガイド](docs/maintenance.ja.md) ([English](docs/maintenance.md)) — コマンド、レシピ、トラブルシューティング