@fictjs/compiler 0.0.8 → 0.0.9

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 (3) hide show
  1. package/dist/index.cjs +413 -171
  2. package/dist/index.js +413 -171
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -14372,6 +14372,9 @@ function pathToString(path) {
14372
14372
 
14373
14373
  // src/ir/build-hir.ts
14374
14374
  var destructuringTempCounter = 0;
14375
+ var getLoc = (node) => {
14376
+ return node?.loc ?? null;
14377
+ };
14375
14378
  function normalizeVarKind(kind) {
14376
14379
  return kind === "const" || kind === "let" || kind === "var" ? kind : "let";
14377
14380
  }
@@ -14441,7 +14444,8 @@ function buildHIR(ast) {
14441
14444
  functions.push(
14442
14445
  convertFunction(name, stmt.params, stmt.body.body, {
14443
14446
  noMemo: programNoMemo,
14444
- directives: stmt.body.directives
14447
+ directives: stmt.body.directives,
14448
+ loc: getLoc(stmt)
14445
14449
  })
14446
14450
  );
14447
14451
  continue;
@@ -14454,7 +14458,8 @@ function buildHIR(ast) {
14454
14458
  functions.push(
14455
14459
  convertFunction(name, decl.params, decl.body.body, {
14456
14460
  noMemo: programNoMemo,
14457
- directives: decl.body.directives
14461
+ directives: decl.body.directives,
14462
+ loc: getLoc(decl)
14458
14463
  })
14459
14464
  );
14460
14465
  postamble.push({ kind: "ExportFunction", name });
@@ -14472,9 +14477,11 @@ function buildHIR(ast) {
14472
14477
  const hasExpressionBody = isArrow && !t.isBlockStatement(body);
14473
14478
  const fnHIR = t.isBlockStatement(body) ? convertFunction(name, params, body.body, {
14474
14479
  noMemo: programNoMemo,
14475
- directives: body.directives
14480
+ directives: body.directives,
14481
+ loc: getLoc(v.init ?? v)
14476
14482
  }) : convertFunction(name, params, [t.returnStatement(body)], {
14477
- noMemo: programNoMemo
14483
+ noMemo: programNoMemo,
14484
+ loc: getLoc(v.init ?? v)
14478
14485
  });
14479
14486
  fnHIR.meta = { ...fnHIR.meta ?? {}, fromExpression: true, isArrow, hasExpressionBody };
14480
14487
  functions.push(fnHIR);
@@ -14499,7 +14506,8 @@ function buildHIR(ast) {
14499
14506
  functions.push(
14500
14507
  convertFunction(name, decl.params, decl.body.body, {
14501
14508
  noMemo: programNoMemo,
14502
- directives: decl.body.directives
14509
+ directives: decl.body.directives,
14510
+ loc: getLoc(decl)
14503
14511
  })
14504
14512
  );
14505
14513
  postamble.push({ kind: "ExportDefault", name });
@@ -14524,13 +14532,15 @@ function buildHIR(ast) {
14524
14532
  const hasExpressionBody = isArrow && !t.isBlockStatement(body);
14525
14533
  const fnHIR = t.isBlockStatement(body) ? convertFunction(name, params, body.body, {
14526
14534
  noMemo: programNoMemo,
14527
- directives: body.directives
14535
+ directives: body.directives,
14536
+ loc: getLoc(decl.init ?? decl)
14528
14537
  }) : convertFunction(
14529
14538
  name,
14530
14539
  params,
14531
14540
  [t.returnStatement(body)],
14532
14541
  {
14533
- noMemo: programNoMemo
14542
+ noMemo: programNoMemo,
14543
+ loc: getLoc(decl.init ?? decl)
14534
14544
  }
14535
14545
  );
14536
14546
  fnHIR.meta = { ...fnHIR.meta ?? {}, fromExpression: true, isArrow, hasExpressionBody };
@@ -15038,11 +15048,12 @@ function convertFunction(name, params, body, options) {
15038
15048
  name,
15039
15049
  params: paramIds,
15040
15050
  blocks,
15041
- meta: hasNoMemo ? { noMemo: true } : void 0
15051
+ meta: hasNoMemo ? { noMemo: true } : void 0,
15052
+ loc: options?.loc ?? null
15042
15053
  };
15043
15054
  }
15044
15055
  function convertStatementsToHIRFunction(name, statements) {
15045
- return convertFunction(name, [], statements);
15056
+ return convertFunction(name, [], statements, { loc: getLoc(statements[0]) });
15046
15057
  }
15047
15058
  function convertAssignmentValue(expr) {
15048
15059
  const right = convertExpression(expr.right);
@@ -15540,14 +15551,16 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15540
15551
  return bb;
15541
15552
  }
15542
15553
  function convertExpression(node) {
15543
- if (t.isIdentifier(node)) return { kind: "Identifier", name: node.name };
15554
+ const loc = getLoc(node);
15555
+ if (t.isIdentifier(node)) return { kind: "Identifier", name: node.name, loc };
15544
15556
  if (t.isStringLiteral(node) || t.isNumericLiteral(node) || t.isBooleanLiteral(node) || t.isNullLiteral(node))
15545
- return { kind: "Literal", value: node.value ?? null };
15557
+ return { kind: "Literal", value: node.value ?? null, loc };
15546
15558
  if (t.isCallExpression(node)) {
15547
15559
  const call = {
15548
15560
  kind: "CallExpression",
15549
15561
  callee: convertExpression(node.callee),
15550
- arguments: node.arguments.map((arg) => t.isExpression(arg) ? convertExpression(arg) : void 0).filter(Boolean)
15562
+ arguments: node.arguments.map((arg) => t.isExpression(arg) ? convertExpression(arg) : void 0).filter(Boolean),
15563
+ loc
15551
15564
  };
15552
15565
  return call;
15553
15566
  }
@@ -15562,7 +15575,8 @@ function convertExpression(node) {
15562
15575
  object,
15563
15576
  property,
15564
15577
  computed: node.computed,
15565
- optional: node.optional ?? true
15578
+ optional: node.optional ?? true,
15579
+ loc
15566
15580
  };
15567
15581
  return optionalMember;
15568
15582
  }
@@ -15571,7 +15585,8 @@ function convertExpression(node) {
15571
15585
  object,
15572
15586
  property,
15573
15587
  computed: node.computed,
15574
- optional: false
15588
+ optional: false,
15589
+ loc
15575
15590
  };
15576
15591
  return member;
15577
15592
  }
@@ -15580,7 +15595,8 @@ function convertExpression(node) {
15580
15595
  kind: "BinaryExpression",
15581
15596
  operator: node.operator,
15582
15597
  left: convertExpression(node.left),
15583
- right: convertExpression(node.right)
15598
+ right: convertExpression(node.right),
15599
+ loc
15584
15600
  };
15585
15601
  return bin;
15586
15602
  }
@@ -15589,7 +15605,8 @@ function convertExpression(node) {
15589
15605
  kind: "UnaryExpression",
15590
15606
  operator: node.operator,
15591
15607
  argument: convertExpression(node.argument),
15592
- prefix: node.prefix
15608
+ prefix: node.prefix,
15609
+ loc
15593
15610
  };
15594
15611
  return un;
15595
15612
  }
@@ -15598,7 +15615,8 @@ function convertExpression(node) {
15598
15615
  kind: "LogicalExpression",
15599
15616
  operator: node.operator,
15600
15617
  left: convertExpression(node.left),
15601
- right: convertExpression(node.right)
15618
+ right: convertExpression(node.right),
15619
+ loc
15602
15620
  };
15603
15621
  return log;
15604
15622
  }
@@ -15607,7 +15625,8 @@ function convertExpression(node) {
15607
15625
  kind: "ConditionalExpression",
15608
15626
  test: convertExpression(node.test),
15609
15627
  consequent: convertExpression(node.consequent),
15610
- alternate: convertExpression(node.alternate)
15628
+ alternate: convertExpression(node.alternate),
15629
+ loc
15611
15630
  };
15612
15631
  return cond;
15613
15632
  }
@@ -15619,12 +15638,14 @@ function convertExpression(node) {
15619
15638
  if (t.isSpreadElement(el)) {
15620
15639
  return {
15621
15640
  kind: "SpreadElement",
15622
- argument: convertExpression(el.argument)
15641
+ argument: convertExpression(el.argument),
15642
+ loc: getLoc(el)
15623
15643
  };
15624
15644
  }
15625
15645
  if (t.isExpression(el)) return convertExpression(el);
15626
15646
  return void 0;
15627
- }).filter(Boolean)
15647
+ }).filter(Boolean),
15648
+ loc
15628
15649
  };
15629
15650
  return arr;
15630
15651
  }
@@ -15635,7 +15656,8 @@ function convertExpression(node) {
15635
15656
  if (t.isSpreadElement(prop)) {
15636
15657
  return {
15637
15658
  kind: "SpreadElement",
15638
- argument: convertExpression(prop.argument)
15659
+ argument: convertExpression(prop.argument),
15660
+ loc: getLoc(prop)
15639
15661
  };
15640
15662
  }
15641
15663
  if (t.isObjectMethod(prop)) {
@@ -15652,7 +15674,8 @@ function convertExpression(node) {
15652
15674
  return {
15653
15675
  kind: "Property",
15654
15676
  key: keyExpr2,
15655
- value: convertExpression(fnExpr)
15677
+ value: convertExpression(fnExpr),
15678
+ loc: getLoc(prop)
15656
15679
  };
15657
15680
  }
15658
15681
  if (!t.isObjectProperty(prop) || prop.computed) return void 0;
@@ -15662,9 +15685,12 @@ function convertExpression(node) {
15662
15685
  return {
15663
15686
  kind: "Property",
15664
15687
  key: keyExpr,
15665
- value: convertExpression(prop.value)
15688
+ value: convertExpression(prop.value),
15689
+ shorthand: prop.shorthand && t.isIdentifier(prop.value),
15690
+ loc: getLoc(prop)
15666
15691
  };
15667
- }).filter(Boolean)
15692
+ }).filter(Boolean),
15693
+ loc
15668
15694
  };
15669
15695
  return obj;
15670
15696
  }
@@ -15677,38 +15703,42 @@ function convertExpression(node) {
15677
15703
  if (t.isJSXText(child)) {
15678
15704
  const text = child.value;
15679
15705
  if (text.trim()) {
15680
- children.push({ kind: "text", value: text });
15706
+ children.push({ kind: "text", value: text, loc: getLoc(child) });
15681
15707
  }
15682
15708
  } else if (t.isJSXExpressionContainer(child)) {
15683
15709
  if (!t.isJSXEmptyExpression(child.expression)) {
15684
15710
  children.push({
15685
15711
  kind: "expression",
15686
- value: convertExpression(child.expression)
15712
+ value: convertExpression(child.expression),
15713
+ loc: getLoc(child)
15687
15714
  });
15688
15715
  }
15689
15716
  } else if (t.isJSXElement(child)) {
15690
15717
  children.push({
15691
15718
  kind: "element",
15692
- value: convertJSXElement(child)
15719
+ value: convertJSXElement(child),
15720
+ loc: getLoc(child)
15693
15721
  });
15694
15722
  } else if (t.isJSXFragment(child)) {
15695
15723
  for (const fragChild of child.children) {
15696
15724
  if (t.isJSXText(fragChild)) {
15697
15725
  const text = fragChild.value;
15698
15726
  if (text.trim()) {
15699
- children.push({ kind: "text", value: text });
15727
+ children.push({ kind: "text", value: text, loc: getLoc(fragChild) });
15700
15728
  }
15701
15729
  } else if (t.isJSXExpressionContainer(fragChild)) {
15702
15730
  if (!t.isJSXEmptyExpression(fragChild.expression)) {
15703
15731
  children.push({
15704
15732
  kind: "expression",
15705
- value: convertExpression(fragChild.expression)
15733
+ value: convertExpression(fragChild.expression),
15734
+ loc: getLoc(fragChild)
15706
15735
  });
15707
15736
  }
15708
15737
  } else if (t.isJSXElement(fragChild)) {
15709
15738
  children.push({
15710
15739
  kind: "element",
15711
- value: convertJSXElement(fragChild)
15740
+ value: convertJSXElement(fragChild),
15741
+ loc: getLoc(fragChild)
15712
15742
  });
15713
15743
  }
15714
15744
  }
@@ -15716,24 +15746,27 @@ function convertExpression(node) {
15716
15746
  }
15717
15747
  return {
15718
15748
  kind: "JSXElement",
15719
- tagName: { kind: "Identifier", name: "Fragment" },
15749
+ tagName: { kind: "Identifier", name: "Fragment", loc: getLoc(node) },
15720
15750
  isComponent: true,
15721
15751
  attributes: [],
15722
- children
15752
+ children,
15753
+ loc: getLoc(node)
15723
15754
  };
15724
15755
  }
15725
15756
  if (t.isArrowFunctionExpression(node)) {
15726
15757
  if (t.isBlockStatement(node.body)) {
15727
15758
  const nested = convertFunction(void 0, node.params, node.body.body, {
15728
15759
  noMemo: hasNoMemoDirectiveInStatements(node.body.body),
15729
- directives: node.body.directives
15760
+ directives: node.body.directives,
15761
+ loc: getLoc(node)
15730
15762
  });
15731
15763
  const arrow = {
15732
15764
  kind: "ArrowFunction",
15733
15765
  params: nested.params,
15734
15766
  body: nested.blocks,
15735
15767
  isExpression: false,
15736
- isAsync: node.async
15768
+ isAsync: node.async,
15769
+ loc
15737
15770
  };
15738
15771
  return arrow;
15739
15772
  } else {
@@ -15744,7 +15777,8 @@ function convertExpression(node) {
15744
15777
  ).flat(),
15745
15778
  body: convertExpression(node.body),
15746
15779
  isExpression: true,
15747
- isAsync: node.async
15780
+ isAsync: node.async,
15781
+ loc
15748
15782
  };
15749
15783
  return arrow;
15750
15784
  }
@@ -15752,14 +15786,16 @@ function convertExpression(node) {
15752
15786
  if (t.isFunctionExpression(node)) {
15753
15787
  const nested = convertFunction(void 0, node.params, node.body.body, {
15754
15788
  noMemo: hasNoMemoDirectiveInStatements(node.body.body),
15755
- directives: node.body.directives
15789
+ directives: node.body.directives,
15790
+ loc: getLoc(node)
15756
15791
  });
15757
15792
  const fn = {
15758
15793
  kind: "FunctionExpression",
15759
15794
  name: node.id?.name ?? "",
15760
15795
  params: nested.params,
15761
15796
  body: nested.blocks,
15762
- isAsync: node.async
15797
+ isAsync: node.async,
15798
+ loc
15763
15799
  };
15764
15800
  return fn;
15765
15801
  }
@@ -15768,7 +15804,8 @@ function convertExpression(node) {
15768
15804
  kind: "AssignmentExpression",
15769
15805
  operator: node.operator,
15770
15806
  left: convertExpression(node.left),
15771
- right: convertExpression(node.right)
15807
+ right: convertExpression(node.right),
15808
+ loc
15772
15809
  };
15773
15810
  return assign;
15774
15811
  }
@@ -15777,7 +15814,8 @@ function convertExpression(node) {
15777
15814
  kind: "UpdateExpression",
15778
15815
  operator: node.operator,
15779
15816
  argument: convertExpression(node.argument),
15780
- prefix: node.prefix
15817
+ prefix: node.prefix,
15818
+ loc
15781
15819
  };
15782
15820
  return update;
15783
15821
  }
@@ -15785,34 +15823,39 @@ function convertExpression(node) {
15785
15823
  const template = {
15786
15824
  kind: "TemplateLiteral",
15787
15825
  quasis: node.quasis.map((q) => q.value.cooked ?? q.value.raw),
15788
- expressions: node.expressions.map((e) => convertExpression(e))
15826
+ expressions: node.expressions.map((e) => convertExpression(e)),
15827
+ loc
15789
15828
  };
15790
15829
  return template;
15791
15830
  }
15792
15831
  if (t.isAwaitExpression(node)) {
15793
15832
  return {
15794
15833
  kind: "AwaitExpression",
15795
- argument: convertExpression(node.argument)
15834
+ argument: convertExpression(node.argument),
15835
+ loc
15796
15836
  };
15797
15837
  }
15798
15838
  if (t.isNewExpression(node)) {
15799
15839
  return {
15800
15840
  kind: "NewExpression",
15801
15841
  callee: convertExpression(node.callee),
15802
- arguments: node.arguments.map((arg) => t.isExpression(arg) ? convertExpression(arg) : void 0).filter(Boolean)
15842
+ arguments: node.arguments.map((arg) => t.isExpression(arg) ? convertExpression(arg) : void 0).filter(Boolean),
15843
+ loc
15803
15844
  };
15804
15845
  }
15805
15846
  if (t.isSequenceExpression(node)) {
15806
15847
  return {
15807
15848
  kind: "SequenceExpression",
15808
- expressions: node.expressions.map((e) => convertExpression(e))
15849
+ expressions: node.expressions.map((e) => convertExpression(e)),
15850
+ loc
15809
15851
  };
15810
15852
  }
15811
15853
  if (t.isYieldExpression(node)) {
15812
15854
  return {
15813
15855
  kind: "YieldExpression",
15814
15856
  argument: node.argument ? convertExpression(node.argument) : null,
15815
- delegate: node.delegate
15857
+ delegate: node.delegate,
15858
+ loc
15816
15859
  };
15817
15860
  }
15818
15861
  if (t.isOptionalCallExpression(node)) {
@@ -15820,7 +15863,8 @@ function convertExpression(node) {
15820
15863
  kind: "OptionalCallExpression",
15821
15864
  callee: convertExpression(node.callee),
15822
15865
  arguments: node.arguments.map((arg) => t.isExpression(arg) ? convertExpression(arg) : void 0).filter(Boolean),
15823
- optional: node.optional
15866
+ optional: node.optional,
15867
+ loc
15824
15868
  };
15825
15869
  }
15826
15870
  if (t.isTaggedTemplateExpression(node)) {
@@ -15832,8 +15876,10 @@ function convertExpression(node) {
15832
15876
  quasis: node.quasi.quasis.map((q) => q.value.cooked ?? q.value.raw),
15833
15877
  expressions: node.quasi.expressions.map(
15834
15878
  (e) => convertExpression(e)
15835
- )
15836
- }
15879
+ ),
15880
+ loc: getLoc(node.quasi)
15881
+ },
15882
+ loc
15837
15883
  };
15838
15884
  }
15839
15885
  if (t.isClassExpression(node)) {
@@ -15841,17 +15887,18 @@ function convertExpression(node) {
15841
15887
  kind: "ClassExpression",
15842
15888
  name: node.id?.name,
15843
15889
  superClass: node.superClass ? convertExpression(node.superClass) : void 0,
15844
- body: node.body.body
15890
+ body: node.body.body,
15845
15891
  // Store as Babel AST for now
15892
+ loc
15846
15893
  };
15847
15894
  }
15848
15895
  if (t.isThisExpression(node)) {
15849
- return { kind: "ThisExpression" };
15896
+ return { kind: "ThisExpression", loc };
15850
15897
  }
15851
15898
  if (t.isSuper(node)) {
15852
- return { kind: "SuperExpression" };
15899
+ return { kind: "SuperExpression", loc };
15853
15900
  }
15854
- const fallback = { kind: "Literal", value: void 0 };
15901
+ const fallback = { kind: "Literal", value: void 0, loc };
15855
15902
  return fallback;
15856
15903
  }
15857
15904
  function convertJSXElement(node) {
@@ -15862,7 +15909,7 @@ function convertJSXElement(node) {
15862
15909
  const name = opening.name.name;
15863
15910
  const firstChar = name[0];
15864
15911
  if (firstChar && firstChar === firstChar.toUpperCase()) {
15865
- tagName = { kind: "Identifier", name };
15912
+ tagName = { kind: "Identifier", name, loc: getLoc(opening.name) };
15866
15913
  isComponent = true;
15867
15914
  } else {
15868
15915
  tagName = name;
@@ -15880,20 +15927,22 @@ function convertJSXElement(node) {
15880
15927
  name: "",
15881
15928
  value: null,
15882
15929
  isSpread: true,
15883
- spreadExpr: convertExpression(attr.argument)
15930
+ spreadExpr: convertExpression(attr.argument),
15931
+ loc: getLoc(attr)
15884
15932
  });
15885
15933
  } else if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
15886
15934
  let value = null;
15887
15935
  if (attr.value) {
15888
15936
  if (t.isStringLiteral(attr.value)) {
15889
- value = { kind: "Literal", value: attr.value.value };
15937
+ value = { kind: "Literal", value: attr.value.value, loc: getLoc(attr.value) };
15890
15938
  } else if (t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
15891
15939
  value = convertExpression(attr.value.expression);
15892
15940
  }
15893
15941
  }
15894
15942
  attributes.push({
15895
15943
  name: attr.name.name,
15896
- value
15944
+ value,
15945
+ loc: getLoc(attr)
15897
15946
  });
15898
15947
  }
15899
15948
  }
@@ -15902,38 +15951,42 @@ function convertJSXElement(node) {
15902
15951
  if (t.isJSXText(child)) {
15903
15952
  const text = child.value;
15904
15953
  if (text.trim()) {
15905
- children.push({ kind: "text", value: text });
15954
+ children.push({ kind: "text", value: text, loc: getLoc(child) });
15906
15955
  }
15907
15956
  } else if (t.isJSXExpressionContainer(child)) {
15908
15957
  if (!t.isJSXEmptyExpression(child.expression)) {
15909
15958
  children.push({
15910
15959
  kind: "expression",
15911
- value: convertExpression(child.expression)
15960
+ value: convertExpression(child.expression),
15961
+ loc: getLoc(child)
15912
15962
  });
15913
15963
  }
15914
15964
  } else if (t.isJSXElement(child)) {
15915
15965
  children.push({
15916
15966
  kind: "element",
15917
- value: convertJSXElement(child)
15967
+ value: convertJSXElement(child),
15968
+ loc: getLoc(child)
15918
15969
  });
15919
15970
  } else if (t.isJSXFragment(child)) {
15920
15971
  for (const fragChild of child.children) {
15921
15972
  if (t.isJSXText(fragChild)) {
15922
15973
  const text = fragChild.value;
15923
15974
  if (text.trim()) {
15924
- children.push({ kind: "text", value: text });
15975
+ children.push({ kind: "text", value: text, loc: getLoc(fragChild) });
15925
15976
  }
15926
15977
  } else if (t.isJSXExpressionContainer(fragChild)) {
15927
15978
  if (!t.isJSXEmptyExpression(fragChild.expression)) {
15928
15979
  children.push({
15929
15980
  kind: "expression",
15930
- value: convertExpression(fragChild.expression)
15981
+ value: convertExpression(fragChild.expression),
15982
+ loc: getLoc(fragChild)
15931
15983
  });
15932
15984
  }
15933
15985
  } else if (t.isJSXElement(fragChild)) {
15934
15986
  children.push({
15935
15987
  kind: "element",
15936
- value: convertJSXElement(fragChild)
15988
+ value: convertJSXElement(fragChild),
15989
+ loc: getLoc(fragChild)
15937
15990
  });
15938
15991
  }
15939
15992
  }
@@ -15944,21 +15997,27 @@ function convertJSXElement(node) {
15944
15997
  tagName,
15945
15998
  isComponent,
15946
15999
  attributes,
15947
- children
16000
+ children,
16001
+ loc: getLoc(node)
15948
16002
  };
15949
16003
  }
15950
16004
  function convertJSXMemberExpr(node) {
15951
16005
  let object;
15952
16006
  if (t.isJSXIdentifier(node.object)) {
15953
- object = { kind: "Identifier", name: node.object.name };
16007
+ object = { kind: "Identifier", name: node.object.name, loc: getLoc(node.object) };
15954
16008
  } else {
15955
16009
  object = convertJSXMemberExpr(node.object);
15956
16010
  }
15957
16011
  return {
15958
16012
  kind: "MemberExpression",
15959
16013
  object,
15960
- property: { kind: "Identifier", name: node.property.name },
15961
- computed: false
16014
+ property: {
16015
+ kind: "Identifier",
16016
+ name: node.property.name,
16017
+ loc: getLoc(node.property)
16018
+ },
16019
+ computed: false,
16020
+ loc: getLoc(node)
15962
16021
  };
15963
16022
  }
15964
16023
 
@@ -18916,11 +18975,17 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18916
18975
  if (uniqueOutputNames.length === 0) {
18917
18976
  ctx.helpersUsed.add("useEffect");
18918
18977
  ctx.needsCtx = true;
18919
- const effectCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
18978
+ const effectCallArgs = [
18920
18979
  t2.identifier("__fictCtx"),
18921
- t2.arrowFunctionExpression([], t2.blockStatement(bodyStatements)),
18922
- t2.numericLiteral(reserveHookSlot(ctx))
18923
- ]);
18980
+ t2.arrowFunctionExpression([], t2.blockStatement(bodyStatements))
18981
+ ];
18982
+ {
18983
+ const slot = reserveHookSlot(ctx);
18984
+ if (slot >= 0) {
18985
+ effectCallArgs.push(t2.numericLiteral(slot));
18986
+ }
18987
+ }
18988
+ const effectCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), effectCallArgs);
18924
18989
  statements.push(t2.expressionStatement(effectCall));
18925
18990
  } else {
18926
18991
  ctx.helpersUsed.add("useMemo");
@@ -18953,11 +19018,15 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18953
19018
  };
18954
19019
  const returnObj = t2.objectExpression(uniqueOutputNames.map((name) => buildOutputProperty(name)));
18955
19020
  const memoBody = t2.blockStatement([...bodyStatements, t2.returnStatement(returnObj)]);
18956
- const memoCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), [
19021
+ const slot = reserveHookSlot(ctx);
19022
+ const memoArgs = [
18957
19023
  t2.identifier("__fictCtx"),
18958
- t2.arrowFunctionExpression([], memoBody),
18959
- t2.numericLiteral(reserveHookSlot(ctx))
18960
- ]);
19024
+ t2.arrowFunctionExpression([], memoBody)
19025
+ ];
19026
+ if (slot >= 0) {
19027
+ memoArgs.push(t2.numericLiteral(slot));
19028
+ }
19029
+ const memoCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), memoArgs);
18961
19030
  const regionVarName = `__region_${region.id}`;
18962
19031
  statements.push(
18963
19032
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
@@ -19014,7 +19083,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
19014
19083
  t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
19015
19084
  t2.identifier("__fictCtx"),
19016
19085
  t2.arrowFunctionExpression([], effectBody),
19017
- t2.numericLiteral(reserveHookSlot(ctx))
19086
+ (() => {
19087
+ const slot2 = reserveHookSlot(ctx);
19088
+ return slot2 >= 0 ? t2.numericLiteral(slot2) : t2.identifier("undefined");
19089
+ })()
19018
19090
  ])
19019
19091
  )
19020
19092
  );
@@ -19232,11 +19304,15 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
19232
19304
  ctx.helpersUsed.add("useMemo");
19233
19305
  ctx.needsCtx = true;
19234
19306
  const regionVarName = `__region_${region.id}`;
19235
- const memoCall = t2.callExpression(t2.identifier("__fictUseMemo"), [
19307
+ const slotForMemo = reserveHookSlot(ctx);
19308
+ const memoArgs = [
19236
19309
  t2.identifier("__fictCtx"),
19237
- t2.arrowFunctionExpression([], t2.blockStatement(memoBody)),
19238
- t2.numericLiteral(reserveHookSlot(ctx))
19239
- ]);
19310
+ t2.arrowFunctionExpression([], t2.blockStatement(memoBody))
19311
+ ];
19312
+ if (slotForMemo >= 0) {
19313
+ memoArgs.push(t2.numericLiteral(slotForMemo));
19314
+ }
19315
+ const memoCall = t2.callExpression(t2.identifier("__fictUseMemo"), memoArgs);
19240
19316
  statements.push(
19241
19317
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
19242
19318
  );
@@ -19258,6 +19334,9 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
19258
19334
  return statements;
19259
19335
  }
19260
19336
  function reserveHookSlot(ctx) {
19337
+ if (ctx.dynamicHookSlotDepth && ctx.dynamicHookSlotDepth > 0) {
19338
+ return -1;
19339
+ }
19261
19340
  const slot = ctx.nextHookSlot ?? 0;
19262
19341
  ctx.nextHookSlot = slot + 1;
19263
19342
  return slot;
@@ -19328,7 +19407,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19328
19407
  t2.arrowFunctionExpression([], expr)
19329
19408
  ];
19330
19409
  if (inRegionMemo) {
19331
- args.push(t2.numericLiteral(reserveHookSlot(ctx)));
19410
+ const slot = reserveHookSlot(ctx);
19411
+ if (slot >= 0) {
19412
+ args.push(t2.numericLiteral(slot));
19413
+ }
19332
19414
  }
19333
19415
  ctx.helpersUsed.add("useMemo");
19334
19416
  ctx.needsCtx = true;
@@ -19843,6 +19925,17 @@ function deSSAJSXChild(child, t2) {
19843
19925
  // src/ir/codegen.ts
19844
19926
  var HOOK_SLOT_BASE = 1e3;
19845
19927
  var HOOK_NAME_PREFIX = "use";
19928
+ var cloneLoc = (loc) => loc === void 0 ? void 0 : loc === null ? null : {
19929
+ start: { ...loc.start },
19930
+ end: { ...loc.end },
19931
+ filename: loc.filename,
19932
+ identifierName: loc.identifierName
19933
+ };
19934
+ function setNodeLoc(node, loc) {
19935
+ if (loc === void 0) return node;
19936
+ node.loc = cloneLoc(loc) ?? null;
19937
+ return node;
19938
+ }
19846
19939
  function isHookName(name) {
19847
19940
  return !!name && name.startsWith(HOOK_NAME_PREFIX);
19848
19941
  }
@@ -19876,13 +19969,22 @@ function applyRegionToContext(ctx, region) {
19876
19969
  return prevRegion;
19877
19970
  }
19878
19971
  function reserveHookSlot2(ctx) {
19972
+ if (ctx.dynamicHookSlotDepth && ctx.dynamicHookSlotDepth > 0) {
19973
+ return -1;
19974
+ }
19879
19975
  const slot = ctx.nextHookSlot ?? HOOK_SLOT_BASE;
19880
19976
  ctx.nextHookSlot = slot + 1;
19881
19977
  return slot;
19882
19978
  }
19883
19979
  function expressionContainsJSX(expr) {
19980
+ if (Array.isArray(expr)) {
19981
+ return expr.some((item) => expressionContainsJSX(item));
19982
+ }
19884
19983
  if (!expr || typeof expr !== "object") return false;
19885
19984
  if (expr.kind === "JSXElement") return true;
19985
+ if (Array.isArray(expr.instructions)) {
19986
+ return expr.instructions.some((i) => expressionContainsJSX(i?.value ?? i));
19987
+ }
19886
19988
  switch (expr.kind) {
19887
19989
  case "CallExpression":
19888
19990
  if (expressionContainsJSX(expr.callee)) return true;
@@ -19895,10 +19997,29 @@ function expressionContainsJSX(expr) {
19895
19997
  return expressionContainsJSX(expr.test) || expressionContainsJSX(expr.consequent) || expressionContainsJSX(expr.alternate);
19896
19998
  case "ArrowFunction":
19897
19999
  return expressionContainsJSX(expr.body);
20000
+ case "FunctionExpression":
20001
+ if (Array.isArray(expr.body)) {
20002
+ return expr.body.some(
20003
+ (block) => block.instructions?.some((i) => expressionContainsJSX(i.value))
20004
+ );
20005
+ }
20006
+ return false;
19898
20007
  default:
19899
20008
  return false;
19900
20009
  }
19901
20010
  }
20011
+ function withNoMemoAndDynamicHooks(ctx, fn) {
20012
+ const prevNoMemo = ctx.noMemo;
20013
+ const prevDynamic = ctx.dynamicHookSlotDepth ?? 0;
20014
+ ctx.noMemo = true;
20015
+ ctx.dynamicHookSlotDepth = prevDynamic + 1;
20016
+ try {
20017
+ return fn();
20018
+ } finally {
20019
+ ctx.noMemo = prevNoMemo;
20020
+ ctx.dynamicHookSlotDepth = prevDynamic;
20021
+ }
20022
+ }
19902
20023
  function functionContainsJSX(fn) {
19903
20024
  for (const block of fn.blocks) {
19904
20025
  for (const instr of block.instructions) {
@@ -20997,18 +21118,25 @@ function lowerTrackedExpression(expr, ctx) {
20997
21118
  }
20998
21119
  function lowerInstruction(instr, ctx) {
20999
21120
  const { t: t2 } = ctx;
21121
+ const applyLoc = (stmt) => {
21122
+ if (!stmt) return stmt;
21123
+ const baseLoc = instr.loc ?? (instr.kind === "Assign" || instr.kind === "Expression" ? instr.value.loc : void 0);
21124
+ return setNodeLoc(stmt, baseLoc);
21125
+ };
21000
21126
  if (instr.kind === "Assign") {
21001
21127
  const baseName2 = deSSAVarName(instr.target.name);
21002
21128
  const isFunctionDecl = instr.value.kind === "FunctionExpression" && (instr.declarationKind === "function" || !instr.declarationKind && instr.value.name === baseName2);
21003
21129
  if (isFunctionDecl) {
21004
21130
  const loweredFn = lowerExpression(instr.value, ctx);
21005
21131
  if (t2.isFunctionExpression(loweredFn)) {
21006
- return t2.functionDeclaration(
21007
- t2.identifier(baseName2),
21008
- loweredFn.params,
21009
- loweredFn.body,
21010
- loweredFn.generator ?? false,
21011
- loweredFn.async ?? false
21132
+ return applyLoc(
21133
+ t2.functionDeclaration(
21134
+ t2.identifier(baseName2),
21135
+ loweredFn.params,
21136
+ loweredFn.body,
21137
+ loweredFn.generator ?? false,
21138
+ loweredFn.async ?? false
21139
+ )
21012
21140
  );
21013
21141
  }
21014
21142
  }
@@ -21023,12 +21151,16 @@ function lowerInstruction(instr, ctx) {
21023
21151
  ctx.memoVars?.add(baseName2);
21024
21152
  }
21025
21153
  if (declKind) {
21026
- return t2.variableDeclaration(declKind, [
21027
- t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
21028
- ]);
21154
+ return applyLoc(
21155
+ t2.variableDeclaration(declKind, [
21156
+ t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
21157
+ ])
21158
+ );
21029
21159
  }
21030
- return t2.expressionStatement(
21031
- t2.assignmentExpression("=", t2.identifier(baseName2), hookMember.member)
21160
+ return applyLoc(
21161
+ t2.expressionStatement(
21162
+ t2.assignmentExpression("=", t2.identifier(baseName2), hookMember.member)
21163
+ )
21032
21164
  );
21033
21165
  }
21034
21166
  if (instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && isHookName(instr.value.callee.name)) {
@@ -21042,16 +21174,24 @@ function lowerInstruction(instr, ctx) {
21042
21174
  }
21043
21175
  }
21044
21176
  if (ctx.signalVars?.has(baseName2)) {
21045
- return t2.expressionStatement(
21046
- t2.callExpression(t2.identifier(baseName2), [lowerTrackedExpression(instr.value, ctx)])
21177
+ return applyLoc(
21178
+ t2.expressionStatement(
21179
+ t2.callExpression(t2.identifier(baseName2), [lowerTrackedExpression(instr.value, ctx)])
21180
+ )
21047
21181
  );
21048
21182
  }
21049
- return t2.expressionStatement(
21050
- t2.assignmentExpression("=", t2.identifier(baseName2), lowerTrackedExpression(instr.value, ctx))
21183
+ return applyLoc(
21184
+ t2.expressionStatement(
21185
+ t2.assignmentExpression(
21186
+ "=",
21187
+ t2.identifier(baseName2),
21188
+ lowerTrackedExpression(instr.value, ctx)
21189
+ )
21190
+ )
21051
21191
  );
21052
21192
  }
21053
21193
  if (instr.kind === "Expression") {
21054
- return t2.expressionStatement(lowerTrackedExpression(instr.value, ctx));
21194
+ return applyLoc(t2.expressionStatement(lowerTrackedExpression(instr.value, ctx)));
21055
21195
  }
21056
21196
  if (instr.kind === "Phi") {
21057
21197
  return null;
@@ -21060,6 +21200,9 @@ function lowerInstruction(instr, ctx) {
21060
21200
  }
21061
21201
  function lowerTerminator(block, ctx) {
21062
21202
  const { t: t2 } = ctx;
21203
+ const baseLoc = block.terminator.loc ?? // eslint-disable-next-line @typescript-eslint/no-explicit-any
21204
+ block.terminator.argument?.loc;
21205
+ const applyLoc = (stmts) => stmts.map((stmt) => setNodeLoc(stmt, baseLoc));
21063
21206
  switch (block.terminator.kind) {
21064
21207
  case "Return": {
21065
21208
  const prevRegion = ctx.currentRegion;
@@ -21072,14 +21215,14 @@ function lowerTerminator(block, ctx) {
21072
21215
  }
21073
21216
  ctx.inReturn = false;
21074
21217
  ctx.currentRegion = prevRegion;
21075
- return [t2.returnStatement(retExpr)];
21218
+ return applyLoc([t2.returnStatement(retExpr)]);
21076
21219
  }
21077
21220
  case "Throw":
21078
- return [t2.throwStatement(lowerTrackedExpression(block.terminator.argument, ctx))];
21221
+ return applyLoc([t2.throwStatement(lowerTrackedExpression(block.terminator.argument, ctx))]);
21079
21222
  case "Jump":
21080
- return [t2.expressionStatement(t2.stringLiteral(`jump ${block.terminator.target}`))];
21223
+ return applyLoc([t2.expressionStatement(t2.stringLiteral(`jump ${block.terminator.target}`))]);
21081
21224
  case "Branch":
21082
- return [
21225
+ return applyLoc([
21083
21226
  t2.ifStatement(
21084
21227
  lowerTrackedExpression(block.terminator.test, ctx),
21085
21228
  t2.blockStatement([
@@ -21089,9 +21232,9 @@ function lowerTerminator(block, ctx) {
21089
21232
  t2.expressionStatement(t2.stringLiteral(`goto ${block.terminator.alternate}`))
21090
21233
  ])
21091
21234
  )
21092
- ];
21235
+ ]);
21093
21236
  case "Switch":
21094
- return [
21237
+ return applyLoc([
21095
21238
  t2.switchStatement(
21096
21239
  lowerTrackedExpression(block.terminator.discriminant, ctx),
21097
21240
  block.terminator.cases.map(
@@ -21100,30 +21243,30 @@ function lowerTerminator(block, ctx) {
21100
21243
  ])
21101
21244
  )
21102
21245
  )
21103
- ];
21246
+ ]);
21104
21247
  case "ForOf": {
21105
21248
  const term = block.terminator;
21106
21249
  const varKind = term.variableKind ?? "const";
21107
21250
  const leftPattern = term.pattern ? term.pattern : t2.identifier(term.variable);
21108
- return [
21251
+ return applyLoc([
21109
21252
  t2.forOfStatement(
21110
21253
  t2.variableDeclaration(varKind, [t2.variableDeclarator(leftPattern)]),
21111
21254
  lowerExpression(term.iterable, ctx),
21112
21255
  t2.blockStatement([t2.expressionStatement(t2.stringLiteral(`body ${term.body}`))])
21113
21256
  )
21114
- ];
21257
+ ]);
21115
21258
  }
21116
21259
  case "ForIn": {
21117
21260
  const term = block.terminator;
21118
21261
  const varKind = term.variableKind ?? "const";
21119
21262
  const leftPattern = term.pattern ? term.pattern : t2.identifier(term.variable);
21120
- return [
21263
+ return applyLoc([
21121
21264
  t2.forInStatement(
21122
21265
  t2.variableDeclaration(varKind, [t2.variableDeclarator(leftPattern)]),
21123
21266
  lowerExpression(term.object, ctx),
21124
21267
  t2.blockStatement([t2.expressionStatement(t2.stringLiteral(`body ${term.body}`))])
21125
21268
  )
21126
- ];
21269
+ ]);
21127
21270
  }
21128
21271
  case "Try": {
21129
21272
  const term = block.terminator;
@@ -21139,20 +21282,20 @@ function lowerTerminator(block, ctx) {
21139
21282
  const finallyBlock = term.finallyBlock !== void 0 ? t2.blockStatement([
21140
21283
  t2.expressionStatement(t2.stringLiteral(`finally ${term.finallyBlock}`))
21141
21284
  ]) : null;
21142
- return [t2.tryStatement(tryBlock, catchClause, finallyBlock)];
21285
+ return applyLoc([t2.tryStatement(tryBlock, catchClause, finallyBlock)]);
21143
21286
  }
21144
21287
  case "Unreachable":
21145
- return [];
21288
+ return applyLoc([]);
21146
21289
  case "Break":
21147
- return [
21290
+ return applyLoc([
21148
21291
  t2.breakStatement(block.terminator.label ? t2.identifier(block.terminator.label) : null)
21149
- ];
21292
+ ]);
21150
21293
  case "Continue":
21151
- return [
21294
+ return applyLoc([
21152
21295
  t2.continueStatement(block.terminator.label ? t2.identifier(block.terminator.label) : null)
21153
- ];
21296
+ ]);
21154
21297
  default:
21155
- return [];
21298
+ return applyLoc([]);
21156
21299
  }
21157
21300
  }
21158
21301
  function attachHelperImports(ctx, body, t2) {
@@ -21291,7 +21434,7 @@ function lowerExpression(expr, ctx, isAssigned = false) {
21291
21434
  }
21292
21435
  ctx.expressionDepth = depth;
21293
21436
  try {
21294
- return lowerExpressionImpl(expr, ctx, isAssigned);
21437
+ return setNodeLoc(lowerExpressionImpl(expr, ctx, isAssigned), expr.loc);
21295
21438
  } finally {
21296
21439
  ctx.expressionDepth = depth - 1;
21297
21440
  }
@@ -21426,14 +21569,22 @@ function lowerExpressionImpl(expr, ctx, isAssigned = false) {
21426
21569
  const calleeIsMemoAccessor = !!calleeName && ctx.memoVars?.has(calleeName);
21427
21570
  const calleeIsSignalLike = !!calleeName && (ctx.signalVars?.has(calleeName) || ctx.storeVars?.has(calleeName));
21428
21571
  if (calleeIsMemoAccessor && !calleeIsSignalLike && expr.arguments.length > 0) {
21429
- const loweredArgs = expr.arguments.map((a) => lowerExpression(a, ctx));
21430
- return t2.callExpression(t2.callExpression(t2.identifier(calleeName), []), loweredArgs);
21572
+ const loweredArgs2 = expr.arguments.map((a) => lowerExpression(a, ctx));
21573
+ return t2.callExpression(t2.callExpression(t2.identifier(calleeName), []), loweredArgs2);
21431
21574
  }
21432
21575
  const lowerCallee = () => isIIFE ? withNonReactiveScope(ctx, () => lowerExpression(expr.callee, ctx)) : lowerExpression(expr.callee, ctx);
21433
- return t2.callExpression(
21434
- lowerCallee(),
21435
- expr.arguments.map((a) => lowerExpression(a, ctx))
21436
- );
21576
+ const isIteratingMethod = expr.callee.kind === "MemberExpression" && (expr.callee.property.kind === "Identifier" && ["map", "reduce", "forEach", "filter", "flatMap", "some", "every", "find"].includes(
21577
+ expr.callee.property.name
21578
+ ) || expr.callee.property.kind === "Literal" && ["map", "reduce", "forEach", "filter", "flatMap", "some", "every", "find"].includes(
21579
+ String(expr.callee.property.value)
21580
+ ));
21581
+ const loweredArgs = expr.arguments.map((a, idx) => {
21582
+ if (idx === 0 && isIteratingMethod && (a.kind === "ArrowFunction" || a.kind === "FunctionExpression")) {
21583
+ return withNoMemoAndDynamicHooks(ctx, () => lowerExpression(a, ctx));
21584
+ }
21585
+ return lowerExpression(a, ctx);
21586
+ });
21587
+ return t2.callExpression(lowerCallee(), loweredArgs);
21437
21588
  }
21438
21589
  case "MemberExpression":
21439
21590
  if (matchesListKeyPattern(expr, ctx)) {
@@ -22290,15 +22441,6 @@ function isStaticValue(expr) {
22290
22441
  if (!expr) return false;
22291
22442
  return expr.kind === "Literal";
22292
22443
  }
22293
- function isComponentLikeCallee(expr) {
22294
- if (expr.kind === "Identifier") {
22295
- return expr.name[0] === expr.name[0]?.toUpperCase();
22296
- }
22297
- if (expr.kind === "MemberExpression" || expr.kind === "OptionalMemberExpression") {
22298
- return isComponentLikeCallee(expr.object);
22299
- }
22300
- return false;
22301
- }
22302
22444
  function isLikelyTextExpression(expr, ctx) {
22303
22445
  let ok = true;
22304
22446
  const isReactiveIdentifier = (name) => {
@@ -22327,15 +22469,9 @@ function isLikelyTextExpression(expr, ctx) {
22327
22469
  ok = false;
22328
22470
  return;
22329
22471
  case "CallExpression":
22330
- case "OptionalCallExpression": {
22331
- if (isComponentLikeCallee(node.callee)) {
22332
- ok = false;
22333
- return;
22334
- }
22335
- visit(node.callee, true);
22336
- node.arguments.forEach((arg) => visit(arg));
22472
+ case "OptionalCallExpression":
22473
+ ok = false;
22337
22474
  return;
22338
- }
22339
22475
  case "MemberExpression":
22340
22476
  case "OptionalMemberExpression":
22341
22477
  visit(node.object, true);
@@ -22790,7 +22926,12 @@ function lowerIntrinsicElement(jsx, ctx) {
22790
22926
  t2.arrowFunctionExpression([], body)
22791
22927
  ];
22792
22928
  if (ctx.isComponentFn) {
22793
- memoArgs.push(t2.numericLiteral(reserveHookSlot2(ctx)));
22929
+ {
22930
+ const slot = reserveHookSlot2(ctx);
22931
+ if (slot >= 0) {
22932
+ memoArgs.push(t2.numericLiteral(slot));
22933
+ }
22934
+ }
22794
22935
  }
22795
22936
  return t2.callExpression(t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), memoArgs), []);
22796
22937
  }
@@ -23345,12 +23486,16 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
23345
23486
  const hoistedStatements = ctx.hoistedTemplateStatements;
23346
23487
  ctx.hoistedTemplates = prevHoistedTemplates;
23347
23488
  ctx.hoistedTemplateStatements = prevHoistedTemplateStatements;
23348
- if (isKeyed && (t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr))) {
23349
- const firstParam = callbackExpr.params[0];
23489
+ if (t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr)) {
23490
+ const [firstParam, secondParam] = callbackExpr.params;
23491
+ const overrides = {};
23350
23492
  if (t2.isIdentifier(firstParam)) {
23351
- const overrides = {
23352
- [firstParam.name]: () => t2.callExpression(t2.identifier(firstParam.name), [])
23353
- };
23493
+ overrides[firstParam.name] = () => t2.callExpression(t2.identifier(firstParam.name), []);
23494
+ }
23495
+ if (t2.isIdentifier(secondParam)) {
23496
+ overrides[secondParam.name] = () => t2.callExpression(t2.identifier(secondParam.name), []);
23497
+ }
23498
+ if (Object.keys(overrides).length > 0) {
23354
23499
  if (t2.isBlockStatement(callbackExpr.body)) {
23355
23500
  replaceIdentifiersWithOverrides(callbackExpr.body, overrides, t2, callbackExpr.type, "body");
23356
23501
  } else {
@@ -24244,10 +24389,9 @@ function lowerFunctionWithRegions(fn, ctx) {
24244
24389
  }
24245
24390
  }
24246
24391
  const params = finalParams;
24247
- const funcDecl = t2.functionDeclaration(
24248
- t2.identifier(fn.name ?? "fn"),
24249
- params,
24250
- t2.blockStatement(statements)
24392
+ const funcDecl = setNodeLoc(
24393
+ t2.functionDeclaration(t2.identifier(fn.name ?? "fn"), params, t2.blockStatement(statements)),
24394
+ fn.loc
24251
24395
  );
24252
24396
  ctx.needsCtx = prevNeedsCtx;
24253
24397
  ctx.shadowedNames = prevShadowed;
@@ -24336,10 +24480,44 @@ function isInsideNestedFunction(path) {
24336
24480
  function isInsideJSX(path) {
24337
24481
  return !!path.findParent((p) => p.isJSXElement?.() || p.isJSXFragment?.());
24338
24482
  }
24339
- function emitWarning(node, code, message, options, fileName) {
24340
- if (!options.onWarn) return;
24483
+ function parseSuppressionCodes(raw) {
24484
+ if (!raw) return void 0;
24485
+ const codes = raw.split(/[,\s]+/).map((c) => c.trim()).filter(Boolean);
24486
+ return codes.length > 0 ? new Set(codes) : void 0;
24487
+ }
24488
+ function parseSuppressions(comments) {
24489
+ if (!comments) return [];
24490
+ const suppressions = [];
24491
+ for (const comment of comments) {
24492
+ const match = comment.value.match(/fict-ignore(-next-line)?(?:\s+(.+))?/i);
24493
+ if (!match || !comment.loc) continue;
24494
+ suppressions.push({
24495
+ line: comment.loc.start.line,
24496
+ nextLine: !!match[1],
24497
+ codes: parseSuppressionCodes(match[2])
24498
+ });
24499
+ }
24500
+ return suppressions;
24501
+ }
24502
+ function shouldSuppressWarning(suppressions, code, line) {
24503
+ return suppressions.some((entry) => {
24504
+ const targetLine = entry.nextLine ? entry.line + 1 : entry.line;
24505
+ if (targetLine !== line) return false;
24506
+ if (!entry.codes || entry.codes.size === 0) return true;
24507
+ return entry.codes.has(code);
24508
+ });
24509
+ }
24510
+ function createWarningDispatcher(onWarn, suppressions) {
24511
+ if (!onWarn) return () => {
24512
+ };
24513
+ return (warning) => {
24514
+ if (shouldSuppressWarning(suppressions, warning.code, warning.line)) return;
24515
+ onWarn(warning);
24516
+ };
24517
+ }
24518
+ function emitWarning(node, code, message, warn, fileName) {
24341
24519
  const loc = node.loc?.start;
24342
- options.onWarn({
24520
+ warn({
24343
24521
  code,
24344
24522
  message,
24345
24523
  fileName,
@@ -24427,8 +24605,7 @@ function isDynamicPropertyAccess(node, t2) {
24427
24605
  if (!node.computed) return false;
24428
24606
  return !(t2.isStringLiteral(node.property) || t2.isNumericLiteral(node.property));
24429
24607
  }
24430
- function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24431
- const fileName = programPath.hub?.file?.opts?.filename || "<unknown>";
24608
+ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t2) {
24432
24609
  const isStateRoot = (expr) => {
24433
24610
  const root = getRootIdentifier(expr, t2);
24434
24611
  return !!(root && stateVars.has(root.name));
@@ -24444,7 +24621,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24444
24621
  path.node,
24445
24622
  "FICT-M",
24446
24623
  "Direct mutation of nested property detected; use immutable update or $store helpers",
24447
- options,
24624
+ warn,
24448
24625
  fileName
24449
24626
  );
24450
24627
  if (isDynamicPropertyAccess(left, t2)) {
@@ -24452,7 +24629,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24452
24629
  path.node,
24453
24630
  "FICT-H",
24454
24631
  "Dynamic property access widens dependency tracking",
24455
- options,
24632
+ warn,
24456
24633
  fileName
24457
24634
  );
24458
24635
  }
@@ -24467,7 +24644,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24467
24644
  path.node,
24468
24645
  "FICT-M",
24469
24646
  "Direct mutation of nested property detected; use immutable update or $store helpers",
24470
- options,
24647
+ warn,
24471
24648
  fileName
24472
24649
  );
24473
24650
  if (isDynamicPropertyAccess(arg, t2)) {
@@ -24475,7 +24652,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24475
24652
  path.node,
24476
24653
  "FICT-H",
24477
24654
  "Dynamic property access widens dependency tracking",
24478
- options,
24655
+ warn,
24479
24656
  fileName
24480
24657
  );
24481
24658
  }
@@ -24491,7 +24668,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24491
24668
  path.node,
24492
24669
  "FICT-H",
24493
24670
  "Dynamic property access widens dependency tracking",
24494
- options,
24671
+ warn,
24495
24672
  fileName
24496
24673
  );
24497
24674
  }
@@ -24520,7 +24697,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24520
24697
  path.node,
24521
24698
  "FICT-R005",
24522
24699
  `Function captures reactive variable(s): ${Array.from(captured).join(", ")}. Pass them as parameters or memoize explicitly to avoid hidden dependencies.`,
24523
- options,
24700
+ warn,
24524
24701
  fileName
24525
24702
  );
24526
24703
  }
@@ -24551,7 +24728,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24551
24728
  path.node,
24552
24729
  "FICT-E001",
24553
24730
  "Effect has no reactive reads; it will run once. Consider removing $effect or adding dependencies.",
24554
- options,
24731
+ warn,
24555
24732
  fileName
24556
24733
  );
24557
24734
  }
@@ -24577,7 +24754,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24577
24754
  arg,
24578
24755
  "FICT-H",
24579
24756
  "State value passed to unknown function (black box); dependency tracking may be imprecise",
24580
- options,
24757
+ warn,
24581
24758
  fileName
24582
24759
  );
24583
24760
  break;
@@ -24593,7 +24770,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24593
24770
  path.node,
24594
24771
  "FICT-H",
24595
24772
  "Dynamic property access widens dependency tracking",
24596
- options,
24773
+ warn,
24597
24774
  fileName
24598
24775
  );
24599
24776
  }
@@ -24643,6 +24820,10 @@ function createHIREntrypointVisitor(t2, options) {
24643
24820
  Program: {
24644
24821
  exit(path) {
24645
24822
  const fileName = path.hub?.file?.opts?.filename || "<unknown>";
24823
+ const comments = path.hub?.file?.ast?.comments || [];
24824
+ const suppressions = parseSuppressions(comments);
24825
+ const warn = createWarningDispatcher(options.onWarn, suppressions);
24826
+ const optionsWithWarnings = { ...options, onWarn: warn };
24646
24827
  const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
24647
24828
  const getFunctionName = (fnPath) => {
24648
24829
  return fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
@@ -24661,14 +24842,75 @@ function createHIREntrypointVisitor(t2, options) {
24661
24842
  return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24662
24843
  };
24663
24844
  const memoHasSideEffects = (fn) => {
24845
+ const pureCalls = new Set(
24846
+ Array.from(SAFE_FUNCTIONS).filter(
24847
+ (name) => !name.startsWith("console.") && name !== "Math.random"
24848
+ )
24849
+ );
24850
+ const effectfulCalls = /* @__PURE__ */ new Set([
24851
+ "$effect",
24852
+ "render",
24853
+ "fetch",
24854
+ "setTimeout",
24855
+ "setInterval",
24856
+ "clearTimeout",
24857
+ "clearInterval",
24858
+ "requestAnimationFrame",
24859
+ "cancelAnimationFrame"
24860
+ ]);
24861
+ const getCalleeName = (callee) => {
24862
+ if (t2.isIdentifier(callee)) return callee.name;
24863
+ if (t2.isMemberExpression(callee) && !callee.computed && t2.isIdentifier(callee.property) && t2.isIdentifier(callee.object)) {
24864
+ return `${callee.object.name}.${callee.property.name}`;
24865
+ }
24866
+ return null;
24867
+ };
24868
+ const mutatingMemberProps = /* @__PURE__ */ new Set([
24869
+ "push",
24870
+ "pop",
24871
+ "splice",
24872
+ "shift",
24873
+ "unshift",
24874
+ "sort",
24875
+ "reverse",
24876
+ "set",
24877
+ "add",
24878
+ "delete",
24879
+ "append",
24880
+ "appendChild",
24881
+ "remove",
24882
+ "removeChild",
24883
+ "setAttribute",
24884
+ "dispatchEvent",
24885
+ "replaceChildren",
24886
+ "replaceWith"
24887
+ ]);
24888
+ const isEffectfulCall = (node) => {
24889
+ const name = getCalleeName(node.callee);
24890
+ if (!name) return true;
24891
+ if (pureCalls.has(name)) return false;
24892
+ if (effectfulCalls.has(name)) return true;
24893
+ if (name.startsWith("console.") || name.startsWith("document.") || name.startsWith("window.")) {
24894
+ return true;
24895
+ }
24896
+ if (t2.isMemberExpression(node.callee) && !node.callee.computed && t2.isIdentifier(node.callee.property)) {
24897
+ const prop = node.callee.property.name;
24898
+ if (mutatingMemberProps.has(prop)) return true;
24899
+ if (t2.isIdentifier(node.callee.object) && (node.callee.object.name === "document" || node.callee.object.name === "window")) {
24900
+ return true;
24901
+ }
24902
+ }
24903
+ return false;
24904
+ };
24664
24905
  const checkNode = (node) => {
24665
24906
  if (!node) return false;
24666
24907
  if (t2.isAssignmentExpression(node) || t2.isUpdateExpression(node) || t2.isThrowStatement(node) || t2.isNewExpression(node)) {
24667
24908
  return true;
24668
24909
  }
24669
- if (t2.isCallExpression(node) && (t2.isIdentifier(node.callee) && node.callee.name === "$effect" || t2.isIdentifier(node.callee) && node.callee.name === "render")) {
24910
+ if (t2.isCallExpression(node) && isEffectfulCall(node)) {
24670
24911
  return true;
24671
24912
  }
24913
+ if (t2.isAwaitExpression(node)) return true;
24672
24914
  if (t2.isExpressionStatement(node)) return checkNode(node.expression);
24673
24915
  if (t2.isBlockStatement(node)) return node.body.some((stmt) => checkNode(stmt));
24674
24916
  if (t2.isReturnStatement(node)) return checkNode(node.argument);
@@ -24692,7 +24934,7 @@ function createHIREntrypointVisitor(t2, options) {
24692
24934
  fnPath.node,
24693
24935
  "FICT-C004",
24694
24936
  "Component has no return statement and will render nothing.",
24695
- options,
24937
+ warn,
24696
24938
  fileName
24697
24939
  );
24698
24940
  },
@@ -24710,7 +24952,7 @@ function createHIREntrypointVisitor(t2, options) {
24710
24952
  init,
24711
24953
  "FICT-C004",
24712
24954
  "Component has no return statement and will render nothing.",
24713
- options,
24955
+ warn,
24714
24956
  fileName
24715
24957
  );
24716
24958
  }
@@ -24760,7 +25002,7 @@ function createHIREntrypointVisitor(t2, options) {
24760
25002
  }
24761
25003
  }
24762
25004
  if (hasKey || hasUnknownSpread) return;
24763
- options.onWarn?.({
25005
+ warn({
24764
25006
  code: "FICT-J002",
24765
25007
  message: "Missing key prop in list rendering.",
24766
25008
  fileName,
@@ -24831,7 +25073,7 @@ function createHIREntrypointVisitor(t2, options) {
24831
25073
  fnPath.node,
24832
25074
  "FICT-C003",
24833
25075
  "Components should not be defined inside other components. Move this definition to module scope to preserve identity and performance.",
24834
- options,
25076
+ warn,
24835
25077
  fileName
24836
25078
  );
24837
25079
  },
@@ -24876,7 +25118,7 @@ function createHIREntrypointVisitor(t2, options) {
24876
25118
  callPath.node,
24877
25119
  "FICT-R004",
24878
25120
  "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
24879
- options,
25121
+ warn,
24880
25122
  fileName
24881
25123
  );
24882
25124
  }
@@ -24911,7 +25153,7 @@ function createHIREntrypointVisitor(t2, options) {
24911
25153
  callPath.node.arguments.forEach((arg) => {
24912
25154
  if (t2.isIdentifier(arg) && stateVars.has(arg.name) && (!calleeId || !allowedStateCallees.has(calleeId))) {
24913
25155
  const loc = arg.loc?.start ?? callPath.node.loc?.start;
24914
- options.onWarn?.({
25156
+ warn({
24915
25157
  code: "FICT-S002",
24916
25158
  message: "State variable is passed as an argument; this passes a value snapshot and may escape component scope.",
24917
25159
  fileName,
@@ -24924,7 +25166,7 @@ function createHIREntrypointVisitor(t2, options) {
24924
25166
  const firstArg = callPath.node.arguments[0];
24925
25167
  if (firstArg && (t2.isArrowFunctionExpression(firstArg) || t2.isFunctionExpression(firstArg)) && memoHasSideEffects(firstArg)) {
24926
25168
  const loc = firstArg.loc?.start ?? callPath.node.loc?.start;
24927
- options.onWarn?.({
25169
+ warn({
24928
25170
  code: "FICT-M003",
24929
25171
  message: "Memo should not contain side effects.",
24930
25172
  fileName,
@@ -25029,10 +25271,10 @@ function createHIREntrypointVisitor(t2, options) {
25029
25271
  }
25030
25272
  });
25031
25273
  }
25032
- runWarningPass(path, stateVars, derivedVars, options, t2);
25274
+ runWarningPass(path, stateVars, derivedVars, warn, fileName, t2);
25033
25275
  const fileAst = t2.file(path.node);
25034
25276
  const hir = buildHIR(fileAst);
25035
- const lowered = lowerHIRWithRegions(hir, t2, options);
25277
+ const lowered = lowerHIRWithRegions(hir, t2, optionsWithWarnings);
25036
25278
  path.node.body = lowered.program.body;
25037
25279
  path.node.directives = lowered.program.directives;
25038
25280
  if (!process.env.FICT_SKIP_SCOPE_CRAWL) {