@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.
- package/ARCHITECTURE.ja.md +524 -0
- package/ARCHITECTURE.md +524 -0
- package/CHANGELOG.md +52 -2
- package/README.md +5 -0
- package/SKILL.md +61 -0
- package/docs/linting-pipeline.ja.md +307 -0
- package/docs/linting-pipeline.md +307 -0
- package/docs/maintenance.ja.md +210 -0
- package/docs/maintenance.md +210 -0
- package/docs/ml-dom/attr.ja.md +103 -0
- package/docs/ml-dom/attr.md +103 -0
- package/docs/ml-dom/block.ja.md +272 -0
- package/docs/ml-dom/block.md +272 -0
- package/docs/ml-dom/document.ja.md +141 -0
- package/docs/ml-dom/document.md +141 -0
- package/docs/ml-dom/element.ja.md +176 -0
- package/docs/ml-dom/element.md +176 -0
- package/docs/ml-dom/helpers.ja.md +203 -0
- package/docs/ml-dom/helpers.md +203 -0
- package/docs/ml-dom/node.ja.md +199 -0
- package/docs/ml-dom/node.md +199 -0
- package/docs/ml-dom/others.ja.md +120 -0
- package/docs/ml-dom/others.md +120 -0
- package/docs/ml-dom/overview.ja.md +102 -0
- package/docs/ml-dom/overview.md +102 -0
- package/docs/ml-dom/pretender.ja.md +269 -0
- package/docs/ml-dom/pretender.md +269 -0
- package/docs/ml-dom/rule-mapping.ja.md +371 -0
- package/docs/ml-dom/rule-mapping.md +371 -0
- package/docs/ml-dom.ja.md +18 -0
- package/docs/ml-dom.md +18 -0
- package/docs/rule-system.ja.md +287 -0
- package/docs/rule-system.md +287 -0
- package/lib/convert-ruleset.d.ts +7 -0
- package/lib/convert-ruleset.js +7 -0
- package/lib/debug.d.ts +4 -0
- package/lib/debug.js +4 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +37 -1
- package/lib/ml-core.js +171 -82
- package/lib/ml-dom/helper/accname.d.ts +8 -0
- package/lib/ml-dom/helper/accname.js +71 -55
- package/lib/ml-dom/helper/create-node.js +1 -0
- package/lib/ml-dom/helper/get-indent.d.ts +4 -1
- package/lib/ml-dom/helper/get-indent.js +21 -30
- package/lib/ml-dom/node/attr.d.ts +65 -4
- package/lib/ml-dom/node/attr.js +151 -53
- package/lib/ml-dom/node/block.d.ts +23 -2
- package/lib/ml-dom/node/block.js +24 -1
- package/lib/ml-dom/node/child-node.d.ts +9 -0
- package/lib/ml-dom/node/child-node.js +9 -0
- package/lib/ml-dom/node/comment.d.ts +7 -0
- package/lib/ml-dom/node/comment.js +7 -0
- package/lib/ml-dom/node/document-fragment.d.ts +8 -0
- package/lib/ml-dom/node/document-fragment.js +8 -0
- package/lib/ml-dom/node/document-type.d.ts +22 -0
- package/lib/ml-dom/node/document-type.js +25 -0
- package/lib/ml-dom/node/document.d.ts +88 -7
- package/lib/ml-dom/node/document.js +128 -32
- package/lib/ml-dom/node/dom-token-list.js +17 -30
- package/lib/ml-dom/node/element-close-tag.js +1 -0
- package/lib/ml-dom/node/element.d.ts +151 -5
- package/lib/ml-dom/node/element.js +242 -50
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +19 -1
- package/lib/ml-dom/node/node.js +175 -166
- package/lib/ml-dom/node/parent-node.js +14 -30
- package/lib/ml-dom/node/rule-mapper.js +7 -20
- package/lib/ml-dom/node/text.d.ts +19 -0
- package/lib/ml-dom/node/text.js +21 -0
- package/lib/ml-dom/node/types.d.ts +68 -0
- package/lib/ml-dom/token/token.d.ts +42 -0
- package/lib/ml-dom/token/token.js +59 -39
- package/lib/ml-rule/create-rule.d.ts +17 -1
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +66 -1
- package/lib/ml-rule/ml-rule.js +95 -25
- package/lib/ml-rule/types.d.ts +41 -0
- package/lib/plugin/plugin.d.ts +8 -0
- package/lib/plugin/plugin.js +8 -0
- package/lib/plugin/types.d.ts +21 -0
- package/lib/ruleset/index.d.ts +10 -0
- package/lib/ruleset/index.js +13 -0
- package/lib/test/index.d.ts +42 -1
- package/lib/test/index.js +39 -2
- package/lib/types.d.ts +10 -1
- package/lib/violation-collector.d.ts +33 -0
- package/lib/violation-collector.js +48 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# リンティングパイプライン
|
|
2
|
+
|
|
3
|
+
`@markuplint/ml-core` の MLCore リンティングエンジンの詳細リファレンスです。
|
|
4
|
+
|
|
5
|
+
## 概要
|
|
6
|
+
|
|
7
|
+
`MLCore` はパース、DOM 構築、ルール実行、違反収集を接続するオーケストレーションエンジンです。パイプラインのフロー:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
ソースコード → パーサー → MLASTDocument → MLDocument → ルール検証 → 違反
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## MLCore クラス
|
|
14
|
+
|
|
15
|
+
ソース: `src/ml-core.ts`
|
|
16
|
+
|
|
17
|
+
### コンストラクタ
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
constructor(params: MLCoreParams)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`MLCoreParams` は `MLFabric` を以下で拡張します:
|
|
24
|
+
|
|
25
|
+
| パラメータ | 型 | 説明 |
|
|
26
|
+
| ------------ | --------- | ---------------------- |
|
|
27
|
+
| `sourceCode` | `string` | リントするソースコード |
|
|
28
|
+
| `filename` | `string` | ソースファイル名 |
|
|
29
|
+
| `debug` | `boolean` | デバッグログの有効化 |
|
|
30
|
+
|
|
31
|
+
### MLFabric
|
|
32
|
+
|
|
33
|
+
`MLFabric` 型はリンティング設定全体を定義します:
|
|
34
|
+
|
|
35
|
+
| フィールド | 型 | 説明 |
|
|
36
|
+
| -------------------- | --------------------------------- | ----------------------------------------------------- |
|
|
37
|
+
| `parser` | `MLParser` | パーサーインスタンス(例:`@markuplint/html-parser`) |
|
|
38
|
+
| `ruleset` | `Ruleset` | 解決済みルール設定 |
|
|
39
|
+
| `rules` | `readonly MLRule[]` | ルールインスタンスの配列 |
|
|
40
|
+
| `locale` | `LocaleSet` | 違反メッセージのロケール |
|
|
41
|
+
| `schemas` | `MLSchema` | HTML/ARIA 仕様タプル |
|
|
42
|
+
| `parserOptions` | `ParserOptions` | パーサー設定オプション |
|
|
43
|
+
| `severity` | `{ parseError?: SeverityOption }` | 重大度オーバーライド |
|
|
44
|
+
| `pretenders` | `readonly Pretender[]` | コンポーネントから HTML へのマッピング |
|
|
45
|
+
| `ruleCommonSettings` | `RuleCommonSettings` | すべてのルールにグローバルに適用される共通設定 |
|
|
46
|
+
| `configErrors` | `readonly ConfigError[]` | 報告する設定エラー |
|
|
47
|
+
|
|
48
|
+
### プロパティ
|
|
49
|
+
|
|
50
|
+
| プロパティ | 型 | 説明 |
|
|
51
|
+
| ---------- | --------------------------- | ------------------------------------------ |
|
|
52
|
+
| `document` | `MLDocument \| ParserError` | パースされたドキュメントまたはパースエラー |
|
|
53
|
+
|
|
54
|
+
### 構築フロー
|
|
55
|
+
|
|
56
|
+
1. **Named nodeRule の展開** — `expandNamedNodeRules()` が `nodeRules` と `childNodeRules` の名前付きエントリを仮想 `MLRule` インスタンスに変換。展開前のオリジナルは `#originalNodeRules` / `#originalChildNodeRules` にホットリロード用に保持。名前空間 disable プレフィックスはワイルドカードエントリ(例: `"a11y/*": false`)から事前計算される。
|
|
57
|
+
2. `_parse()` — `parser.parse(sourceCode, parserOptions)` を呼び出して `MLASTDocument` を生成
|
|
58
|
+
3. `_createDocument()` — AST を `MLDocument` でラップ(ruleset、schemas、オプション付き)
|
|
59
|
+
|
|
60
|
+
パースが失敗した場合、`document` は `MLDocument` の代わりに `ParserError` を保持します。
|
|
61
|
+
|
|
62
|
+
## パースフェーズ
|
|
63
|
+
|
|
64
|
+
### `_parse()`
|
|
65
|
+
|
|
66
|
+
`parser.parse(sourceCode, parserOptions)` を呼び出し、`MLASTDocument` を返します。
|
|
67
|
+
|
|
68
|
+
パーサーがスローした場合:
|
|
69
|
+
|
|
70
|
+
- エラーが `ParserError` としてキャッチされる
|
|
71
|
+
- `document` に `ParserError` オブジェクトが設定される
|
|
72
|
+
- `verify()` が後でこれを `Violation` に変換
|
|
73
|
+
|
|
74
|
+
## ドキュメント作成フェーズ
|
|
75
|
+
|
|
76
|
+
### `_createDocument()`
|
|
77
|
+
|
|
78
|
+
パースされた AST から `MLDocument` を作成します:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
new Document(ast, ruleset, schemas, ruleCommonSettings, {
|
|
82
|
+
filename,
|
|
83
|
+
endTag,
|
|
84
|
+
booleanish,
|
|
85
|
+
tagNameCaseSensitive,
|
|
86
|
+
pretenders,
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
構築時に `MLDocument` は:
|
|
91
|
+
|
|
92
|
+
1. AST を走査してフラットな `nodeList` を構築
|
|
93
|
+
2. `RuleMapper` を初期化してルール設定を配布
|
|
94
|
+
3. pretender 定義が存在する場合、pretender コンテキストをセットアップ
|
|
95
|
+
|
|
96
|
+
## 検証フェーズ
|
|
97
|
+
|
|
98
|
+
### `verify(fix?): Promise<Violation[]>`
|
|
99
|
+
|
|
100
|
+
メインのリンティングメソッド。違反の配列を返します。
|
|
101
|
+
|
|
102
|
+
**フロー:**
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
flowchart TD
|
|
106
|
+
A["verify(fix?)"] --> B{パースエラー?}
|
|
107
|
+
B -->|はい| C["_createParseError()\n→ Violation"]
|
|
108
|
+
B -->|いいえ| D["未定義ルールチェック"]
|
|
109
|
+
D --> E["configErrors を報告"]
|
|
110
|
+
E --> F["各ルールに対して:"]
|
|
111
|
+
F --> G["getRuleInfo(ruleset, rule.name)"]
|
|
112
|
+
G --> H{無効かつ\nオーバーライドなし?}
|
|
113
|
+
H -->|はい| I["スキップ"]
|
|
114
|
+
H -->|いいえ| J["rule.verify(document, locale, fix)"]
|
|
115
|
+
J --> K{ParserError?}
|
|
116
|
+
K -->|はい| L["キャッチ → Violation"]
|
|
117
|
+
K -->|いいえ| M["違反を収集"]
|
|
118
|
+
M --> N["カウントをログ"]
|
|
119
|
+
N --> O["Violation[] を返す"]
|
|
120
|
+
C --> O
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### ステップごとの説明
|
|
124
|
+
|
|
125
|
+
1. **パースエラーチェック**: `document` が `ParserError` の場合、単一の違反を作成して返す
|
|
126
|
+
2. **未定義ルールの検出**: `setRuleNames`(設定内のルール)と `definedRuleName`(実際にロードされたルール)を比較。未定義ルールに対して `config-error` 警告を報告
|
|
127
|
+
3. **設定エラー**: `configErrors` 配列を違反に変換
|
|
128
|
+
4. **ルールループ**: 各ルールに対して:
|
|
129
|
+
- **仮想ルールの無効化チェック** — `rule.baseRuleId` がある場合、3つの条件を確認: 完全一致(`rules["alias"]: false`)、グループ無効化(`rules["groupName"]: false`)、名前空間ワイルドカード(`rules["scope/*"]: false`)。いずれかに該当した場合はスキップ。
|
|
130
|
+
- `rule.getRuleInfo(ruleset, rule.name)` で有効性をチェック
|
|
131
|
+
- `disabled && nodeRules.length === 0 && childNodeRules.length === 0` → スキップ
|
|
132
|
+
- `rule.verify(document, locale, fix)` — ルールを実行
|
|
133
|
+
- 検証中にスローされた `ParserError` をキャッチ
|
|
134
|
+
5. **結果ログ**: デバッグ経由で error/warning/info カウントをログ出力
|
|
135
|
+
|
|
136
|
+
### パースエラーの重大度
|
|
137
|
+
|
|
138
|
+
`severity.parseError` が設定されている場合:
|
|
139
|
+
|
|
140
|
+
| 設定 | 動作 |
|
|
141
|
+
| ---------------------- | ------------------------------ |
|
|
142
|
+
| `false` または `'off'` | パースエラーを抑制(違反なし) |
|
|
143
|
+
| `true` または `null` | `'error'` 重大度として報告 |
|
|
144
|
+
| Severity 値 | その重大度で報告 |
|
|
145
|
+
|
|
146
|
+
## 更新と再パース
|
|
147
|
+
|
|
148
|
+
### `setCode(sourceCode)`
|
|
149
|
+
|
|
150
|
+
新しいソースコードで再パース:
|
|
151
|
+
|
|
152
|
+
1. 保存されたソースコードを更新
|
|
153
|
+
2. `_parse()` を呼び出して新しい AST を生成
|
|
154
|
+
3. `_createDocument()` を呼び出して MLDOM を再構築
|
|
155
|
+
|
|
156
|
+
### `update(partial<MLFabric>)`
|
|
157
|
+
|
|
158
|
+
リンティング設定の部分更新:
|
|
159
|
+
|
|
160
|
+
- Named nodeRules を仮想ルールに再展開。`ruleset` が提供されていない場合、`#originalNodeRules` / `#originalChildNodeRules`(展開前のコピー)をソースとして使用し、仮想ルール化で変換済みのエントリを失うことを防ぐ。
|
|
161
|
+
- `parserOptions` が変更された場合 → 完全な再パース(`_parse()` + `_createDocument()`)
|
|
162
|
+
- それ以外 → `_createDocument()` のみ(既存の AST を再利用)
|
|
163
|
+
|
|
164
|
+
この最適化により、ルールや設定のみの変更時に不要な再パースを回避します。
|
|
165
|
+
|
|
166
|
+
## ViolationCollector
|
|
167
|
+
|
|
168
|
+
ソース: `src/violation-collector.ts`
|
|
169
|
+
|
|
170
|
+
オプションの最大数制限付きで複数ファイルの違反を集約します。
|
|
171
|
+
|
|
172
|
+
### コンストラクタ
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
constructor(maxCount?: number)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### メソッド
|
|
179
|
+
|
|
180
|
+
| メソッド | 戻り値 | 説明 |
|
|
181
|
+
| --------------------------------------- | -------------------------- | --------------------------------------- |
|
|
182
|
+
| `pushWithFile(filePath, ...violations)` | `void` | ファイルの違反を追加(maxCount を尊重) |
|
|
183
|
+
| `isLocked()` | `boolean` | maxCount に到達した場合 `true` |
|
|
184
|
+
| `toArray()` | `FileViolation[]` | すべての違反をフラット配列として返す |
|
|
185
|
+
| `groupByFile()` | `Map<string, Violation[]>` | ファイルパスでグループ化された違反 |
|
|
186
|
+
|
|
187
|
+
### プロパティ
|
|
188
|
+
|
|
189
|
+
| プロパティ | 型 | 説明 |
|
|
190
|
+
| ---------- | -------- | ---------- |
|
|
191
|
+
| `length` | `number` | 違反の総数 |
|
|
192
|
+
|
|
193
|
+
### maxCount の動作
|
|
194
|
+
|
|
195
|
+
`maxCount` が設定されている場合:
|
|
196
|
+
|
|
197
|
+
- `pushWithFile()` は `length >= maxCount` に到達すると違反の受け入れを停止
|
|
198
|
+
- `isLocked()` は制限到達後に `true` を返す
|
|
199
|
+
- 既に追加された違反は保持される
|
|
200
|
+
|
|
201
|
+
## Pretender システム
|
|
202
|
+
|
|
203
|
+
ソース: `src/ml-dom/node/document.ts`、要素の pretending メソッド
|
|
204
|
+
|
|
205
|
+
### Pretender 型
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
type Pretender = {
|
|
209
|
+
selector: string; // コンポーネントにマッチする CSS セレクタ
|
|
210
|
+
as: string; // 偽装する HTML 要素
|
|
211
|
+
aria?: PretenderARIA; // オプションの ARIA オーバーライド
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 動作の仕組み
|
|
216
|
+
|
|
217
|
+
1. `MLDocument` の構築時に `_pretending()` が pretender 定義を処理
|
|
218
|
+
2. pretender セレクタにマッチする各 `MLElement` が `element.pretending(pretenders)` を呼び出す
|
|
219
|
+
3. マッチした要素が `PretenderContext` を取得:
|
|
220
|
+
- コンポーネント要素に `type: 'pretender'`
|
|
221
|
+
- 対象 HTML 要素参照に `type: 'origin'`
|
|
222
|
+
4. ルールが `element.pretenderContext` にアクセスしてセマンティックマッピングを確認
|
|
223
|
+
5. アクセシブル名の計算(`getAccname()`)がロール/名前の解決に pretender コンテキストを使用
|
|
224
|
+
|
|
225
|
+
### PretenderContext
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
type PretenderContext =
|
|
229
|
+
| { type: 'pretender'; as: MLElement; aria?: PretenderARIA }
|
|
230
|
+
| { type: 'origin'; pretender: MLElement };
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## プラグインシステム
|
|
234
|
+
|
|
235
|
+
ソース: `src/plugin/types.ts`, `src/plugin/plugin.ts`
|
|
236
|
+
|
|
237
|
+
### Plugin 型
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
type Plugin = {
|
|
241
|
+
readonly name: string;
|
|
242
|
+
readonly rules?: Record<string, RuleSeed<any, any>>;
|
|
243
|
+
readonly configs?: Record<string, Config>;
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### PluginCreator
|
|
248
|
+
|
|
249
|
+
設定を受け付けるプラグイン用:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
type PluginCreator<S> = {
|
|
253
|
+
readonly name: string;
|
|
254
|
+
create(setting: S): Omit<Plugin, 'name'>;
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### createPlugin
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
function createPlugin<S>(creator: PluginCreator<S>): PluginCreator<S>;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
型安全なプラグインクリエーター定義のためのファクトリ関数。クリエーターをそのまま返し、型ヘルパーとして機能します。
|
|
265
|
+
|
|
266
|
+
### プラグインの利用
|
|
267
|
+
|
|
268
|
+
プラグインが提供するもの:
|
|
269
|
+
|
|
270
|
+
- **カスタムルール** — 名前で登録される追加のルールシード
|
|
271
|
+
- **共有設定** — 再利用可能な設定プリセット
|
|
272
|
+
|
|
273
|
+
## MLSchema
|
|
274
|
+
|
|
275
|
+
ソース: `src/types.ts`
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
type MLSchema = [MLMLSpec, ...ExtendedSpec[]];
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
タプル構造:
|
|
282
|
+
|
|
283
|
+
- 最初の要素: ベースの HTML/ARIA 仕様(`MLMLSpec`)
|
|
284
|
+
- 残り: 拡張仕様(例:フレームワーク固有の要素定義)
|
|
285
|
+
|
|
286
|
+
### schemaToSpec()
|
|
287
|
+
|
|
288
|
+
スキーマタプルを単一の仕様オブジェクトに解決します。MLDOM が要素/属性の検証に使用します。
|
|
289
|
+
|
|
290
|
+
## デバッグ
|
|
291
|
+
|
|
292
|
+
ソース: `src/debug.ts`
|
|
293
|
+
|
|
294
|
+
### enableDebug()
|
|
295
|
+
|
|
296
|
+
ml-core パッケージと CLI のデバッグログを有効化します。
|
|
297
|
+
|
|
298
|
+
### デバッグ名前空間
|
|
299
|
+
|
|
300
|
+
| 名前空間 | 説明 |
|
|
301
|
+
| ------------------------- | ---------------------- |
|
|
302
|
+
| `ml-core` | コアエンジンの操作 |
|
|
303
|
+
| `ml-core:ml-dom` | MLDOM ツリーの操作 |
|
|
304
|
+
| `ml-core:ml-dom:document` | ドキュメント構築の詳細 |
|
|
305
|
+
| `ml-core:rule-mapper` | ルールマッピングの操作 |
|
|
306
|
+
|
|
307
|
+
デバッグ出力は `debug` npm パッケージで制御されます。環境変数 `DEBUG=ml-core*` で有効化できます。
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Linting Pipeline
|
|
2
|
+
|
|
3
|
+
Detailed reference for the MLCore linting engine in `@markuplint/ml-core`.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`MLCore` is the orchestration engine that connects parsing, DOM construction, rule execution, and violation collection. The pipeline flows:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Source Code → Parser → MLASTDocument → MLDocument → Rule Verification → Violations
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## MLCore Class
|
|
14
|
+
|
|
15
|
+
Source: `src/ml-core.ts`
|
|
16
|
+
|
|
17
|
+
### Constructor
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
constructor(params: MLCoreParams)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Where `MLCoreParams` extends `MLFabric` with:
|
|
24
|
+
|
|
25
|
+
| Parameter | Type | Description |
|
|
26
|
+
| ------------ | --------- | ----------------------- |
|
|
27
|
+
| `sourceCode` | `string` | The source code to lint |
|
|
28
|
+
| `filename` | `string` | Source filename |
|
|
29
|
+
| `debug` | `boolean` | Enable debug logging |
|
|
30
|
+
|
|
31
|
+
### MLFabric
|
|
32
|
+
|
|
33
|
+
The `MLFabric` type defines the full linting configuration:
|
|
34
|
+
|
|
35
|
+
| Field | Type | Description |
|
|
36
|
+
| -------------------- | --------------------------------- | ------------------------------------------------- |
|
|
37
|
+
| `parser` | `MLParser` | Parser instance (e.g., `@markuplint/html-parser`) |
|
|
38
|
+
| `ruleset` | `Ruleset` | Resolved rule configuration |
|
|
39
|
+
| `rules` | `readonly MLRule[]` | Array of rule instances |
|
|
40
|
+
| `locale` | `LocaleSet` | Locale for violation messages |
|
|
41
|
+
| `schemas` | `MLSchema` | HTML/ARIA specification tuple |
|
|
42
|
+
| `parserOptions` | `ParserOptions` | Parser configuration options |
|
|
43
|
+
| `severity` | `{ parseError?: SeverityOption }` | Severity overrides |
|
|
44
|
+
| `pretenders` | `readonly Pretender[]` | Component-to-HTML mappings |
|
|
45
|
+
| `ruleCommonSettings` | `RuleCommonSettings` | Common settings applied globally to all rules |
|
|
46
|
+
| `configErrors` | `readonly ConfigError[]` | Configuration errors to report |
|
|
47
|
+
|
|
48
|
+
### Properties
|
|
49
|
+
|
|
50
|
+
| Property | Type | Description |
|
|
51
|
+
| ---------- | --------------------------- | ------------------------------ |
|
|
52
|
+
| `document` | `MLDocument \| ParserError` | Parsed document or parse error |
|
|
53
|
+
|
|
54
|
+
### Construction Flow
|
|
55
|
+
|
|
56
|
+
1. **Named nodeRule expansion** — `expandNamedNodeRules()` converts named entries in `nodeRules` and `childNodeRules` into virtual `MLRule` instances. The originals are preserved in `#originalNodeRules` / `#originalChildNodeRules` for hot-reload. Namespace disable prefixes are pre-computed from wildcard entries (e.g., `"a11y/*": false`).
|
|
57
|
+
2. `_parse()` — Invokes `parser.parse(sourceCode, parserOptions)` to produce `MLASTDocument`
|
|
58
|
+
3. `_createDocument()` — Wraps AST in `MLDocument` with ruleset, schemas, and options
|
|
59
|
+
|
|
60
|
+
If parsing fails, `document` holds a `ParserError` instead of `MLDocument`.
|
|
61
|
+
|
|
62
|
+
## Parse Phase
|
|
63
|
+
|
|
64
|
+
### `_parse()`
|
|
65
|
+
|
|
66
|
+
Calls `parser.parse(sourceCode, parserOptions)` which returns an `MLASTDocument`.
|
|
67
|
+
|
|
68
|
+
If the parser throws:
|
|
69
|
+
|
|
70
|
+
- The error is caught as a `ParserError`
|
|
71
|
+
- `document` is set to the `ParserError` object
|
|
72
|
+
- `verify()` will later convert this to a `Violation`
|
|
73
|
+
|
|
74
|
+
## Document Creation Phase
|
|
75
|
+
|
|
76
|
+
### `_createDocument()`
|
|
77
|
+
|
|
78
|
+
Creates `MLDocument` from the parsed AST:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
new Document(ast, ruleset, schemas, ruleCommonSettings, {
|
|
82
|
+
filename,
|
|
83
|
+
endTag,
|
|
84
|
+
booleanish,
|
|
85
|
+
tagNameCaseSensitive,
|
|
86
|
+
pretenders,
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
During construction, `MLDocument`:
|
|
91
|
+
|
|
92
|
+
1. Builds the flat `nodeList` by traversing the AST
|
|
93
|
+
2. Initializes `RuleMapper` to distribute rule configuration
|
|
94
|
+
3. Sets up pretender contexts when pretender definitions exist
|
|
95
|
+
|
|
96
|
+
## Verification Phase
|
|
97
|
+
|
|
98
|
+
### `verify(fix?): Promise<Violation[]>`
|
|
99
|
+
|
|
100
|
+
The main linting method. Returns an array of violations.
|
|
101
|
+
|
|
102
|
+
**Flow:**
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
flowchart TD
|
|
106
|
+
A["verify(fix?)"] --> B{Parse error?}
|
|
107
|
+
B -->|Yes| C["_createParseError()\n→ Violation"]
|
|
108
|
+
B -->|No| D["Check undefined rules"]
|
|
109
|
+
D --> E["Report configErrors"]
|
|
110
|
+
E --> F["For each rule:"]
|
|
111
|
+
F --> G["getRuleInfo(ruleset, rule.name)"]
|
|
112
|
+
G --> H{Disabled and\nno overrides?}
|
|
113
|
+
H -->|Yes| I["Skip"]
|
|
114
|
+
H -->|No| J["rule.verify(document, locale, fix)"]
|
|
115
|
+
J --> K{ParserError?}
|
|
116
|
+
K -->|Yes| L["Catch → Violation"]
|
|
117
|
+
K -->|No| M["Collect violations"]
|
|
118
|
+
M --> N["Log counts"]
|
|
119
|
+
N --> O["Return Violation[]"]
|
|
120
|
+
C --> O
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Step-by-step
|
|
124
|
+
|
|
125
|
+
1. **Parse error check**: If `document` is a `ParserError`, creates a single violation and returns
|
|
126
|
+
2. **Undefined rule detection**: Compares `setRuleNames` (rules in config) vs `definedRuleName` (rules actually loaded). Reports `config-error` warnings for undefined rules
|
|
127
|
+
3. **Config errors**: Converts `configErrors` array to violations
|
|
128
|
+
4. **Rule loop**: For each rule:
|
|
129
|
+
- **Virtual rule disable check** — If `rule.baseRuleId` is set, checks three disable conditions: exact name match (`rules["alias"]: false`), group disable (`rules["groupName"]: false`), or namespace wildcard (`rules["scope/*"]: false`). Skips the rule if any condition is met.
|
|
130
|
+
- `rule.getRuleInfo(ruleset, rule.name)` checks enablement
|
|
131
|
+
- If `disabled && nodeRules.length === 0 && childNodeRules.length === 0` → skip
|
|
132
|
+
- `rule.verify(document, locale, fix)` — executes the rule
|
|
133
|
+
- Catches `ParserError` if thrown during verification
|
|
134
|
+
5. **Result logging**: Logs error/warning/info counts via debug
|
|
135
|
+
|
|
136
|
+
### Parse Error Violation
|
|
137
|
+
|
|
138
|
+
When `severity.parseError` is configured:
|
|
139
|
+
|
|
140
|
+
| Setting | Behavior |
|
|
141
|
+
| ------------------ | ------------------------------------- |
|
|
142
|
+
| `false` or `'off'` | Parse error suppressed (no violation) |
|
|
143
|
+
| `true` or `null` | Reported as `'error'` severity |
|
|
144
|
+
| Severity value | Reported with that severity |
|
|
145
|
+
|
|
146
|
+
## Update and Re-parse
|
|
147
|
+
|
|
148
|
+
### `setCode(sourceCode)`
|
|
149
|
+
|
|
150
|
+
Re-parses with new source code:
|
|
151
|
+
|
|
152
|
+
1. Updates stored source code
|
|
153
|
+
2. Calls `_parse()` to produce new AST
|
|
154
|
+
3. Calls `_createDocument()` to rebuild MLDOM
|
|
155
|
+
|
|
156
|
+
### `update(partial<MLFabric>)`
|
|
157
|
+
|
|
158
|
+
Partially updates the linting configuration:
|
|
159
|
+
|
|
160
|
+
- Re-expands named nodeRules into virtual rules. When `ruleset` is not provided, uses `#originalNodeRules` / `#originalChildNodeRules` (pre-expansion copies) as the source to avoid losing named entries that were already transformed.
|
|
161
|
+
- If `parserOptions` changed → full re-parse (`_parse()` + `_createDocument()`)
|
|
162
|
+
- Otherwise → only `_createDocument()` (reuses existing AST)
|
|
163
|
+
|
|
164
|
+
This optimization avoids unnecessary re-parsing when only rules or config change.
|
|
165
|
+
|
|
166
|
+
## ViolationCollector
|
|
167
|
+
|
|
168
|
+
Source: `src/violation-collector.ts`
|
|
169
|
+
|
|
170
|
+
Aggregates violations across multiple files with an optional maximum count.
|
|
171
|
+
|
|
172
|
+
### Constructor
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
constructor(maxCount?: number)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Methods
|
|
179
|
+
|
|
180
|
+
| Method | Returns | Description |
|
|
181
|
+
| --------------------------------------- | -------------------------- | --------------------------------------------- |
|
|
182
|
+
| `pushWithFile(filePath, ...violations)` | `void` | Add violations for a file (respects maxCount) |
|
|
183
|
+
| `isLocked()` | `boolean` | `true` if maxCount reached |
|
|
184
|
+
| `toArray()` | `FileViolation[]` | All violations as flat array |
|
|
185
|
+
| `groupByFile()` | `Map<string, Violation[]>` | Violations grouped by file path |
|
|
186
|
+
|
|
187
|
+
### Properties
|
|
188
|
+
|
|
189
|
+
| Property | Type | Description |
|
|
190
|
+
| -------- | -------- | --------------------- |
|
|
191
|
+
| `length` | `number` | Total violation count |
|
|
192
|
+
|
|
193
|
+
### maxCount Behavior
|
|
194
|
+
|
|
195
|
+
When `maxCount` is set:
|
|
196
|
+
|
|
197
|
+
- `pushWithFile()` stops accepting violations once `length >= maxCount`
|
|
198
|
+
- `isLocked()` returns `true` after reaching the limit
|
|
199
|
+
- Already-added violations are preserved
|
|
200
|
+
|
|
201
|
+
## Pretender System
|
|
202
|
+
|
|
203
|
+
Source: `src/ml-dom/node/document.ts`, element pretending methods
|
|
204
|
+
|
|
205
|
+
### Pretender Type
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
type Pretender = {
|
|
209
|
+
selector: string; // CSS selector matching the component
|
|
210
|
+
as: string; // HTML element to pretend as
|
|
211
|
+
aria?: PretenderARIA; // Optional ARIA overrides
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### How It Works
|
|
216
|
+
|
|
217
|
+
1. During `MLDocument` construction, `_pretending()` processes pretender definitions
|
|
218
|
+
2. Each `MLElement` matching a pretender selector calls `element.pretending(pretenders)`
|
|
219
|
+
3. Matched elements get a `PretenderContext`:
|
|
220
|
+
- `type: 'pretender'` on the component element
|
|
221
|
+
- `type: 'origin'` on the target HTML element reference
|
|
222
|
+
4. Rules access `element.pretenderContext` to check semantic mappings
|
|
223
|
+
5. Accessible name computation (`getAccname()`) uses pretender context for role/name resolution
|
|
224
|
+
|
|
225
|
+
### PretenderContext
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
type PretenderContext =
|
|
229
|
+
| { type: 'pretender'; as: MLElement; aria?: PretenderARIA }
|
|
230
|
+
| { type: 'origin'; pretender: MLElement };
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Plugin System
|
|
234
|
+
|
|
235
|
+
Source: `src/plugin/types.ts`, `src/plugin/plugin.ts`
|
|
236
|
+
|
|
237
|
+
### Plugin Type
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
type Plugin = {
|
|
241
|
+
readonly name: string;
|
|
242
|
+
readonly rules?: Record<string, RuleSeed<any, any>>;
|
|
243
|
+
readonly configs?: Record<string, Config>;
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### PluginCreator
|
|
248
|
+
|
|
249
|
+
For plugins that accept settings:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
type PluginCreator<S> = {
|
|
253
|
+
readonly name: string;
|
|
254
|
+
create(setting: S): Omit<Plugin, 'name'>;
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### createPlugin
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
function createPlugin<S>(creator: PluginCreator<S>): PluginCreator<S>;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Factory function for type-safe plugin creator definitions. Returns the creator as-is, serving as a type helper.
|
|
265
|
+
|
|
266
|
+
### Plugin Usage
|
|
267
|
+
|
|
268
|
+
Plugins provide:
|
|
269
|
+
|
|
270
|
+
- **Custom rules** — Additional rule seeds registered by name
|
|
271
|
+
- **Shared configurations** — Reusable config presets
|
|
272
|
+
|
|
273
|
+
## MLSchema
|
|
274
|
+
|
|
275
|
+
Source: `src/types.ts`
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
type MLSchema = [MLMLSpec, ...ExtendedSpec[]];
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
A tuple where:
|
|
282
|
+
|
|
283
|
+
- First element: The base HTML/ARIA specification (`MLMLSpec`)
|
|
284
|
+
- Rest: Extended specifications (e.g., framework-specific element definitions)
|
|
285
|
+
|
|
286
|
+
### schemaToSpec()
|
|
287
|
+
|
|
288
|
+
Resolves the schema tuple into a single spec object used by MLDOM for element/attribute validation.
|
|
289
|
+
|
|
290
|
+
## Debug
|
|
291
|
+
|
|
292
|
+
Source: `src/debug.ts`
|
|
293
|
+
|
|
294
|
+
### enableDebug()
|
|
295
|
+
|
|
296
|
+
Enables debug logging for the ml-core package and CLI.
|
|
297
|
+
|
|
298
|
+
### Debug Namespaces
|
|
299
|
+
|
|
300
|
+
| Namespace | Description |
|
|
301
|
+
| ------------------------- | ----------------------------- |
|
|
302
|
+
| `ml-core` | Core engine operations |
|
|
303
|
+
| `ml-core:ml-dom` | MLDOM tree operations |
|
|
304
|
+
| `ml-core:ml-dom:document` | Document construction details |
|
|
305
|
+
| `ml-core:rule-mapper` | Rule mapping operations |
|
|
306
|
+
|
|
307
|
+
Debug output is controlled via the `debug` npm package. Enable with `DEBUG=ml-core*` environment variable.
|