@fictjs/compiler 0.0.3 → 0.0.5

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.
package/dist/index.cjs CHANGED
@@ -14140,6 +14140,7 @@ var RUNTIME_HELPERS = {
14140
14140
  propsRest: "__fictPropsRest",
14141
14141
  mergeProps: "mergeProps",
14142
14142
  useProp: "useProp",
14143
+ runInScope: "runInScope",
14143
14144
  createElement: "createElement",
14144
14145
  conditional: "createConditional",
14145
14146
  list: "createList",
@@ -14152,6 +14153,7 @@ var RUNTIME_HELPERS = {
14152
14153
  bindClass: "bindClass",
14153
14154
  bindStyle: "bindStyle",
14154
14155
  bindEvent: "bindEvent",
14156
+ callEventHandler: "callEventHandler",
14155
14157
  bindRef: "bindRef",
14156
14158
  toNodeArray: "toNodeArray",
14157
14159
  createKeyedListContainer: "createKeyedListContainer",
@@ -14179,6 +14181,7 @@ var RUNTIME_ALIASES = {
14179
14181
  propsRest: "__fictPropsRest",
14180
14182
  useProp: "useProp",
14181
14183
  mergeProps: "mergeProps",
14184
+ runInScope: "runInScope",
14182
14185
  createElement: "createElement",
14183
14186
  conditional: "createConditional",
14184
14187
  list: "createList",
@@ -14191,6 +14194,7 @@ var RUNTIME_ALIASES = {
14191
14194
  bindClass: "bindClass",
14192
14195
  bindStyle: "bindStyle",
14193
14196
  bindEvent: "bindEvent",
14197
+ callEventHandler: "callEventHandler",
14194
14198
  bindRef: "bindRef",
14195
14199
  toNodeArray: "toNodeArray",
14196
14200
  createKeyedListContainer: "createKeyedListContainer",
@@ -15216,7 +15220,8 @@ function processStatement(stmt, bb, jumpTarget, ctx) {
15216
15220
  push({
15217
15221
  kind: "Assign",
15218
15222
  target: { kind: "Identifier", name: stmt.id.name },
15219
- value: convertExpression(fnExpr)
15223
+ value: convertExpression(fnExpr),
15224
+ declarationKind: "function"
15220
15225
  });
15221
15226
  return bb;
15222
15227
  }
@@ -17705,6 +17710,110 @@ function structurizeTry(ctx, block, term) {
17705
17710
  }
17706
17711
 
17707
17712
  // src/ir/regions.ts
17713
+ var REACTIVE_CREATORS = /* @__PURE__ */ new Set(["createEffect", "createMemo", "createSelector"]);
17714
+ function expressionCreatesReactive(expr) {
17715
+ if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier") {
17716
+ const base = getSSABaseName(expr.callee.name);
17717
+ return REACTIVE_CREATORS.has(base);
17718
+ }
17719
+ return false;
17720
+ }
17721
+ function expressionContainsReactiveCreation(expr) {
17722
+ if (expressionCreatesReactive(expr)) return true;
17723
+ switch (expr.kind) {
17724
+ case "CallExpression":
17725
+ return expressionContainsReactiveCreation(expr.callee) || expr.arguments.some((arg) => expressionContainsReactiveCreation(arg));
17726
+ case "MemberExpression":
17727
+ return expressionContainsReactiveCreation(expr.object) || expressionContainsReactiveCreation(expr.property);
17728
+ case "BinaryExpression":
17729
+ case "LogicalExpression":
17730
+ return expressionContainsReactiveCreation(expr.left) || expressionContainsReactiveCreation(expr.right);
17731
+ case "UnaryExpression":
17732
+ return expressionContainsReactiveCreation(expr.argument);
17733
+ case "ConditionalExpression":
17734
+ return expressionContainsReactiveCreation(expr.test) || expressionContainsReactiveCreation(expr.consequent) || expressionContainsReactiveCreation(expr.alternate);
17735
+ case "ArrayExpression":
17736
+ return expr.elements.some((el) => el && expressionContainsReactiveCreation(el));
17737
+ case "ObjectExpression":
17738
+ return expr.properties.some(
17739
+ (prop) => prop.kind === "SpreadElement" ? expressionContainsReactiveCreation(prop.argument) : expressionContainsReactiveCreation(prop.value)
17740
+ );
17741
+ case "ArrowFunction":
17742
+ if (expr.isExpression) {
17743
+ return expressionContainsReactiveCreation(expr.body);
17744
+ }
17745
+ return Array.isArray(expr.body) ? expr.body.some(
17746
+ (block) => block.instructions.some((i) => instructionContainsReactiveCreation(i))
17747
+ ) : false;
17748
+ case "FunctionExpression":
17749
+ return expr.body.some(
17750
+ (block) => block.instructions.some((i) => instructionContainsReactiveCreation(i))
17751
+ );
17752
+ case "AssignmentExpression":
17753
+ return expressionContainsReactiveCreation(expr.left) || expressionContainsReactiveCreation(expr.right);
17754
+ case "UpdateExpression":
17755
+ return expressionContainsReactiveCreation(expr.argument);
17756
+ case "TemplateLiteral":
17757
+ return expr.expressions.some((e) => expressionContainsReactiveCreation(e));
17758
+ case "SpreadElement":
17759
+ return expressionContainsReactiveCreation(expr.argument);
17760
+ case "AwaitExpression":
17761
+ return expressionContainsReactiveCreation(expr.argument);
17762
+ case "YieldExpression":
17763
+ return expr.argument ? expressionContainsReactiveCreation(expr.argument) : false;
17764
+ case "NewExpression":
17765
+ return expressionContainsReactiveCreation(expr.callee) || expr.arguments.some((arg) => expressionContainsReactiveCreation(arg));
17766
+ case "OptionalCallExpression":
17767
+ return expressionContainsReactiveCreation(expr.callee) || expr.arguments.some((arg) => expressionContainsReactiveCreation(arg));
17768
+ case "JSXElement":
17769
+ return typeof expr.tagName !== "string" && expressionContainsReactiveCreation(expr.tagName) || expr.attributes.some(
17770
+ (attr) => attr.isSpread ? !!attr.spreadExpr && expressionContainsReactiveCreation(attr.spreadExpr) : attr.value ? expressionContainsReactiveCreation(attr.value) : false
17771
+ ) || expr.children.some(
17772
+ (child) => child.kind === "expression" ? expressionContainsReactiveCreation(child.value) : false
17773
+ );
17774
+ default:
17775
+ return false;
17776
+ }
17777
+ }
17778
+ function instructionContainsReactiveCreation(instr) {
17779
+ if (instr.kind === "Assign") {
17780
+ return expressionContainsReactiveCreation(instr.value);
17781
+ }
17782
+ if (instr.kind === "Expression") {
17783
+ return expressionContainsReactiveCreation(instr.value);
17784
+ }
17785
+ return false;
17786
+ }
17787
+ function instructionIsReactiveSetup(instr) {
17788
+ if (instr.kind === "Assign") {
17789
+ return expressionCreatesReactive(instr.value);
17790
+ }
17791
+ if (instr.kind === "Expression") {
17792
+ return expressionCreatesReactive(instr.value);
17793
+ }
17794
+ return false;
17795
+ }
17796
+ function nodeIsPureReactiveScope(node) {
17797
+ let found = false;
17798
+ const visit = (n) => {
17799
+ switch (n.kind) {
17800
+ case "instruction": {
17801
+ const ok = instructionIsReactiveSetup(n.instruction);
17802
+ if (ok && instructionContainsReactiveCreation(n.instruction)) found = true;
17803
+ return ok;
17804
+ }
17805
+ case "sequence":
17806
+ if (n.nodes.length === 0) return false;
17807
+ return n.nodes.every((child) => visit(child));
17808
+ case "block":
17809
+ if (n.statements.length === 0) return false;
17810
+ return n.statements.every((child) => visit(child));
17811
+ default:
17812
+ return false;
17813
+ }
17814
+ };
17815
+ return visit(node) && found;
17816
+ }
17708
17817
  function generateRegions(fn, scopeResult, shapeResult = analyzeObjectShapes(fn)) {
17709
17818
  const regions = [];
17710
17819
  const regionsByBlock = /* @__PURE__ */ new Map();
@@ -18045,14 +18154,67 @@ function lowerNodeWithRegionContext(node, t2, ctx, declaredVars, regionCtx) {
18045
18154
  case "if": {
18046
18155
  const prevConditional = ctx.inConditional ?? 0;
18047
18156
  ctx.inConditional = prevConditional + 1;
18048
- const conseq = t2.blockStatement(
18049
- lowerNodeWithRegionContext(node.consequent, t2, ctx, declaredVars, regionCtx)
18157
+ const conseqStmts = lowerNodeWithRegionContext(
18158
+ node.consequent,
18159
+ t2,
18160
+ ctx,
18161
+ declaredVars,
18162
+ regionCtx
18050
18163
  );
18051
- const alt = node.alternate ? t2.blockStatement(
18052
- lowerNodeWithRegionContext(node.alternate, t2, ctx, declaredVars, regionCtx)
18053
- ) : null;
18164
+ const altStmts = node.alternate ? lowerNodeWithRegionContext(node.alternate, t2, ctx, declaredVars, regionCtx) : null;
18054
18165
  ctx.inConditional = prevConditional;
18055
- const ifStmt = t2.ifStatement(lowerExpressionWithDeSSA(node.test, ctx), conseq, alt);
18166
+ const conseqReactiveOnly = nodeIsPureReactiveScope(node.consequent);
18167
+ const altReactiveOnly = node.alternate ? nodeIsPureReactiveScope(node.alternate) : false;
18168
+ const testExpr = lowerExpressionWithDeSSA(node.test, ctx);
18169
+ const unwrapTestExpr = () => {
18170
+ if (t2.isArrowFunctionExpression(testExpr) && testExpr.params.length === 0 && !t2.isBlockStatement(testExpr.body)) {
18171
+ return t2.cloneNode(testExpr.body);
18172
+ }
18173
+ return t2.cloneNode(testExpr);
18174
+ };
18175
+ const createFlagExpr = (negate = false) => {
18176
+ const body = unwrapTestExpr();
18177
+ const bodyExpr = negate ? t2.unaryExpression("!", body) : body;
18178
+ return t2.arrowFunctionExpression([], bodyExpr);
18179
+ };
18180
+ if (conseqReactiveOnly || altReactiveOnly) {
18181
+ const stmts = [];
18182
+ const runInScopeId = t2.identifier(RUNTIME_ALIASES.runInScope);
18183
+ const addScoped = (flagExpr, body) => {
18184
+ ctx.helpersUsed.add("runInScope");
18185
+ stmts.push(
18186
+ t2.expressionStatement(
18187
+ t2.callExpression(runInScopeId, [
18188
+ flagExpr,
18189
+ t2.arrowFunctionExpression([], t2.blockStatement(body))
18190
+ ])
18191
+ )
18192
+ );
18193
+ };
18194
+ if (conseqReactiveOnly) {
18195
+ addScoped(createFlagExpr(false), conseqStmts);
18196
+ }
18197
+ if (altReactiveOnly && altStmts) {
18198
+ addScoped(createFlagExpr(true), altStmts);
18199
+ }
18200
+ const needsFallbackConseq = !conseqReactiveOnly && conseqStmts.length > 0;
18201
+ const needsFallbackAlt = !altReactiveOnly && altStmts && altStmts.length > 0;
18202
+ if (needsFallbackConseq || needsFallbackAlt) {
18203
+ stmts.push(
18204
+ t2.ifStatement(
18205
+ unwrapTestExpr(),
18206
+ needsFallbackConseq ? t2.blockStatement(conseqStmts) : t2.blockStatement([]),
18207
+ needsFallbackAlt && altStmts ? t2.blockStatement(altStmts) : null
18208
+ )
18209
+ );
18210
+ }
18211
+ return stmts;
18212
+ }
18213
+ const ifStmt = t2.ifStatement(
18214
+ testExpr,
18215
+ t2.blockStatement(conseqStmts),
18216
+ altStmts ? t2.blockStatement(altStmts) : null
18217
+ );
18056
18218
  const shouldWrapEffect = ctx.wrapTrackedExpressions !== false && !ctx.inRegionMemo && expressionUsesTracked(node.test, ctx) && !statementHasEarlyExit(ifStmt, t2);
18057
18219
  if (shouldWrapEffect) {
18058
18220
  ctx.helpersUsed.add("useEffect");
@@ -19046,6 +19208,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19046
19208
  if (instr.kind === "Assign") {
19047
19209
  const ssaName = instr.target.name;
19048
19210
  const baseName2 = deSSAVarName(ssaName);
19211
+ const declKindRaw = instr.declarationKind;
19049
19212
  propagateHookResultAlias(baseName2, instr.value, ctx);
19050
19213
  const hookMember = resolveHookMemberValue(instr.value, ctx);
19051
19214
  if (hookMember) {
@@ -19055,9 +19218,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19055
19218
  } else if (hookMember.kind === "memo") {
19056
19219
  ctx.memoVars?.add(baseName2);
19057
19220
  }
19058
- if (instr.declarationKind) {
19221
+ const declKind2 = declKindRaw && declKindRaw !== "function" ? declKindRaw : null;
19222
+ if (declKind2) {
19059
19223
  declaredVars.add(baseName2);
19060
- return t2.variableDeclaration(instr.declarationKind, [
19224
+ return t2.variableDeclaration(declKind2, [
19061
19225
  t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
19062
19226
  ]);
19063
19227
  }
@@ -19070,11 +19234,25 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19070
19234
  t2.assignmentExpression("=", t2.identifier(baseName2), hookMember.member)
19071
19235
  );
19072
19236
  }
19073
- const declKind = instr.declarationKind;
19237
+ const declKind = declKindRaw && declKindRaw !== "function" ? declKindRaw : void 0;
19238
+ const isFunctionDecl = instr.value.kind === "FunctionExpression" && (declKindRaw === "function" || !declKindRaw && instr.value.name === baseName2);
19239
+ if (isFunctionDecl) {
19240
+ const loweredFn = lowerExpressionWithDeSSA(instr.value, ctx);
19241
+ if (t2.isFunctionExpression(loweredFn)) {
19242
+ declaredVars.add(baseName2);
19243
+ return t2.functionDeclaration(
19244
+ t2.identifier(baseName2),
19245
+ loweredFn.params,
19246
+ loweredFn.body,
19247
+ loweredFn.generator ?? false,
19248
+ loweredFn.async ?? false
19249
+ );
19250
+ }
19251
+ }
19074
19252
  const isTracked = ctx.trackedVars.has(baseName2);
19075
19253
  const isSignal = ctx.signalVars?.has(baseName2) ?? false;
19076
19254
  const aliasVars = ctx.aliasVars ?? (ctx.aliasVars = /* @__PURE__ */ new Set());
19077
- const dependsOnTracked2 = expressionUsesTracked(instr.value, ctx);
19255
+ const dependsOnTracked2 = expressionUsesTracked(instr.value, ctx) || (ctx.memoVars?.has(baseName2) ?? false);
19078
19256
  const capturedTracked = ctx.externalTracked && ctx.externalTracked.has(baseName2) && !declaredVars.has(baseName2);
19079
19257
  const isShadowDeclaration = !!declKind && declaredVars.has(baseName2);
19080
19258
  const treatAsTracked = !isShadowDeclaration && isTracked;
@@ -19082,6 +19260,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19082
19260
  const isStateCall2 = instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && instr.value.callee.name === "$state";
19083
19261
  const inRegionMemo = ctx.inRegionMemo ?? false;
19084
19262
  const isFunctionValue = instr.value.kind === "ArrowFunction" || instr.value.kind === "FunctionExpression";
19263
+ const isAccessorReturningCall = instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && ["$memo", "createMemo", "useProp"].includes(instr.value.callee.name);
19264
+ const isReactiveObjectCall = instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && ["mergeProps", "prop"].includes(instr.value.callee.name);
19265
+ const isMemoReturningCall = isAccessorReturningCall || isReactiveObjectCall;
19085
19266
  const lowerAssignedValue = (forceAssigned = false) => lowerExpressionWithDeSSA(instr.value, ctx, forceAssigned || isFunctionValue);
19086
19267
  const buildMemoCall = (expr) => {
19087
19268
  const args = [
@@ -19128,7 +19309,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19128
19309
  t2.variableDeclarator(t2.identifier(baseName2), derivedExpr)
19129
19310
  ]);
19130
19311
  }
19131
- ctx.memoVars?.add(baseName2);
19312
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19132
19313
  if (ctx.noMemo) {
19133
19314
  return t2.variableDeclaration(normalizedDecl, [
19134
19315
  t2.variableDeclarator(
@@ -19138,7 +19319,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19138
19319
  ]);
19139
19320
  }
19140
19321
  return t2.variableDeclaration(normalizedDecl, [
19141
- t2.variableDeclarator(t2.identifier(baseName2), buildMemoCall(derivedExpr))
19322
+ t2.variableDeclarator(
19323
+ t2.identifier(baseName2),
19324
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19325
+ )
19142
19326
  ]);
19143
19327
  }
19144
19328
  }
@@ -19149,7 +19333,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19149
19333
  t2.variableDeclarator(t2.identifier(baseName2), derivedExpr)
19150
19334
  ]);
19151
19335
  }
19152
- ctx.memoVars?.add(baseName2);
19336
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19153
19337
  if (ctx.noMemo) {
19154
19338
  return t2.variableDeclaration(normalizedDecl, [
19155
19339
  t2.variableDeclarator(
@@ -19159,7 +19343,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19159
19343
  ]);
19160
19344
  }
19161
19345
  return t2.variableDeclaration(normalizedDecl, [
19162
- t2.variableDeclarator(t2.identifier(baseName2), buildMemoCall(derivedExpr))
19346
+ t2.variableDeclarator(
19347
+ t2.identifier(baseName2),
19348
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19349
+ )
19163
19350
  ]);
19164
19351
  }
19165
19352
  return t2.variableDeclaration(fallbackDecl, [
@@ -19217,7 +19404,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19217
19404
  t2.variableDeclarator(t2.identifier(baseName2), derivedExpr)
19218
19405
  ]);
19219
19406
  }
19220
- ctx.memoVars?.add(baseName2);
19407
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19221
19408
  if (ctx.noMemo) {
19222
19409
  return t2.variableDeclaration("const", [
19223
19410
  t2.variableDeclarator(
@@ -19227,7 +19414,10 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19227
19414
  ]);
19228
19415
  }
19229
19416
  return t2.variableDeclaration("const", [
19230
- t2.variableDeclarator(t2.identifier(baseName2), buildMemoCall(derivedExpr))
19417
+ t2.variableDeclarator(
19418
+ t2.identifier(baseName2),
19419
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19420
+ )
19231
19421
  ]);
19232
19422
  }
19233
19423
  return t2.variableDeclaration("let", [
@@ -19241,22 +19431,17 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19241
19431
  t2.variableDeclarator(t2.identifier(baseName2), derivedExpr)
19242
19432
  ]);
19243
19433
  }
19244
- if (ctx.memoVars?.has(baseName2)) {
19245
- if (buildMemoCall) {
19246
- console.log("Declaring memo var:", baseName2);
19247
- return t2.variableDeclaration("const", [
19248
- t2.variableDeclarator(t2.identifier(baseName2), buildMemoCall(derivedExpr))
19249
- ]);
19250
- }
19251
- }
19252
- ctx.memoVars?.add(baseName2);
19434
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19253
19435
  if (ctx.noMemo) {
19254
19436
  return t2.variableDeclaration("const", [
19255
19437
  t2.variableDeclarator(t2.identifier(baseName2), t2.arrowFunctionExpression([], derivedExpr))
19256
19438
  ]);
19257
19439
  }
19258
19440
  return t2.variableDeclaration("const", [
19259
- t2.variableDeclarator(t2.identifier(baseName2), buildMemoCall(derivedExpr))
19441
+ t2.variableDeclarator(
19442
+ t2.identifier(baseName2),
19443
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19444
+ )
19260
19445
  ]);
19261
19446
  }
19262
19447
  return t2.variableDeclaration("let", [
@@ -20334,7 +20519,8 @@ function collectExpressionIdentifiersDeep(expr, into, bound = /* @__PURE__ */ ne
20334
20519
  case "CallExpression":
20335
20520
  case "OptionalCallExpression": {
20336
20521
  const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
20337
- if (!isMacroCallee) {
20522
+ const isIIFE = expr.callee.kind === "ArrowFunction" || expr.callee.kind === "FunctionExpression";
20523
+ if (!isMacroCallee && !isIIFE) {
20338
20524
  collectExpressionIdentifiersDeep(expr.callee, into, bound);
20339
20525
  }
20340
20526
  expr.arguments.forEach(
@@ -20375,73 +20561,81 @@ function collectExpressionIdentifiersDeep(expr, into, bound = /* @__PURE__ */ ne
20375
20561
  );
20376
20562
  return;
20377
20563
  case "ArrowFunction": {
20378
- const nextBound = new Set(bound);
20379
- expr.params.forEach((p) => nextBound.add(deSSAVarName(p.name)));
20564
+ const tempSet = /* @__PURE__ */ new Set();
20380
20565
  if (expr.isExpression && expr.body && !Array.isArray(expr.body)) {
20381
- collectExpressionIdentifiersDeep(expr.body, into, nextBound);
20566
+ collectExpressionIdentifiers(expr.body, tempSet);
20382
20567
  } else if (Array.isArray(expr.body)) {
20383
20568
  for (const block of expr.body) {
20384
20569
  for (const instr of block.instructions) {
20385
20570
  if (instr.kind === "Assign") {
20386
- collectExpressionIdentifiersDeep(instr.value, into, nextBound);
20571
+ collectExpressionIdentifiers(instr.value, tempSet);
20387
20572
  } else if (instr.kind === "Expression") {
20388
- collectExpressionIdentifiersDeep(instr.value, into, nextBound);
20573
+ collectExpressionIdentifiers(instr.value, tempSet);
20389
20574
  } else if (instr.kind === "Phi") {
20390
- instr.sources.forEach((src) => addIdentifier(src.id.name));
20575
+ instr.sources.forEach((src) => tempSet.add(deSSAVarName(src.id.name)));
20391
20576
  }
20392
20577
  }
20393
20578
  const term = block.terminator;
20394
20579
  if (term.kind === "Branch") {
20395
- collectExpressionIdentifiersDeep(term.test, into, nextBound);
20580
+ collectExpressionIdentifiers(term.test, tempSet);
20396
20581
  } else if (term.kind === "Switch") {
20397
- collectExpressionIdentifiersDeep(term.discriminant, into, nextBound);
20582
+ collectExpressionIdentifiers(term.discriminant, tempSet);
20398
20583
  term.cases.forEach((c) => {
20399
- if (c.test) collectExpressionIdentifiersDeep(c.test, into, nextBound);
20584
+ if (c.test) collectExpressionIdentifiers(c.test, tempSet);
20400
20585
  });
20401
20586
  } else if (term.kind === "ForOf") {
20402
- collectExpressionIdentifiersDeep(term.iterable, into, nextBound);
20587
+ collectExpressionIdentifiers(term.iterable, tempSet);
20403
20588
  } else if (term.kind === "ForIn") {
20404
- collectExpressionIdentifiersDeep(term.object, into, nextBound);
20589
+ collectExpressionIdentifiers(term.object, tempSet);
20405
20590
  } else if (term.kind === "Return" && term.argument) {
20406
- collectExpressionIdentifiersDeep(term.argument, into, nextBound);
20591
+ collectExpressionIdentifiers(term.argument, tempSet);
20407
20592
  } else if (term.kind === "Throw") {
20408
- collectExpressionIdentifiersDeep(term.argument, into, nextBound);
20593
+ collectExpressionIdentifiers(term.argument, tempSet);
20409
20594
  }
20410
20595
  }
20411
20596
  }
20597
+ const paramNames = new Set(expr.params.map((p) => deSSAVarName(p.name)));
20598
+ for (const name of bound) paramNames.add(name);
20599
+ for (const dep of tempSet) {
20600
+ if (!paramNames.has(dep)) into.add(dep);
20601
+ }
20412
20602
  return;
20413
20603
  }
20414
20604
  case "FunctionExpression": {
20415
- const nextBound = new Set(bound);
20416
- expr.params.forEach((p) => nextBound.add(deSSAVarName(p.name)));
20605
+ const tempSet = /* @__PURE__ */ new Set();
20417
20606
  for (const block of expr.body) {
20418
20607
  for (const instr of block.instructions) {
20419
20608
  if (instr.kind === "Assign") {
20420
- collectExpressionIdentifiersDeep(instr.value, into, nextBound);
20609
+ collectExpressionIdentifiers(instr.value, tempSet);
20421
20610
  } else if (instr.kind === "Expression") {
20422
- collectExpressionIdentifiersDeep(instr.value, into, nextBound);
20611
+ collectExpressionIdentifiers(instr.value, tempSet);
20423
20612
  } else if (instr.kind === "Phi") {
20424
- instr.sources.forEach((src) => addIdentifier(src.id.name));
20613
+ instr.sources.forEach((src) => tempSet.add(deSSAVarName(src.id.name)));
20425
20614
  }
20426
20615
  }
20427
20616
  const term = block.terminator;
20428
20617
  if (term.kind === "Branch") {
20429
- collectExpressionIdentifiersDeep(term.test, into, nextBound);
20618
+ collectExpressionIdentifiers(term.test, tempSet);
20430
20619
  } else if (term.kind === "Switch") {
20431
- collectExpressionIdentifiersDeep(term.discriminant, into, nextBound);
20620
+ collectExpressionIdentifiers(term.discriminant, tempSet);
20432
20621
  term.cases.forEach((c) => {
20433
- if (c.test) collectExpressionIdentifiersDeep(c.test, into, nextBound);
20622
+ if (c.test) collectExpressionIdentifiers(c.test, tempSet);
20434
20623
  });
20435
20624
  } else if (term.kind === "ForOf") {
20436
- collectExpressionIdentifiersDeep(term.iterable, into, nextBound);
20625
+ collectExpressionIdentifiers(term.iterable, tempSet);
20437
20626
  } else if (term.kind === "ForIn") {
20438
- collectExpressionIdentifiersDeep(term.object, into, nextBound);
20627
+ collectExpressionIdentifiers(term.object, tempSet);
20439
20628
  } else if (term.kind === "Return" && term.argument) {
20440
- collectExpressionIdentifiersDeep(term.argument, into, nextBound);
20629
+ collectExpressionIdentifiers(term.argument, tempSet);
20441
20630
  } else if (term.kind === "Throw") {
20442
- collectExpressionIdentifiersDeep(term.argument, into, nextBound);
20631
+ collectExpressionIdentifiers(term.argument, tempSet);
20443
20632
  }
20444
20633
  }
20634
+ const paramNames = new Set(expr.params.map((p) => deSSAVarName(p.name)));
20635
+ for (const name of bound) paramNames.add(name);
20636
+ for (const dep of tempSet) {
20637
+ if (!paramNames.has(dep)) into.add(dep);
20638
+ }
20445
20639
  return;
20446
20640
  }
20447
20641
  case "AssignmentExpression":
@@ -20510,6 +20704,13 @@ function getExpressionIdentifiers(expr) {
20510
20704
  }
20511
20705
  return deps;
20512
20706
  }
20707
+ function getExpressionIdentifiersDeep(expr) {
20708
+ const deps = /* @__PURE__ */ new Set();
20709
+ if (expr) {
20710
+ collectExpressionIdentifiersDeep(expr, deps);
20711
+ }
20712
+ return deps;
20713
+ }
20513
20714
  function buildControlDependencyMap(fn) {
20514
20715
  const depsByInstruction = /* @__PURE__ */ new Map();
20515
20716
  let structured;
@@ -20636,7 +20837,7 @@ function computeReactiveAccessors(fn, ctx) {
20636
20837
  for (const instr of block.instructions) {
20637
20838
  if (instr.kind === "Assign") {
20638
20839
  const target = deSSAVarName(instr.target.name);
20639
- const dataDeps = getExpressionIdentifiers(instr.value);
20840
+ const dataDeps = getExpressionIdentifiersDeep(instr.value);
20640
20841
  addDepsToTarget(target, dataDeps, dataDepsByTarget);
20641
20842
  const controlDeps = controlDepsByInstr.get(instr) ?? /* @__PURE__ */ new Set();
20642
20843
  addDepsToTarget(target, controlDeps, controlDepsByTarget);
@@ -20697,7 +20898,7 @@ function computeReactiveAccessors(fn, ctx) {
20697
20898
  if (instr.kind === "Assign") {
20698
20899
  const target = deSSAVarName(instr.target.name);
20699
20900
  if (isFunctionVar(target)) continue;
20700
- const dataDeps = getExpressionIdentifiers(instr.value);
20901
+ const dataDeps = getExpressionIdentifiersDeep(instr.value);
20701
20902
  const controlDepsForInstr = controlDepsByInstr.get(instr) ?? /* @__PURE__ */ new Set();
20702
20903
  const hasDataDep = Array.from(dataDeps).some((dep) => tracked.has(dep));
20703
20904
  const hasControlDep = Array.from(controlDepsForInstr).some((dep) => tracked.has(dep));
@@ -20707,7 +20908,8 @@ function computeReactiveAccessors(fn, ctx) {
20707
20908
  tracked.add(target);
20708
20909
  changed = true;
20709
20910
  }
20710
- if (hasDataDep && !isSignal(target) && !isStore(target)) {
20911
+ const isReactiveObjectCall = instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && ["mergeProps", "prop"].includes(instr.value.callee.name);
20912
+ if (hasDataDep && !isSignal(target) && !isStore(target) && !isReactiveObjectCall) {
20711
20913
  memo.add(target);
20712
20914
  }
20713
20915
  } else if (instr.kind === "Phi") {
@@ -20795,6 +20997,20 @@ function lowerInstruction(instr, ctx) {
20795
20997
  const { t: t2 } = ctx;
20796
20998
  if (instr.kind === "Assign") {
20797
20999
  const baseName2 = deSSAVarName(instr.target.name);
21000
+ const isFunctionDecl = instr.value.kind === "FunctionExpression" && (instr.declarationKind === "function" || !instr.declarationKind && instr.value.name === baseName2);
21001
+ if (isFunctionDecl) {
21002
+ const loweredFn = lowerExpression(instr.value, ctx);
21003
+ if (t2.isFunctionExpression(loweredFn)) {
21004
+ return t2.functionDeclaration(
21005
+ t2.identifier(baseName2),
21006
+ loweredFn.params,
21007
+ loweredFn.body,
21008
+ loweredFn.generator ?? false,
21009
+ loweredFn.async ?? false
21010
+ );
21011
+ }
21012
+ }
21013
+ const declKind = instr.declarationKind === "function" ? void 0 : instr.declarationKind;
20798
21014
  propagateHookResultAlias(baseName2, instr.value, ctx);
20799
21015
  const hookMember = resolveHookMemberValue(instr.value, ctx);
20800
21016
  if (hookMember) {
@@ -20804,8 +21020,8 @@ function lowerInstruction(instr, ctx) {
20804
21020
  } else if (hookMember.kind === "memo") {
20805
21021
  ctx.memoVars?.add(baseName2);
20806
21022
  }
20807
- if (instr.declarationKind) {
20808
- return t2.variableDeclaration(instr.declarationKind, [
21023
+ if (declKind) {
21024
+ return t2.variableDeclaration(declKind, [
20809
21025
  t2.variableDeclarator(t2.identifier(baseName2), hookMember.member)
20810
21026
  ]);
20811
21027
  }
@@ -21204,6 +21420,13 @@ function lowerExpressionImpl(expr, ctx, isAssigned = false) {
21204
21420
  return t2.callExpression(t2.identifier(RUNTIME_ALIASES.mergeProps), args);
21205
21421
  }
21206
21422
  const isIIFE = (expr.callee.kind === "ArrowFunction" || expr.callee.kind === "FunctionExpression") && expr.arguments.length === 0 && expr.callee.params.length === 0;
21423
+ const calleeName = expr.callee.kind === "Identifier" ? deSSAVarName(expr.callee.name) : null;
21424
+ const calleeIsMemoAccessor = !!calleeName && ctx.memoVars?.has(calleeName);
21425
+ const calleeIsSignalLike = !!calleeName && (ctx.signalVars?.has(calleeName) || ctx.storeVars?.has(calleeName));
21426
+ if (calleeIsMemoAccessor && !calleeIsSignalLike && expr.arguments.length > 0) {
21427
+ const loweredArgs = expr.arguments.map((a) => lowerExpression(a, ctx));
21428
+ return t2.callExpression(t2.callExpression(t2.identifier(calleeName), []), loweredArgs);
21429
+ }
21207
21430
  const lowerCallee = () => isIIFE ? withNonReactiveScope(ctx, () => lowerExpression(expr.callee, ctx)) : lowerExpression(expr.callee, ctx);
21208
21431
  return t2.callExpression(
21209
21432
  lowerCallee(),
@@ -21556,11 +21779,12 @@ function lowerExpressionImpl(expr, ctx, isAssigned = false) {
21556
21779
  return t2.identifier("undefined");
21557
21780
  }
21558
21781
  }
21559
- function lowerDomExpression(expr, ctx, region) {
21782
+ function lowerDomExpression(expr, ctx, region, options) {
21560
21783
  let lowered = lowerExpression(expr, ctx);
21561
- if (ctx.t.isMemberExpression(lowered) && ctx.t.isIdentifier(lowered.object) && ctx.hookResultVarMap?.has(deSSAVarName(lowered.object.name))) {
21784
+ const skipHookAccessors = options?.skipHookAccessors ?? false;
21785
+ if (!skipHookAccessors && ctx.t.isMemberExpression(lowered) && ctx.t.isIdentifier(lowered.object) && ctx.hookResultVarMap?.has(deSSAVarName(lowered.object.name))) {
21562
21786
  lowered = ctx.t.callExpression(lowered, []);
21563
- } else if (ctx.t.isIdentifier(lowered)) {
21787
+ } else if (!skipHookAccessors && ctx.t.isIdentifier(lowered)) {
21564
21788
  const hookName = ctx.hookResultVarMap?.get(deSSAVarName(lowered.name));
21565
21789
  if (hookName) {
21566
21790
  const info = getHookReturnInfo(hookName, ctx);
@@ -21569,7 +21793,9 @@ function lowerDomExpression(expr, ctx, region) {
21569
21793
  }
21570
21794
  }
21571
21795
  }
21572
- return applyRegionMetadataToExpression(lowered, ctx, region);
21796
+ return applyRegionMetadataToExpression(lowered, ctx, region, {
21797
+ skipRootOverride: options?.skipRegionRootOverride
21798
+ });
21573
21799
  }
21574
21800
  function lowerJSXChildNonFineGrained(child, ctx) {
21575
21801
  const { t: t2 } = ctx;
@@ -21778,12 +22004,13 @@ function getDependencyPathFromNode(node, t2) {
21778
22004
  }
21779
22005
  return null;
21780
22006
  }
21781
- function applyRegionMetadataToExpression(expr, ctx, regionOverride) {
22007
+ function applyRegionMetadataToExpression(expr, ctx, regionOverride, options) {
21782
22008
  if (ctx.inReturn && ctx.currentFnIsHook) {
21783
22009
  return expr;
21784
22010
  }
21785
22011
  const region = regionOverride ?? ctx.currentRegion;
21786
22012
  if (!region) return expr;
22013
+ const skipRootOverride = options?.skipRootOverride ?? false;
21787
22014
  const metadata = regionInfoToMetadata(region);
21788
22015
  const state = {};
21789
22016
  applyRegionMetadata(state, {
@@ -21836,7 +22063,7 @@ function applyRegionMetadataToExpression(expr, ctx, regionOverride) {
21836
22063
  if (Object.keys(overrides).length === 0) {
21837
22064
  return expr;
21838
22065
  }
21839
- if (ctx.t.isIdentifier(expr)) {
22066
+ if (!skipRootOverride && ctx.t.isIdentifier(expr)) {
21840
22067
  const key = normalizeDependencyKey2(expr.name);
21841
22068
  const direct = overrides[key] ?? overrides[expr.name];
21842
22069
  if (direct) {
@@ -21844,10 +22071,10 @@ function applyRegionMetadataToExpression(expr, ctx, regionOverride) {
21844
22071
  }
21845
22072
  }
21846
22073
  const cloned = ctx.t.cloneNode(expr, true);
21847
- replaceIdentifiersWithOverrides(cloned, overrides, ctx.t);
22074
+ replaceIdentifiersWithOverrides(cloned, overrides, ctx.t, void 0, void 0, skipRootOverride);
21848
22075
  return cloned;
21849
22076
  }
21850
- function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parentKey) {
22077
+ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parentKey, skipCurrentNode = false) {
21851
22078
  const isCallTarget = parentKey === "callee" && (parentKind === "CallExpression" || parentKind === "OptionalCallExpression");
21852
22079
  const collectParamNames = (params) => {
21853
22080
  const names = /* @__PURE__ */ new Set();
@@ -21883,7 +22110,7 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
21883
22110
  params.forEach((p) => visitPattern(p));
21884
22111
  return names;
21885
22112
  };
21886
- if (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node)) {
22113
+ if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
21887
22114
  const path = getDependencyPathFromNode(node, t2);
21888
22115
  const normalized = path ? normalizeDependencyKey2(path) : null;
21889
22116
  const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
@@ -21893,7 +22120,7 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
21893
22120
  return;
21894
22121
  }
21895
22122
  }
21896
- if (t2.isIdentifier(node)) {
22123
+ if (!skipCurrentNode && t2.isIdentifier(node)) {
21897
22124
  const key = normalizeDependencyKey2(node.name);
21898
22125
  const override = overrides[key] ?? overrides[node.name];
21899
22126
  if (override && !isCallTarget) {
@@ -21938,7 +22165,8 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
21938
22165
  overrides,
21939
22166
  t2,
21940
22167
  node.type,
21941
- key
22168
+ key,
22169
+ false
21942
22170
  );
21943
22171
  }
21944
22172
  }
@@ -22367,7 +22595,10 @@ function lowerIntrinsicElement(jsx, ctx) {
22367
22595
  const shouldWrapHandler = isExpressionReactive(binding.expr, ctx);
22368
22596
  const prevWrapTracked = ctx.wrapTrackedExpressions;
22369
22597
  ctx.wrapTrackedExpressions = false;
22370
- const valueExpr = lowerDomExpression(binding.expr, ctx, containingRegion);
22598
+ const valueExpr = lowerDomExpression(binding.expr, ctx, containingRegion, {
22599
+ skipHookAccessors: true,
22600
+ skipRegionRootOverride: true
22601
+ });
22371
22602
  ctx.wrapTrackedExpressions = prevWrapTracked;
22372
22603
  const eventParam = t2.identifier("_e");
22373
22604
  const isFn = t2.isArrowFunctionExpression(valueExpr) || t2.isFunctionExpression(valueExpr);
@@ -24093,6 +24324,9 @@ function isInsideNestedFunction(path) {
24093
24324
  }
24094
24325
  return false;
24095
24326
  }
24327
+ function isInsideJSX(path) {
24328
+ return !!path.findParent((p) => p.isJSXElement?.() || p.isJSXFragment?.());
24329
+ }
24096
24330
  function emitWarning(node, code, message, options, fileName) {
24097
24331
  if (!options.onWarn) return;
24098
24332
  const loc = node.loc?.start;
@@ -24190,6 +24424,7 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24190
24424
  const root = getRootIdentifier(expr, t2);
24191
24425
  return !!(root && stateVars.has(root.name));
24192
24426
  };
24427
+ const reactiveNames = /* @__PURE__ */ new Set([...stateVars, ...derivedVars]);
24193
24428
  programPath.traverse({
24194
24429
  AssignmentExpression(path) {
24195
24430
  const { left } = path.node;
@@ -24252,6 +24487,35 @@ function runWarningPass(programPath, stateVars, derivedVars, options, t2) {
24252
24487
  );
24253
24488
  }
24254
24489
  },
24490
+ Function(path) {
24491
+ const captured = /* @__PURE__ */ new Set();
24492
+ path.traverse(
24493
+ {
24494
+ Function(inner) {
24495
+ if (inner === path) return;
24496
+ inner.skip();
24497
+ },
24498
+ Identifier(idPath) {
24499
+ const name = idPath.node.name;
24500
+ if (!reactiveNames.has(name)) return;
24501
+ const binding = idPath.scope.getBinding(name);
24502
+ if (!binding) return;
24503
+ if (binding.scope === idPath.scope || binding.scope === path.scope) return;
24504
+ captured.add(name);
24505
+ }
24506
+ },
24507
+ {}
24508
+ );
24509
+ if (captured.size > 0) {
24510
+ emitWarning(
24511
+ path.node,
24512
+ "FICT-R005",
24513
+ `Function captures reactive variable(s): ${Array.from(captured).join(", ")}. Pass them as parameters or memoize explicitly to avoid hidden dependencies.`,
24514
+ options,
24515
+ fileName
24516
+ );
24517
+ }
24518
+ },
24255
24519
  CallExpression(path) {
24256
24520
  if (t2.isIdentifier(path.node.callee, { name: "$effect" })) {
24257
24521
  const argPath = path.get("arguments.0");
@@ -24573,6 +24837,15 @@ function createHIREntrypointVisitor(t2, options) {
24573
24837
  }
24574
24838
  const callee = callPath.node.callee;
24575
24839
  const calleeId = t2.isIdentifier(callee) ? callee.name : null;
24840
+ if (calleeId && (calleeId === "createEffect" || calleeId === "createMemo" || calleeId === "createSelector") && fictImports.has(calleeId) && (isInsideLoop(callPath) || isInsideConditional(callPath)) && !isInsideJSX(callPath)) {
24841
+ emitWarning(
24842
+ callPath.node,
24843
+ "FICT-R004",
24844
+ "Reactive creation inside non-JSX control flow will not auto-dispose; wrap it in createScope/runInScope or move it into JSX-managed regions.",
24845
+ options,
24846
+ fileName
24847
+ );
24848
+ }
24576
24849
  const allowedStateCallees = /* @__PURE__ */ new Set([
24577
24850
  "$effect",
24578
24851
  "$memo",