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.
@@ -1,6 +1,8 @@
1
1
  // src/plugin/index.ts
2
+ import * as fs from "node:fs";
2
3
  import { createUnplugin } from "unplugin";
3
4
  import { parseSync } from "oxc-parser";
5
+ import { ResolverFactory } from "oxc-resolver";
4
6
  import MagicString from "magic-string";
5
7
 
6
8
  // src/combinators/first-set.ts
@@ -328,15 +330,15 @@ function extractFirstSet(node2) {
328
330
  case "Alternative": {
329
331
  const exprs = node2.expressions;
330
332
  if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
331
- let fs = empty();
333
+ let fs2 = empty();
332
334
  let canNL = false;
333
335
  for (const expr of exprs) {
334
336
  const r = extractFirstSet(expr);
335
- fs = union(fs, r.firstSet);
337
+ fs2 = union(fs2, r.firstSet);
336
338
  canNL = canNL || r.canMatchNewline;
337
339
  if (!canBeEmpty(expr)) break;
338
340
  }
339
- return { firstSet: fs, canMatchNewline: canNL };
341
+ return { firstSet: fs2, canMatchNewline: canNL };
340
342
  }
341
343
  case "Char": {
342
344
  const kind = node2.kind;
@@ -363,22 +365,22 @@ function extractFirstSet(node2) {
363
365
  }
364
366
  case "CharacterClass": {
365
367
  const expressions = node2.expressions;
366
- let fs = empty();
368
+ let fs2 = empty();
367
369
  let canNL = false;
368
370
  for (const expr of expressions) {
369
371
  if (expr.type === "ClassRange") {
370
372
  const from = expr.from.codePoint;
371
373
  const to = expr.to.codePoint;
372
- fs = union(fs, fromRange(from, to));
374
+ fs2 = union(fs2, fromRange(from, to));
373
375
  if (from <= 10 && 10 <= to) canNL = true;
374
376
  } else if (expr.type === "Char") {
375
377
  const r = extractFirstSet(expr);
376
- fs = union(fs, r.firstSet);
378
+ fs2 = union(fs2, r.firstSet);
377
379
  canNL = canNL || r.canMatchNewline;
378
380
  }
379
381
  }
380
382
  if (node2.negative) return { firstSet: any(), canMatchNewline: true };
381
- return { firstSet: fs, canMatchNewline: canNL };
383
+ return { firstSet: fs2, canMatchNewline: canNL };
382
384
  }
383
385
  case "Repetition": {
384
386
  return extractFirstSet(node2.expression);
@@ -687,10 +689,10 @@ function codesToFirstSet(codes) {
687
689
  ranges.push({ lo, hi });
688
690
  return { kind: "ranges", ranges };
689
691
  }
690
- function inFirstSet(code, fs) {
691
- if (fs.kind === "any") return true;
692
- if (fs.kind === "empty") return false;
693
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
692
+ function inFirstSet(code, fs2) {
693
+ if (fs2.kind === "any") return true;
694
+ if (fs2.kind === "empty") return false;
695
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
694
696
  return false;
695
697
  }
696
698
  function areDisjoint(sets) {
@@ -1339,8 +1341,29 @@ function expect(combinator, label2) {
1339
1341
  var BACKSLASH = 92;
1340
1342
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1341
1343
  var META = new Set("()[]{}*+?|^$.".split(""));
1344
+ var SPACE_RANGES = [
1345
+ [9, 13],
1346
+ [32, 32],
1347
+ [160, 160],
1348
+ [5760, 5760],
1349
+ [8192, 8202],
1350
+ [8232, 8232],
1351
+ [8233, 8233],
1352
+ [8239, 8239],
1353
+ [8287, 8287],
1354
+ [12288, 12288],
1355
+ [65279, 65279]
1356
+ ];
1342
1357
  function shorthandRanges(ch) {
1343
- return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1358
+ if (ch === "d") return [[48, 57]];
1359
+ if (ch === "s") return SPACE_RANGES;
1360
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1361
+ }
1362
+ function readUnicodeEscape(body, i) {
1363
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1364
+ const hex = body.slice(i + 2, i + 6);
1365
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1366
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1344
1367
  }
1345
1368
  function parseClassRanges(body) {
1346
1369
  const ranges = [];
@@ -1349,11 +1372,16 @@ function parseClassRanges(body) {
1349
1372
  const ch = body[i];
1350
1373
  if (ch === void 0) return null;
1351
1374
  if (ch === "\\") {
1375
+ const uni = readUnicodeEscape(body, i);
1376
+ if (uni) {
1377
+ i = uni.next;
1378
+ return { cp: uni.cp };
1379
+ }
1352
1380
  const e = body[i + 1];
1353
1381
  if (e === void 0) return null;
1354
1382
  i += 2;
1355
1383
  if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1356
- if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1384
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1357
1385
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1358
1386
  return { cp: e.codePointAt(0) };
1359
1387
  }
@@ -1381,10 +1409,16 @@ function parseClassRanges(body) {
1381
1409
  function classToRanges(cls) {
1382
1410
  if (cls === "\\d") return shorthandRanges("d");
1383
1411
  if (cls === "\\w") return shorthandRanges("w");
1412
+ if (cls === "\\s") return shorthandRanges("s");
1384
1413
  const body = cls.slice(1, -1);
1385
1414
  if (body.startsWith("^")) return null;
1386
1415
  return parseClassRanges(body);
1387
1416
  }
1417
+ var foldEq = (cVar, cp) => {
1418
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1419
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1420
+ return `${cVar} === ${cp}`;
1421
+ };
1388
1422
  function literalCodePoints(frag) {
1389
1423
  const out = [];
1390
1424
  let i = 0;
@@ -1431,22 +1465,446 @@ function parseStringShape(source) {
1431
1465
  if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1432
1466
  return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1433
1467
  }
1468
+ function parseSeqParts(source) {
1469
+ const parts = [];
1470
+ let lit = [];
1471
+ const flush = () => {
1472
+ if (lit.length) {
1473
+ parts.push({ part: "lit", cps: lit, optional: false });
1474
+ lit = [];
1475
+ }
1476
+ };
1477
+ const runFrom = (ranges, negated, q) => {
1478
+ if (q === "+") {
1479
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1480
+ return 1;
1481
+ }
1482
+ if (q === "*") {
1483
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1484
+ return 1;
1485
+ }
1486
+ if (q === "?") {
1487
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1488
+ return 1;
1489
+ }
1490
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1491
+ return 0;
1492
+ };
1493
+ let i = 0;
1494
+ while (i < source.length) {
1495
+ const ch = source[i];
1496
+ if (ch === "[") {
1497
+ let k = i + 1;
1498
+ const neg = source[k] === "^";
1499
+ if (neg) k++;
1500
+ let body = "";
1501
+ while (k < source.length && source[k] !== "]") {
1502
+ if (source[k] === "\\") {
1503
+ body += source[k] + (source[k + 1] ?? "");
1504
+ k += 2;
1505
+ } else {
1506
+ body += source[k];
1507
+ k++;
1508
+ }
1509
+ }
1510
+ if (source[k] !== "]") return null;
1511
+ const ranges = parseClassRanges(body);
1512
+ if (!ranges) return null;
1513
+ flush();
1514
+ i = k + 1;
1515
+ i += runFrom(ranges, neg, source[i]);
1516
+ continue;
1517
+ }
1518
+ if (ch === "\\") {
1519
+ const e = source[i + 1];
1520
+ if (e === "d" || e === "w" || e === "s") {
1521
+ flush();
1522
+ i += 2;
1523
+ i += runFrom(shorthandRanges(e), false, source[i]);
1524
+ continue;
1525
+ }
1526
+ const uni = readUnicodeEscape(source, i);
1527
+ let cp2;
1528
+ if (uni) {
1529
+ cp2 = uni.cp;
1530
+ i = uni.next;
1531
+ } else if (e === void 0) return null;
1532
+ else if (e in CLASS_ESCAPES) {
1533
+ cp2 = CLASS_ESCAPES[e];
1534
+ i += 2;
1535
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1536
+ else {
1537
+ cp2 = e.codePointAt(0);
1538
+ i += 2;
1539
+ }
1540
+ if (source[i] === "?") {
1541
+ flush();
1542
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1543
+ i++;
1544
+ } else lit.push(cp2);
1545
+ continue;
1546
+ }
1547
+ if (ch === "(") {
1548
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1549
+ let k = i + 3;
1550
+ let depth = 1;
1551
+ while (k < source.length && depth > 0) {
1552
+ const c2 = source[k];
1553
+ if (c2 === "\\") {
1554
+ k += 2;
1555
+ continue;
1556
+ }
1557
+ if (c2 === "[") {
1558
+ let j = k + 1;
1559
+ if (source[j] === "^") j++;
1560
+ while (j < source.length && source[j] !== "]") {
1561
+ if (source[j] === "\\") j += 2;
1562
+ else j++;
1563
+ }
1564
+ if (source[j] !== "]") return null;
1565
+ k = j + 1;
1566
+ continue;
1567
+ }
1568
+ if (c2 === "(") depth++;
1569
+ else if (c2 === ")") depth--;
1570
+ k++;
1571
+ }
1572
+ if (depth !== 0) return null;
1573
+ const body = source.slice(i + 3, k - 1);
1574
+ const inner = parseScanShape(body);
1575
+ if (!inner || !groupInnerSafe(inner)) return null;
1576
+ const q = source[k];
1577
+ if (q === "{") return null;
1578
+ flush();
1579
+ if (q === "+") {
1580
+ if (shapeCanBeEmpty(inner)) return null;
1581
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1582
+ i = k + 1;
1583
+ } else if (q === "*") {
1584
+ if (shapeCanBeEmpty(inner)) return null;
1585
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1586
+ i = k + 1;
1587
+ } else if (q === "?") {
1588
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1589
+ i = k + 1;
1590
+ } else {
1591
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1592
+ i = k;
1593
+ }
1594
+ continue;
1595
+ }
1596
+ if (META.has(ch)) return null;
1597
+ const cp = ch.codePointAt(0);
1598
+ i += ch.length;
1599
+ if (source[i] === "?") {
1600
+ flush();
1601
+ parts.push({ part: "lit", cps: [cp], optional: true });
1602
+ i++;
1603
+ } else lit.push(cp);
1604
+ }
1605
+ flush();
1606
+ return parts.length ? parts : null;
1607
+ }
1608
+ function seqIsUnambiguous(parts) {
1609
+ let hasMandatory = false;
1610
+ for (let k = 0; k < parts.length; k++) {
1611
+ const p = parts[k];
1612
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1613
+ if (mandatory) hasMandatory = true;
1614
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1615
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1616
+ if (skippableSingle || isUnboundedRepeat) {
1617
+ const rest = seqFirstAccept(parts.slice(k + 1));
1618
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1619
+ }
1620
+ }
1621
+ return hasMandatory;
1622
+ }
1623
+ function rangesOverlap(a, b) {
1624
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1625
+ }
1626
+ function rangesSubset(sub, sup) {
1627
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1628
+ }
1629
+ function parseClassOperand(body) {
1630
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1631
+ return { ranges: shorthandRanges(body[1]), negated: false };
1632
+ }
1633
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1634
+ let inner = body.slice(1, -1);
1635
+ const negated = inner.startsWith("^");
1636
+ if (negated) inner = inner.slice(1);
1637
+ const ranges = parseClassRanges(inner);
1638
+ return ranges ? { ranges, negated } : null;
1639
+ }
1640
+ const cps = literalCodePoints(body);
1641
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1642
+ return null;
1643
+ }
1644
+ function classSubset(t, u) {
1645
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1646
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1647
+ if (t.negated && !u.negated) return false;
1648
+ return rangesSubset(u.ranges, t.ranges);
1649
+ }
1650
+ function classDisjoint(t, u) {
1651
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1652
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1653
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1654
+ return false;
1655
+ }
1656
+ function firstSetsDisjoint(a, b) {
1657
+ if (a === "any" || b === "any") return false;
1658
+ return classDisjoint(a, b);
1659
+ }
1660
+ function trailingBacktrackClass(shape) {
1661
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1662
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1663
+ if (shape.kind === "litFold") return null;
1664
+ if (shape.kind === "seq") {
1665
+ const last = shape.parts[shape.parts.length - 1];
1666
+ if (last.part === "lit") return null;
1667
+ if (last.part === "run") {
1668
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1669
+ return null;
1670
+ }
1671
+ const innerWiggle = trailingBacktrackClass(last.inner);
1672
+ if (innerWiggle === "unsupported") return "unsupported";
1673
+ const requiredOnce = !last.unbounded && last.min === 1;
1674
+ if (requiredOnce) return innerWiggle;
1675
+ const dropExposed = shapeFirstAccept(last.inner);
1676
+ if (dropExposed === "any") return "unsupported";
1677
+ if (innerWiggle === null) return dropExposed;
1678
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1679
+ return unioned === "any" ? "unsupported" : unioned;
1680
+ }
1681
+ if (shape.kind === "alt") {
1682
+ if (!shape.disjoint) return "unsupported";
1683
+ const classes = shape.arms.map(trailingBacktrackClass);
1684
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1685
+ const concrete = classes.filter((c) => c !== null);
1686
+ if (concrete.length === 0) return null;
1687
+ const unioned = unionCharSets(concrete);
1688
+ return unioned === "any" ? "unsupported" : unioned;
1689
+ }
1690
+ return "unsupported";
1691
+ }
1692
+ function lookaheadUnambiguous(inner, operand, negative) {
1693
+ const tail = trailingBacktrackClass(inner);
1694
+ if (tail === "unsupported") return false;
1695
+ if (tail === null) return true;
1696
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1697
+ }
1698
+ function stripTrailingLookahead(source) {
1699
+ if (!source.endsWith(")")) return null;
1700
+ for (const negative of [true, false]) {
1701
+ const lead = negative ? "(?!" : "(?=";
1702
+ const idx = source.lastIndexOf(lead);
1703
+ if (idx === -1) continue;
1704
+ const body = source.slice(idx + lead.length, -1);
1705
+ const operand = body.length ? parseClassOperand(body) : null;
1706
+ if (!operand) continue;
1707
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1708
+ }
1709
+ return null;
1710
+ }
1711
+ function unwrapOuterGroup(source) {
1712
+ while (source.startsWith("(?:")) {
1713
+ let depth = 1;
1714
+ let i = 3;
1715
+ let ok = true;
1716
+ while (i < source.length && depth > 0) {
1717
+ const ch = source[i];
1718
+ if (ch === "\\") {
1719
+ i += 2;
1720
+ continue;
1721
+ }
1722
+ if (ch === "[") {
1723
+ let j = i + 1;
1724
+ if (source[j] === "^") j++;
1725
+ while (j < source.length && source[j] !== "]") {
1726
+ if (source[j] === "\\") j += 2;
1727
+ else j++;
1728
+ }
1729
+ if (source[j] !== "]") {
1730
+ ok = false;
1731
+ break;
1732
+ }
1733
+ i = j + 1;
1734
+ continue;
1735
+ }
1736
+ if (ch === "(") depth++;
1737
+ else if (ch === ")") depth--;
1738
+ i++;
1739
+ }
1740
+ if (!ok || depth !== 0 || i !== source.length) break;
1741
+ source = source.slice(3, -1);
1742
+ }
1743
+ return source;
1744
+ }
1745
+ function splitTopLevelAlternation(source) {
1746
+ const body = unwrapOuterGroup(source);
1747
+ const parts = [];
1748
+ let depth = 0;
1749
+ let last = 0;
1750
+ let i = 0;
1751
+ while (i < body.length) {
1752
+ const ch = body[i];
1753
+ if (ch === "\\") {
1754
+ i += 2;
1755
+ continue;
1756
+ }
1757
+ if (ch === "[") {
1758
+ let j = i + 1;
1759
+ if (body[j] === "^") j++;
1760
+ while (j < body.length && body[j] !== "]") {
1761
+ if (body[j] === "\\") j += 2;
1762
+ else j++;
1763
+ }
1764
+ if (body[j] !== "]") return null;
1765
+ i = j + 1;
1766
+ continue;
1767
+ }
1768
+ if (ch === "(") {
1769
+ depth++;
1770
+ i++;
1771
+ continue;
1772
+ }
1773
+ if (ch === ")") {
1774
+ depth--;
1775
+ if (depth < 0) return null;
1776
+ i++;
1777
+ continue;
1778
+ }
1779
+ if (ch === "|" && depth === 0) {
1780
+ parts.push(body.slice(last, i));
1781
+ last = i + 1;
1782
+ i++;
1783
+ continue;
1784
+ }
1785
+ i++;
1786
+ }
1787
+ if (depth !== 0) return null;
1788
+ parts.push(body.slice(last));
1789
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1790
+ return parts;
1791
+ }
1792
+ function shapeCanBeEmpty(shape) {
1793
+ if (shape.kind === "chars") return !shape.minOne;
1794
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1795
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1796
+ return false;
1797
+ }
1798
+ function unionCharSets(sets) {
1799
+ if (sets.some((s) => s === "any")) return "any";
1800
+ const concrete = sets;
1801
+ if (concrete.length === 1) return concrete[0];
1802
+ if (concrete.some((s) => s.negated)) return "any";
1803
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1804
+ }
1805
+ function partFirstAccept(part) {
1806
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1807
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1808
+ return shapeFirstAccept(part.inner);
1809
+ }
1810
+ function seqFirstAccept(parts) {
1811
+ const sets = [];
1812
+ for (const p of parts) {
1813
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1814
+ sets.push(partFirstAccept(p));
1815
+ if (mandatory) break;
1816
+ }
1817
+ return unionCharSets(sets);
1818
+ }
1819
+ function groupInnerSafe(shape) {
1820
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1821
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1822
+ if (shape.kind === "alt") return shape.disjoint;
1823
+ return false;
1824
+ }
1825
+ function shapeFirstAccept(shape) {
1826
+ if (shapeCanBeEmpty(shape)) return "any";
1827
+ switch (shape.kind) {
1828
+ case "chars":
1829
+ return { ranges: shape.ranges, negated: false };
1830
+ case "ident":
1831
+ return { ranges: shape.head, negated: false };
1832
+ case "until":
1833
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1834
+ case "delimited":
1835
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1836
+ case "string":
1837
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1838
+ case "litFold": {
1839
+ const cp = shape.open[0];
1840
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1841
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1842
+ return { ranges: [[cp, cp]], negated: false };
1843
+ }
1844
+ case "seq":
1845
+ return seqFirstAccept(shape.parts);
1846
+ case "lookahead":
1847
+ return shapeFirstAccept(shape.inner);
1848
+ // zero-width — doesn't change first-char acceptance
1849
+ case "alt": {
1850
+ if (!shape.disjoint) return "any";
1851
+ const sets = shape.firsts.filter((f) => f !== null);
1852
+ return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
1853
+ }
1854
+ }
1855
+ }
1856
+ function allPairsDisjoint(sets) {
1857
+ for (let i = 0; i < sets.length; i++) {
1858
+ const a = sets[i];
1859
+ if (a === "any") return false;
1860
+ for (let j = i + 1; j < sets.length; j++) {
1861
+ const b = sets[j];
1862
+ if (b === "any" || !classDisjoint(a, b)) return false;
1863
+ }
1864
+ }
1865
+ return true;
1866
+ }
1867
+ function parseAlternation(source) {
1868
+ const parts = splitTopLevelAlternation(source);
1869
+ if (!parts) return null;
1870
+ const arms = [];
1871
+ for (const p of parts) {
1872
+ const arm = parseScanShape(p);
1873
+ if (!arm) return null;
1874
+ arms.push(arm);
1875
+ }
1876
+ const firstSets = arms.map(shapeFirstAccept);
1877
+ const disjoint = allPairsDisjoint(firstSets);
1878
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
1879
+ }
1434
1880
  function parseScanShape(source) {
1881
+ const alt = parseAlternation(source);
1882
+ if (alt) return alt;
1883
+ const la = stripTrailingLookahead(source);
1884
+ if (la) {
1885
+ const inner = parseScanShapeCore(la.base);
1886
+ if (!inner) return null;
1887
+ const operand = { ranges: la.ranges, negated: la.classNegated };
1888
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
1889
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
1890
+ }
1891
+ return parseScanShapeCore(source);
1892
+ }
1893
+ function parseScanShapeCore(source) {
1435
1894
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1436
- if (m) {
1437
- if (m[1].startsWith("^")) return null;
1895
+ if (m && !m[1].startsWith("^")) {
1438
1896
  const ranges = parseClassRanges(m[1]);
1439
1897
  return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1440
1898
  }
1441
- m = /^\\([dw])([+*])$/.exec(source);
1899
+ m = /^\\([dws])([+*])$/.exec(source);
1442
1900
  if (m) {
1443
1901
  return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1444
1902
  }
1445
- m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
1903
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1446
1904
  if (m) {
1447
1905
  const head = classToRanges(m[1]);
1448
1906
  const tail = classToRanges(m[2]);
1449
- return head && tail ? { kind: "ident", head, tail } : null;
1907
+ if (head && tail) return { kind: "ident", head, tail };
1450
1908
  }
1451
1909
  const str = parseStringShape(source);
1452
1910
  if (str) return str;
@@ -1455,7 +1913,6 @@ function parseScanShape(source) {
1455
1913
  const open = literalCodePoints(m[1]);
1456
1914
  const stop = parseClassRanges(m[2]);
1457
1915
  if (open && stop) return { kind: "until", open, stop };
1458
- return null;
1459
1916
  }
1460
1917
  if (!source.includes("\\\\")) {
1461
1918
  m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
@@ -1465,10 +1922,16 @@ function parseScanShape(source) {
1465
1922
  if (open && close) return { kind: "delimited", open, close };
1466
1923
  }
1467
1924
  }
1925
+ const parts = parseSeqParts(source);
1926
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1468
1927
  return null;
1469
1928
  }
1470
1929
  function scanShapeFromRegex(source, flags) {
1471
- if (/[imsu]/.test(flags)) return null;
1930
+ if (/[msu]/.test(flags)) return null;
1931
+ if (/i/.test(flags)) {
1932
+ const open = literalCodePoints(source);
1933
+ return open ? { kind: "litFold", open } : null;
1934
+ }
1472
1935
  return parseScanShape(source);
1473
1936
  }
1474
1937
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
@@ -1483,6 +1946,53 @@ function stringHardStop(shape) {
1483
1946
  }
1484
1947
  var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1485
1948
  function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1949
+ if (shape.kind === "lookahead") {
1950
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
1951
+ const okV = mint("_ok");
1952
+ const endV2 = mint("_end");
1953
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
1954
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
1955
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
1956
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
1957
+ return {
1958
+ setup: [
1959
+ ...inner.setup,
1960
+ `${ind2}let ${okV} = ${inner.ok}`,
1961
+ `${ind2}let ${endV2} = ${inner.end}`,
1962
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
1963
+ ],
1964
+ ok: okV,
1965
+ end: endV2
1966
+ };
1967
+ }
1968
+ if (shape.kind === "alt") {
1969
+ const okV = mint("_ok");
1970
+ const endV2 = mint("_end");
1971
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
1972
+ if (shape.disjoint) {
1973
+ const c0 = codeAt(start, firstChar);
1974
+ shape.arms.forEach((arm, k) => {
1975
+ const fs2 = shape.firsts[k];
1976
+ const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
1977
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
1978
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
1979
+ lines.push(...m.setup);
1980
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
1981
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
1982
+ lines.push(`${ind2}}`);
1983
+ });
1984
+ } else {
1985
+ const lbl = mint("_alt");
1986
+ lines.push(`${ind2}${lbl}: {`);
1987
+ for (const arm of shape.arms) {
1988
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
1989
+ lines.push(...m.setup);
1990
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
1991
+ }
1992
+ lines.push(`${ind2}}`);
1993
+ }
1994
+ return { setup: lines, ok: okV, end: endV2 };
1995
+ }
1486
1996
  if (shape.kind === "chars") {
1487
1997
  const cur = mint("_e");
1488
1998
  return {
@@ -1509,6 +2019,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1509
2019
  end: cur
1510
2020
  };
1511
2021
  }
2022
+ if (shape.kind === "seq") {
2023
+ const endV2 = mint("_e");
2024
+ const okV = mint("_ok");
2025
+ const runCond = (p, at) => {
2026
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2027
+ return p.negated ? `!(${c})` : `(${c})`;
2028
+ };
2029
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2030
+ let first = true;
2031
+ for (const p of shape.parts) {
2032
+ if (p.part === "lit") {
2033
+ const L = p.cps.length;
2034
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2035
+ if (p.optional) {
2036
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2037
+ } else {
2038
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2039
+ lines.push(`${ind2} ${endV2} += ${L}`);
2040
+ }
2041
+ } else if (p.part === "run" && !p.unbounded) {
2042
+ if (p.min === 1) {
2043
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2044
+ lines.push(`${ind2} ${endV2}++`);
2045
+ } else {
2046
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2047
+ }
2048
+ } else if (p.part === "run") {
2049
+ const s = mint("_s");
2050
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2051
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2052
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2053
+ } else {
2054
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2055
+ if (!p.unbounded) {
2056
+ lines.push(...inner.setup);
2057
+ if (p.min === 1) {
2058
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2059
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2060
+ } else {
2061
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2062
+ }
2063
+ } else {
2064
+ const progV = mint("_prog");
2065
+ lines.push(`${ind2} let ${progV} = false`);
2066
+ lines.push(`${ind2} while (true) {`);
2067
+ lines.push(...inner.setup);
2068
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2069
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2070
+ lines.push(`${ind2} ${progV} = true`);
2071
+ lines.push(`${ind2} }`);
2072
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2073
+ }
2074
+ }
2075
+ first = false;
2076
+ }
2077
+ lines.push(`${ind2} ${okV} = true`);
2078
+ lines.push(`${ind2}} while (false)`);
2079
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2080
+ return { setup: lines, ok: okV, end: endV2 };
2081
+ }
2082
+ if (shape.kind === "litFold") {
2083
+ const openLen2 = shape.open.length;
2084
+ const endV2 = mint("_end");
2085
+ const checks = shape.open.map((cp, k) => {
2086
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2087
+ return foldEq(cVar, cp);
2088
+ });
2089
+ return {
2090
+ setup: [
2091
+ `${ind2}let ${endV2} = ${start}`,
2092
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2093
+ ],
2094
+ ok: `${endV2} > ${start}`,
2095
+ end: endV2
2096
+ };
2097
+ }
1512
2098
  if (shape.kind === "until") {
1513
2099
  const j2 = mint("_j");
1514
2100
  const openLen2 = shape.open.length;
@@ -1904,7 +2490,7 @@ function hoistExpected(ctx, constArrSource) {
1904
2490
  let name = ctx.expectedMap.get(constArrSource);
1905
2491
  if (name === void 0) {
1906
2492
  name = `_fx${ctx.expectedDecls.length}`;
1907
- ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
2493
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
1908
2494
  ctx.expectedMap.set(constArrSource, name);
1909
2495
  }
1910
2496
  return name;
@@ -2085,10 +2671,10 @@ function ensureTriviaFn(ctx) {
2085
2671
  function ensureTriviaCaptureFn(ctx) {
2086
2672
  return ensureTriviaFn(ctx);
2087
2673
  }
2088
- function firstSetCond(codeVar, fs) {
2089
- if (fs.kind === "any") return "true";
2090
- if (fs.kind === "empty") return "false";
2091
- return fs.ranges.map(
2674
+ function firstSetCond(codeVar, fs2) {
2675
+ if (fs2.kind === "any") return "true";
2676
+ if (fs2.kind === "empty") return "false";
2677
+ return fs2.ranges.map(
2092
2678
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
2093
2679
  ).join(" || ");
2094
2680
  }
@@ -2100,10 +2686,10 @@ function planDisjointDispatch(parsers) {
2100
2686
  const cases = [];
2101
2687
  let total = 0;
2102
2688
  for (const p of parsers) {
2103
- const fs = p._meta.firstSet;
2104
- if (fs.kind !== "ranges") return { kind: "if" };
2689
+ const fs2 = p._meta.firstSet;
2690
+ if (fs2.kind !== "ranges") return { kind: "if" };
2105
2691
  const pts = [];
2106
- for (const r of fs.ranges) {
2692
+ for (const r of fs2.ranges) {
2107
2693
  if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
2108
2694
  for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
2109
2695
  }
@@ -2226,18 +2812,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2226
2812
  return true;
2227
2813
  }
2228
2814
  }
2229
- function emitFallible(inner, ctx, pos) {
2815
+ function emitFallible(inner, ctx, pos, swallow = false) {
2230
2816
  const lbl = v(ctx, "_lbl");
2231
2817
  const okV = `${lbl}ok`;
2232
2818
  const valV = `${lbl}v`;
2233
2819
  const endV = `${lbl}e`;
2234
2820
  const savedLabel = ctx.failLabel;
2235
2821
  const savedIndent = ctx.indent;
2822
+ const savedRecord = ctx.recordFail;
2236
2823
  ctx.failLabel = lbl;
2237
2824
  ctx.indent = savedIndent + 1;
2825
+ if (swallow) ctx.recordFail = false;
2238
2826
  const r = emit(inner, ctx, pos);
2239
2827
  ctx.failLabel = savedLabel;
2240
2828
  ctx.indent = savedIndent;
2829
+ ctx.recordFail = savedRecord;
2241
2830
  const ind0 = ind(ctx);
2242
2831
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2243
2832
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2260,6 +2849,7 @@ function emitFallible(inner, ctx, pos) {
2260
2849
  ];
2261
2850
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2262
2851
  }
2852
+ var CHARCODE_CHAIN_MAX = 16;
2263
2853
  function emitLit(def, ctx, pos) {
2264
2854
  const { value, caseInsensitive } = def;
2265
2855
  const len = value.length;
@@ -2282,7 +2872,7 @@ function emitLit(def, ctx, pos) {
2282
2872
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2283
2873
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2284
2874
  );
2285
- } else if (len <= 4) {
2875
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2286
2876
  const checks = Array.from(
2287
2877
  { length: len },
2288
2878
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2304,7 +2894,55 @@ function emitLit(def, ctx, pos) {
2304
2894
  function escapeKeywordRe(s) {
2305
2895
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2306
2896
  }
2897
+ function emitKeywordsFast(def, ctx, pos) {
2898
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
2899
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
2900
+ if (def.caseInsensitive && def.boundary) return null;
2901
+ let boundary = null;
2902
+ if (def.boundary) {
2903
+ let body = def.boundary;
2904
+ const negated = body.startsWith("^");
2905
+ if (negated) body = body.slice(1);
2906
+ const ranges = parseClassRanges(body);
2907
+ if (!ranges) return null;
2908
+ boundary = { ranges, negated };
2909
+ }
2910
+ const mint = (prefix = "_v") => v(ctx, prefix);
2911
+ const lbl = v(ctx, "_kwLbl");
2912
+ const valV = v(ctx, "_kwv");
2913
+ const endV = v(ctx, "_kwe");
2914
+ const bodyInd = ind(ctx) + " ";
2915
+ const tries = [];
2916
+ for (const w of def.words) {
2917
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
2918
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
2919
+ if (boundary) {
2920
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
2921
+ }
2922
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
2923
+ tries.push(
2924
+ ...m.setup,
2925
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
2926
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
2927
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
2928
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
2929
+ );
2930
+ }
2931
+ const stmts = [
2932
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
2933
+ `${ind(ctx)}${lbl}: {`,
2934
+ ...tries,
2935
+ `${ind(ctx)}}`,
2936
+ // Every word has length >= 1 (checked above), so a real match always
2937
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
2938
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
2939
+ ];
2940
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
2941
+ return { stmts, valueVar: valV, endVar: endV };
2942
+ }
2307
2943
  function emitKeywords(def, ctx, pos) {
2944
+ const fast = emitKeywordsFast(def, ctx, pos);
2945
+ if (fast) return fast;
2308
2946
  const alt = def.words.map(escapeKeywordRe).join("|");
2309
2947
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2310
2948
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2435,6 +3073,31 @@ function failsAtStart(p) {
2435
3073
  return false;
2436
3074
  }
2437
3075
  }
3076
+ function armStaticExpected(ctx, p) {
3077
+ return hoistExpected(ctx, staticExpectedArr([p]));
3078
+ }
3079
+ function canMatchEmptyAtStart(p) {
3080
+ const d = p._def;
3081
+ switch (d.tag) {
3082
+ case "regex":
3083
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3084
+ case "optional":
3085
+ return true;
3086
+ case "many":
3087
+ return true;
3088
+ case "transform":
3089
+ case "label":
3090
+ return canMatchEmptyAtStart(d.parser);
3091
+ case "literal":
3092
+ return d.value.length === 0;
3093
+ default:
3094
+ return false;
3095
+ }
3096
+ }
3097
+ function needsFirstSetGuard(p) {
3098
+ const fs2 = p._meta.firstSet;
3099
+ return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
3100
+ }
2438
3101
  function emitDisjointArm(p, ctx, pos, valV, endV) {
2439
3102
  if (failsAtStart(p)) {
2440
3103
  const r = emit(p, ctx, pos);
@@ -2576,16 +3239,21 @@ function emitFirstMatch(def, ctx, pos) {
2576
3239
  const resValV = v(ctx, "_crv");
2577
3240
  const resEndV = v(ctx, "_cre");
2578
3241
  const resOkV = v(ctx, "_crok");
3242
+ const codeV = v(ctx, "_chcode");
2579
3243
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
2580
3244
  const ind0 = ind(ctx);
2581
3245
  const stmts = [
2582
3246
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2583
- `${ind0}let ${slots.join(", ")}`
3247
+ `${ind0}let ${slots.join(", ")}`,
3248
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2584
3249
  ];
2585
3250
  for (let i = 0; i < def.parsers.length; i++) {
2586
3251
  const p = def.parsers[i];
2587
3252
  const gate = def.gates[i];
2588
3253
  const autoNot = def.autoNot[i];
3254
+ const atStart = failsAtStart(p);
3255
+ const staticFx = armStaticExpected(ctx, p);
3256
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2589
3257
  let gateCond = null;
2590
3258
  if (gate) {
2591
3259
  const gateIdx = ctx.mapFns.length;
@@ -2600,39 +3268,41 @@ function emitFirstMatch(def, ctx, pos) {
2600
3268
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2601
3269
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2602
3270
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3271
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2603
3272
  stmts.push(`${ind0}if (${skipCond}) {`);
2604
- ctx.indent++;
2605
- const ind1 = ind(ctx);
3273
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3274
+ ctx.indent += fsGuard ? 2 : 1;
2606
3275
  if (markLeaves) {
2607
3276
  stmts.push(
2608
- `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2609
- `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2610
- `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2611
- `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
3277
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3278
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3279
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3280
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2612
3281
  );
2613
3282
  }
2614
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3283
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2615
3284
  stmts.push(...armStmts);
2616
- ctx.indent--;
2617
3285
  if (autoNot && autoNot.length > 0) {
2618
3286
  const anCode = v(ctx, "_anc");
2619
3287
  const rejectCond = autoNot.map(
2620
3288
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2621
3289
  ).join(" || ");
2622
- stmts.push(`${ind0} if (${okVar}) {`);
2623
- stmts.push(`${ind0} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
2624
- stmts.push(`${ind0} if (!(${rejectCond})) {`);
2625
- stmts.push(`${ind0} ${resValV} = ${valVar}`);
2626
- stmts.push(`${ind0} ${resEndV} = ${endVar}`);
2627
- stmts.push(`${ind0} ${resOkV} = true`);
2628
- stmts.push(`${ind0} }`);
2629
- stmts.push(`${ind0} }`);
2630
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
2631
- if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
3290
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3291
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3292
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3293
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3294
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3295
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3296
+ stmts.push(`${ind(ctx)} }`);
3297
+ stmts.push(`${ind(ctx)}}`);
3298
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3299
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2632
3300
  } else {
2633
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2634
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
3301
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3302
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2635
3303
  }
3304
+ ctx.indent -= fsGuard ? 2 : 1;
3305
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2636
3306
  stmts.push(`${ind0}}`);
2637
3307
  }
2638
3308
  const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
@@ -2665,7 +3335,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2665
3335
  function emitLiteralCondition(litVal, pos) {
2666
3336
  const len = litVal.length;
2667
3337
  if (len === 0) return "true";
2668
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3338
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2669
3339
  const checks = [`${pos} + ${len} <= input.length`];
2670
3340
  for (let i = 0; i < len; i++) {
2671
3341
  const code = litVal.codePointAt(i);
@@ -2714,7 +3384,7 @@ function emitMany(def, ctx, pos) {
2714
3384
  itemPos = npV;
2715
3385
  }
2716
3386
  }
2717
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3387
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2718
3388
  stmts.push(
2719
3389
  ...iterStmts,
2720
3390
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2728,7 +3398,7 @@ function emitMany(def, ctx, pos) {
2728
3398
  function emitOptional(def, ctx, pos) {
2729
3399
  const valV = v(ctx, "_opt");
2730
3400
  const endV = v(ctx, "_opte");
2731
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3401
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2732
3402
  const ind0 = ind(ctx);
2733
3403
  const stmts = [
2734
3404
  ...lblStmts,
@@ -2740,7 +3410,7 @@ function emitOptional(def, ctx, pos) {
2740
3410
  function emitSepBy(_p, def, ctx, pos) {
2741
3411
  const arrV = v(ctx, "_arr");
2742
3412
  const curV = v(ctx, "_cur");
2743
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3413
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2744
3414
  const stmts = [
2745
3415
  `${ind(ctx)}const ${arrV} = []`,
2746
3416
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2772,11 +3442,11 @@ function emitSepBy(_p, def, ctx, pos) {
2772
3442
  );
2773
3443
  sepAtPos = spV;
2774
3444
  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}; `;
2775
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3445
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2776
3446
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2777
3447
  const npV = v(ctx, "_np");
2778
3448
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
2779
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3449
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2780
3450
  stmts.push(
2781
3451
  ...nextStmts,
2782
3452
  // item failed → unwind the separator too, back to the end of the last item
@@ -2789,11 +3459,11 @@ function emitSepBy(_p, def, ctx, pos) {
2789
3459
  const spV = v(ctx, "_sp");
2790
3460
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2791
3461
  sepAtPos = spV;
2792
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3462
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2793
3463
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2794
3464
  const npV = v(ctx, "_np");
2795
3465
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2796
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3466
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2797
3467
  stmts.push(
2798
3468
  ...nextStmts,
2799
3469
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2810,10 +3480,10 @@ function emitSepBy(_p, def, ctx, pos) {
2810
3480
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2811
3481
  );
2812
3482
  }
2813
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3483
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2814
3484
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2815
3485
  const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
2816
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
3486
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2817
3487
  stmts.push(
2818
3488
  ...nextStmts,
2819
3489
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -2871,7 +3541,7 @@ function emitScanTo(def, ctx, pos) {
2871
3541
  return { stmts, valueVar: valV, endVar: curV };
2872
3542
  }
2873
3543
  function emitNot(def, ctx, pos) {
2874
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3544
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
2875
3545
  const label2 = JSON.stringify(`not(${def.parser._tag})`);
2876
3546
  return {
2877
3547
  stmts: [
@@ -2962,11 +3632,14 @@ function emitLazy(p, def, ctx, pos) {
2962
3632
  }
2963
3633
  const savedIndent = ctx.indent;
2964
3634
  const savedFailLabel = ctx.failLabel;
3635
+ const savedRecord = ctx.recordFail;
2965
3636
  ctx.indent = 1;
2966
3637
  ctx.failLabel = "_pfail";
3638
+ ctx.recordFail = true;
2967
3639
  const r = emit(resolved, ctx, "_pos");
2968
3640
  ctx.indent = savedIndent;
2969
3641
  ctx.failLabel = savedFailLabel;
3642
+ ctx.recordFail = savedRecord;
2970
3643
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
2971
3644
  }
2972
3645
  const fnName = ctx.namedParsers.get(p);
@@ -3146,11 +3819,14 @@ function emit(p, ctx, pos) {
3146
3819
  ctx.namedParsers.set(innerParser, fnName);
3147
3820
  const savedIndent = ctx.indent;
3148
3821
  const savedFailLabel = ctx.failLabel;
3822
+ const savedRecord = ctx.recordFail;
3149
3823
  ctx.indent = 1;
3150
3824
  ctx.failLabel = "_pfail";
3825
+ ctx.recordFail = true;
3151
3826
  const innerR = emit(innerParser, ctx, "_pos");
3152
3827
  ctx.indent = savedIndent;
3153
3828
  ctx.failLabel = savedFailLabel;
3829
+ ctx.recordFail = savedRecord;
3154
3830
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
3155
3831
  }
3156
3832
  const fn = ctx.namedParsers.get(innerParser);
@@ -3298,6 +3974,7 @@ function compile(combinator, mapFnSources) {
3298
3974
  regexMap: /* @__PURE__ */ new Map(),
3299
3975
  expectedDecls: [],
3300
3976
  expectedMap: /* @__PURE__ */ new Map(),
3977
+ recordFail: true,
3301
3978
  mapFns: [],
3302
3979
  mapFnSrcs: [],
3303
3980
  buildFns: [],
@@ -3380,6 +4057,7 @@ function compileRuleMap(ruleMap) {
3380
4057
  regexMap: /* @__PURE__ */ new Map(),
3381
4058
  expectedDecls: [],
3382
4059
  expectedMap: /* @__PURE__ */ new Map(),
4060
+ recordFail: true,
3383
4061
  mapFns: [],
3384
4062
  mapFnSrcs: [],
3385
4063
  buildFns: [],
@@ -3554,11 +4232,11 @@ function balanced(open, close, options = {}) {
3554
4232
  return combi;
3555
4233
  }
3556
4234
  function firstSetClassChars(c) {
3557
- const fs = c._meta.firstSet;
3558
- if (fs.kind === "empty") return [];
3559
- if (fs.kind !== "ranges") return null;
4235
+ const fs2 = c._meta.firstSet;
4236
+ if (fs2.kind === "empty") return [];
4237
+ if (fs2.kind !== "ranges") return null;
3560
4238
  const out = [];
3561
- for (const { lo, hi } of fs.ranges) {
4239
+ for (const { lo, hi } of fs2.ranges) {
3562
4240
  if (hi - lo > 8) return null;
3563
4241
  for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
3564
4242
  }
@@ -3862,7 +4540,117 @@ function evaluateCombinatorArray(node2, scope, code) {
3862
4540
  if (!val.every(isCombinator)) return null;
3863
4541
  return val;
3864
4542
  }
3865
- function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
4543
+ function asFragmentFactory(expr) {
4544
+ if (expr.type !== "ArrowFunctionExpression" && expr.type !== "FunctionExpression" && expr.type !== "FunctionDeclaration") return null;
4545
+ const fn = expr;
4546
+ return factoryBodyAndReturn(fn) ? fn : null;
4547
+ }
4548
+ var unwrapParen = (e) => e && e.type === "ParenthesizedExpression" ? e.expression : e;
4549
+ function factoryBodyAndReturn(fn) {
4550
+ const body = fn.body;
4551
+ if (!body) return null;
4552
+ const b = unwrapParen(body);
4553
+ if (b.type === "ObjectExpression") return { statements: [], retObj: b };
4554
+ if (b.type === "BlockStatement") {
4555
+ const statements = [];
4556
+ for (const stmt of b.body) {
4557
+ if (stmt.type === "ReturnStatement") {
4558
+ const a = unwrapParen(stmt.argument);
4559
+ if (!a || a.type !== "ObjectExpression") return null;
4560
+ return { statements, retObj: a };
4561
+ }
4562
+ if (stmt.type === "VariableDeclaration") statements.push(stmt);
4563
+ else return null;
4564
+ }
4565
+ }
4566
+ return null;
4567
+ }
4568
+ function spreadFactoryCall(spread, resolve) {
4569
+ const call = spread.argument;
4570
+ if (!call || call.type !== "CallExpression") return null;
4571
+ if (!call.callee || call.callee.type !== "Identifier" || !call.callee.name) return null;
4572
+ const resolved = resolve(call.callee.name);
4573
+ return resolved ? { fn: resolved.fn, args: call.arguments ?? [], code: resolved.code } : null;
4574
+ }
4575
+ function factoryCallScope(fn, args, outer) {
4576
+ const s = new Map(outer);
4577
+ const params = fn.params ?? [];
4578
+ for (let i = 0; i < params.length; i++) {
4579
+ const pn = params[i];
4580
+ if (!pn || pn.type !== "Identifier" || !pn.name) continue;
4581
+ const arg = args[i];
4582
+ if (arg && arg.type === "Identifier" && arg.name && outer.has(arg.name)) {
4583
+ s.set(pn.name, outer.get(arg.name));
4584
+ }
4585
+ }
4586
+ return s;
4587
+ }
4588
+ function propKey(prop) {
4589
+ if (prop.type !== "Property" || prop.computed || !prop.key) return null;
4590
+ if (prop.key.type === "Identifier") return prop.key.name ?? null;
4591
+ if (prop.key.type === "Literal") return String(prop.key.value);
4592
+ return null;
4593
+ }
4594
+ function collectRuleKeys(retObj, resolve, seen) {
4595
+ const out = [];
4596
+ for (const prop of retObj.properties) {
4597
+ if (prop.type === "SpreadElement") {
4598
+ const sf = spreadFactoryCall(prop, resolve);
4599
+ if (!sf || seen.has(sf.fn)) return null;
4600
+ const br = factoryBodyAndReturn(sf.fn);
4601
+ if (!br) return null;
4602
+ const nested = collectRuleKeys(br.retObj, resolve, new Set(seen).add(sf.fn));
4603
+ if (!nested) return null;
4604
+ out.push(...nested);
4605
+ continue;
4606
+ }
4607
+ const key = propKey(prop);
4608
+ if (!key) return null;
4609
+ out.push(key);
4610
+ }
4611
+ return out;
4612
+ }
4613
+ function flattenRuleEntries(retObj, scope, code, resolve, seen) {
4614
+ const out = [];
4615
+ for (const prop of retObj.properties) {
4616
+ if (prop.type === "SpreadElement") {
4617
+ const sf = spreadFactoryCall(prop, resolve);
4618
+ if (!sf || seen.has(sf.fn)) return null;
4619
+ const br = factoryBodyAndReturn(sf.fn);
4620
+ if (!br) return null;
4621
+ const fragScope = factoryCallScope(sf.fn, sf.args, scope);
4622
+ if (!evalBodyStatements(br.statements, fragScope, sf.code)) return null;
4623
+ const nested = flattenRuleEntries(br.retObj, fragScope, sf.code, resolve, new Set(seen).add(sf.fn));
4624
+ if (!nested) return null;
4625
+ out.push(...nested);
4626
+ continue;
4627
+ }
4628
+ const key = propKey(prop);
4629
+ if (!key) return null;
4630
+ out.push({ key, value: prop.value, scope, code });
4631
+ }
4632
+ return out;
4633
+ }
4634
+ function evalBodyStatements(statements, scope, code) {
4635
+ const bodyMfs = [];
4636
+ for (const stmt of statements) {
4637
+ for (const d of stmt.declarations) {
4638
+ const decl = d;
4639
+ if (!decl.init) return false;
4640
+ const id = decl.id;
4641
+ if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
4642
+ const name = id.name;
4643
+ const before = bodyMfs.length;
4644
+ const val = anyValue(decl.init, scope, code, bodyMfs);
4645
+ if (val === null) return false;
4646
+ const thisDeclMfSrcs = bodyMfs.slice(before);
4647
+ if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
4648
+ else scope.set(name, val);
4649
+ }
4650
+ }
4651
+ return true;
4652
+ }
4653
+ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, resolveFactory = () => null) {
3866
4654
  if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
3867
4655
  const factory = factoryNode;
3868
4656
  const params = factory.params;
@@ -3894,40 +4682,21 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3894
4682
  if (!returnExpr) return null;
3895
4683
  const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
3896
4684
  if (retObj.type !== "ObjectExpression") return null;
4685
+ const keys = collectRuleKeys(retObj, resolveFactory, /* @__PURE__ */ new Set());
4686
+ if (!keys) return null;
3897
4687
  const ruleRefs = /* @__PURE__ */ new Map();
3898
- for (const prop of retObj.properties) {
3899
- if (prop.type !== "Property") return null;
3900
- if (prop.computed) return null;
3901
- const p = prop;
3902
- const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
3903
- if (!key) return null;
3904
- ruleRefs.set(key, ref());
4688
+ for (const key of keys) {
4689
+ if (!ruleRefs.has(key)) ruleRefs.set(key, ref());
3905
4690
  }
3906
4691
  const localScope = new Map(scope);
3907
4692
  localScope.set(proxyName, Object.fromEntries(ruleRefs));
3908
- const bodyMfs = [];
3909
- for (const stmt of statements) {
3910
- for (const d of stmt.declarations) {
3911
- const decl = d;
3912
- if (!decl.init) return null;
3913
- const id = decl.id;
3914
- if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return null;
3915
- const name = id.name;
3916
- const before = bodyMfs.length;
3917
- const val = anyValue(decl.init, localScope, code, bodyMfs);
3918
- if (val === null) return null;
3919
- const thisDeclMfSrcs = bodyMfs.slice(before);
3920
- if (isCombinator(val)) {
3921
- localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
3922
- } else {
3923
- localScope.set(name, val);
3924
- }
3925
- }
3926
- }
3927
- for (const prop of retObj.properties) {
3928
- const p = prop;
3929
- const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
3930
- const val = anyValue(p.value, localScope, code, mapFnSources);
4693
+ if (!evalBodyStatements(statements, localScope, code)) return null;
4694
+ const entries = flattenRuleEntries(retObj, localScope, code, resolveFactory, /* @__PURE__ */ new Set());
4695
+ if (!entries) return null;
4696
+ const finalByKey = /* @__PURE__ */ new Map();
4697
+ for (const e of entries) finalByKey.set(e.key, e);
4698
+ for (const [key, e] of finalByKey) {
4699
+ const val = anyValue(e.value, e.scope, e.code, mapFnSources);
3931
4700
  if (!isCombinator(val)) return null;
3932
4701
  ruleRefs.get(key).define(val);
3933
4702
  }
@@ -3985,6 +4754,40 @@ function referencesAny(node2, names, scope) {
3985
4754
 
3986
4755
  // src/plugin/index.ts
3987
4756
  var PARSEMAN_MODULE = "parseman";
4757
+ var _fragmentResolver = null;
4758
+ function getFragmentResolver() {
4759
+ return _fragmentResolver ??= new ResolverFactory({
4760
+ extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
4761
+ // TS/NodeNext style: a `./x.js` import may point at `./x.ts` source.
4762
+ extensionAlias: {
4763
+ ".js": [".ts", ".tsx", ".js", ".jsx"],
4764
+ ".mjs": [".mts", ".mjs"],
4765
+ ".cjs": [".cts", ".cjs"]
4766
+ },
4767
+ conditionNames: ["source", "development", "import", "require", "default"],
4768
+ mainFields: ["source", "module", "main"]
4769
+ });
4770
+ }
4771
+ var _moduleParseCache = /* @__PURE__ */ new Map();
4772
+ function parseModuleCached(filePath) {
4773
+ let mtimeMs;
4774
+ try {
4775
+ mtimeMs = fs.statSync(filePath).mtimeMs;
4776
+ } catch {
4777
+ return null;
4778
+ }
4779
+ const hit = _moduleParseCache.get(filePath);
4780
+ if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
4781
+ let parsed = null;
4782
+ try {
4783
+ const src = fs.readFileSync(filePath, "utf8");
4784
+ const r = parseSync(filePath, src);
4785
+ if (r.errors.length === 0) parsed = { body: r.program.body, src };
4786
+ } catch {
4787
+ }
4788
+ _moduleParseCache.set(filePath, { mtimeMs, parsed });
4789
+ return parsed;
4790
+ }
3988
4791
  var plugin_default = createUnplugin((opts = {}) => ({
3989
4792
  name: "parseman",
3990
4793
  // Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
@@ -4020,15 +4823,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4020
4823
  const body = result.program.body;
4021
4824
  const macroImports = [];
4022
4825
  const allNames = /* @__PURE__ */ new Set();
4826
+ const importBindings = /* @__PURE__ */ new Map();
4023
4827
  for (const stmt of body) {
4024
4828
  if (stmt.type !== "ImportDeclaration") continue;
4025
4829
  const s = stmt;
4026
- if (!moduleAliases.has(s.source.value)) continue;
4027
- const isMacro = s.attributes.some((a) => {
4830
+ const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
4028
4831
  const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
4029
4832
  return key === "type" && a.value.value === "macro";
4030
4833
  });
4031
- if (!isMacro) continue;
4834
+ if (!isMacro) {
4835
+ for (const spec of s.specifiers) {
4836
+ if (spec.type === "ImportSpecifier") {
4837
+ const imported = spec.imported.name ?? spec.local.name;
4838
+ importBindings.set(spec.local.name, { source: s.source.value, imported });
4839
+ }
4840
+ }
4841
+ continue;
4842
+ }
4032
4843
  const names = /* @__PURE__ */ new Set();
4033
4844
  for (const spec of s.specifiers) {
4034
4845
  if (spec.type === "ImportSpecifier") names.add(spec.local.name);
@@ -4038,6 +4849,63 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4038
4849
  }
4039
4850
  if (macroImports.length === 0) return null;
4040
4851
  const scope = /* @__PURE__ */ new Map();
4852
+ const factories = /* @__PURE__ */ new Map();
4853
+ const resolver = getFragmentResolver();
4854
+ const importedFactoryCache = /* @__PURE__ */ new Map();
4855
+ const OUTPUT_DIR = /([\\/])(dist|lib|build|out|output|compiled|cjs|esm|es)([\\/])/;
4856
+ const resolveSourcePath = (spec) => {
4857
+ let resolved = null;
4858
+ try {
4859
+ resolved = resolver.resolveFileSync(id, spec).path ?? null;
4860
+ } catch {
4861
+ }
4862
+ if (!resolved) return null;
4863
+ if (OUTPUT_DIR.test(resolved)) {
4864
+ const srcCandidate = resolved.replace(OUTPUT_DIR, "$1src$3").replace(/\.(c|m)?js$/, ".ts");
4865
+ try {
4866
+ if (fs.statSync(srcCandidate).isFile()) return srcCandidate;
4867
+ } catch {
4868
+ }
4869
+ }
4870
+ return resolved;
4871
+ };
4872
+ const resolveImportedFactory = (name) => {
4873
+ if (importedFactoryCache.has(name)) return importedFactoryCache.get(name);
4874
+ let result2 = null;
4875
+ const binding = importBindings.get(name);
4876
+ if (binding) {
4877
+ const filePath = resolveSourcePath(binding.source);
4878
+ const mod = filePath ? parseModuleCached(filePath) : null;
4879
+ if (mod) {
4880
+ for (const st of mod.body) {
4881
+ if (st.type !== "ExportNamedDeclaration" || !st.declaration) continue;
4882
+ const decl = st.declaration;
4883
+ if (decl.type === "VariableDeclaration") {
4884
+ for (const d of decl.declarations ?? []) {
4885
+ if (d.id?.type === "Identifier" && d.id.name === binding.imported && d.init) {
4886
+ const fac = asFragmentFactory(d.init);
4887
+ if (fac) {
4888
+ result2 = { fn: fac, code: mod.src };
4889
+ break;
4890
+ }
4891
+ }
4892
+ }
4893
+ } else if (decl.type === "FunctionDeclaration" && decl.id?.type === "Identifier" && decl.id.name === binding.imported) {
4894
+ const fac = asFragmentFactory(decl);
4895
+ if (fac) result2 = { fn: fac, code: mod.src };
4896
+ }
4897
+ if (result2) break;
4898
+ }
4899
+ }
4900
+ }
4901
+ importedFactoryCache.set(name, result2);
4902
+ return result2;
4903
+ };
4904
+ const resolveFactory = (n) => {
4905
+ const local = factories.get(n);
4906
+ if (local) return { fn: local, code };
4907
+ return resolveImportedFactory(n);
4908
+ };
4041
4909
  const replacements = [];
4042
4910
  const warnings = [];
4043
4911
  let anyUnresolved = false;
@@ -4058,7 +4926,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4058
4926
  warn(init.start, `${label2}: rules() needs a factory argument`);
4059
4927
  return null;
4060
4928
  }
4061
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
4929
+ const ruleMap = evaluateParserFactory(factoryArg, scope, code, [], resolveFactory);
4062
4930
  if (!ruleMap) {
4063
4931
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
4064
4932
  return null;
@@ -4156,6 +5024,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
4156
5024
  });
4157
5025
  continue;
4158
5026
  }
5027
+ const frag = asFragmentFactory(init);
5028
+ if (frag) {
5029
+ factories.set(varName, frag);
5030
+ continue;
5031
+ }
4159
5032
  const mapFnSources = [];
4160
5033
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
4161
5034
  if (parser2 === null) {