@fictjs/compiler 0.14.0 → 0.16.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.
package/dist/index.cjs CHANGED
@@ -3065,7 +3065,7 @@ var require_identifier = __commonJS({
3065
3065
  value: true
3066
3066
  });
3067
3067
  exports2.isIdentifierChar = isIdentifierChar;
3068
- exports2.isIdentifierName = isIdentifierName;
3068
+ exports2.isIdentifierName = isIdentifierName2;
3069
3069
  exports2.isIdentifierStart = isIdentifierStart;
3070
3070
  var nonASCIIidentifierStartChars = "\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
3071
3071
  var nonASCIIidentifierChars = "\xB7\u0300-\u036F\u0387\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u0897-\u089F\u08CA-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B55-\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C04\u0C3C\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0CF3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D81-\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EBC\u0EC8-\u0ECE\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u180F-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19D0-\u19DA\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1ABF-\u1ADD\u1AE0-\u1AEB\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\u30FB\uA620-\uA629\uA66F\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA82C\uA880\uA881\uA8B4-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F1\uA8FF-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F\uFF65";
@@ -3106,7 +3106,7 @@ var require_identifier = __commonJS({
3106
3106
  }
3107
3107
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
3108
3108
  }
3109
- function isIdentifierName(name) {
3109
+ function isIdentifierName2(name) {
3110
3110
  let isFirst = true;
3111
3111
  for (let i = 0; i < name.length; i++) {
3112
3112
  let cp = name.charCodeAt(i);
@@ -14098,9 +14098,11 @@ var require_lib3 = __commonJS({
14098
14098
  // src/index.ts
14099
14099
  var index_exports = {};
14100
14100
  __export(index_exports, {
14101
+ analyzeFictFile: () => analyzeFictFile,
14101
14102
  clearModuleMetadata: () => clearModuleMetadata,
14102
14103
  createFictPlugin: () => createFictPlugin,
14103
14104
  default: () => index_default,
14105
+ inferTraceMarkersForComponent: () => inferTraceMarkersForComponent,
14104
14106
  resolveModuleMetadata: () => resolveModuleMetadata,
14105
14107
  setModuleMetadata: () => setModuleMetadata
14106
14108
  });
@@ -14174,6 +14176,7 @@ var RUNTIME_HELPERS = {
14174
14176
  bindEvent: "bindEvent",
14175
14177
  callEventHandler: "callEventHandler",
14176
14178
  bindRef: "bindRef",
14179
+ spread: "spread",
14177
14180
  nonReactive: "nonReactive",
14178
14181
  toNodeArray: "toNodeArray",
14179
14182
  template: "template",
@@ -14231,6 +14234,7 @@ var RUNTIME_ALIASES = {
14231
14234
  bindEvent: "bindEvent",
14232
14235
  callEventHandler: "callEventHandler",
14233
14236
  bindRef: "bindRef",
14237
+ spread: "spread",
14234
14238
  nonReactive: "nonReactive",
14235
14239
  toNodeArray: "toNodeArray",
14236
14240
  template: "template",
@@ -17086,6 +17090,37 @@ function clearModuleMetadata(options) {
17086
17090
  }
17087
17091
 
17088
17092
  // src/validation.ts
17093
+ var DiagnosticCode = /* @__PURE__ */ ((DiagnosticCode2) => {
17094
+ DiagnosticCode2["FICT_P001"] = "FICT-P001";
17095
+ DiagnosticCode2["FICT_P002"] = "FICT-P002";
17096
+ DiagnosticCode2["FICT_P003"] = "FICT-P003";
17097
+ DiagnosticCode2["FICT_P004"] = "FICT-P004";
17098
+ DiagnosticCode2["FICT_P005"] = "FICT-P005";
17099
+ DiagnosticCode2["FICT_S001"] = "FICT-S001";
17100
+ DiagnosticCode2["FICT_S002"] = "FICT-S002";
17101
+ DiagnosticCode2["FICT_E001"] = "FICT-E001";
17102
+ DiagnosticCode2["FICT_E002"] = "FICT-E002";
17103
+ DiagnosticCode2["FICT_E003"] = "FICT-E003";
17104
+ DiagnosticCode2["FICT_M001"] = "FICT-M001";
17105
+ DiagnosticCode2["FICT_M002"] = "FICT-M002";
17106
+ DiagnosticCode2["FICT_M003"] = "FICT-M003";
17107
+ DiagnosticCode2["FICT_C001"] = "FICT-C001";
17108
+ DiagnosticCode2["FICT_C002"] = "FICT-C002";
17109
+ DiagnosticCode2["FICT_C003"] = "FICT-C003";
17110
+ DiagnosticCode2["FICT_C004"] = "FICT-C004";
17111
+ DiagnosticCode2["FICT_J001"] = "FICT-J001";
17112
+ DiagnosticCode2["FICT_J002"] = "FICT-J002";
17113
+ DiagnosticCode2["FICT_J003"] = "FICT-J003";
17114
+ DiagnosticCode2["FICT_R001"] = "FICT-R001";
17115
+ DiagnosticCode2["FICT_R002"] = "FICT-R002";
17116
+ DiagnosticCode2["FICT_R003"] = "FICT-R003";
17117
+ DiagnosticCode2["FICT_R004"] = "FICT-R004";
17118
+ DiagnosticCode2["FICT_R005"] = "FICT-R005";
17119
+ DiagnosticCode2["FICT_X001"] = "FICT-X001";
17120
+ DiagnosticCode2["FICT_X002"] = "FICT-X002";
17121
+ DiagnosticCode2["FICT_X003"] = "FICT-X003";
17122
+ return DiagnosticCode2;
17123
+ })(DiagnosticCode || {});
17089
17124
  var DiagnosticMessages = {
17090
17125
  ["FICT-P001" /* FICT_P001 */]: "Props destructuring falls back to non-reactive binding.",
17091
17126
  ["FICT-P002" /* FICT_P002 */]: "Array rest in props destructuring falls back to non-reactive binding.",
@@ -17173,6 +17208,16 @@ function reportDiagnostic(ctx, code, node, context) {
17173
17208
  });
17174
17209
  }
17175
17210
  }
17211
+ function getAllDiagnosticCodes() {
17212
+ return Object.values(DiagnosticCode);
17213
+ }
17214
+ function getDiagnosticInfo(code) {
17215
+ return {
17216
+ code,
17217
+ severity: DiagnosticSeverities[code],
17218
+ message: DiagnosticMessages[code]
17219
+ };
17220
+ }
17176
17221
 
17177
17222
  // src/ir/ssa.ts
17178
17223
  function enterSSA(program) {
@@ -22616,138 +22661,212 @@ function emitConditionalChild(startMarkerId, endMarkerId, expr, statements, ctx,
22616
22661
  );
22617
22662
  }
22618
22663
 
22619
- // src/ir/codegen-expression-deps.ts
22620
- function getMemberDependencyPath(expr) {
22621
- if (expr.kind === "MemberExpression") {
22622
- const prop = expr.property;
22623
- let propName;
22624
- if (!expr.computed && prop.kind === "Identifier") {
22625
- propName = prop.name;
22626
- } else if (prop.kind === "Literal" && typeof prop.value === "string") {
22627
- propName = prop.value;
22628
- }
22629
- if (!propName) return void 0;
22630
- const object = expr.object;
22631
- if (object.kind === "Identifier") {
22632
- return `${deSSAVarName(object.name)}.${propName}`;
22633
- }
22634
- if (object.kind === "MemberExpression") {
22635
- const parent = getMemberDependencyPath(object);
22636
- return parent ? `${parent}.${propName}` : void 0;
22637
- }
22664
+ // src/ir/walk-expression.ts
22665
+ function assertNever(value) {
22666
+ throw new Error(`Unhandled node in walkExpression: ${JSON.stringify(value)}`);
22667
+ }
22668
+ function walkTerminator(term, visitNode, inFunctionBody) {
22669
+ switch (term.kind) {
22670
+ case "Return":
22671
+ if (term.argument) visitNode(term.argument, null, inFunctionBody);
22672
+ return;
22673
+ case "Throw":
22674
+ visitNode(term.argument, null, inFunctionBody);
22675
+ return;
22676
+ case "Branch":
22677
+ visitNode(term.test, null, inFunctionBody);
22678
+ return;
22679
+ case "Switch":
22680
+ visitNode(term.discriminant, null, inFunctionBody);
22681
+ term.cases.forEach((c) => {
22682
+ if (c.test) visitNode(c.test, null, inFunctionBody);
22683
+ });
22684
+ return;
22685
+ case "ForOf":
22686
+ visitNode(term.iterable, null, inFunctionBody);
22687
+ return;
22688
+ case "ForIn":
22689
+ visitNode(term.object, null, inFunctionBody);
22690
+ return;
22691
+ case "Jump":
22692
+ case "Unreachable":
22693
+ case "Break":
22694
+ case "Continue":
22695
+ case "Try":
22696
+ return;
22697
+ default:
22698
+ assertNever(term);
22699
+ }
22700
+ }
22701
+ function walkInstruction(instr, visitNode, inFunctionBody) {
22702
+ switch (instr.kind) {
22703
+ case "Assign":
22704
+ case "Expression":
22705
+ visitNode(instr.value, null, inFunctionBody);
22706
+ return;
22707
+ case "Phi":
22708
+ instr.sources.forEach((source) => visitNode(source.id, null, inFunctionBody));
22709
+ return;
22710
+ default:
22711
+ assertNever(instr);
22638
22712
  }
22639
- return void 0;
22640
22713
  }
22641
- function collectBlockDependencies(blocks, deps) {
22714
+ function walkBlocks(blocks, visitNode, inFunctionBody) {
22642
22715
  for (const block of blocks) {
22643
- for (const instr of block.instructions) {
22644
- switch (instr.kind) {
22645
- case "Assign":
22646
- case "Expression":
22647
- collectExpressionDependencies(instr.value, deps);
22648
- break;
22649
- case "Phi":
22650
- for (const source of instr.sources) {
22651
- deps.add(deSSAVarName(source.id.name));
22716
+ block.instructions.forEach((instr) => walkInstruction(instr, visitNode, inFunctionBody));
22717
+ walkTerminator(block.terminator, visitNode, inFunctionBody);
22718
+ }
22719
+ }
22720
+ function walkExpression(expr, visit, options = {}) {
22721
+ const includeFunctionBodies = options.includeFunctionBodies ?? true;
22722
+ const visitNode = (node, parent, inFunctionBody) => {
22723
+ visit(node, { parent, inFunctionBody });
22724
+ switch (node.kind) {
22725
+ case "Identifier":
22726
+ case "Literal":
22727
+ case "MetaProperty":
22728
+ case "ThisExpression":
22729
+ case "SuperExpression":
22730
+ return;
22731
+ case "ImportExpression":
22732
+ visitNode(node.source, node, inFunctionBody);
22733
+ return;
22734
+ case "CallExpression":
22735
+ case "OptionalCallExpression":
22736
+ visitNode(node.callee, node, inFunctionBody);
22737
+ node.arguments.forEach((arg) => visitNode(arg, node, inFunctionBody));
22738
+ return;
22739
+ case "MemberExpression":
22740
+ case "OptionalMemberExpression":
22741
+ visitNode(node.object, node, inFunctionBody);
22742
+ if (node.computed) {
22743
+ visitNode(node.property, node, inFunctionBody);
22744
+ }
22745
+ return;
22746
+ case "BinaryExpression":
22747
+ case "LogicalExpression":
22748
+ visitNode(node.left, node, inFunctionBody);
22749
+ visitNode(node.right, node, inFunctionBody);
22750
+ return;
22751
+ case "UnaryExpression":
22752
+ case "AwaitExpression":
22753
+ visitNode(node.argument, node, inFunctionBody);
22754
+ return;
22755
+ case "ConditionalExpression":
22756
+ visitNode(node.test, node, inFunctionBody);
22757
+ visitNode(node.consequent, node, inFunctionBody);
22758
+ visitNode(node.alternate, node, inFunctionBody);
22759
+ return;
22760
+ case "ArrayExpression":
22761
+ node.elements.forEach((el) => visitNode(el, node, inFunctionBody));
22762
+ return;
22763
+ case "ObjectExpression":
22764
+ node.properties.forEach((prop) => {
22765
+ if (prop.kind === "SpreadElement") {
22766
+ visitNode(prop.argument, node, inFunctionBody);
22767
+ } else {
22768
+ if (prop.computed) {
22769
+ visitNode(prop.key, node, inFunctionBody);
22770
+ }
22771
+ visitNode(prop.value, node, inFunctionBody);
22652
22772
  }
22653
- break;
22654
- default:
22655
- break;
22656
- }
22657
- }
22658
- const term = block.terminator;
22659
- switch (term.kind) {
22660
- case "Return":
22661
- if (term.argument) collectExpressionDependencies(term.argument, deps);
22662
- break;
22663
- case "Throw":
22664
- collectExpressionDependencies(term.argument, deps);
22665
- break;
22666
- case "Branch":
22667
- collectExpressionDependencies(term.test, deps);
22668
- break;
22669
- case "Switch":
22670
- collectExpressionDependencies(term.discriminant, deps);
22671
- for (const c of term.cases) {
22672
- if (c.test) collectExpressionDependencies(c.test, deps);
22773
+ });
22774
+ return;
22775
+ case "JSXElement":
22776
+ if (typeof node.tagName !== "string") {
22777
+ visitNode(node.tagName, node, inFunctionBody);
22673
22778
  }
22674
- break;
22675
- case "ForOf":
22676
- collectExpressionDependencies(term.iterable, deps);
22677
- break;
22678
- case "ForIn":
22679
- collectExpressionDependencies(term.object, deps);
22680
- break;
22779
+ node.attributes.forEach((attr) => {
22780
+ if (attr.isSpread && attr.spreadExpr) {
22781
+ visitNode(attr.spreadExpr, node, inFunctionBody);
22782
+ } else if (attr.value) {
22783
+ visitNode(attr.value, node, inFunctionBody);
22784
+ }
22785
+ });
22786
+ node.children.forEach((child) => {
22787
+ if (child.kind === "expression") {
22788
+ visitNode(child.value, node, inFunctionBody);
22789
+ } else if (child.kind === "element") {
22790
+ visitNode(child.value, node, inFunctionBody);
22791
+ }
22792
+ });
22793
+ return;
22794
+ case "ArrowFunction":
22795
+ if (!includeFunctionBodies) return;
22796
+ if (node.isExpression && !Array.isArray(node.body)) {
22797
+ visitNode(node.body, node, true);
22798
+ } else if (Array.isArray(node.body)) {
22799
+ walkBlocks(node.body, visitNode, true);
22800
+ }
22801
+ return;
22802
+ case "FunctionExpression":
22803
+ if (!includeFunctionBodies) return;
22804
+ walkBlocks(node.body, visitNode, true);
22805
+ return;
22806
+ case "AssignmentExpression":
22807
+ visitNode(node.left, node, inFunctionBody);
22808
+ visitNode(node.right, node, inFunctionBody);
22809
+ return;
22810
+ case "UpdateExpression":
22811
+ visitNode(node.argument, node, inFunctionBody);
22812
+ return;
22813
+ case "TemplateLiteral":
22814
+ node.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22815
+ return;
22816
+ case "SpreadElement":
22817
+ visitNode(node.argument, node, inFunctionBody);
22818
+ return;
22819
+ case "NewExpression":
22820
+ visitNode(node.callee, node, inFunctionBody);
22821
+ node.arguments.forEach((arg) => visitNode(arg, node, inFunctionBody));
22822
+ return;
22823
+ case "SequenceExpression":
22824
+ node.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22825
+ return;
22826
+ case "YieldExpression":
22827
+ if (node.argument) {
22828
+ visitNode(node.argument, node, inFunctionBody);
22829
+ }
22830
+ return;
22831
+ case "TaggedTemplateExpression":
22832
+ visitNode(node.tag, node, inFunctionBody);
22833
+ node.quasi.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22834
+ return;
22835
+ case "ClassExpression":
22836
+ if (node.superClass) {
22837
+ visitNode(node.superClass, node, inFunctionBody);
22838
+ }
22839
+ return;
22681
22840
  default:
22682
- break;
22841
+ assertNever(node);
22683
22842
  }
22843
+ };
22844
+ visitNode(expr, null, false);
22845
+ }
22846
+
22847
+ // src/ir/codegen-expression-deps.ts
22848
+ function getMemberDependencyPath(expr) {
22849
+ if (expr.kind !== "MemberExpression" && expr.kind !== "OptionalMemberExpression") return void 0;
22850
+ const depPath = extractDependencyPath(expr);
22851
+ if (!depPath || depPath.segments.length === 0) return void 0;
22852
+ const base = deSSAVarName(depPath.base);
22853
+ if (depPath.segments.some((segment) => segment.computed)) {
22854
+ return base;
22684
22855
  }
22856
+ const tail = depPath.segments.map((segment) => segment.property).join(".");
22857
+ return tail ? `${base}.${tail}` : void 0;
22685
22858
  }
22686
22859
  function collectExpressionDependencies(expr, deps) {
22687
- if (expr.kind === "Identifier") {
22688
- deps.add(deSSAVarName(expr.name));
22689
- return;
22690
- }
22691
- if (expr.kind === "ArrowFunction") {
22692
- if (expr.isExpression && !Array.isArray(expr.body)) {
22693
- collectExpressionDependencies(expr.body, deps);
22694
- } else if (Array.isArray(expr.body)) {
22695
- collectBlockDependencies(expr.body, deps);
22860
+ walkExpression(expr, (node) => {
22861
+ if (node.kind === "Identifier") {
22862
+ deps.add(deSSAVarName(node.name));
22863
+ return;
22696
22864
  }
22697
- return;
22698
- }
22699
- if (expr.kind === "FunctionExpression") {
22700
- collectBlockDependencies(expr.body, deps);
22701
- return;
22702
- }
22703
- if (expr.kind === "MemberExpression") {
22704
- const path2 = getMemberDependencyPath(expr);
22705
- if (path2) deps.add(path2);
22706
- collectExpressionDependencies(expr.object, deps);
22707
- if (expr.computed && expr.property.kind !== "Literal") {
22708
- collectExpressionDependencies(expr.property, deps);
22865
+ const path2 = getMemberDependencyPath(node);
22866
+ if (path2) {
22867
+ deps.add(path2);
22709
22868
  }
22710
- return;
22711
- }
22712
- if (expr.kind === "CallExpression") {
22713
- collectExpressionDependencies(expr.callee, deps);
22714
- expr.arguments.forEach((a) => collectExpressionDependencies(a, deps));
22715
- return;
22716
- }
22717
- if (expr.kind === "BinaryExpression" || expr.kind === "LogicalExpression") {
22718
- collectExpressionDependencies(expr.left, deps);
22719
- collectExpressionDependencies(expr.right, deps);
22720
- return;
22721
- }
22722
- if (expr.kind === "ConditionalExpression") {
22723
- collectExpressionDependencies(expr.test, deps);
22724
- collectExpressionDependencies(expr.consequent, deps);
22725
- collectExpressionDependencies(expr.alternate, deps);
22726
- return;
22727
- }
22728
- if (expr.kind === "UnaryExpression") {
22729
- collectExpressionDependencies(expr.argument, deps);
22730
- return;
22731
- }
22732
- if (expr.kind === "ArrayExpression") {
22733
- expr.elements.forEach((el) => collectExpressionDependencies(el, deps));
22734
- return;
22735
- }
22736
- if (expr.kind === "ObjectExpression") {
22737
- expr.properties.forEach((p) => {
22738
- if (p.kind === "SpreadElement") {
22739
- collectExpressionDependencies(p.argument, deps);
22740
- } else {
22741
- if (p.computed) collectExpressionDependencies(p.key, deps);
22742
- collectExpressionDependencies(p.value, deps);
22743
- }
22744
- });
22745
- return;
22746
- }
22747
- if (expr.kind === "TemplateLiteral") {
22748
- expr.expressions.forEach((e) => collectExpressionDependencies(e, deps));
22749
- return;
22750
- }
22869
+ });
22751
22870
  }
22752
22871
 
22753
22872
  // src/ir/codegen-reactive-kind.ts
@@ -24191,34 +24310,138 @@ function extractKeyFromAttributes(attributes) {
24191
24310
  }
24192
24311
  return void 0;
24193
24312
  }
24313
+ function collectReturnedJSXFromExpression(expression, returned) {
24314
+ if (expression.kind === "JSXElement") {
24315
+ returned.push(expression);
24316
+ return;
24317
+ }
24318
+ if (expression.kind === "ConditionalExpression") {
24319
+ collectReturnedJSXFromExpression(expression.consequent, returned);
24320
+ collectReturnedJSXFromExpression(expression.alternate, returned);
24321
+ return;
24322
+ }
24323
+ if (expression.kind === "LogicalExpression") {
24324
+ collectReturnedJSXFromExpression(expression.left, returned);
24325
+ collectReturnedJSXFromExpression(expression.right, returned);
24326
+ return;
24327
+ }
24328
+ if (expression.kind === "SequenceExpression") {
24329
+ const tail = expression.expressions[expression.expressions.length - 1];
24330
+ if (tail) collectReturnedJSXFromExpression(tail, returned);
24331
+ }
24332
+ }
24333
+ function extractKeyExpressionFromReturnedExpression(expression) {
24334
+ if (expression.kind === "JSXElement") {
24335
+ return extractKeyFromAttributes(expression.attributes);
24336
+ }
24337
+ if (expression.kind === "ConditionalExpression") {
24338
+ const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
24339
+ const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
24340
+ if (!consequentKey || !alternateKey) return void 0;
24341
+ return {
24342
+ kind: "ConditionalExpression",
24343
+ test: expression.test,
24344
+ consequent: consequentKey,
24345
+ alternate: alternateKey,
24346
+ loc: expression.loc
24347
+ };
24348
+ }
24349
+ if (expression.kind === "SequenceExpression") {
24350
+ const tail = expression.expressions[expression.expressions.length - 1];
24351
+ return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
24352
+ }
24353
+ return void 0;
24354
+ }
24355
+ function getReturnedKeyExpressionsFromCallback(callback) {
24356
+ const returned = [];
24357
+ if (callback.kind === "ArrowFunction") {
24358
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24359
+ const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
24360
+ if (keyExpr) returned.push(keyExpr);
24361
+ return returned;
24362
+ }
24363
+ if (Array.isArray(callback.body)) {
24364
+ for (const block of callback.body) {
24365
+ const term = block.terminator;
24366
+ if (term.kind !== "Return" || !term.argument) continue;
24367
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24368
+ if (keyExpr) returned.push(keyExpr);
24369
+ }
24370
+ }
24371
+ return returned;
24372
+ }
24373
+ if (callback.kind === "FunctionExpression") {
24374
+ for (const block of callback.body ?? []) {
24375
+ const term = block.terminator;
24376
+ if (term.kind !== "Return" || !term.argument) continue;
24377
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24378
+ if (keyExpr) returned.push(keyExpr);
24379
+ }
24380
+ }
24381
+ return returned;
24382
+ }
24194
24383
  function getReturnedJSXFromCallback(callback) {
24384
+ const returned = [];
24195
24385
  if (callback.kind === "ArrowFunction") {
24196
- if (callback.isExpression && !Array.isArray(callback.body) && callback.body.kind === "JSXElement") {
24197
- return callback.body;
24386
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24387
+ collectReturnedJSXFromExpression(callback.body, returned);
24388
+ return returned;
24198
24389
  }
24199
24390
  if (Array.isArray(callback.body)) {
24200
24391
  for (const block of callback.body) {
24201
24392
  const term = block.terminator;
24202
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24203
- return term.argument;
24393
+ if (term.kind === "Return" && term.argument) {
24394
+ collectReturnedJSXFromExpression(term.argument, returned);
24204
24395
  }
24205
24396
  }
24206
24397
  }
24398
+ return returned;
24207
24399
  }
24208
24400
  if (callback.kind === "FunctionExpression") {
24209
24401
  for (const block of callback.body ?? []) {
24210
24402
  const term = block.terminator;
24211
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24212
- return term.argument;
24403
+ if (term.kind === "Return" && term.argument) {
24404
+ collectReturnedJSXFromExpression(term.argument, returned);
24213
24405
  }
24214
24406
  }
24215
24407
  }
24216
- return null;
24408
+ return returned;
24409
+ }
24410
+ function keyExpressionSignature(expression) {
24411
+ try {
24412
+ return JSON.stringify(expression, (key, value) => {
24413
+ if (key === "loc") return void 0;
24414
+ if (typeof value === "bigint") return `__bigint:${value.toString()}`;
24415
+ return value;
24416
+ }) ?? "";
24417
+ } catch {
24418
+ return "";
24419
+ }
24217
24420
  }
24218
24421
  function extractKeyFromMapCallback(callback) {
24219
- const jsx = getReturnedJSXFromCallback(callback);
24220
- if (!jsx) return void 0;
24221
- return extractKeyFromAttributes(jsx.attributes);
24422
+ const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
24423
+ if (returnedKeyExpressions.length === 1) {
24424
+ return returnedKeyExpressions[0];
24425
+ }
24426
+ if (returnedKeyExpressions.length > 1) {
24427
+ const [firstKey2, ...restKeys2] = returnedKeyExpressions;
24428
+ const firstSignature2 = keyExpressionSignature(firstKey2);
24429
+ if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
24430
+ return firstKey2;
24431
+ }
24432
+ }
24433
+ const returned = getReturnedJSXFromCallback(callback);
24434
+ if (returned.length === 0) return void 0;
24435
+ const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
24436
+ if (keyExpressions.some((expr) => !expr)) return void 0;
24437
+ const [firstKey, ...restKeys] = keyExpressions;
24438
+ const firstSignature = keyExpressionSignature(firstKey);
24439
+ if (!firstSignature) return void 0;
24440
+ const allBranchesSameKey = restKeys.every(
24441
+ (keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
24442
+ );
24443
+ if (!allBranchesSameKey) return void 0;
24444
+ return firstKey;
24222
24445
  }
24223
24446
 
24224
24447
  // src/ir/codegen-overrides.ts
@@ -24823,6 +25046,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24823
25046
  ctx.inListRender = true;
24824
25047
  let callbackExpr = ops.lowerExpression(mapCallback, ctx);
24825
25048
  ctx.inListRender = prevInListRender;
25049
+ const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
25050
+ let deferredCallbackId = null;
25051
+ let deferredCallbackInitId = null;
25052
+ let deferredItemsId = null;
25053
+ if (shouldDeferOptionalCallbackEvaluation) {
25054
+ deferredCallbackId = ops.genTemp(ctx, "mapCb");
25055
+ deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
25056
+ deferredItemsId = ops.genTemp(ctx, "mapItems");
25057
+ }
24826
25058
  const capturedKeyParamName = ctx.listKeyParamName;
24827
25059
  ctx.listKeyExpr = prevListKeyExpr;
24828
25060
  ctx.listItemParamName = prevListItemParamName;
@@ -24922,7 +25154,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24922
25154
  ],
24923
25155
  keyExprAst
24924
25156
  );
24925
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25157
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
24926
25158
  if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
24927
25159
  const newParams = [...callbackExpr.params];
24928
25160
  while (newParams.length < 2) {
@@ -24942,25 +25174,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24942
25174
  }
24943
25175
  }
24944
25176
  statements.push(...hoistedStatements);
25177
+ if (shouldDeferOptionalCallbackEvaluation) {
25178
+ statements.push(
25179
+ t4.variableDeclaration("let", [
25180
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25181
+ ]),
25182
+ t4.variableDeclaration("let", [
25183
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25184
+ ])
25185
+ );
25186
+ }
25187
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25188
+ [],
25189
+ t4.blockStatement([
25190
+ t4.variableDeclaration("const", [
25191
+ t4.variableDeclarator(
25192
+ t4.cloneNode(deferredItemsId, true),
25193
+ t4.cloneNode(arrayExprBase, true)
25194
+ )
25195
+ ]),
25196
+ t4.ifStatement(
25197
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25198
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25199
+ ),
25200
+ t4.ifStatement(
25201
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25202
+ t4.blockStatement([
25203
+ t4.expressionStatement(
25204
+ t4.assignmentExpression(
25205
+ "=",
25206
+ t4.cloneNode(deferredCallbackId, true),
25207
+ t4.cloneNode(callbackExpr, true)
25208
+ )
25209
+ ),
25210
+ t4.expressionStatement(
25211
+ t4.assignmentExpression(
25212
+ "=",
25213
+ t4.cloneNode(deferredCallbackInitId, true),
25214
+ t4.booleanLiteral(true)
25215
+ )
25216
+ )
25217
+ ])
25218
+ ),
25219
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25220
+ ])
25221
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25222
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25223
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25224
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25225
+ t4.identifier("__item"),
25226
+ t4.identifier("__index"),
25227
+ t4.identifier("__key")
25228
+ ])
25229
+ ) : callbackExpr;
24945
25230
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
24946
- t4.arrowFunctionExpression([], arrayExpr),
25231
+ getItemsExpr,
24947
25232
  keyFn,
24948
- callbackExpr,
25233
+ renderExpr,
24949
25234
  t4.booleanLiteral(hasIndexParam)
24950
25235
  ]);
24951
25236
  } else {
24952
25237
  statements.push(...hoistedStatements);
25238
+ if (shouldDeferOptionalCallbackEvaluation) {
25239
+ statements.push(
25240
+ t4.variableDeclaration("let", [
25241
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25242
+ ]),
25243
+ t4.variableDeclaration("let", [
25244
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25245
+ ])
25246
+ );
25247
+ }
24953
25248
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
24954
25249
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[1]) ? callbackExpr.params[1].name : "__index" : "__index";
24955
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25250
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25251
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25252
+ [],
25253
+ t4.blockStatement([
25254
+ t4.variableDeclaration("const", [
25255
+ t4.variableDeclarator(
25256
+ t4.cloneNode(deferredItemsId, true),
25257
+ t4.cloneNode(arrayExprBase, true)
25258
+ )
25259
+ ]),
25260
+ t4.ifStatement(
25261
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25262
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25263
+ ),
25264
+ t4.ifStatement(
25265
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25266
+ t4.blockStatement([
25267
+ t4.expressionStatement(
25268
+ t4.assignmentExpression(
25269
+ "=",
25270
+ t4.cloneNode(deferredCallbackId, true),
25271
+ t4.cloneNode(callbackExpr, true)
25272
+ )
25273
+ ),
25274
+ t4.expressionStatement(
25275
+ t4.assignmentExpression(
25276
+ "=",
25277
+ t4.cloneNode(deferredCallbackInitId, true),
25278
+ t4.booleanLiteral(true)
25279
+ )
25280
+ )
25281
+ ])
25282
+ ),
25283
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25284
+ ])
25285
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25286
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25287
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25288
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25289
+ t4.identifier("__item"),
25290
+ t4.identifier("__index"),
25291
+ t4.identifier("__key")
25292
+ ])
25293
+ ) : callbackExpr;
24956
25294
  const keyFn = t4.arrowFunctionExpression(
24957
25295
  [t4.identifier(itemParamName), t4.identifier(indexParamName)],
24958
25296
  t4.identifier(indexParamName)
24959
25297
  );
24960
25298
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
24961
- t4.arrowFunctionExpression([], arrayExpr),
25299
+ getItemsExpr,
24962
25300
  keyFn,
24963
- callbackExpr,
25301
+ renderExpr,
24964
25302
  t4.booleanLiteral(hasIndexParam)
24965
25303
  ]);
24966
25304
  }
@@ -26125,8 +26463,31 @@ function extractHIRStaticHtml(jsx, ctx, ops, parentPath = [], namespace = null)
26125
26463
  const resolvedNamespace = resolveNamespaceContext(tagName, namespace);
26126
26464
  let html = `<${tagName}`;
26127
26465
  const bindings = [];
26128
- for (const attr of jsx.attributes) {
26466
+ for (let attrIndex = 0; attrIndex < jsx.attributes.length; attrIndex++) {
26467
+ const attr = jsx.attributes[attrIndex];
26129
26468
  if (attr.isSpread) {
26469
+ if (attr.spreadExpr) {
26470
+ const excluded = /* @__PURE__ */ new Set();
26471
+ for (let nextIndex = attrIndex + 1; nextIndex < jsx.attributes.length; nextIndex++) {
26472
+ const nextAttr = jsx.attributes[nextIndex];
26473
+ if (nextAttr.isSpread) continue;
26474
+ let nextName = normalizeHIRAttrName(nextAttr.name);
26475
+ if (nextName.endsWith("$")) {
26476
+ nextName = nextName.slice(0, -1);
26477
+ }
26478
+ if (nextName === "key") continue;
26479
+ excluded.add(nextName);
26480
+ if (nextName !== nextAttr.name) {
26481
+ excluded.add(nextAttr.name);
26482
+ }
26483
+ }
26484
+ bindings.push({
26485
+ type: "spread",
26486
+ path: [...parentPath],
26487
+ expr: attr.spreadExpr,
26488
+ exclude: excluded.size > 0 ? Array.from(excluded) : void 0
26489
+ });
26490
+ }
26130
26491
  continue;
26131
26492
  }
26132
26493
  let name = normalizeHIRAttrName(attr.name);
@@ -28311,10 +28672,28 @@ function lowerIntrinsicElement(jsx, ctx) {
28311
28672
  )
28312
28673
  );
28313
28674
  }
28675
+ fusedPatchGroups.clear();
28314
28676
  };
28315
28677
  for (const binding of bindings) {
28316
28678
  const targetId = resolveHIRBindingPath(binding.path, nodeCache, statements, ctx, genTemp3);
28317
- if (binding.type === "event" && binding.expr && binding.name) {
28679
+ if (binding.type === "spread" && binding.expr) {
28680
+ flushFusedPatchGroups();
28681
+ ctx.helpersUsed.add("spread");
28682
+ const spreadValueExpr = lowerDomExpression(binding.expr, ctx, containingRegion);
28683
+ const spreadGetter = t4.arrowFunctionExpression([], spreadValueExpr);
28684
+ const spreadArgs = [
28685
+ targetId,
28686
+ spreadGetter,
28687
+ t4.booleanLiteral(Boolean(isSVG || isMathML)),
28688
+ t4.booleanLiteral(true)
28689
+ ];
28690
+ if (binding.exclude && binding.exclude.length > 0) {
28691
+ spreadArgs.push(t4.arrayExpression(binding.exclude.map((name) => t4.stringLiteral(name))));
28692
+ }
28693
+ statements.push(
28694
+ t4.expressionStatement(t4.callExpression(t4.identifier(RUNTIME_ALIASES.spread), spreadArgs))
28695
+ );
28696
+ } else if (binding.type === "event" && binding.expr && binding.name) {
28318
28697
  const eventName = binding.name;
28319
28698
  const hasEventOptions = binding.eventOptions && (binding.eventOptions.capture || binding.eventOptions.passive || binding.eventOptions.once);
28320
28699
  const isDelegated = DelegatedEvents.has(eventName) && !hasEventOptions;
@@ -33102,106 +33481,106 @@ function collectUsesFromTerminator(term, add, inFunctionBody = false) {
33102
33481
  }
33103
33482
  }
33104
33483
  function collectUsesFromExpression(expr, add, inFunctionBody = false) {
33105
- walkExpression(expr, add, { inFunctionBody, shadowed: /* @__PURE__ */ new Set() });
33484
+ walkExpression2(expr, add, { inFunctionBody, shadowed: /* @__PURE__ */ new Set() });
33106
33485
  }
33107
- function walkExpression(expr, add, ctx) {
33486
+ function walkExpression2(expr, add, ctx) {
33108
33487
  switch (expr.kind) {
33109
33488
  case "Identifier":
33110
33489
  if (!ctx.shadowed.has(expr.name)) add(expr.name, ctx.inFunctionBody);
33111
33490
  return;
33112
33491
  case "CallExpression":
33113
33492
  case "OptionalCallExpression":
33114
- walkExpression(expr.callee, add, ctx);
33115
- expr.arguments.forEach((arg) => walkExpression(arg, add, ctx));
33493
+ walkExpression2(expr.callee, add, ctx);
33494
+ expr.arguments.forEach((arg) => walkExpression2(arg, add, ctx));
33116
33495
  return;
33117
33496
  case "MemberExpression":
33118
33497
  case "OptionalMemberExpression":
33119
- walkExpression(expr.object, add, ctx);
33120
- if (expr.computed) walkExpression(expr.property, add, ctx);
33498
+ walkExpression2(expr.object, add, ctx);
33499
+ if (expr.computed) walkExpression2(expr.property, add, ctx);
33121
33500
  return;
33122
33501
  case "BinaryExpression":
33123
33502
  case "LogicalExpression":
33124
- walkExpression(expr.left, add, ctx);
33125
- walkExpression(expr.right, add, ctx);
33503
+ walkExpression2(expr.left, add, ctx);
33504
+ walkExpression2(expr.right, add, ctx);
33126
33505
  return;
33127
33506
  case "UnaryExpression":
33128
- walkExpression(expr.argument, add, ctx);
33507
+ walkExpression2(expr.argument, add, ctx);
33129
33508
  return;
33130
33509
  case "ConditionalExpression":
33131
- walkExpression(expr.test, add, ctx);
33132
- walkExpression(expr.consequent, add, ctx);
33133
- walkExpression(expr.alternate, add, ctx);
33510
+ walkExpression2(expr.test, add, ctx);
33511
+ walkExpression2(expr.consequent, add, ctx);
33512
+ walkExpression2(expr.alternate, add, ctx);
33134
33513
  return;
33135
33514
  case "ArrayExpression":
33136
33515
  expr.elements.forEach((el) => {
33137
- if (el) walkExpression(el, add, ctx);
33516
+ if (el) walkExpression2(el, add, ctx);
33138
33517
  });
33139
33518
  return;
33140
33519
  case "ObjectExpression":
33141
33520
  expr.properties.forEach((prop) => {
33142
33521
  if (prop.kind === "SpreadElement") {
33143
- walkExpression(prop.argument, add, ctx);
33522
+ walkExpression2(prop.argument, add, ctx);
33144
33523
  } else {
33145
- if (prop.computed) walkExpression(prop.key, add, ctx);
33146
- walkExpression(prop.value, add, ctx);
33524
+ if (prop.computed) walkExpression2(prop.key, add, ctx);
33525
+ walkExpression2(prop.value, add, ctx);
33147
33526
  }
33148
33527
  });
33149
33528
  return;
33150
33529
  case "TemplateLiteral":
33151
- expr.expressions.forEach((e) => walkExpression(e, add, ctx));
33530
+ expr.expressions.forEach((e) => walkExpression2(e, add, ctx));
33152
33531
  return;
33153
33532
  case "SpreadElement":
33154
- walkExpression(expr.argument, add, ctx);
33533
+ walkExpression2(expr.argument, add, ctx);
33155
33534
  return;
33156
33535
  case "SequenceExpression":
33157
- expr.expressions.forEach((e) => walkExpression(e, add, ctx));
33536
+ expr.expressions.forEach((e) => walkExpression2(e, add, ctx));
33158
33537
  return;
33159
33538
  case "AwaitExpression":
33160
- walkExpression(expr.argument, add, ctx);
33539
+ walkExpression2(expr.argument, add, ctx);
33161
33540
  return;
33162
33541
  case "NewExpression":
33163
- walkExpression(expr.callee, add, ctx);
33164
- expr.arguments.forEach((arg) => walkExpression(arg, add, ctx));
33542
+ walkExpression2(expr.callee, add, ctx);
33543
+ expr.arguments.forEach((arg) => walkExpression2(arg, add, ctx));
33165
33544
  return;
33166
33545
  case "ArrowFunction": {
33167
33546
  const shadowed = new Set(ctx.shadowed);
33168
33547
  expr.params.forEach((p) => shadowed.add(p.name));
33169
33548
  if (expr.isExpression) {
33170
- walkExpression(expr.body, add, { inFunctionBody: true, shadowed });
33549
+ walkExpression2(expr.body, add, { inFunctionBody: true, shadowed });
33171
33550
  return;
33172
33551
  }
33173
- walkBlocks(expr.body, add, { inFunctionBody: true, shadowed });
33552
+ walkBlocks2(expr.body, add, { inFunctionBody: true, shadowed });
33174
33553
  return;
33175
33554
  }
33176
33555
  case "FunctionExpression": {
33177
33556
  const shadowed = new Set(ctx.shadowed);
33178
33557
  expr.params.forEach((p) => shadowed.add(p.name));
33179
- walkBlocks(expr.body, add, { inFunctionBody: true, shadowed });
33558
+ walkBlocks2(expr.body, add, { inFunctionBody: true, shadowed });
33180
33559
  return;
33181
33560
  }
33182
33561
  case "AssignmentExpression":
33183
- walkExpression(expr.left, add, ctx);
33184
- walkExpression(expr.right, add, ctx);
33562
+ walkExpression2(expr.left, add, ctx);
33563
+ walkExpression2(expr.right, add, ctx);
33185
33564
  return;
33186
33565
  case "UpdateExpression":
33187
- walkExpression(expr.argument, add, ctx);
33566
+ walkExpression2(expr.argument, add, ctx);
33188
33567
  return;
33189
33568
  case "JSXElement":
33190
33569
  if (typeof expr.tagName !== "string") {
33191
- walkExpression(expr.tagName, add, ctx);
33570
+ walkExpression2(expr.tagName, add, ctx);
33192
33571
  }
33193
33572
  expr.attributes.forEach((attr) => {
33194
33573
  if (attr.isSpread && attr.spreadExpr) {
33195
- walkExpression(attr.spreadExpr, add, ctx);
33574
+ walkExpression2(attr.spreadExpr, add, ctx);
33196
33575
  } else if (attr.value) {
33197
- walkExpression(attr.value, add, ctx);
33576
+ walkExpression2(attr.value, add, ctx);
33198
33577
  }
33199
33578
  });
33200
33579
  expr.children.forEach((child) => {
33201
33580
  if (child.kind === "expression") {
33202
- walkExpression(child.value, add, ctx);
33581
+ walkExpression2(child.value, add, ctx);
33203
33582
  } else if (child.kind === "element") {
33204
- walkExpression(child.value, add, ctx);
33583
+ walkExpression2(child.value, add, ctx);
33205
33584
  }
33206
33585
  });
33207
33586
  return;
@@ -33209,16 +33588,16 @@ function walkExpression(expr, add, ctx) {
33209
33588
  return;
33210
33589
  }
33211
33590
  }
33212
- function walkBlocks(blocks, add, ctx) {
33591
+ function walkBlocks2(blocks, add, ctx) {
33213
33592
  for (const block of blocks) {
33214
33593
  for (const instr of block.instructions) {
33215
33594
  if (instr.kind === "Assign") {
33216
- walkExpression(instr.value, add, ctx);
33595
+ walkExpression2(instr.value, add, ctx);
33217
33596
  if (instr.declarationKind) {
33218
33597
  ctx.shadowed.add(instr.target.name);
33219
33598
  }
33220
33599
  } else if (instr.kind === "Expression") {
33221
- walkExpression(instr.value, add, ctx);
33600
+ walkExpression2(instr.value, add, ctx);
33222
33601
  } else if (instr.kind === "Phi") {
33223
33602
  instr.sources.forEach((src) => {
33224
33603
  if (!ctx.shadowed.has(src.id.name)) add(src.id.name, ctx.inFunctionBody);
@@ -33237,7 +33616,7 @@ function walkBlocks(blocks, add, ctx) {
33237
33616
  function collectExpressionIdentifiers2(expr, deep = false) {
33238
33617
  const deps = /* @__PURE__ */ new Set();
33239
33618
  const collect = (name) => deps.add(name);
33240
- walkExpression(expr, (name, _inFunctionBody) => collect(name), {
33619
+ walkExpression2(expr, (name, _inFunctionBody) => collect(name), {
33241
33620
  inFunctionBody: deep,
33242
33621
  shadowed: /* @__PURE__ */ new Set()
33243
33622
  });
@@ -33818,6 +34197,506 @@ function getRootIdentifier(expr, t4) {
33818
34197
  return null;
33819
34198
  }
33820
34199
 
34200
+ // src/tooling/analyze.ts
34201
+ var import_core2 = require("@babel/core");
34202
+
34203
+ // src/tooling/trace-infer.ts
34204
+ var TRACE_REGEX_ESCAPES = /[.*+?^${}()|[\]\\]/g;
34205
+ var IDENTIFIER_NAME = /^[A-Za-z_$][\w$]*$/;
34206
+ function isIdentifierName(name) {
34207
+ return IDENTIFIER_NAME.test(name);
34208
+ }
34209
+ function lineContainsIdentifier(lineText, identifier2) {
34210
+ const pattern = new RegExp(`\\b${identifier2.replace(TRACE_REGEX_ESCAPES, "\\$&")}\\b`);
34211
+ return pattern.test(lineText);
34212
+ }
34213
+ function lineContainsAnyIdentifier(lineText, identifiers) {
34214
+ for (const id of identifiers) {
34215
+ if (lineContainsIdentifier(lineText, id)) return true;
34216
+ }
34217
+ return false;
34218
+ }
34219
+ function pushTraceMarker(markersByLine, line, marker) {
34220
+ const markers = markersByLine.get(line);
34221
+ if (!markers) {
34222
+ markersByLine.set(line, [marker]);
34223
+ return;
34224
+ }
34225
+ const duplicate = markers.some(
34226
+ (existing) => existing.kind === marker.kind && existing.label === marker.label
34227
+ );
34228
+ if (!duplicate) markers.push(marker);
34229
+ }
34230
+ function inferReactiveLocalNames(startLine, endLine, sourceLines, baseReactiveNames) {
34231
+ const reactiveNames = new Set(baseReactiveNames);
34232
+ const declarationLines = [];
34233
+ const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?)(?:;)?$/;
34234
+ for (let line = startLine; line <= endLine; line++) {
34235
+ const text = (sourceLines[line - 1] ?? "").trim();
34236
+ if (!text) continue;
34237
+ const withoutComment = text.replace(/\/\/.*$/, "").trim();
34238
+ if (!withoutComment) continue;
34239
+ const match = withoutComment.match(declarationPattern);
34240
+ if (!match) continue;
34241
+ const name = match[1];
34242
+ const expression = match[2];
34243
+ if (!name || !expression) continue;
34244
+ declarationLines.push({ name, expression });
34245
+ }
34246
+ let changed = true;
34247
+ while (changed) {
34248
+ changed = false;
34249
+ for (const declaration of declarationLines) {
34250
+ if (reactiveNames.has(declaration.name)) continue;
34251
+ if (!lineContainsAnyIdentifier(declaration.expression, reactiveNames)) continue;
34252
+ reactiveNames.add(declaration.name);
34253
+ changed = true;
34254
+ }
34255
+ }
34256
+ return reactiveNames;
34257
+ }
34258
+ function isStateCallInstruction(instr) {
34259
+ if (instr.kind !== "Assign") return false;
34260
+ const value = instr.value;
34261
+ return value.kind === "CallExpression" && value.callee.kind === "Identifier" && value.callee.name === "$state";
34262
+ }
34263
+ function collectStateDeclNames(fn) {
34264
+ const result = [];
34265
+ for (const block of fn.blocks) {
34266
+ for (const instr of block.instructions) {
34267
+ if (!isStateCallInstruction(instr) || !instr.loc) continue;
34268
+ const name = deSSAVarName(instr.target.name);
34269
+ if (!isIdentifierName(name)) continue;
34270
+ result.push({ name, line: instr.loc.start.line });
34271
+ }
34272
+ }
34273
+ return result;
34274
+ }
34275
+ function expressionContainsEffectCall(instr) {
34276
+ const value = instr.kind === "Assign" || instr.kind === "Expression" ? instr.value : null;
34277
+ if (!value || !instr.loc) return null;
34278
+ let found = false;
34279
+ walkExpression(value, (expr) => {
34280
+ if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier" && deSSAVarName(expr.callee.name) === "$effect") {
34281
+ found = true;
34282
+ }
34283
+ });
34284
+ return found ? instr.loc.start.line : null;
34285
+ }
34286
+ function flattenRegions2(regions) {
34287
+ if (!regions || regions.length === 0) return [];
34288
+ const result = [];
34289
+ const visit = (region) => {
34290
+ result.push(region);
34291
+ region.children?.forEach((child) => visit(child));
34292
+ };
34293
+ regions.forEach((region) => visit(region));
34294
+ return result;
34295
+ }
34296
+ function findContainingRegion2(line, flatRegions) {
34297
+ let best;
34298
+ for (const region of flatRegions) {
34299
+ if (region.startLine === void 0 || region.endLine === void 0 || line < region.startLine || line > region.endLine) {
34300
+ continue;
34301
+ }
34302
+ const bestSpan = best && best.startLine !== void 0 && best.endLine !== void 0 ? best.endLine - best.startLine : Number.POSITIVE_INFINITY;
34303
+ const span = region.endLine - region.startLine;
34304
+ if (span <= bestSpan) best = region;
34305
+ }
34306
+ return best;
34307
+ }
34308
+ function inferTraceMarkersForComponent(input) {
34309
+ const { fn, sourceLines, startLine, endLine, verbosity, regions } = input;
34310
+ const markersByLine = /* @__PURE__ */ new Map();
34311
+ pushTraceMarker(markersByLine, startLine, {
34312
+ kind: "once",
34313
+ label: "Component setup runs on mount"
34314
+ });
34315
+ const stateDecls = collectStateDeclNames(fn);
34316
+ const reactiveNames = inferReactiveLocalNames(
34317
+ startLine,
34318
+ endLine,
34319
+ sourceLines,
34320
+ stateDecls.map((item) => item.name)
34321
+ );
34322
+ for (const stateDecl of stateDecls) {
34323
+ pushTraceMarker(markersByLine, stateDecl.line, {
34324
+ kind: "once",
34325
+ label: "Signal initialization runs once"
34326
+ });
34327
+ }
34328
+ for (const block of fn.blocks) {
34329
+ for (const instr of block.instructions) {
34330
+ const line = expressionContainsEffectCall(instr);
34331
+ if (!line || line < startLine || line > endLine) continue;
34332
+ pushTraceMarker(markersByLine, line, {
34333
+ kind: "effect",
34334
+ label: "Effect reruns when dependencies change"
34335
+ });
34336
+ }
34337
+ }
34338
+ const flatRegions = flattenRegions2(regions);
34339
+ for (let line = startLine; line <= endLine; line++) {
34340
+ const lineText = sourceLines[line - 1] ?? "";
34341
+ if (!lineText) continue;
34342
+ const hasReactiveName = lineContainsAnyIdentifier(lineText, reactiveNames);
34343
+ if (hasReactiveName && /\{[^}]*\b[A-Za-z_$][\w$]*\b[^}]*\}/.test(lineText)) {
34344
+ pushTraceMarker(markersByLine, line, {
34345
+ kind: "reactive",
34346
+ label: "JSX expression updates with reactive values"
34347
+ });
34348
+ }
34349
+ if (hasReactiveName && /\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText)) {
34350
+ pushTraceMarker(markersByLine, line, {
34351
+ kind: "reactive",
34352
+ label: "Statement reruns when reactive values change"
34353
+ });
34354
+ }
34355
+ if (/\b(?:\$effect|effect)\s*\(/.test(lineText)) {
34356
+ pushTraceMarker(markersByLine, line, {
34357
+ kind: "effect",
34358
+ label: "Effect callback executes reactively"
34359
+ });
34360
+ }
34361
+ if (verbosity === "verbose" && !hasReactiveName && /\{[^}]*\}/.test(lineText)) {
34362
+ pushTraceMarker(markersByLine, line, {
34363
+ kind: "once",
34364
+ label: "JSX expression runs during setup only"
34365
+ });
34366
+ }
34367
+ if (/\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText) && !hasReactiveName && line !== startLine) {
34368
+ pushTraceMarker(markersByLine, line, {
34369
+ kind: "once",
34370
+ label: "Statement runs only during setup"
34371
+ });
34372
+ }
34373
+ const containingRegion = findContainingRegion2(line, flatRegions);
34374
+ if (!containingRegion) continue;
34375
+ const markers = markersByLine.get(line);
34376
+ if (!markers) continue;
34377
+ for (const marker of markers) {
34378
+ marker.regionId = containingRegion.id;
34379
+ marker.deps = containingRegion.dependencies;
34380
+ }
34381
+ }
34382
+ return [...markersByLine.entries()].sort((a, b) => a[0] - b[0]).map(([line, markers]) => ({ line, markers }));
34383
+ }
34384
+
34385
+ // src/tooling/analyze.ts
34386
+ function mergeLoc(a, b) {
34387
+ if (!a) return b ?? null;
34388
+ if (!b) return a;
34389
+ const start = b.start.line < a.start.line || b.start.line === a.start.line && b.start.column < a.start.column ? b.start : a.start;
34390
+ const end = b.end.line > a.end.line || b.end.line === a.end.line && b.end.column > a.end.column ? b.end : a.end;
34391
+ return {
34392
+ start,
34393
+ end,
34394
+ filename: a.filename ?? b.filename,
34395
+ identifierName: a.identifierName ?? b.identifierName
34396
+ };
34397
+ }
34398
+ function expressionContainsMacroCall(expr, macroName) {
34399
+ let found = false;
34400
+ const visit = (value) => {
34401
+ if (found) return;
34402
+ if (value.kind === "CallExpression" && value.callee.kind === "Identifier" && deSSAVarName(value.callee.name) === macroName) {
34403
+ found = true;
34404
+ return;
34405
+ }
34406
+ switch (value.kind) {
34407
+ case "CallExpression":
34408
+ case "OptionalCallExpression":
34409
+ visit(value.callee);
34410
+ value.arguments.forEach((arg) => visit(arg));
34411
+ return;
34412
+ case "MemberExpression":
34413
+ case "OptionalMemberExpression":
34414
+ visit(value.object);
34415
+ if (value.computed) visit(value.property);
34416
+ return;
34417
+ case "BinaryExpression":
34418
+ case "LogicalExpression":
34419
+ visit(value.left);
34420
+ visit(value.right);
34421
+ return;
34422
+ case "UnaryExpression":
34423
+ case "AwaitExpression":
34424
+ case "UpdateExpression":
34425
+ case "SpreadElement":
34426
+ visit(value.argument);
34427
+ return;
34428
+ case "ConditionalExpression":
34429
+ visit(value.test);
34430
+ visit(value.consequent);
34431
+ visit(value.alternate);
34432
+ return;
34433
+ case "ArrayExpression":
34434
+ value.elements.forEach((el) => visit(el));
34435
+ return;
34436
+ case "ObjectExpression":
34437
+ value.properties.forEach((prop) => {
34438
+ if (prop.kind === "SpreadElement") {
34439
+ visit(prop.argument);
34440
+ return;
34441
+ }
34442
+ if (prop.computed) visit(prop.key);
34443
+ visit(prop.value);
34444
+ });
34445
+ return;
34446
+ case "TemplateLiteral":
34447
+ value.expressions.forEach((item) => visit(item));
34448
+ return;
34449
+ case "AssignmentExpression":
34450
+ visit(value.left);
34451
+ visit(value.right);
34452
+ return;
34453
+ case "SequenceExpression":
34454
+ value.expressions.forEach((item) => visit(item));
34455
+ return;
34456
+ case "YieldExpression":
34457
+ if (value.argument) visit(value.argument);
34458
+ return;
34459
+ case "TaggedTemplateExpression":
34460
+ visit(value.tag);
34461
+ value.quasi.expressions.forEach((item) => visit(item));
34462
+ return;
34463
+ case "ImportExpression":
34464
+ visit(value.source);
34465
+ return;
34466
+ case "ArrowFunction":
34467
+ if (Array.isArray(value.body)) {
34468
+ value.body.forEach((block) => {
34469
+ block.instructions.forEach((instr) => {
34470
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34471
+ visit(instr.value);
34472
+ }
34473
+ });
34474
+ });
34475
+ } else {
34476
+ visit(value.body);
34477
+ }
34478
+ return;
34479
+ case "FunctionExpression":
34480
+ value.body.forEach((block) => {
34481
+ block.instructions.forEach((instr) => {
34482
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34483
+ visit(instr.value);
34484
+ }
34485
+ });
34486
+ });
34487
+ return;
34488
+ case "JSXElement":
34489
+ if (typeof value.tagName !== "string") visit(value.tagName);
34490
+ value.attributes.forEach((attr) => {
34491
+ if (attr.isSpread && attr.spreadExpr) {
34492
+ visit(attr.spreadExpr);
34493
+ } else if (attr.value) {
34494
+ visit(attr.value);
34495
+ }
34496
+ });
34497
+ value.children.forEach((child) => {
34498
+ if (child.kind === "expression") visit(child.value);
34499
+ if (child.kind === "element") visit(child.value);
34500
+ });
34501
+ return;
34502
+ case "Identifier":
34503
+ case "Literal":
34504
+ case "MetaProperty":
34505
+ case "NewExpression":
34506
+ case "ClassExpression":
34507
+ case "ThisExpression":
34508
+ case "SuperExpression":
34509
+ if (value.kind === "NewExpression") {
34510
+ visit(value.callee);
34511
+ value.arguments.forEach((arg) => visit(arg));
34512
+ }
34513
+ if (value.kind === "ClassExpression" && value.superClass) visit(value.superClass);
34514
+ return;
34515
+ default:
34516
+ return;
34517
+ }
34518
+ };
34519
+ visit(expr);
34520
+ return found;
34521
+ }
34522
+ function instructionContainsMacroCall(instruction, macroName) {
34523
+ if (instruction.kind !== "Assign" && instruction.kind !== "Expression") return false;
34524
+ return expressionContainsMacroCall(instruction.value, macroName);
34525
+ }
34526
+ function functionUsesMacro(fn, macroName) {
34527
+ for (const block of fn.blocks) {
34528
+ for (const instruction of block.instructions) {
34529
+ if (instructionContainsMacroCall(instruction, macroName)) return true;
34530
+ }
34531
+ const term = block.terminator;
34532
+ if ("argument" in term && term.argument && expressionContainsMacroCall(term.argument, macroName)) {
34533
+ return true;
34534
+ }
34535
+ if (term.kind === "Branch" && expressionContainsMacroCall(term.test, macroName)) {
34536
+ return true;
34537
+ }
34538
+ if (term.kind === "Switch") {
34539
+ if (expressionContainsMacroCall(term.discriminant, macroName)) return true;
34540
+ if (term.cases.some((entry) => entry.test && expressionContainsMacroCall(entry.test, macroName))) {
34541
+ return true;
34542
+ }
34543
+ }
34544
+ }
34545
+ return false;
34546
+ }
34547
+ function computeRegionLoc(region, fn) {
34548
+ let loc = null;
34549
+ for (const instruction of region.instructions) {
34550
+ loc = mergeLoc(loc, instruction.loc);
34551
+ }
34552
+ for (const blockId of region.blocks) {
34553
+ const block = fn.blocks.find((entry) => entry.id === blockId);
34554
+ if (!block) continue;
34555
+ loc = mergeLoc(loc, block.terminator.loc);
34556
+ }
34557
+ return loc;
34558
+ }
34559
+ function regionToSerializable(region, fn) {
34560
+ const loc = computeRegionLoc(region, fn);
34561
+ return {
34562
+ id: region.id,
34563
+ startLine: loc?.start.line,
34564
+ startColumn: loc?.start.column,
34565
+ endLine: loc?.end.line,
34566
+ endColumn: loc?.end.column,
34567
+ dependencies: [...region.dependencies].map(deSSAVarName),
34568
+ declarations: [...region.declarations].map(deSSAVarName),
34569
+ hasControlFlow: region.hasControlFlow,
34570
+ hasReactiveWrites: region.declarations.size > 0,
34571
+ children: region.children.map((child) => regionToSerializable(child, fn))
34572
+ };
34573
+ }
34574
+ function warningSeverity(code) {
34575
+ const diagnosticCodes = new Set(getAllDiagnosticCodes());
34576
+ if (!diagnosticCodes.has(code)) return "warning" /* Warning */;
34577
+ return getDiagnosticInfo(code).severity;
34578
+ }
34579
+ function normalizeWarningToDiagnostic(warning) {
34580
+ return {
34581
+ code: warning.code,
34582
+ message: warning.message,
34583
+ severity: warningSeverity(warning.code),
34584
+ line: warning.line,
34585
+ column: warning.column
34586
+ };
34587
+ }
34588
+ function normalizeThrownError(error) {
34589
+ const message = error instanceof Error ? error.message : String(error);
34590
+ return {
34591
+ code: "FICT-COMPILE",
34592
+ message,
34593
+ severity: "error" /* Error */,
34594
+ line: 0,
34595
+ column: 0
34596
+ };
34597
+ }
34598
+ function analyzeDiagnostics(code, fileName, options) {
34599
+ const warnings = [];
34600
+ const pluginOptions = {
34601
+ dev: true,
34602
+ filename: fileName,
34603
+ emitModuleMetadata: false,
34604
+ strictGuarantee: false,
34605
+ warningLevels: {
34606
+ ...options.compilerOptions?.warningLevels ?? {},
34607
+ "FICT-R004": "warn"
34608
+ },
34609
+ ...options.compilerOptions,
34610
+ onWarn: (warning) => warnings.push(warning)
34611
+ };
34612
+ try {
34613
+ (0, import_core2.transformSync)(code, {
34614
+ filename: fileName,
34615
+ configFile: false,
34616
+ babelrc: false,
34617
+ sourceType: "module",
34618
+ parserOpts: {
34619
+ sourceType: "module",
34620
+ plugins: ["typescript", "jsx"],
34621
+ allowReturnOutsideFunction: true
34622
+ },
34623
+ plugins: [[index_default, pluginOptions]],
34624
+ generatorOpts: {
34625
+ compact: false
34626
+ }
34627
+ });
34628
+ } catch (error) {
34629
+ return [...warnings.map(normalizeWarningToDiagnostic), normalizeThrownError(error)];
34630
+ }
34631
+ return warnings.map(normalizeWarningToDiagnostic);
34632
+ }
34633
+ function shouldIncludeFunction(fn) {
34634
+ return functionContainsJSX(fn) || functionUsesMacro(fn, "$state") || functionUsesMacro(fn, "$effect");
34635
+ }
34636
+ function parseFileAst(code, fileName) {
34637
+ const ast = (0, import_core2.parseSync)(code, {
34638
+ filename: fileName,
34639
+ sourceType: "module",
34640
+ parserOpts: {
34641
+ sourceType: "module",
34642
+ plugins: ["typescript", "jsx"],
34643
+ allowReturnOutsideFunction: true
34644
+ }
34645
+ });
34646
+ if (!ast || ast.type !== "File") {
34647
+ throw new Error("Failed to parse source file for Fict analysis.");
34648
+ }
34649
+ return ast;
34650
+ }
34651
+ function analyzeFictFile(code, fileName, options = {}) {
34652
+ const includeRegions = options.includeRegions ?? true;
34653
+ const includeDiagnostics = options.includeDiagnostics ?? true;
34654
+ const verbosity = options.verbosity ?? "minimal";
34655
+ const ast = parseFileAst(code, fileName);
34656
+ const hir = buildHIR(
34657
+ ast,
34658
+ {
34659
+ state: /* @__PURE__ */ new Set(["$state"]),
34660
+ effect: /* @__PURE__ */ new Set(["$effect"])
34661
+ },
34662
+ {
34663
+ dev: true,
34664
+ fileName
34665
+ }
34666
+ );
34667
+ const sourceLines = code.split(/\r?\n/);
34668
+ const components = [];
34669
+ for (const fn of hir.functions) {
34670
+ if (!fn.loc || !shouldIncludeFunction(fn)) continue;
34671
+ const startLine = fn.loc.start.line;
34672
+ const endLine = fn.loc.end.line;
34673
+ const scopeResult = analyzeReactiveScopesWithSSA(fn);
34674
+ const regionResult = generateRegions(fn, scopeResult);
34675
+ const regions = includeRegions ? regionResult.topLevelRegions.map((region) => regionToSerializable(region, fn)) : void 0;
34676
+ const trace = inferTraceMarkersForComponent({
34677
+ fn,
34678
+ sourceLines,
34679
+ startLine,
34680
+ endLine,
34681
+ verbosity,
34682
+ regions
34683
+ });
34684
+ components.push({
34685
+ name: fn.name ?? "<anonymous>",
34686
+ startLine,
34687
+ endLine,
34688
+ trace,
34689
+ regions
34690
+ });
34691
+ }
34692
+ const diagnostics = includeDiagnostics ? analyzeDiagnostics(code, fileName, options) : [];
34693
+ return {
34694
+ fileName,
34695
+ components,
34696
+ diagnostics
34697
+ };
34698
+ }
34699
+
33821
34700
  // src/index.ts
33822
34701
  function stripMacroImports(path2, t4) {
33823
34702
  path2.traverse({
@@ -34711,46 +35590,77 @@ function createHIREntrypointVisitor(t4, options) {
34711
35590
  path2.traverse({
34712
35591
  JSXExpressionContainer(exprPath) {
34713
35592
  const expr = exprPath.node.expression;
34714
- if (!t4.isCallExpression(expr)) return;
34715
- if (!t4.isMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
35593
+ if (!t4.isCallExpression(expr) && !t4.isOptionalCallExpression(expr)) return;
35594
+ if (!t4.isMemberExpression(expr.callee) && !t4.isOptionalMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
34716
35595
  return;
34717
35596
  }
34718
- const cb = expr.arguments[0];
34719
- if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
34720
- const getReturnedJsx = (fn) => {
34721
- if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
34722
- if (t4.isBlockStatement(fn.body)) {
34723
- const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
34724
- if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
34725
- return ret.argument;
34726
- }
34727
- }
34728
- return null;
34729
- };
34730
- const jsx = getReturnedJsx(cb);
34731
- if (!jsx) return;
34732
- if (t4.isJSXFragment(jsx)) {
34733
- warn({
34734
- code: "FICT-J002",
34735
- message: "Missing key prop in list rendering.",
34736
- fileName,
34737
- line: expr.loc?.start.line ?? 0,
34738
- column: expr.loc ? expr.loc.start.column + 1 : 0
34739
- });
35597
+ const callExprPath = exprPath.get("expression");
35598
+ if (!callExprPath.isCallExpression() && !callExprPath.isOptionalCallExpression()) return;
35599
+ const argPaths = callExprPath.get("arguments");
35600
+ const cbPath = Array.isArray(argPaths) ? argPaths[0] : void 0;
35601
+ if (!cbPath || !cbPath.isArrowFunctionExpression() && !cbPath.isFunctionExpression()) {
34740
35602
  return;
34741
35603
  }
34742
- let hasKey = false;
34743
- let hasUnknownSpread = false;
34744
- for (const attr of jsx.openingElement.attributes) {
34745
- if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
34746
- hasKey = true;
34747
- break;
35604
+ const getReturnedJsx = (fnPath) => {
35605
+ const collectReturnedJsxFromExpression = (node, returned2) => {
35606
+ if (!node) return;
35607
+ if (t4.isJSXElement(node) || t4.isJSXFragment(node)) {
35608
+ returned2.push(node);
35609
+ return;
35610
+ }
35611
+ if (t4.isConditionalExpression(node)) {
35612
+ collectReturnedJsxFromExpression(node.consequent, returned2);
35613
+ collectReturnedJsxFromExpression(node.alternate, returned2);
35614
+ return;
35615
+ }
35616
+ if (t4.isLogicalExpression(node)) {
35617
+ collectReturnedJsxFromExpression(node.left, returned2);
35618
+ collectReturnedJsxFromExpression(node.right, returned2);
35619
+ return;
35620
+ }
35621
+ if (t4.isSequenceExpression(node)) {
35622
+ const tail = node.expressions[node.expressions.length - 1];
35623
+ collectReturnedJsxFromExpression(tail, returned2);
35624
+ return;
35625
+ }
35626
+ if (t4.isParenthesizedExpression(node)) {
35627
+ collectReturnedJsxFromExpression(node.expression, returned2);
35628
+ return;
35629
+ }
35630
+ if (t4.isTSAsExpression(node) || t4.isTSTypeAssertion(node) || t4.isTSNonNullExpression(node) || t4.isTSSatisfiesExpression(node) || t4.isTypeCastExpression(node)) {
35631
+ collectReturnedJsxFromExpression(node.expression, returned2);
35632
+ }
35633
+ };
35634
+ const fn = fnPath.node;
35635
+ const returned = [];
35636
+ if (!t4.isBlockStatement(fn.body)) {
35637
+ collectReturnedJsxFromExpression(fn.body, returned);
35638
+ return returned;
34748
35639
  }
34749
- if (t4.isJSXSpreadAttribute(attr)) {
34750
- hasUnknownSpread = true;
35640
+ fnPath.get("body").traverse({
35641
+ Function(innerFnPath) {
35642
+ innerFnPath.skip();
35643
+ },
35644
+ ReturnStatement(retPath) {
35645
+ collectReturnedJsxFromExpression(retPath.node.argument, returned);
35646
+ }
35647
+ });
35648
+ return returned;
35649
+ };
35650
+ const returnedJsx = getReturnedJsx(cbPath);
35651
+ if (returnedJsx.length === 0) return;
35652
+ const hasMissingKeyBranch = returnedJsx.some((jsx) => {
35653
+ if (t4.isJSXFragment(jsx)) return true;
35654
+ let hasKey = false;
35655
+ for (const attr of jsx.openingElement.attributes) {
35656
+ if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
35657
+ hasKey = true;
35658
+ break;
35659
+ }
34751
35660
  }
34752
- }
34753
- if (hasKey || hasUnknownSpread) return;
35661
+ return !hasKey;
35662
+ });
35663
+ if (!hasMissingKeyBranch) return;
34754
35664
  warn({
34755
35665
  code: "FICT-J002",
34756
35666
  message: "Missing key prop in list rendering.",
@@ -35313,8 +36223,10 @@ var createFictPlugin = (0, import_helper_plugin_utils.declare)(
35313
36223
  var index_default = createFictPlugin;
35314
36224
  // Annotate the CommonJS export names for ESM import in node:
35315
36225
  0 && (module.exports = {
36226
+ analyzeFictFile,
35316
36227
  clearModuleMetadata,
35317
36228
  createFictPlugin,
36229
+ inferTraceMarkersForComponent,
35318
36230
  resolveModuleMetadata,
35319
36231
  setModuleMetadata
35320
36232
  });