svg-eslint-parser 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
- import { t as __exportAll } from "./chunk-pbuEa-1d.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { isNumber } from "@ntnyq/utils";
2
3
  import { unionWith } from "eslint-visitor-keys";
3
4
  //#endregion
4
5
  //#region src/meta.ts
6
+ /**
7
+ * Package metadata exposed to parser consumers.
8
+ */
5
9
  const meta = {
6
10
  name: "svg-eslint-parser",
7
- version: "0.1.0"
11
+ version: "0.3.0"
8
12
  };
9
13
  //#endregion
10
14
  //#region src/parser/error.ts
@@ -15,12 +19,12 @@ var ParseError = class extends SyntaxError {
15
19
  index;
16
20
  lineNumber;
17
21
  column;
18
- constructor(message, offset, line, column) {
19
- super(message);
22
+ constructor(message, options, line, column) {
23
+ super(message, options);
20
24
  this.name = "ParseError";
21
- this.index = offset;
22
- this.lineNumber = line;
23
- this.column = column;
25
+ this.index = isNumber(options) ? options : options.offset;
26
+ this.lineNumber = isNumber(options) ? line ?? 0 : options.line;
27
+ this.column = isNumber(options) ? column ?? 0 : options.column;
24
28
  }
25
29
  };
26
30
  //#endregion
@@ -43,26 +47,29 @@ const XML_DECLARATION_START = "<?xml";
43
47
  const XML_DECLARATION_END = "?>";
44
48
  /**
45
49
  * regexp for open tag start
46
- * @regex101 https://regex101.com/?regex=%5E%3C%5Cw&flavor=javascript
50
+ * @regex101 https://regex101.com/?regex=%5E%3C%5Cw&flags=u&flavor=javascript
47
51
  */
48
52
  const RE_OPEN_TAG_START = /^<\w/u;
49
53
  /**
50
54
  * regexp for open tag name
51
- * @regex101 https://regex101.com/?regex=%5E%3C%28%5CS%2B%29&flavor=javascript
55
+ * @regex101 https://regex101.com/?regex=%5E%3C%28%3F%3CtagName%3E%5CS%2B%29&flags=u&flavor=javascript
52
56
  */
53
- const RE_OPEN_TAG_NAME = /^<(\S+)/u;
57
+ const RE_OPEN_TAG_NAME = /^<(?<tagName>\S+)/u;
54
58
  /**
55
59
  * regexp for close tag name
56
- * @regex101 https://regex101.com/?regex=%5E%3C%5C%2F%28%28%3F%3A.%7C%5Cr%3F%5Cn%29*%29%3E%24&flavor=javascript
60
+ * @regex101 https://regex101.com/?regex=%5E%3C%5C%2F%28%3F%3CtagName%3E%28%3F%3A.%7C%5Cr%3F%5Cn%29*%29%3E%24&flags=u&flavor=javascript
57
61
  */
58
- const RE_CLOSE_TAG_NAME = /^<\/((?:.|\r?\n)*)>$/u;
62
+ const RE_CLOSE_TAG_NAME = /^<\/(?<tagName>(?:.|\r?\n)*)>$/u;
59
63
  /**
60
64
  * regexp for incomplete closing tag
61
- * @regex101 https://regex101.com/?regex=%3C%5C%2F%5B%5E%3E%5D%2B%24&flavor=javascript
65
+ * @regex101 https://regex101.com/?regex=%3C%5C%2F%5B%5E%3E%5D%2B%24&flags=u&flavor=javascript
62
66
  */
63
67
  const RE_INCOMPLETE_CLOSING_TAG = /<\/[^>]+$/u;
64
68
  //#endregion
65
69
  //#region src/constants/nodeTypes.ts
70
+ /**
71
+ * AST node type names emitted by the parser.
72
+ */
66
73
  let NodeTypes = /* @__PURE__ */ function(NodeTypes) {
67
74
  NodeTypes["Attribute"] = "Attribute";
68
75
  NodeTypes["AttributeKey"] = "AttributeKey";
@@ -75,8 +82,6 @@ let NodeTypes = /* @__PURE__ */ function(NodeTypes) {
75
82
  NodeTypes["Element"] = "Element";
76
83
  NodeTypes["Error"] = "Error";
77
84
  NodeTypes["Program"] = "Program";
78
- /** @deprecated Use Element instead. */
79
- NodeTypes["Tag"] = "Element";
80
85
  NodeTypes["Text"] = "Text";
81
86
  NodeTypes["XMLDeclaration"] = "XMLDeclaration";
82
87
  NodeTypes["XMLDeclarationAttribute"] = "XMLDeclarationAttribute";
@@ -86,6 +91,9 @@ let NodeTypes = /* @__PURE__ */ function(NodeTypes) {
86
91
  }({});
87
92
  //#endregion
88
93
  //#region src/constants/tokenTypes.ts
94
+ /**
95
+ * Token type names emitted by tokenizer contexts.
96
+ */
89
97
  let TokenTypes = /* @__PURE__ */ function(TokenTypes) {
90
98
  /**
91
99
  * @pg Content tokens
@@ -133,6 +141,9 @@ let TokenTypes = /* @__PURE__ */ function(TokenTypes) {
133
141
  }({});
134
142
  //#endregion
135
143
  //#region src/constants/specialChar.ts
144
+ /**
145
+ * Character constants used by tokenizer handlers.
146
+ */
136
147
  const SPECIAL_CHAR = {
137
148
  closingCorner: `>`,
138
149
  colon: `:`,
@@ -156,7 +167,7 @@ const SPECIAL_CHAR = {
156
167
  * @copyright {@link https://github.com/davidohlin/svg-elements}
157
168
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element}
158
169
  */
159
- const SVG_ELEMENTS = new Set([
170
+ const SVG_ELEMENTS = /* @__PURE__ */ new Set([
160
171
  "a",
161
172
  "animate",
162
173
  "animateMotion",
@@ -227,7 +238,7 @@ const SVG_ELEMENTS = new Set([
227
238
  *
228
239
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element#obsolete_and_deprecated_elements}
229
240
  */
230
- const DEPRECATED_SVG_ELEMENTS = new Set([
241
+ const DEPRECATED_SVG_ELEMENTS = /* @__PURE__ */ new Set([
231
242
  "altGlyph",
232
243
  "altGlyphDef",
233
244
  "altGlyphItem",
@@ -249,7 +260,7 @@ const DEPRECATED_SVG_ELEMENTS = new Set([
249
260
  /**
250
261
  * self closing svg elements
251
262
  */
252
- const SELF_CLOSING_ELEMENTS = new Set([
263
+ const SELF_CLOSING_ELEMENTS = /* @__PURE__ */ new Set([
253
264
  "animate",
254
265
  "animateMotion",
255
266
  "animateTransform",
@@ -285,6 +296,9 @@ const SELF_CLOSING_ELEMENTS = new Set([
285
296
  ]);
286
297
  //#endregion
287
298
  //#region src/constants/tokenizerContextTypes.ts
299
+ /**
300
+ * Tokenizer state machine contexts.
301
+ */
288
302
  let TokenizerContextTypes = /* @__PURE__ */ function(TokenizerContextTypes) {
289
303
  TokenizerContextTypes["AttributeKey"] = "AttributeKey";
290
304
  TokenizerContextTypes["Attributes"] = "Attributes";
@@ -313,6 +327,9 @@ let TokenizerContextTypes = /* @__PURE__ */ function(TokenizerContextTypes) {
313
327
  }({});
314
328
  //#endregion
315
329
  //#region src/constants/constructTreeContextTypes.ts
330
+ /**
331
+ * AST construction state machine contexts.
332
+ */
316
333
  let ConstructTreeContextTypes = /* @__PURE__ */ function(ConstructTreeContextTypes) {
317
334
  ConstructTreeContextTypes["Attribute"] = "Attribute";
318
335
  ConstructTreeContextTypes["Attributes"] = "Attributes";
@@ -331,6 +348,78 @@ let ConstructTreeContextTypes = /* @__PURE__ */ function(ConstructTreeContextTyp
331
348
  return ConstructTreeContextTypes;
332
349
  }({});
333
350
  //#endregion
351
+ //#region src/parser/errorHandler.ts
352
+ /**
353
+ * Collects recoverable parse errors and warnings during parsing.
354
+ */
355
+ var ErrorHandler = class {
356
+ errors = [];
357
+ warnings = [];
358
+ addError(error) {
359
+ this.errors.push(error);
360
+ }
361
+ addWarning(warning) {
362
+ this.warnings.push(warning);
363
+ }
364
+ hasErrors() {
365
+ return this.errors.length > 0;
366
+ }
367
+ hasWarnings() {
368
+ return this.warnings.length > 0;
369
+ }
370
+ clear() {
371
+ this.errors = [];
372
+ this.warnings = [];
373
+ }
374
+ getErrors() {
375
+ return [...this.errors];
376
+ }
377
+ getWarnings() {
378
+ return [...this.warnings];
379
+ }
380
+ /**
381
+ * Merge errors from another ErrorHandler
382
+ */
383
+ mergeErrors(other) {
384
+ this.errors.push(...other.errors);
385
+ this.warnings.push(...other.warnings);
386
+ }
387
+ /**
388
+ * Get all issues (errors + warnings) sorted by position
389
+ */
390
+ getAllIssues() {
391
+ return [...this.errors, ...this.warnings].sort((a, b) => a.range[0] - b.range[0]);
392
+ }
393
+ /**
394
+ * Format errors for display
395
+ */
396
+ format() {
397
+ const issues = this.getAllIssues();
398
+ if (issues.length === 0) return "No errors or warnings";
399
+ return issues.map((issue) => {
400
+ const { loc, message, type } = issue;
401
+ return `${loc.start.line}:${loc.start.column} [${type}] ${message}`;
402
+ }).join("\n");
403
+ }
404
+ };
405
+ //#endregion
406
+ //#region src/types/errors.ts
407
+ /**
408
+ * Categories of recoverable parse diagnostics.
409
+ */
410
+ let ParseErrorType = /* @__PURE__ */ function(ParseErrorType) {
411
+ ParseErrorType["InvalidAttribute"] = "InvalidAttribute";
412
+ ParseErrorType["InvalidCharacter"] = "InvalidCharacter";
413
+ ParseErrorType["InvalidDoctypeAttribute"] = "InvalidDoctypeAttribute";
414
+ ParseErrorType["InvalidXMLDeclaration"] = "InvalidXMLDeclaration";
415
+ ParseErrorType["MalformedComment"] = "MalformedComment";
416
+ ParseErrorType["MismatchedTag"] = "MismatchedTag";
417
+ ParseErrorType["UnclosedTag"] = "UnclosedTag";
418
+ ParseErrorType["UnexpectedToken"] = "UnexpectedToken";
419
+ ParseErrorType["UnmatchedQuote"] = "UnmatchedQuote";
420
+ return ParseErrorType;
421
+ }({});
422
+ //#endregion
334
423
  //#region src/utils/cloneNode.ts
335
424
  /**
336
425
  * Deep clone a node, removing parent references
@@ -438,8 +527,14 @@ function clearParent(ast) {
438
527
  * @param code - Character code to check
439
528
  * @returns True if code is a newline character (LF, CR, LS, PS)
440
529
  */
530
+ const LINE_BREAK_CODES = /* @__PURE__ */ new Set([
531
+ 10,
532
+ 13,
533
+ 8232,
534
+ 8233
535
+ ]);
441
536
  function isNewLine(code) {
442
- return code === 10 || code === 13 || code === 8232 || code === 8233;
537
+ return LINE_BREAK_CODES.has(code);
443
538
  }
444
539
  /**
445
540
  * Find the next line break position in a string
@@ -474,6 +569,62 @@ function getLineInfo(input, offset) {
474
569
  }
475
570
  }
476
571
  //#endregion
572
+ //#region src/visitorKeys.ts
573
+ const keys = {
574
+ Program: ["document"],
575
+ Document: ["children"],
576
+ XMLDeclaration: ["attributes"],
577
+ XMLDeclarationAttribute: ["key", "value"],
578
+ XMLDeclarationAttributeKey: [],
579
+ XMLDeclarationAttributeValue: [],
580
+ Doctype: ["attributes"],
581
+ DoctypeAttribute: ["value"],
582
+ DoctypeAttributeValue: [],
583
+ Attribute: ["key", "value"],
584
+ AttributeKey: [],
585
+ AttributeValue: [],
586
+ Element: ["attributes", "children"],
587
+ Comment: [],
588
+ Text: [],
589
+ Error: []
590
+ };
591
+ let vistorKeysCache = null;
592
+ /**
593
+ * Get ESLint visitor keys extended with parser-specific AST nodes.
594
+ * @returns Cached visitor key map
595
+ */
596
+ function getVisitorKeys() {
597
+ if (!vistorKeysCache) {
598
+ const merged = unionWith(keys);
599
+ vistorKeysCache = {
600
+ ...merged,
601
+ Tag: merged.Element
602
+ };
603
+ }
604
+ return vistorKeysCache;
605
+ }
606
+ /**
607
+ * Shared visitor key map used by parser traversal and ESLint integration.
608
+ */
609
+ const visitorKeys = getVisitorKeys();
610
+ //#endregion
611
+ //#region src/utils/getChildNodes.ts
612
+ /**
613
+ * Get child AST nodes according to the parser visitor keys.
614
+ * @param node - Parent AST node
615
+ * @returns Child nodes in visitor-key order
616
+ */
617
+ function getChildNodes(node) {
618
+ const keys = visitorKeys[node.type] ?? [];
619
+ const children = [];
620
+ for (const key of keys) {
621
+ const value = node[key];
622
+ if (Array.isArray(value)) children.push(...value);
623
+ else if (value) children.push(value);
624
+ }
625
+ return children;
626
+ }
627
+ //#endregion
477
628
  //#region src/utils/nodeHelpers.ts
478
629
  /**
479
630
  * Filter nodes by a predicate function
@@ -485,8 +636,7 @@ function filterNodes(node, predicate) {
485
636
  const results = [];
486
637
  function traverse(currentNode) {
487
638
  if (predicate(currentNode)) results.push(currentNode);
488
- if ("children" in currentNode && Array.isArray(currentNode.children)) for (const child of currentNode.children) traverse(child);
489
- if ("attributes" in currentNode && Array.isArray(currentNode.attributes)) for (const attr of currentNode.attributes) traverse(attr);
639
+ for (const child of getChildNodes(currentNode)) traverse(child);
490
640
  }
491
641
  traverse(node);
492
642
  return results;
@@ -499,8 +649,11 @@ function filterNodes(node, predicate) {
499
649
  */
500
650
  function mapNodes(node, mapper) {
501
651
  const mapped = mapper(node);
502
- if ("children" in mapped && Array.isArray(mapped.children)) mapped.children = mapped.children.map((child) => mapNodes(child, mapper));
503
- if ("attributes" in mapped && Array.isArray(mapped.attributes)) mapped.attributes = mapped.attributes.map((attr) => mapNodes(attr, mapper));
652
+ for (const key of Object.keys(mapped)) {
653
+ const value = mapped[key];
654
+ if (Array.isArray(value)) mapped[key] = value.map((item) => item && typeof item === "object" && "type" in item ? mapNodes(item, mapper) : item);
655
+ else if (value && typeof value === "object" && "type" in value) mapped[key] = mapNodes(value, mapper);
656
+ }
504
657
  return mapped;
505
658
  }
506
659
  /**
@@ -538,8 +691,7 @@ function getNodeDepth(node) {
538
691
  */
539
692
  function countNodes(node) {
540
693
  let count = 1;
541
- if ("children" in node && Array.isArray(node.children)) for (const child of node.children) count += countNodes(child);
542
- if ("attributes" in node && Array.isArray(node.attributes)) for (const attr of node.attributes) count += countNodes(attr);
694
+ for (const child of getChildNodes(node)) count += countNodes(child);
543
695
  return count;
544
696
  }
545
697
  //#endregion
@@ -555,10 +707,7 @@ function traverseAST(node, visitor, parent = null) {
555
707
  if (visitor.enter) {
556
708
  if (visitor.enter(node, parent) === false) shouldTraverseChildren = false;
557
709
  }
558
- if (shouldTraverseChildren) {
559
- if ("children" in node && Array.isArray(node.children)) for (const child of node.children) traverseAST(child, visitor, node);
560
- if ("attributes" in node && Array.isArray(node.attributes)) for (const attr of node.attributes) traverseAST(attr, visitor, node);
561
- }
710
+ if (shouldTraverseChildren) for (const child of getChildNodes(node)) traverseAST(child, visitor, node);
562
711
  if (visitor.leave) visitor.leave(node, parent);
563
712
  }
564
713
  /**
@@ -571,13 +720,19 @@ function walkAST(node, callback) {
571
720
  }
572
721
  //#endregion
573
722
  //#region src/utils/isWhitespace.ts
723
+ const WHITESPACE_CHARS = /* @__PURE__ */ new Set([
724
+ SPECIAL_CHAR.space,
725
+ SPECIAL_CHAR.newline,
726
+ SPECIAL_CHAR.return,
727
+ SPECIAL_CHAR.tab
728
+ ]);
574
729
  /**
575
730
  * Check if a character is whitespace (space, newline, return, or tab)
576
731
  * @param char - Character to check
577
732
  * @returns True if character is whitespace
578
733
  */
579
734
  function isWhitespace(char) {
580
- return char === SPECIAL_CHAR.space || char === SPECIAL_CHAR.newline || char === SPECIAL_CHAR.return || char === SPECIAL_CHAR.tab;
735
+ return WHITESPACE_CHARS.has(char);
581
736
  }
582
737
  //#endregion
583
738
  //#region src/utils/validateNode.ts
@@ -616,7 +771,6 @@ function validateNode(node) {
616
771
  if (!Array.isArray(node.range) || node.range.length !== 2 || typeof node.range[0] !== "number" || typeof node.range[1] !== "number") return false;
617
772
  if (!node.loc || !node.loc.start || !node.loc.end || typeof node.loc.start.line !== "number" || typeof node.loc.start.column !== "number" || typeof node.loc.end.line !== "number" || typeof node.loc.end.column !== "number") return false;
618
773
  switch (node.type) {
619
- case "Element":
620
774
  case "Element": return validateElementNode(node);
621
775
  case "Attribute": return validateAttributeNode(node);
622
776
  case "Text": return validateTextNode(node);
@@ -694,8 +848,7 @@ function findNodeByType(node, type) {
694
848
  const results = [];
695
849
  function traverse(currentNode) {
696
850
  if (currentNode.type === type) results.push(currentNode);
697
- if ("children" in currentNode && Array.isArray(currentNode.children)) for (const child of currentNode.children) traverse(child);
698
- if ("attributes" in currentNode && Array.isArray(currentNode.attributes)) for (const attr of currentNode.attributes) traverse(attr);
851
+ for (const child of getChildNodes(currentNode)) traverse(child);
699
852
  }
700
853
  traverse(node);
701
854
  return results;
@@ -709,14 +862,10 @@ function findNodeByType(node, type) {
709
862
  function findFirstNodeByType(node, type) {
710
863
  function traverse(currentNode) {
711
864
  if (currentNode.type === type) return currentNode;
712
- if ("children" in currentNode && Array.isArray(currentNode.children)) for (const child of currentNode.children) {
865
+ for (const child of getChildNodes(currentNode)) {
713
866
  const result = traverse(child);
714
867
  if (result) return result;
715
868
  }
716
- if ("attributes" in currentNode && Array.isArray(currentNode.attributes)) for (const attr of currentNode.attributes) {
717
- const result = traverse(attr);
718
- if (result) return result;
719
- }
720
869
  }
721
870
  return traverse(node);
722
871
  }
@@ -737,7 +886,9 @@ function getLastAttribute(state) {
737
886
  function parseOpenTagName(openTagStartTokenContent) {
738
887
  const match = openTagStartTokenContent.match(RE_OPEN_TAG_NAME);
739
888
  if (match === null) throw new Error(`Unable to parse open tag name.\n${openTagStartTokenContent} does not match pattern of opening tag.`);
740
- return match[1].toLowerCase();
889
+ const tagName = match.groups?.tagName;
890
+ if (tagName === void 0) throw new Error(`Unable to parse open tag name. Named capture group "tagName" is missing.`);
891
+ return tagName.toLowerCase();
741
892
  }
742
893
  //#endregion
743
894
  //#region src/utils/parseCloseTagName.ts
@@ -750,7 +901,9 @@ function parseOpenTagName(openTagStartTokenContent) {
750
901
  function parseCloseTagName(closeTagTokenContent) {
751
902
  const match = closeTagTokenContent.match(RE_CLOSE_TAG_NAME);
752
903
  if (match === null) throw new Error(`Unable to parse close tag name.\n${closeTagTokenContent} does not match pattern of closing tag.`);
753
- return match[1].trim().toLowerCase();
904
+ const tagName = match.groups?.tagName;
905
+ if (tagName === void 0) throw new Error(`Unable to parse close tag name. Named capture group "tagName" is missing.`);
906
+ return tagName.trim().toLowerCase();
754
907
  }
755
908
  //#endregion
756
909
  //#region src/utils/calculateTokenLocation.ts
@@ -776,11 +929,7 @@ function calculateTokenLocation(source, range) {
776
929
  * @returns Array containing [startPosition, endPosition + 1]
777
930
  */
778
931
  function calculateTokenCharactersRange(state, options) {
779
- const startPosition = state.sourceCode.index() - (state.accumulatedContent.length() - 1) - state.decisionBuffer.length();
780
- let endPosition = 0;
781
- if (options.keepBuffer) endPosition = state.sourceCode.index();
782
- else endPosition = state.sourceCode.index() - state.decisionBuffer.length();
783
- return [startPosition, endPosition + 1];
932
+ return [state.sourceCode.index() - (state.accumulatedContent.length() - 1) - state.decisionBuffer.length(), (options.keepBuffer ? state.sourceCode.index() : state.sourceCode.index() - state.decisionBuffer.length()) + 1];
784
933
  }
785
934
  //#endregion
786
935
  //#region src/utils/calculateTokenPosition.ts
@@ -829,7 +978,7 @@ const dispatch$13 = createTokenDispatcher([
829
978
  }
830
979
  },
831
980
  {
832
- tokenType: new Set(["AttributeKey", "AttributeAssignment"]),
981
+ tokenType: /* @__PURE__ */ new Set(["AttributeKey", "AttributeAssignment"]),
833
982
  handler(_, state) {
834
983
  state.currentContext = {
835
984
  parentRef: state.currentContext,
@@ -844,7 +993,7 @@ const dispatch$13 = createTokenDispatcher([
844
993
  const tagName = state.currentNode.name;
845
994
  state.currentNode.openEnd = createNodeFrom(token);
846
995
  updateNodeEnd(state.currentNode, token);
847
- if (tagName && SELF_CLOSING_ELEMENTS.has(tagName) && state.currentNode.openEnd.value === "/>") {
996
+ if (tagName && state.currentNode.openEnd.value === "/>") {
848
997
  state.currentNode.selfClosing = true;
849
998
  state.currentNode = state.currentNode.parentRef;
850
999
  state.currentContext = state.currentContext.parentRef;
@@ -875,6 +1024,9 @@ const dispatch$13 = createTokenDispatcher([
875
1024
  state.caretPosition++;
876
1025
  return state;
877
1026
  });
1027
+ /**
1028
+ * Construct an element node after its opening tag has started.
1029
+ */
878
1030
  function construct$13(token, state) {
879
1031
  return dispatch$13(token, state);
880
1032
  }
@@ -893,6 +1045,7 @@ const dispatch$12 = createTokenDispatcher([
893
1045
  tokenType: "CommentContent",
894
1046
  handler(token, state) {
895
1047
  state.currentNode.content = token.value;
1048
+ state.currentNode.value = token.value;
896
1049
  state.caretPosition++;
897
1050
  return state;
898
1051
  }
@@ -908,6 +1061,9 @@ const dispatch$12 = createTokenDispatcher([
908
1061
  }
909
1062
  }
910
1063
  ]);
1064
+ /**
1065
+ * Construct a comment node from comment tokens.
1066
+ */
911
1067
  function construct$12(token, state) {
912
1068
  return dispatch$12(token, state);
913
1069
  }
@@ -935,7 +1091,7 @@ const dispatch$11 = createTokenDispatcher([
935
1091
  }
936
1092
  },
937
1093
  {
938
- tokenType: new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
1094
+ tokenType: /* @__PURE__ */ new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
939
1095
  handler(_, state) {
940
1096
  state.currentContext = {
941
1097
  parentRef: state.currentContext,
@@ -948,6 +1104,9 @@ const dispatch$11 = createTokenDispatcher([
948
1104
  state.caretPosition++;
949
1105
  return state;
950
1106
  });
1107
+ /**
1108
+ * Construct a doctype node from doctype tokens.
1109
+ */
951
1110
  function construct$11(token, state) {
952
1111
  return dispatch$11(token, state);
953
1112
  }
@@ -966,6 +1125,9 @@ const dispatch$10 = createTokenDispatcher([{
966
1125
  state.caretPosition++;
967
1126
  return state;
968
1127
  });
1128
+ /**
1129
+ * Construct the normalized name for an element node.
1130
+ */
969
1131
  function construct$10(token, state) {
970
1132
  return dispatch$10(token, state);
971
1133
  }
@@ -974,7 +1136,7 @@ function construct$10(token, state) {
974
1136
  var attribute_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$9 });
975
1137
  const dispatch$9 = createTokenDispatcher([
976
1138
  {
977
- tokenType: new Set(["OpenTagEnd"]),
1139
+ tokenType: /* @__PURE__ */ new Set(["OpenTagEnd"]),
978
1140
  handler(_, state) {
979
1141
  state.currentContext = state.currentContext.parentRef;
980
1142
  return state;
@@ -1012,6 +1174,9 @@ const dispatch$9 = createTokenDispatcher([
1012
1174
  state.caretPosition++;
1013
1175
  return state;
1014
1176
  });
1177
+ /**
1178
+ * Construct an element attribute from attribute tokens.
1179
+ */
1015
1180
  function construct$9(token, state) {
1016
1181
  return dispatch$9(token, state);
1017
1182
  }
@@ -1019,7 +1184,7 @@ function construct$9(token, state) {
1019
1184
  //#region src/constructor/handlers/attributes.ts
1020
1185
  var attributes_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$8 });
1021
1186
  const dispatch$8 = createTokenDispatcher([{
1022
- tokenType: new Set(["AttributeKey", "AttributeAssignment"]),
1187
+ tokenType: /* @__PURE__ */ new Set(["AttributeKey", "AttributeAssignment"]),
1023
1188
  handler(token, state) {
1024
1189
  initAttributesIfNone(state.currentNode);
1025
1190
  state.currentNode.attributes.push({
@@ -1034,7 +1199,7 @@ const dispatch$8 = createTokenDispatcher([{
1034
1199
  return state;
1035
1200
  }
1036
1201
  }, {
1037
- tokenType: new Set(["OpenTagEnd"]),
1202
+ tokenType: /* @__PURE__ */ new Set(["OpenTagEnd"]),
1038
1203
  handler(_, state) {
1039
1204
  state.currentContext = state.currentContext.parentRef;
1040
1205
  return state;
@@ -1043,18 +1208,54 @@ const dispatch$8 = createTokenDispatcher([{
1043
1208
  state.caretPosition++;
1044
1209
  return state;
1045
1210
  });
1211
+ /**
1212
+ * Construct the attributes collection for an element node.
1213
+ */
1046
1214
  function construct$8(token, state) {
1047
1215
  return dispatch$8(token, state);
1048
1216
  }
1049
1217
  //#endregion
1050
1218
  //#region src/constructor/handlers/tagContent.ts
1051
1219
  var tagContent_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$7 });
1220
+ function addUnclosedTagError(state, node) {
1221
+ state.errorHandler.addError({
1222
+ type: "UnclosedTag",
1223
+ message: `Unclosed tag "${node.name ?? "unknown"}".`,
1224
+ range: cloneRange(node.range),
1225
+ loc: cloneLocation(node.loc),
1226
+ recovery: "The parser kept the partial element in the AST."
1227
+ });
1228
+ }
1052
1229
  const dispatch$7 = createTokenDispatcher([
1230
+ {
1231
+ tokenType: "XMLDeclarationOpen",
1232
+ handler(token, state) {
1233
+ if (state.currentNode.type !== "Document") {
1234
+ state.caretPosition++;
1235
+ return state;
1236
+ }
1237
+ initChildrenIfNone(state.currentNode);
1238
+ const declarationNode = {
1239
+ type: "XMLDeclaration",
1240
+ parentRef: state.currentNode,
1241
+ range: cloneRange(token.range),
1242
+ loc: cloneLocation(token.loc),
1243
+ attributes: []
1244
+ };
1245
+ state.currentNode.children.push(declarationNode);
1246
+ state.currentNode = declarationNode;
1247
+ state.currentContext = {
1248
+ parentRef: state.currentContext,
1249
+ type: "XMLDeclaration"
1250
+ };
1251
+ return state;
1252
+ }
1253
+ },
1053
1254
  {
1054
1255
  tokenType: "OpenTagStart",
1055
1256
  handler(token, state) {
1056
1257
  initChildrenIfNone(state.currentNode);
1057
- const tagNode = {
1258
+ const elementNode = {
1058
1259
  type: "Element",
1059
1260
  parentRef: state.currentNode,
1060
1261
  range: cloneRange(token.range),
@@ -1062,8 +1263,8 @@ const dispatch$7 = createTokenDispatcher([
1062
1263
  attributes: [],
1063
1264
  children: []
1064
1265
  };
1065
- state.currentNode.children.push(tagNode);
1066
- state.currentNode = tagNode;
1266
+ state.currentNode.children.push(elementNode);
1267
+ state.currentNode = elementNode;
1067
1268
  state.currentContext = {
1068
1269
  parentRef: state.currentContext,
1069
1270
  type: "Tag"
@@ -1108,7 +1309,31 @@ const dispatch$7 = createTokenDispatcher([
1108
1309
  {
1109
1310
  tokenType: "CloseTag",
1110
1311
  handler(token, state) {
1111
- if (parseCloseTagName(token.value) !== state.currentNode.name) {
1312
+ const closeTagName = parseCloseTagName(token.value);
1313
+ if (closeTagName !== state.currentNode.name) {
1314
+ state.errorHandler.addError({
1315
+ type: "MismatchedTag",
1316
+ message: `Expected closing tag for "${state.currentNode.name ?? "unknown"}" but found "${closeTagName}".`,
1317
+ range: cloneRange(token.range),
1318
+ loc: cloneLocation(token.loc),
1319
+ recovery: "The mismatched closing tag was skipped."
1320
+ });
1321
+ let ancestor = state.currentNode.parentRef;
1322
+ let context = state.currentContext.parentRef?.parentRef;
1323
+ while (ancestor && ancestor.type !== "Document") {
1324
+ if (ancestor.name === closeTagName) {
1325
+ let unclosedNode = state.currentNode;
1326
+ while (unclosedNode && unclosedNode !== ancestor) {
1327
+ addUnclosedTagError(state, unclosedNode);
1328
+ unclosedNode = unclosedNode.parentRef;
1329
+ }
1330
+ state.currentNode = ancestor;
1331
+ state.currentContext = context;
1332
+ return state;
1333
+ }
1334
+ ancestor = ancestor.parentRef;
1335
+ context = context?.parentRef?.parentRef;
1336
+ }
1112
1337
  state.caretPosition++;
1113
1338
  return state;
1114
1339
  }
@@ -1139,6 +1364,9 @@ const dispatch$7 = createTokenDispatcher([
1139
1364
  state.caretPosition++;
1140
1365
  return state;
1141
1366
  });
1367
+ /**
1368
+ * Construct document or element children from content tokens.
1369
+ */
1142
1370
  function construct$7(token, state) {
1143
1371
  return dispatch$7(token, state);
1144
1372
  }
@@ -1147,7 +1375,7 @@ function construct$7(token, state) {
1147
1375
  var attributeValue_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$6 });
1148
1376
  const dispatch$6 = createTokenDispatcher([
1149
1377
  {
1150
- tokenType: new Set([
1378
+ tokenType: /* @__PURE__ */ new Set([
1151
1379
  "OpenTagEnd",
1152
1380
  "AttributeKey",
1153
1381
  "AttributeAssignment"
@@ -1192,6 +1420,9 @@ const dispatch$6 = createTokenDispatcher([
1192
1420
  state.caretPosition++;
1193
1421
  return state;
1194
1422
  });
1423
+ /**
1424
+ * Construct an element attribute value from value tokens.
1425
+ */
1195
1426
  function construct$6(token, state) {
1196
1427
  return dispatch$6(token, state);
1197
1428
  }
@@ -1199,10 +1430,12 @@ function construct$6(token, state) {
1199
1430
  //#region src/constructor/handlers/xmlDeclaration.ts
1200
1431
  var xmlDeclaration_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$5 });
1201
1432
  const dispatch$5 = createTokenDispatcher([{
1202
- tokenType: "OpenTagStart",
1203
- handler(token, state) {
1204
- state.currentNode.name = parseOpenTagName(token.value);
1205
- state.currentContext = state.currentContext.parentRef;
1433
+ tokenType: "XMLDeclarationOpen",
1434
+ handler(_, state) {
1435
+ state.currentContext = {
1436
+ parentRef: state.currentContext,
1437
+ type: "XMLDeclarationAttributes"
1438
+ };
1206
1439
  state.caretPosition++;
1207
1440
  return state;
1208
1441
  }
@@ -1210,6 +1443,9 @@ const dispatch$5 = createTokenDispatcher([{
1210
1443
  state.caretPosition++;
1211
1444
  return state;
1212
1445
  });
1446
+ /**
1447
+ * Construct an XML declaration node after its opening token.
1448
+ */
1213
1449
  function construct$5(token, state) {
1214
1450
  return dispatch$5(token, state);
1215
1451
  }
@@ -1265,6 +1501,9 @@ const dispatch$4 = createTokenDispatcher([
1265
1501
  state.caretPosition++;
1266
1502
  return state;
1267
1503
  });
1504
+ /**
1505
+ * Construct a doctype attribute from value and wrapper tokens.
1506
+ */
1268
1507
  function construct$4(token, state) {
1269
1508
  return dispatch$4(token, state);
1270
1509
  }
@@ -1278,7 +1517,7 @@ const dispatch$3 = createTokenDispatcher([{
1278
1517
  return state;
1279
1518
  }
1280
1519
  }, {
1281
- tokenType: new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
1520
+ tokenType: /* @__PURE__ */ new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
1282
1521
  handler(token, state) {
1283
1522
  initAttributesIfNone(state.currentNode);
1284
1523
  state.currentNode.attributes.push({
@@ -1296,6 +1535,9 @@ const dispatch$3 = createTokenDispatcher([{
1296
1535
  state.caretPosition++;
1297
1536
  return state;
1298
1537
  });
1538
+ /**
1539
+ * Construct the attributes collection for a doctype node.
1540
+ */
1299
1541
  function construct$3(token, state) {
1300
1542
  return dispatch$3(token, state);
1301
1543
  }
@@ -1304,40 +1546,36 @@ function construct$3(token, state) {
1304
1546
  var xmlDeclarationAttribute_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$2 });
1305
1547
  const dispatch$2 = createTokenDispatcher([
1306
1548
  {
1307
- tokenType: new Set([
1308
- "OpenTagEnd",
1309
- "AttributeKey",
1310
- "AttributeAssignment"
1311
- ]),
1549
+ tokenType: /* @__PURE__ */ new Set(["XMLDeclarationClose"]),
1312
1550
  handler(_, state) {
1313
1551
  state.currentContext = state.currentContext.parentRef;
1314
1552
  return state;
1315
1553
  }
1316
1554
  },
1317
1555
  {
1318
- tokenType: "AttributeValue",
1556
+ tokenType: "XMLDeclarationAttributeKey",
1319
1557
  handler(token, state) {
1320
1558
  const attribute = getLastAttribute(state);
1321
- attribute.value = createNodeFrom(token);
1322
- updateNodeEnd(attribute, token);
1323
- state.caretPosition++;
1324
- return state;
1325
- }
1326
- },
1327
- {
1328
- tokenType: "AttributeValueWrapperStart",
1329
- handler(token, state) {
1330
- const attribute = getLastAttribute(state);
1331
- attribute.quoteChar = token.value;
1332
- if (!attribute.key) attribute.range = cloneRange(token.range);
1559
+ if (attribute.key !== void 0 || attribute.value !== void 0) {
1560
+ state.currentContext = state.currentContext.parentRef;
1561
+ return state;
1562
+ }
1563
+ attribute.key = createNodeFrom(token);
1333
1564
  state.caretPosition++;
1334
1565
  return state;
1335
1566
  }
1336
1567
  },
1337
1568
  {
1338
- tokenType: "AttributeValueWrapperEnd",
1339
- handler(token, state) {
1340
- updateNodeEnd(getLastAttribute(state), token);
1569
+ tokenType: "XMLDeclarationAttributeAssignment",
1570
+ handler(_, state) {
1571
+ if (getLastAttribute(state).value !== void 0) {
1572
+ state.currentContext = state.currentContext.parentRef;
1573
+ return state;
1574
+ }
1575
+ state.currentContext = {
1576
+ parentRef: state.currentContext,
1577
+ type: "XMLDeclarationAttributeValue"
1578
+ };
1341
1579
  state.caretPosition++;
1342
1580
  return state;
1343
1581
  }
@@ -1346,6 +1584,9 @@ const dispatch$2 = createTokenDispatcher([
1346
1584
  state.caretPosition++;
1347
1585
  return state;
1348
1586
  });
1587
+ /**
1588
+ * Construct an XML declaration attribute key and value transition.
1589
+ */
1349
1590
  function construct$2(token, state) {
1350
1591
  return dispatch$2(token, state);
1351
1592
  }
@@ -1353,17 +1594,20 @@ function construct$2(token, state) {
1353
1594
  //#region src/constructor/handlers/xmlDeclarationAttributes.ts
1354
1595
  var xmlDeclarationAttributes_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$1 });
1355
1596
  const dispatch$1 = createTokenDispatcher([{
1356
- tokenType: "OpenTagEnd",
1357
- handler(_, state) {
1597
+ tokenType: "XMLDeclarationClose",
1598
+ handler(token, state) {
1599
+ updateNodeEnd(state.currentNode, token);
1600
+ state.currentNode = state.currentNode.parentRef;
1358
1601
  state.currentContext = state.currentContext.parentRef;
1602
+ state.caretPosition++;
1359
1603
  return state;
1360
1604
  }
1361
1605
  }, {
1362
- tokenType: new Set(["AttributeKey", "AttributeAssignment"]),
1606
+ tokenType: /* @__PURE__ */ new Set(["XMLDeclarationAttributeKey", "XMLDeclarationAttributeAssignment"]),
1363
1607
  handler(token, state) {
1364
1608
  initAttributesIfNone(state.currentNode);
1365
1609
  state.currentNode.attributes.push({
1366
- type: "Attribute",
1610
+ type: "XMLDeclarationAttribute",
1367
1611
  range: cloneRange(token.range),
1368
1612
  loc: cloneLocation(token.loc)
1369
1613
  });
@@ -1377,6 +1621,9 @@ const dispatch$1 = createTokenDispatcher([{
1377
1621
  state.caretPosition++;
1378
1622
  return state;
1379
1623
  });
1624
+ /**
1625
+ * Construct the attributes collection for an XML declaration node.
1626
+ */
1380
1627
  function construct$1(token, state) {
1381
1628
  return dispatch$1(token, state);
1382
1629
  }
@@ -1385,14 +1632,14 @@ function construct$1(token, state) {
1385
1632
  var xmlDeclarationAttributeValue_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct });
1386
1633
  const dispatch = createTokenDispatcher([
1387
1634
  {
1388
- tokenType: "OpenTagEnd",
1635
+ tokenType: "XMLDeclarationClose",
1389
1636
  handler(_, state) {
1390
1637
  state.currentContext = state.currentContext.parentRef;
1391
1638
  return state;
1392
1639
  }
1393
1640
  },
1394
1641
  {
1395
- tokenType: "AttributeValue",
1642
+ tokenType: "XMLDeclarationAttributeValue",
1396
1643
  handler(token, state) {
1397
1644
  const attribute = getLastAttribute(state);
1398
1645
  if (attribute.value !== void 0) {
@@ -1406,7 +1653,7 @@ const dispatch = createTokenDispatcher([
1406
1653
  }
1407
1654
  },
1408
1655
  {
1409
- tokenType: "AttributeValueWrapperStart",
1656
+ tokenType: "XMLDeclarationAttributeValueWrapperStart",
1410
1657
  handler(token, state) {
1411
1658
  const attribute = getLastAttribute(state);
1412
1659
  if (attribute.value !== void 0) {
@@ -1420,7 +1667,7 @@ const dispatch = createTokenDispatcher([
1420
1667
  }
1421
1668
  },
1422
1669
  {
1423
- tokenType: "AttributeValueWrapperEnd",
1670
+ tokenType: "XMLDeclarationAttributeValueWrapperEnd",
1424
1671
  handler(token, state) {
1425
1672
  updateNodeEnd(getLastAttribute(state), token);
1426
1673
  state.currentContext = state.currentContext.parentRef;
@@ -1432,6 +1679,9 @@ const dispatch = createTokenDispatcher([
1432
1679
  state.caretPosition++;
1433
1680
  return state;
1434
1681
  });
1682
+ /**
1683
+ * Construct an XML declaration attribute value from value tokens.
1684
+ */
1435
1685
  function construct(token, state) {
1436
1686
  return dispatch(token, state);
1437
1687
  }
@@ -1474,6 +1724,24 @@ function processTokens(tokens, state, positionOffset) {
1474
1724
  }
1475
1725
  return state;
1476
1726
  }
1727
+ function reportUnclosedNodes(state) {
1728
+ let node = state.currentNode;
1729
+ while (node && node !== state.rootNode) {
1730
+ if (node.type === "Element" && !node.selfClosing && !node.close) state.errorHandler.addError({
1731
+ type: "UnclosedTag",
1732
+ message: `Unclosed tag "${node.name ?? "unknown"}".`,
1733
+ range: cloneRange(node.range),
1734
+ loc: cloneLocation(node.loc),
1735
+ recovery: "The parser kept the partial element in the AST."
1736
+ });
1737
+ node = node.parentRef;
1738
+ }
1739
+ }
1740
+ /**
1741
+ * Build the document AST from tokenizer output.
1742
+ * @param tokens - Tokens produced from SVG source
1743
+ * @returns Constructed AST, final constructor state, and recoverable diagnostics
1744
+ */
1477
1745
  function constructTree(tokens) {
1478
1746
  const rootContext = {
1479
1747
  type: "TagContent",
@@ -1498,48 +1766,84 @@ function constructTree(tokens) {
1498
1766
  caretPosition: 0,
1499
1767
  currentContext: rootContext,
1500
1768
  currentNode: rootNode,
1769
+ errorHandler: new ErrorHandler(),
1501
1770
  rootNode
1502
1771
  };
1503
1772
  const positionOffset = state.caretPosition;
1504
- processTokens(tokens, state, positionOffset);
1773
+ reportUnclosedNodes(processTokens(tokens, state, positionOffset));
1505
1774
  return {
1506
1775
  state,
1507
- ast: state.rootNode
1776
+ ast: state.rootNode,
1777
+ errors: state.errorHandler.getErrors(),
1778
+ warnings: state.errorHandler.getWarnings()
1508
1779
  };
1509
1780
  }
1510
1781
  //#endregion
1511
1782
  //#region src/tokenizer/charsBuffer.ts
1783
+ /**
1784
+ * Mutable buffer used by tokenizer handlers while deciding token boundaries.
1785
+ */
1512
1786
  var CharsBuffer = class {
1513
1787
  charsBuffer = [];
1788
+ /**
1789
+ * Append a character wrapper to the buffer.
1790
+ */
1514
1791
  concat(chars) {
1515
1792
  const theLast = this.last();
1516
- if (theLast) theLast.concat(chars);
1793
+ if (theLast) theLast.append(chars);
1517
1794
  else this.charsBuffer.push(chars);
1518
1795
  }
1796
+ /**
1797
+ * Append another buffer without merging its entries.
1798
+ */
1519
1799
  concatBuffer(buffer) {
1520
1800
  this.charsBuffer.push(...buffer.charsBuffer);
1521
1801
  }
1802
+ /**
1803
+ * Get the total buffered character length.
1804
+ */
1522
1805
  length() {
1523
1806
  return this.charsBuffer.map((chars) => chars.length()).reduce((a, b) => a + b, 0);
1524
1807
  }
1808
+ /**
1809
+ * Clear all buffered characters.
1810
+ */
1525
1811
  clear() {
1526
1812
  this.charsBuffer = [];
1527
1813
  }
1814
+ /**
1815
+ * Get the concatenated buffered string.
1816
+ */
1528
1817
  value() {
1529
1818
  return this.charsBuffer.map((chars) => chars.value).join("");
1530
1819
  }
1820
+ /**
1821
+ * Get the last buffered character wrapper.
1822
+ */
1531
1823
  last() {
1532
1824
  return last(this.charsBuffer);
1533
1825
  }
1826
+ /**
1827
+ * Get the first buffered character wrapper.
1828
+ */
1534
1829
  first() {
1535
1830
  return first(this.charsBuffer);
1536
1831
  }
1832
+ /**
1833
+ * Remove the last buffered entry.
1834
+ */
1537
1835
  removeLast() {
1538
1836
  this.charsBuffer.splice(-1, 1);
1539
1837
  }
1838
+ /**
1839
+ * Remove the first buffered entry.
1840
+ */
1540
1841
  removeFirst() {
1541
1842
  this.charsBuffer.splice(0, 1);
1542
1843
  }
1844
+ /**
1845
+ * Replace this buffer with a copy of another buffer.
1846
+ */
1543
1847
  replace(other) {
1544
1848
  this.charsBuffer = [...other.charsBuffer];
1545
1849
  }
@@ -1550,7 +1854,7 @@ var data_exports = /* @__PURE__ */ __exportAll({
1550
1854
  handleContentEnd: () => handleContentEnd,
1551
1855
  parse: () => parse$22
1552
1856
  });
1553
- const INCOMPLETE_DOCTYPE_CHARS = new Set([
1857
+ const INCOMPLETE_DOCTYPE_CHARS = /* @__PURE__ */ new Set([
1554
1858
  "<!",
1555
1859
  "<!D",
1556
1860
  "<!DO",
@@ -1559,6 +1863,17 @@ const INCOMPLETE_DOCTYPE_CHARS = new Set([
1559
1863
  "<!DOCTY",
1560
1864
  "<!DOCTYP"
1561
1865
  ]);
1866
+ const INCOMPLETE_COMMENT_START_CHARS = /* @__PURE__ */ new Set([
1867
+ SPECIAL_CHAR.openingCorner,
1868
+ "<!",
1869
+ "<!-"
1870
+ ]);
1871
+ const INCOMPLETE_XML_DECLARATION_START_CHARS = /* @__PURE__ */ new Set([
1872
+ SPECIAL_CHAR.openingCorner,
1873
+ "<?",
1874
+ "<?x",
1875
+ "<?xm"
1876
+ ]);
1562
1877
  function generateTextToken(state) {
1563
1878
  const position = calculateTokenPosition(state, { keepBuffer: false });
1564
1879
  return {
@@ -1608,17 +1923,28 @@ function parseDoctypeOpen(state) {
1608
1923
  }
1609
1924
  function parseXMLDeclarationOpen(state) {
1610
1925
  if (state.accumulatedContent.length() !== 0) state.tokens.push(generateTextToken(state));
1926
+ const range = [state.sourceCode.index() - 4, state.sourceCode.index() + 1];
1927
+ state.tokens.push({
1928
+ type: "XMLDeclarationOpen",
1929
+ value: state.decisionBuffer.value(),
1930
+ range,
1931
+ loc: state.sourceCode.getLocationOf(range)
1932
+ });
1611
1933
  state.accumulatedContent.clear();
1612
1934
  state.decisionBuffer.clear();
1613
1935
  state.currentContext = "XMLDeclarationAttributes";
1614
1936
  state.sourceCode.next();
1615
1937
  }
1938
+ /**
1939
+ * Tokenize top-level text and dispatch into markup-specific contexts.
1940
+ */
1616
1941
  function parse$22(chars, state) {
1617
1942
  const value = chars.value();
1618
1943
  if (value === "<?xml") return parseXMLDeclarationOpen(state);
1944
+ if (INCOMPLETE_XML_DECLARATION_START_CHARS.has(value)) return state.sourceCode.next();
1619
1945
  if (RE_OPEN_TAG_START.test(value)) return parseOpeningCornerBraceWithText(state);
1620
1946
  if (value === "</") return parseOpeningCornerBraceWithSlash(state);
1621
- if (value === SPECIAL_CHAR.openingCorner || value === "<!" || value === "<!-") return state.sourceCode.next();
1947
+ if (INCOMPLETE_COMMENT_START_CHARS.has(value)) return state.sourceCode.next();
1622
1948
  if (value === "<!--") return parseCommentOpen(state);
1623
1949
  if (isIncompleteDoctype(value)) return state.sourceCode.next();
1624
1950
  if (value.toUpperCase() === "<!DOCTYPE") return parseDoctypeOpen(state);
@@ -1626,6 +1952,9 @@ function parse$22(chars, state) {
1626
1952
  state.decisionBuffer.clear();
1627
1953
  state.sourceCode.next();
1628
1954
  }
1955
+ /**
1956
+ * Flush buffered text when the source ends in data context.
1957
+ */
1629
1958
  function handleContentEnd(state) {
1630
1959
  const textContent = state.accumulatedContent.value() + state.decisionBuffer.value();
1631
1960
  if (textContent.length !== 0) {
@@ -1654,6 +1983,9 @@ function parseClosingCornerBrace$5(state) {
1654
1983
  state.currentContext = "Data";
1655
1984
  state.sourceCode.next();
1656
1985
  }
1986
+ /**
1987
+ * Tokenize a closing tag.
1988
+ */
1657
1989
  function parse$21(chars, state) {
1658
1990
  if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$5(state);
1659
1991
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -1671,7 +2003,7 @@ function parseTagEnd$3(state) {
1671
2003
  state.contextParams["OpenTagEnd"] = { tagName };
1672
2004
  state.contextParams["Attributes"] = void 0;
1673
2005
  }
1674
- function parseEqual(state) {
2006
+ function parseEqual$1(state) {
1675
2007
  const position = calculateTokenPosition(state, { keepBuffer: true });
1676
2008
  state.tokens.push({
1677
2009
  type: "AttributeAssignment",
@@ -1684,17 +2016,20 @@ function parseEqual(state) {
1684
2016
  state.currentContext = "AttributeValue";
1685
2017
  state.sourceCode.next();
1686
2018
  }
1687
- function parseNoneWhitespace(chars, state) {
2019
+ function parseNoneWhitespace$1(chars, state) {
1688
2020
  state.accumulatedContent.replace(state.decisionBuffer);
1689
2021
  state.currentContext = "AttributeKey";
1690
2022
  state.decisionBuffer.clear();
1691
2023
  state.sourceCode.next();
1692
2024
  }
2025
+ /**
2026
+ * Tokenize whitespace, assignments, and attribute starts inside an open tag.
2027
+ */
1693
2028
  function parse$20(chars, state) {
1694
2029
  const value = chars.value();
1695
2030
  if (value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseTagEnd$3(state);
1696
- if (value === SPECIAL_CHAR.equal) return parseEqual(state);
1697
- if (!isWhitespace(value)) return parseNoneWhitespace(chars, state);
2031
+ if (value === SPECIAL_CHAR.equal) return parseEqual$1(state);
2032
+ if (!isWhitespace(value)) return parseNoneWhitespace$1(chars, state);
1698
2033
  state.decisionBuffer.clear();
1699
2034
  state.sourceCode.next();
1700
2035
  }
@@ -1715,6 +2050,9 @@ function parseClosingCornerBrace$4(state) {
1715
2050
  state.sourceCode.next();
1716
2051
  state.contextParams["OpenTagEnd"] = void 0;
1717
2052
  }
2053
+ /**
2054
+ * Tokenize the end of an opening tag.
2055
+ */
1718
2056
  function parse$19(chars, state) {
1719
2057
  if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$4(state);
1720
2058
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -1745,6 +2083,9 @@ function parseClosingCornerBrace$3(state) {
1745
2083
  state.decisionBuffer.clear();
1746
2084
  state.currentContext = "DoctypeClose";
1747
2085
  }
2086
+ /**
2087
+ * Tokenize the opening delimiter of a doctype declaration.
2088
+ */
1748
2089
  function parse$18(chars, state) {
1749
2090
  const value = chars.value();
1750
2091
  if (isWhitespace(value)) return parseWhitespace$1(state);
@@ -1771,6 +2112,9 @@ function parseKeyEnd$1(state) {
1771
2112
  state.decisionBuffer.clear();
1772
2113
  state.currentContext = "Attributes";
1773
2114
  }
2115
+ /**
2116
+ * Tokenize an element attribute key.
2117
+ */
1774
2118
  function parse$17(chars, state) {
1775
2119
  if (isKeyBreak$1(chars)) return parseKeyEnd$1(state);
1776
2120
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -1780,6 +2124,9 @@ function parse$17(chars, state) {
1780
2124
  //#endregion
1781
2125
  //#region src/tokenizer/handlers/doctypeClose.ts
1782
2126
  var doctypeClose_exports = /* @__PURE__ */ __exportAll({ parse: () => parse$16 });
2127
+ /**
2128
+ * Tokenize the closing delimiter of a doctype declaration.
2129
+ */
1783
2130
  function parse$16(chars, state) {
1784
2131
  const position = calculateTokenPosition(state, { keepBuffer: true });
1785
2132
  state.tokens.push({
@@ -1825,6 +2172,9 @@ function parseWhitespace(state) {
1825
2172
  state.contextParams["Attributes"] = { tagName };
1826
2173
  state.sourceCode.next();
1827
2174
  }
2175
+ /**
2176
+ * Tokenize the start of an opening tag.
2177
+ */
1828
2178
  function parse$15(chars, state) {
1829
2179
  const value = chars.value();
1830
2180
  if (value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseTagEnd$2(state);
@@ -1862,6 +2212,9 @@ function parseBare$2(state) {
1862
2212
  state.currentContext = "AttributeValueBare";
1863
2213
  state.sourceCode.next();
1864
2214
  }
2215
+ /**
2216
+ * Tokenize the start of an element attribute value.
2217
+ */
1865
2218
  function parse$14(chars, state) {
1866
2219
  const value = chars.value();
1867
2220
  if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$5(state);
@@ -1893,6 +2246,9 @@ function parseCommentClose(state) {
1893
2246
  state.currentContext = "Data";
1894
2247
  state.sourceCode.next();
1895
2248
  }
2249
+ /**
2250
+ * Tokenize comment content until the closing delimiter.
2251
+ */
1896
2252
  function parse$13(chars, state) {
1897
2253
  const value = chars.value();
1898
2254
  if (value === SPECIAL_CHAR.hyphen || value === "--") return state.sourceCode.next();
@@ -1930,6 +2286,9 @@ function parseBare$1(state) {
1930
2286
  state.currentContext = "DoctypeAttributeBare";
1931
2287
  state.sourceCode.next();
1932
2288
  }
2289
+ /**
2290
+ * Tokenize doctype attributes and the doctype close transition.
2291
+ */
1933
2292
  function parse$12(chars, state) {
1934
2293
  const value = chars.value();
1935
2294
  if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$4(state);
@@ -1953,6 +2312,9 @@ function parseValueEnd(state) {
1953
2312
  state.decisionBuffer.clear();
1954
2313
  state.currentContext = "Attributes";
1955
2314
  }
2315
+ /**
2316
+ * Tokenize an unquoted element attribute value.
2317
+ */
1956
2318
  function parse$11(chars, state) {
1957
2319
  const value = chars.value();
1958
2320
  if (isWhitespace(value) || value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseValueEnd(state);
@@ -1977,6 +2339,9 @@ function parseClosingCornerBrace$1(state) {
1977
2339
  state.sourceCode.next();
1978
2340
  state.contextParams["OpenTagEnd"] = void 0;
1979
2341
  }
2342
+ /**
2343
+ * Tokenize the opening delimiter of an XML declaration.
2344
+ */
1980
2345
  function parse$10(chars, state) {
1981
2346
  if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$1(state);
1982
2347
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -2000,6 +2365,9 @@ function parseClosingCornerBrace(state) {
2000
2365
  state.sourceCode.next();
2001
2366
  state.contextParams["OpenTagEnd"] = void 0;
2002
2367
  }
2368
+ /**
2369
+ * Tokenize the closing delimiter of an XML declaration.
2370
+ */
2003
2371
  function parse$9(chars, state) {
2004
2372
  if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace(state);
2005
2373
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -2021,6 +2389,9 @@ function parseAttributeEnd(state) {
2021
2389
  state.decisionBuffer.clear();
2022
2390
  state.currentContext = "DoctypeAttributes";
2023
2391
  }
2392
+ /**
2393
+ * Tokenize an unquoted doctype attribute value.
2394
+ */
2024
2395
  function parse$8(chars, state) {
2025
2396
  const value = chars.value();
2026
2397
  if (isWhitespace(value) || value === SPECIAL_CHAR.closingCorner) return parseAttributeEnd(state);
@@ -2053,6 +2424,9 @@ function parseWrapper$3(state) {
2053
2424
  state.sourceCode.next();
2054
2425
  state.contextParams["AttributeValueWrapped"] = void 0;
2055
2426
  }
2427
+ /**
2428
+ * Tokenize a quoted element attribute value.
2429
+ */
2056
2430
  function parse$7(chars, state) {
2057
2431
  const wrapperChar = state.contextParams["AttributeValueWrapped"]?.wrapper;
2058
2432
  if (chars.value() === wrapperChar) return parseWrapper$3(state);
@@ -2085,6 +2459,9 @@ function parseWrapper$2(state) {
2085
2459
  state.sourceCode.next();
2086
2460
  state.contextParams["DoctypeAttributeWrapped"] = void 0;
2087
2461
  }
2462
+ /**
2463
+ * Tokenize a quoted doctype attribute value.
2464
+ */
2088
2465
  function parse$6(chars, state) {
2089
2466
  if (chars.value() === state.contextParams["DoctypeAttributeWrapped"]?.wrapper) return parseWrapper$2(state);
2090
2467
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -2108,11 +2485,34 @@ function parseXMLDeclarationClose(state) {
2108
2485
  state.currentContext = "Data";
2109
2486
  state.sourceCode.next();
2110
2487
  }
2488
+ function parseEqual(state) {
2489
+ const position = calculateTokenPosition(state, { keepBuffer: true });
2490
+ state.tokens.push({
2491
+ type: "XMLDeclarationAttributeAssignment",
2492
+ value: state.decisionBuffer.value(),
2493
+ range: position.range,
2494
+ loc: position.loc
2495
+ });
2496
+ state.accumulatedContent.clear();
2497
+ state.decisionBuffer.clear();
2498
+ state.currentContext = "XMLDeclarationAttributeValue";
2499
+ state.sourceCode.next();
2500
+ }
2501
+ function parseNoneWhitespace(state) {
2502
+ state.accumulatedContent.replace(state.decisionBuffer);
2503
+ state.currentContext = "XMLDeclarationAttributeKey";
2504
+ state.decisionBuffer.clear();
2505
+ state.sourceCode.next();
2506
+ }
2507
+ /**
2508
+ * Tokenize XML declaration attributes and the declaration close transition.
2509
+ */
2111
2510
  function parse$5(chars, state) {
2112
2511
  const value = chars.value();
2113
- if (value === SPECIAL_CHAR.question || value === SPECIAL_CHAR.closingCorner) return parseXMLDeclarationClose(state);
2512
+ if (value === SPECIAL_CHAR.question) return state.sourceCode.next();
2114
2513
  if (value === "?>") return parseXMLDeclarationClose(state);
2115
- state.accumulatedContent.concatBuffer(state.decisionBuffer);
2514
+ if (value === SPECIAL_CHAR.equal) return parseEqual(state);
2515
+ if (!isWhitespace(value)) return parseNoneWhitespace(state);
2116
2516
  state.decisionBuffer.clear();
2117
2517
  state.sourceCode.next();
2118
2518
  }
@@ -2135,6 +2535,9 @@ function parseKeyEnd(state) {
2135
2535
  state.decisionBuffer.clear();
2136
2536
  state.currentContext = "XMLDeclarationAttributes";
2137
2537
  }
2538
+ /**
2539
+ * Tokenize an XML declaration attribute key.
2540
+ */
2138
2541
  function parse$4(chars, state) {
2139
2542
  if (isKeyBreak(chars)) return parseKeyEnd(state);
2140
2543
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
@@ -2148,28 +2551,38 @@ function parseWrapper$1(state) {
2148
2551
  const wrapper = state.decisionBuffer.value();
2149
2552
  const range = [state.sourceCode.index(), state.sourceCode.index() + 1];
2150
2553
  state.tokens.push({
2151
- type: "AttributeValueWrapperStart",
2554
+ type: "XMLDeclarationAttributeValueWrapperStart",
2152
2555
  value: wrapper,
2153
2556
  range,
2154
2557
  loc: state.sourceCode.getLocationOf(range)
2155
2558
  });
2156
2559
  state.accumulatedContent.clear();
2157
2560
  state.decisionBuffer.clear();
2158
- state.currentContext = "AttributeValueWrapped";
2159
- state.contextParams["AttributeValueWrapped"] = { wrapper };
2561
+ state.currentContext = "XMLDeclarationAttributeValueWrapped";
2562
+ state.contextParams["XMLDeclarationAttributeValueWrapped"] = { wrapper };
2160
2563
  state.sourceCode.next();
2161
2564
  }
2162
2565
  function parseTagEnd(state) {
2163
2566
  state.accumulatedContent.clear();
2164
2567
  state.decisionBuffer.clear();
2165
- state.currentContext = "Attributes";
2568
+ state.currentContext = "XMLDeclarationAttributes";
2166
2569
  }
2167
2570
  function parseBare(state) {
2168
- state.accumulatedContent.replace(state.decisionBuffer);
2571
+ const range = [state.sourceCode.index(), state.sourceCode.index() + 1];
2572
+ state.tokens.push({
2573
+ type: "XMLDeclarationAttributeValue",
2574
+ value: state.decisionBuffer.value(),
2575
+ range,
2576
+ loc: state.sourceCode.getLocationOf(range)
2577
+ });
2578
+ state.accumulatedContent.clear();
2169
2579
  state.decisionBuffer.clear();
2170
- state.currentContext = "AttributeValueBare";
2580
+ state.currentContext = "XMLDeclarationAttributes";
2171
2581
  state.sourceCode.next();
2172
2582
  }
2583
+ /**
2584
+ * Tokenize the start of an XML declaration attribute value.
2585
+ */
2173
2586
  function parse$3(chars, state) {
2174
2587
  const value = chars.value();
2175
2588
  if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$1(state);
@@ -2185,26 +2598,29 @@ function parseWrapper(state) {
2185
2598
  const position = calculateTokenPosition(state, { keepBuffer: false });
2186
2599
  const endWrapperPosition = position.range[1];
2187
2600
  state.tokens.push({
2188
- type: "AttributeValue",
2601
+ type: "XMLDeclarationAttributeValue",
2189
2602
  value: state.accumulatedContent.value(),
2190
2603
  range: position.range,
2191
2604
  loc: position.loc
2192
2605
  });
2193
2606
  const range = [endWrapperPosition, endWrapperPosition + 1];
2194
2607
  state.tokens.push({
2195
- type: "AttributeValueWrapperEnd",
2608
+ type: "XMLDeclarationAttributeValueWrapperEnd",
2196
2609
  value: state.decisionBuffer.value(),
2197
2610
  range,
2198
2611
  loc: state.sourceCode.getLocationOf(range)
2199
2612
  });
2200
2613
  state.accumulatedContent.clear();
2201
2614
  state.decisionBuffer.clear();
2202
- state.currentContext = "Attributes";
2615
+ state.currentContext = "XMLDeclarationAttributes";
2203
2616
  state.sourceCode.next();
2204
- state.contextParams["AttributeValueWrapped"] = void 0;
2617
+ state.contextParams["XMLDeclarationAttributeValueWrapped"] = void 0;
2205
2618
  }
2619
+ /**
2620
+ * Tokenize a quoted XML declaration attribute value.
2621
+ */
2206
2622
  function parse$2(chars, state) {
2207
- const wrapperChar = state.contextParams["AttributeValueWrapped"]?.wrapper;
2623
+ const wrapperChar = state.contextParams["XMLDeclarationAttributeValueWrapped"]?.wrapper;
2208
2624
  if (chars.value() === wrapperChar) return parseWrapper(state);
2209
2625
  state.accumulatedContent.concatBuffer(state.decisionBuffer);
2210
2626
  state.decisionBuffer.clear();
@@ -2212,9 +2628,15 @@ function parse$2(chars, state) {
2212
2628
  }
2213
2629
  //#endregion
2214
2630
  //#region src/tokenizer/handlers/index.ts
2631
+ /**
2632
+ * Tokenizer handler for contexts that intentionally consume no content.
2633
+ */
2215
2634
  const noop = { parse: () => {} };
2216
2635
  //#endregion
2217
2636
  //#region src/tokenizer/chars.ts
2637
+ /**
2638
+ * Buffered source characters with their source range.
2639
+ */
2218
2640
  var Chars = class {
2219
2641
  value;
2220
2642
  range;
@@ -2222,19 +2644,31 @@ var Chars = class {
2222
2644
  this.value = value;
2223
2645
  this.range = range;
2224
2646
  }
2225
- concat(chars) {
2647
+ /**
2648
+ * Append adjacent characters into this range.
2649
+ */
2650
+ append(chars) {
2226
2651
  this.value += chars.value;
2227
2652
  this.range[1] = chars.range[1];
2228
2653
  }
2654
+ /**
2655
+ * Compare the buffered value with raw text.
2656
+ */
2229
2657
  equals(chars) {
2230
2658
  return this.value === chars;
2231
2659
  }
2660
+ /**
2661
+ * Get the buffered string length.
2662
+ */
2232
2663
  length() {
2233
2664
  return this.value.length;
2234
2665
  }
2235
2666
  };
2236
2667
  //#endregion
2237
2668
  //#region src/tokenizer/sourceCode.ts
2669
+ /**
2670
+ * Cursor-based view over source text for tokenizer handlers.
2671
+ */
2238
2672
  var SourceCode = class {
2239
2673
  source;
2240
2674
  charsList;
@@ -2243,24 +2677,42 @@ var SourceCode = class {
2243
2677
  this.source = source;
2244
2678
  this.charsList = this.createCharsList();
2245
2679
  }
2680
+ /**
2681
+ * Convert a character range to source locations.
2682
+ */
2246
2683
  getLocationOf(range) {
2247
2684
  return {
2248
2685
  start: getLineInfo(this.source, range[0]),
2249
2686
  end: getLineInfo(this.source, range[1])
2250
2687
  };
2251
2688
  }
2689
+ /**
2690
+ * Get the current character wrapper.
2691
+ */
2252
2692
  current() {
2253
2693
  return this.charsList[this.charsIndex];
2254
2694
  }
2695
+ /**
2696
+ * Advance to the next character.
2697
+ */
2255
2698
  next() {
2256
2699
  this.charsIndex++;
2257
2700
  }
2701
+ /**
2702
+ * Move back to the previous character.
2703
+ */
2258
2704
  prev() {
2259
2705
  this.charsIndex--;
2260
2706
  }
2707
+ /**
2708
+ * Check whether the cursor has reached the end of source.
2709
+ */
2261
2710
  isEof() {
2262
2711
  return this.charsIndex >= this.charsList.length;
2263
2712
  }
2713
+ /**
2714
+ * Get the current zero-based character index.
2715
+ */
2264
2716
  index() {
2265
2717
  return this.current().range[1] - 1;
2266
2718
  }
@@ -2311,6 +2763,11 @@ function tokenizeChars(state) {
2311
2763
  state.sourceCode.prev();
2312
2764
  if (handler.handleContentEnd !== void 0) handler.handleContentEnd(state);
2313
2765
  }
2766
+ /**
2767
+ * Tokenize SVG source into parser tokens.
2768
+ * @param source - SVG source code
2769
+ * @returns Tokenizer state and emitted tokens
2770
+ */
2314
2771
  function tokenize(source) {
2315
2772
  const tokens = [];
2316
2773
  const state = {
@@ -2331,48 +2788,29 @@ function tokenize(source) {
2331
2788
  }
2332
2789
  //#endregion
2333
2790
  //#region src/parser/parse.ts
2791
+ /**
2792
+ * Parse SVG source into a document AST and token list.
2793
+ * @param source - SVG source code
2794
+ * @param _options - Parser options reserved for API compatibility
2795
+ * @returns Direct parser result with document AST, tokens, and diagnostics
2796
+ */
2334
2797
  function parse$1(source, _options = {}) {
2335
2798
  const { tokens } = tokenize(source);
2336
- const { ast } = constructTree(tokens);
2799
+ const { ast, errors, warnings } = constructTree(tokens);
2337
2800
  return {
2338
2801
  ast: clearParent(ast),
2339
- tokens
2802
+ errors,
2803
+ tokens,
2804
+ warnings
2340
2805
  };
2341
2806
  }
2342
2807
  //#endregion
2343
- //#region src/visitorKeys.ts
2344
- const keys = {
2345
- Program: ["document"],
2346
- Document: ["children"],
2347
- XMLDeclaration: ["attributes"],
2348
- XMLDeclarationAttribute: ["key", "value"],
2349
- XMLDeclarationAttributeKey: [],
2350
- XMLDeclarationAttributeValue: [],
2351
- Doctype: ["attributes"],
2352
- DoctypeAttribute: ["value"],
2353
- DoctypeAttributeValue: [],
2354
- Attribute: ["key", "value"],
2355
- AttributeKey: [],
2356
- AttributeValue: [],
2357
- Element: ["attributes", "children"],
2358
- Comment: [],
2359
- Text: [],
2360
- Error: []
2361
- };
2362
- let vistorKeysCache = null;
2363
- function getVisitorKeys() {
2364
- if (!vistorKeysCache) {
2365
- const merged = unionWith(keys);
2366
- vistorKeysCache = {
2367
- ...merged,
2368
- Tag: merged.Element
2369
- };
2370
- }
2371
- return vistorKeysCache;
2372
- }
2373
- const visitorKeys = getVisitorKeys();
2374
- //#endregion
2375
2808
  //#region src/parser/traverse.ts
2809
+ /**
2810
+ * Walk an AST node tree using the parser visitor keys.
2811
+ * @param node - Root node to visit
2812
+ * @param visitor - Callback invoked for each reachable node
2813
+ */
2376
2814
  function traverse(node, visitor) {
2377
2815
  if (!node) return;
2378
2816
  visitor(node);
@@ -2386,8 +2824,14 @@ function traverse(node, visitor) {
2386
2824
  }
2387
2825
  //#endregion
2388
2826
  //#region src/parser/parseForESLint.ts
2827
+ /**
2828
+ * Parse SVG source into an ESLint-compatible parser result.
2829
+ * @param source - SVG source code
2830
+ * @param options - Parser options forwarded to the base parser
2831
+ * @returns ESLint parser result with visitor keys, services, tokens, and comments
2832
+ */
2389
2833
  function parseForESLint(source, options = {}) {
2390
- const { ast, tokens } = parse$1(source, options);
2834
+ const { ast, errors, tokens, warnings } = parse$1(source, options);
2391
2835
  const programNode = {
2392
2836
  type: "Program",
2393
2837
  body: [],
@@ -2411,15 +2855,31 @@ function parseForESLint(source, options = {}) {
2411
2855
  ast: programNode,
2412
2856
  visitorKeys,
2413
2857
  scopeManager: null,
2414
- services: { isSVG: true }
2858
+ services: {
2859
+ errors,
2860
+ isSVG: true,
2861
+ warnings
2862
+ }
2415
2863
  };
2416
2864
  }
2417
2865
  //#endregion
2418
2866
  //#region src/index.ts
2867
+ /**
2868
+ * Parser package name.
2869
+ */
2419
2870
  const name = meta.name;
2871
+ /**
2872
+ * ESLint visitor keys for parser-specific nodes.
2873
+ */
2420
2874
  const VisitorKeys = visitorKeys;
2875
+ /**
2876
+ * Parse SVG source and return the document node directly.
2877
+ * @param code - SVG source code
2878
+ * @param options - Parser options
2879
+ * @returns Parsed SVG document node
2880
+ */
2421
2881
  function parse(code, options = {}) {
2422
2882
  return parseForESLint(code, options).ast.document;
2423
2883
  }
2424
2884
  //#endregion
2425
- export { COMMENT_END, COMMENT_START, ConstructTreeContextTypes, DEPRECATED_SVG_ELEMENTS, NodeTypes, ParseError, RE_CLOSE_TAG_NAME, RE_INCOMPLETE_CLOSING_TAG, RE_OPEN_TAG_NAME, RE_OPEN_TAG_START, SELF_CLOSING_ELEMENTS, SPECIAL_CHAR, SVG_ELEMENTS, TokenTypes, TokenizerContextTypes, VisitorKeys, XML_DECLARATION_END, XML_DECLARATION_START, cloneNode, cloneNodeWithParent, countNodes, filterNodes, findFirstNodeByType, findNodeByType, getNodeDepth, getParentChain, isNodeType, mapNodes, meta, name, parse, parseForESLint, traverseAST, validateNode, walkAST };
2885
+ export { COMMENT_END, COMMENT_START, ConstructTreeContextTypes, DEPRECATED_SVG_ELEMENTS, NodeTypes, ParseError, ParseErrorType, RE_CLOSE_TAG_NAME, RE_INCOMPLETE_CLOSING_TAG, RE_OPEN_TAG_NAME, RE_OPEN_TAG_START, SELF_CLOSING_ELEMENTS, SPECIAL_CHAR, SVG_ELEMENTS, TokenTypes, TokenizerContextTypes, VisitorKeys, XML_DECLARATION_END, XML_DECLARATION_START, cloneNode, cloneNodeWithParent, countNodes, filterNodes, findFirstNodeByType, findNodeByType, getNodeDepth, getParentChain, isNodeType, mapNodes, meta, name, parse, parseForESLint, traverseAST, validateNode, walkAST };