@fictjs/compiler 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +1226 -233
  2. package/dist/index.js +1226 -233
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -107,7 +107,7 @@ var require_generated = __commonJS({
107
107
  });
108
108
  exports2.isAccessor = isAccessor;
109
109
  exports2.isAnyTypeAnnotation = isAnyTypeAnnotation;
110
- exports2.isArgumentPlaceholder = isArgumentPlaceholder;
110
+ exports2.isArgumentPlaceholder = isArgumentPlaceholder2;
111
111
  exports2.isArrayExpression = isArrayExpression2;
112
112
  exports2.isArrayPattern = isArrayPattern4;
113
113
  exports2.isArrayTypeAnnotation = isArrayTypeAnnotation;
@@ -131,7 +131,7 @@ var require_generated = __commonJS({
131
131
  exports2.isClass = isClass;
132
132
  exports2.isClassAccessorProperty = isClassAccessorProperty;
133
133
  exports2.isClassBody = isClassBody;
134
- exports2.isClassDeclaration = isClassDeclaration2;
134
+ exports2.isClassDeclaration = isClassDeclaration3;
135
135
  exports2.isClassExpression = isClassExpression2;
136
136
  exports2.isClassImplements = isClassImplements;
137
137
  exports2.isClassMethod = isClassMethod;
@@ -161,7 +161,7 @@ var require_generated = __commonJS({
161
161
  exports2.isDirectiveLiteral = isDirectiveLiteral;
162
162
  exports2.isDoExpression = isDoExpression;
163
163
  exports2.isDoWhileStatement = isDoWhileStatement2;
164
- exports2.isEmptyStatement = isEmptyStatement;
164
+ exports2.isEmptyStatement = isEmptyStatement2;
165
165
  exports2.isEmptyTypeAnnotation = isEmptyTypeAnnotation;
166
166
  exports2.isEnumBody = isEnumBody;
167
167
  exports2.isEnumBooleanBody = isEnumBooleanBody;
@@ -232,14 +232,14 @@ var require_generated = __commonJS({
232
232
  exports2.isJSXFragment = isJSXFragment2;
233
233
  exports2.isJSXIdentifier = isJSXIdentifier2;
234
234
  exports2.isJSXMemberExpression = isJSXMemberExpression2;
235
- exports2.isJSXNamespacedName = isJSXNamespacedName;
235
+ exports2.isJSXNamespacedName = isJSXNamespacedName2;
236
236
  exports2.isJSXOpeningElement = isJSXOpeningElement;
237
237
  exports2.isJSXOpeningFragment = isJSXOpeningFragment;
238
238
  exports2.isJSXSpreadAttribute = isJSXSpreadAttribute2;
239
- exports2.isJSXSpreadChild = isJSXSpreadChild;
239
+ exports2.isJSXSpreadChild = isJSXSpreadChild2;
240
240
  exports2.isJSXText = isJSXText2;
241
241
  exports2.isLVal = isLVal;
242
- exports2.isLabeledStatement = isLabeledStatement;
242
+ exports2.isLabeledStatement = isLabeledStatement2;
243
243
  exports2.isLiteral = isLiteral;
244
244
  exports2.isLogicalExpression = isLogicalExpression2;
245
245
  exports2.isLoop = isLoop;
@@ -277,7 +277,7 @@ var require_generated = __commonJS({
277
277
  exports2.isOptionalMemberExpression = isOptionalMemberExpression2;
278
278
  exports2.isParenthesizedExpression = isParenthesizedExpression2;
279
279
  exports2.isPattern = isPattern2;
280
- exports2.isPatternLike = isPatternLike2;
280
+ exports2.isPatternLike = isPatternLike3;
281
281
  exports2.isPipelineBareFunction = isPipelineBareFunction;
282
282
  exports2.isPipelinePrimaryTopicReference = isPipelinePrimaryTopicReference;
283
283
  exports2.isPipelineTopicExpression = isPipelineTopicExpression;
@@ -485,7 +485,7 @@ var require_generated = __commonJS({
485
485
  if (node.type !== "DoWhileStatement") return false;
486
486
  return opts == null || (0, _shallowEqual.default)(node, opts);
487
487
  }
488
- function isEmptyStatement(node, opts) {
488
+ function isEmptyStatement2(node, opts) {
489
489
  if (!node) return false;
490
490
  if (node.type !== "EmptyStatement") return false;
491
491
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -530,7 +530,7 @@ var require_generated = __commonJS({
530
530
  if (node.type !== "IfStatement") return false;
531
531
  return opts == null || (0, _shallowEqual.default)(node, opts);
532
532
  }
533
- function isLabeledStatement(node, opts) {
533
+ function isLabeledStatement2(node, opts) {
534
534
  if (!node) return false;
535
535
  if (node.type !== "LabeledStatement") return false;
536
536
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -695,7 +695,7 @@ var require_generated = __commonJS({
695
695
  if (node.type !== "ClassExpression") return false;
696
696
  return opts == null || (0, _shallowEqual.default)(node, opts);
697
697
  }
698
- function isClassDeclaration2(node, opts) {
698
+ function isClassDeclaration3(node, opts) {
699
699
  if (!node) return false;
700
700
  if (node.type !== "ClassDeclaration") return false;
701
701
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1210,7 +1210,7 @@ var require_generated = __commonJS({
1210
1210
  if (node.type !== "JSXExpressionContainer") return false;
1211
1211
  return opts == null || (0, _shallowEqual.default)(node, opts);
1212
1212
  }
1213
- function isJSXSpreadChild(node, opts) {
1213
+ function isJSXSpreadChild2(node, opts) {
1214
1214
  if (!node) return false;
1215
1215
  if (node.type !== "JSXSpreadChild") return false;
1216
1216
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1225,7 +1225,7 @@ var require_generated = __commonJS({
1225
1225
  if (node.type !== "JSXMemberExpression") return false;
1226
1226
  return opts == null || (0, _shallowEqual.default)(node, opts);
1227
1227
  }
1228
- function isJSXNamespacedName(node, opts) {
1228
+ function isJSXNamespacedName2(node, opts) {
1229
1229
  if (!node) return false;
1230
1230
  if (node.type !== "JSXNamespacedName") return false;
1231
1231
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1275,7 +1275,7 @@ var require_generated = __commonJS({
1275
1275
  if (node.type !== "V8IntrinsicIdentifier") return false;
1276
1276
  return opts == null || (0, _shallowEqual.default)(node, opts);
1277
1277
  }
1278
- function isArgumentPlaceholder(node, opts) {
1278
+ function isArgumentPlaceholder2(node, opts) {
1279
1279
  if (!node) return false;
1280
1280
  if (node.type !== "ArgumentPlaceholder") return false;
1281
1281
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -2216,7 +2216,7 @@ var require_generated = __commonJS({
2216
2216
  }
2217
2217
  return opts == null || (0, _shallowEqual.default)(node, opts);
2218
2218
  }
2219
- function isPatternLike2(node, opts) {
2219
+ function isPatternLike3(node, opts) {
2220
2220
  if (!node) return false;
2221
2221
  switch (node.type) {
2222
2222
  case "Identifier":
@@ -7191,7 +7191,7 @@ var require_lowercase = __commonJS({
7191
7191
  exports2.classAccessorProperty = classAccessorProperty;
7192
7192
  exports2.classBody = classBody;
7193
7193
  exports2.classDeclaration = classDeclaration;
7194
- exports2.classExpression = classExpression;
7194
+ exports2.classExpression = classExpression2;
7195
7195
  exports2.classImplements = classImplements;
7196
7196
  exports2.classMethod = classMethod;
7197
7197
  exports2.classPrivateMethod = classPrivateMethod;
@@ -8053,7 +8053,7 @@ var require_lowercase = __commonJS({
8053
8053
  validate(defs.body, node, "body", body, 1);
8054
8054
  return node;
8055
8055
  }
8056
- function classExpression(id = null, superClass = null, body, decorators = null) {
8056
+ function classExpression2(id = null, superClass = null, body, decorators = null) {
8057
8057
  const node = {
8058
8058
  type: "ClassExpression",
8059
8059
  id,
@@ -14611,6 +14611,14 @@ function parseFictReturnAnnotation(node) {
14611
14611
  }
14612
14612
  function extractIdentifiersFromPattern(pattern) {
14613
14613
  const ids = [];
14614
+ if (t.isRestElement(pattern)) {
14615
+ if (t.isIdentifier(pattern.argument)) {
14616
+ ids.push({ kind: "Identifier", name: pattern.argument.name });
14617
+ } else if (t.isPatternLike(pattern.argument)) {
14618
+ ids.push(...extractIdentifiersFromPattern(pattern.argument));
14619
+ }
14620
+ return ids;
14621
+ }
14614
14622
  if (t.isObjectPattern(pattern)) {
14615
14623
  for (const prop of pattern.properties) {
14616
14624
  if (t.isObjectProperty(prop)) {
@@ -14619,14 +14627,18 @@ function extractIdentifiersFromPattern(pattern) {
14619
14627
  } else if (t.isAssignmentPattern(prop.value)) {
14620
14628
  if (t.isIdentifier(prop.value.left)) {
14621
14629
  ids.push({ kind: "Identifier", name: prop.value.left.name });
14622
- } else if (t.isPattern(prop.value.left)) {
14630
+ } else if (t.isPatternLike(prop.value.left)) {
14623
14631
  ids.push(...extractIdentifiersFromPattern(prop.value.left));
14624
14632
  }
14625
- } else if (t.isObjectPattern(prop.value) || t.isArrayPattern(prop.value)) {
14633
+ } else if (t.isPatternLike(prop.value)) {
14626
14634
  ids.push(...extractIdentifiersFromPattern(prop.value));
14627
14635
  }
14628
- } else if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) {
14629
- ids.push({ kind: "Identifier", name: prop.argument.name });
14636
+ } else if (t.isRestElement(prop)) {
14637
+ if (t.isIdentifier(prop.argument)) {
14638
+ ids.push({ kind: "Identifier", name: prop.argument.name });
14639
+ } else if (t.isPatternLike(prop.argument)) {
14640
+ ids.push(...extractIdentifiersFromPattern(prop.argument));
14641
+ }
14630
14642
  }
14631
14643
  }
14632
14644
  } else if (t.isArrayPattern(pattern)) {
@@ -14634,16 +14646,20 @@ function extractIdentifiersFromPattern(pattern) {
14634
14646
  if (!elem) continue;
14635
14647
  if (t.isIdentifier(elem)) {
14636
14648
  ids.push({ kind: "Identifier", name: elem.name });
14637
- } else if (t.isPattern(elem)) {
14649
+ } else if (t.isRestElement(elem)) {
14650
+ if (t.isIdentifier(elem.argument)) {
14651
+ ids.push({ kind: "Identifier", name: elem.argument.name });
14652
+ } else if (t.isPatternLike(elem.argument)) {
14653
+ ids.push(...extractIdentifiersFromPattern(elem.argument));
14654
+ }
14655
+ } else if (t.isPatternLike(elem)) {
14638
14656
  ids.push(...extractIdentifiersFromPattern(elem));
14639
- } else if (t.isRestElement(elem) && t.isIdentifier(elem.argument)) {
14640
- ids.push({ kind: "Identifier", name: elem.argument.name });
14641
14657
  }
14642
14658
  }
14643
14659
  } else if (t.isAssignmentPattern(pattern)) {
14644
14660
  if (t.isIdentifier(pattern.left)) {
14645
14661
  ids.push({ kind: "Identifier", name: pattern.left.name });
14646
- } else if (t.isPattern(pattern.left)) {
14662
+ } else if (t.isPatternLike(pattern.left)) {
14647
14663
  ids.push(...extractIdentifiersFromPattern(pattern.left));
14648
14664
  }
14649
14665
  }
@@ -14829,8 +14845,12 @@ function convertFunction(name, params, body, options) {
14829
14845
  } else if (t.isObjectPattern(p.left) || t.isArrayPattern(p.left)) {
14830
14846
  paramIds.push(...extractIdentifiersFromPattern(p.left));
14831
14847
  }
14832
- } else if (t.isRestElement(p) && t.isIdentifier(p.argument)) {
14833
- paramIds.push({ kind: "Identifier", name: p.argument.name });
14848
+ } else if (t.isRestElement(p)) {
14849
+ if (t.isIdentifier(p.argument)) {
14850
+ paramIds.push({ kind: "Identifier", name: p.argument.name });
14851
+ } else if (t.isPattern(p.argument)) {
14852
+ paramIds.push(...extractIdentifiersFromPattern(p.argument));
14853
+ }
14834
14854
  }
14835
14855
  }
14836
14856
  const bodyStatements = [...body];
@@ -14915,8 +14935,21 @@ function convertFunction(name, params, body, options) {
14915
14935
  const excludeKeys = [];
14916
14936
  decl.id.properties.forEach((prop) => {
14917
14937
  if (t.isObjectProperty(prop)) {
14938
+ if (prop.computed) {
14939
+ reportUnsupportedExpression(
14940
+ prop.key,
14941
+ "Computed keys in object destructuring are not supported in HIR conversion."
14942
+ );
14943
+ return;
14944
+ }
14918
14945
  const keyName = t.isIdentifier(prop.key) ? prop.key.name : t.isStringLiteral(prop.key) ? prop.key.value : t.isNumericLiteral(prop.key) ? String(prop.key.value) : null;
14919
- if (!keyName) return;
14946
+ if (!keyName) {
14947
+ reportUnsupportedExpression(
14948
+ prop.key,
14949
+ "Unsupported object destructuring key in HIR conversion."
14950
+ );
14951
+ return;
14952
+ }
14920
14953
  excludeKeys.push(t.stringLiteral(keyName));
14921
14954
  if (t.isIdentifier(prop.value)) {
14922
14955
  const memberExpr = t.memberExpression(
@@ -14930,8 +14963,21 @@ function convertFunction(name, params, body, options) {
14930
14963
  value: convertExpression(memberExpr),
14931
14964
  declarationKind: declKind
14932
14965
  });
14966
+ } else {
14967
+ reportUnsupportedExpression(
14968
+ prop.value,
14969
+ "Unsupported object destructuring pattern in HIR conversion."
14970
+ );
14971
+ return;
14972
+ }
14973
+ } else if (t.isRestElement(prop)) {
14974
+ if (!t.isIdentifier(prop.argument)) {
14975
+ reportUnsupportedExpression(
14976
+ prop.argument,
14977
+ "Rest destructuring patterns must be identifiers in HIR conversion."
14978
+ );
14979
+ return;
14933
14980
  }
14934
- } else if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) {
14935
14981
  const restExpr = t.callExpression(t.identifier("__fictPropsRest"), [
14936
14982
  useTemp ? t.identifier(tempName) : babelSourceExpr,
14937
14983
  t.arrayExpression(excludeKeys)
@@ -14942,6 +14988,12 @@ function convertFunction(name, params, body, options) {
14942
14988
  value: convertExpression(restExpr),
14943
14989
  declarationKind: declKind
14944
14990
  });
14991
+ } else {
14992
+ reportUnsupportedExpression(
14993
+ prop,
14994
+ "Unsupported object destructuring property in HIR conversion."
14995
+ );
14996
+ return;
14945
14997
  }
14946
14998
  });
14947
14999
  }
@@ -14967,7 +15019,14 @@ function convertFunction(name, params, body, options) {
14967
15019
  value: convertExpression(memberExpr),
14968
15020
  declarationKind: declKind
14969
15021
  });
14970
- } else if (t.isRestElement(elem) && t.isIdentifier(elem.argument)) {
15022
+ } else if (t.isRestElement(elem)) {
15023
+ if (!t.isIdentifier(elem.argument)) {
15024
+ reportUnsupportedExpression(
15025
+ elem.argument,
15026
+ "Rest destructuring patterns must be identifiers in HIR conversion."
15027
+ );
15028
+ return;
15029
+ }
14971
15030
  const sliceCall = t.callExpression(
14972
15031
  t.memberExpression(t.identifier(tempName), t.identifier("slice")),
14973
15032
  [t.numericLiteral(index)]
@@ -14978,6 +15037,12 @@ function convertFunction(name, params, body, options) {
14978
15037
  value: convertExpression(sliceCall),
14979
15038
  declarationKind: declKind
14980
15039
  });
15040
+ } else {
15041
+ reportUnsupportedExpression(
15042
+ elem,
15043
+ "Unsupported array destructuring pattern in HIR conversion."
15044
+ );
15045
+ return;
14981
15046
  }
14982
15047
  });
14983
15048
  }
@@ -15140,11 +15205,15 @@ function convertFunction(name, params, body, options) {
15140
15205
  if (t.isSwitchStatement(stmt)) {
15141
15206
  const exitBlock = createBlock();
15142
15207
  blocks.push(exitBlock.block);
15208
+ const caseBlocks = stmt.cases.map(() => createBlock());
15209
+ for (const caseBlock of caseBlocks) {
15210
+ blocks.push(caseBlock.block);
15211
+ }
15143
15212
  const cases = [];
15144
15213
  let defaultTarget;
15145
- for (const switchCase of stmt.cases) {
15146
- const caseBlock = createBlock();
15147
- blocks.push(caseBlock.block);
15214
+ for (let index = 0; index < stmt.cases.length; index++) {
15215
+ const switchCase = stmt.cases[index];
15216
+ const caseBlock = caseBlocks[index];
15148
15217
  if (switchCase.test) {
15149
15218
  cases.push({
15150
15219
  test: convertExpression(switchCase.test),
@@ -15153,20 +15222,26 @@ function convertFunction(name, params, body, options) {
15153
15222
  } else {
15154
15223
  defaultTarget = caseBlock.block.id;
15155
15224
  }
15225
+ }
15226
+ cfgContext.loopStack.push({
15227
+ breakTarget: exitBlock.block.id
15228
+ });
15229
+ for (let index = 0; index < stmt.cases.length; index++) {
15230
+ const switchCase = stmt.cases[index];
15231
+ const caseBlock = caseBlocks[index];
15232
+ const nextCaseBlock = caseBlocks[index + 1];
15233
+ const fallthroughTarget = nextCaseBlock ? nextCaseBlock.block.id : exitBlock.block.id;
15156
15234
  let caseBuilder = caseBlock;
15157
15235
  for (const s of switchCase.consequent) {
15158
- if (t.isBreakStatement(s)) {
15159
- caseBuilder.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15160
- caseBuilder.sealed = true;
15161
- break;
15162
- }
15236
+ if (caseBuilder.sealed) break;
15163
15237
  caseBuilder = processStatement(s, caseBuilder, exitBlock.block.id, cfgContext);
15164
15238
  }
15165
15239
  if (!caseBuilder.sealed) {
15166
- caseBuilder.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15240
+ caseBuilder.block.terminator = { kind: "Jump", target: fallthroughTarget };
15167
15241
  caseBuilder.sealed = true;
15168
15242
  }
15169
15243
  }
15244
+ cfgContext.loopStack.pop();
15170
15245
  if (defaultTarget === void 0) {
15171
15246
  cases.push({ target: exitBlock.block.id });
15172
15247
  } else {
@@ -15299,6 +15374,10 @@ function convertFunction(name, params, body, options) {
15299
15374
  current = exitBlock;
15300
15375
  continue;
15301
15376
  }
15377
+ current = processStatement(stmt, current, current.block.id, cfgContext);
15378
+ if (current.sealed) {
15379
+ current = startNewBlock();
15380
+ }
15302
15381
  }
15303
15382
  if (!current.sealed) {
15304
15383
  current.block.terminator = { kind: "Unreachable" };
@@ -15385,6 +15464,30 @@ function handleExpressionStatement(expr, push) {
15385
15464
  }
15386
15465
  return false;
15387
15466
  }
15467
+ function findBreakContext(ctx, label) {
15468
+ if (label) {
15469
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15470
+ const entry = ctx.loopStack[i];
15471
+ if (entry?.label === label) return entry;
15472
+ }
15473
+ return void 0;
15474
+ }
15475
+ return ctx.loopStack[ctx.loopStack.length - 1];
15476
+ }
15477
+ function findContinueContext(ctx, label) {
15478
+ if (label) {
15479
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15480
+ const entry = ctx.loopStack[i];
15481
+ if (entry?.label === label && entry.continueTarget !== void 0) return entry;
15482
+ }
15483
+ return void 0;
15484
+ }
15485
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15486
+ const entry = ctx.loopStack[i];
15487
+ if (entry?.continueTarget !== void 0) return entry;
15488
+ }
15489
+ return void 0;
15490
+ }
15388
15491
  function fillStatements(stmt, bb, jumpTarget, ctx) {
15389
15492
  if (t.isBlockStatement(stmt)) {
15390
15493
  let current = bb;
@@ -15410,8 +15513,50 @@ function fillStatements(stmt, bb, jumpTarget, ctx) {
15410
15513
  }
15411
15514
  return result;
15412
15515
  }
15413
- function processStatement(stmt, bb, jumpTarget, ctx) {
15516
+ function processStatement(stmt, bb, jumpTarget, ctx, labelOverride) {
15414
15517
  const push = (instr) => bb.block.instructions.push(instr);
15518
+ if (t.isLabeledStatement(stmt) && ctx) {
15519
+ const label = stmt.label.name;
15520
+ const body = stmt.body;
15521
+ if (t.isWhileStatement(body) || t.isForStatement(body) || t.isDoWhileStatement(body) || t.isForInStatement(body) || t.isForOfStatement(body) || t.isSwitchStatement(body)) {
15522
+ return processStatement(body, bb, jumpTarget, ctx, label);
15523
+ }
15524
+ ctx.loopStack.push({
15525
+ breakTarget: jumpTarget,
15526
+ label
15527
+ });
15528
+ try {
15529
+ return processStatement(body, bb, jumpTarget, ctx);
15530
+ } finally {
15531
+ ctx.loopStack.pop();
15532
+ }
15533
+ }
15534
+ if (t.isEmptyStatement(stmt)) {
15535
+ return bb;
15536
+ }
15537
+ if (t.isBlockStatement(stmt)) {
15538
+ const shouldScopeLabel = !!labelOverride && !!ctx;
15539
+ if (shouldScopeLabel) {
15540
+ ctx.loopStack.push({
15541
+ breakTarget: jumpTarget,
15542
+ label: labelOverride
15543
+ });
15544
+ }
15545
+ let current = bb;
15546
+ try {
15547
+ for (const inner of stmt.body) {
15548
+ current = processStatement(inner, current, jumpTarget, ctx);
15549
+ if (current.sealed) {
15550
+ return current;
15551
+ }
15552
+ }
15553
+ return current;
15554
+ } finally {
15555
+ if (shouldScopeLabel) {
15556
+ ctx.loopStack.pop();
15557
+ }
15558
+ }
15559
+ }
15415
15560
  if (t.isExpressionStatement(stmt)) {
15416
15561
  if (!handleExpressionStatement(stmt.expression, push)) {
15417
15562
  push({ kind: "Expression", value: convertExpression(stmt.expression) });
@@ -15526,6 +15671,21 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15526
15671
  });
15527
15672
  return bb;
15528
15673
  }
15674
+ if (t.isClassDeclaration(stmt) && stmt.id) {
15675
+ const classExpr = t.classExpression(
15676
+ stmt.id,
15677
+ stmt.superClass,
15678
+ stmt.body,
15679
+ stmt.decorators ?? null
15680
+ );
15681
+ push({
15682
+ kind: "Assign",
15683
+ target: { kind: "Identifier", name: stmt.id.name },
15684
+ value: convertExpression(classExpr),
15685
+ declarationKind: "let"
15686
+ });
15687
+ return bb;
15688
+ }
15529
15689
  if (t.isReturnStatement(stmt)) {
15530
15690
  bb.block.terminator = {
15531
15691
  kind: "Return",
@@ -15544,7 +15704,7 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15544
15704
  }
15545
15705
  if (t.isBreakStatement(stmt) && ctx) {
15546
15706
  const label = stmt.label?.name;
15547
- const loopCtx = label ? ctx.loopStack.find((l) => l.label === label) : ctx.loopStack[ctx.loopStack.length - 1];
15707
+ const loopCtx = findBreakContext(ctx, label);
15548
15708
  if (loopCtx) {
15549
15709
  bb.block.terminator = { kind: "Break", target: loopCtx.breakTarget, label };
15550
15710
  bb.sealed = true;
@@ -15556,7 +15716,7 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15556
15716
  }
15557
15717
  if (t.isContinueStatement(stmt) && ctx) {
15558
15718
  const label = stmt.label?.name;
15559
- const loopCtx = label ? ctx.loopStack.find((l) => l.label === label) : ctx.loopStack[ctx.loopStack.length - 1];
15719
+ const loopCtx = findContinueContext(ctx, label);
15560
15720
  if (loopCtx) {
15561
15721
  bb.block.terminator = { kind: "Continue", target: loopCtx.continueTarget, label };
15562
15722
  bb.sealed = true;
@@ -15605,7 +15765,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15605
15765
  condBlock.sealed = true;
15606
15766
  ctx.loopStack.push({
15607
15767
  breakTarget: exitBlock.block.id,
15608
- continueTarget: condBlock.block.id
15768
+ continueTarget: condBlock.block.id,
15769
+ label: labelOverride
15609
15770
  });
15610
15771
  fillStatements(stmt.body, bodyBlock, condBlock.block.id, ctx);
15611
15772
  ctx.loopStack.pop();
@@ -15650,8 +15811,9 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15650
15811
  condBlock.sealed = true;
15651
15812
  ctx.loopStack.push({
15652
15813
  breakTarget: exitBlock.block.id,
15653
- continueTarget: updateBlock.block.id
15814
+ continueTarget: updateBlock.block.id,
15654
15815
  // continue goes to update in for loop
15816
+ label: labelOverride
15655
15817
  });
15656
15818
  fillStatements(stmt.body, bodyBlock, updateBlock.block.id, ctx);
15657
15819
  ctx.loopStack.pop();
@@ -15674,7 +15836,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15674
15836
  bb.sealed = true;
15675
15837
  ctx.loopStack.push({
15676
15838
  breakTarget: exitBlock.block.id,
15677
- continueTarget: condBlock.block.id
15839
+ continueTarget: condBlock.block.id,
15840
+ label: labelOverride
15678
15841
  });
15679
15842
  fillStatements(stmt.body, bodyBlock, condBlock.block.id, ctx);
15680
15843
  ctx.loopStack.pop();
@@ -15722,7 +15885,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15722
15885
  bb.sealed = true;
15723
15886
  ctx.loopStack.push({
15724
15887
  breakTarget: exitBlock.block.id,
15725
- continueTarget: bodyBlock.block.id
15888
+ continueTarget: bodyBlock.block.id,
15889
+ label: labelOverride
15726
15890
  });
15727
15891
  fillStatements(stmt.body, bodyBlock, exitBlock.block.id, ctx);
15728
15892
  ctx.loopStack.pop();
@@ -15762,7 +15926,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15762
15926
  bb.sealed = true;
15763
15927
  ctx.loopStack.push({
15764
15928
  breakTarget: exitBlock.block.id,
15765
- continueTarget: bodyBlock.block.id
15929
+ continueTarget: bodyBlock.block.id,
15930
+ label: labelOverride
15766
15931
  });
15767
15932
  fillStatements(stmt.body, bodyBlock, exitBlock.block.id, ctx);
15768
15933
  ctx.loopStack.pop();
@@ -15771,11 +15936,15 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15771
15936
  if (t.isSwitchStatement(stmt) && ctx) {
15772
15937
  const exitBlock = ctx.createBlock();
15773
15938
  ctx.blocks.push(exitBlock.block);
15939
+ const caseBlocks = stmt.cases.map(() => ctx.createBlock());
15940
+ for (const caseBlock of caseBlocks) {
15941
+ ctx.blocks.push(caseBlock.block);
15942
+ }
15774
15943
  const cases = [];
15775
15944
  let defaultTarget;
15776
- for (const switchCase of stmt.cases) {
15777
- const caseBlock = ctx.createBlock();
15778
- ctx.blocks.push(caseBlock.block);
15945
+ for (let index = 0; index < stmt.cases.length; index++) {
15946
+ const switchCase = stmt.cases[index];
15947
+ const caseBlock = caseBlocks[index];
15779
15948
  if (switchCase.test) {
15780
15949
  cases.push({
15781
15950
  test: convertExpression(switchCase.test),
@@ -15784,20 +15953,27 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15784
15953
  } else {
15785
15954
  defaultTarget = caseBlock.block.id;
15786
15955
  }
15956
+ }
15957
+ ctx.loopStack.push({
15958
+ breakTarget: exitBlock.block.id,
15959
+ label: labelOverride
15960
+ });
15961
+ for (let index = 0; index < stmt.cases.length; index++) {
15962
+ const switchCase = stmt.cases[index];
15963
+ const caseBlock = caseBlocks[index];
15964
+ const nextCaseBlock = caseBlocks[index + 1];
15965
+ const fallthroughTarget = nextCaseBlock ? nextCaseBlock.block.id : exitBlock.block.id;
15787
15966
  let current = caseBlock;
15788
15967
  for (const s of switchCase.consequent) {
15789
- if (t.isBreakStatement(s)) {
15790
- current.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15791
- current.sealed = true;
15792
- break;
15793
- }
15968
+ if (current.sealed) break;
15794
15969
  current = processStatement(s, current, exitBlock.block.id, ctx);
15795
15970
  }
15796
15971
  if (!current.sealed) {
15797
- current.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15972
+ current.block.terminator = { kind: "Jump", target: fallthroughTarget };
15798
15973
  current.sealed = true;
15799
15974
  }
15800
15975
  }
15976
+ ctx.loopStack.pop();
15801
15977
  if (defaultTarget === void 0) {
15802
15978
  cases.push({ target: exitBlock.block.id });
15803
15979
  } else {
@@ -15846,30 +16022,41 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15846
16022
  }
15847
16023
  return exitBlock;
15848
16024
  }
15849
- if (!bb.sealed) {
15850
- bb.block.terminator = { kind: "Jump", target: jumpTarget };
15851
- bb.sealed = true;
15852
- }
15853
- return bb;
16025
+ throw new HIRError(`Unsupported statement in HIR lowering: ${stmt.type}`, "BUILD_ERROR", {
16026
+ blockId: bb.block.id
16027
+ });
15854
16028
  }
15855
16029
  function convertExpression(node, options) {
15856
16030
  const loc = getLoc(node);
15857
- const convertCallArguments = (args, reactiveScope) => args.map((arg) => {
15858
- if (t.isSpreadElement(arg)) {
15859
- return {
15860
- kind: "SpreadElement",
15861
- argument: convertExpression(arg.argument),
15862
- loc: getLoc(arg)
15863
- };
15864
- }
15865
- if (t.isExpression(arg)) {
15866
- if (reactiveScope && arg === args[0] && (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg))) {
15867
- return convertExpression(arg, { reactiveScope });
16031
+ const convertCallArguments = (args, reactiveScope) => {
16032
+ const converted = [];
16033
+ for (const arg of args) {
16034
+ if (t.isSpreadElement(arg)) {
16035
+ converted.push({
16036
+ kind: "SpreadElement",
16037
+ argument: convertExpression(arg.argument),
16038
+ loc: getLoc(arg)
16039
+ });
16040
+ continue;
16041
+ }
16042
+ if (t.isExpression(arg)) {
16043
+ if (reactiveScope && arg === args[0] && (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg))) {
16044
+ converted.push(convertExpression(arg, { reactiveScope }));
16045
+ continue;
16046
+ }
16047
+ converted.push(convertExpression(arg));
16048
+ continue;
16049
+ }
16050
+ if (t.isArgumentPlaceholder?.(arg)) {
16051
+ return reportUnsupportedExpression(
16052
+ arg,
16053
+ "Argument placeholders are not supported in HIR conversion."
16054
+ );
15868
16055
  }
15869
- return convertExpression(arg);
16056
+ return reportUnsupportedExpression(arg, "Unsupported call argument in HIR conversion.");
15870
16057
  }
15871
- return void 0;
15872
- }).filter(Boolean);
16058
+ return converted;
16059
+ };
15873
16060
  const resolveReactiveScope = (callee) => {
15874
16061
  const reactiveScopes = activeBuildOptions?.reactiveScopes;
15875
16062
  if (!reactiveScopes || reactiveScopes.size === 0) return void 0;
@@ -15881,6 +16068,9 @@ function convertExpression(node, options) {
15881
16068
  }
15882
16069
  return void 0;
15883
16070
  };
16071
+ if (t.isChainExpression?.(node) || node.type === "ChainExpression") {
16072
+ return convertExpression(node.expression, options);
16073
+ }
15884
16074
  if (t.isParenthesizedExpression(node) && t.isExpression(node.expression)) {
15885
16075
  return convertExpression(node.expression);
15886
16076
  }
@@ -15938,7 +16128,13 @@ function convertExpression(node, options) {
15938
16128
  return call;
15939
16129
  }
15940
16130
  if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
15941
- const propertyNode = t.isPrivateName(node.property) ? t.identifier(node.property.id.name) : node.property;
16131
+ if (t.isPrivateName(node.property)) {
16132
+ return reportUnsupportedExpression(
16133
+ node.property,
16134
+ "Private field access is not supported in HIR conversion."
16135
+ );
16136
+ }
16137
+ const propertyNode = node.property;
15942
16138
  const isOptional = t.isOptionalMemberExpression(node);
15943
16139
  const object = convertExpression(node.object);
15944
16140
  const property = t.isExpression(propertyNode) ? convertExpression(propertyNode) : { kind: "Literal", value: void 0 };
@@ -16004,67 +16200,106 @@ function convertExpression(node, options) {
16004
16200
  return cond;
16005
16201
  }
16006
16202
  if (t.isArrayExpression(node)) {
16203
+ if ((node.elements ?? []).some((el) => el == null)) {
16204
+ return reportUnsupportedExpression(
16205
+ node,
16206
+ "Array literal holes are not supported in HIR conversion. Use explicit undefined values instead."
16207
+ );
16208
+ }
16209
+ const elements = [];
16210
+ for (const el of node.elements ?? []) {
16211
+ if (!el) continue;
16212
+ if (t.isSpreadElement(el)) {
16213
+ elements.push({
16214
+ kind: "SpreadElement",
16215
+ argument: convertExpression(el.argument),
16216
+ loc: getLoc(el)
16217
+ });
16218
+ continue;
16219
+ }
16220
+ if (t.isExpression(el)) {
16221
+ elements.push(convertExpression(el));
16222
+ continue;
16223
+ }
16224
+ return reportUnsupportedExpression(el, "Unsupported array literal element in HIR conversion.");
16225
+ }
16007
16226
  const arr = {
16008
16227
  kind: "ArrayExpression",
16009
- elements: (node.elements ?? []).map((el) => {
16010
- if (!el) return void 0;
16011
- if (t.isSpreadElement(el)) {
16012
- return {
16013
- kind: "SpreadElement",
16014
- argument: convertExpression(el.argument),
16015
- loc: getLoc(el)
16016
- };
16017
- }
16018
- if (t.isExpression(el)) return convertExpression(el);
16019
- return void 0;
16020
- }).filter(Boolean),
16228
+ elements,
16021
16229
  loc
16022
16230
  };
16023
16231
  return arr;
16024
16232
  }
16025
16233
  if (t.isObjectExpression(node)) {
16234
+ const properties = [];
16026
16235
  const obj = {
16027
16236
  kind: "ObjectExpression",
16028
- properties: node.properties.map((prop) => {
16029
- if (t.isSpreadElement(prop)) {
16030
- return {
16031
- kind: "SpreadElement",
16032
- argument: convertExpression(prop.argument),
16033
- loc: getLoc(prop)
16034
- };
16237
+ properties,
16238
+ loc
16239
+ };
16240
+ for (const prop of node.properties) {
16241
+ if (t.isSpreadElement(prop)) {
16242
+ properties.push({
16243
+ kind: "SpreadElement",
16244
+ argument: convertExpression(prop.argument),
16245
+ loc: getLoc(prop)
16246
+ });
16247
+ continue;
16248
+ }
16249
+ if (t.isObjectMethod(prop)) {
16250
+ const keyExpr = prop.computed ? t.isExpression(prop.key) ? convertExpression(prop.key) : void 0 : t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isNumericLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isBigIntLiteral(prop.key) ? { kind: "Literal", value: BigInt(prop.key.value) } : void 0;
16251
+ if (!keyExpr) {
16252
+ return reportUnsupportedExpression(
16253
+ prop.key,
16254
+ "Unsupported object literal key in HIR conversion."
16255
+ );
16035
16256
  }
16036
- if (t.isObjectMethod(prop)) {
16037
- if (prop.computed) return void 0;
16038
- const keyExpr2 = t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isNumericLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : void 0;
16039
- if (!keyExpr2) return void 0;
16040
- const fnExpr = t.functionExpression(
16041
- null,
16042
- prop.params,
16043
- prop.body,
16044
- prop.generator,
16045
- prop.async
16257
+ const fnExpr = t.functionExpression(
16258
+ null,
16259
+ prop.params,
16260
+ prop.body,
16261
+ prop.generator,
16262
+ prop.async
16263
+ );
16264
+ properties.push({
16265
+ kind: "Property",
16266
+ key: keyExpr,
16267
+ value: convertExpression(fnExpr),
16268
+ computed: prop.computed,
16269
+ propertyKind: prop.kind ?? "method",
16270
+ loc: getLoc(prop)
16271
+ });
16272
+ continue;
16273
+ }
16274
+ if (t.isObjectProperty(prop)) {
16275
+ const keyExpr = prop.computed ? t.isExpression(prop.key) ? convertExpression(prop.key) : void 0 : t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isNumericLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isBigIntLiteral(prop.key) ? { kind: "Literal", value: BigInt(prop.key.value) } : void 0;
16276
+ if (!keyExpr) {
16277
+ return reportUnsupportedExpression(
16278
+ prop.key,
16279
+ "Unsupported object literal key in HIR conversion."
16046
16280
  );
16047
- return {
16048
- kind: "Property",
16049
- key: keyExpr2,
16050
- value: convertExpression(fnExpr),
16051
- loc: getLoc(prop)
16052
- };
16053
16281
  }
16054
- if (!t.isObjectProperty(prop) || prop.computed) return void 0;
16055
- const keyExpr = t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : t.isNumericLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : void 0;
16056
- if (!keyExpr) return void 0;
16057
- if (!t.isExpression(prop.value)) return void 0;
16058
- return {
16282
+ if (!t.isExpression(prop.value)) {
16283
+ return reportUnsupportedExpression(
16284
+ prop.value,
16285
+ "Unsupported object literal value in HIR conversion."
16286
+ );
16287
+ }
16288
+ properties.push({
16059
16289
  kind: "Property",
16060
16290
  key: keyExpr,
16061
16291
  value: convertExpression(prop.value),
16292
+ computed: prop.computed,
16062
16293
  shorthand: prop.shorthand && t.isIdentifier(prop.value),
16063
16294
  loc: getLoc(prop)
16064
- };
16065
- }).filter(Boolean),
16066
- loc
16067
- };
16295
+ });
16296
+ continue;
16297
+ }
16298
+ return reportUnsupportedExpression(
16299
+ prop,
16300
+ "Unsupported object literal property in HIR conversion."
16301
+ );
16302
+ }
16068
16303
  return obj;
16069
16304
  }
16070
16305
  if (t.isJSXElement(node)) {
@@ -16092,6 +16327,11 @@ function convertExpression(node, options) {
16092
16327
  value: convertJSXElement(child),
16093
16328
  loc: getLoc(child)
16094
16329
  });
16330
+ } else if (t.isJSXSpreadChild(child)) {
16331
+ return reportUnsupportedExpression(
16332
+ child,
16333
+ "JSX spread children are not supported in HIR conversion."
16334
+ );
16095
16335
  } else if (t.isJSXFragment(child)) {
16096
16336
  for (const fragChild of child.children) {
16097
16337
  if (t.isJSXText(fragChild)) {
@@ -16113,6 +16353,11 @@ function convertExpression(node, options) {
16113
16353
  value: convertJSXElement(fragChild),
16114
16354
  loc: getLoc(fragChild)
16115
16355
  });
16356
+ } else if (t.isJSXSpreadChild(fragChild)) {
16357
+ return reportUnsupportedExpression(
16358
+ fragChild,
16359
+ "JSX spread children are not supported in HIR conversion."
16360
+ );
16116
16361
  }
16117
16362
  }
16118
16363
  }
@@ -16308,7 +16553,10 @@ function convertJSXElement(node) {
16308
16553
  tagName = convertJSXMemberExpr(opening.name);
16309
16554
  isComponent = true;
16310
16555
  } else {
16311
- tagName = "div";
16556
+ return reportUnsupportedExpression(
16557
+ opening.name,
16558
+ `Unsupported JSX tag syntax '${opening.name.type}' in HIR conversion`
16559
+ );
16312
16560
  }
16313
16561
  const attributes = [];
16314
16562
  for (const attr of opening.attributes) {
@@ -16320,17 +16568,33 @@ function convertJSXElement(node) {
16320
16568
  spreadExpr: convertExpression(attr.argument),
16321
16569
  loc: getLoc(attr)
16322
16570
  });
16323
- } else if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
16571
+ } else if (t.isJSXAttribute(attr)) {
16572
+ const nameNode = attr.name;
16573
+ let attrName = null;
16574
+ if (t.isJSXIdentifier(attr.name)) {
16575
+ attrName = attr.name.name;
16576
+ } else if (t.isJSXNamespacedName(attr.name)) {
16577
+ attrName = `${attr.name.namespace.name}:${attr.name.name.name}`;
16578
+ } else {
16579
+ return reportUnsupportedExpression(
16580
+ nameNode,
16581
+ "Unsupported JSX attribute name in HIR conversion"
16582
+ );
16583
+ }
16324
16584
  let value = null;
16325
16585
  if (attr.value) {
16326
16586
  if (t.isStringLiteral(attr.value)) {
16327
16587
  value = { kind: "Literal", value: attr.value.value, loc: getLoc(attr.value) };
16328
16588
  } else if (t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
16329
16589
  value = convertExpression(attr.value.expression);
16590
+ } else if (t.isJSXElement(attr.value)) {
16591
+ value = convertJSXElement(attr.value);
16592
+ } else if (t.isJSXFragment(attr.value)) {
16593
+ value = convertExpression(attr.value);
16330
16594
  }
16331
16595
  }
16332
16596
  attributes.push({
16333
- name: attr.name.name,
16597
+ name: attrName,
16334
16598
  value,
16335
16599
  loc: getLoc(attr)
16336
16600
  });
@@ -16357,6 +16621,11 @@ function convertJSXElement(node) {
16357
16621
  value: convertJSXElement(child),
16358
16622
  loc: getLoc(child)
16359
16623
  });
16624
+ } else if (t.isJSXSpreadChild(child)) {
16625
+ return reportUnsupportedExpression(
16626
+ child,
16627
+ "JSX spread children are not supported in HIR conversion."
16628
+ );
16360
16629
  } else if (t.isJSXFragment(child)) {
16361
16630
  for (const fragChild of child.children) {
16362
16631
  if (t.isJSXText(fragChild)) {
@@ -16378,6 +16647,11 @@ function convertJSXElement(node) {
16378
16647
  value: convertJSXElement(fragChild),
16379
16648
  loc: getLoc(fragChild)
16380
16649
  });
16650
+ } else if (t.isJSXSpreadChild(fragChild)) {
16651
+ return reportUnsupportedExpression(
16652
+ fragChild,
16653
+ "JSX spread children are not supported in HIR conversion."
16654
+ );
16381
16655
  }
16382
16656
  }
16383
16657
  }
@@ -16613,7 +16887,7 @@ var DiagnosticMessages = {
16613
16887
  ["FICT-R001" /* FICT_R001 */]: "Expression crosses reactive region boundary.",
16614
16888
  ["FICT-R002" /* FICT_R002 */]: "Scope escape detected, value may not be tracked.",
16615
16889
  ["FICT-R003" /* FICT_R003 */]: "Expression cannot be memoized automatically.",
16616
- ["FICT-R004" /* FICT_R004 */]: "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
16890
+ ["FICT-R004" /* FICT_R004 */]: "Reactive creation inside non-JSX control flow may not auto-dispose in complex paths. Prefer createScope/runInScope (or JSX-managed regions) for explicit lifecycle control.",
16617
16891
  ["FICT-R005" /* FICT_R005 */]: "Function captures reactive variables from outer scope; pass them as parameters or memoize explicitly to avoid hidden dependencies.",
16618
16892
  ["FICT-X001" /* FICT_X001 */]: "Object is recreated on each render, consider memoizing.",
16619
16893
  ["FICT-X002" /* FICT_X002 */]: "Array is recreated on each render, consider memoizing.",
@@ -16643,7 +16917,7 @@ var DiagnosticSeverities = {
16643
16917
  ["FICT-R001" /* FICT_R001 */]: "info" /* Info */,
16644
16918
  ["FICT-R002" /* FICT_R002 */]: "warning" /* Warning */,
16645
16919
  ["FICT-R003" /* FICT_R003 */]: "info" /* Info */,
16646
- ["FICT-R004" /* FICT_R004 */]: "warning" /* Warning */,
16920
+ ["FICT-R004" /* FICT_R004 */]: "error" /* Error */,
16647
16921
  ["FICT-R005" /* FICT_R005 */]: "warning" /* Warning */,
16648
16922
  ["FICT-X001" /* FICT_X001 */]: "hint" /* Hint */,
16649
16923
  ["FICT-X002" /* FICT_X002 */]: "hint" /* Hint */,
@@ -17123,7 +17397,11 @@ function rewriteExprWithMap(expr, rewrites) {
17123
17397
  if (p.kind === "SpreadElement") {
17124
17398
  return { ...p, argument: rewriteExprWithMap(p.argument, rewrites) };
17125
17399
  }
17126
- return { ...p, value: rewriteExprWithMap(p.value, rewrites) };
17400
+ return {
17401
+ ...p,
17402
+ key: p.computed ? rewriteExprWithMap(p.key, rewrites) : p.key,
17403
+ value: rewriteExprWithMap(p.value, rewrites)
17404
+ };
17127
17405
  })
17128
17406
  };
17129
17407
  case "ImportExpression":
@@ -17270,7 +17548,11 @@ function toSSA(fn) {
17270
17548
  if (p.kind === "SpreadElement") {
17271
17549
  return { ...p, argument: renameExpr(p.argument) };
17272
17550
  }
17273
- return { ...p, value: renameExpr(p.value) };
17551
+ return {
17552
+ ...p,
17553
+ key: p.computed ? renameExpr(p.key) : p.key,
17554
+ value: renameExpr(p.value)
17555
+ };
17274
17556
  })
17275
17557
  };
17276
17558
  default:
@@ -18700,16 +18982,104 @@ function structurizeBranchUntilJoin(ctx, block, term, outerJoin) {
18700
18982
  return ifNode;
18701
18983
  }
18702
18984
  function structurizeSwitch(ctx, block, term) {
18985
+ const uniqueTargets = Array.from(new Set(term.cases.map((c) => c.target)));
18986
+ const joinBlock = findSwitchJoinBlock(ctx, uniqueTargets);
18703
18987
  const cases = [];
18988
+ const emittedBeforeSwitch = new Set(ctx.emitted);
18989
+ const emittedBySwitchCases = /* @__PURE__ */ new Set();
18704
18990
  for (const c of term.cases) {
18705
- const body = structurizeBlock(ctx, c.target);
18706
- cases.push({ test: c.test ?? null, body });
18991
+ const caseCtx = {
18992
+ ...ctx,
18993
+ emitted: new Set(emittedBeforeSwitch),
18994
+ processing: new Set(ctx.processing)
18995
+ };
18996
+ const body = joinBlock !== void 0 ? structurizeBlockUntilJoin(caseCtx, c.target, joinBlock) : structurizeBlock(caseCtx, c.target);
18997
+ for (const emittedBlock of caseCtx.emitted) {
18998
+ if (!emittedBeforeSwitch.has(emittedBlock)) {
18999
+ emittedBySwitchCases.add(emittedBlock);
19000
+ }
19001
+ }
19002
+ const normalizedBody = appendSwitchCaseBreak(body);
19003
+ cases.push({ test: c.test ?? null, body: normalizedBody });
18707
19004
  }
18708
- return {
19005
+ for (const emittedBlock of emittedBySwitchCases) {
19006
+ ctx.emitted.add(emittedBlock);
19007
+ }
19008
+ const switchNode = {
18709
19009
  kind: "switch",
18710
19010
  discriminant: term.discriminant,
18711
19011
  cases
18712
19012
  };
19013
+ if (joinBlock !== void 0 && !ctx.emitted.has(joinBlock)) {
19014
+ const joinNode = structurizeBlock(ctx, joinBlock);
19015
+ return { kind: "sequence", nodes: [switchNode, joinNode] };
19016
+ }
19017
+ return switchNode;
19018
+ }
19019
+ function findSwitchJoinBlock(ctx, caseTargets) {
19020
+ const uniqueTargets = Array.from(new Set(caseTargets));
19021
+ if (uniqueTargets.length === 0) return void 0;
19022
+ const reachableByCase = uniqueTargets.map(
19023
+ (target) => collectReachableBlocks(ctx, target, /* @__PURE__ */ new Set())
19024
+ );
19025
+ const reachableUnion = /* @__PURE__ */ new Set();
19026
+ for (const set of reachableByCase) {
19027
+ for (const id of set) reachableUnion.add(id);
19028
+ }
19029
+ const minCaseCoverage = uniqueTargets.length > 1 ? 2 : 1;
19030
+ const candidates = [];
19031
+ for (const id of reachableUnion) {
19032
+ if (uniqueTargets.includes(id)) continue;
19033
+ let reachableCases = 0;
19034
+ for (const set of reachableByCase) {
19035
+ if (set.has(id)) reachableCases++;
19036
+ }
19037
+ if (reachableCases < minCaseCoverage) continue;
19038
+ const predecessors = ctx.predecessors.get(id) ?? [];
19039
+ const predecessorCount = predecessors.filter((pred) => reachableUnion.has(pred)).length;
19040
+ candidates.push({
19041
+ id,
19042
+ reachableCases,
19043
+ predecessorCount,
19044
+ isJoinPoint: predecessors.length > 1
19045
+ });
19046
+ }
19047
+ if (candidates.length === 0) return void 0;
19048
+ candidates.sort((a, b) => {
19049
+ if (a.reachableCases !== b.reachableCases) return b.reachableCases - a.reachableCases;
19050
+ if (a.predecessorCount !== b.predecessorCount) return b.predecessorCount - a.predecessorCount;
19051
+ if (a.isJoinPoint !== b.isJoinPoint) return Number(b.isJoinPoint) - Number(a.isJoinPoint);
19052
+ return a.id - b.id;
19053
+ });
19054
+ return candidates[0]?.id;
19055
+ }
19056
+ function appendSwitchCaseBreak(body) {
19057
+ if (isSwitchCaseTerminated(body)) return body;
19058
+ if (body.kind === "sequence") {
19059
+ return { kind: "sequence", nodes: [...body.nodes, { kind: "break" }] };
19060
+ }
19061
+ return { kind: "sequence", nodes: [body, { kind: "break" }] };
19062
+ }
19063
+ function isSwitchCaseTerminated(node) {
19064
+ if (!node) return false;
19065
+ switch (node.kind) {
19066
+ case "return":
19067
+ case "throw":
19068
+ case "break":
19069
+ case "continue":
19070
+ return true;
19071
+ case "sequence":
19072
+ return node.nodes.length > 0 && isSwitchCaseTerminated(node.nodes[node.nodes.length - 1]);
19073
+ case "block":
19074
+ return node.statements.length > 0 && isSwitchCaseTerminated(node.statements[node.statements.length - 1]);
19075
+ case "if":
19076
+ return isSwitchCaseTerminated(node.consequent) && isSwitchCaseTerminated(node.alternate ?? void 0);
19077
+ case "try":
19078
+ if (node.finalizer && isSwitchCaseTerminated(node.finalizer)) return true;
19079
+ return isSwitchCaseTerminated(node.block) && isSwitchCaseTerminated(node.handler?.body ?? void 0);
19080
+ default:
19081
+ return false;
19082
+ }
18713
19083
  }
18714
19084
  function structurizeForOf(ctx, block, term) {
18715
19085
  const body = structurizeBlock(ctx, term.body);
@@ -18911,6 +19281,7 @@ function resolveKeySet(expr, ctx, shapes) {
18911
19281
  const values = [];
18912
19282
  for (const prop of arg.properties) {
18913
19283
  if (prop.kind !== "Property") return null;
19284
+ if (prop.computed) return null;
18914
19285
  if (prop.key.kind === "Identifier") {
18915
19286
  values.push(prop.key.name);
18916
19287
  } else if (prop.key.kind === "Literal") {
@@ -19310,7 +19681,10 @@ function analyzeExpression(expr, shapes, propertyReads, ctx) {
19310
19681
  }
19311
19682
  analyzeExpression(prop.argument, shapes, propertyReads, ctx);
19312
19683
  } else if (prop.kind === "Property") {
19313
- if (prop.key.kind === "Identifier") {
19684
+ if (prop.computed) {
19685
+ shape.dynamicAccess = true;
19686
+ analyzeExpression(prop.key, shapes, propertyReads, ctx);
19687
+ } else if (prop.key.kind === "Identifier") {
19314
19688
  shape.knownKeys.add(prop.key.name);
19315
19689
  } else if (prop.key.kind === "Literal" && typeof prop.key.value === "string") {
19316
19690
  shape.knownKeys.add(prop.key.value);
@@ -19550,6 +19924,7 @@ function markEscaping(expr, shapes) {
19550
19924
  if (prop.kind === "SpreadElement") {
19551
19925
  markEscaping(prop.argument, shapes);
19552
19926
  } else if (prop.kind === "Property") {
19927
+ if (prop.computed) markEscaping(prop.key, shapes);
19553
19928
  markEscaping(prop.value, shapes);
19554
19929
  }
19555
19930
  }
@@ -19654,7 +20029,7 @@ function expressionContainsReactiveCreation(expr, memoMacroNames) {
19654
20029
  return expr.elements.some((el) => el && expressionContainsReactiveCreation(el, memoMacroNames));
19655
20030
  case "ObjectExpression":
19656
20031
  return expr.properties.some(
19657
- (prop) => prop.kind === "SpreadElement" ? expressionContainsReactiveCreation(prop.argument, memoMacroNames) : expressionContainsReactiveCreation(prop.value, memoMacroNames)
20032
+ (prop) => prop.kind === "SpreadElement" ? expressionContainsReactiveCreation(prop.argument, memoMacroNames) : prop.computed && expressionContainsReactiveCreation(prop.key, memoMacroNames) || expressionContainsReactiveCreation(prop.value, memoMacroNames)
19658
20033
  );
19659
20034
  case "ArrowFunction":
19660
20035
  if (expr.isExpression) {
@@ -19940,7 +20315,7 @@ function expressionUsesTracked(expr, ctx) {
19940
20315
  case "ObjectExpression":
19941
20316
  return expr.properties.some((p) => {
19942
20317
  if (p.kind === "SpreadElement") return expressionUsesTracked(p.argument, ctx);
19943
- return expressionUsesTracked(p.value, ctx);
20318
+ return p.computed && expressionUsesTracked(p.key, ctx) || expressionUsesTracked(p.value, ctx);
19944
20319
  });
19945
20320
  case "TemplateLiteral":
19946
20321
  return expr.expressions.some((e) => expressionUsesTracked(e, ctx));
@@ -20033,6 +20408,16 @@ function lowerStructuredNodeInternal(node, t4, ctx, declaredVars, regionResult)
20033
20408
  } : void 0;
20034
20409
  return lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx);
20035
20410
  }
20411
+ function ensureSwitchCaseBreak(stmts, t4) {
20412
+ if (stmts.length === 0) {
20413
+ return stmts;
20414
+ }
20415
+ const tail = stmts[stmts.length - 1];
20416
+ if (tail && (t4.isBreakStatement(tail) || t4.isReturnStatement(tail) || t4.isThrowStatement(tail) || t4.isContinueStatement(tail))) {
20417
+ return stmts;
20418
+ }
20419
+ return [...stmts, t4.breakStatement()];
20420
+ }
20036
20421
  function lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx) {
20037
20422
  switch (node.kind) {
20038
20423
  case "sequence": {
@@ -20222,10 +20607,20 @@ function lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx) {
20222
20607
  return [t4.forInStatement(left, right, body)];
20223
20608
  }
20224
20609
  case "switch": {
20225
- const cases = node.cases.map((c) => {
20226
- const stmts = lowerNodeWithRegionContext(c.body, t4, ctx, declaredVars, regionCtx);
20227
- return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20228
- });
20610
+ const prevConditional = ctx.inConditional ?? 0;
20611
+ ctx.inConditional = prevConditional + 1;
20612
+ let cases;
20613
+ try {
20614
+ cases = node.cases.map((c) => {
20615
+ const stmts = ensureSwitchCaseBreak(
20616
+ lowerNodeWithRegionContext(c.body, t4, ctx, declaredVars, regionCtx),
20617
+ t4
20618
+ );
20619
+ return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20620
+ });
20621
+ } finally {
20622
+ ctx.inConditional = prevConditional;
20623
+ }
20229
20624
  return [t4.switchStatement(lowerExpressionWithDeSSA(node.discriminant, ctx), cases)];
20230
20625
  }
20231
20626
  case "try": {
@@ -20514,19 +20909,29 @@ function lowerStructuredNodeForRegion(node, region, t4, ctx, declaredVars, regio
20514
20909
  return [t4.forInStatement(left, right, t4.blockStatement(body))];
20515
20910
  }
20516
20911
  case "switch": {
20517
- const cases = node.cases.map((c) => {
20518
- const stmts = lowerStructuredNodeForRegion(
20519
- c.body,
20520
- region,
20521
- t4,
20522
- ctx,
20523
- declaredVars,
20524
- regionCtx,
20525
- skipInstructions
20526
- );
20527
- if (stmts.length === 0) return null;
20528
- return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20529
- }).filter((c) => !!c);
20912
+ const prevConditional = ctx.inConditional ?? 0;
20913
+ ctx.inConditional = prevConditional + 1;
20914
+ let cases;
20915
+ try {
20916
+ cases = node.cases.map((c) => {
20917
+ const stmts = ensureSwitchCaseBreak(
20918
+ lowerStructuredNodeForRegion(
20919
+ c.body,
20920
+ region,
20921
+ t4,
20922
+ ctx,
20923
+ declaredVars,
20924
+ regionCtx,
20925
+ skipInstructions
20926
+ ),
20927
+ t4
20928
+ );
20929
+ if (stmts.length === 0) return null;
20930
+ return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20931
+ }).filter((c) => !!c);
20932
+ } finally {
20933
+ ctx.inConditional = prevConditional;
20934
+ }
20530
20935
  if (cases.length === 0) return [];
20531
20936
  return [t4.switchStatement(lowerExpressionWithDeSSA(node.discriminant, ctx), cases)];
20532
20937
  }
@@ -20979,6 +21384,7 @@ function collectExprDependencies(expr) {
20979
21384
  if (p.kind === "SpreadElement") {
20980
21385
  visit(p.argument);
20981
21386
  } else {
21387
+ if (p.computed) visit(p.key);
20982
21388
  visit(p.value);
20983
21389
  }
20984
21390
  });
@@ -21842,7 +22248,10 @@ function expressionContainsJSX(expr) {
21842
22248
  case "ArrayExpression":
21843
22249
  return expr.elements?.some((el) => expressionContainsJSX(el)) ?? false;
21844
22250
  case "ObjectExpression":
21845
- return expr.properties?.some((p) => expressionContainsJSX(p.value)) ?? false;
22251
+ return expr.properties?.some((p) => {
22252
+ if (p.kind === "SpreadElement") return expressionContainsJSX(p.argument);
22253
+ return (p.computed ?? false) && expressionContainsJSX(p.key) || expressionContainsJSX(p.value);
22254
+ }) ?? false;
21846
22255
  case "ConditionalExpression":
21847
22256
  return expressionContainsJSX(expr.test) || expressionContainsJSX(expr.consequent) || expressionContainsJSX(expr.alternate);
21848
22257
  case "ArrowFunction":
@@ -21956,7 +22365,7 @@ function expressionHasAwait(expr) {
21956
22365
  return expr.elements.some((el) => el && expressionHasAwait(el));
21957
22366
  case "ObjectExpression":
21958
22367
  return expr.properties.some(
21959
- (prop) => prop.kind === "Property" && expressionHasAwait(prop.value) || prop.kind === "SpreadElement" && expressionHasAwait(prop.argument)
22368
+ (prop) => prop.kind === "Property" && ((prop.computed ?? false) && expressionHasAwait(prop.key) || expressionHasAwait(prop.value)) || prop.kind === "SpreadElement" && expressionHasAwait(prop.argument)
21960
22369
  );
21961
22370
  case "TemplateLiteral":
21962
22371
  return expr.expressions.some((ex) => expressionHasAwait(ex));
@@ -22027,6 +22436,7 @@ function collectCalledIdentifiers(fn) {
22027
22436
  if (p.kind === "SpreadElement") {
22028
22437
  visitExpr(p.argument);
22029
22438
  } else {
22439
+ if (p.computed) visitExpr(p.key);
22030
22440
  visitExpr(p.value);
22031
22441
  }
22032
22442
  });
@@ -22253,6 +22663,7 @@ function analyzeHookReturnInfo(fn, ctx) {
22253
22663
  if (expr.kind === "ObjectExpression") {
22254
22664
  expr.properties.forEach((prop) => {
22255
22665
  if (prop.kind !== "Property") return;
22666
+ if (prop.computed) return;
22256
22667
  const keyName = prop.key.kind === "Identifier" ? prop.key.name : prop.key.kind === "Literal" ? String(prop.key.value) : void 0;
22257
22668
  if (!keyName) return;
22258
22669
  if (prop.value.kind === "Identifier") {
@@ -22561,6 +22972,7 @@ function collectExpressionIdentifiers(expr, into) {
22561
22972
  collectExpressionIdentifiers(prop.argument, into);
22562
22973
  return;
22563
22974
  }
22975
+ if (prop.computed) collectExpressionIdentifiers(prop.key, into);
22564
22976
  collectExpressionIdentifiers(prop.value, into);
22565
22977
  });
22566
22978
  return;
@@ -22678,6 +23090,7 @@ function collectExpressionIdentifiersDeep(expr, into, bound = /* @__PURE__ */ ne
22678
23090
  collectExpressionIdentifiersDeep(prop.argument, into, bound);
22679
23091
  return;
22680
23092
  }
23093
+ if (prop.computed) collectExpressionIdentifiersDeep(prop.key, into, bound);
22681
23094
  collectExpressionIdentifiersDeep(prop.value, into, bound);
22682
23095
  });
22683
23096
  return;
@@ -24184,6 +24597,28 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
24184
24597
  if (p.kind === "SpreadElement") {
24185
24598
  return t4.spreadElement(lowerExpression(p.argument, ctx));
24186
24599
  }
24600
+ const keyIsIdentifier = !p.computed && p.key.kind === "Identifier";
24601
+ const keyIdent = keyIsIdentifier ? p.key.name : "";
24602
+ const keyNode = p.computed ? lowerExpression(p.key, ctx) : keyIsIdentifier ? t4.identifier(keyIdent) : lowerExpression(p.key, ctx);
24603
+ if (p.propertyKind && p.propertyKind !== "init") {
24604
+ const valueExpr2 = lowerExpression(p.value, ctx);
24605
+ if (!t4.isFunctionExpression(valueExpr2)) {
24606
+ throw new HIRError(
24607
+ `Object method property did not lower to function expression.`,
24608
+ "CODEGEN_ERROR"
24609
+ );
24610
+ }
24611
+ const method = t4.objectMethod(
24612
+ p.propertyKind === "method" ? "method" : p.propertyKind,
24613
+ keyNode,
24614
+ valueExpr2.params,
24615
+ valueExpr2.body,
24616
+ !!p.computed
24617
+ );
24618
+ method.async = valueExpr2.async;
24619
+ method.generator = valueExpr2.generator;
24620
+ return method;
24621
+ }
24187
24622
  const usesTracked = !!ctx.inPropsContext && (!ctx.nonReactiveScopeDepth || ctx.nonReactiveScopeDepth === 0) && p.value.kind !== "ArrowFunction" && p.value.kind !== "FunctionExpression" && expressionUsesTracked(p.value, ctx);
24188
24623
  const valueExprRaw = usesTracked ? lowerTrackedExpression(p.value, ctx) : lowerExpression(p.value, ctx);
24189
24624
  const shouldMemoProp = usesTracked && !t4.isIdentifier(valueExprRaw) && !t4.isMemberExpression(valueExprRaw) && !t4.isLiteral(valueExprRaw);
@@ -24199,13 +24634,11 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
24199
24634
  t4.arrowFunctionExpression([], valueExprRaw)
24200
24635
  ]);
24201
24636
  })() : valueExprRaw;
24202
- const keyName = p.key.kind === "Identifier" ? p.key.name : String(p.key.value ?? "");
24203
- const keyNode = p.key.kind === "Identifier" ? t4.identifier(keyName) : t4.stringLiteral(keyName);
24204
- const useShorthand = p.shorthand && t4.isIdentifier(valueExpr) && p.key.kind === "Identifier" && deSSAVarName(keyName) === valueExpr.name;
24637
+ const useShorthand = p.shorthand && t4.isIdentifier(valueExpr) && keyIsIdentifier && deSSAVarName(keyIdent) === valueExpr.name;
24205
24638
  return t4.objectProperty(
24206
24639
  useShorthand ? t4.identifier(valueExpr.name) : keyNode,
24207
24640
  valueExpr,
24208
- false,
24641
+ !!p.computed,
24209
24642
  useShorthand
24210
24643
  );
24211
24644
  })
@@ -24675,6 +25108,7 @@ function collectExpressionDependencies(expr, deps) {
24675
25108
  if (p.kind === "SpreadElement") {
24676
25109
  collectExpressionDependencies(p.argument, deps);
24677
25110
  } else {
25111
+ if (p.computed) collectExpressionDependencies(p.key, deps);
24678
25112
  collectExpressionDependencies(p.value, deps);
24679
25113
  }
24680
25114
  });
@@ -25199,6 +25633,7 @@ function countExpressionNodes(expr) {
25199
25633
  case "ObjectExpression":
25200
25634
  for (const prop of expr.properties) {
25201
25635
  if (prop.kind === "Property") {
25636
+ if (prop.computed) count += countExpressionNodes(prop.key);
25202
25637
  count += countExpressionNodes(prop.value);
25203
25638
  } else if (prop.kind === "SpreadElement") {
25204
25639
  count += countExpressionNodes(prop.argument);
@@ -26560,7 +26995,7 @@ function hirExpressionUsesIdentifiers(expr, names) {
26560
26995
  if (prop.kind === "SpreadElement") {
26561
26996
  return hirExpressionUsesIdentifiers(prop.argument, names);
26562
26997
  }
26563
- return hirExpressionUsesIdentifiers(prop.key, names) || hirExpressionUsesIdentifiers(prop.value, names);
26998
+ return (prop.computed ?? false) && hirExpressionUsesIdentifiers(prop.key, names) || hirExpressionUsesIdentifiers(prop.value, names);
26564
26999
  });
26565
27000
  case "TemplateLiteral":
26566
27001
  return expr.expressions.some((e) => hirExpressionUsesIdentifiers(e, names));
@@ -27370,18 +27805,82 @@ function lowerTopLevelStatementBlock(statements, ctx, t4, name = "__module_segme
27370
27805
  }
27371
27806
  function transformControlFlowReturns(statements, ctx) {
27372
27807
  const { t: t4 } = ctx;
27808
+ const reactiveAccessorNames = /* @__PURE__ */ new Set([
27809
+ ...ctx.trackedVars,
27810
+ ...ctx.signalVars ?? [],
27811
+ ...ctx.memoVars ?? [],
27812
+ ...ctx.aliasVars ?? [],
27813
+ ...ctx.storeVars ?? []
27814
+ ]);
27373
27815
  const toStatements = (node) => t4.isBlockStatement(node) ? node.body : [node];
27374
27816
  const endsWithReturn = (stmts) => {
27375
27817
  if (stmts.length === 0) return false;
27376
27818
  const tail = stmts[stmts.length - 1];
27377
27819
  if (t4.isReturnStatement(tail)) return true;
27820
+ if (t4.isBlockStatement(tail)) {
27821
+ return endsWithReturn(tail.body);
27822
+ }
27378
27823
  if (t4.isIfStatement(tail) && tail.consequent && tail.alternate) {
27379
27824
  const conseqStmts = toStatements(tail.consequent);
27380
27825
  const altStmts = toStatements(tail.alternate);
27381
27826
  return endsWithReturn(conseqStmts) && endsWithReturn(altStmts);
27382
27827
  }
27828
+ if (t4.isTryStatement(tail)) {
27829
+ if (tail.finalizer && endsWithReturn(tail.finalizer.body)) return true;
27830
+ if (!tail.handler) return false;
27831
+ return endsWithReturn(tail.block.body) && endsWithReturn(tail.handler.body.body);
27832
+ }
27383
27833
  return false;
27384
27834
  };
27835
+ function hasNodeMatch(nodes, predicate) {
27836
+ let found = false;
27837
+ const visit = (node) => {
27838
+ if (!node || found) return;
27839
+ if (predicate(node)) {
27840
+ found = true;
27841
+ return;
27842
+ }
27843
+ const keys = t4.VISITOR_KEYS;
27844
+ const visitorKeys = keys?.[node.type] ?? [];
27845
+ for (const key of visitorKeys) {
27846
+ const value = node[key];
27847
+ if (Array.isArray(value)) {
27848
+ for (const child of value) {
27849
+ if (child && typeof child === "object" && "type" in child) {
27850
+ visit(child);
27851
+ }
27852
+ if (found) return;
27853
+ }
27854
+ } else if (value && typeof value === "object" && "type" in value) {
27855
+ visit(value);
27856
+ }
27857
+ if (found) return;
27858
+ }
27859
+ };
27860
+ for (const node of nodes) {
27861
+ visit(node);
27862
+ if (found) return true;
27863
+ }
27864
+ return found;
27865
+ }
27866
+ const containsReturnStatement = (nodes) => hasNodeMatch(nodes, (node) => t4.isReturnStatement(node));
27867
+ const containsReactiveAccessorRead = (nodes) => hasNodeMatch(nodes, (node) => {
27868
+ if (!t4.isCallExpression(node) && !t4.isOptionalCallExpression(node)) return false;
27869
+ const callee = node.callee;
27870
+ return t4.isIdentifier(callee) && reactiveAccessorNames.has(callee.name);
27871
+ });
27872
+ const emitControlFlowFallbackWarning = (node, kind) => {
27873
+ const onWarn = ctx.options?.onWarn;
27874
+ if (!onWarn) return;
27875
+ const loc = node.loc?.start;
27876
+ onWarn({
27877
+ code: "FICT-R003" /* FICT_R003 */,
27878
+ message: `Reactive ${kind}-return lowering was skipped for this branch. The branch structure will not update reactively; refactor to a supported ${kind} form or use explicit runtime conditionals.`,
27879
+ fileName: ctx.options?.filename ?? "<unknown>",
27880
+ line: loc?.line ?? 0,
27881
+ column: loc ? loc.column + 1 : 0
27882
+ });
27883
+ };
27385
27884
  function buildReturnBlock(stmts) {
27386
27885
  if (stmts.length === 0) return null;
27387
27886
  for (let i = 0; i < stmts.length; i++) {
@@ -27396,33 +27895,55 @@ function transformControlFlowReturns(statements, ctx) {
27396
27895
  if (!endsWithReturn(stmts)) return null;
27397
27896
  return stmts;
27398
27897
  }
27399
- function buildBranchFunction(stmts) {
27898
+ function buildBranchFunction(stmts, options) {
27400
27899
  const block = buildReturnBlock(stmts);
27401
27900
  if (!block) return null;
27901
+ if (options?.disallowRenderHooks && containsRenderOnlyHooks(block)) return null;
27402
27902
  return t4.arrowFunctionExpression([], t4.blockStatement(block));
27403
27903
  }
27404
- function buildConditionalExpr(ifStmt, rest) {
27405
- const consequentStmts = toStatements(ifStmt.consequent);
27406
- if (!endsWithReturn(consequentStmts)) return null;
27407
- let alternateStmts = null;
27408
- if (ifStmt.alternate) {
27409
- if (rest.length > 0) return null;
27410
- alternateStmts = toStatements(ifStmt.alternate);
27411
- if (!endsWithReturn(alternateStmts)) return null;
27412
- } else {
27413
- if (rest.length === 0) return null;
27414
- alternateStmts = rest;
27415
- if (!buildReturnBlock(alternateStmts)) return null;
27904
+ function containsRenderOnlyHooks(nodes) {
27905
+ let found = false;
27906
+ const visit = (node) => {
27907
+ if (!node || found) return;
27908
+ if (t4.isCallExpression(node) || t4.isOptionalCallExpression(node)) {
27909
+ const callee = node.callee;
27910
+ if (t4.isIdentifier(callee) && callee.name.startsWith("__fictUse")) {
27911
+ if (callee.name !== "__fictUseEffect") {
27912
+ found = true;
27913
+ return;
27914
+ }
27915
+ }
27916
+ }
27917
+ const keys = t4.VISITOR_KEYS;
27918
+ const visitorKeys = keys?.[node.type] ?? [];
27919
+ for (const key of visitorKeys) {
27920
+ const value = node[key];
27921
+ if (Array.isArray(value)) {
27922
+ for (const child of value) {
27923
+ if (child && typeof child === "object" && "type" in child) {
27924
+ visit(child);
27925
+ }
27926
+ if (found) return;
27927
+ }
27928
+ } else if (value && typeof value === "object" && "type" in value) {
27929
+ visit(value);
27930
+ }
27931
+ if (found) return;
27932
+ }
27933
+ };
27934
+ for (const node of nodes) {
27935
+ visit(node);
27936
+ if (found) break;
27416
27937
  }
27417
- const trueFn = buildBranchFunction(consequentStmts);
27418
- const falseFn = alternateStmts ? buildBranchFunction(alternateStmts) : null;
27419
- if (!trueFn || !falseFn) return null;
27938
+ return found;
27939
+ }
27940
+ function buildConditionalBindingExpr(testExpr, trueFn, falseFn) {
27420
27941
  ctx.helpersUsed.add("conditional");
27421
27942
  ctx.helpersUsed.add("createElement");
27422
27943
  ctx.helpersUsed.add("onDestroy");
27423
27944
  const bindingId = genTemp(ctx, "cond");
27424
27945
  const args = [
27425
- t4.arrowFunctionExpression([], ifStmt.test),
27946
+ t4.arrowFunctionExpression([], testExpr),
27426
27947
  trueFn,
27427
27948
  t4.identifier(RUNTIME_ALIASES.createElement),
27428
27949
  falseFn
@@ -27444,15 +27965,210 @@ function transformControlFlowReturns(statements, ctx) {
27444
27965
  []
27445
27966
  );
27446
27967
  }
27447
- for (let i = 0; i < statements.length; i++) {
27448
- const stmt = statements[i];
27968
+ function buildConditionalExpr(ifStmt, rest) {
27969
+ const consequentStmts = toStatements(ifStmt.consequent);
27970
+ if (!endsWithReturn(consequentStmts)) return null;
27971
+ let alternateStmts = null;
27972
+ if (ifStmt.alternate) {
27973
+ if (rest.length > 0) return null;
27974
+ alternateStmts = toStatements(ifStmt.alternate);
27975
+ if (!endsWithReturn(alternateStmts)) return null;
27976
+ } else {
27977
+ if (rest.length === 0) return null;
27978
+ alternateStmts = rest;
27979
+ if (!buildReturnBlock(alternateStmts)) return null;
27980
+ }
27981
+ const trueFn = buildBranchFunction(consequentStmts);
27982
+ const falseFn = alternateStmts ? buildBranchFunction(alternateStmts) : null;
27983
+ if (!trueFn || !falseFn) return null;
27984
+ return buildConditionalBindingExpr(ifStmt.test, trueFn, falseFn);
27985
+ }
27986
+ function isSupportedSwitchDiscriminant(_expr) {
27987
+ return true;
27988
+ }
27989
+ function buildSwitchConditionalExpr(switchStmt, rest) {
27990
+ const discriminant = switchStmt.discriminant;
27991
+ if (!isSupportedSwitchDiscriminant(discriminant)) return null;
27992
+ const trailingStatements = rest.length > 0 ? buildReturnBlock([...rest]) : [];
27993
+ if (rest.length > 0 && !trailingStatements) return null;
27994
+ const caseEntryCache = /* @__PURE__ */ new Map();
27995
+ const resolveCaseEntry = (startIndex) => {
27996
+ if (caseEntryCache.has(startIndex)) {
27997
+ return caseEntryCache.get(startIndex) ?? null;
27998
+ }
27999
+ const entry = [];
28000
+ for (let i = startIndex; i < switchStmt.cases.length; i++) {
28001
+ const currentCase = switchStmt.cases[i];
28002
+ const consequent = currentCase.consequent;
28003
+ for (let stmtIndex = 0; stmtIndex < consequent.length; stmtIndex++) {
28004
+ const stmt = consequent[stmtIndex];
28005
+ if (t4.isBreakStatement(stmt)) {
28006
+ if (stmt.label || stmtIndex !== consequent.length - 1) {
28007
+ caseEntryCache.set(startIndex, null);
28008
+ return null;
28009
+ }
28010
+ if (!trailingStatements || trailingStatements.length === 0) {
28011
+ caseEntryCache.set(startIndex, null);
28012
+ return null;
28013
+ }
28014
+ const withTrailing2 = [...entry, ...trailingStatements];
28015
+ caseEntryCache.set(startIndex, withTrailing2);
28016
+ return withTrailing2;
28017
+ }
28018
+ if (t4.isContinueStatement(stmt)) {
28019
+ caseEntryCache.set(startIndex, null);
28020
+ return null;
28021
+ }
28022
+ entry.push(stmt);
28023
+ }
28024
+ if (endsWithReturn(entry)) {
28025
+ caseEntryCache.set(startIndex, entry);
28026
+ return entry;
28027
+ }
28028
+ }
28029
+ if (!trailingStatements || trailingStatements.length === 0) {
28030
+ caseEntryCache.set(startIndex, null);
28031
+ return null;
28032
+ }
28033
+ const withTrailing = [...entry, ...trailingStatements];
28034
+ caseEntryCache.set(startIndex, withTrailing);
28035
+ return withTrailing;
28036
+ };
28037
+ const branches = [];
28038
+ let defaultStatements = null;
28039
+ for (let i = 0; i < switchStmt.cases.length; i++) {
28040
+ const caseNode = switchStmt.cases[i];
28041
+ const statements2 = resolveCaseEntry(i);
28042
+ if (!statements2) return null;
28043
+ if (caseNode.test) {
28044
+ branches.push({
28045
+ tests: [caseNode.test],
28046
+ statements: statements2
28047
+ });
28048
+ } else {
28049
+ defaultStatements = statements2;
28050
+ }
28051
+ }
28052
+ if (branches.length === 0 && !defaultStatements) return null;
28053
+ const fallbackStatements = defaultStatements ?? trailingStatements;
28054
+ if (!fallbackStatements || fallbackStatements.length === 0) return null;
28055
+ const fallbackFn = buildBranchFunction(fallbackStatements, { disallowRenderHooks: true });
28056
+ if (!fallbackFn) return null;
28057
+ ctx.helpersUsed.add("memo");
28058
+ const discriminantAccessor = genTemp(ctx, "switchDisc");
28059
+ const discriminantMemoDecl = t4.variableDeclaration("const", [
28060
+ t4.variableDeclarator(
28061
+ discriminantAccessor,
28062
+ t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), [
28063
+ t4.arrowFunctionExpression(
28064
+ [],
28065
+ t4.cloneNode(discriminant, true)
28066
+ )
28067
+ ])
28068
+ )
28069
+ ]);
28070
+ let currentExpr = t4.callExpression(
28071
+ t4.arrowFunctionExpression(
28072
+ [],
28073
+ t4.blockStatement(fallbackFn.body.body)
28074
+ ),
28075
+ []
28076
+ );
28077
+ for (let i = branches.length - 1; i >= 0; i--) {
28078
+ const branch = branches[i];
28079
+ const trueFn = buildBranchFunction(branch.statements, { disallowRenderHooks: true });
28080
+ if (!trueFn) return null;
28081
+ const falseFn = t4.arrowFunctionExpression(
28082
+ [],
28083
+ t4.blockStatement([t4.returnStatement(currentExpr)])
28084
+ );
28085
+ const comparisons = branch.tests.map(
28086
+ (test) => t4.binaryExpression(
28087
+ "===",
28088
+ t4.callExpression(t4.cloneNode(discriminantAccessor), []),
28089
+ t4.cloneNode(test, true)
28090
+ )
28091
+ );
28092
+ if (comparisons.length === 0) return null;
28093
+ const testExpr = comparisons.slice(1).reduce(
28094
+ (acc, expr) => t4.logicalExpression("||", acc, expr),
28095
+ comparisons[0]
28096
+ );
28097
+ currentExpr = buildConditionalBindingExpr(testExpr, trueFn, falseFn);
28098
+ }
28099
+ return t4.callExpression(
28100
+ t4.arrowFunctionExpression(
28101
+ [],
28102
+ t4.blockStatement([discriminantMemoDecl, t4.returnStatement(currentExpr)])
28103
+ ),
28104
+ []
28105
+ );
28106
+ }
28107
+ let nestedChanged = false;
28108
+ const rewrittenStatements = statements.map((stmt) => {
28109
+ if (!t4.isTryStatement(stmt)) return stmt;
28110
+ const transformedTryBlock = transformControlFlowReturns(stmt.block.body, ctx);
28111
+ const nextTryBlockBody = transformedTryBlock ?? stmt.block.body;
28112
+ let nextHandler = stmt.handler;
28113
+ if (stmt.handler) {
28114
+ const transformedCatchBlock = transformControlFlowReturns(stmt.handler.body.body, ctx);
28115
+ if (transformedCatchBlock) {
28116
+ nextHandler = t4.catchClause(
28117
+ stmt.handler.param ? t4.cloneNode(stmt.handler.param, true) : null,
28118
+ t4.blockStatement(transformedCatchBlock)
28119
+ );
28120
+ }
28121
+ }
28122
+ let nextFinalizer = stmt.finalizer;
28123
+ if (stmt.finalizer) {
28124
+ const transformedFinalizer = transformControlFlowReturns(stmt.finalizer.body, ctx);
28125
+ if (transformedFinalizer) {
28126
+ nextFinalizer = t4.blockStatement(transformedFinalizer);
28127
+ }
28128
+ }
28129
+ if (!transformedTryBlock && nextHandler === stmt.handler && nextFinalizer === stmt.finalizer) {
28130
+ return stmt;
28131
+ }
28132
+ nestedChanged = true;
28133
+ return t4.tryStatement(
28134
+ t4.blockStatement(nextTryBlockBody),
28135
+ nextHandler,
28136
+ nextFinalizer ? t4.cloneNode(nextFinalizer, true) : null
28137
+ );
28138
+ });
28139
+ for (let i = 0; i < rewrittenStatements.length; i++) {
28140
+ const stmt = rewrittenStatements[i];
27449
28141
  if (!t4.isIfStatement(stmt)) continue;
27450
- const conditionalExpr = buildConditionalExpr(stmt, statements.slice(i + 1));
27451
- if (!conditionalExpr) continue;
27452
- const prefix = statements.slice(0, i);
28142
+ const rest = rewrittenStatements.slice(i + 1);
28143
+ const conditionalExpr = buildConditionalExpr(stmt, rest);
28144
+ if (!conditionalExpr) {
28145
+ const hasReturn = containsReturnStatement([stmt, ...rest]);
28146
+ const hasReactiveReads = containsReactiveAccessorRead([stmt, ...rest]);
28147
+ if (hasReturn && hasReactiveReads) {
28148
+ emitControlFlowFallbackWarning(stmt, "if");
28149
+ }
28150
+ continue;
28151
+ }
28152
+ const prefix = rewrittenStatements.slice(0, i);
27453
28153
  return [...prefix, t4.returnStatement(conditionalExpr)];
27454
28154
  }
27455
- return null;
28155
+ for (let i = 0; i < rewrittenStatements.length; i++) {
28156
+ const stmt = rewrittenStatements[i];
28157
+ if (!t4.isSwitchStatement(stmt)) continue;
28158
+ const rest = rewrittenStatements.slice(i + 1);
28159
+ const conditionalExpr = buildSwitchConditionalExpr(stmt, rest);
28160
+ if (!conditionalExpr) {
28161
+ const hasReturn = containsReturnStatement([stmt, ...rest]);
28162
+ const hasReactiveReads = containsReactiveAccessorRead([stmt, ...rest]);
28163
+ if (hasReturn && hasReactiveReads) {
28164
+ emitControlFlowFallbackWarning(stmt, "switch");
28165
+ }
28166
+ continue;
28167
+ }
28168
+ const prefix = rewrittenStatements.slice(0, i);
28169
+ return [...prefix, t4.returnStatement(conditionalExpr)];
28170
+ }
28171
+ return nestedChanged ? rewrittenStatements : null;
27456
28172
  }
27457
28173
  function lowerFunctionWithRegions(fn, ctx, options) {
27458
28174
  const { t: t4 } = ctx;
@@ -28314,6 +29030,7 @@ function collectRootNodesFromExpression(expr, onBinding, onEffect) {
28314
29030
  if (prop.kind === "SpreadElement") {
28315
29031
  visit(prop.argument, shadowed);
28316
29032
  } else {
29033
+ if (prop.computed) visit(prop.key, shadowed);
28317
29034
  visit(prop.value, shadowed);
28318
29035
  }
28319
29036
  });
@@ -28458,6 +29175,14 @@ function collectDependenciesFromExpression(expr, deps, includeFunctionBodies, sh
28458
29175
  shadowed
28459
29176
  );
28460
29177
  } else {
29178
+ if (prop.computed) {
29179
+ collectDependenciesFromExpression(
29180
+ prop.key,
29181
+ deps,
29182
+ includeFunctionBodies,
29183
+ shadowed
29184
+ );
29185
+ }
28461
29186
  collectDependenciesFromExpression(
28462
29187
  prop.value,
28463
29188
  deps,
@@ -29420,7 +30145,7 @@ function expressionDependsOnReactive(expr, ctx) {
29420
30145
  if (prop.kind === "SpreadElement") {
29421
30146
  return expressionDependsOnReactive(prop.argument, ctx);
29422
30147
  }
29423
- return expressionDependsOnReactive(prop.value, ctx);
30148
+ return prop.computed && expressionDependsOnReactive(prop.key, ctx) || expressionDependsOnReactive(prop.value, ctx);
29424
30149
  });
29425
30150
  case "TemplateLiteral":
29426
30151
  return expr.expressions.some((e) => expressionDependsOnReactive(e, ctx));
@@ -29520,6 +30245,7 @@ function collectWriteTargets(expr) {
29520
30245
  if (prop.kind === "SpreadElement") {
29521
30246
  visit(prop.argument);
29522
30247
  } else {
30248
+ if (prop.computed) visit(prop.key);
29523
30249
  visit(prop.value);
29524
30250
  }
29525
30251
  });
@@ -29593,6 +30319,7 @@ function collectMemberCallTargets(expr) {
29593
30319
  if (prop.kind === "SpreadElement") {
29594
30320
  visit(prop.argument);
29595
30321
  } else {
30322
+ if (prop.computed) visit(prop.key);
29596
30323
  visit(prop.value);
29597
30324
  }
29598
30325
  });
@@ -29720,7 +30447,7 @@ function expressionContainsImpureMarkers(expr) {
29720
30447
  if (prop.kind === "SpreadElement") {
29721
30448
  return expressionContainsImpureMarkers(prop.argument);
29722
30449
  }
29723
- return expressionContainsImpureMarkers(prop.value);
30450
+ return prop.computed && expressionContainsImpureMarkers(prop.key) || expressionContainsImpureMarkers(prop.value);
29724
30451
  });
29725
30452
  case "TemplateLiteral":
29726
30453
  return expr.expressions.some((e) => expressionContainsImpureMarkers(e));
@@ -29925,6 +30652,7 @@ function extractConstObjectFields(expr, constants) {
29925
30652
  const fields = /* @__PURE__ */ new Map();
29926
30653
  for (const prop of expr.properties) {
29927
30654
  if (prop.kind === "SpreadElement") return null;
30655
+ if (prop.computed) return null;
29928
30656
  const key = getObjectLiteralKey(prop.key);
29929
30657
  if (!key) return null;
29930
30658
  const value = evaluateLiteral(prop.value, constants);
@@ -30052,6 +30780,7 @@ function replaceConstMemberExpressions(expr, constObjects, constArrays) {
30052
30780
  }
30053
30781
  return {
30054
30782
  ...prop,
30783
+ key: prop.computed ? replaceConstMemberExpressions(prop.key, constObjects, constArrays) : prop.key,
30055
30784
  value: replaceConstMemberExpressions(
30056
30785
  prop.value,
30057
30786
  constObjects,
@@ -30302,6 +31031,7 @@ function simplifyChildren(expr, constants, options) {
30302
31031
  if (prop.kind === "Property") {
30303
31032
  return {
30304
31033
  ...prop,
31034
+ key: prop.computed ? simplifyAlgebraically(prop.key, constants, options) : prop.key,
30305
31035
  value: simplifyAlgebraically(prop.value, constants, options)
30306
31036
  };
30307
31037
  }
@@ -30420,8 +31150,8 @@ function replaceIdentifiersWithConstants(expr, constants, context = {}) {
30420
31150
  }
30421
31151
  return {
30422
31152
  ...prop,
30423
- value: replaceIdentifiersWithConstants(prop.value, constants),
30424
- key: prop.key
31153
+ key: prop.computed ? replaceIdentifiersWithConstants(prop.key, constants) : prop.key,
31154
+ value: replaceIdentifiersWithConstants(prop.value, constants)
30425
31155
  };
30426
31156
  })
30427
31157
  };
@@ -30973,6 +31703,7 @@ function walkExpression(expr, add, ctx) {
30973
31703
  if (prop.kind === "SpreadElement") {
30974
31704
  walkExpression(prop.argument, add, ctx);
30975
31705
  } else {
31706
+ if (prop.computed) walkExpression(prop.key, add, ctx);
30976
31707
  walkExpression(prop.value, add, ctx);
30977
31708
  }
30978
31709
  });
@@ -31112,7 +31843,9 @@ function hashExpression(expr) {
31112
31843
  return `arr:${expr.elements.map((el) => el ? hashExpression(el) : "null").join(",")}`;
31113
31844
  case "ObjectExpression":
31114
31845
  return `obj:${expr.properties.map(
31115
- (p) => p.kind === "SpreadElement" ? `...${hashExpression(p.argument)}` : `${hashExpression(p.key)}:${hashExpression(p.value)}`
31846
+ (p) => p.kind === "SpreadElement" ? `...${hashExpression(p.argument)}` : `${p.computed ? "[]" : "."}${hashExpression(p.key)}:${hashExpression(
31847
+ p.value
31848
+ )}`
31116
31849
  ).join(",")}`;
31117
31850
  case "TemplateLiteral":
31118
31851
  return `tpl:${expr.quasis.join("|")}:${expr.expressions.map((e) => hashExpression(e)).join("|")}`;
@@ -31159,7 +31892,7 @@ function isPureExpression(expr, ctx) {
31159
31892
  case "ObjectExpression":
31160
31893
  return expr.properties.every((prop) => {
31161
31894
  if (prop.kind === "SpreadElement") return isPureExpression(prop.argument, ctx);
31162
- return isPureExpression(prop.value, ctx);
31895
+ return (!prop.computed || isPureExpression(prop.key, ctx)) && isPureExpression(prop.value, ctx);
31163
31896
  });
31164
31897
  case "MemberExpression":
31165
31898
  case "OptionalMemberExpression":
@@ -31371,7 +32104,7 @@ function replaceIdentifier(expr, target, replacement, inFunctionBody) {
31371
32104
  }
31372
32105
  return {
31373
32106
  ...prop,
31374
- key: prop.key,
32107
+ key: prop.computed ? replaceIdentifier(prop.key, target, replacement, inFunctionBody) : prop.key,
31375
32108
  value: replaceIdentifier(prop.value, target, replacement, inFunctionBody)
31376
32109
  };
31377
32110
  })
@@ -31706,7 +32439,9 @@ function shouldSuppressWarning(suppressions, code, line) {
31706
32439
  return entry.codes.has(code);
31707
32440
  });
31708
32441
  }
32442
+ var DEFAULT_ERROR_WARNING_CODES = /* @__PURE__ */ new Set(["FICT-R004"]);
31709
32443
  function hasErrorEscalation(options) {
32444
+ if (DEFAULT_ERROR_WARNING_CODES.size > 0) return true;
31710
32445
  if (options.warningsAsErrors === true) return true;
31711
32446
  if (Array.isArray(options.warningsAsErrors) && options.warningsAsErrors.length > 0) return true;
31712
32447
  if (options.warningLevels) {
@@ -31721,6 +32456,7 @@ function resolveWarningLevel(code, options) {
31721
32456
  if (Array.isArray(options.warningsAsErrors) && options.warningsAsErrors.includes(code)) {
31722
32457
  return "error";
31723
32458
  }
32459
+ if (DEFAULT_ERROR_WARNING_CODES.has(code)) return "error";
31724
32460
  return "warn";
31725
32461
  }
31726
32462
  function formatWarningAsError(warning) {
@@ -31834,18 +32570,66 @@ function isDynamicPropertyAccess(node, t4) {
31834
32570
  if (!node.computed) return false;
31835
32571
  return !(t4.isStringLiteral(node.property) || t4.isNumericLiteral(node.property));
31836
32572
  }
31837
- function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4) {
31838
- const isStateRoot = (expr) => {
32573
+ function runWarningPass(programPath, stateBindingIds, stateRootBindingIds, reactiveBindingIds, effectMacroNames, warn, fileName, t4) {
32574
+ const hasTrackedBinding = (path2, name, tracked) => {
32575
+ const binding = path2.scope.getBinding(name);
32576
+ return !!(binding && tracked.has(binding.identifier));
32577
+ };
32578
+ const isStateRoot = (expr, path2) => {
32579
+ const root = getRootIdentifier(expr, t4);
32580
+ if (!root) return false;
32581
+ return hasTrackedBinding(path2, root.name, stateRootBindingIds);
32582
+ };
32583
+ const isReactiveRoot = (expr, path2) => {
31839
32584
  const root = getRootIdentifier(expr, t4);
31840
- return !!(root && stateVars.has(root.name));
32585
+ if (!root) return false;
32586
+ return hasTrackedBinding(path2, root.name, reactiveBindingIds);
32587
+ };
32588
+ const argumentHasReactive = (argPath) => {
32589
+ if (argPath.isSpreadElement()) {
32590
+ const inner = argPath.get("argument");
32591
+ return argumentHasReactive(inner);
32592
+ }
32593
+ if (argPath.isJSXElement() || argPath.isJSXFragment()) return false;
32594
+ if (argPath.isIdentifier()) {
32595
+ return hasTrackedBinding(argPath, argPath.node.name, reactiveBindingIds);
32596
+ }
32597
+ if (!argPath.isExpression()) return false;
32598
+ let found = false;
32599
+ argPath.traverse({
32600
+ Function(path2) {
32601
+ path2.skip();
32602
+ },
32603
+ JSXElement(path2) {
32604
+ path2.skip();
32605
+ },
32606
+ JSXFragment(path2) {
32607
+ path2.skip();
32608
+ },
32609
+ Identifier(idPath) {
32610
+ if (idPath.parentPath.isMemberExpression({ property: idPath.node }) && !idPath.parent.computed) {
32611
+ return;
32612
+ }
32613
+ if (idPath.parentPath.isObjectProperty({ key: idPath.node }) && !idPath.parent.computed && !idPath.parent.shorthand) {
32614
+ return;
32615
+ }
32616
+ const binding = idPath.scope.getBinding(idPath.node.name);
32617
+ if (binding && reactiveBindingIds.has(binding.identifier)) {
32618
+ found = true;
32619
+ idPath.stop();
32620
+ }
32621
+ }
32622
+ });
32623
+ return found;
31841
32624
  };
31842
- const reactiveNames = /* @__PURE__ */ new Set([...stateVars, ...derivedVars]);
31843
32625
  programPath.traverse({
31844
32626
  AssignmentExpression(path2) {
31845
32627
  const { left } = path2.node;
31846
32628
  if (t4.isIdentifier(left)) return;
31847
32629
  if (t4.isMemberExpression(left) || t4.isOptionalMemberExpression(left)) {
31848
- if (isStateRoot(left.object)) {
32630
+ const stateRoot = isStateRoot(left.object, path2);
32631
+ const reactiveRoot = isReactiveRoot(left.object, path2);
32632
+ if (stateRoot || reactiveRoot) {
31849
32633
  emitWarning(
31850
32634
  path2.node,
31851
32635
  "FICT-M",
@@ -31862,13 +32646,16 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31862
32646
  fileName
31863
32647
  );
31864
32648
  }
32649
+ return;
31865
32650
  }
31866
32651
  }
31867
32652
  },
31868
32653
  UpdateExpression(path2) {
31869
32654
  const arg = path2.node.argument;
31870
32655
  if (t4.isMemberExpression(arg) || t4.isOptionalMemberExpression(arg)) {
31871
- if (isStateRoot(arg.object)) {
32656
+ const stateRoot = isStateRoot(arg.object, path2);
32657
+ const reactiveRoot = isReactiveRoot(arg.object, path2);
32658
+ if (stateRoot || reactiveRoot) {
31872
32659
  emitWarning(
31873
32660
  path2.node,
31874
32661
  "FICT-M",
@@ -31885,6 +32672,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31885
32672
  fileName
31886
32673
  );
31887
32674
  }
32675
+ return;
31888
32676
  }
31889
32677
  }
31890
32678
  },
@@ -31892,7 +32680,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31892
32680
  if (!path2.node.computed) return;
31893
32681
  if (path2.parentPath.isAssignmentExpression({ left: path2.node })) return;
31894
32682
  if (path2.parentPath.isUpdateExpression({ argument: path2.node })) return;
31895
- if (isDynamicPropertyAccess(path2.node, t4) && isStateRoot(path2.node.object)) {
32683
+ if (isDynamicPropertyAccess(path2.node, t4) && isReactiveRoot(path2.node.object, path2)) {
31896
32684
  emitWarning(
31897
32685
  path2.node,
31898
32686
  "FICT-H",
@@ -31912,9 +32700,9 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31912
32700
  },
31913
32701
  Identifier(idPath) {
31914
32702
  const name = idPath.node.name;
31915
- if (!reactiveNames.has(name)) return;
31916
32703
  const binding = idPath.scope.getBinding(name);
31917
32704
  if (!binding) return;
32705
+ if (!reactiveBindingIds.has(binding.identifier)) return;
31918
32706
  if (binding.scope === idPath.scope || binding.scope === path2.scope) return;
31919
32707
  captured.add(name);
31920
32708
  }
@@ -31932,7 +32720,8 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31932
32720
  }
31933
32721
  },
31934
32722
  CallExpression(path2) {
31935
- if (t4.isIdentifier(path2.node.callee, { name: "$effect" })) {
32723
+ const isEffect = isEffectCall(path2.node, t4, effectMacroNames);
32724
+ if (isEffect) {
31936
32725
  const argPath = path2.get("arguments.0");
31937
32726
  if (argPath?.isFunctionExpression() || argPath?.isArrowFunctionExpression()) {
31938
32727
  let hasReactiveDependency = false;
@@ -31946,7 +32735,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31946
32735
  }
31947
32736
  const binding = idPath.scope.getBinding(idPath.node.name);
31948
32737
  if (binding && binding.scope === argPath.scope) return;
31949
- if (stateVars.has(idPath.node.name) || derivedVars.has(idPath.node.name)) {
32738
+ if (binding && reactiveBindingIds.has(binding.identifier)) {
31950
32739
  hasReactiveDependency = true;
31951
32740
  idPath.stop();
31952
32741
  }
@@ -31976,13 +32765,16 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31976
32765
  }
31977
32766
  const isSafe = calleeName && SAFE_FUNCTIONS.has(calleeName);
31978
32767
  if (isSafe) return;
31979
- for (const arg of path2.node.arguments) {
31980
- if (!t4.isExpression(arg)) continue;
31981
- if (isStateRoot(arg)) {
32768
+ const argPaths = path2.get("arguments");
32769
+ for (const argPath of argPaths) {
32770
+ if (argPath.isIdentifier() && hasTrackedBinding(argPath, argPath.node.name, stateBindingIds)) {
32771
+ continue;
32772
+ }
32773
+ if (argumentHasReactive(argPath)) {
31982
32774
  emitWarning(
31983
- arg,
31984
- "FICT-H",
31985
- "State value passed to unknown function (black box); dependency tracking may be imprecise",
32775
+ argPath.node,
32776
+ "FICT-R002",
32777
+ "Reactive value escapes scope when passed to an unknown function; dependency tracking may be imprecise",
31986
32778
  warn,
31987
32779
  fileName
31988
32780
  );
@@ -31994,7 +32786,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31994
32786
  if (!path2.node.computed) return;
31995
32787
  if (path2.parentPath.isAssignmentExpression({ left: path2.node })) return;
31996
32788
  if (path2.parentPath.isUpdateExpression({ argument: path2.node })) return;
31997
- if (isDynamicPropertyAccess(path2.node, t4) && isStateRoot(path2.node.object)) {
32789
+ if (isDynamicPropertyAccess(path2.node, t4) && isReactiveRoot(path2.node.object, path2)) {
31998
32790
  emitWarning(
31999
32791
  path2.node,
32000
32792
  "FICT-H",
@@ -32123,6 +32915,17 @@ function createHIREntrypointVisitor(t4, options) {
32123
32915
  const name = getFunctionName(fnPath);
32124
32916
  return name && isComponentName2(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t4);
32125
32917
  };
32918
+ const isBoundDefinition = (fnPath) => fnPath.isFunctionDeclaration() || fnPath.parentPath.isVariableDeclarator() && fnPath.parentPath.node.init === fnPath.node;
32919
+ const isExportDefaultDefinition = (fnPath) => fnPath.parentPath?.isExportDefaultDeclaration() && fnPath.parentPath.node.declaration === fnPath.node;
32920
+ const isNamedComponentOrHookDefinition = (fnPath) => {
32921
+ if (!isBoundDefinition(fnPath)) return false;
32922
+ const name = getFunctionName(fnPath);
32923
+ return !!name && (isComponentName2(name) || isHookName2(name));
32924
+ };
32925
+ const isComponentDefinitionForProps = (fnPath) => {
32926
+ if (!isComponentDefinition(fnPath)) return false;
32927
+ return isBoundDefinition(fnPath) || isExportDefaultDefinition(fnPath);
32928
+ };
32126
32929
  const memoHasSideEffects = (fn) => {
32127
32930
  const pureCalls = new Set(
32128
32931
  Array.from(SAFE_FUNCTIONS).filter(
@@ -32243,6 +33046,7 @@ function createHIREntrypointVisitor(t4, options) {
32243
33046
  const stateMacroNames = /* @__PURE__ */ new Set(["$state"]);
32244
33047
  const effectMacroNames = /* @__PURE__ */ new Set(["$effect"]);
32245
33048
  const memoMacroNames = /* @__PURE__ */ new Set(["$memo", "createMemo"]);
33049
+ const importedReactiveBindingIds = /* @__PURE__ */ new Set();
32246
33050
  path2.traverse({
32247
33051
  ImportDeclaration(importPath) {
32248
33052
  if (importPath.node.source.value !== "fict" && importPath.node.source.value !== "fict/slim")
@@ -32263,6 +33067,44 @@ function createHIREntrypointVisitor(t4, options) {
32263
33067
  }
32264
33068
  }
32265
33069
  });
33070
+ path2.traverse({
33071
+ ImportDeclaration(importPath) {
33072
+ const meta = resolveModuleMetadata(
33073
+ importPath.node.source.value,
33074
+ fileName,
33075
+ optionsWithWarnings
33076
+ );
33077
+ if (!meta) return;
33078
+ const hasReactiveExports = Object.keys(meta.exports).length > 0;
33079
+ for (const spec of importPath.node.specifiers) {
33080
+ if (t4.isImportSpecifier(spec)) {
33081
+ const importedName = t4.isIdentifier(spec.imported) ? spec.imported.name : String(spec.imported.value);
33082
+ if (meta.exports[importedName]) {
33083
+ const binding = importPath.scope.getBinding(spec.local.name);
33084
+ if (binding) {
33085
+ importedReactiveBindingIds.add(binding.identifier);
33086
+ }
33087
+ }
33088
+ continue;
33089
+ }
33090
+ if (t4.isImportDefaultSpecifier(spec)) {
33091
+ if (meta.exports.default) {
33092
+ const binding = importPath.scope.getBinding(spec.local.name);
33093
+ if (binding) {
33094
+ importedReactiveBindingIds.add(binding.identifier);
33095
+ }
33096
+ }
33097
+ continue;
33098
+ }
33099
+ if (t4.isImportNamespaceSpecifier(spec) && hasReactiveExports) {
33100
+ const binding = importPath.scope.getBinding(spec.local.name);
33101
+ if (binding) {
33102
+ importedReactiveBindingIds.add(binding.identifier);
33103
+ }
33104
+ }
33105
+ }
33106
+ }
33107
+ });
32266
33108
  path2.traverse({
32267
33109
  JSXExpressionContainer(exprPath) {
32268
33110
  const expr = exprPath.node.expression;
@@ -32273,10 +33115,10 @@ function createHIREntrypointVisitor(t4, options) {
32273
33115
  const cb = expr.arguments[0];
32274
33116
  if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
32275
33117
  const getReturnedJsx = (fn) => {
32276
- if (t4.isJSXElement(fn.body)) return fn.body;
33118
+ if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
32277
33119
  if (t4.isBlockStatement(fn.body)) {
32278
33120
  const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
32279
- if (ret && t4.isReturnStatement(ret) && ret.argument && t4.isJSXElement(ret.argument)) {
33121
+ if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
32280
33122
  return ret.argument;
32281
33123
  }
32282
33124
  }
@@ -32284,6 +33126,16 @@ function createHIREntrypointVisitor(t4, options) {
32284
33126
  };
32285
33127
  const jsx = getReturnedJsx(cb);
32286
33128
  if (!jsx) return;
33129
+ if (t4.isJSXFragment(jsx)) {
33130
+ warn({
33131
+ code: "FICT-J002",
33132
+ message: "Missing key prop in list rendering.",
33133
+ fileName,
33134
+ line: expr.loc?.start.line ?? 0,
33135
+ column: expr.loc ? expr.loc.start.column + 1 : 0
33136
+ });
33137
+ return;
33138
+ }
32287
33139
  let hasKey = false;
32288
33140
  let hasUnknownSpread = false;
32289
33141
  for (const attr of jsx.openingElement.attributes) {
@@ -32306,8 +33158,48 @@ function createHIREntrypointVisitor(t4, options) {
32306
33158
  }
32307
33159
  });
32308
33160
  const stateVars = /* @__PURE__ */ new Set();
32309
- const derivedVars = /* @__PURE__ */ new Set();
33161
+ const stateBindingIds = /* @__PURE__ */ new Set();
33162
+ const derivedBindingIds = /* @__PURE__ */ new Set();
32310
33163
  const destructuredAliases = /* @__PURE__ */ new Set();
33164
+ const aliasBindingIds = /* @__PURE__ */ new Set();
33165
+ const stateAliasBindingIds = /* @__PURE__ */ new Set();
33166
+ const propsBindingIds = /* @__PURE__ */ new Set();
33167
+ const hasTrackedBinding = (path3, name, tracked) => {
33168
+ const binding = path3.scope.getBinding(name);
33169
+ return !!(binding && tracked.has(binding.identifier));
33170
+ };
33171
+ const trackBindingByName = (path3, name, tracked) => {
33172
+ const binding = path3.scope.getBinding(name);
33173
+ if (binding) tracked.add(binding.identifier);
33174
+ };
33175
+ const hasReactiveAliasSourceBinding = (path3, name) => hasTrackedBinding(path3, name, stateBindingIds) || hasTrackedBinding(path3, name, derivedBindingIds) || hasTrackedBinding(path3, name, aliasBindingIds) || hasTrackedBinding(path3, name, destructuredAliases) || hasTrackedBinding(path3, name, importedReactiveBindingIds);
33176
+ const hasStateRootBinding = (path3, name) => hasTrackedBinding(path3, name, stateBindingIds) || hasTrackedBinding(path3, name, stateAliasBindingIds);
33177
+ const unwrapIdentifier = (node) => {
33178
+ if (!node) return null;
33179
+ let current = node;
33180
+ while (true) {
33181
+ if (t4.isTSAsExpression(current)) {
33182
+ current = current.expression;
33183
+ continue;
33184
+ }
33185
+ if (t4.isTSNonNullExpression(current)) {
33186
+ current = current.expression;
33187
+ continue;
33188
+ }
33189
+ if (t4.isTypeCastExpression?.(current)) {
33190
+ current = current.expression;
33191
+ continue;
33192
+ }
33193
+ break;
33194
+ }
33195
+ return t4.isIdentifier(current) ? current : null;
33196
+ };
33197
+ const isStateRootIdentifier = (exprPath) => {
33198
+ if (!exprPath) return null;
33199
+ const id = unwrapIdentifier(exprPath.node);
33200
+ if (!id) return null;
33201
+ return hasStateRootBinding(exprPath, id.name) ? id : null;
33202
+ };
32311
33203
  path2.traverse({
32312
33204
  VariableDeclarator(varPath) {
32313
33205
  const init = varPath.node.init;
@@ -32343,6 +33235,7 @@ For module-level shared state, use one of these alternatives:
32343
33235
  );
32344
33236
  }
32345
33237
  stateVars.add(varPath.node.id.name);
33238
+ trackBindingByName(varPath, varPath.node.id.name, stateBindingIds);
32346
33239
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
32347
33240
  throw varPath.buildCodeFrameError(
32348
33241
  `$state() cannot be declared inside loops or conditionals.
@@ -32365,25 +33258,36 @@ or extract the nested logic into a custom hook (useXxx).`
32365
33258
  let dependsOnState = false;
32366
33259
  varPath.get("init").traverse({
32367
33260
  Identifier(idPath) {
32368
- if (stateVars.has(idPath.node.name)) {
33261
+ if (hasTrackedBinding(idPath, idPath.node.name, stateBindingIds)) {
32369
33262
  dependsOnState = true;
32370
33263
  idPath.stop();
32371
33264
  }
32372
33265
  }
32373
33266
  });
32374
33267
  if (dependsOnState) {
32375
- derivedVars.add(varPath.node.id.name);
33268
+ trackBindingByName(varPath, varPath.node.id.name, derivedBindingIds);
32376
33269
  }
32377
33270
  }
32378
- } else if ((t4.isObjectPattern(varPath.node.id) || t4.isArrayPattern(varPath.node.id)) && t4.isIdentifier(init) && stateVars.has(init.name)) {
32379
- collectPatternIdentifiers(varPath.node.id).forEach((id) => destructuredAliases.add(id));
32380
33271
  }
32381
33272
  },
32382
33273
  Function(fnPath) {
33274
+ if (isComponentDefinitionForProps(fnPath)) {
33275
+ for (const param of fnPath.node.params) {
33276
+ if (t4.isIdentifier(param)) {
33277
+ trackBindingByName(fnPath, param.name, propsBindingIds);
33278
+ continue;
33279
+ }
33280
+ if (t4.isPatternLike(param)) {
33281
+ collectPatternIdentifiers(param).forEach(
33282
+ (name) => trackBindingByName(fnPath, name, propsBindingIds)
33283
+ );
33284
+ }
33285
+ }
33286
+ }
32383
33287
  const parentFn = fnPath.getFunctionParent();
32384
33288
  if (!parentFn) return;
32385
33289
  if (!isComponentLike(parentFn)) return;
32386
- if (!isComponentLike(fnPath)) return;
33290
+ if (!isNamedComponentOrHookDefinition(fnPath)) return;
32387
33291
  emitWarning(
32388
33292
  fnPath.node,
32389
33293
  "FICT-C003",
@@ -32478,7 +33382,7 @@ or extract the nested logic into a custom hook (useXxx).`
32478
33382
  emitWarning(
32479
33383
  callPath.node,
32480
33384
  "FICT-R004",
32481
- "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
33385
+ "Reactive creation inside non-JSX control flow may not auto-dispose in complex paths. Prefer createScope/runInScope (or JSX-managed regions) for explicit lifecycle control.",
32482
33386
  warn,
32483
33387
  fileName
32484
33388
  );
@@ -32512,7 +33416,7 @@ or extract the nested logic into a custom hook (useXxx).`
32512
33416
  "createEffect"
32513
33417
  ]);
32514
33418
  callPath.node.arguments.forEach((arg) => {
32515
- if (t4.isIdentifier(arg) && stateVars.has(arg.name) && (!calleeId || !allowedStateCallees.has(calleeId))) {
33419
+ if (t4.isIdentifier(arg) && hasTrackedBinding(callPath, arg.name, stateBindingIds) && (!calleeId || !allowedStateCallees.has(calleeId))) {
32516
33420
  const loc = arg.loc?.start ?? callPath.node.loc?.start;
32517
33421
  warn({
32518
33422
  code: "FICT-S002",
@@ -32540,15 +33444,42 @@ or extract the nested logic into a custom hook (useXxx).`
32540
33444
  });
32541
33445
  const aliasStack = [/* @__PURE__ */ new Set()];
32542
33446
  const currentAliasSet = () => aliasStack[aliasStack.length - 1];
32543
- const rhsUsesState = (exprPath) => {
33447
+ const getBindingIdentifier = (path3, name) => {
33448
+ const binding = path3.scope.getBinding(name);
33449
+ return binding ? binding.identifier : null;
33450
+ };
33451
+ const addAliasBinding = (path3, name) => {
33452
+ const aliasSet = currentAliasSet();
33453
+ if (!aliasSet) return;
33454
+ const bindingId = getBindingIdentifier(path3, name);
33455
+ if (!bindingId) return;
33456
+ aliasSet.add(bindingId);
33457
+ aliasBindingIds.add(bindingId);
33458
+ };
33459
+ const addStateAliasBinding = (path3, name) => {
33460
+ const bindingId = getBindingIdentifier(path3, name);
33461
+ if (!bindingId) return;
33462
+ stateAliasBindingIds.add(bindingId);
33463
+ };
33464
+ const isAliasBinding = (path3, name) => {
33465
+ const aliasSet = currentAliasSet();
33466
+ if (!aliasSet) return false;
33467
+ const bindingId = getBindingIdentifier(path3, name);
33468
+ return !!(bindingId && aliasSet.has(bindingId));
33469
+ };
33470
+ const isDestructuredAliasBinding = (path3, name) => {
33471
+ const bindingId = getBindingIdentifier(path3, name);
33472
+ return !!(bindingId && destructuredAliases.has(bindingId));
33473
+ };
33474
+ const rhsUsesReactive = (exprPath) => {
32544
33475
  if (!exprPath) return false;
32545
- if (exprPath.isIdentifier() && t4.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
33476
+ if (exprPath.isIdentifier() && t4.isIdentifier(exprPath.node) && hasReactiveAliasSourceBinding(exprPath, exprPath.node.name)) {
32546
33477
  return true;
32547
33478
  }
32548
33479
  let usesState = false;
32549
33480
  exprPath.traverse({
32550
33481
  Identifier(idPath) {
32551
- if (stateVars.has(idPath.node.name)) {
33482
+ if (hasReactiveAliasSourceBinding(idPath, idPath.node.name)) {
32552
33483
  usesState = true;
32553
33484
  idPath.stop();
32554
33485
  }
@@ -32567,26 +33498,47 @@ or extract the nested logic into a custom hook (useXxx).`
32567
33498
  }
32568
33499
  },
32569
33500
  VariableDeclarator(varPath) {
32570
- const aliasSet = currentAliasSet();
32571
- if (aliasSet && t4.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
33501
+ const initPath = varPath.get("init");
33502
+ const stateRootId = isStateRootIdentifier(initPath);
33503
+ if (t4.isIdentifier(varPath.node.id) && rhsUsesReactive(initPath)) {
32572
33504
  debugLog("alias", "add from decl", varPath.node.id.name);
32573
- aliasSet.add(varPath.node.id.name);
33505
+ addAliasBinding(varPath, varPath.node.id.name);
33506
+ }
33507
+ if (t4.isIdentifier(varPath.node.id) && stateRootId) {
33508
+ addStateAliasBinding(varPath, varPath.node.id.name);
33509
+ }
33510
+ if (t4.isObjectPattern(varPath.node.id) || t4.isArrayPattern(varPath.node.id)) {
33511
+ if (rhsUsesReactive(initPath)) {
33512
+ const targets = collectPatternIdentifiers(varPath.node.id);
33513
+ for (const target of targets) {
33514
+ debugLog("alias", "add from destructuring decl", target);
33515
+ addAliasBinding(varPath, target);
33516
+ }
33517
+ }
33518
+ if (stateRootId) {
33519
+ collectPatternIdentifiers(varPath.node.id).forEach(
33520
+ (id) => trackBindingByName(varPath, id, destructuredAliases)
33521
+ );
33522
+ }
32574
33523
  }
32575
33524
  },
32576
33525
  AssignmentExpression(assignPath) {
32577
- const aliasSet = currentAliasSet();
32578
- if (!aliasSet) return;
32579
33526
  const rightPath = assignPath.get("right");
32580
- const usesState = rhsUsesState(rightPath);
33527
+ const usesState = rhsUsesReactive(rightPath);
33528
+ const stateRootId = isStateRootIdentifier(rightPath);
32581
33529
  const left = assignPath.node.left;
32582
33530
  if (t4.isIdentifier(left)) {
32583
33531
  const targetName = left.name;
32584
33532
  if (usesState) {
32585
33533
  debugLog("alias", "add from assign", targetName);
32586
- aliasSet.add(targetName);
33534
+ addAliasBinding(assignPath, targetName);
33535
+ if (stateRootId) {
33536
+ addStateAliasBinding(assignPath, targetName);
33537
+ }
32587
33538
  return;
32588
33539
  }
32589
- if (aliasSet.has(targetName)) {
33540
+ if (isAliasBinding(assignPath, targetName)) {
33541
+ if (isDestructuredAliasBinding(assignPath, targetName)) return;
32590
33542
  debugLog("alias", "reassignment detected", targetName);
32591
33543
  throw assignPath.buildCodeFrameError(
32592
33544
  `Alias reassignment is not supported for "${targetName}"`
@@ -32600,11 +33552,18 @@ or extract the nested logic into a custom hook (useXxx).`
32600
33552
  if (usesState) {
32601
33553
  for (const target of targets) {
32602
33554
  debugLog("alias", "add from destructuring assign", target);
32603
- aliasSet.add(target);
33555
+ addAliasBinding(assignPath, target);
33556
+ }
33557
+ if (stateRootId) {
33558
+ targets.forEach(
33559
+ (target) => trackBindingByName(assignPath, target, destructuredAliases)
33560
+ );
32604
33561
  }
32605
33562
  return;
32606
33563
  }
32607
- const reassigned = targets.find((target) => aliasSet.has(target));
33564
+ const reassigned = targets.find(
33565
+ (target) => isAliasBinding(assignPath, target) && !isDestructuredAliasBinding(assignPath, target)
33566
+ );
32608
33567
  if (reassigned) {
32609
33568
  debugLog("alias", "reassignment detected", reassigned);
32610
33569
  throw assignPath.buildCodeFrameError(
@@ -32612,20 +33571,31 @@ or extract the nested logic into a custom hook (useXxx).`
32612
33571
  );
32613
33572
  }
32614
33573
  }
33574
+ },
33575
+ UpdateExpression(updatePath) {
33576
+ const arg = updatePath.node.argument;
33577
+ if (t4.isIdentifier(arg) && isAliasBinding(updatePath, arg.name) && !isDestructuredAliasBinding(updatePath, arg.name)) {
33578
+ debugLog("alias", "reassignment detected", arg.name);
33579
+ throw updatePath.buildCodeFrameError(
33580
+ `Alias reassignment is not supported for "${arg.name}"`
33581
+ );
33582
+ }
32615
33583
  }
32616
33584
  });
32617
- if (derivedVars.size > 0) {
33585
+ if (derivedBindingIds.size > 0) {
32618
33586
  path2.traverse({
32619
33587
  AssignmentExpression(assignPath) {
32620
33588
  const { left } = assignPath.node;
32621
- if (t4.isIdentifier(left) && derivedVars.has(left.name)) {
33589
+ if (t4.isIdentifier(left) && hasTrackedBinding(assignPath, left.name, derivedBindingIds)) {
32622
33590
  throw assignPath.buildCodeFrameError(
32623
33591
  `Cannot reassign derived value '${left.name}'. Derived values are read-only.`
32624
33592
  );
32625
33593
  }
32626
33594
  if (t4.isObjectPattern(left) || t4.isArrayPattern(left)) {
32627
33595
  const targets = collectPatternIdentifiers(left);
32628
- const derivedTarget = targets.find((target) => derivedVars.has(target));
33596
+ const derivedTarget = targets.find(
33597
+ (target) => hasTrackedBinding(assignPath, target, derivedBindingIds)
33598
+ );
32629
33599
  if (derivedTarget) {
32630
33600
  throw assignPath.buildCodeFrameError(
32631
33601
  `Cannot reassign derived value '${derivedTarget}'. Derived values are read-only.`
@@ -32639,14 +33609,16 @@ or extract the nested logic into a custom hook (useXxx).`
32639
33609
  path2.traverse({
32640
33610
  AssignmentExpression(assignPath) {
32641
33611
  const { left } = assignPath.node;
32642
- if (t4.isIdentifier(left) && destructuredAliases.has(left.name)) {
33612
+ if (t4.isIdentifier(left) && hasTrackedBinding(assignPath, left.name, destructuredAliases)) {
32643
33613
  throw assignPath.buildCodeFrameError(
32644
33614
  `Cannot write to destructured state alias '${left.name}'. Update the original state (e.g. state.count++ or immutable update).`
32645
33615
  );
32646
33616
  }
32647
33617
  if (t4.isObjectPattern(left) || t4.isArrayPattern(left)) {
32648
33618
  const targets = collectPatternIdentifiers(left);
32649
- const aliasTarget = targets.find((target) => destructuredAliases.has(target));
33619
+ const aliasTarget = targets.find(
33620
+ (target) => hasTrackedBinding(assignPath, target, destructuredAliases)
33621
+ );
32650
33622
  if (aliasTarget) {
32651
33623
  throw assignPath.buildCodeFrameError(
32652
33624
  `Cannot write to destructured state alias '${aliasTarget}'. Update the original state (e.g. state.count++ or immutable update).`
@@ -32656,7 +33628,7 @@ or extract the nested logic into a custom hook (useXxx).`
32656
33628
  },
32657
33629
  UpdateExpression(updatePath) {
32658
33630
  const arg = updatePath.node.argument;
32659
- if (t4.isIdentifier(arg) && destructuredAliases.has(arg.name)) {
33631
+ if (t4.isIdentifier(arg) && hasTrackedBinding(updatePath, arg.name, destructuredAliases)) {
32660
33632
  throw updatePath.buildCodeFrameError(
32661
33633
  `Cannot write to destructured state alias '${arg.name}'. Update the original state (e.g. state.count++ or immutable update).`
32662
33634
  );
@@ -32666,7 +33638,28 @@ or extract the nested logic into a custom hook (useXxx).`
32666
33638
  }
32667
33639
  const shouldRunWarnings = dev || hasErrorEscalation(options);
32668
33640
  if (shouldRunWarnings) {
32669
- runWarningPass(path2, stateVars, derivedVars, warn, fileName, t4);
33641
+ const stateRootBindingIds = /* @__PURE__ */ new Set([
33642
+ ...stateBindingIds,
33643
+ ...stateAliasBindingIds
33644
+ ]);
33645
+ const reactiveBindingIds = /* @__PURE__ */ new Set([
33646
+ ...stateBindingIds,
33647
+ ...derivedBindingIds,
33648
+ ...aliasBindingIds,
33649
+ ...destructuredAliases,
33650
+ ...propsBindingIds,
33651
+ ...importedReactiveBindingIds
33652
+ ]);
33653
+ runWarningPass(
33654
+ path2,
33655
+ stateBindingIds,
33656
+ stateRootBindingIds,
33657
+ reactiveBindingIds,
33658
+ effectMacroNames,
33659
+ warn,
33660
+ fileName,
33661
+ t4
33662
+ );
32670
33663
  }
32671
33664
  const fileAst = t4.file(path2.node);
32672
33665
  const hir = buildHIR(