parseman 0.12.0 → 0.13.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.
@@ -34,8 +34,10 @@ __export(plugin_exports, {
34
34
  transformMacro: () => transformMacro
35
35
  });
36
36
  module.exports = __toCommonJS(plugin_exports);
37
+ var fs = __toESM(require("node:fs"), 1);
37
38
  var import_unplugin = require("unplugin");
38
39
  var import_oxc_parser = require("oxc-parser");
40
+ var import_oxc_resolver = require("oxc-resolver");
39
41
  var import_magic_string = __toESM(require("magic-string"), 1);
40
42
 
41
43
  // src/combinators/first-set.ts
@@ -363,15 +365,15 @@ function extractFirstSet(node2) {
363
365
  case "Alternative": {
364
366
  const exprs = node2.expressions;
365
367
  if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
366
- let fs = empty();
368
+ let fs2 = empty();
367
369
  let canNL = false;
368
370
  for (const expr of exprs) {
369
371
  const r = extractFirstSet(expr);
370
- fs = union(fs, r.firstSet);
372
+ fs2 = union(fs2, r.firstSet);
371
373
  canNL = canNL || r.canMatchNewline;
372
374
  if (!canBeEmpty(expr)) break;
373
375
  }
374
- return { firstSet: fs, canMatchNewline: canNL };
376
+ return { firstSet: fs2, canMatchNewline: canNL };
375
377
  }
376
378
  case "Char": {
377
379
  const kind = node2.kind;
@@ -398,22 +400,22 @@ function extractFirstSet(node2) {
398
400
  }
399
401
  case "CharacterClass": {
400
402
  const expressions = node2.expressions;
401
- let fs = empty();
403
+ let fs2 = empty();
402
404
  let canNL = false;
403
405
  for (const expr of expressions) {
404
406
  if (expr.type === "ClassRange") {
405
407
  const from = expr.from.codePoint;
406
408
  const to = expr.to.codePoint;
407
- fs = union(fs, fromRange(from, to));
409
+ fs2 = union(fs2, fromRange(from, to));
408
410
  if (from <= 10 && 10 <= to) canNL = true;
409
411
  } else if (expr.type === "Char") {
410
412
  const r = extractFirstSet(expr);
411
- fs = union(fs, r.firstSet);
413
+ fs2 = union(fs2, r.firstSet);
412
414
  canNL = canNL || r.canMatchNewline;
413
415
  }
414
416
  }
415
417
  if (node2.negative) return { firstSet: any(), canMatchNewline: true };
416
- return { firstSet: fs, canMatchNewline: canNL };
418
+ return { firstSet: fs2, canMatchNewline: canNL };
417
419
  }
418
420
  case "Repetition": {
419
421
  return extractFirstSet(node2.expression);
@@ -722,10 +724,10 @@ function codesToFirstSet(codes) {
722
724
  ranges.push({ lo, hi });
723
725
  return { kind: "ranges", ranges };
724
726
  }
725
- function inFirstSet(code, fs) {
726
- if (fs.kind === "any") return true;
727
- if (fs.kind === "empty") return false;
728
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
727
+ function inFirstSet(code, fs2) {
728
+ if (fs2.kind === "any") return true;
729
+ if (fs2.kind === "empty") return false;
730
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
729
731
  return false;
730
732
  }
731
733
  function areDisjoint(sets) {
@@ -1374,8 +1376,29 @@ function expect(combinator, label2) {
1374
1376
  var BACKSLASH = 92;
1375
1377
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1376
1378
  var META = new Set("()[]{}*+?|^$.".split(""));
1379
+ var SPACE_RANGES = [
1380
+ [9, 13],
1381
+ [32, 32],
1382
+ [160, 160],
1383
+ [5760, 5760],
1384
+ [8192, 8202],
1385
+ [8232, 8232],
1386
+ [8233, 8233],
1387
+ [8239, 8239],
1388
+ [8287, 8287],
1389
+ [12288, 12288],
1390
+ [65279, 65279]
1391
+ ];
1377
1392
  function shorthandRanges(ch) {
1378
- return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1393
+ if (ch === "d") return [[48, 57]];
1394
+ if (ch === "s") return SPACE_RANGES;
1395
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1396
+ }
1397
+ function readUnicodeEscape(body, i) {
1398
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1399
+ const hex = body.slice(i + 2, i + 6);
1400
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1401
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1379
1402
  }
1380
1403
  function parseClassRanges(body) {
1381
1404
  const ranges = [];
@@ -1384,11 +1407,16 @@ function parseClassRanges(body) {
1384
1407
  const ch = body[i];
1385
1408
  if (ch === void 0) return null;
1386
1409
  if (ch === "\\") {
1410
+ const uni = readUnicodeEscape(body, i);
1411
+ if (uni) {
1412
+ i = uni.next;
1413
+ return { cp: uni.cp };
1414
+ }
1387
1415
  const e = body[i + 1];
1388
1416
  if (e === void 0) return null;
1389
1417
  i += 2;
1390
1418
  if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1391
- if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1419
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1392
1420
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1393
1421
  return { cp: e.codePointAt(0) };
1394
1422
  }
@@ -1416,10 +1444,16 @@ function parseClassRanges(body) {
1416
1444
  function classToRanges(cls) {
1417
1445
  if (cls === "\\d") return shorthandRanges("d");
1418
1446
  if (cls === "\\w") return shorthandRanges("w");
1447
+ if (cls === "\\s") return shorthandRanges("s");
1419
1448
  const body = cls.slice(1, -1);
1420
1449
  if (body.startsWith("^")) return null;
1421
1450
  return parseClassRanges(body);
1422
1451
  }
1452
+ var foldEq = (cVar, cp) => {
1453
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1454
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1455
+ return `${cVar} === ${cp}`;
1456
+ };
1423
1457
  function literalCodePoints(frag) {
1424
1458
  const out = [];
1425
1459
  let i = 0;
@@ -1466,22 +1500,446 @@ function parseStringShape(source) {
1466
1500
  if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1467
1501
  return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1468
1502
  }
1503
+ function parseSeqParts(source) {
1504
+ const parts = [];
1505
+ let lit = [];
1506
+ const flush = () => {
1507
+ if (lit.length) {
1508
+ parts.push({ part: "lit", cps: lit, optional: false });
1509
+ lit = [];
1510
+ }
1511
+ };
1512
+ const runFrom = (ranges, negated, q) => {
1513
+ if (q === "+") {
1514
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1515
+ return 1;
1516
+ }
1517
+ if (q === "*") {
1518
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1519
+ return 1;
1520
+ }
1521
+ if (q === "?") {
1522
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1523
+ return 1;
1524
+ }
1525
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1526
+ return 0;
1527
+ };
1528
+ let i = 0;
1529
+ while (i < source.length) {
1530
+ const ch = source[i];
1531
+ if (ch === "[") {
1532
+ let k = i + 1;
1533
+ const neg = source[k] === "^";
1534
+ if (neg) k++;
1535
+ let body = "";
1536
+ while (k < source.length && source[k] !== "]") {
1537
+ if (source[k] === "\\") {
1538
+ body += source[k] + (source[k + 1] ?? "");
1539
+ k += 2;
1540
+ } else {
1541
+ body += source[k];
1542
+ k++;
1543
+ }
1544
+ }
1545
+ if (source[k] !== "]") return null;
1546
+ const ranges = parseClassRanges(body);
1547
+ if (!ranges) return null;
1548
+ flush();
1549
+ i = k + 1;
1550
+ i += runFrom(ranges, neg, source[i]);
1551
+ continue;
1552
+ }
1553
+ if (ch === "\\") {
1554
+ const e = source[i + 1];
1555
+ if (e === "d" || e === "w" || e === "s") {
1556
+ flush();
1557
+ i += 2;
1558
+ i += runFrom(shorthandRanges(e), false, source[i]);
1559
+ continue;
1560
+ }
1561
+ const uni = readUnicodeEscape(source, i);
1562
+ let cp2;
1563
+ if (uni) {
1564
+ cp2 = uni.cp;
1565
+ i = uni.next;
1566
+ } else if (e === void 0) return null;
1567
+ else if (e in CLASS_ESCAPES) {
1568
+ cp2 = CLASS_ESCAPES[e];
1569
+ i += 2;
1570
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1571
+ else {
1572
+ cp2 = e.codePointAt(0);
1573
+ i += 2;
1574
+ }
1575
+ if (source[i] === "?") {
1576
+ flush();
1577
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1578
+ i++;
1579
+ } else lit.push(cp2);
1580
+ continue;
1581
+ }
1582
+ if (ch === "(") {
1583
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1584
+ let k = i + 3;
1585
+ let depth = 1;
1586
+ while (k < source.length && depth > 0) {
1587
+ const c2 = source[k];
1588
+ if (c2 === "\\") {
1589
+ k += 2;
1590
+ continue;
1591
+ }
1592
+ if (c2 === "[") {
1593
+ let j = k + 1;
1594
+ if (source[j] === "^") j++;
1595
+ while (j < source.length && source[j] !== "]") {
1596
+ if (source[j] === "\\") j += 2;
1597
+ else j++;
1598
+ }
1599
+ if (source[j] !== "]") return null;
1600
+ k = j + 1;
1601
+ continue;
1602
+ }
1603
+ if (c2 === "(") depth++;
1604
+ else if (c2 === ")") depth--;
1605
+ k++;
1606
+ }
1607
+ if (depth !== 0) return null;
1608
+ const body = source.slice(i + 3, k - 1);
1609
+ const inner = parseScanShape(body);
1610
+ if (!inner || !groupInnerSafe(inner)) return null;
1611
+ const q = source[k];
1612
+ if (q === "{") return null;
1613
+ flush();
1614
+ if (q === "+") {
1615
+ if (shapeCanBeEmpty(inner)) return null;
1616
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1617
+ i = k + 1;
1618
+ } else if (q === "*") {
1619
+ if (shapeCanBeEmpty(inner)) return null;
1620
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1621
+ i = k + 1;
1622
+ } else if (q === "?") {
1623
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1624
+ i = k + 1;
1625
+ } else {
1626
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1627
+ i = k;
1628
+ }
1629
+ continue;
1630
+ }
1631
+ if (META.has(ch)) return null;
1632
+ const cp = ch.codePointAt(0);
1633
+ i += ch.length;
1634
+ if (source[i] === "?") {
1635
+ flush();
1636
+ parts.push({ part: "lit", cps: [cp], optional: true });
1637
+ i++;
1638
+ } else lit.push(cp);
1639
+ }
1640
+ flush();
1641
+ return parts.length ? parts : null;
1642
+ }
1643
+ function seqIsUnambiguous(parts) {
1644
+ let hasMandatory = false;
1645
+ for (let k = 0; k < parts.length; k++) {
1646
+ const p = parts[k];
1647
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1648
+ if (mandatory) hasMandatory = true;
1649
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1650
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1651
+ if (skippableSingle || isUnboundedRepeat) {
1652
+ const rest = seqFirstAccept(parts.slice(k + 1));
1653
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1654
+ }
1655
+ }
1656
+ return hasMandatory;
1657
+ }
1658
+ function rangesOverlap(a, b) {
1659
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1660
+ }
1661
+ function rangesSubset(sub, sup) {
1662
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1663
+ }
1664
+ function parseClassOperand(body) {
1665
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1666
+ return { ranges: shorthandRanges(body[1]), negated: false };
1667
+ }
1668
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1669
+ let inner = body.slice(1, -1);
1670
+ const negated = inner.startsWith("^");
1671
+ if (negated) inner = inner.slice(1);
1672
+ const ranges = parseClassRanges(inner);
1673
+ return ranges ? { ranges, negated } : null;
1674
+ }
1675
+ const cps = literalCodePoints(body);
1676
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1677
+ return null;
1678
+ }
1679
+ function classSubset(t, u) {
1680
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1681
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1682
+ if (t.negated && !u.negated) return false;
1683
+ return rangesSubset(u.ranges, t.ranges);
1684
+ }
1685
+ function classDisjoint(t, u) {
1686
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1687
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1688
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1689
+ return false;
1690
+ }
1691
+ function firstSetsDisjoint(a, b) {
1692
+ if (a === "any" || b === "any") return false;
1693
+ return classDisjoint(a, b);
1694
+ }
1695
+ function trailingBacktrackClass(shape) {
1696
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1697
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1698
+ if (shape.kind === "litFold") return null;
1699
+ if (shape.kind === "seq") {
1700
+ const last = shape.parts[shape.parts.length - 1];
1701
+ if (last.part === "lit") return null;
1702
+ if (last.part === "run") {
1703
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1704
+ return null;
1705
+ }
1706
+ const innerWiggle = trailingBacktrackClass(last.inner);
1707
+ if (innerWiggle === "unsupported") return "unsupported";
1708
+ const requiredOnce = !last.unbounded && last.min === 1;
1709
+ if (requiredOnce) return innerWiggle;
1710
+ const dropExposed = shapeFirstAccept(last.inner);
1711
+ if (dropExposed === "any") return "unsupported";
1712
+ if (innerWiggle === null) return dropExposed;
1713
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1714
+ return unioned === "any" ? "unsupported" : unioned;
1715
+ }
1716
+ if (shape.kind === "alt") {
1717
+ if (!shape.disjoint) return "unsupported";
1718
+ const classes = shape.arms.map(trailingBacktrackClass);
1719
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1720
+ const concrete = classes.filter((c) => c !== null);
1721
+ if (concrete.length === 0) return null;
1722
+ const unioned = unionCharSets(concrete);
1723
+ return unioned === "any" ? "unsupported" : unioned;
1724
+ }
1725
+ return "unsupported";
1726
+ }
1727
+ function lookaheadUnambiguous(inner, operand, negative) {
1728
+ const tail = trailingBacktrackClass(inner);
1729
+ if (tail === "unsupported") return false;
1730
+ if (tail === null) return true;
1731
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1732
+ }
1733
+ function stripTrailingLookahead(source) {
1734
+ if (!source.endsWith(")")) return null;
1735
+ for (const negative of [true, false]) {
1736
+ const lead = negative ? "(?!" : "(?=";
1737
+ const idx = source.lastIndexOf(lead);
1738
+ if (idx === -1) continue;
1739
+ const body = source.slice(idx + lead.length, -1);
1740
+ const operand = body.length ? parseClassOperand(body) : null;
1741
+ if (!operand) continue;
1742
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1743
+ }
1744
+ return null;
1745
+ }
1746
+ function unwrapOuterGroup(source) {
1747
+ while (source.startsWith("(?:")) {
1748
+ let depth = 1;
1749
+ let i = 3;
1750
+ let ok = true;
1751
+ while (i < source.length && depth > 0) {
1752
+ const ch = source[i];
1753
+ if (ch === "\\") {
1754
+ i += 2;
1755
+ continue;
1756
+ }
1757
+ if (ch === "[") {
1758
+ let j = i + 1;
1759
+ if (source[j] === "^") j++;
1760
+ while (j < source.length && source[j] !== "]") {
1761
+ if (source[j] === "\\") j += 2;
1762
+ else j++;
1763
+ }
1764
+ if (source[j] !== "]") {
1765
+ ok = false;
1766
+ break;
1767
+ }
1768
+ i = j + 1;
1769
+ continue;
1770
+ }
1771
+ if (ch === "(") depth++;
1772
+ else if (ch === ")") depth--;
1773
+ i++;
1774
+ }
1775
+ if (!ok || depth !== 0 || i !== source.length) break;
1776
+ source = source.slice(3, -1);
1777
+ }
1778
+ return source;
1779
+ }
1780
+ function splitTopLevelAlternation(source) {
1781
+ const body = unwrapOuterGroup(source);
1782
+ const parts = [];
1783
+ let depth = 0;
1784
+ let last = 0;
1785
+ let i = 0;
1786
+ while (i < body.length) {
1787
+ const ch = body[i];
1788
+ if (ch === "\\") {
1789
+ i += 2;
1790
+ continue;
1791
+ }
1792
+ if (ch === "[") {
1793
+ let j = i + 1;
1794
+ if (body[j] === "^") j++;
1795
+ while (j < body.length && body[j] !== "]") {
1796
+ if (body[j] === "\\") j += 2;
1797
+ else j++;
1798
+ }
1799
+ if (body[j] !== "]") return null;
1800
+ i = j + 1;
1801
+ continue;
1802
+ }
1803
+ if (ch === "(") {
1804
+ depth++;
1805
+ i++;
1806
+ continue;
1807
+ }
1808
+ if (ch === ")") {
1809
+ depth--;
1810
+ if (depth < 0) return null;
1811
+ i++;
1812
+ continue;
1813
+ }
1814
+ if (ch === "|" && depth === 0) {
1815
+ parts.push(body.slice(last, i));
1816
+ last = i + 1;
1817
+ i++;
1818
+ continue;
1819
+ }
1820
+ i++;
1821
+ }
1822
+ if (depth !== 0) return null;
1823
+ parts.push(body.slice(last));
1824
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1825
+ return parts;
1826
+ }
1827
+ function shapeCanBeEmpty(shape) {
1828
+ if (shape.kind === "chars") return !shape.minOne;
1829
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1830
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1831
+ return false;
1832
+ }
1833
+ function unionCharSets(sets) {
1834
+ if (sets.some((s) => s === "any")) return "any";
1835
+ const concrete = sets;
1836
+ if (concrete.length === 1) return concrete[0];
1837
+ if (concrete.some((s) => s.negated)) return "any";
1838
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1839
+ }
1840
+ function partFirstAccept(part) {
1841
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1842
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1843
+ return shapeFirstAccept(part.inner);
1844
+ }
1845
+ function seqFirstAccept(parts) {
1846
+ const sets = [];
1847
+ for (const p of parts) {
1848
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1849
+ sets.push(partFirstAccept(p));
1850
+ if (mandatory) break;
1851
+ }
1852
+ return unionCharSets(sets);
1853
+ }
1854
+ function groupInnerSafe(shape) {
1855
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1856
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1857
+ if (shape.kind === "alt") return shape.disjoint;
1858
+ return false;
1859
+ }
1860
+ function shapeFirstAccept(shape) {
1861
+ if (shapeCanBeEmpty(shape)) return "any";
1862
+ switch (shape.kind) {
1863
+ case "chars":
1864
+ return { ranges: shape.ranges, negated: false };
1865
+ case "ident":
1866
+ return { ranges: shape.head, negated: false };
1867
+ case "until":
1868
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1869
+ case "delimited":
1870
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1871
+ case "string":
1872
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1873
+ case "litFold": {
1874
+ const cp = shape.open[0];
1875
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1876
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1877
+ return { ranges: [[cp, cp]], negated: false };
1878
+ }
1879
+ case "seq":
1880
+ return seqFirstAccept(shape.parts);
1881
+ case "lookahead":
1882
+ return shapeFirstAccept(shape.inner);
1883
+ // zero-width — doesn't change first-char acceptance
1884
+ case "alt": {
1885
+ if (!shape.disjoint) return "any";
1886
+ const sets = shape.firsts.filter((f) => f !== null);
1887
+ return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
1888
+ }
1889
+ }
1890
+ }
1891
+ function allPairsDisjoint(sets) {
1892
+ for (let i = 0; i < sets.length; i++) {
1893
+ const a = sets[i];
1894
+ if (a === "any") return false;
1895
+ for (let j = i + 1; j < sets.length; j++) {
1896
+ const b = sets[j];
1897
+ if (b === "any" || !classDisjoint(a, b)) return false;
1898
+ }
1899
+ }
1900
+ return true;
1901
+ }
1902
+ function parseAlternation(source) {
1903
+ const parts = splitTopLevelAlternation(source);
1904
+ if (!parts) return null;
1905
+ const arms = [];
1906
+ for (const p of parts) {
1907
+ const arm = parseScanShape(p);
1908
+ if (!arm) return null;
1909
+ arms.push(arm);
1910
+ }
1911
+ const firstSets = arms.map(shapeFirstAccept);
1912
+ const disjoint = allPairsDisjoint(firstSets);
1913
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
1914
+ }
1469
1915
  function parseScanShape(source) {
1916
+ const alt = parseAlternation(source);
1917
+ if (alt) return alt;
1918
+ const la = stripTrailingLookahead(source);
1919
+ if (la) {
1920
+ const inner = parseScanShapeCore(la.base);
1921
+ if (!inner) return null;
1922
+ const operand = { ranges: la.ranges, negated: la.classNegated };
1923
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
1924
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
1925
+ }
1926
+ return parseScanShapeCore(source);
1927
+ }
1928
+ function parseScanShapeCore(source) {
1470
1929
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1471
- if (m) {
1472
- if (m[1].startsWith("^")) return null;
1930
+ if (m && !m[1].startsWith("^")) {
1473
1931
  const ranges = parseClassRanges(m[1]);
1474
1932
  return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1475
1933
  }
1476
- m = /^\\([dw])([+*])$/.exec(source);
1934
+ m = /^\\([dws])([+*])$/.exec(source);
1477
1935
  if (m) {
1478
1936
  return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1479
1937
  }
1480
- m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
1938
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1481
1939
  if (m) {
1482
1940
  const head = classToRanges(m[1]);
1483
1941
  const tail = classToRanges(m[2]);
1484
- return head && tail ? { kind: "ident", head, tail } : null;
1942
+ if (head && tail) return { kind: "ident", head, tail };
1485
1943
  }
1486
1944
  const str = parseStringShape(source);
1487
1945
  if (str) return str;
@@ -1490,7 +1948,6 @@ function parseScanShape(source) {
1490
1948
  const open = literalCodePoints(m[1]);
1491
1949
  const stop = parseClassRanges(m[2]);
1492
1950
  if (open && stop) return { kind: "until", open, stop };
1493
- return null;
1494
1951
  }
1495
1952
  if (!source.includes("\\\\")) {
1496
1953
  m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
@@ -1500,10 +1957,16 @@ function parseScanShape(source) {
1500
1957
  if (open && close) return { kind: "delimited", open, close };
1501
1958
  }
1502
1959
  }
1960
+ const parts = parseSeqParts(source);
1961
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1503
1962
  return null;
1504
1963
  }
1505
1964
  function scanShapeFromRegex(source, flags) {
1506
- if (/[imsu]/.test(flags)) return null;
1965
+ if (/[msu]/.test(flags)) return null;
1966
+ if (/i/.test(flags)) {
1967
+ const open = literalCodePoints(source);
1968
+ return open ? { kind: "litFold", open } : null;
1969
+ }
1507
1970
  return parseScanShape(source);
1508
1971
  }
1509
1972
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
@@ -1518,6 +1981,53 @@ function stringHardStop(shape) {
1518
1981
  }
1519
1982
  var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1520
1983
  function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1984
+ if (shape.kind === "lookahead") {
1985
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
1986
+ const okV = mint("_ok");
1987
+ const endV2 = mint("_end");
1988
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
1989
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
1990
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
1991
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
1992
+ return {
1993
+ setup: [
1994
+ ...inner.setup,
1995
+ `${ind2}let ${okV} = ${inner.ok}`,
1996
+ `${ind2}let ${endV2} = ${inner.end}`,
1997
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
1998
+ ],
1999
+ ok: okV,
2000
+ end: endV2
2001
+ };
2002
+ }
2003
+ if (shape.kind === "alt") {
2004
+ const okV = mint("_ok");
2005
+ const endV2 = mint("_end");
2006
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
2007
+ if (shape.disjoint) {
2008
+ const c0 = codeAt(start, firstChar);
2009
+ shape.arms.forEach((arm, k) => {
2010
+ const fs2 = shape.firsts[k];
2011
+ const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
2012
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2013
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
2014
+ lines.push(...m.setup);
2015
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
2016
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
2017
+ lines.push(`${ind2}}`);
2018
+ });
2019
+ } else {
2020
+ const lbl = mint("_alt");
2021
+ lines.push(`${ind2}${lbl}: {`);
2022
+ for (const arm of shape.arms) {
2023
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2024
+ lines.push(...m.setup);
2025
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
2026
+ }
2027
+ lines.push(`${ind2}}`);
2028
+ }
2029
+ return { setup: lines, ok: okV, end: endV2 };
2030
+ }
1521
2031
  if (shape.kind === "chars") {
1522
2032
  const cur = mint("_e");
1523
2033
  return {
@@ -1544,6 +2054,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1544
2054
  end: cur
1545
2055
  };
1546
2056
  }
2057
+ if (shape.kind === "seq") {
2058
+ const endV2 = mint("_e");
2059
+ const okV = mint("_ok");
2060
+ const runCond = (p, at) => {
2061
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2062
+ return p.negated ? `!(${c})` : `(${c})`;
2063
+ };
2064
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2065
+ let first = true;
2066
+ for (const p of shape.parts) {
2067
+ if (p.part === "lit") {
2068
+ const L = p.cps.length;
2069
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2070
+ if (p.optional) {
2071
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2072
+ } else {
2073
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2074
+ lines.push(`${ind2} ${endV2} += ${L}`);
2075
+ }
2076
+ } else if (p.part === "run" && !p.unbounded) {
2077
+ if (p.min === 1) {
2078
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2079
+ lines.push(`${ind2} ${endV2}++`);
2080
+ } else {
2081
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2082
+ }
2083
+ } else if (p.part === "run") {
2084
+ const s = mint("_s");
2085
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2086
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2087
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2088
+ } else {
2089
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2090
+ if (!p.unbounded) {
2091
+ lines.push(...inner.setup);
2092
+ if (p.min === 1) {
2093
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2094
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2095
+ } else {
2096
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2097
+ }
2098
+ } else {
2099
+ const progV = mint("_prog");
2100
+ lines.push(`${ind2} let ${progV} = false`);
2101
+ lines.push(`${ind2} while (true) {`);
2102
+ lines.push(...inner.setup);
2103
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2104
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2105
+ lines.push(`${ind2} ${progV} = true`);
2106
+ lines.push(`${ind2} }`);
2107
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2108
+ }
2109
+ }
2110
+ first = false;
2111
+ }
2112
+ lines.push(`${ind2} ${okV} = true`);
2113
+ lines.push(`${ind2}} while (false)`);
2114
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2115
+ return { setup: lines, ok: okV, end: endV2 };
2116
+ }
2117
+ if (shape.kind === "litFold") {
2118
+ const openLen2 = shape.open.length;
2119
+ const endV2 = mint("_end");
2120
+ const checks = shape.open.map((cp, k) => {
2121
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2122
+ return foldEq(cVar, cp);
2123
+ });
2124
+ return {
2125
+ setup: [
2126
+ `${ind2}let ${endV2} = ${start}`,
2127
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2128
+ ],
2129
+ ok: `${endV2} > ${start}`,
2130
+ end: endV2
2131
+ };
2132
+ }
1547
2133
  if (shape.kind === "until") {
1548
2134
  const j2 = mint("_j");
1549
2135
  const openLen2 = shape.open.length;
@@ -1939,7 +2525,7 @@ function hoistExpected(ctx, constArrSource) {
1939
2525
  let name = ctx.expectedMap.get(constArrSource);
1940
2526
  if (name === void 0) {
1941
2527
  name = `_fx${ctx.expectedDecls.length}`;
1942
- ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
2528
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
1943
2529
  ctx.expectedMap.set(constArrSource, name);
1944
2530
  }
1945
2531
  return name;
@@ -2120,10 +2706,10 @@ function ensureTriviaFn(ctx) {
2120
2706
  function ensureTriviaCaptureFn(ctx) {
2121
2707
  return ensureTriviaFn(ctx);
2122
2708
  }
2123
- function firstSetCond(codeVar, fs) {
2124
- if (fs.kind === "any") return "true";
2125
- if (fs.kind === "empty") return "false";
2126
- return fs.ranges.map(
2709
+ function firstSetCond(codeVar, fs2) {
2710
+ if (fs2.kind === "any") return "true";
2711
+ if (fs2.kind === "empty") return "false";
2712
+ return fs2.ranges.map(
2127
2713
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
2128
2714
  ).join(" || ");
2129
2715
  }
@@ -2135,10 +2721,10 @@ function planDisjointDispatch(parsers) {
2135
2721
  const cases = [];
2136
2722
  let total = 0;
2137
2723
  for (const p of parsers) {
2138
- const fs = p._meta.firstSet;
2139
- if (fs.kind !== "ranges") return { kind: "if" };
2724
+ const fs2 = p._meta.firstSet;
2725
+ if (fs2.kind !== "ranges") return { kind: "if" };
2140
2726
  const pts = [];
2141
- for (const r of fs.ranges) {
2727
+ for (const r of fs2.ranges) {
2142
2728
  if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
2143
2729
  for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
2144
2730
  }
@@ -2261,18 +2847,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2261
2847
  return true;
2262
2848
  }
2263
2849
  }
2264
- function emitFallible(inner, ctx, pos) {
2850
+ function emitFallible(inner, ctx, pos, swallow = false) {
2265
2851
  const lbl = v(ctx, "_lbl");
2266
2852
  const okV = `${lbl}ok`;
2267
2853
  const valV = `${lbl}v`;
2268
2854
  const endV = `${lbl}e`;
2269
2855
  const savedLabel = ctx.failLabel;
2270
2856
  const savedIndent = ctx.indent;
2857
+ const savedRecord = ctx.recordFail;
2271
2858
  ctx.failLabel = lbl;
2272
2859
  ctx.indent = savedIndent + 1;
2860
+ if (swallow) ctx.recordFail = false;
2273
2861
  const r = emit(inner, ctx, pos);
2274
2862
  ctx.failLabel = savedLabel;
2275
2863
  ctx.indent = savedIndent;
2864
+ ctx.recordFail = savedRecord;
2276
2865
  const ind0 = ind(ctx);
2277
2866
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2278
2867
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2295,6 +2884,7 @@ function emitFallible(inner, ctx, pos) {
2295
2884
  ];
2296
2885
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2297
2886
  }
2887
+ var CHARCODE_CHAIN_MAX = 16;
2298
2888
  function emitLit(def, ctx, pos) {
2299
2889
  const { value, caseInsensitive } = def;
2300
2890
  const len = value.length;
@@ -2317,7 +2907,7 @@ function emitLit(def, ctx, pos) {
2317
2907
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2318
2908
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2319
2909
  );
2320
- } else if (len <= 4) {
2910
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2321
2911
  const checks = Array.from(
2322
2912
  { length: len },
2323
2913
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2339,7 +2929,55 @@ function emitLit(def, ctx, pos) {
2339
2929
  function escapeKeywordRe(s) {
2340
2930
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2341
2931
  }
2932
+ function emitKeywordsFast(def, ctx, pos) {
2933
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
2934
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
2935
+ if (def.caseInsensitive && def.boundary) return null;
2936
+ let boundary = null;
2937
+ if (def.boundary) {
2938
+ let body = def.boundary;
2939
+ const negated = body.startsWith("^");
2940
+ if (negated) body = body.slice(1);
2941
+ const ranges = parseClassRanges(body);
2942
+ if (!ranges) return null;
2943
+ boundary = { ranges, negated };
2944
+ }
2945
+ const mint = (prefix = "_v") => v(ctx, prefix);
2946
+ const lbl = v(ctx, "_kwLbl");
2947
+ const valV = v(ctx, "_kwv");
2948
+ const endV = v(ctx, "_kwe");
2949
+ const bodyInd = ind(ctx) + " ";
2950
+ const tries = [];
2951
+ for (const w of def.words) {
2952
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
2953
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
2954
+ if (boundary) {
2955
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
2956
+ }
2957
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
2958
+ tries.push(
2959
+ ...m.setup,
2960
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
2961
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
2962
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
2963
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
2964
+ );
2965
+ }
2966
+ const stmts = [
2967
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
2968
+ `${ind(ctx)}${lbl}: {`,
2969
+ ...tries,
2970
+ `${ind(ctx)}}`,
2971
+ // Every word has length >= 1 (checked above), so a real match always
2972
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
2973
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
2974
+ ];
2975
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
2976
+ return { stmts, valueVar: valV, endVar: endV };
2977
+ }
2342
2978
  function emitKeywords(def, ctx, pos) {
2979
+ const fast = emitKeywordsFast(def, ctx, pos);
2980
+ if (fast) return fast;
2343
2981
  const alt = def.words.map(escapeKeywordRe).join("|");
2344
2982
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2345
2983
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2470,6 +3108,31 @@ function failsAtStart(p) {
2470
3108
  return false;
2471
3109
  }
2472
3110
  }
3111
+ function armStaticExpected(ctx, p) {
3112
+ return hoistExpected(ctx, staticExpectedArr([p]));
3113
+ }
3114
+ function canMatchEmptyAtStart(p) {
3115
+ const d = p._def;
3116
+ switch (d.tag) {
3117
+ case "regex":
3118
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3119
+ case "optional":
3120
+ return true;
3121
+ case "many":
3122
+ return true;
3123
+ case "transform":
3124
+ case "label":
3125
+ return canMatchEmptyAtStart(d.parser);
3126
+ case "literal":
3127
+ return d.value.length === 0;
3128
+ default:
3129
+ return false;
3130
+ }
3131
+ }
3132
+ function needsFirstSetGuard(p) {
3133
+ const fs2 = p._meta.firstSet;
3134
+ return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
3135
+ }
2473
3136
  function emitDisjointArm(p, ctx, pos, valV, endV) {
2474
3137
  if (failsAtStart(p)) {
2475
3138
  const r = emit(p, ctx, pos);
@@ -2611,16 +3274,21 @@ function emitFirstMatch(def, ctx, pos) {
2611
3274
  const resValV = v(ctx, "_crv");
2612
3275
  const resEndV = v(ctx, "_cre");
2613
3276
  const resOkV = v(ctx, "_crok");
3277
+ const codeV = v(ctx, "_chcode");
2614
3278
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
2615
3279
  const ind0 = ind(ctx);
2616
3280
  const stmts = [
2617
3281
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2618
- `${ind0}let ${slots.join(", ")}`
3282
+ `${ind0}let ${slots.join(", ")}`,
3283
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2619
3284
  ];
2620
3285
  for (let i = 0; i < def.parsers.length; i++) {
2621
3286
  const p = def.parsers[i];
2622
3287
  const gate = def.gates[i];
2623
3288
  const autoNot = def.autoNot[i];
3289
+ const atStart = failsAtStart(p);
3290
+ const staticFx = armStaticExpected(ctx, p);
3291
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2624
3292
  let gateCond = null;
2625
3293
  if (gate) {
2626
3294
  const gateIdx = ctx.mapFns.length;
@@ -2635,39 +3303,41 @@ function emitFirstMatch(def, ctx, pos) {
2635
3303
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2636
3304
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2637
3305
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3306
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2638
3307
  stmts.push(`${ind0}if (${skipCond}) {`);
2639
- ctx.indent++;
2640
- const ind1 = ind(ctx);
3308
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3309
+ ctx.indent += fsGuard ? 2 : 1;
2641
3310
  if (markLeaves) {
2642
3311
  stmts.push(
2643
- `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2644
- `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2645
- `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2646
- `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
3312
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3313
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3314
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3315
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2647
3316
  );
2648
3317
  }
2649
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3318
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2650
3319
  stmts.push(...armStmts);
2651
- ctx.indent--;
2652
3320
  if (autoNot && autoNot.length > 0) {
2653
3321
  const anCode = v(ctx, "_anc");
2654
3322
  const rejectCond = autoNot.map(
2655
3323
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2656
3324
  ).join(" || ");
2657
- stmts.push(`${ind0} if (${okVar}) {`);
2658
- stmts.push(`${ind0} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
2659
- stmts.push(`${ind0} if (!(${rejectCond})) {`);
2660
- stmts.push(`${ind0} ${resValV} = ${valVar}`);
2661
- stmts.push(`${ind0} ${resEndV} = ${endVar}`);
2662
- stmts.push(`${ind0} ${resOkV} = true`);
2663
- stmts.push(`${ind0} }`);
2664
- stmts.push(`${ind0} }`);
2665
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
2666
- if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
3325
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3326
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3327
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3328
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3329
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3330
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3331
+ stmts.push(`${ind(ctx)} }`);
3332
+ stmts.push(`${ind(ctx)}}`);
3333
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3334
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2667
3335
  } else {
2668
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2669
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
3336
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3337
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2670
3338
  }
3339
+ ctx.indent -= fsGuard ? 2 : 1;
3340
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2671
3341
  stmts.push(`${ind0}}`);
2672
3342
  }
2673
3343
  const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
@@ -2700,7 +3370,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2700
3370
  function emitLiteralCondition(litVal, pos) {
2701
3371
  const len = litVal.length;
2702
3372
  if (len === 0) return "true";
2703
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3373
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2704
3374
  const checks = [`${pos} + ${len} <= input.length`];
2705
3375
  for (let i = 0; i < len; i++) {
2706
3376
  const code = litVal.codePointAt(i);
@@ -2749,7 +3419,7 @@ function emitMany(def, ctx, pos) {
2749
3419
  itemPos = npV;
2750
3420
  }
2751
3421
  }
2752
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3422
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2753
3423
  stmts.push(
2754
3424
  ...iterStmts,
2755
3425
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2763,7 +3433,7 @@ function emitMany(def, ctx, pos) {
2763
3433
  function emitOptional(def, ctx, pos) {
2764
3434
  const valV = v(ctx, "_opt");
2765
3435
  const endV = v(ctx, "_opte");
2766
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3436
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2767
3437
  const ind0 = ind(ctx);
2768
3438
  const stmts = [
2769
3439
  ...lblStmts,
@@ -2775,7 +3445,7 @@ function emitOptional(def, ctx, pos) {
2775
3445
  function emitSepBy(_p, def, ctx, pos) {
2776
3446
  const arrV = v(ctx, "_arr");
2777
3447
  const curV = v(ctx, "_cur");
2778
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3448
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2779
3449
  const stmts = [
2780
3450
  `${ind(ctx)}const ${arrV} = []`,
2781
3451
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2807,11 +3477,11 @@ function emitSepBy(_p, def, ctx, pos) {
2807
3477
  );
2808
3478
  sepAtPos = spV;
2809
3479
  const rollbackToSep = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
2810
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3480
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2811
3481
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2812
3482
  const npV = v(ctx, "_np");
2813
3483
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
2814
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3484
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2815
3485
  stmts.push(
2816
3486
  ...nextStmts,
2817
3487
  // item failed → unwind the separator too, back to the end of the last item
@@ -2824,11 +3494,11 @@ function emitSepBy(_p, def, ctx, pos) {
2824
3494
  const spV = v(ctx, "_sp");
2825
3495
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2826
3496
  sepAtPos = spV;
2827
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3497
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2828
3498
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2829
3499
  const npV = v(ctx, "_np");
2830
3500
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2831
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3501
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2832
3502
  stmts.push(
2833
3503
  ...nextStmts,
2834
3504
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2845,10 +3515,10 @@ function emitSepBy(_p, def, ctx, pos) {
2845
3515
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2846
3516
  );
2847
3517
  }
2848
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3518
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2849
3519
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2850
3520
  const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
2851
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
3521
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2852
3522
  stmts.push(
2853
3523
  ...nextStmts,
2854
3524
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -2906,7 +3576,7 @@ function emitScanTo(def, ctx, pos) {
2906
3576
  return { stmts, valueVar: valV, endVar: curV };
2907
3577
  }
2908
3578
  function emitNot(def, ctx, pos) {
2909
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3579
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
2910
3580
  const label2 = JSON.stringify(`not(${def.parser._tag})`);
2911
3581
  return {
2912
3582
  stmts: [
@@ -2997,11 +3667,14 @@ function emitLazy(p, def, ctx, pos) {
2997
3667
  }
2998
3668
  const savedIndent = ctx.indent;
2999
3669
  const savedFailLabel = ctx.failLabel;
3670
+ const savedRecord = ctx.recordFail;
3000
3671
  ctx.indent = 1;
3001
3672
  ctx.failLabel = "_pfail";
3673
+ ctx.recordFail = true;
3002
3674
  const r = emit(resolved, ctx, "_pos");
3003
3675
  ctx.indent = savedIndent;
3004
3676
  ctx.failLabel = savedFailLabel;
3677
+ ctx.recordFail = savedRecord;
3005
3678
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
3006
3679
  }
3007
3680
  const fnName = ctx.namedParsers.get(p);
@@ -3181,11 +3854,14 @@ function emit(p, ctx, pos) {
3181
3854
  ctx.namedParsers.set(innerParser, fnName);
3182
3855
  const savedIndent = ctx.indent;
3183
3856
  const savedFailLabel = ctx.failLabel;
3857
+ const savedRecord = ctx.recordFail;
3184
3858
  ctx.indent = 1;
3185
3859
  ctx.failLabel = "_pfail";
3860
+ ctx.recordFail = true;
3186
3861
  const innerR = emit(innerParser, ctx, "_pos");
3187
3862
  ctx.indent = savedIndent;
3188
3863
  ctx.failLabel = savedFailLabel;
3864
+ ctx.recordFail = savedRecord;
3189
3865
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
3190
3866
  }
3191
3867
  const fn = ctx.namedParsers.get(innerParser);
@@ -3333,6 +4009,7 @@ function compile(combinator, mapFnSources) {
3333
4009
  regexMap: /* @__PURE__ */ new Map(),
3334
4010
  expectedDecls: [],
3335
4011
  expectedMap: /* @__PURE__ */ new Map(),
4012
+ recordFail: true,
3336
4013
  mapFns: [],
3337
4014
  mapFnSrcs: [],
3338
4015
  buildFns: [],
@@ -3415,6 +4092,7 @@ function compileRuleMap(ruleMap) {
3415
4092
  regexMap: /* @__PURE__ */ new Map(),
3416
4093
  expectedDecls: [],
3417
4094
  expectedMap: /* @__PURE__ */ new Map(),
4095
+ recordFail: true,
3418
4096
  mapFns: [],
3419
4097
  mapFnSrcs: [],
3420
4098
  buildFns: [],
@@ -3589,11 +4267,11 @@ function balanced(open, close, options = {}) {
3589
4267
  return combi;
3590
4268
  }
3591
4269
  function firstSetClassChars(c) {
3592
- const fs = c._meta.firstSet;
3593
- if (fs.kind === "empty") return [];
3594
- if (fs.kind !== "ranges") return null;
4270
+ const fs2 = c._meta.firstSet;
4271
+ if (fs2.kind === "empty") return [];
4272
+ if (fs2.kind !== "ranges") return null;
3595
4273
  const out = [];
3596
- for (const { lo, hi } of fs.ranges) {
4274
+ for (const { lo, hi } of fs2.ranges) {
3597
4275
  if (hi - lo > 8) return null;
3598
4276
  for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
3599
4277
  }
@@ -3897,7 +4575,117 @@ function evaluateCombinatorArray(node2, scope, code) {
3897
4575
  if (!val.every(isCombinator)) return null;
3898
4576
  return val;
3899
4577
  }
3900
- function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
4578
+ function asFragmentFactory(expr) {
4579
+ if (expr.type !== "ArrowFunctionExpression" && expr.type !== "FunctionExpression" && expr.type !== "FunctionDeclaration") return null;
4580
+ const fn = expr;
4581
+ return factoryBodyAndReturn(fn) ? fn : null;
4582
+ }
4583
+ var unwrapParen = (e) => e && e.type === "ParenthesizedExpression" ? e.expression : e;
4584
+ function factoryBodyAndReturn(fn) {
4585
+ const body = fn.body;
4586
+ if (!body) return null;
4587
+ const b = unwrapParen(body);
4588
+ if (b.type === "ObjectExpression") return { statements: [], retObj: b };
4589
+ if (b.type === "BlockStatement") {
4590
+ const statements = [];
4591
+ for (const stmt of b.body) {
4592
+ if (stmt.type === "ReturnStatement") {
4593
+ const a = unwrapParen(stmt.argument);
4594
+ if (!a || a.type !== "ObjectExpression") return null;
4595
+ return { statements, retObj: a };
4596
+ }
4597
+ if (stmt.type === "VariableDeclaration") statements.push(stmt);
4598
+ else return null;
4599
+ }
4600
+ }
4601
+ return null;
4602
+ }
4603
+ function spreadFactoryCall(spread, resolve) {
4604
+ const call = spread.argument;
4605
+ if (!call || call.type !== "CallExpression") return null;
4606
+ if (!call.callee || call.callee.type !== "Identifier" || !call.callee.name) return null;
4607
+ const resolved = resolve(call.callee.name);
4608
+ return resolved ? { fn: resolved.fn, args: call.arguments ?? [], code: resolved.code } : null;
4609
+ }
4610
+ function factoryCallScope(fn, args, outer) {
4611
+ const s = new Map(outer);
4612
+ const params = fn.params ?? [];
4613
+ for (let i = 0; i < params.length; i++) {
4614
+ const pn = params[i];
4615
+ if (!pn || pn.type !== "Identifier" || !pn.name) continue;
4616
+ const arg = args[i];
4617
+ if (arg && arg.type === "Identifier" && arg.name && outer.has(arg.name)) {
4618
+ s.set(pn.name, outer.get(arg.name));
4619
+ }
4620
+ }
4621
+ return s;
4622
+ }
4623
+ function propKey(prop) {
4624
+ if (prop.type !== "Property" || prop.computed || !prop.key) return null;
4625
+ if (prop.key.type === "Identifier") return prop.key.name ?? null;
4626
+ if (prop.key.type === "Literal") return String(prop.key.value);
4627
+ return null;
4628
+ }
4629
+ function collectRuleKeys(retObj, resolve, seen) {
4630
+ const out = [];
4631
+ for (const prop of retObj.properties) {
4632
+ if (prop.type === "SpreadElement") {
4633
+ const sf = spreadFactoryCall(prop, resolve);
4634
+ if (!sf || seen.has(sf.fn)) return null;
4635
+ const br = factoryBodyAndReturn(sf.fn);
4636
+ if (!br) return null;
4637
+ const nested = collectRuleKeys(br.retObj, resolve, new Set(seen).add(sf.fn));
4638
+ if (!nested) return null;
4639
+ out.push(...nested);
4640
+ continue;
4641
+ }
4642
+ const key = propKey(prop);
4643
+ if (!key) return null;
4644
+ out.push(key);
4645
+ }
4646
+ return out;
4647
+ }
4648
+ function flattenRuleEntries(retObj, scope, code, resolve, seen) {
4649
+ const out = [];
4650
+ for (const prop of retObj.properties) {
4651
+ if (prop.type === "SpreadElement") {
4652
+ const sf = spreadFactoryCall(prop, resolve);
4653
+ if (!sf || seen.has(sf.fn)) return null;
4654
+ const br = factoryBodyAndReturn(sf.fn);
4655
+ if (!br) return null;
4656
+ const fragScope = factoryCallScope(sf.fn, sf.args, scope);
4657
+ if (!evalBodyStatements(br.statements, fragScope, sf.code)) return null;
4658
+ const nested = flattenRuleEntries(br.retObj, fragScope, sf.code, resolve, new Set(seen).add(sf.fn));
4659
+ if (!nested) return null;
4660
+ out.push(...nested);
4661
+ continue;
4662
+ }
4663
+ const key = propKey(prop);
4664
+ if (!key) return null;
4665
+ out.push({ key, value: prop.value, scope, code });
4666
+ }
4667
+ return out;
4668
+ }
4669
+ function evalBodyStatements(statements, scope, code) {
4670
+ const bodyMfs = [];
4671
+ for (const stmt of statements) {
4672
+ for (const d of stmt.declarations) {
4673
+ const decl = d;
4674
+ if (!decl.init) return false;
4675
+ const id = decl.id;
4676
+ if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
4677
+ const name = id.name;
4678
+ const before = bodyMfs.length;
4679
+ const val = anyValue(decl.init, scope, code, bodyMfs);
4680
+ if (val === null) return false;
4681
+ const thisDeclMfSrcs = bodyMfs.slice(before);
4682
+ if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
4683
+ else scope.set(name, val);
4684
+ }
4685
+ }
4686
+ return true;
4687
+ }
4688
+ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, resolveFactory = () => null) {
3901
4689
  if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
3902
4690
  const factory = factoryNode;
3903
4691
  const params = factory.params;
@@ -3929,40 +4717,21 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3929
4717
  if (!returnExpr) return null;
3930
4718
  const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
3931
4719
  if (retObj.type !== "ObjectExpression") return null;
4720
+ const keys = collectRuleKeys(retObj, resolveFactory, /* @__PURE__ */ new Set());
4721
+ if (!keys) return null;
3932
4722
  const ruleRefs = /* @__PURE__ */ new Map();
3933
- for (const prop of retObj.properties) {
3934
- if (prop.type !== "Property") return null;
3935
- if (prop.computed) return null;
3936
- const p = prop;
3937
- const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
3938
- if (!key) return null;
3939
- ruleRefs.set(key, ref());
4723
+ for (const key of keys) {
4724
+ if (!ruleRefs.has(key)) ruleRefs.set(key, ref());
3940
4725
  }
3941
4726
  const localScope = new Map(scope);
3942
4727
  localScope.set(proxyName, Object.fromEntries(ruleRefs));
3943
- const bodyMfs = [];
3944
- for (const stmt of statements) {
3945
- for (const d of stmt.declarations) {
3946
- const decl = d;
3947
- if (!decl.init) return null;
3948
- const id = decl.id;
3949
- if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return null;
3950
- const name = id.name;
3951
- const before = bodyMfs.length;
3952
- const val = anyValue(decl.init, localScope, code, bodyMfs);
3953
- if (val === null) return null;
3954
- const thisDeclMfSrcs = bodyMfs.slice(before);
3955
- if (isCombinator(val)) {
3956
- localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
3957
- } else {
3958
- localScope.set(name, val);
3959
- }
3960
- }
3961
- }
3962
- for (const prop of retObj.properties) {
3963
- const p = prop;
3964
- const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
3965
- const val = anyValue(p.value, localScope, code, mapFnSources);
4728
+ if (!evalBodyStatements(statements, localScope, code)) return null;
4729
+ const entries = flattenRuleEntries(retObj, localScope, code, resolveFactory, /* @__PURE__ */ new Set());
4730
+ if (!entries) return null;
4731
+ const finalByKey = /* @__PURE__ */ new Map();
4732
+ for (const e of entries) finalByKey.set(e.key, e);
4733
+ for (const [key, e] of finalByKey) {
4734
+ const val = anyValue(e.value, e.scope, e.code, mapFnSources);
3966
4735
  if (!isCombinator(val)) return null;
3967
4736
  ruleRefs.get(key).define(val);
3968
4737
  }
@@ -4020,6 +4789,40 @@ function referencesAny(node2, names, scope) {
4020
4789
 
4021
4790
  // src/plugin/index.ts
4022
4791
  var PARSEMAN_MODULE = "parseman";
4792
+ var _fragmentResolver = null;
4793
+ function getFragmentResolver() {
4794
+ return _fragmentResolver ??= new import_oxc_resolver.ResolverFactory({
4795
+ extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
4796
+ // TS/NodeNext style: a `./x.js` import may point at `./x.ts` source.
4797
+ extensionAlias: {
4798
+ ".js": [".ts", ".tsx", ".js", ".jsx"],
4799
+ ".mjs": [".mts", ".mjs"],
4800
+ ".cjs": [".cts", ".cjs"]
4801
+ },
4802
+ conditionNames: ["source", "development", "import", "require", "default"],
4803
+ mainFields: ["source", "module", "main"]
4804
+ });
4805
+ }
4806
+ var _moduleParseCache = /* @__PURE__ */ new Map();
4807
+ function parseModuleCached(filePath) {
4808
+ let mtimeMs;
4809
+ try {
4810
+ mtimeMs = fs.statSync(filePath).mtimeMs;
4811
+ } catch {
4812
+ return null;
4813
+ }
4814
+ const hit = _moduleParseCache.get(filePath);
4815
+ if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
4816
+ let parsed = null;
4817
+ try {
4818
+ const src = fs.readFileSync(filePath, "utf8");
4819
+ const r = (0, import_oxc_parser.parseSync)(filePath, src);
4820
+ if (r.errors.length === 0) parsed = { body: r.program.body, src };
4821
+ } catch {
4822
+ }
4823
+ _moduleParseCache.set(filePath, { mtimeMs, parsed });
4824
+ return parsed;
4825
+ }
4023
4826
  var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
4024
4827
  name: "parseman",
4025
4828
  // Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
@@ -4055,15 +4858,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4055
4858
  const body = result.program.body;
4056
4859
  const macroImports = [];
4057
4860
  const allNames = /* @__PURE__ */ new Set();
4861
+ const importBindings = /* @__PURE__ */ new Map();
4058
4862
  for (const stmt of body) {
4059
4863
  if (stmt.type !== "ImportDeclaration") continue;
4060
4864
  const s = stmt;
4061
- if (!moduleAliases.has(s.source.value)) continue;
4062
- const isMacro = s.attributes.some((a) => {
4865
+ const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
4063
4866
  const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
4064
4867
  return key === "type" && a.value.value === "macro";
4065
4868
  });
4066
- if (!isMacro) continue;
4869
+ if (!isMacro) {
4870
+ for (const spec of s.specifiers) {
4871
+ if (spec.type === "ImportSpecifier") {
4872
+ const imported = spec.imported.name ?? spec.local.name;
4873
+ importBindings.set(spec.local.name, { source: s.source.value, imported });
4874
+ }
4875
+ }
4876
+ continue;
4877
+ }
4067
4878
  const names = /* @__PURE__ */ new Set();
4068
4879
  for (const spec of s.specifiers) {
4069
4880
  if (spec.type === "ImportSpecifier") names.add(spec.local.name);
@@ -4073,6 +4884,63 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4073
4884
  }
4074
4885
  if (macroImports.length === 0) return null;
4075
4886
  const scope = /* @__PURE__ */ new Map();
4887
+ const factories = /* @__PURE__ */ new Map();
4888
+ const resolver = getFragmentResolver();
4889
+ const importedFactoryCache = /* @__PURE__ */ new Map();
4890
+ const OUTPUT_DIR = /([\\/])(dist|lib|build|out|output|compiled|cjs|esm|es)([\\/])/;
4891
+ const resolveSourcePath = (spec) => {
4892
+ let resolved = null;
4893
+ try {
4894
+ resolved = resolver.resolveFileSync(id, spec).path ?? null;
4895
+ } catch {
4896
+ }
4897
+ if (!resolved) return null;
4898
+ if (OUTPUT_DIR.test(resolved)) {
4899
+ const srcCandidate = resolved.replace(OUTPUT_DIR, "$1src$3").replace(/\.(c|m)?js$/, ".ts");
4900
+ try {
4901
+ if (fs.statSync(srcCandidate).isFile()) return srcCandidate;
4902
+ } catch {
4903
+ }
4904
+ }
4905
+ return resolved;
4906
+ };
4907
+ const resolveImportedFactory = (name) => {
4908
+ if (importedFactoryCache.has(name)) return importedFactoryCache.get(name);
4909
+ let result2 = null;
4910
+ const binding = importBindings.get(name);
4911
+ if (binding) {
4912
+ const filePath = resolveSourcePath(binding.source);
4913
+ const mod = filePath ? parseModuleCached(filePath) : null;
4914
+ if (mod) {
4915
+ for (const st of mod.body) {
4916
+ if (st.type !== "ExportNamedDeclaration" || !st.declaration) continue;
4917
+ const decl = st.declaration;
4918
+ if (decl.type === "VariableDeclaration") {
4919
+ for (const d of decl.declarations ?? []) {
4920
+ if (d.id?.type === "Identifier" && d.id.name === binding.imported && d.init) {
4921
+ const fac = asFragmentFactory(d.init);
4922
+ if (fac) {
4923
+ result2 = { fn: fac, code: mod.src };
4924
+ break;
4925
+ }
4926
+ }
4927
+ }
4928
+ } else if (decl.type === "FunctionDeclaration" && decl.id?.type === "Identifier" && decl.id.name === binding.imported) {
4929
+ const fac = asFragmentFactory(decl);
4930
+ if (fac) result2 = { fn: fac, code: mod.src };
4931
+ }
4932
+ if (result2) break;
4933
+ }
4934
+ }
4935
+ }
4936
+ importedFactoryCache.set(name, result2);
4937
+ return result2;
4938
+ };
4939
+ const resolveFactory = (n) => {
4940
+ const local = factories.get(n);
4941
+ if (local) return { fn: local, code };
4942
+ return resolveImportedFactory(n);
4943
+ };
4076
4944
  const replacements = [];
4077
4945
  const warnings = [];
4078
4946
  let anyUnresolved = false;
@@ -4093,7 +4961,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4093
4961
  warn(init.start, `${label2}: rules() needs a factory argument`);
4094
4962
  return null;
4095
4963
  }
4096
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
4964
+ const ruleMap = evaluateParserFactory(factoryArg, scope, code, [], resolveFactory);
4097
4965
  if (!ruleMap) {
4098
4966
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
4099
4967
  return null;
@@ -4191,6 +5059,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4191
5059
  });
4192
5060
  continue;
4193
5061
  }
5062
+ const frag = asFragmentFactory(init);
5063
+ if (frag) {
5064
+ factories.set(varName, frag);
5065
+ continue;
5066
+ }
4194
5067
  const mapFnSources = [];
4195
5068
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
4196
5069
  if (parser2 === null) {