@fictjs/compiler 0.1.0 → 0.2.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 +329 -249
  2. package/dist/index.js +329 -249
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -14279,6 +14279,35 @@ var SAFE_FUNCTIONS = /* @__PURE__ */ new Set([
14279
14279
  "Date.parse"
14280
14280
  ]);
14281
14281
 
14282
+ // src/debug.ts
14283
+ function parseFlag(raw) {
14284
+ if (!raw) return false;
14285
+ const val = raw.toLowerCase();
14286
+ return val === "1" || val === "true" || val === "yes" || val === "on";
14287
+ }
14288
+ function debugEnabled(flag) {
14289
+ const normalized = flag.toLowerCase();
14290
+ const legacy = process.env[`DEBUG_${flag.toUpperCase()}`];
14291
+ if (parseFlag(legacy)) return true;
14292
+ const fictLegacy = process.env[`FICT_DEBUG_${flag.toUpperCase()}`];
14293
+ if (parseFlag(fictLegacy)) return true;
14294
+ const raw = process.env.FICT_DEBUG ?? process.env.DEBUG_FICT;
14295
+ if (!raw) return false;
14296
+ if (parseFlag(raw)) return true;
14297
+ const parts = raw.split(",").map((p) => p.trim().toLowerCase()).filter(Boolean);
14298
+ return parts.includes(normalized) || parts.includes("all");
14299
+ }
14300
+ function debugLog(flag, message, data) {
14301
+ if (!debugEnabled(flag)) return;
14302
+ const msg = typeof message === "function" ? message() : message;
14303
+ const prefix = `[fict:${flag}]`;
14304
+ if (data !== void 0) {
14305
+ console.log(prefix, msg, data);
14306
+ } else {
14307
+ console.log(prefix, msg);
14308
+ }
14309
+ }
14310
+
14282
14311
  // src/ir/build-hir.ts
14283
14312
  var t = __toESM(require_lib3(), 1);
14284
14313
 
@@ -15680,7 +15709,7 @@ function convertExpression(node) {
15680
15709
  }
15681
15710
  if (t.isObjectMethod(prop)) {
15682
15711
  if (prop.computed) return void 0;
15683
- const keyExpr2 = t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : void 0;
15712
+ 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;
15684
15713
  if (!keyExpr2) return void 0;
15685
15714
  const fnExpr = t.functionExpression(
15686
15715
  null,
@@ -15697,7 +15726,7 @@ function convertExpression(node) {
15697
15726
  };
15698
15727
  }
15699
15728
  if (!t.isObjectProperty(prop) || prop.computed) return void 0;
15700
- const keyExpr = t.isIdentifier(prop.key) ? { kind: "Identifier", name: prop.key.name } : t.isStringLiteral(prop.key) ? { kind: "Literal", value: prop.key.value } : void 0;
15729
+ 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;
15701
15730
  if (!keyExpr) return void 0;
15702
15731
  if (!t.isExpression(prop.value)) return void 0;
15703
15732
  return {
@@ -16039,23 +16068,6 @@ function convertJSXMemberExpr(node) {
16039
16068
  };
16040
16069
  }
16041
16070
 
16042
- // src/debug.ts
16043
- function parseFlag(raw) {
16044
- if (!raw) return false;
16045
- const val = raw.toLowerCase();
16046
- return val === "1" || val === "true" || val === "yes" || val === "on";
16047
- }
16048
- function debugEnabled(flag) {
16049
- const normalized = flag.toLowerCase();
16050
- const legacy = process.env[`DEBUG_${flag.toUpperCase()}`];
16051
- if (parseFlag(legacy)) return true;
16052
- const raw = process.env.FICT_DEBUG ?? process.env.DEBUG_FICT;
16053
- if (!raw) return false;
16054
- if (parseFlag(raw)) return true;
16055
- const parts = raw.split(",").map((p) => p.trim().toLowerCase()).filter(Boolean);
16056
- return parts.includes(normalized) || parts.includes("all");
16057
- }
16058
-
16059
16071
  // src/fine-grained-dom.ts
16060
16072
  function normalizeDependencyKey(name) {
16061
16073
  return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
@@ -18105,6 +18117,35 @@ function structurizeTry(ctx, block, term) {
18105
18117
 
18106
18118
  // src/ir/regions.ts
18107
18119
  var REACTIVE_CREATORS = /* @__PURE__ */ new Set(["createEffect", "createMemo", "createSelector"]);
18120
+ function buildEffectCall(ctx, t2, effectFn, options) {
18121
+ if (ctx.inModule) {
18122
+ ctx.helpersUsed.add("effect");
18123
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.effect), [effectFn]);
18124
+ }
18125
+ ctx.helpersUsed.add("useEffect");
18126
+ ctx.needsCtx = true;
18127
+ const args = [t2.identifier("__fictCtx"), effectFn];
18128
+ const slot = options?.slot;
18129
+ if (options?.forceSlot) {
18130
+ args.push(slot !== void 0 && slot >= 0 ? t2.numericLiteral(slot) : t2.identifier("undefined"));
18131
+ } else if (slot !== void 0 && slot >= 0) {
18132
+ args.push(t2.numericLiteral(slot));
18133
+ }
18134
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), args);
18135
+ }
18136
+ function buildMemoCall(ctx, t2, memoFn, slot) {
18137
+ if (ctx.inModule) {
18138
+ ctx.helpersUsed.add("memo");
18139
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.memo), [memoFn]);
18140
+ }
18141
+ ctx.helpersUsed.add("useMemo");
18142
+ ctx.needsCtx = true;
18143
+ const args = [t2.identifier("__fictCtx"), memoFn];
18144
+ if (slot !== void 0 && slot >= 0) {
18145
+ args.push(t2.numericLiteral(slot));
18146
+ }
18147
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), args);
18148
+ }
18108
18149
  function expressionCreatesReactive(expr) {
18109
18150
  if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier") {
18110
18151
  const base = getSSABaseName(expr.callee.name);
@@ -18395,8 +18436,10 @@ function expressionUsesTracked(expr, ctx) {
18395
18436
  case "Identifier":
18396
18437
  return ctx.trackedVars.has(deSSAVarName(expr.name)) || (ctx.externalTracked?.has(deSSAVarName(expr.name)) ?? false) || (ctx.memoVars?.has(deSSAVarName(expr.name)) ?? false) || (ctx.aliasVars?.has(deSSAVarName(expr.name)) ?? false);
18397
18438
  case "MemberExpression":
18439
+ case "OptionalMemberExpression":
18398
18440
  return expressionUsesTracked(expr.object, ctx);
18399
18441
  case "CallExpression":
18442
+ case "OptionalCallExpression":
18400
18443
  if (expressionUsesTracked(expr.callee, ctx)) return true;
18401
18444
  return expr.arguments.some((arg) => expressionUsesTracked(arg, ctx));
18402
18445
  case "LogicalExpression":
@@ -18618,16 +18661,8 @@ function lowerNodeWithRegionContext(node, t2, ctx, declaredVars, regionCtx) {
18618
18661
  const inNonReactiveScope = !!(ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0);
18619
18662
  const shouldWrapEffect = ctx.wrapTrackedExpressions !== false && !ctx.inRegionMemo && !inNonReactiveScope && expressionUsesTracked(node.test, ctx) && !statementHasEarlyExit(ifStmt, t2);
18620
18663
  if (shouldWrapEffect) {
18621
- ctx.helpersUsed.add("useEffect");
18622
- ctx.needsCtx = true;
18623
- return [
18624
- t2.expressionStatement(
18625
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
18626
- t2.identifier("__fictCtx"),
18627
- t2.arrowFunctionExpression([], t2.blockStatement([ifStmt]))
18628
- ])
18629
- )
18630
- ];
18664
+ const effectFn = t2.arrowFunctionExpression([], t2.blockStatement([ifStmt]));
18665
+ return [t2.expressionStatement(buildEffectCall(ctx, t2, effectFn))];
18631
18666
  }
18632
18667
  return [ifStmt];
18633
18668
  }
@@ -18892,16 +18927,8 @@ function lowerStructuredNodeForRegion(node, region, t2, ctx, declaredVars, regio
18892
18927
  ifStmt = buildIfStmt(consequent, alternate);
18893
18928
  }
18894
18929
  if (shouldWrapEffect) {
18895
- ctx.helpersUsed.add("useEffect");
18896
- ctx.needsCtx = true;
18897
- return [
18898
- t2.expressionStatement(
18899
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
18900
- t2.identifier("__fictCtx"),
18901
- t2.arrowFunctionExpression([], t2.blockStatement([ifStmt]))
18902
- ])
18903
- )
18904
- ];
18930
+ const effectFn = t2.arrowFunctionExpression([], t2.blockStatement([ifStmt]));
18931
+ return [t2.expressionStatement(buildEffectCall(ctx, t2, effectFn))];
18905
18932
  }
18906
18933
  return [ifStmt];
18907
18934
  }
@@ -19273,30 +19300,16 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
19273
19300
  const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
19274
19301
  const uniqueOutputNames = [...new Set(outputNames)];
19275
19302
  const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
19276
- if (debugEnabled("region")) {
19277
- console.log("Region memo", region.id, {
19278
- instructions: region.instructions.map((instr) => instr.kind),
19279
- outputs: uniqueOutputNames
19280
- });
19281
- }
19303
+ debugLog("region", `Region memo ${region.id}`, {
19304
+ instructions: region.instructions.map((instr) => instr.kind),
19305
+ outputs: uniqueOutputNames
19306
+ });
19282
19307
  if (uniqueOutputNames.length === 0) {
19283
- ctx.helpersUsed.add("useEffect");
19284
- ctx.needsCtx = true;
19285
- const effectCallArgs = [
19286
- t2.identifier("__fictCtx"),
19287
- t2.arrowFunctionExpression([], t2.blockStatement(bodyStatements))
19288
- ];
19289
- {
19290
- const slot = reserveHookSlot(ctx);
19291
- if (slot >= 0) {
19292
- effectCallArgs.push(t2.numericLiteral(slot));
19293
- }
19294
- }
19295
- const effectCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), effectCallArgs);
19308
+ const effectFn = t2.arrowFunctionExpression([], t2.blockStatement(bodyStatements));
19309
+ const slot = ctx.inModule ? void 0 : reserveHookSlot(ctx);
19310
+ const effectCall = buildEffectCall(ctx, t2, effectFn, { slot });
19296
19311
  statements.push(t2.expressionStatement(effectCall));
19297
19312
  } else {
19298
- ctx.helpersUsed.add("useMemo");
19299
- ctx.needsCtx = true;
19300
19313
  if (!bodyStatementsOverride) {
19301
19314
  const lazyInfo = analyzeHIRConditionalUsage(region, ctx);
19302
19315
  if (lazyInfo) {
@@ -19325,15 +19338,8 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
19325
19338
  };
19326
19339
  const returnObj = t2.objectExpression(uniqueOutputNames.map((name) => buildOutputProperty(name)));
19327
19340
  const memoBody = t2.blockStatement([...bodyStatements, t2.returnStatement(returnObj)]);
19328
- const slot = reserveHookSlot(ctx);
19329
- const memoArgs = [
19330
- t2.identifier("__fictCtx"),
19331
- t2.arrowFunctionExpression([], memoBody)
19332
- ];
19333
- if (slot >= 0) {
19334
- memoArgs.push(t2.numericLiteral(slot));
19335
- }
19336
- const memoCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), memoArgs);
19341
+ const slot = ctx.inModule ? void 0 : reserveHookSlot(ctx);
19342
+ const memoCall = buildMemoCall(ctx, t2, t2.arrowFunctionExpression([], memoBody), slot);
19337
19343
  const regionVarName = `__region_${region.id}`;
19338
19344
  statements.push(
19339
19345
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
@@ -19343,16 +19349,13 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
19343
19349
  (name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
19344
19350
  );
19345
19351
  const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
19346
- if (debugEnabled("region")) {
19347
- console.log("Region debug", {
19348
- id: region.id,
19349
- outputs: uniqueOutputNames,
19350
- getterOutputs,
19351
- directOutputs,
19352
- tracked: Array.from(ctx.trackedVars),
19353
- memoVars: Array.from(ctx.memoVars ?? [])
19354
- });
19355
- }
19352
+ debugLog("region", `Region debug ${region.id}`, {
19353
+ outputs: uniqueOutputNames,
19354
+ getterOutputs,
19355
+ directOutputs,
19356
+ tracked: Array.from(ctx.trackedVars),
19357
+ memoVars: Array.from(ctx.memoVars ?? [])
19358
+ });
19356
19359
  if (directOutputs.length > 0) {
19357
19360
  directOutputs.forEach((name) => declaredVars.add(name));
19358
19361
  statements.push(
@@ -19380,21 +19383,15 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
19380
19383
  ctx.memoVars?.add(name);
19381
19384
  }
19382
19385
  if (region.hasControlFlow && getterOutputs.length > 0) {
19383
- ctx.helpersUsed.add("useEffect");
19384
- ctx.needsCtx = true;
19385
19386
  const effectBody = t2.blockStatement(
19386
19387
  getterOutputs.map((name) => t2.expressionStatement(t2.callExpression(t2.identifier(name), [])))
19387
19388
  );
19388
19389
  statements.push(
19389
19390
  t2.expressionStatement(
19390
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
19391
- t2.identifier("__fictCtx"),
19392
- t2.arrowFunctionExpression([], effectBody),
19393
- (() => {
19394
- const slot2 = reserveHookSlot(ctx);
19395
- return slot2 >= 0 ? t2.numericLiteral(slot2) : t2.identifier("undefined");
19396
- })()
19397
- ])
19391
+ buildEffectCall(ctx, t2, t2.arrowFunctionExpression([], effectBody), {
19392
+ slot: ctx.inModule ? void 0 : reserveHookSlot(ctx),
19393
+ forceSlot: true
19394
+ })
19398
19395
  )
19399
19396
  );
19400
19397
  }
@@ -19608,18 +19605,13 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
19608
19605
  t2.ifStatement(conditionId, t2.blockStatement(trueBlock), t2.blockStatement(falseBlock))
19609
19606
  );
19610
19607
  }
19611
- ctx.helpersUsed.add("useMemo");
19612
- ctx.needsCtx = true;
19613
19608
  const regionVarName = `__region_${region.id}`;
19614
- const slotForMemo = reserveHookSlot(ctx);
19615
- const memoArgs = [
19616
- t2.identifier("__fictCtx"),
19617
- t2.arrowFunctionExpression([], t2.blockStatement(memoBody))
19618
- ];
19619
- if (slotForMemo >= 0) {
19620
- memoArgs.push(t2.numericLiteral(slotForMemo));
19621
- }
19622
- const memoCall = t2.callExpression(t2.identifier("__fictUseMemo"), memoArgs);
19609
+ const memoCall = buildMemoCall(
19610
+ ctx,
19611
+ t2,
19612
+ t2.arrowFunctionExpression([], t2.blockStatement(memoBody)),
19613
+ ctx.inModule ? void 0 : reserveHookSlot(ctx)
19614
+ );
19623
19615
  statements.push(
19624
19616
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
19625
19617
  );
@@ -19708,20 +19700,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19708
19700
  const isReactiveObjectCall = instr.value.kind === "CallExpression" && instr.value.callee.kind === "Identifier" && ["mergeProps"].includes(instr.value.callee.name);
19709
19701
  const isMemoReturningCall = isAccessorReturningCall || isReactiveObjectCall;
19710
19702
  const lowerAssignedValue = (forceAssigned = false) => lowerExpressionWithDeSSA(instr.value, ctx, forceAssigned || isFunctionValue);
19711
- const buildMemoCall = (expr) => {
19712
- const args = [
19713
- t2.identifier("__fictCtx"),
19714
- t2.arrowFunctionExpression([], expr)
19715
- ];
19716
- if (inRegionMemo) {
19717
- const slot = reserveHookSlot(ctx);
19718
- if (slot >= 0) {
19719
- args.push(t2.numericLiteral(slot));
19720
- }
19721
- }
19722
- ctx.helpersUsed.add("useMemo");
19723
- ctx.needsCtx = true;
19724
- return t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), args);
19703
+ const buildDerivedMemoCall = (expr) => {
19704
+ const slot = !ctx.inModule && inRegionMemo ? reserveHookSlot(ctx) : void 0;
19705
+ return buildMemoCall(ctx, t2, t2.arrowFunctionExpression([], expr), slot);
19725
19706
  };
19726
19707
  if (isShadowDeclaration && declKind) {
19727
19708
  ctx.trackedVars.delete(baseName2);
@@ -19760,7 +19741,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19760
19741
  return t2.variableDeclaration(normalizedDecl, [
19761
19742
  t2.variableDeclarator(
19762
19743
  t2.identifier(baseName2),
19763
- isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19744
+ isMemoReturningCall ? derivedExpr : buildDerivedMemoCall(derivedExpr)
19764
19745
  )
19765
19746
  ]);
19766
19747
  }
@@ -19787,7 +19768,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19787
19768
  return t2.variableDeclaration(normalizedDecl, [
19788
19769
  t2.variableDeclarator(
19789
19770
  t2.identifier(baseName2),
19790
- isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19771
+ isMemoReturningCall ? derivedExpr : buildDerivedMemoCall(derivedExpr)
19791
19772
  )
19792
19773
  ]);
19793
19774
  }
@@ -19796,7 +19777,14 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19796
19777
  ]);
19797
19778
  }
19798
19779
  if (aliasVars.has(baseName2) && declaredVars.has(baseName2)) {
19799
- throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19780
+ throw new Error(
19781
+ `Alias reassignment is not supported for "${baseName2}".
19782
+
19783
+ "${baseName2}" was assigned from a reactive value and cannot be reassigned.
19784
+ Consider:
19785
+ - Using a new variable name for the new value
19786
+ - Updating the original reactive source instead`
19787
+ );
19800
19788
  }
19801
19789
  if (capturedTracked && isSignal) {
19802
19790
  return t2.expressionStatement(
@@ -19804,7 +19792,14 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19804
19792
  );
19805
19793
  }
19806
19794
  if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
19807
- throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19795
+ throw new Error(
19796
+ `Alias reassignment is not supported for "${baseName2}".
19797
+
19798
+ "${baseName2}" was assigned from a reactive value and cannot be reassigned.
19799
+ Consider:
19800
+ - Using a new variable name for the new value
19801
+ - Updating the original reactive source instead`
19802
+ );
19808
19803
  }
19809
19804
  if (dependsOnTracked && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
19810
19805
  const derivedExpr = lowerAssignedValue(true);
@@ -19828,13 +19823,20 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19828
19823
  t2.assignmentExpression(
19829
19824
  "=",
19830
19825
  t2.identifier(baseName2),
19831
- isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19826
+ isMemoReturningCall ? derivedExpr : buildDerivedMemoCall(derivedExpr)
19832
19827
  )
19833
19828
  );
19834
19829
  }
19835
19830
  if (declaredVars.has(baseName2)) {
19836
19831
  if (aliasVars.has(baseName2)) {
19837
- throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19832
+ throw new Error(
19833
+ `Alias reassignment is not supported for "${baseName2}".
19834
+
19835
+ "${baseName2}" was assigned from a reactive value and cannot be reassigned.
19836
+ Consider:
19837
+ - Using a new variable name for the new value
19838
+ - Updating the original reactive source instead`
19839
+ );
19838
19840
  }
19839
19841
  if (isSignal) {
19840
19842
  return t2.expressionStatement(
@@ -19876,7 +19878,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19876
19878
  return t2.variableDeclaration("const", [
19877
19879
  t2.variableDeclarator(
19878
19880
  t2.identifier(baseName2),
19879
- isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19881
+ isMemoReturningCall ? derivedExpr : buildDerivedMemoCall(derivedExpr)
19880
19882
  )
19881
19883
  ]);
19882
19884
  }
@@ -19900,7 +19902,7 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19900
19902
  return t2.variableDeclaration("const", [
19901
19903
  t2.variableDeclarator(
19902
19904
  t2.identifier(baseName2),
19903
- isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19905
+ isMemoReturningCall ? derivedExpr : buildDerivedMemoCall(derivedExpr)
19904
19906
  )
19905
19907
  ]);
19906
19908
  }
@@ -19920,8 +19922,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19920
19922
  const inNonReactiveScope = !!(ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0);
19921
19923
  const shouldWrapExpr = ctx.wrapTrackedExpressions !== false && !inNonReactiveScope && (usesTracked || hasTrackedControlDep);
19922
19924
  if (shouldWrapExpr) {
19923
- ctx.helpersUsed.add("useEffect");
19924
- ctx.needsCtx = true;
19925
19925
  const depReads = [];
19926
19926
  if (hasTrackedControlDep) {
19927
19927
  const uniqueDeps = new Set(Array.from(controlDeps).map((dep) => deSSAVarName(dep)));
@@ -19933,12 +19933,8 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19933
19933
  }
19934
19934
  const loweredExpr = lowerExpressionWithDeSSA(instr.value, ctx);
19935
19935
  const effectBody = depReads.length > 0 ? ctx.t.blockStatement([...depReads, ctx.t.expressionStatement(loweredExpr)]) : loweredExpr;
19936
- return t2.expressionStatement(
19937
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
19938
- t2.identifier("__fictCtx"),
19939
- ctx.t.isBlockStatement(effectBody) ? t2.arrowFunctionExpression([], effectBody) : t2.arrowFunctionExpression([], effectBody)
19940
- ])
19941
- );
19936
+ const effectFn = ctx.t.isBlockStatement(effectBody) ? t2.arrowFunctionExpression([], effectBody) : t2.arrowFunctionExpression([], effectBody);
19937
+ return t2.expressionStatement(buildEffectCall(ctx, t2, effectFn));
19942
19938
  }
19943
19939
  return t2.expressionStatement(lowerExpressionWithDeSSA(instr.value, ctx));
19944
19940
  }
@@ -20481,6 +20477,7 @@ function createCodegenContext(t2) {
20481
20477
  mutatedVars: /* @__PURE__ */ new Set(),
20482
20478
  inRegionMemo: false,
20483
20479
  inListRender: false,
20480
+ inModule: false,
20484
20481
  nextHookSlot: HOOK_SLOT_BASE,
20485
20482
  nonReactiveScopeDepth: 0,
20486
20483
  inConditional: 0,
@@ -20801,7 +20798,15 @@ function detectDerivedCycles(fn, _scopeResult) {
20801
20798
  if (visiting.has(node)) {
20802
20799
  const idx = stack.indexOf(node);
20803
20800
  const cycle = idx >= 0 ? [...stack.slice(idx), node] : [...stack, node];
20804
- throw new Error(`Detected cyclic derived dependency: ${cycle.join(" -> ")}`);
20801
+ throw new Error(
20802
+ `Detected cyclic derived dependency: ${cycle.join(" -> ")}
20803
+
20804
+ Tip: This usually happens when derived values depend on each other in a loop.
20805
+ Consider:
20806
+ - Using untrack() to break the dependency cycle
20807
+ - Restructuring your derived values to avoid circular dependencies
20808
+ - Moving one of the values to $state if it should be independently mutable`
20809
+ );
20805
20810
  }
20806
20811
  if (visited.has(node)) return;
20807
20812
  visiting.add(node);
@@ -20816,12 +20821,11 @@ function detectDerivedCycles(fn, _scopeResult) {
20816
20821
  for (const node of graph.keys()) {
20817
20822
  visit(node);
20818
20823
  }
20819
- if (debugEnabled("cycles")) {
20820
- console.error(
20821
- "cycle graph",
20822
- Array.from(graph.entries()).map(([k, v]) => [k, Array.from(v)])
20823
- );
20824
- }
20824
+ debugLog(
20825
+ "cycles",
20826
+ "cycle graph",
20827
+ Array.from(graph.entries()).map(([k, v]) => [k, Array.from(v)])
20828
+ );
20825
20829
  }
20826
20830
  function collectExpressionIdentifiers(expr, into) {
20827
20831
  if (!expr || typeof expr !== "object") return;
@@ -21917,22 +21921,32 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
21917
21921
  return t2.identifier("undefined");
21918
21922
  case "CallExpression": {
21919
21923
  if (expr.callee.kind === "Identifier" && expr.callee.name === "$state") {
21924
+ const args = lowerCallArguments(expr.arguments);
21925
+ if (ctx.inModule) {
21926
+ ctx.helpersUsed.add("signal");
21927
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.signal), args);
21928
+ }
21920
21929
  ctx.helpersUsed.add("useSignal");
21921
21930
  ctx.needsCtx = true;
21922
21931
  return t2.callExpression(t2.identifier(RUNTIME_ALIASES.useSignal), [
21923
21932
  t2.identifier("__fictCtx"),
21924
- ...lowerCallArguments(expr.arguments)
21933
+ ...args
21925
21934
  ]);
21926
21935
  }
21927
21936
  if (expr.callee.kind === "Identifier" && expr.callee.name === "$effect") {
21937
+ const args = lowerCallArguments(
21938
+ expr.arguments,
21939
+ (arg) => arg.kind === "ArrowFunction" || arg.kind === "FunctionExpression" ? withNonReactiveScope(ctx, () => lowerExpression(arg, ctx)) : lowerExpression(arg, ctx)
21940
+ );
21941
+ if (ctx.inModule) {
21942
+ ctx.helpersUsed.add("effect");
21943
+ return t2.callExpression(t2.identifier(RUNTIME_ALIASES.effect), args);
21944
+ }
21928
21945
  ctx.helpersUsed.add("useEffect");
21929
21946
  ctx.needsCtx = true;
21930
21947
  return t2.callExpression(t2.identifier(RUNTIME_ALIASES.useEffect), [
21931
21948
  t2.identifier("__fictCtx"),
21932
- ...lowerCallArguments(
21933
- expr.arguments,
21934
- (arg) => arg.kind === "ArrowFunction" || arg.kind === "FunctionExpression" ? withNonReactiveScope(ctx, () => lowerExpression(arg, ctx)) : lowerExpression(arg, ctx)
21935
- )
21949
+ ...args
21936
21950
  ]);
21937
21951
  }
21938
21952
  if (expr.callee.kind === "Identifier" && expr.callee.name === "__forOf") {
@@ -22824,6 +22838,16 @@ function isExpressionReactive(expr, ctx) {
22824
22838
  for (const dep of deps) {
22825
22839
  if (ctx.trackedVars.has(dep)) return true;
22826
22840
  }
22841
+ if (ctx.memoVars) {
22842
+ for (const dep of deps) {
22843
+ if (ctx.memoVars.has(dep)) return true;
22844
+ }
22845
+ }
22846
+ if (ctx.signalVars) {
22847
+ for (const dep of deps) {
22848
+ if (ctx.signalVars.has(dep)) return true;
22849
+ }
22850
+ }
22827
22851
  for (const region of regionsToCheck) {
22828
22852
  for (const dep of deps) {
22829
22853
  if (region.declarations.has(dep) || region.dependencies.has(dep)) {
@@ -23091,8 +23115,12 @@ function lowerIntrinsicElement(jsx, ctx) {
23091
23115
  const regionMeta = containingRegion ? regionInfoToMetadata(containingRegion) : null;
23092
23116
  const shouldMemo = !ctx.inListRender && !(ctx.inConditional && ctx.inConditional > 0) && regionMeta ? shouldMemoizeRegion(regionMeta) : false;
23093
23117
  if (shouldMemo) {
23094
- ctx.helpersUsed.add("useMemo");
23095
- ctx.needsCtx = true;
23118
+ if (ctx.inModule) {
23119
+ ctx.helpersUsed.add("memo");
23120
+ } else {
23121
+ ctx.helpersUsed.add("useMemo");
23122
+ ctx.needsCtx = true;
23123
+ }
23096
23124
  }
23097
23125
  const hoistedTmplId = getOrCreateHoistedTemplate(html, ctx);
23098
23126
  const rootId = genTemp(ctx, "root");
@@ -23366,16 +23394,15 @@ function lowerIntrinsicElement(jsx, ctx) {
23366
23394
  statements.push(t2.returnStatement(elId));
23367
23395
  const body = t2.blockStatement(statements);
23368
23396
  if (shouldMemo && containingRegion) {
23369
- const memoArgs = [
23370
- t2.identifier("__fictCtx"),
23371
- t2.arrowFunctionExpression([], body)
23372
- ];
23397
+ const memoBody = t2.arrowFunctionExpression([], body);
23398
+ if (ctx.inModule) {
23399
+ return t2.callExpression(t2.callExpression(t2.identifier(RUNTIME_ALIASES.memo), [memoBody]), []);
23400
+ }
23401
+ const memoArgs = [t2.identifier("__fictCtx"), memoBody];
23373
23402
  if (ctx.isComponentFn) {
23374
- {
23375
- const slot = reserveHookSlot2(ctx);
23376
- if (slot >= 0) {
23377
- memoArgs.push(t2.numericLiteral(slot));
23378
- }
23403
+ const slot = reserveHookSlot2(ctx);
23404
+ if (slot >= 0) {
23405
+ memoArgs.push(t2.numericLiteral(slot));
23379
23406
  }
23380
23407
  }
23381
23408
  return t2.callExpression(t2.callExpression(t2.identifier(RUNTIME_ALIASES.useMemo), memoArgs), []);
@@ -23435,9 +23462,9 @@ function emitHIRChildBinding(markerId, expr, statements, ctx, containingRegion)
23435
23462
  emitConditionalChild(parentId, markerId, expr, statements, ctx);
23436
23463
  return;
23437
23464
  }
23438
- if (expr.kind === "CallExpression") {
23465
+ if (expr.kind === "CallExpression" || expr.kind === "OptionalCallExpression") {
23439
23466
  const callee = expr.callee;
23440
- if (callee.kind === "MemberExpression" && callee.property.kind === "Identifier" && callee.property.name === "map") {
23467
+ if ((callee.kind === "MemberExpression" || callee.kind === "OptionalMemberExpression") && callee.property.kind === "Identifier" && callee.property.name === "map") {
23441
23468
  emitListChild(parentId, markerId, expr, statements, ctx);
23442
23469
  return;
23443
23470
  }
@@ -23477,10 +23504,14 @@ function emitConditionalChild(parentId, markerId, expr, statements, ctx) {
23477
23504
  ctx.helpersUsed.add("conditional");
23478
23505
  ctx.helpersUsed.add("createElement");
23479
23506
  ctx.helpersUsed.add("onDestroy");
23480
- ctx.helpersUsed.add("toNodeArray");
23481
23507
  let condition;
23482
23508
  let consequent;
23483
23509
  let alternate = null;
23510
+ const lowerBranch = (branch) => {
23511
+ const listExpr = buildListCallExpression(branch, statements, ctx);
23512
+ if (listExpr) return listExpr;
23513
+ return lowerDomExpression(branch, ctx);
23514
+ };
23484
23515
  const enterConditional = () => {
23485
23516
  ctx.inConditional = (ctx.inConditional ?? 0) + 1;
23486
23517
  };
@@ -23490,13 +23521,13 @@ function emitConditionalChild(parentId, markerId, expr, statements, ctx) {
23490
23521
  if (expr.kind === "ConditionalExpression") {
23491
23522
  condition = lowerDomExpression(expr.test, ctx);
23492
23523
  enterConditional();
23493
- consequent = lowerDomExpression(expr.consequent, ctx);
23494
- alternate = lowerDomExpression(expr.alternate, ctx);
23524
+ consequent = lowerBranch(expr.consequent);
23525
+ alternate = lowerBranch(expr.alternate);
23495
23526
  exitConditional();
23496
23527
  } else if (expr.kind === "LogicalExpression" && expr.operator === "&&") {
23497
23528
  condition = lowerDomExpression(expr.left, ctx);
23498
23529
  enterConditional();
23499
- consequent = lowerDomExpression(expr.right, ctx);
23530
+ consequent = lowerBranch(expr.right);
23500
23531
  exitConditional();
23501
23532
  } else {
23502
23533
  return;
@@ -23518,29 +23549,11 @@ function emitConditionalChild(parentId, markerId, expr, statements, ctx) {
23518
23549
  )
23519
23550
  ])
23520
23551
  );
23521
- const markersId = genTemp(ctx, "markers");
23522
- statements.push(
23523
- t2.variableDeclaration("const", [
23524
- t2.variableDeclarator(
23525
- markersId,
23526
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.toNodeArray), [
23527
- t2.memberExpression(bindingId, t2.identifier("marker"))
23528
- ])
23529
- )
23530
- ])
23531
- );
23532
- const mId = genTemp(ctx, "m");
23533
23552
  statements.push(
23534
- t2.forOfStatement(
23535
- t2.variableDeclaration("const", [t2.variableDeclarator(mId)]),
23536
- markersId,
23537
- t2.blockStatement([
23538
- t2.expressionStatement(
23539
- t2.callExpression(t2.memberExpression(parentId, t2.identifier("insertBefore")), [
23540
- mId,
23541
- markerId
23542
- ])
23543
- )
23553
+ t2.expressionStatement(
23554
+ t2.callExpression(t2.memberExpression(parentId, t2.identifier("insertBefore")), [
23555
+ t2.memberExpression(bindingId, t2.identifier("marker")),
23556
+ markerId
23544
23557
  ])
23545
23558
  )
23546
23559
  );
@@ -23890,10 +23903,18 @@ function applySelectorHoist(callbackExpr, itemParamName, keyParamName, statement
23890
23903
  };
23891
23904
  const visitNode = (node) => {
23892
23905
  if (t2.isFunctionExpression(node) || t2.isArrowFunctionExpression(node)) {
23893
- if (node !== callbackExpr) return;
23906
+ if (node !== callbackExpr) {
23907
+ if (t2.isBlockStatement(node.body)) {
23908
+ node.body.body.forEach((stmt) => visitNode(stmt));
23909
+ } else if (t2.isExpression(node.body)) {
23910
+ visitNode(node.body);
23911
+ }
23912
+ return;
23913
+ }
23894
23914
  }
23895
- if (t2.isCallExpression(node) && t2.isIdentifier(node.callee)) {
23896
- if (node.callee.name === RUNTIME_ALIASES.bindClass) {
23915
+ if (t2.isCallExpression(node)) {
23916
+ const calleeName = t2.isIdentifier(node.callee) ? node.callee.name : t2.isMemberExpression(node.callee) && t2.isIdentifier(node.callee.property) ? node.callee.property.name : null;
23917
+ if (calleeName === RUNTIME_ALIASES.bindClass || calleeName === "bindClass") {
23897
23918
  const handler = node.arguments[1];
23898
23919
  if (handler && (t2.isArrowFunctionExpression(handler) || t2.isFunctionExpression(handler))) {
23899
23920
  rewriteInFunction(handler);
@@ -23976,20 +23997,26 @@ function applySelectorHoist(callbackExpr, itemParamName, keyParamName, statement
23976
23997
  }
23977
23998
  }
23978
23999
  }
23979
- function emitListChild(parentId, markerId, expr, statements, ctx) {
24000
+ function buildListCallExpression(expr, statements, ctx) {
23980
24001
  const { t: t2 } = ctx;
23981
- if (expr.kind !== "CallExpression" || expr.callee.kind !== "MemberExpression") {
23982
- return;
24002
+ if (expr.kind !== "CallExpression" && expr.kind !== "OptionalCallExpression") {
24003
+ return null;
24004
+ }
24005
+ if (expr.callee.kind !== "MemberExpression" && expr.callee.kind !== "OptionalMemberExpression") {
24006
+ return null;
23983
24007
  }
23984
- const arrayExpr = lowerDomExpression(expr.callee.object, ctx);
24008
+ if (expr.callee.property.kind !== "Identifier" || expr.callee.property.name !== "map") {
24009
+ return null;
24010
+ }
24011
+ const isOptional = expr.kind === "OptionalCallExpression" || expr.callee.kind === "OptionalMemberExpression" && expr.callee.optional;
24012
+ const arrayExprBase = lowerDomExpression(expr.callee.object, ctx);
24013
+ const arrayExpr = isOptional ? t2.logicalExpression("??", arrayExprBase, t2.arrayExpression([])) : arrayExprBase;
23985
24014
  const mapCallback = expr.arguments[0];
23986
24015
  if (!mapCallback) {
23987
24016
  throw new Error("map callback is required");
23988
24017
  }
23989
24018
  const keyExpr = extractKeyFromMapCallback(mapCallback);
23990
24019
  const isKeyed = !!keyExpr;
23991
- ctx.helpersUsed.add("onDestroy");
23992
- ctx.helpersUsed.add("toNodeArray");
23993
24020
  if (isKeyed) {
23994
24021
  ctx.helpersUsed.add("keyedList");
23995
24022
  } else {
@@ -24017,6 +24044,7 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
24017
24044
  ctx.inListRender = true;
24018
24045
  let callbackExpr = lowerExpression(mapCallback, ctx);
24019
24046
  ctx.inListRender = prevInListRender;
24047
+ const capturedKeyParamName = ctx.listKeyParamName;
24020
24048
  ctx.listKeyExpr = prevListKeyExpr;
24021
24049
  ctx.listItemParamName = prevListItemParamName;
24022
24050
  ctx.listKeyParamName = prevListKeyParamName;
@@ -24054,18 +24082,17 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
24054
24082
  }
24055
24083
  }
24056
24084
  }
24057
- const listId = genTemp(ctx, "list");
24058
24085
  if (isKeyed) {
24059
24086
  const itemParamName = t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr) ? t2.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : null : null;
24060
- const keyParamName = ctx.listKeyParamName ?? null;
24061
24087
  applySelectorHoist(
24062
24088
  callbackExpr,
24063
24089
  itemParamName,
24064
- keyParamName,
24090
+ capturedKeyParamName ?? null,
24065
24091
  statements,
24066
24092
  ctx
24067
24093
  );
24068
24094
  }
24095
+ let listCall;
24069
24096
  if (isKeyed && keyExpr) {
24070
24097
  let keyExprAst = lowerExpression(keyExpr, ctx);
24071
24098
  if (t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr)) {
@@ -24108,19 +24135,12 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
24108
24135
  }
24109
24136
  }
24110
24137
  statements.push(...hoistedStatements);
24111
- statements.push(
24112
- t2.variableDeclaration("const", [
24113
- t2.variableDeclarator(
24114
- listId,
24115
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.keyedList), [
24116
- t2.arrowFunctionExpression([], arrayExpr),
24117
- keyFn,
24118
- callbackExpr,
24119
- t2.booleanLiteral(hasIndexParam)
24120
- ])
24121
- )
24122
- ])
24123
- );
24138
+ listCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.keyedList), [
24139
+ t2.arrowFunctionExpression([], arrayExpr),
24140
+ keyFn,
24141
+ callbackExpr,
24142
+ t2.booleanLiteral(hasIndexParam)
24143
+ ]);
24124
24144
  } else {
24125
24145
  statements.push(...hoistedStatements);
24126
24146
  const itemParamName = t2.isArrowFunctionExpression(callbackExpr) || t2.isFunctionExpression(callbackExpr) ? t2.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
@@ -24130,20 +24150,23 @@ function emitListChild(parentId, markerId, expr, statements, ctx) {
24130
24150
  [t2.identifier(itemParamName), t2.identifier(indexParamName)],
24131
24151
  t2.identifier(indexParamName)
24132
24152
  );
24133
- statements.push(
24134
- t2.variableDeclaration("const", [
24135
- t2.variableDeclarator(
24136
- listId,
24137
- t2.callExpression(t2.identifier(RUNTIME_ALIASES.keyedList), [
24138
- t2.arrowFunctionExpression([], arrayExpr),
24139
- keyFn,
24140
- callbackExpr,
24141
- t2.booleanLiteral(hasIndexParam)
24142
- ])
24143
- )
24144
- ])
24145
- );
24153
+ listCall = t2.callExpression(t2.identifier(RUNTIME_ALIASES.keyedList), [
24154
+ t2.arrowFunctionExpression([], arrayExpr),
24155
+ keyFn,
24156
+ callbackExpr,
24157
+ t2.booleanLiteral(hasIndexParam)
24158
+ ]);
24146
24159
  }
24160
+ return listCall;
24161
+ }
24162
+ function emitListChild(parentId, markerId, expr, statements, ctx) {
24163
+ const { t: t2 } = ctx;
24164
+ const listCall = buildListCallExpression(expr, statements, ctx);
24165
+ if (!listCall) return;
24166
+ ctx.helpersUsed.add("onDestroy");
24167
+ ctx.helpersUsed.add("toNodeArray");
24168
+ const listId = genTemp(ctx, "list");
24169
+ statements.push(t2.variableDeclaration("const", [t2.variableDeclarator(listId, listCall)]));
24147
24170
  const markersId = genTemp(ctx, "markers");
24148
24171
  statements.push(
24149
24172
  t2.variableDeclaration("const", [
@@ -24498,8 +24521,14 @@ function lowerTopLevelStatementBlock(statements, ctx, t2, name = "__module_segme
24498
24521
  ctx.hookReturnInfo.set(fn.name, info);
24499
24522
  }
24500
24523
  }
24501
- const lowered = generateRegionCode(fn, scopeResult, t2, ctx);
24502
- return { statements: lowered, aliases: aliasVars };
24524
+ const prevInModule = ctx.inModule;
24525
+ ctx.inModule = true;
24526
+ try {
24527
+ const lowered = generateRegionCode(fn, scopeResult, t2, ctx);
24528
+ return { statements: lowered, aliases: aliasVars };
24529
+ } finally {
24530
+ ctx.inModule = prevInModule;
24531
+ }
24503
24532
  }
24504
24533
  function transformControlFlowReturns(statements, ctx) {
24505
24534
  const { t: t2 } = ctx;
@@ -24600,12 +24629,14 @@ function lowerFunctionWithRegions(fn, ctx) {
24600
24629
  const prevWrapTracked = ctx.wrapTrackedExpressions;
24601
24630
  const prevIsComponent = ctx.isComponentFn;
24602
24631
  const prevHookResultVarMap = ctx.hookResultVarMap;
24632
+ const prevInModule = ctx.inModule;
24603
24633
  const scopedTracked = new Set(ctx.trackedVars);
24604
24634
  const shadowedParams = new Set(fn.params.map((p) => deSSAVarName(p.name)));
24605
24635
  fn.params.forEach((p) => scopedTracked.delete(deSSAVarName(p.name)));
24606
24636
  ctx.trackedVars = scopedTracked;
24607
24637
  const prevNeedsCtx = ctx.needsCtx;
24608
24638
  ctx.needsCtx = false;
24639
+ ctx.inModule = false;
24609
24640
  const prevShadowed = ctx.shadowedNames;
24610
24641
  const functionShadowed = new Set(prevShadowed ?? []);
24611
24642
  shadowedParams.forEach((n) => functionShadowed.add(n));
@@ -24890,9 +24921,9 @@ function lowerFunctionWithRegions(fn, ctx) {
24890
24921
  ctx.hookReturnInfo.set(fn.name, info);
24891
24922
  }
24892
24923
  }
24893
- if (debugEnabled("region") && fn.name === "Counter") {
24894
- console.log("Tracked vars for Counter", Array.from(ctx.trackedVars));
24895
- console.log("Memo vars for Counter", Array.from(ctx.memoVars));
24924
+ if (fn.name === "Counter") {
24925
+ debugLog("region", "Tracked vars for Counter", Array.from(ctx.trackedVars));
24926
+ debugLog("region", "Memo vars for Counter", Array.from(ctx.memoVars));
24896
24927
  }
24897
24928
  hookResultVars.forEach((varName) => {
24898
24929
  const hookName = ctx.hookResultVarMap?.get(varName);
@@ -24922,6 +24953,7 @@ function lowerFunctionWithRegions(fn, ctx) {
24922
24953
  ctx.noMemo = prevNoMemo;
24923
24954
  ctx.wrapTrackedExpressions = prevWrapTracked;
24924
24955
  ctx.hookResultVarMap = prevHookResultVarMap;
24956
+ ctx.inModule = prevInModule;
24925
24957
  return null;
24926
24958
  }
24927
24959
  let statements;
@@ -25010,6 +25042,7 @@ function lowerFunctionWithRegions(fn, ctx) {
25010
25042
  ctx.propsParamName = prevPropsParam;
25011
25043
  ctx.propAccessorDecls = prevPropAccessors;
25012
25044
  ctx.delegatedEventsUsed = prevDelegatedEventsUsed;
25045
+ ctx.inModule = prevInModule;
25013
25046
  return funcDecl;
25014
25047
  }
25015
25048
  function flattenRegions(regions) {
@@ -25653,28 +25686,49 @@ function createHIREntrypointVisitor(t2, options) {
25653
25686
  if (!init) return;
25654
25687
  if (isStateCall(init, t2)) {
25655
25688
  if (!fictImports.has("$state")) {
25656
- throw varPath.buildCodeFrameError('$state() must be imported from "fict"');
25689
+ throw varPath.buildCodeFrameError(
25690
+ `$state() must be imported from "fict".
25691
+
25692
+ Add this import at the top of your file:
25693
+ import { $state } from 'fict'`
25694
+ );
25657
25695
  }
25658
25696
  if (!t2.isIdentifier(varPath.node.id)) {
25659
25697
  throw varPath.buildCodeFrameError(
25660
- "Destructuring $state is not supported. Use a simple identifier."
25698
+ `Destructuring $state is not supported.
25699
+
25700
+ Instead of: const { a, b } = $state({ a: 1, b: 2 })
25701
+ Use: let state = $state({ a: 1, b: 2 })
25702
+ const { a, b } = state // read-only aliases
25703
+
25704
+ For deep reactivity, consider using $store from 'fict'.`
25661
25705
  );
25662
25706
  }
25663
25707
  const ownerComponent = varPath.getFunctionParent();
25664
25708
  if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
25665
25709
  throw varPath.buildCodeFrameError(
25666
- "$state() must be declared inside a component or hook function body"
25710
+ `$state() must be declared inside a component or hook function body.
25711
+
25712
+ For module-level shared state, use one of these alternatives:
25713
+ \u2022 $store from 'fict' - for deep reactive objects
25714
+ \u2022 createSignal from 'fict/advanced' - for primitives`
25667
25715
  );
25668
25716
  }
25669
25717
  stateVars.add(varPath.node.id.name);
25670
25718
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
25671
25719
  throw varPath.buildCodeFrameError(
25672
- "$state() cannot be declared inside loops or conditionals"
25720
+ `$state() cannot be declared inside loops or conditionals.
25721
+
25722
+ Signals must be created at the top level of components for stable identity.
25723
+ Move the $state() declaration before the loop/condition.`
25673
25724
  );
25674
25725
  }
25675
25726
  if (isInsideNestedFunction(varPath)) {
25676
25727
  throw varPath.buildCodeFrameError(
25677
- "$state() cannot be declared inside nested functions"
25728
+ `$state() cannot be declared inside nested functions.
25729
+
25730
+ Move the $state() declaration to the component's top level,
25731
+ or extract the nested logic into a custom hook (useXxx).`
25678
25732
  );
25679
25733
  }
25680
25734
  } else if (t2.isIdentifier(varPath.node.id)) {
@@ -25716,43 +25770,77 @@ function createHIREntrypointVisitor(t2, options) {
25716
25770
  const isVariableDeclarator = parentPath?.isVariableDeclarator() && parentPath.node.init === callPath.node;
25717
25771
  if (!isVariableDeclarator) {
25718
25772
  throw callPath.buildCodeFrameError(
25719
- "$state() must be assigned directly to a variable (e.g. let count = $state(0)). For object state, consider using $store from fict/plus."
25773
+ `$state() must be assigned directly to a variable.
25774
+
25775
+ Correct usage:
25776
+ let count = $state(0)
25777
+ let user = $state({ name: 'Alice' })
25778
+
25779
+ For object state with deep reactivity, consider:
25780
+ import { $store } from 'fict'
25781
+ const user = $store({ name: 'Alice', address: { city: 'NYC' } })`
25720
25782
  );
25721
25783
  }
25722
25784
  if (!t2.isIdentifier(parentPath.node.id)) {
25723
25785
  throw callPath.buildCodeFrameError(
25724
- "Destructuring $state is not supported. Use a simple identifier."
25786
+ `Destructuring $state is not supported.
25787
+
25788
+ Instead of: const { a, b } = $state({ a: 1, b: 2 })
25789
+ Use: let state = $state({ a: 1, b: 2 })
25790
+ const { a, b } = state // read-only aliases`
25725
25791
  );
25726
25792
  }
25727
25793
  const ownerComponent = callPath.getFunctionParent();
25728
25794
  if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
25729
25795
  throw callPath.buildCodeFrameError(
25730
- "$state() must be declared inside a component or hook function body"
25796
+ `$state() must be declared inside a component or hook function body.
25797
+
25798
+ For module-level shared state, use one of these alternatives:
25799
+ \u2022 $store from 'fict' - for deep reactive objects
25800
+ \u2022 createSignal from 'fict/advanced' - for primitives`
25731
25801
  );
25732
25802
  }
25733
25803
  if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
25734
25804
  throw callPath.buildCodeFrameError(
25735
- "$state() cannot be declared inside loops or conditionals"
25805
+ `$state() cannot be declared inside loops or conditionals.
25806
+
25807
+ Move the declaration to the top of your component.
25808
+ For dynamic collections, consider using $store with an array/object.`
25736
25809
  );
25737
25810
  }
25738
25811
  if (isInsideNestedFunction(callPath)) {
25739
25812
  throw callPath.buildCodeFrameError(
25740
- "$state() cannot be declared inside nested functions"
25813
+ `$state() cannot be declared inside nested functions.
25814
+
25815
+ Move the declaration to the component's top level,
25816
+ or extract the nested logic into a custom hook (useXxx).`
25741
25817
  );
25742
25818
  }
25743
25819
  }
25744
25820
  if (isEffectCall(callPath.node, t2)) {
25745
25821
  if (!fictImports.has("$effect")) {
25746
- throw callPath.buildCodeFrameError('$effect() must be imported from "fict"');
25822
+ throw callPath.buildCodeFrameError(
25823
+ `$effect() must be imported from "fict".
25824
+
25825
+ Add this import at the top of your file:
25826
+ import { $effect } from 'fict'`
25827
+ );
25747
25828
  }
25748
25829
  if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
25749
25830
  throw callPath.buildCodeFrameError(
25750
- "$effect() cannot be called inside loops or conditionals"
25831
+ `$effect() cannot be called inside loops or conditionals.
25832
+
25833
+ Effects must be registered at the top level of components.
25834
+ For conditional effects, use a condition inside the effect body instead:
25835
+ $effect(() => { if (condition) { /* ... */ } })`
25751
25836
  );
25752
25837
  }
25753
25838
  if (isInsideNestedFunction(callPath)) {
25754
25839
  throw callPath.buildCodeFrameError(
25755
- "$effect() cannot be called inside nested functions"
25840
+ `$effect() cannot be called inside nested functions.
25841
+
25842
+ Move the effect to the component's top level,
25843
+ or extract the nested logic into a custom hook (useXxx).`
25756
25844
  );
25757
25845
  }
25758
25846
  }
@@ -25840,9 +25928,7 @@ function createHIREntrypointVisitor(t2, options) {
25840
25928
  });
25841
25929
  return usesState;
25842
25930
  };
25843
- if (process.env.FICT_DEBUG_ALIAS) {
25844
- console.log("[fict] alias check state vars", Array.from(stateVars));
25845
- }
25931
+ debugLog("alias", "state vars", Array.from(stateVars));
25846
25932
  path.traverse({
25847
25933
  Function: {
25848
25934
  enter() {
@@ -25855,9 +25941,7 @@ function createHIREntrypointVisitor(t2, options) {
25855
25941
  VariableDeclarator(varPath) {
25856
25942
  const aliasSet = currentAliasSet();
25857
25943
  if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
25858
- if (process.env.FICT_DEBUG_ALIAS) {
25859
- console.log("[fict] alias add from decl", varPath.node.id.name);
25860
- }
25944
+ debugLog("alias", "add from decl", varPath.node.id.name);
25861
25945
  aliasSet.add(varPath.node.id.name);
25862
25946
  }
25863
25947
  },
@@ -25868,16 +25952,12 @@ function createHIREntrypointVisitor(t2, options) {
25868
25952
  const targetName = assignPath.node.left.name;
25869
25953
  const rightPath = assignPath.get("right");
25870
25954
  if (rhsUsesState(rightPath)) {
25871
- if (process.env.FICT_DEBUG_ALIAS) {
25872
- console.log("[fict] alias add from assign", targetName);
25873
- }
25955
+ debugLog("alias", "add from assign", targetName);
25874
25956
  aliasSet.add(targetName);
25875
25957
  return;
25876
25958
  }
25877
25959
  if (aliasSet.has(targetName)) {
25878
- if (process.env.FICT_DEBUG_ALIAS) {
25879
- console.log("[fict] alias reassignment detected", targetName);
25880
- }
25960
+ debugLog("alias", "reassignment detected", targetName);
25881
25961
  throw assignPath.buildCodeFrameError(
25882
25962
  `Alias reassignment is not supported for "${targetName}"`
25883
25963
  );