@fictjs/compiler 0.9.0 → 0.11.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 +723 -176
  2. package/dist/index.js +723 -176
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -16704,10 +16704,12 @@ function convertJSXMemberExpr(node) {
16704
16704
  };
16705
16705
  }
16706
16706
 
16707
- // src/fine-grained-dom.ts
16707
+ // src/ir/dependency-key.ts
16708
16708
  function normalizeDependencyKey(name) {
16709
- return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
16709
+ return name.split(".").map((part) => getSSABaseName(part)).join(".");
16710
16710
  }
16711
+
16712
+ // src/fine-grained-dom.ts
16711
16713
  function applyRegionMetadata(state, options) {
16712
16714
  if (!options.region) return;
16713
16715
  const region = options.region;
@@ -19927,15 +19929,38 @@ function buildEffectCall(ctx, t4, effectFn, options) {
19927
19929
  }
19928
19930
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.useEffect), args);
19929
19931
  }
19930
- function buildMemoCall(ctx, t4, memoFn, slot) {
19932
+ function buildMemoCall(ctx, t4, memoFn, options) {
19933
+ const slot = options?.slot;
19934
+ const memoOptionsProperties = [];
19935
+ if (options?.name) {
19936
+ memoOptionsProperties.push(
19937
+ t4.objectProperty(t4.identifier("name"), t4.stringLiteral(options.name))
19938
+ );
19939
+ }
19940
+ if (options?.source) {
19941
+ memoOptionsProperties.push(
19942
+ t4.objectProperty(t4.identifier("devToolsSource"), t4.stringLiteral(options.source))
19943
+ );
19944
+ }
19945
+ if (options?.internal) {
19946
+ memoOptionsProperties.push(t4.objectProperty(t4.identifier("internal"), t4.booleanLiteral(true)));
19947
+ }
19948
+ const memoOptions = memoOptionsProperties.length > 0 ? t4.objectExpression(memoOptionsProperties) : null;
19931
19949
  if (ctx.inModule) {
19932
19950
  ctx.helpersUsed.add("memo");
19933
- return t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), [memoFn]);
19951
+ const args2 = [memoFn];
19952
+ if (memoOptions) args2.push(memoOptions);
19953
+ return t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), args2);
19934
19954
  }
19935
19955
  ctx.helpersUsed.add("useMemo");
19936
19956
  ctx.needsCtx = true;
19937
19957
  const args = [t4.identifier("__fictCtx"), memoFn];
19938
- if (slot !== void 0 && slot >= 0) {
19958
+ if (memoOptions) {
19959
+ args.push(memoOptions);
19960
+ if (slot !== void 0 && slot >= 0) {
19961
+ args.push(t4.numericLiteral(slot));
19962
+ }
19963
+ } else if (slot !== void 0 && slot >= 0) {
19939
19964
  args.push(t4.numericLiteral(slot));
19940
19965
  }
19941
19966
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.useMemo), args);
@@ -21196,7 +21221,10 @@ function wrapInMemo(region, t4, declaredVars, ctx, bodyStatementsOverride, outpu
21196
21221
  const returnObj = t4.objectExpression(uniqueOutputNames.map((name) => buildOutputProperty(name)));
21197
21222
  const memoBody = t4.blockStatement([...bodyStatements, t4.returnStatement(returnObj)]);
21198
21223
  const slot = ctx.inModule ? void 0 : reserveHookSlot(ctx);
21199
- const memoCall = buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], memoBody), slot);
21224
+ const memoCall = buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], memoBody), {
21225
+ slot,
21226
+ internal: true
21227
+ });
21200
21228
  const regionVarName = `__region_${region.id}`;
21201
21229
  statements.push(
21202
21230
  t4.variableDeclaration("const", [t4.variableDeclarator(t4.identifier(regionVarName), memoCall)])
@@ -21455,7 +21483,10 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
21455
21483
  ctx,
21456
21484
  t4,
21457
21485
  t4.arrowFunctionExpression([], t4.blockStatement(memoBody)),
21458
- ctx.inModule ? void 0 : reserveHookSlot(ctx)
21486
+ {
21487
+ slot: ctx.inModule ? void 0 : reserveHookSlot(ctx),
21488
+ internal: true
21489
+ }
21459
21490
  );
21460
21491
  statements.push(
21461
21492
  t4.variableDeclaration("const", [t4.variableDeclarator(t4.identifier(regionVarName), memoCall)])
@@ -21568,7 +21599,12 @@ Context: ${location}`
21568
21599
  };
21569
21600
  const buildDerivedMemoCall = (expr) => {
21570
21601
  const slot = !ctx.inModule && inRegionMemo ? reserveHookSlot(ctx) : void 0;
21571
- return buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], expr), slot);
21602
+ const source = ctx.options?.dev !== false && instr.loc ? `${ctx.options?.filename ?? ""}:${instr.loc.start.line}:${instr.loc.start.column}` : void 0;
21603
+ return buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], expr), {
21604
+ slot,
21605
+ name: baseName2,
21606
+ source
21607
+ });
21572
21608
  };
21573
21609
  if (isShadowDeclaration && declKind) {
21574
21610
  ctx.trackedVars.delete(baseName2);
@@ -22930,7 +22966,7 @@ function expressionUsesIdentifier(expr, name, t4) {
22930
22966
  visit(expr);
22931
22967
  return found;
22932
22968
  }
22933
- function extractDelegatedEventData(expr, t4) {
22969
+ function extractDelegatedEventData(expr, t4, options) {
22934
22970
  const isSimpleHandler = t4.isIdentifier(expr) || t4.isMemberExpression(expr);
22935
22971
  if (isSimpleHandler) {
22936
22972
  return { handler: expr };
@@ -22943,6 +22979,9 @@ function extractDelegatedEventData(expr, t4) {
22943
22979
  if (!bodyExpr || !t4.isCallExpression(bodyExpr)) return null;
22944
22980
  if (paramNames.some((name) => expressionUsesIdentifier(bodyExpr, name, t4))) return null;
22945
22981
  if (!t4.isIdentifier(bodyExpr.callee)) return null;
22982
+ if (options?.isKnownHandlerIdentifier && !options.isKnownHandlerIdentifier(bodyExpr.callee.name)) {
22983
+ return null;
22984
+ }
22946
22985
  if (bodyExpr.arguments.length === 0) return null;
22947
22986
  if (bodyExpr.arguments.length > 1) return null;
22948
22987
  const dataArg = bodyExpr.arguments[0];
@@ -23005,6 +23044,10 @@ function extractDelegatedEventDataFromHIR(expr, ctx) {
23005
23044
  return null;
23006
23045
  }
23007
23046
  const handlerName = callee.name;
23047
+ const normalizedHandlerName = deSSAVarName(handlerName);
23048
+ if (!ctx.functionVars?.has(normalizedHandlerName)) {
23049
+ return null;
23050
+ }
23008
23051
  if (ctx.signalVars?.has(handlerName) || ctx.memoVars?.has(handlerName) || ctx.aliasVars?.has(handlerName) || ctx.storeVars?.has(handlerName) || ctx.trackedVars.has(handlerName)) {
23009
23052
  return null;
23010
23053
  }
@@ -23015,7 +23058,10 @@ function extractDelegatedEventDataFromHIR(expr, ctx) {
23015
23058
  if (isTrackedAccessor) {
23016
23059
  return null;
23017
23060
  }
23018
- const paramNames = new Set(expr.params.map((p) => p.name));
23061
+ const paramNames = new Set(expr.params.map((p) => deSSAVarName(p.name)));
23062
+ if (paramNames.has(deSSAVarName(callee.name))) {
23063
+ return null;
23064
+ }
23019
23065
  const dataExpr = bodyExpr.arguments[0];
23020
23066
  if (!dataExpr) {
23021
23067
  return null;
@@ -24156,7 +24202,7 @@ function extractKeyFromMapCallback(callback) {
24156
24202
 
24157
24203
  // src/ir/codegen-overrides.ts
24158
24204
  function normalizeDependencyKey2(name) {
24159
- return name.split(".").map((part) => deSSAVarName(part)).join(".");
24205
+ return normalizeDependencyKey(name);
24160
24206
  }
24161
24207
  function getDependencyPathFromNode(node, t4) {
24162
24208
  if (t4.isIdentifier(node)) {
@@ -24182,8 +24228,8 @@ function getDependencyPathFromNode(node, t4) {
24182
24228
  }
24183
24229
  return null;
24184
24230
  }
24185
- function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parentKey, skipCurrentNode = false) {
24186
- const isCallTarget = parentKey === "callee" && (parentKind === "CallExpression" || parentKind === "OptionalCallExpression");
24231
+ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parentKey, skipCurrentNode = false, allowCallCalleeReplacement = false) {
24232
+ const isCallTarget = !allowCallCalleeReplacement && parentKey === "callee" && (parentKind === "CallExpression" || parentKind === "OptionalCallExpression");
24187
24233
  if (parentKind === "VariableDeclarator" && parentKey === "id") {
24188
24234
  return;
24189
24235
  }
@@ -24255,9 +24301,25 @@ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parent
24255
24301
  }
24256
24302
  }
24257
24303
  if (t4.isBlockStatement(node.body)) {
24258
- replaceIdentifiersWithOverrides(node.body, scopedOverrides, t4, node.type, "body");
24304
+ replaceIdentifiersWithOverrides(
24305
+ node.body,
24306
+ scopedOverrides,
24307
+ t4,
24308
+ node.type,
24309
+ "body",
24310
+ false,
24311
+ allowCallCalleeReplacement
24312
+ );
24259
24313
  } else {
24260
- replaceIdentifiersWithOverrides(node.body, scopedOverrides, t4, node.type, "body");
24314
+ replaceIdentifiersWithOverrides(
24315
+ node.body,
24316
+ scopedOverrides,
24317
+ t4,
24318
+ node.type,
24319
+ "body",
24320
+ false,
24321
+ allowCallCalleeReplacement
24322
+ );
24261
24323
  }
24262
24324
  return;
24263
24325
  }
@@ -24285,12 +24347,21 @@ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parent
24285
24347
  t4,
24286
24348
  node.type,
24287
24349
  key,
24288
- false
24350
+ false,
24351
+ allowCallCalleeReplacement
24289
24352
  );
24290
24353
  }
24291
24354
  }
24292
24355
  } else if (value && typeof value === "object" && "type" in value) {
24293
- replaceIdentifiersWithOverrides(value, overrides, t4, node.type, key);
24356
+ replaceIdentifiersWithOverrides(
24357
+ value,
24358
+ overrides,
24359
+ t4,
24360
+ node.type,
24361
+ key,
24362
+ false,
24363
+ allowCallCalleeReplacement
24364
+ );
24294
24365
  }
24295
24366
  }
24296
24367
  }
@@ -24563,6 +24634,124 @@ function applySelectorHoist(callbackExpr, itemParamName, keyParamName, statement
24563
24634
  }
24564
24635
 
24565
24636
  // src/ir/codegen-list-child.ts
24637
+ function getCallbackBlocks(callback) {
24638
+ if (callback.kind === "FunctionExpression") {
24639
+ return callback.body;
24640
+ }
24641
+ if (callback.kind === "ArrowFunction" && Array.isArray(callback.body)) {
24642
+ return callback.body;
24643
+ }
24644
+ return [];
24645
+ }
24646
+ function collectMapCallbackAliasDeclarations(callback) {
24647
+ const blocks = getCallbackBlocks(callback);
24648
+ if (blocks.length === 0) {
24649
+ return /* @__PURE__ */ new Map();
24650
+ }
24651
+ const paramNames = callback.kind === "ArrowFunction" || callback.kind === "FunctionExpression" ? new Set(callback.params.map((param) => param.name)) : /* @__PURE__ */ new Set();
24652
+ const declarationState = /* @__PURE__ */ new Map();
24653
+ for (const block of blocks) {
24654
+ for (const instr of block.instructions) {
24655
+ if (instr.kind !== "Assign" || instr.target.kind !== "Identifier") {
24656
+ continue;
24657
+ }
24658
+ const name = instr.target.name;
24659
+ if (paramNames.has(name)) continue;
24660
+ const isDeclaration = !!instr.declarationKind;
24661
+ const previous = declarationState.get(name);
24662
+ if (previous) {
24663
+ declarationState.set(name, {
24664
+ declarationCount: previous.declarationCount + (isDeclaration ? 1 : 0),
24665
+ hasNonDeclarationWrite: previous.hasNonDeclarationWrite || !isDeclaration,
24666
+ declarationValue: previous.declarationValue,
24667
+ lastAssignedValue: instr.value
24668
+ });
24669
+ } else {
24670
+ declarationState.set(name, {
24671
+ declarationCount: isDeclaration ? 1 : 0,
24672
+ hasNonDeclarationWrite: !isDeclaration,
24673
+ declarationValue: isDeclaration ? instr.value : null,
24674
+ lastAssignedValue: instr.value
24675
+ });
24676
+ }
24677
+ }
24678
+ }
24679
+ const aliasMap = /* @__PURE__ */ new Map();
24680
+ const effectiveBlocks = blocks.filter(
24681
+ (block) => block.instructions.length > 0 || block.terminator.kind !== "Unreachable"
24682
+ );
24683
+ const isSingleLinearBlock = effectiveBlocks.length === 1 && effectiveBlocks[0].terminator.kind === "Return";
24684
+ for (const [name, state] of declarationState) {
24685
+ if (isSingleLinearBlock) {
24686
+ if (state.declarationCount <= 1) {
24687
+ aliasMap.set(name, state.lastAssignedValue);
24688
+ }
24689
+ continue;
24690
+ }
24691
+ if (state.declarationCount === 1 && !state.hasNonDeclarationWrite && state.declarationValue) {
24692
+ aliasMap.set(name, state.declarationValue);
24693
+ }
24694
+ }
24695
+ return aliasMap;
24696
+ }
24697
+ function resolveMapCallbackKeyExpression(keyExpr, callback) {
24698
+ if (keyExpr.kind !== "Identifier") {
24699
+ return keyExpr;
24700
+ }
24701
+ const aliasMap = collectMapCallbackAliasDeclarations(callback);
24702
+ if (aliasMap.size === 0) {
24703
+ return keyExpr;
24704
+ }
24705
+ let resolved = keyExpr;
24706
+ const seen = /* @__PURE__ */ new Set();
24707
+ while (resolved.kind === "Identifier") {
24708
+ const next = aliasMap.get(resolved.name);
24709
+ if (!next || seen.has(resolved.name)) break;
24710
+ seen.add(resolved.name);
24711
+ resolved = next;
24712
+ }
24713
+ return resolved;
24714
+ }
24715
+ function collectMapCallbackLocalNames(callback) {
24716
+ const blocks = getCallbackBlocks(callback);
24717
+ if (blocks.length === 0) {
24718
+ return /* @__PURE__ */ new Set();
24719
+ }
24720
+ const paramNames = callback.kind === "ArrowFunction" || callback.kind === "FunctionExpression" ? new Set(callback.params.map((param) => deSSAVarName(param.name))) : /* @__PURE__ */ new Set();
24721
+ const locals = /* @__PURE__ */ new Set();
24722
+ for (const block of blocks) {
24723
+ for (const instr of block.instructions) {
24724
+ if (instr.kind === "Assign" && instr.target.kind === "Identifier") {
24725
+ const name = deSSAVarName(instr.target.name);
24726
+ if (!paramNames.has(name)) locals.add(name);
24727
+ }
24728
+ if (instr.kind === "Phi" && instr.target.kind === "Identifier") {
24729
+ const name = deSSAVarName(instr.target.name);
24730
+ if (!paramNames.has(name)) locals.add(name);
24731
+ }
24732
+ }
24733
+ }
24734
+ return locals;
24735
+ }
24736
+ function hasUnresolvedCallbackLocalKeyDependencies(keyExpr, callback, keyAliasDeclarations) {
24737
+ const callbackLocals = collectMapCallbackLocalNames(callback);
24738
+ if (callbackLocals.size === 0) {
24739
+ return false;
24740
+ }
24741
+ const resolvableAliases = new Set(
24742
+ Array.from(keyAliasDeclarations.keys()).map((name) => deSSAVarName(name))
24743
+ );
24744
+ const deps = /* @__PURE__ */ new Set();
24745
+ collectExpressionDependencies(keyExpr, deps);
24746
+ for (const dep of deps) {
24747
+ const base = dep.split(".")[0] ?? dep;
24748
+ if (!base) continue;
24749
+ if (callbackLocals.has(base) && !resolvableAliases.has(base)) {
24750
+ return true;
24751
+ }
24752
+ }
24753
+ return false;
24754
+ }
24566
24755
  function buildListCallExpression(expr, statements, ctx, ops) {
24567
24756
  const { t: t4 } = ctx;
24568
24757
  if (expr.kind !== "CallExpression" && expr.kind !== "OptionalCallExpression") {
@@ -24581,7 +24770,8 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24581
24770
  if (!mapCallback) {
24582
24771
  throw new Error("map callback is required");
24583
24772
  }
24584
- const keyExpr = extractKeyFromMapCallback(mapCallback);
24773
+ const extractedKeyExpr = extractKeyFromMapCallback(mapCallback);
24774
+ const keyExpr = extractedKeyExpr ? resolveMapCallbackKeyExpression(extractedKeyExpr, mapCallback) : void 0;
24585
24775
  const isKeyed = !!keyExpr;
24586
24776
  const hasRestParam = (mapCallback.kind === "ArrowFunction" || mapCallback.kind === "FunctionExpression") && Array.isArray(mapCallback.rawParams) && mapCallback.rawParams.some((param) => t4.isRestElement(param));
24587
24777
  const canConstifyKey = isKeyed && keyExpr && !hasRestParam;
@@ -24632,7 +24822,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24632
24822
  if (Object.keys(overrides).length > 0) {
24633
24823
  if (t4.isBlockStatement(callbackExpr.body)) {
24634
24824
  for (const stmt of callbackExpr.body.body) {
24635
- if (!t4.isVariableDeclaration(stmt)) continue;
24825
+ if (!t4.isVariableDeclaration(stmt) || stmt.kind !== "const") continue;
24636
24826
  for (const decl of stmt.declarations) {
24637
24827
  if (!t4.isIdentifier(decl.id) || !decl.init) continue;
24638
24828
  const replacement = t4.cloneNode(decl.init, true);
@@ -24662,7 +24852,32 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24662
24852
  }
24663
24853
  let listCall;
24664
24854
  if (isKeyed && keyExpr) {
24855
+ const keyAliasDeclarations = collectMapCallbackAliasDeclarations(mapCallback);
24856
+ const hasUnresolvedLocalKeyDeps = hasUnresolvedCallbackLocalKeyDependencies(
24857
+ keyExpr,
24858
+ mapCallback,
24859
+ keyAliasDeclarations
24860
+ );
24665
24861
  let keyExprAst = ops.lowerExpression(keyExpr, ctx);
24862
+ if (keyAliasDeclarations.size > 0) {
24863
+ const keyOverrides = {};
24864
+ for (const [name, value] of keyAliasDeclarations) {
24865
+ const replacement = ops.lowerExpression(value, ctx);
24866
+ replaceIdentifiersWithOverrides(replacement, keyOverrides, t4);
24867
+ keyOverrides[name] = () => t4.cloneNode(replacement, true);
24868
+ }
24869
+ if (Object.keys(keyOverrides).length > 0) {
24870
+ replaceIdentifiersWithOverrides(
24871
+ keyExprAst,
24872
+ keyOverrides,
24873
+ t4,
24874
+ void 0,
24875
+ void 0,
24876
+ false,
24877
+ true
24878
+ );
24879
+ }
24880
+ }
24666
24881
  if (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) {
24667
24882
  const itemParam = callbackExpr.params[0];
24668
24883
  const indexParam = callbackExpr.params[1];
@@ -24676,6 +24891,9 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24676
24891
  }
24677
24892
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? callbackExpr.params[0] : null;
24678
24893
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? callbackExpr.params[1] : null;
24894
+ if (hasUnresolvedLocalKeyDeps) {
24895
+ keyExprAst = t4.identifier(t4.isIdentifier(indexParamName) ? indexParamName.name : "__index");
24896
+ }
24679
24897
  const keyFn = t4.arrowFunctionExpression(
24680
24898
  [
24681
24899
  t4.isIdentifier(itemParamName) ? itemParamName : t4.identifier("__item"),
@@ -25117,33 +25335,42 @@ function dependencyCoveredByDeclarations(dep, region) {
25117
25335
 
25118
25336
  // src/ir/codegen-resumable-utils.ts
25119
25337
  import { pathToFileURL } from "url";
25120
- function renameIdentifiersInExpr(expr, renames) {
25121
- const cloned = JSON.parse(JSON.stringify(expr));
25122
- function visit(node) {
25123
- if (!node || typeof node !== "object") return;
25124
- const n = node;
25125
- if (n.type === "Identifier" && typeof n.name === "string") {
25126
- const newName = renames.get(n.name);
25127
- if (newName) {
25128
- n.name = newName;
25129
- }
25130
- }
25131
- for (const key of Object.keys(n)) {
25132
- if (key === "loc" || key === "start" || key === "end" || key === "extra" || key === "comments" || key === "leadingComments" || key === "trailingComments") {
25133
- continue;
25134
- }
25135
- const value = n[key];
25136
- if (Array.isArray(value)) {
25137
- for (const item of value) {
25138
- visit(item);
25139
- }
25140
- } else if (value && typeof value === "object") {
25141
- visit(value);
25338
+ import traverseModule2 from "@babel/traverse";
25339
+ function renameIdentifiersInExpr(expr, renames, t4) {
25340
+ const traverse = traverseModule2.default ?? traverseModule2;
25341
+ const cloned = t4.cloneNode(expr, true);
25342
+ const file = t4.file(t4.program([t4.expressionStatement(cloned)]));
25343
+ traverse(file, {
25344
+ Identifier(path2) {
25345
+ const oldName = path2.node.name;
25346
+ const nextName = renames.get(oldName);
25347
+ if (!nextName) return;
25348
+ if (path2.parentPath.isObjectProperty() && path2.parentPath.node.shorthand && path2.parentPath.node.value === path2.node && t4.isIdentifier(path2.parentPath.node.key)) {
25349
+ path2.parentPath.node.shorthand = false;
25350
+ path2.parentPath.node.value = t4.identifier(nextName);
25351
+ return;
25142
25352
  }
25353
+ if (!path2.isReferencedIdentifier()) return;
25354
+ const binding = path2.scope.getBinding(oldName);
25355
+ if (binding && binding.scope !== path2.scope.getProgramParent()) return;
25356
+ path2.node.name = nextName;
25143
25357
  }
25144
- }
25145
- visit(cloned);
25146
- return cloned;
25358
+ });
25359
+ const first = file.program.body[0];
25360
+ return t4.isExpressionStatement(first) ? first.expression : cloned;
25361
+ }
25362
+ function collectFreeIdentifiersInExpr(expr, t4) {
25363
+ const traverse = traverseModule2.default ?? traverseModule2;
25364
+ const file = t4.file(t4.program([t4.expressionStatement(t4.cloneNode(expr, true))]));
25365
+ const names = /* @__PURE__ */ new Set();
25366
+ traverse(file, {
25367
+ ReferencedIdentifier(path2) {
25368
+ const name = path2.node.name;
25369
+ if (path2.scope.getBinding(name)) return;
25370
+ names.add(name);
25371
+ }
25372
+ });
25373
+ return names;
25147
25374
  }
25148
25375
  function genModuleUrlExpr(ctx) {
25149
25376
  const { t: t4 } = ctx;
@@ -25280,10 +25507,10 @@ function registerResumableComponent(componentName, ctx) {
25280
25507
  }
25281
25508
 
25282
25509
  // src/ir/codegen-resumable-events.ts
25283
- function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, containingRegion, ops) {
25510
+ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, containingRegion, ops, options) {
25284
25511
  const { t: t4 } = ctx;
25285
25512
  if (!ctx.resumableEnabled) {
25286
- return;
25513
+ return false;
25287
25514
  }
25288
25515
  const prevWrapTracked = ctx.wrapTrackedExpressions;
25289
25516
  ctx.wrapTrackedExpressions = false;
@@ -25297,53 +25524,88 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25297
25524
  const scopeParam = t4.identifier("scopeId");
25298
25525
  const ensureHandlerParam = (fn) => {
25299
25526
  if (t4.isArrowFunctionExpression(fn)) {
25300
- if (fn.params.length > 0) return fn;
25301
- return t4.arrowFunctionExpression([eventParam], fn.body, fn.async);
25527
+ return fn;
25302
25528
  }
25303
25529
  if (t4.isFunctionExpression(fn)) {
25304
- if (fn.params.length > 0) return fn;
25305
- return t4.functionExpression(fn.id, [eventParam], fn.body, fn.generator, fn.async);
25530
+ return fn;
25306
25531
  }
25307
25532
  if (t4.isIdentifier(fn) || t4.isMemberExpression(fn)) {
25308
25533
  return fn;
25309
25534
  }
25310
- if (t4.isCallExpression(fn) && fn.arguments.length === 0 && (t4.isIdentifier(fn.callee) || t4.isMemberExpression(fn.callee))) {
25311
- return fn.callee;
25312
- }
25313
25535
  return t4.functionExpression(
25314
25536
  null,
25315
- [eventParam],
25316
- t4.blockStatement([
25317
- t4.returnStatement(
25318
- t4.callExpression(
25319
- t4.memberExpression(fn, t4.identifier("call")),
25320
- [t4.thisExpression(), eventParam]
25321
- )
25322
- )
25323
- ])
25537
+ [],
25538
+ t4.blockStatement([t4.returnStatement(fn)])
25324
25539
  );
25325
25540
  };
25326
25541
  const handlerExpr = ensureHandlerParam(valueExpr);
25327
25542
  const handlerId = t4.identifier(`__fict_e${ctx.resumableHandlerCounter ?? 0}`);
25328
25543
  ctx.resumableHandlerCounter = (ctx.resumableHandlerCounter ?? 0) + 1;
25329
- const captured = /* @__PURE__ */ new Set();
25330
- collectExpressionIdentifiersDeep(expr, captured);
25544
+ const captured = collectFreeIdentifiersInExpr(handlerExpr, t4);
25331
25545
  const lexicalNames = Array.from(captured).filter((name) => ctx.signalVars?.has(name));
25332
25546
  const propsName = ctx.propsParamName && captured.has(ctx.propsParamName) ? ctx.propsParamName : null;
25547
+ const unsupportedLocals = Array.from(captured).filter((name) => {
25548
+ if (ctx.inListRender && ctx.listKeyParamName && name === ctx.listKeyParamName) return true;
25549
+ if (!ctx.localDeclaredNames?.has(name)) return false;
25550
+ if (ctx.signalVars?.has(name)) return false;
25551
+ if (ctx.functionVars?.has(name)) return false;
25552
+ if (propsName && name === propsName) return false;
25553
+ return true;
25554
+ });
25555
+ const loweredFunctionDeps = /* @__PURE__ */ new Map();
25556
+ const unsafeFunctionCaptures = [];
25557
+ for (const name of captured) {
25558
+ if (!ctx.functionVars?.has(name) || ctx.signalVars?.has(name)) continue;
25559
+ if (ctx.hoistedFunctionDepNames?.has(name)) continue;
25560
+ const hirDef = ctx.componentFunctionDefs?.get(name);
25561
+ if (!hirDef) {
25562
+ if (ctx.localDeclaredNames?.has(name)) {
25563
+ unsafeFunctionCaptures.push(`${name} -> <unhoistable>`);
25564
+ }
25565
+ continue;
25566
+ }
25567
+ const loweredFn = ops.lowerDomExpression(hirDef, ctx, null, {
25568
+ skipHookAccessors: true,
25569
+ skipRegionRootOverride: true
25570
+ });
25571
+ const fnCaptured = collectFreeIdentifiersInExpr(loweredFn, t4);
25572
+ const localFnCaptures = Array.from(fnCaptured).filter((dep) => ctx.localDeclaredNames?.has(dep)).sort();
25573
+ if (localFnCaptures.length > 0) {
25574
+ unsafeFunctionCaptures.push(`${name} -> ${localFnCaptures.join(", ")}`);
25575
+ continue;
25576
+ }
25577
+ loweredFunctionDeps.set(name, loweredFn);
25578
+ }
25579
+ if (unsupportedLocals.length > 0 || unsafeFunctionCaptures.length > 0) {
25580
+ const detailParts = [];
25581
+ if (unsupportedLocals.length > 0) {
25582
+ detailParts.push(`direct: ${unsupportedLocals.sort().join(", ")}`);
25583
+ }
25584
+ if (unsafeFunctionCaptures.length > 0) {
25585
+ detailParts.push(`function deps: ${unsafeFunctionCaptures.sort().join("; ")}`);
25586
+ }
25587
+ const detail = `Resumable handlers cannot capture non-serializable local variables (${detailParts.join(" | ")}).`;
25588
+ if (options?.explicit) {
25589
+ const loc = expr.loc?.start;
25590
+ const fileName = ctx.options?.filename ?? "<unknown>";
25591
+ const location = loc ? `${fileName}:${loc.line}:${loc.column + 1}` : fileName;
25592
+ throw new Error(
25593
+ `${detail} Use signals/props/function references or remove '$' suffix.
25594
+ at ${location}`
25595
+ );
25596
+ }
25597
+ return false;
25598
+ }
25333
25599
  const functionDepRenames = /* @__PURE__ */ new Map();
25334
25600
  for (const name of captured) {
25335
25601
  if (ctx.functionVars?.has(name) && !ctx.signalVars?.has(name)) {
25336
- const hirDef = ctx.componentFunctionDefs?.get(name);
25337
- if (!hirDef) continue;
25338
25602
  let hoistedName = ctx.hoistedFunctionDepNames?.get(name);
25339
25603
  if (!hoistedName) {
25604
+ const loweredFn = loweredFunctionDeps.get(name);
25605
+ if (!loweredFn) continue;
25340
25606
  hoistedName = `__fict_fn_${name}_${ctx.hoistedFunctionDepCounter ?? 0}`;
25341
25607
  ctx.hoistedFunctionDepCounter = (ctx.hoistedFunctionDepCounter ?? 0) + 1;
25342
25608
  ctx.hoistedFunctionDepNames?.set(name, hoistedName);
25343
- const loweredFn = ops.lowerDomExpression(hirDef, ctx, null, {
25344
- skipHookAccessors: true,
25345
- skipRegionRootOverride: true
25346
- });
25347
25609
  const hoistedDecl = t4.variableDeclaration("const", [
25348
25610
  t4.variableDeclarator(t4.identifier(hoistedName), loweredFn)
25349
25611
  ]);
@@ -25355,7 +25617,7 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25355
25617
  }
25356
25618
  let finalHandlerExpr = handlerExpr;
25357
25619
  if (functionDepRenames.size > 0) {
25358
- finalHandlerExpr = renameIdentifiersInExpr(handlerExpr, functionDepRenames);
25620
+ finalHandlerExpr = renameIdentifiersInExpr(handlerExpr, functionDepRenames, t4);
25359
25621
  }
25360
25622
  const bodyStatements = [];
25361
25623
  if (lexicalNames.length > 0) {
@@ -25388,12 +25650,97 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25388
25650
  );
25389
25651
  }
25390
25652
  const handlerVar = t4.identifier("__handler");
25653
+ const resultVar = t4.identifier("__result");
25391
25654
  bodyStatements.push(
25392
25655
  t4.variableDeclaration("const", [t4.variableDeclarator(handlerVar, finalHandlerExpr)])
25393
25656
  );
25394
25657
  bodyStatements.push(
25395
- t4.returnStatement(
25396
- t4.callExpression(t4.memberExpression(handlerVar, t4.identifier("call")), [elParam, eventParam])
25658
+ t4.ifStatement(
25659
+ t4.binaryExpression(
25660
+ "===",
25661
+ t4.unaryExpression("typeof", handlerVar),
25662
+ t4.stringLiteral("function")
25663
+ ),
25664
+ t4.blockStatement([
25665
+ t4.variableDeclaration("const", [
25666
+ t4.variableDeclarator(
25667
+ resultVar,
25668
+ t4.callExpression(t4.memberExpression(handlerVar, t4.identifier("call")), [
25669
+ elParam,
25670
+ eventParam
25671
+ ])
25672
+ )
25673
+ ]),
25674
+ t4.ifStatement(
25675
+ t4.logicalExpression(
25676
+ "&&",
25677
+ t4.binaryExpression(
25678
+ "===",
25679
+ t4.unaryExpression("typeof", resultVar),
25680
+ t4.stringLiteral("function")
25681
+ ),
25682
+ t4.binaryExpression("!==", resultVar, handlerVar)
25683
+ ),
25684
+ t4.blockStatement([
25685
+ t4.returnStatement(
25686
+ t4.callExpression(t4.memberExpression(resultVar, t4.identifier("call")), [
25687
+ elParam,
25688
+ eventParam
25689
+ ])
25690
+ )
25691
+ ])
25692
+ ),
25693
+ t4.ifStatement(
25694
+ t4.logicalExpression(
25695
+ "&&",
25696
+ resultVar,
25697
+ t4.binaryExpression(
25698
+ "===",
25699
+ t4.unaryExpression(
25700
+ "typeof",
25701
+ t4.memberExpression(resultVar, t4.identifier("handleEvent"))
25702
+ ),
25703
+ t4.stringLiteral("function")
25704
+ )
25705
+ ),
25706
+ t4.blockStatement([
25707
+ t4.returnStatement(
25708
+ t4.callExpression(
25709
+ t4.memberExpression(
25710
+ t4.memberExpression(resultVar, t4.identifier("handleEvent")),
25711
+ t4.identifier("call")
25712
+ ),
25713
+ [resultVar, eventParam]
25714
+ )
25715
+ )
25716
+ ])
25717
+ ),
25718
+ t4.returnStatement(resultVar)
25719
+ ])
25720
+ )
25721
+ );
25722
+ bodyStatements.push(
25723
+ t4.ifStatement(
25724
+ t4.logicalExpression(
25725
+ "&&",
25726
+ handlerVar,
25727
+ t4.binaryExpression(
25728
+ "===",
25729
+ t4.unaryExpression("typeof", t4.memberExpression(handlerVar, t4.identifier("handleEvent"))),
25730
+ t4.stringLiteral("function")
25731
+ )
25732
+ ),
25733
+ t4.blockStatement([
25734
+ t4.returnStatement(
25735
+ t4.callExpression(
25736
+ t4.memberExpression(
25737
+ t4.memberExpression(handlerVar, t4.identifier("handleEvent")),
25738
+ t4.identifier("call")
25739
+ ),
25740
+ [handlerVar, eventParam]
25741
+ )
25742
+ )
25743
+ ])
25397
25744
  )
25398
25745
  );
25399
25746
  const exportedHandler = t4.exportNamedDeclaration(
@@ -25422,6 +25769,7 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25422
25769
  ])
25423
25770
  )
25424
25771
  );
25772
+ return true;
25425
25773
  }
25426
25774
 
25427
25775
  // src/ir/codegen-runtime-imports.ts
@@ -25806,7 +26154,8 @@ function extractHIRStaticHtml(jsx, ctx, ops, parentPath = [], namespace = null)
25806
26154
  name: eventName.toLowerCase(),
25807
26155
  expr: attr.value ?? void 0,
25808
26156
  eventOptions: { capture, passive, once },
25809
- resumable: shouldBeResumable
26157
+ resumable: shouldBeResumable,
26158
+ resumableExplicit: isResumableEvent
25810
26159
  });
25811
26160
  continue;
25812
26161
  }
@@ -26379,7 +26728,7 @@ function buildOutputParams(fn, t4) {
26379
26728
  }
26380
26729
  return fn.params.map((p) => t4.identifier(deSSAVarName(p.name)));
26381
26730
  }
26382
- function lowerTrackedExpression(expr, ctx) {
26731
+ function lowerTrackedExpression(expr, ctx, valueUsed = true) {
26383
26732
  const regionOverride = ctx.inReturn && ctx.currentFnIsHook ? null : ctx.currentRegion ?? (ctx.trackedVars.size ? {
26384
26733
  id: -1,
26385
26734
  dependencies: new Set(ctx.trackedVars),
@@ -26387,7 +26736,7 @@ function lowerTrackedExpression(expr, ctx) {
26387
26736
  hasControlFlow: false,
26388
26737
  hasReactiveWrites: false
26389
26738
  } : null);
26390
- const lowered = lowerExpression2(expr, ctx);
26739
+ const lowered = lowerExpression2(expr, ctx, valueUsed);
26391
26740
  if (ctx.t.isAssignmentExpression(lowered)) {
26392
26741
  const right = applyRegionMetadataToExpression2(lowered.right, ctx, regionOverride ?? void 0);
26393
26742
  return ctx.t.assignmentExpression(lowered.operator, lowered.left, right);
@@ -26509,7 +26858,7 @@ function lowerInstruction(instr, ctx) {
26509
26858
  );
26510
26859
  }
26511
26860
  if (instr.kind === "Expression") {
26512
- return applyLoc(t4.expressionStatement(lowerTrackedExpression(instr.value, ctx)));
26861
+ return applyLoc(t4.expressionStatement(lowerTrackedExpression(instr.value, ctx, false)));
26513
26862
  }
26514
26863
  if (instr.kind === "Phi") {
26515
26864
  return null;
@@ -26673,7 +27022,7 @@ function collectLocalDeclaredNames(params, blocks, t4) {
26673
27022
  }
26674
27023
  return declared;
26675
27024
  }
26676
- function lowerExpression2(expr, ctx, isAssigned = false) {
27025
+ function lowerExpression2(expr, ctx, valueUsed = true) {
26677
27026
  const depth = (ctx.expressionDepth ?? 0) + 1;
26678
27027
  const maxDepth = ctx.maxExpressionDepth ?? 500;
26679
27028
  if (depth > maxDepth) {
@@ -26684,12 +27033,12 @@ function lowerExpression2(expr, ctx, isAssigned = false) {
26684
27033
  }
26685
27034
  ctx.expressionDepth = depth;
26686
27035
  try {
26687
- return setNodeLoc(lowerExpressionImpl(expr, ctx, isAssigned), expr.loc);
27036
+ return setNodeLoc(lowerExpressionImpl(expr, ctx, valueUsed), expr.loc);
26688
27037
  } finally {
26689
27038
  ctx.expressionDepth = depth - 1;
26690
27039
  }
26691
27040
  }
26692
- function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27041
+ function lowerExpressionImpl(expr, ctx, valueUsed = true) {
26693
27042
  const { t: t4 } = ctx;
26694
27043
  const mapParams = (params) => params.map((p) => t4.identifier(deSSAVarName(p.name)));
26695
27044
  const lowerArgsAsExpressions = (args) => args.map(
@@ -26730,6 +27079,152 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
26730
27079
  ctx.localDeclaredNames = prevLocalDeclared;
26731
27080
  return result;
26732
27081
  };
27082
+ const lowerTrackedWriteCall = (callee, nextValue) => {
27083
+ if (!valueUsed) {
27084
+ return t4.callExpression(t4.cloneNode(callee, true), [nextValue]);
27085
+ }
27086
+ const nextId = genTemp3(ctx, "next");
27087
+ const nextRef = t4.identifier(nextId.name);
27088
+ return t4.callExpression(
27089
+ t4.arrowFunctionExpression(
27090
+ [t4.cloneNode(nextId, true)],
27091
+ t4.sequenceExpression([
27092
+ t4.callExpression(t4.cloneNode(callee, true), [nextRef]),
27093
+ t4.identifier(nextId.name)
27094
+ ])
27095
+ ),
27096
+ [nextValue]
27097
+ );
27098
+ };
27099
+ const buildTrackedAssignmentNext = (operator, current, right) => {
27100
+ switch (operator) {
27101
+ case "=":
27102
+ return right;
27103
+ case "+=":
27104
+ return t4.binaryExpression("+", current, right);
27105
+ case "-=":
27106
+ return t4.binaryExpression("-", current, right);
27107
+ case "*=":
27108
+ return t4.binaryExpression("*", current, right);
27109
+ case "/=":
27110
+ return t4.binaryExpression("/", current, right);
27111
+ case "%=":
27112
+ return t4.binaryExpression("%", current, right);
27113
+ case "**=":
27114
+ return t4.binaryExpression("**", current, right);
27115
+ case "<<=":
27116
+ return t4.binaryExpression("<<", current, right);
27117
+ case ">>=":
27118
+ return t4.binaryExpression(">>", current, right);
27119
+ case ">>>=":
27120
+ return t4.binaryExpression(">>>", current, right);
27121
+ case "|=":
27122
+ return t4.binaryExpression("|", current, right);
27123
+ case "^=":
27124
+ return t4.binaryExpression("^", current, right);
27125
+ case "&=":
27126
+ return t4.binaryExpression("&", current, right);
27127
+ case "&&=":
27128
+ return t4.logicalExpression("&&", current, right);
27129
+ case "||=":
27130
+ return t4.logicalExpression("||", current, right);
27131
+ case "??=":
27132
+ return t4.logicalExpression("??", current, right);
27133
+ default:
27134
+ return right;
27135
+ }
27136
+ };
27137
+ const buildStaticSignalKeyTest = (keyRef, keys) => {
27138
+ if (keys.length === 0) return null;
27139
+ let test = null;
27140
+ for (const key of keys) {
27141
+ const literal = typeof key === "number" ? t4.numericLiteral(key) : t4.stringLiteral(String(key));
27142
+ const eq = t4.binaryExpression("===", t4.cloneNode(keyRef, true), literal);
27143
+ test = test ? t4.logicalExpression("||", test, eq) : eq;
27144
+ }
27145
+ return test;
27146
+ };
27147
+ const lowerComputedHookSignalAssignment = (objectName, keyExpr, signalKeys, operator, rightExpr) => {
27148
+ const keyTestKeys = signalKeys.filter(
27149
+ (key) => typeof key === "number" && Number.isFinite(key) || typeof key === "string"
27150
+ );
27151
+ if (keyTestKeys.length === 0) return null;
27152
+ const keyId = genTemp3(ctx, "key");
27153
+ const keyRef = t4.identifier(keyId.name);
27154
+ const memberForAccessor = t4.memberExpression(
27155
+ t4.identifier(objectName),
27156
+ t4.identifier(keyId.name),
27157
+ true
27158
+ );
27159
+ const current = t4.callExpression(t4.cloneNode(memberForAccessor, true), []);
27160
+ const right = lowerExpression2(rightExpr, ctx);
27161
+ const signalWrite = lowerTrackedWriteCall(
27162
+ memberForAccessor,
27163
+ buildTrackedAssignmentNext(operator, current, t4.cloneNode(right, true))
27164
+ );
27165
+ const fallback = t4.assignmentExpression(
27166
+ operator,
27167
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27168
+ right
27169
+ );
27170
+ const keyTest = buildStaticSignalKeyTest(keyRef, keyTestKeys);
27171
+ if (!keyTest) return null;
27172
+ return t4.callExpression(
27173
+ t4.arrowFunctionExpression(
27174
+ [t4.cloneNode(keyId, true)],
27175
+ t4.conditionalExpression(keyTest, signalWrite, fallback)
27176
+ ),
27177
+ [lowerExpression2(keyExpr, ctx)]
27178
+ );
27179
+ };
27180
+ const lowerComputedHookSignalUpdate = (objectName, keyExpr, signalKeys, operator, prefix) => {
27181
+ const keyTestKeys = signalKeys.filter(
27182
+ (key) => typeof key === "number" && Number.isFinite(key) || typeof key === "string"
27183
+ );
27184
+ if (keyTestKeys.length === 0) return null;
27185
+ const keyId = genTemp3(ctx, "key");
27186
+ const keyRef = t4.identifier(keyId.name);
27187
+ const signalUpdate = lowerTrackedUpdateCall(
27188
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27189
+ operator,
27190
+ prefix
27191
+ );
27192
+ const fallback = t4.updateExpression(
27193
+ operator,
27194
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27195
+ prefix
27196
+ );
27197
+ const keyTest = buildStaticSignalKeyTest(keyRef, keyTestKeys);
27198
+ if (!keyTest) return null;
27199
+ return t4.callExpression(
27200
+ t4.arrowFunctionExpression(
27201
+ [t4.cloneNode(keyId, true)],
27202
+ t4.conditionalExpression(keyTest, signalUpdate, fallback)
27203
+ ),
27204
+ [lowerExpression2(keyExpr, ctx)]
27205
+ );
27206
+ };
27207
+ const lowerTrackedUpdateCall = (callee, operator, prefix) => {
27208
+ const op = operator === "++" ? "+" : "-";
27209
+ const delta = t4.numericLiteral(1);
27210
+ const current = t4.callExpression(t4.cloneNode(callee, true), []);
27211
+ if (!valueUsed) {
27212
+ return t4.callExpression(t4.cloneNode(callee, true), [t4.binaryExpression(op, current, delta)]);
27213
+ }
27214
+ const prevId = genTemp3(ctx, "prev");
27215
+ const prevForSet = t4.identifier(prevId.name);
27216
+ const prevForResult = t4.identifier(prevId.name);
27217
+ return t4.callExpression(
27218
+ t4.arrowFunctionExpression(
27219
+ [t4.cloneNode(prevId, true)],
27220
+ t4.sequenceExpression([
27221
+ t4.callExpression(t4.cloneNode(callee, true), [t4.binaryExpression(op, prevForSet, delta)]),
27222
+ prefix ? t4.binaryExpression(op, prevForResult, t4.numericLiteral(1)) : prevForResult
27223
+ ])
27224
+ ),
27225
+ [current]
27226
+ );
27227
+ };
26733
27228
  const lowerBlocksToStatements = (blocks) => {
26734
27229
  const stmts = [];
26735
27230
  for (const block of blocks) {
@@ -26899,6 +27394,24 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
26899
27394
  expr.arguments,
26900
27395
  (arg) => arg.kind === "ArrowFunction" || arg.kind === "FunctionExpression" ? withNonReactiveScope(ctx, () => lowerExpression2(arg, ctx)) : lowerExpression2(arg, ctx)
26901
27396
  );
27397
+ const includeDevtools = ctx.options?.dev !== false;
27398
+ if (includeDevtools && expr.loc) {
27399
+ const source = `${ctx.options?.filename ?? ""}:${expr.loc.start.line}:${expr.loc.start.column}`;
27400
+ const sourceProp = t4.objectProperty(
27401
+ t4.identifier("devToolsSource"),
27402
+ t4.stringLiteral(source)
27403
+ );
27404
+ if (args.length === 1) {
27405
+ args.push(t4.objectExpression([sourceProp]));
27406
+ } else if (args.length > 1 && t4.isObjectExpression(args[1])) {
27407
+ const hasSourceProp = args[1].properties.some(
27408
+ (prop) => t4.isObjectProperty(prop) && t4.isIdentifier(prop.key) && prop.key.name === "devToolsSource"
27409
+ );
27410
+ if (!hasSourceProp) {
27411
+ args[1].properties.push(sourceProp);
27412
+ }
27413
+ }
27414
+ }
26902
27415
  if (ctx.inModule) {
26903
27416
  ctx.helpersUsed.add("effect");
26904
27417
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.effect), args);
@@ -27206,59 +27719,42 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27206
27719
  expr.left.computed,
27207
27720
  expr.left.optional
27208
27721
  );
27209
- const current = t4.callExpression(member, []);
27722
+ const current = t4.callExpression(t4.cloneNode(member, true), []);
27210
27723
  const right = lowerExpression2(expr.right, ctx);
27211
- let next;
27212
- switch (expr.operator) {
27213
- case "=":
27214
- next = right;
27215
- break;
27216
- case "+=":
27217
- next = t4.binaryExpression("+", current, right);
27218
- break;
27219
- case "-=":
27220
- next = t4.binaryExpression("-", current, right);
27221
- break;
27222
- case "*=":
27223
- next = t4.binaryExpression("*", current, right);
27224
- break;
27225
- case "/=":
27226
- next = t4.binaryExpression("/", current, right);
27227
- break;
27228
- default:
27229
- next = right;
27724
+ const next = buildTrackedAssignmentNext(expr.operator, current, right);
27725
+ return lowerTrackedWriteCall(member, next);
27726
+ }
27727
+ if (expr.left.computed) {
27728
+ const signalKeys = [];
27729
+ if (info?.objectProps) {
27730
+ for (const [key, accessorKind] of info.objectProps.entries()) {
27731
+ if (accessorKind === "signal") signalKeys.push(key);
27732
+ }
27230
27733
  }
27231
- return t4.callExpression(member, [next]);
27734
+ if (info?.arrayProps) {
27735
+ for (const [key, accessorKind] of info.arrayProps.entries()) {
27736
+ if (accessorKind === "signal") signalKeys.push(key);
27737
+ }
27738
+ }
27739
+ const lowered = lowerComputedHookSignalAssignment(
27740
+ deSSAVarName(expr.left.object.name),
27741
+ expr.left.property,
27742
+ signalKeys,
27743
+ expr.operator,
27744
+ expr.right
27745
+ );
27746
+ if (lowered) return lowered;
27232
27747
  }
27233
27748
  }
27234
27749
  }
27235
27750
  if (expr.left.kind === "Identifier") {
27236
27751
  const baseName2 = deSSAVarName(expr.left.name);
27237
27752
  if (ctx.trackedVars.has(baseName2)) {
27238
- const id = t4.identifier(baseName2);
27753
+ const callee = t4.identifier(baseName2);
27239
27754
  const current = t4.callExpression(t4.identifier(baseName2), []);
27240
27755
  const right = lowerExpression2(expr.right, ctx);
27241
- let next;
27242
- switch (expr.operator) {
27243
- case "=":
27244
- next = right;
27245
- break;
27246
- case "+=":
27247
- next = t4.binaryExpression("+", current, right);
27248
- break;
27249
- case "-=":
27250
- next = t4.binaryExpression("-", current, right);
27251
- break;
27252
- case "*=":
27253
- next = t4.binaryExpression("*", current, right);
27254
- break;
27255
- case "/=":
27256
- next = t4.binaryExpression("/", current, right);
27257
- break;
27258
- default:
27259
- next = right;
27260
- }
27261
- return t4.callExpression(id, [next]);
27756
+ const next = buildTrackedAssignmentNext(expr.operator, current, right);
27757
+ return lowerTrackedWriteCall(callee, next);
27262
27758
  }
27263
27759
  }
27264
27760
  return t4.assignmentExpression(
@@ -27290,21 +27786,35 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27290
27786
  expr.argument.computed,
27291
27787
  expr.argument.optional
27292
27788
  );
27293
- const current = t4.callExpression(member, []);
27294
- const delta = t4.numericLiteral(1);
27295
- const next = expr.operator === "++" ? t4.binaryExpression("+", current, delta) : t4.binaryExpression("-", current, delta);
27296
- return t4.callExpression(member, [next]);
27789
+ return lowerTrackedUpdateCall(member, expr.operator, expr.prefix);
27790
+ }
27791
+ if (expr.argument.computed) {
27792
+ const signalKeys = [];
27793
+ if (info?.objectProps) {
27794
+ for (const [key, accessorKind] of info.objectProps.entries()) {
27795
+ if (accessorKind === "signal") signalKeys.push(key);
27796
+ }
27797
+ }
27798
+ if (info?.arrayProps) {
27799
+ for (const [key, accessorKind] of info.arrayProps.entries()) {
27800
+ if (accessorKind === "signal") signalKeys.push(key);
27801
+ }
27802
+ }
27803
+ const lowered = lowerComputedHookSignalUpdate(
27804
+ deSSAVarName(expr.argument.object.name),
27805
+ expr.argument.property,
27806
+ signalKeys,
27807
+ expr.operator,
27808
+ expr.prefix
27809
+ );
27810
+ if (lowered) return lowered;
27297
27811
  }
27298
27812
  }
27299
27813
  }
27300
27814
  if (expr.argument.kind === "Identifier") {
27301
27815
  const baseName2 = deSSAVarName(expr.argument.name);
27302
27816
  if (ctx.trackedVars.has(baseName2)) {
27303
- const id = t4.identifier(baseName2);
27304
- const current = t4.callExpression(t4.identifier(baseName2), []);
27305
- const delta = t4.numericLiteral(1);
27306
- const next = expr.operator === "++" ? t4.binaryExpression("+", current, delta) : t4.binaryExpression("-", current, delta);
27307
- return t4.callExpression(id, [next]);
27817
+ return lowerTrackedUpdateCall(t4.identifier(baseName2), expr.operator, expr.prefix);
27308
27818
  }
27309
27819
  }
27310
27820
  return t4.updateExpression(
@@ -27326,7 +27836,11 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27326
27836
  case "NewExpression":
27327
27837
  return t4.newExpression(lowerExpression2(expr.callee, ctx), lowerCallArguments(expr.arguments));
27328
27838
  case "SequenceExpression":
27329
- return t4.sequenceExpression(expr.expressions.map((e) => lowerExpression2(e, ctx)));
27839
+ return t4.sequenceExpression(
27840
+ expr.expressions.map(
27841
+ (e, index) => lowerExpression2(e, ctx, index === expr.expressions.length - 1 ? valueUsed : false)
27842
+ )
27843
+ );
27330
27844
  case "YieldExpression":
27331
27845
  return t4.yieldExpression(
27332
27846
  expr.argument ? lowerExpression2(expr.argument, ctx) : null,
@@ -27784,16 +28298,17 @@ function lowerIntrinsicElement(jsx, ctx) {
27784
28298
  const hasEventOptions = binding.eventOptions && (binding.eventOptions.capture || binding.eventOptions.passive || binding.eventOptions.once);
27785
28299
  const isDelegated = DelegatedEvents.has(eventName) && !hasEventOptions;
27786
28300
  if (binding.resumable && !hasEventOptions) {
27787
- emitResumableEventBinding(
28301
+ const emitted = emitResumableEventBinding(
27788
28302
  targetId,
27789
28303
  eventName,
27790
28304
  binding.expr,
27791
28305
  statements,
27792
28306
  ctx,
27793
28307
  containingRegion,
27794
- createResumableEventBindingOps()
28308
+ createResumableEventBindingOps(),
28309
+ { explicit: binding.resumableExplicit === true }
27795
28310
  );
27796
- continue;
28311
+ if (emitted) continue;
27797
28312
  }
27798
28313
  const hirDataBinding = isDelegated && binding.expr ? extractDelegatedEventDataFromHIR(binding.expr, ctx) : null;
27799
28314
  if (hirDataBinding) {
@@ -27835,34 +28350,24 @@ function lowerIntrinsicElement(jsx, ctx) {
27835
28350
  const isFn = t4.isArrowFunctionExpression(valueExpr) || t4.isFunctionExpression(valueExpr);
27836
28351
  const ensureHandlerParam = (fn) => {
27837
28352
  if (t4.isArrowFunctionExpression(fn)) {
27838
- if (fn.params.length > 0) return fn;
27839
- return t4.arrowFunctionExpression([eventParam], fn.body, fn.async);
28353
+ return fn;
27840
28354
  }
27841
28355
  if (t4.isFunctionExpression(fn)) {
27842
- if (fn.params.length > 0) return fn;
27843
- return t4.functionExpression(fn.id, [eventParam], fn.body, fn.generator, fn.async);
28356
+ return fn;
27844
28357
  }
27845
28358
  if (t4.isIdentifier(fn) || t4.isMemberExpression(fn)) {
27846
28359
  return fn;
27847
28360
  }
27848
- if (t4.isCallExpression(fn) && fn.arguments.length === 0 && (t4.isIdentifier(fn.callee) || t4.isMemberExpression(fn.callee))) {
27849
- return fn.callee;
27850
- }
27851
28361
  return t4.functionExpression(
27852
28362
  null,
27853
- [eventParam],
27854
- t4.blockStatement([
27855
- t4.returnStatement(
27856
- t4.callExpression(
27857
- t4.memberExpression(fn, t4.identifier("call")),
27858
- [t4.thisExpression(), eventParam]
27859
- )
27860
- )
27861
- ])
28363
+ [],
28364
+ t4.blockStatement([t4.returnStatement(fn)])
27862
28365
  );
27863
28366
  };
27864
28367
  const handlerExpr = !isFn && shouldWrapHandler ? t4.arrowFunctionExpression([], valueExpr) : ensureHandlerParam(valueExpr);
27865
- let dataBinding = isDelegated && !shouldWrapHandler ? extractDelegatedEventData(valueExpr, t4) : null;
28368
+ let dataBinding = isDelegated && !shouldWrapHandler ? extractDelegatedEventData(valueExpr, t4, {
28369
+ isKnownHandlerIdentifier: (name) => ctx.functionVars?.has(deSSAVarName(name)) ?? false
28370
+ }) : null;
27866
28371
  if (dataBinding && t4.isIdentifier(dataBinding.handler)) {
27867
28372
  const handlerName = dataBinding.handler.name;
27868
28373
  if (ctx.signalVars?.has(handlerName) || ctx.memoVars?.has(handlerName) || ctx.aliasVars?.has(handlerName) || ctx.storeVars?.has(handlerName) || ctx.trackedVars.has(handlerName)) {
@@ -27874,8 +28379,6 @@ function lowerIntrinsicElement(jsx, ctx) {
27874
28379
  let handlerName = null;
27875
28380
  if (t4.isIdentifier(valueExpr)) {
27876
28381
  handlerName = valueExpr.name;
27877
- } else if (t4.isCallExpression(valueExpr) && valueExpr.arguments.length === 0 && t4.isIdentifier(valueExpr.callee)) {
27878
- handlerName = valueExpr.callee.name;
27879
28382
  }
27880
28383
  const handlerForCall = handlerName ? t4.identifier(handlerName) : t4.cloneNode(valueExpr, true);
27881
28384
  const finalHandler = !isFn && shouldWrapHandler ? t4.functionExpression(
@@ -27891,9 +28394,6 @@ function lowerIntrinsicElement(jsx, ctx) {
27891
28394
  ])
27892
28395
  ) : handlerExpr;
27893
28396
  const normalizeHandler = (expr) => {
27894
- if (t4.isCallExpression(expr) && (t4.isIdentifier(expr.callee) || t4.isMemberExpression(expr.callee))) {
27895
- return expr.callee;
27896
- }
27897
28397
  return expr;
27898
28398
  };
27899
28399
  const normalizedDataHandler = dataBinding !== null ? normalizeHandler(
@@ -30344,11 +30844,27 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30344
30844
  const constArrays = /* @__PURE__ */ new Map();
30345
30845
  const cseMap = /* @__PURE__ */ new Map();
30346
30846
  const instructions = [];
30847
+ const deleteByBase = (map, name) => {
30848
+ const base = getSSABaseName(name);
30849
+ for (const key of Array.from(map.keys())) {
30850
+ if (getSSABaseName(key) === base) {
30851
+ map.delete(key);
30852
+ }
30853
+ }
30854
+ };
30347
30855
  const invalidateCSE = (name) => {
30856
+ const base = getSSABaseName(name);
30348
30857
  const toDelete = [];
30349
30858
  for (const [hash, entry] of cseMap.entries()) {
30350
- if (entry.name === name || entry.deps.has(name)) {
30859
+ if (getSSABaseName(entry.name) === base) {
30351
30860
  toDelete.push(hash);
30861
+ continue;
30862
+ }
30863
+ for (const dep of entry.deps) {
30864
+ if (getSSABaseName(dep) === base) {
30865
+ toDelete.push(hash);
30866
+ break;
30867
+ }
30352
30868
  }
30353
30869
  }
30354
30870
  toDelete.forEach((hash) => cseMap.delete(hash));
@@ -30358,22 +30874,22 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30358
30874
  const target = instr.target.name;
30359
30875
  const declKind = instr.declarationKind;
30360
30876
  invalidateCSE(target);
30361
- constants.delete(target);
30362
- constObjects.delete(target);
30363
- constArrays.delete(target);
30877
+ deleteByBase(constants, target);
30878
+ deleteByBase(constObjects, target);
30879
+ deleteByBase(constArrays, target);
30364
30880
  const sideWrites = collectWriteTargets(instr.value);
30365
30881
  for (const name of sideWrites) {
30366
30882
  if (name !== target) {
30367
- constants.delete(name);
30883
+ deleteByBase(constants, name);
30368
30884
  invalidateCSE(name);
30369
30885
  }
30370
- constObjects.delete(name);
30371
- constArrays.delete(name);
30886
+ deleteByBase(constObjects, name);
30887
+ deleteByBase(constArrays, name);
30372
30888
  }
30373
30889
  const memberCalls = collectMemberCallTargets(instr.value);
30374
30890
  for (const name of memberCalls) {
30375
- constObjects.delete(name);
30376
- constArrays.delete(name);
30891
+ deleteByBase(constObjects, name);
30892
+ deleteByBase(constArrays, name);
30377
30893
  }
30378
30894
  const dependsOnReactiveValue = expressionDependsOnReactive(instr.value, reactive);
30379
30895
  let value = dependsOnReactiveValue ? instr.value : foldExpressionWithConstants(instr.value, constants, options, constObjects, constArrays);
@@ -30413,15 +30929,15 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30413
30929
  if (instr.kind === "Expression") {
30414
30930
  const writes = collectWriteTargets(instr.value);
30415
30931
  for (const name of writes) {
30416
- constants.delete(name);
30932
+ deleteByBase(constants, name);
30417
30933
  invalidateCSE(name);
30418
- constObjects.delete(name);
30419
- constArrays.delete(name);
30934
+ deleteByBase(constObjects, name);
30935
+ deleteByBase(constArrays, name);
30420
30936
  }
30421
30937
  const memberCalls = collectMemberCallTargets(instr.value);
30422
30938
  for (const name of memberCalls) {
30423
- constObjects.delete(name);
30424
- constArrays.delete(name);
30939
+ deleteByBase(constObjects, name);
30940
+ deleteByBase(constArrays, name);
30425
30941
  }
30426
30942
  const dependsOnReactiveValue = expressionDependsOnReactive(instr.value, reactive);
30427
30943
  const value = dependsOnReactiveValue ? instr.value : foldExpressionWithConstants(instr.value, constants, options, constObjects, constArrays);
@@ -31071,6 +31587,8 @@ function collectWriteTargets(expr) {
31071
31587
  } else if (left.kind === "MemberExpression" || left.kind === "OptionalMemberExpression") {
31072
31588
  const base = getMemberBaseIdentifier(left);
31073
31589
  if (base) writes.add(base.name);
31590
+ visit(left.object);
31591
+ if (left.computed) visit(left.property);
31074
31592
  } else {
31075
31593
  visit(left);
31076
31594
  }
@@ -31087,8 +31605,10 @@ function collectWriteTargets(expr) {
31087
31605
  const base = getMemberBaseIdentifier(arg);
31088
31606
  if (base) {
31089
31607
  writes.add(base.name);
31090
- return;
31091
31608
  }
31609
+ visit(arg.object);
31610
+ if (arg.computed) visit(arg.property);
31611
+ return;
31092
31612
  }
31093
31613
  visit(arg);
31094
31614
  return;
@@ -31394,6 +31914,16 @@ function propagateConstants(fn, options) {
31394
31914
  }
31395
31915
  function computeConstantMap(fn) {
31396
31916
  const constants = /* @__PURE__ */ new Map();
31917
+ const invalidateWrittenName = (writtenName) => {
31918
+ const writtenBase = getSSABaseName(writtenName);
31919
+ let removed = false;
31920
+ for (const constantName of Array.from(constants.keys())) {
31921
+ if (constantName === writtenName || getSSABaseName(constantName) === writtenBase) {
31922
+ removed = constants.delete(constantName) || removed;
31923
+ }
31924
+ }
31925
+ return removed;
31926
+ };
31397
31927
  let changed = true;
31398
31928
  let iterations = 0;
31399
31929
  const maxIterations = 10;
@@ -31402,6 +31932,14 @@ function computeConstantMap(fn) {
31402
31932
  changed = false;
31403
31933
  for (const block of fn.blocks) {
31404
31934
  for (const instr of block.instructions) {
31935
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
31936
+ const writes = collectWriteTargets(instr.value);
31937
+ for (const writtenName of writes) {
31938
+ if (invalidateWrittenName(writtenName)) {
31939
+ changed = true;
31940
+ }
31941
+ }
31942
+ }
31405
31943
  if (instr.kind === "Assign") {
31406
31944
  const value = evaluateConstant(instr.value, constants);
31407
31945
  if (value !== UNKNOWN_CONST) {
@@ -33288,9 +33826,18 @@ function isInsideLoop(path2) {
33288
33826
  );
33289
33827
  }
33290
33828
  function isInsideConditional(path2) {
33291
- return !!path2.findParent(
33292
- (p) => p.isIfStatement?.() || p.isConditionalExpression?.() || p.isSwitchCase?.()
33293
- );
33829
+ let current = path2;
33830
+ while (current?.parentPath) {
33831
+ const parent = current.parentPath;
33832
+ if (parent.isIfStatement?.() || parent.isConditionalExpression?.() || parent.isSwitchCase?.()) {
33833
+ return true;
33834
+ }
33835
+ if (parent.isLogicalExpression?.() && current.key === "right") {
33836
+ return true;
33837
+ }
33838
+ current = parent;
33839
+ }
33840
+ return false;
33294
33841
  }
33295
33842
  function isInsideJSX(path2) {
33296
33843
  return !!path2.findParent((p) => p.isJSXElement?.() || p.isJSXFragment?.());