parseman 0.42.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2777,6 +2777,74 @@ function markUnusedValues(root) {
2777
2777
  visit(resolveRoot(root), true);
2778
2778
  }
2779
2779
 
2780
+ // src/combinators/grammar.ts
2781
+ function annotateResultLines(result, index) {
2782
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2783
+ const errors = annotated.errors;
2784
+ if (errors) {
2785
+ ;
2786
+ annotated.errors = errors.map((error) => ({
2787
+ ...error,
2788
+ span: annotateSpan(error.span, index)
2789
+ }));
2790
+ }
2791
+ return annotated;
2792
+ }
2793
+ function createParseLineContext(input, pos) {
2794
+ const lineIndex = createLineIndex();
2795
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2796
+ return { lineIndex, lineScannedTo: pos };
2797
+ }
2798
+ function parser(opts, root) {
2799
+ const clearTrivia = opts.trivia === null;
2800
+ return {
2801
+ _tag: "grammar",
2802
+ _meta: root._meta,
2803
+ _def: {
2804
+ tag: "grammar",
2805
+ parser: root,
2806
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2807
+ clearTrivia,
2808
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2809
+ trackLines: opts.trackLines ?? false
2810
+ },
2811
+ parse(input, pos, _ctx) {
2812
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2813
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2814
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2815
+ const ctx = {
2816
+ ..._ctx,
2817
+ trackLines,
2818
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2819
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2820
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2821
+ ...clearTrivia ? {
2822
+ trivia: void 0,
2823
+ triviaKindLabels: void 0
2824
+ } : opts.trivia != null ? {
2825
+ trivia: opts.trivia,
2826
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2827
+ } : {},
2828
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2829
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2830
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2831
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2832
+ // trivia is inherited rather than re-declared here. No labels → undefined
2833
+ // (capture all).
2834
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2835
+ };
2836
+ const result = root.parse(input, pos ?? 0, ctx);
2837
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2838
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2839
+ }
2840
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2841
+ }
2842
+ };
2843
+ }
2844
+ function noTrivia(root) {
2845
+ return parser({ trivia: null }, root);
2846
+ }
2847
+
2780
2848
  // src/combinators/parser.ts
2781
2849
  var RULE_ORDER = "__parsemanRuleOrder";
2782
2850
  function tagRule(r, key) {
@@ -2852,6 +2920,21 @@ function rules(a, b) {
2852
2920
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2853
2921
  }
2854
2922
  }
2923
+ if (options?.trackLines === true) {
2924
+ for (const key of Object.keys(definitions)) {
2925
+ const rule = cache[key];
2926
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
2927
+ }
2928
+ for (const key of Object.keys(definitions)) {
2929
+ const rule = cache[key];
2930
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
2931
+ const wrapped = parser({ trackLines: true }, rule);
2932
+ tagRule(wrapped, key);
2933
+ wrapped._meta.grammarTrackLines = true;
2934
+ cache[key] = wrapped;
2935
+ }
2936
+ }
2937
+ }
2855
2938
  for (const key of Object.keys(definitions)) {
2856
2939
  markUnusedValues(cache[key]);
2857
2940
  }
@@ -3308,74 +3391,6 @@ function field(name, combinator) {
3308
3391
  };
3309
3392
  }
3310
3393
 
3311
- // src/combinators/grammar.ts
3312
- function annotateResultLines(result, index) {
3313
- const annotated = { ...result, span: annotateSpan(result.span, index) };
3314
- const errors = annotated.errors;
3315
- if (errors) {
3316
- ;
3317
- annotated.errors = errors.map((error) => ({
3318
- ...error,
3319
- span: annotateSpan(error.span, index)
3320
- }));
3321
- }
3322
- return annotated;
3323
- }
3324
- function createParseLineContext(input, pos) {
3325
- const lineIndex = createLineIndex();
3326
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3327
- return { lineIndex, lineScannedTo: pos };
3328
- }
3329
- function parser(opts, root) {
3330
- const clearTrivia = opts.trivia === null;
3331
- return {
3332
- _tag: "grammar",
3333
- _meta: root._meta,
3334
- _def: {
3335
- tag: "grammar",
3336
- parser: root,
3337
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3338
- clearTrivia,
3339
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3340
- trackLines: opts.trackLines ?? false
3341
- },
3342
- parse(input, pos, _ctx) {
3343
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3344
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3345
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3346
- const ctx = {
3347
- ..._ctx,
3348
- trackLines,
3349
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3350
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3351
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3352
- ...clearTrivia ? {
3353
- trivia: void 0,
3354
- triviaKindLabels: void 0
3355
- } : opts.trivia != null ? {
3356
- trivia: opts.trivia,
3357
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3358
- } : {},
3359
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3360
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3361
- // labels — this parser's own trivia if it declares one, else the INHERITED
3362
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3363
- // trivia is inherited rather than re-declared here. No labels → undefined
3364
- // (capture all).
3365
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3366
- };
3367
- const result = root.parse(input, pos ?? 0, ctx);
3368
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3369
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3370
- }
3371
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3372
- }
3373
- };
3374
- }
3375
- function noTrivia(root) {
3376
- return parser({ trivia: null }, root);
3377
- }
3378
-
3379
3394
  // src/combinators/token.ts
3380
3395
  function token(root) {
3381
3396
  const meta = {
@@ -3472,7 +3487,7 @@ function leaf(root, fn) {
3472
3487
  }
3473
3488
 
3474
3489
  // src/version.ts
3475
- var PARSEMAN_VERSION = "0.42.0";
3490
+ var PARSEMAN_VERSION = "0.42.1";
3476
3491
 
3477
3492
  // src/cst/host-mode.ts
3478
3493
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -6840,13 +6855,16 @@ function reindentStmts(stmts, targetLevels) {
6840
6855
  const targetPrefix = " ".repeat(targetLevels);
6841
6856
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6842
6857
  }
6843
- function failReturn(expected, posExpr) {
6858
+ function failReturn(ctx, expected, posExpr) {
6859
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6844
6860
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6845
6861
  }
6846
- function failReturnArr(expectedArr, posExpr) {
6862
+ function failReturnArr(ctx, expectedArr, posExpr) {
6863
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6847
6864
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6848
6865
  }
6849
- function committedReturnArr(expectedArr, posExpr) {
6866
+ function committedReturnArr(ctx, expectedArr, posExpr) {
6867
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6850
6868
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6851
6869
  }
6852
6870
  function hoistExpected(ctx, constArrSource) {
@@ -6871,7 +6889,7 @@ function failBody(ctx, expected, posExpr) {
6871
6889
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6872
6890
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6873
6891
  }
6874
- return probe + trace + failReturn(expected, posExpr);
6892
+ return probe + trace + failReturn(ctx, expected, posExpr);
6875
6893
  }
6876
6894
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6877
6895
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6881,18 +6899,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6881
6899
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6882
6900
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6883
6901
  }
6884
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6885
- return probe + trace + failReturnArr(expectedArr, posExpr);
6902
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
6903
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
6886
6904
  }
6887
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6888
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6905
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6906
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
6907
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
6889
6908
  }
6890
6909
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6891
6910
  if (ctx.failLabel) {
6892
6911
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6893
6912
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6894
6913
  }
6895
- return committedReturnArr(expectedArr, posExpr);
6914
+ return committedReturnArr(ctx, expectedArr, posExpr);
6896
6915
  }
6897
6916
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6898
6917
  if (srcCtx !== "_ctx") {
@@ -6900,10 +6919,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6900
6919
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6901
6920
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6902
6921
  }
6903
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
6922
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
6904
6923
  }
6905
6924
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6906
- return resultFromRecorded();
6925
+ return resultFromRecorded(ctx);
6907
6926
  }
6908
6927
  function emitIfFail(ctx, cond, body) {
6909
6928
  return [
@@ -8336,7 +8355,7 @@ function emitAttempt(p, def, ctx, pos) {
8336
8355
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8337
8356
  const gcV = v(ctx, "_agc");
8338
8357
  const gExp = armStaticExpected(ctx, def.parser);
8339
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8358
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8340
8359
  preGuard.push(
8341
8360
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8342
8361
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8835,7 +8854,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8835
8854
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8836
8855
  const gcV = v(ctx, "_ngc");
8837
8856
  const gExp = armStaticExpected(ctx, def.parser);
8838
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
8857
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8839
8858
  preGuard.push(
8840
8859
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8841
8860
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -9351,6 +9370,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9351
9370
  if (seen.has(p)) return false;
9352
9371
  seen.add(p);
9353
9372
  const d = p._def;
9373
+ if (p._meta.grammarTrackLines) return true;
9354
9374
  if (d.tag === "grammar" && d.trackLines) return true;
9355
9375
  if (d.tag === "lazy") {
9356
9376
  try {
@@ -9708,6 +9728,7 @@ function compile(combinator, mapFnSources, opts) {
9708
9728
  const grammarTrivia = combinator._meta.grammarTrivia;
9709
9729
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9710
9730
  const grammarHostMode = combinator._meta.grammarHostMode;
9731
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9711
9732
  const ctx = {
9712
9733
  vars: 0,
9713
9734
  indent: 1,
@@ -9728,7 +9749,7 @@ function compile(combinator, mapFnSources, opts) {
9728
9749
  capturing: hasNodeDef(combinator),
9729
9750
  recovery: opts?.recovery ?? false,
9730
9751
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9731
- lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9752
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9732
9753
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9733
9754
  lazyUsage: analyzeLazyUsage(combinator),
9734
9755
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9892,6 +9913,7 @@ function compileRuleMap(ruleMap, opts) {
9892
9913
  const grammarTrivia = opts?.trivia;
9893
9914
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9894
9915
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9916
+ const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
9895
9917
  const ctx = {
9896
9918
  vars: 0,
9897
9919
  indent: 1,
@@ -9912,6 +9934,7 @@ function compileRuleMap(ruleMap, opts) {
9912
9934
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
9913
9935
  recovery: opts?.recovery ?? false,
9914
9936
  hostMode: grammarHostMode ?? "ast",
9937
+ lineTracking: grammarTrackLines,
9915
9938
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9916
9939
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9917
9940
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9945,6 +9968,10 @@ function compileRuleMap(ruleMap, opts) {
9945
9968
  const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
9946
9969
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9947
9970
  if (!canInline) return null;
9971
+ if (ctx.lineTracking) {
9972
+ ctx.needsLineTrack = true;
9973
+ ctx.needsLineSpan = true;
9974
+ }
9948
9975
  const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
9949
9976
  const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
9950
9977
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
@@ -9974,15 +10001,23 @@ function compileRuleMap(ruleMap, opts) {
9974
10001
  };
9975
10002
  const entryFnText = (r, rule) => {
9976
10003
  const ruleId = entryRuleId(rule);
10004
+ const linePreamble = ctx.lineTracking ? [
10005
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10006
+ ` else _ctx.trackLines = true`,
10007
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10008
+ ] : [];
10009
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9977
10010
  const body = ruleId === void 0 ? [
10011
+ ...linePreamble,
9978
10012
  ` let pos = _pos`,
9979
10013
  ...r.stmts,
9980
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
10014
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`
9981
10015
  ] : [
10016
+ ...linePreamble,
9982
10017
  ` const _coverageResult = (() => {`,
9983
10018
  ` let pos = _pos`,
9984
10019
  ...r.stmts,
9985
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10020
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9986
10021
  ` })()`,
9987
10022
  ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
9988
10023
  ` return _coverageResult`
@@ -10020,6 +10055,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10020
10055
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
10021
10056
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
10022
10057
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10058
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
10023
10059
  const ruleMap = ruleMapArg.filter(([, val]) => {
10024
10060
  const d = val._def;
10025
10061
  if (d.tag !== "lazy") return true;
@@ -10054,6 +10090,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10054
10090
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
10055
10091
  recovery: opts?.recovery ?? false,
10056
10092
  hostMode: grammarHostMode ?? "ast",
10093
+ lineTracking: grammarTrackLines,
10057
10094
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
10058
10095
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10059
10096
  ns,
@@ -10150,6 +10187,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
10150
10187
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
10151
10188
  }
10152
10189
  if (ctx.runtimeParsers.length !== 0) return null;
10190
+ if (ctx.lineTracking) {
10191
+ ctx.needsLineTrack = true;
10192
+ ctx.needsLineSpan = true;
10193
+ }
10153
10194
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
10154
10195
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
10155
10196
  const ruleFns = /* @__PURE__ */ new Map();
@@ -10163,6 +10204,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
10163
10204
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
10164
10205
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
10165
10206
  const prelude = [
10207
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10208
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
10166
10209
  ...ctx.regexDecls,
10167
10210
  ...ctx.expectedDecls,
10168
10211
  mfDecl,
@@ -10171,11 +10214,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
10171
10214
  ].filter(Boolean);
10172
10215
  const wrappers = /* @__PURE__ */ new Map();
10173
10216
  for (const { key, rule, r } of perEntry) {
10217
+ const linePreamble = ctx.lineTracking ? [
10218
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10219
+ ` else _ctx.trackLines = true`,
10220
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10221
+ ] : [];
10222
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
10174
10223
  wrappers.set(key, publicRuleWrapperSource(rule, [
10175
10224
  `function(input, _pos, _ctx) {`,
10225
+ ...linePreamble,
10176
10226
  ` let pos = _pos`,
10177
10227
  ...r.stmts,
10178
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10228
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10179
10229
  `}`
10180
10230
  ].join("\n"), ctx.triviaKindLabels));
10181
10231
  }
@@ -11009,7 +11059,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
11009
11059
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
11010
11060
  ...trivia2 ? { trivia: trivia2 } : {},
11011
11061
  ...captureTerminals ? { captureTerminals: true } : {},
11012
- ...hostMode ? { hostMode } : {}
11062
+ ...hostMode ? { hostMode } : {},
11063
+ ...p.trackLines ? { trackLines: true } : {}
11013
11064
  });
11014
11065
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
11015
11066
  return pieces;
@@ -12256,6 +12307,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12256
12307
  const macroImports = [];
12257
12308
  const allNames = /* @__PURE__ */ new Set();
12258
12309
  const importBindings = /* @__PURE__ */ new Map();
12310
+ const ordinaryImports = [];
12259
12311
  for (const stmt of body) {
12260
12312
  if (stmt.type !== "ImportDeclaration") continue;
12261
12313
  const s = stmt;
@@ -12264,12 +12316,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12264
12316
  return key === "type" && a.value.value === "macro";
12265
12317
  });
12266
12318
  if (!isMacro) {
12319
+ const specifiers = [];
12267
12320
  for (const spec of s.specifiers) {
12268
12321
  if (spec.type === "ImportSpecifier") {
12269
12322
  const imported = spec.imported.name ?? spec.local.name;
12270
12323
  importBindings.set(spec.local.name, { source: s.source.value, imported });
12324
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12325
+ } else {
12326
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12271
12327
  }
12272
12328
  }
12329
+ ordinaryImports.push({ start: s.start, end: s.end, specifiers });
12273
12330
  continue;
12274
12331
  }
12275
12332
  const names = /* @__PURE__ */ new Set();
@@ -12280,6 +12337,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12280
12337
  for (const n of names) allNames.add(n);
12281
12338
  }
12282
12339
  if (macroImports.length === 0) return null;
12340
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12283
12341
  const factoryDecls = /* @__PURE__ */ new Map();
12284
12342
  for (const stmt of body) {
12285
12343
  const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
@@ -12297,6 +12355,101 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12297
12355
  }
12298
12356
  }
12299
12357
  }
12358
+ const topLevelFunction = (moduleBody, name) => {
12359
+ for (const st of moduleBody) {
12360
+ const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
12361
+ const idn = decl?.type === "FunctionDeclaration" ? decl.id : void 0;
12362
+ if (idn?.name === name) return decl ?? null;
12363
+ }
12364
+ return null;
12365
+ };
12366
+ const isParsemanMacroImport = (stmt) => {
12367
+ if (stmt.type !== "ImportDeclaration") return false;
12368
+ const s = stmt;
12369
+ return moduleAliases.has(s.source.value) && s.attributes.some((a) => {
12370
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
12371
+ return key === "type" && a.value.value === "macro";
12372
+ });
12373
+ };
12374
+ const sourceScopeUntil = (moduleBody, source, until) => {
12375
+ const out = /* @__PURE__ */ new Map();
12376
+ for (const stmt of moduleBody) {
12377
+ if (stmt.type === "ImportDeclaration") {
12378
+ if (!isParsemanMacroImport(stmt)) return null;
12379
+ continue;
12380
+ }
12381
+ if (stmt.type === "FunctionDeclaration") continue;
12382
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration") continue;
12383
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration) {
12384
+ if (stmt.source) return null;
12385
+ continue;
12386
+ }
12387
+ const vd = unwrapVd(stmt);
12388
+ if (!vd || vd.kind !== "const") return null;
12389
+ for (const decl of vd.declarations) {
12390
+ const d = decl;
12391
+ const idn = d.id;
12392
+ const init = d.init;
12393
+ if (idn.type !== "Identifier" && idn.type !== "BindingIdentifier" || !idn.name || !init) return null;
12394
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") continue;
12395
+ const mapFnSources = [];
12396
+ const combi = evaluateExpr(init, out, source, mapFnSources);
12397
+ if (combi) {
12398
+ if (stmt.start < until) out.set(idn.name, { combi, mfSrcs: mapFnSources });
12399
+ continue;
12400
+ }
12401
+ const wordFactory = evaluateWordFactory(init, out, source);
12402
+ if (wordFactory) {
12403
+ if (stmt.start < until) {
12404
+ ;
12405
+ out.set(idn.name, wordFactory);
12406
+ }
12407
+ continue;
12408
+ }
12409
+ const whenFactory = evaluateWhenFactory(init, out, source);
12410
+ if (whenFactory) {
12411
+ if (stmt.start < until) {
12412
+ ;
12413
+ out.set(idn.name, whenFactory);
12414
+ }
12415
+ continue;
12416
+ }
12417
+ const combiArray = evaluateCombinatorArray(init, out, source);
12418
+ if (combiArray) {
12419
+ if (stmt.start < until) {
12420
+ ;
12421
+ out.set(idn.name, combiArray);
12422
+ }
12423
+ continue;
12424
+ }
12425
+ return null;
12426
+ }
12427
+ continue;
12428
+ }
12429
+ return out;
12430
+ };
12431
+ const usedImportedFactories = /* @__PURE__ */ new Set();
12432
+ const markUsedImportedFactories = (names) => {
12433
+ for (const name of names ?? []) usedImportedFactories.add(name);
12434
+ };
12435
+ for (const [localName, binding] of importBindings) {
12436
+ const file = resolvePrivateSourceModule(id, binding.source);
12437
+ if (!file) continue;
12438
+ const mod = parseModuleCached(file);
12439
+ if (!mod) continue;
12440
+ const localFor = exportLocalName(mod.body, binding.imported);
12441
+ const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
12442
+ if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression" && init.type !== "FunctionDeclaration") continue;
12443
+ const factoryScope = sourceScopeUntil(mod.body, mod.src, init.start);
12444
+ if (!factoryScope) continue;
12445
+ factoryDecls.set(localName, {
12446
+ fn: init,
12447
+ declaredAt: -1,
12448
+ code: mod.src,
12449
+ scope: factoryScope,
12450
+ imported: true
12451
+ });
12452
+ }
12300
12453
  const scope = /* @__PURE__ */ new Map();
12301
12454
  const replacements = [];
12302
12455
  const warnings = [];
@@ -12326,6 +12479,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12326
12479
  }
12327
12480
  const namedFactory = factoryArgRaw.type === "Identifier" ? factoryDecls.get(factoryArgRaw.name) : void 0;
12328
12481
  const factoryArg = namedFactory !== void 0 && namedFactory.declaredAt <= init.start ? namedFactory.fn : factoryArgRaw;
12482
+ const factoryScope = namedFactory?.scope ?? scope;
12483
+ const factoryCode = namedFactory?.code ?? code;
12484
+ const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
12329
12485
  const optionsArg = optionsFirst ? arg0 : arg1;
12330
12486
  const optionValue = (name) => optionsArg?.type === "ObjectExpression" ? (optionsArg.properties ?? []).find(
12331
12487
  (p) => p.key?.name === name
@@ -12336,7 +12492,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12336
12492
  warn(init.start, `${label2}: rules({ hostMode }) must be the literal 'ast' or 'cst'`);
12337
12493
  return null;
12338
12494
  }
12339
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
12495
+ const trackLinesValue = optionValue("trackLines");
12496
+ const gTrackLines = trackLinesValue?.type === "Literal" ? trackLinesValue.value : void 0;
12497
+ if (trackLinesValue !== void 0 && typeof gTrackLines !== "boolean") {
12498
+ warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
12499
+ return null;
12500
+ }
12501
+ const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, []);
12340
12502
  if (!ruleMap) {
12341
12503
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
12342
12504
  return null;
@@ -12361,12 +12523,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12361
12523
  }
12362
12524
  }
12363
12525
  }
12364
- return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {}, ...gScanSkip ? { scanSkip: gScanSkip } : {} };
12526
+ if (gTrackLines === true) {
12527
+ for (const rule of ruleMap.values()) {
12528
+ if (rule && !rule._meta.isTrivia) {
12529
+ ;
12530
+ rule._meta.grammarTrackLines = true;
12531
+ }
12532
+ }
12533
+ }
12534
+ return {
12535
+ ruleMap,
12536
+ ...gTrivia ? { trivia: gTrivia } : {},
12537
+ ...gScanSkip ? { scanSkip: gScanSkip } : {},
12538
+ ...gTrackLines === true ? { trackLines: true } : {},
12539
+ ...importedFactory ? { importedFactory } : {}
12540
+ };
12365
12541
  };
12366
12542
  const compileRulesFactory = (init, label2) => {
12367
12543
  const evaluated = evaluateRulesFactory(init, label2);
12368
12544
  if (!evaluated) return null;
12369
- const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
12545
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage });
12370
12546
  return {
12371
12547
  replacement: compiled?.replacement ?? null,
12372
12548
  ruleMap: evaluated.ruleMap,
@@ -12374,7 +12550,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12374
12550
  hostBranchElided: compiled?.hostBranchElided ?? false,
12375
12551
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12376
12552
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12377
- ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}
12553
+ ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
12554
+ ...evaluated.trackLines ? { trackLines: true } : {},
12555
+ ...evaluated.importedFactory ? { importedFactory: evaluated.importedFactory } : {}
12378
12556
  };
12379
12557
  };
12380
12558
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
@@ -12394,7 +12572,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12394
12572
  };
12395
12573
  const isSpread = (it) => "__spreadLocal" in it;
12396
12574
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
12397
- const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
12575
+ const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)}${it.trackLines ? ", trackLines: true" : ""} }` : serializePieces(it);
12398
12576
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
12399
12577
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12400
12578
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
@@ -12525,10 +12703,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12525
12703
  const localCarried = (rm, label2, composing, scanSkip) => {
12526
12704
  const entries = [...rm];
12527
12705
  const ns = nsFor(label2);
12706
+ const trackLines = entries.some(([, rule]) => rule?._meta?.grammarTrackLines === true);
12528
12707
  const ir = serializeRuleMap(entries, scanSkip);
12529
- if (ir) return { carried: [{ ns, ir }] };
12708
+ if (ir) return { carried: [{ ns, ir, ...trackLines ? { trackLines: true } : {} }] };
12530
12709
  warn(0, `${label2}: rule map could not be serialized to IR; carrying FULL pieces instead. The artifact is correct but larger, and a downstream compose cannot re-lower it. Re-run with PARSEMAN_IR_DEBUG=1 to print the exact combinator that blocked serialization.`);
12531
- const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, recovery });
12710
+ const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, ...trackLines ? { trackLines: true } : {}, recovery });
12532
12711
  return p ? { carried: [p] } : null;
12533
12712
  };
12534
12713
  const argPieces = (arg, label2, composing) => {
@@ -12546,7 +12725,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12546
12725
  const innerArg = argPieces(inner, `${label2}_pick`);
12547
12726
  if (!innerArg) return null;
12548
12727
  try {
12549
- return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
12728
+ return {
12729
+ carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names),
12730
+ ...innerArg.importedFactories ? { importedFactories: innerArg.importedFactories } : {}
12731
+ };
12550
12732
  } catch (e) {
12551
12733
  warn(arg.start, `pick(): ${e.message}`);
12552
12734
  return null;
@@ -12554,7 +12736,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12554
12736
  }
12555
12737
  if (isRulesCall(arg)) {
12556
12738
  const evaluated = evaluateRulesFactory(arg, label2);
12557
- return evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12739
+ const carried = evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12740
+ return carried ? { ...carried, ...evaluated?.importedFactory ? { importedFactories: [evaluated.importedFactory] } : {} } : null;
12558
12741
  }
12559
12742
  if (arg.type === "Identifier") {
12560
12743
  const name = arg.name;
@@ -12626,6 +12809,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12626
12809
  }
12627
12810
  const composing = composingTrivia(elements);
12628
12811
  const carried = [];
12812
+ const importedFactories = [];
12629
12813
  for (let i = 0; i < elements.length; i++) {
12630
12814
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
12631
12815
  if (!r) {
@@ -12633,6 +12817,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12633
12817
  return null;
12634
12818
  }
12635
12819
  carried.push(...r.carried);
12820
+ importedFactories.push(...r.importedFactories ?? []);
12636
12821
  }
12637
12822
  const detailed = once(() => carriedRuleMapsDetailed(carried));
12638
12823
  runFusedGatingDiagnostic(
@@ -12642,7 +12827,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12642
12827
  );
12643
12828
  const pieces = materializeCarried(carried, composing, false, cHostMode);
12644
12829
  try {
12645
- return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
12830
+ return {
12831
+ replacement: emitFusedSource(pieces),
12832
+ carried,
12833
+ ...composing ? { trivia: composing } : {},
12834
+ ...importedFactories.length ? { importedFactories } : {}
12835
+ };
12646
12836
  } catch (e) {
12647
12837
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
12648
12838
  return null;
@@ -12663,10 +12853,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12663
12853
  const localArg = elements[elements.length - 1];
12664
12854
  let localRules = null;
12665
12855
  let localScanSkip;
12856
+ const importedFactories = [];
12666
12857
  if (isRulesCall(localArg)) {
12667
12858
  const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
12668
12859
  localRules = evaluated?.ruleMap ?? null;
12669
12860
  localScanSkip = evaluated?.scanSkip;
12861
+ if (evaluated?.importedFactory) importedFactories.push(evaluated.importedFactory);
12670
12862
  } else if (localArg.type === "Identifier") {
12671
12863
  const name = localArg.name;
12672
12864
  localRules = localRuleMaps.get(name) ?? null;
@@ -12685,6 +12877,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12685
12877
  return null;
12686
12878
  }
12687
12879
  carried.push(...r.carried);
12880
+ importedFactories.push(...r.importedFactories ?? []);
12688
12881
  }
12689
12882
  try {
12690
12883
  const localNs = nsFor(`composeLeaf${init.start}`);
@@ -12710,13 +12903,15 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12710
12903
  return null;
12711
12904
  }
12712
12905
  const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
12713
- return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
12906
+ return {
12907
+ replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
12908
+ ...importedFactories.length ? { importedFactories } : {}
12909
+ };
12714
12910
  } catch (e) {
12715
12911
  warn(init.start, `composeLeaf(): ${e.message}`);
12716
12912
  return null;
12717
12913
  }
12718
12914
  };
12719
- const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12720
12915
  const refNames = /* @__PURE__ */ new Set();
12721
12916
  for (const stmt of body) {
12722
12917
  const innerVd = unwrapVd(stmt);
@@ -12800,6 +12995,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12800
12995
  const ns = nsFor(varName);
12801
12996
  const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
12802
12997
  ...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
12998
+ ...compiledRules.trackLines ? { trackLines: true } : {},
12803
12999
  recovery
12804
13000
  }) : null;
12805
13001
  if (compiledRules.replacement === null) {
@@ -12819,9 +13015,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12819
13015
  }
12820
13016
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
12821
13017
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
12822
- replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
13018
+ replacement = withCarriedPieces(replacement, [ir ? { ns, ir, ...compiledRules.trackLines ? { trackLines: true } : {} } : pieces]);
12823
13019
  }
12824
13020
  replacements.push({ start: init.start, end: init.end, replacement });
13021
+ if (compiledRules.replacement !== null) {
13022
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
13023
+ }
12825
13024
  continue;
12826
13025
  }
12827
13026
  if (isComposeCall(init)) {
@@ -12834,6 +13033,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12834
13033
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
12835
13034
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
12836
13035
  replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
13036
+ markUsedImportedFactories(fused.importedFactories);
12837
13037
  continue;
12838
13038
  }
12839
13039
  if (isComposeLeafCall(init)) {
@@ -12842,6 +13042,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12842
13042
  throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
12843
13043
  }
12844
13044
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13045
+ markUsedImportedFactories(fused.importedFactories);
12845
13046
  continue;
12846
13047
  }
12847
13048
  const mapFnSources = [];
@@ -12930,6 +13131,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12930
13131
  end: stmtEnd,
12931
13132
  replacement: lines.join("\n")
12932
13133
  });
13134
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
12933
13135
  }
12934
13136
  }
12935
13137
  }
@@ -12938,6 +13140,40 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12938
13140
  imp.fullyResolved = !anyUnresolved && !keepMacroImport;
12939
13141
  }
12940
13142
  const ms = new import_magic_string.default(code);
13143
+ const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13144
+ const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13145
+ const importedBindingStillReferenced = (local) => {
13146
+ const walk2 = (node2, parent, parentKey) => {
13147
+ if (!node2 || typeof node2 !== "object") return false;
13148
+ const rec = node2;
13149
+ if (rec.type === "ImportDeclaration") return false;
13150
+ if (typeof rec.start === "number" && typeof rec.end === "number" && isInsideReplacement(rec.start, rec.end)) return false;
13151
+ if ((rec.type === "Identifier" || rec.type === "BindingIdentifier") && rec.name === local) {
13152
+ if (parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true) return false;
13153
+ if (parentKey === "property" && parent?.type === "StaticMemberExpression") return false;
13154
+ if (parentKey === "property" && parent?.type === "MemberExpression" && parent.computed !== true) return false;
13155
+ return true;
13156
+ }
13157
+ for (const key of Object.keys(rec)) {
13158
+ if (key === "type" || key === "start" || key === "end") continue;
13159
+ const value = rec[key];
13160
+ if (Array.isArray(value)) {
13161
+ if (value.some((child) => walk2(child, rec, key))) return true;
13162
+ } else if (walk2(value, rec, key)) {
13163
+ return true;
13164
+ }
13165
+ }
13166
+ return false;
13167
+ };
13168
+ return body.some((stmt) => walk2(stmt));
13169
+ };
13170
+ for (const imp of ordinaryImports) {
13171
+ if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
13172
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
13173
+ )) {
13174
+ ms.remove(imp.start, imp.end);
13175
+ }
13176
+ }
12941
13177
  if (!anyUnresolved) {
12942
13178
  for (const { start, end } of defineRemovals) ms.remove(start, end);
12943
13179
  }