@markuplint/ml-core 5.0.0-alpha.1 → 5.0.0-alpha.3
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 +158 -5
- package/ARCHITECTURE.md +209 -6
- package/CHANGELOG.md +25 -0
- package/docs/ml-dom/document.ja.md +7 -14
- package/docs/ml-dom/document.md +7 -14
- package/docs/ml-dom/element.ja.md +9 -24
- package/docs/ml-dom/element.md +9 -24
- package/docs/rule-system.ja.md +1 -1
- package/docs/rule-system.md +1 -1
- package/lib/cursor-offset.d.ts +13 -0
- package/lib/cursor-offset.js +34 -0
- package/lib/fix-applier.d.ts +32 -0
- package/lib/fix-applier.js +75 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/ml-core.d.ts +53 -8
- package/lib/ml-core.js +197 -59
- package/lib/ml-dom/helper/get-indent.d.ts +0 -1
- package/lib/ml-dom/helper/get-indent.js +5 -18
- package/lib/ml-dom/node/attr.d.ts +2 -13
- package/lib/ml-dom/node/attr.js +3 -35
- package/lib/ml-dom/node/block.js +2 -1
- package/lib/ml-dom/node/character-data.d.ts +35 -0
- package/lib/ml-dom/node/character-data.js +35 -6
- package/lib/ml-dom/node/document.d.ts +2 -27
- package/lib/ml-dom/node/document.js +7 -46
- package/lib/ml-dom/node/dom-token-list.d.ts +0 -1
- package/lib/ml-dom/node/dom-token-list.js +3 -3
- package/lib/ml-dom/node/element-close-tag.d.ts +1 -1
- package/lib/ml-dom/node/element-close-tag.js +2 -16
- package/lib/ml-dom/node/element.d.ts +2 -19
- package/lib/ml-dom/node/element.js +3 -65
- package/lib/ml-dom/token/token.d.ts +3 -18
- package/lib/ml-dom/token/token.js +7 -28
- package/lib/ml-rule/index.d.ts +1 -0
- package/lib/ml-rule/index.js +1 -0
- package/lib/ml-rule/ml-rule-context.d.ts +2 -31
- package/lib/ml-rule/ml-rule-context.js +18 -18
- package/lib/ml-rule/ml-rule.d.ts +6 -11
- package/lib/ml-rule/ml-rule.js +36 -37
- package/lib/ml-rule/rule-fixer.d.ts +20 -0
- package/lib/ml-rule/rule-fixer.js +38 -0
- package/lib/ml-rule/types.d.ts +1 -2
- package/lib/test/index.d.ts +1 -10
- package/lib/test/index.js +0 -11
- package/package.json +12 -12
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -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`
|
|
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(
|
|
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()`
|
|
296
|
+
`provide()` メソッドは `RuleSeed.verify()` に渡されるコンテキストオブジェクトを返します。自動修正ロジックは、個々の `report()` 呼び出しのインライン `fix` コールバックとして提供され、独立したライフサイクルメソッドではありません。
|
|
296
297
|
|
|
297
298
|
### ルール設定の解決
|
|
298
299
|
|
|
@@ -367,6 +368,158 @@ 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` パラメータは `FixToken` 型(`@markuplint/ml-config` で定義)を満たす任意のオブジェクト — つまり `{ startOffset: number; raw: string }` — を受け付けます。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
|
+
`applyFixes()` がレンジの重複により一部の fix をスキップした場合、エンジンはマルチパスループ(`_multiPassFix()`)に入り、再パース・再検証を繰り返して修正可能な違反をすべて解決します:
|
|
474
|
+
|
|
475
|
+
```mermaid
|
|
476
|
+
flowchart TD
|
|
477
|
+
A["violations から fix を抽出"] --> B["applyFixes(code, fixes)"]
|
|
478
|
+
B --> C{"applied.length === 0?"}
|
|
479
|
+
C -- Yes --> Z["現在のコードを返す"]
|
|
480
|
+
C -- No --> D{"output === currentCode?"}
|
|
481
|
+
D -- Yes --> Z
|
|
482
|
+
D -- No --> E{"サイクル検出?\n(2パス前の出力と一致)"}
|
|
483
|
+
E -- Yes --> Z
|
|
484
|
+
E -- No --> F{"skipped.length === 0?"}
|
|
485
|
+
F -- Yes --> Z["修正済みコードを返す\n(全 fix 適用完了)"]
|
|
486
|
+
F -- No --> G["再パース + 再検証"]
|
|
487
|
+
G --> H{"ParserError?"}
|
|
488
|
+
H -- Yes --> Z["最後の正常なコードに戻す"]
|
|
489
|
+
H -- No --> I{"新たな修正可能な違反?"}
|
|
490
|
+
I -- No --> Z
|
|
491
|
+
I -- Yes --> B
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
主な設計ポイント:
|
|
495
|
+
|
|
496
|
+
- **ゼロコストパス**: fix を持つ violation がなければ、マルチパスループは完全にスキップされる
|
|
497
|
+
- **シングルパス高速パス**: `skipped.length === 0` のとき即座にループを抜ける(Phase 1 と同等の動作)
|
|
498
|
+
- **サイクル検出**: 2パス前の出力と比較し、A→B→A の振動パターンを検出
|
|
499
|
+
- **安全上限**: 最大10パス(ESLint の `SourceCodeFixer` と同じ)
|
|
500
|
+
- **状態復元**: `verify()` は `try/finally` で `#sourceCode`、`#ast`、`#document` を保存・復元
|
|
501
|
+
|
|
502
|
+
**重要**: `VerifyResult` の `violations` 配列は初回パスの結果のみを反映し、`fixedCode` は複数パスの結果である場合があります。修正後コードの正確な違反リストが必要な場合は、出力を再検証してください。
|
|
503
|
+
|
|
504
|
+
### 実例: ルールの Fix 実装
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// ルールの verify 関数内:
|
|
508
|
+
context.report({
|
|
509
|
+
scope: node,
|
|
510
|
+
message: '属性値にはダブルクォートを使用してください',
|
|
511
|
+
fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
これにより以下の処理が行われます:
|
|
516
|
+
|
|
517
|
+
1. **レポート** → `MLRuleContext` に格納
|
|
518
|
+
2. **Fix コールバック** → `(fixer) => fixer.replaceText(token, text)`(まだ呼び出されない)
|
|
519
|
+
3. **`fix=true` の場合** → 共有 `RuleFixer` でコールバック実行 → `TextEdit` を返す
|
|
520
|
+
4. **TextEdit** → `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }` としてラップ
|
|
521
|
+
5. **applyFixes** → ソースコードに置換を適用
|
|
522
|
+
|
|
370
523
|
## Pretender システム
|
|
371
524
|
|
|
372
525
|
pretender システムにより、コンポーネントをリンティング時にセマンティック HTML 要素として扱うことができます。これにより、ルールがカスタムコンポーネント(例:`<MyButton>`)を標準要素(例:`<button>`)として検証できます。
|
package/ARCHITECTURE.md
CHANGED
|
@@ -49,9 +49,12 @@ 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
|
+
├── cursor-offset.ts — computeCursorOffset (cursor remapping after edits)
|
|
57
|
+
├── fix-applier.ts — applyFixes (overlap-aware TextEdit applicator)
|
|
55
58
|
├── ruleset/
|
|
56
59
|
│ └── index.ts — Ruleset class (rules + nodeRules + childNodeRules)
|
|
57
60
|
├── plugin/
|
|
@@ -131,7 +134,7 @@ flowchart LR
|
|
|
131
134
|
A["MLCore\nconstructor"]
|
|
132
135
|
B["_parse()\nParser → MLASTDocument"]
|
|
133
136
|
C["_createDocument()\nMLASTDocument → MLDocument"]
|
|
134
|
-
D["verify(fix?)\nFor each rule:"]
|
|
137
|
+
D["verify(fix? | options?)\nFor each rule:"]
|
|
135
138
|
E["document.setRule(rule)\nRuleMapper maps config → nodes"]
|
|
136
139
|
F["rule.verify(document)\nMLRuleContext collects reports"]
|
|
137
140
|
G["Violations[]"]
|
|
@@ -143,8 +146,8 @@ flowchart LR
|
|
|
143
146
|
|
|
144
147
|
1. **Parse**: `MLCore` invokes the configured parser (`MLParser`) to produce an `MLASTDocument`
|
|
145
148
|
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`,
|
|
149
|
+
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
|
|
150
|
+
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. When fixes require multiple passes, `_multiPassFix()` orchestrates re-parsing and re-verification, returning a `FixSummary` with pass count, applied/skipped totals, and first-pass edits for cursor offset computation
|
|
148
151
|
|
|
149
152
|
## MLDOM Class Hierarchy
|
|
150
153
|
|
|
@@ -210,10 +213,9 @@ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tupl
|
|
|
210
213
|
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
211
214
|
| `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
|
|
212
215
|
| `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
|
|
213
|
-
| `getTokenList()` | Returns all tokens for source reconstruction |
|
|
214
216
|
| `searchNodeByLocation(line, col)` | Finds the node at a given source position |
|
|
215
217
|
| `getAccessibilityProp(node)` | Computes ARIA accessibility properties (delegates to `MLElement.getAccessibleName()` for cached accessible name) |
|
|
216
|
-
| `toString(
|
|
218
|
+
| `toString()` | Returns the raw source code of the document |
|
|
217
219
|
|
|
218
220
|
## MLElement
|
|
219
221
|
|
|
@@ -292,7 +294,7 @@ type RuleSeed<T, O> = {
|
|
|
292
294
|
- `translate` / `t` — Locale-aware message translator
|
|
293
295
|
- `report(report)` — Reports a violation with node, message, and optional fix
|
|
294
296
|
|
|
295
|
-
The `provide()` method returns the context object passed to `RuleSeed.verify()`
|
|
297
|
+
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
298
|
|
|
297
299
|
### Rule Configuration Resolution
|
|
298
300
|
|
|
@@ -367,6 +369,207 @@ Virtual rules can be disabled at three levels in the `rules` config:
|
|
|
367
369
|
2. **Group disable**: `rules["custom/multi"]: false` (for multi-entry named nodeRules)
|
|
368
370
|
3. **Namespace wildcard**: `rules["a11y/*"]: false` (disables all virtual rules starting with `a11y/`)
|
|
369
371
|
|
|
372
|
+
## Autofix System
|
|
373
|
+
|
|
374
|
+
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).
|
|
375
|
+
|
|
376
|
+
### Autofix Data Flow
|
|
377
|
+
|
|
378
|
+
```mermaid
|
|
379
|
+
flowchart LR
|
|
380
|
+
subgraph RulePhase ["Rule Phase"]
|
|
381
|
+
report["context.report({\n message,\n scope,\n fix: callback\n})"]
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
subgraph FixPhase ["Fix Callback Execution"]
|
|
385
|
+
callback["fix(fixer) → TextEdit[]"]
|
|
386
|
+
fixer["RuleFixer\n(shared instance)"]
|
|
387
|
+
callback --> fixer
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
subgraph ApplyPhase ["Apply Phase"]
|
|
391
|
+
fixdata["FixData\n{ edits: TextEdit[] }"]
|
|
392
|
+
applier["applyFixes(\n sourceCode,\n allFixes\n)"]
|
|
393
|
+
output["fixedCode"]
|
|
394
|
+
fixdata --> applier --> output
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
report --> callback
|
|
398
|
+
fixer --> fixdata
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### How Fix Callbacks Work
|
|
402
|
+
|
|
403
|
+
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()`.
|
|
404
|
+
|
|
405
|
+
```mermaid
|
|
406
|
+
sequenceDiagram
|
|
407
|
+
participant Rule as Rule (verify)
|
|
408
|
+
participant Ctx as MLRuleContext
|
|
409
|
+
participant MLR as MLRule.verify()
|
|
410
|
+
participant Fixer as RuleFixer
|
|
411
|
+
participant Core as MLCore.verify()
|
|
412
|
+
participant FA as applyFixes()
|
|
413
|
+
|
|
414
|
+
Rule->>Ctx: report({ scope, message, fix })
|
|
415
|
+
Note over Ctx: Stores report with fix callback
|
|
416
|
+
|
|
417
|
+
MLR->>Ctx: context.reports
|
|
418
|
+
loop Each report with fix callback
|
|
419
|
+
MLR->>Fixer: report.fix(sharedFixer)
|
|
420
|
+
Fixer-->>MLR: TextEdit | TextEdit[]
|
|
421
|
+
MLR->>MLR: Wrap as FixData { edits }
|
|
422
|
+
end
|
|
423
|
+
MLR-->>Core: Violation[] (with FixData)
|
|
424
|
+
|
|
425
|
+
Core->>Core: Collect all FixData from violations
|
|
426
|
+
Core->>FA: applyFixes(sourceCode, allFixes)
|
|
427
|
+
FA-->>Core: FixResult { output, applied, skipped, appliedEdits }
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### RuleFixer API
|
|
431
|
+
|
|
432
|
+
`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.
|
|
433
|
+
|
|
434
|
+
| Method | Input | TextEdit Produced |
|
|
435
|
+
| --------------------------- | -------------------------------- | ------------------------------------------ |
|
|
436
|
+
| `replaceText(token, text)` | Token with `startOffset` + `raw` | `range: [start, start+len], text` |
|
|
437
|
+
| `replaceRange(range, text)` | Explicit `[start, end)` range | `range: [start, end], text` |
|
|
438
|
+
| `insertBefore(token, text)` | Token with `startOffset` | `range: [start, start], text` (zero-width) |
|
|
439
|
+
| `insertAfter(token, text)` | Token with `startOffset` + `raw` | `range: [end, end], text` (zero-width) |
|
|
440
|
+
| `remove(token)` | Token with `startOffset` + `raw` | `range: [start, start+len], text: ""` |
|
|
441
|
+
| `removeRange(range)` | Explicit `[start, end)` range | `range: [start, end], text: ""` |
|
|
442
|
+
|
|
443
|
+
The `token` parameter accepts any object satisfying the `FixToken` type (defined in `@markuplint/ml-config`) — i.e., `{ startOffset: number; raw: string }`. MLDOM tokens (`MLToken`, `MLAttr`, etc.) satisfy this naturally.
|
|
444
|
+
|
|
445
|
+
### FixApplier Algorithm
|
|
446
|
+
|
|
447
|
+
`applyFixes()` (in `fix-applier.ts`) merges all `FixData` from all rules and applies them in a single pass:
|
|
448
|
+
|
|
449
|
+
```mermaid
|
|
450
|
+
flowchart TD
|
|
451
|
+
A["Flatten: FixData[] → tagged TextEdit[]"]
|
|
452
|
+
B["Sort: by range start ascending,\nthen range end descending"]
|
|
453
|
+
C["Apply sequentially:\nfor each edit, check overlap"]
|
|
454
|
+
D{{"edit.start < lastAppliedEnd?"}}
|
|
455
|
+
E["Skip edit\n(mark parent FixData as skipped)"]
|
|
456
|
+
F["Apply edit\n(splice into output)"]
|
|
457
|
+
G["Classify: each FixData as\napplied or skipped"]
|
|
458
|
+
H["Return FixResult\n{ output, applied, skipped, appliedEdits }"]
|
|
459
|
+
|
|
460
|
+
A --> B --> C --> D
|
|
461
|
+
D -- Yes --> E --> C
|
|
462
|
+
D -- No --> F --> C
|
|
463
|
+
C -. "all edits processed" .-> G --> H
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Key constraints:
|
|
467
|
+
|
|
468
|
+
- Edits within a single `FixData` must not overlap each other
|
|
469
|
+
- Inter-`FixData` overlap is handled by the skip mechanism
|
|
470
|
+
- If any edit in a `FixData` is skipped, the entire `FixData` is classified as skipped
|
|
471
|
+
- `appliedEdits` is a flat list of all successfully applied `TextEdit` objects, sorted by `range[0]` ascending — used for cursor offset computation
|
|
472
|
+
|
|
473
|
+
### Multi-Pass Fix Loop
|
|
474
|
+
|
|
475
|
+
When `applyFixes()` skips some fixes due to range overlap, the engine enters a multi-pass loop (`_multiPassFix()`) that re-parses and re-verifies until all fixable violations are resolved:
|
|
476
|
+
|
|
477
|
+
```mermaid
|
|
478
|
+
flowchart TD
|
|
479
|
+
A["Extract fixes from violations"] --> B["applyFixes(code, fixes)"]
|
|
480
|
+
B --> C{"applied.length === 0?"}
|
|
481
|
+
C -- Yes --> Z["Return current code"]
|
|
482
|
+
C -- No --> D{"output === currentCode?"}
|
|
483
|
+
D -- Yes --> Z
|
|
484
|
+
D -- No --> E{"Cycle detected?\n(output === code from 2 passes ago)"}
|
|
485
|
+
E -- Yes --> Z
|
|
486
|
+
E -- No --> F{"skipped.length === 0?"}
|
|
487
|
+
F -- Yes --> Z["Return fixed code\n(all fixes applied)"]
|
|
488
|
+
F -- No --> G["Re-parse + re-verify"]
|
|
489
|
+
G --> H{"ParserError?"}
|
|
490
|
+
H -- Yes --> Z["Revert to last good code"]
|
|
491
|
+
H -- No --> I{"New fixable violations?"}
|
|
492
|
+
I -- No --> Z
|
|
493
|
+
I -- Yes --> B
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Key design points:
|
|
497
|
+
|
|
498
|
+
- **Zero-cost path**: If no violations have fixes, the multi-pass loop is skipped entirely
|
|
499
|
+
- **Single-pass fast path**: When `skipped.length === 0`, the loop exits immediately — equivalent to Phase 1 behavior
|
|
500
|
+
- **Cycle detection**: Compares current output against the output from two passes ago to detect A→B→A oscillation
|
|
501
|
+
- **Safety cap**: Maximum 10 passes (same as ESLint's `SourceCodeFixer`)
|
|
502
|
+
- **State restoration**: `verify()` saves and restores `#sourceCode`, `#ast`, and `#document` via `try/finally`
|
|
503
|
+
|
|
504
|
+
**Important**: The `violations` array in `VerifyResult` reflects the first pass only, while `fixedCode` may be the result of multiple passes. Callers needing an accurate violation list for the fixed code should re-verify the output.
|
|
505
|
+
|
|
506
|
+
### VerifyResult and FixSummary
|
|
507
|
+
|
|
508
|
+
`MLCore.verify()` accepts either a `boolean` or a `VerifyOptions` object:
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
verify(fix?: boolean): Promise<VerifyResult>;
|
|
512
|
+
verify(options?: VerifyOptions): Promise<VerifyResult>;
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
`VerifyResult` contains:
|
|
516
|
+
|
|
517
|
+
| Field | Type | Description |
|
|
518
|
+
| ------------ | ------------------------- | ------------------------------------------------------------- |
|
|
519
|
+
| `violations` | `readonly Violation[]` | Violations from the first verification pass |
|
|
520
|
+
| `fixedCode` | `string \| undefined` | Source after all fix passes; `undefined` when fix is disabled |
|
|
521
|
+
| `fixSummary` | `FixSummary \| undefined` | Fix process summary; present when `fix=true` |
|
|
522
|
+
|
|
523
|
+
`FixSummary` provides diagnostics about the multi-pass fix process:
|
|
524
|
+
|
|
525
|
+
| Field | Type | Description |
|
|
526
|
+
| ------------------ | --------------------- | ---------------------------------------------------------------- |
|
|
527
|
+
| `passCount` | `number` | Number of fix passes executed |
|
|
528
|
+
| `totalApplied` | `number` | Total fixes applied across all passes |
|
|
529
|
+
| `totalSkipped` | `number` | Total fixes skipped (overlap) across all passes |
|
|
530
|
+
| `reachedMaxPasses` | `boolean` | Whether the 10-pass safety cap was reached |
|
|
531
|
+
| `firstPassEdits` | `readonly TextEdit[]` | Applied edits from the first pass only (original source offsets) |
|
|
532
|
+
|
|
533
|
+
`firstPassEdits` references the original source code offsets, making them suitable for cursor remapping via `computeCursorOffset()`.
|
|
534
|
+
|
|
535
|
+
### Cursor Offset Computation
|
|
536
|
+
|
|
537
|
+
`computeCursorOffset()` (in `cursor-offset.ts`) maps a cursor position from the original source to the fixed source using the first-pass applied edits:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
import { computeCursorOffset } from '@markuplint/ml-core';
|
|
541
|
+
|
|
542
|
+
const newOffset = computeCursorOffset(fixSummary.firstPassEdits, originalCursorOffset);
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Algorithm:
|
|
546
|
+
|
|
547
|
+
1. Walk through edits sorted by `range[0]` ascending
|
|
548
|
+
2. For each edit before the cursor: accumulate `delta = text.length - (end - start)`
|
|
549
|
+
3. For edits after the cursor: stop (no effect)
|
|
550
|
+
4. If the cursor falls inside a replaced range `[start, end)`: place at `start + text.length`
|
|
551
|
+
|
|
552
|
+
Ranges use half-open intervals: a cursor at position `end` is considered **outside** the edit.
|
|
553
|
+
|
|
554
|
+
### Example: Rule Fix in Practice
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
// In a rule's verify function:
|
|
558
|
+
context.report({
|
|
559
|
+
scope: node,
|
|
560
|
+
message: 'Attribute value must use double quotes',
|
|
561
|
+
fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
This produces:
|
|
566
|
+
|
|
567
|
+
1. **Report** → stored in `MLRuleContext`
|
|
568
|
+
2. **Fix callback** → `(fixer) => fixer.replaceText(token, text)` (not yet called)
|
|
569
|
+
3. **When `fix=true`** → callback invoked with shared `RuleFixer` → returns `TextEdit`
|
|
570
|
+
4. **TextEdit** → wrapped as `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }`
|
|
571
|
+
5. **applyFixes** → splices the replacement into source code
|
|
572
|
+
|
|
370
573
|
## Pretender System
|
|
371
574
|
|
|
372
575
|
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,31 @@
|
|
|
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.3](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.2...v5.0.0-alpha.3) (2026-02-26)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **ml-core:** treat edits within a single FixData as atomic unit ([0bb980b](https://github.com/markuplint/markuplint/commit/0bb980b7cc6fc9b89a82f3d4df58b7137a6b8766))
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- **ml-core:** add cursor offset computation and fix summary metadata ([74b6e28](https://github.com/markuplint/markuplint/commit/74b6e28e4be2802e841697899f57f6ae04e4ffe9))
|
|
15
|
+
- **ml-core:** add multi-pass fix loop and cycle detection ([866b1d5](https://github.com/markuplint/markuplint/commit/866b1d54199ed1f1b5195cd0f61f3ee392b1d8a7))
|
|
16
|
+
|
|
17
|
+
# [5.0.0-alpha.2](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2026-02-23)
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- **ml-core:** implement autofix engine with fix-applier and rule-fixer ([36efcec](https://github.com/markuplint/markuplint/commit/36efcecb17e2f4e0729390b1684e571c13c38a38))
|
|
22
|
+
|
|
23
|
+
### BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
- **ml-core:** verify() now returns VerifyResult instead of
|
|
26
|
+
Violation[]. RuleSeed.fix() is removed in favor of inline fix
|
|
27
|
+
callbacks on report().
|
|
28
|
+
|
|
29
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
30
|
+
|
|
6
31
|
# [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
32
|
|
|
8
33
|
**Note:** Version bump only for package @markuplint/ml-core
|
|
@@ -109,15 +109,9 @@ await document.walkOn(
|
|
|
109
109
|
|
|
110
110
|
`AccessibilityProperties` を返します([型ユーティリティ](./helpers.ja.md#型ユーティリティ)を参照)。
|
|
111
111
|
|
|
112
|
-
## `toString(
|
|
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
|
-
| `
|
|
132
|
-
| `
|
|
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
|
|
package/docs/ml-dom/document.md
CHANGED
|
@@ -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(
|
|
112
|
+
## `toString()`
|
|
113
113
|
|
|
114
|
-
|
|
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
|
-
| `
|
|
132
|
-
| `
|
|
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
|
-
| プロパティ
|
|
12
|
-
|
|
|
13
|
-
| `localName`
|
|
14
|
-
| `nodeName`
|
|
15
|
-
| `rawName`
|
|
16
|
-
| `
|
|
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(
|
|
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(
|
|
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
|
|