@markuplint/parser-utils 5.0.0-rc.0 → 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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
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
+ **Note:** Version bump only for package @markuplint/parser-utils
9
+
6
10
  # [5.0.0-rc.0](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.3...v5.0.0-rc.0) (2026-03-12)
7
11
 
8
12
  **Note:** Version bump only for package @markuplint/parser-utils
package/lib/debugger.js CHANGED
@@ -70,7 +70,7 @@ export function nodeTreeDebugView(nodeTree, idFilter = false) {
70
70
  return;
71
71
  }
72
72
  lines.push(`${i.toString().padStart(3, '0')}: [${filter(n.uuid)}] ${' '.repeat(Math.max(n.depth, 0))}${n.type === 'endtag' ? '/' : ''}${n.nodeName}(${filter(n.uuid)})${n.type === 'starttag' && n.isGhost ? '[👻]' : ''}${n.type === 'starttag'
73
- ? ` => ${n.pairNode ? `/${n.pairNode.nodeName}(${filter(n.pairNode.uuid)})` : '💀'}`
73
+ ? ` => ${n.pairNodeUuid ? `/${nodeTree.find(p => p.uuid === n.pairNodeUuid)?.nodeName ?? '?'}(${filter(n.pairNodeUuid)})` : '💀'}`
74
74
  : ''}`);
75
75
  if (n.type === 'starttag' || n.type === 'psblock') {
76
76
  for (const c of n.childNodes ?? []) {
@@ -34,5 +34,5 @@ function getParentNamespace(parentNode) {
34
34
  ? 'http://www.w3.org/1998/Math/MathML'
35
35
  : 'http://www.w3.org/1999/xhtml';
36
36
  }
37
- return getParentNamespace(parentNode.parentNode);
37
+ return getParentNamespace(parentNode.parentNode ?? null);
38
38
  }
@@ -75,6 +75,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
75
75
  type: 'psblock',
76
76
  depth: node.depth,
77
77
  nodeName: `#ps:${tag.type}`,
78
+ parentNodeUuid: node.parentNode?.uuid ?? null,
78
79
  parentNode: node.parentNode,
79
80
  childNodes: [],
80
81
  blockBehavior: null,
@@ -94,6 +95,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
94
95
  ...token,
95
96
  nodeName: '#text',
96
97
  type: 'text',
98
+ parentNodeUuid: node.parentNode?.uuid ?? null,
97
99
  parentNode: node.parentNode,
98
100
  depth: node.depth,
99
101
  };
@@ -106,6 +108,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
106
108
  type: 'psblock',
107
109
  depth: node.depth,
108
110
  nodeName: `#ps:${tag.type}`,
111
+ parentNodeUuid: node.parentNode?.uuid ?? null,
109
112
  parentNode: node.parentNode,
110
113
  childNodes: [],
111
114
  blockBehavior: null,
@@ -120,6 +123,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
120
123
  ...token,
121
124
  nodeName: '#text',
122
125
  type: 'text',
126
+ parentNodeUuid: node.parentNode?.uuid ?? null,
123
127
  parentNode: node.parentNode,
124
128
  depth: node.depth,
125
129
  };
@@ -1,43 +1,2 @@
1
- /**
2
- * Positional and contextual information associated with a parser error,
3
- * used to construct meaningful error messages with source locations.
4
- */
5
- export type ParserErrorInfo = {
6
- readonly line?: number;
7
- readonly col?: number;
8
- readonly raw?: string;
9
- readonly stack?: string;
10
- };
11
- /**
12
- * An error that occurs during parsing, carrying the source line, column,
13
- * and raw text where the error was encountered.
14
- */
15
- export declare class ParserError extends Error {
16
- readonly col: number;
17
- readonly line: number;
18
- name: string;
19
- readonly raw: string;
20
- constructor(message: string, info: ParserErrorInfo);
21
- }
22
- /**
23
- * A parser error specific to a particular HTML element, including
24
- * the node name of the element that caused the error in the message.
25
- */
26
- export declare class TargetParserError extends ParserError {
27
- name: string;
28
- readonly nodeName: string | null;
29
- constructor(message: string, info: ParserErrorInfo & {
30
- readonly nodeName?: string | null;
31
- });
32
- }
33
- /**
34
- * A parser error that occurs while reading a configuration file,
35
- * including the file path in the error message for easier debugging.
36
- */
37
- export declare class ConfigParserError extends ParserError {
38
- readonly filePath: string;
39
- name: string;
40
- constructor(message: string, info: ParserErrorInfo & {
41
- readonly filePath: string;
42
- });
43
- }
1
+ export { ConfigParserError, ParserError, TargetParserError } from '@markuplint/shared';
2
+ export type { ParserErrorInfo } from '@markuplint/shared';
@@ -1,47 +1,2 @@
1
- /**
2
- * An error that occurs during parsing, carrying the source line, column,
3
- * and raw text where the error was encountered.
4
- */
5
- export class ParserError extends Error {
6
- col;
7
- line;
8
- name = 'ParserError';
9
- raw;
10
- constructor(message, info) {
11
- super(message);
12
- this.line = info.line ?? 1;
13
- this.col = info.col ?? 0;
14
- this.raw = info.raw ?? '';
15
- this.stack = info.stack ?? this.stack;
16
- }
17
- }
18
- /**
19
- * A parser error specific to a particular HTML element, including
20
- * the node name of the element that caused the error in the message.
21
- */
22
- export class TargetParserError extends ParserError {
23
- name = 'TargetParserError';
24
- nodeName;
25
- constructor(message, info) {
26
- const errMsg = info.nodeName
27
- ? `The ${info.nodeName} is invalid element (${info.line}:${info.col}): ${message}`
28
- : message;
29
- super(errMsg, info);
30
- this.nodeName = info.nodeName ?? null;
31
- }
32
- }
33
- /**
34
- * A parser error that occurs while reading a configuration file,
35
- * including the file path in the error message for easier debugging.
36
- */
37
- export class ConfigParserError extends ParserError {
38
- filePath;
39
- name = 'ConfigParserError';
40
- constructor(message, info) {
41
- const pos = info.line != null && info.line != null ? `(${info.line}:${info.col})` : '';
42
- const file = ` in ${info.filePath}${pos}`;
43
- const errMsg = `${message}${file}`;
44
- super(errMsg, info);
45
- this.filePath = info.filePath;
46
- }
47
- }
1
+ // Re-export from @markuplint/shared — these classes are now defined there.
2
+ export { ConfigParserError, ParserError, TargetParserError } from '@markuplint/shared';
package/lib/parser.js CHANGED
@@ -196,6 +196,13 @@ export class Parser {
196
196
  }
197
197
  nodeList = [fmNode, ...newNodeList];
198
198
  }
199
+ timer.push('removeCircularRefs');
200
+ // Remove circular object references (parentNodeUuid/pairNodeUuid are already set)
201
+ for (const node of nodeList) {
202
+ const n = node;
203
+ delete n.parentNode;
204
+ delete n.pairNode;
205
+ }
199
206
  timer.log();
200
207
  domLog(nodeList);
201
208
  this.#reset();
@@ -381,6 +388,7 @@ export class Parser {
381
388
  ...this.createToken(token),
382
389
  type: 'doctype',
383
390
  nodeName: '#doctype',
391
+ parentNodeUuid: token.parentNode?.uuid ?? null,
384
392
  };
385
393
  return [node];
386
394
  }
@@ -401,6 +409,7 @@ export class Parser {
401
409
  type: 'comment',
402
410
  nodeName: '#comment',
403
411
  isBogus,
412
+ parentNodeUuid: token.parentNode?.uuid ?? null,
404
413
  };
405
414
  return [node];
406
415
  }
@@ -419,6 +428,7 @@ export class Parser {
419
428
  ...this.createToken(token),
420
429
  type: 'text',
421
430
  nodeName: '#text',
431
+ parentNodeUuid: token.parentNode?.uuid ?? null,
422
432
  };
423
433
  if (options?.researchTags) {
424
434
  const nodes = this.parseCodeFragment(token);
@@ -457,7 +467,9 @@ export class Parser {
457
467
  childNodes: [],
458
468
  blockBehavior: null,
459
469
  parentNode: token.parentNode,
470
+ parentNodeUuid: token.parentNode?.uuid ?? null,
460
471
  pairNode: null,
472
+ pairNodeUuid: null,
461
473
  tagCloseChar: '',
462
474
  tagOpenChar: '',
463
475
  isGhost: true,
@@ -504,6 +516,7 @@ export class Parser {
504
516
  nodeName: `#ps:${token.nodeName}`,
505
517
  childNodes: [],
506
518
  isBogus: false,
519
+ parentNodeUuid: token.parentNode?.uuid ?? null,
507
520
  };
508
521
  const siblings = this.visitChildren(childNodes, block);
509
522
  return [block, ...siblings];
@@ -664,6 +677,7 @@ export class Parser {
664
677
  depth,
665
678
  nodeName: '#text',
666
679
  parentNode: null,
680
+ parentNodeUuid: null,
667
681
  };
668
682
  nodes.push(textNode);
669
683
  }
@@ -853,7 +867,7 @@ export class Parser {
853
867
  const newChildNodes = [...parentNode.childNodes];
854
868
  for (const appendingChild of childNodes) {
855
869
  const currentIndex = parentNode.childNodes.findIndex(n => n.uuid === appendingChild.uuid);
856
- Object.assign(appendingChild, { parentNode });
870
+ Object.assign(appendingChild, { parentNode, parentNodeUuid: parentNode.uuid });
857
871
  if (currentIndex === -1) {
858
872
  newChildNodes.push(appendingChild);
859
873
  continue;
@@ -921,9 +935,9 @@ export class Parser {
921
935
  raw: nodes.map(n => n.raw).join(''),
922
936
  };
923
937
  for (const node of nodes) {
924
- this.#removeChild(node.parentNode, node);
938
+ this.#removeChild(node.parentNode ?? null, node);
925
939
  }
926
- this.appendChild(textNode.parentNode, textNode);
940
+ this.appendChild(textNode.parentNode ?? null, textNode);
927
941
  return textNode;
928
942
  }
929
943
  #convertIntoInvalidNode(node) {
@@ -986,7 +1000,7 @@ export class Parser {
986
1000
  const prevEndOffset = sequentailPrevNode
987
1001
  ? sequentailPrevNode.offset + sequentailPrevNode.raw.length
988
1002
  : 0;
989
- const remnantNodes = this.#createRemnantNode(prevEndOffset, node.offset, node.depth, node.parentNode, invalidNode, whitespace);
1003
+ const remnantNodes = this.#createRemnantNode(prevEndOffset, node.offset, node.depth, node.parentNode ?? null, invalidNode, whitespace);
990
1004
  if (remnantNodes) {
991
1005
  newNodeList.push(...remnantNodes);
992
1006
  }
@@ -997,7 +1011,7 @@ export class Parser {
997
1011
  if (!lastNode) {
998
1012
  return newNodeList;
999
1013
  }
1000
- const remnantNodes = this.#createRemnantNode(lastNode.offset + lastNode.raw.length, undefined, lastNode.depth, lastNode.parentNode, invalidNode, whitespace);
1014
+ const remnantNodes = this.#createRemnantNode(lastNode.offset + lastNode.raw.length, undefined, lastNode.depth, lastNode.parentNode ?? null, invalidNode, whitespace);
1001
1015
  if (!remnantNodes) {
1002
1016
  return newNodeList;
1003
1017
  }
@@ -1020,12 +1034,12 @@ export class Parser {
1020
1034
  * @returns `true` if `node` is a descendant of `potentialAncestor`.
1021
1035
  */
1022
1036
  #isDescendantOf(node, potentialAncestor) {
1023
- let current = 'parentNode' in node ? node.parentNode : null;
1037
+ let current = 'parentNode' in node ? (node.parentNode ?? null) : null;
1024
1038
  while (current) {
1025
1039
  if (current === potentialAncestor) {
1026
1040
  return true;
1027
1041
  }
1028
- current = current.parentNode;
1042
+ current = current.parentNode ?? null;
1029
1043
  }
1030
1044
  return false;
1031
1045
  }
@@ -1034,7 +1048,7 @@ export class Parser {
1034
1048
  for (let node of nodeList) {
1035
1049
  if (node.type === 'endtag') {
1036
1050
  const endTagUUID = node.uuid;
1037
- const openTag = newNodeList.findLast((n) => n.type === 'starttag' && !n.isGhost ? n.pairNode?.uuid === endTagUUID : false);
1051
+ const openTag = newNodeList.findLast((n) => n.type === 'starttag' && !n.isGhost ? n.pairNodeUuid === endTagUUID : false);
1038
1052
  if (!openTag) {
1039
1053
  node = this.#convertIntoInvalidNode(node);
1040
1054
  }
@@ -1044,9 +1058,9 @@ export class Parser {
1044
1058
  return newNodeList;
1045
1059
  }
1046
1060
  #pairing(startTag, endTag) {
1047
- Object.assign(startTag, { pairNode: endTag });
1048
- Object.assign(endTag, { pairNode: startTag });
1049
- this.appendChild(startTag.parentNode, endTag);
1061
+ Object.assign(startTag, { pairNode: endTag, pairNodeUuid: endTag.uuid });
1062
+ Object.assign(endTag, { pairNode: startTag, pairNodeUuid: startTag.uuid });
1063
+ this.appendChild(startTag.parentNode ?? null, endTag);
1050
1064
  }
1051
1065
  #parseEndTag(token, namelessFragment) {
1052
1066
  const parsed = this.#parseTag(token, true, false, namelessFragment);
@@ -1219,6 +1233,7 @@ export class Parser {
1219
1233
  depth,
1220
1234
  nodeName: isFragment ? '#jsx-fragment' : tagName,
1221
1235
  parentNode: null,
1236
+ parentNodeUuid: null,
1222
1237
  };
1223
1238
  const tag = isOpenTag
1224
1239
  ? {
@@ -1232,6 +1247,7 @@ export class Parser {
1232
1247
  attributes: attrs,
1233
1248
  childNodes: [],
1234
1249
  pairNode: null,
1250
+ pairNodeUuid: null,
1235
1251
  tagOpenChar: '<',
1236
1252
  tagCloseChar: selfClosingSolidusChar + '>',
1237
1253
  blockBehavior: null,
@@ -1243,6 +1259,7 @@ export class Parser {
1243
1259
  ...commons,
1244
1260
  type: 'endtag',
1245
1261
  pairNode: {},
1262
+ pairNodeUuid: null,
1246
1263
  tagOpenChar: '</',
1247
1264
  tagCloseChar: '>',
1248
1265
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/parser-utils",
3
- "version": "5.0.0-rc.0",
3
+ "version": "5.0.0-rc.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,16 +31,16 @@
31
31
  "clean": "tsc --build --clean tsconfig.build.json"
32
32
  },
33
33
  "dependencies": {
34
- "@markuplint/ml-ast": "5.0.0-rc.0",
35
- "@markuplint/ml-spec": "5.0.0-rc.0",
36
- "@markuplint/shared": "5.0.0-rc.0",
37
- "@markuplint/types": "5.0.0-rc.0",
34
+ "@markuplint/ml-ast": "5.0.0-rc.1",
35
+ "@markuplint/ml-spec": "5.0.0-rc.1",
36
+ "@markuplint/shared": "5.0.0-rc.1",
37
+ "@markuplint/types": "5.0.0-rc.1",
38
38
  "debug": "4.4.3",
39
39
  "espree": "11.2.0",
40
- "type-fest": "5.4.4"
40
+ "type-fest": "5.5.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@typescript-eslint/typescript-estree": "8.57.0"
43
+ "@typescript-eslint/typescript-estree": "8.57.2"
44
44
  },
45
- "gitHead": "ebf4d7cfca0c259aead3b292c6b8a202db4cd802"
45
+ "gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
46
46
  }