@markuplint/ml-core 4.13.3 → 5.0.0-alpha.1
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 +92 -35
- package/ARCHITECTURE.md +85 -28
- package/CHANGELOG.md +54 -0
- package/docs/linting-pipeline.ja.md +18 -14
- package/docs/linting-pipeline.md +18 -14
- package/docs/maintenance.ja.md +1 -1
- package/docs/maintenance.md +1 -1
- package/docs/ml-dom/attr.ja.md +8 -0
- package/docs/ml-dom/attr.md +8 -0
- package/docs/ml-dom/block.ja.md +33 -33
- package/docs/ml-dom/block.md +33 -33
- package/docs/ml-dom/element.ja.md +17 -17
- package/docs/ml-dom/element.md +17 -17
- package/docs/ml-dom/node.ja.md +4 -5
- package/docs/ml-dom/node.md +4 -5
- package/docs/ml-dom/others.ja.md +2 -1
- package/docs/ml-dom/others.md +5 -4
- package/docs/rule-system.ja.md +23 -6
- package/docs/rule-system.md +23 -6
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +1 -1
- package/lib/ml-core.js +142 -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.js +17 -29
- package/lib/ml-dom/node/attr.js +122 -73
- package/lib/ml-dom/node/block.d.ts +3 -3
- package/lib/ml-dom/node/block.js +10 -1
- package/lib/ml-dom/node/document-type.js +12 -0
- package/lib/ml-dom/node/document.d.ts +14 -3
- package/lib/ml-dom/node/document.js +75 -34
- 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 +19 -7
- package/lib/ml-dom/node/element.js +134 -55
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +3 -1
- package/lib/ml-dom/node/node.js +159 -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 +7 -0
- package/lib/ml-dom/node/text.js +9 -0
- package/lib/ml-dom/token/token.js +23 -39
- package/lib/ml-rule/create-rule.d.ts +8 -1
- package/lib/ml-rule/create-rule.js +0 -9
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +33 -1
- package/lib/ml-rule/ml-rule.js +65 -25
- package/lib/ruleset/index.js +6 -0
- package/lib/test/index.js +4 -1
- package/lib/types.d.ts +2 -1
- package/lib/violation-collector.js +15 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -44,6 +44,8 @@ src/
|
|
|
44
44
|
│ └── manipulations/
|
|
45
45
|
│ ├── child-node-methods.ts — ChildNode インターフェーススタブ
|
|
46
46
|
│ └── get-children.ts — 要素の子要素抽出
|
|
47
|
+
├── virtual-rule.ts — Named nodeRule の展開(expandNamedNodeRules)
|
|
48
|
+
├── virtual-rule.spec.ts — 仮想ルールのユニットテスト
|
|
47
49
|
├── ml-rule/
|
|
48
50
|
│ ├── ml-rule.ts — MLRule クラス(ルール実行)
|
|
49
51
|
│ ├── ml-rule-context.ts — MLRuleContext(レポート収集)
|
|
@@ -173,7 +175,7 @@ MLToken<A extends MLASTToken>
|
|
|
173
175
|
| `MLText` | `Text` | テキストノード、`isWhitespace()`, `isRawTextElementContent()` |
|
|
174
176
|
| `MLComment` | `Comment` | コメントノード(`textContent`) |
|
|
175
177
|
| `MLDocumentType` | `DocumentType` | `<!DOCTYPE>`(`name`, `publicId`, `systemId`) |
|
|
176
|
-
| `MLBlock` | — | プリプロセッサ固有ブロック(if/each/switch)、`
|
|
178
|
+
| `MLBlock` | — | プリプロセッサ固有ブロック(if/each/switch)、`blockBehavior`, `isTransparent` |
|
|
177
179
|
| `MLElementCloseTag` | — | 開始タグ要素とペアになる閉じタグ |
|
|
178
180
|
| `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
|
|
179
181
|
| `MLElement` | `Element` | 属性、セレクタ、名前空間、pretender コンテキスト、`elementType`, `closeTag` |
|
|
@@ -204,14 +206,14 @@ MLToken<A extends MLASTToken>
|
|
|
204
206
|
|
|
205
207
|
### 主要メソッド
|
|
206
208
|
|
|
207
|
-
| メソッド | 説明
|
|
208
|
-
| --------------------------------- |
|
|
209
|
-
| `walkOn(type, walker)` | 指定した型(`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`)のノードを走査
|
|
210
|
-
| `setRule(rule)` | 現在のルールを設定(検証時に `MLCore` が使用)
|
|
211
|
-
| `getTokenList()` | ソース再構築用の全トークンを返す
|
|
212
|
-
| `searchNodeByLocation(line, col)` | 指定したソース位置のノードを検索
|
|
213
|
-
| `getAccessibilityProp(node)` | ARIA
|
|
214
|
-
| `toString(fixed?)` | ソースコードを再構築(オプションで修正適用)
|
|
209
|
+
| メソッド | 説明 |
|
|
210
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
211
|
+
| `walkOn(type, walker)` | 指定した型(`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`)のノードを走査 |
|
|
212
|
+
| `setRule(rule)` | 現在のルールを設定(検証時に `MLCore` が使用) |
|
|
213
|
+
| `getTokenList()` | ソース再構築用の全トークンを返す |
|
|
214
|
+
| `searchNodeByLocation(line, col)` | 指定したソース位置のノードを検索 |
|
|
215
|
+
| `getAccessibilityProp(node)` | ARIA アクセシビリティプロパティを計算(`MLElement.getAccessibleName()` のキャッシュ経由でアクセシブルネームを取得) |
|
|
216
|
+
| `toString(fixed?)` | ソースコードを再構築(オプションで修正適用) |
|
|
215
217
|
|
|
216
218
|
## MLElement
|
|
217
219
|
|
|
@@ -232,15 +234,16 @@ MLToken<A extends MLASTToken>
|
|
|
232
234
|
|
|
233
235
|
### 主要メソッド
|
|
234
236
|
|
|
235
|
-
| メソッド | 説明
|
|
236
|
-
| ---------------------------- |
|
|
237
|
-
| `getAttribute(name)` | 属性値または `null` を返す
|
|
238
|
-
| `getAttributeToken(name)` | 名前付き属性の `MLAttr[]` を返す
|
|
239
|
-
| `hasAttribute(name)` | 属性の存在を確認
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
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)` | マッチするすべての子孫を検索 |
|
|
244
247
|
|
|
245
248
|
## ルールシステム
|
|
246
249
|
|
|
@@ -248,13 +251,17 @@ MLToken<A extends MLASTToken>
|
|
|
248
251
|
|
|
249
252
|
`MLRule<T, O>` はリンティングルールを検証およびオプションの修正ロジックとともにカプセル化します。
|
|
250
253
|
|
|
251
|
-
| プロパティ/メソッド | 説明
|
|
252
|
-
| --------------------------------- |
|
|
253
|
-
| `name` | ルール識別子(例:`"attr-duplication"`)
|
|
254
|
-
| `defaultSeverity` | デフォルトの重大度レベル
|
|
255
|
-
| `defaultValue` / `defaultOptions` | デフォルト設定
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
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` に正規化 |
|
|
258
265
|
|
|
259
266
|
### RuleSeed
|
|
260
267
|
|
|
@@ -310,6 +317,56 @@ flowchart TD
|
|
|
310
317
|
G --> H["Violation[] を返す"]
|
|
311
318
|
```
|
|
312
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
|
+
|
|
313
370
|
## Pretender システム
|
|
314
371
|
|
|
315
372
|
pretender システムにより、コンポーネントをリンティング時にセマンティック HTML 要素として扱うことができます。これにより、ルールがカスタムコンポーネント(例:`<MyButton>`)を標準要素(例:`<button>`)として検証できます。
|
|
@@ -338,16 +395,16 @@ type Pretender = {
|
|
|
338
395
|
|
|
339
396
|
テンプレートエンジン(Pug, EJS, Nunjucks など)はプリプロセッサ固有のブロックを生成し、`MLBlock` ノードで表現されます。これらのブロックは子ノードを条件付きでラップできます:
|
|
340
397
|
|
|
341
|
-
| `
|
|
342
|
-
|
|
|
343
|
-
| `'if
|
|
344
|
-
| `'if:else'`
|
|
345
|
-
| `'
|
|
346
|
-
| `'each
|
|
347
|
-
| `'
|
|
348
|
-
| `'switch:
|
|
349
|
-
| `'switch:
|
|
350
|
-
| `'
|
|
398
|
+
| `blockBehavior.type` | テンプレート構文 | 説明 |
|
|
399
|
+
| -------------------- | ----------------- | ------------------ |
|
|
400
|
+
| `'if'` | `{% if %}` | 条件ブロックの開始 |
|
|
401
|
+
| `'if:else'` | `{% else %}` | 代替分岐 |
|
|
402
|
+
| `'end'` | `{% endif %}` | 条件ブロックの終了 |
|
|
403
|
+
| `'each'` | `{% for %}` | ループの開始 |
|
|
404
|
+
| `'end'` | `{% endfor %}` | ループの終了 |
|
|
405
|
+
| `'switch:case'` | `{% switch %}` | switch の開始 |
|
|
406
|
+
| `'switch:default'` | `{% case %}` | switch ケース |
|
|
407
|
+
| `'end'` | `{% endswitch %}` | switch の終了 |
|
|
351
408
|
|
|
352
409
|
`MLNode.conditionalChildNodes()` は `NodeListOf` 配列の配列を返します(条件分岐ごとに 1 つ)。これにより、ルールは各分岐を独立して分析できます。
|
|
353
410
|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -44,6 +44,8 @@ src/
|
|
|
44
44
|
│ └── manipulations/
|
|
45
45
|
│ ├── child-node-methods.ts — ChildNode interface stubs
|
|
46
46
|
│ └── get-children.ts — Element children extraction
|
|
47
|
+
├── virtual-rule.ts — Named nodeRule expansion (expandNamedNodeRules)
|
|
48
|
+
├── virtual-rule.spec.ts — Virtual rule unit tests
|
|
47
49
|
├── ml-rule/
|
|
48
50
|
│ ├── ml-rule.ts — MLRule class (rule execution)
|
|
49
51
|
│ ├── ml-rule-context.ts — MLRuleContext (report collection)
|
|
@@ -173,7 +175,7 @@ MLToken<A extends MLASTToken>
|
|
|
173
175
|
| `MLText` | `Text` | Text nodes, `isWhitespace()`, `isRawTextElementContent()` |
|
|
174
176
|
| `MLComment` | `Comment` | Comment nodes with `textContent` |
|
|
175
177
|
| `MLDocumentType` | `DocumentType` | `<!DOCTYPE>` with `name`, `publicId`, `systemId` |
|
|
176
|
-
| `MLBlock` | — | Preprocessor-specific blocks (if/each/switch), `
|
|
178
|
+
| `MLBlock` | — | Preprocessor-specific blocks (if/each/switch), `blockBehavior`, `isTransparent` |
|
|
177
179
|
| `MLElementCloseTag` | — | Close tag paired with its open tag element |
|
|
178
180
|
| `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
|
|
179
181
|
| `MLElement` | `Element` | Attributes, selectors, namespaces, pretender context, `elementType`, `closeTag` |
|
|
@@ -204,14 +206,14 @@ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tupl
|
|
|
204
206
|
|
|
205
207
|
### Key Methods
|
|
206
208
|
|
|
207
|
-
| Method | Description
|
|
208
|
-
| --------------------------------- |
|
|
209
|
-
| `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`)
|
|
210
|
-
| `setRule(rule)` | Sets the current rule, used by `MLCore` during verification
|
|
211
|
-
| `getTokenList()` | Returns all tokens for source reconstruction
|
|
212
|
-
| `searchNodeByLocation(line, col)` | Finds the node at a given source position
|
|
213
|
-
| `getAccessibilityProp(node)` | Computes ARIA accessibility properties
|
|
214
|
-
| `toString(fixed?)` | Reconstructs source code, optionally with fixes applied
|
|
209
|
+
| Method | Description |
|
|
210
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
211
|
+
| `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
|
|
212
|
+
| `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
|
|
213
|
+
| `getTokenList()` | Returns all tokens for source reconstruction |
|
|
214
|
+
| `searchNodeByLocation(line, col)` | Finds the node at a given source position |
|
|
215
|
+
| `getAccessibilityProp(node)` | Computes ARIA accessibility properties (delegates to `MLElement.getAccessibleName()` for cached accessible name) |
|
|
216
|
+
| `toString(fixed?)` | Reconstructs source code, optionally with fixes applied |
|
|
215
217
|
|
|
216
218
|
## MLElement
|
|
217
219
|
|
|
@@ -237,6 +239,7 @@ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tupl
|
|
|
237
239
|
| `getAttribute(name)` | Returns attribute value or `null` |
|
|
238
240
|
| `getAttributeToken(name)` | Returns `MLAttr[]` for the named attribute |
|
|
239
241
|
| `hasAttribute(name)` | Checks attribute existence |
|
|
242
|
+
| `getAccessibleName(version)` | Cached accessible name computation (memoized per ARIA version) |
|
|
240
243
|
| `matches(selector)` | CSS selector matching |
|
|
241
244
|
| `matchMLSelector(selector)` | Extended markuplint selector matching (supports `RegexSelector`) |
|
|
242
245
|
| `querySelector(selector)` | Finds first matching descendant |
|
|
@@ -248,13 +251,17 @@ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tupl
|
|
|
248
251
|
|
|
249
252
|
`MLRule<T, O>` encapsulates a linting rule with verification and optional fix logic.
|
|
250
253
|
|
|
251
|
-
| Property/Method | Description
|
|
252
|
-
| --------------------------------- |
|
|
253
|
-
| `name` | Rule identifier (e.g., `"attr-duplication"`)
|
|
254
|
-
| `defaultSeverity` | Default severity level
|
|
255
|
-
| `defaultValue` / `defaultOptions` | Default configuration
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
254
|
+
| Property/Method | Description |
|
|
255
|
+
| --------------------------------- | --------------------------------------------------------------------------- |
|
|
256
|
+
| `name` | Rule identifier (e.g., `"attr-duplication"`) |
|
|
257
|
+
| `defaultSeverity` | Default severity level |
|
|
258
|
+
| `defaultValue` / `defaultOptions` | Default configuration |
|
|
259
|
+
| `baseRuleId` | For virtual rules: the base rule's name (e.g., `"required-attr"`) |
|
|
260
|
+
| `groupName` | For multi-entry virtual rules: group name for batch disable |
|
|
261
|
+
| `specConformance` | For virtual rules: `'normative'` or `'non-normative'` (from named nodeRule) |
|
|
262
|
+
| `verify(document, locale, fix)` | Executes the rule and returns violations |
|
|
263
|
+
| `createAlias(name, options?)` | Creates a virtual rule that reuses this rule's verify/fix logic |
|
|
264
|
+
| `optimizeOption(settings)` | Normalizes raw rule configuration into `RuleInfo` |
|
|
258
265
|
|
|
259
266
|
### RuleSeed
|
|
260
267
|
|
|
@@ -310,6 +317,56 @@ flowchart TD
|
|
|
310
317
|
G --> H["Return Violation[]"]
|
|
311
318
|
```
|
|
312
319
|
|
|
320
|
+
### Virtual Rule System
|
|
321
|
+
|
|
322
|
+
Source: `src/virtual-rule.ts`
|
|
323
|
+
|
|
324
|
+
> **Terminology policy**: "Virtual rule" is an **internal implementation term** for contributors only. User-facing documentation (website, migration guides, README) must use **"named rule"** instead. From a config user's perspective, there are only two concepts: a **base rule** (e.g., `required-attr`) and a **named rule** (e.g., `a11y/html-lang`). The internal mechanics of `MLRule` aliasing should not be exposed.
|
|
325
|
+
|
|
326
|
+
Virtual rules are independent `MLRule` instances created from **named nodeRules** — nodeRule entries with a `name` property containing `/` (e.g., `"a11y/html-lang"`). This enables per-check control: each virtual rule can be independently enabled/disabled via `rules["alias/name"]: false`.
|
|
327
|
+
|
|
328
|
+
#### Named NodeRule Expansion
|
|
329
|
+
|
|
330
|
+
`expandNamedNodeRules()` converts named nodeRules (and childNodeRules) into virtual rules during `MLCore` construction:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
Named nodeRule (config) Virtual MLRule (runtime)
|
|
334
|
+
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
335
|
+
│ name: "a11y/html-lang" │ │ name: "a11y/html-lang" │
|
|
336
|
+
│ specConformance: "norm."│ ──────► │ baseRuleId: "required-attr" │
|
|
337
|
+
│ selector: ":where(html)"│ │ specConformance: metadata│
|
|
338
|
+
│ rules: │ │ verify/fix: from base │
|
|
339
|
+
│ required-attr: [lang] │ └──────────────────────────┘
|
|
340
|
+
└─────────────────────────┘
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Key behaviors:
|
|
344
|
+
|
|
345
|
+
- **False entry separation**: `false` entries in `rules` are automatically separated into unnamed nodeRules, preserving their semantics as base-rule specificity overrides
|
|
346
|
+
- **Multi-entry support**: Named nodeRules with 2+ non-false entries create derived names (`name/baseRuleName`) with a `groupName` for group disable
|
|
347
|
+
- **Metadata**: `specConformance` is attached to the virtual rule as metadata for downstream tools and reporting
|
|
348
|
+
- **Hot-reload**: Pre-expansion nodeRules are preserved in `#originalNodeRules` / `#originalChildNodeRules` so `update()` can re-expand them
|
|
349
|
+
|
|
350
|
+
#### Why `specConformance` Is Restricted to Named NodeRules
|
|
351
|
+
|
|
352
|
+
`specConformance` is intentionally available **only on named nodeRules** (in presets), not on regular built-in rules. The design rationale:
|
|
353
|
+
|
|
354
|
+
1. **Built-in rules already have correct default severity.** Rules like `permitted-contents` or `required-attr` are inherently normative (they enforce WHATWG MUST requirements), and their `defaultSeverity` is already set to `'error'`. There is no need for a separate `specConformance` flag — the severity is baked in.
|
|
355
|
+
|
|
356
|
+
2. **Named nodeRules are preset-authored spec interpretations.** When a preset like `preset.html-standard.jsonc` creates a named nodeRule `"html-standard/head-charset-utf8"`, the preset author is expressing a specific spec requirement as a check. `specConformance` lets the author declare the RFC 2119 keyword strength of that requirement, so downstream tools and reports can identify which violations originate from spec requirements and at what normative level.
|
|
357
|
+
|
|
358
|
+
3. **Users should not set `specConformance` on their own rules.** A user-defined nodeRule for a custom component (e.g., validating `<MyComponent>` props) is not a spec conformance check — it is a project convention. Allowing `specConformance` on arbitrary user config would blur the distinction between "the HTML spec requires this" and "our team prefers this". The `name` property (which requires `/`) serves as a gatekeeper: only named nodeRules can carry `specConformance`, and named nodeRules are designed for preset authors who understand the spec.
|
|
359
|
+
|
|
360
|
+
In summary: `specConformance` is a **preset-level annotation** that provides metadata about which spec requirements a check enforces. Built-in rules handle their own severity via `defaultSeverity`. User-defined rules express severity directly via the `severity` field in rule config.
|
|
361
|
+
|
|
362
|
+
#### Virtual Rule Disable
|
|
363
|
+
|
|
364
|
+
Virtual rules can be disabled at three levels in the `rules` config:
|
|
365
|
+
|
|
366
|
+
1. **Exact name**: `rules["a11y/html-lang"]: false`
|
|
367
|
+
2. **Group disable**: `rules["custom/multi"]: false` (for multi-entry named nodeRules)
|
|
368
|
+
3. **Namespace wildcard**: `rules["a11y/*"]: false` (disables all virtual rules starting with `a11y/`)
|
|
369
|
+
|
|
313
370
|
## Pretender System
|
|
314
371
|
|
|
315
372
|
The pretender system allows components to be treated as semantic HTML elements during linting. This enables rules to validate custom components (e.g., `<MyButton>`) as if they were standard elements (e.g., `<button>`).
|
|
@@ -338,18 +395,18 @@ type Pretender = {
|
|
|
338
395
|
|
|
339
396
|
Template engines (Pug, EJS, Nunjucks, etc.) produce preprocessor-specific blocks represented by `MLBlock` nodes. These blocks can wrap child nodes conditionally:
|
|
340
397
|
|
|
341
|
-
| `
|
|
342
|
-
|
|
|
343
|
-
| `'if
|
|
344
|
-
| `'if:else'`
|
|
345
|
-
| `'
|
|
346
|
-
| `'each
|
|
347
|
-
| `'
|
|
348
|
-
| `'switch:
|
|
349
|
-
| `'switch:
|
|
350
|
-
| `'
|
|
351
|
-
|
|
352
|
-
`MLNode.conditionalChildNodes()` returns an array of `NodeListOf` arrays — one per conditional branch — so rules can analyze each branch independently.
|
|
398
|
+
| `blockBehavior.type` | Template Construct | Description |
|
|
399
|
+
| -------------------- | ------------------ | -------------------------- |
|
|
400
|
+
| `'if'` | `{% if %}` | Start of conditional block |
|
|
401
|
+
| `'if:else'` | `{% else %}` | Alternative branch |
|
|
402
|
+
| `'end'` | `{% endif %}` | End of conditional block |
|
|
403
|
+
| `'each'` | `{% for %}` | Start of loop |
|
|
404
|
+
| `'end'` | `{% endfor %}` | End of loop |
|
|
405
|
+
| `'switch:case'` | `{% switch %}` | Start of switch |
|
|
406
|
+
| `'switch:default'` | `{% case %}` | Switch case |
|
|
407
|
+
| `'end'` | `{% endswitch %}` | End of switch |
|
|
408
|
+
|
|
409
|
+
`MLNode.conditionalChildNodes()` returns an array of `NodeListOf` arrays — one per conditional branch — so rules can analyze each branch independently. Note that `'each'` blocks do not start a new conditional mode (`'if'` or `'switch'`); they are flattened into `childNodes` and their content is treated as always-present rather than as an alternative branch. Only `'if'`/`'if:elseif'` and `'switch:case'` start new modes that generate null sentinels for the "empty branch" case.
|
|
353
410
|
|
|
354
411
|
## Plugin System
|
|
355
412
|
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,60 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.0.0-alpha.1](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2026-02-22)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @markuplint/ml-core
|
|
9
|
+
|
|
10
|
+
# [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- **ml-core:** improve detection of namespace ([5b507ad](https://github.com/markuplint/markuplint/commit/5b507ad7c19c5015b8ce587845d901e31dfa6518))
|
|
15
|
+
- treat orphaned end tags as bogus instead of plain text ([#1575](https://github.com/markuplint/markuplint/issues/1575)) ([557199a](https://github.com/markuplint/markuplint/commit/557199a6960ab35573a544f9a33c00e98eb9967e))
|
|
16
|
+
- use explicit `export type` for type-only re-exports ([7c77c05](https://github.com/markuplint/markuplint/commit/7c77c05619518c8d18a183132040f5b2cd0ab6ec))
|
|
17
|
+
|
|
18
|
+
- feat(ml-core)!: adapt DOM layer to simplified AST types ([5d92f2b](https://github.com/markuplint/markuplint/commit/5d92f2be75ce0d45823fb26f72588aecee278ba3))
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
- delete htmx-parser, simplify alpine-parser, add migration guide and tests ([f8dbb09](https://github.com/markuplint/markuplint/commit/f8dbb090707d8cfbf3d859a9b868b2087064f89b))
|
|
23
|
+
- **ml-core:** add directive and IDL resolution to MLAttr constructor ([ba0ad66](https://github.com/markuplint/markuplint/commit/ba0ad66585c022cdb34fda8a8191bcc9af078e07))
|
|
24
|
+
- **ml-core:** add expandNamedRules for named rule groups in rules section ([7eed355](https://github.com/markuplint/markuplint/commit/7eed355075cee90b17a79c0f8a5b18213d1ce54e))
|
|
25
|
+
- **ml-core:** implement VirtualRule system for named nodeRules ([864f51d](https://github.com/markuplint/markuplint/commit/864f51d54dba26c6af2bc45eea3566db5f7d8e26))
|
|
26
|
+
- **ml-core:** require defaultValue for non-boolean rule types in createRule ([6c99908](https://github.com/markuplint/markuplint/commit/6c999087feff4fb8906cf47d564ee08ca8e5f450)), closes [#808](https://github.com/markuplint/markuplint/issues/808)
|
|
27
|
+
- **ml-core:** the each block skips linting in childNodes ([d5ca83d](https://github.com/markuplint/markuplint/commit/d5ca83d5ec6dc9b2f40b5d6599b07cc4746f3dca))
|
|
28
|
+
- **ml-core:** wire ruleCommonSettings through MLCore to Document ([28bb176](https://github.com/markuplint/markuplint/commit/28bb17601b983b3789b2ae200bd77ad887905cda))
|
|
29
|
+
- **ml-spec:** add declarative directivePatterns for parser-less framework support ([ceb9aa6](https://github.com/markuplint/markuplint/commit/ceb9aa67048e3a058b40a9e4d91eb903c8ff1861))
|
|
30
|
+
|
|
31
|
+
### Performance Improvements
|
|
32
|
+
|
|
33
|
+
- **ml-core:** add memoization cache to MLElement.getAccessibleName() ([cdbe289](https://github.com/markuplint/markuplint/commit/cdbe289755312ee30e3f02171f42bf2c00412eea)), closes [#2179](https://github.com/markuplint/markuplint/issues/2179)
|
|
34
|
+
|
|
35
|
+
### BREAKING CHANGES
|
|
36
|
+
|
|
37
|
+
- Multiple breaking changes to DOM API:
|
|
38
|
+
|
|
39
|
+
MLToken:
|
|
40
|
+
|
|
41
|
+
- Compute end positions via getEndCol/getEndLine helpers
|
|
42
|
+
instead of storing them as private fields
|
|
43
|
+
- Use \_astToken.offset/line/col directly
|
|
44
|
+
|
|
45
|
+
MLElement:
|
|
46
|
+
|
|
47
|
+
- Remove selfClosingSolidus property
|
|
48
|
+
- Add blockBehavior: MLASTBlockBehavior | null
|
|
49
|
+
|
|
50
|
+
MLBlock:
|
|
51
|
+
|
|
52
|
+
- Replace conditionalType with blockBehavior property
|
|
53
|
+
|
|
54
|
+
Node traversal:
|
|
55
|
+
|
|
56
|
+
- Use blockBehavior?.type instead of conditionalType
|
|
57
|
+
|
|
58
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
59
|
+
|
|
6
60
|
## [4.13.3](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.2...@markuplint/ml-core@4.13.3) (2026-02-10)
|
|
7
61
|
|
|
8
62
|
**Note:** Version bump only for package @markuplint/ml-core
|
|
@@ -32,17 +32,18 @@ constructor(params: MLCoreParams)
|
|
|
32
32
|
|
|
33
33
|
`MLFabric` 型はリンティング設定全体を定義します:
|
|
34
34
|
|
|
35
|
-
| フィールド
|
|
36
|
-
|
|
|
37
|
-
| `parser`
|
|
38
|
-
| `ruleset`
|
|
39
|
-
| `rules`
|
|
40
|
-
| `locale`
|
|
41
|
-
| `schemas`
|
|
42
|
-
| `parserOptions`
|
|
43
|
-
| `severity`
|
|
44
|
-
| `pretenders`
|
|
45
|
-
| `
|
|
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[]` | 報告する設定エラー |
|
|
46
47
|
|
|
47
48
|
### プロパティ
|
|
48
49
|
|
|
@@ -52,8 +53,9 @@ constructor(params: MLCoreParams)
|
|
|
52
53
|
|
|
53
54
|
### 構築フロー
|
|
54
55
|
|
|
55
|
-
1. `
|
|
56
|
-
2. `
|
|
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、オプション付き)
|
|
57
59
|
|
|
58
60
|
パースが失敗した場合、`document` は `MLDocument` の代わりに `ParserError` を保持します。
|
|
59
61
|
|
|
@@ -76,7 +78,7 @@ constructor(params: MLCoreParams)
|
|
|
76
78
|
パースされた AST から `MLDocument` を作成します:
|
|
77
79
|
|
|
78
80
|
```typescript
|
|
79
|
-
new Document(ast, ruleset, schemas, {
|
|
81
|
+
new Document(ast, ruleset, schemas, ruleCommonSettings, {
|
|
80
82
|
filename,
|
|
81
83
|
endTag,
|
|
82
84
|
booleanish,
|
|
@@ -124,6 +126,7 @@ flowchart TD
|
|
|
124
126
|
2. **未定義ルールの検出**: `setRuleNames`(設定内のルール)と `definedRuleName`(実際にロードされたルール)を比較。未定義ルールに対して `config-error` 警告を報告
|
|
125
127
|
3. **設定エラー**: `configErrors` 配列を違反に変換
|
|
126
128
|
4. **ルールループ**: 各ルールに対して:
|
|
129
|
+
- **仮想ルールの無効化チェック** — `rule.baseRuleId` がある場合、3つの条件を確認: 完全一致(`rules["alias"]: false`)、グループ無効化(`rules["groupName"]: false`)、名前空間ワイルドカード(`rules["scope/*"]: false`)。いずれかに該当した場合はスキップ。
|
|
127
130
|
- `rule.getRuleInfo(ruleset, rule.name)` で有効性をチェック
|
|
128
131
|
- `disabled && nodeRules.length === 0 && childNodeRules.length === 0` → スキップ
|
|
129
132
|
- `rule.verify(document, locale, fix)` — ルールを実行
|
|
@@ -154,6 +157,7 @@ flowchart TD
|
|
|
154
157
|
|
|
155
158
|
リンティング設定の部分更新:
|
|
156
159
|
|
|
160
|
+
- Named nodeRules を仮想ルールに再展開。`ruleset` が提供されていない場合、`#originalNodeRules` / `#originalChildNodeRules`(展開前のコピー)をソースとして使用し、仮想ルール化で変換済みのエントリを失うことを防ぐ。
|
|
157
161
|
- `parserOptions` が変更された場合 → 完全な再パース(`_parse()` + `_createDocument()`)
|
|
158
162
|
- それ以外 → `_createDocument()` のみ(既存の AST を再利用)
|
|
159
163
|
|
package/docs/linting-pipeline.md
CHANGED
|
@@ -32,17 +32,18 @@ Where `MLCoreParams` extends `MLFabric` with:
|
|
|
32
32
|
|
|
33
33
|
The `MLFabric` type defines the full linting configuration:
|
|
34
34
|
|
|
35
|
-
| Field
|
|
36
|
-
|
|
|
37
|
-
| `parser`
|
|
38
|
-
| `ruleset`
|
|
39
|
-
| `rules`
|
|
40
|
-
| `locale`
|
|
41
|
-
| `schemas`
|
|
42
|
-
| `parserOptions`
|
|
43
|
-
| `severity`
|
|
44
|
-
| `pretenders`
|
|
45
|
-
| `
|
|
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 |
|
|
46
47
|
|
|
47
48
|
### Properties
|
|
48
49
|
|
|
@@ -52,8 +53,9 @@ The `MLFabric` type defines the full linting configuration:
|
|
|
52
53
|
|
|
53
54
|
### Construction Flow
|
|
54
55
|
|
|
55
|
-
1. `
|
|
56
|
-
2. `
|
|
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
|
|
57
59
|
|
|
58
60
|
If parsing fails, `document` holds a `ParserError` instead of `MLDocument`.
|
|
59
61
|
|
|
@@ -76,7 +78,7 @@ If the parser throws:
|
|
|
76
78
|
Creates `MLDocument` from the parsed AST:
|
|
77
79
|
|
|
78
80
|
```typescript
|
|
79
|
-
new Document(ast, ruleset, schemas, {
|
|
81
|
+
new Document(ast, ruleset, schemas, ruleCommonSettings, {
|
|
80
82
|
filename,
|
|
81
83
|
endTag,
|
|
82
84
|
booleanish,
|
|
@@ -124,6 +126,7 @@ flowchart TD
|
|
|
124
126
|
2. **Undefined rule detection**: Compares `setRuleNames` (rules in config) vs `definedRuleName` (rules actually loaded). Reports `config-error` warnings for undefined rules
|
|
125
127
|
3. **Config errors**: Converts `configErrors` array to violations
|
|
126
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.
|
|
127
130
|
- `rule.getRuleInfo(ruleset, rule.name)` checks enablement
|
|
128
131
|
- If `disabled && nodeRules.length === 0 && childNodeRules.length === 0` → skip
|
|
129
132
|
- `rule.verify(document, locale, fix)` — executes the rule
|
|
@@ -154,6 +157,7 @@ Re-parses with new source code:
|
|
|
154
157
|
|
|
155
158
|
Partially updates the linting configuration:
|
|
156
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.
|
|
157
161
|
- If `parserOptions` changed → full re-parse (`_parse()` + `_createDocument()`)
|
|
158
162
|
- Otherwise → only `_createDocument()` (reuses existing AST)
|
|
159
163
|
|
package/docs/maintenance.ja.md
CHANGED
|
@@ -206,5 +206,5 @@ pretender は `MLDocument` コンストラクタ(`src/ml-dom/node/document.ts`
|
|
|
206
206
|
1. `document.debugMap()` の出力を期待される構造と比較
|
|
207
207
|
2. パーサー出力を確認:同じソースをパーサーで直接パースして AST を検査
|
|
208
208
|
3. ゴースト要素(暗黙の HTML/head/body)が正しく処理されているか確認
|
|
209
|
-
4. テンプレートエンジンの場合、`
|
|
209
|
+
4. テンプレートエンジンの場合、`blockBehavior` 付きの `MLBlock` ノードが期待される子をラップしているか確認
|
|
210
210
|
5. `document.nodeList` でフラットノードリストを検査し、親子関係を確認
|
package/docs/maintenance.md
CHANGED
|
@@ -206,5 +206,5 @@ Changes to `@markuplint/ml-core` can affect:
|
|
|
206
206
|
1. Compare `document.debugMap()` output with expected structure
|
|
207
207
|
2. Check the parser output: parse the same source with the parser directly and inspect the AST
|
|
208
208
|
3. Verify ghost elements (implicit HTML/head/body) are handled correctly
|
|
209
|
-
4. For template engines, check that `MLBlock` nodes with `
|
|
209
|
+
4. For template engines, check that `MLBlock` nodes with `blockBehavior` wrap the expected children
|
|
210
210
|
5. Use `document.nodeList` to inspect the flat node list and verify parent-child relationships
|
package/docs/ml-dom/attr.ja.md
CHANGED
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
- `astToken.potentialName` が存在する場合 → `name` として使用する。そうでなければ `nameNode.raw` を使用する
|
|
25
25
|
- `astToken.potentialValue` が存在する場合 → `value` として使用する。そうでなければ `valueNode.raw` を使用する
|
|
26
26
|
|
|
27
|
+
### ディレクティブパターン解決
|
|
28
|
+
|
|
29
|
+
パーサーが `astToken.potentialName` を設定していない場合、`MLAttr` コンストラクタは spec(例: `@markuplint/vue-spec`、`@markuplint/svelte-spec`)からの `directivePatterns` をチェックします。パターンが存在する場合、生の属性名とコンパイル済みパターンで `resolveDirective()` が呼び出されます。最初にマッチしたパターンが `potentialName`、`isDynamicValue`、`isDirective`、`isDuplicatable` を決定します。
|
|
30
|
+
|
|
31
|
+
### IDL 属性名解決
|
|
32
|
+
|
|
33
|
+
ディレクティブパターン解決の後、spec が `acceptedAttrNames` を設定しており(例: `@markuplint/react-spec` では `'idl'`、`@markuplint/svelte-spec` では `'both'`)、属性がディレクティブでない場合、コンストラクタは `@markuplint/parser-utils` の `searchIDLAttribute()` を呼び出して IDL プロパティ名を HTML コンテンツ属性名にマッピングします(例: `className` → `class`、`htmlFor` → `for`)。`'idl'` モードでは、IDL プロパティ名が候補として設定されます(例: `tabindex` → 「`tabIndex` の間違いでは?」)。`'both'` モードでは、コンテンツ名と IDL 名の両方が候補提案なしで受け入れられます。これはパーサーレベルではなくコアレベルの関心事です。
|
|
34
|
+
|
|
27
35
|
## トークン分解
|
|
28
36
|
|
|
29
37
|
各属性は個々の `MLToken` インスタンスに分解されます:
|
package/docs/ml-dom/attr.md
CHANGED
|
@@ -24,6 +24,14 @@ The parser may provide resolved "potential" names and values for attributes wher
|
|
|
24
24
|
- If `astToken.potentialName` exists → use it as `name`; otherwise use `nameNode.raw`
|
|
25
25
|
- If `astToken.potentialValue` exists → use it as `value`; otherwise use `valueNode.raw`
|
|
26
26
|
|
|
27
|
+
### Directive Pattern Resolution
|
|
28
|
+
|
|
29
|
+
When `astToken.potentialName` is not set by the parser, the `MLAttr` constructor checks for `directivePatterns` from the spec (e.g., `@markuplint/vue-spec`, `@markuplint/svelte-spec`). If patterns exist, `resolveDirective()` is called with the raw attribute name and the compiled patterns. The first matching pattern determines `potentialName`, `isDynamicValue`, `isDirective`, and `isDuplicatable`.
|
|
30
|
+
|
|
31
|
+
### IDL Attribute Name Resolution
|
|
32
|
+
|
|
33
|
+
After directive pattern resolution, if the spec sets `acceptedAttrNames` (e.g., `'idl'` in `@markuplint/react-spec`, `'both'` in `@markuplint/svelte-spec`) and the attribute is not a directive, the constructor calls `searchIDLAttribute()` from `@markuplint/parser-utils` to map IDL property names to HTML content attribute names (e.g., `className` -> `class`, `htmlFor` -> `for`). In `'idl'` mode, the IDL property name is set as a candidate for suggestion (e.g., `tabindex` -> "Did you mean `tabIndex`?"). In `'both'` mode, both content and IDL names are accepted without suggestions. This is a core-level concern, not a parser-level one.
|
|
34
|
+
|
|
27
35
|
## Token Decomposition
|
|
28
36
|
|
|
29
37
|
Each attribute is decomposed into individual `MLToken` instances:
|