@markuplint/ml-core 5.0.0-alpha.0 → 5.0.0-alpha.2

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 (41) hide show
  1. package/ARCHITECTURE.ja.md +125 -5
  2. package/ARCHITECTURE.md +125 -5
  3. package/CHANGELOG.md +18 -0
  4. package/docs/ml-dom/attr.ja.md +1 -1
  5. package/docs/ml-dom/attr.md +1 -1
  6. package/docs/ml-dom/document.ja.md +7 -14
  7. package/docs/ml-dom/document.md +7 -14
  8. package/docs/ml-dom/element.ja.md +9 -24
  9. package/docs/ml-dom/element.md +9 -24
  10. package/docs/rule-system.ja.md +1 -1
  11. package/docs/rule-system.md +1 -1
  12. package/lib/fix-applier.d.ts +30 -0
  13. package/lib/fix-applier.js +70 -0
  14. package/lib/index.d.ts +2 -0
  15. package/lib/index.js +1 -0
  16. package/lib/ml-core.d.ts +14 -2
  17. package/lib/ml-core.js +25 -4
  18. package/lib/ml-dom/helper/get-indent.d.ts +0 -1
  19. package/lib/ml-dom/helper/get-indent.js +5 -18
  20. package/lib/ml-dom/node/attr.d.ts +2 -13
  21. package/lib/ml-dom/node/attr.js +6 -38
  22. package/lib/ml-dom/node/document.d.ts +2 -13
  23. package/lib/ml-dom/node/document.js +3 -41
  24. package/lib/ml-dom/node/element-close-tag.d.ts +1 -1
  25. package/lib/ml-dom/node/element-close-tag.js +2 -16
  26. package/lib/ml-dom/node/element.d.ts +2 -19
  27. package/lib/ml-dom/node/element.js +3 -62
  28. package/lib/ml-dom/token/token.d.ts +3 -18
  29. package/lib/ml-dom/token/token.js +7 -28
  30. package/lib/ml-rule/index.d.ts +1 -0
  31. package/lib/ml-rule/index.js +1 -0
  32. package/lib/ml-rule/ml-rule-context.d.ts +2 -30
  33. package/lib/ml-rule/ml-rule-context.js +15 -15
  34. package/lib/ml-rule/ml-rule.d.ts +6 -10
  35. package/lib/ml-rule/ml-rule.js +32 -33
  36. package/lib/ml-rule/rule-fixer.d.ts +25 -0
  37. package/lib/ml-rule/rule-fixer.js +32 -0
  38. package/lib/ml-rule/types.d.ts +1 -2
  39. package/lib/test/index.d.ts +1 -10
  40. package/lib/test/index.js +0 -11
  41. package/package.json +12 -12
@@ -49,9 +49,11 @@ src/
49
49
  ├── ml-rule/
50
50
  │ ├── ml-rule.ts — MLRule クラス(ルール実行)
51
51
  │ ├── ml-rule-context.ts — MLRuleContext(レポート収集)
52
+ │ ├── rule-fixer.ts — RuleFixer(fix コールバック用の TextEdit ビルダー)
52
53
  │ ├── create-rule.ts — createRule ファクトリ
53
54
  │ ├── create-test-rule.ts — テスト用ルールファクトリ
54
55
  │ └── types.ts — RuleSeed, Checker 型
56
+ ├── fix-applier.ts — applyFixes(重複検出付き TextEdit 適用エンジン)
55
57
  ├── ruleset/
56
58
  │ └── index.ts — Ruleset クラス(rules + nodeRules + childNodeRules)
57
59
  ├── plugin/
@@ -143,8 +145,8 @@ flowchart LR
143
145
 
144
146
  1. **パース**: `MLCore` は設定されたパーサー(`MLParser`)を呼び出し、`MLASTDocument` を生成
145
147
  2. **ドキュメント作成**: AST を `MLDocument` でラップし、`createNode()` ファクトリで MLDOM ツリー全体を構築。`RuleMapper` が各ノードのルール設定を解決
146
- 3. **検証**: 各 `MLRule` に対して、`document.setRule(rule)` を呼び出した後 `rule.verify(document)` を実行。ルールは `document.walkOn()` で対象ノードを走査し、`MLRuleContext` を通じて違反を報告
147
- 4. **修正**(オプション): `fix=true` の場合、ルールが `node.fix()` でトークン内容を変更。`document.toString(true)` で修正後のソースを生成
148
+ 3. **検証**: 各 `MLRule` に対して、`document.setRule(rule)` を呼び出した後 `rule.verify(document)` を実行。ルールは `document.walkOn()` で対象ノードを走査し、`MLRuleContext` を通じて違反を報告。ルールは report にインライン `fix` コールバックを付与して `TextEdit` オブジェクトを返す
149
+ 4. **修正**(オプション): `fix=true` の場合、report fix コールバックが `RuleFixer` を使って `TextEdit[]` を生成。`FixApplier.applyFixes(sourceCode, fixes)` が全編集をソーステキストに一括適用(重複検出付き)
148
150
 
149
151
  ## MLDOM クラス階層
150
152
 
@@ -210,10 +212,9 @@ MLToken<A extends MLASTToken>
210
212
  | --------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
211
213
  | `walkOn(type, walker)` | 指定した型(`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`)のノードを走査 |
212
214
  | `setRule(rule)` | 現在のルールを設定(検証時に `MLCore` が使用) |
213
- | `getTokenList()` | ソース再構築用の全トークンを返す |
214
215
  | `searchNodeByLocation(line, col)` | 指定したソース位置のノードを検索 |
215
216
  | `getAccessibilityProp(node)` | ARIA アクセシビリティプロパティを計算(`MLElement.getAccessibleName()` のキャッシュ経由でアクセシブルネームを取得) |
216
- | `toString(fixed?)` | ソースコードを再構築(オプションで修正適用) |
217
+ | `toString()` | ドキュメントの生のソースコードを返す |
217
218
 
218
219
  ## MLElement
219
220
 
@@ -292,7 +293,7 @@ type RuleSeed<T, O> = {
292
293
  - `translate` / `t` — ロケール対応のメッセージ翻訳
293
294
  - `report(report)` — ノード、メッセージ、オプションの修正とともに違反を報告
294
295
 
295
- `provide()` メソッドは `RuleSeed.verify()` `RuleSeed.fix()` に渡されるコンテキストオブジェクトを返します。
296
+ `provide()` メソッドは `RuleSeed.verify()` に渡されるコンテキストオブジェクトを返します。自動修正ロジックは、個々の `report()` 呼び出しのインライン `fix` コールバックとして提供され、独立したライフサイクルメソッドではありません。
296
297
 
297
298
  ### ルール設定の解決
298
299
 
@@ -367,6 +368,125 @@ Named nodeRule(設定) 仮想 MLRule(ランタイム)
367
368
  2. **グループ無効化**: `rules["custom/multi"]: false`(複数エントリの named nodeRule 用)
368
369
  3. **名前空間ワイルドカード**: `rules["a11y/*"]: false`(`a11y/` で始まるすべての仮想ルールを無効化)
369
370
 
371
+ ## 自動修正(Autofix)システム
372
+
373
+ 自動修正システムは、ルールが違反に対する自動修正を提供する仕組みです。**RuleFixer**(TextEdit ビルダー)、**fix コールバック**(ルール作者が記述するロジック)、**FixApplier**(編集適用エンジン)の 3 つのコンポーネントで構成されます。
374
+
375
+ ### 自動修正のデータフロー
376
+
377
+ ```mermaid
378
+ flowchart LR
379
+ subgraph RulePhase ["ルールフェーズ"]
380
+ report["context.report({\n message,\n scope,\n fix: コールバック\n})"]
381
+ end
382
+
383
+ subgraph FixPhase ["Fix コールバック実行"]
384
+ callback["fix(fixer) → TextEdit[]"]
385
+ fixer["RuleFixer\n(共有インスタンス)"]
386
+ callback --> fixer
387
+ end
388
+
389
+ subgraph ApplyPhase ["適用フェーズ"]
390
+ fixdata["FixData\n{ edits: TextEdit[] }"]
391
+ applier["applyFixes(\n sourceCode,\n allFixes\n)"]
392
+ output["fixedCode"]
393
+ fixdata --> applier --> output
394
+ end
395
+
396
+ report --> callback
397
+ fixer --> fixdata
398
+ ```
399
+
400
+ ### Fix コールバックの動作
401
+
402
+ ルールは各 `report()` 呼び出しにオプションの `fix` コールバックを付与します。このコールバックはルール検証中には**実行されず**、保存されるだけです。`MLCore.verify()` に `fix=true` が渡された場合にのみ呼び出されます。
403
+
404
+ ```mermaid
405
+ sequenceDiagram
406
+ participant Rule as ルール (verify)
407
+ participant Ctx as MLRuleContext
408
+ participant MLR as MLRule.verify()
409
+ participant Fixer as RuleFixer
410
+ participant Core as MLCore.verify()
411
+ participant FA as applyFixes()
412
+
413
+ Rule->>Ctx: report({ scope, message, fix })
414
+ Note over Ctx: fix コールバック付きレポートを格納
415
+
416
+ MLR->>Ctx: context.reports
417
+ loop fix コールバックを持つ各レポート
418
+ MLR->>Fixer: report.fix(sharedFixer)
419
+ Fixer-->>MLR: TextEdit | TextEdit[]
420
+ MLR->>MLR: FixData { edits } としてラップ
421
+ end
422
+ MLR-->>Core: Violation[](FixData 付き)
423
+
424
+ Core->>Core: 全 Violation から FixData を収集
425
+ Core->>FA: applyFixes(sourceCode, allFixes)
426
+ FA-->>Core: FixResult { output, applied, skipped }
427
+ ```
428
+
429
+ ### RuleFixer API
430
+
431
+ `RuleFixer` は `IRuleFixer`(`@markuplint/ml-config` で定義)を実装します。**ステートレス**なヘルパーであり、全ルールで 1 つのインスタンスを共有します。各メソッドはソースコードのレンジ置換を記述する `TextEdit` オブジェクトを生成します。
432
+
433
+ | メソッド | 入力 | 生成される TextEdit |
434
+ | --------------------------- | ------------------------------------ | --------------------------------------- |
435
+ | `replaceText(token, text)` | `startOffset` + `raw` を持つトークン | `range: [start, start+len], text` |
436
+ | `replaceRange(range, text)` | 明示的な `[start, end)` レンジ | `range: [start, end], text` |
437
+ | `insertBefore(token, text)` | `startOffset` を持つトークン | `range: [start, start], text`(ゼロ幅) |
438
+ | `insertAfter(token, text)` | `startOffset` + `raw` を持つトークン | `range: [end, end], text`(ゼロ幅) |
439
+ | `remove(token)` | `startOffset` + `raw` を持つトークン | `range: [start, start+len], text: ""` |
440
+ | `removeRange(range)` | 明示的な `[start, end)` レンジ | `range: [start, end], text: ""` |
441
+
442
+ `token` パラメータは `startOffset` と `raw` プロパティを持つ任意のオブジェクトを受け付けます。MLDOM トークン(`MLToken`, `MLAttr` 等)は自然にこの要件を満たします。
443
+
444
+ ### FixApplier アルゴリズム
445
+
446
+ `applyFixes()`(`fix-applier.ts`)は全ルールの `FixData` をマージし、1 パスで適用します:
447
+
448
+ ```mermaid
449
+ flowchart TD
450
+ A["展開: FixData[] → タグ付き TextEdit[]"]
451
+ B["ソート: range start 昇順、\nthen range end 降順"]
452
+ C["逐次適用:\n各 edit の重複をチェック"]
453
+ D{{"edit.start < lastAppliedEnd?"}}
454
+ E["スキップ\n(親 FixData をスキップとしてマーク)"]
455
+ F["適用\n(出力にスプライス)"]
456
+ G["分類: 各 FixData を\napplied または skipped に"]
457
+ H["FixResult を返す\n{ output, applied, skipped }"]
458
+
459
+ A --> B --> C --> D
460
+ D -- Yes --> E --> C
461
+ D -- No --> F --> C
462
+ C -. "全 edit 処理完了" .-> G --> H
463
+ ```
464
+
465
+ 主要な制約:
466
+
467
+ - 1 つの `FixData` 内の edit は互いに重複してはならない
468
+ - `FixData` 間の重複はスキップ機構で処理される
469
+ - `FixData` 内のいずれかの edit がスキップされると、その `FixData` 全体がスキップとして分類される
470
+
471
+ ### 実例: ルールの Fix 実装
472
+
473
+ ```typescript
474
+ // ルールの verify 関数内:
475
+ context.report({
476
+ scope: node,
477
+ message: '属性値にはダブルクォートを使用してください',
478
+ fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
479
+ });
480
+ ```
481
+
482
+ これにより以下の処理が行われます:
483
+
484
+ 1. **レポート** → `MLRuleContext` に格納
485
+ 2. **Fix コールバック** → `(fixer) => fixer.replaceText(token, text)`(まだ呼び出されない)
486
+ 3. **`fix=true` の場合** → 共有 `RuleFixer` でコールバック実行 → `TextEdit` を返す
487
+ 4. **TextEdit** → `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }` としてラップ
488
+ 5. **applyFixes** → ソースコードに置換を適用
489
+
370
490
  ## Pretender システム
371
491
 
372
492
  pretender システムにより、コンポーネントをリンティング時にセマンティック HTML 要素として扱うことができます。これにより、ルールがカスタムコンポーネント(例:`<MyButton>`)を標準要素(例:`<button>`)として検証できます。
package/ARCHITECTURE.md CHANGED
@@ -49,9 +49,11 @@ src/
49
49
  ├── ml-rule/
50
50
  │ ├── ml-rule.ts — MLRule class (rule execution)
51
51
  │ ├── ml-rule-context.ts — MLRuleContext (report collection)
52
+ │ ├── rule-fixer.ts — RuleFixer (TextEdit builder for fix callbacks)
52
53
  │ ├── create-rule.ts — createRule factory
53
54
  │ ├── create-test-rule.ts — Test rule factory
54
55
  │ └── types.ts — RuleSeed, Checker types
56
+ ├── fix-applier.ts — applyFixes (overlap-aware TextEdit applicator)
55
57
  ├── ruleset/
56
58
  │ └── index.ts — Ruleset class (rules + nodeRules + childNodeRules)
57
59
  ├── plugin/
@@ -143,8 +145,8 @@ flowchart LR
143
145
 
144
146
  1. **Parse**: `MLCore` invokes the configured parser (`MLParser`) to produce an `MLASTDocument`
145
147
  2. **Create Document**: The AST is wrapped in an `MLDocument`, which builds the full MLDOM tree via `createNode()` factory. `RuleMapper` resolves rule configuration for every node
146
- 3. **Verify**: For each `MLRule`, the engine calls `document.setRule(rule)` then `rule.verify(document)`. The rule walks relevant nodes via `document.walkOn()` and reports violations through `MLRuleContext`
147
- 4. **Fix** (optional): When `fix=true`, rules may call `node.fix()` to modify token content. `document.toString(true)` produces the fixed source
148
+ 3. **Verify**: For each `MLRule`, the engine calls `document.setRule(rule)` then `rule.verify(document)`. The rule walks relevant nodes via `document.walkOn()` and reports violations through `MLRuleContext`. Rules may attach inline `fix` callbacks to reports that return `TextEdit` objects
149
+ 4. **Fix** (optional): When `fix=true`, fix callbacks on reports are executed via `RuleFixer` to produce `TextEdit[]`. `FixApplier.applyFixes(sourceCode, fixes)` applies all edits to the source text with overlap detection
148
150
 
149
151
  ## MLDOM Class Hierarchy
150
152
 
@@ -210,10 +212,9 @@ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tupl
210
212
  | --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
211
213
  | `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
212
214
  | `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
213
- | `getTokenList()` | Returns all tokens for source reconstruction |
214
215
  | `searchNodeByLocation(line, col)` | Finds the node at a given source position |
215
216
  | `getAccessibilityProp(node)` | Computes ARIA accessibility properties (delegates to `MLElement.getAccessibleName()` for cached accessible name) |
216
- | `toString(fixed?)` | Reconstructs source code, optionally with fixes applied |
217
+ | `toString()` | Returns the raw source code of the document |
217
218
 
218
219
  ## MLElement
219
220
 
@@ -292,7 +293,7 @@ type RuleSeed<T, O> = {
292
293
  - `translate` / `t` — Locale-aware message translator
293
294
  - `report(report)` — Reports a violation with node, message, and optional fix
294
295
 
295
- The `provide()` method returns the context object passed to `RuleSeed.verify()` and `RuleSeed.fix()`.
296
+ The `provide()` method returns the context object passed to `RuleSeed.verify()`. Auto-fix logic is provided as an inline `fix` callback on individual `report()` calls, not as a separate lifecycle method.
296
297
 
297
298
  ### Rule Configuration Resolution
298
299
 
@@ -367,6 +368,125 @@ Virtual rules can be disabled at three levels in the `rules` config:
367
368
  2. **Group disable**: `rules["custom/multi"]: false` (for multi-entry named nodeRules)
368
369
  3. **Namespace wildcard**: `rules["a11y/*"]: false` (disables all virtual rules starting with `a11y/`)
369
370
 
371
+ ## Autofix System
372
+
373
+ The autofix system allows rules to provide automatic fixes for violations. It operates through three components: **RuleFixer** (TextEdit builder), **fix callbacks** (rule-authored logic), and **FixApplier** (edit application engine).
374
+
375
+ ### Autofix Data Flow
376
+
377
+ ```mermaid
378
+ flowchart LR
379
+ subgraph RulePhase ["Rule Phase"]
380
+ report["context.report({\n message,\n scope,\n fix: callback\n})"]
381
+ end
382
+
383
+ subgraph FixPhase ["Fix Callback Execution"]
384
+ callback["fix(fixer) → TextEdit[]"]
385
+ fixer["RuleFixer\n(shared instance)"]
386
+ callback --> fixer
387
+ end
388
+
389
+ subgraph ApplyPhase ["Apply Phase"]
390
+ fixdata["FixData\n{ edits: TextEdit[] }"]
391
+ applier["applyFixes(\n sourceCode,\n allFixes\n)"]
392
+ output["fixedCode"]
393
+ fixdata --> applier --> output
394
+ end
395
+
396
+ report --> callback
397
+ fixer --> fixdata
398
+ ```
399
+
400
+ ### How Fix Callbacks Work
401
+
402
+ Rules attach an optional `fix` callback to each `report()` call. The callback is **not** executed during rule verification — it is stored and only invoked when `fix=true` is passed to `MLCore.verify()`.
403
+
404
+ ```mermaid
405
+ sequenceDiagram
406
+ participant Rule as Rule (verify)
407
+ participant Ctx as MLRuleContext
408
+ participant MLR as MLRule.verify()
409
+ participant Fixer as RuleFixer
410
+ participant Core as MLCore.verify()
411
+ participant FA as applyFixes()
412
+
413
+ Rule->>Ctx: report({ scope, message, fix })
414
+ Note over Ctx: Stores report with fix callback
415
+
416
+ MLR->>Ctx: context.reports
417
+ loop Each report with fix callback
418
+ MLR->>Fixer: report.fix(sharedFixer)
419
+ Fixer-->>MLR: TextEdit | TextEdit[]
420
+ MLR->>MLR: Wrap as FixData { edits }
421
+ end
422
+ MLR-->>Core: Violation[] (with FixData)
423
+
424
+ Core->>Core: Collect all FixData from violations
425
+ Core->>FA: applyFixes(sourceCode, allFixes)
426
+ FA-->>Core: FixResult { output, applied, skipped }
427
+ ```
428
+
429
+ ### RuleFixer API
430
+
431
+ `RuleFixer` implements `IRuleFixer` (defined in `@markuplint/ml-config`). It is a **stateless** helper — a single instance is shared across all rules. Each method builds a `TextEdit` object describing a range replacement on the source code.
432
+
433
+ | Method | Input | TextEdit Produced |
434
+ | --------------------------- | -------------------------------- | ------------------------------------------ |
435
+ | `replaceText(token, text)` | Token with `startOffset` + `raw` | `range: [start, start+len], text` |
436
+ | `replaceRange(range, text)` | Explicit `[start, end)` range | `range: [start, end], text` |
437
+ | `insertBefore(token, text)` | Token with `startOffset` | `range: [start, start], text` (zero-width) |
438
+ | `insertAfter(token, text)` | Token with `startOffset` + `raw` | `range: [end, end], text` (zero-width) |
439
+ | `remove(token)` | Token with `startOffset` + `raw` | `range: [start, start+len], text: ""` |
440
+ | `removeRange(range)` | Explicit `[start, end)` range | `range: [start, end], text: ""` |
441
+
442
+ The `token` parameter accepts any object with `startOffset` and `raw` properties. MLDOM tokens (`MLToken`, `MLAttr`, etc.) satisfy this naturally.
443
+
444
+ ### FixApplier Algorithm
445
+
446
+ `applyFixes()` (in `fix-applier.ts`) merges all `FixData` from all rules and applies them in a single pass:
447
+
448
+ ```mermaid
449
+ flowchart TD
450
+ A["Flatten: FixData[] → tagged TextEdit[]"]
451
+ B["Sort: by range start ascending,\nthen range end descending"]
452
+ C["Apply sequentially:\nfor each edit, check overlap"]
453
+ D{{"edit.start < lastAppliedEnd?"}}
454
+ E["Skip edit\n(mark parent FixData as skipped)"]
455
+ F["Apply edit\n(splice into output)"]
456
+ G["Classify: each FixData as\napplied or skipped"]
457
+ H["Return FixResult\n{ output, applied, skipped }"]
458
+
459
+ A --> B --> C --> D
460
+ D -- Yes --> E --> C
461
+ D -- No --> F --> C
462
+ C -. "all edits processed" .-> G --> H
463
+ ```
464
+
465
+ Key constraints:
466
+
467
+ - Edits within a single `FixData` must not overlap each other
468
+ - Inter-`FixData` overlap is handled by the skip mechanism
469
+ - If any edit in a `FixData` is skipped, the entire `FixData` is classified as skipped
470
+
471
+ ### Example: Rule Fix in Practice
472
+
473
+ ```typescript
474
+ // In a rule's verify function:
475
+ context.report({
476
+ scope: node,
477
+ message: 'Attribute value must use double quotes',
478
+ fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
479
+ });
480
+ ```
481
+
482
+ This produces:
483
+
484
+ 1. **Report** → stored in `MLRuleContext`
485
+ 2. **Fix callback** → `(fixer) => fixer.replaceText(token, text)` (not yet called)
486
+ 3. **When `fix=true`** → callback invoked with shared `RuleFixer` → returns `TextEdit`
487
+ 4. **TextEdit** → wrapped as `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }`
488
+ 5. **applyFixes** → splices the replacement into source code
489
+
370
490
  ## Pretender System
371
491
 
372
492
  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>`).
package/CHANGELOG.md CHANGED
@@ -3,6 +3,24 @@
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.2](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2026-02-23)
7
+
8
+ ### Features
9
+
10
+ - **ml-core:** implement autofix engine with fix-applier and rule-fixer ([36efcec](https://github.com/markuplint/markuplint/commit/36efcecb17e2f4e0729390b1684e571c13c38a38))
11
+
12
+ ### BREAKING CHANGES
13
+
14
+ - **ml-core:** verify() now returns VerifyResult instead of
15
+ Violation[]. RuleSeed.fix() is removed in favor of inline fix
16
+ callbacks on report().
17
+
18
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
19
+
20
+ # [5.0.0-alpha.1](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2026-02-22)
21
+
22
+ **Note:** Version bump only for package @markuplint/ml-core
23
+
6
24
  # [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
7
25
 
8
26
  ### Bug Fixes
@@ -30,7 +30,7 @@
30
30
 
31
31
  ### IDL 属性名解決
32
32
 
33
- ディレクティブパターン解決の後、spec が `useIDLAttributeNames: true` を設定しており(例: `@markuplint/react-spec`、`@markuplint/svelte-spec`)、属性がディレクティブでない場合、コンストラクタは `@markuplint/parser-utils` の `searchIDLAttribute()` を呼び出して IDL プロパティ名を HTML コンテンツ属性名にマッピングします(例: `className` → `class`、`htmlFor` → `for`)。これはパーサーレベルではなくコアレベルの関心事です。
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
34
 
35
35
  ## トークン分解
36
36
 
@@ -30,7 +30,7 @@ When `astToken.potentialName` is not set by the parser, the `MLAttr` constructor
30
30
 
31
31
  ### IDL Attribute Name Resolution
32
32
 
33
- After directive pattern resolution, if the spec sets `useIDLAttributeNames: true` (e.g., `@markuplint/react-spec`, `@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`). This is a core-level concern, not a parser-level one.
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
34
 
35
35
  ## Token Decomposition
36
36
 
@@ -109,15 +109,9 @@ await document.walkOn(
109
109
 
110
110
  `AccessibilityProperties` を返します([型ユーティリティ](./helpers.ja.md#型ユーティリティ)を参照)。
111
111
 
112
- ## `toString(fixed?)`
112
+ ## `toString()`
113
113
 
114
- トークンリストからソースコードを再構築します。
115
-
116
- - `fixed=false`(デフォルト):元の `raw` 文字列を返す
117
- - `fixed=true`:オフセット追跡による置換を適用する:
118
- 1. `getTokenList()` を取得する(`startOffset` でソート済み)
119
- 2. 各トークンに対して:`toString(true) !== raw` の場合、修正済みコンテンツを文字列にスプライスする
120
- 3. 正確な位置を維持するために累積オフセット差を追跡する
114
+ ドキュメントの元の `raw` ソース文字列を返します。修正は DOM ノードの変更とトークンリストの再構築を通じて適用されなくなりました。代わりに、レポートの修正コールバックが `RuleFixer` を通じて実行され、`TextEdit[]` を生成します。`FixApplier.applyFixes()` がすべての編集をソーステキストに直接適用します。
121
115
 
122
116
  ## `defaultView`
123
117
 
@@ -125,12 +119,11 @@ await document.walkOn(
125
119
 
126
120
  ## その他のメソッド
127
121
 
128
- | メソッド | シグネチャ | 説明 |
129
- | ---------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
130
- | `setRule` | `setRule(rule: MLRule \| null): void` | 現在評価中のルールを設定/クリアする |
131
- | `getTokenList` | `getTokenList(): ReadonlyArray<MLToken>` | ソース再構築用のすべてのトークン。オフセットでソート済み(キャッシュ)。ノードと閉じタグの両方を含む |
132
- | `searchNodeByLocation` | `searchNodeByLocation(line, col): MLNode \| null` | 1始まりのソース位置でノードを検索する |
133
- | `debugMap` | `debugMap(): string[]` | ドキュメントツリー構造のデバッグ出力 |
122
+ | メソッド | シグネチャ | 説明 |
123
+ | ---------------------- | ------------------------------------------------- | ------------------------------------- |
124
+ | `setRule` | `setRule(rule: MLRule \| null): void` | 現在評価中のルールを設定/クリアする |
125
+ | `searchNodeByLocation` | `searchNodeByLocation(line, col): MLNode \| null` | 1始まりのソース位置でノードを検索する |
126
+ | `debugMap` | `debugMap(): string[]` | ドキュメントツリー構造のデバッグ出力 |
134
127
 
135
128
  ## endTag モード
136
129
 
@@ -109,15 +109,9 @@ Computes ARIA accessibility properties for a node.
109
109
 
110
110
  Returns `AccessibilityProperties` (see [Type Utilities](./helpers.md#type-utilities)).
111
111
 
112
- ## `toString(fixed?)`
112
+ ## `toString()`
113
113
 
114
- Reconstructs the source code from the token list.
115
-
116
- - `fixed=false` (default): Returns the original `raw` string
117
- - `fixed=true`: Applies offset-tracked replacement:
118
- 1. Get `getTokenList()` (sorted by `startOffset`)
119
- 2. For each token: if `toString(true) !== raw`, splice the fixed content into the string
120
- 3. Track cumulative offset differences to maintain correct positions
114
+ Returns the original `raw` source string of the document. Fixes are no longer applied through DOM node mutation and token-list reconstruction. Instead, fix callbacks on reports are executed via `RuleFixer` to produce `TextEdit[]`. `FixApplier.applyFixes()` applies all edits to the source text.
121
115
 
122
116
  ## `defaultView`
123
117
 
@@ -125,12 +119,11 @@ Returns a mock window object providing `getComputedStyle()` that returns an obje
125
119
 
126
120
  ## Other Methods
127
121
 
128
- | Method | Signature | Description |
129
- | ---------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
130
- | `setRule` | `setRule(rule: MLRule \| null): void` | Set/clear the current rule being evaluated |
131
- | `getTokenList` | `getTokenList(): ReadonlyArray<MLToken>` | All tokens for source reconstruction, sorted by offset (cached). Includes both nodes and close tags. |
132
- | `searchNodeByLocation` | `searchNodeByLocation(line, col): MLNode \| null` | Find node at a 1-based source position |
133
- | `debugMap` | `debugMap(): string[]` | Debug output of document tree structure |
122
+ | Method | Signature | Description |
123
+ | ---------------------- | ------------------------------------------------- | ------------------------------------------ |
124
+ | `setRule` | `setRule(rule: MLRule \| null): void` | Set/clear the current rule being evaluated |
125
+ | `searchNodeByLocation` | `searchNodeByLocation(line, col): MLNode \| null` | Find node at a 1-based source position |
126
+ | `debugMap` | `debugMap(): string[]` | Debug output of document tree structure |
134
127
 
135
128
  ## endTag Modes
136
129
 
@@ -8,20 +8,18 @@ HTML/SVG/MathML の要素ノードです。`MLParentNode` を継承し、`Elemen
8
8
 
9
9
  要素には異なる目的に使用される複数の名前関連プロパティがあります:
10
10
 
11
- | プロパティ | HTML `<DIV>` | SVG `<foreignObject>` | Pretender(`MyButton` → `button`) |
12
- | --------------- | --------------------------- | --------------------- | ---------------------------------- |
13
- | `localName` | `"div"` | `"foreignObject"` | `"button"` |
14
- | `nodeName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
15
- | `rawName` | `"DIV"` | `"foreignObject"` | `"MyButton"` |
16
- | `fixedNodeName` | `"DIV"`(修正後は `"div"`) | `"foreignObject"` | `"MyButton"` |
17
- | `tagName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
11
+ | プロパティ | HTML `<DIV>` | SVG `<foreignObject>` | Pretender(`MyButton` → `button`) |
12
+ | ----------- | ------------ | --------------------- | ---------------------------------- |
13
+ | `localName` | `"div"` | `"foreignObject"` | `"button"` |
14
+ | `nodeName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
15
+ | `rawName` | `"DIV"` | `"foreignObject"` | `"MyButton"` |
16
+ | `tagName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
18
17
 
19
18
  **ルール:**
20
19
 
21
20
  - **`localName`**: HTML 要素 → 小文字化。外部要素または非 `'html'` の elementType → そのまま。pretender コンテキスト → pretender の `localName`。`tagNameCaseSensitive` が `true` の場合 → 小文字化しない。
22
21
  - **`nodeName`**: HTML 要素 → 大文字化(DOM 慣例)。外部要素または非 `'html'` の elementType → AST からそのまま。pretender コンテキスト → pretender の `nodeName`。
23
22
  - **`rawName`**: 常に元の AST `nodeName`。正規化なし、pretender の影響なし。
24
- - **`fixedNodeName`**: 初期値は `rawName`。リント修正がタグ名を変更した場合に `fixNodeName(name)` で更新される。
25
23
  - **`tagName`**: `nodeName` と同じ(pretender コンテキストに従う)。
26
24
 
27
25
  ## 要素型の判定
@@ -98,7 +96,6 @@ Pretender システムのアーキテクチャ、初期化フロー、プロパ
98
96
 
99
97
  | メソッド | シグネチャ | 説明 |
100
98
  | ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------------------- |
101
- | `fixNodeName` | `fixNodeName(name: string): void` | リント自動修正用に `fixedNodeName` を更新する |
102
99
  | `getAccessibleName` | `getAccessibleName(version: ARIAVersion): string` | `getAccname()` を通じてアクセシブル名を計算する |
103
100
  | `toNormalizeString` | `toNormalizeString(): string` | 比較用の正規化された表現を返す(キャッシュ済み)。子要素と属性を再帰的に正規化する |
104
101
  | `nextElementSibling` | `get nextElementSibling: MLElement \| null` | 次の兄弟要素 |
@@ -134,7 +131,7 @@ Pretender システムのアーキテクチャ、初期化フロー、プロパ
134
131
 
135
132
  - 対応するソーストークンを持たない
136
133
  - `prevToken` によってスキップされる(有効なオフセットチェーンを維持するため)
137
- - `toString(fixed)` から `raw` を返す(ソースに修正すべきものがないため修正は適用されない)
134
+ - `toString()` から `raw` を返す(ソースに対応するものがないため)
138
135
  - `getChildElementsAndTextNodeWithoutWhitespaces()` によってフラット化される
139
136
 
140
137
  ## 閉じタグ
@@ -143,21 +140,9 @@ Pretender システムのアーキテクチャ、初期化フロー、プロパ
143
140
  | ---------- | --------------------------- | ------------------------------------------------------------------------------------ |
144
141
  | `closeTag` | `MLElementCloseTag \| null` | ペアの閉じタグ。void 要素、自己閉じ要素、または `endTag === 'never'` の場合は `null` |
145
142
 
146
- ## `toString(fixed?)`
143
+ ## `toString()`
147
144
 
148
- 修正を適用して要素のソース文字列を再構築します。
149
-
150
- - `fixed=false` または pretender/省略要素/`#` プレフィックスの nodeName → `raw` を返す
151
- - `fixed=true`:
152
- 1. 置換可能なノードのリストを構築する:`[tagOpenChar + fixedNodeName, ...overriddenCommentNodes, ...attributes]`
153
- 2. 各ノードに対して、正しいオフセットで `node.toString(true)` をスプライスする
154
- 3. 正確な位置決めのために累積オフセット差を追跡する
155
-
156
- ```
157
- Original: <DIV class="foo" >
158
- Fixed: <div class="foo" >
159
- ^^^^ (fixedNodeName が "DIV" から "div" に変更)
160
- ```
145
+ 要素の生のソース文字列を返します。このメソッドはパースされたドキュメントに記述されたままの元の `raw` ソーステキストを単純に返します。修正は DOM ノードの変更を通じて適用されなくなりました。代わりに、修正操作は `RuleFixer` を通じて `TextEdit[]` を生成し、`FixApplier.applyFixes()` がすべての編集をソーステキストに直接適用します。
161
146
 
162
147
  ## その他のプロパティ
163
148
 
@@ -8,20 +8,18 @@ HTML/SVG/MathML element node. Extends `MLParentNode` and implements `Element`, `
8
8
 
9
9
  The element has multiple name-related properties that serve different purposes:
10
10
 
11
- | Property | HTML `<DIV>` | SVG `<foreignObject>` | Pretender (`MyButton` → `button`) |
12
- | --------------- | ------------------------------ | --------------------- | --------------------------------- |
13
- | `localName` | `"div"` | `"foreignObject"` | `"button"` |
14
- | `nodeName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
15
- | `rawName` | `"DIV"` | `"foreignObject"` | `"MyButton"` |
16
- | `fixedNodeName` | `"DIV"` (or `"div"` after fix) | `"foreignObject"` | `"MyButton"` |
17
- | `tagName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
11
+ | Property | HTML `<DIV>` | SVG `<foreignObject>` | Pretender (`MyButton` → `button`) |
12
+ | ----------- | ------------ | --------------------- | --------------------------------- |
13
+ | `localName` | `"div"` | `"foreignObject"` | `"button"` |
14
+ | `nodeName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
15
+ | `rawName` | `"DIV"` | `"foreignObject"` | `"MyButton"` |
16
+ | `tagName` | `"DIV"` | `"foreignObject"` | `"BUTTON"` |
18
17
 
19
18
  **Rules:**
20
19
 
21
20
  - **`localName`**: HTML elements → lowercased. Foreign elements or non-`'html'` elementType → as-is. Pretender context → pretender's `localName`. If `tagNameCaseSensitive` is `true` → no lowercasing.
22
21
  - **`nodeName`**: HTML elements → uppercased (DOM convention). Foreign elements or non-`'html'` elementType → as-is from AST. Pretender context → pretender's `nodeName`.
23
22
  - **`rawName`**: Always the original AST `nodeName`, with no normalization and no pretender influence.
24
- - **`fixedNodeName`**: Starts as `rawName`. Updated by `fixNodeName(name)` when a lint fix modifies the tag name.
25
23
  - **`tagName`**: Same as `nodeName` (follows pretender context).
26
24
 
27
25
  ## Element Type Resolution
@@ -98,7 +96,6 @@ For comprehensive documentation on the pretender system's architecture, initiali
98
96
 
99
97
  | Method | Signature | Description |
100
98
  | ------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
101
- | `fixNodeName` | `fixNodeName(name: string): void` | Updates `fixedNodeName` for lint auto-fix of the tag name |
102
99
  | `getAccessibleName` | `getAccessibleName(version: ARIAVersion): string` | Computes the accessible name via `getAccname()` |
103
100
  | `toNormalizeString` | `toNormalizeString(): string` | Returns a normalized representation for comparison (cached). Recursively normalizes children and attributes. |
104
101
  | `nextElementSibling` | `get nextElementSibling: MLElement \| null` | Next sibling element |
@@ -134,7 +131,7 @@ Elements with `isOmitted === true` were implicitly inserted by the parser (e.g.,
134
131
 
135
132
  - Have no corresponding source tokens
136
133
  - Are skipped by `prevToken` (to maintain valid offset chains)
137
- - Return `raw` from `toString(fixed)` (no fix is applied since there's nothing in the source to fix)
134
+ - Return `raw` from `toString()` (there's nothing in the source to represent)
138
135
  - Are flattened by `getChildElementsAndTextNodeWithoutWhitespaces()`
139
136
 
140
137
  ## Close Tag
@@ -143,21 +140,9 @@ Elements with `isOmitted === true` were implicitly inserted by the parser (e.g.,
143
140
  | ---------- | --------------------------- | ------------------------------------------------------------------------------------------------ |
144
141
  | `closeTag` | `MLElementCloseTag \| null` | Paired close tag. `null` for void elements, self-closing elements, or when `endTag === 'never'`. |
145
142
 
146
- ## `toString(fixed?)`
143
+ ## `toString()`
147
144
 
148
- Reconstructs the element's source string with fixes applied.
149
-
150
- - `fixed=false` or pretender/omitted/`#`-prefixed nodeName → return `raw`
151
- - `fixed=true`:
152
- 1. Build a list of replaceable nodes: `[tagOpenChar + fixedNodeName, ...overriddenCommentNodes, ...attributes]`
153
- 2. For each node, splice `node.toString(true)` at the correct offset
154
- 3. Track cumulative offset differences for accurate positioning
155
-
156
- ```
157
- Original: <DIV class="foo" >
158
- Fixed: <div class="foo" >
159
- ^^^^ (fixedNodeName changed from "DIV" to "div")
160
- ```
145
+ Returns the element's raw source string. This method simply returns the original `raw` source text as it appeared in the parsed document. Fixes are no longer applied through DOM node mutation; instead, fix operations produce `TextEdit[]` via `RuleFixer`, and `FixApplier.applyFixes()` applies all edits directly to the source text.
161
146
 
162
147
  ## Other Properties
163
148
 
@@ -189,7 +189,7 @@ constructor(document: MLDocument<T, O>, locale: LocaleSet)
189
189
 
190
190
  ### `provide(): ProvidedContext`
191
191
 
192
- `RuleSeed.verify()` `RuleSeed.fix()` に渡されるコンテキストオブジェクトを返します:
192
+ `RuleSeed.verify()` に渡されるコンテキストオブジェクトを返します。自動修正ロジックは個々の `report()` 呼び出しのインライン `fix` コールバックとして提供されます:
193
193
 
194
194
  ```typescript
195
195
  {
@@ -189,7 +189,7 @@ Creates translator from locale, stores document reference.
189
189
 
190
190
  ### `provide(): ProvidedContext`
191
191
 
192
- Returns the context object passed to `RuleSeed.verify()` and `RuleSeed.fix()`:
192
+ Returns the context object passed to `RuleSeed.verify()`. Auto-fix logic is provided as an inline `fix` callback on individual `report()` calls:
193
193
 
194
194
  ```typescript
195
195
  {