@markuplint/parser-utils 4.8.10 → 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.
package/lib/debug.js CHANGED
@@ -1,15 +1,3 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
- if (kind === "m") throw new TypeError("Private method is not writable");
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
- };
12
- var _PerformanceTimer_logs, _PerformanceTimer_counter;
13
1
  import debug from 'debug';
14
2
  import { nodeListToDebugMaps } from './debugger.js';
15
3
  export const log = debug('ml-parser');
@@ -17,32 +5,29 @@ export function domLog(nodeList) {
17
5
  log('Parse result: %O', nodeListToDebugMaps(nodeList, true));
18
6
  }
19
7
  export class PerformanceTimer {
20
- constructor() {
21
- _PerformanceTimer_logs.set(this, []);
22
- _PerformanceTimer_counter.set(this, -1);
23
- }
8
+ #logs = [];
9
+ #counter = -1;
24
10
  push(name) {
25
- var _a;
26
11
  if (!log.enabled) {
27
12
  return '';
28
13
  }
29
- __classPrivateFieldSet(this, _PerformanceTimer_counter, (_a = __classPrivateFieldGet(this, _PerformanceTimer_counter, "f"), _a++, _a), "f");
14
+ this.#counter++;
30
15
  const now = performance.now();
31
- const last = __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").at(-1);
16
+ const last = this.#logs.at(-1);
32
17
  if (last && Number.isNaN(last[2])) {
33
18
  last[2] = now;
34
19
  }
35
- name = name || `#${__classPrivateFieldGet(this, _PerformanceTimer_counter, "f")}`;
36
- __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").push([name, now, Number.NaN]);
20
+ name = name || `#${this.#counter}`;
21
+ this.#logs.push([name, now, Number.NaN]);
37
22
  }
38
23
  log() {
39
24
  if (!log.enabled) {
40
25
  return;
41
26
  }
42
27
  this.push('end');
43
- __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").pop();
28
+ this.#logs.pop();
44
29
  const map = new Map();
45
- for (const content of __classPrivateFieldGet(this, _PerformanceTimer_logs, "f")) {
30
+ for (const content of this.#logs) {
46
31
  const diff = content[2] - content[1];
47
32
  const name = content[0];
48
33
  if (map.has(name)) {
@@ -59,4 +44,3 @@ export class PerformanceTimer {
59
44
  }
60
45
  }
61
46
  }
62
- _PerformanceTimer_logs = new WeakMap(), _PerformanceTimer_counter = new WeakMap();
package/lib/debugger.d.ts CHANGED
@@ -1,4 +1,29 @@
1
1
  import type { MLASTAttr, MLASTNode } from '@markuplint/ml-ast';
2
+ /**
3
+ * Converts a list of AST nodes into human-readable debug strings showing
4
+ * each node's position, type, and raw content. Useful for snapshot testing.
5
+ *
6
+ * @param nodeList - The flat list of AST nodes to convert
7
+ * @param withAttr - Whether to include detailed attribute debug info for start tags
8
+ * @returns An array of formatted debug strings, one per node (plus attribute lines when enabled)
9
+ */
2
10
  export declare function nodeListToDebugMaps(nodeList: readonly (MLASTNode | null)[], withAttr?: boolean): string[];
11
+ /**
12
+ * Converts a list of AST attributes into detailed debug strings showing
13
+ * each attribute's components (name, equal sign, value, quotes) with
14
+ * their positions and additional metadata like directives and dynamic values.
15
+ *
16
+ * @param attributes - The list of attributes to convert into debug representations
17
+ * @returns An array of string arrays, one inner array per attribute containing its debug lines
18
+ */
3
19
  export declare function attributesToDebugMaps(attributes: readonly MLASTAttr[]): string[][];
20
+ /**
21
+ * Produces a tree-style debug view of AST nodes, showing indentation
22
+ * based on depth, parent-child relationships, pair node links,
23
+ * and ghost/bogus markers. Useful for visualizing the parsed DOM structure.
24
+ *
25
+ * @param nodeTree - The flat list of AST nodes to visualize as a tree
26
+ * @param idFilter - Whether to replace UUIDs with short sequential hex IDs for readability
27
+ * @returns An array of formatted strings representing the tree view
28
+ */
4
29
  export declare function nodeTreeDebugView(nodeTree: readonly MLASTNode[], idFilter?: boolean): (string | undefined)[];
package/lib/debugger.js CHANGED
@@ -1,13 +1,29 @@
1
+ import { getEndCol, getEndLine } from './get-location.js';
2
+ /**
3
+ * Converts a list of AST nodes into human-readable debug strings showing
4
+ * each node's position, type, and raw content. Useful for snapshot testing.
5
+ *
6
+ * @param nodeList - The flat list of AST nodes to convert
7
+ * @param withAttr - Whether to include detailed attribute debug info for start tags
8
+ * @returns An array of formatted debug strings, one per node (plus attribute lines when enabled)
9
+ */
1
10
  export function nodeListToDebugMaps(nodeList, withAttr = false) {
2
11
  return nodeList.flatMap(n => {
3
- const r = [];
4
- r.push(tokenDebug(n));
12
+ const r = [tokenDebug(n)];
5
13
  if (withAttr && n && n.type === 'starttag') {
6
14
  r.push(...attributesToDebugMaps(n.attributes).flat());
7
15
  }
8
16
  return r;
9
17
  });
10
18
  }
19
+ /**
20
+ * Converts a list of AST attributes into detailed debug strings showing
21
+ * each attribute's components (name, equal sign, value, quotes) with
22
+ * their positions and additional metadata like directives and dynamic values.
23
+ *
24
+ * @param attributes - The list of attributes to convert into debug representations
25
+ * @returns An array of string arrays, one inner array per attribute containing its debug lines
26
+ */
11
27
  export function attributesToDebugMaps(attributes) {
12
28
  return attributes.map(n => {
13
29
  const r = [
@@ -36,6 +52,15 @@ export function attributesToDebugMaps(attributes) {
36
52
  return r;
37
53
  });
38
54
  }
55
+ /**
56
+ * Produces a tree-style debug view of AST nodes, showing indentation
57
+ * based on depth, parent-child relationships, pair node links,
58
+ * and ghost/bogus markers. Useful for visualizing the parsed DOM structure.
59
+ *
60
+ * @param nodeTree - The flat list of AST nodes to visualize as a tree
61
+ * @param idFilter - Whether to replace UUIDs with short sequential hex IDs for readability
62
+ * @returns An array of formatted strings representing the tree view
63
+ */
39
64
  export function nodeTreeDebugView(nodeTree, idFilter = false) {
40
65
  const filter = idFilter ? uuidFilter : (id) => id;
41
66
  return nodeTree
@@ -61,9 +86,14 @@ function tokenDebug(n, type = '') {
61
86
  if (!n) {
62
87
  return 'NULL';
63
88
  }
64
- return `[${n.startLine}:${n.startCol}]>[${n.endLine}:${n.endCol}](${n.startOffset},${n.endOffset})${
89
+ const endOffset = n.offset + n.raw.length;
90
+ const endLine = getEndLine(n.raw, n.line);
91
+ const endCol = getEndCol(n.raw, n.col);
92
+ return `[${n.line}:${n.col}]>[${endLine}:${endCol}](${n.offset},${endOffset})${
65
93
  // @ts-ignore
66
- n.potentialName ?? n.nodeName ?? n.name ?? n.type ?? type}${'isGhost' in n && n.isGhost ? '(👻)' : ''}${'isBogus' in n && n.isBogus ? '(👿)' : ''}${'conditionalType' in n && n.conditionalType ? ` (${n.conditionalType})` : ''}: ${visibleWhiteSpace(n.raw)}`;
94
+ n.potentialName ?? n.nodeName ?? n.name ?? n.type ?? type}${'isGhost' in n && n.isGhost ? '(👻)' : ''}${'isBogus' in n && n.isBogus ? '(👿)' : ''}${
95
+ // @ts-ignore
96
+ n.blockBehavior?.type ? ` (${n.blockBehavior.type})` : ''}: ${visibleWhiteSpace(n.raw)}`;
67
97
  }
68
98
  function visibleWhiteSpace(chars) {
69
99
  return chars.replaceAll('\n', '⏎').replaceAll('\t', '→').replaceAll(/\s/g, '␣');
package/lib/enums.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * States of the tag-level state machine used during tokenization.
3
+ * Transitions drive the parser through detecting the opening bracket,
4
+ * tag name, attributes, and closing bracket of an HTML/XML tag.
5
+ */
1
6
  export declare enum TagState {
2
7
  BeforeOpenTag = 0,
3
8
  FirstCharOfTagName = 1,
@@ -6,6 +11,11 @@ export declare enum TagState {
6
11
  AfterAttrs = 4,
7
12
  AfterOpenTag = 5
8
13
  }
14
+ /**
15
+ * States of the attribute-level state machine used during attribute tokenization.
16
+ * Transitions drive the parser through the attribute name, equals sign,
17
+ * and value portions of an HTML/XML attribute.
18
+ */
9
19
  export declare enum AttrState {
10
20
  BeforeName = 0,
11
21
  Name = 1,
package/lib/enums.js CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * States of the tag-level state machine used during tokenization.
3
+ * Transitions drive the parser through detecting the opening bracket,
4
+ * tag name, attributes, and closing bracket of an HTML/XML tag.
5
+ */
1
6
  export var TagState;
2
7
  (function (TagState) {
3
8
  TagState[TagState["BeforeOpenTag"] = 0] = "BeforeOpenTag";
@@ -7,6 +12,11 @@ export var TagState;
7
12
  TagState[TagState["AfterAttrs"] = 4] = "AfterAttrs";
8
13
  TagState[TagState["AfterOpenTag"] = 5] = "AfterOpenTag";
9
14
  })(TagState || (TagState = {}));
15
+ /**
16
+ * States of the attribute-level state machine used during attribute tokenization.
17
+ * Transitions drive the parser through the attribute name, equals sign,
18
+ * and value portions of an HTML/XML attribute.
19
+ */
10
20
  export var AttrState;
11
21
  (function (AttrState) {
12
22
  AttrState[AttrState["BeforeName"] = 0] = "BeforeName";
@@ -6,12 +6,43 @@ export declare function getLine(rawCodeFragment: string, startOffset: number): n
6
6
  * @deprecated Use {@link getPosition} instead. Will be removed in v5.0.0.
7
7
  */
8
8
  export declare function getCol(rawCodeFragment: string, startOffset: number): number;
9
+ /**
10
+ * Computes the line and column of a position within a code fragment.
11
+ *
12
+ * @param rawCodeFragment - The full raw source text
13
+ * @param startOffset - The zero-based byte offset to compute the position of
14
+ * @returns An object containing one-based `line` and `column`
15
+ */
9
16
  export declare function getPosition(rawCodeFragment: string, startOffset: number): {
10
17
  readonly line: number;
11
18
  readonly column: number;
12
19
  };
13
20
  export declare function getEndLine(rawCodeFragment: string, startLine: number): number;
14
21
  export declare function getEndCol(rawCodeFragment: string, startCol: number): number;
22
+ /**
23
+ * Computes the end position of a code fragment given its start position.
24
+ *
25
+ * @param rawCodeFragment - The raw source text of the fragment
26
+ * @param startOffset - The zero-based byte offset where the fragment starts
27
+ * @param startLine - The one-based line number where the fragment starts
28
+ * @param startCol - The one-based column number where the fragment starts
29
+ * @returns An object containing `endOffset`, `endLine`, and `endCol`
30
+ */
31
+ export declare function getEndPosition(rawCodeFragment: string, startOffset: number, startLine: number, startCol: number): {
32
+ endOffset: number;
33
+ endLine: number;
34
+ endCol: number;
35
+ };
36
+ /**
37
+ * Converts line/column ranges to byte offsets within a code string.
38
+ *
39
+ * @param rawCode - The full raw source text
40
+ * @param startLine - The one-based start line number
41
+ * @param startCol - The one-based start column number
42
+ * @param endLine - The one-based end line number
43
+ * @param endCol - The one-based end column number
44
+ * @returns An object containing zero-based `offset` and `endOffset`
45
+ */
15
46
  export declare function getOffsetsFromCode(rawCode: string, startLine: number, startCol: number, endLine: number, endCol: number): {
16
47
  offset: number;
17
48
  endOffset: number;
@@ -12,6 +12,13 @@ export function getCol(rawCodeFragment, startOffset) {
12
12
  const lines = rawCodeFragment.slice(0, startOffset).split(LINE_BREAK);
13
13
  return (lines.at(-1) ?? '').length + 1;
14
14
  }
15
+ /**
16
+ * Computes the line and column of a position within a code fragment.
17
+ *
18
+ * @param rawCodeFragment - The full raw source text
19
+ * @param startOffset - The zero-based byte offset to compute the position of
20
+ * @returns An object containing one-based `line` and `column`
21
+ */
15
22
  export function getPosition(rawCodeFragment, startOffset) {
16
23
  const lines = rawCodeFragment.slice(0, startOffset).split(LINE_BREAK);
17
24
  const line = lines.length;
@@ -27,6 +34,32 @@ export function getEndCol(rawCodeFragment, startCol) {
27
34
  const lastLine = lines.pop();
28
35
  return lineCount > 1 ? lastLine.length + 1 : startCol + rawCodeFragment.length;
29
36
  }
37
+ /**
38
+ * Computes the end position of a code fragment given its start position.
39
+ *
40
+ * @param rawCodeFragment - The raw source text of the fragment
41
+ * @param startOffset - The zero-based byte offset where the fragment starts
42
+ * @param startLine - The one-based line number where the fragment starts
43
+ * @param startCol - The one-based column number where the fragment starts
44
+ * @returns An object containing `endOffset`, `endLine`, and `endCol`
45
+ */
46
+ export function getEndPosition(rawCodeFragment, startOffset, startLine, startCol) {
47
+ return {
48
+ endOffset: startOffset + rawCodeFragment.length,
49
+ endLine: getEndLine(rawCodeFragment, startLine),
50
+ endCol: getEndCol(rawCodeFragment, startCol),
51
+ };
52
+ }
53
+ /**
54
+ * Converts line/column ranges to byte offsets within a code string.
55
+ *
56
+ * @param rawCode - The full raw source text
57
+ * @param startLine - The one-based start line number
58
+ * @param startCol - The one-based start column number
59
+ * @param endLine - The one-based end line number
60
+ * @param endCol - The one-based end column number
61
+ * @returns An object containing zero-based `offset` and `endOffset`
62
+ */
30
63
  export function getOffsetsFromCode(rawCode, startLine, startCol, endLine, endCol) {
31
64
  const lines = rawCode.split('\n');
32
65
  let offset = 0;
@@ -0,0 +1,11 @@
1
+ import type { MLASTParentNode, NamespaceURI } from '@markuplint/ml-ast';
2
+ /**
3
+ * Determines the namespace URI for a node based on its tag name and parent node context.
4
+ * Handles namespace transitions between HTML, SVG, and MathML
5
+ * (e.g., entering `<svg>` switches to SVG namespace, `<foreignObject>` switches back to HTML).
6
+ *
7
+ * @param currentNodeName - The tag name of the current node, or null for anonymous nodes
8
+ * @param parentNode - The parent node in the AST, or null for root-level nodes
9
+ * @returns The resolved namespace URI for the node
10
+ */
11
+ export declare function getNamespace(currentNodeName: string | null, parentNode: MLASTParentNode | null): NamespaceURI;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Determines the namespace URI for a node based on its tag name and parent node context.
3
+ * Handles namespace transitions between HTML, SVG, and MathML
4
+ * (e.g., entering `<svg>` switches to SVG namespace, `<foreignObject>` switches back to HTML).
5
+ *
6
+ * @param currentNodeName - The tag name of the current node, or null for anonymous nodes
7
+ * @param parentNode - The parent node in the AST, or null for root-level nodes
8
+ * @returns The resolved namespace URI for the node
9
+ */
10
+ export function getNamespace(currentNodeName, parentNode) {
11
+ const parentNS = getParentNamespace(parentNode);
12
+ if (parentNS === 'http://www.w3.org/1999/xhtml' && currentNodeName?.toLowerCase() === 'svg') {
13
+ return 'http://www.w3.org/2000/svg';
14
+ }
15
+ else if (parentNS === 'http://www.w3.org/2000/svg' && parentNode?.nodeName === 'foreignObject') {
16
+ return 'http://www.w3.org/1999/xhtml';
17
+ }
18
+ else if (parentNS === 'http://www.w3.org/1999/xhtml' && currentNodeName?.toLowerCase() === 'math') {
19
+ return 'http://www.w3.org/1998/Math/MathML';
20
+ }
21
+ return parentNS;
22
+ }
23
+ function getParentNamespace(parentNode) {
24
+ if (!parentNode) {
25
+ return 'http://www.w3.org/1999/xhtml';
26
+ }
27
+ if ('namespace' in parentNode && parentNode.namespace) {
28
+ const ns = parentNode.namespace.toLowerCase().trim();
29
+ return ns === 'http://www.w3.org/1999/xhtml'
30
+ ? 'http://www.w3.org/1999/xhtml'
31
+ : ns === 'http://www.w3.org/2000/svg'
32
+ ? 'http://www.w3.org/2000/svg'
33
+ : ns === 'http://www.w3.org/1998/Math/MathML'
34
+ ? 'http://www.w3.org/1998/Math/MathML'
35
+ : 'http://www.w3.org/1999/xhtml';
36
+ }
37
+ return getParentNamespace(parentNode.parentNode);
38
+ }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Searches the IDL-to-content-attribute mapping to find the corresponding
3
+ * IDL property name and content attribute name for a given attribute name.
4
+ * Handles camelCase IDL names, lowercase content attribute names, hyphenated names,
5
+ * and event handler attributes (e.g., `onclick`).
6
+ *
7
+ * @param name - The attribute name to look up (can be IDL, content, or hyphenated form)
8
+ * @returns An object with the matching `idlPropName` and `contentAttrName`, both undefined if no match is found
9
+ */
1
10
  export declare function searchIDLAttribute(name: string): {
2
11
  idlPropName: string | undefined;
3
12
  contentAttrName: string | undefined;
@@ -416,6 +416,15 @@ const idlContentMap = {
416
416
  credentialless: 'credentialless',
417
417
  };
418
418
  const list = Object.entries(idlContentMap);
419
+ /**
420
+ * Searches the IDL-to-content-attribute mapping to find the corresponding
421
+ * IDL property name and content attribute name for a given attribute name.
422
+ * Handles camelCase IDL names, lowercase content attribute names, hyphenated names,
423
+ * and event handler attributes (e.g., `onclick`).
424
+ *
425
+ * @param name - The attribute name to look up (can be IDL, content, or hyphenated form)
426
+ * @returns An object with the matching `idlPropName` and `contentAttrName`, both undefined if no match is found
427
+ */
419
428
  export function searchIDLAttribute(name) {
420
429
  const camelizedName = camelize(name);
421
430
  const [idlPropName, contentAttrName] = /^on[a-z]/.test(name)
@@ -15,11 +15,10 @@ export function ignoreBlock(source, tags, maskChar = MASK_CHAR) {
15
15
  replaced = text.replaced;
16
16
  stack.push(...text.stack.map(res => ({ ...res, type: tag.type })));
17
17
  }
18
- stack.sort((a, b) => a.index - b.index);
19
18
  return {
20
19
  source,
21
20
  replaced,
22
- stack,
21
+ stack: stack.toSorted((a, b) => a.index - b.index),
23
22
  maskChar,
24
23
  };
25
24
  }
@@ -63,33 +62,34 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
63
62
  for (const tag of stack) {
64
63
  const raw = `${tag.startTag}${tag.taggedCode}${tag.endTag ?? ''}`;
65
64
  const tagIndexEnd = tag.index + raw.length;
66
- const node = newNodeList.find(node => node.startOffset <= tag.index && node.endOffset >= tagIndexEnd);
65
+ const nodeEndOffset = (n) => n.offset + n.raw.length;
66
+ const node = newNodeList.find(node => node.offset <= tag.index && nodeEndOffset(node) >= tagIndexEnd);
67
67
  if (!node) {
68
68
  continue;
69
69
  }
70
70
  const replacementChildNodes = [];
71
- if (node.startOffset === tag.index && node.endOffset === tagIndexEnd) {
72
- const token = parser.createToken(raw, node.startOffset, node.startLine, node.startCol);
71
+ if (node.offset === tag.index && nodeEndOffset(node) === tagIndexEnd) {
72
+ const token = parser.createToken(raw, node.offset, node.line, node.col);
73
73
  const psNode = {
74
74
  ...token,
75
75
  type: 'psblock',
76
- conditionalType: null,
77
76
  depth: node.depth,
78
77
  nodeName: `#ps:${tag.type}`,
79
78
  parentNode: node.parentNode,
80
79
  childNodes: [],
80
+ blockBehavior: null,
81
81
  isBogus: false,
82
82
  isFragment: false, // TODO: Case by case
83
83
  };
84
84
  replacementChildNodes.push(psNode);
85
85
  }
86
86
  else if (node.type === 'text') {
87
- const offset = tag.index - node.startOffset;
87
+ const offset = tag.index - node.offset;
88
88
  const above = node.raw.slice(0, offset);
89
89
  const below = node.raw.slice(offset + raw.length);
90
90
  if (above) {
91
91
  const { line, column } = getPosition(node.raw, 0);
92
- const token = parser.createToken(above, node.startOffset, node.startLine + line - 1, node.startCol + column - 1);
92
+ const token = parser.createToken(above, node.offset, node.line + line - 1, node.col + column - 1);
93
93
  const aboveNode = {
94
94
  ...token,
95
95
  nodeName: '#text',
@@ -100,22 +100,22 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
100
100
  replacementChildNodes.push(aboveNode);
101
101
  }
102
102
  const { line, column } = getPosition(raw, offset);
103
- const token = parser.createToken(raw, node.startOffset + offset, node.startLine + line - 1, node.startCol + column - 1);
103
+ const token = parser.createToken(raw, node.offset + offset, node.line + line - 1, node.col + column - 1);
104
104
  const psNode = {
105
105
  ...token,
106
106
  type: 'psblock',
107
- conditionalType: null,
108
107
  depth: node.depth,
109
108
  nodeName: `#ps:${tag.type}`,
110
109
  parentNode: node.parentNode,
111
110
  childNodes: [],
111
+ blockBehavior: null,
112
112
  isBogus: false,
113
113
  isFragment: false, // TODO: Case by case
114
114
  };
115
115
  replacementChildNodes.push(psNode);
116
116
  if (below) {
117
117
  const { line, column } = getPosition(node.raw, offset + raw.length);
118
- const token = parser.createToken(below, node.startOffset + offset + raw.length, node.startLine + line - 1, node.startCol + column - 1);
118
+ const token = parser.createToken(below, node.offset + offset + raw.length, node.line + line - 1, node.col + column - 1);
119
119
  const aboveNode = {
120
120
  ...token,
121
121
  nodeName: '#text',
@@ -144,8 +144,9 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
144
144
  for (const tag of stack) {
145
145
  const raw = tag.startTag + tag.taggedCode + tag.endTag;
146
146
  const length = raw.length;
147
- if (attr.value.startOffset <= tag.index && tag.index + length <= attr.value.endOffset) {
148
- const offset = tag.index - attr.value.startOffset;
147
+ if (attr.value.offset <= tag.index &&
148
+ tag.index + length <= attr.value.offset + attr.value.raw.length) {
149
+ const offset = tag.index - attr.value.offset;
149
150
  const above = attr.value.raw.slice(0, offset);
150
151
  const below = attr.value.raw.slice(offset + length);
151
152
  parser.updateRaw(attr.value, above + raw + below);
@@ -162,7 +163,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
162
163
  }
163
164
  // Update node raw
164
165
  const length = attr.raw.length;
165
- const offset = attr.startOffset - node.startOffset;
166
+ const offset = attr.offset - node.offset;
166
167
  const above = node.raw.slice(0, offset);
167
168
  const below = node.raw.slice(offset + length);
168
169
  parser.updateRaw(node, above + attr.raw + below);
package/lib/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from './debugger.js';
2
2
  export * from './enums.js';
3
+ export * from './get-namespace.js';
3
4
  export * from './idl-attributes.js';
4
5
  export * from './parser-error.js';
5
6
  export * from './parser.js';
6
7
  export * from './script-parser.js';
7
- export * from './types.js';
8
+ export type * from './types.js';
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from './debugger.js';
2
2
  export * from './enums.js';
3
+ export * from './get-namespace.js';
3
4
  export * from './idl-attributes.js';
4
5
  export * from './parser-error.js';
5
6
  export * from './parser.js';
6
7
  export * from './script-parser.js';
7
- export * from './types.js';
@@ -1,9 +1,17 @@
1
+ /**
2
+ * Positional and contextual information associated with a parser error,
3
+ * used to construct meaningful error messages with source locations.
4
+ */
1
5
  export type ParserErrorInfo = {
2
6
  readonly line?: number;
3
7
  readonly col?: number;
4
8
  readonly raw?: string;
5
9
  readonly stack?: string;
6
10
  };
11
+ /**
12
+ * An error that occurs during parsing, carrying the source line, column,
13
+ * and raw text where the error was encountered.
14
+ */
7
15
  export declare class ParserError extends Error {
8
16
  readonly col: number;
9
17
  readonly line: number;
@@ -11,6 +19,10 @@ export declare class ParserError extends Error {
11
19
  readonly raw: string;
12
20
  constructor(message: string, info: ParserErrorInfo);
13
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
+ */
14
26
  export declare class TargetParserError extends ParserError {
15
27
  name: string;
16
28
  readonly nodeName: string | null;
@@ -18,6 +30,10 @@ export declare class TargetParserError extends ParserError {
18
30
  readonly nodeName?: string | null;
19
31
  });
20
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
+ */
21
37
  export declare class ConfigParserError extends ParserError {
22
38
  readonly filePath: string;
23
39
  name: string;
@@ -1,30 +1,47 @@
1
+ /**
2
+ * An error that occurs during parsing, carrying the source line, column,
3
+ * and raw text where the error was encountered.
4
+ */
1
5
  export class ParserError extends Error {
6
+ col;
7
+ line;
8
+ name = 'ParserError';
9
+ raw;
2
10
  constructor(message, info) {
3
11
  super(message);
4
- this.name = 'ParserError';
5
12
  this.line = info.line ?? 1;
6
13
  this.col = info.col ?? 0;
7
14
  this.raw = info.raw ?? '';
8
15
  this.stack = info.stack ?? this.stack;
9
16
  }
10
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
+ */
11
22
  export class TargetParserError extends ParserError {
23
+ name = 'TargetParserError';
24
+ nodeName;
12
25
  constructor(message, info) {
13
26
  const errMsg = info.nodeName
14
27
  ? `The ${info.nodeName} is invalid element (${info.line}:${info.col}): ${message}`
15
28
  : message;
16
29
  super(errMsg, info);
17
- this.name = 'TargetParserError';
18
30
  this.nodeName = info.nodeName ?? null;
19
31
  }
20
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
+ */
21
37
  export class ConfigParserError extends ParserError {
38
+ filePath;
39
+ name = 'ConfigParserError';
22
40
  constructor(message, info) {
23
41
  const pos = info.line != null && info.line != null ? `(${info.line}:${info.col})` : '';
24
42
  const file = ` in ${info.filePath}${pos}`;
25
43
  const errMsg = `${message}${file}`;
26
44
  super(errMsg, info);
27
- this.name = 'ConfigParserError';
28
45
  this.filePath = info.filePath;
29
46
  }
30
47
  }