svg-eslint-parser 0.2.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/README.md +1 -1
- package/dist/index.d.ts +297 -33
- package/dist/index.js +599 -140
- package/package.json +24 -24
- /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { t as __exportAll } from "./
|
|
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.
|
|
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,
|
|
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
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
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";
|
|
@@ -84,6 +91,9 @@ let NodeTypes = /* @__PURE__ */ function(NodeTypes) {
|
|
|
84
91
|
}({});
|
|
85
92
|
//#endregion
|
|
86
93
|
//#region src/constants/tokenTypes.ts
|
|
94
|
+
/**
|
|
95
|
+
* Token type names emitted by tokenizer contexts.
|
|
96
|
+
*/
|
|
87
97
|
let TokenTypes = /* @__PURE__ */ function(TokenTypes) {
|
|
88
98
|
/**
|
|
89
99
|
* @pg Content tokens
|
|
@@ -131,6 +141,9 @@ let TokenTypes = /* @__PURE__ */ function(TokenTypes) {
|
|
|
131
141
|
}({});
|
|
132
142
|
//#endregion
|
|
133
143
|
//#region src/constants/specialChar.ts
|
|
144
|
+
/**
|
|
145
|
+
* Character constants used by tokenizer handlers.
|
|
146
|
+
*/
|
|
134
147
|
const SPECIAL_CHAR = {
|
|
135
148
|
closingCorner: `>`,
|
|
136
149
|
colon: `:`,
|
|
@@ -154,7 +167,7 @@ const SPECIAL_CHAR = {
|
|
|
154
167
|
* @copyright {@link https://github.com/davidohlin/svg-elements}
|
|
155
168
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element}
|
|
156
169
|
*/
|
|
157
|
-
const SVG_ELEMENTS = new Set([
|
|
170
|
+
const SVG_ELEMENTS = /* @__PURE__ */ new Set([
|
|
158
171
|
"a",
|
|
159
172
|
"animate",
|
|
160
173
|
"animateMotion",
|
|
@@ -225,7 +238,7 @@ const SVG_ELEMENTS = new Set([
|
|
|
225
238
|
*
|
|
226
239
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element#obsolete_and_deprecated_elements}
|
|
227
240
|
*/
|
|
228
|
-
const DEPRECATED_SVG_ELEMENTS = new Set([
|
|
241
|
+
const DEPRECATED_SVG_ELEMENTS = /* @__PURE__ */ new Set([
|
|
229
242
|
"altGlyph",
|
|
230
243
|
"altGlyphDef",
|
|
231
244
|
"altGlyphItem",
|
|
@@ -247,7 +260,7 @@ const DEPRECATED_SVG_ELEMENTS = new Set([
|
|
|
247
260
|
/**
|
|
248
261
|
* self closing svg elements
|
|
249
262
|
*/
|
|
250
|
-
const SELF_CLOSING_ELEMENTS = new Set([
|
|
263
|
+
const SELF_CLOSING_ELEMENTS = /* @__PURE__ */ new Set([
|
|
251
264
|
"animate",
|
|
252
265
|
"animateMotion",
|
|
253
266
|
"animateTransform",
|
|
@@ -283,6 +296,9 @@ const SELF_CLOSING_ELEMENTS = new Set([
|
|
|
283
296
|
]);
|
|
284
297
|
//#endregion
|
|
285
298
|
//#region src/constants/tokenizerContextTypes.ts
|
|
299
|
+
/**
|
|
300
|
+
* Tokenizer state machine contexts.
|
|
301
|
+
*/
|
|
286
302
|
let TokenizerContextTypes = /* @__PURE__ */ function(TokenizerContextTypes) {
|
|
287
303
|
TokenizerContextTypes["AttributeKey"] = "AttributeKey";
|
|
288
304
|
TokenizerContextTypes["Attributes"] = "Attributes";
|
|
@@ -311,6 +327,9 @@ let TokenizerContextTypes = /* @__PURE__ */ function(TokenizerContextTypes) {
|
|
|
311
327
|
}({});
|
|
312
328
|
//#endregion
|
|
313
329
|
//#region src/constants/constructTreeContextTypes.ts
|
|
330
|
+
/**
|
|
331
|
+
* AST construction state machine contexts.
|
|
332
|
+
*/
|
|
314
333
|
let ConstructTreeContextTypes = /* @__PURE__ */ function(ConstructTreeContextTypes) {
|
|
315
334
|
ConstructTreeContextTypes["Attribute"] = "Attribute";
|
|
316
335
|
ConstructTreeContextTypes["Attributes"] = "Attributes";
|
|
@@ -329,6 +348,78 @@ let ConstructTreeContextTypes = /* @__PURE__ */ function(ConstructTreeContextTyp
|
|
|
329
348
|
return ConstructTreeContextTypes;
|
|
330
349
|
}({});
|
|
331
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
|
|
332
423
|
//#region src/utils/cloneNode.ts
|
|
333
424
|
/**
|
|
334
425
|
* Deep clone a node, removing parent references
|
|
@@ -436,8 +527,14 @@ function clearParent(ast) {
|
|
|
436
527
|
* @param code - Character code to check
|
|
437
528
|
* @returns True if code is a newline character (LF, CR, LS, PS)
|
|
438
529
|
*/
|
|
530
|
+
const LINE_BREAK_CODES = /* @__PURE__ */ new Set([
|
|
531
|
+
10,
|
|
532
|
+
13,
|
|
533
|
+
8232,
|
|
534
|
+
8233
|
|
535
|
+
]);
|
|
439
536
|
function isNewLine(code) {
|
|
440
|
-
return code
|
|
537
|
+
return LINE_BREAK_CODES.has(code);
|
|
441
538
|
}
|
|
442
539
|
/**
|
|
443
540
|
* Find the next line break position in a string
|
|
@@ -472,6 +569,62 @@ function getLineInfo(input, offset) {
|
|
|
472
569
|
}
|
|
473
570
|
}
|
|
474
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
|
|
475
628
|
//#region src/utils/nodeHelpers.ts
|
|
476
629
|
/**
|
|
477
630
|
* Filter nodes by a predicate function
|
|
@@ -483,8 +636,7 @@ function filterNodes(node, predicate) {
|
|
|
483
636
|
const results = [];
|
|
484
637
|
function traverse(currentNode) {
|
|
485
638
|
if (predicate(currentNode)) results.push(currentNode);
|
|
486
|
-
|
|
487
|
-
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);
|
|
488
640
|
}
|
|
489
641
|
traverse(node);
|
|
490
642
|
return results;
|
|
@@ -497,8 +649,11 @@ function filterNodes(node, predicate) {
|
|
|
497
649
|
*/
|
|
498
650
|
function mapNodes(node, mapper) {
|
|
499
651
|
const mapped = mapper(node);
|
|
500
|
-
|
|
501
|
-
|
|
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
|
+
}
|
|
502
657
|
return mapped;
|
|
503
658
|
}
|
|
504
659
|
/**
|
|
@@ -536,8 +691,7 @@ function getNodeDepth(node) {
|
|
|
536
691
|
*/
|
|
537
692
|
function countNodes(node) {
|
|
538
693
|
let count = 1;
|
|
539
|
-
|
|
540
|
-
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);
|
|
541
695
|
return count;
|
|
542
696
|
}
|
|
543
697
|
//#endregion
|
|
@@ -553,10 +707,7 @@ function traverseAST(node, visitor, parent = null) {
|
|
|
553
707
|
if (visitor.enter) {
|
|
554
708
|
if (visitor.enter(node, parent) === false) shouldTraverseChildren = false;
|
|
555
709
|
}
|
|
556
|
-
if (shouldTraverseChildren)
|
|
557
|
-
if ("children" in node && Array.isArray(node.children)) for (const child of node.children) traverseAST(child, visitor, node);
|
|
558
|
-
if ("attributes" in node && Array.isArray(node.attributes)) for (const attr of node.attributes) traverseAST(attr, visitor, node);
|
|
559
|
-
}
|
|
710
|
+
if (shouldTraverseChildren) for (const child of getChildNodes(node)) traverseAST(child, visitor, node);
|
|
560
711
|
if (visitor.leave) visitor.leave(node, parent);
|
|
561
712
|
}
|
|
562
713
|
/**
|
|
@@ -569,13 +720,19 @@ function walkAST(node, callback) {
|
|
|
569
720
|
}
|
|
570
721
|
//#endregion
|
|
571
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
|
+
]);
|
|
572
729
|
/**
|
|
573
730
|
* Check if a character is whitespace (space, newline, return, or tab)
|
|
574
731
|
* @param char - Character to check
|
|
575
732
|
* @returns True if character is whitespace
|
|
576
733
|
*/
|
|
577
734
|
function isWhitespace(char) {
|
|
578
|
-
return
|
|
735
|
+
return WHITESPACE_CHARS.has(char);
|
|
579
736
|
}
|
|
580
737
|
//#endregion
|
|
581
738
|
//#region src/utils/validateNode.ts
|
|
@@ -691,8 +848,7 @@ function findNodeByType(node, type) {
|
|
|
691
848
|
const results = [];
|
|
692
849
|
function traverse(currentNode) {
|
|
693
850
|
if (currentNode.type === type) results.push(currentNode);
|
|
694
|
-
|
|
695
|
-
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);
|
|
696
852
|
}
|
|
697
853
|
traverse(node);
|
|
698
854
|
return results;
|
|
@@ -706,14 +862,10 @@ function findNodeByType(node, type) {
|
|
|
706
862
|
function findFirstNodeByType(node, type) {
|
|
707
863
|
function traverse(currentNode) {
|
|
708
864
|
if (currentNode.type === type) return currentNode;
|
|
709
|
-
|
|
865
|
+
for (const child of getChildNodes(currentNode)) {
|
|
710
866
|
const result = traverse(child);
|
|
711
867
|
if (result) return result;
|
|
712
868
|
}
|
|
713
|
-
if ("attributes" in currentNode && Array.isArray(currentNode.attributes)) for (const attr of currentNode.attributes) {
|
|
714
|
-
const result = traverse(attr);
|
|
715
|
-
if (result) return result;
|
|
716
|
-
}
|
|
717
869
|
}
|
|
718
870
|
return traverse(node);
|
|
719
871
|
}
|
|
@@ -777,11 +929,7 @@ function calculateTokenLocation(source, range) {
|
|
|
777
929
|
* @returns Array containing [startPosition, endPosition + 1]
|
|
778
930
|
*/
|
|
779
931
|
function calculateTokenCharactersRange(state, options) {
|
|
780
|
-
|
|
781
|
-
let endPosition = 0;
|
|
782
|
-
if (options.keepBuffer) endPosition = state.sourceCode.index();
|
|
783
|
-
else endPosition = state.sourceCode.index() - state.decisionBuffer.length();
|
|
784
|
-
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];
|
|
785
933
|
}
|
|
786
934
|
//#endregion
|
|
787
935
|
//#region src/utils/calculateTokenPosition.ts
|
|
@@ -830,7 +978,7 @@ const dispatch$13 = createTokenDispatcher([
|
|
|
830
978
|
}
|
|
831
979
|
},
|
|
832
980
|
{
|
|
833
|
-
tokenType: new Set(["AttributeKey", "AttributeAssignment"]),
|
|
981
|
+
tokenType: /* @__PURE__ */ new Set(["AttributeKey", "AttributeAssignment"]),
|
|
834
982
|
handler(_, state) {
|
|
835
983
|
state.currentContext = {
|
|
836
984
|
parentRef: state.currentContext,
|
|
@@ -845,7 +993,7 @@ const dispatch$13 = createTokenDispatcher([
|
|
|
845
993
|
const tagName = state.currentNode.name;
|
|
846
994
|
state.currentNode.openEnd = createNodeFrom(token);
|
|
847
995
|
updateNodeEnd(state.currentNode, token);
|
|
848
|
-
if (tagName &&
|
|
996
|
+
if (tagName && state.currentNode.openEnd.value === "/>") {
|
|
849
997
|
state.currentNode.selfClosing = true;
|
|
850
998
|
state.currentNode = state.currentNode.parentRef;
|
|
851
999
|
state.currentContext = state.currentContext.parentRef;
|
|
@@ -876,6 +1024,9 @@ const dispatch$13 = createTokenDispatcher([
|
|
|
876
1024
|
state.caretPosition++;
|
|
877
1025
|
return state;
|
|
878
1026
|
});
|
|
1027
|
+
/**
|
|
1028
|
+
* Construct an element node after its opening tag has started.
|
|
1029
|
+
*/
|
|
879
1030
|
function construct$13(token, state) {
|
|
880
1031
|
return dispatch$13(token, state);
|
|
881
1032
|
}
|
|
@@ -894,6 +1045,7 @@ const dispatch$12 = createTokenDispatcher([
|
|
|
894
1045
|
tokenType: "CommentContent",
|
|
895
1046
|
handler(token, state) {
|
|
896
1047
|
state.currentNode.content = token.value;
|
|
1048
|
+
state.currentNode.value = token.value;
|
|
897
1049
|
state.caretPosition++;
|
|
898
1050
|
return state;
|
|
899
1051
|
}
|
|
@@ -909,6 +1061,9 @@ const dispatch$12 = createTokenDispatcher([
|
|
|
909
1061
|
}
|
|
910
1062
|
}
|
|
911
1063
|
]);
|
|
1064
|
+
/**
|
|
1065
|
+
* Construct a comment node from comment tokens.
|
|
1066
|
+
*/
|
|
912
1067
|
function construct$12(token, state) {
|
|
913
1068
|
return dispatch$12(token, state);
|
|
914
1069
|
}
|
|
@@ -936,7 +1091,7 @@ const dispatch$11 = createTokenDispatcher([
|
|
|
936
1091
|
}
|
|
937
1092
|
},
|
|
938
1093
|
{
|
|
939
|
-
tokenType: new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
|
|
1094
|
+
tokenType: /* @__PURE__ */ new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
|
|
940
1095
|
handler(_, state) {
|
|
941
1096
|
state.currentContext = {
|
|
942
1097
|
parentRef: state.currentContext,
|
|
@@ -949,6 +1104,9 @@ const dispatch$11 = createTokenDispatcher([
|
|
|
949
1104
|
state.caretPosition++;
|
|
950
1105
|
return state;
|
|
951
1106
|
});
|
|
1107
|
+
/**
|
|
1108
|
+
* Construct a doctype node from doctype tokens.
|
|
1109
|
+
*/
|
|
952
1110
|
function construct$11(token, state) {
|
|
953
1111
|
return dispatch$11(token, state);
|
|
954
1112
|
}
|
|
@@ -967,6 +1125,9 @@ const dispatch$10 = createTokenDispatcher([{
|
|
|
967
1125
|
state.caretPosition++;
|
|
968
1126
|
return state;
|
|
969
1127
|
});
|
|
1128
|
+
/**
|
|
1129
|
+
* Construct the normalized name for an element node.
|
|
1130
|
+
*/
|
|
970
1131
|
function construct$10(token, state) {
|
|
971
1132
|
return dispatch$10(token, state);
|
|
972
1133
|
}
|
|
@@ -975,7 +1136,7 @@ function construct$10(token, state) {
|
|
|
975
1136
|
var attribute_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$9 });
|
|
976
1137
|
const dispatch$9 = createTokenDispatcher([
|
|
977
1138
|
{
|
|
978
|
-
tokenType: new Set(["OpenTagEnd"]),
|
|
1139
|
+
tokenType: /* @__PURE__ */ new Set(["OpenTagEnd"]),
|
|
979
1140
|
handler(_, state) {
|
|
980
1141
|
state.currentContext = state.currentContext.parentRef;
|
|
981
1142
|
return state;
|
|
@@ -1013,6 +1174,9 @@ const dispatch$9 = createTokenDispatcher([
|
|
|
1013
1174
|
state.caretPosition++;
|
|
1014
1175
|
return state;
|
|
1015
1176
|
});
|
|
1177
|
+
/**
|
|
1178
|
+
* Construct an element attribute from attribute tokens.
|
|
1179
|
+
*/
|
|
1016
1180
|
function construct$9(token, state) {
|
|
1017
1181
|
return dispatch$9(token, state);
|
|
1018
1182
|
}
|
|
@@ -1020,7 +1184,7 @@ function construct$9(token, state) {
|
|
|
1020
1184
|
//#region src/constructor/handlers/attributes.ts
|
|
1021
1185
|
var attributes_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$8 });
|
|
1022
1186
|
const dispatch$8 = createTokenDispatcher([{
|
|
1023
|
-
tokenType: new Set(["AttributeKey", "AttributeAssignment"]),
|
|
1187
|
+
tokenType: /* @__PURE__ */ new Set(["AttributeKey", "AttributeAssignment"]),
|
|
1024
1188
|
handler(token, state) {
|
|
1025
1189
|
initAttributesIfNone(state.currentNode);
|
|
1026
1190
|
state.currentNode.attributes.push({
|
|
@@ -1035,7 +1199,7 @@ const dispatch$8 = createTokenDispatcher([{
|
|
|
1035
1199
|
return state;
|
|
1036
1200
|
}
|
|
1037
1201
|
}, {
|
|
1038
|
-
tokenType: new Set(["OpenTagEnd"]),
|
|
1202
|
+
tokenType: /* @__PURE__ */ new Set(["OpenTagEnd"]),
|
|
1039
1203
|
handler(_, state) {
|
|
1040
1204
|
state.currentContext = state.currentContext.parentRef;
|
|
1041
1205
|
return state;
|
|
@@ -1044,13 +1208,49 @@ const dispatch$8 = createTokenDispatcher([{
|
|
|
1044
1208
|
state.caretPosition++;
|
|
1045
1209
|
return state;
|
|
1046
1210
|
});
|
|
1211
|
+
/**
|
|
1212
|
+
* Construct the attributes collection for an element node.
|
|
1213
|
+
*/
|
|
1047
1214
|
function construct$8(token, state) {
|
|
1048
1215
|
return dispatch$8(token, state);
|
|
1049
1216
|
}
|
|
1050
1217
|
//#endregion
|
|
1051
1218
|
//#region src/constructor/handlers/tagContent.ts
|
|
1052
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
|
+
}
|
|
1053
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
|
+
},
|
|
1054
1254
|
{
|
|
1055
1255
|
tokenType: "OpenTagStart",
|
|
1056
1256
|
handler(token, state) {
|
|
@@ -1109,7 +1309,31 @@ const dispatch$7 = createTokenDispatcher([
|
|
|
1109
1309
|
{
|
|
1110
1310
|
tokenType: "CloseTag",
|
|
1111
1311
|
handler(token, state) {
|
|
1112
|
-
|
|
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
|
+
}
|
|
1113
1337
|
state.caretPosition++;
|
|
1114
1338
|
return state;
|
|
1115
1339
|
}
|
|
@@ -1140,6 +1364,9 @@ const dispatch$7 = createTokenDispatcher([
|
|
|
1140
1364
|
state.caretPosition++;
|
|
1141
1365
|
return state;
|
|
1142
1366
|
});
|
|
1367
|
+
/**
|
|
1368
|
+
* Construct document or element children from content tokens.
|
|
1369
|
+
*/
|
|
1143
1370
|
function construct$7(token, state) {
|
|
1144
1371
|
return dispatch$7(token, state);
|
|
1145
1372
|
}
|
|
@@ -1148,7 +1375,7 @@ function construct$7(token, state) {
|
|
|
1148
1375
|
var attributeValue_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$6 });
|
|
1149
1376
|
const dispatch$6 = createTokenDispatcher([
|
|
1150
1377
|
{
|
|
1151
|
-
tokenType: new Set([
|
|
1378
|
+
tokenType: /* @__PURE__ */ new Set([
|
|
1152
1379
|
"OpenTagEnd",
|
|
1153
1380
|
"AttributeKey",
|
|
1154
1381
|
"AttributeAssignment"
|
|
@@ -1193,6 +1420,9 @@ const dispatch$6 = createTokenDispatcher([
|
|
|
1193
1420
|
state.caretPosition++;
|
|
1194
1421
|
return state;
|
|
1195
1422
|
});
|
|
1423
|
+
/**
|
|
1424
|
+
* Construct an element attribute value from value tokens.
|
|
1425
|
+
*/
|
|
1196
1426
|
function construct$6(token, state) {
|
|
1197
1427
|
return dispatch$6(token, state);
|
|
1198
1428
|
}
|
|
@@ -1200,10 +1430,12 @@ function construct$6(token, state) {
|
|
|
1200
1430
|
//#region src/constructor/handlers/xmlDeclaration.ts
|
|
1201
1431
|
var xmlDeclaration_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$5 });
|
|
1202
1432
|
const dispatch$5 = createTokenDispatcher([{
|
|
1203
|
-
tokenType: "
|
|
1204
|
-
handler(
|
|
1205
|
-
state.
|
|
1206
|
-
|
|
1433
|
+
tokenType: "XMLDeclarationOpen",
|
|
1434
|
+
handler(_, state) {
|
|
1435
|
+
state.currentContext = {
|
|
1436
|
+
parentRef: state.currentContext,
|
|
1437
|
+
type: "XMLDeclarationAttributes"
|
|
1438
|
+
};
|
|
1207
1439
|
state.caretPosition++;
|
|
1208
1440
|
return state;
|
|
1209
1441
|
}
|
|
@@ -1211,6 +1443,9 @@ const dispatch$5 = createTokenDispatcher([{
|
|
|
1211
1443
|
state.caretPosition++;
|
|
1212
1444
|
return state;
|
|
1213
1445
|
});
|
|
1446
|
+
/**
|
|
1447
|
+
* Construct an XML declaration node after its opening token.
|
|
1448
|
+
*/
|
|
1214
1449
|
function construct$5(token, state) {
|
|
1215
1450
|
return dispatch$5(token, state);
|
|
1216
1451
|
}
|
|
@@ -1266,6 +1501,9 @@ const dispatch$4 = createTokenDispatcher([
|
|
|
1266
1501
|
state.caretPosition++;
|
|
1267
1502
|
return state;
|
|
1268
1503
|
});
|
|
1504
|
+
/**
|
|
1505
|
+
* Construct a doctype attribute from value and wrapper tokens.
|
|
1506
|
+
*/
|
|
1269
1507
|
function construct$4(token, state) {
|
|
1270
1508
|
return dispatch$4(token, state);
|
|
1271
1509
|
}
|
|
@@ -1279,7 +1517,7 @@ const dispatch$3 = createTokenDispatcher([{
|
|
|
1279
1517
|
return state;
|
|
1280
1518
|
}
|
|
1281
1519
|
}, {
|
|
1282
|
-
tokenType: new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
|
|
1520
|
+
tokenType: /* @__PURE__ */ new Set(["DoctypeAttributeWrapperStart", "DoctypeAttributeValue"]),
|
|
1283
1521
|
handler(token, state) {
|
|
1284
1522
|
initAttributesIfNone(state.currentNode);
|
|
1285
1523
|
state.currentNode.attributes.push({
|
|
@@ -1297,6 +1535,9 @@ const dispatch$3 = createTokenDispatcher([{
|
|
|
1297
1535
|
state.caretPosition++;
|
|
1298
1536
|
return state;
|
|
1299
1537
|
});
|
|
1538
|
+
/**
|
|
1539
|
+
* Construct the attributes collection for a doctype node.
|
|
1540
|
+
*/
|
|
1300
1541
|
function construct$3(token, state) {
|
|
1301
1542
|
return dispatch$3(token, state);
|
|
1302
1543
|
}
|
|
@@ -1305,40 +1546,36 @@ function construct$3(token, state) {
|
|
|
1305
1546
|
var xmlDeclarationAttribute_exports = /* @__PURE__ */ __exportAll({ construct: () => construct$2 });
|
|
1306
1547
|
const dispatch$2 = createTokenDispatcher([
|
|
1307
1548
|
{
|
|
1308
|
-
tokenType: new Set([
|
|
1309
|
-
"OpenTagEnd",
|
|
1310
|
-
"AttributeKey",
|
|
1311
|
-
"AttributeAssignment"
|
|
1312
|
-
]),
|
|
1549
|
+
tokenType: /* @__PURE__ */ new Set(["XMLDeclarationClose"]),
|
|
1313
1550
|
handler(_, state) {
|
|
1314
1551
|
state.currentContext = state.currentContext.parentRef;
|
|
1315
1552
|
return state;
|
|
1316
1553
|
}
|
|
1317
1554
|
},
|
|
1318
1555
|
{
|
|
1319
|
-
tokenType: "
|
|
1556
|
+
tokenType: "XMLDeclarationAttributeKey",
|
|
1320
1557
|
handler(token, state) {
|
|
1321
1558
|
const attribute = getLastAttribute(state);
|
|
1322
|
-
attribute.value
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
},
|
|
1328
|
-
{
|
|
1329
|
-
tokenType: "AttributeValueWrapperStart",
|
|
1330
|
-
handler(token, state) {
|
|
1331
|
-
const attribute = getLastAttribute(state);
|
|
1332
|
-
attribute.quoteChar = token.value;
|
|
1333
|
-
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);
|
|
1334
1564
|
state.caretPosition++;
|
|
1335
1565
|
return state;
|
|
1336
1566
|
}
|
|
1337
1567
|
},
|
|
1338
1568
|
{
|
|
1339
|
-
tokenType: "
|
|
1340
|
-
handler(
|
|
1341
|
-
|
|
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
|
+
};
|
|
1342
1579
|
state.caretPosition++;
|
|
1343
1580
|
return state;
|
|
1344
1581
|
}
|
|
@@ -1347,6 +1584,9 @@ const dispatch$2 = createTokenDispatcher([
|
|
|
1347
1584
|
state.caretPosition++;
|
|
1348
1585
|
return state;
|
|
1349
1586
|
});
|
|
1587
|
+
/**
|
|
1588
|
+
* Construct an XML declaration attribute key and value transition.
|
|
1589
|
+
*/
|
|
1350
1590
|
function construct$2(token, state) {
|
|
1351
1591
|
return dispatch$2(token, state);
|
|
1352
1592
|
}
|
|
@@ -1354,17 +1594,20 @@ function construct$2(token, state) {
|
|
|
1354
1594
|
//#region src/constructor/handlers/xmlDeclarationAttributes.ts
|
|
1355
1595
|
var xmlDeclarationAttributes_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct$1 });
|
|
1356
1596
|
const dispatch$1 = createTokenDispatcher([{
|
|
1357
|
-
tokenType: "
|
|
1358
|
-
handler(
|
|
1597
|
+
tokenType: "XMLDeclarationClose",
|
|
1598
|
+
handler(token, state) {
|
|
1599
|
+
updateNodeEnd(state.currentNode, token);
|
|
1600
|
+
state.currentNode = state.currentNode.parentRef;
|
|
1359
1601
|
state.currentContext = state.currentContext.parentRef;
|
|
1602
|
+
state.caretPosition++;
|
|
1360
1603
|
return state;
|
|
1361
1604
|
}
|
|
1362
1605
|
}, {
|
|
1363
|
-
tokenType: new Set(["
|
|
1606
|
+
tokenType: /* @__PURE__ */ new Set(["XMLDeclarationAttributeKey", "XMLDeclarationAttributeAssignment"]),
|
|
1364
1607
|
handler(token, state) {
|
|
1365
1608
|
initAttributesIfNone(state.currentNode);
|
|
1366
1609
|
state.currentNode.attributes.push({
|
|
1367
|
-
type: "
|
|
1610
|
+
type: "XMLDeclarationAttribute",
|
|
1368
1611
|
range: cloneRange(token.range),
|
|
1369
1612
|
loc: cloneLocation(token.loc)
|
|
1370
1613
|
});
|
|
@@ -1378,6 +1621,9 @@ const dispatch$1 = createTokenDispatcher([{
|
|
|
1378
1621
|
state.caretPosition++;
|
|
1379
1622
|
return state;
|
|
1380
1623
|
});
|
|
1624
|
+
/**
|
|
1625
|
+
* Construct the attributes collection for an XML declaration node.
|
|
1626
|
+
*/
|
|
1381
1627
|
function construct$1(token, state) {
|
|
1382
1628
|
return dispatch$1(token, state);
|
|
1383
1629
|
}
|
|
@@ -1386,14 +1632,14 @@ function construct$1(token, state) {
|
|
|
1386
1632
|
var xmlDeclarationAttributeValue_exports$1 = /* @__PURE__ */ __exportAll({ construct: () => construct });
|
|
1387
1633
|
const dispatch = createTokenDispatcher([
|
|
1388
1634
|
{
|
|
1389
|
-
tokenType: "
|
|
1635
|
+
tokenType: "XMLDeclarationClose",
|
|
1390
1636
|
handler(_, state) {
|
|
1391
1637
|
state.currentContext = state.currentContext.parentRef;
|
|
1392
1638
|
return state;
|
|
1393
1639
|
}
|
|
1394
1640
|
},
|
|
1395
1641
|
{
|
|
1396
|
-
tokenType: "
|
|
1642
|
+
tokenType: "XMLDeclarationAttributeValue",
|
|
1397
1643
|
handler(token, state) {
|
|
1398
1644
|
const attribute = getLastAttribute(state);
|
|
1399
1645
|
if (attribute.value !== void 0) {
|
|
@@ -1407,7 +1653,7 @@ const dispatch = createTokenDispatcher([
|
|
|
1407
1653
|
}
|
|
1408
1654
|
},
|
|
1409
1655
|
{
|
|
1410
|
-
tokenType: "
|
|
1656
|
+
tokenType: "XMLDeclarationAttributeValueWrapperStart",
|
|
1411
1657
|
handler(token, state) {
|
|
1412
1658
|
const attribute = getLastAttribute(state);
|
|
1413
1659
|
if (attribute.value !== void 0) {
|
|
@@ -1421,7 +1667,7 @@ const dispatch = createTokenDispatcher([
|
|
|
1421
1667
|
}
|
|
1422
1668
|
},
|
|
1423
1669
|
{
|
|
1424
|
-
tokenType: "
|
|
1670
|
+
tokenType: "XMLDeclarationAttributeValueWrapperEnd",
|
|
1425
1671
|
handler(token, state) {
|
|
1426
1672
|
updateNodeEnd(getLastAttribute(state), token);
|
|
1427
1673
|
state.currentContext = state.currentContext.parentRef;
|
|
@@ -1433,6 +1679,9 @@ const dispatch = createTokenDispatcher([
|
|
|
1433
1679
|
state.caretPosition++;
|
|
1434
1680
|
return state;
|
|
1435
1681
|
});
|
|
1682
|
+
/**
|
|
1683
|
+
* Construct an XML declaration attribute value from value tokens.
|
|
1684
|
+
*/
|
|
1436
1685
|
function construct(token, state) {
|
|
1437
1686
|
return dispatch(token, state);
|
|
1438
1687
|
}
|
|
@@ -1475,6 +1724,24 @@ function processTokens(tokens, state, positionOffset) {
|
|
|
1475
1724
|
}
|
|
1476
1725
|
return state;
|
|
1477
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
|
+
*/
|
|
1478
1745
|
function constructTree(tokens) {
|
|
1479
1746
|
const rootContext = {
|
|
1480
1747
|
type: "TagContent",
|
|
@@ -1499,48 +1766,84 @@ function constructTree(tokens) {
|
|
|
1499
1766
|
caretPosition: 0,
|
|
1500
1767
|
currentContext: rootContext,
|
|
1501
1768
|
currentNode: rootNode,
|
|
1769
|
+
errorHandler: new ErrorHandler(),
|
|
1502
1770
|
rootNode
|
|
1503
1771
|
};
|
|
1504
1772
|
const positionOffset = state.caretPosition;
|
|
1505
|
-
processTokens(tokens, state, positionOffset);
|
|
1773
|
+
reportUnclosedNodes(processTokens(tokens, state, positionOffset));
|
|
1506
1774
|
return {
|
|
1507
1775
|
state,
|
|
1508
|
-
ast: state.rootNode
|
|
1776
|
+
ast: state.rootNode,
|
|
1777
|
+
errors: state.errorHandler.getErrors(),
|
|
1778
|
+
warnings: state.errorHandler.getWarnings()
|
|
1509
1779
|
};
|
|
1510
1780
|
}
|
|
1511
1781
|
//#endregion
|
|
1512
1782
|
//#region src/tokenizer/charsBuffer.ts
|
|
1783
|
+
/**
|
|
1784
|
+
* Mutable buffer used by tokenizer handlers while deciding token boundaries.
|
|
1785
|
+
*/
|
|
1513
1786
|
var CharsBuffer = class {
|
|
1514
1787
|
charsBuffer = [];
|
|
1788
|
+
/**
|
|
1789
|
+
* Append a character wrapper to the buffer.
|
|
1790
|
+
*/
|
|
1515
1791
|
concat(chars) {
|
|
1516
1792
|
const theLast = this.last();
|
|
1517
|
-
if (theLast) theLast.
|
|
1793
|
+
if (theLast) theLast.append(chars);
|
|
1518
1794
|
else this.charsBuffer.push(chars);
|
|
1519
1795
|
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Append another buffer without merging its entries.
|
|
1798
|
+
*/
|
|
1520
1799
|
concatBuffer(buffer) {
|
|
1521
1800
|
this.charsBuffer.push(...buffer.charsBuffer);
|
|
1522
1801
|
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Get the total buffered character length.
|
|
1804
|
+
*/
|
|
1523
1805
|
length() {
|
|
1524
1806
|
return this.charsBuffer.map((chars) => chars.length()).reduce((a, b) => a + b, 0);
|
|
1525
1807
|
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Clear all buffered characters.
|
|
1810
|
+
*/
|
|
1526
1811
|
clear() {
|
|
1527
1812
|
this.charsBuffer = [];
|
|
1528
1813
|
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Get the concatenated buffered string.
|
|
1816
|
+
*/
|
|
1529
1817
|
value() {
|
|
1530
1818
|
return this.charsBuffer.map((chars) => chars.value).join("");
|
|
1531
1819
|
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Get the last buffered character wrapper.
|
|
1822
|
+
*/
|
|
1532
1823
|
last() {
|
|
1533
1824
|
return last(this.charsBuffer);
|
|
1534
1825
|
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Get the first buffered character wrapper.
|
|
1828
|
+
*/
|
|
1535
1829
|
first() {
|
|
1536
1830
|
return first(this.charsBuffer);
|
|
1537
1831
|
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Remove the last buffered entry.
|
|
1834
|
+
*/
|
|
1538
1835
|
removeLast() {
|
|
1539
1836
|
this.charsBuffer.splice(-1, 1);
|
|
1540
1837
|
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Remove the first buffered entry.
|
|
1840
|
+
*/
|
|
1541
1841
|
removeFirst() {
|
|
1542
1842
|
this.charsBuffer.splice(0, 1);
|
|
1543
1843
|
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Replace this buffer with a copy of another buffer.
|
|
1846
|
+
*/
|
|
1544
1847
|
replace(other) {
|
|
1545
1848
|
this.charsBuffer = [...other.charsBuffer];
|
|
1546
1849
|
}
|
|
@@ -1551,7 +1854,7 @@ var data_exports = /* @__PURE__ */ __exportAll({
|
|
|
1551
1854
|
handleContentEnd: () => handleContentEnd,
|
|
1552
1855
|
parse: () => parse$22
|
|
1553
1856
|
});
|
|
1554
|
-
const INCOMPLETE_DOCTYPE_CHARS = new Set([
|
|
1857
|
+
const INCOMPLETE_DOCTYPE_CHARS = /* @__PURE__ */ new Set([
|
|
1555
1858
|
"<!",
|
|
1556
1859
|
"<!D",
|
|
1557
1860
|
"<!DO",
|
|
@@ -1560,6 +1863,17 @@ const INCOMPLETE_DOCTYPE_CHARS = new Set([
|
|
|
1560
1863
|
"<!DOCTY",
|
|
1561
1864
|
"<!DOCTYP"
|
|
1562
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
|
+
]);
|
|
1563
1877
|
function generateTextToken(state) {
|
|
1564
1878
|
const position = calculateTokenPosition(state, { keepBuffer: false });
|
|
1565
1879
|
return {
|
|
@@ -1609,17 +1923,28 @@ function parseDoctypeOpen(state) {
|
|
|
1609
1923
|
}
|
|
1610
1924
|
function parseXMLDeclarationOpen(state) {
|
|
1611
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
|
+
});
|
|
1612
1933
|
state.accumulatedContent.clear();
|
|
1613
1934
|
state.decisionBuffer.clear();
|
|
1614
1935
|
state.currentContext = "XMLDeclarationAttributes";
|
|
1615
1936
|
state.sourceCode.next();
|
|
1616
1937
|
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Tokenize top-level text and dispatch into markup-specific contexts.
|
|
1940
|
+
*/
|
|
1617
1941
|
function parse$22(chars, state) {
|
|
1618
1942
|
const value = chars.value();
|
|
1619
1943
|
if (value === "<?xml") return parseXMLDeclarationOpen(state);
|
|
1944
|
+
if (INCOMPLETE_XML_DECLARATION_START_CHARS.has(value)) return state.sourceCode.next();
|
|
1620
1945
|
if (RE_OPEN_TAG_START.test(value)) return parseOpeningCornerBraceWithText(state);
|
|
1621
1946
|
if (value === "</") return parseOpeningCornerBraceWithSlash(state);
|
|
1622
|
-
if (
|
|
1947
|
+
if (INCOMPLETE_COMMENT_START_CHARS.has(value)) return state.sourceCode.next();
|
|
1623
1948
|
if (value === "<!--") return parseCommentOpen(state);
|
|
1624
1949
|
if (isIncompleteDoctype(value)) return state.sourceCode.next();
|
|
1625
1950
|
if (value.toUpperCase() === "<!DOCTYPE") return parseDoctypeOpen(state);
|
|
@@ -1627,6 +1952,9 @@ function parse$22(chars, state) {
|
|
|
1627
1952
|
state.decisionBuffer.clear();
|
|
1628
1953
|
state.sourceCode.next();
|
|
1629
1954
|
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Flush buffered text when the source ends in data context.
|
|
1957
|
+
*/
|
|
1630
1958
|
function handleContentEnd(state) {
|
|
1631
1959
|
const textContent = state.accumulatedContent.value() + state.decisionBuffer.value();
|
|
1632
1960
|
if (textContent.length !== 0) {
|
|
@@ -1655,6 +1983,9 @@ function parseClosingCornerBrace$5(state) {
|
|
|
1655
1983
|
state.currentContext = "Data";
|
|
1656
1984
|
state.sourceCode.next();
|
|
1657
1985
|
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Tokenize a closing tag.
|
|
1988
|
+
*/
|
|
1658
1989
|
function parse$21(chars, state) {
|
|
1659
1990
|
if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$5(state);
|
|
1660
1991
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -1672,7 +2003,7 @@ function parseTagEnd$3(state) {
|
|
|
1672
2003
|
state.contextParams["OpenTagEnd"] = { tagName };
|
|
1673
2004
|
state.contextParams["Attributes"] = void 0;
|
|
1674
2005
|
}
|
|
1675
|
-
function parseEqual(state) {
|
|
2006
|
+
function parseEqual$1(state) {
|
|
1676
2007
|
const position = calculateTokenPosition(state, { keepBuffer: true });
|
|
1677
2008
|
state.tokens.push({
|
|
1678
2009
|
type: "AttributeAssignment",
|
|
@@ -1685,17 +2016,20 @@ function parseEqual(state) {
|
|
|
1685
2016
|
state.currentContext = "AttributeValue";
|
|
1686
2017
|
state.sourceCode.next();
|
|
1687
2018
|
}
|
|
1688
|
-
function parseNoneWhitespace(chars, state) {
|
|
2019
|
+
function parseNoneWhitespace$1(chars, state) {
|
|
1689
2020
|
state.accumulatedContent.replace(state.decisionBuffer);
|
|
1690
2021
|
state.currentContext = "AttributeKey";
|
|
1691
2022
|
state.decisionBuffer.clear();
|
|
1692
2023
|
state.sourceCode.next();
|
|
1693
2024
|
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Tokenize whitespace, assignments, and attribute starts inside an open tag.
|
|
2027
|
+
*/
|
|
1694
2028
|
function parse$20(chars, state) {
|
|
1695
2029
|
const value = chars.value();
|
|
1696
2030
|
if (value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseTagEnd$3(state);
|
|
1697
|
-
if (value === SPECIAL_CHAR.equal) return parseEqual(state);
|
|
1698
|
-
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);
|
|
1699
2033
|
state.decisionBuffer.clear();
|
|
1700
2034
|
state.sourceCode.next();
|
|
1701
2035
|
}
|
|
@@ -1716,6 +2050,9 @@ function parseClosingCornerBrace$4(state) {
|
|
|
1716
2050
|
state.sourceCode.next();
|
|
1717
2051
|
state.contextParams["OpenTagEnd"] = void 0;
|
|
1718
2052
|
}
|
|
2053
|
+
/**
|
|
2054
|
+
* Tokenize the end of an opening tag.
|
|
2055
|
+
*/
|
|
1719
2056
|
function parse$19(chars, state) {
|
|
1720
2057
|
if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$4(state);
|
|
1721
2058
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -1746,6 +2083,9 @@ function parseClosingCornerBrace$3(state) {
|
|
|
1746
2083
|
state.decisionBuffer.clear();
|
|
1747
2084
|
state.currentContext = "DoctypeClose";
|
|
1748
2085
|
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Tokenize the opening delimiter of a doctype declaration.
|
|
2088
|
+
*/
|
|
1749
2089
|
function parse$18(chars, state) {
|
|
1750
2090
|
const value = chars.value();
|
|
1751
2091
|
if (isWhitespace(value)) return parseWhitespace$1(state);
|
|
@@ -1772,6 +2112,9 @@ function parseKeyEnd$1(state) {
|
|
|
1772
2112
|
state.decisionBuffer.clear();
|
|
1773
2113
|
state.currentContext = "Attributes";
|
|
1774
2114
|
}
|
|
2115
|
+
/**
|
|
2116
|
+
* Tokenize an element attribute key.
|
|
2117
|
+
*/
|
|
1775
2118
|
function parse$17(chars, state) {
|
|
1776
2119
|
if (isKeyBreak$1(chars)) return parseKeyEnd$1(state);
|
|
1777
2120
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -1781,6 +2124,9 @@ function parse$17(chars, state) {
|
|
|
1781
2124
|
//#endregion
|
|
1782
2125
|
//#region src/tokenizer/handlers/doctypeClose.ts
|
|
1783
2126
|
var doctypeClose_exports = /* @__PURE__ */ __exportAll({ parse: () => parse$16 });
|
|
2127
|
+
/**
|
|
2128
|
+
* Tokenize the closing delimiter of a doctype declaration.
|
|
2129
|
+
*/
|
|
1784
2130
|
function parse$16(chars, state) {
|
|
1785
2131
|
const position = calculateTokenPosition(state, { keepBuffer: true });
|
|
1786
2132
|
state.tokens.push({
|
|
@@ -1826,6 +2172,9 @@ function parseWhitespace(state) {
|
|
|
1826
2172
|
state.contextParams["Attributes"] = { tagName };
|
|
1827
2173
|
state.sourceCode.next();
|
|
1828
2174
|
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Tokenize the start of an opening tag.
|
|
2177
|
+
*/
|
|
1829
2178
|
function parse$15(chars, state) {
|
|
1830
2179
|
const value = chars.value();
|
|
1831
2180
|
if (value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseTagEnd$2(state);
|
|
@@ -1863,6 +2212,9 @@ function parseBare$2(state) {
|
|
|
1863
2212
|
state.currentContext = "AttributeValueBare";
|
|
1864
2213
|
state.sourceCode.next();
|
|
1865
2214
|
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Tokenize the start of an element attribute value.
|
|
2217
|
+
*/
|
|
1866
2218
|
function parse$14(chars, state) {
|
|
1867
2219
|
const value = chars.value();
|
|
1868
2220
|
if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$5(state);
|
|
@@ -1894,6 +2246,9 @@ function parseCommentClose(state) {
|
|
|
1894
2246
|
state.currentContext = "Data";
|
|
1895
2247
|
state.sourceCode.next();
|
|
1896
2248
|
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Tokenize comment content until the closing delimiter.
|
|
2251
|
+
*/
|
|
1897
2252
|
function parse$13(chars, state) {
|
|
1898
2253
|
const value = chars.value();
|
|
1899
2254
|
if (value === SPECIAL_CHAR.hyphen || value === "--") return state.sourceCode.next();
|
|
@@ -1931,6 +2286,9 @@ function parseBare$1(state) {
|
|
|
1931
2286
|
state.currentContext = "DoctypeAttributeBare";
|
|
1932
2287
|
state.sourceCode.next();
|
|
1933
2288
|
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Tokenize doctype attributes and the doctype close transition.
|
|
2291
|
+
*/
|
|
1934
2292
|
function parse$12(chars, state) {
|
|
1935
2293
|
const value = chars.value();
|
|
1936
2294
|
if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$4(state);
|
|
@@ -1954,6 +2312,9 @@ function parseValueEnd(state) {
|
|
|
1954
2312
|
state.decisionBuffer.clear();
|
|
1955
2313
|
state.currentContext = "Attributes";
|
|
1956
2314
|
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Tokenize an unquoted element attribute value.
|
|
2317
|
+
*/
|
|
1957
2318
|
function parse$11(chars, state) {
|
|
1958
2319
|
const value = chars.value();
|
|
1959
2320
|
if (isWhitespace(value) || value === SPECIAL_CHAR.closingCorner || value === SPECIAL_CHAR.slash) return parseValueEnd(state);
|
|
@@ -1978,6 +2339,9 @@ function parseClosingCornerBrace$1(state) {
|
|
|
1978
2339
|
state.sourceCode.next();
|
|
1979
2340
|
state.contextParams["OpenTagEnd"] = void 0;
|
|
1980
2341
|
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Tokenize the opening delimiter of an XML declaration.
|
|
2344
|
+
*/
|
|
1981
2345
|
function parse$10(chars, state) {
|
|
1982
2346
|
if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace$1(state);
|
|
1983
2347
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -2001,6 +2365,9 @@ function parseClosingCornerBrace(state) {
|
|
|
2001
2365
|
state.sourceCode.next();
|
|
2002
2366
|
state.contextParams["OpenTagEnd"] = void 0;
|
|
2003
2367
|
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Tokenize the closing delimiter of an XML declaration.
|
|
2370
|
+
*/
|
|
2004
2371
|
function parse$9(chars, state) {
|
|
2005
2372
|
if (chars.value() === SPECIAL_CHAR.closingCorner) return parseClosingCornerBrace(state);
|
|
2006
2373
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -2022,6 +2389,9 @@ function parseAttributeEnd(state) {
|
|
|
2022
2389
|
state.decisionBuffer.clear();
|
|
2023
2390
|
state.currentContext = "DoctypeAttributes";
|
|
2024
2391
|
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Tokenize an unquoted doctype attribute value.
|
|
2394
|
+
*/
|
|
2025
2395
|
function parse$8(chars, state) {
|
|
2026
2396
|
const value = chars.value();
|
|
2027
2397
|
if (isWhitespace(value) || value === SPECIAL_CHAR.closingCorner) return parseAttributeEnd(state);
|
|
@@ -2054,6 +2424,9 @@ function parseWrapper$3(state) {
|
|
|
2054
2424
|
state.sourceCode.next();
|
|
2055
2425
|
state.contextParams["AttributeValueWrapped"] = void 0;
|
|
2056
2426
|
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Tokenize a quoted element attribute value.
|
|
2429
|
+
*/
|
|
2057
2430
|
function parse$7(chars, state) {
|
|
2058
2431
|
const wrapperChar = state.contextParams["AttributeValueWrapped"]?.wrapper;
|
|
2059
2432
|
if (chars.value() === wrapperChar) return parseWrapper$3(state);
|
|
@@ -2086,6 +2459,9 @@ function parseWrapper$2(state) {
|
|
|
2086
2459
|
state.sourceCode.next();
|
|
2087
2460
|
state.contextParams["DoctypeAttributeWrapped"] = void 0;
|
|
2088
2461
|
}
|
|
2462
|
+
/**
|
|
2463
|
+
* Tokenize a quoted doctype attribute value.
|
|
2464
|
+
*/
|
|
2089
2465
|
function parse$6(chars, state) {
|
|
2090
2466
|
if (chars.value() === state.contextParams["DoctypeAttributeWrapped"]?.wrapper) return parseWrapper$2(state);
|
|
2091
2467
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -2109,11 +2485,34 @@ function parseXMLDeclarationClose(state) {
|
|
|
2109
2485
|
state.currentContext = "Data";
|
|
2110
2486
|
state.sourceCode.next();
|
|
2111
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
|
+
*/
|
|
2112
2510
|
function parse$5(chars, state) {
|
|
2113
2511
|
const value = chars.value();
|
|
2114
|
-
if (value === SPECIAL_CHAR.question
|
|
2512
|
+
if (value === SPECIAL_CHAR.question) return state.sourceCode.next();
|
|
2115
2513
|
if (value === "?>") return parseXMLDeclarationClose(state);
|
|
2116
|
-
|
|
2514
|
+
if (value === SPECIAL_CHAR.equal) return parseEqual(state);
|
|
2515
|
+
if (!isWhitespace(value)) return parseNoneWhitespace(state);
|
|
2117
2516
|
state.decisionBuffer.clear();
|
|
2118
2517
|
state.sourceCode.next();
|
|
2119
2518
|
}
|
|
@@ -2136,6 +2535,9 @@ function parseKeyEnd(state) {
|
|
|
2136
2535
|
state.decisionBuffer.clear();
|
|
2137
2536
|
state.currentContext = "XMLDeclarationAttributes";
|
|
2138
2537
|
}
|
|
2538
|
+
/**
|
|
2539
|
+
* Tokenize an XML declaration attribute key.
|
|
2540
|
+
*/
|
|
2139
2541
|
function parse$4(chars, state) {
|
|
2140
2542
|
if (isKeyBreak(chars)) return parseKeyEnd(state);
|
|
2141
2543
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
@@ -2149,28 +2551,38 @@ function parseWrapper$1(state) {
|
|
|
2149
2551
|
const wrapper = state.decisionBuffer.value();
|
|
2150
2552
|
const range = [state.sourceCode.index(), state.sourceCode.index() + 1];
|
|
2151
2553
|
state.tokens.push({
|
|
2152
|
-
type: "
|
|
2554
|
+
type: "XMLDeclarationAttributeValueWrapperStart",
|
|
2153
2555
|
value: wrapper,
|
|
2154
2556
|
range,
|
|
2155
2557
|
loc: state.sourceCode.getLocationOf(range)
|
|
2156
2558
|
});
|
|
2157
2559
|
state.accumulatedContent.clear();
|
|
2158
2560
|
state.decisionBuffer.clear();
|
|
2159
|
-
state.currentContext = "
|
|
2160
|
-
state.contextParams["
|
|
2561
|
+
state.currentContext = "XMLDeclarationAttributeValueWrapped";
|
|
2562
|
+
state.contextParams["XMLDeclarationAttributeValueWrapped"] = { wrapper };
|
|
2161
2563
|
state.sourceCode.next();
|
|
2162
2564
|
}
|
|
2163
2565
|
function parseTagEnd(state) {
|
|
2164
2566
|
state.accumulatedContent.clear();
|
|
2165
2567
|
state.decisionBuffer.clear();
|
|
2166
|
-
state.currentContext = "
|
|
2568
|
+
state.currentContext = "XMLDeclarationAttributes";
|
|
2167
2569
|
}
|
|
2168
2570
|
function parseBare(state) {
|
|
2169
|
-
state.
|
|
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();
|
|
2170
2579
|
state.decisionBuffer.clear();
|
|
2171
|
-
state.currentContext = "
|
|
2580
|
+
state.currentContext = "XMLDeclarationAttributes";
|
|
2172
2581
|
state.sourceCode.next();
|
|
2173
2582
|
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Tokenize the start of an XML declaration attribute value.
|
|
2585
|
+
*/
|
|
2174
2586
|
function parse$3(chars, state) {
|
|
2175
2587
|
const value = chars.value();
|
|
2176
2588
|
if (value === SPECIAL_CHAR.doubleQuote || value === SPECIAL_CHAR.singleQuote) return parseWrapper$1(state);
|
|
@@ -2186,26 +2598,29 @@ function parseWrapper(state) {
|
|
|
2186
2598
|
const position = calculateTokenPosition(state, { keepBuffer: false });
|
|
2187
2599
|
const endWrapperPosition = position.range[1];
|
|
2188
2600
|
state.tokens.push({
|
|
2189
|
-
type: "
|
|
2601
|
+
type: "XMLDeclarationAttributeValue",
|
|
2190
2602
|
value: state.accumulatedContent.value(),
|
|
2191
2603
|
range: position.range,
|
|
2192
2604
|
loc: position.loc
|
|
2193
2605
|
});
|
|
2194
2606
|
const range = [endWrapperPosition, endWrapperPosition + 1];
|
|
2195
2607
|
state.tokens.push({
|
|
2196
|
-
type: "
|
|
2608
|
+
type: "XMLDeclarationAttributeValueWrapperEnd",
|
|
2197
2609
|
value: state.decisionBuffer.value(),
|
|
2198
2610
|
range,
|
|
2199
2611
|
loc: state.sourceCode.getLocationOf(range)
|
|
2200
2612
|
});
|
|
2201
2613
|
state.accumulatedContent.clear();
|
|
2202
2614
|
state.decisionBuffer.clear();
|
|
2203
|
-
state.currentContext = "
|
|
2615
|
+
state.currentContext = "XMLDeclarationAttributes";
|
|
2204
2616
|
state.sourceCode.next();
|
|
2205
|
-
state.contextParams["
|
|
2617
|
+
state.contextParams["XMLDeclarationAttributeValueWrapped"] = void 0;
|
|
2206
2618
|
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Tokenize a quoted XML declaration attribute value.
|
|
2621
|
+
*/
|
|
2207
2622
|
function parse$2(chars, state) {
|
|
2208
|
-
const wrapperChar = state.contextParams["
|
|
2623
|
+
const wrapperChar = state.contextParams["XMLDeclarationAttributeValueWrapped"]?.wrapper;
|
|
2209
2624
|
if (chars.value() === wrapperChar) return parseWrapper(state);
|
|
2210
2625
|
state.accumulatedContent.concatBuffer(state.decisionBuffer);
|
|
2211
2626
|
state.decisionBuffer.clear();
|
|
@@ -2213,9 +2628,15 @@ function parse$2(chars, state) {
|
|
|
2213
2628
|
}
|
|
2214
2629
|
//#endregion
|
|
2215
2630
|
//#region src/tokenizer/handlers/index.ts
|
|
2631
|
+
/**
|
|
2632
|
+
* Tokenizer handler for contexts that intentionally consume no content.
|
|
2633
|
+
*/
|
|
2216
2634
|
const noop = { parse: () => {} };
|
|
2217
2635
|
//#endregion
|
|
2218
2636
|
//#region src/tokenizer/chars.ts
|
|
2637
|
+
/**
|
|
2638
|
+
* Buffered source characters with their source range.
|
|
2639
|
+
*/
|
|
2219
2640
|
var Chars = class {
|
|
2220
2641
|
value;
|
|
2221
2642
|
range;
|
|
@@ -2223,19 +2644,31 @@ var Chars = class {
|
|
|
2223
2644
|
this.value = value;
|
|
2224
2645
|
this.range = range;
|
|
2225
2646
|
}
|
|
2226
|
-
|
|
2647
|
+
/**
|
|
2648
|
+
* Append adjacent characters into this range.
|
|
2649
|
+
*/
|
|
2650
|
+
append(chars) {
|
|
2227
2651
|
this.value += chars.value;
|
|
2228
2652
|
this.range[1] = chars.range[1];
|
|
2229
2653
|
}
|
|
2654
|
+
/**
|
|
2655
|
+
* Compare the buffered value with raw text.
|
|
2656
|
+
*/
|
|
2230
2657
|
equals(chars) {
|
|
2231
2658
|
return this.value === chars;
|
|
2232
2659
|
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Get the buffered string length.
|
|
2662
|
+
*/
|
|
2233
2663
|
length() {
|
|
2234
2664
|
return this.value.length;
|
|
2235
2665
|
}
|
|
2236
2666
|
};
|
|
2237
2667
|
//#endregion
|
|
2238
2668
|
//#region src/tokenizer/sourceCode.ts
|
|
2669
|
+
/**
|
|
2670
|
+
* Cursor-based view over source text for tokenizer handlers.
|
|
2671
|
+
*/
|
|
2239
2672
|
var SourceCode = class {
|
|
2240
2673
|
source;
|
|
2241
2674
|
charsList;
|
|
@@ -2244,24 +2677,42 @@ var SourceCode = class {
|
|
|
2244
2677
|
this.source = source;
|
|
2245
2678
|
this.charsList = this.createCharsList();
|
|
2246
2679
|
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Convert a character range to source locations.
|
|
2682
|
+
*/
|
|
2247
2683
|
getLocationOf(range) {
|
|
2248
2684
|
return {
|
|
2249
2685
|
start: getLineInfo(this.source, range[0]),
|
|
2250
2686
|
end: getLineInfo(this.source, range[1])
|
|
2251
2687
|
};
|
|
2252
2688
|
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Get the current character wrapper.
|
|
2691
|
+
*/
|
|
2253
2692
|
current() {
|
|
2254
2693
|
return this.charsList[this.charsIndex];
|
|
2255
2694
|
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Advance to the next character.
|
|
2697
|
+
*/
|
|
2256
2698
|
next() {
|
|
2257
2699
|
this.charsIndex++;
|
|
2258
2700
|
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Move back to the previous character.
|
|
2703
|
+
*/
|
|
2259
2704
|
prev() {
|
|
2260
2705
|
this.charsIndex--;
|
|
2261
2706
|
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Check whether the cursor has reached the end of source.
|
|
2709
|
+
*/
|
|
2262
2710
|
isEof() {
|
|
2263
2711
|
return this.charsIndex >= this.charsList.length;
|
|
2264
2712
|
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Get the current zero-based character index.
|
|
2715
|
+
*/
|
|
2265
2716
|
index() {
|
|
2266
2717
|
return this.current().range[1] - 1;
|
|
2267
2718
|
}
|
|
@@ -2312,6 +2763,11 @@ function tokenizeChars(state) {
|
|
|
2312
2763
|
state.sourceCode.prev();
|
|
2313
2764
|
if (handler.handleContentEnd !== void 0) handler.handleContentEnd(state);
|
|
2314
2765
|
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Tokenize SVG source into parser tokens.
|
|
2768
|
+
* @param source - SVG source code
|
|
2769
|
+
* @returns Tokenizer state and emitted tokens
|
|
2770
|
+
*/
|
|
2315
2771
|
function tokenize(source) {
|
|
2316
2772
|
const tokens = [];
|
|
2317
2773
|
const state = {
|
|
@@ -2332,48 +2788,29 @@ function tokenize(source) {
|
|
|
2332
2788
|
}
|
|
2333
2789
|
//#endregion
|
|
2334
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
|
+
*/
|
|
2335
2797
|
function parse$1(source, _options = {}) {
|
|
2336
2798
|
const { tokens } = tokenize(source);
|
|
2337
|
-
const { ast } = constructTree(tokens);
|
|
2799
|
+
const { ast, errors, warnings } = constructTree(tokens);
|
|
2338
2800
|
return {
|
|
2339
2801
|
ast: clearParent(ast),
|
|
2340
|
-
|
|
2802
|
+
errors,
|
|
2803
|
+
tokens,
|
|
2804
|
+
warnings
|
|
2341
2805
|
};
|
|
2342
2806
|
}
|
|
2343
2807
|
//#endregion
|
|
2344
|
-
//#region src/visitorKeys.ts
|
|
2345
|
-
const keys = {
|
|
2346
|
-
Program: ["document"],
|
|
2347
|
-
Document: ["children"],
|
|
2348
|
-
XMLDeclaration: ["attributes"],
|
|
2349
|
-
XMLDeclarationAttribute: ["key", "value"],
|
|
2350
|
-
XMLDeclarationAttributeKey: [],
|
|
2351
|
-
XMLDeclarationAttributeValue: [],
|
|
2352
|
-
Doctype: ["attributes"],
|
|
2353
|
-
DoctypeAttribute: ["value"],
|
|
2354
|
-
DoctypeAttributeValue: [],
|
|
2355
|
-
Attribute: ["key", "value"],
|
|
2356
|
-
AttributeKey: [],
|
|
2357
|
-
AttributeValue: [],
|
|
2358
|
-
Element: ["attributes", "children"],
|
|
2359
|
-
Comment: [],
|
|
2360
|
-
Text: [],
|
|
2361
|
-
Error: []
|
|
2362
|
-
};
|
|
2363
|
-
let vistorKeysCache = null;
|
|
2364
|
-
function getVisitorKeys() {
|
|
2365
|
-
if (!vistorKeysCache) {
|
|
2366
|
-
const merged = unionWith(keys);
|
|
2367
|
-
vistorKeysCache = {
|
|
2368
|
-
...merged,
|
|
2369
|
-
Tag: merged.Element
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
return vistorKeysCache;
|
|
2373
|
-
}
|
|
2374
|
-
const visitorKeys = getVisitorKeys();
|
|
2375
|
-
//#endregion
|
|
2376
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
|
+
*/
|
|
2377
2814
|
function traverse(node, visitor) {
|
|
2378
2815
|
if (!node) return;
|
|
2379
2816
|
visitor(node);
|
|
@@ -2387,8 +2824,14 @@ function traverse(node, visitor) {
|
|
|
2387
2824
|
}
|
|
2388
2825
|
//#endregion
|
|
2389
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
|
+
*/
|
|
2390
2833
|
function parseForESLint(source, options = {}) {
|
|
2391
|
-
const { ast, tokens } = parse$1(source, options);
|
|
2834
|
+
const { ast, errors, tokens, warnings } = parse$1(source, options);
|
|
2392
2835
|
const programNode = {
|
|
2393
2836
|
type: "Program",
|
|
2394
2837
|
body: [],
|
|
@@ -2412,15 +2855,31 @@ function parseForESLint(source, options = {}) {
|
|
|
2412
2855
|
ast: programNode,
|
|
2413
2856
|
visitorKeys,
|
|
2414
2857
|
scopeManager: null,
|
|
2415
|
-
services: {
|
|
2858
|
+
services: {
|
|
2859
|
+
errors,
|
|
2860
|
+
isSVG: true,
|
|
2861
|
+
warnings
|
|
2862
|
+
}
|
|
2416
2863
|
};
|
|
2417
2864
|
}
|
|
2418
2865
|
//#endregion
|
|
2419
2866
|
//#region src/index.ts
|
|
2867
|
+
/**
|
|
2868
|
+
* Parser package name.
|
|
2869
|
+
*/
|
|
2420
2870
|
const name = meta.name;
|
|
2871
|
+
/**
|
|
2872
|
+
* ESLint visitor keys for parser-specific nodes.
|
|
2873
|
+
*/
|
|
2421
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
|
+
*/
|
|
2422
2881
|
function parse(code, options = {}) {
|
|
2423
2882
|
return parseForESLint(code, options).ast.document;
|
|
2424
2883
|
}
|
|
2425
2884
|
//#endregion
|
|
2426
|
-
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 };
|