parseman 0.45.0 → 0.46.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.
Files changed (64) hide show
  1. package/dist/analysis/choice-cost-gate.d.ts +158 -0
  2. package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
  3. package/dist/analysis/choice-cost-render.d.ts +88 -0
  4. package/dist/analysis/choice-cost-render.d.ts.map +1 -0
  5. package/dist/analysis/choice-cost.d.ts +383 -0
  6. package/dist/analysis/choice-cost.d.ts.map +1 -0
  7. package/dist/analysis/corpus.d.ts +73 -0
  8. package/dist/analysis/corpus.d.ts.map +1 -0
  9. package/dist/analysis/diagnose-render.d.ts +65 -0
  10. package/dist/analysis/diagnose-render.d.ts.map +1 -0
  11. package/dist/analysis/diagnose.d.ts +15 -0
  12. package/dist/analysis/diagnose.d.ts.map +1 -1
  13. package/dist/analysis/diagnostics.cjs +11272 -0
  14. package/dist/analysis/diagnostics.cjs.map +7 -0
  15. package/dist/analysis/diagnostics.d.ts +27 -0
  16. package/dist/analysis/diagnostics.d.ts.map +1 -0
  17. package/dist/analysis/diagnostics.js +11249 -0
  18. package/dist/analysis/diagnostics.js.map +7 -0
  19. package/dist/analysis/fix-render.d.ts +43 -0
  20. package/dist/analysis/fix-render.d.ts.map +1 -0
  21. package/dist/analysis/fix.d.ts +172 -0
  22. package/dist/analysis/fix.d.ts.map +1 -0
  23. package/dist/analysis/gating.d.ts +8 -0
  24. package/dist/analysis/gating.d.ts.map +1 -1
  25. package/dist/analysis/rebuild.d.ts +61 -0
  26. package/dist/analysis/rebuild.d.ts.map +1 -0
  27. package/dist/analysis/terminal.d.ts +142 -0
  28. package/dist/analysis/terminal.d.ts.map +1 -0
  29. package/dist/cli/index.cjs +12507 -0
  30. package/dist/cli/index.cjs.map +7 -0
  31. package/dist/cli/index.d.ts +3 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +12484 -0
  34. package/dist/cli/index.js.map +7 -0
  35. package/dist/compiler/build-arity.d.ts.map +1 -1
  36. package/dist/compiler/linker.d.ts +44 -6
  37. package/dist/compiler/linker.d.ts.map +1 -1
  38. package/dist/compiler/module-hoist.d.ts +106 -0
  39. package/dist/compiler/module-hoist.d.ts.map +1 -0
  40. package/dist/index.cjs +1297 -297
  41. package/dist/index.cjs.map +4 -4
  42. package/dist/index.d.ts +8 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1297 -297
  45. package/dist/index.js.map +4 -4
  46. package/dist/language-service/index.cjs.map +2 -2
  47. package/dist/language-service/index.js.map +2 -2
  48. package/dist/plugin/evaluator.d.ts +1 -1
  49. package/dist/plugin/evaluator.d.ts.map +1 -1
  50. package/dist/plugin/free-identifiers.d.ts +23 -0
  51. package/dist/plugin/free-identifiers.d.ts.map +1 -0
  52. package/dist/plugin/index.cjs +429 -43
  53. package/dist/plugin/index.cjs.map +4 -4
  54. package/dist/plugin/index.d.ts.map +1 -1
  55. package/dist/plugin/index.js +429 -43
  56. package/dist/plugin/index.js.map +4 -4
  57. package/dist/plugin/reducer-resolver.d.ts +17 -1
  58. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  59. package/dist/spec/index.cjs +2 -1
  60. package/dist/spec/index.cjs.map +2 -2
  61. package/dist/spec/index.js +2 -1
  62. package/dist/spec/index.js.map +2 -2
  63. package/dist/version.d.ts +1 -1
  64. package/package.json +18 -3
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(plugin_exports);
37
37
  var fs2 = __toESM(require("node:fs"), 1);
38
38
  var path2 = __toESM(require("node:path"), 1);
39
39
  var import_unplugin = require("unplugin");
40
- var import_oxc_parser3 = require("oxc-parser");
40
+ var import_oxc_parser4 = require("oxc-parser");
41
41
  var import_oxc_resolver = require("oxc-resolver");
42
42
  var import_magic_string = __toESM(require("magic-string"), 1);
43
43
 
@@ -1527,8 +1527,8 @@ function classifyTriviaArm(arm) {
1527
1527
  }
1528
1528
  const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1529
1529
  if (lc) {
1530
- const marker = lc[1];
1531
- return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1530
+ const marker2 = lc[1];
1531
+ return { kind: "untilLineBreak", code: (marker2.length === 2 ? marker2[1] : marker2[0]).charCodeAt(0) };
1532
1532
  }
1533
1533
  return null;
1534
1534
  }
@@ -3426,7 +3426,8 @@ var RESOLVER_REASONS = {
3426
3426
  "unresolved-import": "its module could not be resolved or parsed",
3427
3427
  "not-found": "the name did not resolve to any binding",
3428
3428
  "computed": "it is a computed expression",
3429
- "foreign-source": "it is named inside an imported factory, whose offsets the entry-file resolver cannot index"
3429
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
3430
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
3430
3431
  };
3431
3432
  function unconfirmableReason(def, src) {
3432
3433
  const declared = def.buildArityUnresolved;
@@ -3955,7 +3956,7 @@ function leaf(root, fn) {
3955
3956
  }
3956
3957
 
3957
3958
  // src/version.ts
3958
- var PARSEMAN_VERSION = "0.45.0";
3959
+ var PARSEMAN_VERSION = "0.46.0";
3959
3960
 
3960
3961
  // src/cst/host-mode.ts
3961
3962
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5575,15 +5576,15 @@ function parseStringShape(source) {
5575
5576
  if (source.length < 3 || source[source.length - 1] !== q) return null;
5576
5577
  const inner = source.slice(1, -1);
5577
5578
  const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
5578
- const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
5579
+ const ESC2 = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
5579
5580
  let body;
5580
5581
  let esc;
5581
- let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
5582
+ let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC2}\\)\\*$`).exec(inner);
5582
5583
  if (m) {
5583
5584
  body = m[1];
5584
5585
  esc = m[2];
5585
5586
  } else {
5586
- m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
5587
+ m = new RegExp(`^\\((?:\\?:)?${ESC2}\\|${CLS}\\)\\*$`).exec(inner);
5587
5588
  if (m) {
5588
5589
  esc = m[1];
5589
5590
  body = m[2];
@@ -11259,7 +11260,7 @@ function pickPieces(pieces, names) {
11259
11260
  const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
11260
11261
  return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc), nodeMeta: filt(pc.nodeMeta, pc) })).filter((pc) => pc.keys.length > 0);
11261
11262
  }
11262
- function fusedBody(pieces) {
11263
+ function fusedBody(pieces, hoist) {
11263
11264
  for (const p of pieces) {
11264
11265
  if (p.v === void 0) {
11265
11266
  throw new Error(
@@ -11299,8 +11300,7 @@ function fusedBody(pieces) {
11299
11300
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11300
11301
  const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
11301
11302
  const { mode, elided } = hostModeOfPieces(pieces);
11302
- const rawBody = [
11303
- ...lines,
11303
+ const tailSrc = [
11304
11304
  "const _map = {",
11305
11305
  wrapperEntries.join(",\n"),
11306
11306
  "}",
@@ -11316,6 +11316,7 @@ function fusedBody(pieces) {
11316
11316
  "}",
11317
11317
  "return _map"
11318
11318
  ].join("\n");
11319
+ const rawBody = [...lines, tailSrc].join("\n");
11319
11320
  const ANY2 = { kind: "any" };
11320
11321
  const EMPTY3 = { kind: "empty" };
11321
11322
  const recipes = /* @__PURE__ */ new Map();
@@ -11356,7 +11357,7 @@ function fusedBody(pieces) {
11356
11357
  }
11357
11358
  if (!changed) break;
11358
11359
  }
11359
- const body = rawBody.replace(
11360
+ const resolveFS = (src) => src.replace(
11360
11361
  // Rule name + code-point var are both JS identifiers (rule names are validated
11361
11362
  // at compile time — see assertRuleName in codegen), so an identifier class
11362
11363
  // matches every well-formed placeholder.
@@ -11367,6 +11368,7 @@ function fusedBody(pieces) {
11367
11368
  return `(${firstSetCond(codevar, fs3)})`;
11368
11369
  }
11369
11370
  );
11371
+ const body = hoist === void 0 ? resolveFS(rawBody) : [...hoist.claim(lines.map(resolveFS)), resolveFS(tailSrc)].join("\n");
11370
11372
  const env = {};
11371
11373
  for (const p of contributingPieces) {
11372
11374
  if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
@@ -11384,8 +11386,8 @@ function hostModeOfPieces(pieces) {
11384
11386
  }
11385
11387
  return { mode, elided: elided.length > 0 };
11386
11388
  }
11387
- function emitFusedSource(pieces) {
11388
- const { body, env } = fusedBody(pieces);
11389
+ function emitFusedSource(pieces, hoist) {
11390
+ const { body, env } = fusedBody(pieces, hoist);
11389
11391
  if (Object.keys(env).length > 0) {
11390
11392
  throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
11391
11393
  }
@@ -11408,6 +11410,34 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
11408
11410
  return pieces;
11409
11411
  }
11410
11412
 
11413
+ // src/functional/run.ts
11414
+ var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
11415
+ var REMOVED_RUN_RESULT_FIELDS = [
11416
+ [
11417
+ "triviaMap",
11418
+ `It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse capture, because most grammars paid for an index they never read. Migration: name the trivia labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia\`, whose \`.index\` carries the labels and gap lookups \`triviaMap\` exposed.`
11419
+ ],
11420
+ [
11421
+ "triviaLog",
11422
+ `It was the flat \`start, end[, kindIndex]\` log every other trivia view was derived from, and it is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: rows are a different width and a different unit (one row per SELECTED marker, not one per trivia chunk), so an old loop reads confidently wrong offsets rather than failing.`
11423
+ ],
11424
+ [
11425
+ "triviaKindLabels",
11426
+ `It was already optional, so nothing ever flagged its disappearance \u2014 and \`triviaKindMask(undefined, \u2026)\` means "capture everything", so reading it now silently WIDENS capture instead of erroring. Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is \`RunResult.rootTrivia.select\` and the resolved table is \`RunResult.rootTrivia.index.labels\`.`
11427
+ ]
11428
+ ];
11429
+
11430
+ // src/analysis/choice-cost-render.ts
11431
+ var ESC = "\x1B";
11432
+ var ANSI = {
11433
+ dim: `${ESC}[2m`,
11434
+ bold: `${ESC}[1m`,
11435
+ red: `${ESC}[31m`,
11436
+ yellow: `${ESC}[33m`,
11437
+ cyan: `${ESC}[36m`,
11438
+ reset: `${ESC}[0m`
11439
+ };
11440
+
11411
11441
  // src/combinators/gate.ts
11412
11442
  function gate(predicate) {
11413
11443
  const meta = {
@@ -11427,23 +11457,6 @@ function gate(predicate) {
11427
11457
  };
11428
11458
  }
11429
11459
 
11430
- // src/functional/run.ts
11431
- var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
11432
- var REMOVED_RUN_RESULT_FIELDS = [
11433
- [
11434
- "triviaMap",
11435
- `It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse capture, because most grammars paid for an index they never read. Migration: name the trivia labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia\`, whose \`.index\` carries the labels and gap lookups \`triviaMap\` exposed.`
11436
- ],
11437
- [
11438
- "triviaLog",
11439
- `It was the flat \`start, end[, kindIndex]\` log every other trivia view was derived from, and it is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: rows are a different width and a different unit (one row per SELECTED marker, not one per trivia chunk), so an old loop reads confidently wrong offsets rather than failing.`
11440
- ],
11441
- [
11442
- "triviaKindLabels",
11443
- `It was already optional, so nothing ever flagged its disappearance \u2014 and \`triviaKindMask(undefined, \u2026)\` means "capture everything", so reading it now silently WIDENS capture instead of erroring. Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is \`RunResult.rootTrivia.select\` and the resolved table is \`RunResult.rootTrivia.index.labels\`.`
11444
- ]
11445
- ];
11446
-
11447
11460
  // src/index.ts
11448
11461
  registerRegexAnalyzer(firstSetFromRegex);
11449
11462
 
@@ -11637,8 +11650,10 @@ function stripTsFromSource(node2, code) {
11637
11650
  return out + code.slice(cur, end);
11638
11651
  }
11639
11652
  var _reducers = null;
11640
- function setReducerResolver(r) {
11653
+ var _entrySource = null;
11654
+ function setReducerResolver(r, entrySource = null) {
11641
11655
  _reducers = r;
11656
+ _entrySource = entrySource;
11642
11657
  }
11643
11658
  function isWordFactory(v2) {
11644
11659
  return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
@@ -12010,6 +12025,9 @@ function exprToCombi(node2, scope, code, mfs) {
12010
12025
  const resolved = _reducers?.resolve(buildSrc, be.start, code);
12011
12026
  if (resolved) {
12012
12027
  if (resolved.src !== null) combi._def.buildSigSrc = resolved.src;
12028
+ if (_entrySource !== null && code !== _entrySource && resolved.src !== null && be.type === "Identifier") {
12029
+ combi._def.buildSrc = resolved.src;
12030
+ }
12013
12031
  if (combi._def.buildArity === void 0 && resolved.arity !== null) combi._def.buildArity = resolved.arity;
12014
12032
  if (resolved.reason !== void 0) combi._def.buildArityUnresolved = resolved.reason;
12015
12033
  }
@@ -12825,6 +12843,8 @@ function createReducerResolver(entryFile, body, src) {
12825
12843
  const scopes = buildScopeTree(body, src.length);
12826
12844
  markReassignments(body, scopes);
12827
12845
  const entry = { file: entryFile, src, body, scopes };
12846
+ const registered = /* @__PURE__ */ new Map();
12847
+ const ambiguous = /* @__PURE__ */ new Set();
12828
12848
  const fromBinding = (mod, binding, memberName, hops, seen) => {
12829
12849
  if (hops > MAX_HOPS) return { arity: null, src: null, reason: "not-found" };
12830
12850
  if (binding.reassigned) return { arity: null, src: null, reason: "reassigned" };
@@ -12869,19 +12889,342 @@ function createReducerResolver(entryFile, body, src) {
12869
12889
  return { arity, src: mod.src.slice(init.start, init.end), ...reason ? { reason } : {} };
12870
12890
  };
12871
12891
  return {
12892
+ register(file) {
12893
+ const mod = analyseModuleFile(file);
12894
+ if (!mod) return false;
12895
+ if (mod.src === src) return true;
12896
+ const prior = registered.get(mod.src);
12897
+ if (prior !== void 0 && prior.file !== mod.file) {
12898
+ ambiguous.add(mod.src);
12899
+ registered.delete(mod.src);
12900
+ return false;
12901
+ }
12902
+ if (!ambiguous.has(mod.src)) registered.set(mod.src, mod);
12903
+ return !ambiguous.has(mod.src);
12904
+ },
12872
12905
  resolve(exprSrc, offset, offsetSrc) {
12873
12906
  const text = exprSrc.trim();
12874
12907
  const ident = IDENT_RE.test(text) ? text : null;
12875
12908
  const member = ident ? null : MEMBER_RE.exec(text);
12876
12909
  if (!ident && !member) return null;
12877
- if (offsetSrc !== src) return { arity: null, src: null, reason: "foreign-source" };
12910
+ const mod = offsetSrc === src ? entry : registered.get(offsetSrc);
12911
+ if (!mod) {
12912
+ return { arity: null, src: null, reason: ambiguous.has(offsetSrc) ? "ambiguous-source" : "foreign-source" };
12913
+ }
12878
12914
  const name = ident ?? member[1];
12879
12915
  const memberName = ident ? void 0 : member[2];
12880
- const hit = lookup(scopeAt(scopes, offset), name);
12916
+ const hit = lookup(scopeAt(mod.scopes, offset), name);
12881
12917
  if (!hit) return { arity: null, src: null, reason: "not-found" };
12882
- return fromBinding(entry, hit.binding, memberName, 0, /* @__PURE__ */ new Set());
12918
+ return fromBinding(mod, hit.binding, memberName, 0, /* @__PURE__ */ new Set());
12919
+ }
12920
+ };
12921
+ }
12922
+
12923
+ // src/plugin/free-identifiers.ts
12924
+ var import_oxc_parser3 = require("oxc-parser");
12925
+ var isNode = (v2) => typeof v2 === "object" && v2 !== null && typeof v2.type === "string";
12926
+ var TYPE_KEYS = /* @__PURE__ */ new Set([
12927
+ "typeAnnotation",
12928
+ "returnType",
12929
+ "typeParameters",
12930
+ "typeArguments",
12931
+ "superTypeArguments",
12932
+ "typeName",
12933
+ "implements",
12934
+ "extends",
12935
+ "accessibility"
12936
+ ]);
12937
+ var TS_VALUE_WRAPPERS = /* @__PURE__ */ new Set([
12938
+ "TSAsExpression",
12939
+ "TSSatisfiesExpression",
12940
+ "TSNonNullExpression",
12941
+ "TSInstantiationExpression",
12942
+ "TSTypeAssertion"
12943
+ ]);
12944
+ var HOST_GLOBALS = /* @__PURE__ */ new Set([
12945
+ ...Object.getOwnPropertyNames(globalThis),
12946
+ "undefined",
12947
+ "NaN",
12948
+ "Infinity",
12949
+ "globalThis",
12950
+ "arguments",
12951
+ "window",
12952
+ "document",
12953
+ "self",
12954
+ "navigator",
12955
+ "location",
12956
+ "top",
12957
+ "parent",
12958
+ "require",
12959
+ "module",
12960
+ "exports",
12961
+ "__dirname",
12962
+ "__filename"
12963
+ ]);
12964
+ function bindPattern(pattern, out) {
12965
+ if (!isNode(pattern)) return;
12966
+ switch (pattern.type) {
12967
+ case "Identifier":
12968
+ case "BindingIdentifier":
12969
+ out.add(String(pattern.name));
12970
+ return;
12971
+ case "ObjectPattern":
12972
+ for (const prop of pattern.properties ?? []) {
12973
+ if (!isNode(prop)) continue;
12974
+ if (prop.type === "RestElement" || prop.type === "BindingRestElement") bindPattern(prop.argument, out);
12975
+ else bindPattern(prop.value, out);
12976
+ }
12977
+ return;
12978
+ case "ArrayPattern":
12979
+ for (const el of pattern.elements ?? []) bindPattern(el, out);
12980
+ return;
12981
+ case "AssignmentPattern":
12982
+ bindPattern(pattern.left, out);
12983
+ return;
12984
+ case "RestElement":
12985
+ case "BindingRestElement":
12986
+ bindPattern(pattern.argument, out);
12987
+ return;
12988
+ case "TSParameterProperty":
12989
+ bindPattern(pattern.parameter, out);
12990
+ return;
12991
+ default:
12992
+ return;
12993
+ }
12994
+ }
12995
+ function collectBindings(node2, out) {
12996
+ if (Array.isArray(node2)) {
12997
+ for (const child of node2) collectBindings(child, out);
12998
+ return;
12999
+ }
13000
+ if (!isNode(node2)) return;
13001
+ switch (node2.type) {
13002
+ case "ImportSpecifier":
13003
+ case "ImportDefaultSpecifier":
13004
+ case "ImportNamespaceSpecifier":
13005
+ if (isNode(node2.local)) out.add(String(node2.local.name));
13006
+ break;
13007
+ case "VariableDeclarator":
13008
+ bindPattern(node2.id, out);
13009
+ break;
13010
+ case "FunctionDeclaration":
13011
+ case "FunctionExpression":
13012
+ case "TSDeclareFunction":
13013
+ case "ArrowFunctionExpression":
13014
+ if (isNode(node2.id)) out.add(String(node2.id.name));
13015
+ for (const p of node2.params?.items ?? node2.params ?? []) bindPattern(p, out);
13016
+ if (isNode(node2.params) && node2.params.rest) bindPattern(node2.params.rest, out);
13017
+ break;
13018
+ case "ClassDeclaration":
13019
+ case "ClassExpression":
13020
+ if (isNode(node2.id)) out.add(String(node2.id.name));
13021
+ break;
13022
+ case "CatchClause":
13023
+ if (node2.param) {
13024
+ bindPattern(isNode(node2.param) && node2.param.pattern ? node2.param.pattern : node2.param, out);
13025
+ }
13026
+ break;
13027
+ default:
13028
+ break;
13029
+ }
13030
+ for (const key of Object.keys(node2)) {
13031
+ if (key === "type" || key === "start" || key === "end") continue;
13032
+ collectBindings(node2[key], out);
13033
+ }
13034
+ }
13035
+ var FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
13036
+ function describeTopLevel(stmt) {
13037
+ const declared = /* @__PURE__ */ new Set();
13038
+ let target = stmt;
13039
+ let prefix = "";
13040
+ let exported = false;
13041
+ if (stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration") {
13042
+ exported = true;
13043
+ prefix = "export ";
13044
+ if (!isNode(stmt.declaration)) return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}{ \u2026 }` };
13045
+ target = stmt.declaration;
13046
+ }
13047
+ if (target.type === "VariableDeclaration") {
13048
+ const decls = (target.declarations ?? []).filter(isNode);
13049
+ for (const d of decls) bindPattern(d.id, declared);
13050
+ const declaresOnlyFunctions = decls.length > 0 && decls.every((d) => isNode(d.init) && FUNCTION_TYPES2.has(d.init.type));
13051
+ const name = [...declared][0];
13052
+ return {
13053
+ declared,
13054
+ exported,
13055
+ declaresOnlyFunctions,
13056
+ label: name ? `${prefix}${String(target.kind)} ${name}` : null
13057
+ };
13058
+ }
13059
+ if (target.type === "FunctionDeclaration" && isNode(target.id)) {
13060
+ declared.add(String(target.id.name));
13061
+ return { declared, exported, declaresOnlyFunctions: true, label: `${prefix}function ${String(target.id.name)}` };
13062
+ }
13063
+ if (target.type === "ClassDeclaration" && isNode(target.id)) {
13064
+ declared.add(String(target.id.name));
13065
+ return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}class ${String(target.id.name)}` };
13066
+ }
13067
+ return { declared, exported, declaresOnlyFunctions: false, label: null };
13068
+ }
13069
+ function findFreeIdentifiers(code, filename) {
13070
+ let parsed;
13071
+ try {
13072
+ parsed = (0, import_oxc_parser3.parseSync)(filename, code);
13073
+ } catch {
13074
+ return [];
13075
+ }
13076
+ if (parsed.errors.length > 0) return [];
13077
+ const bound = /* @__PURE__ */ new Set();
13078
+ collectBindings(parsed.program.body, bound);
13079
+ const lineStarts = [0];
13080
+ for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) lineStarts.push(i + 1);
13081
+ const positionOf = (offset) => {
13082
+ let lo = 0;
13083
+ let hi = lineStarts.length - 1;
13084
+ while (lo < hi) {
13085
+ const mid = lo + hi + 1 >> 1;
13086
+ if (lineStarts[mid] <= offset) lo = mid;
13087
+ else hi = mid - 1;
13088
+ }
13089
+ return { line: lo + 1, column: offset - lineStarts[lo] + 1 };
13090
+ };
13091
+ const statements = parsed.program.body.filter(isNode);
13092
+ const tops = statements.map(describeTopLevel);
13093
+ const candidates = [];
13094
+ const seen = /* @__PURE__ */ new Set();
13095
+ const readIn = /* @__PURE__ */ new Map();
13096
+ const visit = (node2, stmt, insideFunction) => {
13097
+ if (Array.isArray(node2)) {
13098
+ for (const child of node2) visit(child, stmt, insideFunction);
13099
+ return;
13100
+ }
13101
+ if (!isNode(node2)) return;
13102
+ const type = node2.type;
13103
+ if (type === "ImportDeclaration" || type === "ExportAllDeclaration") return;
13104
+ if (type === "MetaProperty" || type === "PrivateIdentifier") return;
13105
+ if (type === "ExportNamedDeclaration" && node2.source) return;
13106
+ if (type.startsWith("TS")) {
13107
+ if (TS_VALUE_WRAPPERS.has(type)) visit(node2.expression, stmt, insideFunction);
13108
+ return;
13109
+ }
13110
+ if (type === "Identifier" || type === "IdentifierReference") {
13111
+ const name = String(node2.name);
13112
+ let readers = readIn.get(name);
13113
+ if (!readers) readIn.set(name, readers = /* @__PURE__ */ new Set());
13114
+ readers.add(stmt);
13115
+ if (bound.has(name) || HOST_GLOBALS.has(name)) return;
13116
+ const offset = typeof node2.start === "number" ? node2.start : 0;
13117
+ const { line, column } = positionOf(offset);
13118
+ const key = `${name}@${line}:${column}`;
13119
+ if (seen.has(key)) return;
13120
+ seen.add(key);
13121
+ candidates.push({ name, line, column, enclosing: tops[stmt].label, stmt, insideFunction });
13122
+ return;
13123
+ }
13124
+ const nowInsideFunction = insideFunction || FUNCTION_TYPES2.has(type);
13125
+ for (const key of Object.keys(node2)) {
13126
+ if (key === "type" || key === "start" || key === "end") continue;
13127
+ if (TYPE_KEYS.has(key)) continue;
13128
+ if (key === "key" && node2.computed !== true) continue;
13129
+ if (key === "property" && node2.computed !== true) continue;
13130
+ if (key === "label") continue;
13131
+ if (key === "exported") continue;
13132
+ if (key === "imported") continue;
13133
+ visit(node2[key], stmt, nowInsideFunction);
13134
+ }
13135
+ };
13136
+ for (let i = 0; i < statements.length; i++) visit(statements[i], i, false);
13137
+ return candidates.filter((c) => {
13138
+ const top = tops[c.stmt];
13139
+ if (!c.insideFunction || top.exported || !top.declaresOnlyFunctions || top.declared.size === 0) return true;
13140
+ for (const name of top.declared) {
13141
+ const readers = readIn.get(name);
13142
+ if (readers && [...readers].some((i) => i !== c.stmt)) return true;
13143
+ }
13144
+ return false;
13145
+ }).map(({ name, line, column, enclosing }) => ({ name, line, column, enclosing }));
13146
+ }
13147
+
13148
+ // src/compiler/module-hoist.ts
13149
+ var marker = (id) => `/*@pmh:${id}@*/`;
13150
+ var MARKER_RE = /\/\*@pmh:(\d+)@\*\/\n?/g;
13151
+ var HOIST_MARKER_PROBE = /\/\*@pmh:\d+@\*\//;
13152
+ var DECL_RE = /^(?:const|let|var|function\*?)\s+([A-Za-z_$][\w$]*)/;
13153
+ var ALWAYS_LOCAL = /* @__PURE__ */ new Set(["_map", "_k", "_env"]);
13154
+ function declaredNames(text) {
13155
+ const out = [];
13156
+ for (const line of text.split("\n")) {
13157
+ const m = DECL_RE.exec(line);
13158
+ if (m) out.push(m[1]);
13159
+ }
13160
+ return out;
13161
+ }
13162
+ function createModuleHoist() {
13163
+ const texts = [];
13164
+ const claim = (decls) => decls.map((text) => {
13165
+ const id = texts.length;
13166
+ texts.push(text);
13167
+ return marker(id);
13168
+ });
13169
+ const finalize = () => {
13170
+ const nameTexts = /* @__PURE__ */ new Map();
13171
+ const textCount = /* @__PURE__ */ new Map();
13172
+ const textNames = /* @__PURE__ */ new Map();
13173
+ const order = [];
13174
+ for (const text of texts) {
13175
+ const seen = textCount.get(text);
13176
+ textCount.set(text, (seen ?? 0) + 1);
13177
+ if (seen === void 0) {
13178
+ order.push(text);
13179
+ const names = declaredNames(text);
13180
+ textNames.set(text, names);
13181
+ for (const n of names) {
13182
+ const s = nameTexts.get(n);
13183
+ if (s) s.add(text);
13184
+ else nameTexts.set(n, /* @__PURE__ */ new Set([text]));
13185
+ }
13186
+ }
13187
+ }
13188
+ const hoisted = new Set(
13189
+ order.filter(
13190
+ (text) => (textCount.get(text) ?? 0) >= 2 && textNames.get(text).length > 0 && textNames.get(text).every((n) => !ALWAYS_LOCAL.has(n) && nameTexts.get(n).size === 1)
13191
+ )
13192
+ );
13193
+ for (; ; ) {
13194
+ const localNames = [];
13195
+ for (const [n, ts] of nameTexts) {
13196
+ if (ts.size !== 1 || !hoisted.has([...ts][0])) localNames.push(n);
13197
+ }
13198
+ let changed = false;
13199
+ for (const text of [...hoisted]) {
13200
+ if (localNames.some((n) => new RegExp(`\\b${escapeName(n)}\\b`).test(text))) {
13201
+ hoisted.delete(text);
13202
+ changed = true;
13203
+ }
13204
+ }
13205
+ if (!changed) break;
12883
13206
  }
13207
+ const preludeTexts = order.filter((t) => hoisted.has(t));
13208
+ const prelude = preludeTexts.length === 0 ? "" : `${preludeTexts.join("\n")}
13209
+ `;
13210
+ const removed = texts.reduce((n, t) => hoisted.has(t) ? n + t.length + 1 : n, 0);
13211
+ return {
13212
+ prelude,
13213
+ savedBytes: removed - prelude.length,
13214
+ hoistedNames: preludeTexts.flatMap((t) => textNames.get(t)),
13215
+ resolve: (src) => src.replace(MARKER_RE, (m, idStr) => {
13216
+ const text = texts[Number(idStr)];
13217
+ if (text === void 0) throw new Error(`parseman: unknown hoist marker ${m}`);
13218
+ if (hoisted.has(text)) return "";
13219
+ return m.endsWith("\n") ? `${text}
13220
+ ` : text;
13221
+ })
13222
+ };
12884
13223
  };
13224
+ return { claim, finalize };
13225
+ }
13226
+ function escapeName(n) {
13227
+ return n.replace(/\$/g, "\\$");
12885
13228
  }
12886
13229
 
12887
13230
  // src/plugin/index.ts
@@ -12977,7 +13320,7 @@ function parseModuleCached(filePath) {
12977
13320
  let parsed = null;
12978
13321
  try {
12979
13322
  const src = fs2.readFileSync(filePath, "utf8");
12980
- const r = (0, import_oxc_parser3.parseSync)(filePath, src);
13323
+ const r = (0, import_oxc_parser4.parseSync)(filePath, src);
12981
13324
  if (r.errors.length === 0) parsed = { body: r.program.body, src };
12982
13325
  } catch {
12983
13326
  }
@@ -13118,7 +13461,7 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
13118
13461
  const source = fs2.readFileSync(file, "utf8");
13119
13462
  const transformed = transformMacro(source, file, moduleAliases, warnUnloweredRegex, recovery);
13120
13463
  if (!transformed || transformed.warnings.length > 0) return null;
13121
- const parsed = (0, import_oxc_parser3.parseSync)(file, transformed.code);
13464
+ const parsed = (0, import_oxc_parser4.parseSync)(file, transformed.code);
13122
13465
  parsedModule = parsed.errors.length === 0 ? { body: parsed.program.body, src: transformed.code } : null;
13123
13466
  return parsedModule;
13124
13467
  } catch {
@@ -13142,7 +13485,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13142
13485
  function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage) {
13143
13486
  let result;
13144
13487
  try {
13145
- result = (0, import_oxc_parser3.parseSync)(id, code);
13488
+ result = (0, import_oxc_parser4.parseSync)(id, code);
13146
13489
  } catch {
13147
13490
  return null;
13148
13491
  }
@@ -13199,7 +13542,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13199
13542
  }
13200
13543
  }
13201
13544
  }
13202
- setReducerResolver(createReducerResolver(id, body, code));
13545
+ const reducerResolver = createReducerResolver(id, body, code);
13546
+ setReducerResolver(reducerResolver, code);
13203
13547
  const topLevelFunction = (moduleBody, name) => {
13204
13548
  for (const st of moduleBody) {
13205
13549
  const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
@@ -13294,15 +13638,18 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13294
13638
  scope: factoryScope,
13295
13639
  imported: true
13296
13640
  });
13641
+ reducerResolver.register(file);
13297
13642
  }
13298
13643
  const scope = /* @__PURE__ */ new Map();
13299
13644
  const replacements = [];
13300
13645
  const warnings = [];
13646
+ const moduleHoist = grammarCoverage ? void 0 : createModuleHoist();
13301
13647
  beginLoweringCapture();
13302
13648
  beginInlineCapCapture();
13303
13649
  beginDegradationCapture();
13304
13650
  let anyUnresolved = false;
13305
13651
  let keepMacroImport = false;
13652
+ const exportedFactories = [];
13306
13653
  let runtimeComposeFallback = false;
13307
13654
  let ruleMapHolderCounter = 0;
13308
13655
  const lineOf = (pos) => {
@@ -13661,7 +14008,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13661
14008
  const pieces = materializeCarried(carried, composing, false, cHostMode);
13662
14009
  try {
13663
14010
  return {
13664
- replacement: emitFusedSource(pieces),
14011
+ replacement: emitFusedSource(pieces, moduleHoist),
13665
14012
  carried,
13666
14013
  ...composing ? { trivia: composing } : {},
13667
14014
  ...importedFactories.length ? { importedFactories } : {}
@@ -13729,7 +14076,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13729
14076
  warn(init.start, "composeLeaf(): every pre-final grammar must explicitly prove recognition-only");
13730
14077
  return null;
13731
14078
  }
13732
- const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
14079
+ const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece], moduleHoist));
13733
14080
  return {
13734
14081
  replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement, `${id} composeLeaf()`)),
13735
14082
  ...importedFactories.length ? { importedFactories } : {}
@@ -13914,7 +14261,10 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13914
14261
  scope.set(varName, staticValue);
13915
14262
  continue;
13916
14263
  }
13917
- if (factoryDecls.has(varName)) continue;
14264
+ if (factoryDecls.has(varName)) {
14265
+ if (exportPrefix) exportedFactories.push({ name: varName, pos: init.start });
14266
+ continue;
14267
+ }
13918
14268
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
13919
14269
  continue;
13920
14270
  }
@@ -13985,6 +14335,14 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
13985
14335
  for (const imp of macroImports) {
13986
14336
  imp.fullyResolved = !anyUnresolved && !keepMacroImport;
13987
14337
  }
14338
+ if (exportedFactories.length > 0 && macroImports.some((imp) => imp.fullyResolved)) {
14339
+ const which = exportedFactories.map((f) => ` - ${id}:${lineOf(f.pos)} \u2014 export const ${f.name}`).join("\n");
14340
+ throw new Error(
14341
+ `${id} \u2014 a rules() factory cannot be exported; lowering leaves its body verbatim, so the export would ship a binding that throws ReferenceError on first call:
14342
+ ${which}
14343
+ Drop the \`export\`. The factory is macro-only; \`rules(${exportedFactories[0].name})\` is lowered in place and needs no runtime value.`
14344
+ );
14345
+ }
13988
14346
  const ms = new import_magic_string.default(code);
13989
14347
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13990
14348
  const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
@@ -14033,8 +14391,21 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
14033
14391
  }
14034
14392
  }
14035
14393
  const applied = runtimeComposeFallback ? [] : replacements;
14394
+ const hoisted = applied.length > 0 ? moduleHoist?.finalize() : void 0;
14036
14395
  for (const { start, end, replacement } of applied.slice().sort((a, b) => b.start - a.start)) {
14037
- ms.overwrite(start, end, replacement);
14396
+ ms.overwrite(start, end, hoisted ? hoisted.resolve(replacement) : replacement);
14397
+ }
14398
+ if (hoisted !== void 0 && hoisted.prelude !== "") {
14399
+ const claiming = applied.filter((r) => HOIST_MARKER_PROBE.test(r.replacement));
14400
+ if (claiming.length === 0) {
14401
+ throw new Error(`${id} \u2014 internal: a module-hoist prelude was produced but no applied replacement carries a hoist marker`);
14402
+ }
14403
+ const firstStart = Math.min(...claiming.map((r) => r.start));
14404
+ const anchor = body.find((s) => s.start <= firstStart && firstStart < s.end);
14405
+ if (anchor === void 0) {
14406
+ throw new Error(`${id} \u2014 internal: no top-level statement encloses macro replacement at ${firstStart}`);
14407
+ }
14408
+ ms.appendLeft(anchor.start, hoisted.prelude);
14038
14409
  }
14039
14410
  if (applied.length > 0) {
14040
14411
  ms.prepend(
@@ -14044,6 +14415,21 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
14044
14415
  `
14045
14416
  );
14046
14417
  }
14418
+ if (applied.length > 0) {
14419
+ let sourceFree = null;
14420
+ const freeInSource = () => sourceFree ??= new Set(findFreeIdentifiers(code, id).map((f) => f.name));
14421
+ const free = findFreeIdentifiers(ms.toString(), id).filter((f) => !freeInSource().has(f.name));
14422
+ if (free.length > 0) {
14423
+ const macroProvided = free.filter((f) => allNames.has(f.name));
14424
+ const where = (f) => ` - ${id}:${f.line}:${f.column} \u2014 \`${f.name}\`` + (f.enclosing ? `, inside \`${f.enclosing}\`` : "");
14425
+ const cause = macroProvided.length > 0 ? `
14426
+ ${macroProvided.length === free.length ? "All" : `${macroProvided.length}`} of these came from the \`with { type: 'macro' }\` import, which was removed because every macro declaration lowered. Something still names them verbatim \u2014 a declaration parseman left as text rather than compiling. Make that declaration macro-buildable, or stop exporting it so lowering can drop it.` : "";
14427
+ throw new Error(
14428
+ `${id} \u2014 parseman will not emit this module: ${free.length} identifier(s) are read but bound by nothing, so it would throw ReferenceError at runtime (positions are in the EMITTED module):
14429
+ ${free.map(where).join("\n")}${cause}`
14430
+ );
14431
+ }
14432
+ }
14047
14433
  for (const line of formatInlineCapSites(endInlineCapCapture(), resolveInlineMax())) warnings.push(`${id}: ${line}`);
14048
14434
  const unlowered = endLoweringCapture();
14049
14435
  if (warnUnloweredRegex) {