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