@reteps/tree-sitter-htmlmustache 0.9.2 → 0.9.3

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.
@@ -24,7 +24,11 @@
24
24
  * the outer compound), plus any other selector (including Mustache
25
25
  * literals and type selectors) as a whole-selector check against the
26
26
  * node itself. Example: `{{*}}:not({{internal.*}})` matches any
27
- * interpolation whose path does not start with `internal.`.
27
+ * interpolation whose path does not start with `internal.`. Multi-segment
28
+ * selectors with combinators are supported and tested against the node's
29
+ * ancestor/sibling context, matching `Element.matches` semantics — e.g.
30
+ * `img:not(pl-overlay img)` matches any `img` that is not a descendant of
31
+ * `pl-overlay`, and `td:not(thead td)` matches `td`s outside a `thead`.
28
32
  * - `:root` — the tree-sitter fragment root (the whole document). Unlike
29
33
  * browser CSS where `:root` matches `<html>`, this matches the parse-tree
30
34
  * root so it works on partials/fragments too. Useful as a document-scoped
@@ -1 +1 @@
1
- {"version":3,"file":"selectorMatcher.d.ts","sourceRoot":"","sources":["../../../src/core/selectorMatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY3D,MAAM,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,KAAK,GACL,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,iBAAiB,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAAC;AAEzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,OAAO,EAAE,EAAE,CAAC;AAQzC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuFrE;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CA2BhE;AA2pBD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,cAAc,EACxB,QAAQ,GAAE,WAAW,EAAO,EAC5B,eAAe,SAAI,GAClB,WAAW,EAAE,CAYf"}
1
+ {"version":3,"file":"selectorMatcher.d.ts","sourceRoot":"","sources":["../../../src/core/selectorMatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY3D,MAAM,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,KAAK,GACL,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,iBAAiB,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAAC;AAEzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,OAAO,EAAE,EAAE,CAAC;AAQzC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuFrE;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CA2BhE;AA2qBD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,cAAc,EACxB,QAAQ,GAAE,WAAW,EAAO,EAC5B,eAAe,SAAI,GAClB,WAAW,EAAE,CAYf"}
package/cli/out/main.js CHANGED
@@ -2326,11 +2326,16 @@ function hasDescendantMatch(node, selector) {
2326
2326
  }
2327
2327
  return false;
2328
2328
  }
2329
- function checkSelfNegations(node, negations, rootNode) {
2329
+ function checkSelfNegations(node, negations, rootNode, cursor) {
2330
2330
  for (const sel of negations) {
2331
2331
  for (const alt of sel) {
2332
- if (alt.length !== 1) continue;
2333
- if (nodeMatchesSegment(node, alt[0], rootNode)) return false;
2332
+ if (alt.length === 0) continue;
2333
+ const lastSegment = alt[alt.length - 1];
2334
+ if (!nodeMatchesSegment(node, lastSegment, rootNode, cursor)) continue;
2335
+ if (alt.length === 1) return false;
2336
+ if (checkPrefix(cursor, alt, alt.length - 2, lastSegment.combinator, rootNode)) {
2337
+ return false;
2338
+ }
2334
2339
  }
2335
2340
  }
2336
2341
  return true;
@@ -2341,10 +2346,10 @@ function matchesName(actual, segment) {
2341
2346
  if (segment.pathRegex) return segment.pathRegex.test(actual);
2342
2347
  return actual === segment.name;
2343
2348
  }
2344
- function nodeMatchesSegment(node, segment, rootNode) {
2349
+ function nodeMatchesSegment(node, segment, rootNode, cursor) {
2345
2350
  if (segment.rootOnly) {
2346
2351
  if (node !== rootNode) return false;
2347
- return checkDescendants(node, segment.descendantChecks) && checkSelfNegations(node, segment.selfNegations, rootNode);
2352
+ return checkDescendants(node, segment.descendantChecks) && checkSelfNegations(node, segment.selfNegations, rootNode, cursor);
2348
2353
  }
2349
2354
  const baseMatches = (() => {
2350
2355
  switch (segment.kind) {
@@ -2383,7 +2388,7 @@ function nodeMatchesSegment(node, segment, rootNode) {
2383
2388
  }
2384
2389
  })();
2385
2390
  if (!baseMatches) return false;
2386
- return checkSelfNegations(node, segment.selfNegations, rootNode);
2391
+ return checkSelfNegations(node, segment.selfNegations, rootNode, cursor);
2387
2392
  }
2388
2393
  function checkPrefix(cursor, segments, segIdx, stepCombinator, rootNode) {
2389
2394
  if (segIdx < 0) return true;
@@ -2392,16 +2397,16 @@ function checkPrefix(cursor, segments, segIdx, stepCombinator, rootNode) {
2392
2397
  for (let i2 = cursor.indexInSiblings - 1; i2 >= 0; i2--) {
2393
2398
  const sib = cursor.siblings[i2];
2394
2399
  if (!isMatchableNode(sib)) continue;
2395
- if (!nodeMatchesSegment(sib, segment, rootNode)) {
2396
- if (stepCombinator === "adjacent-sibling") return false;
2397
- continue;
2398
- }
2399
- const newCursor = {
2400
+ const sibCursor = {
2400
2401
  ancestors: cursor.ancestors,
2401
2402
  siblings: cursor.siblings,
2402
2403
  indexInSiblings: i2
2403
2404
  };
2404
- if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
2405
+ if (!nodeMatchesSegment(sib, segment, rootNode, sibCursor)) {
2406
+ if (stepCombinator === "adjacent-sibling") return false;
2407
+ continue;
2408
+ }
2409
+ if (checkPrefix(sibCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
2405
2410
  if (stepCombinator === "adjacent-sibling") return false;
2406
2411
  }
2407
2412
  return false;
@@ -2418,13 +2423,13 @@ function checkPrefix(cursor, segments, segIdx, stepCombinator, rootNode) {
2418
2423
  if (!matchesName(entry.name, segment)) return false;
2419
2424
  if (segment.kind === "html" && !checkAttributes(entry.node, segment.attributes)) return false;
2420
2425
  if (!checkDescendants(entry.node, segment.descendantChecks)) return false;
2421
- if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) return false;
2422
- const newCursor = {
2426
+ const ancestorCursor = {
2423
2427
  ancestors: cursor.ancestors.slice(0, a2),
2424
2428
  siblings: entry.siblings,
2425
2429
  indexInSiblings: entry.indexInSiblings
2426
2430
  };
2427
- return checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode);
2431
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode, ancestorCursor)) return false;
2432
+ return checkPrefix(ancestorCursor, segments, segIdx - 1, segment.combinator, rootNode);
2428
2433
  }
2429
2434
  return false;
2430
2435
  }
@@ -2434,13 +2439,13 @@ function checkPrefix(cursor, segments, segIdx, stepCombinator, rootNode) {
2434
2439
  if (!matchesName(entry.name, segment)) continue;
2435
2440
  if (segment.kind === "html" && !checkAttributes(entry.node, segment.attributes)) continue;
2436
2441
  if (!checkDescendants(entry.node, segment.descendantChecks)) continue;
2437
- if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) continue;
2438
- const newCursor = {
2442
+ const ancestorCursor = {
2439
2443
  ancestors: cursor.ancestors.slice(0, a2),
2440
2444
  siblings: entry.siblings,
2441
2445
  indexInSiblings: entry.indexInSiblings
2442
2446
  };
2443
- if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
2447
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode, ancestorCursor)) continue;
2448
+ if (checkPrefix(ancestorCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
2444
2449
  }
2445
2450
  return false;
2446
2451
  }
@@ -2484,8 +2489,8 @@ function matchAlternative(rootNode, segments, rootSiblings, rootIndexInSiblings)
2484
2489
  const results = [];
2485
2490
  const lastSegment = segments[segments.length - 1];
2486
2491
  function walk(node, ancestors, siblings, indexInSiblings) {
2487
- if (nodeMatchesSegment(node, lastSegment, rootNode)) {
2488
- const cursor = { ancestors, siblings, indexInSiblings };
2492
+ const cursor = { ancestors, siblings, indexInSiblings };
2493
+ if (nodeMatchesSegment(node, lastSegment, rootNode, cursor)) {
2489
2494
  if (segments.length === 1 || checkPrefix(cursor, segments, segments.length - 2, lastSegment.combinator, rootNode)) {
2490
2495
  results.push(getReportNode(node, rootNode));
2491
2496
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reteps/tree-sitter-htmlmustache",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "HTML with Mustache/Handlebars template syntax grammar for tree-sitter",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,7 +24,11 @@
24
24
  * the outer compound), plus any other selector (including Mustache
25
25
  * literals and type selectors) as a whole-selector check against the
26
26
  * node itself. Example: `{{*}}:not({{internal.*}})` matches any
27
- * interpolation whose path does not start with `internal.`.
27
+ * interpolation whose path does not start with `internal.`. Multi-segment
28
+ * selectors with combinators are supported and tested against the node's
29
+ * ancestor/sibling context, matching `Element.matches` semantics — e.g.
30
+ * `img:not(pl-overlay img)` matches any `img` that is not a descendant of
31
+ * `pl-overlay`, and `td:not(thead td)` matches `td`s outside a `thead`.
28
32
  * - `:root` — the tree-sitter fragment root (the whole document). Unlike
29
33
  * browser CSS where `:root` matches `<html>`, this matches the parse-tree
30
34
  * root so it works on partials/fragments too. Useful as a document-scoped
@@ -651,14 +655,25 @@ function hasDescendantMatch(node: BalanceNode, selector: ParsedSelector): boolea
651
655
  return false;
652
656
  }
653
657
 
654
- function checkSelfNegations(node: BalanceNode, negations: ParsedSelector[], rootNode: BalanceNode): boolean {
658
+ function checkSelfNegations(
659
+ node: BalanceNode,
660
+ negations: ParsedSelector[],
661
+ rootNode: BalanceNode,
662
+ cursor: Cursor,
663
+ ): boolean {
655
664
  for (const sel of negations) {
656
665
  for (const alt of sel) {
657
- // A selfNegation selector is a single-segment check against the node itself.
658
- // Multi-segment alternatives (e.g. `:not(a b)`) aren't sensibly testable
659
- // against a single node, so they're treated as never matching => pass.
660
- if (alt.length !== 1) continue;
661
- if (nodeMatchesSegment(node, alt[0], rootNode)) return false;
666
+ if (alt.length === 0) continue;
667
+ const lastSegment = alt[alt.length - 1];
668
+ if (!nodeMatchesSegment(node, lastSegment, rootNode, cursor)) continue;
669
+ if (alt.length === 1) return false;
670
+ // Multi-segment negation (e.g. `:not(ancestor descendant)`): the last
671
+ // segment matched the node itself; walk back through the remaining
672
+ // segments against the node's ancestor/sibling cursor. If the chain
673
+ // also matches, the negation fires.
674
+ if (checkPrefix(cursor, alt, alt.length - 2, lastSegment.combinator, rootNode)) {
675
+ return false;
676
+ }
662
677
  }
663
678
  }
664
679
  return true;
@@ -671,11 +686,16 @@ function matchesName(actual: string | null, segment: Segment): boolean {
671
686
  return actual === segment.name;
672
687
  }
673
688
 
674
- function nodeMatchesSegment(node: BalanceNode, segment: Segment, rootNode: BalanceNode): boolean {
689
+ function nodeMatchesSegment(
690
+ node: BalanceNode,
691
+ segment: Segment,
692
+ rootNode: BalanceNode,
693
+ cursor: Cursor,
694
+ ): boolean {
675
695
  if (segment.rootOnly) {
676
696
  if (node !== rootNode) return false;
677
697
  return checkDescendants(node, segment.descendantChecks)
678
- && checkSelfNegations(node, segment.selfNegations, rootNode);
698
+ && checkSelfNegations(node, segment.selfNegations, rootNode, cursor);
679
699
  }
680
700
  const baseMatches = (() => {
681
701
  switch (segment.kind) {
@@ -714,7 +734,7 @@ function nodeMatchesSegment(node: BalanceNode, segment: Segment, rootNode: Balan
714
734
  }
715
735
  })();
716
736
  if (!baseMatches) return false;
717
- return checkSelfNegations(node, segment.selfNegations, rootNode);
737
+ return checkSelfNegations(node, segment.selfNegations, rootNode, cursor);
718
738
  }
719
739
 
720
740
  interface Cursor {
@@ -738,16 +758,16 @@ function checkPrefix(
738
758
  for (let i = cursor.indexInSiblings - 1; i >= 0; i--) {
739
759
  const sib = cursor.siblings[i];
740
760
  if (!isMatchableNode(sib)) continue;
741
- if (!nodeMatchesSegment(sib, segment, rootNode)) {
742
- if (stepCombinator === 'adjacent-sibling') return false;
743
- continue;
744
- }
745
- const newCursor: Cursor = {
761
+ const sibCursor: Cursor = {
746
762
  ancestors: cursor.ancestors,
747
763
  siblings: cursor.siblings,
748
764
  indexInSiblings: i,
749
765
  };
750
- if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
766
+ if (!nodeMatchesSegment(sib, segment, rootNode, sibCursor)) {
767
+ if (stepCombinator === 'adjacent-sibling') return false;
768
+ continue;
769
+ }
770
+ if (checkPrefix(sibCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
751
771
  if (stepCombinator === 'adjacent-sibling') return false;
752
772
  }
753
773
  return false;
@@ -770,13 +790,13 @@ function checkPrefix(
770
790
  if (!matchesName(entry.name, segment)) return false;
771
791
  if (segment.kind === 'html' && !checkAttributes(entry.node, segment.attributes)) return false;
772
792
  if (!checkDescendants(entry.node, segment.descendantChecks)) return false;
773
- if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) return false;
774
- const newCursor: Cursor = {
793
+ const ancestorCursor: Cursor = {
775
794
  ancestors: cursor.ancestors.slice(0, a),
776
795
  siblings: entry.siblings,
777
796
  indexInSiblings: entry.indexInSiblings,
778
797
  };
779
- return checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode);
798
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode, ancestorCursor)) return false;
799
+ return checkPrefix(ancestorCursor, segments, segIdx - 1, segment.combinator, rootNode);
780
800
  }
781
801
  return false;
782
802
  }
@@ -787,13 +807,13 @@ function checkPrefix(
787
807
  if (!matchesName(entry.name, segment)) continue;
788
808
  if (segment.kind === 'html' && !checkAttributes(entry.node, segment.attributes)) continue;
789
809
  if (!checkDescendants(entry.node, segment.descendantChecks)) continue;
790
- if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode)) continue;
791
- const newCursor: Cursor = {
810
+ const ancestorCursor: Cursor = {
792
811
  ancestors: cursor.ancestors.slice(0, a),
793
812
  siblings: entry.siblings,
794
813
  indexInSiblings: entry.indexInSiblings,
795
814
  };
796
- if (checkPrefix(newCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
815
+ if (!checkSelfNegations(entry.node, segment.selfNegations, rootNode, ancestorCursor)) continue;
816
+ if (checkPrefix(ancestorCursor, segments, segIdx - 1, segment.combinator, rootNode)) return true;
797
817
  }
798
818
  return false;
799
819
  }
@@ -862,8 +882,8 @@ function matchAlternative(
862
882
  siblings: BalanceNode[],
863
883
  indexInSiblings: number,
864
884
  ) {
865
- if (nodeMatchesSegment(node, lastSegment, rootNode)) {
866
- const cursor: Cursor = { ancestors, siblings, indexInSiblings };
885
+ const cursor: Cursor = { ancestors, siblings, indexInSiblings };
886
+ if (nodeMatchesSegment(node, lastSegment, rootNode, cursor)) {
867
887
  if (
868
888
  segments.length === 1 ||
869
889
  checkPrefix(cursor, segments, segments.length - 2, lastSegment.combinator, rootNode)