@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/lib/parser.js CHANGED
@@ -1,17 +1,5 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
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
- _Parser_instances.add(this);
44
- _Parser_booleanish.set(this, false);
45
- _Parser_defaultState.set(this, void 0);
46
- _Parser_endTagType.set(this, 'omittable');
47
- _Parser_ignoreTags.set(this, []);
48
- _Parser_maskChar.set(this, void 0);
49
- _Parser_tagNameCaseSensitive.set(this, false);
50
- _Parser_selfCloseType.set(this, 'html');
51
- _Parser_spaceChars.set(this, defaultSpaces);
52
- _Parser_rawTextElements.set(this, ['style', 'script']);
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 __classPrivateFieldGet(this, _Parser_authoredElementName, "f");
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 __classPrivateFieldGet(this, _Parser_booleanish, "f");
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 __classPrivateFieldGet(this, _Parser_endTagType, "f");
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 __classPrivateFieldGet(this, _Parser_rawCode, "f");
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 __classPrivateFieldGet(this, _Parser_tagNameCaseSensitive, "f");
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 = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_createOffsetSpaces).call(this, options);
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
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, rawCode, rawCode);
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
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, beforeParsedCode);
156
- __classPrivateFieldSet(this, _Parser_authoredElementName, options?.authoredElementName, "f");
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
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, fm.code);
150
+ this.#setRawCode(fm.code);
162
151
  frontMatter = fm.frontMatter;
163
152
  }
164
153
  timer.push('ignoreBlock');
165
- const blocks = ignoreBlock(this.rawCode, __classPrivateFieldGet(this, _Parser_ignoreTags, "f"), __classPrivateFieldGet(this, _Parser_maskChar, "f"));
166
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, blocks.replaced);
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
- __classPrivateFieldSet(this, _Parser_defaultDepth, options?.depth ?? __classPrivateFieldGet(this, _Parser_defaultDepth, "f"), "f");
160
+ this.#defaultDepth = options?.depth ?? this.#defaultDepth;
172
161
  timer.push('traverse');
173
- const traversed = this.traverse(ast, null, __classPrivateFieldGet(this, _Parser_defaultDepth, "f"));
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
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_reset).call(this);
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 __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_removeOffsetSpaces).call(this, nodeList, options);
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.startOffset}:${node.endOffset}:${node.nodeName}:${node.type}:${node.raw}`;
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 Array.prototype.toSorted == null
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 __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_arrayize).call(this, nodeTree);
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 = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_exposeRemnantNodes).call(this, nodeList, exposeInvalidNode, exposeWhiteSpace);
377
- nodeList = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_orphanEndTagToBogusMark).call(this, nodeList);
362
+ nodeList = this.#exposeRemnantNodes(nodeList, exposeInvalidNode, exposeWhiteSpace);
363
+ nodeList = this.#orphanEndTagToBogusMark(nodeList);
378
364
  if (concatText) {
379
- nodeList = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_concatText).call(this, nodeList);
365
+ nodeList = this.#concatText(nodeList);
380
366
  }
381
- nodeList = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_trimText).call(this, 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 namespace
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
- ...__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseStartTag).call(this, token, {
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 = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseEndTag).call(this, endTagToken, namelessFragment);
494
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_pairing).call(this, startTag, endTag);
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 conditionalType - The conditional type if this is a conditional block (e.g., "if", "else")
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 = [], conditionalType = null, originBlockNode) {
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
- conditionalType,
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 && __classPrivateFieldGet(this, _Parser_rawTextElements, "f").includes(parentNode.nodeName.toLowerCase())) {
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.startOffset, token.startLine, token.startCol);
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 startOffset = token.startOffset;
590
- let startLine = token.startLine;
591
- let startCol = token.startCol;
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, startOffset, startLine, startCol);
603
- startLine = spacesBeforeName.endLine;
604
- startCol = spacesBeforeName.endCol;
605
- startOffset = spacesBeforeName.endOffset;
606
- const name = this.createToken(tokens.attrName, startOffset, startLine, startCol);
607
- startLine = name.endLine;
608
- startCol = name.endCol;
609
- startOffset = name.endOffset;
610
- const spacesBeforeEqual = this.createToken(tokens.spacesBeforeEqual, startOffset, startLine, startCol);
611
- startLine = spacesBeforeEqual.endLine;
612
- startCol = spacesBeforeEqual.endCol;
613
- startOffset = spacesBeforeEqual.endOffset;
614
- const equal = this.createToken(tokens.equal, startOffset, startLine, startCol);
615
- startLine = equal.endLine;
616
- startCol = equal.endCol;
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.startOffset, name.startLine, name.startCol);
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 startOffset = token.startOffset;
674
- let startLine = token.startLine;
675
- let startCol = token.startCol;
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 = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseTag).call(this, {
651
+ const parsed = this.#parseTag({
680
652
  raw,
681
- startOffset,
682
- startLine,
683
- startCol,
653
+ offset: curOffset,
654
+ line: curLine,
655
+ col: curCol,
684
656
  depth,
685
- parentNode: null,
657
+ parentNode: token.parentNode,
686
658
  }, true, true, options?.namelessFragment ?? false);
687
659
  if (parsed.__left) {
688
- const token = this.createToken(parsed.__left, startOffset, startLine, startCol);
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
- startLine = tag.endLine;
704
- startCol = tag.endCol;
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 (__classPrivateFieldGet(this, _Parser_selfCloseType, "f")) {
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, __classPrivateFieldGet(this, _Parser_defaultDepth, "f"));
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, recalculating
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
- startOffset: props.startOffset ?? node.startOffset,
764
- startLine: props.startLine ?? node.startLine,
765
- startCol: props.startCol ?? node.startCol,
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, __classPrivateFieldGet(this, _Parser_authoredElementName, "f"), defaultPattern);
772
+ return detectElementType(nodeName, this.#authoredElementName, defaultPattern);
821
773
  }
822
- createToken(token, startOffset, startLine, startCol) {
774
+ createToken(token, offset, line, col) {
823
775
  const props = typeof token === 'string'
824
776
  ? {
825
777
  raw: token,
826
- startOffset: startOffset ?? 0,
827
- startLine: startLine ?? 1,
828
- startCol: startCol ?? 1,
778
+ offset: offset ?? 0,
779
+ line: line ?? 1,
780
+ col: col ?? 1,
829
781
  }
830
782
  : token;
831
783
  return {
832
- uuid: uuid().slice(0, 8),
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
- startOffset: start,
851
- startLine: line,
852
- startCol: column,
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, __classPrivateFieldGet(this, _Parser_walkMethodSequentailPrevNode, "f"), depth);
881
- __classPrivateFieldSet(this, _Parser_walkMethodSequentailPrevNode, node, "f");
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
- __classPrivateFieldSet(this, _Parser_walkMethodSequentailPrevNode, null, "f");
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: Array.prototype.toSorted == null
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
- _Parser_booleanish = new WeakMap(), _Parser_defaultState = new WeakMap(), _Parser_endTagType = new WeakMap(), _Parser_ignoreTags = new WeakMap(), _Parser_maskChar = new WeakMap(), _Parser_tagNameCaseSensitive = new WeakMap(), _Parser_selfCloseType = new WeakMap(), _Parser_spaceChars = new WeakMap(), _Parser_rawTextElements = new WeakMap(), _Parser_authoredElementName = new WeakMap(), _Parser_originalRawCode = new WeakMap(), _Parser_rawCode = new WeakMap(), _Parser_defaultDepth = new WeakMap(), _Parser_walkMethodSequentailPrevNode = new WeakMap(), _Parser_instances = new WeakSet(), _Parser_arrayize = function _Parser_arrayize(nodeTree) {
944
- let nodeList = [];
945
- this.walk(nodeTree, node => {
946
- nodeList.push(node);
947
- });
948
- nodeList = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_removeDeprecatedNode).call(this, nodeList);
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
- this.appendChild(textNode.parentNode, textNode);
988
- return textNode;
989
- }, _Parser_convertIntoInvalidNode = function _Parser_convertIntoInvalidNode(node) {
990
- if (node.type === 'invalid') {
991
- return node;
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
- return {
994
- ...node,
995
- type: 'invalid',
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 remnantNode = remnantNodes[0];
1021
- if (!remnantNode) {
1022
- return null;
913
+ const firstNode = nodes.at(0);
914
+ const lastNode = nodes.at(-1);
915
+ if (firstNode.uuid === lastNode.uuid) {
916
+ return firstNode;
1023
917
  }
1024
- if (exposeInvalidNode && remnantNode.raw.trim() !== '') {
1025
- const invalidNode = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_convertIntoInvalidNode).call(this, remnantNode);
1026
- this.appendChild(parentNode, invalidNode);
1027
- return [remnantNode];
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
- if (exposeWhitespace && remnantNode.type === 'text' && remnantNode.raw.trim() === '') {
1030
- this.appendChild(parentNode, remnantNode);
1031
- return [remnantNode];
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
- return null;
1035
- }, _Parser_exposeRemnantNodes = function _Parser_exposeRemnantNodes(nodeList, invalidNode, whitespace) {
1036
- if (!invalidNode && !whitespace) {
1037
- return nodeList;
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
- const newNodeList = [];
1040
- for (const [i, node] of nodeList.entries()) {
1041
- const sequentailPrevNode = nodeList[i - 1] ?? null;
1042
- if (!__classPrivateFieldGet(this, _Parser_rawTextElements, "f").includes(node.nodeName.toLowerCase())) {
1043
- const endOffset = sequentailPrevNode?.endOffset ?? 0;
1044
- const remnantNodes = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_createRemnantNode).call(this, endOffset, node.startOffset, node.depth, node.parentNode, invalidNode, whitespace);
1045
- if (remnantNodes) {
1046
- newNodeList.push(...remnantNodes);
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
- newNodeList.push(node);
976
+ return null;
1050
977
  }
1051
- const lastNode = newNodeList.at(-1);
1052
- if (!lastNode) {
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
- const remnantNodes = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_createRemnantNode).call(this, lastNode.endOffset, undefined, lastNode.depth, lastNode.parentNode, invalidNode, whitespace);
1056
- if (!remnantNodes) {
1057
- return newNodeList;
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
- newNodeList.push(...remnantNodes);
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);
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.push(node);
1026
+ return newNodeList;
1079
1027
  }
1080
- return newNodeList;
1081
- }, _Parser_pairing = function _Parser_pairing(startTag, endTag) {
1082
- Object.assign(startTag, { pairNode: endTag });
1083
- Object.assign(endTag, { pairNode: startTag });
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
- return parsed.token;
1091
- }, _Parser_parseStartTag = function _Parser_parseStartTag(token, overwriteProps, namelessFragment) {
1092
- const parsed = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseTag).call(this, token, true, false, namelessFragment);
1093
- if (!parsed.token || parsed.token.type !== 'starttag') {
1094
- throw new ParserError("Expected start tag but it's not start tag", token);
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
- const startTag = {
1097
- ...parsed.token,
1098
- ...overwriteProps,
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 char = chars.shift();
1127
- stateSwitch: switch (state) {
1128
- case TagState.BeforeOpenTag: {
1129
- if (char === '<') {
1130
- const beforeOpenTag = this.createToken(beforeOpenTagChars, offset, line, col);
1131
- line = beforeOpenTag.endLine;
1132
- col = beforeOpenTag.endCol;
1133
- offset = beforeOpenTag.endOffset;
1134
- tagStartOffset = offset;
1135
- tagStartLine = line;
1136
- tagStartCol = col;
1137
- // Add `<` length
1138
- col += 1;
1139
- offset += 1;
1140
- state = TagState.FirstCharOfTagName;
1141
- break;
1142
- }
1143
- beforeOpenTagChars += char;
1144
- break;
1145
- }
1146
- case TagState.FirstCharOfTagName: {
1147
- if (/[a-z]/i.test(char)) {
1148
- tagName += char;
1149
- state = TagState.TagName;
1150
- break;
1151
- }
1152
- if (char === '/') {
1153
- isOpenTag = false;
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
- case TagState.TagName: {
1165
- if (__classPrivateFieldGet(this, _Parser_spaceChars, "f").includes(char)) {
1166
- chars.unshift(char);
1167
- if (!isOpenTag) {
1168
- // Add `/` of `</`(close tag) length
1169
- offset += 1;
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
- offset += tagName.length;
1173
- col += tagName.length;
1174
- state = TagState.Attrs;
1090
+ beforeOpenTagChars += char;
1175
1091
  break;
1176
1092
  }
1177
- if (char === '/') {
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
- tagName += char;
1187
- break;
1188
- }
1189
- case TagState.Attrs: {
1190
- if (!praseAttr) {
1191
- state = TagState.AfterAttrs;
1192
- break stateSwitch;
1193
- }
1194
- let leftover = char + chars.join('');
1195
- while (leftover.trim()) {
1196
- if (leftover.trim().startsWith('/') || leftover.trim().startsWith('>')) {
1197
- chars.length = 0;
1198
- chars.push(...leftover);
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 stateSwitch;
1127
+ break;
1201
1128
  }
1202
- const attr = this.visitAttr({
1203
- raw: leftover,
1204
- startOffset: offset,
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
- leftover = attr.__rightText == null ? '' : `${attr.__rightText}`;
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
- if (char === '/') {
1230
- selfClosingSolidusChar = char;
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
- if (!praseAttr) {
1234
- break;
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
- const leftover = chars.join('');
1241
- if (!failSafe && !leftover && state === TagState.TagName) {
1242
- throw new SyntaxError(`Invalid tag syntax: "${raw}"`);
1243
- }
1244
- if (!failSafe && !namelessFragment && tagName === '') {
1245
- throw new SyntaxError(`No tag name: "${raw}"`);
1246
- }
1247
- const endSpace = this.createToken(afterAttrsSpaceChars, offset, line, col);
1248
- line = endSpace.endLine;
1249
- col = endSpace.endCol;
1250
- offset = endSpace.endOffset;
1251
- const selfClosingSolidus = this.createToken(selfClosingSolidusChar, offset, line, col);
1252
- const rawCodeFragment = raw.slice(beforeOpenTagChars.length, raw.length - leftover.length);
1253
- if (!rawCodeFragment) {
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
- const tagToken = this.createToken(rawCodeFragment, tagStartOffset, tagStartLine, tagStartCol);
1260
- const isFragment = tagName === '';
1261
- const commons = {
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
- ...tagToken,
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
- * remove duplicated node
1245
+ *
1246
+ * @disruptive
1247
+ * @param nodeOrders [Disruptive change]
1311
1248
  */
1312
- const stack = {};
1313
- const removeIndexes = [];
1314
- for (const [i, node] of sorted.entries()) {
1315
- const id = `${node.startOffset}::${node.nodeName}`;
1316
- if (stack[id] != null) {
1317
- removeIndexes.push(i);
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
- stack[id] = i;
1320
- }
1321
- let r = sorted.length;
1322
- while (r-- > 0) {
1323
- if (removeIndexes.includes(r)) {
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
- return sorted;
1328
- }, _Parser_removeOffsetSpaces = function _Parser_removeOffsetSpaces(nodeList, options) {
1329
- const offsetOffset = options?.offsetOffset ?? 0;
1330
- const offsetLine = options?.offsetLine ?? 1;
1331
- const offsetColumn = options?.offsetColumn ?? 1;
1332
- if (offsetOffset === 0) {
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
- const firstNode = nodeList.at(0);
1336
- if (!firstNode || firstNode.type !== 'text') {
1337
- return nodeList;
1297
+ #reset() {
1298
+ // Reset state
1299
+ this.state = structuredClone(this.#defaultState);
1300
+ this.#defaultDepth = 0;
1338
1301
  }
1339
- const raw = firstNode.raw.slice(offsetOffset);
1340
- if (!raw) {
1341
- if (Array.prototype.toSpliced == null) {
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
- this.updateRaw(firstNode, raw);
1350
- this.updateLocation(firstNode, {
1351
- startOffset: offsetOffset,
1352
- startLine: offsetLine,
1353
- startCol: offsetColumn,
1354
- });
1355
- return nodeList;
1356
- }, _Parser_reset = function _Parser_reset() {
1357
- // Reset state
1358
- this.state = structuredClone(__classPrivateFieldGet(this, _Parser_defaultState, "f"));
1359
- __classPrivateFieldSet(this, _Parser_defaultDepth, 0, "f");
1360
- }, _Parser_setRawCode = function _Parser_setRawCode(rawCode, originalRawCode) {
1361
- __classPrivateFieldSet(this, _Parser_rawCode, rawCode, "f");
1362
- __classPrivateFieldSet(this, _Parser_originalRawCode, originalRawCode ?? __classPrivateFieldGet(this, _Parser_originalRawCode, "f"), "f");
1363
- }, _Parser_trimText = function _Parser_trimText(nodeList) {
1364
- const newNodeList = [];
1365
- let prevNode = null;
1366
- for (const node of nodeList) {
1367
- if (prevNode?.type === 'text' &&
1368
- // Empty node
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.push(node);
1379
- prevNode = node;
1330
+ return newNodeList;
1380
1331
  }
1381
- return newNodeList;
1382
- };
1332
+ }