@fictjs/compiler 0.9.0 → 0.11.0

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