@fictjs/compiler 0.0.7 → 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 +640 -267
  2. package/dist/index.js +640 -267
  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
 
@@ -15979,6 +16038,75 @@ function debugEnabled(flag) {
15979
16038
  return parts.includes(normalized) || parts.includes("all");
15980
16039
  }
15981
16040
 
16041
+ // src/utils.ts
16042
+ function isStateCall(node, t2) {
16043
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
16044
+ }
16045
+ function isEffectCall(node, t2) {
16046
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
16047
+ }
16048
+ function getRootIdentifier(expr, t2) {
16049
+ if (t2.isIdentifier(expr)) {
16050
+ return expr;
16051
+ }
16052
+ if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
16053
+ return getRootIdentifier(expr.object, t2);
16054
+ }
16055
+ if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
16056
+ return getRootIdentifier(expr.object, t2);
16057
+ }
16058
+ if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
16059
+ return getRootIdentifier(expr.callee, t2);
16060
+ }
16061
+ if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
16062
+ return getRootIdentifier(expr.callee, t2);
16063
+ }
16064
+ if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
16065
+ return getRootIdentifier(expr.expression, t2);
16066
+ }
16067
+ if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
16068
+ return getRootIdentifier(expr.expression, t2);
16069
+ }
16070
+ return null;
16071
+ }
16072
+
16073
+ // src/fine-grained-dom.ts
16074
+ function normalizeDependencyKey(name) {
16075
+ return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
16076
+ }
16077
+ function applyRegionMetadata(state, options) {
16078
+ if (!options.region) return;
16079
+ const region = options.region;
16080
+ state.regionMetadata = region;
16081
+ if (region.dependencies.size > 0) {
16082
+ state.identifierOverrides = state.identifierOverrides ?? {};
16083
+ const dependencyGetter = options.dependencyGetter ?? null;
16084
+ if (!dependencyGetter) {
16085
+ return;
16086
+ }
16087
+ for (const dep of region.dependencies) {
16088
+ const key = normalizeDependencyKey(dep);
16089
+ state.identifierOverrides[key] = () => dependencyGetter(dep);
16090
+ const base = key.split(".")[0];
16091
+ if (base && !state.identifierOverrides[base]) {
16092
+ state.identifierOverrides[base] = () => dependencyGetter(base);
16093
+ }
16094
+ }
16095
+ }
16096
+ }
16097
+ function shouldMemoizeRegion(region) {
16098
+ if (region.dependencies.size > 0) {
16099
+ return true;
16100
+ }
16101
+ if (region.hasControlFlow) {
16102
+ return true;
16103
+ }
16104
+ if (region.hasReactiveWrites) {
16105
+ return true;
16106
+ }
16107
+ return false;
16108
+ }
16109
+
15982
16110
  // src/ir/ssa.ts
15983
16111
  function analyzeCFG(blocks) {
15984
16112
  const predecessors = computePredecessors(blocks);
@@ -16469,7 +16597,7 @@ function collectExprReads(expr, into, paths, bound = /* @__PURE__ */ new Set(),
16469
16597
  }
16470
16598
  return;
16471
16599
  case "CallExpression": {
16472
- const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect");
16600
+ const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
16473
16601
  if (!isMacroCallee) {
16474
16602
  collectExprReads(expr.callee, into, paths, bound);
16475
16603
  }
@@ -18837,6 +18965,7 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18837
18965
  }
18838
18966
  const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
18839
18967
  const uniqueOutputNames = [...new Set(outputNames)];
18968
+ const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
18840
18969
  if (debugEnabled("region")) {
18841
18970
  console.log("Region memo", region.id, {
18842
18971
  instructions: region.instructions.map((instr) => instr.kind),
@@ -18846,11 +18975,17 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18846
18975
  if (uniqueOutputNames.length === 0) {
18847
18976
  ctx.helpersUsed.add("useEffect");
18848
18977
  ctx.needsCtx = true;
18849
- const effectCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
18978
+ const effectCallArgs = [
18850
18979
  t2.identifier("__fictCtx"),
18851
- t2.arrowFunctionExpression([], t2.blockStatement(bodyStatements)),
18852
- t2.numericLiteral(reserveHookSlot(ctx))
18853
- ]);
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);
18854
18989
  statements.push(t2.expressionStatement(effectCall));
18855
18990
  } else {
18856
18991
  ctx.helpersUsed.add("useMemo");
@@ -18883,20 +19018,24 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18883
19018
  };
18884
19019
  const returnObj = t2.objectExpression(uniqueOutputNames.map((name) => buildOutputProperty(name)));
18885
19020
  const memoBody = t2.blockStatement([...bodyStatements, t2.returnStatement(returnObj)]);
18886
- const memoCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), [
19021
+ const slot = reserveHookSlot(ctx);
19022
+ const memoArgs = [
18887
19023
  t2.identifier("__fictCtx"),
18888
- t2.arrowFunctionExpression([], memoBody),
18889
- t2.numericLiteral(reserveHookSlot(ctx))
18890
- ]);
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);
18891
19030
  const regionVarName = `__region_${region.id}`;
18892
19031
  statements.push(
18893
19032
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
18894
19033
  );
18895
19034
  const isAccessorOutput = (name) => ctx.signalVars?.has(name) || ctx.memoVars?.has(name) || ctx.aliasVars?.has(name) || ctx.storeVars?.has(name);
18896
- const getterOutputs = uniqueOutputNames.filter(
19035
+ const getterOutputs = bindableOutputs.filter(
18897
19036
  (name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
18898
19037
  );
18899
- const directOutputs = uniqueOutputNames.filter((name) => !getterOutputs.includes(name));
19038
+ const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
18900
19039
  if (debugEnabled("region")) {
18901
19040
  console.log("Region debug", {
18902
19041
  id: region.id,
@@ -18944,7 +19083,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18944
19083
  t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
18945
19084
  t2.identifier("__fictCtx"),
18946
19085
  t2.arrowFunctionExpression([], effectBody),
18947
- t2.numericLiteral(reserveHookSlot(ctx))
19086
+ (() => {
19087
+ const slot2 = reserveHookSlot(ctx);
19088
+ return slot2 >= 0 ? t2.numericLiteral(slot2) : t2.identifier("undefined");
19089
+ })()
18948
19090
  ])
18949
19091
  )
18950
19092
  );
@@ -19162,11 +19304,15 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
19162
19304
  ctx.helpersUsed.add("useMemo");
19163
19305
  ctx.needsCtx = true;
19164
19306
  const regionVarName = `__region_${region.id}`;
19165
- const memoCall = t2.callExpression(t2.identifier("__fictUseMemo"), [
19307
+ const slotForMemo = reserveHookSlot(ctx);
19308
+ const memoArgs = [
19166
19309
  t2.identifier("__fictCtx"),
19167
- t2.arrowFunctionExpression([], t2.blockStatement(memoBody)),
19168
- t2.numericLiteral(reserveHookSlot(ctx))
19169
- ]);
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);
19170
19316
  statements.push(
19171
19317
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
19172
19318
  );
@@ -19188,6 +19334,9 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
19188
19334
  return statements;
19189
19335
  }
19190
19336
  function reserveHookSlot(ctx) {
19337
+ if (ctx.dynamicHookSlotDepth && ctx.dynamicHookSlotDepth > 0) {
19338
+ return -1;
19339
+ }
19191
19340
  const slot = ctx.nextHookSlot ?? 0;
19192
19341
  ctx.nextHookSlot = slot + 1;
19193
19342
  return slot;
@@ -19258,7 +19407,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19258
19407
  t2.arrowFunctionExpression([], expr)
19259
19408
  ];
19260
19409
  if (inRegionMemo) {
19261
- args.push(t2.numericLiteral(reserveHookSlot(ctx)));
19410
+ const slot = reserveHookSlot(ctx);
19411
+ if (slot >= 0) {
19412
+ args.push(t2.numericLiteral(slot));
19413
+ }
19262
19414
  }
19263
19415
  ctx.helpersUsed.add("useMemo");
19264
19416
  ctx.needsCtx = true;
@@ -19272,17 +19424,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19272
19424
  const needsMutable = ctx.mutatedVars?.has(baseName2) ?? false;
19273
19425
  const isExternalAlias = declKind === "const" && instr.value.kind === "Identifier" && !(ctx.scopes?.byName?.has(deSSAVarName(instr.value.name)) ?? false);
19274
19426
  const fallbackDecl = !treatAsTracked && (!dependsOnTracked2 || isDestructuringTemp) ? declKind === "const" && (needsMutable || isExternalAlias) ? "let" : declKind : normalizedDecl;
19275
- const isAliasOfTracked = !isShadowDeclaration && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp;
19276
- if (isAliasOfTracked) {
19277
- declaredVars.add(baseName2);
19278
- const sourceIdent = instr.value;
19279
- return t2.variableDeclaration(fallbackDecl, [
19280
- t2.variableDeclarator(
19281
- t2.identifier(baseName2),
19282
- t2.callExpression(t2.identifier(deSSAVarName(sourceIdent.name)), [])
19283
- )
19284
- ]);
19285
- }
19286
19427
  declaredVars.add(baseName2);
19287
19428
  if (treatAsTracked && !isDestructuringTemp) {
19288
19429
  if (isStateCall2) {
@@ -19291,6 +19432,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19291
19432
  ]);
19292
19433
  }
19293
19434
  if (dependsOnTracked2) {
19435
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19436
+ aliasVars.add(baseName2);
19437
+ }
19294
19438
  const derivedExpr = lowerAssignedValue(true);
19295
19439
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19296
19440
  return t2.variableDeclaration(normalizedDecl, [
@@ -19315,6 +19459,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19315
19459
  }
19316
19460
  }
19317
19461
  if (dependsOnTracked2 && !isDestructuringTemp) {
19462
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19463
+ aliasVars.add(baseName2);
19464
+ }
19318
19465
  const derivedExpr = lowerAssignedValue(true);
19319
19466
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19320
19467
  return t2.variableDeclaration(normalizedDecl, [
@@ -19349,17 +19496,35 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19349
19496
  t2.callExpression(t2.identifier(baseName2), [lowerAssignedValue(true)])
19350
19497
  );
19351
19498
  }
19352
- if (instr.value.kind === "Identifier" && !isDestructuringTemp) {
19353
- const source = deSSAVarName(instr.value.name);
19354
- if (ctx.trackedVars.has(source) && !declaredVars.has(baseName2)) {
19355
- return t2.variableDeclaration(declKind ?? "const", [
19356
- t2.variableDeclarator(t2.identifier(baseName2), t2.callExpression(t2.identifier(source), []))
19357
- ]);
19358
- }
19359
- }
19360
19499
  if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
19361
19500
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19362
19501
  }
19502
+ if (dependsOnTracked2 && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
19503
+ const derivedExpr = lowerAssignedValue(true);
19504
+ aliasVars.add(baseName2);
19505
+ if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19506
+ return t2.expressionStatement(
19507
+ t2.assignmentExpression("=", t2.identifier(baseName2), derivedExpr)
19508
+ );
19509
+ }
19510
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19511
+ if (ctx.noMemo) {
19512
+ return t2.expressionStatement(
19513
+ t2.assignmentExpression(
19514
+ "=",
19515
+ t2.identifier(baseName2),
19516
+ t2.arrowFunctionExpression([], derivedExpr)
19517
+ )
19518
+ );
19519
+ }
19520
+ return t2.expressionStatement(
19521
+ t2.assignmentExpression(
19522
+ "=",
19523
+ t2.identifier(baseName2),
19524
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19525
+ )
19526
+ );
19527
+ }
19363
19528
  if (declaredVars.has(baseName2)) {
19364
19529
  if (aliasVars.has(baseName2)) {
19365
19530
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
@@ -19757,78 +19922,20 @@ function deSSAJSXChild(child, t2) {
19757
19922
  return child;
19758
19923
  }
19759
19924
 
19760
- // src/utils.ts
19761
- function isStateCall(node, t2) {
19762
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
19763
- }
19764
- function isEffectCall(node, t2) {
19765
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
19766
- }
19767
- function getRootIdentifier(expr, t2) {
19768
- if (t2.isIdentifier(expr)) {
19769
- return expr;
19770
- }
19771
- if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
19772
- return getRootIdentifier(expr.object, t2);
19773
- }
19774
- if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
19775
- return getRootIdentifier(expr.object, t2);
19776
- }
19777
- if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
19778
- return getRootIdentifier(expr.callee, t2);
19779
- }
19780
- if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
19781
- return getRootIdentifier(expr.callee, t2);
19782
- }
19783
- if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
19784
- return getRootIdentifier(expr.expression, t2);
19785
- }
19786
- if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
19787
- return getRootIdentifier(expr.expression, t2);
19788
- }
19789
- return null;
19790
- }
19791
-
19792
- // src/fine-grained-dom.ts
19793
- function normalizeDependencyKey(name) {
19794
- return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
19795
- }
19796
- function applyRegionMetadata(state, options) {
19797
- if (!options.region) return;
19798
- const region = options.region;
19799
- state.regionMetadata = region;
19800
- if (region.dependencies.size > 0) {
19801
- state.identifierOverrides = state.identifierOverrides ?? {};
19802
- const dependencyGetter = options.dependencyGetter ?? null;
19803
- if (!dependencyGetter) {
19804
- return;
19805
- }
19806
- for (const dep of region.dependencies) {
19807
- const key = normalizeDependencyKey(dep);
19808
- state.identifierOverrides[key] = () => dependencyGetter(dep);
19809
- const base = key.split(".")[0];
19810
- if (base && !state.identifierOverrides[base]) {
19811
- state.identifierOverrides[base] = () => dependencyGetter(base);
19812
- }
19813
- }
19814
- }
19815
- }
19816
- function shouldMemoizeRegion(region) {
19817
- if (region.dependencies.size > 0) {
19818
- return true;
19819
- }
19820
- if (region.hasControlFlow) {
19821
- return true;
19822
- }
19823
- if (region.hasReactiveWrites) {
19824
- return true;
19825
- }
19826
- return false;
19827
- }
19828
-
19829
19925
  // src/ir/codegen.ts
19830
19926
  var HOOK_SLOT_BASE = 1e3;
19831
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
+ }
19832
19939
  function isHookName(name) {
19833
19940
  return !!name && name.startsWith(HOOK_NAME_PREFIX);
19834
19941
  }
@@ -19862,13 +19969,22 @@ function applyRegionToContext(ctx, region) {
19862
19969
  return prevRegion;
19863
19970
  }
19864
19971
  function reserveHookSlot2(ctx) {
19972
+ if (ctx.dynamicHookSlotDepth && ctx.dynamicHookSlotDepth > 0) {
19973
+ return -1;
19974
+ }
19865
19975
  const slot = ctx.nextHookSlot ?? HOOK_SLOT_BASE;
19866
19976
  ctx.nextHookSlot = slot + 1;
19867
19977
  return slot;
19868
19978
  }
19869
19979
  function expressionContainsJSX(expr) {
19980
+ if (Array.isArray(expr)) {
19981
+ return expr.some((item) => expressionContainsJSX(item));
19982
+ }
19870
19983
  if (!expr || typeof expr !== "object") return false;
19871
19984
  if (expr.kind === "JSXElement") return true;
19985
+ if (Array.isArray(expr.instructions)) {
19986
+ return expr.instructions.some((i) => expressionContainsJSX(i?.value ?? i));
19987
+ }
19872
19988
  switch (expr.kind) {
19873
19989
  case "CallExpression":
19874
19990
  if (expressionContainsJSX(expr.callee)) return true;
@@ -19881,10 +19997,29 @@ function expressionContainsJSX(expr) {
19881
19997
  return expressionContainsJSX(expr.test) || expressionContainsJSX(expr.consequent) || expressionContainsJSX(expr.alternate);
19882
19998
  case "ArrowFunction":
19883
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;
19884
20007
  default:
19885
20008
  return false;
19886
20009
  }
19887
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
+ }
19888
20023
  function functionContainsJSX(fn) {
19889
20024
  for (const block of fn.blocks) {
19890
20025
  for (const instr of block.instructions) {
@@ -20983,18 +21118,25 @@ function lowerTrackedExpression(expr, ctx) {
20983
21118
  }
20984
21119
  function lowerInstruction(instr, ctx) {
20985
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
+ };
20986
21126
  if (instr.kind === "Assign") {
20987
21127
  const baseName2 = deSSAVarName(instr.target.name);
20988
21128
  const isFunctionDecl = instr.value.kind === "FunctionExpression" && (instr.declarationKind === "function" || !instr.declarationKind && instr.value.name === baseName2);
20989
21129
  if (isFunctionDecl) {
20990
21130
  const loweredFn = lowerExpression(instr.value, ctx);
20991
21131
  if (t2.isFunctionExpression(loweredFn)) {
20992
- return t2.functionDeclaration(
20993
- t2.identifier(baseName2),
20994
- loweredFn.params,
20995
- loweredFn.body,
20996
- loweredFn.generator ?? false,
20997
- 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
+ )
20998
21140
  );
20999
21141
  }
21000
21142
  }
@@ -21009,12 +21151,16 @@ function lowerInstruction(instr, ctx) {
21009
21151
  ctx.memoVars?.add(baseName2);
21010
21152
  }
21011
21153
  if (declKind) {
21012
- return t2.variableDeclaration(declKind, [
21013
- t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
21014
- ]);
21154
+ return applyLoc(
21155
+ t2.variableDeclaration(declKind, [
21156
+ t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
21157
+ ])
21158
+ );
21015
21159
  }
21016
- return t2.expressionStatement(
21017
- t2.assignmentExpression("=", t2.identifier(baseName2), hookMember.member)
21160
+ return applyLoc(
21161
+ t2.expressionStatement(
21162
+ t2.assignmentExpression("=", t2.identifier(baseName2), hookMember.member)
21163
+ )
21018
21164
  );
21019
21165
  }
21020
21166
  if (instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && isHookName(instr.value.callee.name)) {
@@ -21028,16 +21174,24 @@ function lowerInstruction(instr, ctx) {
21028
21174
  }
21029
21175
  }
21030
21176
  if (ctx.signalVars?.has(baseName2)) {
21031
- return t2.expressionStatement(
21032
- 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
+ )
21033
21181
  );
21034
21182
  }
21035
- return t2.expressionStatement(
21036
- 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
+ )
21037
21191
  );
21038
21192
  }
21039
21193
  if (instr.kind === "Expression") {
21040
- return t2.expressionStatement(lowerTrackedExpression(instr.value, ctx));
21194
+ return applyLoc(t2.expressionStatement(lowerTrackedExpression(instr.value, ctx)));
21041
21195
  }
21042
21196
  if (instr.kind === "Phi") {
21043
21197
  return null;
@@ -21046,6 +21200,9 @@ function lowerInstruction(instr, ctx) {
21046
21200
  }
21047
21201
  function lowerTerminator(block, ctx) {
21048
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));
21049
21206
  switch (block.terminator.kind) {
21050
21207
  case "Return": {
21051
21208
  const prevRegion = ctx.currentRegion;
@@ -21058,14 +21215,14 @@ function lowerTerminator(block, ctx) {
21058
21215
  }
21059
21216
  ctx.inReturn = false;
21060
21217
  ctx.currentRegion = prevRegion;
21061
- return [t2.returnStatement(retExpr)];
21218
+ return applyLoc([t2.returnStatement(retExpr)]);
21062
21219
  }
21063
21220
  case "Throw":
21064
- return [t2.throwStatement(lowerTrackedExpression(block.terminator.argument, ctx))];
21221
+ return applyLoc([t2.throwStatement(lowerTrackedExpression(block.terminator.argument, ctx))]);
21065
21222
  case "Jump":
21066
- return [t2.expressionStatement(t2.stringLiteral(`jump ${block.terminator.target}`))];
21223
+ return applyLoc([t2.expressionStatement(t2.stringLiteral(`jump ${block.terminator.target}`))]);
21067
21224
  case "Branch":
21068
- return [
21225
+ return applyLoc([
21069
21226
  t2.ifStatement(
21070
21227
  lowerTrackedExpression(block.terminator.test, ctx),
21071
21228
  t2.blockStatement([
@@ -21075,9 +21232,9 @@ function lowerTerminator(block, ctx) {
21075
21232
  t2.expressionStatement(t2.stringLiteral(`goto ${block.terminator.alternate}`))
21076
21233
  ])
21077
21234
  )
21078
- ];
21235
+ ]);
21079
21236
  case "Switch":
21080
- return [
21237
+ return applyLoc([
21081
21238
  t2.switchStatement(
21082
21239
  lowerTrackedExpression(block.terminator.discriminant, ctx),
21083
21240
  block.terminator.cases.map(
@@ -21086,30 +21243,30 @@ function lowerTerminator(block, ctx) {
21086
21243
  ])
21087
21244
  )
21088
21245
  )
21089
- ];
21246
+ ]);
21090
21247
  case "ForOf": {
21091
21248
  const term = block.terminator;
21092
21249
  const varKind = term.variableKind ?? "const";
21093
21250
  const leftPattern = term.pattern ? term.pattern : t2.identifier(term.variable);
21094
- return [
21251
+ return applyLoc([
21095
21252
  t2.forOfStatement(
21096
21253
  t2.variableDeclaration(varKind, [t2.variableDeclarator(leftPattern)]),
21097
21254
  lowerExpression(term.iterable, ctx),
21098
21255
  t2.blockStatement([t2.expressionStatement(t2.stringLiteral(`body ${term.body}`))])
21099
21256
  )
21100
- ];
21257
+ ]);
21101
21258
  }
21102
21259
  case "ForIn": {
21103
21260
  const term = block.terminator;
21104
21261
  const varKind = term.variableKind ?? "const";
21105
21262
  const leftPattern = term.pattern ? term.pattern : t2.identifier(term.variable);
21106
- return [
21263
+ return applyLoc([
21107
21264
  t2.forInStatement(
21108
21265
  t2.variableDeclaration(varKind, [t2.variableDeclarator(leftPattern)]),
21109
21266
  lowerExpression(term.object, ctx),
21110
21267
  t2.blockStatement([t2.expressionStatement(t2.stringLiteral(`body ${term.body}`))])
21111
21268
  )
21112
- ];
21269
+ ]);
21113
21270
  }
21114
21271
  case "Try": {
21115
21272
  const term = block.terminator;
@@ -21125,20 +21282,20 @@ function lowerTerminator(block, ctx) {
21125
21282
  const finallyBlock = term.finallyBlock !== void 0 ? t2.blockStatement([
21126
21283
  t2.expressionStatement(t2.stringLiteral(`finally ${term.finallyBlock}`))
21127
21284
  ]) : null;
21128
- return [t2.tryStatement(tryBlock, catchClause, finallyBlock)];
21285
+ return applyLoc([t2.tryStatement(tryBlock, catchClause, finallyBlock)]);
21129
21286
  }
21130
21287
  case "Unreachable":
21131
- return [];
21288
+ return applyLoc([]);
21132
21289
  case "Break":
21133
- return [
21290
+ return applyLoc([
21134
21291
  t2.breakStatement(block.terminator.label ? t2.identifier(block.terminator.label) : null)
21135
- ];
21292
+ ]);
21136
21293
  case "Continue":
21137
- return [
21294
+ return applyLoc([
21138
21295
  t2.continueStatement(block.terminator.label ? t2.identifier(block.terminator.label) : null)
21139
- ];
21296
+ ]);
21140
21297
  default:
21141
- return [];
21298
+ return applyLoc([]);
21142
21299
  }
21143
21300
  }
21144
21301
  function attachHelperImports(ctx, body, t2) {
@@ -21277,7 +21434,7 @@ function lowerExpression(expr, ctx, isAssigned = false) {
21277
21434
  }
21278
21435
  ctx.expressionDepth = depth;
21279
21436
  try {
21280
- return lowerExpressionImpl(expr, ctx, isAssigned);
21437
+ return setNodeLoc(lowerExpressionImpl(expr, ctx, isAssigned), expr.loc);
21281
21438
  } finally {
21282
21439
  ctx.expressionDepth = depth - 1;
21283
21440
  }
@@ -21412,14 +21569,22 @@ function lowerExpressionImpl(expr, ctx, isAssigned = false) {
21412
21569
  const calleeIsMemoAccessor = !!calleeName && ctx.memoVars?.has(calleeName);
21413
21570
  const calleeIsSignalLike = !!calleeName && (ctx.signalVars?.has(calleeName) || ctx.storeVars?.has(calleeName));
21414
21571
  if (calleeIsMemoAccessor && !calleeIsSignalLike && expr.arguments.length > 0) {
21415
- const loweredArgs = expr.arguments.map((a) => lowerExpression(a, ctx));
21416
- 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);
21417
21574
  }
21418
21575
  const lowerCallee = () => isIIFE ? withNonReactiveScope(ctx, () => lowerExpression(expr.callee, ctx)) : lowerExpression(expr.callee, ctx);
21419
- return t2.callExpression(
21420
- lowerCallee(),
21421
- expr.arguments.map((a) => lowerExpression(a, ctx))
21422
- );
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);
21423
21588
  }
21424
21589
  case "MemberExpression":
21425
21590
  if (matchesListKeyPattern(expr, ctx)) {
@@ -21983,6 +22148,8 @@ function getDependencyPathFromNode(node, t2) {
21983
22148
  if (node.computed) {
21984
22149
  if (t2.isStringLiteral(property) || t2.isNumericLiteral(property)) {
21985
22150
  propName = String(property.value);
22151
+ } else {
22152
+ return objectPath;
21986
22153
  }
21987
22154
  } else if (t2.isIdentifier(property)) {
21988
22155
  propName = property.name;
@@ -22099,10 +22266,12 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
22099
22266
  return names;
22100
22267
  };
22101
22268
  if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
22269
+ const propertyNode = node.property;
22270
+ const isDynamicComputed = (node.computed ?? false) && !t2.isStringLiteral(propertyNode) && !t2.isNumericLiteral(propertyNode);
22102
22271
  const path = getDependencyPathFromNode(node, t2);
22103
22272
  const normalized = path ? normalizeDependencyKey2(path) : null;
22104
22273
  const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
22105
- if (override && !isCallTarget) {
22274
+ if (override && !isCallTarget && !isDynamicComputed) {
22106
22275
  const replacement = override();
22107
22276
  Object.assign(node, replacement);
22108
22277
  return;
@@ -22272,15 +22441,6 @@ function isStaticValue(expr) {
22272
22441
  if (!expr) return false;
22273
22442
  return expr.kind === "Literal";
22274
22443
  }
22275
- function isComponentLikeCallee(expr) {
22276
- if (expr.kind === "Identifier") {
22277
- return expr.name[0] === expr.name[0]?.toUpperCase();
22278
- }
22279
- if (expr.kind === "MemberExpression" || expr.kind === "OptionalMemberExpression") {
22280
- return isComponentLikeCallee(expr.object);
22281
- }
22282
- return false;
22283
- }
22284
22444
  function isLikelyTextExpression(expr, ctx) {
22285
22445
  let ok = true;
22286
22446
  const isReactiveIdentifier = (name) => {
@@ -22309,15 +22469,9 @@ function isLikelyTextExpression(expr, ctx) {
22309
22469
  ok = false;
22310
22470
  return;
22311
22471
  case "CallExpression":
22312
- case "OptionalCallExpression": {
22313
- if (isComponentLikeCallee(node.callee)) {
22314
- ok = false;
22315
- return;
22316
- }
22317
- visit(node.callee, true);
22318
- node.arguments.forEach((arg) => visit(arg));
22472
+ case "OptionalCallExpression":
22473
+ ok = false;
22319
22474
  return;
22320
- }
22321
22475
  case "MemberExpression":
22322
22476
  case "OptionalMemberExpression":
22323
22477
  visit(node.object, true);
@@ -22772,7 +22926,12 @@ function lowerIntrinsicElement(jsx, ctx) {
22772
22926
  t2.arrowFunctionExpression([], body)
22773
22927
  ];
22774
22928
  if (ctx.isComponentFn) {
22775
- memoArgs.push(t2.numericLiteral(reserveHookSlot2(ctx)));
22929
+ {
22930
+ const slot = reserveHookSlot2(ctx);
22931
+ if (slot >= 0) {
22932
+ memoArgs.push(t2.numericLiteral(slot));
22933
+ }
22934
+ }
22776
22935
  }
22777
22936
  return t2.callExpression(t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), memoArgs), []);
22778
22937
  }
@@ -23327,12 +23486,16 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
23327
23486
  const hoistedStatements = ctx.hoistedTemplateStatements;
23328
23487
  ctx.hoistedTemplates = prevHoistedTemplates;
23329
23488
  ctx.hoistedTemplateStatements = prevHoistedTemplateStatements;
23330
- if (isKeyed && (t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr))) {
23331
- const firstParam = callbackExpr.params[0];
23489
+ if (t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr)) {
23490
+ const [firstParam, secondParam] = callbackExpr.params;
23491
+ const overrides = {};
23332
23492
  if (t2.isIdentifier(firstParam)) {
23333
- const overrides = {
23334
- [firstParam.name]: () => t2.callExpression(t2.identifier(firstParam.name), [])
23335
- };
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) {
23336
23499
  if (t2.isBlockStatement(callbackExpr.body)) {
23337
23500
  replaceIdentifiersWithOverrides(callbackExpr.body, overrides, t2, callbackExpr.type, "body");
23338
23501
  } else {
@@ -23579,8 +23742,11 @@ function lowerHIRWithRegions(program, t2, options) {
23579
23742
  for (const stmt of originalBody) {
23580
23743
  if (t2.isVariableDeclaration(stmt)) {
23581
23744
  for (const decl of stmt.declarations) {
23582
- if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && decl.init.callee.name === "$state") {
23745
+ if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && (decl.init.callee.name === "$state" || decl.init.callee.name === "$store")) {
23583
23746
  ctx.trackedVars.add(decl.id.name);
23747
+ if (decl.init.callee.name === "$store") {
23748
+ ctx.storeVars?.add(decl.id.name);
23749
+ }
23584
23750
  }
23585
23751
  }
23586
23752
  }
@@ -24223,10 +24389,9 @@ function lowerFunctionWithRegions(fn, ctx) {
24223
24389
  }
24224
24390
  }
24225
24391
  const params = finalParams;
24226
- const funcDecl = t2.functionDeclaration(
24227
- t2.identifier(fn.name ?? "fn"),
24228
- params,
24229
- t2.blockStatement(statements)
24392
+ const funcDecl = setNodeLoc(
24393
+ t2.functionDeclaration(t2.identifier(fn.name ?? "fn"), params, t2.blockStatement(statements)),
24394
+ fn.loc
24230
24395
  );
24231
24396
  ctx.needsCtx = prevNeedsCtx;
24232
24397
  ctx.shadowedNames = prevShadowed;
@@ -24315,10 +24480,44 @@ function isInsideNestedFunction(path) {
24315
24480
  function isInsideJSX(path) {
24316
24481
  return !!path.findParent((p) => p.isJSXElement?.() || p.isJSXFragment?.());
24317
24482
  }
24318
- function emitWarning(node, code, message, options, fileName) {
24319
- 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) {
24320
24519
  const loc = node.loc?.start;
24321
- options.onWarn({
24520
+ warn({
24322
24521
  code,
24323
24522
  message,
24324
24523
  fileName,
@@ -24406,8 +24605,7 @@ function isDynamicPropertyAccess(node, t2) {
24406
24605
  if (!node.computed) return false;
24407
24606
  return !(t2.isStringLiteral(node.property) || t2.isNumericLiteral(node.property));
24408
24607
  }
24409
- function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24410
- const fileName = programPath.hub?.file?.opts?.filename || "<unknown>";
24608
+ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t2) {
24411
24609
  const isStateRoot = (expr) => {
24412
24610
  const root = getRootIdentifier(expr, t2);
24413
24611
  return !!(root && stateVars.has(root.name));
@@ -24423,7 +24621,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24423
24621
  path.node,
24424
24622
  "FICT-M",
24425
24623
  "Direct mutation of nested property detected; use immutable update or $store helpers",
24426
- options,
24624
+ warn,
24427
24625
  fileName
24428
24626
  );
24429
24627
  if (isDynamicPropertyAccess(left, t2)) {
@@ -24431,7 +24629,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24431
24629
  path.node,
24432
24630
  "FICT-H",
24433
24631
  "Dynamic property access widens dependency tracking",
24434
- options,
24632
+ warn,
24435
24633
  fileName
24436
24634
  );
24437
24635
  }
@@ -24446,7 +24644,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24446
24644
  path.node,
24447
24645
  "FICT-M",
24448
24646
  "Direct mutation of nested property detected; use immutable update or $store helpers",
24449
- options,
24647
+ warn,
24450
24648
  fileName
24451
24649
  );
24452
24650
  if (isDynamicPropertyAccess(arg, t2)) {
@@ -24454,7 +24652,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24454
24652
  path.node,
24455
24653
  "FICT-H",
24456
24654
  "Dynamic property access widens dependency tracking",
24457
- options,
24655
+ warn,
24458
24656
  fileName
24459
24657
  );
24460
24658
  }
@@ -24470,7 +24668,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24470
24668
  path.node,
24471
24669
  "FICT-H",
24472
24670
  "Dynamic property access widens dependency tracking",
24473
- options,
24671
+ warn,
24474
24672
  fileName
24475
24673
  );
24476
24674
  }
@@ -24499,7 +24697,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24499
24697
  path.node,
24500
24698
  "FICT-R005",
24501
24699
  `Function captures reactive variable(s): ${Array.from(captured).join(", ")}. Pass them as parameters or memoize explicitly to avoid hidden dependencies.`,
24502
- options,
24700
+ warn,
24503
24701
  fileName
24504
24702
  );
24505
24703
  }
@@ -24530,7 +24728,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24530
24728
  path.node,
24531
24729
  "FICT-E001",
24532
24730
  "Effect has no reactive reads; it will run once. Consider removing $effect or adding dependencies.",
24533
- options,
24731
+ warn,
24534
24732
  fileName
24535
24733
  );
24536
24734
  }
@@ -24556,7 +24754,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24556
24754
  arg,
24557
24755
  "FICT-H",
24558
24756
  "State value passed to unknown function (black box); dependency tracking may be imprecise",
24559
- options,
24757
+ warn,
24560
24758
  fileName
24561
24759
  );
24562
24760
  break;
@@ -24572,7 +24770,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24572
24770
  path.node,
24573
24771
  "FICT-H",
24574
24772
  "Dynamic property access widens dependency tracking",
24575
- options,
24773
+ warn,
24576
24774
  fileName
24577
24775
  );
24578
24776
  }
@@ -24622,19 +24820,97 @@ function createHIREntrypointVisitor(t2, options) {
24622
24820
  Program: {
24623
24821
  exit(path) {
24624
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 };
24827
+ const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
24828
+ const getFunctionName = (fnPath) => {
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;
24830
+ };
24831
+ const isComponentDefinition = (fnPath) => {
24832
+ const name = getFunctionName(fnPath);
24833
+ return name && isComponentName(name) || functionHasJSX(fnPath);
24834
+ };
24835
+ const isHookDefinition = (fnPath) => {
24836
+ const name = getFunctionName(fnPath);
24837
+ return isHookName2(name);
24838
+ };
24839
+ const isComponentOrHookDefinition = (fnPath) => isComponentDefinition(fnPath) || isHookDefinition(fnPath);
24625
24840
  const isComponentLike = (fnPath) => {
24626
- const name = 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;
24627
- return name && isComponentName(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24841
+ const name = getFunctionName(fnPath);
24842
+ return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24628
24843
  };
24629
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
+ };
24630
24905
  const checkNode = (node) => {
24631
24906
  if (!node) return false;
24632
24907
  if (t2.isAssignmentExpression(node) || t2.isUpdateExpression(node) || t2.isThrowStatement(node) || t2.isNewExpression(node)) {
24633
24908
  return true;
24634
24909
  }
24635
- 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)) {
24636
24911
  return true;
24637
24912
  }
24913
+ if (t2.isAwaitExpression(node)) return true;
24638
24914
  if (t2.isExpressionStatement(node)) return checkNode(node.expression);
24639
24915
  if (t2.isBlockStatement(node)) return node.body.some((stmt) => checkNode(stmt));
24640
24916
  if (t2.isReturnStatement(node)) return checkNode(node.argument);
@@ -24658,7 +24934,7 @@ function createHIREntrypointVisitor(t2, options) {
24658
24934
  fnPath.node,
24659
24935
  "FICT-C004",
24660
24936
  "Component has no return statement and will render nothing.",
24661
- options,
24937
+ warn,
24662
24938
  fileName
24663
24939
  );
24664
24940
  },
@@ -24676,7 +24952,7 @@ function createHIREntrypointVisitor(t2, options) {
24676
24952
  init,
24677
24953
  "FICT-C004",
24678
24954
  "Component has no return statement and will render nothing.",
24679
- options,
24955
+ warn,
24680
24956
  fileName
24681
24957
  );
24682
24958
  }
@@ -24726,7 +25002,7 @@ function createHIREntrypointVisitor(t2, options) {
24726
25002
  }
24727
25003
  }
24728
25004
  if (hasKey || hasUnknownSpread) return;
24729
- options.onWarn?.({
25005
+ warn({
24730
25006
  code: "FICT-J002",
24731
25007
  message: "Missing key prop in list rendering.",
24732
25008
  fileName,
@@ -24751,6 +25027,12 @@ function createHIREntrypointVisitor(t2, options) {
24751
25027
  "Destructuring $state is not supported. Use a simple identifier."
24752
25028
  );
24753
25029
  }
25030
+ const ownerComponent = varPath.getFunctionParent();
25031
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
25032
+ throw varPath.buildCodeFrameError(
25033
+ "$state() must be declared inside a component or hook function body"
25034
+ );
25035
+ }
24754
25036
  stateVars.add(varPath.node.id.name);
24755
25037
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
24756
25038
  throw varPath.buildCodeFrameError(
@@ -24791,12 +25073,18 @@ function createHIREntrypointVisitor(t2, options) {
24791
25073
  fnPath.node,
24792
25074
  "FICT-C003",
24793
25075
  "Components should not be defined inside other components. Move this definition to module scope to preserve identity and performance.",
24794
- options,
25076
+ warn,
24795
25077
  fileName
24796
25078
  );
24797
25079
  },
24798
25080
  CallExpression(callPath) {
24799
25081
  if (isStateCall(callPath.node, t2)) {
25082
+ const ownerComponent = callPath.getFunctionParent();
25083
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
25084
+ throw callPath.buildCodeFrameError(
25085
+ "$state() must be declared inside a component or hook function body"
25086
+ );
25087
+ }
24800
25088
  if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
24801
25089
  throw callPath.buildCodeFrameError(
24802
25090
  "$state() cannot be declared inside loops or conditionals"
@@ -24830,10 +25118,31 @@ function createHIREntrypointVisitor(t2, options) {
24830
25118
  callPath.node,
24831
25119
  "FICT-R004",
24832
25120
  "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
24833
- options,
25121
+ warn,
24834
25122
  fileName
24835
25123
  );
24836
25124
  }
25125
+ if (calleeId && isHookName2(calleeId)) {
25126
+ const binding = callPath.scope.getBinding(calleeId);
25127
+ const bindingPath = binding?.path;
25128
+ const bindingIsHook = !bindingPath && isHookName2(calleeId) || bindingPath?.isImportSpecifier() || bindingPath?.isImportDefaultSpecifier() || bindingPath?.isFunctionDeclaration() && isHookDefinition(bindingPath) || bindingPath?.isVariableDeclarator() && (() => {
25129
+ const init = bindingPath.get?.("init");
25130
+ return init ? isHookDefinition(init) : false;
25131
+ })();
25132
+ if (bindingIsHook) {
25133
+ const ownerFunction = callPath.getFunctionParent();
25134
+ if (!ownerFunction || !isComponentOrHookDefinition(ownerFunction)) {
25135
+ throw callPath.buildCodeFrameError(
25136
+ `${calleeId}() must be called inside a component or hook (useX)`
25137
+ );
25138
+ }
25139
+ if (isInsideLoop(callPath) || isInsideConditional(callPath) || isInsideNestedFunction(callPath)) {
25140
+ throw callPath.buildCodeFrameError(
25141
+ `${calleeId}() must be called at the top level of a component or hook (no loops/conditions/nested functions)`
25142
+ );
25143
+ }
25144
+ }
25145
+ }
24837
25146
  const allowedStateCallees = /* @__PURE__ */ new Set([
24838
25147
  "$effect",
24839
25148
  "$memo",
@@ -24844,7 +25153,7 @@ function createHIREntrypointVisitor(t2, options) {
24844
25153
  callPath.node.arguments.forEach((arg) => {
24845
25154
  if (t2.isIdentifier(arg) && stateVars.has(arg.name) && (!calleeId || !allowedStateCallees.has(calleeId))) {
24846
25155
  const loc = arg.loc?.start ?? callPath.node.loc?.start;
24847
- options.onWarn?.({
25156
+ warn({
24848
25157
  code: "FICT-S002",
24849
25158
  message: "State variable is passed as an argument; this passes a value snapshot and may escape component scope.",
24850
25159
  fileName,
@@ -24857,7 +25166,7 @@ function createHIREntrypointVisitor(t2, options) {
24857
25166
  const firstArg = callPath.node.arguments[0];
24858
25167
  if (firstArg && (t2.isArrowFunctionExpression(firstArg) || t2.isFunctionExpression(firstArg)) && memoHasSideEffects(firstArg)) {
24859
25168
  const loc = firstArg.loc?.start ?? callPath.node.loc?.start;
24860
- options.onWarn?.({
25169
+ warn({
24861
25170
  code: "FICT-M003",
24862
25171
  message: "Memo should not contain side effects.",
24863
25172
  fileName,
@@ -24868,6 +25177,68 @@ function createHIREntrypointVisitor(t2, options) {
24868
25177
  }
24869
25178
  }
24870
25179
  });
25180
+ const aliasStack = [/* @__PURE__ */ new Set()];
25181
+ const currentAliasSet = () => aliasStack[aliasStack.length - 1];
25182
+ const rhsUsesState = (exprPath) => {
25183
+ if (!exprPath) return false;
25184
+ if (exprPath.isIdentifier() && t2.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
25185
+ return true;
25186
+ }
25187
+ let usesState = false;
25188
+ exprPath.traverse({
25189
+ Identifier(idPath) {
25190
+ if (stateVars.has(idPath.node.name)) {
25191
+ usesState = true;
25192
+ idPath.stop();
25193
+ }
25194
+ }
25195
+ });
25196
+ return usesState;
25197
+ };
25198
+ if (process.env.FICT_DEBUG_ALIAS) {
25199
+ console.log("[fict] alias check state vars", Array.from(stateVars));
25200
+ }
25201
+ path.traverse({
25202
+ Function: {
25203
+ enter() {
25204
+ aliasStack.push(/* @__PURE__ */ new Set());
25205
+ },
25206
+ exit() {
25207
+ aliasStack.pop();
25208
+ }
25209
+ },
25210
+ VariableDeclarator(varPath) {
25211
+ const aliasSet = currentAliasSet();
25212
+ if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
25213
+ if (process.env.FICT_DEBUG_ALIAS) {
25214
+ console.log("[fict] alias add from decl", varPath.node.id.name);
25215
+ }
25216
+ aliasSet.add(varPath.node.id.name);
25217
+ }
25218
+ },
25219
+ AssignmentExpression(assignPath) {
25220
+ const aliasSet = currentAliasSet();
25221
+ if (!aliasSet) return;
25222
+ if (!t2.isIdentifier(assignPath.node.left)) return;
25223
+ const targetName = assignPath.node.left.name;
25224
+ const rightPath = assignPath.get("right");
25225
+ if (rhsUsesState(rightPath)) {
25226
+ if (process.env.FICT_DEBUG_ALIAS) {
25227
+ console.log("[fict] alias add from assign", targetName);
25228
+ }
25229
+ aliasSet.add(targetName);
25230
+ return;
25231
+ }
25232
+ if (aliasSet.has(targetName)) {
25233
+ if (process.env.FICT_DEBUG_ALIAS) {
25234
+ console.log("[fict] alias reassignment detected", targetName);
25235
+ }
25236
+ throw assignPath.buildCodeFrameError(
25237
+ `Alias reassignment is not supported for "${targetName}"`
25238
+ );
25239
+ }
25240
+ }
25241
+ });
24871
25242
  if (derivedVars.size > 0) {
24872
25243
  path.traverse({
24873
25244
  AssignmentExpression(assignPath) {
@@ -24900,13 +25271,15 @@ function createHIREntrypointVisitor(t2, options) {
24900
25271
  }
24901
25272
  });
24902
25273
  }
24903
- runWarningPass(path, stateVars, derivedVars, options, t2);
25274
+ runWarningPass(path, stateVars, derivedVars, warn, fileName, t2);
24904
25275
  const fileAst = t2.file(path.node);
24905
25276
  const hir = buildHIR(fileAst);
24906
- const lowered = lowerHIRWithRegions(hir, t2, options);
25277
+ const lowered = lowerHIRWithRegions(hir, t2, optionsWithWarnings);
24907
25278
  path.node.body = lowered.program.body;
24908
25279
  path.node.directives = lowered.program.directives;
24909
- path.scope.crawl();
25280
+ if (!process.env.FICT_SKIP_SCOPE_CRAWL) {
25281
+ path.scope.crawl();
25282
+ }
24910
25283
  stripMacroImports(path, t2);
24911
25284
  }
24912
25285
  }