@fictjs/compiler 0.0.5 → 0.0.8

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 +227 -96
  2. package/dist/index.js +227 -96
  3. package/package.json +9 -3
package/dist/index.cjs CHANGED
@@ -15991,6 +15991,75 @@ function debugEnabled(flag) {
15991
15991
  return parts.includes(normalized) || parts.includes("all");
15992
15992
  }
15993
15993
 
15994
+ // src/utils.ts
15995
+ function isStateCall(node, t2) {
15996
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
15997
+ }
15998
+ function isEffectCall(node, t2) {
15999
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
16000
+ }
16001
+ function getRootIdentifier(expr, t2) {
16002
+ if (t2.isIdentifier(expr)) {
16003
+ return expr;
16004
+ }
16005
+ if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
16006
+ return getRootIdentifier(expr.object, t2);
16007
+ }
16008
+ if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
16009
+ return getRootIdentifier(expr.object, t2);
16010
+ }
16011
+ if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
16012
+ return getRootIdentifier(expr.callee, t2);
16013
+ }
16014
+ if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
16015
+ return getRootIdentifier(expr.callee, t2);
16016
+ }
16017
+ if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
16018
+ return getRootIdentifier(expr.expression, t2);
16019
+ }
16020
+ if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
16021
+ return getRootIdentifier(expr.expression, t2);
16022
+ }
16023
+ return null;
16024
+ }
16025
+
16026
+ // src/fine-grained-dom.ts
16027
+ function normalizeDependencyKey(name) {
16028
+ return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
16029
+ }
16030
+ function applyRegionMetadata(state, options) {
16031
+ if (!options.region) return;
16032
+ const region = options.region;
16033
+ state.regionMetadata = region;
16034
+ if (region.dependencies.size > 0) {
16035
+ state.identifierOverrides = state.identifierOverrides ?? {};
16036
+ const dependencyGetter = options.dependencyGetter ?? null;
16037
+ if (!dependencyGetter) {
16038
+ return;
16039
+ }
16040
+ for (const dep of region.dependencies) {
16041
+ const key = normalizeDependencyKey(dep);
16042
+ state.identifierOverrides[key] = () => dependencyGetter(dep);
16043
+ const base = key.split(".")[0];
16044
+ if (base && !state.identifierOverrides[base]) {
16045
+ state.identifierOverrides[base] = () => dependencyGetter(base);
16046
+ }
16047
+ }
16048
+ }
16049
+ }
16050
+ function shouldMemoizeRegion(region) {
16051
+ if (region.dependencies.size > 0) {
16052
+ return true;
16053
+ }
16054
+ if (region.hasControlFlow) {
16055
+ return true;
16056
+ }
16057
+ if (region.hasReactiveWrites) {
16058
+ return true;
16059
+ }
16060
+ return false;
16061
+ }
16062
+
15994
16063
  // src/ir/ssa.ts
15995
16064
  function analyzeCFG(blocks) {
15996
16065
  const predecessors = computePredecessors(blocks);
@@ -16481,7 +16550,7 @@ function collectExprReads(expr, into, paths, bound = /* @__PURE__ */ new Set(),
16481
16550
  }
16482
16551
  return;
16483
16552
  case "CallExpression": {
16484
- const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect");
16553
+ const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
16485
16554
  if (!isMacroCallee) {
16486
16555
  collectExprReads(expr.callee, into, paths, bound);
16487
16556
  }
@@ -18849,6 +18918,7 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18849
18918
  }
18850
18919
  const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
18851
18920
  const uniqueOutputNames = [...new Set(outputNames)];
18921
+ const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
18852
18922
  if (debugEnabled("region")) {
18853
18923
  console.log("Region memo", region.id, {
18854
18924
  instructions: region.instructions.map((instr) => instr.kind),
@@ -18905,10 +18975,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18905
18975
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
18906
18976
  );
18907
18977
  const isAccessorOutput = (name) => ctx.signalVars?.has(name) || ctx.memoVars?.has(name) || ctx.aliasVars?.has(name) || ctx.storeVars?.has(name);
18908
- const getterOutputs = uniqueOutputNames.filter(
18978
+ const getterOutputs = bindableOutputs.filter(
18909
18979
  (name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
18910
18980
  );
18911
- const directOutputs = uniqueOutputNames.filter((name) => !getterOutputs.includes(name));
18981
+ const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
18912
18982
  if (debugEnabled("region")) {
18913
18983
  console.log("Region debug", {
18914
18984
  id: region.id,
@@ -19284,17 +19354,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19284
19354
  const needsMutable = ctx.mutatedVars?.has(baseName2) ?? false;
19285
19355
  const isExternalAlias = declKind === "const" && instr.value.kind === "Identifier" && !(ctx.scopes?.byName?.has(deSSAVarName(instr.value.name)) ?? false);
19286
19356
  const fallbackDecl = !treatAsTracked && (!dependsOnTracked2 || isDestructuringTemp) ? declKind === "const" && (needsMutable || isExternalAlias) ? "let" : declKind : normalizedDecl;
19287
- const isAliasOfTracked = !isShadowDeclaration && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp;
19288
- if (isAliasOfTracked) {
19289
- declaredVars.add(baseName2);
19290
- const sourceIdent = instr.value;
19291
- return t2.variableDeclaration(fallbackDecl, [
19292
- t2.variableDeclarator(
19293
- t2.identifier(baseName2),
19294
- t2.callExpression(t2.identifier(deSSAVarName(sourceIdent.name)), [])
19295
- )
19296
- ]);
19297
- }
19298
19357
  declaredVars.add(baseName2);
19299
19358
  if (treatAsTracked && !isDestructuringTemp) {
19300
19359
  if (isStateCall2) {
@@ -19303,6 +19362,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19303
19362
  ]);
19304
19363
  }
19305
19364
  if (dependsOnTracked2) {
19365
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19366
+ aliasVars.add(baseName2);
19367
+ }
19306
19368
  const derivedExpr = lowerAssignedValue(true);
19307
19369
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19308
19370
  return t2.variableDeclaration(normalizedDecl, [
@@ -19327,6 +19389,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19327
19389
  }
19328
19390
  }
19329
19391
  if (dependsOnTracked2 && !isDestructuringTemp) {
19392
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19393
+ aliasVars.add(baseName2);
19394
+ }
19330
19395
  const derivedExpr = lowerAssignedValue(true);
19331
19396
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19332
19397
  return t2.variableDeclaration(normalizedDecl, [
@@ -19361,17 +19426,35 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19361
19426
  t2.callExpression(t2.identifier(baseName2), [lowerAssignedValue(true)])
19362
19427
  );
19363
19428
  }
19364
- if (instr.value.kind === "Identifier" && !isDestructuringTemp) {
19365
- const source = deSSAVarName(instr.value.name);
19366
- if (ctx.trackedVars.has(source) && !declaredVars.has(baseName2)) {
19367
- return t2.variableDeclaration(declKind ?? "const", [
19368
- t2.variableDeclarator(t2.identifier(baseName2), t2.callExpression(t2.identifier(source), []))
19369
- ]);
19370
- }
19371
- }
19372
19429
  if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
19373
19430
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19374
19431
  }
19432
+ if (dependsOnTracked2 && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
19433
+ const derivedExpr = lowerAssignedValue(true);
19434
+ aliasVars.add(baseName2);
19435
+ if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19436
+ return t2.expressionStatement(
19437
+ t2.assignmentExpression("=", t2.identifier(baseName2), derivedExpr)
19438
+ );
19439
+ }
19440
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19441
+ if (ctx.noMemo) {
19442
+ return t2.expressionStatement(
19443
+ t2.assignmentExpression(
19444
+ "=",
19445
+ t2.identifier(baseName2),
19446
+ t2.arrowFunctionExpression([], derivedExpr)
19447
+ )
19448
+ );
19449
+ }
19450
+ return t2.expressionStatement(
19451
+ t2.assignmentExpression(
19452
+ "=",
19453
+ t2.identifier(baseName2),
19454
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19455
+ )
19456
+ );
19457
+ }
19375
19458
  if (declaredVars.has(baseName2)) {
19376
19459
  if (aliasVars.has(baseName2)) {
19377
19460
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
@@ -19769,75 +19852,6 @@ function deSSAJSXChild(child, t2) {
19769
19852
  return child;
19770
19853
  }
19771
19854
 
19772
- // src/utils.ts
19773
- function isStateCall(node, t2) {
19774
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
19775
- }
19776
- function isEffectCall(node, t2) {
19777
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
19778
- }
19779
- function getRootIdentifier(expr, t2) {
19780
- if (t2.isIdentifier(expr)) {
19781
- return expr;
19782
- }
19783
- if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
19784
- return getRootIdentifier(expr.object, t2);
19785
- }
19786
- if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
19787
- return getRootIdentifier(expr.object, t2);
19788
- }
19789
- if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
19790
- return getRootIdentifier(expr.callee, t2);
19791
- }
19792
- if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
19793
- return getRootIdentifier(expr.callee, t2);
19794
- }
19795
- if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
19796
- return getRootIdentifier(expr.expression, t2);
19797
- }
19798
- if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
19799
- return getRootIdentifier(expr.expression, t2);
19800
- }
19801
- return null;
19802
- }
19803
-
19804
- // src/fine-grained-dom.ts
19805
- function normalizeDependencyKey(name) {
19806
- return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
19807
- }
19808
- function applyRegionMetadata(state, options) {
19809
- if (!options.region) return;
19810
- const region = options.region;
19811
- state.regionMetadata = region;
19812
- if (region.dependencies.size > 0) {
19813
- state.identifierOverrides = state.identifierOverrides ?? {};
19814
- const dependencyGetter = options.dependencyGetter ?? null;
19815
- if (!dependencyGetter) {
19816
- return;
19817
- }
19818
- for (const dep of region.dependencies) {
19819
- const key = normalizeDependencyKey(dep);
19820
- state.identifierOverrides[key] = () => dependencyGetter(dep);
19821
- const base = key.split(".")[0];
19822
- if (base && !state.identifierOverrides[base]) {
19823
- state.identifierOverrides[base] = () => dependencyGetter(base);
19824
- }
19825
- }
19826
- }
19827
- }
19828
- function shouldMemoizeRegion(region) {
19829
- if (region.dependencies.size > 0) {
19830
- return true;
19831
- }
19832
- if (region.hasControlFlow) {
19833
- return true;
19834
- }
19835
- if (region.hasReactiveWrites) {
19836
- return true;
19837
- }
19838
- return false;
19839
- }
19840
-
19841
19855
  // src/ir/codegen.ts
19842
19856
  var HOOK_SLOT_BASE = 1e3;
19843
19857
  var HOOK_NAME_PREFIX = "use";
@@ -21995,6 +22009,8 @@ function getDependencyPathFromNode(node, t2) {
21995
22009
  if (node.computed) {
21996
22010
  if (t2.isStringLiteral(property) || t2.isNumericLiteral(property)) {
21997
22011
  propName = String(property.value);
22012
+ } else {
22013
+ return objectPath;
21998
22014
  }
21999
22015
  } else if (t2.isIdentifier(property)) {
22000
22016
  propName = property.name;
@@ -22111,10 +22127,12 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
22111
22127
  return names;
22112
22128
  };
22113
22129
  if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
22130
+ const propertyNode = node.property;
22131
+ const isDynamicComputed = (node.computed ?? false) && !t2.isStringLiteral(propertyNode) && !t2.isNumericLiteral(propertyNode);
22114
22132
  const path = getDependencyPathFromNode(node, t2);
22115
22133
  const normalized = path ? normalizeDependencyKey2(path) : null;
22116
22134
  const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
22117
- if (override && !isCallTarget) {
22135
+ if (override && !isCallTarget && !isDynamicComputed) {
22118
22136
  const replacement = override();
22119
22137
  Object.assign(node, replacement);
22120
22138
  return;
@@ -23591,8 +23609,11 @@ function lowerHIRWithRegions(program, t2, options) {
23591
23609
  for (const stmt of originalBody) {
23592
23610
  if (t2.isVariableDeclaration(stmt)) {
23593
23611
  for (const decl of stmt.declarations) {
23594
- if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && decl.init.callee.name === "$state") {
23612
+ if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && (decl.init.callee.name === "$state" || decl.init.callee.name === "$store")) {
23595
23613
  ctx.trackedVars.add(decl.id.name);
23614
+ if (decl.init.callee.name === "$store") {
23615
+ ctx.storeVars?.add(decl.id.name);
23616
+ }
23596
23617
  }
23597
23618
  }
23598
23619
  }
@@ -24634,9 +24655,22 @@ function createHIREntrypointVisitor(t2, options) {
24634
24655
  Program: {
24635
24656
  exit(path) {
24636
24657
  const fileName = path.hub?.file?.opts?.filename || "<unknown>";
24658
+ const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
24659
+ const getFunctionName = (fnPath) => {
24660
+ return fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
24661
+ };
24662
+ const isComponentDefinition = (fnPath) => {
24663
+ const name = getFunctionName(fnPath);
24664
+ return name && isComponentName(name) || functionHasJSX(fnPath);
24665
+ };
24666
+ const isHookDefinition = (fnPath) => {
24667
+ const name = getFunctionName(fnPath);
24668
+ return isHookName2(name);
24669
+ };
24670
+ const isComponentOrHookDefinition = (fnPath) => isComponentDefinition(fnPath) || isHookDefinition(fnPath);
24637
24671
  const isComponentLike = (fnPath) => {
24638
- const name = fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
24639
- return name && isComponentName(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24672
+ const name = getFunctionName(fnPath);
24673
+ return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24640
24674
  };
24641
24675
  const memoHasSideEffects = (fn) => {
24642
24676
  const checkNode = (node) => {
@@ -24763,6 +24797,12 @@ function createHIREntrypointVisitor(t2, options) {
24763
24797
  "Destructuring $state is not supported. Use a simple identifier."
24764
24798
  );
24765
24799
  }
24800
+ const ownerComponent = varPath.getFunctionParent();
24801
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
24802
+ throw varPath.buildCodeFrameError(
24803
+ "$state() must be declared inside a component or hook function body"
24804
+ );
24805
+ }
24766
24806
  stateVars.add(varPath.node.id.name);
24767
24807
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
24768
24808
  throw varPath.buildCodeFrameError(
@@ -24809,6 +24849,12 @@ function createHIREntrypointVisitor(t2, options) {
24809
24849
  },
24810
24850
  CallExpression(callPath) {
24811
24851
  if (isStateCall(callPath.node, t2)) {
24852
+ const ownerComponent = callPath.getFunctionParent();
24853
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
24854
+ throw callPath.buildCodeFrameError(
24855
+ "$state() must be declared inside a component or hook function body"
24856
+ );
24857
+ }
24812
24858
  if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
24813
24859
  throw callPath.buildCodeFrameError(
24814
24860
  "$state() cannot be declared inside loops or conditionals"
@@ -24846,6 +24892,27 @@ function createHIREntrypointVisitor(t2, options) {
24846
24892
  fileName
24847
24893
  );
24848
24894
  }
24895
+ if (calleeId && isHookName2(calleeId)) {
24896
+ const binding = callPath.scope.getBinding(calleeId);
24897
+ const bindingPath = binding?.path;
24898
+ const bindingIsHook = !bindingPath && isHookName2(calleeId) || bindingPath?.isImportSpecifier() || bindingPath?.isImportDefaultSpecifier() || bindingPath?.isFunctionDeclaration() && isHookDefinition(bindingPath) || bindingPath?.isVariableDeclarator() && (() => {
24899
+ const init = bindingPath.get?.("init");
24900
+ return init ? isHookDefinition(init) : false;
24901
+ })();
24902
+ if (bindingIsHook) {
24903
+ const ownerFunction = callPath.getFunctionParent();
24904
+ if (!ownerFunction || !isComponentOrHookDefinition(ownerFunction)) {
24905
+ throw callPath.buildCodeFrameError(
24906
+ `${calleeId}() must be called inside a component or hook (useX)`
24907
+ );
24908
+ }
24909
+ if (isInsideLoop(callPath) || isInsideConditional(callPath) || isInsideNestedFunction(callPath)) {
24910
+ throw callPath.buildCodeFrameError(
24911
+ `${calleeId}() must be called at the top level of a component or hook (no loops/conditions/nested functions)`
24912
+ );
24913
+ }
24914
+ }
24915
+ }
24849
24916
  const allowedStateCallees = /* @__PURE__ */ new Set([
24850
24917
  "$effect",
24851
24918
  "$memo",
@@ -24880,6 +24947,68 @@ function createHIREntrypointVisitor(t2, options) {
24880
24947
  }
24881
24948
  }
24882
24949
  });
24950
+ const aliasStack = [/* @__PURE__ */ new Set()];
24951
+ const currentAliasSet = () => aliasStack[aliasStack.length - 1];
24952
+ const rhsUsesState = (exprPath) => {
24953
+ if (!exprPath) return false;
24954
+ if (exprPath.isIdentifier() && t2.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
24955
+ return true;
24956
+ }
24957
+ let usesState = false;
24958
+ exprPath.traverse({
24959
+ Identifier(idPath) {
24960
+ if (stateVars.has(idPath.node.name)) {
24961
+ usesState = true;
24962
+ idPath.stop();
24963
+ }
24964
+ }
24965
+ });
24966
+ return usesState;
24967
+ };
24968
+ if (process.env.FICT_DEBUG_ALIAS) {
24969
+ console.log("[fict] alias check state vars", Array.from(stateVars));
24970
+ }
24971
+ path.traverse({
24972
+ Function: {
24973
+ enter() {
24974
+ aliasStack.push(/* @__PURE__ */ new Set());
24975
+ },
24976
+ exit() {
24977
+ aliasStack.pop();
24978
+ }
24979
+ },
24980
+ VariableDeclarator(varPath) {
24981
+ const aliasSet = currentAliasSet();
24982
+ if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
24983
+ if (process.env.FICT_DEBUG_ALIAS) {
24984
+ console.log("[fict] alias add from decl", varPath.node.id.name);
24985
+ }
24986
+ aliasSet.add(varPath.node.id.name);
24987
+ }
24988
+ },
24989
+ AssignmentExpression(assignPath) {
24990
+ const aliasSet = currentAliasSet();
24991
+ if (!aliasSet) return;
24992
+ if (!t2.isIdentifier(assignPath.node.left)) return;
24993
+ const targetName = assignPath.node.left.name;
24994
+ const rightPath = assignPath.get("right");
24995
+ if (rhsUsesState(rightPath)) {
24996
+ if (process.env.FICT_DEBUG_ALIAS) {
24997
+ console.log("[fict] alias add from assign", targetName);
24998
+ }
24999
+ aliasSet.add(targetName);
25000
+ return;
25001
+ }
25002
+ if (aliasSet.has(targetName)) {
25003
+ if (process.env.FICT_DEBUG_ALIAS) {
25004
+ console.log("[fict] alias reassignment detected", targetName);
25005
+ }
25006
+ throw assignPath.buildCodeFrameError(
25007
+ `Alias reassignment is not supported for "${targetName}"`
25008
+ );
25009
+ }
25010
+ }
25011
+ });
24883
25012
  if (derivedVars.size > 0) {
24884
25013
  path.traverse({
24885
25014
  AssignmentExpression(assignPath) {
@@ -24918,7 +25047,9 @@ function createHIREntrypointVisitor(t2, options) {
24918
25047
  const lowered = lowerHIRWithRegions(hir, t2, options);
24919
25048
  path.node.body = lowered.program.body;
24920
25049
  path.node.directives = lowered.program.directives;
24921
- path.scope.crawl();
25050
+ if (!process.env.FICT_SKIP_SCOPE_CRAWL) {
25051
+ path.scope.crawl();
25052
+ }
24922
25053
  stripMacroImports(path, t2);
24923
25054
  }
24924
25055
  }
package/dist/index.js CHANGED
@@ -15979,6 +15979,75 @@ function debugEnabled(flag) {
15979
15979
  return parts.includes(normalized) || parts.includes("all");
15980
15980
  }
15981
15981
 
15982
+ // src/utils.ts
15983
+ function isStateCall(node, t2) {
15984
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
15985
+ }
15986
+ function isEffectCall(node, t2) {
15987
+ return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
15988
+ }
15989
+ function getRootIdentifier(expr, t2) {
15990
+ if (t2.isIdentifier(expr)) {
15991
+ return expr;
15992
+ }
15993
+ if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
15994
+ return getRootIdentifier(expr.object, t2);
15995
+ }
15996
+ if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
15997
+ return getRootIdentifier(expr.object, t2);
15998
+ }
15999
+ if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
16000
+ return getRootIdentifier(expr.callee, t2);
16001
+ }
16002
+ if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
16003
+ return getRootIdentifier(expr.callee, t2);
16004
+ }
16005
+ if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
16006
+ return getRootIdentifier(expr.expression, t2);
16007
+ }
16008
+ if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
16009
+ return getRootIdentifier(expr.expression, t2);
16010
+ }
16011
+ return null;
16012
+ }
16013
+
16014
+ // src/fine-grained-dom.ts
16015
+ function normalizeDependencyKey(name) {
16016
+ return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
16017
+ }
16018
+ function applyRegionMetadata(state, options) {
16019
+ if (!options.region) return;
16020
+ const region = options.region;
16021
+ state.regionMetadata = region;
16022
+ if (region.dependencies.size > 0) {
16023
+ state.identifierOverrides = state.identifierOverrides ?? {};
16024
+ const dependencyGetter = options.dependencyGetter ?? null;
16025
+ if (!dependencyGetter) {
16026
+ return;
16027
+ }
16028
+ for (const dep of region.dependencies) {
16029
+ const key = normalizeDependencyKey(dep);
16030
+ state.identifierOverrides[key] = () => dependencyGetter(dep);
16031
+ const base = key.split(".")[0];
16032
+ if (base && !state.identifierOverrides[base]) {
16033
+ state.identifierOverrides[base] = () => dependencyGetter(base);
16034
+ }
16035
+ }
16036
+ }
16037
+ }
16038
+ function shouldMemoizeRegion(region) {
16039
+ if (region.dependencies.size > 0) {
16040
+ return true;
16041
+ }
16042
+ if (region.hasControlFlow) {
16043
+ return true;
16044
+ }
16045
+ if (region.hasReactiveWrites) {
16046
+ return true;
16047
+ }
16048
+ return false;
16049
+ }
16050
+
15982
16051
  // src/ir/ssa.ts
15983
16052
  function analyzeCFG(blocks) {
15984
16053
  const predecessors = computePredecessors(blocks);
@@ -16469,7 +16538,7 @@ function collectExprReads(expr, into, paths, bound = /* @__PURE__ */ new Set(),
16469
16538
  }
16470
16539
  return;
16471
16540
  case "CallExpression": {
16472
- const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect");
16541
+ const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
16473
16542
  if (!isMacroCallee) {
16474
16543
  collectExprReads(expr.callee, into, paths, bound);
16475
16544
  }
@@ -18837,6 +18906,7 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18837
18906
  }
18838
18907
  const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
18839
18908
  const uniqueOutputNames = [...new Set(outputNames)];
18909
+ const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
18840
18910
  if (debugEnabled("region")) {
18841
18911
  console.log("Region memo", region.id, {
18842
18912
  instructions: region.instructions.map((instr) => instr.kind),
@@ -18893,10 +18963,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
18893
18963
  t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
18894
18964
  );
18895
18965
  const isAccessorOutput = (name) => ctx.signalVars?.has(name) || ctx.memoVars?.has(name) || ctx.aliasVars?.has(name) || ctx.storeVars?.has(name);
18896
- const getterOutputs = uniqueOutputNames.filter(
18966
+ const getterOutputs = bindableOutputs.filter(
18897
18967
  (name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
18898
18968
  );
18899
- const directOutputs = uniqueOutputNames.filter((name) => !getterOutputs.includes(name));
18969
+ const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
18900
18970
  if (debugEnabled("region")) {
18901
18971
  console.log("Region debug", {
18902
18972
  id: region.id,
@@ -19272,17 +19342,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19272
19342
  const needsMutable = ctx.mutatedVars?.has(baseName2) ?? false;
19273
19343
  const isExternalAlias = declKind === "const" && instr.value.kind === "Identifier" && !(ctx.scopes?.byName?.has(deSSAVarName(instr.value.name)) ?? false);
19274
19344
  const fallbackDecl = !treatAsTracked && (!dependsOnTracked2 || isDestructuringTemp) ? declKind === "const" && (needsMutable || isExternalAlias) ? "let" : declKind : normalizedDecl;
19275
- const isAliasOfTracked = !isShadowDeclaration && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp;
19276
- if (isAliasOfTracked) {
19277
- declaredVars.add(baseName2);
19278
- const sourceIdent = instr.value;
19279
- return t2.variableDeclaration(fallbackDecl, [
19280
- t2.variableDeclarator(
19281
- t2.identifier(baseName2),
19282
- t2.callExpression(t2.identifier(deSSAVarName(sourceIdent.name)), [])
19283
- )
19284
- ]);
19285
- }
19286
19345
  declaredVars.add(baseName2);
19287
19346
  if (treatAsTracked && !isDestructuringTemp) {
19288
19347
  if (isStateCall2) {
@@ -19291,6 +19350,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19291
19350
  ]);
19292
19351
  }
19293
19352
  if (dependsOnTracked2) {
19353
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19354
+ aliasVars.add(baseName2);
19355
+ }
19294
19356
  const derivedExpr = lowerAssignedValue(true);
19295
19357
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19296
19358
  return t2.variableDeclaration(normalizedDecl, [
@@ -19315,6 +19377,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19315
19377
  }
19316
19378
  }
19317
19379
  if (dependsOnTracked2 && !isDestructuringTemp) {
19380
+ if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
19381
+ aliasVars.add(baseName2);
19382
+ }
19318
19383
  const derivedExpr = lowerAssignedValue(true);
19319
19384
  if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19320
19385
  return t2.variableDeclaration(normalizedDecl, [
@@ -19349,17 +19414,35 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
19349
19414
  t2.callExpression(t2.identifier(baseName2), [lowerAssignedValue(true)])
19350
19415
  );
19351
19416
  }
19352
- if (instr.value.kind === "Identifier" && !isDestructuringTemp) {
19353
- const source = deSSAVarName(instr.value.name);
19354
- if (ctx.trackedVars.has(source) && !declaredVars.has(baseName2)) {
19355
- return t2.variableDeclaration(declKind ?? "const", [
19356
- t2.variableDeclarator(t2.identifier(baseName2), t2.callExpression(t2.identifier(source), []))
19357
- ]);
19358
- }
19359
- }
19360
19417
  if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
19361
19418
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
19362
19419
  }
19420
+ if (dependsOnTracked2 && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
19421
+ const derivedExpr = lowerAssignedValue(true);
19422
+ aliasVars.add(baseName2);
19423
+ if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
19424
+ return t2.expressionStatement(
19425
+ t2.assignmentExpression("=", t2.identifier(baseName2), derivedExpr)
19426
+ );
19427
+ }
19428
+ if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
19429
+ if (ctx.noMemo) {
19430
+ return t2.expressionStatement(
19431
+ t2.assignmentExpression(
19432
+ "=",
19433
+ t2.identifier(baseName2),
19434
+ t2.arrowFunctionExpression([], derivedExpr)
19435
+ )
19436
+ );
19437
+ }
19438
+ return t2.expressionStatement(
19439
+ t2.assignmentExpression(
19440
+ "=",
19441
+ t2.identifier(baseName2),
19442
+ isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
19443
+ )
19444
+ );
19445
+ }
19363
19446
  if (declaredVars.has(baseName2)) {
19364
19447
  if (aliasVars.has(baseName2)) {
19365
19448
  throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
@@ -19757,75 +19840,6 @@ function deSSAJSXChild(child, t2) {
19757
19840
  return child;
19758
19841
  }
19759
19842
 
19760
- // src/utils.ts
19761
- function isStateCall(node, t2) {
19762
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
19763
- }
19764
- function isEffectCall(node, t2) {
19765
- return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
19766
- }
19767
- function getRootIdentifier(expr, t2) {
19768
- if (t2.isIdentifier(expr)) {
19769
- return expr;
19770
- }
19771
- if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
19772
- return getRootIdentifier(expr.object, t2);
19773
- }
19774
- if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
19775
- return getRootIdentifier(expr.object, t2);
19776
- }
19777
- if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
19778
- return getRootIdentifier(expr.callee, t2);
19779
- }
19780
- if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
19781
- return getRootIdentifier(expr.callee, t2);
19782
- }
19783
- if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
19784
- return getRootIdentifier(expr.expression, t2);
19785
- }
19786
- if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
19787
- return getRootIdentifier(expr.expression, t2);
19788
- }
19789
- return null;
19790
- }
19791
-
19792
- // src/fine-grained-dom.ts
19793
- function normalizeDependencyKey(name) {
19794
- return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
19795
- }
19796
- function applyRegionMetadata(state, options) {
19797
- if (!options.region) return;
19798
- const region = options.region;
19799
- state.regionMetadata = region;
19800
- if (region.dependencies.size > 0) {
19801
- state.identifierOverrides = state.identifierOverrides ?? {};
19802
- const dependencyGetter = options.dependencyGetter ?? null;
19803
- if (!dependencyGetter) {
19804
- return;
19805
- }
19806
- for (const dep of region.dependencies) {
19807
- const key = normalizeDependencyKey(dep);
19808
- state.identifierOverrides[key] = () => dependencyGetter(dep);
19809
- const base = key.split(".")[0];
19810
- if (base && !state.identifierOverrides[base]) {
19811
- state.identifierOverrides[base] = () => dependencyGetter(base);
19812
- }
19813
- }
19814
- }
19815
- }
19816
- function shouldMemoizeRegion(region) {
19817
- if (region.dependencies.size > 0) {
19818
- return true;
19819
- }
19820
- if (region.hasControlFlow) {
19821
- return true;
19822
- }
19823
- if (region.hasReactiveWrites) {
19824
- return true;
19825
- }
19826
- return false;
19827
- }
19828
-
19829
19843
  // src/ir/codegen.ts
19830
19844
  var HOOK_SLOT_BASE = 1e3;
19831
19845
  var HOOK_NAME_PREFIX = "use";
@@ -21983,6 +21997,8 @@ function getDependencyPathFromNode(node, t2) {
21983
21997
  if (node.computed) {
21984
21998
  if (t2.isStringLiteral(property) || t2.isNumericLiteral(property)) {
21985
21999
  propName = String(property.value);
22000
+ } else {
22001
+ return objectPath;
21986
22002
  }
21987
22003
  } else if (t2.isIdentifier(property)) {
21988
22004
  propName = property.name;
@@ -22099,10 +22115,12 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
22099
22115
  return names;
22100
22116
  };
22101
22117
  if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
22118
+ const propertyNode = node.property;
22119
+ const isDynamicComputed = (node.computed ?? false) && !t2.isStringLiteral(propertyNode) && !t2.isNumericLiteral(propertyNode);
22102
22120
  const path = getDependencyPathFromNode(node, t2);
22103
22121
  const normalized = path ? normalizeDependencyKey2(path) : null;
22104
22122
  const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
22105
- if (override && !isCallTarget) {
22123
+ if (override && !isCallTarget && !isDynamicComputed) {
22106
22124
  const replacement = override();
22107
22125
  Object.assign(node, replacement);
22108
22126
  return;
@@ -23579,8 +23597,11 @@ function lowerHIRWithRegions(program, t2, options) {
23579
23597
  for (const stmt of originalBody) {
23580
23598
  if (t2.isVariableDeclaration(stmt)) {
23581
23599
  for (const decl of stmt.declarations) {
23582
- if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && decl.init.callee.name === "$state") {
23600
+ if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && (decl.init.callee.name === "$state" || decl.init.callee.name === "$store")) {
23583
23601
  ctx.trackedVars.add(decl.id.name);
23602
+ if (decl.init.callee.name === "$store") {
23603
+ ctx.storeVars?.add(decl.id.name);
23604
+ }
23584
23605
  }
23585
23606
  }
23586
23607
  }
@@ -24622,9 +24643,22 @@ function createHIREntrypointVisitor(t2, options) {
24622
24643
  Program: {
24623
24644
  exit(path) {
24624
24645
  const fileName = path.hub?.file?.opts?.filename || "<unknown>";
24646
+ const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
24647
+ const getFunctionName = (fnPath) => {
24648
+ return fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
24649
+ };
24650
+ const isComponentDefinition = (fnPath) => {
24651
+ const name = getFunctionName(fnPath);
24652
+ return name && isComponentName(name) || functionHasJSX(fnPath);
24653
+ };
24654
+ const isHookDefinition = (fnPath) => {
24655
+ const name = getFunctionName(fnPath);
24656
+ return isHookName2(name);
24657
+ };
24658
+ const isComponentOrHookDefinition = (fnPath) => isComponentDefinition(fnPath) || isHookDefinition(fnPath);
24625
24659
  const isComponentLike = (fnPath) => {
24626
- const name = fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
24627
- return name && isComponentName(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24660
+ const name = getFunctionName(fnPath);
24661
+ return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
24628
24662
  };
24629
24663
  const memoHasSideEffects = (fn) => {
24630
24664
  const checkNode = (node) => {
@@ -24751,6 +24785,12 @@ function createHIREntrypointVisitor(t2, options) {
24751
24785
  "Destructuring $state is not supported. Use a simple identifier."
24752
24786
  );
24753
24787
  }
24788
+ const ownerComponent = varPath.getFunctionParent();
24789
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
24790
+ throw varPath.buildCodeFrameError(
24791
+ "$state() must be declared inside a component or hook function body"
24792
+ );
24793
+ }
24754
24794
  stateVars.add(varPath.node.id.name);
24755
24795
  if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
24756
24796
  throw varPath.buildCodeFrameError(
@@ -24797,6 +24837,12 @@ function createHIREntrypointVisitor(t2, options) {
24797
24837
  },
24798
24838
  CallExpression(callPath) {
24799
24839
  if (isStateCall(callPath.node, t2)) {
24840
+ const ownerComponent = callPath.getFunctionParent();
24841
+ if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
24842
+ throw callPath.buildCodeFrameError(
24843
+ "$state() must be declared inside a component or hook function body"
24844
+ );
24845
+ }
24800
24846
  if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
24801
24847
  throw callPath.buildCodeFrameError(
24802
24848
  "$state() cannot be declared inside loops or conditionals"
@@ -24834,6 +24880,27 @@ function createHIREntrypointVisitor(t2, options) {
24834
24880
  fileName
24835
24881
  );
24836
24882
  }
24883
+ if (calleeId && isHookName2(calleeId)) {
24884
+ const binding = callPath.scope.getBinding(calleeId);
24885
+ const bindingPath = binding?.path;
24886
+ const bindingIsHook = !bindingPath && isHookName2(calleeId) || bindingPath?.isImportSpecifier() || bindingPath?.isImportDefaultSpecifier() || bindingPath?.isFunctionDeclaration() && isHookDefinition(bindingPath) || bindingPath?.isVariableDeclarator() && (() => {
24887
+ const init = bindingPath.get?.("init");
24888
+ return init ? isHookDefinition(init) : false;
24889
+ })();
24890
+ if (bindingIsHook) {
24891
+ const ownerFunction = callPath.getFunctionParent();
24892
+ if (!ownerFunction || !isComponentOrHookDefinition(ownerFunction)) {
24893
+ throw callPath.buildCodeFrameError(
24894
+ `${calleeId}() must be called inside a component or hook (useX)`
24895
+ );
24896
+ }
24897
+ if (isInsideLoop(callPath) || isInsideConditional(callPath) || isInsideNestedFunction(callPath)) {
24898
+ throw callPath.buildCodeFrameError(
24899
+ `${calleeId}() must be called at the top level of a component or hook (no loops/conditions/nested functions)`
24900
+ );
24901
+ }
24902
+ }
24903
+ }
24837
24904
  const allowedStateCallees = /* @__PURE__ */ new Set([
24838
24905
  "$effect",
24839
24906
  "$memo",
@@ -24868,6 +24935,68 @@ function createHIREntrypointVisitor(t2, options) {
24868
24935
  }
24869
24936
  }
24870
24937
  });
24938
+ const aliasStack = [/* @__PURE__ */ new Set()];
24939
+ const currentAliasSet = () => aliasStack[aliasStack.length - 1];
24940
+ const rhsUsesState = (exprPath) => {
24941
+ if (!exprPath) return false;
24942
+ if (exprPath.isIdentifier() && t2.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
24943
+ return true;
24944
+ }
24945
+ let usesState = false;
24946
+ exprPath.traverse({
24947
+ Identifier(idPath) {
24948
+ if (stateVars.has(idPath.node.name)) {
24949
+ usesState = true;
24950
+ idPath.stop();
24951
+ }
24952
+ }
24953
+ });
24954
+ return usesState;
24955
+ };
24956
+ if (process.env.FICT_DEBUG_ALIAS) {
24957
+ console.log("[fict] alias check state vars", Array.from(stateVars));
24958
+ }
24959
+ path.traverse({
24960
+ Function: {
24961
+ enter() {
24962
+ aliasStack.push(/* @__PURE__ */ new Set());
24963
+ },
24964
+ exit() {
24965
+ aliasStack.pop();
24966
+ }
24967
+ },
24968
+ VariableDeclarator(varPath) {
24969
+ const aliasSet = currentAliasSet();
24970
+ if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
24971
+ if (process.env.FICT_DEBUG_ALIAS) {
24972
+ console.log("[fict] alias add from decl", varPath.node.id.name);
24973
+ }
24974
+ aliasSet.add(varPath.node.id.name);
24975
+ }
24976
+ },
24977
+ AssignmentExpression(assignPath) {
24978
+ const aliasSet = currentAliasSet();
24979
+ if (!aliasSet) return;
24980
+ if (!t2.isIdentifier(assignPath.node.left)) return;
24981
+ const targetName = assignPath.node.left.name;
24982
+ const rightPath = assignPath.get("right");
24983
+ if (rhsUsesState(rightPath)) {
24984
+ if (process.env.FICT_DEBUG_ALIAS) {
24985
+ console.log("[fict] alias add from assign", targetName);
24986
+ }
24987
+ aliasSet.add(targetName);
24988
+ return;
24989
+ }
24990
+ if (aliasSet.has(targetName)) {
24991
+ if (process.env.FICT_DEBUG_ALIAS) {
24992
+ console.log("[fict] alias reassignment detected", targetName);
24993
+ }
24994
+ throw assignPath.buildCodeFrameError(
24995
+ `Alias reassignment is not supported for "${targetName}"`
24996
+ );
24997
+ }
24998
+ }
24999
+ });
24871
25000
  if (derivedVars.size > 0) {
24872
25001
  path.traverse({
24873
25002
  AssignmentExpression(assignPath) {
@@ -24906,7 +25035,9 @@ function createHIREntrypointVisitor(t2, options) {
24906
25035
  const lowered = lowerHIRWithRegions(hir, t2, options);
24907
25036
  path.node.body = lowered.program.body;
24908
25037
  path.node.directives = lowered.program.directives;
24909
- path.scope.crawl();
25038
+ if (!process.env.FICT_SKIP_SCOPE_CRAWL) {
25039
+ path.scope.crawl();
25040
+ }
24910
25041
  stripMacroImports(path, t2);
24911
25042
  }
24912
25043
  }
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@fictjs/compiler",
3
- "version": "0.0.5",
3
+ "version": "0.0.8",
4
4
  "description": "Babel plugin for Fict Compiler",
5
5
  "publishConfig": {
6
- "access": "public"
6
+ "access": "public",
7
+ "provenance": true
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/fictjs/fict.git",
12
+ "directory": "packages/compiler"
7
13
  },
8
14
  "type": "module",
9
15
  "main": "dist/index.cjs",
@@ -38,7 +44,7 @@
38
44
  "@types/babel__helper-plugin-utils": "^7.10.3",
39
45
  "@types/babel__traverse": "^7.28.0",
40
46
  "tsup": "^8.5.1",
41
- "@fictjs/runtime": "0.0.5"
47
+ "@fictjs/runtime": "0.0.8"
42
48
  },
43
49
  "scripts": {
44
50
  "build": "tsup src/index.ts --format cjs,esm --dts",