@fictjs/compiler 0.9.0 → 0.10.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 +706 -170
  2. package/dist/index.js +706 -170
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -19942,15 +19942,38 @@ function buildEffectCall(ctx, t4, effectFn, options) {
19942
19942
  }
19943
19943
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.useEffect), args);
19944
19944
  }
19945
- function buildMemoCall(ctx, t4, memoFn, slot) {
19945
+ function buildMemoCall(ctx, t4, memoFn, options) {
19946
+ const slot = options?.slot;
19947
+ const memoOptionsProperties = [];
19948
+ if (options?.name) {
19949
+ memoOptionsProperties.push(
19950
+ t4.objectProperty(t4.identifier("name"), t4.stringLiteral(options.name))
19951
+ );
19952
+ }
19953
+ if (options?.source) {
19954
+ memoOptionsProperties.push(
19955
+ t4.objectProperty(t4.identifier("devToolsSource"), t4.stringLiteral(options.source))
19956
+ );
19957
+ }
19958
+ if (options?.internal) {
19959
+ memoOptionsProperties.push(t4.objectProperty(t4.identifier("internal"), t4.booleanLiteral(true)));
19960
+ }
19961
+ const memoOptions = memoOptionsProperties.length > 0 ? t4.objectExpression(memoOptionsProperties) : null;
19946
19962
  if (ctx.inModule) {
19947
19963
  ctx.helpersUsed.add("memo");
19948
- return t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), [memoFn]);
19964
+ const args2 = [memoFn];
19965
+ if (memoOptions) args2.push(memoOptions);
19966
+ return t4.callExpression(t4.identifier(RUNTIME_ALIASES.memo), args2);
19949
19967
  }
19950
19968
  ctx.helpersUsed.add("useMemo");
19951
19969
  ctx.needsCtx = true;
19952
19970
  const args = [t4.identifier("__fictCtx"), memoFn];
19953
- if (slot !== void 0 && slot >= 0) {
19971
+ if (memoOptions) {
19972
+ args.push(memoOptions);
19973
+ if (slot !== void 0 && slot >= 0) {
19974
+ args.push(t4.numericLiteral(slot));
19975
+ }
19976
+ } else if (slot !== void 0 && slot >= 0) {
19954
19977
  args.push(t4.numericLiteral(slot));
19955
19978
  }
19956
19979
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.useMemo), args);
@@ -21211,7 +21234,10 @@ function wrapInMemo(region, t4, declaredVars, ctx, bodyStatementsOverride, outpu
21211
21234
  const returnObj = t4.objectExpression(uniqueOutputNames.map((name) => buildOutputProperty(name)));
21212
21235
  const memoBody = t4.blockStatement([...bodyStatements, t4.returnStatement(returnObj)]);
21213
21236
  const slot = ctx.inModule ? void 0 : reserveHookSlot(ctx);
21214
- const memoCall = buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], memoBody), slot);
21237
+ const memoCall = buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], memoBody), {
21238
+ slot,
21239
+ internal: true
21240
+ });
21215
21241
  const regionVarName = `__region_${region.id}`;
21216
21242
  statements.push(
21217
21243
  t4.variableDeclaration("const", [t4.variableDeclarator(t4.identifier(regionVarName), memoCall)])
@@ -21470,7 +21496,10 @@ function generateLazyConditionalMemo(region, orderedOutputs, bodyStatements, con
21470
21496
  ctx,
21471
21497
  t4,
21472
21498
  t4.arrowFunctionExpression([], t4.blockStatement(memoBody)),
21473
- ctx.inModule ? void 0 : reserveHookSlot(ctx)
21499
+ {
21500
+ slot: ctx.inModule ? void 0 : reserveHookSlot(ctx),
21501
+ internal: true
21502
+ }
21474
21503
  );
21475
21504
  statements.push(
21476
21505
  t4.variableDeclaration("const", [t4.variableDeclarator(t4.identifier(regionVarName), memoCall)])
@@ -21583,7 +21612,12 @@ Context: ${location}`
21583
21612
  };
21584
21613
  const buildDerivedMemoCall = (expr) => {
21585
21614
  const slot = !ctx.inModule && inRegionMemo ? reserveHookSlot(ctx) : void 0;
21586
- return buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], expr), slot);
21615
+ const source = ctx.options?.dev !== false && instr.loc ? `${ctx.options?.filename ?? ""}:${instr.loc.start.line}:${instr.loc.start.column}` : void 0;
21616
+ return buildMemoCall(ctx, t4, t4.arrowFunctionExpression([], expr), {
21617
+ slot,
21618
+ name: baseName2,
21619
+ source
21620
+ });
21587
21621
  };
21588
21622
  if (isShadowDeclaration && declKind) {
21589
21623
  ctx.trackedVars.delete(baseName2);
@@ -22945,7 +22979,7 @@ function expressionUsesIdentifier(expr, name, t4) {
22945
22979
  visit(expr);
22946
22980
  return found;
22947
22981
  }
22948
- function extractDelegatedEventData(expr, t4) {
22982
+ function extractDelegatedEventData(expr, t4, options) {
22949
22983
  const isSimpleHandler = t4.isIdentifier(expr) || t4.isMemberExpression(expr);
22950
22984
  if (isSimpleHandler) {
22951
22985
  return { handler: expr };
@@ -22958,6 +22992,9 @@ function extractDelegatedEventData(expr, t4) {
22958
22992
  if (!bodyExpr || !t4.isCallExpression(bodyExpr)) return null;
22959
22993
  if (paramNames.some((name) => expressionUsesIdentifier(bodyExpr, name, t4))) return null;
22960
22994
  if (!t4.isIdentifier(bodyExpr.callee)) return null;
22995
+ if (options?.isKnownHandlerIdentifier && !options.isKnownHandlerIdentifier(bodyExpr.callee.name)) {
22996
+ return null;
22997
+ }
22961
22998
  if (bodyExpr.arguments.length === 0) return null;
22962
22999
  if (bodyExpr.arguments.length > 1) return null;
22963
23000
  const dataArg = bodyExpr.arguments[0];
@@ -23020,6 +23057,10 @@ function extractDelegatedEventDataFromHIR(expr, ctx) {
23020
23057
  return null;
23021
23058
  }
23022
23059
  const handlerName = callee.name;
23060
+ const normalizedHandlerName = deSSAVarName(handlerName);
23061
+ if (!ctx.functionVars?.has(normalizedHandlerName)) {
23062
+ return null;
23063
+ }
23023
23064
  if (ctx.signalVars?.has(handlerName) || ctx.memoVars?.has(handlerName) || ctx.aliasVars?.has(handlerName) || ctx.storeVars?.has(handlerName) || ctx.trackedVars.has(handlerName)) {
23024
23065
  return null;
23025
23066
  }
@@ -23030,7 +23071,10 @@ function extractDelegatedEventDataFromHIR(expr, ctx) {
23030
23071
  if (isTrackedAccessor) {
23031
23072
  return null;
23032
23073
  }
23033
- const paramNames = new Set(expr.params.map((p) => p.name));
23074
+ const paramNames = new Set(expr.params.map((p) => deSSAVarName(p.name)));
23075
+ if (paramNames.has(deSSAVarName(callee.name))) {
23076
+ return null;
23077
+ }
23034
23078
  const dataExpr = bodyExpr.arguments[0];
23035
23079
  if (!dataExpr) {
23036
23080
  return null;
@@ -24197,8 +24241,8 @@ function getDependencyPathFromNode(node, t4) {
24197
24241
  }
24198
24242
  return null;
24199
24243
  }
24200
- function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parentKey, skipCurrentNode = false) {
24201
- const isCallTarget = parentKey === "callee" && (parentKind === "CallExpression" || parentKind === "OptionalCallExpression");
24244
+ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parentKey, skipCurrentNode = false, allowCallCalleeReplacement = false) {
24245
+ const isCallTarget = !allowCallCalleeReplacement && parentKey === "callee" && (parentKind === "CallExpression" || parentKind === "OptionalCallExpression");
24202
24246
  if (parentKind === "VariableDeclarator" && parentKey === "id") {
24203
24247
  return;
24204
24248
  }
@@ -24270,9 +24314,25 @@ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parent
24270
24314
  }
24271
24315
  }
24272
24316
  if (t4.isBlockStatement(node.body)) {
24273
- replaceIdentifiersWithOverrides(node.body, scopedOverrides, t4, node.type, "body");
24317
+ replaceIdentifiersWithOverrides(
24318
+ node.body,
24319
+ scopedOverrides,
24320
+ t4,
24321
+ node.type,
24322
+ "body",
24323
+ false,
24324
+ allowCallCalleeReplacement
24325
+ );
24274
24326
  } else {
24275
- replaceIdentifiersWithOverrides(node.body, scopedOverrides, t4, node.type, "body");
24327
+ replaceIdentifiersWithOverrides(
24328
+ node.body,
24329
+ scopedOverrides,
24330
+ t4,
24331
+ node.type,
24332
+ "body",
24333
+ false,
24334
+ allowCallCalleeReplacement
24335
+ );
24276
24336
  }
24277
24337
  return;
24278
24338
  }
@@ -24300,12 +24360,21 @@ function replaceIdentifiersWithOverrides(node, overrides, t4, parentKind, parent
24300
24360
  t4,
24301
24361
  node.type,
24302
24362
  key,
24303
- false
24363
+ false,
24364
+ allowCallCalleeReplacement
24304
24365
  );
24305
24366
  }
24306
24367
  }
24307
24368
  } else if (value && typeof value === "object" && "type" in value) {
24308
- replaceIdentifiersWithOverrides(value, overrides, t4, node.type, key);
24369
+ replaceIdentifiersWithOverrides(
24370
+ value,
24371
+ overrides,
24372
+ t4,
24373
+ node.type,
24374
+ key,
24375
+ false,
24376
+ allowCallCalleeReplacement
24377
+ );
24309
24378
  }
24310
24379
  }
24311
24380
  }
@@ -24578,6 +24647,124 @@ function applySelectorHoist(callbackExpr, itemParamName, keyParamName, statement
24578
24647
  }
24579
24648
 
24580
24649
  // src/ir/codegen-list-child.ts
24650
+ function getCallbackBlocks(callback) {
24651
+ if (callback.kind === "FunctionExpression") {
24652
+ return callback.body;
24653
+ }
24654
+ if (callback.kind === "ArrowFunction" && Array.isArray(callback.body)) {
24655
+ return callback.body;
24656
+ }
24657
+ return [];
24658
+ }
24659
+ function collectMapCallbackAliasDeclarations(callback) {
24660
+ const blocks = getCallbackBlocks(callback);
24661
+ if (blocks.length === 0) {
24662
+ return /* @__PURE__ */ new Map();
24663
+ }
24664
+ const paramNames = callback.kind === "ArrowFunction" || callback.kind === "FunctionExpression" ? new Set(callback.params.map((param) => param.name)) : /* @__PURE__ */ new Set();
24665
+ const declarationState = /* @__PURE__ */ new Map();
24666
+ for (const block of blocks) {
24667
+ for (const instr of block.instructions) {
24668
+ if (instr.kind !== "Assign" || instr.target.kind !== "Identifier") {
24669
+ continue;
24670
+ }
24671
+ const name = instr.target.name;
24672
+ if (paramNames.has(name)) continue;
24673
+ const isDeclaration = !!instr.declarationKind;
24674
+ const previous = declarationState.get(name);
24675
+ if (previous) {
24676
+ declarationState.set(name, {
24677
+ declarationCount: previous.declarationCount + (isDeclaration ? 1 : 0),
24678
+ hasNonDeclarationWrite: previous.hasNonDeclarationWrite || !isDeclaration,
24679
+ declarationValue: previous.declarationValue,
24680
+ lastAssignedValue: instr.value
24681
+ });
24682
+ } else {
24683
+ declarationState.set(name, {
24684
+ declarationCount: isDeclaration ? 1 : 0,
24685
+ hasNonDeclarationWrite: !isDeclaration,
24686
+ declarationValue: isDeclaration ? instr.value : null,
24687
+ lastAssignedValue: instr.value
24688
+ });
24689
+ }
24690
+ }
24691
+ }
24692
+ const aliasMap = /* @__PURE__ */ new Map();
24693
+ const effectiveBlocks = blocks.filter(
24694
+ (block) => block.instructions.length > 0 || block.terminator.kind !== "Unreachable"
24695
+ );
24696
+ const isSingleLinearBlock = effectiveBlocks.length === 1 && effectiveBlocks[0].terminator.kind === "Return";
24697
+ for (const [name, state] of declarationState) {
24698
+ if (isSingleLinearBlock) {
24699
+ if (state.declarationCount <= 1) {
24700
+ aliasMap.set(name, state.lastAssignedValue);
24701
+ }
24702
+ continue;
24703
+ }
24704
+ if (state.declarationCount === 1 && !state.hasNonDeclarationWrite && state.declarationValue) {
24705
+ aliasMap.set(name, state.declarationValue);
24706
+ }
24707
+ }
24708
+ return aliasMap;
24709
+ }
24710
+ function resolveMapCallbackKeyExpression(keyExpr, callback) {
24711
+ if (keyExpr.kind !== "Identifier") {
24712
+ return keyExpr;
24713
+ }
24714
+ const aliasMap = collectMapCallbackAliasDeclarations(callback);
24715
+ if (aliasMap.size === 0) {
24716
+ return keyExpr;
24717
+ }
24718
+ let resolved = keyExpr;
24719
+ const seen = /* @__PURE__ */ new Set();
24720
+ while (resolved.kind === "Identifier") {
24721
+ const next = aliasMap.get(resolved.name);
24722
+ if (!next || seen.has(resolved.name)) break;
24723
+ seen.add(resolved.name);
24724
+ resolved = next;
24725
+ }
24726
+ return resolved;
24727
+ }
24728
+ function collectMapCallbackLocalNames(callback) {
24729
+ const blocks = getCallbackBlocks(callback);
24730
+ if (blocks.length === 0) {
24731
+ return /* @__PURE__ */ new Set();
24732
+ }
24733
+ const paramNames = callback.kind === "ArrowFunction" || callback.kind === "FunctionExpression" ? new Set(callback.params.map((param) => deSSAVarName(param.name))) : /* @__PURE__ */ new Set();
24734
+ const locals = /* @__PURE__ */ new Set();
24735
+ for (const block of blocks) {
24736
+ for (const instr of block.instructions) {
24737
+ if (instr.kind === "Assign" && instr.target.kind === "Identifier") {
24738
+ const name = deSSAVarName(instr.target.name);
24739
+ if (!paramNames.has(name)) locals.add(name);
24740
+ }
24741
+ if (instr.kind === "Phi" && instr.target.kind === "Identifier") {
24742
+ const name = deSSAVarName(instr.target.name);
24743
+ if (!paramNames.has(name)) locals.add(name);
24744
+ }
24745
+ }
24746
+ }
24747
+ return locals;
24748
+ }
24749
+ function hasUnresolvedCallbackLocalKeyDependencies(keyExpr, callback, keyAliasDeclarations) {
24750
+ const callbackLocals = collectMapCallbackLocalNames(callback);
24751
+ if (callbackLocals.size === 0) {
24752
+ return false;
24753
+ }
24754
+ const resolvableAliases = new Set(
24755
+ Array.from(keyAliasDeclarations.keys()).map((name) => deSSAVarName(name))
24756
+ );
24757
+ const deps = /* @__PURE__ */ new Set();
24758
+ collectExpressionDependencies(keyExpr, deps);
24759
+ for (const dep of deps) {
24760
+ const base = dep.split(".")[0] ?? dep;
24761
+ if (!base) continue;
24762
+ if (callbackLocals.has(base) && !resolvableAliases.has(base)) {
24763
+ return true;
24764
+ }
24765
+ }
24766
+ return false;
24767
+ }
24581
24768
  function buildListCallExpression(expr, statements, ctx, ops) {
24582
24769
  const { t: t4 } = ctx;
24583
24770
  if (expr.kind !== "CallExpression" && expr.kind !== "OptionalCallExpression") {
@@ -24596,7 +24783,8 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24596
24783
  if (!mapCallback) {
24597
24784
  throw new Error("map callback is required");
24598
24785
  }
24599
- const keyExpr = extractKeyFromMapCallback(mapCallback);
24786
+ const extractedKeyExpr = extractKeyFromMapCallback(mapCallback);
24787
+ const keyExpr = extractedKeyExpr ? resolveMapCallbackKeyExpression(extractedKeyExpr, mapCallback) : void 0;
24600
24788
  const isKeyed = !!keyExpr;
24601
24789
  const hasRestParam = (mapCallback.kind === "ArrowFunction" || mapCallback.kind === "FunctionExpression") && Array.isArray(mapCallback.rawParams) && mapCallback.rawParams.some((param) => t4.isRestElement(param));
24602
24790
  const canConstifyKey = isKeyed && keyExpr && !hasRestParam;
@@ -24647,7 +24835,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24647
24835
  if (Object.keys(overrides).length > 0) {
24648
24836
  if (t4.isBlockStatement(callbackExpr.body)) {
24649
24837
  for (const stmt of callbackExpr.body.body) {
24650
- if (!t4.isVariableDeclaration(stmt)) continue;
24838
+ if (!t4.isVariableDeclaration(stmt) || stmt.kind !== "const") continue;
24651
24839
  for (const decl of stmt.declarations) {
24652
24840
  if (!t4.isIdentifier(decl.id) || !decl.init) continue;
24653
24841
  const replacement = t4.cloneNode(decl.init, true);
@@ -24677,7 +24865,32 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24677
24865
  }
24678
24866
  let listCall;
24679
24867
  if (isKeyed && keyExpr) {
24868
+ const keyAliasDeclarations = collectMapCallbackAliasDeclarations(mapCallback);
24869
+ const hasUnresolvedLocalKeyDeps = hasUnresolvedCallbackLocalKeyDependencies(
24870
+ keyExpr,
24871
+ mapCallback,
24872
+ keyAliasDeclarations
24873
+ );
24680
24874
  let keyExprAst = ops.lowerExpression(keyExpr, ctx);
24875
+ if (keyAliasDeclarations.size > 0) {
24876
+ const keyOverrides = {};
24877
+ for (const [name, value] of keyAliasDeclarations) {
24878
+ const replacement = ops.lowerExpression(value, ctx);
24879
+ replaceIdentifiersWithOverrides(replacement, keyOverrides, t4);
24880
+ keyOverrides[name] = () => t4.cloneNode(replacement, true);
24881
+ }
24882
+ if (Object.keys(keyOverrides).length > 0) {
24883
+ replaceIdentifiersWithOverrides(
24884
+ keyExprAst,
24885
+ keyOverrides,
24886
+ t4,
24887
+ void 0,
24888
+ void 0,
24889
+ false,
24890
+ true
24891
+ );
24892
+ }
24893
+ }
24681
24894
  if (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) {
24682
24895
  const itemParam = callbackExpr.params[0];
24683
24896
  const indexParam = callbackExpr.params[1];
@@ -24691,6 +24904,9 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24691
24904
  }
24692
24905
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? callbackExpr.params[0] : null;
24693
24906
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? callbackExpr.params[1] : null;
24907
+ if (hasUnresolvedLocalKeyDeps) {
24908
+ keyExprAst = t4.identifier(t4.isIdentifier(indexParamName) ? indexParamName.name : "__index");
24909
+ }
24694
24910
  const keyFn = t4.arrowFunctionExpression(
24695
24911
  [
24696
24912
  t4.isIdentifier(itemParamName) ? itemParamName : t4.identifier("__item"),
@@ -25132,33 +25348,42 @@ function dependencyCoveredByDeclarations(dep, region) {
25132
25348
 
25133
25349
  // src/ir/codegen-resumable-utils.ts
25134
25350
  var import_node_url2 = require("url");
25135
- function renameIdentifiersInExpr(expr, renames) {
25136
- const cloned = JSON.parse(JSON.stringify(expr));
25137
- function visit(node) {
25138
- if (!node || typeof node !== "object") return;
25139
- const n = node;
25140
- if (n.type === "Identifier" && typeof n.name === "string") {
25141
- const newName = renames.get(n.name);
25142
- if (newName) {
25143
- n.name = newName;
25144
- }
25145
- }
25146
- for (const key of Object.keys(n)) {
25147
- if (key === "loc" || key === "start" || key === "end" || key === "extra" || key === "comments" || key === "leadingComments" || key === "trailingComments") {
25148
- continue;
25149
- }
25150
- const value = n[key];
25151
- if (Array.isArray(value)) {
25152
- for (const item of value) {
25153
- visit(item);
25154
- }
25155
- } else if (value && typeof value === "object") {
25156
- visit(value);
25351
+ var import_traverse2 = __toESM(require("@babel/traverse"), 1);
25352
+ function renameIdentifiersInExpr(expr, renames, t4) {
25353
+ const traverse = import_traverse2.default.default ?? import_traverse2.default;
25354
+ const cloned = t4.cloneNode(expr, true);
25355
+ const file = t4.file(t4.program([t4.expressionStatement(cloned)]));
25356
+ traverse(file, {
25357
+ Identifier(path2) {
25358
+ const oldName = path2.node.name;
25359
+ const nextName = renames.get(oldName);
25360
+ if (!nextName) return;
25361
+ if (path2.parentPath.isObjectProperty() && path2.parentPath.node.shorthand && path2.parentPath.node.value === path2.node && t4.isIdentifier(path2.parentPath.node.key)) {
25362
+ path2.parentPath.node.shorthand = false;
25363
+ path2.parentPath.node.value = t4.identifier(nextName);
25364
+ return;
25157
25365
  }
25366
+ if (!path2.isReferencedIdentifier()) return;
25367
+ const binding = path2.scope.getBinding(oldName);
25368
+ if (binding && binding.scope !== path2.scope.getProgramParent()) return;
25369
+ path2.node.name = nextName;
25158
25370
  }
25159
- }
25160
- visit(cloned);
25161
- return cloned;
25371
+ });
25372
+ const first = file.program.body[0];
25373
+ return t4.isExpressionStatement(first) ? first.expression : cloned;
25374
+ }
25375
+ function collectFreeIdentifiersInExpr(expr, t4) {
25376
+ const traverse = import_traverse2.default.default ?? import_traverse2.default;
25377
+ const file = t4.file(t4.program([t4.expressionStatement(t4.cloneNode(expr, true))]));
25378
+ const names = /* @__PURE__ */ new Set();
25379
+ traverse(file, {
25380
+ ReferencedIdentifier(path2) {
25381
+ const name = path2.node.name;
25382
+ if (path2.scope.getBinding(name)) return;
25383
+ names.add(name);
25384
+ }
25385
+ });
25386
+ return names;
25162
25387
  }
25163
25388
  function genModuleUrlExpr(ctx) {
25164
25389
  const { t: t4 } = ctx;
@@ -25295,10 +25520,10 @@ function registerResumableComponent(componentName, ctx) {
25295
25520
  }
25296
25521
 
25297
25522
  // src/ir/codegen-resumable-events.ts
25298
- function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, containingRegion, ops) {
25523
+ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, containingRegion, ops, options) {
25299
25524
  const { t: t4 } = ctx;
25300
25525
  if (!ctx.resumableEnabled) {
25301
- return;
25526
+ return false;
25302
25527
  }
25303
25528
  const prevWrapTracked = ctx.wrapTrackedExpressions;
25304
25529
  ctx.wrapTrackedExpressions = false;
@@ -25312,53 +25537,88 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25312
25537
  const scopeParam = t4.identifier("scopeId");
25313
25538
  const ensureHandlerParam = (fn) => {
25314
25539
  if (t4.isArrowFunctionExpression(fn)) {
25315
- if (fn.params.length > 0) return fn;
25316
- return t4.arrowFunctionExpression([eventParam], fn.body, fn.async);
25540
+ return fn;
25317
25541
  }
25318
25542
  if (t4.isFunctionExpression(fn)) {
25319
- if (fn.params.length > 0) return fn;
25320
- return t4.functionExpression(fn.id, [eventParam], fn.body, fn.generator, fn.async);
25543
+ return fn;
25321
25544
  }
25322
25545
  if (t4.isIdentifier(fn) || t4.isMemberExpression(fn)) {
25323
25546
  return fn;
25324
25547
  }
25325
- if (t4.isCallExpression(fn) && fn.arguments.length === 0 && (t4.isIdentifier(fn.callee) || t4.isMemberExpression(fn.callee))) {
25326
- return fn.callee;
25327
- }
25328
25548
  return t4.functionExpression(
25329
25549
  null,
25330
- [eventParam],
25331
- t4.blockStatement([
25332
- t4.returnStatement(
25333
- t4.callExpression(
25334
- t4.memberExpression(fn, t4.identifier("call")),
25335
- [t4.thisExpression(), eventParam]
25336
- )
25337
- )
25338
- ])
25550
+ [],
25551
+ t4.blockStatement([t4.returnStatement(fn)])
25339
25552
  );
25340
25553
  };
25341
25554
  const handlerExpr = ensureHandlerParam(valueExpr);
25342
25555
  const handlerId = t4.identifier(`__fict_e${ctx.resumableHandlerCounter ?? 0}`);
25343
25556
  ctx.resumableHandlerCounter = (ctx.resumableHandlerCounter ?? 0) + 1;
25344
- const captured = /* @__PURE__ */ new Set();
25345
- collectExpressionIdentifiersDeep(expr, captured);
25557
+ const captured = collectFreeIdentifiersInExpr(handlerExpr, t4);
25346
25558
  const lexicalNames = Array.from(captured).filter((name) => ctx.signalVars?.has(name));
25347
25559
  const propsName = ctx.propsParamName && captured.has(ctx.propsParamName) ? ctx.propsParamName : null;
25560
+ const unsupportedLocals = Array.from(captured).filter((name) => {
25561
+ if (ctx.inListRender && ctx.listKeyParamName && name === ctx.listKeyParamName) return true;
25562
+ if (!ctx.localDeclaredNames?.has(name)) return false;
25563
+ if (ctx.signalVars?.has(name)) return false;
25564
+ if (ctx.functionVars?.has(name)) return false;
25565
+ if (propsName && name === propsName) return false;
25566
+ return true;
25567
+ });
25568
+ const loweredFunctionDeps = /* @__PURE__ */ new Map();
25569
+ const unsafeFunctionCaptures = [];
25570
+ for (const name of captured) {
25571
+ if (!ctx.functionVars?.has(name) || ctx.signalVars?.has(name)) continue;
25572
+ if (ctx.hoistedFunctionDepNames?.has(name)) continue;
25573
+ const hirDef = ctx.componentFunctionDefs?.get(name);
25574
+ if (!hirDef) {
25575
+ if (ctx.localDeclaredNames?.has(name)) {
25576
+ unsafeFunctionCaptures.push(`${name} -> <unhoistable>`);
25577
+ }
25578
+ continue;
25579
+ }
25580
+ const loweredFn = ops.lowerDomExpression(hirDef, ctx, null, {
25581
+ skipHookAccessors: true,
25582
+ skipRegionRootOverride: true
25583
+ });
25584
+ const fnCaptured = collectFreeIdentifiersInExpr(loweredFn, t4);
25585
+ const localFnCaptures = Array.from(fnCaptured).filter((dep) => ctx.localDeclaredNames?.has(dep)).sort();
25586
+ if (localFnCaptures.length > 0) {
25587
+ unsafeFunctionCaptures.push(`${name} -> ${localFnCaptures.join(", ")}`);
25588
+ continue;
25589
+ }
25590
+ loweredFunctionDeps.set(name, loweredFn);
25591
+ }
25592
+ if (unsupportedLocals.length > 0 || unsafeFunctionCaptures.length > 0) {
25593
+ const detailParts = [];
25594
+ if (unsupportedLocals.length > 0) {
25595
+ detailParts.push(`direct: ${unsupportedLocals.sort().join(", ")}`);
25596
+ }
25597
+ if (unsafeFunctionCaptures.length > 0) {
25598
+ detailParts.push(`function deps: ${unsafeFunctionCaptures.sort().join("; ")}`);
25599
+ }
25600
+ const detail = `Resumable handlers cannot capture non-serializable local variables (${detailParts.join(" | ")}).`;
25601
+ if (options?.explicit) {
25602
+ const loc = expr.loc?.start;
25603
+ const fileName = ctx.options?.filename ?? "<unknown>";
25604
+ const location = loc ? `${fileName}:${loc.line}:${loc.column + 1}` : fileName;
25605
+ throw new Error(
25606
+ `${detail} Use signals/props/function references or remove '$' suffix.
25607
+ at ${location}`
25608
+ );
25609
+ }
25610
+ return false;
25611
+ }
25348
25612
  const functionDepRenames = /* @__PURE__ */ new Map();
25349
25613
  for (const name of captured) {
25350
25614
  if (ctx.functionVars?.has(name) && !ctx.signalVars?.has(name)) {
25351
- const hirDef = ctx.componentFunctionDefs?.get(name);
25352
- if (!hirDef) continue;
25353
25615
  let hoistedName = ctx.hoistedFunctionDepNames?.get(name);
25354
25616
  if (!hoistedName) {
25617
+ const loweredFn = loweredFunctionDeps.get(name);
25618
+ if (!loweredFn) continue;
25355
25619
  hoistedName = `__fict_fn_${name}_${ctx.hoistedFunctionDepCounter ?? 0}`;
25356
25620
  ctx.hoistedFunctionDepCounter = (ctx.hoistedFunctionDepCounter ?? 0) + 1;
25357
25621
  ctx.hoistedFunctionDepNames?.set(name, hoistedName);
25358
- const loweredFn = ops.lowerDomExpression(hirDef, ctx, null, {
25359
- skipHookAccessors: true,
25360
- skipRegionRootOverride: true
25361
- });
25362
25622
  const hoistedDecl = t4.variableDeclaration("const", [
25363
25623
  t4.variableDeclarator(t4.identifier(hoistedName), loweredFn)
25364
25624
  ]);
@@ -25370,7 +25630,7 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25370
25630
  }
25371
25631
  let finalHandlerExpr = handlerExpr;
25372
25632
  if (functionDepRenames.size > 0) {
25373
- finalHandlerExpr = renameIdentifiersInExpr(handlerExpr, functionDepRenames);
25633
+ finalHandlerExpr = renameIdentifiersInExpr(handlerExpr, functionDepRenames, t4);
25374
25634
  }
25375
25635
  const bodyStatements = [];
25376
25636
  if (lexicalNames.length > 0) {
@@ -25403,12 +25663,97 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25403
25663
  );
25404
25664
  }
25405
25665
  const handlerVar = t4.identifier("__handler");
25666
+ const resultVar = t4.identifier("__result");
25406
25667
  bodyStatements.push(
25407
25668
  t4.variableDeclaration("const", [t4.variableDeclarator(handlerVar, finalHandlerExpr)])
25408
25669
  );
25409
25670
  bodyStatements.push(
25410
- t4.returnStatement(
25411
- t4.callExpression(t4.memberExpression(handlerVar, t4.identifier("call")), [elParam, eventParam])
25671
+ t4.ifStatement(
25672
+ t4.binaryExpression(
25673
+ "===",
25674
+ t4.unaryExpression("typeof", handlerVar),
25675
+ t4.stringLiteral("function")
25676
+ ),
25677
+ t4.blockStatement([
25678
+ t4.variableDeclaration("const", [
25679
+ t4.variableDeclarator(
25680
+ resultVar,
25681
+ t4.callExpression(t4.memberExpression(handlerVar, t4.identifier("call")), [
25682
+ elParam,
25683
+ eventParam
25684
+ ])
25685
+ )
25686
+ ]),
25687
+ t4.ifStatement(
25688
+ t4.logicalExpression(
25689
+ "&&",
25690
+ t4.binaryExpression(
25691
+ "===",
25692
+ t4.unaryExpression("typeof", resultVar),
25693
+ t4.stringLiteral("function")
25694
+ ),
25695
+ t4.binaryExpression("!==", resultVar, handlerVar)
25696
+ ),
25697
+ t4.blockStatement([
25698
+ t4.returnStatement(
25699
+ t4.callExpression(t4.memberExpression(resultVar, t4.identifier("call")), [
25700
+ elParam,
25701
+ eventParam
25702
+ ])
25703
+ )
25704
+ ])
25705
+ ),
25706
+ t4.ifStatement(
25707
+ t4.logicalExpression(
25708
+ "&&",
25709
+ resultVar,
25710
+ t4.binaryExpression(
25711
+ "===",
25712
+ t4.unaryExpression(
25713
+ "typeof",
25714
+ t4.memberExpression(resultVar, t4.identifier("handleEvent"))
25715
+ ),
25716
+ t4.stringLiteral("function")
25717
+ )
25718
+ ),
25719
+ t4.blockStatement([
25720
+ t4.returnStatement(
25721
+ t4.callExpression(
25722
+ t4.memberExpression(
25723
+ t4.memberExpression(resultVar, t4.identifier("handleEvent")),
25724
+ t4.identifier("call")
25725
+ ),
25726
+ [resultVar, eventParam]
25727
+ )
25728
+ )
25729
+ ])
25730
+ ),
25731
+ t4.returnStatement(resultVar)
25732
+ ])
25733
+ )
25734
+ );
25735
+ bodyStatements.push(
25736
+ t4.ifStatement(
25737
+ t4.logicalExpression(
25738
+ "&&",
25739
+ handlerVar,
25740
+ t4.binaryExpression(
25741
+ "===",
25742
+ t4.unaryExpression("typeof", t4.memberExpression(handlerVar, t4.identifier("handleEvent"))),
25743
+ t4.stringLiteral("function")
25744
+ )
25745
+ ),
25746
+ t4.blockStatement([
25747
+ t4.returnStatement(
25748
+ t4.callExpression(
25749
+ t4.memberExpression(
25750
+ t4.memberExpression(handlerVar, t4.identifier("handleEvent")),
25751
+ t4.identifier("call")
25752
+ ),
25753
+ [handlerVar, eventParam]
25754
+ )
25755
+ )
25756
+ ])
25412
25757
  )
25413
25758
  );
25414
25759
  const exportedHandler = t4.exportNamedDeclaration(
@@ -25437,6 +25782,7 @@ function emitResumableEventBinding(targetId, eventName, expr, statements, ctx, c
25437
25782
  ])
25438
25783
  )
25439
25784
  );
25785
+ return true;
25440
25786
  }
25441
25787
 
25442
25788
  // src/ir/codegen-runtime-imports.ts
@@ -25821,7 +26167,8 @@ function extractHIRStaticHtml(jsx, ctx, ops, parentPath = [], namespace = null)
25821
26167
  name: eventName.toLowerCase(),
25822
26168
  expr: attr.value ?? void 0,
25823
26169
  eventOptions: { capture, passive, once },
25824
- resumable: shouldBeResumable
26170
+ resumable: shouldBeResumable,
26171
+ resumableExplicit: isResumableEvent
25825
26172
  });
25826
26173
  continue;
25827
26174
  }
@@ -26394,7 +26741,7 @@ function buildOutputParams(fn, t4) {
26394
26741
  }
26395
26742
  return fn.params.map((p) => t4.identifier(deSSAVarName(p.name)));
26396
26743
  }
26397
- function lowerTrackedExpression(expr, ctx) {
26744
+ function lowerTrackedExpression(expr, ctx, valueUsed = true) {
26398
26745
  const regionOverride = ctx.inReturn && ctx.currentFnIsHook ? null : ctx.currentRegion ?? (ctx.trackedVars.size ? {
26399
26746
  id: -1,
26400
26747
  dependencies: new Set(ctx.trackedVars),
@@ -26402,7 +26749,7 @@ function lowerTrackedExpression(expr, ctx) {
26402
26749
  hasControlFlow: false,
26403
26750
  hasReactiveWrites: false
26404
26751
  } : null);
26405
- const lowered = lowerExpression2(expr, ctx);
26752
+ const lowered = lowerExpression2(expr, ctx, valueUsed);
26406
26753
  if (ctx.t.isAssignmentExpression(lowered)) {
26407
26754
  const right = applyRegionMetadataToExpression2(lowered.right, ctx, regionOverride ?? void 0);
26408
26755
  return ctx.t.assignmentExpression(lowered.operator, lowered.left, right);
@@ -26524,7 +26871,7 @@ function lowerInstruction(instr, ctx) {
26524
26871
  );
26525
26872
  }
26526
26873
  if (instr.kind === "Expression") {
26527
- return applyLoc(t4.expressionStatement(lowerTrackedExpression(instr.value, ctx)));
26874
+ return applyLoc(t4.expressionStatement(lowerTrackedExpression(instr.value, ctx, false)));
26528
26875
  }
26529
26876
  if (instr.kind === "Phi") {
26530
26877
  return null;
@@ -26688,7 +27035,7 @@ function collectLocalDeclaredNames(params, blocks, t4) {
26688
27035
  }
26689
27036
  return declared;
26690
27037
  }
26691
- function lowerExpression2(expr, ctx, isAssigned = false) {
27038
+ function lowerExpression2(expr, ctx, valueUsed = true) {
26692
27039
  const depth = (ctx.expressionDepth ?? 0) + 1;
26693
27040
  const maxDepth = ctx.maxExpressionDepth ?? 500;
26694
27041
  if (depth > maxDepth) {
@@ -26699,12 +27046,12 @@ function lowerExpression2(expr, ctx, isAssigned = false) {
26699
27046
  }
26700
27047
  ctx.expressionDepth = depth;
26701
27048
  try {
26702
- return setNodeLoc(lowerExpressionImpl(expr, ctx, isAssigned), expr.loc);
27049
+ return setNodeLoc(lowerExpressionImpl(expr, ctx, valueUsed), expr.loc);
26703
27050
  } finally {
26704
27051
  ctx.expressionDepth = depth - 1;
26705
27052
  }
26706
27053
  }
26707
- function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27054
+ function lowerExpressionImpl(expr, ctx, valueUsed = true) {
26708
27055
  const { t: t4 } = ctx;
26709
27056
  const mapParams = (params) => params.map((p) => t4.identifier(deSSAVarName(p.name)));
26710
27057
  const lowerArgsAsExpressions = (args) => args.map(
@@ -26745,6 +27092,152 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
26745
27092
  ctx.localDeclaredNames = prevLocalDeclared;
26746
27093
  return result;
26747
27094
  };
27095
+ const lowerTrackedWriteCall = (callee, nextValue) => {
27096
+ if (!valueUsed) {
27097
+ return t4.callExpression(t4.cloneNode(callee, true), [nextValue]);
27098
+ }
27099
+ const nextId = genTemp3(ctx, "next");
27100
+ const nextRef = t4.identifier(nextId.name);
27101
+ return t4.callExpression(
27102
+ t4.arrowFunctionExpression(
27103
+ [t4.cloneNode(nextId, true)],
27104
+ t4.sequenceExpression([
27105
+ t4.callExpression(t4.cloneNode(callee, true), [nextRef]),
27106
+ t4.identifier(nextId.name)
27107
+ ])
27108
+ ),
27109
+ [nextValue]
27110
+ );
27111
+ };
27112
+ const buildTrackedAssignmentNext = (operator, current, right) => {
27113
+ switch (operator) {
27114
+ case "=":
27115
+ return right;
27116
+ case "+=":
27117
+ return t4.binaryExpression("+", current, right);
27118
+ case "-=":
27119
+ return t4.binaryExpression("-", current, right);
27120
+ case "*=":
27121
+ return t4.binaryExpression("*", current, right);
27122
+ case "/=":
27123
+ return t4.binaryExpression("/", current, right);
27124
+ case "%=":
27125
+ return t4.binaryExpression("%", current, right);
27126
+ case "**=":
27127
+ return t4.binaryExpression("**", current, right);
27128
+ case "<<=":
27129
+ return t4.binaryExpression("<<", current, right);
27130
+ case ">>=":
27131
+ return t4.binaryExpression(">>", current, right);
27132
+ case ">>>=":
27133
+ return t4.binaryExpression(">>>", current, right);
27134
+ case "|=":
27135
+ return t4.binaryExpression("|", current, right);
27136
+ case "^=":
27137
+ return t4.binaryExpression("^", current, right);
27138
+ case "&=":
27139
+ return t4.binaryExpression("&", current, right);
27140
+ case "&&=":
27141
+ return t4.logicalExpression("&&", current, right);
27142
+ case "||=":
27143
+ return t4.logicalExpression("||", current, right);
27144
+ case "??=":
27145
+ return t4.logicalExpression("??", current, right);
27146
+ default:
27147
+ return right;
27148
+ }
27149
+ };
27150
+ const buildStaticSignalKeyTest = (keyRef, keys) => {
27151
+ if (keys.length === 0) return null;
27152
+ let test = null;
27153
+ for (const key of keys) {
27154
+ const literal = typeof key === "number" ? t4.numericLiteral(key) : t4.stringLiteral(String(key));
27155
+ const eq = t4.binaryExpression("===", t4.cloneNode(keyRef, true), literal);
27156
+ test = test ? t4.logicalExpression("||", test, eq) : eq;
27157
+ }
27158
+ return test;
27159
+ };
27160
+ const lowerComputedHookSignalAssignment = (objectName, keyExpr, signalKeys, operator, rightExpr) => {
27161
+ const keyTestKeys = signalKeys.filter(
27162
+ (key) => typeof key === "number" && Number.isFinite(key) || typeof key === "string"
27163
+ );
27164
+ if (keyTestKeys.length === 0) return null;
27165
+ const keyId = genTemp3(ctx, "key");
27166
+ const keyRef = t4.identifier(keyId.name);
27167
+ const memberForAccessor = t4.memberExpression(
27168
+ t4.identifier(objectName),
27169
+ t4.identifier(keyId.name),
27170
+ true
27171
+ );
27172
+ const current = t4.callExpression(t4.cloneNode(memberForAccessor, true), []);
27173
+ const right = lowerExpression2(rightExpr, ctx);
27174
+ const signalWrite = lowerTrackedWriteCall(
27175
+ memberForAccessor,
27176
+ buildTrackedAssignmentNext(operator, current, t4.cloneNode(right, true))
27177
+ );
27178
+ const fallback = t4.assignmentExpression(
27179
+ operator,
27180
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27181
+ right
27182
+ );
27183
+ const keyTest = buildStaticSignalKeyTest(keyRef, keyTestKeys);
27184
+ if (!keyTest) return null;
27185
+ return t4.callExpression(
27186
+ t4.arrowFunctionExpression(
27187
+ [t4.cloneNode(keyId, true)],
27188
+ t4.conditionalExpression(keyTest, signalWrite, fallback)
27189
+ ),
27190
+ [lowerExpression2(keyExpr, ctx)]
27191
+ );
27192
+ };
27193
+ const lowerComputedHookSignalUpdate = (objectName, keyExpr, signalKeys, operator, prefix) => {
27194
+ const keyTestKeys = signalKeys.filter(
27195
+ (key) => typeof key === "number" && Number.isFinite(key) || typeof key === "string"
27196
+ );
27197
+ if (keyTestKeys.length === 0) return null;
27198
+ const keyId = genTemp3(ctx, "key");
27199
+ const keyRef = t4.identifier(keyId.name);
27200
+ const signalUpdate = lowerTrackedUpdateCall(
27201
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27202
+ operator,
27203
+ prefix
27204
+ );
27205
+ const fallback = t4.updateExpression(
27206
+ operator,
27207
+ t4.memberExpression(t4.identifier(objectName), t4.identifier(keyId.name), true),
27208
+ prefix
27209
+ );
27210
+ const keyTest = buildStaticSignalKeyTest(keyRef, keyTestKeys);
27211
+ if (!keyTest) return null;
27212
+ return t4.callExpression(
27213
+ t4.arrowFunctionExpression(
27214
+ [t4.cloneNode(keyId, true)],
27215
+ t4.conditionalExpression(keyTest, signalUpdate, fallback)
27216
+ ),
27217
+ [lowerExpression2(keyExpr, ctx)]
27218
+ );
27219
+ };
27220
+ const lowerTrackedUpdateCall = (callee, operator, prefix) => {
27221
+ const op = operator === "++" ? "+" : "-";
27222
+ const delta = t4.numericLiteral(1);
27223
+ const current = t4.callExpression(t4.cloneNode(callee, true), []);
27224
+ if (!valueUsed) {
27225
+ return t4.callExpression(t4.cloneNode(callee, true), [t4.binaryExpression(op, current, delta)]);
27226
+ }
27227
+ const prevId = genTemp3(ctx, "prev");
27228
+ const prevForSet = t4.identifier(prevId.name);
27229
+ const prevForResult = t4.identifier(prevId.name);
27230
+ return t4.callExpression(
27231
+ t4.arrowFunctionExpression(
27232
+ [t4.cloneNode(prevId, true)],
27233
+ t4.sequenceExpression([
27234
+ t4.callExpression(t4.cloneNode(callee, true), [t4.binaryExpression(op, prevForSet, delta)]),
27235
+ prefix ? t4.binaryExpression(op, prevForResult, t4.numericLiteral(1)) : prevForResult
27236
+ ])
27237
+ ),
27238
+ [current]
27239
+ );
27240
+ };
26748
27241
  const lowerBlocksToStatements = (blocks) => {
26749
27242
  const stmts = [];
26750
27243
  for (const block of blocks) {
@@ -26914,6 +27407,24 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
26914
27407
  expr.arguments,
26915
27408
  (arg) => arg.kind === "ArrowFunction" || arg.kind === "FunctionExpression" ? withNonReactiveScope(ctx, () => lowerExpression2(arg, ctx)) : lowerExpression2(arg, ctx)
26916
27409
  );
27410
+ const includeDevtools = ctx.options?.dev !== false;
27411
+ if (includeDevtools && expr.loc) {
27412
+ const source = `${ctx.options?.filename ?? ""}:${expr.loc.start.line}:${expr.loc.start.column}`;
27413
+ const sourceProp = t4.objectProperty(
27414
+ t4.identifier("devToolsSource"),
27415
+ t4.stringLiteral(source)
27416
+ );
27417
+ if (args.length === 1) {
27418
+ args.push(t4.objectExpression([sourceProp]));
27419
+ } else if (args.length > 1 && t4.isObjectExpression(args[1])) {
27420
+ const hasSourceProp = args[1].properties.some(
27421
+ (prop) => t4.isObjectProperty(prop) && t4.isIdentifier(prop.key) && prop.key.name === "devToolsSource"
27422
+ );
27423
+ if (!hasSourceProp) {
27424
+ args[1].properties.push(sourceProp);
27425
+ }
27426
+ }
27427
+ }
26917
27428
  if (ctx.inModule) {
26918
27429
  ctx.helpersUsed.add("effect");
26919
27430
  return t4.callExpression(t4.identifier(RUNTIME_ALIASES.effect), args);
@@ -27221,59 +27732,42 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27221
27732
  expr.left.computed,
27222
27733
  expr.left.optional
27223
27734
  );
27224
- const current = t4.callExpression(member, []);
27735
+ const current = t4.callExpression(t4.cloneNode(member, true), []);
27225
27736
  const right = lowerExpression2(expr.right, ctx);
27226
- let next;
27227
- switch (expr.operator) {
27228
- case "=":
27229
- next = right;
27230
- break;
27231
- case "+=":
27232
- next = t4.binaryExpression("+", current, right);
27233
- break;
27234
- case "-=":
27235
- next = t4.binaryExpression("-", current, right);
27236
- break;
27237
- case "*=":
27238
- next = t4.binaryExpression("*", current, right);
27239
- break;
27240
- case "/=":
27241
- next = t4.binaryExpression("/", current, right);
27242
- break;
27243
- default:
27244
- next = right;
27737
+ const next = buildTrackedAssignmentNext(expr.operator, current, right);
27738
+ return lowerTrackedWriteCall(member, next);
27739
+ }
27740
+ if (expr.left.computed) {
27741
+ const signalKeys = [];
27742
+ if (info?.objectProps) {
27743
+ for (const [key, accessorKind] of info.objectProps.entries()) {
27744
+ if (accessorKind === "signal") signalKeys.push(key);
27745
+ }
27245
27746
  }
27246
- return t4.callExpression(member, [next]);
27747
+ if (info?.arrayProps) {
27748
+ for (const [key, accessorKind] of info.arrayProps.entries()) {
27749
+ if (accessorKind === "signal") signalKeys.push(key);
27750
+ }
27751
+ }
27752
+ const lowered = lowerComputedHookSignalAssignment(
27753
+ deSSAVarName(expr.left.object.name),
27754
+ expr.left.property,
27755
+ signalKeys,
27756
+ expr.operator,
27757
+ expr.right
27758
+ );
27759
+ if (lowered) return lowered;
27247
27760
  }
27248
27761
  }
27249
27762
  }
27250
27763
  if (expr.left.kind === "Identifier") {
27251
27764
  const baseName2 = deSSAVarName(expr.left.name);
27252
27765
  if (ctx.trackedVars.has(baseName2)) {
27253
- const id = t4.identifier(baseName2);
27766
+ const callee = t4.identifier(baseName2);
27254
27767
  const current = t4.callExpression(t4.identifier(baseName2), []);
27255
27768
  const right = lowerExpression2(expr.right, ctx);
27256
- let next;
27257
- switch (expr.operator) {
27258
- case "=":
27259
- next = right;
27260
- break;
27261
- case "+=":
27262
- next = t4.binaryExpression("+", current, right);
27263
- break;
27264
- case "-=":
27265
- next = t4.binaryExpression("-", current, right);
27266
- break;
27267
- case "*=":
27268
- next = t4.binaryExpression("*", current, right);
27269
- break;
27270
- case "/=":
27271
- next = t4.binaryExpression("/", current, right);
27272
- break;
27273
- default:
27274
- next = right;
27275
- }
27276
- return t4.callExpression(id, [next]);
27769
+ const next = buildTrackedAssignmentNext(expr.operator, current, right);
27770
+ return lowerTrackedWriteCall(callee, next);
27277
27771
  }
27278
27772
  }
27279
27773
  return t4.assignmentExpression(
@@ -27305,21 +27799,35 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27305
27799
  expr.argument.computed,
27306
27800
  expr.argument.optional
27307
27801
  );
27308
- const current = t4.callExpression(member, []);
27309
- const delta = t4.numericLiteral(1);
27310
- const next = expr.operator === "++" ? t4.binaryExpression("+", current, delta) : t4.binaryExpression("-", current, delta);
27311
- return t4.callExpression(member, [next]);
27802
+ return lowerTrackedUpdateCall(member, expr.operator, expr.prefix);
27803
+ }
27804
+ if (expr.argument.computed) {
27805
+ const signalKeys = [];
27806
+ if (info?.objectProps) {
27807
+ for (const [key, accessorKind] of info.objectProps.entries()) {
27808
+ if (accessorKind === "signal") signalKeys.push(key);
27809
+ }
27810
+ }
27811
+ if (info?.arrayProps) {
27812
+ for (const [key, accessorKind] of info.arrayProps.entries()) {
27813
+ if (accessorKind === "signal") signalKeys.push(key);
27814
+ }
27815
+ }
27816
+ const lowered = lowerComputedHookSignalUpdate(
27817
+ deSSAVarName(expr.argument.object.name),
27818
+ expr.argument.property,
27819
+ signalKeys,
27820
+ expr.operator,
27821
+ expr.prefix
27822
+ );
27823
+ if (lowered) return lowered;
27312
27824
  }
27313
27825
  }
27314
27826
  }
27315
27827
  if (expr.argument.kind === "Identifier") {
27316
27828
  const baseName2 = deSSAVarName(expr.argument.name);
27317
27829
  if (ctx.trackedVars.has(baseName2)) {
27318
- const id = t4.identifier(baseName2);
27319
- const current = t4.callExpression(t4.identifier(baseName2), []);
27320
- const delta = t4.numericLiteral(1);
27321
- const next = expr.operator === "++" ? t4.binaryExpression("+", current, delta) : t4.binaryExpression("-", current, delta);
27322
- return t4.callExpression(id, [next]);
27830
+ return lowerTrackedUpdateCall(t4.identifier(baseName2), expr.operator, expr.prefix);
27323
27831
  }
27324
27832
  }
27325
27833
  return t4.updateExpression(
@@ -27341,7 +27849,11 @@ function lowerExpressionImpl(expr, ctx, _isAssigned = false) {
27341
27849
  case "NewExpression":
27342
27850
  return t4.newExpression(lowerExpression2(expr.callee, ctx), lowerCallArguments(expr.arguments));
27343
27851
  case "SequenceExpression":
27344
- return t4.sequenceExpression(expr.expressions.map((e) => lowerExpression2(e, ctx)));
27852
+ return t4.sequenceExpression(
27853
+ expr.expressions.map(
27854
+ (e, index) => lowerExpression2(e, ctx, index === expr.expressions.length - 1 ? valueUsed : false)
27855
+ )
27856
+ );
27345
27857
  case "YieldExpression":
27346
27858
  return t4.yieldExpression(
27347
27859
  expr.argument ? lowerExpression2(expr.argument, ctx) : null,
@@ -27799,16 +28311,17 @@ function lowerIntrinsicElement(jsx, ctx) {
27799
28311
  const hasEventOptions = binding.eventOptions && (binding.eventOptions.capture || binding.eventOptions.passive || binding.eventOptions.once);
27800
28312
  const isDelegated = DelegatedEvents.has(eventName) && !hasEventOptions;
27801
28313
  if (binding.resumable && !hasEventOptions) {
27802
- emitResumableEventBinding(
28314
+ const emitted = emitResumableEventBinding(
27803
28315
  targetId,
27804
28316
  eventName,
27805
28317
  binding.expr,
27806
28318
  statements,
27807
28319
  ctx,
27808
28320
  containingRegion,
27809
- createResumableEventBindingOps()
28321
+ createResumableEventBindingOps(),
28322
+ { explicit: binding.resumableExplicit === true }
27810
28323
  );
27811
- continue;
28324
+ if (emitted) continue;
27812
28325
  }
27813
28326
  const hirDataBinding = isDelegated && binding.expr ? extractDelegatedEventDataFromHIR(binding.expr, ctx) : null;
27814
28327
  if (hirDataBinding) {
@@ -27850,34 +28363,24 @@ function lowerIntrinsicElement(jsx, ctx) {
27850
28363
  const isFn = t4.isArrowFunctionExpression(valueExpr) || t4.isFunctionExpression(valueExpr);
27851
28364
  const ensureHandlerParam = (fn) => {
27852
28365
  if (t4.isArrowFunctionExpression(fn)) {
27853
- if (fn.params.length > 0) return fn;
27854
- return t4.arrowFunctionExpression([eventParam], fn.body, fn.async);
28366
+ return fn;
27855
28367
  }
27856
28368
  if (t4.isFunctionExpression(fn)) {
27857
- if (fn.params.length > 0) return fn;
27858
- return t4.functionExpression(fn.id, [eventParam], fn.body, fn.generator, fn.async);
28369
+ return fn;
27859
28370
  }
27860
28371
  if (t4.isIdentifier(fn) || t4.isMemberExpression(fn)) {
27861
28372
  return fn;
27862
28373
  }
27863
- if (t4.isCallExpression(fn) && fn.arguments.length === 0 && (t4.isIdentifier(fn.callee) || t4.isMemberExpression(fn.callee))) {
27864
- return fn.callee;
27865
- }
27866
28374
  return t4.functionExpression(
27867
28375
  null,
27868
- [eventParam],
27869
- t4.blockStatement([
27870
- t4.returnStatement(
27871
- t4.callExpression(
27872
- t4.memberExpression(fn, t4.identifier("call")),
27873
- [t4.thisExpression(), eventParam]
27874
- )
27875
- )
27876
- ])
28376
+ [],
28377
+ t4.blockStatement([t4.returnStatement(fn)])
27877
28378
  );
27878
28379
  };
27879
28380
  const handlerExpr = !isFn && shouldWrapHandler ? t4.arrowFunctionExpression([], valueExpr) : ensureHandlerParam(valueExpr);
27880
- let dataBinding = isDelegated && !shouldWrapHandler ? extractDelegatedEventData(valueExpr, t4) : null;
28381
+ let dataBinding = isDelegated && !shouldWrapHandler ? extractDelegatedEventData(valueExpr, t4, {
28382
+ isKnownHandlerIdentifier: (name) => ctx.functionVars?.has(deSSAVarName(name)) ?? false
28383
+ }) : null;
27881
28384
  if (dataBinding && t4.isIdentifier(dataBinding.handler)) {
27882
28385
  const handlerName = dataBinding.handler.name;
27883
28386
  if (ctx.signalVars?.has(handlerName) || ctx.memoVars?.has(handlerName) || ctx.aliasVars?.has(handlerName) || ctx.storeVars?.has(handlerName) || ctx.trackedVars.has(handlerName)) {
@@ -27889,8 +28392,6 @@ function lowerIntrinsicElement(jsx, ctx) {
27889
28392
  let handlerName = null;
27890
28393
  if (t4.isIdentifier(valueExpr)) {
27891
28394
  handlerName = valueExpr.name;
27892
- } else if (t4.isCallExpression(valueExpr) && valueExpr.arguments.length === 0 && t4.isIdentifier(valueExpr.callee)) {
27893
- handlerName = valueExpr.callee.name;
27894
28395
  }
27895
28396
  const handlerForCall = handlerName ? t4.identifier(handlerName) : t4.cloneNode(valueExpr, true);
27896
28397
  const finalHandler = !isFn && shouldWrapHandler ? t4.functionExpression(
@@ -27906,9 +28407,6 @@ function lowerIntrinsicElement(jsx, ctx) {
27906
28407
  ])
27907
28408
  ) : handlerExpr;
27908
28409
  const normalizeHandler = (expr) => {
27909
- if (t4.isCallExpression(expr) && (t4.isIdentifier(expr.callee) || t4.isMemberExpression(expr.callee))) {
27910
- return expr.callee;
27911
- }
27912
28410
  return expr;
27913
28411
  };
27914
28412
  const normalizedDataHandler = dataBinding !== null ? normalizeHandler(
@@ -30359,11 +30857,27 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30359
30857
  const constArrays = /* @__PURE__ */ new Map();
30360
30858
  const cseMap = /* @__PURE__ */ new Map();
30361
30859
  const instructions = [];
30860
+ const deleteByBase = (map, name) => {
30861
+ const base = getSSABaseName(name);
30862
+ for (const key of Array.from(map.keys())) {
30863
+ if (getSSABaseName(key) === base) {
30864
+ map.delete(key);
30865
+ }
30866
+ }
30867
+ };
30362
30868
  const invalidateCSE = (name) => {
30869
+ const base = getSSABaseName(name);
30363
30870
  const toDelete = [];
30364
30871
  for (const [hash, entry] of cseMap.entries()) {
30365
- if (entry.name === name || entry.deps.has(name)) {
30872
+ if (getSSABaseName(entry.name) === base) {
30366
30873
  toDelete.push(hash);
30874
+ continue;
30875
+ }
30876
+ for (const dep of entry.deps) {
30877
+ if (getSSABaseName(dep) === base) {
30878
+ toDelete.push(hash);
30879
+ break;
30880
+ }
30367
30881
  }
30368
30882
  }
30369
30883
  toDelete.forEach((hash) => cseMap.delete(hash));
@@ -30373,22 +30887,22 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30373
30887
  const target = instr.target.name;
30374
30888
  const declKind = instr.declarationKind;
30375
30889
  invalidateCSE(target);
30376
- constants.delete(target);
30377
- constObjects.delete(target);
30378
- constArrays.delete(target);
30890
+ deleteByBase(constants, target);
30891
+ deleteByBase(constObjects, target);
30892
+ deleteByBase(constArrays, target);
30379
30893
  const sideWrites = collectWriteTargets(instr.value);
30380
30894
  for (const name of sideWrites) {
30381
30895
  if (name !== target) {
30382
- constants.delete(name);
30896
+ deleteByBase(constants, name);
30383
30897
  invalidateCSE(name);
30384
30898
  }
30385
- constObjects.delete(name);
30386
- constArrays.delete(name);
30899
+ deleteByBase(constObjects, name);
30900
+ deleteByBase(constArrays, name);
30387
30901
  }
30388
30902
  const memberCalls = collectMemberCallTargets(instr.value);
30389
30903
  for (const name of memberCalls) {
30390
- constObjects.delete(name);
30391
- constArrays.delete(name);
30904
+ deleteByBase(constObjects, name);
30905
+ deleteByBase(constArrays, name);
30392
30906
  }
30393
30907
  const dependsOnReactiveValue = expressionDependsOnReactive(instr.value, reactive);
30394
30908
  let value = dependsOnReactiveValue ? instr.value : foldExpressionWithConstants(instr.value, constants, options, constObjects, constArrays);
@@ -30428,15 +30942,15 @@ function optimizeReactiveBlock(block, reactive, purity, options) {
30428
30942
  if (instr.kind === "Expression") {
30429
30943
  const writes = collectWriteTargets(instr.value);
30430
30944
  for (const name of writes) {
30431
- constants.delete(name);
30945
+ deleteByBase(constants, name);
30432
30946
  invalidateCSE(name);
30433
- constObjects.delete(name);
30434
- constArrays.delete(name);
30947
+ deleteByBase(constObjects, name);
30948
+ deleteByBase(constArrays, name);
30435
30949
  }
30436
30950
  const memberCalls = collectMemberCallTargets(instr.value);
30437
30951
  for (const name of memberCalls) {
30438
- constObjects.delete(name);
30439
- constArrays.delete(name);
30952
+ deleteByBase(constObjects, name);
30953
+ deleteByBase(constArrays, name);
30440
30954
  }
30441
30955
  const dependsOnReactiveValue = expressionDependsOnReactive(instr.value, reactive);
30442
30956
  const value = dependsOnReactiveValue ? instr.value : foldExpressionWithConstants(instr.value, constants, options, constObjects, constArrays);
@@ -31086,6 +31600,8 @@ function collectWriteTargets(expr) {
31086
31600
  } else if (left.kind === "MemberExpression" || left.kind === "OptionalMemberExpression") {
31087
31601
  const base = getMemberBaseIdentifier(left);
31088
31602
  if (base) writes.add(base.name);
31603
+ visit(left.object);
31604
+ if (left.computed) visit(left.property);
31089
31605
  } else {
31090
31606
  visit(left);
31091
31607
  }
@@ -31102,8 +31618,10 @@ function collectWriteTargets(expr) {
31102
31618
  const base = getMemberBaseIdentifier(arg);
31103
31619
  if (base) {
31104
31620
  writes.add(base.name);
31105
- return;
31106
31621
  }
31622
+ visit(arg.object);
31623
+ if (arg.computed) visit(arg.property);
31624
+ return;
31107
31625
  }
31108
31626
  visit(arg);
31109
31627
  return;
@@ -31409,6 +31927,16 @@ function propagateConstants(fn, options) {
31409
31927
  }
31410
31928
  function computeConstantMap(fn) {
31411
31929
  const constants = /* @__PURE__ */ new Map();
31930
+ const invalidateWrittenName = (writtenName) => {
31931
+ const writtenBase = getSSABaseName(writtenName);
31932
+ let removed = false;
31933
+ for (const constantName of Array.from(constants.keys())) {
31934
+ if (constantName === writtenName || getSSABaseName(constantName) === writtenBase) {
31935
+ removed = constants.delete(constantName) || removed;
31936
+ }
31937
+ }
31938
+ return removed;
31939
+ };
31412
31940
  let changed = true;
31413
31941
  let iterations = 0;
31414
31942
  const maxIterations = 10;
@@ -31417,6 +31945,14 @@ function computeConstantMap(fn) {
31417
31945
  changed = false;
31418
31946
  for (const block of fn.blocks) {
31419
31947
  for (const instr of block.instructions) {
31948
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
31949
+ const writes = collectWriteTargets(instr.value);
31950
+ for (const writtenName of writes) {
31951
+ if (invalidateWrittenName(writtenName)) {
31952
+ changed = true;
31953
+ }
31954
+ }
31955
+ }
31420
31956
  if (instr.kind === "Assign") {
31421
31957
  const value = evaluateConstant(instr.value, constants);
31422
31958
  if (value !== UNKNOWN_CONST) {