@reteps/tree-sitter-htmlmustache 0.9.0 → 0.9.2

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.
@@ -9,8 +9,17 @@ import type { PrettierLike } from '../core/formatting/embedded.js';
9
9
  import type { Diagnostic } from '../core/diagnostic.js';
10
10
  import type { HtmlMustacheConfig, RulesConfig, RuleSeverity, CustomRule as CustomRuleType } from '../core/configSchema.js';
11
11
  import type { CustomCodeTagConfig } from '../core/customCodeTags.js';
12
- export type Config = Omit<HtmlMustacheConfig, 'include' | 'exclude'>;
13
- export type CustomRule = CustomRuleType;
12
+ /**
13
+ * `include`/`exclude` on custom rules are stripped from the browser surface:
14
+ * the browser API has no filesystem path to match against, so those fields
15
+ * would silently do nothing. Users who share a `.htmlmustache.jsonc` config
16
+ * between the CLI and a web playground should strip them before passing, or
17
+ * let TypeScript catch the mismatch.
18
+ */
19
+ export type CustomRule = Omit<CustomRuleType, 'include' | 'exclude'>;
20
+ export type Config = Omit<HtmlMustacheConfig, 'include' | 'exclude' | 'customRules'> & {
21
+ customRules?: CustomRule[];
22
+ };
14
23
  export type CustomTag = CustomCodeTagConfig;
15
24
  export type { RulesConfig, RuleSeverity, PrettierLike, Diagnostic };
16
25
  export type LocateWasm = string | ((filename: string) => string);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,UAAU,IAAI,cAAc,EAC7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;AACrE,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AACxC,MAAM,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAEpE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,UAAU,EAAE,UAAU,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IACpD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChF;AAED,kDAAkD;AAClD,eAAO,MAAM,cAAc,EAAE,MAAgD,CAAC;AAc9E;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiD7E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,UAAU,IAAI,cAAc,EAC7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;AACrE,MAAM,MAAM,MAAM,GAChB,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC,GAC7D;IAAE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AACnC,MAAM,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAEpE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,UAAU,EAAE,UAAU,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IACpD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChF;AAED,kDAAkD;AAClD,eAAO,MAAM,cAAc,EAAE,MAAgD,CAAC;AAc9E;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiD7E"}
@@ -1307,13 +1307,85 @@ function parseSelector(raw) {
1307
1307
  const tops = ast.type === "list" ? ast.list : [ast];
1308
1308
  const alts = [];
1309
1309
  for (const top of tops) {
1310
- const segments = [];
1311
- if (!collectSegments(top, "descendant", segments)) return null;
1312
- if (segments.length === 0) return null;
1313
- alts.push(segments);
1310
+ const expanded = expandIs(top);
1311
+ if (expanded === null) return null;
1312
+ for (const exp of expanded) {
1313
+ const segments = [];
1314
+ if (!collectSegments(exp, "descendant", segments)) return null;
1315
+ if (segments.length === 0) return null;
1316
+ alts.push(segments);
1317
+ }
1314
1318
  }
1315
1319
  return alts.length > 0 ? alts : null;
1316
1320
  }
1321
+ function expandIs(ast) {
1322
+ switch (ast.type) {
1323
+ case "list": {
1324
+ const out = [];
1325
+ for (const alt of ast.list) {
1326
+ const expanded = expandIs(alt);
1327
+ if (expanded === null) return null;
1328
+ out.push(...expanded);
1329
+ }
1330
+ return out;
1331
+ }
1332
+ case "complex": {
1333
+ const lefts = expandIs(ast.left);
1334
+ if (lefts === null) return null;
1335
+ const rights = expandIs(ast.right);
1336
+ if (rights === null) return null;
1337
+ const out = [];
1338
+ for (const l of lefts) for (const r of rights) {
1339
+ out.push({ ...ast, left: l, right: r });
1340
+ }
1341
+ return out;
1342
+ }
1343
+ case "compound": {
1344
+ if (ast.list.length === 1) {
1345
+ const tok = ast.list[0];
1346
+ if (tok.type === "pseudo-class" && tok.name === "is") {
1347
+ if (!tok.subtree) return null;
1348
+ return expandIs(tok.subtree);
1349
+ }
1350
+ }
1351
+ return expandCompoundWithIs(ast.list);
1352
+ }
1353
+ default:
1354
+ if (ast.type === "pseudo-class" && ast.name === "is") {
1355
+ if (!ast.subtree) return null;
1356
+ return expandIs(ast.subtree);
1357
+ }
1358
+ return [ast];
1359
+ }
1360
+ }
1361
+ function expandCompoundWithIs(tokens) {
1362
+ let variants = [[]];
1363
+ for (const tok of tokens) {
1364
+ if (tok.type === "pseudo-class" && tok.name === "is") {
1365
+ if (!tok.subtree) return null;
1366
+ const alts = expandIs(tok.subtree);
1367
+ if (alts === null) return null;
1368
+ const next = [];
1369
+ for (const base of variants) {
1370
+ for (const alt of alts) {
1371
+ if (alt.type === "compound") {
1372
+ next.push([...base, ...alt.list]);
1373
+ } else if (alt.type === "complex" || alt.type === "list" || alt.type === "relative") {
1374
+ return null;
1375
+ } else {
1376
+ next.push([...base, alt]);
1377
+ }
1378
+ }
1379
+ }
1380
+ variants = next;
1381
+ } else {
1382
+ variants = variants.map((v) => [...v, tok]);
1383
+ }
1384
+ }
1385
+ return variants.map(
1386
+ (list) => list.length === 1 ? list[0] : { type: "compound", list }
1387
+ );
1388
+ }
1317
1389
  function collectSegments(ast, combinator, out) {
1318
1390
  if (ast.type === "complex") {
1319
1391
  const mapped = mapCombinator(ast.combinator);
@@ -1331,6 +1403,8 @@ function mapCombinator(c) {
1331
1403
  const trimmed = c.trim();
1332
1404
  if (trimmed === "") return "descendant";
1333
1405
  if (trimmed === ">") return "child";
1406
+ if (trimmed === "+") return "adjacent-sibling";
1407
+ if (trimmed === "~") return "general-sibling";
1334
1408
  return null;
1335
1409
  }
1336
1410
  function segmentFromCompound(ast) {
@@ -1341,6 +1415,7 @@ function segmentFromCompound(ast) {
1341
1415
  let rootOnly = false;
1342
1416
  const attributes = [];
1343
1417
  const descendantChecks = [];
1418
+ const selfNegations = [];
1344
1419
  const forbidChange = (requested) => {
1345
1420
  if (kind === void 0) return false;
1346
1421
  if (kind === requested) return false;
@@ -1394,7 +1469,7 @@ function segmentFromCompound(ast) {
1394
1469
  break;
1395
1470
  }
1396
1471
  if (token.name === "not") {
1397
- if (!applyNegatedSubtree(token.subtree, attributes, descendantChecks)) return null;
1472
+ if (!applyNegatedSubtree(token.subtree, attributes, descendantChecks, selfNegations)) return null;
1398
1473
  break;
1399
1474
  }
1400
1475
  if (token.name === "root") {
@@ -1416,7 +1491,7 @@ function segmentFromCompound(ast) {
1416
1491
  }
1417
1492
  const isHtml = kind === "html";
1418
1493
  const finalAttrs = isHtml ? attributes : [];
1419
- return { kind, rootOnly, name, pathRegex, attributes: finalAttrs, descendantChecks, combinator: "descendant" };
1494
+ return { kind, rootOnly, name, pathRegex, attributes: finalAttrs, descendantChecks, selfNegations, combinator: "descendant" };
1420
1495
  }
1421
1496
  function mustacheKindFromMarker(name) {
1422
1497
  switch (name) {
@@ -1481,7 +1556,7 @@ function classConstraint(token, negated) {
1481
1556
  function idConstraint(token, negated) {
1482
1557
  return { name: "id", op: "=", value: token.name, negated };
1483
1558
  }
1484
- function applyNegatedSubtree(subtree, attributes, descendantChecks) {
1559
+ function applyNegatedSubtree(subtree, attributes, descendantChecks, selfNegations) {
1485
1560
  if (!subtree) return false;
1486
1561
  if (subtree.type === "attribute") {
1487
1562
  const c = attributeConstraint(subtree, true);
@@ -1498,9 +1573,14 @@ function applyNegatedSubtree(subtree, attributes, descendantChecks) {
1498
1573
  return true;
1499
1574
  }
1500
1575
  if (subtree.type === "pseudo-class" && subtree.name === "has") {
1501
- const sel = subtreeToSelector(subtree.subtree);
1502
- if (!sel) return false;
1503
- descendantChecks.push({ selector: sel, negated: true });
1576
+ const sel2 = subtreeToSelector(subtree.subtree);
1577
+ if (!sel2) return false;
1578
+ descendantChecks.push({ selector: sel2, negated: true });
1579
+ return true;
1580
+ }
1581
+ const sel = subtreeToSelector(subtree);
1582
+ if (sel) {
1583
+ selfNegations.push(sel);
1504
1584
  return true;
1505
1585
  }
1506
1586
  return false;
@@ -1600,11 +1680,20 @@ function checkDescendants(node, checks) {
1600
1680
  return true;
1601
1681
  }
1602
1682
  function hasDescendantMatch(node, selector) {
1603
- for (const child of node.children) {
1604
- if (matchSelector(child, selector).length > 0) return true;
1683
+ for (let i = 0; i < node.children.length; i++) {
1684
+ if (matchSelector(node.children[i], selector, node.children, i).length > 0) return true;
1605
1685
  }
1606
1686
  return false;
1607
1687
  }
1688
+ function checkSelfNegations(node, negations, rootNode) {
1689
+ for (const sel of negations) {
1690
+ for (const alt of sel) {
1691
+ if (alt.length !== 1) continue;
1692
+ if (nodeMatchesSegment(node, alt[0], rootNode)) return false;
1693
+ }
1694
+ }
1695
+ return true;
1696
+ }
1608
1697
  function matchesName(actual, segment) {
1609
1698
  if (segment.name === null) return true;
1610
1699
  if (actual === null) return false;
@@ -1614,51 +1703,73 @@ function matchesName(actual, segment) {
1614
1703
  function nodeMatchesSegment(node, segment, rootNode) {
1615
1704
  if (segment.rootOnly) {
1616
1705
  if (node !== rootNode) return false;
1617
- return checkDescendants(node, segment.descendantChecks);
1618
- }
1619
- switch (segment.kind) {
1620
- case "html": {
1621
- if (!HTML_ELEMENT_TYPES.has(node.type)) return false;
1622
- if (segment.name !== null) {
1623
- const tagName = getTagName(node)?.toLowerCase();
1624
- if (tagName !== segment.name) return false;
1625
- }
1626
- return checkAttributes(node, segment.attributes) && checkDescendants(node, segment.descendantChecks);
1627
- }
1628
- case "section":
1629
- if (node.type !== "mustache_section") return false;
1630
- if (!matchesName(getSectionName(node)?.toLowerCase() ?? null, segment)) return false;
1631
- return checkDescendants(node, segment.descendantChecks);
1632
- case "inverted":
1633
- if (node.type !== "mustache_inverted_section") return false;
1634
- if (!matchesName(getSectionName(node)?.toLowerCase() ?? null, segment)) return false;
1635
- return checkDescendants(node, segment.descendantChecks);
1636
- case "variable":
1637
- if (node.type !== "mustache_interpolation") return false;
1638
- if (!matchesName(getInterpolationPath(node)?.toLowerCase() ?? null, segment)) return false;
1639
- return checkDescendants(node, segment.descendantChecks);
1640
- case "raw":
1641
- if (node.type !== "mustache_triple") return false;
1642
- if (!matchesName(getInterpolationPath(node)?.toLowerCase() ?? null, segment)) return false;
1643
- return checkDescendants(node, segment.descendantChecks);
1644
- case "comment":
1645
- if (node.type !== "mustache_comment") return false;
1646
- if (!matchesName(getCommentContent(node)?.toLowerCase() ?? null, segment)) return false;
1647
- return checkDescendants(node, segment.descendantChecks);
1648
- case "partial":
1649
- if (node.type !== "mustache_partial") return false;
1650
- if (!matchesName(getPartialName(node)?.toLowerCase() ?? null, segment)) return false;
1651
- return checkDescendants(node, segment.descendantChecks);
1652
- }
1653
- }
1654
- function checkAncestors(ancestors, segments, segIdx, childCombinator) {
1706
+ return checkDescendants(node, segment.descendantChecks) && checkSelfNegations(node, segment.selfNegations, rootNode);
1707
+ }
1708
+ const baseMatches = (() => {
1709
+ switch (segment.kind) {
1710
+ case "html": {
1711
+ if (!HTML_ELEMENT_TYPES.has(node.type)) return false;
1712
+ if (segment.name !== null) {
1713
+ const tagName = getTagName(node)?.toLowerCase();
1714
+ if (tagName !== segment.name) return false;
1715
+ }
1716
+ return checkAttributes(node, segment.attributes) && checkDescendants(node, segment.descendantChecks);
1717
+ }
1718
+ case "section":
1719
+ if (node.type !== "mustache_section") return false;
1720
+ if (!matchesName(getSectionName(node)?.toLowerCase() ?? null, segment)) return false;
1721
+ return checkDescendants(node, segment.descendantChecks);
1722
+ case "inverted":
1723
+ if (node.type !== "mustache_inverted_section") return false;
1724
+ if (!matchesName(getSectionName(node)?.toLowerCase() ?? null, segment)) return false;
1725
+ return checkDescendants(node, segment.descendantChecks);
1726
+ case "variable":
1727
+ if (node.type !== "mustache_interpolation") return false;
1728
+ if (!matchesName(getInterpolationPath(node)?.toLowerCase() ?? null, segment)) return false;
1729
+ return checkDescendants(node, segment.descendantChecks);
1730
+ case "raw":
1731
+ if (node.type !== "mustache_triple") return false;
1732
+ if (!matchesName(getInterpolationPath(node)?.toLowerCase() ?? null, segment)) return false;
1733
+ return checkDescendants(node, segment.descendantChecks);
1734
+ case "comment":
1735
+ if (node.type !== "mustache_comment") return false;
1736
+ if (!matchesName(getCommentContent(node)?.toLowerCase() ?? null, segment)) return false;
1737
+ return checkDescendants(node, segment.descendantChecks);
1738
+ case "partial":
1739
+ if (node.type !== "mustache_partial") return false;
1740
+ if (!matchesName(getPartialName(node)?.toLowerCase() ?? null, segment)) return false;
1741
+ return checkDescendants(node, segment.descendantChecks);
1742
+ }
1743
+ })();
1744
+ if (!baseMatches) return false;
1745
+ return checkSelfNegations(node, segment.selfNegations, rootNode);
1746
+ }
1747
+ function checkPrefix(cursor, segments, segIdx, stepCombinator, rootNode) {
1655
1748
  if (segIdx < 0) return true;
1656
1749
  const segment = segments[segIdx];
1750
+ if (stepCombinator === "adjacent-sibling" || stepCombinator === "general-sibling") {
1751
+ for (let i = cursor.indexInSiblings - 1; i >= 0; i--) {
1752
+ const sib = cursor.siblings[i];
1753
+ if (!isMatchableNode(sib)) continue;
1754
+ if (!nodeMatchesSegment(sib, segment, rootNode)) {
1755
+ if (stepCombinator === "adjacent-sibling") return false;
1756
+ continue;
1757
+ }
1758
+ const newCursor = {
1759
+ ancestors: cursor.ancestors,
1760
+ siblings: cursor.siblings,
1761
+ indexInSiblings: i
1762
+ };
1763
+ if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
1764
+ if (stepCombinator === "adjacent-sibling") return false;
1765
+ }
1766
+ return false;
1767
+ }
1657
1768
  const ancestorKind = ancestorKindForSegment(segment);
1658
1769
  if (ancestorKind === null) return false;
1659
- if (childCombinator === "child") {
1660
- for (let a = ancestors.length - 1; a >= 0; a--) {
1661
- const entry = ancestors[a];
1770
+ if (stepCombinator === "child") {
1771
+ for (let a = cursor.ancestors.length - 1; a >= 0; a--) {
1772
+ const entry = cursor.ancestors[a];
1662
1773
  if (entry.kind !== ancestorKind) {
1663
1774
  if (ancestorKind === "root" && entry.kind === "html") return false;
1664
1775
  continue;
@@ -1666,22 +1777,35 @@ function checkAncestors(ancestors, segments, segIdx, childCombinator) {
1666
1777
  if (!matchesName(entry.name, segment)) return false;
1667
1778
  if (segment.kind === "html" && !checkAttributes(entry.node, segment.attributes)) return false;
1668
1779
  if (!checkDescendants(entry.node, segment.descendantChecks)) return false;
1669
- return checkAncestors(ancestors.slice(0, a), segments, segIdx - 1, segment.combinator);
1780
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) return false;
1781
+ const newCursor = {
1782
+ ancestors: cursor.ancestors.slice(0, a),
1783
+ siblings: entry.siblings,
1784
+ indexInSiblings: entry.indexInSiblings
1785
+ };
1786
+ return checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode);
1670
1787
  }
1671
1788
  return false;
1672
1789
  }
1673
- for (let a = ancestors.length - 1; a >= 0; a--) {
1674
- const entry = ancestors[a];
1790
+ for (let a = cursor.ancestors.length - 1; a >= 0; a--) {
1791
+ const entry = cursor.ancestors[a];
1675
1792
  if (entry.kind !== ancestorKind) continue;
1676
1793
  if (!matchesName(entry.name, segment)) continue;
1677
1794
  if (segment.kind === "html" && !checkAttributes(entry.node, segment.attributes)) continue;
1678
1795
  if (!checkDescendants(entry.node, segment.descendantChecks)) continue;
1679
- if (checkAncestors(ancestors.slice(0, a), segments, segIdx - 1, segment.combinator)) {
1680
- return true;
1681
- }
1796
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) continue;
1797
+ const newCursor = {
1798
+ ancestors: cursor.ancestors.slice(0, a),
1799
+ siblings: entry.siblings,
1800
+ indexInSiblings: entry.indexInSiblings
1801
+ };
1802
+ if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
1682
1803
  }
1683
1804
  return false;
1684
1805
  }
1806
+ function isMatchableNode(node) {
1807
+ return HTML_ELEMENT_TYPES.has(node.type) || node.type === "mustache_section" || node.type === "mustache_inverted_section" || node.type === "mustache_interpolation" || node.type === "mustache_triple" || node.type === "mustache_comment" || node.type === "mustache_partial";
1808
+ }
1685
1809
  function ancestorKindForSegment(segment) {
1686
1810
  if (segment.rootOnly) return "root";
1687
1811
  if (segment.kind === "html") return "html";
@@ -1715,12 +1839,13 @@ function getReportNode(node, rootNode) {
1715
1839
  }
1716
1840
  return node;
1717
1841
  }
1718
- function matchAlternative(rootNode, segments) {
1842
+ function matchAlternative(rootNode, segments, rootSiblings, rootIndexInSiblings) {
1719
1843
  const results = [];
1720
1844
  const lastSegment = segments[segments.length - 1];
1721
- function walk(node, ancestors) {
1845
+ function walk(node, ancestors, siblings, indexInSiblings) {
1722
1846
  if (nodeMatchesSegment(node, lastSegment, rootNode)) {
1723
- if (segments.length === 1 || checkAncestors(ancestors, segments, segments.length - 2, lastSegment.combinator)) {
1847
+ const cursor = { ancestors, siblings, indexInSiblings };
1848
+ if (segments.length === 1 || checkPrefix(cursor, segments, segments.length - 2, lastSegment.combinator, rootNode)) {
1724
1849
  results.push(getReportNode(node, rootNode));
1725
1850
  }
1726
1851
  }
@@ -1729,19 +1854,28 @@ function matchAlternative(rootNode, segments) {
1729
1854
  if (ancestorKind !== null) {
1730
1855
  const name = ancestorKind === "html" ? getTagName(node)?.toLowerCase() : getSectionName(node)?.toLowerCase();
1731
1856
  if (name) {
1732
- newAncestors = [...ancestors, { kind: ancestorKind, name, node }];
1857
+ newAncestors = [...ancestors, { kind: ancestorKind, name, node, siblings, indexInSiblings }];
1733
1858
  }
1734
1859
  }
1735
- for (const child of node.children) walk(child, newAncestors);
1860
+ for (let i = 0; i < node.children.length; i++) {
1861
+ walk(node.children[i], newAncestors, node.children, i);
1862
+ }
1736
1863
  }
1737
- walk(rootNode, [{ kind: "root", name: "", node: rootNode }]);
1864
+ const rootEntry = {
1865
+ kind: "root",
1866
+ name: "",
1867
+ node: rootNode,
1868
+ siblings: rootSiblings,
1869
+ indexInSiblings: rootIndexInSiblings
1870
+ };
1871
+ walk(rootNode, [rootEntry], rootSiblings, rootIndexInSiblings);
1738
1872
  return results;
1739
1873
  }
1740
- function matchSelector(rootNode, selector) {
1874
+ function matchSelector(rootNode, selector, siblings = [], indexInSiblings = 0) {
1741
1875
  const allResults = [];
1742
1876
  const seen = /* @__PURE__ */ new Set();
1743
1877
  for (const alt of selector) {
1744
- for (const node of matchAlternative(rootNode, alt)) {
1878
+ for (const node of matchAlternative(rootNode, alt, siblings, indexInSiblings)) {
1745
1879
  if (!seen.has(node)) {
1746
1880
  seen.add(node);
1747
1881
  allResults.push(node);