@reteps/tree-sitter-htmlmustache 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/out/browser/index.mjs +201 -67
- package/browser/out/browser/index.mjs.map +3 -3
- package/browser/out/core/selectorMatcher.d.ts +19 -6
- package/browser/out/core/selectorMatcher.d.ts.map +1 -1
- package/cli/out/main.js +200 -66
- package/package.json +1 -1
- package/src/core/selectorMatcher.ts +272 -72
|
@@ -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
|
|
1311
|
-
if (
|
|
1312
|
-
|
|
1313
|
-
|
|
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
|
|
1502
|
-
if (!
|
|
1503
|
-
descendantChecks.push({ selector:
|
|
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 (
|
|
1604
|
-
if (matchSelector(
|
|
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
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
}
|
|
1654
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
1680
|
-
|
|
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
|
-
|
|
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 (
|
|
1860
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
1861
|
+
walk(node.children[i], newAncestors, node.children, i);
|
|
1862
|
+
}
|
|
1736
1863
|
}
|
|
1737
|
-
|
|
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);
|