@markuplint/parser-utils 5.0.0-alpha.0 → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -148,7 +148,7 @@ ParserError (基底クラス)
148
148
 
149
149
  ## IDL 属性マッピング
150
150
 
151
- `searchIDLAttribute` は React スタイルの IDL 属性名と HTML コンテンツ属性名の双方向マッピングを提供します(例: `className` → `class`、`htmlFor` → `for`)。spec が `useIDLAttributeNames: true` を設定している場合に、`@markuplint/ml-core` の `MLAttr` コンストラクタから呼び出されます。
151
+ `searchIDLAttribute` は React スタイルの IDL 属性名と HTML コンテンツ属性名の双方向マッピングを提供します(例: `className` → `class`、`htmlFor` → `for`)。spec が `acceptedAttrNames`(`'idl'` または `'both'`)を設定している場合に、`@markuplint/ml-core` の `MLAttr` コンストラクタから呼び出されます。
152
152
 
153
153
  ## 外部依存
154
154
 
package/ARCHITECTURE.md CHANGED
@@ -174,7 +174,7 @@ Additionally, `debug.ts` provides `PerformanceTimer` for measuring parse phase d
174
174
 
175
175
  ## IDL Attribute Mapping
176
176
 
177
- `searchIDLAttribute` maps between React-style IDL attribute names and HTML content attribute names. It is called by `@markuplint/ml-core`'s `MLAttr` constructor when the spec sets `useIDLAttributeNames: true`. It maintains a comprehensive mapping table derived from React's `possibleStandardNames.js`, covering:
177
+ `searchIDLAttribute` maps between React-style IDL attribute names and HTML content attribute names. It is called by `@markuplint/ml-core`'s `MLAttr` constructor when the spec sets `acceptedAttrNames` (either `'idl'` or `'both'`). It maintains a comprehensive mapping table derived from React's `possibleStandardNames.js`, covering:
178
178
 
179
179
  - HTML attributes (e.g., `className` -> `class`, `htmlFor` -> `for`, `tabIndex` -> `tabindex`)
180
180
  - SVG attributes (e.g., `strokeWidth` -> `stroke-width`, `clipPath` -> `clip-path`)
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.0.0-alpha.1](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2026-02-22)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **parser-utils:** skip text trimming when text node is a descendant of the next node ([1bfcede](https://github.com/markuplint/markuplint/commit/1bfcedebbd115ea817df76a979b4e10a26d0a2b2))
11
+
6
12
  # [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
7
13
 
8
14
  ### Bug Fixes
@@ -173,7 +173,7 @@ afterFlattenNodes(
173
173
  1. **残留ノードの露出** — 既知のノード間の空白と無効なマークアップを発見する
174
174
  2. **孤立終了タグ → ボーガス** — 対応する開始タグがない終了タグを `invalid` ノードに変換する
175
175
  3. **テキストの結合** — 同じオフセットにある隣接する `#text` ノードをマージする
176
- 4. **テキストのトリム** — 重複するテキストノードの境界をトリムする
176
+ 4. **テキストのトリム** — フラットリスト上で次のノードとソース範囲が重複するテキストノードをトリムする。テキストノードがツリー上で次のノードの子孫である場合はスキップする(Markdown 等の合成パーサーでは、子要素が親要素と同じソース範囲を共有することがあるため)
177
177
 
178
178
  ### ステップ 9: restoreNode()
179
179
 
@@ -173,7 +173,7 @@ Performs four cleanup passes:
173
173
  1. **Expose remnant nodes** — discovers whitespace and invalid markup between known nodes
174
174
  2. **Orphan end tags → bogus** — converts unmatched end tags to `invalid` nodes
175
175
  3. **Concatenate text** — merges adjacent `#text` nodes at the same offset
176
- 4. **Trim text** — trims overlapping text node boundaries
176
+ 4. **Trim text** — trims text nodes whose source range overlaps with the next node in the flat list. Skips trimming when the text node is a tree-descendant of the next node, which can occur with synthetic parsers (e.g., Markdown) where child elements share the same source range as their parent
177
177
 
178
178
  ### Step 9: restoreNode()
179
179
 
package/lib/parser.js CHANGED
@@ -1011,6 +1011,24 @@ export class Parser {
1011
1011
  col: getEndCol(token.raw, token.col),
1012
1012
  };
1013
1013
  }
1014
+ /**
1015
+ * Checks whether a node is a descendant of another node by walking up
1016
+ * the parent chain.
1017
+ *
1018
+ * @param node - The node to test.
1019
+ * @param potentialAncestor - The node that may be an ancestor.
1020
+ * @returns `true` if `node` is a descendant of `potentialAncestor`.
1021
+ */
1022
+ #isDescendantOf(node, potentialAncestor) {
1023
+ let current = 'parentNode' in node ? node.parentNode : null;
1024
+ while (current) {
1025
+ if (current === potentialAncestor) {
1026
+ return true;
1027
+ }
1028
+ current = current.parentNode;
1029
+ }
1030
+ return false;
1031
+ }
1014
1032
  #orphanEndTagToBogusMark(nodeList) {
1015
1033
  const newNodeList = [];
1016
1034
  for (let node of nodeList) {
@@ -1304,10 +1322,17 @@ export class Parser {
1304
1322
  this.#originalRawCode = originalRawCode ?? this.#originalRawCode;
1305
1323
  }
1306
1324
  /**
1307
- * Trim overlapping sections of text nodes for proper node separation
1325
+ * Trims text nodes whose source range overlaps with the next node in
1326
+ * the flat list. This prevents text content from bleeding into adjacent
1327
+ * elements that occupy a later (or overlapping) source range.
1328
+ *
1329
+ * Skips trimming when the text node is a descendant of the next node
1330
+ * in the tree hierarchy, because synthetic parsers (e.g., Markdown)
1331
+ * can produce child elements that share the same source range as their
1332
+ * parent and therefore appear before the parent in offset-sorted order.
1308
1333
  *
1309
- * @param nodeList
1310
- * @returns
1334
+ * @param nodeList - The flat, offset-sorted node list to process.
1335
+ * @returns A new node list with overlapping text nodes trimmed.
1311
1336
  */
1312
1337
  #trimText(nodeList) {
1313
1338
  const newNodeList = [];
@@ -1318,7 +1343,7 @@ export class Parser {
1318
1343
  node.raw.length > 0) {
1319
1344
  const prevNodeEndOffset = prevNode.offset + prevNode.raw.length;
1320
1345
  const nodeStartOffset = node.offset;
1321
- if (prevNodeEndOffset > nodeStartOffset) {
1346
+ if (prevNodeEndOffset > nodeStartOffset && !this.#isDescendantOf(prevNode, node)) {
1322
1347
  const prevNodeRaw = prevNode.raw;
1323
1348
  const prevNodeTrimmedRaw = prevNodeRaw.slice(0, nodeStartOffset - prevNode.offset);
1324
1349
  this.updateRaw(prevNode, prevNodeTrimmedRaw);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/parser-utils",
3
- "version": "5.0.0-alpha.0",
3
+ "version": "5.0.0-alpha.1",
4
4
  "description": "Utility module for markuplint parser plugin",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -31,15 +31,15 @@
31
31
  "clean": "tsc --build --clean tsconfig.build.json"
32
32
  },
33
33
  "dependencies": {
34
- "@markuplint/ml-ast": "5.0.0-alpha.0",
35
- "@markuplint/ml-spec": "5.0.0-alpha.0",
36
- "@markuplint/types": "5.0.0-alpha.0",
34
+ "@markuplint/ml-ast": "5.0.0-alpha.1",
35
+ "@markuplint/ml-spec": "5.0.0-alpha.1",
36
+ "@markuplint/types": "5.0.0-alpha.1",
37
37
  "debug": "4.4.3",
38
- "espree": "11.1.0",
38
+ "espree": "11.1.1",
39
39
  "type-fest": "5.4.4"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@typescript-eslint/typescript-estree": "8.56.0"
43
43
  },
44
- "gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
44
+ "gitHead": "78a295e73a097a1ce09c777c06fa21ab68136387"
45
45
  }