@fictjs/compiler 0.5.2 → 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.js CHANGED
@@ -101,7 +101,7 @@ var require_generated = __commonJS({
101
101
  });
102
102
  exports.isAccessor = isAccessor;
103
103
  exports.isAnyTypeAnnotation = isAnyTypeAnnotation;
104
- exports.isArgumentPlaceholder = isArgumentPlaceholder;
104
+ exports.isArgumentPlaceholder = isArgumentPlaceholder2;
105
105
  exports.isArrayExpression = isArrayExpression2;
106
106
  exports.isArrayPattern = isArrayPattern4;
107
107
  exports.isArrayTypeAnnotation = isArrayTypeAnnotation;
@@ -125,7 +125,7 @@ var require_generated = __commonJS({
125
125
  exports.isClass = isClass;
126
126
  exports.isClassAccessorProperty = isClassAccessorProperty;
127
127
  exports.isClassBody = isClassBody;
128
- exports.isClassDeclaration = isClassDeclaration2;
128
+ exports.isClassDeclaration = isClassDeclaration3;
129
129
  exports.isClassExpression = isClassExpression2;
130
130
  exports.isClassImplements = isClassImplements;
131
131
  exports.isClassMethod = isClassMethod;
@@ -155,7 +155,7 @@ var require_generated = __commonJS({
155
155
  exports.isDirectiveLiteral = isDirectiveLiteral;
156
156
  exports.isDoExpression = isDoExpression;
157
157
  exports.isDoWhileStatement = isDoWhileStatement2;
158
- exports.isEmptyStatement = isEmptyStatement;
158
+ exports.isEmptyStatement = isEmptyStatement2;
159
159
  exports.isEmptyTypeAnnotation = isEmptyTypeAnnotation;
160
160
  exports.isEnumBody = isEnumBody;
161
161
  exports.isEnumBooleanBody = isEnumBooleanBody;
@@ -226,14 +226,14 @@ var require_generated = __commonJS({
226
226
  exports.isJSXFragment = isJSXFragment2;
227
227
  exports.isJSXIdentifier = isJSXIdentifier2;
228
228
  exports.isJSXMemberExpression = isJSXMemberExpression2;
229
- exports.isJSXNamespacedName = isJSXNamespacedName;
229
+ exports.isJSXNamespacedName = isJSXNamespacedName2;
230
230
  exports.isJSXOpeningElement = isJSXOpeningElement;
231
231
  exports.isJSXOpeningFragment = isJSXOpeningFragment;
232
232
  exports.isJSXSpreadAttribute = isJSXSpreadAttribute2;
233
- exports.isJSXSpreadChild = isJSXSpreadChild;
233
+ exports.isJSXSpreadChild = isJSXSpreadChild2;
234
234
  exports.isJSXText = isJSXText2;
235
235
  exports.isLVal = isLVal;
236
- exports.isLabeledStatement = isLabeledStatement;
236
+ exports.isLabeledStatement = isLabeledStatement2;
237
237
  exports.isLiteral = isLiteral;
238
238
  exports.isLogicalExpression = isLogicalExpression2;
239
239
  exports.isLoop = isLoop;
@@ -271,7 +271,7 @@ var require_generated = __commonJS({
271
271
  exports.isOptionalMemberExpression = isOptionalMemberExpression2;
272
272
  exports.isParenthesizedExpression = isParenthesizedExpression2;
273
273
  exports.isPattern = isPattern2;
274
- exports.isPatternLike = isPatternLike2;
274
+ exports.isPatternLike = isPatternLike3;
275
275
  exports.isPipelineBareFunction = isPipelineBareFunction;
276
276
  exports.isPipelinePrimaryTopicReference = isPipelinePrimaryTopicReference;
277
277
  exports.isPipelineTopicExpression = isPipelineTopicExpression;
@@ -479,7 +479,7 @@ var require_generated = __commonJS({
479
479
  if (node.type !== "DoWhileStatement") return false;
480
480
  return opts == null || (0, _shallowEqual.default)(node, opts);
481
481
  }
482
- function isEmptyStatement(node, opts) {
482
+ function isEmptyStatement2(node, opts) {
483
483
  if (!node) return false;
484
484
  if (node.type !== "EmptyStatement") return false;
485
485
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -524,7 +524,7 @@ var require_generated = __commonJS({
524
524
  if (node.type !== "IfStatement") return false;
525
525
  return opts == null || (0, _shallowEqual.default)(node, opts);
526
526
  }
527
- function isLabeledStatement(node, opts) {
527
+ function isLabeledStatement2(node, opts) {
528
528
  if (!node) return false;
529
529
  if (node.type !== "LabeledStatement") return false;
530
530
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -689,7 +689,7 @@ var require_generated = __commonJS({
689
689
  if (node.type !== "ClassExpression") return false;
690
690
  return opts == null || (0, _shallowEqual.default)(node, opts);
691
691
  }
692
- function isClassDeclaration2(node, opts) {
692
+ function isClassDeclaration3(node, opts) {
693
693
  if (!node) return false;
694
694
  if (node.type !== "ClassDeclaration") return false;
695
695
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1204,7 +1204,7 @@ var require_generated = __commonJS({
1204
1204
  if (node.type !== "JSXExpressionContainer") return false;
1205
1205
  return opts == null || (0, _shallowEqual.default)(node, opts);
1206
1206
  }
1207
- function isJSXSpreadChild(node, opts) {
1207
+ function isJSXSpreadChild2(node, opts) {
1208
1208
  if (!node) return false;
1209
1209
  if (node.type !== "JSXSpreadChild") return false;
1210
1210
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1219,7 +1219,7 @@ var require_generated = __commonJS({
1219
1219
  if (node.type !== "JSXMemberExpression") return false;
1220
1220
  return opts == null || (0, _shallowEqual.default)(node, opts);
1221
1221
  }
1222
- function isJSXNamespacedName(node, opts) {
1222
+ function isJSXNamespacedName2(node, opts) {
1223
1223
  if (!node) return false;
1224
1224
  if (node.type !== "JSXNamespacedName") return false;
1225
1225
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -1269,7 +1269,7 @@ var require_generated = __commonJS({
1269
1269
  if (node.type !== "V8IntrinsicIdentifier") return false;
1270
1270
  return opts == null || (0, _shallowEqual.default)(node, opts);
1271
1271
  }
1272
- function isArgumentPlaceholder(node, opts) {
1272
+ function isArgumentPlaceholder2(node, opts) {
1273
1273
  if (!node) return false;
1274
1274
  if (node.type !== "ArgumentPlaceholder") return false;
1275
1275
  return opts == null || (0, _shallowEqual.default)(node, opts);
@@ -2210,7 +2210,7 @@ var require_generated = __commonJS({
2210
2210
  }
2211
2211
  return opts == null || (0, _shallowEqual.default)(node, opts);
2212
2212
  }
2213
- function isPatternLike2(node, opts) {
2213
+ function isPatternLike3(node, opts) {
2214
2214
  if (!node) return false;
2215
2215
  switch (node.type) {
2216
2216
  case "Identifier":
@@ -7185,7 +7185,7 @@ var require_lowercase = __commonJS({
7185
7185
  exports.classAccessorProperty = classAccessorProperty;
7186
7186
  exports.classBody = classBody;
7187
7187
  exports.classDeclaration = classDeclaration;
7188
- exports.classExpression = classExpression;
7188
+ exports.classExpression = classExpression2;
7189
7189
  exports.classImplements = classImplements;
7190
7190
  exports.classMethod = classMethod;
7191
7191
  exports.classPrivateMethod = classPrivateMethod;
@@ -8047,7 +8047,7 @@ var require_lowercase = __commonJS({
8047
8047
  validate(defs.body, node, "body", body, 1);
8048
8048
  return node;
8049
8049
  }
8050
- function classExpression(id = null, superClass = null, body, decorators = null) {
8050
+ function classExpression2(id = null, superClass = null, body, decorators = null) {
8051
8051
  const node = {
8052
8052
  type: "ClassExpression",
8053
8053
  id,
@@ -14596,6 +14596,14 @@ function parseFictReturnAnnotation(node) {
14596
14596
  }
14597
14597
  function extractIdentifiersFromPattern(pattern) {
14598
14598
  const ids = [];
14599
+ if (t.isRestElement(pattern)) {
14600
+ if (t.isIdentifier(pattern.argument)) {
14601
+ ids.push({ kind: "Identifier", name: pattern.argument.name });
14602
+ } else if (t.isPatternLike(pattern.argument)) {
14603
+ ids.push(...extractIdentifiersFromPattern(pattern.argument));
14604
+ }
14605
+ return ids;
14606
+ }
14599
14607
  if (t.isObjectPattern(pattern)) {
14600
14608
  for (const prop of pattern.properties) {
14601
14609
  if (t.isObjectProperty(prop)) {
@@ -14604,14 +14612,18 @@ function extractIdentifiersFromPattern(pattern) {
14604
14612
  } else if (t.isAssignmentPattern(prop.value)) {
14605
14613
  if (t.isIdentifier(prop.value.left)) {
14606
14614
  ids.push({ kind: "Identifier", name: prop.value.left.name });
14607
- } else if (t.isPattern(prop.value.left)) {
14615
+ } else if (t.isPatternLike(prop.value.left)) {
14608
14616
  ids.push(...extractIdentifiersFromPattern(prop.value.left));
14609
14617
  }
14610
- } else if (t.isObjectPattern(prop.value) || t.isArrayPattern(prop.value)) {
14618
+ } else if (t.isPatternLike(prop.value)) {
14611
14619
  ids.push(...extractIdentifiersFromPattern(prop.value));
14612
14620
  }
14613
- } else if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) {
14614
- ids.push({ kind: "Identifier", name: prop.argument.name });
14621
+ } else if (t.isRestElement(prop)) {
14622
+ if (t.isIdentifier(prop.argument)) {
14623
+ ids.push({ kind: "Identifier", name: prop.argument.name });
14624
+ } else if (t.isPatternLike(prop.argument)) {
14625
+ ids.push(...extractIdentifiersFromPattern(prop.argument));
14626
+ }
14615
14627
  }
14616
14628
  }
14617
14629
  } else if (t.isArrayPattern(pattern)) {
@@ -14619,16 +14631,20 @@ function extractIdentifiersFromPattern(pattern) {
14619
14631
  if (!elem) continue;
14620
14632
  if (t.isIdentifier(elem)) {
14621
14633
  ids.push({ kind: "Identifier", name: elem.name });
14622
- } else if (t.isPattern(elem)) {
14634
+ } else if (t.isRestElement(elem)) {
14635
+ if (t.isIdentifier(elem.argument)) {
14636
+ ids.push({ kind: "Identifier", name: elem.argument.name });
14637
+ } else if (t.isPatternLike(elem.argument)) {
14638
+ ids.push(...extractIdentifiersFromPattern(elem.argument));
14639
+ }
14640
+ } else if (t.isPatternLike(elem)) {
14623
14641
  ids.push(...extractIdentifiersFromPattern(elem));
14624
- } else if (t.isRestElement(elem) && t.isIdentifier(elem.argument)) {
14625
- ids.push({ kind: "Identifier", name: elem.argument.name });
14626
14642
  }
14627
14643
  }
14628
14644
  } else if (t.isAssignmentPattern(pattern)) {
14629
14645
  if (t.isIdentifier(pattern.left)) {
14630
14646
  ids.push({ kind: "Identifier", name: pattern.left.name });
14631
- } else if (t.isPattern(pattern.left)) {
14647
+ } else if (t.isPatternLike(pattern.left)) {
14632
14648
  ids.push(...extractIdentifiersFromPattern(pattern.left));
14633
14649
  }
14634
14650
  }
@@ -14814,8 +14830,12 @@ function convertFunction(name, params, body, options) {
14814
14830
  } else if (t.isObjectPattern(p.left) || t.isArrayPattern(p.left)) {
14815
14831
  paramIds.push(...extractIdentifiersFromPattern(p.left));
14816
14832
  }
14817
- } else if (t.isRestElement(p) && t.isIdentifier(p.argument)) {
14818
- paramIds.push({ kind: "Identifier", name: p.argument.name });
14833
+ } else if (t.isRestElement(p)) {
14834
+ if (t.isIdentifier(p.argument)) {
14835
+ paramIds.push({ kind: "Identifier", name: p.argument.name });
14836
+ } else if (t.isPattern(p.argument)) {
14837
+ paramIds.push(...extractIdentifiersFromPattern(p.argument));
14838
+ }
14819
14839
  }
14820
14840
  }
14821
14841
  const bodyStatements = [...body];
@@ -14900,8 +14920,21 @@ function convertFunction(name, params, body, options) {
14900
14920
  const excludeKeys = [];
14901
14921
  decl.id.properties.forEach((prop) => {
14902
14922
  if (t.isObjectProperty(prop)) {
14923
+ if (prop.computed) {
14924
+ reportUnsupportedExpression(
14925
+ prop.key,
14926
+ "Computed keys in object destructuring are not supported in HIR conversion."
14927
+ );
14928
+ return;
14929
+ }
14903
14930
  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;
14904
- if (!keyName) return;
14931
+ if (!keyName) {
14932
+ reportUnsupportedExpression(
14933
+ prop.key,
14934
+ "Unsupported object destructuring key in HIR conversion."
14935
+ );
14936
+ return;
14937
+ }
14905
14938
  excludeKeys.push(t.stringLiteral(keyName));
14906
14939
  if (t.isIdentifier(prop.value)) {
14907
14940
  const memberExpr = t.memberExpression(
@@ -14915,8 +14948,21 @@ function convertFunction(name, params, body, options) {
14915
14948
  value: convertExpression(memberExpr),
14916
14949
  declarationKind: declKind
14917
14950
  });
14951
+ } else {
14952
+ reportUnsupportedExpression(
14953
+ prop.value,
14954
+ "Unsupported object destructuring pattern in HIR conversion."
14955
+ );
14956
+ return;
14957
+ }
14958
+ } else if (t.isRestElement(prop)) {
14959
+ if (!t.isIdentifier(prop.argument)) {
14960
+ reportUnsupportedExpression(
14961
+ prop.argument,
14962
+ "Rest destructuring patterns must be identifiers in HIR conversion."
14963
+ );
14964
+ return;
14918
14965
  }
14919
- } else if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) {
14920
14966
  const restExpr = t.callExpression(t.identifier("__fictPropsRest"), [
14921
14967
  useTemp ? t.identifier(tempName) : babelSourceExpr,
14922
14968
  t.arrayExpression(excludeKeys)
@@ -14927,6 +14973,12 @@ function convertFunction(name, params, body, options) {
14927
14973
  value: convertExpression(restExpr),
14928
14974
  declarationKind: declKind
14929
14975
  });
14976
+ } else {
14977
+ reportUnsupportedExpression(
14978
+ prop,
14979
+ "Unsupported object destructuring property in HIR conversion."
14980
+ );
14981
+ return;
14930
14982
  }
14931
14983
  });
14932
14984
  }
@@ -14952,7 +15004,14 @@ function convertFunction(name, params, body, options) {
14952
15004
  value: convertExpression(memberExpr),
14953
15005
  declarationKind: declKind
14954
15006
  });
14955
- } else if (t.isRestElement(elem) && t.isIdentifier(elem.argument)) {
15007
+ } else if (t.isRestElement(elem)) {
15008
+ if (!t.isIdentifier(elem.argument)) {
15009
+ reportUnsupportedExpression(
15010
+ elem.argument,
15011
+ "Rest destructuring patterns must be identifiers in HIR conversion."
15012
+ );
15013
+ return;
15014
+ }
14956
15015
  const sliceCall = t.callExpression(
14957
15016
  t.memberExpression(t.identifier(tempName), t.identifier("slice")),
14958
15017
  [t.numericLiteral(index)]
@@ -14963,6 +15022,12 @@ function convertFunction(name, params, body, options) {
14963
15022
  value: convertExpression(sliceCall),
14964
15023
  declarationKind: declKind
14965
15024
  });
15025
+ } else {
15026
+ reportUnsupportedExpression(
15027
+ elem,
15028
+ "Unsupported array destructuring pattern in HIR conversion."
15029
+ );
15030
+ return;
14966
15031
  }
14967
15032
  });
14968
15033
  }
@@ -15125,11 +15190,15 @@ function convertFunction(name, params, body, options) {
15125
15190
  if (t.isSwitchStatement(stmt)) {
15126
15191
  const exitBlock = createBlock();
15127
15192
  blocks.push(exitBlock.block);
15193
+ const caseBlocks = stmt.cases.map(() => createBlock());
15194
+ for (const caseBlock of caseBlocks) {
15195
+ blocks.push(caseBlock.block);
15196
+ }
15128
15197
  const cases = [];
15129
15198
  let defaultTarget;
15130
- for (const switchCase of stmt.cases) {
15131
- const caseBlock = createBlock();
15132
- blocks.push(caseBlock.block);
15199
+ for (let index = 0; index < stmt.cases.length; index++) {
15200
+ const switchCase = stmt.cases[index];
15201
+ const caseBlock = caseBlocks[index];
15133
15202
  if (switchCase.test) {
15134
15203
  cases.push({
15135
15204
  test: convertExpression(switchCase.test),
@@ -15138,20 +15207,26 @@ function convertFunction(name, params, body, options) {
15138
15207
  } else {
15139
15208
  defaultTarget = caseBlock.block.id;
15140
15209
  }
15210
+ }
15211
+ cfgContext.loopStack.push({
15212
+ breakTarget: exitBlock.block.id
15213
+ });
15214
+ for (let index = 0; index < stmt.cases.length; index++) {
15215
+ const switchCase = stmt.cases[index];
15216
+ const caseBlock = caseBlocks[index];
15217
+ const nextCaseBlock = caseBlocks[index + 1];
15218
+ const fallthroughTarget = nextCaseBlock ? nextCaseBlock.block.id : exitBlock.block.id;
15141
15219
  let caseBuilder = caseBlock;
15142
15220
  for (const s of switchCase.consequent) {
15143
- if (t.isBreakStatement(s)) {
15144
- caseBuilder.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15145
- caseBuilder.sealed = true;
15146
- break;
15147
- }
15221
+ if (caseBuilder.sealed) break;
15148
15222
  caseBuilder = processStatement(s, caseBuilder, exitBlock.block.id, cfgContext);
15149
15223
  }
15150
15224
  if (!caseBuilder.sealed) {
15151
- caseBuilder.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15225
+ caseBuilder.block.terminator = { kind: "Jump", target: fallthroughTarget };
15152
15226
  caseBuilder.sealed = true;
15153
15227
  }
15154
15228
  }
15229
+ cfgContext.loopStack.pop();
15155
15230
  if (defaultTarget === void 0) {
15156
15231
  cases.push({ target: exitBlock.block.id });
15157
15232
  } else {
@@ -15284,6 +15359,10 @@ function convertFunction(name, params, body, options) {
15284
15359
  current = exitBlock;
15285
15360
  continue;
15286
15361
  }
15362
+ current = processStatement(stmt, current, current.block.id, cfgContext);
15363
+ if (current.sealed) {
15364
+ current = startNewBlock();
15365
+ }
15287
15366
  }
15288
15367
  if (!current.sealed) {
15289
15368
  current.block.terminator = { kind: "Unreachable" };
@@ -15370,6 +15449,30 @@ function handleExpressionStatement(expr, push) {
15370
15449
  }
15371
15450
  return false;
15372
15451
  }
15452
+ function findBreakContext(ctx, label) {
15453
+ if (label) {
15454
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15455
+ const entry = ctx.loopStack[i];
15456
+ if (entry?.label === label) return entry;
15457
+ }
15458
+ return void 0;
15459
+ }
15460
+ return ctx.loopStack[ctx.loopStack.length - 1];
15461
+ }
15462
+ function findContinueContext(ctx, label) {
15463
+ if (label) {
15464
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15465
+ const entry = ctx.loopStack[i];
15466
+ if (entry?.label === label && entry.continueTarget !== void 0) return entry;
15467
+ }
15468
+ return void 0;
15469
+ }
15470
+ for (let i = ctx.loopStack.length - 1; i >= 0; i--) {
15471
+ const entry = ctx.loopStack[i];
15472
+ if (entry?.continueTarget !== void 0) return entry;
15473
+ }
15474
+ return void 0;
15475
+ }
15373
15476
  function fillStatements(stmt, bb, jumpTarget, ctx) {
15374
15477
  if (t.isBlockStatement(stmt)) {
15375
15478
  let current = bb;
@@ -15395,8 +15498,50 @@ function fillStatements(stmt, bb, jumpTarget, ctx) {
15395
15498
  }
15396
15499
  return result;
15397
15500
  }
15398
- function processStatement(stmt, bb, jumpTarget, ctx) {
15501
+ function processStatement(stmt, bb, jumpTarget, ctx, labelOverride) {
15399
15502
  const push = (instr) => bb.block.instructions.push(instr);
15503
+ if (t.isLabeledStatement(stmt) && ctx) {
15504
+ const label = stmt.label.name;
15505
+ const body = stmt.body;
15506
+ if (t.isWhileStatement(body) || t.isForStatement(body) || t.isDoWhileStatement(body) || t.isForInStatement(body) || t.isForOfStatement(body) || t.isSwitchStatement(body)) {
15507
+ return processStatement(body, bb, jumpTarget, ctx, label);
15508
+ }
15509
+ ctx.loopStack.push({
15510
+ breakTarget: jumpTarget,
15511
+ label
15512
+ });
15513
+ try {
15514
+ return processStatement(body, bb, jumpTarget, ctx);
15515
+ } finally {
15516
+ ctx.loopStack.pop();
15517
+ }
15518
+ }
15519
+ if (t.isEmptyStatement(stmt)) {
15520
+ return bb;
15521
+ }
15522
+ if (t.isBlockStatement(stmt)) {
15523
+ const shouldScopeLabel = !!labelOverride && !!ctx;
15524
+ if (shouldScopeLabel) {
15525
+ ctx.loopStack.push({
15526
+ breakTarget: jumpTarget,
15527
+ label: labelOverride
15528
+ });
15529
+ }
15530
+ let current = bb;
15531
+ try {
15532
+ for (const inner of stmt.body) {
15533
+ current = processStatement(inner, current, jumpTarget, ctx);
15534
+ if (current.sealed) {
15535
+ return current;
15536
+ }
15537
+ }
15538
+ return current;
15539
+ } finally {
15540
+ if (shouldScopeLabel) {
15541
+ ctx.loopStack.pop();
15542
+ }
15543
+ }
15544
+ }
15400
15545
  if (t.isExpressionStatement(stmt)) {
15401
15546
  if (!handleExpressionStatement(stmt.expression, push)) {
15402
15547
  push({ kind: "Expression", value: convertExpression(stmt.expression) });
@@ -15511,6 +15656,21 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15511
15656
  });
15512
15657
  return bb;
15513
15658
  }
15659
+ if (t.isClassDeclaration(stmt) && stmt.id) {
15660
+ const classExpr = t.classExpression(
15661
+ stmt.id,
15662
+ stmt.superClass,
15663
+ stmt.body,
15664
+ stmt.decorators ?? null
15665
+ );
15666
+ push({
15667
+ kind: "Assign",
15668
+ target: { kind: "Identifier", name: stmt.id.name },
15669
+ value: convertExpression(classExpr),
15670
+ declarationKind: "let"
15671
+ });
15672
+ return bb;
15673
+ }
15514
15674
  if (t.isReturnStatement(stmt)) {
15515
15675
  bb.block.terminator = {
15516
15676
  kind: "Return",
@@ -15529,7 +15689,7 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15529
15689
  }
15530
15690
  if (t.isBreakStatement(stmt) && ctx) {
15531
15691
  const label = stmt.label?.name;
15532
- const loopCtx = label ? ctx.loopStack.find((l) => l.label === label) : ctx.loopStack[ctx.loopStack.length - 1];
15692
+ const loopCtx = findBreakContext(ctx, label);
15533
15693
  if (loopCtx) {
15534
15694
  bb.block.terminator = { kind: "Break", target: loopCtx.breakTarget, label };
15535
15695
  bb.sealed = true;
@@ -15541,7 +15701,7 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15541
15701
  }
15542
15702
  if (t.isContinueStatement(stmt) && ctx) {
15543
15703
  const label = stmt.label?.name;
15544
- const loopCtx = label ? ctx.loopStack.find((l) => l.label === label) : ctx.loopStack[ctx.loopStack.length - 1];
15704
+ const loopCtx = findContinueContext(ctx, label);
15545
15705
  if (loopCtx) {
15546
15706
  bb.block.terminator = { kind: "Continue", target: loopCtx.continueTarget, label };
15547
15707
  bb.sealed = true;
@@ -15590,7 +15750,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15590
15750
  condBlock.sealed = true;
15591
15751
  ctx.loopStack.push({
15592
15752
  breakTarget: exitBlock.block.id,
15593
- continueTarget: condBlock.block.id
15753
+ continueTarget: condBlock.block.id,
15754
+ label: labelOverride
15594
15755
  });
15595
15756
  fillStatements(stmt.body, bodyBlock, condBlock.block.id, ctx);
15596
15757
  ctx.loopStack.pop();
@@ -15635,8 +15796,9 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15635
15796
  condBlock.sealed = true;
15636
15797
  ctx.loopStack.push({
15637
15798
  breakTarget: exitBlock.block.id,
15638
- continueTarget: updateBlock.block.id
15799
+ continueTarget: updateBlock.block.id,
15639
15800
  // continue goes to update in for loop
15801
+ label: labelOverride
15640
15802
  });
15641
15803
  fillStatements(stmt.body, bodyBlock, updateBlock.block.id, ctx);
15642
15804
  ctx.loopStack.pop();
@@ -15659,7 +15821,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15659
15821
  bb.sealed = true;
15660
15822
  ctx.loopStack.push({
15661
15823
  breakTarget: exitBlock.block.id,
15662
- continueTarget: condBlock.block.id
15824
+ continueTarget: condBlock.block.id,
15825
+ label: labelOverride
15663
15826
  });
15664
15827
  fillStatements(stmt.body, bodyBlock, condBlock.block.id, ctx);
15665
15828
  ctx.loopStack.pop();
@@ -15707,7 +15870,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15707
15870
  bb.sealed = true;
15708
15871
  ctx.loopStack.push({
15709
15872
  breakTarget: exitBlock.block.id,
15710
- continueTarget: bodyBlock.block.id
15873
+ continueTarget: bodyBlock.block.id,
15874
+ label: labelOverride
15711
15875
  });
15712
15876
  fillStatements(stmt.body, bodyBlock, exitBlock.block.id, ctx);
15713
15877
  ctx.loopStack.pop();
@@ -15747,7 +15911,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15747
15911
  bb.sealed = true;
15748
15912
  ctx.loopStack.push({
15749
15913
  breakTarget: exitBlock.block.id,
15750
- continueTarget: bodyBlock.block.id
15914
+ continueTarget: bodyBlock.block.id,
15915
+ label: labelOverride
15751
15916
  });
15752
15917
  fillStatements(stmt.body, bodyBlock, exitBlock.block.id, ctx);
15753
15918
  ctx.loopStack.pop();
@@ -15756,11 +15921,15 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15756
15921
  if (t.isSwitchStatement(stmt) && ctx) {
15757
15922
  const exitBlock = ctx.createBlock();
15758
15923
  ctx.blocks.push(exitBlock.block);
15924
+ const caseBlocks = stmt.cases.map(() => ctx.createBlock());
15925
+ for (const caseBlock of caseBlocks) {
15926
+ ctx.blocks.push(caseBlock.block);
15927
+ }
15759
15928
  const cases = [];
15760
15929
  let defaultTarget;
15761
- for (const switchCase of stmt.cases) {
15762
- const caseBlock = ctx.createBlock();
15763
- ctx.blocks.push(caseBlock.block);
15930
+ for (let index = 0; index < stmt.cases.length; index++) {
15931
+ const switchCase = stmt.cases[index];
15932
+ const caseBlock = caseBlocks[index];
15764
15933
  if (switchCase.test) {
15765
15934
  cases.push({
15766
15935
  test: convertExpression(switchCase.test),
@@ -15769,20 +15938,27 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15769
15938
  } else {
15770
15939
  defaultTarget = caseBlock.block.id;
15771
15940
  }
15941
+ }
15942
+ ctx.loopStack.push({
15943
+ breakTarget: exitBlock.block.id,
15944
+ label: labelOverride
15945
+ });
15946
+ for (let index = 0; index < stmt.cases.length; index++) {
15947
+ const switchCase = stmt.cases[index];
15948
+ const caseBlock = caseBlocks[index];
15949
+ const nextCaseBlock = caseBlocks[index + 1];
15950
+ const fallthroughTarget = nextCaseBlock ? nextCaseBlock.block.id : exitBlock.block.id;
15772
15951
  let current = caseBlock;
15773
15952
  for (const s of switchCase.consequent) {
15774
- if (t.isBreakStatement(s)) {
15775
- current.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15776
- current.sealed = true;
15777
- break;
15778
- }
15953
+ if (current.sealed) break;
15779
15954
  current = processStatement(s, current, exitBlock.block.id, ctx);
15780
15955
  }
15781
15956
  if (!current.sealed) {
15782
- current.block.terminator = { kind: "Jump", target: exitBlock.block.id };
15957
+ current.block.terminator = { kind: "Jump", target: fallthroughTarget };
15783
15958
  current.sealed = true;
15784
15959
  }
15785
15960
  }
15961
+ ctx.loopStack.pop();
15786
15962
  if (defaultTarget === void 0) {
15787
15963
  cases.push({ target: exitBlock.block.id });
15788
15964
  } else {
@@ -15831,30 +16007,41 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15831
16007
  }
15832
16008
  return exitBlock;
15833
16009
  }
15834
- if (!bb.sealed) {
15835
- bb.block.terminator = { kind: "Jump", target: jumpTarget };
15836
- bb.sealed = true;
15837
- }
15838
- return bb;
16010
+ throw new HIRError(`Unsupported statement in HIR lowering: ${stmt.type}`, "BUILD_ERROR", {
16011
+ blockId: bb.block.id
16012
+ });
15839
16013
  }
15840
16014
  function convertExpression(node, options) {
15841
16015
  const loc = getLoc(node);
15842
- const convertCallArguments = (args, reactiveScope) => args.map((arg) => {
15843
- if (t.isSpreadElement(arg)) {
15844
- return {
15845
- kind: "SpreadElement",
15846
- argument: convertExpression(arg.argument),
15847
- loc: getLoc(arg)
15848
- };
15849
- }
15850
- if (t.isExpression(arg)) {
15851
- if (reactiveScope && arg === args[0] && (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg))) {
15852
- return convertExpression(arg, { reactiveScope });
16016
+ const convertCallArguments = (args, reactiveScope) => {
16017
+ const converted = [];
16018
+ for (const arg of args) {
16019
+ if (t.isSpreadElement(arg)) {
16020
+ converted.push({
16021
+ kind: "SpreadElement",
16022
+ argument: convertExpression(arg.argument),
16023
+ loc: getLoc(arg)
16024
+ });
16025
+ continue;
16026
+ }
16027
+ if (t.isExpression(arg)) {
16028
+ if (reactiveScope && arg === args[0] && (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg))) {
16029
+ converted.push(convertExpression(arg, { reactiveScope }));
16030
+ continue;
16031
+ }
16032
+ converted.push(convertExpression(arg));
16033
+ continue;
16034
+ }
16035
+ if (t.isArgumentPlaceholder?.(arg)) {
16036
+ return reportUnsupportedExpression(
16037
+ arg,
16038
+ "Argument placeholders are not supported in HIR conversion."
16039
+ );
15853
16040
  }
15854
- return convertExpression(arg);
16041
+ return reportUnsupportedExpression(arg, "Unsupported call argument in HIR conversion.");
15855
16042
  }
15856
- return void 0;
15857
- }).filter(Boolean);
16043
+ return converted;
16044
+ };
15858
16045
  const resolveReactiveScope = (callee) => {
15859
16046
  const reactiveScopes = activeBuildOptions?.reactiveScopes;
15860
16047
  if (!reactiveScopes || reactiveScopes.size === 0) return void 0;
@@ -15866,6 +16053,9 @@ function convertExpression(node, options) {
15866
16053
  }
15867
16054
  return void 0;
15868
16055
  };
16056
+ if (t.isChainExpression?.(node) || node.type === "ChainExpression") {
16057
+ return convertExpression(node.expression, options);
16058
+ }
15869
16059
  if (t.isParenthesizedExpression(node) && t.isExpression(node.expression)) {
15870
16060
  return convertExpression(node.expression);
15871
16061
  }
@@ -15923,7 +16113,13 @@ function convertExpression(node, options) {
15923
16113
  return call;
15924
16114
  }
15925
16115
  if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
15926
- const propertyNode = t.isPrivateName(node.property) ? t.identifier(node.property.id.name) : node.property;
16116
+ if (t.isPrivateName(node.property)) {
16117
+ return reportUnsupportedExpression(
16118
+ node.property,
16119
+ "Private field access is not supported in HIR conversion."
16120
+ );
16121
+ }
16122
+ const propertyNode = node.property;
15927
16123
  const isOptional = t.isOptionalMemberExpression(node);
15928
16124
  const object = convertExpression(node.object);
15929
16125
  const property = t.isExpression(propertyNode) ? convertExpression(propertyNode) : { kind: "Literal", value: void 0 };
@@ -15989,67 +16185,106 @@ function convertExpression(node, options) {
15989
16185
  return cond;
15990
16186
  }
15991
16187
  if (t.isArrayExpression(node)) {
16188
+ if ((node.elements ?? []).some((el) => el == null)) {
16189
+ return reportUnsupportedExpression(
16190
+ node,
16191
+ "Array literal holes are not supported in HIR conversion. Use explicit undefined values instead."
16192
+ );
16193
+ }
16194
+ const elements = [];
16195
+ for (const el of node.elements ?? []) {
16196
+ if (!el) continue;
16197
+ if (t.isSpreadElement(el)) {
16198
+ elements.push({
16199
+ kind: "SpreadElement",
16200
+ argument: convertExpression(el.argument),
16201
+ loc: getLoc(el)
16202
+ });
16203
+ continue;
16204
+ }
16205
+ if (t.isExpression(el)) {
16206
+ elements.push(convertExpression(el));
16207
+ continue;
16208
+ }
16209
+ return reportUnsupportedExpression(el, "Unsupported array literal element in HIR conversion.");
16210
+ }
15992
16211
  const arr = {
15993
16212
  kind: "ArrayExpression",
15994
- elements: (node.elements ?? []).map((el) => {
15995
- if (!el) return void 0;
15996
- if (t.isSpreadElement(el)) {
15997
- return {
15998
- kind: "SpreadElement",
15999
- argument: convertExpression(el.argument),
16000
- loc: getLoc(el)
16001
- };
16002
- }
16003
- if (t.isExpression(el)) return convertExpression(el);
16004
- return void 0;
16005
- }).filter(Boolean),
16213
+ elements,
16006
16214
  loc
16007
16215
  };
16008
16216
  return arr;
16009
16217
  }
16010
16218
  if (t.isObjectExpression(node)) {
16219
+ const properties = [];
16011
16220
  const obj = {
16012
16221
  kind: "ObjectExpression",
16013
- properties: node.properties.map((prop) => {
16014
- if (t.isSpreadElement(prop)) {
16015
- return {
16016
- kind: "SpreadElement",
16017
- argument: convertExpression(prop.argument),
16018
- loc: getLoc(prop)
16019
- };
16222
+ properties,
16223
+ loc
16224
+ };
16225
+ for (const prop of node.properties) {
16226
+ if (t.isSpreadElement(prop)) {
16227
+ properties.push({
16228
+ kind: "SpreadElement",
16229
+ argument: convertExpression(prop.argument),
16230
+ loc: getLoc(prop)
16231
+ });
16232
+ continue;
16233
+ }
16234
+ if (t.isObjectMethod(prop)) {
16235
+ 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;
16236
+ if (!keyExpr) {
16237
+ return reportUnsupportedExpression(
16238
+ prop.key,
16239
+ "Unsupported object literal key in HIR conversion."
16240
+ );
16020
16241
  }
16021
- if (t.isObjectMethod(prop)) {
16022
- if (prop.computed) return void 0;
16023
- 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;
16024
- if (!keyExpr2) return void 0;
16025
- const fnExpr = t.functionExpression(
16026
- null,
16027
- prop.params,
16028
- prop.body,
16029
- prop.generator,
16030
- prop.async
16242
+ const fnExpr = t.functionExpression(
16243
+ null,
16244
+ prop.params,
16245
+ prop.body,
16246
+ prop.generator,
16247
+ prop.async
16248
+ );
16249
+ properties.push({
16250
+ kind: "Property",
16251
+ key: keyExpr,
16252
+ value: convertExpression(fnExpr),
16253
+ computed: prop.computed,
16254
+ propertyKind: prop.kind ?? "method",
16255
+ loc: getLoc(prop)
16256
+ });
16257
+ continue;
16258
+ }
16259
+ if (t.isObjectProperty(prop)) {
16260
+ 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;
16261
+ if (!keyExpr) {
16262
+ return reportUnsupportedExpression(
16263
+ prop.key,
16264
+ "Unsupported object literal key in HIR conversion."
16031
16265
  );
16032
- return {
16033
- kind: "Property",
16034
- key: keyExpr2,
16035
- value: convertExpression(fnExpr),
16036
- loc: getLoc(prop)
16037
- };
16038
16266
  }
16039
- if (!t.isObjectProperty(prop) || prop.computed) return void 0;
16040
- 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;
16041
- if (!keyExpr) return void 0;
16042
- if (!t.isExpression(prop.value)) return void 0;
16043
- return {
16267
+ if (!t.isExpression(prop.value)) {
16268
+ return reportUnsupportedExpression(
16269
+ prop.value,
16270
+ "Unsupported object literal value in HIR conversion."
16271
+ );
16272
+ }
16273
+ properties.push({
16044
16274
  kind: "Property",
16045
16275
  key: keyExpr,
16046
16276
  value: convertExpression(prop.value),
16277
+ computed: prop.computed,
16047
16278
  shorthand: prop.shorthand && t.isIdentifier(prop.value),
16048
16279
  loc: getLoc(prop)
16049
- };
16050
- }).filter(Boolean),
16051
- loc
16052
- };
16280
+ });
16281
+ continue;
16282
+ }
16283
+ return reportUnsupportedExpression(
16284
+ prop,
16285
+ "Unsupported object literal property in HIR conversion."
16286
+ );
16287
+ }
16053
16288
  return obj;
16054
16289
  }
16055
16290
  if (t.isJSXElement(node)) {
@@ -16077,6 +16312,11 @@ function convertExpression(node, options) {
16077
16312
  value: convertJSXElement(child),
16078
16313
  loc: getLoc(child)
16079
16314
  });
16315
+ } else if (t.isJSXSpreadChild(child)) {
16316
+ return reportUnsupportedExpression(
16317
+ child,
16318
+ "JSX spread children are not supported in HIR conversion."
16319
+ );
16080
16320
  } else if (t.isJSXFragment(child)) {
16081
16321
  for (const fragChild of child.children) {
16082
16322
  if (t.isJSXText(fragChild)) {
@@ -16098,6 +16338,11 @@ function convertExpression(node, options) {
16098
16338
  value: convertJSXElement(fragChild),
16099
16339
  loc: getLoc(fragChild)
16100
16340
  });
16341
+ } else if (t.isJSXSpreadChild(fragChild)) {
16342
+ return reportUnsupportedExpression(
16343
+ fragChild,
16344
+ "JSX spread children are not supported in HIR conversion."
16345
+ );
16101
16346
  }
16102
16347
  }
16103
16348
  }
@@ -16293,7 +16538,10 @@ function convertJSXElement(node) {
16293
16538
  tagName = convertJSXMemberExpr(opening.name);
16294
16539
  isComponent = true;
16295
16540
  } else {
16296
- tagName = "div";
16541
+ return reportUnsupportedExpression(
16542
+ opening.name,
16543
+ `Unsupported JSX tag syntax '${opening.name.type}' in HIR conversion`
16544
+ );
16297
16545
  }
16298
16546
  const attributes = [];
16299
16547
  for (const attr of opening.attributes) {
@@ -16305,17 +16553,33 @@ function convertJSXElement(node) {
16305
16553
  spreadExpr: convertExpression(attr.argument),
16306
16554
  loc: getLoc(attr)
16307
16555
  });
16308
- } else if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
16556
+ } else if (t.isJSXAttribute(attr)) {
16557
+ const nameNode = attr.name;
16558
+ let attrName = null;
16559
+ if (t.isJSXIdentifier(attr.name)) {
16560
+ attrName = attr.name.name;
16561
+ } else if (t.isJSXNamespacedName(attr.name)) {
16562
+ attrName = `${attr.name.namespace.name}:${attr.name.name.name}`;
16563
+ } else {
16564
+ return reportUnsupportedExpression(
16565
+ nameNode,
16566
+ "Unsupported JSX attribute name in HIR conversion"
16567
+ );
16568
+ }
16309
16569
  let value = null;
16310
16570
  if (attr.value) {
16311
16571
  if (t.isStringLiteral(attr.value)) {
16312
16572
  value = { kind: "Literal", value: attr.value.value, loc: getLoc(attr.value) };
16313
16573
  } else if (t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
16314
16574
  value = convertExpression(attr.value.expression);
16575
+ } else if (t.isJSXElement(attr.value)) {
16576
+ value = convertJSXElement(attr.value);
16577
+ } else if (t.isJSXFragment(attr.value)) {
16578
+ value = convertExpression(attr.value);
16315
16579
  }
16316
16580
  }
16317
16581
  attributes.push({
16318
- name: attr.name.name,
16582
+ name: attrName,
16319
16583
  value,
16320
16584
  loc: getLoc(attr)
16321
16585
  });
@@ -16342,6 +16606,11 @@ function convertJSXElement(node) {
16342
16606
  value: convertJSXElement(child),
16343
16607
  loc: getLoc(child)
16344
16608
  });
16609
+ } else if (t.isJSXSpreadChild(child)) {
16610
+ return reportUnsupportedExpression(
16611
+ child,
16612
+ "JSX spread children are not supported in HIR conversion."
16613
+ );
16345
16614
  } else if (t.isJSXFragment(child)) {
16346
16615
  for (const fragChild of child.children) {
16347
16616
  if (t.isJSXText(fragChild)) {
@@ -16363,6 +16632,11 @@ function convertJSXElement(node) {
16363
16632
  value: convertJSXElement(fragChild),
16364
16633
  loc: getLoc(fragChild)
16365
16634
  });
16635
+ } else if (t.isJSXSpreadChild(fragChild)) {
16636
+ return reportUnsupportedExpression(
16637
+ fragChild,
16638
+ "JSX spread children are not supported in HIR conversion."
16639
+ );
16366
16640
  }
16367
16641
  }
16368
16642
  }
@@ -16598,7 +16872,7 @@ var DiagnosticMessages = {
16598
16872
  ["FICT-R001" /* FICT_R001 */]: "Expression crosses reactive region boundary.",
16599
16873
  ["FICT-R002" /* FICT_R002 */]: "Scope escape detected, value may not be tracked.",
16600
16874
  ["FICT-R003" /* FICT_R003 */]: "Expression cannot be memoized automatically.",
16601
- ["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.",
16875
+ ["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.",
16602
16876
  ["FICT-R005" /* FICT_R005 */]: "Function captures reactive variables from outer scope; pass them as parameters or memoize explicitly to avoid hidden dependencies.",
16603
16877
  ["FICT-X001" /* FICT_X001 */]: "Object is recreated on each render, consider memoizing.",
16604
16878
  ["FICT-X002" /* FICT_X002 */]: "Array is recreated on each render, consider memoizing.",
@@ -16628,7 +16902,7 @@ var DiagnosticSeverities = {
16628
16902
  ["FICT-R001" /* FICT_R001 */]: "info" /* Info */,
16629
16903
  ["FICT-R002" /* FICT_R002 */]: "warning" /* Warning */,
16630
16904
  ["FICT-R003" /* FICT_R003 */]: "info" /* Info */,
16631
- ["FICT-R004" /* FICT_R004 */]: "warning" /* Warning */,
16905
+ ["FICT-R004" /* FICT_R004 */]: "error" /* Error */,
16632
16906
  ["FICT-R005" /* FICT_R005 */]: "warning" /* Warning */,
16633
16907
  ["FICT-X001" /* FICT_X001 */]: "hint" /* Hint */,
16634
16908
  ["FICT-X002" /* FICT_X002 */]: "hint" /* Hint */,
@@ -17108,7 +17382,11 @@ function rewriteExprWithMap(expr, rewrites) {
17108
17382
  if (p.kind === "SpreadElement") {
17109
17383
  return { ...p, argument: rewriteExprWithMap(p.argument, rewrites) };
17110
17384
  }
17111
- return { ...p, value: rewriteExprWithMap(p.value, rewrites) };
17385
+ return {
17386
+ ...p,
17387
+ key: p.computed ? rewriteExprWithMap(p.key, rewrites) : p.key,
17388
+ value: rewriteExprWithMap(p.value, rewrites)
17389
+ };
17112
17390
  })
17113
17391
  };
17114
17392
  case "ImportExpression":
@@ -17255,7 +17533,11 @@ function toSSA(fn) {
17255
17533
  if (p.kind === "SpreadElement") {
17256
17534
  return { ...p, argument: renameExpr(p.argument) };
17257
17535
  }
17258
- return { ...p, value: renameExpr(p.value) };
17536
+ return {
17537
+ ...p,
17538
+ key: p.computed ? renameExpr(p.key) : p.key,
17539
+ value: renameExpr(p.value)
17540
+ };
17259
17541
  })
17260
17542
  };
17261
17543
  default:
@@ -18685,16 +18967,104 @@ function structurizeBranchUntilJoin(ctx, block, term, outerJoin) {
18685
18967
  return ifNode;
18686
18968
  }
18687
18969
  function structurizeSwitch(ctx, block, term) {
18970
+ const uniqueTargets = Array.from(new Set(term.cases.map((c) => c.target)));
18971
+ const joinBlock = findSwitchJoinBlock(ctx, uniqueTargets);
18688
18972
  const cases = [];
18973
+ const emittedBeforeSwitch = new Set(ctx.emitted);
18974
+ const emittedBySwitchCases = /* @__PURE__ */ new Set();
18689
18975
  for (const c of term.cases) {
18690
- const body = structurizeBlock(ctx, c.target);
18691
- cases.push({ test: c.test ?? null, body });
18976
+ const caseCtx = {
18977
+ ...ctx,
18978
+ emitted: new Set(emittedBeforeSwitch),
18979
+ processing: new Set(ctx.processing)
18980
+ };
18981
+ const body = joinBlock !== void 0 ? structurizeBlockUntilJoin(caseCtx, c.target, joinBlock) : structurizeBlock(caseCtx, c.target);
18982
+ for (const emittedBlock of caseCtx.emitted) {
18983
+ if (!emittedBeforeSwitch.has(emittedBlock)) {
18984
+ emittedBySwitchCases.add(emittedBlock);
18985
+ }
18986
+ }
18987
+ const normalizedBody = appendSwitchCaseBreak(body);
18988
+ cases.push({ test: c.test ?? null, body: normalizedBody });
18692
18989
  }
18693
- return {
18990
+ for (const emittedBlock of emittedBySwitchCases) {
18991
+ ctx.emitted.add(emittedBlock);
18992
+ }
18993
+ const switchNode = {
18694
18994
  kind: "switch",
18695
18995
  discriminant: term.discriminant,
18696
18996
  cases
18697
18997
  };
18998
+ if (joinBlock !== void 0 && !ctx.emitted.has(joinBlock)) {
18999
+ const joinNode = structurizeBlock(ctx, joinBlock);
19000
+ return { kind: "sequence", nodes: [switchNode, joinNode] };
19001
+ }
19002
+ return switchNode;
19003
+ }
19004
+ function findSwitchJoinBlock(ctx, caseTargets) {
19005
+ const uniqueTargets = Array.from(new Set(caseTargets));
19006
+ if (uniqueTargets.length === 0) return void 0;
19007
+ const reachableByCase = uniqueTargets.map(
19008
+ (target) => collectReachableBlocks(ctx, target, /* @__PURE__ */ new Set())
19009
+ );
19010
+ const reachableUnion = /* @__PURE__ */ new Set();
19011
+ for (const set of reachableByCase) {
19012
+ for (const id of set) reachableUnion.add(id);
19013
+ }
19014
+ const minCaseCoverage = uniqueTargets.length > 1 ? 2 : 1;
19015
+ const candidates = [];
19016
+ for (const id of reachableUnion) {
19017
+ if (uniqueTargets.includes(id)) continue;
19018
+ let reachableCases = 0;
19019
+ for (const set of reachableByCase) {
19020
+ if (set.has(id)) reachableCases++;
19021
+ }
19022
+ if (reachableCases < minCaseCoverage) continue;
19023
+ const predecessors = ctx.predecessors.get(id) ?? [];
19024
+ const predecessorCount = predecessors.filter((pred) => reachableUnion.has(pred)).length;
19025
+ candidates.push({
19026
+ id,
19027
+ reachableCases,
19028
+ predecessorCount,
19029
+ isJoinPoint: predecessors.length > 1
19030
+ });
19031
+ }
19032
+ if (candidates.length === 0) return void 0;
19033
+ candidates.sort((a, b) => {
19034
+ if (a.reachableCases !== b.reachableCases) return b.reachableCases - a.reachableCases;
19035
+ if (a.predecessorCount !== b.predecessorCount) return b.predecessorCount - a.predecessorCount;
19036
+ if (a.isJoinPoint !== b.isJoinPoint) return Number(b.isJoinPoint) - Number(a.isJoinPoint);
19037
+ return a.id - b.id;
19038
+ });
19039
+ return candidates[0]?.id;
19040
+ }
19041
+ function appendSwitchCaseBreak(body) {
19042
+ if (isSwitchCaseTerminated(body)) return body;
19043
+ if (body.kind === "sequence") {
19044
+ return { kind: "sequence", nodes: [...body.nodes, { kind: "break" }] };
19045
+ }
19046
+ return { kind: "sequence", nodes: [body, { kind: "break" }] };
19047
+ }
19048
+ function isSwitchCaseTerminated(node) {
19049
+ if (!node) return false;
19050
+ switch (node.kind) {
19051
+ case "return":
19052
+ case "throw":
19053
+ case "break":
19054
+ case "continue":
19055
+ return true;
19056
+ case "sequence":
19057
+ return node.nodes.length > 0 && isSwitchCaseTerminated(node.nodes[node.nodes.length - 1]);
19058
+ case "block":
19059
+ return node.statements.length > 0 && isSwitchCaseTerminated(node.statements[node.statements.length - 1]);
19060
+ case "if":
19061
+ return isSwitchCaseTerminated(node.consequent) && isSwitchCaseTerminated(node.alternate ?? void 0);
19062
+ case "try":
19063
+ if (node.finalizer && isSwitchCaseTerminated(node.finalizer)) return true;
19064
+ return isSwitchCaseTerminated(node.block) && isSwitchCaseTerminated(node.handler?.body ?? void 0);
19065
+ default:
19066
+ return false;
19067
+ }
18698
19068
  }
18699
19069
  function structurizeForOf(ctx, block, term) {
18700
19070
  const body = structurizeBlock(ctx, term.body);
@@ -18896,6 +19266,7 @@ function resolveKeySet(expr, ctx, shapes) {
18896
19266
  const values = [];
18897
19267
  for (const prop of arg.properties) {
18898
19268
  if (prop.kind !== "Property") return null;
19269
+ if (prop.computed) return null;
18899
19270
  if (prop.key.kind === "Identifier") {
18900
19271
  values.push(prop.key.name);
18901
19272
  } else if (prop.key.kind === "Literal") {
@@ -19295,7 +19666,10 @@ function analyzeExpression(expr, shapes, propertyReads, ctx) {
19295
19666
  }
19296
19667
  analyzeExpression(prop.argument, shapes, propertyReads, ctx);
19297
19668
  } else if (prop.kind === "Property") {
19298
- if (prop.key.kind === "Identifier") {
19669
+ if (prop.computed) {
19670
+ shape.dynamicAccess = true;
19671
+ analyzeExpression(prop.key, shapes, propertyReads, ctx);
19672
+ } else if (prop.key.kind === "Identifier") {
19299
19673
  shape.knownKeys.add(prop.key.name);
19300
19674
  } else if (prop.key.kind === "Literal" && typeof prop.key.value === "string") {
19301
19675
  shape.knownKeys.add(prop.key.value);
@@ -19535,6 +19909,7 @@ function markEscaping(expr, shapes) {
19535
19909
  if (prop.kind === "SpreadElement") {
19536
19910
  markEscaping(prop.argument, shapes);
19537
19911
  } else if (prop.kind === "Property") {
19912
+ if (prop.computed) markEscaping(prop.key, shapes);
19538
19913
  markEscaping(prop.value, shapes);
19539
19914
  }
19540
19915
  }
@@ -19639,7 +20014,7 @@ function expressionContainsReactiveCreation(expr, memoMacroNames) {
19639
20014
  return expr.elements.some((el) => el && expressionContainsReactiveCreation(el, memoMacroNames));
19640
20015
  case "ObjectExpression":
19641
20016
  return expr.properties.some(
19642
- (prop) => prop.kind === "SpreadElement" ? expressionContainsReactiveCreation(prop.argument, memoMacroNames) : expressionContainsReactiveCreation(prop.value, memoMacroNames)
20017
+ (prop) => prop.kind === "SpreadElement" ? expressionContainsReactiveCreation(prop.argument, memoMacroNames) : prop.computed && expressionContainsReactiveCreation(prop.key, memoMacroNames) || expressionContainsReactiveCreation(prop.value, memoMacroNames)
19643
20018
  );
19644
20019
  case "ArrowFunction":
19645
20020
  if (expr.isExpression) {
@@ -19925,7 +20300,7 @@ function expressionUsesTracked(expr, ctx) {
19925
20300
  case "ObjectExpression":
19926
20301
  return expr.properties.some((p) => {
19927
20302
  if (p.kind === "SpreadElement") return expressionUsesTracked(p.argument, ctx);
19928
- return expressionUsesTracked(p.value, ctx);
20303
+ return p.computed && expressionUsesTracked(p.key, ctx) || expressionUsesTracked(p.value, ctx);
19929
20304
  });
19930
20305
  case "TemplateLiteral":
19931
20306
  return expr.expressions.some((e) => expressionUsesTracked(e, ctx));
@@ -20018,6 +20393,16 @@ function lowerStructuredNodeInternal(node, t4, ctx, declaredVars, regionResult)
20018
20393
  } : void 0;
20019
20394
  return lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx);
20020
20395
  }
20396
+ function ensureSwitchCaseBreak(stmts, t4) {
20397
+ if (stmts.length === 0) {
20398
+ return stmts;
20399
+ }
20400
+ const tail = stmts[stmts.length - 1];
20401
+ if (tail && (t4.isBreakStatement(tail) || t4.isReturnStatement(tail) || t4.isThrowStatement(tail) || t4.isContinueStatement(tail))) {
20402
+ return stmts;
20403
+ }
20404
+ return [...stmts, t4.breakStatement()];
20405
+ }
20021
20406
  function lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx) {
20022
20407
  switch (node.kind) {
20023
20408
  case "sequence": {
@@ -20207,10 +20592,20 @@ function lowerNodeWithRegionContext(node, t4, ctx, declaredVars, regionCtx) {
20207
20592
  return [t4.forInStatement(left, right, body)];
20208
20593
  }
20209
20594
  case "switch": {
20210
- const cases = node.cases.map((c) => {
20211
- const stmts = lowerNodeWithRegionContext(c.body, t4, ctx, declaredVars, regionCtx);
20212
- return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20213
- });
20595
+ const prevConditional = ctx.inConditional ?? 0;
20596
+ ctx.inConditional = prevConditional + 1;
20597
+ let cases;
20598
+ try {
20599
+ cases = node.cases.map((c) => {
20600
+ const stmts = ensureSwitchCaseBreak(
20601
+ lowerNodeWithRegionContext(c.body, t4, ctx, declaredVars, regionCtx),
20602
+ t4
20603
+ );
20604
+ return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20605
+ });
20606
+ } finally {
20607
+ ctx.inConditional = prevConditional;
20608
+ }
20214
20609
  return [t4.switchStatement(lowerExpressionWithDeSSA(node.discriminant, ctx), cases)];
20215
20610
  }
20216
20611
  case "try": {
@@ -20499,19 +20894,29 @@ function lowerStructuredNodeForRegion(node, region, t4, ctx, declaredVars, regio
20499
20894
  return [t4.forInStatement(left, right, t4.blockStatement(body))];
20500
20895
  }
20501
20896
  case "switch": {
20502
- const cases = node.cases.map((c) => {
20503
- const stmts = lowerStructuredNodeForRegion(
20504
- c.body,
20505
- region,
20506
- t4,
20507
- ctx,
20508
- declaredVars,
20509
- regionCtx,
20510
- skipInstructions
20511
- );
20512
- if (stmts.length === 0) return null;
20513
- return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20514
- }).filter((c) => !!c);
20897
+ const prevConditional = ctx.inConditional ?? 0;
20898
+ ctx.inConditional = prevConditional + 1;
20899
+ let cases;
20900
+ try {
20901
+ cases = node.cases.map((c) => {
20902
+ const stmts = ensureSwitchCaseBreak(
20903
+ lowerStructuredNodeForRegion(
20904
+ c.body,
20905
+ region,
20906
+ t4,
20907
+ ctx,
20908
+ declaredVars,
20909
+ regionCtx,
20910
+ skipInstructions
20911
+ ),
20912
+ t4
20913
+ );
20914
+ if (stmts.length === 0) return null;
20915
+ return t4.switchCase(c.test ? lowerExpressionWithDeSSA(c.test, ctx) : null, stmts);
20916
+ }).filter((c) => !!c);
20917
+ } finally {
20918
+ ctx.inConditional = prevConditional;
20919
+ }
20515
20920
  if (cases.length === 0) return [];
20516
20921
  return [t4.switchStatement(lowerExpressionWithDeSSA(node.discriminant, ctx), cases)];
20517
20922
  }
@@ -20964,6 +21369,7 @@ function collectExprDependencies(expr) {
20964
21369
  if (p.kind === "SpreadElement") {
20965
21370
  visit(p.argument);
20966
21371
  } else {
21372
+ if (p.computed) visit(p.key);
20967
21373
  visit(p.value);
20968
21374
  }
20969
21375
  });
@@ -21827,7 +22233,10 @@ function expressionContainsJSX(expr) {
21827
22233
  case "ArrayExpression":
21828
22234
  return expr.elements?.some((el) => expressionContainsJSX(el)) ?? false;
21829
22235
  case "ObjectExpression":
21830
- return expr.properties?.some((p) => expressionContainsJSX(p.value)) ?? false;
22236
+ return expr.properties?.some((p) => {
22237
+ if (p.kind === "SpreadElement") return expressionContainsJSX(p.argument);
22238
+ return (p.computed ?? false) && expressionContainsJSX(p.key) || expressionContainsJSX(p.value);
22239
+ }) ?? false;
21831
22240
  case "ConditionalExpression":
21832
22241
  return expressionContainsJSX(expr.test) || expressionContainsJSX(expr.consequent) || expressionContainsJSX(expr.alternate);
21833
22242
  case "ArrowFunction":
@@ -21941,7 +22350,7 @@ function expressionHasAwait(expr) {
21941
22350
  return expr.elements.some((el) => el && expressionHasAwait(el));
21942
22351
  case "ObjectExpression":
21943
22352
  return expr.properties.some(
21944
- (prop) => prop.kind === "Property" && expressionHasAwait(prop.value) || prop.kind === "SpreadElement" && expressionHasAwait(prop.argument)
22353
+ (prop) => prop.kind === "Property" && ((prop.computed ?? false) && expressionHasAwait(prop.key) || expressionHasAwait(prop.value)) || prop.kind === "SpreadElement" && expressionHasAwait(prop.argument)
21945
22354
  );
21946
22355
  case "TemplateLiteral":
21947
22356
  return expr.expressions.some((ex) => expressionHasAwait(ex));
@@ -22012,6 +22421,7 @@ function collectCalledIdentifiers(fn) {
22012
22421
  if (p.kind === "SpreadElement") {
22013
22422
  visitExpr(p.argument);
22014
22423
  } else {
22424
+ if (p.computed) visitExpr(p.key);
22015
22425
  visitExpr(p.value);
22016
22426
  }
22017
22427
  });
@@ -22238,6 +22648,7 @@ function analyzeHookReturnInfo(fn, ctx) {
22238
22648
  if (expr.kind === "ObjectExpression") {
22239
22649
  expr.properties.forEach((prop) => {
22240
22650
  if (prop.kind !== "Property") return;
22651
+ if (prop.computed) return;
22241
22652
  const keyName = prop.key.kind === "Identifier" ? prop.key.name : prop.key.kind === "Literal" ? String(prop.key.value) : void 0;
22242
22653
  if (!keyName) return;
22243
22654
  if (prop.value.kind === "Identifier") {
@@ -22546,6 +22957,7 @@ function collectExpressionIdentifiers(expr, into) {
22546
22957
  collectExpressionIdentifiers(prop.argument, into);
22547
22958
  return;
22548
22959
  }
22960
+ if (prop.computed) collectExpressionIdentifiers(prop.key, into);
22549
22961
  collectExpressionIdentifiers(prop.value, into);
22550
22962
  });
22551
22963
  return;
@@ -22663,6 +23075,7 @@ function collectExpressionIdentifiersDeep(expr, into, bound = /* @__PURE__ */ ne
22663
23075
  collectExpressionIdentifiersDeep(prop.argument, into, bound);
22664
23076
  return;
22665
23077
  }
23078
+ if (prop.computed) collectExpressionIdentifiersDeep(prop.key, into, bound);
22666
23079
  collectExpressionIdentifiersDeep(prop.value, into, bound);
22667
23080
  });
22668
23081
  return;
@@ -24169,6 +24582,28 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
24169
24582
  if (p.kind === "SpreadElement") {
24170
24583
  return t4.spreadElement(lowerExpression(p.argument, ctx));
24171
24584
  }
24585
+ const keyIsIdentifier = !p.computed && p.key.kind === "Identifier";
24586
+ const keyIdent = keyIsIdentifier ? p.key.name : "";
24587
+ const keyNode = p.computed ? lowerExpression(p.key, ctx) : keyIsIdentifier ? t4.identifier(keyIdent) : lowerExpression(p.key, ctx);
24588
+ if (p.propertyKind && p.propertyKind !== "init") {
24589
+ const valueExpr2 = lowerExpression(p.value, ctx);
24590
+ if (!t4.isFunctionExpression(valueExpr2)) {
24591
+ throw new HIRError(
24592
+ `Object method property did not lower to function expression.`,
24593
+ "CODEGEN_ERROR"
24594
+ );
24595
+ }
24596
+ const method = t4.objectMethod(
24597
+ p.propertyKind === "method" ? "method" : p.propertyKind,
24598
+ keyNode,
24599
+ valueExpr2.params,
24600
+ valueExpr2.body,
24601
+ !!p.computed
24602
+ );
24603
+ method.async = valueExpr2.async;
24604
+ method.generator = valueExpr2.generator;
24605
+ return method;
24606
+ }
24172
24607
  const usesTracked = !!ctx.inPropsContext && (!ctx.nonReactiveScopeDepth || ctx.nonReactiveScopeDepth === 0) && p.value.kind !== "ArrowFunction" && p.value.kind !== "FunctionExpression" && expressionUsesTracked(p.value, ctx);
24173
24608
  const valueExprRaw = usesTracked ? lowerTrackedExpression(p.value, ctx) : lowerExpression(p.value, ctx);
24174
24609
  const shouldMemoProp = usesTracked && !t4.isIdentifier(valueExprRaw) && !t4.isMemberExpression(valueExprRaw) && !t4.isLiteral(valueExprRaw);
@@ -24184,13 +24619,11 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
24184
24619
  t4.arrowFunctionExpression([], valueExprRaw)
24185
24620
  ]);
24186
24621
  })() : valueExprRaw;
24187
- const keyName = p.key.kind === "Identifier" ? p.key.name : String(p.key.value ?? "");
24188
- const keyNode = p.key.kind === "Identifier" ? t4.identifier(keyName) : t4.stringLiteral(keyName);
24189
- const useShorthand = p.shorthand && t4.isIdentifier(valueExpr) && p.key.kind === "Identifier" && deSSAVarName(keyName) === valueExpr.name;
24622
+ const useShorthand = p.shorthand && t4.isIdentifier(valueExpr) && keyIsIdentifier && deSSAVarName(keyIdent) === valueExpr.name;
24190
24623
  return t4.objectProperty(
24191
24624
  useShorthand ? t4.identifier(valueExpr.name) : keyNode,
24192
24625
  valueExpr,
24193
- false,
24626
+ !!p.computed,
24194
24627
  useShorthand
24195
24628
  );
24196
24629
  })
@@ -24660,6 +25093,7 @@ function collectExpressionDependencies(expr, deps) {
24660
25093
  if (p.kind === "SpreadElement") {
24661
25094
  collectExpressionDependencies(p.argument, deps);
24662
25095
  } else {
25096
+ if (p.computed) collectExpressionDependencies(p.key, deps);
24663
25097
  collectExpressionDependencies(p.value, deps);
24664
25098
  }
24665
25099
  });
@@ -25184,6 +25618,7 @@ function countExpressionNodes(expr) {
25184
25618
  case "ObjectExpression":
25185
25619
  for (const prop of expr.properties) {
25186
25620
  if (prop.kind === "Property") {
25621
+ if (prop.computed) count += countExpressionNodes(prop.key);
25187
25622
  count += countExpressionNodes(prop.value);
25188
25623
  } else if (prop.kind === "SpreadElement") {
25189
25624
  count += countExpressionNodes(prop.argument);
@@ -26545,7 +26980,7 @@ function hirExpressionUsesIdentifiers(expr, names) {
26545
26980
  if (prop.kind === "SpreadElement") {
26546
26981
  return hirExpressionUsesIdentifiers(prop.argument, names);
26547
26982
  }
26548
- return hirExpressionUsesIdentifiers(prop.key, names) || hirExpressionUsesIdentifiers(prop.value, names);
26983
+ return (prop.computed ?? false) && hirExpressionUsesIdentifiers(prop.key, names) || hirExpressionUsesIdentifiers(prop.value, names);
26549
26984
  });
26550
26985
  case "TemplateLiteral":
26551
26986
  return expr.expressions.some((e) => hirExpressionUsesIdentifiers(e, names));
@@ -27355,18 +27790,82 @@ function lowerTopLevelStatementBlock(statements, ctx, t4, name = "__module_segme
27355
27790
  }
27356
27791
  function transformControlFlowReturns(statements, ctx) {
27357
27792
  const { t: t4 } = ctx;
27793
+ const reactiveAccessorNames = /* @__PURE__ */ new Set([
27794
+ ...ctx.trackedVars,
27795
+ ...ctx.signalVars ?? [],
27796
+ ...ctx.memoVars ?? [],
27797
+ ...ctx.aliasVars ?? [],
27798
+ ...ctx.storeVars ?? []
27799
+ ]);
27358
27800
  const toStatements = (node) => t4.isBlockStatement(node) ? node.body : [node];
27359
27801
  const endsWithReturn = (stmts) => {
27360
27802
  if (stmts.length === 0) return false;
27361
27803
  const tail = stmts[stmts.length - 1];
27362
27804
  if (t4.isReturnStatement(tail)) return true;
27805
+ if (t4.isBlockStatement(tail)) {
27806
+ return endsWithReturn(tail.body);
27807
+ }
27363
27808
  if (t4.isIfStatement(tail) && tail.consequent && tail.alternate) {
27364
27809
  const conseqStmts = toStatements(tail.consequent);
27365
27810
  const altStmts = toStatements(tail.alternate);
27366
27811
  return endsWithReturn(conseqStmts) && endsWithReturn(altStmts);
27367
27812
  }
27813
+ if (t4.isTryStatement(tail)) {
27814
+ if (tail.finalizer && endsWithReturn(tail.finalizer.body)) return true;
27815
+ if (!tail.handler) return false;
27816
+ return endsWithReturn(tail.block.body) && endsWithReturn(tail.handler.body.body);
27817
+ }
27368
27818
  return false;
27369
27819
  };
27820
+ function hasNodeMatch(nodes, predicate) {
27821
+ let found = false;
27822
+ const visit = (node) => {
27823
+ if (!node || found) return;
27824
+ if (predicate(node)) {
27825
+ found = true;
27826
+ return;
27827
+ }
27828
+ const keys = t4.VISITOR_KEYS;
27829
+ const visitorKeys = keys?.[node.type] ?? [];
27830
+ for (const key of visitorKeys) {
27831
+ const value = node[key];
27832
+ if (Array.isArray(value)) {
27833
+ for (const child of value) {
27834
+ if (child && typeof child === "object" && "type" in child) {
27835
+ visit(child);
27836
+ }
27837
+ if (found) return;
27838
+ }
27839
+ } else if (value && typeof value === "object" && "type" in value) {
27840
+ visit(value);
27841
+ }
27842
+ if (found) return;
27843
+ }
27844
+ };
27845
+ for (const node of nodes) {
27846
+ visit(node);
27847
+ if (found) return true;
27848
+ }
27849
+ return found;
27850
+ }
27851
+ const containsReturnStatement = (nodes) => hasNodeMatch(nodes, (node) => t4.isReturnStatement(node));
27852
+ const containsReactiveAccessorRead = (nodes) => hasNodeMatch(nodes, (node) => {
27853
+ if (!t4.isCallExpression(node) && !t4.isOptionalCallExpression(node)) return false;
27854
+ const callee = node.callee;
27855
+ return t4.isIdentifier(callee) && reactiveAccessorNames.has(callee.name);
27856
+ });
27857
+ const emitControlFlowFallbackWarning = (node, kind) => {
27858
+ const onWarn = ctx.options?.onWarn;
27859
+ if (!onWarn) return;
27860
+ const loc = node.loc?.start;
27861
+ onWarn({
27862
+ code: "FICT-R003" /* FICT_R003 */,
27863
+ 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.`,
27864
+ fileName: ctx.options?.filename ?? "<unknown>",
27865
+ line: loc?.line ?? 0,
27866
+ column: loc ? loc.column + 1 : 0
27867
+ });
27868
+ };
27370
27869
  function buildReturnBlock(stmts) {
27371
27870
  if (stmts.length === 0) return null;
27372
27871
  for (let i = 0; i < stmts.length; i++) {
@@ -27381,33 +27880,55 @@ function transformControlFlowReturns(statements, ctx) {
27381
27880
  if (!endsWithReturn(stmts)) return null;
27382
27881
  return stmts;
27383
27882
  }
27384
- function buildBranchFunction(stmts) {
27883
+ function buildBranchFunction(stmts, options) {
27385
27884
  const block = buildReturnBlock(stmts);
27386
27885
  if (!block) return null;
27886
+ if (options?.disallowRenderHooks && containsRenderOnlyHooks(block)) return null;
27387
27887
  return t4.arrowFunctionExpression([], t4.blockStatement(block));
27388
27888
  }
27389
- function buildConditionalExpr(ifStmt, rest) {
27390
- const consequentStmts = toStatements(ifStmt.consequent);
27391
- if (!endsWithReturn(consequentStmts)) return null;
27392
- let alternateStmts = null;
27393
- if (ifStmt.alternate) {
27394
- if (rest.length > 0) return null;
27395
- alternateStmts = toStatements(ifStmt.alternate);
27396
- if (!endsWithReturn(alternateStmts)) return null;
27397
- } else {
27398
- if (rest.length === 0) return null;
27399
- alternateStmts = rest;
27400
- if (!buildReturnBlock(alternateStmts)) return null;
27889
+ function containsRenderOnlyHooks(nodes) {
27890
+ let found = false;
27891
+ const visit = (node) => {
27892
+ if (!node || found) return;
27893
+ if (t4.isCallExpression(node) || t4.isOptionalCallExpression(node)) {
27894
+ const callee = node.callee;
27895
+ if (t4.isIdentifier(callee) && callee.name.startsWith("__fictUse")) {
27896
+ if (callee.name !== "__fictUseEffect") {
27897
+ found = true;
27898
+ return;
27899
+ }
27900
+ }
27901
+ }
27902
+ const keys = t4.VISITOR_KEYS;
27903
+ const visitorKeys = keys?.[node.type] ?? [];
27904
+ for (const key of visitorKeys) {
27905
+ const value = node[key];
27906
+ if (Array.isArray(value)) {
27907
+ for (const child of value) {
27908
+ if (child && typeof child === "object" && "type" in child) {
27909
+ visit(child);
27910
+ }
27911
+ if (found) return;
27912
+ }
27913
+ } else if (value && typeof value === "object" && "type" in value) {
27914
+ visit(value);
27915
+ }
27916
+ if (found) return;
27917
+ }
27918
+ };
27919
+ for (const node of nodes) {
27920
+ visit(node);
27921
+ if (found) break;
27401
27922
  }
27402
- const trueFn = buildBranchFunction(consequentStmts);
27403
- const falseFn = alternateStmts ? buildBranchFunction(alternateStmts) : null;
27404
- if (!trueFn || !falseFn) return null;
27923
+ return found;
27924
+ }
27925
+ function buildConditionalBindingExpr(testExpr, trueFn, falseFn) {
27405
27926
  ctx.helpersUsed.add("conditional");
27406
27927
  ctx.helpersUsed.add("createElement");
27407
27928
  ctx.helpersUsed.add("onDestroy");
27408
27929
  const bindingId = genTemp(ctx, "cond");
27409
27930
  const args = [
27410
- t4.arrowFunctionExpression([], ifStmt.test),
27931
+ t4.arrowFunctionExpression([], testExpr),
27411
27932
  trueFn,
27412
27933
  t4.identifier(RUNTIME_ALIASES.createElement),
27413
27934
  falseFn
@@ -27429,15 +27950,210 @@ function transformControlFlowReturns(statements, ctx) {
27429
27950
  []
27430
27951
  );
27431
27952
  }
27432
- for (let i = 0; i < statements.length; i++) {
27433
- const stmt = statements[i];
27953
+ function buildConditionalExpr(ifStmt, rest) {
27954
+ const consequentStmts = toStatements(ifStmt.consequent);
27955
+ if (!endsWithReturn(consequentStmts)) return null;
27956
+ let alternateStmts = null;
27957
+ if (ifStmt.alternate) {
27958
+ if (rest.length > 0) return null;
27959
+ alternateStmts = toStatements(ifStmt.alternate);
27960
+ if (!endsWithReturn(alternateStmts)) return null;
27961
+ } else {
27962
+ if (rest.length === 0) return null;
27963
+ alternateStmts = rest;
27964
+ if (!buildReturnBlock(alternateStmts)) return null;
27965
+ }
27966
+ const trueFn = buildBranchFunction(consequentStmts);
27967
+ const falseFn = alternateStmts ? buildBranchFunction(alternateStmts) : null;
27968
+ if (!trueFn || !falseFn) return null;
27969
+ return buildConditionalBindingExpr(ifStmt.test, trueFn, falseFn);
27970
+ }
27971
+ function isSupportedSwitchDiscriminant(_expr) {
27972
+ return true;
27973
+ }
27974
+ function buildSwitchConditionalExpr(switchStmt, rest) {
27975
+ const discriminant = switchStmt.discriminant;
27976
+ if (!isSupportedSwitchDiscriminant(discriminant)) return null;
27977
+ const trailingStatements = rest.length > 0 ? buildReturnBlock([...rest]) : [];
27978
+ if (rest.length > 0 && !trailingStatements) return null;
27979
+ const caseEntryCache = /* @__PURE__ */ new Map();
27980
+ const resolveCaseEntry = (startIndex) => {
27981
+ if (caseEntryCache.has(startIndex)) {
27982
+ return caseEntryCache.get(startIndex) ?? null;
27983
+ }
27984
+ const entry = [];
27985
+ for (let i = startIndex; i < switchStmt.cases.length; i++) {
27986
+ const currentCase = switchStmt.cases[i];
27987
+ const consequent = currentCase.consequent;
27988
+ for (let stmtIndex = 0; stmtIndex < consequent.length; stmtIndex++) {
27989
+ const stmt = consequent[stmtIndex];
27990
+ if (t4.isBreakStatement(stmt)) {
27991
+ if (stmt.label || stmtIndex !== consequent.length - 1) {
27992
+ caseEntryCache.set(startIndex, null);
27993
+ return null;
27994
+ }
27995
+ if (!trailingStatements || trailingStatements.length === 0) {
27996
+ caseEntryCache.set(startIndex, null);
27997
+ return null;
27998
+ }
27999
+ const withTrailing2 = [...entry, ...trailingStatements];
28000
+ caseEntryCache.set(startIndex, withTrailing2);
28001
+ return withTrailing2;
28002
+ }
28003
+ if (t4.isContinueStatement(stmt)) {
28004
+ caseEntryCache.set(startIndex, null);
28005
+ return null;
28006
+ }
28007
+ entry.push(stmt);
28008
+ }
28009
+ if (endsWithReturn(entry)) {
28010
+ caseEntryCache.set(startIndex, entry);
28011
+ return entry;
28012
+ }
28013
+ }
28014
+ if (!trailingStatements || trailingStatements.length === 0) {
28015
+ caseEntryCache.set(startIndex, null);
28016
+ return null;
28017
+ }
28018
+ const withTrailing = [...entry, ...trailingStatements];
28019
+ caseEntryCache.set(startIndex, withTrailing);
28020
+ return withTrailing;
28021
+ };
28022
+ const branches = [];
28023
+ let defaultStatements = null;
28024
+ for (let i = 0; i < switchStmt.cases.length; i++) {
28025
+ const caseNode = switchStmt.cases[i];
28026
+ const statements2 = resolveCaseEntry(i);
28027
+ if (!statements2) return null;
28028
+ if (caseNode.test) {
28029
+ branches.push({
28030
+ tests: [caseNode.test],
28031
+ statements: statements2
28032
+ });
28033
+ } else {
28034
+ defaultStatements = statements2;
28035
+ }
28036
+ }
28037
+ if (branches.length === 0 && !defaultStatements) return null;
28038
+ const fallbackStatements = defaultStatements ?? trailingStatements;
28039
+ if (!fallbackStatements || fallbackStatements.length === 0) return null;
28040
+ const fallbackFn = buildBranchFunction(fallbackStatements, { disallowRenderHooks: true });
28041
+ if (!fallbackFn) return null;
28042
+ ctx.helpersUsed.add("memo");
28043
+ const discriminantAccessor = genTemp(ctx, "switchDisc");
28044
+ const discriminantMemoDecl = t4.variableDeclaration("const", [
28045
+ t4.variableDeclarator(
28046
+ discriminantAccessor,
28047
+ t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), [
28048
+ t4.arrowFunctionExpression(
28049
+ [],
28050
+ t4.cloneNode(discriminant, true)
28051
+ )
28052
+ ])
28053
+ )
28054
+ ]);
28055
+ let currentExpr = t4.callExpression(
28056
+ t4.arrowFunctionExpression(
28057
+ [],
28058
+ t4.blockStatement(fallbackFn.body.body)
28059
+ ),
28060
+ []
28061
+ );
28062
+ for (let i = branches.length - 1; i >= 0; i--) {
28063
+ const branch = branches[i];
28064
+ const trueFn = buildBranchFunction(branch.statements, { disallowRenderHooks: true });
28065
+ if (!trueFn) return null;
28066
+ const falseFn = t4.arrowFunctionExpression(
28067
+ [],
28068
+ t4.blockStatement([t4.returnStatement(currentExpr)])
28069
+ );
28070
+ const comparisons = branch.tests.map(
28071
+ (test) => t4.binaryExpression(
28072
+ "===",
28073
+ t4.callExpression(t4.cloneNode(discriminantAccessor), []),
28074
+ t4.cloneNode(test, true)
28075
+ )
28076
+ );
28077
+ if (comparisons.length === 0) return null;
28078
+ const testExpr = comparisons.slice(1).reduce(
28079
+ (acc, expr) => t4.logicalExpression("||", acc, expr),
28080
+ comparisons[0]
28081
+ );
28082
+ currentExpr = buildConditionalBindingExpr(testExpr, trueFn, falseFn);
28083
+ }
28084
+ return t4.callExpression(
28085
+ t4.arrowFunctionExpression(
28086
+ [],
28087
+ t4.blockStatement([discriminantMemoDecl, t4.returnStatement(currentExpr)])
28088
+ ),
28089
+ []
28090
+ );
28091
+ }
28092
+ let nestedChanged = false;
28093
+ const rewrittenStatements = statements.map((stmt) => {
28094
+ if (!t4.isTryStatement(stmt)) return stmt;
28095
+ const transformedTryBlock = transformControlFlowReturns(stmt.block.body, ctx);
28096
+ const nextTryBlockBody = transformedTryBlock ?? stmt.block.body;
28097
+ let nextHandler = stmt.handler;
28098
+ if (stmt.handler) {
28099
+ const transformedCatchBlock = transformControlFlowReturns(stmt.handler.body.body, ctx);
28100
+ if (transformedCatchBlock) {
28101
+ nextHandler = t4.catchClause(
28102
+ stmt.handler.param ? t4.cloneNode(stmt.handler.param, true) : null,
28103
+ t4.blockStatement(transformedCatchBlock)
28104
+ );
28105
+ }
28106
+ }
28107
+ let nextFinalizer = stmt.finalizer;
28108
+ if (stmt.finalizer) {
28109
+ const transformedFinalizer = transformControlFlowReturns(stmt.finalizer.body, ctx);
28110
+ if (transformedFinalizer) {
28111
+ nextFinalizer = t4.blockStatement(transformedFinalizer);
28112
+ }
28113
+ }
28114
+ if (!transformedTryBlock && nextHandler === stmt.handler && nextFinalizer === stmt.finalizer) {
28115
+ return stmt;
28116
+ }
28117
+ nestedChanged = true;
28118
+ return t4.tryStatement(
28119
+ t4.blockStatement(nextTryBlockBody),
28120
+ nextHandler,
28121
+ nextFinalizer ? t4.cloneNode(nextFinalizer, true) : null
28122
+ );
28123
+ });
28124
+ for (let i = 0; i < rewrittenStatements.length; i++) {
28125
+ const stmt = rewrittenStatements[i];
27434
28126
  if (!t4.isIfStatement(stmt)) continue;
27435
- const conditionalExpr = buildConditionalExpr(stmt, statements.slice(i + 1));
27436
- if (!conditionalExpr) continue;
27437
- const prefix = statements.slice(0, i);
28127
+ const rest = rewrittenStatements.slice(i + 1);
28128
+ const conditionalExpr = buildConditionalExpr(stmt, rest);
28129
+ if (!conditionalExpr) {
28130
+ const hasReturn = containsReturnStatement([stmt, ...rest]);
28131
+ const hasReactiveReads = containsReactiveAccessorRead([stmt, ...rest]);
28132
+ if (hasReturn && hasReactiveReads) {
28133
+ emitControlFlowFallbackWarning(stmt, "if");
28134
+ }
28135
+ continue;
28136
+ }
28137
+ const prefix = rewrittenStatements.slice(0, i);
27438
28138
  return [...prefix, t4.returnStatement(conditionalExpr)];
27439
28139
  }
27440
- return null;
28140
+ for (let i = 0; i < rewrittenStatements.length; i++) {
28141
+ const stmt = rewrittenStatements[i];
28142
+ if (!t4.isSwitchStatement(stmt)) continue;
28143
+ const rest = rewrittenStatements.slice(i + 1);
28144
+ const conditionalExpr = buildSwitchConditionalExpr(stmt, rest);
28145
+ if (!conditionalExpr) {
28146
+ const hasReturn = containsReturnStatement([stmt, ...rest]);
28147
+ const hasReactiveReads = containsReactiveAccessorRead([stmt, ...rest]);
28148
+ if (hasReturn && hasReactiveReads) {
28149
+ emitControlFlowFallbackWarning(stmt, "switch");
28150
+ }
28151
+ continue;
28152
+ }
28153
+ const prefix = rewrittenStatements.slice(0, i);
28154
+ return [...prefix, t4.returnStatement(conditionalExpr)];
28155
+ }
28156
+ return nestedChanged ? rewrittenStatements : null;
27441
28157
  }
27442
28158
  function lowerFunctionWithRegions(fn, ctx, options) {
27443
28159
  const { t: t4 } = ctx;
@@ -28299,6 +29015,7 @@ function collectRootNodesFromExpression(expr, onBinding, onEffect) {
28299
29015
  if (prop.kind === "SpreadElement") {
28300
29016
  visit(prop.argument, shadowed);
28301
29017
  } else {
29018
+ if (prop.computed) visit(prop.key, shadowed);
28302
29019
  visit(prop.value, shadowed);
28303
29020
  }
28304
29021
  });
@@ -28443,6 +29160,14 @@ function collectDependenciesFromExpression(expr, deps, includeFunctionBodies, sh
28443
29160
  shadowed
28444
29161
  );
28445
29162
  } else {
29163
+ if (prop.computed) {
29164
+ collectDependenciesFromExpression(
29165
+ prop.key,
29166
+ deps,
29167
+ includeFunctionBodies,
29168
+ shadowed
29169
+ );
29170
+ }
28446
29171
  collectDependenciesFromExpression(
28447
29172
  prop.value,
28448
29173
  deps,
@@ -29405,7 +30130,7 @@ function expressionDependsOnReactive(expr, ctx) {
29405
30130
  if (prop.kind === "SpreadElement") {
29406
30131
  return expressionDependsOnReactive(prop.argument, ctx);
29407
30132
  }
29408
- return expressionDependsOnReactive(prop.value, ctx);
30133
+ return prop.computed && expressionDependsOnReactive(prop.key, ctx) || expressionDependsOnReactive(prop.value, ctx);
29409
30134
  });
29410
30135
  case "TemplateLiteral":
29411
30136
  return expr.expressions.some((e) => expressionDependsOnReactive(e, ctx));
@@ -29505,6 +30230,7 @@ function collectWriteTargets(expr) {
29505
30230
  if (prop.kind === "SpreadElement") {
29506
30231
  visit(prop.argument);
29507
30232
  } else {
30233
+ if (prop.computed) visit(prop.key);
29508
30234
  visit(prop.value);
29509
30235
  }
29510
30236
  });
@@ -29578,6 +30304,7 @@ function collectMemberCallTargets(expr) {
29578
30304
  if (prop.kind === "SpreadElement") {
29579
30305
  visit(prop.argument);
29580
30306
  } else {
30307
+ if (prop.computed) visit(prop.key);
29581
30308
  visit(prop.value);
29582
30309
  }
29583
30310
  });
@@ -29705,7 +30432,7 @@ function expressionContainsImpureMarkers(expr) {
29705
30432
  if (prop.kind === "SpreadElement") {
29706
30433
  return expressionContainsImpureMarkers(prop.argument);
29707
30434
  }
29708
- return expressionContainsImpureMarkers(prop.value);
30435
+ return prop.computed && expressionContainsImpureMarkers(prop.key) || expressionContainsImpureMarkers(prop.value);
29709
30436
  });
29710
30437
  case "TemplateLiteral":
29711
30438
  return expr.expressions.some((e) => expressionContainsImpureMarkers(e));
@@ -29910,6 +30637,7 @@ function extractConstObjectFields(expr, constants) {
29910
30637
  const fields = /* @__PURE__ */ new Map();
29911
30638
  for (const prop of expr.properties) {
29912
30639
  if (prop.kind === "SpreadElement") return null;
30640
+ if (prop.computed) return null;
29913
30641
  const key = getObjectLiteralKey(prop.key);
29914
30642
  if (!key) return null;
29915
30643
  const value = evaluateLiteral(prop.value, constants);
@@ -30037,6 +30765,7 @@ function replaceConstMemberExpressions(expr, constObjects, constArrays) {
30037
30765
  }
30038
30766
  return {
30039
30767
  ...prop,
30768
+ key: prop.computed ? replaceConstMemberExpressions(prop.key, constObjects, constArrays) : prop.key,
30040
30769
  value: replaceConstMemberExpressions(
30041
30770
  prop.value,
30042
30771
  constObjects,
@@ -30287,6 +31016,7 @@ function simplifyChildren(expr, constants, options) {
30287
31016
  if (prop.kind === "Property") {
30288
31017
  return {
30289
31018
  ...prop,
31019
+ key: prop.computed ? simplifyAlgebraically(prop.key, constants, options) : prop.key,
30290
31020
  value: simplifyAlgebraically(prop.value, constants, options)
30291
31021
  };
30292
31022
  }
@@ -30405,8 +31135,8 @@ function replaceIdentifiersWithConstants(expr, constants, context = {}) {
30405
31135
  }
30406
31136
  return {
30407
31137
  ...prop,
30408
- value: replaceIdentifiersWithConstants(prop.value, constants),
30409
- key: prop.key
31138
+ key: prop.computed ? replaceIdentifiersWithConstants(prop.key, constants) : prop.key,
31139
+ value: replaceIdentifiersWithConstants(prop.value, constants)
30410
31140
  };
30411
31141
  })
30412
31142
  };
@@ -30958,6 +31688,7 @@ function walkExpression(expr, add, ctx) {
30958
31688
  if (prop.kind === "SpreadElement") {
30959
31689
  walkExpression(prop.argument, add, ctx);
30960
31690
  } else {
31691
+ if (prop.computed) walkExpression(prop.key, add, ctx);
30961
31692
  walkExpression(prop.value, add, ctx);
30962
31693
  }
30963
31694
  });
@@ -31097,7 +31828,9 @@ function hashExpression(expr) {
31097
31828
  return `arr:${expr.elements.map((el) => el ? hashExpression(el) : "null").join(",")}`;
31098
31829
  case "ObjectExpression":
31099
31830
  return `obj:${expr.properties.map(
31100
- (p) => p.kind === "SpreadElement" ? `...${hashExpression(p.argument)}` : `${hashExpression(p.key)}:${hashExpression(p.value)}`
31831
+ (p) => p.kind === "SpreadElement" ? `...${hashExpression(p.argument)}` : `${p.computed ? "[]" : "."}${hashExpression(p.key)}:${hashExpression(
31832
+ p.value
31833
+ )}`
31101
31834
  ).join(",")}`;
31102
31835
  case "TemplateLiteral":
31103
31836
  return `tpl:${expr.quasis.join("|")}:${expr.expressions.map((e) => hashExpression(e)).join("|")}`;
@@ -31144,7 +31877,7 @@ function isPureExpression(expr, ctx) {
31144
31877
  case "ObjectExpression":
31145
31878
  return expr.properties.every((prop) => {
31146
31879
  if (prop.kind === "SpreadElement") return isPureExpression(prop.argument, ctx);
31147
- return isPureExpression(prop.value, ctx);
31880
+ return (!prop.computed || isPureExpression(prop.key, ctx)) && isPureExpression(prop.value, ctx);
31148
31881
  });
31149
31882
  case "MemberExpression":
31150
31883
  case "OptionalMemberExpression":
@@ -31356,7 +32089,7 @@ function replaceIdentifier(expr, target, replacement, inFunctionBody) {
31356
32089
  }
31357
32090
  return {
31358
32091
  ...prop,
31359
- key: prop.key,
32092
+ key: prop.computed ? replaceIdentifier(prop.key, target, replacement, inFunctionBody) : prop.key,
31360
32093
  value: replaceIdentifier(prop.value, target, replacement, inFunctionBody)
31361
32094
  };
31362
32095
  })
@@ -31691,7 +32424,9 @@ function shouldSuppressWarning(suppressions, code, line) {
31691
32424
  return entry.codes.has(code);
31692
32425
  });
31693
32426
  }
32427
+ var DEFAULT_ERROR_WARNING_CODES = /* @__PURE__ */ new Set(["FICT-R004"]);
31694
32428
  function hasErrorEscalation(options) {
32429
+ if (DEFAULT_ERROR_WARNING_CODES.size > 0) return true;
31695
32430
  if (options.warningsAsErrors === true) return true;
31696
32431
  if (Array.isArray(options.warningsAsErrors) && options.warningsAsErrors.length > 0) return true;
31697
32432
  if (options.warningLevels) {
@@ -31706,6 +32441,7 @@ function resolveWarningLevel(code, options) {
31706
32441
  if (Array.isArray(options.warningsAsErrors) && options.warningsAsErrors.includes(code)) {
31707
32442
  return "error";
31708
32443
  }
32444
+ if (DEFAULT_ERROR_WARNING_CODES.has(code)) return "error";
31709
32445
  return "warn";
31710
32446
  }
31711
32447
  function formatWarningAsError(warning) {
@@ -31819,18 +32555,66 @@ function isDynamicPropertyAccess(node, t4) {
31819
32555
  if (!node.computed) return false;
31820
32556
  return !(t4.isStringLiteral(node.property) || t4.isNumericLiteral(node.property));
31821
32557
  }
31822
- function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4) {
31823
- const isStateRoot = (expr) => {
32558
+ function runWarningPass(programPath, stateBindingIds, stateRootBindingIds, reactiveBindingIds, effectMacroNames, warn, fileName, t4) {
32559
+ const hasTrackedBinding = (path2, name, tracked) => {
32560
+ const binding = path2.scope.getBinding(name);
32561
+ return !!(binding && tracked.has(binding.identifier));
32562
+ };
32563
+ const isStateRoot = (expr, path2) => {
32564
+ const root = getRootIdentifier(expr, t4);
32565
+ if (!root) return false;
32566
+ return hasTrackedBinding(path2, root.name, stateRootBindingIds);
32567
+ };
32568
+ const isReactiveRoot = (expr, path2) => {
31824
32569
  const root = getRootIdentifier(expr, t4);
31825
- return !!(root && stateVars.has(root.name));
32570
+ if (!root) return false;
32571
+ return hasTrackedBinding(path2, root.name, reactiveBindingIds);
32572
+ };
32573
+ const argumentHasReactive = (argPath) => {
32574
+ if (argPath.isSpreadElement()) {
32575
+ const inner = argPath.get("argument");
32576
+ return argumentHasReactive(inner);
32577
+ }
32578
+ if (argPath.isJSXElement() || argPath.isJSXFragment()) return false;
32579
+ if (argPath.isIdentifier()) {
32580
+ return hasTrackedBinding(argPath, argPath.node.name, reactiveBindingIds);
32581
+ }
32582
+ if (!argPath.isExpression()) return false;
32583
+ let found = false;
32584
+ argPath.traverse({
32585
+ Function(path2) {
32586
+ path2.skip();
32587
+ },
32588
+ JSXElement(path2) {
32589
+ path2.skip();
32590
+ },
32591
+ JSXFragment(path2) {
32592
+ path2.skip();
32593
+ },
32594
+ Identifier(idPath) {
32595
+ if (idPath.parentPath.isMemberExpression({ property: idPath.node }) && !idPath.parent.computed) {
32596
+ return;
32597
+ }
32598
+ if (idPath.parentPath.isObjectProperty({ key: idPath.node }) && !idPath.parent.computed && !idPath.parent.shorthand) {
32599
+ return;
32600
+ }
32601
+ const binding = idPath.scope.getBinding(idPath.node.name);
32602
+ if (binding && reactiveBindingIds.has(binding.identifier)) {
32603
+ found = true;
32604
+ idPath.stop();
32605
+ }
32606
+ }
32607
+ });
32608
+ return found;
31826
32609
  };
31827
- const reactiveNames = /* @__PURE__ */ new Set([...stateVars, ...derivedVars]);
31828
32610
  programPath.traverse({
31829
32611
  AssignmentExpression(path2) {
31830
32612
  const { left } = path2.node;
31831
32613
  if (t4.isIdentifier(left)) return;
31832
32614
  if (t4.isMemberExpression(left) || t4.isOptionalMemberExpression(left)) {
31833
- if (isStateRoot(left.object)) {
32615
+ const stateRoot = isStateRoot(left.object, path2);
32616
+ const reactiveRoot = isReactiveRoot(left.object, path2);
32617
+ if (stateRoot || reactiveRoot) {
31834
32618
  emitWarning(
31835
32619
  path2.node,
31836
32620
  "FICT-M",
@@ -31847,13 +32631,16 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31847
32631
  fileName
31848
32632
  );
31849
32633
  }
32634
+ return;
31850
32635
  }
31851
32636
  }
31852
32637
  },
31853
32638
  UpdateExpression(path2) {
31854
32639
  const arg = path2.node.argument;
31855
32640
  if (t4.isMemberExpression(arg) || t4.isOptionalMemberExpression(arg)) {
31856
- if (isStateRoot(arg.object)) {
32641
+ const stateRoot = isStateRoot(arg.object, path2);
32642
+ const reactiveRoot = isReactiveRoot(arg.object, path2);
32643
+ if (stateRoot || reactiveRoot) {
31857
32644
  emitWarning(
31858
32645
  path2.node,
31859
32646
  "FICT-M",
@@ -31870,6 +32657,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31870
32657
  fileName
31871
32658
  );
31872
32659
  }
32660
+ return;
31873
32661
  }
31874
32662
  }
31875
32663
  },
@@ -31877,7 +32665,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31877
32665
  if (!path2.node.computed) return;
31878
32666
  if (path2.parentPath.isAssignmentExpression({ left: path2.node })) return;
31879
32667
  if (path2.parentPath.isUpdateExpression({ argument: path2.node })) return;
31880
- if (isDynamicPropertyAccess(path2.node, t4) && isStateRoot(path2.node.object)) {
32668
+ if (isDynamicPropertyAccess(path2.node, t4) && isReactiveRoot(path2.node.object, path2)) {
31881
32669
  emitWarning(
31882
32670
  path2.node,
31883
32671
  "FICT-H",
@@ -31897,9 +32685,9 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31897
32685
  },
31898
32686
  Identifier(idPath) {
31899
32687
  const name = idPath.node.name;
31900
- if (!reactiveNames.has(name)) return;
31901
32688
  const binding = idPath.scope.getBinding(name);
31902
32689
  if (!binding) return;
32690
+ if (!reactiveBindingIds.has(binding.identifier)) return;
31903
32691
  if (binding.scope === idPath.scope || binding.scope === path2.scope) return;
31904
32692
  captured.add(name);
31905
32693
  }
@@ -31917,7 +32705,8 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31917
32705
  }
31918
32706
  },
31919
32707
  CallExpression(path2) {
31920
- if (t4.isIdentifier(path2.node.callee, { name: "$effect" })) {
32708
+ const isEffect = isEffectCall(path2.node, t4, effectMacroNames);
32709
+ if (isEffect) {
31921
32710
  const argPath = path2.get("arguments.0");
31922
32711
  if (argPath?.isFunctionExpression() || argPath?.isArrowFunctionExpression()) {
31923
32712
  let hasReactiveDependency = false;
@@ -31931,7 +32720,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31931
32720
  }
31932
32721
  const binding = idPath.scope.getBinding(idPath.node.name);
31933
32722
  if (binding && binding.scope === argPath.scope) return;
31934
- if (stateVars.has(idPath.node.name) || derivedVars.has(idPath.node.name)) {
32723
+ if (binding && reactiveBindingIds.has(binding.identifier)) {
31935
32724
  hasReactiveDependency = true;
31936
32725
  idPath.stop();
31937
32726
  }
@@ -31961,13 +32750,16 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31961
32750
  }
31962
32751
  const isSafe = calleeName && SAFE_FUNCTIONS.has(calleeName);
31963
32752
  if (isSafe) return;
31964
- for (const arg of path2.node.arguments) {
31965
- if (!t4.isExpression(arg)) continue;
31966
- if (isStateRoot(arg)) {
32753
+ const argPaths = path2.get("arguments");
32754
+ for (const argPath of argPaths) {
32755
+ if (argPath.isIdentifier() && hasTrackedBinding(argPath, argPath.node.name, stateBindingIds)) {
32756
+ continue;
32757
+ }
32758
+ if (argumentHasReactive(argPath)) {
31967
32759
  emitWarning(
31968
- arg,
31969
- "FICT-H",
31970
- "State value passed to unknown function (black box); dependency tracking may be imprecise",
32760
+ argPath.node,
32761
+ "FICT-R002",
32762
+ "Reactive value escapes scope when passed to an unknown function; dependency tracking may be imprecise",
31971
32763
  warn,
31972
32764
  fileName
31973
32765
  );
@@ -31979,7 +32771,7 @@ function runWarningPass(programPath, stateVars, derivedVars, warn, fileName, t4)
31979
32771
  if (!path2.node.computed) return;
31980
32772
  if (path2.parentPath.isAssignmentExpression({ left: path2.node })) return;
31981
32773
  if (path2.parentPath.isUpdateExpression({ argument: path2.node })) return;
31982
- if (isDynamicPropertyAccess(path2.node, t4) && isStateRoot(path2.node.object)) {
32774
+ if (isDynamicPropertyAccess(path2.node, t4) && isReactiveRoot(path2.node.object, path2)) {
31983
32775
  emitWarning(
31984
32776
  path2.node,
31985
32777
  "FICT-H",
@@ -32108,6 +32900,17 @@ function createHIREntrypointVisitor(t4, options) {
32108
32900
  const name = getFunctionName(fnPath);
32109
32901
  return name && isComponentName2(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t4);
32110
32902
  };
32903
+ const isBoundDefinition = (fnPath) => fnPath.isFunctionDeclaration() || fnPath.parentPath.isVariableDeclarator() && fnPath.parentPath.node.init === fnPath.node;
32904
+ const isExportDefaultDefinition = (fnPath) => fnPath.parentPath?.isExportDefaultDeclaration() && fnPath.parentPath.node.declaration === fnPath.node;
32905
+ const isNamedComponentOrHookDefinition = (fnPath) => {
32906
+ if (!isBoundDefinition(fnPath)) return false;
32907
+ const name = getFunctionName(fnPath);
32908
+ return !!name && (isComponentName2(name) || isHookName2(name));
32909
+ };
32910
+ const isComponentDefinitionForProps = (fnPath) => {
32911
+ if (!isComponentDefinition(fnPath)) return false;
32912
+ return isBoundDefinition(fnPath) || isExportDefaultDefinition(fnPath);
32913
+ };
32111
32914
  const memoHasSideEffects = (fn) => {
32112
32915
  const pureCalls = new Set(
32113
32916
  Array.from(SAFE_FUNCTIONS).filter(
@@ -32228,6 +33031,7 @@ function createHIREntrypointVisitor(t4, options) {
32228
33031
  const stateMacroNames = /* @__PURE__ */ new Set(["$state"]);
32229
33032
  const effectMacroNames = /* @__PURE__ */ new Set(["$effect"]);
32230
33033
  const memoMacroNames = /* @__PURE__ */ new Set(["$memo", "createMemo"]);
33034
+ const importedReactiveBindingIds = /* @__PURE__ */ new Set();
32231
33035
  path2.traverse({
32232
33036
  ImportDeclaration(importPath) {
32233
33037
  if (importPath.node.source.value !== "fict" && importPath.node.source.value !== "fict/slim")
@@ -32248,6 +33052,44 @@ function createHIREntrypointVisitor(t4, options) {
32248
33052
  }
32249
33053
  }
32250
33054
  });
33055
+ path2.traverse({
33056
+ ImportDeclaration(importPath) {
33057
+ const meta = resolveModuleMetadata(
33058
+ importPath.node.source.value,
33059
+ fileName,
33060
+ optionsWithWarnings
33061
+ );
33062
+ if (!meta) return;
33063
+ const hasReactiveExports = Object.keys(meta.exports).length > 0;
33064
+ for (const spec of importPath.node.specifiers) {
33065
+ if (t4.isImportSpecifier(spec)) {
33066
+ const importedName = t4.isIdentifier(spec.imported) ? spec.imported.name : String(spec.imported.value);
33067
+ if (meta.exports[importedName]) {
33068
+ const binding = importPath.scope.getBinding(spec.local.name);
33069
+ if (binding) {
33070
+ importedReactiveBindingIds.add(binding.identifier);
33071
+ }
33072
+ }
33073
+ continue;
33074
+ }
33075
+ if (t4.isImportDefaultSpecifier(spec)) {
33076
+ if (meta.exports.default) {
33077
+ const binding = importPath.scope.getBinding(spec.local.name);
33078
+ if (binding) {
33079
+ importedReactiveBindingIds.add(binding.identifier);
33080
+ }
33081
+ }
33082
+ continue;
33083
+ }
33084
+ if (t4.isImportNamespaceSpecifier(spec) && hasReactiveExports) {
33085
+ const binding = importPath.scope.getBinding(spec.local.name);
33086
+ if (binding) {
33087
+ importedReactiveBindingIds.add(binding.identifier);
33088
+ }
33089
+ }
33090
+ }
33091
+ }
33092
+ });
32251
33093
  path2.traverse({
32252
33094
  JSXExpressionContainer(exprPath) {
32253
33095
  const expr = exprPath.node.expression;
@@ -32258,10 +33100,10 @@ function createHIREntrypointVisitor(t4, options) {
32258
33100
  const cb = expr.arguments[0];
32259
33101
  if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
32260
33102
  const getReturnedJsx = (fn) => {
32261
- if (t4.isJSXElement(fn.body)) return fn.body;
33103
+ if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
32262
33104
  if (t4.isBlockStatement(fn.body)) {
32263
33105
  const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
32264
- if (ret && t4.isReturnStatement(ret) && ret.argument && t4.isJSXElement(ret.argument)) {
33106
+ if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
32265
33107
  return ret.argument;
32266
33108
  }
32267
33109
  }
@@ -32269,6 +33111,16 @@ function createHIREntrypointVisitor(t4, options) {
32269
33111
  };
32270
33112
  const jsx = getReturnedJsx(cb);
32271
33113
  if (!jsx) return;
33114
+ if (t4.isJSXFragment(jsx)) {
33115
+ warn({
33116
+ code: "FICT-J002",
33117
+ message: "Missing key prop in list rendering.",
33118
+ fileName,
33119
+ line: expr.loc?.start.line ?? 0,
33120
+ column: expr.loc ? expr.loc.start.column + 1 : 0
33121
+ });
33122
+ return;
33123
+ }
32272
33124
  let hasKey = false;
32273
33125
  let hasUnknownSpread = false;
32274
33126
  for (const attr of jsx.openingElement.attributes) {
@@ -32291,8 +33143,48 @@ function createHIREntrypointVisitor(t4, options) {
32291
33143
  }
32292
33144
  });
32293
33145
  const stateVars = /* @__PURE__ */ new Set();
32294
- const derivedVars = /* @__PURE__ */ new Set();
33146
+ const stateBindingIds = /* @__PURE__ */ new Set();
33147
+ const derivedBindingIds = /* @__PURE__ */ new Set();
32295
33148
  const destructuredAliases = /* @__PURE__ */ new Set();
33149
+ const aliasBindingIds = /* @__PURE__ */ new Set();
33150
+ const stateAliasBindingIds = /* @__PURE__ */ new Set();
33151
+ const propsBindingIds = /* @__PURE__ */ new Set();
33152
+ const hasTrackedBinding = (path3, name, tracked) => {
33153
+ const binding = path3.scope.getBinding(name);
33154
+ return !!(binding && tracked.has(binding.identifier));
33155
+ };
33156
+ const trackBindingByName = (path3, name, tracked) => {
33157
+ const binding = path3.scope.getBinding(name);
33158
+ if (binding) tracked.add(binding.identifier);
33159
+ };
33160
+ const hasReactiveAliasSourceBinding = (path3, name) => hasTrackedBinding(path3, name, stateBindingIds) || hasTrackedBinding(path3, name, derivedBindingIds) || hasTrackedBinding(path3, name, aliasBindingIds) || hasTrackedBinding(path3, name, destructuredAliases) || hasTrackedBinding(path3, name, importedReactiveBindingIds);
33161
+ const hasStateRootBinding = (path3, name) => hasTrackedBinding(path3, name, stateBindingIds) || hasTrackedBinding(path3, name, stateAliasBindingIds);
33162
+ const unwrapIdentifier = (node) => {
33163
+ if (!node) return null;
33164
+ let current = node;
33165
+ while (true) {
33166
+ if (t4.isTSAsExpression(current)) {
33167
+ current = current.expression;
33168
+ continue;
33169
+ }
33170
+ if (t4.isTSNonNullExpression(current)) {
33171
+ current = current.expression;
33172
+ continue;
33173
+ }
33174
+ if (t4.isTypeCastExpression?.(current)) {
33175
+ current = current.expression;
33176
+ continue;
33177
+ }
33178
+ break;
33179
+ }
33180
+ return t4.isIdentifier(current) ? current : null;
33181
+ };
33182
+ const isStateRootIdentifier = (exprPath) => {
33183
+ if (!exprPath) return null;
33184
+ const id = unwrapIdentifier(exprPath.node);
33185
+ if (!id) return null;
33186
+ return hasStateRootBinding(exprPath, id.name) ? id : null;
33187
+ };
32296
33188
  path2.traverse({
32297
33189
  VariableDeclarator(varPath) {
32298
33190
  const init = varPath.node.init;
@@ -32328,6 +33220,7 @@ For module-level shared state, use one of these alternatives:
32328
33220
  );
32329
33221
  }
32330
33222
  stateVars.add(varPath.node.id.name);
33223
+ trackBindingByName(varPath, varPath.node.id.name, stateBindingIds);
32331
33224
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
32332
33225
  throw varPath.buildCodeFrameError(
32333
33226
  `$state() cannot be declared inside loops or conditionals.
@@ -32350,25 +33243,36 @@ or extract the nested logic into a custom hook (useXxx).`
32350
33243
  let dependsOnState = false;
32351
33244
  varPath.get("init").traverse({
32352
33245
  Identifier(idPath) {
32353
- if (stateVars.has(idPath.node.name)) {
33246
+ if (hasTrackedBinding(idPath, idPath.node.name, stateBindingIds)) {
32354
33247
  dependsOnState = true;
32355
33248
  idPath.stop();
32356
33249
  }
32357
33250
  }
32358
33251
  });
32359
33252
  if (dependsOnState) {
32360
- derivedVars.add(varPath.node.id.name);
33253
+ trackBindingByName(varPath, varPath.node.id.name, derivedBindingIds);
32361
33254
  }
32362
33255
  }
32363
- } else if ((t4.isObjectPattern(varPath.node.id) || t4.isArrayPattern(varPath.node.id)) && t4.isIdentifier(init) && stateVars.has(init.name)) {
32364
- collectPatternIdentifiers(varPath.node.id).forEach((id) => destructuredAliases.add(id));
32365
33256
  }
32366
33257
  },
32367
33258
  Function(fnPath) {
33259
+ if (isComponentDefinitionForProps(fnPath)) {
33260
+ for (const param of fnPath.node.params) {
33261
+ if (t4.isIdentifier(param)) {
33262
+ trackBindingByName(fnPath, param.name, propsBindingIds);
33263
+ continue;
33264
+ }
33265
+ if (t4.isPatternLike(param)) {
33266
+ collectPatternIdentifiers(param).forEach(
33267
+ (name) => trackBindingByName(fnPath, name, propsBindingIds)
33268
+ );
33269
+ }
33270
+ }
33271
+ }
32368
33272
  const parentFn = fnPath.getFunctionParent();
32369
33273
  if (!parentFn) return;
32370
33274
  if (!isComponentLike(parentFn)) return;
32371
- if (!isComponentLike(fnPath)) return;
33275
+ if (!isNamedComponentOrHookDefinition(fnPath)) return;
32372
33276
  emitWarning(
32373
33277
  fnPath.node,
32374
33278
  "FICT-C003",
@@ -32463,7 +33367,7 @@ or extract the nested logic into a custom hook (useXxx).`
32463
33367
  emitWarning(
32464
33368
  callPath.node,
32465
33369
  "FICT-R004",
32466
- "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
33370
+ "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.",
32467
33371
  warn,
32468
33372
  fileName
32469
33373
  );
@@ -32497,7 +33401,7 @@ or extract the nested logic into a custom hook (useXxx).`
32497
33401
  "createEffect"
32498
33402
  ]);
32499
33403
  callPath.node.arguments.forEach((arg) => {
32500
- if (t4.isIdentifier(arg) && stateVars.has(arg.name) && (!calleeId || !allowedStateCallees.has(calleeId))) {
33404
+ if (t4.isIdentifier(arg) && hasTrackedBinding(callPath, arg.name, stateBindingIds) && (!calleeId || !allowedStateCallees.has(calleeId))) {
32501
33405
  const loc = arg.loc?.start ?? callPath.node.loc?.start;
32502
33406
  warn({
32503
33407
  code: "FICT-S002",
@@ -32525,15 +33429,42 @@ or extract the nested logic into a custom hook (useXxx).`
32525
33429
  });
32526
33430
  const aliasStack = [/* @__PURE__ */ new Set()];
32527
33431
  const currentAliasSet = () => aliasStack[aliasStack.length - 1];
32528
- const rhsUsesState = (exprPath) => {
33432
+ const getBindingIdentifier = (path3, name) => {
33433
+ const binding = path3.scope.getBinding(name);
33434
+ return binding ? binding.identifier : null;
33435
+ };
33436
+ const addAliasBinding = (path3, name) => {
33437
+ const aliasSet = currentAliasSet();
33438
+ if (!aliasSet) return;
33439
+ const bindingId = getBindingIdentifier(path3, name);
33440
+ if (!bindingId) return;
33441
+ aliasSet.add(bindingId);
33442
+ aliasBindingIds.add(bindingId);
33443
+ };
33444
+ const addStateAliasBinding = (path3, name) => {
33445
+ const bindingId = getBindingIdentifier(path3, name);
33446
+ if (!bindingId) return;
33447
+ stateAliasBindingIds.add(bindingId);
33448
+ };
33449
+ const isAliasBinding = (path3, name) => {
33450
+ const aliasSet = currentAliasSet();
33451
+ if (!aliasSet) return false;
33452
+ const bindingId = getBindingIdentifier(path3, name);
33453
+ return !!(bindingId && aliasSet.has(bindingId));
33454
+ };
33455
+ const isDestructuredAliasBinding = (path3, name) => {
33456
+ const bindingId = getBindingIdentifier(path3, name);
33457
+ return !!(bindingId && destructuredAliases.has(bindingId));
33458
+ };
33459
+ const rhsUsesReactive = (exprPath) => {
32529
33460
  if (!exprPath) return false;
32530
- if (exprPath.isIdentifier() && t4.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
33461
+ if (exprPath.isIdentifier() && t4.isIdentifier(exprPath.node) && hasReactiveAliasSourceBinding(exprPath, exprPath.node.name)) {
32531
33462
  return true;
32532
33463
  }
32533
33464
  let usesState = false;
32534
33465
  exprPath.traverse({
32535
33466
  Identifier(idPath) {
32536
- if (stateVars.has(idPath.node.name)) {
33467
+ if (hasReactiveAliasSourceBinding(idPath, idPath.node.name)) {
32537
33468
  usesState = true;
32538
33469
  idPath.stop();
32539
33470
  }
@@ -32552,26 +33483,47 @@ or extract the nested logic into a custom hook (useXxx).`
32552
33483
  }
32553
33484
  },
32554
33485
  VariableDeclarator(varPath) {
32555
- const aliasSet = currentAliasSet();
32556
- if (aliasSet && t4.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
33486
+ const initPath = varPath.get("init");
33487
+ const stateRootId = isStateRootIdentifier(initPath);
33488
+ if (t4.isIdentifier(varPath.node.id) && rhsUsesReactive(initPath)) {
32557
33489
  debugLog("alias", "add from decl", varPath.node.id.name);
32558
- aliasSet.add(varPath.node.id.name);
33490
+ addAliasBinding(varPath, varPath.node.id.name);
33491
+ }
33492
+ if (t4.isIdentifier(varPath.node.id) && stateRootId) {
33493
+ addStateAliasBinding(varPath, varPath.node.id.name);
33494
+ }
33495
+ if (t4.isObjectPattern(varPath.node.id) || t4.isArrayPattern(varPath.node.id)) {
33496
+ if (rhsUsesReactive(initPath)) {
33497
+ const targets = collectPatternIdentifiers(varPath.node.id);
33498
+ for (const target of targets) {
33499
+ debugLog("alias", "add from destructuring decl", target);
33500
+ addAliasBinding(varPath, target);
33501
+ }
33502
+ }
33503
+ if (stateRootId) {
33504
+ collectPatternIdentifiers(varPath.node.id).forEach(
33505
+ (id) => trackBindingByName(varPath, id, destructuredAliases)
33506
+ );
33507
+ }
32559
33508
  }
32560
33509
  },
32561
33510
  AssignmentExpression(assignPath) {
32562
- const aliasSet = currentAliasSet();
32563
- if (!aliasSet) return;
32564
33511
  const rightPath = assignPath.get("right");
32565
- const usesState = rhsUsesState(rightPath);
33512
+ const usesState = rhsUsesReactive(rightPath);
33513
+ const stateRootId = isStateRootIdentifier(rightPath);
32566
33514
  const left = assignPath.node.left;
32567
33515
  if (t4.isIdentifier(left)) {
32568
33516
  const targetName = left.name;
32569
33517
  if (usesState) {
32570
33518
  debugLog("alias", "add from assign", targetName);
32571
- aliasSet.add(targetName);
33519
+ addAliasBinding(assignPath, targetName);
33520
+ if (stateRootId) {
33521
+ addStateAliasBinding(assignPath, targetName);
33522
+ }
32572
33523
  return;
32573
33524
  }
32574
- if (aliasSet.has(targetName)) {
33525
+ if (isAliasBinding(assignPath, targetName)) {
33526
+ if (isDestructuredAliasBinding(assignPath, targetName)) return;
32575
33527
  debugLog("alias", "reassignment detected", targetName);
32576
33528
  throw assignPath.buildCodeFrameError(
32577
33529
  `Alias reassignment is not supported for "${targetName}"`
@@ -32585,11 +33537,18 @@ or extract the nested logic into a custom hook (useXxx).`
32585
33537
  if (usesState) {
32586
33538
  for (const target of targets) {
32587
33539
  debugLog("alias", "add from destructuring assign", target);
32588
- aliasSet.add(target);
33540
+ addAliasBinding(assignPath, target);
33541
+ }
33542
+ if (stateRootId) {
33543
+ targets.forEach(
33544
+ (target) => trackBindingByName(assignPath, target, destructuredAliases)
33545
+ );
32589
33546
  }
32590
33547
  return;
32591
33548
  }
32592
- const reassigned = targets.find((target) => aliasSet.has(target));
33549
+ const reassigned = targets.find(
33550
+ (target) => isAliasBinding(assignPath, target) && !isDestructuredAliasBinding(assignPath, target)
33551
+ );
32593
33552
  if (reassigned) {
32594
33553
  debugLog("alias", "reassignment detected", reassigned);
32595
33554
  throw assignPath.buildCodeFrameError(
@@ -32597,20 +33556,31 @@ or extract the nested logic into a custom hook (useXxx).`
32597
33556
  );
32598
33557
  }
32599
33558
  }
33559
+ },
33560
+ UpdateExpression(updatePath) {
33561
+ const arg = updatePath.node.argument;
33562
+ if (t4.isIdentifier(arg) && isAliasBinding(updatePath, arg.name) && !isDestructuredAliasBinding(updatePath, arg.name)) {
33563
+ debugLog("alias", "reassignment detected", arg.name);
33564
+ throw updatePath.buildCodeFrameError(
33565
+ `Alias reassignment is not supported for "${arg.name}"`
33566
+ );
33567
+ }
32600
33568
  }
32601
33569
  });
32602
- if (derivedVars.size > 0) {
33570
+ if (derivedBindingIds.size > 0) {
32603
33571
  path2.traverse({
32604
33572
  AssignmentExpression(assignPath) {
32605
33573
  const { left } = assignPath.node;
32606
- if (t4.isIdentifier(left) && derivedVars.has(left.name)) {
33574
+ if (t4.isIdentifier(left) && hasTrackedBinding(assignPath, left.name, derivedBindingIds)) {
32607
33575
  throw assignPath.buildCodeFrameError(
32608
33576
  `Cannot reassign derived value '${left.name}'. Derived values are read-only.`
32609
33577
  );
32610
33578
  }
32611
33579
  if (t4.isObjectPattern(left) || t4.isArrayPattern(left)) {
32612
33580
  const targets = collectPatternIdentifiers(left);
32613
- const derivedTarget = targets.find((target) => derivedVars.has(target));
33581
+ const derivedTarget = targets.find(
33582
+ (target) => hasTrackedBinding(assignPath, target, derivedBindingIds)
33583
+ );
32614
33584
  if (derivedTarget) {
32615
33585
  throw assignPath.buildCodeFrameError(
32616
33586
  `Cannot reassign derived value '${derivedTarget}'. Derived values are read-only.`
@@ -32624,14 +33594,16 @@ or extract the nested logic into a custom hook (useXxx).`
32624
33594
  path2.traverse({
32625
33595
  AssignmentExpression(assignPath) {
32626
33596
  const { left } = assignPath.node;
32627
- if (t4.isIdentifier(left) && destructuredAliases.has(left.name)) {
33597
+ if (t4.isIdentifier(left) && hasTrackedBinding(assignPath, left.name, destructuredAliases)) {
32628
33598
  throw assignPath.buildCodeFrameError(
32629
33599
  `Cannot write to destructured state alias '${left.name}'. Update the original state (e.g. state.count++ or immutable update).`
32630
33600
  );
32631
33601
  }
32632
33602
  if (t4.isObjectPattern(left) || t4.isArrayPattern(left)) {
32633
33603
  const targets = collectPatternIdentifiers(left);
32634
- const aliasTarget = targets.find((target) => destructuredAliases.has(target));
33604
+ const aliasTarget = targets.find(
33605
+ (target) => hasTrackedBinding(assignPath, target, destructuredAliases)
33606
+ );
32635
33607
  if (aliasTarget) {
32636
33608
  throw assignPath.buildCodeFrameError(
32637
33609
  `Cannot write to destructured state alias '${aliasTarget}'. Update the original state (e.g. state.count++ or immutable update).`
@@ -32641,7 +33613,7 @@ or extract the nested logic into a custom hook (useXxx).`
32641
33613
  },
32642
33614
  UpdateExpression(updatePath) {
32643
33615
  const arg = updatePath.node.argument;
32644
- if (t4.isIdentifier(arg) && destructuredAliases.has(arg.name)) {
33616
+ if (t4.isIdentifier(arg) && hasTrackedBinding(updatePath, arg.name, destructuredAliases)) {
32645
33617
  throw updatePath.buildCodeFrameError(
32646
33618
  `Cannot write to destructured state alias '${arg.name}'. Update the original state (e.g. state.count++ or immutable update).`
32647
33619
  );
@@ -32651,7 +33623,28 @@ or extract the nested logic into a custom hook (useXxx).`
32651
33623
  }
32652
33624
  const shouldRunWarnings = dev || hasErrorEscalation(options);
32653
33625
  if (shouldRunWarnings) {
32654
- runWarningPass(path2, stateVars, derivedVars, warn, fileName, t4);
33626
+ const stateRootBindingIds = /* @__PURE__ */ new Set([
33627
+ ...stateBindingIds,
33628
+ ...stateAliasBindingIds
33629
+ ]);
33630
+ const reactiveBindingIds = /* @__PURE__ */ new Set([
33631
+ ...stateBindingIds,
33632
+ ...derivedBindingIds,
33633
+ ...aliasBindingIds,
33634
+ ...destructuredAliases,
33635
+ ...propsBindingIds,
33636
+ ...importedReactiveBindingIds
33637
+ ]);
33638
+ runWarningPass(
33639
+ path2,
33640
+ stateBindingIds,
33641
+ stateRootBindingIds,
33642
+ reactiveBindingIds,
33643
+ effectMacroNames,
33644
+ warn,
33645
+ fileName,
33646
+ t4
33647
+ );
32655
33648
  }
32656
33649
  const fileAst = t4.file(path2.node);
32657
33650
  const hir = buildHIR(