@markuplint/parser-utils 4.8.11 → 5.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +6 -6
- package/ARCHITECTURE.md +5 -5
- package/CHANGELOG.md +37 -0
- package/docs/parser-class.ja.md +7 -7
- package/docs/parser-class.md +7 -7
- package/lib/debug.js +8 -24
- package/lib/debugger.js +9 -4
- package/lib/get-location.d.ts +31 -0
- package/lib/get-location.js +33 -0
- package/lib/get-namespace.d.ts +9 -0
- package/lib/get-namespace.js +9 -0
- package/lib/ignore-block.js +15 -14
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/parser-error.js +8 -3
- package/lib/parser.d.ts +12 -16
- package/lib/parser.js +526 -551
- package/lib/sort-nodes.d.ts +8 -0
- package/lib/sort-nodes.js +11 -3
- package/lib/types.d.ts +3 -3
- package/package.json +11 -10
package/lib/parser.js
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _Parser_instances, _Parser_booleanish, _Parser_defaultState, _Parser_endTagType, _Parser_ignoreTags, _Parser_maskChar, _Parser_tagNameCaseSensitive, _Parser_selfCloseType, _Parser_spaceChars, _Parser_rawTextElements, _Parser_authoredElementName, _Parser_originalRawCode, _Parser_rawCode, _Parser_defaultDepth, _Parser_walkMethodSequentailPrevNode, _Parser_arrayize, _Parser_concatText, _Parser_concatTextNodes, _Parser_convertIntoInvalidNode, _Parser_createOffsetSpaces, _Parser_createRemnantNode, _Parser_exposeRemnantNodes, _Parser_getEndLocation, _Parser_orphanEndTagToBogusMark, _Parser_pairing, _Parser_parseEndTag, _Parser_parseStartTag, _Parser_parseTag, _Parser_removeChild, _Parser_removeDeprecatedNode, _Parser_removeOffsetSpaces, _Parser_reset, _Parser_setRawCode, _Parser_trimText;
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
13
2
|
import { isVoidElement as detectVoidElement } from '@markuplint/ml-spec';
|
|
14
|
-
import { v4 as uuid } from 'uuid';
|
|
15
3
|
import { attrTokenizer } from './attr-tokenizer.js';
|
|
16
4
|
import { defaultSpaces } from './const.js';
|
|
17
5
|
import { domLog, PerformanceTimer } from './debug.js';
|
|
@@ -22,6 +10,7 @@ import { ignoreBlock, restoreNode } from './ignore-block.js';
|
|
|
22
10
|
import { ignoreFrontMatter } from './ignore-front-matter.js';
|
|
23
11
|
import { ParserError } from './parser-error.js';
|
|
24
12
|
import { sortNodes } from './sort-nodes.js';
|
|
13
|
+
import { getNamespace } from './get-namespace.js';
|
|
25
14
|
const timer = new PerformanceTimer();
|
|
26
15
|
/**
|
|
27
16
|
* Abstract base class for all markuplint parsers. Provides the core parsing pipeline
|
|
@@ -33,6 +22,21 @@ const timer = new PerformanceTimer();
|
|
|
33
22
|
* @template State - An optional parser state type that persists across tokenization
|
|
34
23
|
*/
|
|
35
24
|
export class Parser {
|
|
25
|
+
#booleanish = false;
|
|
26
|
+
#defaultState;
|
|
27
|
+
#endTagType = 'omittable';
|
|
28
|
+
#ignoreTags = [];
|
|
29
|
+
#maskChar;
|
|
30
|
+
#tagNameCaseSensitive = false;
|
|
31
|
+
#selfCloseType = 'html';
|
|
32
|
+
#spaceChars = defaultSpaces;
|
|
33
|
+
#rawTextElements = ['style', 'script'];
|
|
34
|
+
#authoredElementName;
|
|
35
|
+
#originalRawCode = '';
|
|
36
|
+
#rawCode = '';
|
|
37
|
+
#defaultDepth = 0;
|
|
38
|
+
#walkMethodSequentailPrevNode = null;
|
|
39
|
+
state;
|
|
36
40
|
/**
|
|
37
41
|
* Creates a new Parser instance with the given options and initial state.
|
|
38
42
|
*
|
|
@@ -40,38 +44,23 @@ export class Parser {
|
|
|
40
44
|
* @param defaultState - The initial parser state, cloned and restored after each parse call
|
|
41
45
|
*/
|
|
42
46
|
constructor(options, defaultState) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
_Parser_authoredElementName.set(this, void 0);
|
|
54
|
-
_Parser_originalRawCode.set(this, '');
|
|
55
|
-
_Parser_rawCode.set(this, '');
|
|
56
|
-
_Parser_defaultDepth.set(this, 0);
|
|
57
|
-
_Parser_walkMethodSequentailPrevNode.set(this, null);
|
|
58
|
-
__classPrivateFieldSet(this, _Parser_booleanish, options?.booleanish ?? __classPrivateFieldGet(this, _Parser_booleanish, "f"), "f");
|
|
59
|
-
__classPrivateFieldSet(this, _Parser_endTagType, options?.endTagType ?? __classPrivateFieldGet(this, _Parser_endTagType, "f"), "f");
|
|
60
|
-
__classPrivateFieldSet(this, _Parser_ignoreTags, options?.ignoreTags ?? __classPrivateFieldGet(this, _Parser_ignoreTags, "f"), "f");
|
|
61
|
-
__classPrivateFieldSet(this, _Parser_maskChar, options?.maskChar ?? __classPrivateFieldGet(this, _Parser_maskChar, "f"), "f");
|
|
62
|
-
__classPrivateFieldSet(this, _Parser_tagNameCaseSensitive, options?.tagNameCaseSensitive ?? __classPrivateFieldGet(this, _Parser_tagNameCaseSensitive, "f"), "f");
|
|
63
|
-
__classPrivateFieldSet(this, _Parser_selfCloseType, options?.selfCloseType ?? __classPrivateFieldGet(this, _Parser_selfCloseType, "f"), "f");
|
|
64
|
-
__classPrivateFieldSet(this, _Parser_spaceChars, options?.spaceChars ?? __classPrivateFieldGet(this, _Parser_spaceChars, "f"), "f");
|
|
65
|
-
__classPrivateFieldSet(this, _Parser_rawTextElements, options?.rawTextElements ?? __classPrivateFieldGet(this, _Parser_rawTextElements, "f"), "f");
|
|
66
|
-
__classPrivateFieldSet(this, _Parser_defaultState, defaultState ?? null, "f");
|
|
67
|
-
this.state = structuredClone(__classPrivateFieldGet(this, _Parser_defaultState, "f"));
|
|
47
|
+
this.#booleanish = options?.booleanish ?? this.#booleanish;
|
|
48
|
+
this.#endTagType = options?.endTagType ?? this.#endTagType;
|
|
49
|
+
this.#ignoreTags = options?.ignoreTags ?? this.#ignoreTags;
|
|
50
|
+
this.#maskChar = options?.maskChar ?? this.#maskChar;
|
|
51
|
+
this.#tagNameCaseSensitive = options?.tagNameCaseSensitive ?? this.#tagNameCaseSensitive;
|
|
52
|
+
this.#selfCloseType = options?.selfCloseType ?? this.#selfCloseType;
|
|
53
|
+
this.#spaceChars = options?.spaceChars ?? this.#spaceChars;
|
|
54
|
+
this.#rawTextElements = options?.rawTextElements ?? this.#rawTextElements;
|
|
55
|
+
this.#defaultState = defaultState ?? null;
|
|
56
|
+
this.state = structuredClone(this.#defaultState);
|
|
68
57
|
}
|
|
69
58
|
/**
|
|
70
59
|
* The pattern used to distinguish authored (component) element names
|
|
71
60
|
* from native HTML elements, as specified by the parse options.
|
|
72
61
|
*/
|
|
73
62
|
get authoredElementName() {
|
|
74
|
-
return
|
|
63
|
+
return this.#authoredElementName;
|
|
75
64
|
}
|
|
76
65
|
/**
|
|
77
66
|
* Detect value as a true if its attribute is booleanish value and omitted.
|
|
@@ -84,7 +73,7 @@ export class Parser {
|
|
|
84
73
|
* In the above, the `aria-hidden` is `true`.
|
|
85
74
|
*/
|
|
86
75
|
get booleanish() {
|
|
87
|
-
return
|
|
76
|
+
return this.#booleanish;
|
|
88
77
|
}
|
|
89
78
|
/**
|
|
90
79
|
* The end tag omittable type.
|
|
@@ -94,21 +83,21 @@ export class Parser {
|
|
|
94
83
|
* - `"never"`: Never need
|
|
95
84
|
*/
|
|
96
85
|
get endTag() {
|
|
97
|
-
return
|
|
86
|
+
return this.#endTagType;
|
|
98
87
|
}
|
|
99
88
|
/**
|
|
100
89
|
* The current raw source code being parsed, which may have been
|
|
101
90
|
* preprocessed (e.g., ignore blocks masked, front matter removed).
|
|
102
91
|
*/
|
|
103
92
|
get rawCode() {
|
|
104
|
-
return
|
|
93
|
+
return this.#rawCode;
|
|
105
94
|
}
|
|
106
95
|
/**
|
|
107
96
|
* Whether tag names should be compared in a case-sensitive manner.
|
|
108
97
|
* When false (the default), tag name comparisons are case-insensitive (HTML behavior).
|
|
109
98
|
*/
|
|
110
99
|
get tagNameCaseSensitive() {
|
|
111
|
-
return
|
|
100
|
+
return this.#tagNameCaseSensitive;
|
|
112
101
|
}
|
|
113
102
|
/**
|
|
114
103
|
* Tokenizes the raw source code into language-specific AST nodes.
|
|
@@ -133,7 +122,7 @@ export class Parser {
|
|
|
133
122
|
* @returns The preprocessed source code to be used for tokenization
|
|
134
123
|
*/
|
|
135
124
|
beforeParse(rawCode, options) {
|
|
136
|
-
const spaces =
|
|
125
|
+
const spaces = this.#createOffsetSpaces(options);
|
|
137
126
|
return spaces + rawCode;
|
|
138
127
|
}
|
|
139
128
|
/**
|
|
@@ -148,29 +137,29 @@ export class Parser {
|
|
|
148
137
|
parse(rawCode, options) {
|
|
149
138
|
try {
|
|
150
139
|
// Initialize raw code
|
|
151
|
-
|
|
140
|
+
this.#setRawCode(rawCode, rawCode);
|
|
152
141
|
timer.push('beforeParse');
|
|
153
142
|
const beforeParsedCode = this.beforeParse(this.rawCode, options);
|
|
154
143
|
// Override raw code
|
|
155
|
-
|
|
156
|
-
|
|
144
|
+
this.#setRawCode(beforeParsedCode);
|
|
145
|
+
this.#authoredElementName = options?.authoredElementName;
|
|
157
146
|
let frontMatter = null;
|
|
158
147
|
if (options?.ignoreFrontMatter) {
|
|
159
148
|
timer.push('ignoreFrontMatter');
|
|
160
149
|
const fm = ignoreFrontMatter(this.rawCode);
|
|
161
|
-
|
|
150
|
+
this.#setRawCode(fm.code);
|
|
162
151
|
frontMatter = fm.frontMatter;
|
|
163
152
|
}
|
|
164
153
|
timer.push('ignoreBlock');
|
|
165
|
-
const blocks = ignoreBlock(this.rawCode,
|
|
166
|
-
|
|
154
|
+
const blocks = ignoreBlock(this.rawCode, this.#ignoreTags, this.#maskChar);
|
|
155
|
+
this.#setRawCode(blocks.replaced);
|
|
167
156
|
timer.push('tokenize');
|
|
168
157
|
const tokenized = this.tokenize(options);
|
|
169
158
|
const ast = tokenized.ast;
|
|
170
159
|
const isFragment = tokenized.isFragment;
|
|
171
|
-
|
|
160
|
+
this.#defaultDepth = options?.depth ?? this.#defaultDepth;
|
|
172
161
|
timer.push('traverse');
|
|
173
|
-
const traversed = this.traverse(ast, null,
|
|
162
|
+
const traversed = this.traverse(ast, null, this.#defaultDepth);
|
|
174
163
|
timer.push('afterTraverse');
|
|
175
164
|
const nodeTree = this.afterTraverse([...traversed.childNodes, ...traversed.siblings]);
|
|
176
165
|
timer.push('flattenNodes');
|
|
@@ -209,7 +198,7 @@ export class Parser {
|
|
|
209
198
|
}
|
|
210
199
|
timer.log();
|
|
211
200
|
domLog(nodeList);
|
|
212
|
-
|
|
201
|
+
this.#reset();
|
|
213
202
|
return {
|
|
214
203
|
raw: rawCode,
|
|
215
204
|
nodeList,
|
|
@@ -230,7 +219,7 @@ export class Parser {
|
|
|
230
219
|
* @returns The post-processed node list
|
|
231
220
|
*/
|
|
232
221
|
afterParse(nodeList, options) {
|
|
233
|
-
return
|
|
222
|
+
return this.#removeOffsetSpaces(nodeList, options);
|
|
234
223
|
}
|
|
235
224
|
/**
|
|
236
225
|
* Wraps an arbitrary error into a ParserError with source location information.
|
|
@@ -272,7 +261,7 @@ export class Parser {
|
|
|
272
261
|
const filteredNodes = [];
|
|
273
262
|
for (const node of nodes) {
|
|
274
263
|
// Remove duplicated nodes
|
|
275
|
-
const id = `${node.
|
|
264
|
+
const id = `${node.offset}:${node.offset + node.raw.length}:${node.nodeName}:${node.type}:${node.raw}`;
|
|
276
265
|
if (existence.has(id)) {
|
|
277
266
|
continue;
|
|
278
267
|
}
|
|
@@ -297,10 +286,7 @@ export class Parser {
|
|
|
297
286
|
* @returns The node tree sorted by source position
|
|
298
287
|
*/
|
|
299
288
|
afterTraverse(nodeTree) {
|
|
300
|
-
return
|
|
301
|
-
? // TODO: Use sort instead of toSorted until we end support for Node 18
|
|
302
|
-
[...nodeTree].sort(sortNodes)
|
|
303
|
-
: nodeTree.toSorted(sortNodes);
|
|
289
|
+
return nodeTree.toSorted(sortNodes);
|
|
304
290
|
}
|
|
305
291
|
/**
|
|
306
292
|
* Converts a single language-specific AST node into one or more markuplint AST nodes.
|
|
@@ -358,7 +344,7 @@ export class Parser {
|
|
|
358
344
|
* @returns A flat array of all nodes in source order
|
|
359
345
|
*/
|
|
360
346
|
flattenNodes(nodeTree) {
|
|
361
|
-
return
|
|
347
|
+
return this.#arrayize(nodeTree);
|
|
362
348
|
}
|
|
363
349
|
/**
|
|
364
350
|
* Post-processes the flattened node list by exposing remnant whitespace and
|
|
@@ -373,12 +359,12 @@ export class Parser {
|
|
|
373
359
|
const exposeInvalidNode = options?.exposeInvalidNode ?? true;
|
|
374
360
|
const exposeWhiteSpace = options?.exposeWhiteSpace ?? true;
|
|
375
361
|
const concatText = options?.concatText ?? true;
|
|
376
|
-
nodeList =
|
|
377
|
-
nodeList =
|
|
362
|
+
nodeList = this.#exposeRemnantNodes(nodeList, exposeInvalidNode, exposeWhiteSpace);
|
|
363
|
+
nodeList = this.#orphanEndTagToBogusMark(nodeList);
|
|
378
364
|
if (concatText) {
|
|
379
|
-
nodeList =
|
|
365
|
+
nodeList = this.#concatText(nodeList);
|
|
380
366
|
}
|
|
381
|
-
nodeList =
|
|
367
|
+
nodeList = this.#trimText(nodeList);
|
|
382
368
|
return nodeList;
|
|
383
369
|
}
|
|
384
370
|
/**
|
|
@@ -450,7 +436,7 @@ export class Parser {
|
|
|
450
436
|
* and recursively traversed child nodes. Handles ghost elements (empty raw),
|
|
451
437
|
* self-closing tags, and nameless fragments (e.g., JSX `<>`).
|
|
452
438
|
*
|
|
453
|
-
* @param token - The child token with the element's node name and
|
|
439
|
+
* @param token - The child token with the element's node name; namespace is auto-detected from tag name and parent node
|
|
454
440
|
* @param childNodes - The language-specific child AST nodes to traverse
|
|
455
441
|
* @param options - Controls end tag creation, fragment handling, and property overrides
|
|
456
442
|
* @returns An array of AST nodes including the start tag, optional end tag, and any sibling nodes
|
|
@@ -469,20 +455,21 @@ export class Parser {
|
|
|
469
455
|
elementType: 'html',
|
|
470
456
|
attributes: [],
|
|
471
457
|
childNodes: [],
|
|
458
|
+
blockBehavior: null,
|
|
472
459
|
parentNode: token.parentNode,
|
|
473
460
|
pairNode: null,
|
|
474
461
|
tagCloseChar: '',
|
|
475
462
|
tagOpenChar: '',
|
|
476
463
|
isGhost: true,
|
|
477
464
|
isFragment: false,
|
|
465
|
+
namespace: getNamespace(null, token.parentNode),
|
|
478
466
|
...overwriteProps,
|
|
479
467
|
};
|
|
480
468
|
const siblings = this.visitChildren(childNodes, startTag);
|
|
481
469
|
return [startTag, ...siblings];
|
|
482
470
|
}
|
|
483
471
|
const startTag = {
|
|
484
|
-
...
|
|
485
|
-
namespace: token.namespace,
|
|
472
|
+
...this.#parseStartTag(token, {
|
|
486
473
|
...overwriteProps,
|
|
487
474
|
}, namelessFragment),
|
|
488
475
|
};
|
|
@@ -490,8 +477,8 @@ export class Parser {
|
|
|
490
477
|
if (createEndTagToken) {
|
|
491
478
|
const endTagToken = createEndTagToken(startTag);
|
|
492
479
|
if (endTagToken) {
|
|
493
|
-
const endTag =
|
|
494
|
-
|
|
480
|
+
const endTag = this.#parseEndTag(endTagToken, namelessFragment);
|
|
481
|
+
this.#pairing(startTag, endTag);
|
|
495
482
|
return [startTag, endTag, ...siblings];
|
|
496
483
|
}
|
|
497
484
|
}
|
|
@@ -503,17 +490,17 @@ export class Parser {
|
|
|
503
490
|
*
|
|
504
491
|
* @param token - The child token with the block's node name and fragment flag
|
|
505
492
|
* @param childNodes - The language-specific child AST nodes to traverse
|
|
506
|
-
* @param
|
|
493
|
+
* @param blockBehavior - The block behavior if this is a control-flow block (e.g., "if", "each")
|
|
507
494
|
* @param originBlockNode - The original language-specific block node for reference
|
|
508
495
|
* @returns An array of AST nodes including the block node and any sibling nodes
|
|
509
496
|
*/
|
|
510
|
-
visitPsBlock(token, childNodes = [],
|
|
497
|
+
visitPsBlock(token, childNodes = [], blockBehavior = null, originBlockNode) {
|
|
511
498
|
timer.push('visitPsBlock');
|
|
512
499
|
const block = {
|
|
513
500
|
...token,
|
|
514
501
|
...this.createToken(token),
|
|
515
502
|
type: 'psblock',
|
|
516
|
-
|
|
503
|
+
blockBehavior,
|
|
517
504
|
nodeName: `#ps:${token.nodeName}`,
|
|
518
505
|
childNodes: [],
|
|
519
506
|
isBogus: false,
|
|
@@ -534,7 +521,7 @@ export class Parser {
|
|
|
534
521
|
if (children.length === 0) {
|
|
535
522
|
return [];
|
|
536
523
|
}
|
|
537
|
-
if (parentNode &&
|
|
524
|
+
if (parentNode && this.#rawTextElements.includes(parentNode.nodeName.toLowerCase())) {
|
|
538
525
|
return [];
|
|
539
526
|
}
|
|
540
527
|
const traversed = this.traverse(children, parentNode, parentNode ? parentNode.depth + 1 : 0);
|
|
@@ -561,10 +548,9 @@ export class Parser {
|
|
|
561
548
|
if (!raw.endsWith('}')) {
|
|
562
549
|
return null;
|
|
563
550
|
}
|
|
564
|
-
const node = this.createToken(raw, token.
|
|
551
|
+
const node = this.createToken(raw, token.offset, token.line, token.col);
|
|
565
552
|
return {
|
|
566
553
|
...node,
|
|
567
|
-
...__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_getEndLocation).call(this, node),
|
|
568
554
|
type: 'spread',
|
|
569
555
|
nodeName: '#spread',
|
|
570
556
|
};
|
|
@@ -586,9 +572,9 @@ export class Parser {
|
|
|
586
572
|
const startState = options?.startState ?? AttrState.BeforeName;
|
|
587
573
|
const noQuoteValueType = options?.noQuoteValueType;
|
|
588
574
|
const endOfUnquotedValueChars = options?.endOfUnquotedValueChars;
|
|
589
|
-
let
|
|
590
|
-
let
|
|
591
|
-
let
|
|
575
|
+
let curOffset = token.offset;
|
|
576
|
+
let curLine = token.line;
|
|
577
|
+
let curCol = token.col;
|
|
592
578
|
let tokens;
|
|
593
579
|
try {
|
|
594
580
|
tokens = attrTokenizer(raw, quoteSet, startState, noQuoteValueType, endOfUnquotedValueChars);
|
|
@@ -599,42 +585,28 @@ export class Parser {
|
|
|
599
585
|
}
|
|
600
586
|
throw error;
|
|
601
587
|
}
|
|
602
|
-
const spacesBeforeName = this.createToken(tokens.spacesBeforeAttrName,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
startOffset = equal.endOffset;
|
|
618
|
-
const spacesAfterEqual = this.createToken(tokens.spacesAfterEqual, startOffset, startLine, startCol);
|
|
619
|
-
startLine = spacesAfterEqual.endLine;
|
|
620
|
-
startCol = spacesAfterEqual.endCol;
|
|
621
|
-
startOffset = spacesAfterEqual.endOffset;
|
|
622
|
-
const startQuote = this.createToken(tokens.quoteStart, startOffset, startLine, startCol);
|
|
623
|
-
startLine = startQuote.endLine;
|
|
624
|
-
startCol = startQuote.endCol;
|
|
625
|
-
startOffset = startQuote.endOffset;
|
|
626
|
-
const value = this.createToken(tokens.attrValue, startOffset, startLine, startCol);
|
|
627
|
-
startLine = value.endLine;
|
|
628
|
-
startCol = value.endCol;
|
|
629
|
-
startOffset = value.endOffset;
|
|
630
|
-
const endQuote = this.createToken(tokens.quoteEnd, startOffset, startLine, startCol);
|
|
588
|
+
const spacesBeforeName = this.createToken(tokens.spacesBeforeAttrName, curOffset, curLine, curCol);
|
|
589
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(spacesBeforeName));
|
|
590
|
+
const name = this.createToken(tokens.attrName, curOffset, curLine, curCol);
|
|
591
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(name));
|
|
592
|
+
const spacesBeforeEqual = this.createToken(tokens.spacesBeforeEqual, curOffset, curLine, curCol);
|
|
593
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(spacesBeforeEqual));
|
|
594
|
+
const equal = this.createToken(tokens.equal, curOffset, curLine, curCol);
|
|
595
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(equal));
|
|
596
|
+
const spacesAfterEqual = this.createToken(tokens.spacesAfterEqual, curOffset, curLine, curCol);
|
|
597
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(spacesAfterEqual));
|
|
598
|
+
const startQuote = this.createToken(tokens.quoteStart, curOffset, curLine, curCol);
|
|
599
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(startQuote));
|
|
600
|
+
const value = this.createToken(tokens.attrValue, curOffset, curLine, curCol);
|
|
601
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(value));
|
|
602
|
+
const endQuote = this.createToken(tokens.quoteEnd, curOffset, curLine, curCol);
|
|
631
603
|
const attrToken = this.createToken(tokens.attrName +
|
|
632
604
|
tokens.spacesBeforeEqual +
|
|
633
605
|
tokens.equal +
|
|
634
606
|
tokens.spacesAfterEqual +
|
|
635
607
|
tokens.quoteStart +
|
|
636
608
|
tokens.attrValue +
|
|
637
|
-
tokens.quoteEnd, name.
|
|
609
|
+
tokens.quoteEnd, name.offset, name.line, name.col);
|
|
638
610
|
const htmlAttr = {
|
|
639
611
|
...attrToken,
|
|
640
612
|
type: 'attr',
|
|
@@ -670,22 +642,22 @@ export class Parser {
|
|
|
670
642
|
parseCodeFragment(token, options) {
|
|
671
643
|
const nodes = [];
|
|
672
644
|
let raw = token.raw;
|
|
673
|
-
let
|
|
674
|
-
let
|
|
675
|
-
let
|
|
645
|
+
let curOffset = token.offset;
|
|
646
|
+
let curLine = token.line;
|
|
647
|
+
let curCol = token.col;
|
|
676
648
|
let depth = token.depth;
|
|
677
649
|
const depthStack = new Map();
|
|
678
650
|
while (raw) {
|
|
679
|
-
const parsed =
|
|
651
|
+
const parsed = this.#parseTag({
|
|
680
652
|
raw,
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
653
|
+
offset: curOffset,
|
|
654
|
+
line: curLine,
|
|
655
|
+
col: curCol,
|
|
684
656
|
depth,
|
|
685
|
-
parentNode:
|
|
657
|
+
parentNode: token.parentNode,
|
|
686
658
|
}, true, true, options?.namelessFragment ?? false);
|
|
687
659
|
if (parsed.__left) {
|
|
688
|
-
const token = this.createToken(parsed.__left,
|
|
660
|
+
const token = this.createToken(parsed.__left, curOffset, curLine, curCol);
|
|
689
661
|
const textNode = {
|
|
690
662
|
...token,
|
|
691
663
|
type: 'text',
|
|
@@ -700,12 +672,10 @@ export class Parser {
|
|
|
700
672
|
continue;
|
|
701
673
|
}
|
|
702
674
|
const tag = parsed.token;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
startOffset = tag.endOffset;
|
|
706
|
-
let isSelfClose = tag.type === 'starttag' && tag.selfClosingSolidus?.raw === '/';
|
|
675
|
+
({ offset: curOffset, line: curLine, col: curCol } = this.#getEndLocation(tag));
|
|
676
|
+
let isSelfClose = tag.type === 'starttag' && tag.tagCloseChar.startsWith('/');
|
|
707
677
|
const isVoidElement = detectVoidElement({ localName: tag.nodeName.toLowerCase() });
|
|
708
|
-
switch (
|
|
678
|
+
switch (this.#selfCloseType) {
|
|
709
679
|
case 'html': {
|
|
710
680
|
isSelfClose = isVoidElement;
|
|
711
681
|
break;
|
|
@@ -730,7 +700,7 @@ export class Parser {
|
|
|
730
700
|
depthStack.delete(tag.nodeName);
|
|
731
701
|
}
|
|
732
702
|
else {
|
|
733
|
-
depth = Math.max(depth - 1,
|
|
703
|
+
depth = Math.max(depth - 1, this.#defaultDepth);
|
|
734
704
|
}
|
|
735
705
|
this.updateLocation(tag, {
|
|
736
706
|
depth,
|
|
@@ -752,46 +722,28 @@ export class Parser {
|
|
|
752
722
|
return nodes;
|
|
753
723
|
}
|
|
754
724
|
/**
|
|
755
|
-
* Updates the position and depth properties of an AST node
|
|
756
|
-
* end offsets, lines, and columns based on the new start values.
|
|
725
|
+
* Updates the position and depth properties of an AST node.
|
|
757
726
|
*
|
|
758
727
|
* @param node - The AST node whose location should be updated
|
|
759
728
|
* @param props - The new position and depth values to apply (only provided values are changed)
|
|
760
729
|
*/
|
|
761
730
|
updateLocation(node, props) {
|
|
762
731
|
Object.assign(node, {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
endOffset: props.startOffset == null ? node.endOffset : props.startOffset + node.raw.length,
|
|
767
|
-
endLine: props.startLine == null ? node.endLine : getEndLine(node.raw, props.startLine),
|
|
768
|
-
endCol: props.startCol == null ? node.endCol : getEndCol(node.raw, props.startCol),
|
|
732
|
+
offset: props.offset ?? node.offset,
|
|
733
|
+
line: props.line ?? node.line,
|
|
734
|
+
col: props.col ?? node.col,
|
|
769
735
|
depth: props.depth ?? node.depth,
|
|
770
736
|
});
|
|
771
737
|
}
|
|
772
738
|
/**
|
|
773
739
|
* Set new raw code to target node.
|
|
774
740
|
*
|
|
775
|
-
* Replace the raw code and update the start/end offset/line/column.
|
|
776
|
-
*
|
|
777
741
|
* @param node target node
|
|
778
742
|
* @param raw new raw code
|
|
779
743
|
*/
|
|
780
744
|
updateRaw(node, raw) {
|
|
781
|
-
const startOffset = node.startOffset;
|
|
782
|
-
const startLine = node.startLine;
|
|
783
|
-
const startCol = node.startCol;
|
|
784
|
-
const endOffset = startOffset + raw.length;
|
|
785
|
-
const endLine = getEndLine(raw, startLine);
|
|
786
|
-
const endCol = getEndCol(raw, startCol);
|
|
787
745
|
Object.assign(node, {
|
|
788
746
|
raw,
|
|
789
|
-
startOffset,
|
|
790
|
-
endOffset,
|
|
791
|
-
startLine,
|
|
792
|
-
endLine,
|
|
793
|
-
startCol,
|
|
794
|
-
endCol,
|
|
795
747
|
});
|
|
796
748
|
}
|
|
797
749
|
updateElement(el, props) {
|
|
@@ -817,21 +769,20 @@ export class Parser {
|
|
|
817
769
|
* @returns The element type classification
|
|
818
770
|
*/
|
|
819
771
|
detectElementType(nodeName, defaultPattern) {
|
|
820
|
-
return detectElementType(nodeName,
|
|
772
|
+
return detectElementType(nodeName, this.#authoredElementName, defaultPattern);
|
|
821
773
|
}
|
|
822
|
-
createToken(token,
|
|
774
|
+
createToken(token, offset, line, col) {
|
|
823
775
|
const props = typeof token === 'string'
|
|
824
776
|
? {
|
|
825
777
|
raw: token,
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
778
|
+
offset: offset ?? 0,
|
|
779
|
+
line: line ?? 1,
|
|
780
|
+
col: col ?? 1,
|
|
829
781
|
}
|
|
830
782
|
: token;
|
|
831
783
|
return {
|
|
832
|
-
uuid:
|
|
784
|
+
uuid: randomUUID().slice(0, 8),
|
|
833
785
|
...props,
|
|
834
|
-
...__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_getEndLocation).call(this, props),
|
|
835
786
|
};
|
|
836
787
|
}
|
|
837
788
|
/**
|
|
@@ -844,12 +795,12 @@ export class Parser {
|
|
|
844
795
|
*/
|
|
845
796
|
sliceFragment(start, end) {
|
|
846
797
|
const raw = this.rawCode.slice(start, end);
|
|
847
|
-
const { line, column } = getPosition(this.rawCode, start);
|
|
798
|
+
const { line: l, column } = getPosition(this.rawCode, start);
|
|
848
799
|
return {
|
|
849
800
|
raw,
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
801
|
+
offset: start,
|
|
802
|
+
line: l,
|
|
803
|
+
col: column,
|
|
853
804
|
};
|
|
854
805
|
}
|
|
855
806
|
/**
|
|
@@ -877,14 +828,14 @@ export class Parser {
|
|
|
877
828
|
*/
|
|
878
829
|
walk(nodeList, walker, depth = 0) {
|
|
879
830
|
for (const node of nodeList) {
|
|
880
|
-
walker(node,
|
|
881
|
-
|
|
831
|
+
walker(node, this.#walkMethodSequentailPrevNode, depth);
|
|
832
|
+
this.#walkMethodSequentailPrevNode = node;
|
|
882
833
|
if ('childNodes' in node && node.childNodes.length > 0) {
|
|
883
834
|
this.walk(node.childNodes, walker, depth + 1);
|
|
884
835
|
}
|
|
885
836
|
}
|
|
886
837
|
if (depth === 0) {
|
|
887
|
-
|
|
838
|
+
this.#walkMethodSequentailPrevNode = null;
|
|
888
839
|
}
|
|
889
840
|
}
|
|
890
841
|
/**
|
|
@@ -910,10 +861,7 @@ export class Parser {
|
|
|
910
861
|
newChildNodes.splice(currentIndex, 1, appendingChild);
|
|
911
862
|
}
|
|
912
863
|
Object.assign(parentNode, {
|
|
913
|
-
childNodes:
|
|
914
|
-
? // TODO: Use sort instead of toSorted until we end support for Node 18
|
|
915
|
-
[...newChildNodes].sort(sortNodes)
|
|
916
|
-
: newChildNodes.toSorted(sortNodes),
|
|
864
|
+
childNodes: newChildNodes.toSorted(sortNodes),
|
|
917
865
|
});
|
|
918
866
|
}
|
|
919
867
|
/**
|
|
@@ -929,454 +877,481 @@ export class Parser {
|
|
|
929
877
|
if (index === -1) {
|
|
930
878
|
return;
|
|
931
879
|
}
|
|
932
|
-
if (Array.prototype.toSpliced == null) {
|
|
933
|
-
const newChildNodes = [...parentNode.childNodes];
|
|
934
|
-
// TODO: Use splice instead of toSpliced until we end support for Node 18
|
|
935
|
-
newChildNodes.splice(index, 1, ...replacementChildNodes);
|
|
936
|
-
Object.assign(parentNode, { childNodes: newChildNodes });
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
880
|
const newChildNodes = parentNode.childNodes.toSpliced(index, 1, ...replacementChildNodes);
|
|
940
881
|
Object.assign(parentNode, { childNodes: newChildNodes });
|
|
941
882
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
883
|
+
#arrayize(nodeTree) {
|
|
884
|
+
let nodeList = [];
|
|
885
|
+
this.walk(nodeTree, node => {
|
|
886
|
+
nodeList.push(node);
|
|
887
|
+
});
|
|
888
|
+
nodeList = this.#removeDeprecatedNode(nodeList);
|
|
889
|
+
return nodeList;
|
|
890
|
+
}
|
|
891
|
+
#concatText(nodeList) {
|
|
892
|
+
const newNodeList = [];
|
|
893
|
+
for (const node of nodeList) {
|
|
894
|
+
const prevNode = newNodeList.at(-1) ?? null;
|
|
895
|
+
if (prevNode?.type === 'text' &&
|
|
896
|
+
prevNode?.nodeName === '#text' &&
|
|
897
|
+
node.type === 'text' &&
|
|
898
|
+
node.nodeName === '#text' &&
|
|
899
|
+
prevNode.offset + prevNode.raw.length === node.offset) {
|
|
900
|
+
const newNode = this.#concatTextNodes(prevNode, node);
|
|
901
|
+
newNodeList.pop();
|
|
902
|
+
newNodeList.push(newNode);
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
newNodeList.push(node);
|
|
963
906
|
}
|
|
964
|
-
newNodeList
|
|
907
|
+
return newNodeList;
|
|
965
908
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
909
|
+
#concatTextNodes(...nodes) {
|
|
910
|
+
if (nodes.length === 0) {
|
|
911
|
+
throw new Error('Empty node list');
|
|
912
|
+
}
|
|
913
|
+
const firstNode = nodes.at(0);
|
|
914
|
+
const lastNode = nodes.at(-1);
|
|
915
|
+
if (firstNode.uuid === lastNode.uuid) {
|
|
916
|
+
return firstNode;
|
|
917
|
+
}
|
|
918
|
+
const textNode = {
|
|
919
|
+
...firstNode,
|
|
920
|
+
uuid: randomUUID().slice(0, 8),
|
|
921
|
+
raw: nodes.map(n => n.raw).join(''),
|
|
922
|
+
};
|
|
923
|
+
for (const node of nodes) {
|
|
924
|
+
this.#removeChild(node.parentNode, node);
|
|
925
|
+
}
|
|
926
|
+
this.appendChild(textNode.parentNode, textNode);
|
|
927
|
+
return textNode;
|
|
970
928
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
929
|
+
#convertIntoInvalidNode(node) {
|
|
930
|
+
if (node.type === 'invalid') {
|
|
931
|
+
return node;
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
...node,
|
|
935
|
+
type: 'invalid',
|
|
936
|
+
nodeName: '#invalid',
|
|
937
|
+
isBogus: true,
|
|
938
|
+
kind: node.type,
|
|
939
|
+
};
|
|
975
940
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
for (const node of nodes) {
|
|
985
|
-
__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_removeChild).call(this, node.parentNode, node);
|
|
941
|
+
#createOffsetSpaces(options) {
|
|
942
|
+
const offsetOffset = Math.max(options?.offsetOffset ?? 0, 0);
|
|
943
|
+
const offsetLine = Math.max((options?.offsetLine ?? 0) - 1, 0);
|
|
944
|
+
const offsetColumn = Math.max((options?.offsetColumn ?? 0) - 1, 0);
|
|
945
|
+
const offsetSpaces = ' '.repeat(offsetOffset - offsetLine - offsetColumn);
|
|
946
|
+
const offsetLines = '\n'.repeat(offsetLine);
|
|
947
|
+
const offsetColumns = ' '.repeat(offsetColumn);
|
|
948
|
+
return offsetSpaces + offsetLines + offsetColumns;
|
|
986
949
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
950
|
+
#createRemnantNode(start, end, depth, parentNode, exposeInvalidNode, exposeWhitespace) {
|
|
951
|
+
const codeFragment = this.sliceFragment(start, end);
|
|
952
|
+
if (codeFragment.raw) {
|
|
953
|
+
const remnantNodes = this.visitText({
|
|
954
|
+
...codeFragment,
|
|
955
|
+
depth: depth,
|
|
956
|
+
parentNode: parentNode,
|
|
957
|
+
}, { researchTags: true }).filter((node) => 'parentNode' in node);
|
|
958
|
+
if (remnantNodes.length > 1) {
|
|
959
|
+
this.appendChild(parentNode, ...remnantNodes);
|
|
960
|
+
return remnantNodes;
|
|
961
|
+
}
|
|
962
|
+
const remnantNode = remnantNodes[0];
|
|
963
|
+
if (!remnantNode) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
if (exposeInvalidNode && remnantNode.raw.trim() !== '') {
|
|
967
|
+
const invalidNode = this.#convertIntoInvalidNode(remnantNode);
|
|
968
|
+
this.appendChild(parentNode, invalidNode);
|
|
969
|
+
return [remnantNode];
|
|
970
|
+
}
|
|
971
|
+
if (exposeWhitespace && remnantNode.type === 'text' && remnantNode.raw.trim() === '') {
|
|
972
|
+
this.appendChild(parentNode, remnantNode);
|
|
973
|
+
return [remnantNode];
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return null;
|
|
992
977
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
nodeName: '#invalid',
|
|
997
|
-
isBogus: true,
|
|
998
|
-
kind: node.type,
|
|
999
|
-
};
|
|
1000
|
-
}, _Parser_createOffsetSpaces = function _Parser_createOffsetSpaces(options) {
|
|
1001
|
-
const offsetOffset = Math.max(options?.offsetOffset ?? 0, 0);
|
|
1002
|
-
const offsetLine = Math.max((options?.offsetLine ?? 0) - 1, 0);
|
|
1003
|
-
const offsetColumn = Math.max((options?.offsetColumn ?? 0) - 1, 0);
|
|
1004
|
-
const offsetSpaces = ' '.repeat(offsetOffset - offsetLine - offsetColumn);
|
|
1005
|
-
const offsetLines = '\n'.repeat(offsetLine);
|
|
1006
|
-
const offsetColumns = ' '.repeat(offsetColumn);
|
|
1007
|
-
return offsetSpaces + offsetLines + offsetColumns;
|
|
1008
|
-
}, _Parser_createRemnantNode = function _Parser_createRemnantNode(start, end, depth, parentNode, exposeInvalidNode, exposeWhitespace) {
|
|
1009
|
-
const codeFragment = this.sliceFragment(start, end);
|
|
1010
|
-
if (codeFragment.raw) {
|
|
1011
|
-
const remnantNodes = this.visitText({
|
|
1012
|
-
...codeFragment,
|
|
1013
|
-
depth: depth,
|
|
1014
|
-
parentNode: parentNode,
|
|
1015
|
-
}, { researchTags: true }).filter((node) => 'parentNode' in node);
|
|
1016
|
-
if (remnantNodes.length > 1) {
|
|
1017
|
-
this.appendChild(parentNode, ...remnantNodes);
|
|
1018
|
-
return remnantNodes;
|
|
978
|
+
#exposeRemnantNodes(nodeList, invalidNode, whitespace) {
|
|
979
|
+
if (!invalidNode && !whitespace) {
|
|
980
|
+
return nodeList;
|
|
1019
981
|
}
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
982
|
+
const newNodeList = [];
|
|
983
|
+
for (const [i, node] of nodeList.entries()) {
|
|
984
|
+
const sequentailPrevNode = nodeList[i - 1] ?? null;
|
|
985
|
+
if (!this.#rawTextElements.includes(node.nodeName.toLowerCase())) {
|
|
986
|
+
const prevEndOffset = sequentailPrevNode
|
|
987
|
+
? sequentailPrevNode.offset + sequentailPrevNode.raw.length
|
|
988
|
+
: 0;
|
|
989
|
+
const remnantNodes = this.#createRemnantNode(prevEndOffset, node.offset, node.depth, node.parentNode, invalidNode, whitespace);
|
|
990
|
+
if (remnantNodes) {
|
|
991
|
+
newNodeList.push(...remnantNodes);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
newNodeList.push(node);
|
|
1023
995
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
return [remnantNode];
|
|
996
|
+
const lastNode = newNodeList.at(-1);
|
|
997
|
+
if (!lastNode) {
|
|
998
|
+
return newNodeList;
|
|
1028
999
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
return
|
|
1000
|
+
const remnantNodes = this.#createRemnantNode(lastNode.offset + lastNode.raw.length, undefined, lastNode.depth, lastNode.parentNode, invalidNode, whitespace);
|
|
1001
|
+
if (!remnantNodes) {
|
|
1002
|
+
return newNodeList;
|
|
1032
1003
|
}
|
|
1004
|
+
newNodeList.push(...remnantNodes);
|
|
1005
|
+
return newNodeList;
|
|
1033
1006
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1007
|
+
#getEndLocation(token) {
|
|
1008
|
+
return {
|
|
1009
|
+
offset: token.offset + token.raw.length,
|
|
1010
|
+
line: getEndLine(token.raw, token.line),
|
|
1011
|
+
col: getEndCol(token.raw, token.col),
|
|
1012
|
+
};
|
|
1038
1013
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1014
|
+
/**
|
|
1015
|
+
* Checks whether a node is a descendant of another node by walking up
|
|
1016
|
+
* the parent chain.
|
|
1017
|
+
*
|
|
1018
|
+
* @param node - The node to test.
|
|
1019
|
+
* @param potentialAncestor - The node that may be an ancestor.
|
|
1020
|
+
* @returns `true` if `node` is a descendant of `potentialAncestor`.
|
|
1021
|
+
*/
|
|
1022
|
+
#isDescendantOf(node, potentialAncestor) {
|
|
1023
|
+
let current = 'parentNode' in node ? node.parentNode : null;
|
|
1024
|
+
while (current) {
|
|
1025
|
+
if (current === potentialAncestor) {
|
|
1026
|
+
return true;
|
|
1047
1027
|
}
|
|
1028
|
+
current = current.parentNode;
|
|
1048
1029
|
}
|
|
1049
|
-
|
|
1030
|
+
return false;
|
|
1050
1031
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
return newNodeList;
|
|
1061
|
-
}, _Parser_getEndLocation = function _Parser_getEndLocation(token) {
|
|
1062
|
-
const endOffset = token.startOffset + token.raw.length;
|
|
1063
|
-
return {
|
|
1064
|
-
endOffset,
|
|
1065
|
-
endLine: getEndLine(token.raw, token.startLine),
|
|
1066
|
-
endCol: getEndCol(token.raw, token.startCol),
|
|
1067
|
-
};
|
|
1068
|
-
}, _Parser_orphanEndTagToBogusMark = function _Parser_orphanEndTagToBogusMark(nodeList) {
|
|
1069
|
-
const newNodeList = [];
|
|
1070
|
-
for (let node of nodeList) {
|
|
1071
|
-
if (node.type === 'endtag') {
|
|
1072
|
-
const endTagUUID = node.uuid;
|
|
1073
|
-
const openTag = newNodeList.findLast((n) => n.type === 'starttag' && !n.isGhost ? n.pairNode?.uuid === endTagUUID : false);
|
|
1074
|
-
if (!openTag) {
|
|
1075
|
-
node = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_convertIntoInvalidNode).call(this, node);
|
|
1032
|
+
#orphanEndTagToBogusMark(nodeList) {
|
|
1033
|
+
const newNodeList = [];
|
|
1034
|
+
for (let node of nodeList) {
|
|
1035
|
+
if (node.type === 'endtag') {
|
|
1036
|
+
const endTagUUID = node.uuid;
|
|
1037
|
+
const openTag = newNodeList.findLast((n) => n.type === 'starttag' && !n.isGhost ? n.pairNode?.uuid === endTagUUID : false);
|
|
1038
|
+
if (!openTag) {
|
|
1039
|
+
node = this.#convertIntoInvalidNode(node);
|
|
1040
|
+
}
|
|
1076
1041
|
}
|
|
1042
|
+
newNodeList.push(node);
|
|
1077
1043
|
}
|
|
1078
|
-
newNodeList
|
|
1044
|
+
return newNodeList;
|
|
1079
1045
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
this.appendChild(startTag.parentNode, endTag);
|
|
1085
|
-
}, _Parser_parseEndTag = function _Parser_parseEndTag(token, namelessFragment) {
|
|
1086
|
-
const parsed = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseTag).call(this, token, true, false, namelessFragment);
|
|
1087
|
-
if (!parsed.token || parsed.token.type !== 'endtag') {
|
|
1088
|
-
throw new ParserError("Expected end tag but it's not end tag", token);
|
|
1046
|
+
#pairing(startTag, endTag) {
|
|
1047
|
+
Object.assign(startTag, { pairNode: endTag });
|
|
1048
|
+
Object.assign(endTag, { pairNode: startTag });
|
|
1049
|
+
this.appendChild(startTag.parentNode, endTag);
|
|
1089
1050
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1051
|
+
#parseEndTag(token, namelessFragment) {
|
|
1052
|
+
const parsed = this.#parseTag(token, true, false, namelessFragment);
|
|
1053
|
+
if (!parsed.token || parsed.token.type !== 'endtag') {
|
|
1054
|
+
throw new ParserError("Expected end tag but it's not end tag", token);
|
|
1055
|
+
}
|
|
1056
|
+
return parsed.token;
|
|
1095
1057
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
this.appendChild(token.parentNode, startTag);
|
|
1101
|
-
return startTag;
|
|
1102
|
-
}, _Parser_parseTag = function _Parser_parseTag(token, praseAttr, failSafe, namelessFragment) {
|
|
1103
|
-
const raw = token.raw;
|
|
1104
|
-
const depth = token.depth;
|
|
1105
|
-
const initialOffset = token.startOffset;
|
|
1106
|
-
const initialLine = token.startLine;
|
|
1107
|
-
const initialCol = token.startCol;
|
|
1108
|
-
let offset = initialOffset;
|
|
1109
|
-
let line = initialLine;
|
|
1110
|
-
let col = initialCol;
|
|
1111
|
-
let tagStartOffset = offset;
|
|
1112
|
-
let tagStartLine = line;
|
|
1113
|
-
let tagStartCol = col;
|
|
1114
|
-
let state = TagState.BeforeOpenTag;
|
|
1115
|
-
let beforeOpenTagChars = '';
|
|
1116
|
-
let tagName = '';
|
|
1117
|
-
let afterAttrsSpaceChars = '';
|
|
1118
|
-
let selfClosingSolidusChar = '';
|
|
1119
|
-
let isOpenTag = true;
|
|
1120
|
-
const attrs = [];
|
|
1121
|
-
const chars = [...raw];
|
|
1122
|
-
while (chars.length > 0) {
|
|
1123
|
-
if (state === TagState.AfterOpenTag) {
|
|
1124
|
-
break;
|
|
1058
|
+
#parseStartTag(token, overwriteProps, namelessFragment) {
|
|
1059
|
+
const parsed = this.#parseTag(token, true, false, namelessFragment);
|
|
1060
|
+
if (!parsed.token || parsed.token.type !== 'starttag') {
|
|
1061
|
+
throw new ParserError("Expected start tag but it's not start tag", token);
|
|
1125
1062
|
}
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
break;
|
|
1155
|
-
}
|
|
1156
|
-
if (namelessFragment && char === '>') {
|
|
1157
|
-
state = TagState.AfterOpenTag;
|
|
1158
|
-
break;
|
|
1159
|
-
}
|
|
1160
|
-
chars.unshift(char);
|
|
1161
|
-
state = TagState.AfterOpenTag;
|
|
1063
|
+
const startTag = {
|
|
1064
|
+
...parsed.token,
|
|
1065
|
+
...overwriteProps,
|
|
1066
|
+
};
|
|
1067
|
+
this.appendChild(token.parentNode, startTag);
|
|
1068
|
+
return startTag;
|
|
1069
|
+
}
|
|
1070
|
+
#parseTag(token, praseAttr, failSafe, namelessFragment) {
|
|
1071
|
+
const raw = token.raw;
|
|
1072
|
+
const depth = token.depth;
|
|
1073
|
+
const initialOffset = token.offset;
|
|
1074
|
+
const initialLine = token.line;
|
|
1075
|
+
const initialCol = token.col;
|
|
1076
|
+
let offset = initialOffset;
|
|
1077
|
+
let line = initialLine;
|
|
1078
|
+
let col = initialCol;
|
|
1079
|
+
let tagStartOffset = offset;
|
|
1080
|
+
let tagStartLine = line;
|
|
1081
|
+
let tagStartCol = col;
|
|
1082
|
+
let state = TagState.BeforeOpenTag;
|
|
1083
|
+
let beforeOpenTagChars = '';
|
|
1084
|
+
let tagName = '';
|
|
1085
|
+
let selfClosingSolidusChar = '';
|
|
1086
|
+
let isOpenTag = true;
|
|
1087
|
+
const attrs = [];
|
|
1088
|
+
const chars = [...raw];
|
|
1089
|
+
while (chars.length > 0) {
|
|
1090
|
+
if (state === TagState.AfterOpenTag) {
|
|
1162
1091
|
break;
|
|
1163
1092
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
if (
|
|
1168
|
-
|
|
1169
|
-
offset
|
|
1093
|
+
const char = chars.shift();
|
|
1094
|
+
stateSwitch: switch (state) {
|
|
1095
|
+
case TagState.BeforeOpenTag: {
|
|
1096
|
+
if (char === '<') {
|
|
1097
|
+
const beforeOpenTag = this.createToken(beforeOpenTagChars, offset, line, col);
|
|
1098
|
+
({ offset, line, col } = this.#getEndLocation(beforeOpenTag));
|
|
1099
|
+
tagStartOffset = offset;
|
|
1100
|
+
tagStartLine = line;
|
|
1101
|
+
tagStartCol = col;
|
|
1102
|
+
// Add `<` length
|
|
1170
1103
|
col += 1;
|
|
1104
|
+
offset += 1;
|
|
1105
|
+
state = TagState.FirstCharOfTagName;
|
|
1106
|
+
break;
|
|
1171
1107
|
}
|
|
1172
|
-
|
|
1173
|
-
col += tagName.length;
|
|
1174
|
-
state = TagState.Attrs;
|
|
1108
|
+
beforeOpenTagChars += char;
|
|
1175
1109
|
break;
|
|
1176
1110
|
}
|
|
1177
|
-
|
|
1111
|
+
case TagState.FirstCharOfTagName: {
|
|
1112
|
+
if (/[a-z]/i.test(char)) {
|
|
1113
|
+
tagName += char;
|
|
1114
|
+
state = TagState.TagName;
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
if (char === '/') {
|
|
1118
|
+
isOpenTag = false;
|
|
1119
|
+
break;
|
|
1120
|
+
}
|
|
1121
|
+
if (namelessFragment && char === '>') {
|
|
1122
|
+
state = TagState.AfterOpenTag;
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1178
1125
|
chars.unshift(char);
|
|
1179
|
-
state = TagState.AfterAttrs;
|
|
1180
|
-
break;
|
|
1181
|
-
}
|
|
1182
|
-
if (char === '>') {
|
|
1183
1126
|
state = TagState.AfterOpenTag;
|
|
1184
1127
|
break;
|
|
1185
1128
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1129
|
+
case TagState.TagName: {
|
|
1130
|
+
if (this.#spaceChars.includes(char)) {
|
|
1131
|
+
chars.unshift(char);
|
|
1132
|
+
if (!isOpenTag) {
|
|
1133
|
+
// Add `/` of `</`(close tag) length
|
|
1134
|
+
offset += 1;
|
|
1135
|
+
col += 1;
|
|
1136
|
+
}
|
|
1137
|
+
offset += tagName.length;
|
|
1138
|
+
col += tagName.length;
|
|
1139
|
+
state = TagState.Attrs;
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
if (char === '/') {
|
|
1143
|
+
chars.unshift(char);
|
|
1199
1144
|
state = TagState.AfterAttrs;
|
|
1200
|
-
break
|
|
1145
|
+
break;
|
|
1201
1146
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
startLine: line,
|
|
1206
|
-
startCol: col,
|
|
1207
|
-
});
|
|
1208
|
-
line = attr.endLine;
|
|
1209
|
-
col = attr.endCol;
|
|
1210
|
-
offset = attr.endOffset;
|
|
1211
|
-
if (leftover === attr.__rightText) {
|
|
1212
|
-
throw new SyntaxError(`Invalid attribute syntax: ${leftover}`);
|
|
1147
|
+
if (char === '>') {
|
|
1148
|
+
state = TagState.AfterOpenTag;
|
|
1149
|
+
break;
|
|
1213
1150
|
}
|
|
1214
|
-
|
|
1215
|
-
delete attr.__rightText;
|
|
1216
|
-
attrs.push(attr);
|
|
1217
|
-
}
|
|
1218
|
-
break;
|
|
1219
|
-
}
|
|
1220
|
-
case TagState.AfterAttrs: {
|
|
1221
|
-
if (char === '>') {
|
|
1222
|
-
state = TagState.AfterOpenTag;
|
|
1223
|
-
break;
|
|
1224
|
-
}
|
|
1225
|
-
if (__classPrivateFieldGet(this, _Parser_spaceChars, "f").includes(char)) {
|
|
1226
|
-
afterAttrsSpaceChars += char;
|
|
1151
|
+
tagName += char;
|
|
1227
1152
|
break;
|
|
1228
1153
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1154
|
+
case TagState.Attrs: {
|
|
1155
|
+
if (!praseAttr) {
|
|
1156
|
+
state = TagState.AfterAttrs;
|
|
1157
|
+
break stateSwitch;
|
|
1158
|
+
}
|
|
1159
|
+
let leftover = char + chars.join('');
|
|
1160
|
+
while (leftover.trim()) {
|
|
1161
|
+
if (leftover.trim().startsWith('/') || leftover.trim().startsWith('>')) {
|
|
1162
|
+
chars.length = 0;
|
|
1163
|
+
chars.push(...leftover);
|
|
1164
|
+
state = TagState.AfterAttrs;
|
|
1165
|
+
break stateSwitch;
|
|
1166
|
+
}
|
|
1167
|
+
const attr = this.visitAttr({
|
|
1168
|
+
raw: leftover,
|
|
1169
|
+
offset,
|
|
1170
|
+
line,
|
|
1171
|
+
col,
|
|
1172
|
+
});
|
|
1173
|
+
({ offset, line, col } = this.#getEndLocation(attr));
|
|
1174
|
+
if (leftover === attr.__rightText) {
|
|
1175
|
+
throw new SyntaxError(`Invalid attribute syntax: ${leftover}`);
|
|
1176
|
+
}
|
|
1177
|
+
leftover = attr.__rightText == null ? '' : `${attr.__rightText}`;
|
|
1178
|
+
delete attr.__rightText;
|
|
1179
|
+
attrs.push(attr);
|
|
1180
|
+
}
|
|
1231
1181
|
break;
|
|
1232
1182
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1183
|
+
case TagState.AfterAttrs: {
|
|
1184
|
+
if (char === '>') {
|
|
1185
|
+
state = TagState.AfterOpenTag;
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
if (this.#spaceChars.includes(char)) {
|
|
1189
|
+
break;
|
|
1190
|
+
}
|
|
1191
|
+
if (char === '/') {
|
|
1192
|
+
selfClosingSolidusChar = char;
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
if (!praseAttr) {
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1235
1199
|
}
|
|
1236
|
-
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1237
1200
|
}
|
|
1238
1201
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1202
|
+
const leftover = chars.join('');
|
|
1203
|
+
if (!failSafe && !leftover && state === TagState.TagName) {
|
|
1204
|
+
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1205
|
+
}
|
|
1206
|
+
if (!failSafe && !namelessFragment && tagName === '') {
|
|
1207
|
+
throw new SyntaxError(`No tag name: "${raw}"`);
|
|
1208
|
+
}
|
|
1209
|
+
const rawCodeFragment = raw.slice(beforeOpenTagChars.length, raw.length - leftover.length);
|
|
1210
|
+
if (!rawCodeFragment) {
|
|
1211
|
+
return {
|
|
1212
|
+
__left: beforeOpenTagChars,
|
|
1213
|
+
__right: leftover,
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
const tagToken = this.createToken(rawCodeFragment, tagStartOffset, tagStartLine, tagStartCol);
|
|
1217
|
+
const isFragment = tagName === '';
|
|
1218
|
+
const commons = {
|
|
1219
|
+
depth,
|
|
1220
|
+
nodeName: isFragment ? '#jsx-fragment' : tagName,
|
|
1221
|
+
parentNode: null,
|
|
1222
|
+
};
|
|
1223
|
+
const tag = isOpenTag
|
|
1224
|
+
? {
|
|
1225
|
+
...tagToken,
|
|
1226
|
+
...commons,
|
|
1227
|
+
type: 'starttag',
|
|
1228
|
+
elementType: this.detectElementType(tagName),
|
|
1229
|
+
namespace: 'namespace' in token
|
|
1230
|
+
? token.namespace
|
|
1231
|
+
: getNamespace(tagName, token.parentNode),
|
|
1232
|
+
attributes: attrs,
|
|
1233
|
+
childNodes: [],
|
|
1234
|
+
pairNode: null,
|
|
1235
|
+
tagOpenChar: '<',
|
|
1236
|
+
tagCloseChar: selfClosingSolidusChar + '>',
|
|
1237
|
+
blockBehavior: null,
|
|
1238
|
+
isGhost: false,
|
|
1239
|
+
isFragment,
|
|
1240
|
+
}
|
|
1241
|
+
: {
|
|
1242
|
+
...tagToken,
|
|
1243
|
+
...commons,
|
|
1244
|
+
type: 'endtag',
|
|
1245
|
+
pairNode: {},
|
|
1246
|
+
tagOpenChar: '</',
|
|
1247
|
+
tagCloseChar: '>',
|
|
1248
|
+
};
|
|
1254
1249
|
return {
|
|
1250
|
+
token: tag,
|
|
1255
1251
|
__left: beforeOpenTagChars,
|
|
1256
1252
|
__right: leftover,
|
|
1257
1253
|
};
|
|
1258
1254
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
depth,
|
|
1263
|
-
nodeName: isFragment ? '#jsx-fragment' : tagName,
|
|
1264
|
-
parentNode: null,
|
|
1265
|
-
};
|
|
1266
|
-
const tag = isOpenTag
|
|
1267
|
-
? {
|
|
1268
|
-
...tagToken,
|
|
1269
|
-
...commons,
|
|
1270
|
-
type: 'starttag',
|
|
1271
|
-
elementType: this.detectElementType(tagName),
|
|
1272
|
-
namespace: '',
|
|
1273
|
-
attributes: attrs,
|
|
1274
|
-
childNodes: [],
|
|
1275
|
-
pairNode: null,
|
|
1276
|
-
tagOpenChar: '<',
|
|
1277
|
-
tagCloseChar: selfClosingSolidusChar + '>',
|
|
1278
|
-
selfClosingSolidus,
|
|
1279
|
-
isGhost: false,
|
|
1280
|
-
isFragment,
|
|
1255
|
+
#removeChild(parentNode, ...childNodes) {
|
|
1256
|
+
if (!parentNode || childNodes.length === 0) {
|
|
1257
|
+
return;
|
|
1281
1258
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
...commons,
|
|
1285
|
-
type: 'endtag',
|
|
1286
|
-
pairNode: {},
|
|
1287
|
-
tagOpenChar: '</',
|
|
1288
|
-
tagCloseChar: '>',
|
|
1289
|
-
};
|
|
1290
|
-
return {
|
|
1291
|
-
token: tag,
|
|
1292
|
-
__left: beforeOpenTagChars,
|
|
1293
|
-
__right: leftover,
|
|
1294
|
-
};
|
|
1295
|
-
}, _Parser_removeChild = function _Parser_removeChild(parentNode, ...childNodes) {
|
|
1296
|
-
if (!parentNode || childNodes.length === 0) {
|
|
1297
|
-
return;
|
|
1259
|
+
const newChildNodes = parentNode.childNodes.filter(n => !childNodes.includes(n));
|
|
1260
|
+
Object.assign(parentNode, { childNodes: newChildNodes });
|
|
1298
1261
|
}
|
|
1299
|
-
const newChildNodes = parentNode.childNodes.filter(n => !childNodes.includes(n));
|
|
1300
|
-
Object.assign(parentNode, { childNodes: newChildNodes });
|
|
1301
|
-
}, _Parser_removeDeprecatedNode = function _Parser_removeDeprecatedNode(nodeOrders) {
|
|
1302
|
-
/**
|
|
1303
|
-
* sorting
|
|
1304
|
-
*/
|
|
1305
|
-
const sorted = Array.prototype.toSorted == null
|
|
1306
|
-
? // TODO: Use sort instead of toSorted until we end support for Node 18
|
|
1307
|
-
[...nodeOrders].sort(sortNodes)
|
|
1308
|
-
: nodeOrders.toSorted(sortNodes);
|
|
1309
1262
|
/**
|
|
1310
|
-
*
|
|
1263
|
+
*
|
|
1264
|
+
* @disruptive
|
|
1265
|
+
* @param nodeOrders [Disruptive change]
|
|
1311
1266
|
*/
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1267
|
+
#removeDeprecatedNode(nodeOrders) {
|
|
1268
|
+
/**
|
|
1269
|
+
* sorting
|
|
1270
|
+
*/
|
|
1271
|
+
const sorted = nodeOrders.toSorted(sortNodes);
|
|
1272
|
+
/**
|
|
1273
|
+
* remove duplicated node
|
|
1274
|
+
*/
|
|
1275
|
+
const stack = {};
|
|
1276
|
+
const removeIndexes = [];
|
|
1277
|
+
for (const [i, node] of sorted.entries()) {
|
|
1278
|
+
const id = `${node.offset}::${node.nodeName}`;
|
|
1279
|
+
if (stack[id] != null) {
|
|
1280
|
+
removeIndexes.push(i);
|
|
1281
|
+
}
|
|
1282
|
+
stack[id] = i;
|
|
1318
1283
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
sorted.splice(r, 1);
|
|
1284
|
+
let r = sorted.length;
|
|
1285
|
+
while (r-- > 0) {
|
|
1286
|
+
if (removeIndexes.includes(r)) {
|
|
1287
|
+
sorted.splice(r, 1);
|
|
1288
|
+
}
|
|
1325
1289
|
}
|
|
1290
|
+
return sorted;
|
|
1326
1291
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1292
|
+
#removeOffsetSpaces(nodeList, options) {
|
|
1293
|
+
const offsetOffset = options?.offsetOffset ?? 0;
|
|
1294
|
+
const offsetLine = options?.offsetLine ?? 1;
|
|
1295
|
+
const offsetColumn = options?.offsetColumn ?? 1;
|
|
1296
|
+
if (offsetOffset === 0) {
|
|
1297
|
+
return nodeList;
|
|
1298
|
+
}
|
|
1299
|
+
const firstNode = nodeList.at(0);
|
|
1300
|
+
if (!firstNode || firstNode.type !== 'text') {
|
|
1301
|
+
return nodeList;
|
|
1302
|
+
}
|
|
1303
|
+
const raw = firstNode.raw.slice(offsetOffset);
|
|
1304
|
+
if (!raw) {
|
|
1305
|
+
return nodeList.toSpliced(0, 1);
|
|
1306
|
+
}
|
|
1307
|
+
this.updateRaw(firstNode, raw);
|
|
1308
|
+
this.updateLocation(firstNode, {
|
|
1309
|
+
offset: offsetOffset,
|
|
1310
|
+
line: offsetLine,
|
|
1311
|
+
col: offsetColumn,
|
|
1312
|
+
});
|
|
1333
1313
|
return nodeList;
|
|
1334
1314
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1315
|
+
#reset() {
|
|
1316
|
+
// Reset state
|
|
1317
|
+
this.state = structuredClone(this.#defaultState);
|
|
1318
|
+
this.#defaultDepth = 0;
|
|
1338
1319
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
const newNodeList = [...nodeList];
|
|
1343
|
-
// TODO: Use splice instead of toSpliced until we end support for Node 18
|
|
1344
|
-
newNodeList.splice(0, 1);
|
|
1345
|
-
return newNodeList;
|
|
1346
|
-
}
|
|
1347
|
-
return nodeList.toSpliced(0, 1);
|
|
1320
|
+
#setRawCode(rawCode, originalRawCode) {
|
|
1321
|
+
this.#rawCode = rawCode;
|
|
1322
|
+
this.#originalRawCode = originalRawCode ?? this.#originalRawCode;
|
|
1348
1323
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1324
|
+
/**
|
|
1325
|
+
* Trims text nodes whose source range overlaps with the next node in
|
|
1326
|
+
* the flat list. This prevents text content from bleeding into adjacent
|
|
1327
|
+
* elements that occupy a later (or overlapping) source range.
|
|
1328
|
+
*
|
|
1329
|
+
* Skips trimming when the text node is a descendant of the next node
|
|
1330
|
+
* in the tree hierarchy, because synthetic parsers (e.g., Markdown)
|
|
1331
|
+
* can produce child elements that share the same source range as their
|
|
1332
|
+
* parent and therefore appear before the parent in offset-sorted order.
|
|
1333
|
+
*
|
|
1334
|
+
* @param nodeList - The flat, offset-sorted node list to process.
|
|
1335
|
+
* @returns A new node list with overlapping text nodes trimmed.
|
|
1336
|
+
*/
|
|
1337
|
+
#trimText(nodeList) {
|
|
1338
|
+
const newNodeList = [];
|
|
1339
|
+
let prevNode = null;
|
|
1340
|
+
for (const node of nodeList) {
|
|
1341
|
+
if (prevNode?.type === 'text' &&
|
|
1342
|
+
// Empty node
|
|
1343
|
+
node.raw.length > 0) {
|
|
1344
|
+
const prevNodeEndOffset = prevNode.offset + prevNode.raw.length;
|
|
1345
|
+
const nodeStartOffset = node.offset;
|
|
1346
|
+
if (prevNodeEndOffset > nodeStartOffset && !this.#isDescendantOf(prevNode, node)) {
|
|
1347
|
+
const prevNodeRaw = prevNode.raw;
|
|
1348
|
+
const prevNodeTrimmedRaw = prevNodeRaw.slice(0, nodeStartOffset - prevNode.offset);
|
|
1349
|
+
this.updateRaw(prevNode, prevNodeTrimmedRaw);
|
|
1350
|
+
}
|
|
1376
1351
|
}
|
|
1352
|
+
newNodeList.push(node);
|
|
1353
|
+
prevNode = node;
|
|
1377
1354
|
}
|
|
1378
|
-
newNodeList
|
|
1379
|
-
prevNode = node;
|
|
1355
|
+
return newNodeList;
|
|
1380
1356
|
}
|
|
1381
|
-
|
|
1382
|
-
};
|
|
1357
|
+
}
|