@markuplint/parser-utils 4.8.11 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +6 -6
- package/ARCHITECTURE.md +5 -5
- package/CHANGELOG.md +31 -0
- package/docs/parser-class.ja.md +6 -6
- package/docs/parser-class.md +6 -6
- 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 +502 -552
- 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 +10 -9
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,456 @@ 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
|
-
return nodeList;
|
|
950
|
-
}, _Parser_concatText = function _Parser_concatText(nodeList) {
|
|
951
|
-
const newNodeList = [];
|
|
952
|
-
for (const node of nodeList) {
|
|
953
|
-
const prevNode = newNodeList.at(-1) ?? null;
|
|
954
|
-
if (prevNode?.type === 'text' &&
|
|
955
|
-
prevNode?.nodeName === '#text' &&
|
|
956
|
-
node.type === 'text' &&
|
|
957
|
-
node.nodeName === '#text' &&
|
|
958
|
-
prevNode?.endOffset === node.startOffset) {
|
|
959
|
-
const newNode = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_concatTextNodes).call(this, prevNode, node);
|
|
960
|
-
newNodeList.pop();
|
|
961
|
-
newNodeList.push(newNode);
|
|
962
|
-
continue;
|
|
963
|
-
}
|
|
964
|
-
newNodeList.push(node);
|
|
965
|
-
}
|
|
966
|
-
return newNodeList;
|
|
967
|
-
}, _Parser_concatTextNodes = function _Parser_concatTextNodes(...nodes) {
|
|
968
|
-
if (nodes.length === 0) {
|
|
969
|
-
throw new Error('Empty node list');
|
|
970
|
-
}
|
|
971
|
-
const firstNode = nodes.at(0);
|
|
972
|
-
const lastNode = nodes.at(-1);
|
|
973
|
-
if (firstNode.uuid === lastNode.uuid) {
|
|
974
|
-
return firstNode;
|
|
975
|
-
}
|
|
976
|
-
const textNode = {
|
|
977
|
-
...firstNode,
|
|
978
|
-
uuid: uuid().slice(0, 8),
|
|
979
|
-
raw: nodes.map(n => n.raw).join(''),
|
|
980
|
-
endOffset: lastNode.endOffset,
|
|
981
|
-
endLine: lastNode.endLine,
|
|
982
|
-
endCol: lastNode.endCol,
|
|
983
|
-
};
|
|
984
|
-
for (const node of nodes) {
|
|
985
|
-
__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_removeChild).call(this, node.parentNode, node);
|
|
883
|
+
#arrayize(nodeTree) {
|
|
884
|
+
let nodeList = [];
|
|
885
|
+
this.walk(nodeTree, node => {
|
|
886
|
+
nodeList.push(node);
|
|
887
|
+
});
|
|
888
|
+
nodeList = this.#removeDeprecatedNode(nodeList);
|
|
889
|
+
return nodeList;
|
|
986
890
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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);
|
|
906
|
+
}
|
|
907
|
+
return newNodeList;
|
|
992
908
|
}
|
|
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;
|
|
909
|
+
#concatTextNodes(...nodes) {
|
|
910
|
+
if (nodes.length === 0) {
|
|
911
|
+
throw new Error('Empty node list');
|
|
1019
912
|
}
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
913
|
+
const firstNode = nodes.at(0);
|
|
914
|
+
const lastNode = nodes.at(-1);
|
|
915
|
+
if (firstNode.uuid === lastNode.uuid) {
|
|
916
|
+
return firstNode;
|
|
1023
917
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
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);
|
|
1028
925
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
926
|
+
this.appendChild(textNode.parentNode, textNode);
|
|
927
|
+
return textNode;
|
|
928
|
+
}
|
|
929
|
+
#convertIntoInvalidNode(node) {
|
|
930
|
+
if (node.type === 'invalid') {
|
|
931
|
+
return node;
|
|
1032
932
|
}
|
|
933
|
+
return {
|
|
934
|
+
...node,
|
|
935
|
+
type: 'invalid',
|
|
936
|
+
nodeName: '#invalid',
|
|
937
|
+
isBogus: true,
|
|
938
|
+
kind: node.type,
|
|
939
|
+
};
|
|
1033
940
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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;
|
|
1038
949
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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];
|
|
1047
974
|
}
|
|
1048
975
|
}
|
|
1049
|
-
|
|
976
|
+
return null;
|
|
1050
977
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
978
|
+
#exposeRemnantNodes(nodeList, invalidNode, whitespace) {
|
|
979
|
+
if (!invalidNode && !whitespace) {
|
|
980
|
+
return nodeList;
|
|
981
|
+
}
|
|
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);
|
|
995
|
+
}
|
|
996
|
+
const lastNode = newNodeList.at(-1);
|
|
997
|
+
if (!lastNode) {
|
|
998
|
+
return newNodeList;
|
|
999
|
+
}
|
|
1000
|
+
const remnantNodes = this.#createRemnantNode(lastNode.offset + lastNode.raw.length, undefined, lastNode.depth, lastNode.parentNode, invalidNode, whitespace);
|
|
1001
|
+
if (!remnantNodes) {
|
|
1002
|
+
return newNodeList;
|
|
1003
|
+
}
|
|
1004
|
+
newNodeList.push(...remnantNodes);
|
|
1053
1005
|
return newNodeList;
|
|
1054
1006
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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
|
+
};
|
|
1058
1013
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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);
|
|
1014
|
+
#orphanEndTagToBogusMark(nodeList) {
|
|
1015
|
+
const newNodeList = [];
|
|
1016
|
+
for (let node of nodeList) {
|
|
1017
|
+
if (node.type === 'endtag') {
|
|
1018
|
+
const endTagUUID = node.uuid;
|
|
1019
|
+
const openTag = newNodeList.findLast((n) => n.type === 'starttag' && !n.isGhost ? n.pairNode?.uuid === endTagUUID : false);
|
|
1020
|
+
if (!openTag) {
|
|
1021
|
+
node = this.#convertIntoInvalidNode(node);
|
|
1022
|
+
}
|
|
1076
1023
|
}
|
|
1024
|
+
newNodeList.push(node);
|
|
1077
1025
|
}
|
|
1078
|
-
newNodeList
|
|
1026
|
+
return newNodeList;
|
|
1079
1027
|
}
|
|
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);
|
|
1028
|
+
#pairing(startTag, endTag) {
|
|
1029
|
+
Object.assign(startTag, { pairNode: endTag });
|
|
1030
|
+
Object.assign(endTag, { pairNode: startTag });
|
|
1031
|
+
this.appendChild(startTag.parentNode, endTag);
|
|
1089
1032
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1033
|
+
#parseEndTag(token, namelessFragment) {
|
|
1034
|
+
const parsed = this.#parseTag(token, true, false, namelessFragment);
|
|
1035
|
+
if (!parsed.token || parsed.token.type !== 'endtag') {
|
|
1036
|
+
throw new ParserError("Expected end tag but it's not end tag", token);
|
|
1037
|
+
}
|
|
1038
|
+
return parsed.token;
|
|
1095
1039
|
}
|
|
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;
|
|
1040
|
+
#parseStartTag(token, overwriteProps, namelessFragment) {
|
|
1041
|
+
const parsed = this.#parseTag(token, true, false, namelessFragment);
|
|
1042
|
+
if (!parsed.token || parsed.token.type !== 'starttag') {
|
|
1043
|
+
throw new ParserError("Expected start tag but it's not start tag", token);
|
|
1125
1044
|
}
|
|
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;
|
|
1045
|
+
const startTag = {
|
|
1046
|
+
...parsed.token,
|
|
1047
|
+
...overwriteProps,
|
|
1048
|
+
};
|
|
1049
|
+
this.appendChild(token.parentNode, startTag);
|
|
1050
|
+
return startTag;
|
|
1051
|
+
}
|
|
1052
|
+
#parseTag(token, praseAttr, failSafe, namelessFragment) {
|
|
1053
|
+
const raw = token.raw;
|
|
1054
|
+
const depth = token.depth;
|
|
1055
|
+
const initialOffset = token.offset;
|
|
1056
|
+
const initialLine = token.line;
|
|
1057
|
+
const initialCol = token.col;
|
|
1058
|
+
let offset = initialOffset;
|
|
1059
|
+
let line = initialLine;
|
|
1060
|
+
let col = initialCol;
|
|
1061
|
+
let tagStartOffset = offset;
|
|
1062
|
+
let tagStartLine = line;
|
|
1063
|
+
let tagStartCol = col;
|
|
1064
|
+
let state = TagState.BeforeOpenTag;
|
|
1065
|
+
let beforeOpenTagChars = '';
|
|
1066
|
+
let tagName = '';
|
|
1067
|
+
let selfClosingSolidusChar = '';
|
|
1068
|
+
let isOpenTag = true;
|
|
1069
|
+
const attrs = [];
|
|
1070
|
+
const chars = [...raw];
|
|
1071
|
+
while (chars.length > 0) {
|
|
1072
|
+
if (state === TagState.AfterOpenTag) {
|
|
1162
1073
|
break;
|
|
1163
1074
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
if (
|
|
1168
|
-
|
|
1169
|
-
offset
|
|
1075
|
+
const char = chars.shift();
|
|
1076
|
+
stateSwitch: switch (state) {
|
|
1077
|
+
case TagState.BeforeOpenTag: {
|
|
1078
|
+
if (char === '<') {
|
|
1079
|
+
const beforeOpenTag = this.createToken(beforeOpenTagChars, offset, line, col);
|
|
1080
|
+
({ offset, line, col } = this.#getEndLocation(beforeOpenTag));
|
|
1081
|
+
tagStartOffset = offset;
|
|
1082
|
+
tagStartLine = line;
|
|
1083
|
+
tagStartCol = col;
|
|
1084
|
+
// Add `<` length
|
|
1170
1085
|
col += 1;
|
|
1086
|
+
offset += 1;
|
|
1087
|
+
state = TagState.FirstCharOfTagName;
|
|
1088
|
+
break;
|
|
1171
1089
|
}
|
|
1172
|
-
|
|
1173
|
-
col += tagName.length;
|
|
1174
|
-
state = TagState.Attrs;
|
|
1090
|
+
beforeOpenTagChars += char;
|
|
1175
1091
|
break;
|
|
1176
1092
|
}
|
|
1177
|
-
|
|
1093
|
+
case TagState.FirstCharOfTagName: {
|
|
1094
|
+
if (/[a-z]/i.test(char)) {
|
|
1095
|
+
tagName += char;
|
|
1096
|
+
state = TagState.TagName;
|
|
1097
|
+
break;
|
|
1098
|
+
}
|
|
1099
|
+
if (char === '/') {
|
|
1100
|
+
isOpenTag = false;
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
if (namelessFragment && char === '>') {
|
|
1104
|
+
state = TagState.AfterOpenTag;
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1178
1107
|
chars.unshift(char);
|
|
1179
|
-
state = TagState.AfterAttrs;
|
|
1180
|
-
break;
|
|
1181
|
-
}
|
|
1182
|
-
if (char === '>') {
|
|
1183
1108
|
state = TagState.AfterOpenTag;
|
|
1184
1109
|
break;
|
|
1185
1110
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1111
|
+
case TagState.TagName: {
|
|
1112
|
+
if (this.#spaceChars.includes(char)) {
|
|
1113
|
+
chars.unshift(char);
|
|
1114
|
+
if (!isOpenTag) {
|
|
1115
|
+
// Add `/` of `</`(close tag) length
|
|
1116
|
+
offset += 1;
|
|
1117
|
+
col += 1;
|
|
1118
|
+
}
|
|
1119
|
+
offset += tagName.length;
|
|
1120
|
+
col += tagName.length;
|
|
1121
|
+
state = TagState.Attrs;
|
|
1122
|
+
break;
|
|
1123
|
+
}
|
|
1124
|
+
if (char === '/') {
|
|
1125
|
+
chars.unshift(char);
|
|
1199
1126
|
state = TagState.AfterAttrs;
|
|
1200
|
-
break
|
|
1127
|
+
break;
|
|
1201
1128
|
}
|
|
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}`);
|
|
1129
|
+
if (char === '>') {
|
|
1130
|
+
state = TagState.AfterOpenTag;
|
|
1131
|
+
break;
|
|
1213
1132
|
}
|
|
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;
|
|
1133
|
+
tagName += char;
|
|
1227
1134
|
break;
|
|
1228
1135
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1136
|
+
case TagState.Attrs: {
|
|
1137
|
+
if (!praseAttr) {
|
|
1138
|
+
state = TagState.AfterAttrs;
|
|
1139
|
+
break stateSwitch;
|
|
1140
|
+
}
|
|
1141
|
+
let leftover = char + chars.join('');
|
|
1142
|
+
while (leftover.trim()) {
|
|
1143
|
+
if (leftover.trim().startsWith('/') || leftover.trim().startsWith('>')) {
|
|
1144
|
+
chars.length = 0;
|
|
1145
|
+
chars.push(...leftover);
|
|
1146
|
+
state = TagState.AfterAttrs;
|
|
1147
|
+
break stateSwitch;
|
|
1148
|
+
}
|
|
1149
|
+
const attr = this.visitAttr({
|
|
1150
|
+
raw: leftover,
|
|
1151
|
+
offset,
|
|
1152
|
+
line,
|
|
1153
|
+
col,
|
|
1154
|
+
});
|
|
1155
|
+
({ offset, line, col } = this.#getEndLocation(attr));
|
|
1156
|
+
if (leftover === attr.__rightText) {
|
|
1157
|
+
throw new SyntaxError(`Invalid attribute syntax: ${leftover}`);
|
|
1158
|
+
}
|
|
1159
|
+
leftover = attr.__rightText == null ? '' : `${attr.__rightText}`;
|
|
1160
|
+
delete attr.__rightText;
|
|
1161
|
+
attrs.push(attr);
|
|
1162
|
+
}
|
|
1231
1163
|
break;
|
|
1232
1164
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1165
|
+
case TagState.AfterAttrs: {
|
|
1166
|
+
if (char === '>') {
|
|
1167
|
+
state = TagState.AfterOpenTag;
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
if (this.#spaceChars.includes(char)) {
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
if (char === '/') {
|
|
1174
|
+
selfClosingSolidusChar = char;
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
if (!praseAttr) {
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1235
1181
|
}
|
|
1236
|
-
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1237
1182
|
}
|
|
1238
1183
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1184
|
+
const leftover = chars.join('');
|
|
1185
|
+
if (!failSafe && !leftover && state === TagState.TagName) {
|
|
1186
|
+
throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
|
|
1187
|
+
}
|
|
1188
|
+
if (!failSafe && !namelessFragment && tagName === '') {
|
|
1189
|
+
throw new SyntaxError(`No tag name: "${raw}"`);
|
|
1190
|
+
}
|
|
1191
|
+
const rawCodeFragment = raw.slice(beforeOpenTagChars.length, raw.length - leftover.length);
|
|
1192
|
+
if (!rawCodeFragment) {
|
|
1193
|
+
return {
|
|
1194
|
+
__left: beforeOpenTagChars,
|
|
1195
|
+
__right: leftover,
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
const tagToken = this.createToken(rawCodeFragment, tagStartOffset, tagStartLine, tagStartCol);
|
|
1199
|
+
const isFragment = tagName === '';
|
|
1200
|
+
const commons = {
|
|
1201
|
+
depth,
|
|
1202
|
+
nodeName: isFragment ? '#jsx-fragment' : tagName,
|
|
1203
|
+
parentNode: null,
|
|
1204
|
+
};
|
|
1205
|
+
const tag = isOpenTag
|
|
1206
|
+
? {
|
|
1207
|
+
...tagToken,
|
|
1208
|
+
...commons,
|
|
1209
|
+
type: 'starttag',
|
|
1210
|
+
elementType: this.detectElementType(tagName),
|
|
1211
|
+
namespace: 'namespace' in token
|
|
1212
|
+
? token.namespace
|
|
1213
|
+
: getNamespace(tagName, token.parentNode),
|
|
1214
|
+
attributes: attrs,
|
|
1215
|
+
childNodes: [],
|
|
1216
|
+
pairNode: null,
|
|
1217
|
+
tagOpenChar: '<',
|
|
1218
|
+
tagCloseChar: selfClosingSolidusChar + '>',
|
|
1219
|
+
blockBehavior: null,
|
|
1220
|
+
isGhost: false,
|
|
1221
|
+
isFragment,
|
|
1222
|
+
}
|
|
1223
|
+
: {
|
|
1224
|
+
...tagToken,
|
|
1225
|
+
...commons,
|
|
1226
|
+
type: 'endtag',
|
|
1227
|
+
pairNode: {},
|
|
1228
|
+
tagOpenChar: '</',
|
|
1229
|
+
tagCloseChar: '>',
|
|
1230
|
+
};
|
|
1254
1231
|
return {
|
|
1232
|
+
token: tag,
|
|
1255
1233
|
__left: beforeOpenTagChars,
|
|
1256
1234
|
__right: leftover,
|
|
1257
1235
|
};
|
|
1258
1236
|
}
|
|
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,
|
|
1237
|
+
#removeChild(parentNode, ...childNodes) {
|
|
1238
|
+
if (!parentNode || childNodes.length === 0) {
|
|
1239
|
+
return;
|
|
1281
1240
|
}
|
|
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;
|
|
1241
|
+
const newChildNodes = parentNode.childNodes.filter(n => !childNodes.includes(n));
|
|
1242
|
+
Object.assign(parentNode, { childNodes: newChildNodes });
|
|
1298
1243
|
}
|
|
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
1244
|
/**
|
|
1310
|
-
*
|
|
1245
|
+
*
|
|
1246
|
+
* @disruptive
|
|
1247
|
+
* @param nodeOrders [Disruptive change]
|
|
1311
1248
|
*/
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1249
|
+
#removeDeprecatedNode(nodeOrders) {
|
|
1250
|
+
/**
|
|
1251
|
+
* sorting
|
|
1252
|
+
*/
|
|
1253
|
+
const sorted = nodeOrders.toSorted(sortNodes);
|
|
1254
|
+
/**
|
|
1255
|
+
* remove duplicated node
|
|
1256
|
+
*/
|
|
1257
|
+
const stack = {};
|
|
1258
|
+
const removeIndexes = [];
|
|
1259
|
+
for (const [i, node] of sorted.entries()) {
|
|
1260
|
+
const id = `${node.offset}::${node.nodeName}`;
|
|
1261
|
+
if (stack[id] != null) {
|
|
1262
|
+
removeIndexes.push(i);
|
|
1263
|
+
}
|
|
1264
|
+
stack[id] = i;
|
|
1318
1265
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
sorted.splice(r, 1);
|
|
1266
|
+
let r = sorted.length;
|
|
1267
|
+
while (r-- > 0) {
|
|
1268
|
+
if (removeIndexes.includes(r)) {
|
|
1269
|
+
sorted.splice(r, 1);
|
|
1270
|
+
}
|
|
1325
1271
|
}
|
|
1272
|
+
return sorted;
|
|
1326
1273
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1274
|
+
#removeOffsetSpaces(nodeList, options) {
|
|
1275
|
+
const offsetOffset = options?.offsetOffset ?? 0;
|
|
1276
|
+
const offsetLine = options?.offsetLine ?? 1;
|
|
1277
|
+
const offsetColumn = options?.offsetColumn ?? 1;
|
|
1278
|
+
if (offsetOffset === 0) {
|
|
1279
|
+
return nodeList;
|
|
1280
|
+
}
|
|
1281
|
+
const firstNode = nodeList.at(0);
|
|
1282
|
+
if (!firstNode || firstNode.type !== 'text') {
|
|
1283
|
+
return nodeList;
|
|
1284
|
+
}
|
|
1285
|
+
const raw = firstNode.raw.slice(offsetOffset);
|
|
1286
|
+
if (!raw) {
|
|
1287
|
+
return nodeList.toSpliced(0, 1);
|
|
1288
|
+
}
|
|
1289
|
+
this.updateRaw(firstNode, raw);
|
|
1290
|
+
this.updateLocation(firstNode, {
|
|
1291
|
+
offset: offsetOffset,
|
|
1292
|
+
line: offsetLine,
|
|
1293
|
+
col: offsetColumn,
|
|
1294
|
+
});
|
|
1333
1295
|
return nodeList;
|
|
1334
1296
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1297
|
+
#reset() {
|
|
1298
|
+
// Reset state
|
|
1299
|
+
this.state = structuredClone(this.#defaultState);
|
|
1300
|
+
this.#defaultDepth = 0;
|
|
1338
1301
|
}
|
|
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);
|
|
1302
|
+
#setRawCode(rawCode, originalRawCode) {
|
|
1303
|
+
this.#rawCode = rawCode;
|
|
1304
|
+
this.#originalRawCode = originalRawCode ?? this.#originalRawCode;
|
|
1348
1305
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
node.startOffset !== node.endOffset) {
|
|
1370
|
-
const prevNodeEndOffset = prevNode.endOffset;
|
|
1371
|
-
const nodeStartOffset = node.startOffset;
|
|
1372
|
-
if (prevNodeEndOffset > nodeStartOffset) {
|
|
1373
|
-
const prevNodeRaw = prevNode.raw;
|
|
1374
|
-
const prevNodeTrimmedRaw = prevNodeRaw.slice(0, nodeStartOffset - prevNode.startOffset);
|
|
1375
|
-
this.updateRaw(prevNode, prevNodeTrimmedRaw);
|
|
1306
|
+
/**
|
|
1307
|
+
* Trim overlapping sections of text nodes for proper node separation
|
|
1308
|
+
*
|
|
1309
|
+
* @param nodeList
|
|
1310
|
+
* @returns
|
|
1311
|
+
*/
|
|
1312
|
+
#trimText(nodeList) {
|
|
1313
|
+
const newNodeList = [];
|
|
1314
|
+
let prevNode = null;
|
|
1315
|
+
for (const node of nodeList) {
|
|
1316
|
+
if (prevNode?.type === 'text' &&
|
|
1317
|
+
// Empty node
|
|
1318
|
+
node.raw.length > 0) {
|
|
1319
|
+
const prevNodeEndOffset = prevNode.offset + prevNode.raw.length;
|
|
1320
|
+
const nodeStartOffset = node.offset;
|
|
1321
|
+
if (prevNodeEndOffset > nodeStartOffset) {
|
|
1322
|
+
const prevNodeRaw = prevNode.raw;
|
|
1323
|
+
const prevNodeTrimmedRaw = prevNodeRaw.slice(0, nodeStartOffset - prevNode.offset);
|
|
1324
|
+
this.updateRaw(prevNode, prevNodeTrimmedRaw);
|
|
1325
|
+
}
|
|
1376
1326
|
}
|
|
1327
|
+
newNodeList.push(node);
|
|
1328
|
+
prevNode = node;
|
|
1377
1329
|
}
|
|
1378
|
-
newNodeList
|
|
1379
|
-
prevNode = node;
|
|
1330
|
+
return newNodeList;
|
|
1380
1331
|
}
|
|
1381
|
-
|
|
1382
|
-
};
|
|
1332
|
+
}
|