@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,287 @@
1
+ # ルールシステム
2
+
3
+ `@markuplint/ml-core` のルールフレームワークの詳細リファレンスです。
4
+
5
+ ## 概要
6
+
7
+ ルールフレームワークはリントルールの完全なライフサイクルを処理します:定義、設定、ノードへのマッピング、実行、違反収集。主要コンポーネント:
8
+
9
+ - **RuleSeed** — ルール定義型(verify/fix 関数 + デフォルト値)
10
+ - **MLRule** — ルール実行クラス(シードを名前と設定解決でラップ)
11
+ - **MLRuleContext** — ルールの実行コンテキスト(ドキュメントアクセス、翻訳、違反報告)
12
+ - **RuleMapper** — セレクタ詳細度に基づいてルール設定を特定の DOM ノードにマッピング
13
+ - **Ruleset** — Config から rules、nodeRules、childNodeRules を抽出
14
+
15
+ ## RuleSeed
16
+
17
+ ソース: `src/ml-rule/types.ts`
18
+
19
+ `RuleSeed<T, O>` 型はルールの実装を定義します。
20
+
21
+ ```typescript
22
+ type RuleSeed<T extends RuleConfigValue = boolean, O extends PlainData = undefined> = {
23
+ readonly meta?: {
24
+ readonly category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
25
+ };
26
+ readonly defaultSeverity?: Severity;
27
+ readonly defaultValue?: T;
28
+ readonly defaultOptions?: O;
29
+ verify(context: ProvidedContext<T, O>): void | Promise<void>;
30
+ fix?(context: ProvidedContext<T, O>): void | Promise<void>;
31
+ };
32
+ ```
33
+
34
+ ### カテゴリ値
35
+
36
+ | カテゴリ | 説明 |
37
+ | --------------------- | ------------------------------------ |
38
+ | `'validation'` | HTML 標準準拠チェック |
39
+ | `'style'` | コードスタイルとフォーマットのルール |
40
+ | `'naming-convention'` | 命名規則の強制 |
41
+ | `'a11y'` | アクセシビリティチェック |
42
+ | `'maintainability'` | コード保守性のルール |
43
+
44
+ ### デフォルト値
45
+
46
+ - `defaultSeverity` — 未指定の場合 `'error'`
47
+ - `defaultValue` — 未指定の場合 `true`
48
+ - `defaultOptions` — 未指定の場合 `undefined`
49
+
50
+ ## createRule
51
+
52
+ ソース: `src/ml-rule/create-rule.ts`
53
+
54
+ 型安全なルールシード作成のためのファクトリ関数:
55
+
56
+ ```typescript
57
+ function createRule<T extends RuleConfigValue, O extends PlainData = undefined>(
58
+ seed: Readonly<RuleSeed<T, O>>,
59
+ ): RuleSeed<T, O>;
60
+ ```
61
+
62
+ シードをそのまま返します。主に TypeScript の型推論のためのヘルパーとして機能します。
63
+
64
+ ### 使用例
65
+
66
+ ```typescript
67
+ import { createRule } from '@markuplint/ml-core';
68
+
69
+ export default createRule({
70
+ defaultSeverity: 'error',
71
+ defaultValue: true,
72
+ async verify({ document, report, t }) {
73
+ await document.walkOn('Element', el => {
74
+ if (/* 違反条件 */) {
75
+ report({ scope: el, message: t('エラーメッセージ') });
76
+ }
77
+ });
78
+ },
79
+ });
80
+ ```
81
+
82
+ ## MLRule
83
+
84
+ ソース: `src/ml-rule/ml-rule.ts`
85
+
86
+ `MLRule<T, O>` は `RuleSeed` を名前でラップし、設定解決と検証実行を提供します。
87
+
88
+ ### コンストラクタ
89
+
90
+ ```typescript
91
+ constructor(o: Readonly<RuleSeed<T, O>> & { readonly name: string })
92
+ ```
93
+
94
+ ### プロパティ
95
+
96
+ | プロパティ | 型 | 説明 |
97
+ | ----------------- | ------------------------------ | ------------------------------------------------------------------ |
98
+ | `name` | `string` | ルール識別子(例: `"attr-duplication"` または `"a11y/html-lang"`) |
99
+ | `defaultSeverity` | `Severity` | デフォルトの重大度レベル(シードまたは `'error'`) |
100
+ | `defaultValue` | `T` | デフォルト設定値(シードまたは `true`) |
101
+ | `defaultOptions` | `O` | デフォルトオプション(シードから) |
102
+ | `baseRuleId` | `string \| undefined` | 仮想ルールの場合: ベースルール名(例: `"required-attr"`) |
103
+ | `groupName` | `string \| undefined` | 複数エントリ仮想ルールの場合: 一括無効化用のグループ名 |
104
+ | `specConformance` | `SpecConformance \| undefined` | 仮想ルールの場合: `'normative'` または `'non-normative'` |
105
+
106
+ ### メソッド
107
+
108
+ #### `verify(document, locale, fix): Promise<Violation[]>`
109
+
110
+ ドキュメントに対してルールを実行します。
111
+
112
+ **フロー:**
113
+
114
+ 1. `document.setRule(this)` — ドキュメントに現在のルールコンテキストを設定
115
+ 2. `new MLRuleContext(document, locale)` — 実行コンテキストを作成
116
+ 3. `context.provide()` — 提供可能なコンテキストオブジェクトを生成
117
+ 4. `await seed.verify(context)` — 検証を実行
118
+ 5. `await seed.fix(context)` — 修正を実行(`fix=true` かつ fix 関数がある場合)
119
+ 6. `context.reports` → `Violation[]` — レポートを違反にマッピング
120
+ 7. `document.setRule(null)` — ルールコンテキストをクリア
121
+
122
+ **Report → Violation のマッピング:**
123
+
124
+ - スコープベースのレポート: `report.scope`(ノード)から `line`, `col`, `raw` を抽出、重大度は `report.scope.rule.severity` から
125
+ - 直接レポート: `report.line`, `report.col`, `report.raw` を直接使用、重大度は `document.rule.severity` から
126
+
127
+ #### `getRuleInfo(ruleSet, ruleName): GlobalRuleInfo<T, O>`
128
+
129
+ ルールセットから完全なルール情報を解決します。
130
+
131
+ 戻り値:
132
+
133
+ ```typescript
134
+ {
135
+ ...RuleInfo<T, O>, // グローバルルール設定
136
+ nodeRules: RuleInfo<T, O>[], // 無効でないノードレベルのオーバーライド
137
+ childNodeRules: RuleInfo<T, O>[], // 無効でない子ノードレベルのオーバーライド
138
+ }
139
+ ```
140
+
141
+ #### `createAlias(aliasName, options?): MLRule<T, O>`
142
+
143
+ このルールの verify/fix ロジックを別名で再利用する仮想ルールを作成します。named nodeRules を実装するために `expandNamedNodeRules()` が内部的に使用します。
144
+
145
+ オプション:
146
+
147
+ | オプション | 型 | 説明 |
148
+ | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------ |
149
+ | `defaultSeverity` | `Severity` | ベースルールのデフォルト重大度をオーバーライド(明示的な指定のみ。specConformance による自動設定なし) |
150
+ | `specConformance` | `SpecConformance` | `'normative'` または `'non-normative'` |
151
+ | `groupName` | `string` | 複数エントリ named nodeRule のグループ名 |
152
+
153
+ エイリアスルールはベースルールの verify/fix 関数、meta、デフォルト値を継承します。`baseRuleId` プロパティでベースルール名を返します。
154
+
155
+ #### `optimizeOption(configSettings): RuleInfo<T, O>`
156
+
157
+ 生のルール設定を解決済み `RuleInfo` に正規化します。
158
+
159
+ | 入力 | 結果 |
160
+ | -------------------------- | --------------------------------------------------------------------------------------------- |
161
+ | `undefined` または `false` | `{ disabled: true, severity: デフォルト, value: デフォルト, options: デフォルト }` |
162
+ | `true` | `{ disabled: false, severity: デフォルト, value: デフォルト, options: デフォルト }` |
163
+ | `RuleConfig` オブジェクト | `{ disabled: false, severity: 設定/デフォルト, value: 設定/デフォルト, options: マージ済み }` |
164
+ | プリミティブ値 | `{ disabled: false, severity: デフォルト, value: 入力値, options: デフォルト }` |
165
+
166
+ オプションのマージ: 配列はスプレッド(`[...a, ...b]`)、オブジェクトはスプレッド(`{...a, ...b}`)、それ以外は `b ?? a` にフォールバック。
167
+
168
+ ## MLRuleContext
169
+
170
+ ソース: `src/ml-rule/ml-rule-context.ts`
171
+
172
+ `MLRuleContext<T, O>` はルールの実行コンテキストを提供します。
173
+
174
+ ### コンストラクタ
175
+
176
+ ```typescript
177
+ constructor(document: MLDocument<T, O>, locale: LocaleSet)
178
+ ```
179
+
180
+ ロケールから翻訳関数を作成し、ドキュメント参照を保存します。
181
+
182
+ ### プロパティ
183
+
184
+ | プロパティ | 型 | 説明 |
185
+ | ----------- | ------------------ | ----------------------- |
186
+ | `document` | `MLDocument<T, O>` | 検証対象のドキュメント |
187
+ | `locale` | `string` | ロケール文字列 |
188
+ | `translate` | `Translator` | i18n メッセージ翻訳関数 |
189
+
190
+ ### `provide(): ProvidedContext`
191
+
192
+ `RuleSeed.verify()` と `RuleSeed.fix()` に渡されるコンテキストオブジェクトを返します:
193
+
194
+ ```typescript
195
+ {
196
+ document: MLDocument<T, O>,
197
+ translate: Translator,
198
+ t: Translator, // translate のエイリアス
199
+ reports: Report<T, O>[],
200
+ report: (report) => void | boolean,
201
+ }
202
+ ```
203
+
204
+ ### `report(report)`
205
+
206
+ 2 つのオーバーロード:
207
+
208
+ 1. **直接レポート**(`Report<T, O>`): レポートを直接プッシュ。`undefined` を返す。
209
+ 2. **チェッカーレポート**(`CheckerReport<T, O>`): 翻訳関数で関数を呼び出す。レポートが返された場合プッシュして `true` を返す。`null`/`undefined` の場合 `false` を返す。
210
+
211
+ ### 重複排除
212
+
213
+ レポートは `_push()` で以下の基準で重複排除されます:
214
+
215
+ - **スコープベース**: 同じ `scope` オブジェクト + 同じ `message`
216
+ - **位置ベース**: 同じ `col` + `line` + `message` + `raw`
217
+
218
+ ### メッセージの最終処理
219
+
220
+ 英語ロケール(`'en'`)の場合、最初の小文字が大文字に変換されます。他のロケールはそのまま通過します。
221
+
222
+ ## チェッカー型
223
+
224
+ ソース: `src/ml-rule/types.ts`
225
+
226
+ チェッカー関数を構築するためのユーティリティ型:
227
+
228
+ | 型 | シグネチャ | 説明 |
229
+ | ------------------------- | ------------------------------------------------------------ | ------------------ |
230
+ | `Checker<T, O, P>` | `(params: P) => CheckerReport<T, O>` | 汎用チェッカー |
231
+ | `ElementChecker<T, O, P>` | `(params: P & { el: Element<T, O> }) => CheckerReport<T, O>` | 要素固有チェッカー |
232
+ | `AttrChecker<T, O, P>` | `(params: P & { attr: Attr<T, O> }) => CheckerReport<T, O>` | 属性固有チェッカー |
233
+ | `CheckerReport<T, O>` | `(t: Translator) => Report<T, O> \| undefined \| null` | 遅延レポート関数 |
234
+
235
+ ## ルールマッピング
236
+
237
+ `RuleMapper`、ルール設定の解決(`rules`、`nodeRules`、`childNodeRules` の3層処理)、詳細度ベースの競合解決、マージ動作、正規表現セレクタのテンプレート変数についての詳細なドキュメントは、専用の[ルールマッピング](./ml-dom/rule-mapping.ja.md)リファレンスを参照してください。
238
+
239
+ ## Ruleset
240
+
241
+ ソース: `src/ruleset/index.ts`
242
+
243
+ `Config` オブジェクトからルール設定を抽出します。
244
+
245
+ ```typescript
246
+ class Ruleset {
247
+ readonly rules: Rules;
248
+ readonly nodeRules: readonly NodeRule[];
249
+ readonly childNodeRules: readonly ChildNodeRule[];
250
+
251
+ constructor(config: Config);
252
+ }
253
+ ```
254
+
255
+ - `rules` — グローバルルール定義(`config.rules` から、デフォルトは `{}`)
256
+ - `nodeRules` — ノード固有のオーバーライド(`config.nodeRules` から、デフォルトは `[]`)
257
+ - `childNodeRules` — 子ノード固有のオーバーライド(`config.childNodeRules` から、デフォルトは `[]`)
258
+
259
+ ## テストユーティリティ
260
+
261
+ ソース: `src/ml-rule/create-test-rule.ts`
262
+
263
+ ### createTestRule
264
+
265
+ ```typescript
266
+ function createRule<T, O>(seed: Readonly<RuleSeed<T, O>> & { readonly name: string }): MLRule<T, O>;
267
+ ```
268
+
269
+ テスト用の `MLRule` インスタンスを作成します。`create-rule.ts` の `createRule()` とは異なり、`name` プロパティが必須で、実際の `MLRule` インスタンスを返します。
270
+
271
+ ### テストパターン
272
+
273
+ ```typescript
274
+ import { createRule } from '@markuplint/ml-core/test';
275
+ import { createTestDocument } from '@markuplint/ml-core/test';
276
+
277
+ const rule = createRule({
278
+ name: 'my-rule',
279
+ defaultSeverity: 'error',
280
+ async verify({ document, report, t }) {
281
+ // 検証ロジック
282
+ },
283
+ });
284
+
285
+ const doc = createTestDocument('<div></div>');
286
+ const violations = await rule.verify(doc, { locale: 'en' }, false);
287
+ ```
@@ -0,0 +1,287 @@
1
+ # Rule System
2
+
3
+ Detailed reference for the rule framework in `@markuplint/ml-core`.
4
+
5
+ ## Overview
6
+
7
+ The rule framework handles the full lifecycle of lint rules: definition, configuration, mapping to nodes, execution, and violation collection. The key components are:
8
+
9
+ - **RuleSeed** -- Rule definition type (verify/fix functions + defaults)
10
+ - **MLRule** -- Rule execution class (wraps a seed with name and config resolution)
11
+ - **MLRuleContext** -- Execution context for rules (document access, translation, violation reporting)
12
+ - **RuleMapper** -- Maps rule configurations to specific DOM nodes based on selector specificity
13
+ - **Ruleset** -- Extracts rules, nodeRules, and childNodeRules from Config
14
+
15
+ ## RuleSeed
16
+
17
+ Source: `src/ml-rule/types.ts`
18
+
19
+ The `RuleSeed<T, O>` type defines a rule's implementation.
20
+
21
+ ```typescript
22
+ type RuleSeed<T extends RuleConfigValue = boolean, O extends PlainData = undefined> = {
23
+ readonly meta?: {
24
+ readonly category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
25
+ };
26
+ readonly defaultSeverity?: Severity;
27
+ readonly defaultValue?: T;
28
+ readonly defaultOptions?: O;
29
+ verify(context: ProvidedContext<T, O>): void | Promise<void>;
30
+ fix?(context: ProvidedContext<T, O>): void | Promise<void>;
31
+ };
32
+ ```
33
+
34
+ ### Category Values
35
+
36
+ | Category | Description |
37
+ | --------------------- | ------------------------------- |
38
+ | `'validation'` | HTML standard compliance checks |
39
+ | `'style'` | Code style and formatting rules |
40
+ | `'naming-convention'` | Naming convention enforcement |
41
+ | `'a11y'` | Accessibility checks |
42
+ | `'maintainability'` | Code maintainability rules |
43
+
44
+ ### Default Values
45
+
46
+ - `defaultSeverity` defaults to `'error'` if not specified
47
+ - `defaultValue` defaults to `true` if not specified
48
+ - `defaultOptions` defaults to `undefined`
49
+
50
+ ## createRule
51
+
52
+ Source: `src/ml-rule/create-rule.ts`
53
+
54
+ Factory function for type-safe rule seed creation:
55
+
56
+ ```typescript
57
+ function createRule<T extends RuleConfigValue, O extends PlainData = undefined>(
58
+ seed: Readonly<RuleSeed<T, O>>,
59
+ ): RuleSeed<T, O>;
60
+ ```
61
+
62
+ Returns the seed as-is. Serves primarily as a type helper for TypeScript inference.
63
+
64
+ ### Usage
65
+
66
+ ```typescript
67
+ import { createRule } from '@markuplint/ml-core';
68
+
69
+ export default createRule({
70
+ defaultSeverity: 'error',
71
+ defaultValue: true,
72
+ async verify({ document, report, t }) {
73
+ await document.walkOn('Element', el => {
74
+ if (/* violation condition */) {
75
+ report({ scope: el, message: t('Error message') });
76
+ }
77
+ });
78
+ },
79
+ });
80
+ ```
81
+
82
+ ## MLRule
83
+
84
+ Source: `src/ml-rule/ml-rule.ts`
85
+
86
+ `MLRule<T, O>` wraps a `RuleSeed` with a name and provides configuration resolution and verification execution.
87
+
88
+ ### Constructor
89
+
90
+ ```typescript
91
+ constructor(o: Readonly<RuleSeed<T, O>> & { readonly name: string })
92
+ ```
93
+
94
+ ### Properties
95
+
96
+ | Property | Type | Description |
97
+ | ----------------- | ------------------------------ | ------------------------------------------------------------------ |
98
+ | `name` | `string` | Rule identifier (e.g., `"attr-duplication"` or `"a11y/html-lang"`) |
99
+ | `defaultSeverity` | `Severity` | Default severity level (from seed or `'error'`) |
100
+ | `defaultValue` | `T` | Default config value (from seed or `true`) |
101
+ | `defaultOptions` | `O` | Default options (from seed) |
102
+ | `baseRuleId` | `string \| undefined` | For virtual rules: the base rule's name (e.g., `"required-attr"`) |
103
+ | `groupName` | `string \| undefined` | For multi-entry virtual rules: group name for batch disable |
104
+ | `specConformance` | `SpecConformance \| undefined` | For virtual rules: `'normative'` or `'non-normative'` |
105
+
106
+ ### Methods
107
+
108
+ #### `verify(document, locale, fix): Promise<Violation[]>`
109
+
110
+ Executes the rule against a document.
111
+
112
+ **Flow:**
113
+
114
+ 1. `document.setRule(this)` -- sets current rule context on document
115
+ 2. `new MLRuleContext(document, locale)` -- creates execution context
116
+ 3. `context.provide()` -- generates providable context object
117
+ 4. `await seed.verify(context)` -- runs verification
118
+ 5. `await seed.fix(context)` -- runs fix (if `fix=true` and fix function exists)
119
+ 6. `context.reports` -> `Violation[]` -- maps reports to violations
120
+ 7. `document.setRule(null)` -- clears rule context
121
+
122
+ **Report -> Violation mapping:**
123
+
124
+ - Scope-based reports: extracts `line`, `col`, `raw` from `report.scope` (the node), severity from `report.scope.rule.severity`
125
+ - Direct reports: uses `report.line`, `report.col`, `report.raw` directly, severity from `document.rule.severity`
126
+
127
+ #### `getRuleInfo(ruleSet, ruleName): GlobalRuleInfo<T, O>`
128
+
129
+ Resolves the full rule information from a ruleset.
130
+
131
+ Returns:
132
+
133
+ ```typescript
134
+ {
135
+ ...RuleInfo<T, O>, // Global rule config
136
+ nodeRules: RuleInfo<T, O>[], // Non-disabled node-level overrides
137
+ childNodeRules: RuleInfo<T, O>[], // Non-disabled child-node-level overrides
138
+ }
139
+ ```
140
+
141
+ #### `createAlias(aliasName, options?): MLRule<T, O>`
142
+
143
+ Creates a virtual rule that reuses this rule's verify/fix logic under a different name. Used internally by `expandNamedNodeRules()` to implement named nodeRules.
144
+
145
+ Options:
146
+
147
+ | Option | Type | Description |
148
+ | ----------------- | ----------------- | ------------------------------------------------------------------------------------- |
149
+ | `defaultSeverity` | `Severity` | Override the base rule's default severity (explicit only; not set by specConformance) |
150
+ | `specConformance` | `SpecConformance` | `'normative'` or `'non-normative'` |
151
+ | `groupName` | `string` | Group name for multi-entry named nodeRules |
152
+
153
+ The alias rule inherits the base rule's verify/fix functions, meta, and defaults. Its `baseRuleId` property returns the base rule's name.
154
+
155
+ #### `optimizeOption(configSettings): RuleInfo<T, O>`
156
+
157
+ Normalizes raw rule settings into resolved `RuleInfo`.
158
+
159
+ | Input | Result |
160
+ | ---------------------- | --------------------------------------------------------------------------------------- |
161
+ | `undefined` or `false` | `{ disabled: true, severity: default, value: default, options: default }` |
162
+ | `true` | `{ disabled: false, severity: default, value: default, options: default }` |
163
+ | `RuleConfig` object | `{ disabled: false, severity: config/default, value: config/default, options: merged }` |
164
+ | Primitive value | `{ disabled: false, severity: default, value: input, options: default }` |
165
+
166
+ Options merging: arrays are spread (`[...a, ...b]`), objects are spread (`{...a, ...b}`), otherwise fallback to `b ?? a`.
167
+
168
+ ## MLRuleContext
169
+
170
+ Source: `src/ml-rule/ml-rule-context.ts`
171
+
172
+ `MLRuleContext<T, O>` provides the execution context for rules.
173
+
174
+ ### Constructor
175
+
176
+ ```typescript
177
+ constructor(document: MLDocument<T, O>, locale: LocaleSet)
178
+ ```
179
+
180
+ Creates translator from locale, stores document reference.
181
+
182
+ ### Properties
183
+
184
+ | Property | Type | Description |
185
+ | ----------- | ------------------ | --------------------------- |
186
+ | `document` | `MLDocument<T, O>` | The document being verified |
187
+ | `locale` | `string` | Locale string |
188
+ | `translate` | `Translator` | i18n message translator |
189
+
190
+ ### `provide(): ProvidedContext`
191
+
192
+ Returns the context object passed to `RuleSeed.verify()` and `RuleSeed.fix()`:
193
+
194
+ ```typescript
195
+ {
196
+ document: MLDocument<T, O>,
197
+ translate: Translator,
198
+ t: Translator, // alias for translate
199
+ reports: Report<T, O>[],
200
+ report: (report) => void | boolean,
201
+ }
202
+ ```
203
+
204
+ ### `report(report)`
205
+
206
+ Two overloads:
207
+
208
+ 1. **Direct report** (`Report<T, O>`): Pushes the report directly. Returns `undefined`.
209
+ 2. **Checker report** (`CheckerReport<T, O>`): Calls the function with translator. If it returns a report, pushes it and returns `true`. If `null`/`undefined`, returns `false`.
210
+
211
+ ### Deduplication
212
+
213
+ Reports are deduplicated in `_push()` using:
214
+
215
+ - **Scope-based**: same `scope` object + same `message`
216
+ - **Position-based**: same `col` + `line` + `message` + `raw`
217
+
218
+ ### Message Finalization
219
+
220
+ For English locale (`'en'`), the first lowercase letter is capitalized. Other locales pass through unchanged.
221
+
222
+ ## Checker Types
223
+
224
+ Source: `src/ml-rule/types.ts`
225
+
226
+ Utility types for building checker functions:
227
+
228
+ | Type | Signature | Description |
229
+ | ------------------------- | ------------------------------------------------------------ | -------------------------- |
230
+ | `Checker<T, O, P>` | `(params: P) => CheckerReport<T, O>` | Generic checker |
231
+ | `ElementChecker<T, O, P>` | `(params: P & { el: Element<T, O> }) => CheckerReport<T, O>` | Element-specific checker |
232
+ | `AttrChecker<T, O, P>` | `(params: P & { attr: Attr<T, O> }) => CheckerReport<T, O>` | Attribute-specific checker |
233
+ | `CheckerReport<T, O>` | `(t: Translator) => Report<T, O> \| undefined \| null` | Deferred report function |
234
+
235
+ ## Rule Mapping
236
+
237
+ For detailed documentation on `RuleMapper`, rule configuration resolution (three-layer processing with `rules`, `nodeRules`, `childNodeRules`), specificity-based conflict resolution, merging behavior, and regex selector templates, see the dedicated [Rule Mapping](./ml-dom/rule-mapping.md) reference.
238
+
239
+ ## Ruleset
240
+
241
+ Source: `src/ruleset/index.ts`
242
+
243
+ Extracts rule configuration from a `Config` object.
244
+
245
+ ```typescript
246
+ class Ruleset {
247
+ readonly rules: Rules;
248
+ readonly nodeRules: readonly NodeRule[];
249
+ readonly childNodeRules: readonly ChildNodeRule[];
250
+
251
+ constructor(config: Config);
252
+ }
253
+ ```
254
+
255
+ - `rules` -- Global rule definitions (from `config.rules`, defaults to `{}`)
256
+ - `nodeRules` -- Node-specific overrides (from `config.nodeRules`, defaults to `[]`)
257
+ - `childNodeRules` -- Child-node-specific overrides (from `config.childNodeRules`, defaults to `[]`)
258
+
259
+ ## Test Utilities
260
+
261
+ Source: `src/ml-rule/create-test-rule.ts`
262
+
263
+ ### createTestRule
264
+
265
+ ```typescript
266
+ function createRule<T, O>(seed: Readonly<RuleSeed<T, O>> & { readonly name: string }): MLRule<T, O>;
267
+ ```
268
+
269
+ Creates an `MLRule` instance for testing. Unlike `createRule()` in `create-rule.ts`, this requires a `name` property and returns an actual `MLRule` instance.
270
+
271
+ ### Test Pattern
272
+
273
+ ```typescript
274
+ import { createRule } from '@markuplint/ml-core/test';
275
+ import { createTestDocument } from '@markuplint/ml-core/test';
276
+
277
+ const rule = createRule({
278
+ name: 'my-rule',
279
+ defaultSeverity: 'error',
280
+ async verify({ document, report, t }) {
281
+ // verification logic
282
+ },
283
+ });
284
+
285
+ const doc = createTestDocument('<div></div>');
286
+ const violations = await rule.verify(doc, { locale: 'en' }, false);
287
+ ```
@@ -1,3 +1,10 @@
1
1
  import type { Config } from '@markuplint/ml-config';
2
2
  import { Ruleset } from './ruleset/index.js';
3
+ /**
4
+ * Converts a markuplint {@link Config} object into a {@link Ruleset} instance
5
+ * that can be used by the linting engine.
6
+ *
7
+ * @param config - The configuration to convert (defaults to an empty config)
8
+ * @returns A new Ruleset instance
9
+ */
3
10
  export declare function convertRuleset(config?: Config): Ruleset;
@@ -1,4 +1,11 @@
1
1
  import { Ruleset } from './ruleset/index.js';
2
+ /**
3
+ * Converts a markuplint {@link Config} object into a {@link Ruleset} instance
4
+ * that can be used by the linting engine.
5
+ *
6
+ * @param config - The configuration to convert (defaults to an empty config)
7
+ * @returns A new Ruleset instance
8
+ */
2
9
  export function convertRuleset(config = {}) {
3
10
  return new Ruleset(config);
4
11
  }
package/lib/debug.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  import debug from 'debug';
2
2
  export declare const log: debug.Debugger;
3
+ /**
4
+ * Enables debug logging for the `ml-core` namespace and the CLI namespace.
5
+ * No-op if debug logging is already enabled.
6
+ */
3
7
  export declare function enableDebug(): void;
package/lib/debug.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import debug from 'debug';
2
2
  const CLI_NS = 'markuplint-cli';
3
3
  export const log = debug('ml-core');
4
+ /**
5
+ * Enables debug logging for the `ml-core` namespace and the CLI namespace.
6
+ * No-op if debug logging is already enabled.
7
+ */
4
8
  export function enableDebug() {
5
9
  if (!log.enabled) {
6
10
  debug.enable(`${log.namespace}*`);
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { RuleInfo, RuleConfig, RuleConfigValue } from '@markuplint/ml-config';
1
+ export type { RuleInfo, RuleConfig, RuleConfigValue } from '@markuplint/ml-config';
2
2
  export { ariaSpecs, contentModelCategoryToTagNames, getAttrSpecs, getComputedRole, getImplicitRole, getPermittedRoles, getRoleSpec, getSpec, resolveNamespace, } from '@markuplint/ml-spec';
3
3
  export { Ruleset } from './ruleset/index.js';
4
4
  export { enableDebug } from './debug.js';
@@ -9,7 +9,8 @@ export * from './ml-dom/index.js';
9
9
  export * from './ml-rule/index.js';
10
10
  export * from './plugin/index.js';
11
11
  export * from './test/index.js';
12
- export * from './types.js';
12
+ export type * from './types.js';
13
13
  export * from './utils/index.js';
14
14
  export * from './violation-collector.js';
15
- export { AccessibilityProperties } from './ml-dom/node/types.js';
15
+ export * from './virtual-rule.js';
16
+ export type { AccessibilityProperties } from './ml-dom/node/types.js';
package/lib/index.js CHANGED
@@ -8,6 +8,6 @@ export * from './ml-dom/index.js';
8
8
  export * from './ml-rule/index.js';
9
9
  export * from './plugin/index.js';
10
10
  export * from './test/index.js';
11
- export * from './types.js';
12
11
  export * from './utils/index.js';
13
12
  export * from './violation-collector.js';
13
+ export * from './virtual-rule.js';