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/README.md +7 -9
- package/dist/index.d.ts +298 -41
- package/dist/index.js +610 -150
- package/package.json +29 -24
- /package/dist/{chunk-pbuEa-1d.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
|
-
const RE_OPEN_TAG_NAME = /^<(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
|
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(
|
|
1066
|
-
state.currentNode =
|
|
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
|
-
|
|
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: "
|
|
1203
|
-
handler(
|
|
1204
|
-
state.
|
|
1205
|
-
|
|
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: "
|
|
1556
|
+
tokenType: "XMLDeclarationAttributeKey",
|
|
1319
1557
|
handler(token, state) {
|
|
1320
1558
|
const attribute = getLastAttribute(state);
|
|
1321
|
-
attribute.value
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
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: "
|
|
1339
|
-
handler(
|
|
1340
|
-
|
|
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: "
|
|
1357
|
-
handler(
|
|
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(["
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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.
|
|
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 (
|
|
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
|
|
2512
|
+
if (value === SPECIAL_CHAR.question) return state.sourceCode.next();
|
|
2114
2513
|
if (value === "?>") return parseXMLDeclarationClose(state);
|
|
2115
|
-
|
|
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: "
|
|
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 = "
|
|
2159
|
-
state.contextParams["
|
|
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 = "
|
|
2568
|
+
state.currentContext = "XMLDeclarationAttributes";
|
|
2166
2569
|
}
|
|
2167
2570
|
function parseBare(state) {
|
|
2168
|
-
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();
|
|
2169
2579
|
state.decisionBuffer.clear();
|
|
2170
|
-
state.currentContext = "
|
|
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: "
|
|
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: "
|
|
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 = "
|
|
2615
|
+
state.currentContext = "XMLDeclarationAttributes";
|
|
2203
2616
|
state.sourceCode.next();
|
|
2204
|
-
state.contextParams["
|
|
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["
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
|
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 };
|