@markuplint/ml-ast 4.4.11 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,12 +53,9 @@ classDiagram
53
53
  <<interface>>
54
54
  +uuid: string
55
55
  +raw: string
56
- +startOffset: number
57
- +endOffset: number
58
- +startLine: number
59
- +endLine: number
60
- +startCol: number
61
- +endCol: number
56
+ +offset: number
57
+ +line: number
58
+ +col: number
62
59
  }
63
60
 
64
61
  class MLASTAbstractNode {
@@ -85,6 +82,7 @@ classDiagram
85
82
  +elementType: ElementType
86
83
  +attributes: MLASTAttr[]
87
84
  +childNodes: MLASTChildNode[]
85
+ +blockBehavior: MLASTBlockBehavior | null
88
86
  +pairNode: MLASTElementCloseTag | null
89
87
  +isGhost: boolean
90
88
  +isFragment: boolean
@@ -114,7 +112,7 @@ classDiagram
114
112
  class MLASTPreprocessorSpecificBlock {
115
113
  <<interface>>
116
114
  +type: "psblock"
117
- +conditionalType: ...ConditionalType
115
+ +blockBehavior: MLASTBlockBehavior | null
118
116
  +depth: number
119
117
  +childNodes: MLASTChildNode[]
120
118
  +isBogus: boolean
@@ -242,12 +240,10 @@ flowchart TD
242
240
 
243
241
  ## パーサーインターフェース
244
242
 
245
- | 型 | 説明 |
246
- | ------------------------ | -------------------------------------------------------- |
247
- | `MLParser` | markuplint 互換パーサーのインターフェース |
248
- | `MLParserModule` | パーサーインスタンスをエクスポートするモジュールラッパー |
249
- | `MLMarkupLanguageParser` | 非推奨(v5 で削除予定)。代わりに `MLParser` を使用 |
250
- | `Parse` | 非推奨。パース関数シグネチャの型エイリアス |
243
+ | 型 | 説明 |
244
+ | ---------------- | -------------------------------------------------------- |
245
+ | `MLParser` | markuplint 互換パーサーのインターフェース |
246
+ | `MLParserModule` | パーサーインスタンスをエクスポートするモジュールラッパー |
251
247
 
252
248
  `MLParser` は `MLASTDocument` を返す `parse(sourceCode, options?)` メソッドを必要とします。オプションフィールドには `endTag`(終了タグ処理戦略)、`booleanish`(ブール属性検出)、`tagNameCaseSensitive`(XHTML/JSX 用)があります。
253
249
 
package/ARCHITECTURE.md CHANGED
@@ -53,12 +53,9 @@ classDiagram
53
53
  <<interface>>
54
54
  +uuid: string
55
55
  +raw: string
56
- +startOffset: number
57
- +endOffset: number
58
- +startLine: number
59
- +endLine: number
60
- +startCol: number
61
- +endCol: number
56
+ +offset: number
57
+ +line: number
58
+ +col: number
62
59
  }
63
60
 
64
61
  class MLASTAbstractNode {
@@ -85,6 +82,7 @@ classDiagram
85
82
  +elementType: ElementType
86
83
  +attributes: MLASTAttr[]
87
84
  +childNodes: MLASTChildNode[]
85
+ +blockBehavior: MLASTBlockBehavior | null
88
86
  +pairNode: MLASTElementCloseTag | null
89
87
  +isGhost: boolean
90
88
  +isFragment: boolean
@@ -114,7 +112,7 @@ classDiagram
114
112
  class MLASTPreprocessorSpecificBlock {
115
113
  <<interface>>
116
114
  +type: "psblock"
117
- +conditionalType: ...ConditionalType
115
+ +blockBehavior: MLASTBlockBehavior | null
118
116
  +depth: number
119
117
  +childNodes: MLASTChildNode[]
120
118
  +isBogus: boolean
@@ -242,12 +240,10 @@ This enables lint rules to validate whitespace around `=`, quoting style, and at
242
240
 
243
241
  ## Parser Interface
244
242
 
245
- | Type | Description |
246
- | ------------------------ | ------------------------------------------------------ |
247
- | `MLParser` | Interface for a markuplint-compatible parser |
248
- | `MLParserModule` | Module wrapper that exports a parser instance |
249
- | `MLMarkupLanguageParser` | Deprecated (v5 removal). Use `MLParser` instead |
250
- | `Parse` | Deprecated type alias for the parse function signature |
243
+ | Type | Description |
244
+ | ---------------- | --------------------------------------------- |
245
+ | `MLParser` | Interface for a markuplint-compatible parser |
246
+ | `MLParserModule` | Module wrapper that exports a parser instance |
251
247
 
252
248
  `MLParser` requires a `parse(sourceCode, options?)` method that returns an `MLASTDocument`. Optional fields include `endTag` (end tag handling strategy), `booleanish` (boolean attribute detection), and `tagNameCaseSensitive` (for XHTML/JSX).
253
249
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,47 @@
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.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **ml-core:** improve detection of namespace ([5b507ad](https://github.com/markuplint/markuplint/commit/5b507ad7c19c5015b8ce587845d901e31dfa6518))
11
+
12
+ - feat(ml-ast)!: simplify AST token properties and restructure block types ([78f8a77](https://github.com/markuplint/markuplint/commit/78f8a77c76728df8090fcf54c7c5541bedb56f9d))
13
+
14
+ ### BREAKING CHANGES
15
+
16
+ - Multiple breaking changes to AST interfaces:
17
+
18
+ Token property renames (MLASTToken):
19
+
20
+ - startOffset -> offset
21
+ - startLine -> line
22
+ - startCol -> col
23
+ - Remove endOffset, endLine, endCol (derive via helpers)
24
+
25
+ Element changes (MLASTElement):
26
+
27
+ - Remove selfClosingSolidus property
28
+ - Add blockBehavior: MLASTBlockBehavior | null
29
+
30
+ Block changes (MLASTPreprocessorSpecificBlock):
31
+
32
+ - Remove conditionalType property
33
+ - Add blockBehavior: MLASTBlockBehavior | null
34
+
35
+ New types:
36
+
37
+ - MLASTBlockBehavior interface (type + expression)
38
+ - MLASTBlockBehaviorType (replaces MLASTPreprocessorSpecificBlockConditionalType)
39
+
40
+ Removed deprecated types:
41
+
42
+ - MLMarkupLanguageParser interface
43
+ - Parse type alias
44
+
45
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
46
+
6
47
  ## [4.4.11](https://github.com/markuplint/markuplint/compare/@markuplint/ml-ast@4.4.10...@markuplint/ml-ast@4.4.11) (2026-02-10)
7
48
 
8
49
  **Note:** Version bump only for package @markuplint/ml-ast
package/SKILL.md CHANGED
@@ -102,7 +102,6 @@ Modify the `MLParser` interface. Follow recipe #5 in `docs/maintenance.md`.
102
102
 
103
103
  1. Read `src/types.ts` and find `MLParser`
104
104
  2. Make changes (prefer adding optional fields for backward compatibility)
105
- 3. Update `MLMarkupLanguageParser` (deprecated) if needed for consistency
106
105
 
107
106
  ### Step 2: Update all parsers
108
107
 
@@ -116,22 +116,21 @@ yarn build --scope @markuplint/ml-ast
116
116
 
117
117
  5. **ビルドして検証**
118
118
 
119
- ### 4. `PreprocessorSpecificBlockConditionalType` の値追加
119
+ ### 4. `MLASTBlockBehaviorType` の値追加
120
120
 
121
- プリプロセッサブロックの新しい conditional type 値を追加する場合:
121
+ プリプロセッサブロックの新しいブロック動作種別を追加する場合:
122
122
 
123
- 1. **`src/types.ts` の `MLASTPreprocessorSpecificBlockConditionalType` に値を追加**:
123
+ 1. **`src/types.ts` の `MLASTBlockBehaviorType` に値を追加**:
124
124
 
125
125
  ```typescript
126
- export type MLASTPreprocessorSpecificBlockConditionalType =
126
+ export type MLASTBlockBehaviorType =
127
127
  | 'if'
128
128
  | 'if:elseif'
129
129
  // ... 既存の値
130
- | 'newvalue' // ここに追加
131
- | null;
130
+ | 'newvalue'; // ここに追加
132
131
  ```
133
132
 
134
- 2. **この conditional type でブロックを生成するパーサーを更新**
133
+ 2. **この動作種別で `blockBehavior` を設定するパーサーを更新**
135
134
 
136
135
  3. **新しい値が DOM 作成時に特別な処理を必要とする場合は `ml-core` を更新**
137
136
 
@@ -149,7 +148,6 @@ yarn build --scope @markuplint/ml-ast
149
148
  2. **後方互換性を考慮**:
150
149
  - オプションフィールドの追加は安全
151
150
  - 必須フィールドの追加やシグネチャの変更は破壊的変更
152
- - 一貫性のため必要に応じて `MLMarkupLanguageParser`(非推奨)も更新
153
151
 
154
152
  3. **すべてのパーサー実装を更新**:
155
153
  - `@markuplint/html-parser`
@@ -149,7 +149,6 @@ When changing the parser interface:
149
149
  2. **Consider backward compatibility**:
150
150
  - Adding optional fields is safe
151
151
  - Adding required fields or changing signatures is a breaking change
152
- - Update `MLMarkupLanguageParser` (deprecated) if needed for consistency
153
152
 
154
153
  3. **Update all parser implementations**:
155
154
  - `@markuplint/html-parser`
@@ -61,16 +61,15 @@ function handle(node: MLASTNode) {
61
61
 
62
62
  すべての位置情報の基盤となるインターフェースです。すべての AST ノードとサブトークンがこれを継承します。
63
63
 
64
- | フィールド | 型 | 説明 |
65
- | ------------- | -------- | ------------------------------------------ |
66
- | `uuid` | `string` | このトークンインスタンスの一意識別子 |
67
- | `raw` | `string` | 元のソーステキスト |
68
- | `startOffset` | `number` | トークン開始位置のゼロベース文字オフセット |
69
- | `endOffset` | `number` | トークン終了位置のゼロベース文字オフセット |
70
- | `startLine` | `number` | トークン開始位置の1ベース行番号 |
71
- | `endLine` | `number` | トークン終了位置の1ベース行番号 |
72
- | `startCol` | `number` | トークン開始位置の1ベース列番号 |
73
- | `endCol` | `number` | トークン終了位置の1ベース列番号 |
64
+ | フィールド | 型 | 説明 |
65
+ | ---------- | -------- | ------------------------------------------ |
66
+ | `uuid` | `string` | このトークンインスタンスの一意識別子 |
67
+ | `raw` | `string` | 元のソーステキスト |
68
+ | `offset` | `number` | トークン開始位置のゼロベース文字オフセット |
69
+ | `line` | `number` | トークン開始位置の1ベース行番号 |
70
+ | `col` | `number` | トークン開始位置の1ベース列番号 |
71
+
72
+ **終了位置の導出:** 終了位置のプロパティはありません。終了位置は開始位置と `raw` から導出できます。ユーティリティ関数 `getEndLine()`、`getEndCol()`、`getEndPosition()`(`@markuplint/parser-utils` 提供)が利用可能です。
74
73
 
75
74
  **座標系について:** オフセットはゼロベース(0から数える)、行と列は1ベース(1から数える)です。これはほとんどのテキストエディタやエラーレポーターの慣例に一致しています。
76
75
 
@@ -125,21 +124,21 @@ function handle(node: MLASTNode) {
125
124
 
126
125
  **役割:** 開始タグ(例:`<div class="foo">`)を表します。AST における主要な要素表現であり、最も機能豊富なノード型です。子ノード、属性を所有し、対応する閉じタグへの参照を保持します。
127
126
 
128
- | フィールド | 型 | 説明 |
129
- | -------------------- | ------------------------------ | ---------------------------------------------------------------- |
130
- | `type` | `'starttag'` | 判別タグ |
131
- | `depth` | `number` | ドキュメントツリーでのネストの深さ |
132
- | `namespace` | `string` | 名前空間 URI(例:`"http://www.w3.org/1999/xhtml"`) |
133
- | `elementType` | `ElementType` | `'html'`、`'web-component'`、`'authored'` のいずれか |
134
- | `isFragment` | `boolean` | フラグメントとして機能するか(例:React `<>`、Vue `<template>`) |
135
- | `attributes` | `readonly MLASTAttr[]` | この要素の属性 |
136
- | `hasSpreadAttr` | `boolean \| undefined` | スプレッド属性を持つかどうか |
137
- | `childNodes` | `readonly MLASTChildNode[]` | この要素の直接の子ノード |
138
- | `pairNode` | `MLASTElementCloseTag \| null` | 対応する閉じタグ、void/自己閉じ要素の場合は `null` |
139
- | `selfClosingSolidus` | `MLASTToken \| undefined` | 自己閉じスラッシュトークン(`/`)(例:`<br />`) |
140
- | `tagOpenChar` | `string` | タグを開く文字(通常 `"<"`) |
141
- | `tagCloseChar` | `string` | タグを閉じる文字(通常 `">"`) |
142
- | `isGhost` | `boolean` | ゴーストノード(パーサーが推定した省略タグ)かどうか |
127
+ | フィールド | 型 | 説明 |
128
+ | --------------- | ------------------------------ | ---------------------------------------------------------------- |
129
+ | `type` | `'starttag'` | 判別タグ |
130
+ | `depth` | `number` | ドキュメントツリーでのネストの深さ |
131
+ | `namespace` | `string` | 名前空間 URI(例:`"http://www.w3.org/1999/xhtml"`) |
132
+ | `elementType` | `ElementType` | `'html'`、`'web-component'`、`'authored'` のいずれか |
133
+ | `isFragment` | `boolean` | フラグメントとして機能するか(例:React `<>`、Vue `<template>`) |
134
+ | `attributes` | `readonly MLASTAttr[]` | この要素の属性 |
135
+ | `hasSpreadAttr` | `boolean \| undefined` | スプレッド属性を持つかどうか |
136
+ | `childNodes` | `readonly MLASTChildNode[]` | この要素の直接の子ノード |
137
+ | `blockBehavior` | `MLASTBlockBehavior \| null` | この要素に関連するブロック動作(ある場合) |
138
+ | `pairNode` | `MLASTElementCloseTag \| null` | 対応する閉じタグ、void/自己閉じ要素の場合は `null` |
139
+ | `tagOpenChar` | `string` | タグを開く文字(通常 `"<"`) |
140
+ | `tagCloseChar` | `string` | タグを閉じる文字(通常 `">"`) |
141
+ | `isGhost` | `boolean` | ゴーストノード(パーサーが推定した省略タグ)かどうか |
143
142
 
144
143
  ### 要素タイプの分類
145
144
 
@@ -256,34 +255,37 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
256
255
 
257
256
  **役割:** テンプレートエンジンやフレームワークの制御フロー・反復構文を表します。標準 HTML には存在しないが、Svelte、Vue、EJS、ERB などのプリプロセッサで使用される構文です。
258
257
 
259
- | フィールド | 型 | 説明 |
260
- | ----------------- | ----------------------------------------------- | ---------------------------------------- |
261
- | `type` | `'psblock'` | 判別タグ |
262
- | `conditionalType` | `MLASTPreprocessorSpecificBlockConditionalType` | 条件分岐・反復構文の種別 |
263
- | `depth` | `number` | ドキュメントツリーでのネストの深さ |
264
- | `nodeName` | `string` | パーサーが決定したブロック名 |
265
- | `isFragment` | `boolean` | 透過的なフラグメントとして機能するか |
266
- | `childNodes` | `readonly MLASTChildNode[]` | このブロック内の直接の子ノード |
267
- | `isBogus` | `boolean` | ボーガス(パース不能・不正形式)かどうか |
268
-
269
- ### conditionalType の値
270
-
271
- `conditionalType` フィールドはブロックの意味的な役割を示します:
272
-
273
- | | 説明 | 例(Svelte) | 例(EJS/ERB) |
274
- | ------------------ | ---------------------------- | ----------------------- | ------------------- |
275
- | `'if'` | 条件分岐(開始) | `{#if condition}` | `<% if (x) { %>` |
276
- | `'if:elseif'` | 代替条件分岐 | `{:else if condition}` | `<% } else if { %>` |
277
- | `'if:else'` | デフォルト(else)分岐 | `{:else}` | `<% } else { %>` |
278
- | `'switch:case'` | switch case 分岐 | -- | -- |
279
- | `'switch:default'` | switch default 分岐 | -- | -- |
280
- | `'each'` | 反復(ループ)ブロック | `{#each items as item}` | `<% for (...) { %>` |
281
- | `'each:empty'` | 反復ブロックの空状態 | `{:else}`(`#each` 内) | -- |
282
- | `'await'` | 非同期ブロック(保留状態) | `{#await promise}` | -- |
283
- | `'await:then'` | 非同期ブロックの解決状態 | `{:then value}` | -- |
284
- | `'await:catch'` | 非同期ブロックの拒否状態 | `{:catch error}` | -- |
285
- | `'end'` | 閉じブロック | `{/if}`, `{/each}` | `<% } %>` |
286
- | `null` | 特定の条件セマンティクスなし | -- | `<%= expr %>` |
258
+ | フィールド | 型 | 説明 |
259
+ | --------------- | ---------------------------- | ---------------------------------------------------- |
260
+ | `type` | `'psblock'` | 判別タグ |
261
+ | `blockBehavior` | `MLASTBlockBehavior \| null` | ブロックの制御フロー構文の動作を記述する(ある場合) |
262
+ | `depth` | `number` | ドキュメントツリーでのネストの深さ |
263
+ | `nodeName` | `string` | パーサーが決定したブロック名 |
264
+ | `isFragment` | `boolean` | 透過的なフラグメントとして機能するか |
265
+ | `childNodes` | `readonly MLASTChildNode[]` | このブロック内の直接の子ノード |
266
+ | `isBogus` | `boolean` | ボーガス(パース不能・不正形式)かどうか |
267
+
268
+ ### ブロック動作の種別
269
+
270
+ `blockBehavior` フィールドは `MLASTBlockBehavior` オブジェクト(制御フローセマンティクスを持たないブロックの場合は `null`)です。存在する場合、`blockBehavior.type` はブロックのセマンティックな役割を示し、`blockBehavior.expression` は構文を駆動するソース式を含みます:
271
+
272
+ | `blockBehavior.type` | 説明 | 例(Svelte) | 例(EJS/ERB) |
273
+ | -------------------- | -------------------------- | ----------------------- | ------------------- |
274
+ | `'if'` | 条件分岐(開始) | `{#if condition}` | `<% if (x) { %>` |
275
+ | `'if:elseif'` | 代替条件分岐 | `{:else if condition}` | `<% } else if { %>` |
276
+ | `'if:else'` | デフォルト(else)分岐 | `{:else}` | `<% } else { %>` |
277
+ | `'switch:case'` | switch case 分岐 | -- | -- |
278
+ | `'switch:default'` | switch default 分岐 | -- | -- |
279
+ | `'each'` | 反復(ループ)ブロック | `{#each items as item}` | `<% for (...) { %>` |
280
+ | `'each:empty'` | 反復ブロックの空状態 | `{:else}`(`#each` 内) | -- |
281
+ | `'await'` | 非同期ブロック(保留状態) | `{#await promise}` | -- |
282
+ | `'await:then'` | 非同期ブロックの解決状態 | `{:then value}` | -- |
283
+ | `'await:catch'` | 非同期ブロックの拒否状態 | `{:catch error}` | -- |
284
+ | `'end'` | 閉じブロック | `{/if}`, `{/each}` | `<% } %>` |
285
+
286
+ `blockBehavior` が `null` の場合、ブロックには特定の制御フローセマンティクスがありません(例:EJS の `<%= expr %>` は純粋な式出力です)。
287
+
288
+ `MLASTBlockBehavior` の `expression` フィールドには、ブロックに関連するソース式が格納されます(例:Svelte の if ブロックでは `'{#if loggedIn}'`)。
287
289
 
288
290
  ### フレームワーク固有の例
289
291
 
@@ -299,9 +301,9 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
299
301
 
300
302
  3つの `psblock` ノードが生成されます:
301
303
 
302
- 1. `conditionalType: 'if'`:`{#if loggedIn}`
303
- 2. `conditionalType: 'if:else'`:`{:else}`
304
- 3. `conditionalType: 'end'`:`{/if}`
304
+ 1. `blockBehavior: { type: 'if', expression: '{#if loggedIn}' }`:`{#if loggedIn}`
305
+ 2. `blockBehavior: { type: 'if:else', expression: '{:else}' }`:`{:else}`
306
+ 3. `blockBehavior: { type: 'end', expression: '{/if}' }`:`{/if}`
305
307
 
306
308
  **Vue(v-if ディレクティブは異なる方法で処理されます -- psblock ではなく要素属性経由)。**
307
309
 
@@ -315,9 +317,9 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
315
317
 
316
318
  生成されるノード:
317
319
 
318
- 1. `conditionalType: 'if'`:`<% if (user) { %>`
319
- 2. `conditionalType: 'end'`:`<% } %>`
320
- 3. `conditionalType: null`:`<%= user.name %>`(式出力、条件セマンティクスなし)
320
+ 1. `blockBehavior: { type: 'if', expression: '<% if (user) { %>' }`:`<% if (user) { %>`
321
+ 2. `blockBehavior: { type: 'end', expression: '<% } %>' }`:`<% } %>`
322
+ 3. `blockBehavior: null`:`<%= user.name %>`(式出力、制御フローセマンティクスなし)
321
323
 
322
324
  ## MLASTInvalid
323
325
 
@@ -412,7 +414,7 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
412
414
  | `type` | `'spread'` | 判別タグ |
413
415
  | `nodeName` | `'#spread'` | 常に `'#spread'` |
414
416
 
415
- `MLASTSpreadAttr` は `MLASTAbstractNode` ではなく `MLASTToken` を直接継承するため、位置情報(`uuid`、`raw`、`startOffset` など)はありますが、`parentNode` や `depth` はありません。
417
+ `MLASTSpreadAttr` は `MLASTAbstractNode` ではなく `MLASTToken` を直接継承するため、位置情報(`uuid`、`raw`、`offset` など)はありますが、`parentNode` や `depth` はありません。
416
418
 
417
419
  ## 共用体型リファレンス
418
420
 
@@ -449,7 +451,7 @@ function processChild(node: MLASTChildNode) {
449
451
  console.log(`コメント (bogus: ${node.isBogus})`);
450
452
  break;
451
453
  case 'psblock':
452
- console.log(`ブロック: ${node.nodeName}, conditional: ${node.conditionalType}`);
454
+ console.log(`ブロック: ${node.nodeName}, behavior: ${node.blockBehavior?.type ?? 'none'}`);
453
455
  break;
454
456
  case 'invalid':
455
457
  console.log(`不正: kind=${node.kind}`);
@@ -61,18 +61,17 @@ The mapping is performed by `createNode()` in `ml-core`:
61
61
 
62
62
  The foundational interface for all positional information. Every AST node and sub-token extends this.
63
63
 
64
- | Field | Type | Description |
65
- | ------------- | -------- | ---------------------------------------------- |
66
- | `uuid` | `string` | Unique identifier for this token instance |
67
- | `raw` | `string` | The original raw source text |
68
- | `startOffset` | `number` | Zero-based character offset of the token start |
69
- | `endOffset` | `number` | Zero-based character offset of the token end |
70
- | `startLine` | `number` | One-based line number where the token starts |
71
- | `endLine` | `number` | One-based line number where the token ends |
72
- | `startCol` | `number` | One-based column number where the token starts |
73
- | `endCol` | `number` | One-based column number where the token ends |
74
-
75
- **Coordinate system:** Offsets are zero-based (counting from 0), while lines and columns are one-based (counting from 1). This matches the conventions used by most text editors and error reporters.
64
+ | Field | Type | Description |
65
+ | -------- | -------- | ---------------------------------------------- |
66
+ | `uuid` | `string` | Unique identifier for this token instance |
67
+ | `raw` | `string` | The original raw source text |
68
+ | `offset` | `number` | Zero-based character offset of the token start |
69
+ | `line` | `number` | One-based line number where the token starts |
70
+ | `col` | `number` | One-based column number where the token starts |
71
+
72
+ End positions can be derived: `endOffset = offset + raw.length`. For `endLine` and `endCol`, use the helper functions from `@markuplint/parser-utils`.
73
+
74
+ **Coordinate system:** `offset` is zero-based (counting from 0), while `line` and `col` are one-based (counting from 1).
76
75
 
77
76
  ### MLASTAbstractNode
78
77
 
@@ -125,21 +124,21 @@ Produces a node with `name: "html"`, `publicId: ""`, `systemId: ""`.
125
124
 
126
125
  **Role:** Represents an opening element tag (e.g., `<div class="foo">`). This is the primary element representation in the AST and is the most feature-rich node type. It owns child nodes, attributes, and maintains a reference to its matching closing tag.
127
126
 
128
- | Field | Type | Description |
129
- | -------------------- | ------------------------------ | --------------------------------------------------------------------------- |
130
- | `type` | `'starttag'` | Discriminant tag |
131
- | `depth` | `number` | Nesting depth in the document tree |
132
- | `namespace` | `string` | Namespace URI (e.g., `"http://www.w3.org/1999/xhtml"`) |
133
- | `elementType` | `ElementType` | Whether the element is `'html'`, `'web-component'`, or `'authored'` |
134
- | `isFragment` | `boolean` | Whether the element acts as a fragment (e.g., React `<>`, Vue `<template>`) |
135
- | `attributes` | `readonly MLASTAttr[]` | Attributes on this element |
136
- | `hasSpreadAttr` | `boolean \| undefined` | Whether the element has one or more spread attributes |
137
- | `childNodes` | `readonly MLASTChildNode[]` | Direct child nodes of this element |
138
- | `pairNode` | `MLASTElementCloseTag \| null` | The matching closing tag, or `null` for void/self-closing elements |
139
- | `selfClosingSolidus` | `MLASTToken \| undefined` | The self-closing solidus token (`/`), if present (e.g., `<br />`) |
140
- | `tagOpenChar` | `string` | The characters that open this tag (usually `"<"`) |
141
- | `tagCloseChar` | `string` | The characters that close this tag (usually `">"`) |
142
- | `isGhost` | `boolean` | Whether this is a ghost node (omitted tag inferred by the parser) |
127
+ | Field | Type | Description |
128
+ | --------------- | ------------------------------ | --------------------------------------------------------------------------- |
129
+ | `type` | `'starttag'` | Discriminant tag |
130
+ | `depth` | `number` | Nesting depth in the document tree |
131
+ | `namespace` | `string` | Namespace URI (e.g., `"http://www.w3.org/1999/xhtml"`) |
132
+ | `elementType` | `ElementType` | Whether the element is `'html'`, `'web-component'`, or `'authored'` |
133
+ | `isFragment` | `boolean` | Whether the element acts as a fragment (e.g., React `<>`, Vue `<template>`) |
134
+ | `attributes` | `readonly MLASTAttr[]` | Attributes on this element |
135
+ | `hasSpreadAttr` | `boolean \| undefined` | Whether the element has one or more spread attributes |
136
+ | `childNodes` | `readonly MLASTChildNode[]` | Direct child nodes of this element |
137
+ | `blockBehavior` | `MLASTBlockBehavior \| null` | Block behavior associated with this element, if any |
138
+ | `pairNode` | `MLASTElementCloseTag \| null` | The matching closing tag, or `null` for void/self-closing elements |
139
+ | `tagOpenChar` | `string` | The characters that open this tag (usually `"<"`) |
140
+ | `tagCloseChar` | `string` | The characters that close this tag (usually `">"`) |
141
+ | `isGhost` | `boolean` | Whether this is a ghost node (omitted tag inferred by the parser) |
143
142
 
144
143
  ### Element Type Classification
145
144
 
@@ -256,34 +255,37 @@ The text `Hello, world!` is represented as an `MLASTText` node with `raw: "Hello
256
255
 
257
256
  **Role:** Represents control-flow and iteration constructs from template engines and frameworks. These are syntax constructs that do not exist in standard HTML but are used by preprocessors like Svelte, Vue, EJS, ERB, and others.
258
257
 
259
- | Field | Type | Description |
260
- | ----------------- | ----------------------------------------------- | ----------------------------------------------------- |
261
- | `type` | `'psblock'` | Discriminant tag |
262
- | `conditionalType` | `MLASTPreprocessorSpecificBlockConditionalType` | The kind of conditional or iteration construct |
263
- | `depth` | `number` | Nesting depth in the document tree |
264
- | `nodeName` | `string` | The block's name as determined by the parser |
265
- | `isFragment` | `boolean` | Whether this block acts as a transparent fragment |
266
- | `childNodes` | `readonly MLASTChildNode[]` | Direct child nodes within this block |
267
- | `isBogus` | `boolean` | Whether this block is bogus (unparsable or malformed) |
268
-
269
- ### Conditional Type Values
270
-
271
- The `conditionalType` field indicates the semantic role of the block:
272
-
273
- | Value | Description | Example (Svelte) | Example (EJS/ERB) |
274
- | ------------------ | ---------------------------------- | ----------------------- | ------------------- |
275
- | `'if'` | Conditional branch (opening) | `{#if condition}` | `<% if (x) { %>` |
276
- | `'if:elseif'` | Alternative conditional branch | `{:else if condition}` | `<% } else if { %>` |
277
- | `'if:else'` | Default (else) branch | `{:else}` | `<% } else { %>` |
278
- | `'switch:case'` | Switch case branch | -- | -- |
279
- | `'switch:default'` | Switch default branch | -- | -- |
280
- | `'each'` | Iteration (loop) block | `{#each items as item}` | `<% for (...) { %>` |
281
- | `'each:empty'` | Empty state for an iteration block | `{:else}` (in `#each`) | -- |
282
- | `'await'` | Asynchronous block (pending state) | `{#await promise}` | -- |
283
- | `'await:then'` | Resolved state of an async block | `{:then value}` | -- |
284
- | `'await:catch'` | Rejected state of an async block | `{:catch error}` | -- |
285
- | `'end'` | Closing block | `{/if}`, `{/each}` | `<% } %>` |
286
- | `null` | No specific conditional semantic | -- | `<%= expr %>` |
258
+ | Field | Type | Description |
259
+ | --------------- | ---------------------------- | ------------------------------------------------------------ |
260
+ | `type` | `'psblock'` | Discriminant tag |
261
+ | `blockBehavior` | `MLASTBlockBehavior \| null` | Block behavior describing the control-flow construct, if any |
262
+ | `depth` | `number` | Nesting depth in the document tree |
263
+ | `nodeName` | `string` | The block's name as determined by the parser |
264
+ | `isFragment` | `boolean` | Whether this block acts as a transparent fragment |
265
+ | `childNodes` | `readonly MLASTChildNode[]` | Direct child nodes within this block |
266
+ | `isBogus` | `boolean` | Whether this block is bogus (unparsable or malformed) |
267
+
268
+ ### Block Behavior Types
269
+
270
+ The `blockBehavior` field is an `MLASTBlockBehavior` object (or `null` for blocks with no control-flow semantic). When present, `blockBehavior.type` indicates the semantic role of the block, and `blockBehavior.expression` contains the source expression that drives the construct:
271
+
272
+ | `blockBehavior.type` | Description | Example (Svelte) | Example (EJS/ERB) |
273
+ | -------------------- | ---------------------------------- | ----------------------- | ------------------- |
274
+ | `'if'` | Conditional branch (opening) | `{#if condition}` | `<% if (x) { %>` |
275
+ | `'if:elseif'` | Alternative conditional branch | `{:else if condition}` | `<% } else if { %>` |
276
+ | `'if:else'` | Default (else) branch | `{:else}` | `<% } else { %>` |
277
+ | `'switch:case'` | Switch case branch | -- | -- |
278
+ | `'switch:default'` | Switch default branch | -- | -- |
279
+ | `'each'` | Iteration (loop) block | `{#each items as item}` | `<% for (...) { %>` |
280
+ | `'each:empty'` | Empty state for an iteration block | `{:else}` (in `#each`) | -- |
281
+ | `'await'` | Asynchronous block (pending state) | `{#await promise}` | -- |
282
+ | `'await:then'` | Resolved state of an async block | `{:then value}` | -- |
283
+ | `'await:catch'` | Rejected state of an async block | `{:catch error}` | -- |
284
+ | `'end'` | Closing block | `{/if}`, `{/each}` | `<% } %>` |
285
+
286
+ When `blockBehavior` is `null`, the block has no specific control-flow semantic (e.g., `<%= expr %>` in EJS is a pure expression output).
287
+
288
+ The `expression` field in `MLASTBlockBehavior` contains the raw source expression associated with the block (e.g., `'{#if loggedIn}'` for a Svelte if-block).
287
289
 
288
290
  ### Framework-Specific Examples
289
291
 
@@ -299,9 +301,9 @@ The `conditionalType` field indicates the semantic role of the block:
299
301
 
300
302
  Produces three `psblock` nodes:
301
303
 
302
- 1. `conditionalType: 'if'` for `{#if loggedIn}`
303
- 2. `conditionalType: 'if:else'` for `{:else}`
304
- 3. `conditionalType: 'end'` for `{/if}`
304
+ 1. `blockBehavior: { type: 'if', expression: '{#if loggedIn}' }` for `{#if loggedIn}`
305
+ 2. `blockBehavior: { type: 'if:else', expression: '{:else}' }` for `{:else}`
306
+ 3. `blockBehavior: { type: 'end', expression: '{/if}' }` for `{/if}`
305
307
 
306
308
  **Vue (v-if directive is handled differently -- via element attributes, not psblock).**
307
309
 
@@ -315,9 +317,9 @@ Produces three `psblock` nodes:
315
317
 
316
318
  Produces:
317
319
 
318
- 1. `conditionalType: 'if'` for `<% if (user) { %>`
319
- 2. `conditionalType: 'end'` for `<% } %>`
320
- 3. `conditionalType: null` for `<%= user.name %>` (expression output, no conditional semantic)
320
+ 1. `blockBehavior: { type: 'if', expression: '<% if (user) { %>' }` for `<% if (user) { %>`
321
+ 2. `blockBehavior: { type: 'end', expression: '<% } %>' }` for `<% } %>`
322
+ 3. `blockBehavior: null` for `<%= user.name %>` (expression output, no control-flow semantic)
321
323
 
322
324
  ## MLASTInvalid
323
325
 
@@ -412,7 +414,7 @@ These fields are set by framework-specific parsers:
412
414
  | `type` | `'spread'` | Discriminant tag |
413
415
  | `nodeName` | `'#spread'` | Always `'#spread'` |
414
416
 
415
- Note that `MLASTSpreadAttr` extends `MLASTToken` directly (not `MLASTAbstractNode`), so it has positional information (`uuid`, `raw`, `startOffset`, etc.) but no `parentNode` or `depth`.
417
+ Note that `MLASTSpreadAttr` extends `MLASTToken` directly (not `MLASTAbstractNode`), so it has positional information (`uuid`, `raw`, `offset`, etc.) but no `parentNode` or `depth`.
416
418
 
417
419
  ## Union Types Reference
418
420
 
@@ -449,7 +451,7 @@ function processChild(node: MLASTChildNode) {
449
451
  console.log(`Comment (bogus: ${node.isBogus})`);
450
452
  break;
451
453
  case 'psblock':
452
- console.log(`Block: ${node.nodeName}, conditional: ${node.conditionalType}`);
454
+ console.log(`Block: ${node.nodeName}, behavior: ${node.blockBehavior?.type ?? 'none'}`);
453
455
  break;
454
456
  case 'invalid':
455
457
  console.log(`Invalid: kind=${node.kind}`);
package/lib/types.d.ts CHANGED
@@ -48,6 +48,10 @@ export type MLASTAttr = MLASTHTMLAttr | MLASTSpreadAttr;
48
48
  /**
49
49
  * Base token representing a span of source text with positional information.
50
50
  * Every AST node ultimately extends this interface.
51
+ *
52
+ * End positions (`endOffset`, `endLine`, `endCol`) are not stored;
53
+ * they can be derived from `offset + raw.length`, or via helper utilities
54
+ * in `@markuplint/parser-utils`.
51
55
  */
52
56
  export interface MLASTToken {
53
57
  /** Unique identifier for this token instance */
@@ -55,17 +59,11 @@ export interface MLASTToken {
55
59
  /** The original raw source text of this token */
56
60
  readonly raw: string;
57
61
  /** Zero-based character offset of the token start in the source */
58
- readonly startOffset: number;
59
- /** Zero-based character offset of the token end in the source */
60
- readonly endOffset: number;
62
+ readonly offset: number;
61
63
  /** One-based line number where the token starts */
62
- readonly startLine: number;
63
- /** One-based line number where the token ends */
64
- readonly endLine: number;
64
+ readonly line: number;
65
65
  /** One-based column number where the token starts */
66
- readonly startCol: number;
67
- /** One-based column number where the token ends */
68
- readonly endCol: number;
66
+ readonly col: number;
69
67
  }
70
68
  /**
71
69
  * Abstract base for all AST nodes. Extends {@link MLASTToken} with
@@ -103,7 +101,7 @@ export interface MLASTElement extends MLASTAbstractNode {
103
101
  /** Nesting depth in the document tree */
104
102
  readonly depth: number;
105
103
  /** Namespace URI of the element (e.g. `"http://www.w3.org/1999/xhtml"`) */
106
- readonly namespace: string;
104
+ readonly namespace: NamespaceURI;
107
105
  /** Whether the element is native HTML, a Web Component, or an authored component */
108
106
  readonly elementType: ElementType;
109
107
  /** Whether this element acts as a fragment (no actual DOM node) */
@@ -114,10 +112,10 @@ export interface MLASTElement extends MLASTAbstractNode {
114
112
  readonly hasSpreadAttr?: boolean;
115
113
  /** Direct child nodes of this element */
116
114
  readonly childNodes: readonly MLASTChildNode[];
115
+ /** Block behavior associated with this element, if any */
116
+ readonly blockBehavior: MLASTBlockBehavior | null;
117
117
  /** The matching closing tag, or `null` for void / self-closing elements */
118
118
  readonly pairNode: MLASTElementCloseTag | null;
119
- /** The self-closing solidus token (`/`), if present (e.g. `<br />`) */
120
- readonly selfClosingSolidus?: MLASTToken;
121
119
  /** The characters that open this tag (usually `"<"`) */
122
120
  readonly tagOpenChar: string;
123
121
  /** The characters that close this tag (usually `">"`) */
@@ -149,8 +147,6 @@ export interface MLASTElementCloseTag extends MLASTAbstractNode {
149
147
  */
150
148
  export interface MLASTPreprocessorSpecificBlock extends MLASTAbstractNode {
151
149
  readonly type: 'psblock';
152
- /** The kind of conditional or iteration construct this block represents */
153
- readonly conditionalType: MLASTPreprocessorSpecificBlockConditionalType;
154
150
  /** Nesting depth in the document tree */
155
151
  readonly depth: number;
156
152
  /** The block's name as determined by the parser */
@@ -159,15 +155,26 @@ export interface MLASTPreprocessorSpecificBlock extends MLASTAbstractNode {
159
155
  readonly isFragment: boolean;
160
156
  /** Direct child nodes within this block */
161
157
  readonly childNodes: readonly MLASTChildNode[];
158
+ /** Block behavior associated with this block, if any */
159
+ readonly blockBehavior: MLASTBlockBehavior | null;
162
160
  /** Whether this block is bogus (unparsable or malformed) */
163
161
  readonly isBogus: boolean;
164
162
  }
165
163
  /**
166
- * The type of conditional or iteration construct represented by a
167
- * {@link MLASTPreprocessorSpecificBlock}. `null` indicates a block
168
- * without a specific conditional semantic.
164
+ * Describes the behavior of a preprocessor block or element,
165
+ * capturing both the kind of control-flow construct and the
166
+ * source expression that drives it.
167
+ */
168
+ export interface MLASTBlockBehavior {
169
+ /** The kind of block behavior (e.g. `'if'`, `'each'`, `'await'`) */
170
+ readonly type: MLASTBlockBehaviorType;
171
+ /** The source expression associated with this block */
172
+ readonly expression: string;
173
+ }
174
+ /**
175
+ * The type of control-flow construct represented by a block behavior.
169
176
  */
170
- export type MLASTPreprocessorSpecificBlockConditionalType = 'if' | 'if:elseif' | 'if:else' | 'switch:case' | 'switch:default' | 'each' | 'each:empty' | 'await' | 'await:then' | 'await:catch' | 'end' | null;
177
+ export type MLASTBlockBehaviorType = 'if' | 'if:elseif' | 'if:else' | 'switch:case' | 'switch:default' | 'each' | 'each:empty' | 'await' | 'await:then' | 'await:catch' | 'end';
171
178
  /**
172
179
  * An HTML comment node (e.g. `<!-- ... -->`).
173
180
  */
@@ -262,37 +269,6 @@ export interface MLASTDocument {
262
269
  /** A description of any unknown parse error that occurred, if any */
263
270
  readonly unknownParseError?: string;
264
271
  }
265
- /**
266
- * @deprecated Use `MLParser` instead. This will be dropped in v5.
267
- */
268
- export interface MLMarkupLanguageParser {
269
- /**
270
- * @deprecated
271
- */
272
- parse(sourceCode: string, options?: ParserOptions & {
273
- readonly offsetOffset?: number;
274
- readonly offsetLine?: number;
275
- readonly offsetColumn?: number;
276
- }): MLASTDocument;
277
- /**
278
- * @default "omittable"
279
- * @deprecated
280
- */
281
- endTag?: EndTagType;
282
- /**
283
- * Detect value as a true if its attribute is booleanish value and omitted.
284
- *
285
- * Ex:
286
- * ```jsx
287
- * <Component aria-hidden />
288
- * ```
289
- *
290
- * In the above, the `aria-hidden` is `true`.
291
- *
292
- * @deprecated
293
- */
294
- booleanish?: boolean;
295
- }
296
272
  /**
297
273
  * Interface for a markuplint-compatible parser.
298
274
  * Implementations parse markup source code and produce an {@link MLASTDocument}.
@@ -358,10 +334,6 @@ export type ParserAuthoredElementNameDistinguishing = string | Readonly<RegExp>
358
334
  * @returns `true` if the name represents an authored element
359
335
  */
360
336
  export type ParserAuthoredElementNameDistinguishingFunction = (name: string) => boolean;
361
- /**
362
- * @deprecated
363
- */
364
- export type Parse = MLMarkupLanguageParser['parse'];
365
337
  /**
366
338
  * Callback function used for walking through AST nodes.
367
339
  *
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@markuplint/ml-ast",
3
- "version": "4.4.11",
3
+ "version": "5.0.0-alpha.0",
4
4
  "description": "The markuplint AST types.",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
7
7
  "license": "MIT",
8
+ "engines": {
9
+ "node": ">=22"
10
+ },
8
11
  "type": "module",
9
12
  "exports": {
10
13
  ".": {
@@ -23,5 +26,5 @@
23
26
  "dev": "tsc --watch --project tsconfig.build.json",
24
27
  "clean": "tsc --build --clean tsconfig.build.json"
25
28
  },
26
- "gitHead": "193ee7c1262bbed95424e38efdf1a8e56ff049f4"
29
+ "gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
27
30
  }