@markuplint/ml-ast 5.0.0-dev.5 → 5.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,7 +62,7 @@ classDiagram
62
62
  <<interface>>
63
63
  +type: MLASTNodeType
64
64
  +nodeName: string
65
- +parentNode: MLASTParentNode | null
65
+ +parentNodeUuid: string | null
66
66
  }
67
67
 
68
68
  class MLASTDoctype {
@@ -83,7 +83,7 @@ classDiagram
83
83
  +attributes: MLASTAttr[]
84
84
  +childNodes: MLASTChildNode[]
85
85
  +blockBehavior: MLASTBlockBehavior | null
86
- +pairNode: MLASTElementCloseTag | null
86
+ +pairNodeUuid: string | null
87
87
  +isGhost: boolean
88
88
  +isFragment: boolean
89
89
  }
@@ -92,8 +92,7 @@ classDiagram
92
92
  <<interface>>
93
93
  +type: "endtag"
94
94
  +depth: number
95
- +parentNode: null
96
- +pairNode: MLASTElement
95
+ +pairNodeUuid: string | null
97
96
  }
98
97
 
99
98
  class MLASTComment {
@@ -216,7 +215,7 @@ flowchart TD
216
215
  **特殊なノード:**
217
216
 
218
217
  - **`MLBlock`**(`nodeType: 101`)は DOM Standard に相当するものがない markuplint 独自の拡張です。透過的なコンテナとして機能し、子ノードはツリー走査時に親に属するものとして扱われます。
219
- - **`MLElementCloseTag`** は `createNode()` で生成されません。代わりに `MLElement` が内部で `pairNode` 参照から生成します。ペアとなる要素の付属物としてのみ存在し、DOM ツリー走査の対象ではありません。
218
+ - **`MLElementCloseTag`** は `createNode()` で生成されません。代わりに `MLElement` `pairNodeUuid` を解決して閉じタグの AST ノードを検索し、`MLElementCloseTag` を生成します。ペアとなる要素の付属物としてのみ存在し、DOM ツリー走査の対象ではありません。
220
219
  - **`MLASTInvalid`** はリカバリノードです。MLDOM にそのまま保持されることはなく、`kind` フィールドに応じて `MLElement`(タグ名 `x-invalid`)または `MLText` に変換されます。
221
220
 
222
221
  詳細は[ノードリファレンス -- AST から MLDOM へのマッピング](docs/node-reference.ja.md#ast-から-mldom-へのマッピング)を参照してください。
package/ARCHITECTURE.md CHANGED
@@ -62,7 +62,7 @@ classDiagram
62
62
  <<interface>>
63
63
  +type: MLASTNodeType
64
64
  +nodeName: string
65
- +parentNode: MLASTParentNode | null
65
+ +parentNodeUuid: string | null
66
66
  }
67
67
 
68
68
  class MLASTDoctype {
@@ -83,7 +83,7 @@ classDiagram
83
83
  +attributes: MLASTAttr[]
84
84
  +childNodes: MLASTChildNode[]
85
85
  +blockBehavior: MLASTBlockBehavior | null
86
- +pairNode: MLASTElementCloseTag | null
86
+ +pairNodeUuid: string | null
87
87
  +isGhost: boolean
88
88
  +isFragment: boolean
89
89
  }
@@ -92,8 +92,7 @@ classDiagram
92
92
  <<interface>>
93
93
  +type: "endtag"
94
94
  +depth: number
95
- +parentNode: null
96
- +pairNode: MLASTElement
95
+ +pairNodeUuid: string | null
97
96
  }
98
97
 
99
98
  class MLASTComment {
@@ -216,7 +215,7 @@ Each AST node is ultimately converted into an **MLDOM** node by `@markuplint/ml-
216
215
  **Special nodes:**
217
216
 
218
217
  - **`MLBlock`** (`nodeType: 101`) is a markuplint-specific extension with no DOM Standard equivalent. It acts as a transparent container -- its children are treated as belonging to the parent for tree traversal.
219
- - **`MLElementCloseTag`** is not created by `createNode()`. Instead, `MLElement` internally creates it from its `pairNode` reference. It exists only as a satellite of its paired element and is not part of the DOM tree traversal.
218
+ - **`MLElementCloseTag`** is not created by `createNode()`. Instead, `MLElement` internally resolves the `pairNodeUuid` to look up the closing tag's AST node and creates an `MLElementCloseTag` from it. It exists only as a satellite of its paired element and is not part of the DOM tree traversal.
220
219
  - **`MLASTInvalid`** is a recovery node -- it is never preserved as-is in MLDOM, but converted to either an `MLElement` (with tag name `x-invalid`) or an `MLText`, depending on its `kind` field.
221
220
 
222
221
  See [Node Reference -- AST to MLDOM Mapping](docs/node-reference.md#ast-to-mldom-mapping) for details.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
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-rc.1](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-03-27)
7
+
8
+ - feat(ml-ast)!: replace parentNode/pairNode with UUID string references ([9d56f45](https://github.com/markuplint/markuplint/commit/9d56f4545e7e6b2378043c3c578130bb4ddd72cd))
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ - `parentNode` and `pairNode` properties on AST nodes
13
+ are replaced by `parentNodeUuid` and `pairNodeUuid` (UUID strings).
14
+ The old object reference properties are removed from the parse output
15
+ by post-processing.
16
+
17
+ # [5.0.0-rc.0](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.3...v5.0.0-rc.0) (2026-03-12)
18
+
19
+ **Note:** Version bump only for package @markuplint/ml-ast
20
+
6
21
  # [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
22
 
8
23
  **Note:** Version bump only for package @markuplint/ml-ast
@@ -51,7 +51,7 @@ function handle(node: MLASTNode) {
51
51
 
52
52
  ### 特殊なノード
53
53
 
54
- - **`MLASTElementCloseTag`** は `createNode()` を**経由しません**。代わりに `MLElement` が内部で `pairNode` 参照から `MLElementCloseTag` を生成します。`MLElementCloseTag` は DOM ツリー走査の対象ではなく、ペアとなる要素の付属物としてのみ存在します。
54
+ - **`MLASTElementCloseTag`** は `createNode()` を**経由しません**。代わりに `MLElement` `pairNodeUuid` を解決して閉じタグの AST ノードを検索し、`MLElementCloseTag` を生成します。`MLElementCloseTag` は DOM ツリー走査の対象ではなく、ペアとなる要素の付属物としてのみ存在します。
55
55
  - **`MLASTPreprocessorSpecificBlock`** は `MLBlock` にマッピングされます。これは DOM Standard に相当するものがない **markuplint 独自の拡張**です。DOM Standard の範囲外であるカスタム `nodeType` `101` を使用します。`MLBlock` は透過的であり、子ノードはツリー走査時に親ノードに属するものとして扱われます。
56
56
  - **`MLASTHTMLAttr`** と **`MLASTSpreadAttr`** は `MLAttr` にマッピングされ、DOM `Attr` インターフェース(`nodeType: 2`)を実装します。属性は `createNode()` を経由せず、`MLElement.attributes`(`MLNamedNodeMap`)を通じてアクセスされます。
57
57
 
@@ -77,11 +77,11 @@ function handle(node: MLASTNode) {
77
77
 
78
78
  `MLASTToken` を継承し構造的メタデータを追加する内部(非エクスポート)基底インターフェースです。すべての具象ノード型がこれを継承します。
79
79
 
80
- | フィールド | 型 | 説明 |
81
- | ------------ | ------------------------- | --------------------------------------------- |
82
- | `type` | `MLASTNodeType` | 具象ノード種別を識別する判別タグ |
83
- | `nodeName` | `string` | ノード名(タグ名、`#text`、`#comment` など) |
84
- | `parentNode` | `MLASTParentNode \| null` | 親ノードへの参照、トップレベルの場合は `null` |
80
+ | フィールド | 型 | 説明 |
81
+ | ---------------- | ---------------- | -------------------------------------------- |
82
+ | `type` | `MLASTNodeType` | 具象ノード種別を識別する判別タグ |
83
+ | `nodeName` | `string` | ノード名(タグ名、`#text`、`#comment` など) |
84
+ | `parentNodeUuid` | `string \| null` | 親ノードの UUID、トップレベルの場合は `null` |
85
85
 
86
86
  ## MLASTDocument
87
87
 
@@ -124,21 +124,21 @@ function handle(node: MLASTNode) {
124
124
 
125
125
  **役割:** 開始タグ(例:`<div class="foo">`)を表します。AST における主要な要素表現であり、最も機能豊富なノード型です。子ノード、属性を所有し、対応する閉じタグへの参照を保持します。
126
126
 
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` | ゴーストノード(パーサーが推定した省略タグ)かどうか |
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
+ | `pairNodeUuid` | `string \| null` | 対応する閉じタグの UUID、void/自己閉じ要素の場合は `null` |
139
+ | `tagOpenChar` | `string` | タグを開く文字(通常 `"<"`) |
140
+ | `tagCloseChar` | `string` | タグを閉じる文字(通常 `">"`) |
141
+ | `isGhost` | `boolean` | ゴーストノード(パーサーが推定した省略タグ)かどうか |
142
142
 
143
143
  ### 要素タイプの分類
144
144
 
@@ -160,12 +160,14 @@ function handle(node: MLASTNode) {
160
160
 
161
161
  ### ペアノードの関係
162
162
 
163
- `pairNode` フィールドは開始タグと閉じタグの間に**双方向リンク**を作成します:
163
+ `pairNodeUuid` フィールドは開始タグと閉じタグの間に UUID 文字列による**双方向リンク**を作成します(オブジェクト参照ではなく、JSON シリアライズが可能):
164
164
 
165
- - `MLASTElement.pairNode` は対応する `MLASTElementCloseTag` を指す
166
- - `MLASTElementCloseTag.pairNode` は対応する `MLASTElement` を指す
165
+ - `MLASTElement.pairNodeUuid` は対応する `MLASTElementCloseTag` の UUID を含む
166
+ - `MLASTElementCloseTag.pairNodeUuid` は対応する `MLASTElement` の UUID を含む
167
167
 
168
- void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNode` `null` です。
168
+ UUID を実際のノードに解決するには、`MLASTDocument.nodeList` 内で `uuid` フィールドを照合して検索します。
169
+
170
+ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNodeUuid` は `null` です。
169
171
 
170
172
  ### フラグメント要素
171
173
 
@@ -186,24 +188,22 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
186
188
  - `namespace: "http://www.w3.org/1999/xhtml"`
187
189
  - `attributes`:`class` と `id` 属性を含む配列
188
190
  - `childNodes`:`<p>` 要素とテキストノードを含む配列
189
- - `pairNode`:`</div>` 閉じタグへの参照
191
+ - `pairNodeUuid`:`</div>` 閉じタグの UUID
190
192
 
191
193
  ## MLASTElementCloseTag
192
194
 
193
195
  **type 判別子:** `'endtag'`
194
196
 
195
- **役割:** 閉じタグ(例:`</div>`)を表します。`pairNode` フィールドを通じて常に `MLASTElement` とペアになります。
196
-
197
- | フィールド | 型 | 説明 |
198
- | -------------- | -------------- | ---------------------------------- |
199
- | `type` | `'endtag'` | 判別タグ |
200
- | `depth` | `number` | ドキュメントツリーでのネストの深さ |
201
- | `parentNode` | `null` | 閉じタグでは常に `null` |
202
- | `pairNode` | `MLASTElement` | 対応する開始タグ |
203
- | `tagOpenChar` | `string` | タグを開く文字(通常 `"</"`) |
204
- | `tagCloseChar` | `string` | タグを閉じる文字(通常 `">"`) |
197
+ **役割:** 閉じタグ(例:`</div>`)を表します。`pairNodeUuid` フィールドを通じて常に `MLASTElement` とペアになります。
205
198
 
206
- **`parentNode` が常に `null` である理由:** AST モデルでは、開始タグ(`MLASTElement`)のみが親子ツリー構造に参加します。閉じタグは `pairNode` を通じて開始タグにリンクされた別のノードとして存在しますが、どの親ノードの子でもありません。これによりツリー内での要素の重複を避けています。
199
+ | フィールド | 型 | 説明 |
200
+ | ---------------- | ---------------- | --------------------------------------------- |
201
+ | `type` | `'endtag'` | 判別タグ |
202
+ | `depth` | `number` | ドキュメントツリーでのネストの深さ |
203
+ | `parentNodeUuid` | `string \| null` | 親ノードの UUID(ペアとなる開始タグと同じ親) |
204
+ | `pairNodeUuid` | `string \| null` | 対応する開始タグの UUID |
205
+ | `tagOpenChar` | `string` | タグを開く文字(通常 `"</"`) |
206
+ | `tagCloseChar` | `string` | タグを閉じる文字(通常 `">"`) |
207
207
 
208
208
  ## MLASTComment
209
209
 
@@ -414,7 +414,7 @@ void 要素(`<br>`、`<img>` など)と自己閉じ要素の場合、`pairNo
414
414
  | `type` | `'spread'` | 判別タグ |
415
415
  | `nodeName` | `'#spread'` | 常に `'#spread'` |
416
416
 
417
- `MLASTSpreadAttr` は `MLASTAbstractNode` ではなく `MLASTToken` を直接継承するため、位置情報(`uuid`、`raw`、`offset` など)はありますが、`parentNode` や `depth` はありません。
417
+ `MLASTSpreadAttr` は `MLASTAbstractNode` ではなく `MLASTToken` を直接継承するため、位置情報(`uuid`、`raw`、`offset` など)はありますが、`parentNodeUuid` や `depth` はありません。
418
418
 
419
419
  ## 共用体型リファレンス
420
420
 
@@ -51,7 +51,7 @@ The mapping is performed by `createNode()` in `ml-core`:
51
51
 
52
52
  ### Special Nodes
53
53
 
54
- - **`MLASTElementCloseTag`** is **not** passed through `createNode()`. Instead, `MLElement` internally creates an `MLElementCloseTag` from its `pairNode` reference. `MLElementCloseTag` is not part of the DOM tree traversal; it exists only as a satellite of its paired element.
54
+ - **`MLASTElementCloseTag`** is **not** passed through `createNode()`. Instead, `MLElement` resolves the `pairNodeUuid` to look up the closing tag's AST node and creates an `MLElementCloseTag` from it. `MLElementCloseTag` is not part of the DOM tree traversal; it exists only as a satellite of its paired element.
55
55
  - **`MLASTPreprocessorSpecificBlock`** maps to `MLBlock`, which is a **markuplint-specific extension** with no DOM Standard equivalent. It uses a custom `nodeType` of `101` (beyond the DOM Standard range). `MLBlock` is transparent -- its children are treated as belonging to the parent node for tree traversal purposes.
56
56
  - **`MLASTHTMLAttr`** and **`MLASTSpreadAttr`** map to `MLAttr`, which implements the DOM `Attr` interface (`nodeType: 2`). Attributes are accessed via `MLElement.attributes` (`MLNamedNodeMap`), not through `createNode()`.
57
57
 
@@ -77,11 +77,11 @@ End positions can be derived: `endOffset = offset + raw.length`. For `endLine` a
77
77
 
78
78
  An internal (non-exported) base interface that extends `MLASTToken` with structural metadata. All concrete node types extend this.
79
79
 
80
- | Field | Type | Description |
81
- | ------------ | ------------------------- | ----------------------------------------------------------- |
82
- | `type` | `MLASTNodeType` | Discriminant tag identifying the concrete node kind |
83
- | `nodeName` | `string` | The node name (tag name, `#text`, `#comment`, etc.) |
84
- | `parentNode` | `MLASTParentNode \| null` | Reference to the parent node, or `null` for top-level nodes |
80
+ | Field | Type | Description |
81
+ | ---------------- | ---------------- | ------------------------------------------------------ |
82
+ | `type` | `MLASTNodeType` | Discriminant tag identifying the concrete node kind |
83
+ | `nodeName` | `string` | The node name (tag name, `#text`, `#comment`, etc.) |
84
+ | `parentNodeUuid` | `string \| null` | UUID of the parent node, or `null` for top-level nodes |
85
85
 
86
86
  ## MLASTDocument
87
87
 
@@ -124,21 +124,21 @@ Produces a node with `name: "html"`, `publicId: ""`, `systemId: ""`.
124
124
 
125
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.
126
126
 
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) |
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
+ | `pairNodeUuid` | `string \| null` | UUID of 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) |
142
142
 
143
143
  ### Element Type Classification
144
144
 
@@ -160,12 +160,14 @@ When `isGhost` is `true`, the element was not explicitly written in the source b
160
160
 
161
161
  ### Pair Node Relationship
162
162
 
163
- The `pairNode` field creates a **bidirectional link** between opening and closing tags:
163
+ The `pairNodeUuid` field creates a **bidirectional link** between opening and closing tags via UUID strings (not object references, enabling JSON serialization):
164
164
 
165
- - `MLASTElement.pairNode` points to its `MLASTElementCloseTag`
166
- - `MLASTElementCloseTag.pairNode` points back to the `MLASTElement`
165
+ - `MLASTElement.pairNodeUuid` contains the UUID of its `MLASTElementCloseTag`
166
+ - `MLASTElementCloseTag.pairNodeUuid` contains the UUID of its `MLASTElement`
167
167
 
168
- For void elements (`<br>`, `<img>`, etc.) and self-closing elements, `pairNode` is `null`.
168
+ To resolve a UUID to the actual node, look it up in `MLASTDocument.nodeList` by matching the `uuid` field.
169
+
170
+ For void elements (`<br>`, `<img>`, etc.) and self-closing elements, `pairNodeUuid` is `null`.
169
171
 
170
172
  ### Fragment Elements
171
173
 
@@ -186,24 +188,22 @@ The `<div>` produces an `MLASTElement` with:
186
188
  - `namespace: "http://www.w3.org/1999/xhtml"`
187
189
  - `attributes`: array containing `class` and `id` attributes
188
190
  - `childNodes`: array containing the `<p>` element and text nodes
189
- - `pairNode`: reference to the `</div>` closing tag
191
+ - `pairNodeUuid`: UUID of the `</div>` closing tag
190
192
 
191
193
  ## MLASTElementCloseTag
192
194
 
193
195
  **Type discriminant:** `'endtag'`
194
196
 
195
- **Role:** Represents a closing element tag (e.g., `</div>`). Always paired with an `MLASTElement` via the `pairNode` field.
196
-
197
- | Field | Type | Description |
198
- | -------------- | -------------- | -------------------------------------------------- |
199
- | `type` | `'endtag'` | Discriminant tag |
200
- | `depth` | `number` | Nesting depth in the document tree |
201
- | `parentNode` | `null` | Always `null` for closing tags |
202
- | `pairNode` | `MLASTElement` | The matching opening element tag |
203
- | `tagOpenChar` | `string` | The characters that open this tag (usually `"</"`) |
204
- | `tagCloseChar` | `string` | The characters that close this tag (usually `">"`) |
197
+ **Role:** Represents a closing element tag (e.g., `</div>`). Always paired with an `MLASTElement` via the `pairNodeUuid` field.
205
198
 
206
- **Why `parentNode` is always `null`:** In the AST model, only the opening tag (`MLASTElement`) participates in the parent-child tree structure. The closing tag exists as a separate node linked to the opening tag via `pairNode`, but it is not a child of any parent node. This avoids duplicating the element in the tree.
199
+ | Field | Type | Description |
200
+ | ---------------- | ---------------- | --------------------------------------------------------------- |
201
+ | `type` | `'endtag'` | Discriminant tag |
202
+ | `depth` | `number` | Nesting depth in the document tree |
203
+ | `parentNodeUuid` | `string \| null` | UUID of the parent node (same parent as the paired opening tag) |
204
+ | `pairNodeUuid` | `string \| null` | UUID of the matching opening element tag |
205
+ | `tagOpenChar` | `string` | The characters that open this tag (usually `"</"`) |
206
+ | `tagCloseChar` | `string` | The characters that close this tag (usually `">"`) |
207
207
 
208
208
  ## MLASTComment
209
209
 
@@ -414,7 +414,7 @@ These fields are set by framework-specific parsers:
414
414
  | `type` | `'spread'` | Discriminant tag |
415
415
  | `nodeName` | `'#spread'` | Always `'#spread'` |
416
416
 
417
- Note that `MLASTSpreadAttr` extends `MLASTToken` directly (not `MLASTAbstractNode`), so it has positional information (`uuid`, `raw`, `offset`, 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 `parentNodeUuid` or `depth`.
418
418
 
419
419
  ## Union Types Reference
420
420
 
package/lib/types.d.ts CHANGED
@@ -74,8 +74,13 @@ interface MLASTAbstractNode extends MLASTToken {
74
74
  readonly type: MLASTNodeType;
75
75
  /** The node name (tag name, `#text`, `#comment`, etc.) */
76
76
  readonly nodeName: string;
77
- /** Reference to the parent node, or `null` for top-level nodes */
78
- readonly parentNode: MLASTParentNode | null;
77
+ /** UUID of the parent node, or `null` for top-level nodes */
78
+ readonly parentNodeUuid: string | null;
79
+ /**
80
+ * @internal Temporary object reference set during parsing and removed by post-processing.
81
+ * Use {@link parentNodeUuid} instead.
82
+ */
83
+ readonly parentNode?: MLASTParentNode | null;
79
84
  }
80
85
  /**
81
86
  * A DOCTYPE declaration node (e.g. `<!DOCTYPE html>`).
@@ -114,8 +119,13 @@ export interface MLASTElement extends MLASTAbstractNode {
114
119
  readonly childNodes: readonly MLASTChildNode[];
115
120
  /** Block behavior associated with this element, if any */
116
121
  readonly blockBehavior: MLASTBlockBehavior | null;
117
- /** The matching closing tag, or `null` for void / self-closing elements */
118
- readonly pairNode: MLASTElementCloseTag | null;
122
+ /** UUID of the matching closing tag, or `null` for void / self-closing elements */
123
+ readonly pairNodeUuid: string | null;
124
+ /**
125
+ * @internal Temporary object reference set during parsing and removed by post-processing.
126
+ * Use {@link pairNodeUuid} instead.
127
+ */
128
+ readonly pairNode?: MLASTElementCloseTag | null;
119
129
  /** The characters that open this tag (usually `"<"`) */
120
130
  readonly tagOpenChar: string;
121
131
  /** The characters that close this tag (usually `">"`) */
@@ -125,16 +135,19 @@ export interface MLASTElement extends MLASTAbstractNode {
125
135
  }
126
136
  /**
127
137
  * A closing element tag (e.g. `</div>`).
128
- * Always paired with an {@link MLASTElement} via `pairNode`.
138
+ * Always paired with an {@link MLASTElement} via `pairNodeUuid`.
129
139
  */
130
140
  export interface MLASTElementCloseTag extends MLASTAbstractNode {
131
141
  readonly type: 'endtag';
132
142
  /** Nesting depth in the document tree */
133
143
  readonly depth: number;
134
- /** Closing tags do not have a parent node in the AST */
135
- readonly parentNode: null;
136
- /** The matching opening element tag */
137
- readonly pairNode: MLASTElement;
144
+ /** UUID of the matching opening element tag */
145
+ readonly pairNodeUuid: string | null;
146
+ /**
147
+ * @internal Temporary object reference set during parsing and removed by post-processing.
148
+ * Use {@link pairNodeUuid} instead.
149
+ */
150
+ readonly pairNode?: MLASTElement;
138
151
  /** The characters that open this tag (usually `"</"`) */
139
152
  readonly tagOpenChar: string;
140
153
  /** The characters that close this tag (usually `">"`) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-ast",
3
- "version": "5.0.0-dev.5+e96392f56",
3
+ "version": "5.0.0-rc.1",
4
4
  "description": "The markuplint AST types.",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -26,5 +26,5 @@
26
26
  "dev": "tsc --watch --project tsconfig.build.json",
27
27
  "clean": "tsc --build --clean tsconfig.build.json"
28
28
  },
29
- "gitHead": "e96392f56e4bc8165ba59622b41c822703a96372"
29
+ "gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
30
30
  }