@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.js CHANGED
@@ -3059,7 +3059,7 @@ var require_identifier = __commonJS({
3059
3059
  value: true
3060
3060
  });
3061
3061
  exports.isIdentifierChar = isIdentifierChar;
3062
- exports.isIdentifierName = isIdentifierName;
3062
+ exports.isIdentifierName = isIdentifierName2;
3063
3063
  exports.isIdentifierStart = isIdentifierStart;
3064
3064
  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";
3065
3065
  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";
@@ -3100,7 +3100,7 @@ var require_identifier = __commonJS({
3100
3100
  }
3101
3101
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
3102
3102
  }
3103
- function isIdentifierName(name) {
3103
+ function isIdentifierName2(name) {
3104
3104
  let isFirst = true;
3105
3105
  for (let i = 0; i < name.length; i++) {
3106
3106
  let cp = name.charCodeAt(i);
@@ -14159,6 +14159,7 @@ var RUNTIME_HELPERS = {
14159
14159
  bindEvent: "bindEvent",
14160
14160
  callEventHandler: "callEventHandler",
14161
14161
  bindRef: "bindRef",
14162
+ spread: "spread",
14162
14163
  nonReactive: "nonReactive",
14163
14164
  toNodeArray: "toNodeArray",
14164
14165
  template: "template",
@@ -14216,6 +14217,7 @@ var RUNTIME_ALIASES = {
14216
14217
  bindEvent: "bindEvent",
14217
14218
  callEventHandler: "callEventHandler",
14218
14219
  bindRef: "bindRef",
14220
+ spread: "spread",
14219
14221
  nonReactive: "nonReactive",
14220
14222
  toNodeArray: "toNodeArray",
14221
14223
  template: "template",
@@ -17071,6 +17073,37 @@ function clearModuleMetadata(options) {
17071
17073
  }
17072
17074
 
17073
17075
  // src/validation.ts
17076
+ var DiagnosticCode = /* @__PURE__ */ ((DiagnosticCode2) => {
17077
+ DiagnosticCode2["FICT_P001"] = "FICT-P001";
17078
+ DiagnosticCode2["FICT_P002"] = "FICT-P002";
17079
+ DiagnosticCode2["FICT_P003"] = "FICT-P003";
17080
+ DiagnosticCode2["FICT_P004"] = "FICT-P004";
17081
+ DiagnosticCode2["FICT_P005"] = "FICT-P005";
17082
+ DiagnosticCode2["FICT_S001"] = "FICT-S001";
17083
+ DiagnosticCode2["FICT_S002"] = "FICT-S002";
17084
+ DiagnosticCode2["FICT_E001"] = "FICT-E001";
17085
+ DiagnosticCode2["FICT_E002"] = "FICT-E002";
17086
+ DiagnosticCode2["FICT_E003"] = "FICT-E003";
17087
+ DiagnosticCode2["FICT_M001"] = "FICT-M001";
17088
+ DiagnosticCode2["FICT_M002"] = "FICT-M002";
17089
+ DiagnosticCode2["FICT_M003"] = "FICT-M003";
17090
+ DiagnosticCode2["FICT_C001"] = "FICT-C001";
17091
+ DiagnosticCode2["FICT_C002"] = "FICT-C002";
17092
+ DiagnosticCode2["FICT_C003"] = "FICT-C003";
17093
+ DiagnosticCode2["FICT_C004"] = "FICT-C004";
17094
+ DiagnosticCode2["FICT_J001"] = "FICT-J001";
17095
+ DiagnosticCode2["FICT_J002"] = "FICT-J002";
17096
+ DiagnosticCode2["FICT_J003"] = "FICT-J003";
17097
+ DiagnosticCode2["FICT_R001"] = "FICT-R001";
17098
+ DiagnosticCode2["FICT_R002"] = "FICT-R002";
17099
+ DiagnosticCode2["FICT_R003"] = "FICT-R003";
17100
+ DiagnosticCode2["FICT_R004"] = "FICT-R004";
17101
+ DiagnosticCode2["FICT_R005"] = "FICT-R005";
17102
+ DiagnosticCode2["FICT_X001"] = "FICT-X001";
17103
+ DiagnosticCode2["FICT_X002"] = "FICT-X002";
17104
+ DiagnosticCode2["FICT_X003"] = "FICT-X003";
17105
+ return DiagnosticCode2;
17106
+ })(DiagnosticCode || {});
17074
17107
  var DiagnosticMessages = {
17075
17108
  ["FICT-P001" /* FICT_P001 */]: "Props destructuring falls back to non-reactive binding.",
17076
17109
  ["FICT-P002" /* FICT_P002 */]: "Array rest in props destructuring falls back to non-reactive binding.",
@@ -17158,6 +17191,16 @@ function reportDiagnostic(ctx, code, node, context) {
17158
17191
  });
17159
17192
  }
17160
17193
  }
17194
+ function getAllDiagnosticCodes() {
17195
+ return Object.values(DiagnosticCode);
17196
+ }
17197
+ function getDiagnosticInfo(code) {
17198
+ return {
17199
+ code,
17200
+ severity: DiagnosticSeverities[code],
17201
+ message: DiagnosticMessages[code]
17202
+ };
17203
+ }
17161
17204
 
17162
17205
  // src/ir/ssa.ts
17163
17206
  function enterSSA(program) {
@@ -22601,138 +22644,212 @@ function emitConditionalChild(startMarkerId, endMarkerId, expr, statements, ctx,
22601
22644
  );
22602
22645
  }
22603
22646
 
22604
- // src/ir/codegen-expression-deps.ts
22605
- function getMemberDependencyPath(expr) {
22606
- if (expr.kind === "MemberExpression") {
22607
- const prop = expr.property;
22608
- let propName;
22609
- if (!expr.computed && prop.kind === "Identifier") {
22610
- propName = prop.name;
22611
- } else if (prop.kind === "Literal" && typeof prop.value === "string") {
22612
- propName = prop.value;
22613
- }
22614
- if (!propName) return void 0;
22615
- const object = expr.object;
22616
- if (object.kind === "Identifier") {
22617
- return `${deSSAVarName(object.name)}.${propName}`;
22618
- }
22619
- if (object.kind === "MemberExpression") {
22620
- const parent = getMemberDependencyPath(object);
22621
- return parent ? `${parent}.${propName}` : void 0;
22622
- }
22647
+ // src/ir/walk-expression.ts
22648
+ function assertNever(value) {
22649
+ throw new Error(`Unhandled node in walkExpression: ${JSON.stringify(value)}`);
22650
+ }
22651
+ function walkTerminator(term, visitNode, inFunctionBody) {
22652
+ switch (term.kind) {
22653
+ case "Return":
22654
+ if (term.argument) visitNode(term.argument, null, inFunctionBody);
22655
+ return;
22656
+ case "Throw":
22657
+ visitNode(term.argument, null, inFunctionBody);
22658
+ return;
22659
+ case "Branch":
22660
+ visitNode(term.test, null, inFunctionBody);
22661
+ return;
22662
+ case "Switch":
22663
+ visitNode(term.discriminant, null, inFunctionBody);
22664
+ term.cases.forEach((c) => {
22665
+ if (c.test) visitNode(c.test, null, inFunctionBody);
22666
+ });
22667
+ return;
22668
+ case "ForOf":
22669
+ visitNode(term.iterable, null, inFunctionBody);
22670
+ return;
22671
+ case "ForIn":
22672
+ visitNode(term.object, null, inFunctionBody);
22673
+ return;
22674
+ case "Jump":
22675
+ case "Unreachable":
22676
+ case "Break":
22677
+ case "Continue":
22678
+ case "Try":
22679
+ return;
22680
+ default:
22681
+ assertNever(term);
22682
+ }
22683
+ }
22684
+ function walkInstruction(instr, visitNode, inFunctionBody) {
22685
+ switch (instr.kind) {
22686
+ case "Assign":
22687
+ case "Expression":
22688
+ visitNode(instr.value, null, inFunctionBody);
22689
+ return;
22690
+ case "Phi":
22691
+ instr.sources.forEach((source) => visitNode(source.id, null, inFunctionBody));
22692
+ return;
22693
+ default:
22694
+ assertNever(instr);
22623
22695
  }
22624
- return void 0;
22625
22696
  }
22626
- function collectBlockDependencies(blocks, deps) {
22697
+ function walkBlocks(blocks, visitNode, inFunctionBody) {
22627
22698
  for (const block of blocks) {
22628
- for (const instr of block.instructions) {
22629
- switch (instr.kind) {
22630
- case "Assign":
22631
- case "Expression":
22632
- collectExpressionDependencies(instr.value, deps);
22633
- break;
22634
- case "Phi":
22635
- for (const source of instr.sources) {
22636
- deps.add(deSSAVarName(source.id.name));
22699
+ block.instructions.forEach((instr) => walkInstruction(instr, visitNode, inFunctionBody));
22700
+ walkTerminator(block.terminator, visitNode, inFunctionBody);
22701
+ }
22702
+ }
22703
+ function walkExpression(expr, visit, options = {}) {
22704
+ const includeFunctionBodies = options.includeFunctionBodies ?? true;
22705
+ const visitNode = (node, parent, inFunctionBody) => {
22706
+ visit(node, { parent, inFunctionBody });
22707
+ switch (node.kind) {
22708
+ case "Identifier":
22709
+ case "Literal":
22710
+ case "MetaProperty":
22711
+ case "ThisExpression":
22712
+ case "SuperExpression":
22713
+ return;
22714
+ case "ImportExpression":
22715
+ visitNode(node.source, node, inFunctionBody);
22716
+ return;
22717
+ case "CallExpression":
22718
+ case "OptionalCallExpression":
22719
+ visitNode(node.callee, node, inFunctionBody);
22720
+ node.arguments.forEach((arg) => visitNode(arg, node, inFunctionBody));
22721
+ return;
22722
+ case "MemberExpression":
22723
+ case "OptionalMemberExpression":
22724
+ visitNode(node.object, node, inFunctionBody);
22725
+ if (node.computed) {
22726
+ visitNode(node.property, node, inFunctionBody);
22727
+ }
22728
+ return;
22729
+ case "BinaryExpression":
22730
+ case "LogicalExpression":
22731
+ visitNode(node.left, node, inFunctionBody);
22732
+ visitNode(node.right, node, inFunctionBody);
22733
+ return;
22734
+ case "UnaryExpression":
22735
+ case "AwaitExpression":
22736
+ visitNode(node.argument, node, inFunctionBody);
22737
+ return;
22738
+ case "ConditionalExpression":
22739
+ visitNode(node.test, node, inFunctionBody);
22740
+ visitNode(node.consequent, node, inFunctionBody);
22741
+ visitNode(node.alternate, node, inFunctionBody);
22742
+ return;
22743
+ case "ArrayExpression":
22744
+ node.elements.forEach((el) => visitNode(el, node, inFunctionBody));
22745
+ return;
22746
+ case "ObjectExpression":
22747
+ node.properties.forEach((prop) => {
22748
+ if (prop.kind === "SpreadElement") {
22749
+ visitNode(prop.argument, node, inFunctionBody);
22750
+ } else {
22751
+ if (prop.computed) {
22752
+ visitNode(prop.key, node, inFunctionBody);
22753
+ }
22754
+ visitNode(prop.value, node, inFunctionBody);
22637
22755
  }
22638
- break;
22639
- default:
22640
- break;
22641
- }
22642
- }
22643
- const term = block.terminator;
22644
- switch (term.kind) {
22645
- case "Return":
22646
- if (term.argument) collectExpressionDependencies(term.argument, deps);
22647
- break;
22648
- case "Throw":
22649
- collectExpressionDependencies(term.argument, deps);
22650
- break;
22651
- case "Branch":
22652
- collectExpressionDependencies(term.test, deps);
22653
- break;
22654
- case "Switch":
22655
- collectExpressionDependencies(term.discriminant, deps);
22656
- for (const c of term.cases) {
22657
- if (c.test) collectExpressionDependencies(c.test, deps);
22756
+ });
22757
+ return;
22758
+ case "JSXElement":
22759
+ if (typeof node.tagName !== "string") {
22760
+ visitNode(node.tagName, node, inFunctionBody);
22658
22761
  }
22659
- break;
22660
- case "ForOf":
22661
- collectExpressionDependencies(term.iterable, deps);
22662
- break;
22663
- case "ForIn":
22664
- collectExpressionDependencies(term.object, deps);
22665
- break;
22762
+ node.attributes.forEach((attr) => {
22763
+ if (attr.isSpread && attr.spreadExpr) {
22764
+ visitNode(attr.spreadExpr, node, inFunctionBody);
22765
+ } else if (attr.value) {
22766
+ visitNode(attr.value, node, inFunctionBody);
22767
+ }
22768
+ });
22769
+ node.children.forEach((child) => {
22770
+ if (child.kind === "expression") {
22771
+ visitNode(child.value, node, inFunctionBody);
22772
+ } else if (child.kind === "element") {
22773
+ visitNode(child.value, node, inFunctionBody);
22774
+ }
22775
+ });
22776
+ return;
22777
+ case "ArrowFunction":
22778
+ if (!includeFunctionBodies) return;
22779
+ if (node.isExpression && !Array.isArray(node.body)) {
22780
+ visitNode(node.body, node, true);
22781
+ } else if (Array.isArray(node.body)) {
22782
+ walkBlocks(node.body, visitNode, true);
22783
+ }
22784
+ return;
22785
+ case "FunctionExpression":
22786
+ if (!includeFunctionBodies) return;
22787
+ walkBlocks(node.body, visitNode, true);
22788
+ return;
22789
+ case "AssignmentExpression":
22790
+ visitNode(node.left, node, inFunctionBody);
22791
+ visitNode(node.right, node, inFunctionBody);
22792
+ return;
22793
+ case "UpdateExpression":
22794
+ visitNode(node.argument, node, inFunctionBody);
22795
+ return;
22796
+ case "TemplateLiteral":
22797
+ node.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22798
+ return;
22799
+ case "SpreadElement":
22800
+ visitNode(node.argument, node, inFunctionBody);
22801
+ return;
22802
+ case "NewExpression":
22803
+ visitNode(node.callee, node, inFunctionBody);
22804
+ node.arguments.forEach((arg) => visitNode(arg, node, inFunctionBody));
22805
+ return;
22806
+ case "SequenceExpression":
22807
+ node.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22808
+ return;
22809
+ case "YieldExpression":
22810
+ if (node.argument) {
22811
+ visitNode(node.argument, node, inFunctionBody);
22812
+ }
22813
+ return;
22814
+ case "TaggedTemplateExpression":
22815
+ visitNode(node.tag, node, inFunctionBody);
22816
+ node.quasi.expressions.forEach((item) => visitNode(item, node, inFunctionBody));
22817
+ return;
22818
+ case "ClassExpression":
22819
+ if (node.superClass) {
22820
+ visitNode(node.superClass, node, inFunctionBody);
22821
+ }
22822
+ return;
22666
22823
  default:
22667
- break;
22824
+ assertNever(node);
22668
22825
  }
22826
+ };
22827
+ visitNode(expr, null, false);
22828
+ }
22829
+
22830
+ // src/ir/codegen-expression-deps.ts
22831
+ function getMemberDependencyPath(expr) {
22832
+ if (expr.kind !== "MemberExpression" && expr.kind !== "OptionalMemberExpression") return void 0;
22833
+ const depPath = extractDependencyPath(expr);
22834
+ if (!depPath || depPath.segments.length === 0) return void 0;
22835
+ const base = deSSAVarName(depPath.base);
22836
+ if (depPath.segments.some((segment) => segment.computed)) {
22837
+ return base;
22669
22838
  }
22839
+ const tail = depPath.segments.map((segment) => segment.property).join(".");
22840
+ return tail ? `${base}.${tail}` : void 0;
22670
22841
  }
22671
22842
  function collectExpressionDependencies(expr, deps) {
22672
- if (expr.kind === "Identifier") {
22673
- deps.add(deSSAVarName(expr.name));
22674
- return;
22675
- }
22676
- if (expr.kind === "ArrowFunction") {
22677
- if (expr.isExpression && !Array.isArray(expr.body)) {
22678
- collectExpressionDependencies(expr.body, deps);
22679
- } else if (Array.isArray(expr.body)) {
22680
- collectBlockDependencies(expr.body, deps);
22843
+ walkExpression(expr, (node) => {
22844
+ if (node.kind === "Identifier") {
22845
+ deps.add(deSSAVarName(node.name));
22846
+ return;
22681
22847
  }
22682
- return;
22683
- }
22684
- if (expr.kind === "FunctionExpression") {
22685
- collectBlockDependencies(expr.body, deps);
22686
- return;
22687
- }
22688
- if (expr.kind === "MemberExpression") {
22689
- const path2 = getMemberDependencyPath(expr);
22690
- if (path2) deps.add(path2);
22691
- collectExpressionDependencies(expr.object, deps);
22692
- if (expr.computed && expr.property.kind !== "Literal") {
22693
- collectExpressionDependencies(expr.property, deps);
22848
+ const path2 = getMemberDependencyPath(node);
22849
+ if (path2) {
22850
+ deps.add(path2);
22694
22851
  }
22695
- return;
22696
- }
22697
- if (expr.kind === "CallExpression") {
22698
- collectExpressionDependencies(expr.callee, deps);
22699
- expr.arguments.forEach((a) => collectExpressionDependencies(a, deps));
22700
- return;
22701
- }
22702
- if (expr.kind === "BinaryExpression" || expr.kind === "LogicalExpression") {
22703
- collectExpressionDependencies(expr.left, deps);
22704
- collectExpressionDependencies(expr.right, deps);
22705
- return;
22706
- }
22707
- if (expr.kind === "ConditionalExpression") {
22708
- collectExpressionDependencies(expr.test, deps);
22709
- collectExpressionDependencies(expr.consequent, deps);
22710
- collectExpressionDependencies(expr.alternate, deps);
22711
- return;
22712
- }
22713
- if (expr.kind === "UnaryExpression") {
22714
- collectExpressionDependencies(expr.argument, deps);
22715
- return;
22716
- }
22717
- if (expr.kind === "ArrayExpression") {
22718
- expr.elements.forEach((el) => collectExpressionDependencies(el, deps));
22719
- return;
22720
- }
22721
- if (expr.kind === "ObjectExpression") {
22722
- expr.properties.forEach((p) => {
22723
- if (p.kind === "SpreadElement") {
22724
- collectExpressionDependencies(p.argument, deps);
22725
- } else {
22726
- if (p.computed) collectExpressionDependencies(p.key, deps);
22727
- collectExpressionDependencies(p.value, deps);
22728
- }
22729
- });
22730
- return;
22731
- }
22732
- if (expr.kind === "TemplateLiteral") {
22733
- expr.expressions.forEach((e) => collectExpressionDependencies(e, deps));
22734
- return;
22735
- }
22852
+ });
22736
22853
  }
22737
22854
 
22738
22855
  // src/ir/codegen-reactive-kind.ts
@@ -24176,34 +24293,138 @@ function extractKeyFromAttributes(attributes) {
24176
24293
  }
24177
24294
  return void 0;
24178
24295
  }
24296
+ function collectReturnedJSXFromExpression(expression, returned) {
24297
+ if (expression.kind === "JSXElement") {
24298
+ returned.push(expression);
24299
+ return;
24300
+ }
24301
+ if (expression.kind === "ConditionalExpression") {
24302
+ collectReturnedJSXFromExpression(expression.consequent, returned);
24303
+ collectReturnedJSXFromExpression(expression.alternate, returned);
24304
+ return;
24305
+ }
24306
+ if (expression.kind === "LogicalExpression") {
24307
+ collectReturnedJSXFromExpression(expression.left, returned);
24308
+ collectReturnedJSXFromExpression(expression.right, returned);
24309
+ return;
24310
+ }
24311
+ if (expression.kind === "SequenceExpression") {
24312
+ const tail = expression.expressions[expression.expressions.length - 1];
24313
+ if (tail) collectReturnedJSXFromExpression(tail, returned);
24314
+ }
24315
+ }
24316
+ function extractKeyExpressionFromReturnedExpression(expression) {
24317
+ if (expression.kind === "JSXElement") {
24318
+ return extractKeyFromAttributes(expression.attributes);
24319
+ }
24320
+ if (expression.kind === "ConditionalExpression") {
24321
+ const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
24322
+ const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
24323
+ if (!consequentKey || !alternateKey) return void 0;
24324
+ return {
24325
+ kind: "ConditionalExpression",
24326
+ test: expression.test,
24327
+ consequent: consequentKey,
24328
+ alternate: alternateKey,
24329
+ loc: expression.loc
24330
+ };
24331
+ }
24332
+ if (expression.kind === "SequenceExpression") {
24333
+ const tail = expression.expressions[expression.expressions.length - 1];
24334
+ return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
24335
+ }
24336
+ return void 0;
24337
+ }
24338
+ function getReturnedKeyExpressionsFromCallback(callback) {
24339
+ const returned = [];
24340
+ if (callback.kind === "ArrowFunction") {
24341
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24342
+ const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
24343
+ if (keyExpr) returned.push(keyExpr);
24344
+ return returned;
24345
+ }
24346
+ if (Array.isArray(callback.body)) {
24347
+ for (const block of callback.body) {
24348
+ const term = block.terminator;
24349
+ if (term.kind !== "Return" || !term.argument) continue;
24350
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24351
+ if (keyExpr) returned.push(keyExpr);
24352
+ }
24353
+ }
24354
+ return returned;
24355
+ }
24356
+ if (callback.kind === "FunctionExpression") {
24357
+ for (const block of callback.body ?? []) {
24358
+ const term = block.terminator;
24359
+ if (term.kind !== "Return" || !term.argument) continue;
24360
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24361
+ if (keyExpr) returned.push(keyExpr);
24362
+ }
24363
+ }
24364
+ return returned;
24365
+ }
24179
24366
  function getReturnedJSXFromCallback(callback) {
24367
+ const returned = [];
24180
24368
  if (callback.kind === "ArrowFunction") {
24181
- if (callback.isExpression && !Array.isArray(callback.body) && callback.body.kind === "JSXElement") {
24182
- return callback.body;
24369
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24370
+ collectReturnedJSXFromExpression(callback.body, returned);
24371
+ return returned;
24183
24372
  }
24184
24373
  if (Array.isArray(callback.body)) {
24185
24374
  for (const block of callback.body) {
24186
24375
  const term = block.terminator;
24187
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24188
- return term.argument;
24376
+ if (term.kind === "Return" && term.argument) {
24377
+ collectReturnedJSXFromExpression(term.argument, returned);
24189
24378
  }
24190
24379
  }
24191
24380
  }
24381
+ return returned;
24192
24382
  }
24193
24383
  if (callback.kind === "FunctionExpression") {
24194
24384
  for (const block of callback.body ?? []) {
24195
24385
  const term = block.terminator;
24196
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24197
- return term.argument;
24386
+ if (term.kind === "Return" && term.argument) {
24387
+ collectReturnedJSXFromExpression(term.argument, returned);
24198
24388
  }
24199
24389
  }
24200
24390
  }
24201
- return null;
24391
+ return returned;
24392
+ }
24393
+ function keyExpressionSignature(expression) {
24394
+ try {
24395
+ return JSON.stringify(expression, (key, value) => {
24396
+ if (key === "loc") return void 0;
24397
+ if (typeof value === "bigint") return `__bigint:${value.toString()}`;
24398
+ return value;
24399
+ }) ?? "";
24400
+ } catch {
24401
+ return "";
24402
+ }
24202
24403
  }
24203
24404
  function extractKeyFromMapCallback(callback) {
24204
- const jsx = getReturnedJSXFromCallback(callback);
24205
- if (!jsx) return void 0;
24206
- return extractKeyFromAttributes(jsx.attributes);
24405
+ const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
24406
+ if (returnedKeyExpressions.length === 1) {
24407
+ return returnedKeyExpressions[0];
24408
+ }
24409
+ if (returnedKeyExpressions.length > 1) {
24410
+ const [firstKey2, ...restKeys2] = returnedKeyExpressions;
24411
+ const firstSignature2 = keyExpressionSignature(firstKey2);
24412
+ if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
24413
+ return firstKey2;
24414
+ }
24415
+ }
24416
+ const returned = getReturnedJSXFromCallback(callback);
24417
+ if (returned.length === 0) return void 0;
24418
+ const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
24419
+ if (keyExpressions.some((expr) => !expr)) return void 0;
24420
+ const [firstKey, ...restKeys] = keyExpressions;
24421
+ const firstSignature = keyExpressionSignature(firstKey);
24422
+ if (!firstSignature) return void 0;
24423
+ const allBranchesSameKey = restKeys.every(
24424
+ (keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
24425
+ );
24426
+ if (!allBranchesSameKey) return void 0;
24427
+ return firstKey;
24207
24428
  }
24208
24429
 
24209
24430
  // src/ir/codegen-overrides.ts
@@ -24808,6 +25029,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24808
25029
  ctx.inListRender = true;
24809
25030
  let callbackExpr = ops.lowerExpression(mapCallback, ctx);
24810
25031
  ctx.inListRender = prevInListRender;
25032
+ const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
25033
+ let deferredCallbackId = null;
25034
+ let deferredCallbackInitId = null;
25035
+ let deferredItemsId = null;
25036
+ if (shouldDeferOptionalCallbackEvaluation) {
25037
+ deferredCallbackId = ops.genTemp(ctx, "mapCb");
25038
+ deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
25039
+ deferredItemsId = ops.genTemp(ctx, "mapItems");
25040
+ }
24811
25041
  const capturedKeyParamName = ctx.listKeyParamName;
24812
25042
  ctx.listKeyExpr = prevListKeyExpr;
24813
25043
  ctx.listItemParamName = prevListItemParamName;
@@ -24907,7 +25137,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24907
25137
  ],
24908
25138
  keyExprAst
24909
25139
  );
24910
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25140
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
24911
25141
  if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
24912
25142
  const newParams = [...callbackExpr.params];
24913
25143
  while (newParams.length < 2) {
@@ -24927,25 +25157,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24927
25157
  }
24928
25158
  }
24929
25159
  statements.push(...hoistedStatements);
25160
+ if (shouldDeferOptionalCallbackEvaluation) {
25161
+ statements.push(
25162
+ t4.variableDeclaration("let", [
25163
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25164
+ ]),
25165
+ t4.variableDeclaration("let", [
25166
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25167
+ ])
25168
+ );
25169
+ }
25170
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25171
+ [],
25172
+ t4.blockStatement([
25173
+ t4.variableDeclaration("const", [
25174
+ t4.variableDeclarator(
25175
+ t4.cloneNode(deferredItemsId, true),
25176
+ t4.cloneNode(arrayExprBase, true)
25177
+ )
25178
+ ]),
25179
+ t4.ifStatement(
25180
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25181
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25182
+ ),
25183
+ t4.ifStatement(
25184
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25185
+ t4.blockStatement([
25186
+ t4.expressionStatement(
25187
+ t4.assignmentExpression(
25188
+ "=",
25189
+ t4.cloneNode(deferredCallbackId, true),
25190
+ t4.cloneNode(callbackExpr, true)
25191
+ )
25192
+ ),
25193
+ t4.expressionStatement(
25194
+ t4.assignmentExpression(
25195
+ "=",
25196
+ t4.cloneNode(deferredCallbackInitId, true),
25197
+ t4.booleanLiteral(true)
25198
+ )
25199
+ )
25200
+ ])
25201
+ ),
25202
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25203
+ ])
25204
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25205
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25206
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25207
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25208
+ t4.identifier("__item"),
25209
+ t4.identifier("__index"),
25210
+ t4.identifier("__key")
25211
+ ])
25212
+ ) : callbackExpr;
24930
25213
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
24931
- t4.arrowFunctionExpression([], arrayExpr),
25214
+ getItemsExpr,
24932
25215
  keyFn,
24933
- callbackExpr,
25216
+ renderExpr,
24934
25217
  t4.booleanLiteral(hasIndexParam)
24935
25218
  ]);
24936
25219
  } else {
24937
25220
  statements.push(...hoistedStatements);
25221
+ if (shouldDeferOptionalCallbackEvaluation) {
25222
+ statements.push(
25223
+ t4.variableDeclaration("let", [
25224
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25225
+ ]),
25226
+ t4.variableDeclaration("let", [
25227
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25228
+ ])
25229
+ );
25230
+ }
24938
25231
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
24939
25232
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[1]) ? callbackExpr.params[1].name : "__index" : "__index";
24940
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25233
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25234
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25235
+ [],
25236
+ t4.blockStatement([
25237
+ t4.variableDeclaration("const", [
25238
+ t4.variableDeclarator(
25239
+ t4.cloneNode(deferredItemsId, true),
25240
+ t4.cloneNode(arrayExprBase, true)
25241
+ )
25242
+ ]),
25243
+ t4.ifStatement(
25244
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25245
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25246
+ ),
25247
+ t4.ifStatement(
25248
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25249
+ t4.blockStatement([
25250
+ t4.expressionStatement(
25251
+ t4.assignmentExpression(
25252
+ "=",
25253
+ t4.cloneNode(deferredCallbackId, true),
25254
+ t4.cloneNode(callbackExpr, true)
25255
+ )
25256
+ ),
25257
+ t4.expressionStatement(
25258
+ t4.assignmentExpression(
25259
+ "=",
25260
+ t4.cloneNode(deferredCallbackInitId, true),
25261
+ t4.booleanLiteral(true)
25262
+ )
25263
+ )
25264
+ ])
25265
+ ),
25266
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25267
+ ])
25268
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25269
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25270
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25271
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25272
+ t4.identifier("__item"),
25273
+ t4.identifier("__index"),
25274
+ t4.identifier("__key")
25275
+ ])
25276
+ ) : callbackExpr;
24941
25277
  const keyFn = t4.arrowFunctionExpression(
24942
25278
  [t4.identifier(itemParamName), t4.identifier(indexParamName)],
24943
25279
  t4.identifier(indexParamName)
24944
25280
  );
24945
25281
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
24946
- t4.arrowFunctionExpression([], arrayExpr),
25282
+ getItemsExpr,
24947
25283
  keyFn,
24948
- callbackExpr,
25284
+ renderExpr,
24949
25285
  t4.booleanLiteral(hasIndexParam)
24950
25286
  ]);
24951
25287
  }
@@ -26110,8 +26446,31 @@ function extractHIRStaticHtml(jsx, ctx, ops, parentPath = [], namespace = null)
26110
26446
  const resolvedNamespace = resolveNamespaceContext(tagName, namespace);
26111
26447
  let html = `<${tagName}`;
26112
26448
  const bindings = [];
26113
- for (const attr of jsx.attributes) {
26449
+ for (let attrIndex = 0; attrIndex < jsx.attributes.length; attrIndex++) {
26450
+ const attr = jsx.attributes[attrIndex];
26114
26451
  if (attr.isSpread) {
26452
+ if (attr.spreadExpr) {
26453
+ const excluded = /* @__PURE__ */ new Set();
26454
+ for (let nextIndex = attrIndex + 1; nextIndex < jsx.attributes.length; nextIndex++) {
26455
+ const nextAttr = jsx.attributes[nextIndex];
26456
+ if (nextAttr.isSpread) continue;
26457
+ let nextName = normalizeHIRAttrName(nextAttr.name);
26458
+ if (nextName.endsWith("$")) {
26459
+ nextName = nextName.slice(0, -1);
26460
+ }
26461
+ if (nextName === "key") continue;
26462
+ excluded.add(nextName);
26463
+ if (nextName !== nextAttr.name) {
26464
+ excluded.add(nextAttr.name);
26465
+ }
26466
+ }
26467
+ bindings.push({
26468
+ type: "spread",
26469
+ path: [...parentPath],
26470
+ expr: attr.spreadExpr,
26471
+ exclude: excluded.size > 0 ? Array.from(excluded) : void 0
26472
+ });
26473
+ }
26115
26474
  continue;
26116
26475
  }
26117
26476
  let name = normalizeHIRAttrName(attr.name);
@@ -28296,10 +28655,28 @@ function lowerIntrinsicElement(jsx, ctx) {
28296
28655
  )
28297
28656
  );
28298
28657
  }
28658
+ fusedPatchGroups.clear();
28299
28659
  };
28300
28660
  for (const binding of bindings) {
28301
28661
  const targetId = resolveHIRBindingPath(binding.path, nodeCache, statements, ctx, genTemp3);
28302
- if (binding.type === "event" && binding.expr && binding.name) {
28662
+ if (binding.type === "spread" && binding.expr) {
28663
+ flushFusedPatchGroups();
28664
+ ctx.helpersUsed.add("spread");
28665
+ const spreadValueExpr = lowerDomExpression(binding.expr, ctx, containingRegion);
28666
+ const spreadGetter = t4.arrowFunctionExpression([], spreadValueExpr);
28667
+ const spreadArgs = [
28668
+ targetId,
28669
+ spreadGetter,
28670
+ t4.booleanLiteral(Boolean(isSVG || isMathML)),
28671
+ t4.booleanLiteral(true)
28672
+ ];
28673
+ if (binding.exclude && binding.exclude.length > 0) {
28674
+ spreadArgs.push(t4.arrayExpression(binding.exclude.map((name) => t4.stringLiteral(name))));
28675
+ }
28676
+ statements.push(
28677
+ t4.expressionStatement(t4.callExpression(t4.identifier(RUNTIME_ALIASES.spread), spreadArgs))
28678
+ );
28679
+ } else if (binding.type === "event" && binding.expr && binding.name) {
28303
28680
  const eventName = binding.name;
28304
28681
  const hasEventOptions = binding.eventOptions && (binding.eventOptions.capture || binding.eventOptions.passive || binding.eventOptions.once);
28305
28682
  const isDelegated = DelegatedEvents.has(eventName) && !hasEventOptions;
@@ -33087,106 +33464,106 @@ function collectUsesFromTerminator(term, add, inFunctionBody = false) {
33087
33464
  }
33088
33465
  }
33089
33466
  function collectUsesFromExpression(expr, add, inFunctionBody = false) {
33090
- walkExpression(expr, add, { inFunctionBody, shadowed: /* @__PURE__ */ new Set() });
33467
+ walkExpression2(expr, add, { inFunctionBody, shadowed: /* @__PURE__ */ new Set() });
33091
33468
  }
33092
- function walkExpression(expr, add, ctx) {
33469
+ function walkExpression2(expr, add, ctx) {
33093
33470
  switch (expr.kind) {
33094
33471
  case "Identifier":
33095
33472
  if (!ctx.shadowed.has(expr.name)) add(expr.name, ctx.inFunctionBody);
33096
33473
  return;
33097
33474
  case "CallExpression":
33098
33475
  case "OptionalCallExpression":
33099
- walkExpression(expr.callee, add, ctx);
33100
- expr.arguments.forEach((arg) => walkExpression(arg, add, ctx));
33476
+ walkExpression2(expr.callee, add, ctx);
33477
+ expr.arguments.forEach((arg) => walkExpression2(arg, add, ctx));
33101
33478
  return;
33102
33479
  case "MemberExpression":
33103
33480
  case "OptionalMemberExpression":
33104
- walkExpression(expr.object, add, ctx);
33105
- if (expr.computed) walkExpression(expr.property, add, ctx);
33481
+ walkExpression2(expr.object, add, ctx);
33482
+ if (expr.computed) walkExpression2(expr.property, add, ctx);
33106
33483
  return;
33107
33484
  case "BinaryExpression":
33108
33485
  case "LogicalExpression":
33109
- walkExpression(expr.left, add, ctx);
33110
- walkExpression(expr.right, add, ctx);
33486
+ walkExpression2(expr.left, add, ctx);
33487
+ walkExpression2(expr.right, add, ctx);
33111
33488
  return;
33112
33489
  case "UnaryExpression":
33113
- walkExpression(expr.argument, add, ctx);
33490
+ walkExpression2(expr.argument, add, ctx);
33114
33491
  return;
33115
33492
  case "ConditionalExpression":
33116
- walkExpression(expr.test, add, ctx);
33117
- walkExpression(expr.consequent, add, ctx);
33118
- walkExpression(expr.alternate, add, ctx);
33493
+ walkExpression2(expr.test, add, ctx);
33494
+ walkExpression2(expr.consequent, add, ctx);
33495
+ walkExpression2(expr.alternate, add, ctx);
33119
33496
  return;
33120
33497
  case "ArrayExpression":
33121
33498
  expr.elements.forEach((el) => {
33122
- if (el) walkExpression(el, add, ctx);
33499
+ if (el) walkExpression2(el, add, ctx);
33123
33500
  });
33124
33501
  return;
33125
33502
  case "ObjectExpression":
33126
33503
  expr.properties.forEach((prop) => {
33127
33504
  if (prop.kind === "SpreadElement") {
33128
- walkExpression(prop.argument, add, ctx);
33505
+ walkExpression2(prop.argument, add, ctx);
33129
33506
  } else {
33130
- if (prop.computed) walkExpression(prop.key, add, ctx);
33131
- walkExpression(prop.value, add, ctx);
33507
+ if (prop.computed) walkExpression2(prop.key, add, ctx);
33508
+ walkExpression2(prop.value, add, ctx);
33132
33509
  }
33133
33510
  });
33134
33511
  return;
33135
33512
  case "TemplateLiteral":
33136
- expr.expressions.forEach((e) => walkExpression(e, add, ctx));
33513
+ expr.expressions.forEach((e) => walkExpression2(e, add, ctx));
33137
33514
  return;
33138
33515
  case "SpreadElement":
33139
- walkExpression(expr.argument, add, ctx);
33516
+ walkExpression2(expr.argument, add, ctx);
33140
33517
  return;
33141
33518
  case "SequenceExpression":
33142
- expr.expressions.forEach((e) => walkExpression(e, add, ctx));
33519
+ expr.expressions.forEach((e) => walkExpression2(e, add, ctx));
33143
33520
  return;
33144
33521
  case "AwaitExpression":
33145
- walkExpression(expr.argument, add, ctx);
33522
+ walkExpression2(expr.argument, add, ctx);
33146
33523
  return;
33147
33524
  case "NewExpression":
33148
- walkExpression(expr.callee, add, ctx);
33149
- expr.arguments.forEach((arg) => walkExpression(arg, add, ctx));
33525
+ walkExpression2(expr.callee, add, ctx);
33526
+ expr.arguments.forEach((arg) => walkExpression2(arg, add, ctx));
33150
33527
  return;
33151
33528
  case "ArrowFunction": {
33152
33529
  const shadowed = new Set(ctx.shadowed);
33153
33530
  expr.params.forEach((p) => shadowed.add(p.name));
33154
33531
  if (expr.isExpression) {
33155
- walkExpression(expr.body, add, { inFunctionBody: true, shadowed });
33532
+ walkExpression2(expr.body, add, { inFunctionBody: true, shadowed });
33156
33533
  return;
33157
33534
  }
33158
- walkBlocks(expr.body, add, { inFunctionBody: true, shadowed });
33535
+ walkBlocks2(expr.body, add, { inFunctionBody: true, shadowed });
33159
33536
  return;
33160
33537
  }
33161
33538
  case "FunctionExpression": {
33162
33539
  const shadowed = new Set(ctx.shadowed);
33163
33540
  expr.params.forEach((p) => shadowed.add(p.name));
33164
- walkBlocks(expr.body, add, { inFunctionBody: true, shadowed });
33541
+ walkBlocks2(expr.body, add, { inFunctionBody: true, shadowed });
33165
33542
  return;
33166
33543
  }
33167
33544
  case "AssignmentExpression":
33168
- walkExpression(expr.left, add, ctx);
33169
- walkExpression(expr.right, add, ctx);
33545
+ walkExpression2(expr.left, add, ctx);
33546
+ walkExpression2(expr.right, add, ctx);
33170
33547
  return;
33171
33548
  case "UpdateExpression":
33172
- walkExpression(expr.argument, add, ctx);
33549
+ walkExpression2(expr.argument, add, ctx);
33173
33550
  return;
33174
33551
  case "JSXElement":
33175
33552
  if (typeof expr.tagName !== "string") {
33176
- walkExpression(expr.tagName, add, ctx);
33553
+ walkExpression2(expr.tagName, add, ctx);
33177
33554
  }
33178
33555
  expr.attributes.forEach((attr) => {
33179
33556
  if (attr.isSpread && attr.spreadExpr) {
33180
- walkExpression(attr.spreadExpr, add, ctx);
33557
+ walkExpression2(attr.spreadExpr, add, ctx);
33181
33558
  } else if (attr.value) {
33182
- walkExpression(attr.value, add, ctx);
33559
+ walkExpression2(attr.value, add, ctx);
33183
33560
  }
33184
33561
  });
33185
33562
  expr.children.forEach((child) => {
33186
33563
  if (child.kind === "expression") {
33187
- walkExpression(child.value, add, ctx);
33564
+ walkExpression2(child.value, add, ctx);
33188
33565
  } else if (child.kind === "element") {
33189
- walkExpression(child.value, add, ctx);
33566
+ walkExpression2(child.value, add, ctx);
33190
33567
  }
33191
33568
  });
33192
33569
  return;
@@ -33194,16 +33571,16 @@ function walkExpression(expr, add, ctx) {
33194
33571
  return;
33195
33572
  }
33196
33573
  }
33197
- function walkBlocks(blocks, add, ctx) {
33574
+ function walkBlocks2(blocks, add, ctx) {
33198
33575
  for (const block of blocks) {
33199
33576
  for (const instr of block.instructions) {
33200
33577
  if (instr.kind === "Assign") {
33201
- walkExpression(instr.value, add, ctx);
33578
+ walkExpression2(instr.value, add, ctx);
33202
33579
  if (instr.declarationKind) {
33203
33580
  ctx.shadowed.add(instr.target.name);
33204
33581
  }
33205
33582
  } else if (instr.kind === "Expression") {
33206
- walkExpression(instr.value, add, ctx);
33583
+ walkExpression2(instr.value, add, ctx);
33207
33584
  } else if (instr.kind === "Phi") {
33208
33585
  instr.sources.forEach((src) => {
33209
33586
  if (!ctx.shadowed.has(src.id.name)) add(src.id.name, ctx.inFunctionBody);
@@ -33222,7 +33599,7 @@ function walkBlocks(blocks, add, ctx) {
33222
33599
  function collectExpressionIdentifiers2(expr, deep = false) {
33223
33600
  const deps = /* @__PURE__ */ new Set();
33224
33601
  const collect = (name) => deps.add(name);
33225
- walkExpression(expr, (name, _inFunctionBody) => collect(name), {
33602
+ walkExpression2(expr, (name, _inFunctionBody) => collect(name), {
33226
33603
  inFunctionBody: deep,
33227
33604
  shadowed: /* @__PURE__ */ new Set()
33228
33605
  });
@@ -33803,6 +34180,506 @@ function getRootIdentifier(expr, t4) {
33803
34180
  return null;
33804
34181
  }
33805
34182
 
34183
+ // src/tooling/analyze.ts
34184
+ import { parseSync, transformSync } from "@babel/core";
34185
+
34186
+ // src/tooling/trace-infer.ts
34187
+ var TRACE_REGEX_ESCAPES = /[.*+?^${}()|[\]\\]/g;
34188
+ var IDENTIFIER_NAME = /^[A-Za-z_$][\w$]*$/;
34189
+ function isIdentifierName(name) {
34190
+ return IDENTIFIER_NAME.test(name);
34191
+ }
34192
+ function lineContainsIdentifier(lineText, identifier2) {
34193
+ const pattern = new RegExp(`\\b${identifier2.replace(TRACE_REGEX_ESCAPES, "\\$&")}\\b`);
34194
+ return pattern.test(lineText);
34195
+ }
34196
+ function lineContainsAnyIdentifier(lineText, identifiers) {
34197
+ for (const id of identifiers) {
34198
+ if (lineContainsIdentifier(lineText, id)) return true;
34199
+ }
34200
+ return false;
34201
+ }
34202
+ function pushTraceMarker(markersByLine, line, marker) {
34203
+ const markers = markersByLine.get(line);
34204
+ if (!markers) {
34205
+ markersByLine.set(line, [marker]);
34206
+ return;
34207
+ }
34208
+ const duplicate = markers.some(
34209
+ (existing) => existing.kind === marker.kind && existing.label === marker.label
34210
+ );
34211
+ if (!duplicate) markers.push(marker);
34212
+ }
34213
+ function inferReactiveLocalNames(startLine, endLine, sourceLines, baseReactiveNames) {
34214
+ const reactiveNames = new Set(baseReactiveNames);
34215
+ const declarationLines = [];
34216
+ const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?)(?:;)?$/;
34217
+ for (let line = startLine; line <= endLine; line++) {
34218
+ const text = (sourceLines[line - 1] ?? "").trim();
34219
+ if (!text) continue;
34220
+ const withoutComment = text.replace(/\/\/.*$/, "").trim();
34221
+ if (!withoutComment) continue;
34222
+ const match = withoutComment.match(declarationPattern);
34223
+ if (!match) continue;
34224
+ const name = match[1];
34225
+ const expression = match[2];
34226
+ if (!name || !expression) continue;
34227
+ declarationLines.push({ name, expression });
34228
+ }
34229
+ let changed = true;
34230
+ while (changed) {
34231
+ changed = false;
34232
+ for (const declaration of declarationLines) {
34233
+ if (reactiveNames.has(declaration.name)) continue;
34234
+ if (!lineContainsAnyIdentifier(declaration.expression, reactiveNames)) continue;
34235
+ reactiveNames.add(declaration.name);
34236
+ changed = true;
34237
+ }
34238
+ }
34239
+ return reactiveNames;
34240
+ }
34241
+ function isStateCallInstruction(instr) {
34242
+ if (instr.kind !== "Assign") return false;
34243
+ const value = instr.value;
34244
+ return value.kind === "CallExpression" && value.callee.kind === "Identifier" && value.callee.name === "$state";
34245
+ }
34246
+ function collectStateDeclNames(fn) {
34247
+ const result = [];
34248
+ for (const block of fn.blocks) {
34249
+ for (const instr of block.instructions) {
34250
+ if (!isStateCallInstruction(instr) || !instr.loc) continue;
34251
+ const name = deSSAVarName(instr.target.name);
34252
+ if (!isIdentifierName(name)) continue;
34253
+ result.push({ name, line: instr.loc.start.line });
34254
+ }
34255
+ }
34256
+ return result;
34257
+ }
34258
+ function expressionContainsEffectCall(instr) {
34259
+ const value = instr.kind === "Assign" || instr.kind === "Expression" ? instr.value : null;
34260
+ if (!value || !instr.loc) return null;
34261
+ let found = false;
34262
+ walkExpression(value, (expr) => {
34263
+ if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier" && deSSAVarName(expr.callee.name) === "$effect") {
34264
+ found = true;
34265
+ }
34266
+ });
34267
+ return found ? instr.loc.start.line : null;
34268
+ }
34269
+ function flattenRegions2(regions) {
34270
+ if (!regions || regions.length === 0) return [];
34271
+ const result = [];
34272
+ const visit = (region) => {
34273
+ result.push(region);
34274
+ region.children?.forEach((child) => visit(child));
34275
+ };
34276
+ regions.forEach((region) => visit(region));
34277
+ return result;
34278
+ }
34279
+ function findContainingRegion2(line, flatRegions) {
34280
+ let best;
34281
+ for (const region of flatRegions) {
34282
+ if (region.startLine === void 0 || region.endLine === void 0 || line < region.startLine || line > region.endLine) {
34283
+ continue;
34284
+ }
34285
+ const bestSpan = best && best.startLine !== void 0 && best.endLine !== void 0 ? best.endLine - best.startLine : Number.POSITIVE_INFINITY;
34286
+ const span = region.endLine - region.startLine;
34287
+ if (span <= bestSpan) best = region;
34288
+ }
34289
+ return best;
34290
+ }
34291
+ function inferTraceMarkersForComponent(input) {
34292
+ const { fn, sourceLines, startLine, endLine, verbosity, regions } = input;
34293
+ const markersByLine = /* @__PURE__ */ new Map();
34294
+ pushTraceMarker(markersByLine, startLine, {
34295
+ kind: "once",
34296
+ label: "Component setup runs on mount"
34297
+ });
34298
+ const stateDecls = collectStateDeclNames(fn);
34299
+ const reactiveNames = inferReactiveLocalNames(
34300
+ startLine,
34301
+ endLine,
34302
+ sourceLines,
34303
+ stateDecls.map((item) => item.name)
34304
+ );
34305
+ for (const stateDecl of stateDecls) {
34306
+ pushTraceMarker(markersByLine, stateDecl.line, {
34307
+ kind: "once",
34308
+ label: "Signal initialization runs once"
34309
+ });
34310
+ }
34311
+ for (const block of fn.blocks) {
34312
+ for (const instr of block.instructions) {
34313
+ const line = expressionContainsEffectCall(instr);
34314
+ if (!line || line < startLine || line > endLine) continue;
34315
+ pushTraceMarker(markersByLine, line, {
34316
+ kind: "effect",
34317
+ label: "Effect reruns when dependencies change"
34318
+ });
34319
+ }
34320
+ }
34321
+ const flatRegions = flattenRegions2(regions);
34322
+ for (let line = startLine; line <= endLine; line++) {
34323
+ const lineText = sourceLines[line - 1] ?? "";
34324
+ if (!lineText) continue;
34325
+ const hasReactiveName = lineContainsAnyIdentifier(lineText, reactiveNames);
34326
+ if (hasReactiveName && /\{[^}]*\b[A-Za-z_$][\w$]*\b[^}]*\}/.test(lineText)) {
34327
+ pushTraceMarker(markersByLine, line, {
34328
+ kind: "reactive",
34329
+ label: "JSX expression updates with reactive values"
34330
+ });
34331
+ }
34332
+ if (hasReactiveName && /\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText)) {
34333
+ pushTraceMarker(markersByLine, line, {
34334
+ kind: "reactive",
34335
+ label: "Statement reruns when reactive values change"
34336
+ });
34337
+ }
34338
+ if (/\b(?:\$effect|effect)\s*\(/.test(lineText)) {
34339
+ pushTraceMarker(markersByLine, line, {
34340
+ kind: "effect",
34341
+ label: "Effect callback executes reactively"
34342
+ });
34343
+ }
34344
+ if (verbosity === "verbose" && !hasReactiveName && /\{[^}]*\}/.test(lineText)) {
34345
+ pushTraceMarker(markersByLine, line, {
34346
+ kind: "once",
34347
+ label: "JSX expression runs during setup only"
34348
+ });
34349
+ }
34350
+ if (/\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText) && !hasReactiveName && line !== startLine) {
34351
+ pushTraceMarker(markersByLine, line, {
34352
+ kind: "once",
34353
+ label: "Statement runs only during setup"
34354
+ });
34355
+ }
34356
+ const containingRegion = findContainingRegion2(line, flatRegions);
34357
+ if (!containingRegion) continue;
34358
+ const markers = markersByLine.get(line);
34359
+ if (!markers) continue;
34360
+ for (const marker of markers) {
34361
+ marker.regionId = containingRegion.id;
34362
+ marker.deps = containingRegion.dependencies;
34363
+ }
34364
+ }
34365
+ return [...markersByLine.entries()].sort((a, b) => a[0] - b[0]).map(([line, markers]) => ({ line, markers }));
34366
+ }
34367
+
34368
+ // src/tooling/analyze.ts
34369
+ function mergeLoc(a, b) {
34370
+ if (!a) return b ?? null;
34371
+ if (!b) return a;
34372
+ const start = b.start.line < a.start.line || b.start.line === a.start.line && b.start.column < a.start.column ? b.start : a.start;
34373
+ const end = b.end.line > a.end.line || b.end.line === a.end.line && b.end.column > a.end.column ? b.end : a.end;
34374
+ return {
34375
+ start,
34376
+ end,
34377
+ filename: a.filename ?? b.filename,
34378
+ identifierName: a.identifierName ?? b.identifierName
34379
+ };
34380
+ }
34381
+ function expressionContainsMacroCall(expr, macroName) {
34382
+ let found = false;
34383
+ const visit = (value) => {
34384
+ if (found) return;
34385
+ if (value.kind === "CallExpression" && value.callee.kind === "Identifier" && deSSAVarName(value.callee.name) === macroName) {
34386
+ found = true;
34387
+ return;
34388
+ }
34389
+ switch (value.kind) {
34390
+ case "CallExpression":
34391
+ case "OptionalCallExpression":
34392
+ visit(value.callee);
34393
+ value.arguments.forEach((arg) => visit(arg));
34394
+ return;
34395
+ case "MemberExpression":
34396
+ case "OptionalMemberExpression":
34397
+ visit(value.object);
34398
+ if (value.computed) visit(value.property);
34399
+ return;
34400
+ case "BinaryExpression":
34401
+ case "LogicalExpression":
34402
+ visit(value.left);
34403
+ visit(value.right);
34404
+ return;
34405
+ case "UnaryExpression":
34406
+ case "AwaitExpression":
34407
+ case "UpdateExpression":
34408
+ case "SpreadElement":
34409
+ visit(value.argument);
34410
+ return;
34411
+ case "ConditionalExpression":
34412
+ visit(value.test);
34413
+ visit(value.consequent);
34414
+ visit(value.alternate);
34415
+ return;
34416
+ case "ArrayExpression":
34417
+ value.elements.forEach((el) => visit(el));
34418
+ return;
34419
+ case "ObjectExpression":
34420
+ value.properties.forEach((prop) => {
34421
+ if (prop.kind === "SpreadElement") {
34422
+ visit(prop.argument);
34423
+ return;
34424
+ }
34425
+ if (prop.computed) visit(prop.key);
34426
+ visit(prop.value);
34427
+ });
34428
+ return;
34429
+ case "TemplateLiteral":
34430
+ value.expressions.forEach((item) => visit(item));
34431
+ return;
34432
+ case "AssignmentExpression":
34433
+ visit(value.left);
34434
+ visit(value.right);
34435
+ return;
34436
+ case "SequenceExpression":
34437
+ value.expressions.forEach((item) => visit(item));
34438
+ return;
34439
+ case "YieldExpression":
34440
+ if (value.argument) visit(value.argument);
34441
+ return;
34442
+ case "TaggedTemplateExpression":
34443
+ visit(value.tag);
34444
+ value.quasi.expressions.forEach((item) => visit(item));
34445
+ return;
34446
+ case "ImportExpression":
34447
+ visit(value.source);
34448
+ return;
34449
+ case "ArrowFunction":
34450
+ if (Array.isArray(value.body)) {
34451
+ value.body.forEach((block) => {
34452
+ block.instructions.forEach((instr) => {
34453
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34454
+ visit(instr.value);
34455
+ }
34456
+ });
34457
+ });
34458
+ } else {
34459
+ visit(value.body);
34460
+ }
34461
+ return;
34462
+ case "FunctionExpression":
34463
+ value.body.forEach((block) => {
34464
+ block.instructions.forEach((instr) => {
34465
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34466
+ visit(instr.value);
34467
+ }
34468
+ });
34469
+ });
34470
+ return;
34471
+ case "JSXElement":
34472
+ if (typeof value.tagName !== "string") visit(value.tagName);
34473
+ value.attributes.forEach((attr) => {
34474
+ if (attr.isSpread && attr.spreadExpr) {
34475
+ visit(attr.spreadExpr);
34476
+ } else if (attr.value) {
34477
+ visit(attr.value);
34478
+ }
34479
+ });
34480
+ value.children.forEach((child) => {
34481
+ if (child.kind === "expression") visit(child.value);
34482
+ if (child.kind === "element") visit(child.value);
34483
+ });
34484
+ return;
34485
+ case "Identifier":
34486
+ case "Literal":
34487
+ case "MetaProperty":
34488
+ case "NewExpression":
34489
+ case "ClassExpression":
34490
+ case "ThisExpression":
34491
+ case "SuperExpression":
34492
+ if (value.kind === "NewExpression") {
34493
+ visit(value.callee);
34494
+ value.arguments.forEach((arg) => visit(arg));
34495
+ }
34496
+ if (value.kind === "ClassExpression" && value.superClass) visit(value.superClass);
34497
+ return;
34498
+ default:
34499
+ return;
34500
+ }
34501
+ };
34502
+ visit(expr);
34503
+ return found;
34504
+ }
34505
+ function instructionContainsMacroCall(instruction, macroName) {
34506
+ if (instruction.kind !== "Assign" && instruction.kind !== "Expression") return false;
34507
+ return expressionContainsMacroCall(instruction.value, macroName);
34508
+ }
34509
+ function functionUsesMacro(fn, macroName) {
34510
+ for (const block of fn.blocks) {
34511
+ for (const instruction of block.instructions) {
34512
+ if (instructionContainsMacroCall(instruction, macroName)) return true;
34513
+ }
34514
+ const term = block.terminator;
34515
+ if ("argument" in term && term.argument && expressionContainsMacroCall(term.argument, macroName)) {
34516
+ return true;
34517
+ }
34518
+ if (term.kind === "Branch" && expressionContainsMacroCall(term.test, macroName)) {
34519
+ return true;
34520
+ }
34521
+ if (term.kind === "Switch") {
34522
+ if (expressionContainsMacroCall(term.discriminant, macroName)) return true;
34523
+ if (term.cases.some((entry) => entry.test && expressionContainsMacroCall(entry.test, macroName))) {
34524
+ return true;
34525
+ }
34526
+ }
34527
+ }
34528
+ return false;
34529
+ }
34530
+ function computeRegionLoc(region, fn) {
34531
+ let loc = null;
34532
+ for (const instruction of region.instructions) {
34533
+ loc = mergeLoc(loc, instruction.loc);
34534
+ }
34535
+ for (const blockId of region.blocks) {
34536
+ const block = fn.blocks.find((entry) => entry.id === blockId);
34537
+ if (!block) continue;
34538
+ loc = mergeLoc(loc, block.terminator.loc);
34539
+ }
34540
+ return loc;
34541
+ }
34542
+ function regionToSerializable(region, fn) {
34543
+ const loc = computeRegionLoc(region, fn);
34544
+ return {
34545
+ id: region.id,
34546
+ startLine: loc?.start.line,
34547
+ startColumn: loc?.start.column,
34548
+ endLine: loc?.end.line,
34549
+ endColumn: loc?.end.column,
34550
+ dependencies: [...region.dependencies].map(deSSAVarName),
34551
+ declarations: [...region.declarations].map(deSSAVarName),
34552
+ hasControlFlow: region.hasControlFlow,
34553
+ hasReactiveWrites: region.declarations.size > 0,
34554
+ children: region.children.map((child) => regionToSerializable(child, fn))
34555
+ };
34556
+ }
34557
+ function warningSeverity(code) {
34558
+ const diagnosticCodes = new Set(getAllDiagnosticCodes());
34559
+ if (!diagnosticCodes.has(code)) return "warning" /* Warning */;
34560
+ return getDiagnosticInfo(code).severity;
34561
+ }
34562
+ function normalizeWarningToDiagnostic(warning) {
34563
+ return {
34564
+ code: warning.code,
34565
+ message: warning.message,
34566
+ severity: warningSeverity(warning.code),
34567
+ line: warning.line,
34568
+ column: warning.column
34569
+ };
34570
+ }
34571
+ function normalizeThrownError(error) {
34572
+ const message = error instanceof Error ? error.message : String(error);
34573
+ return {
34574
+ code: "FICT-COMPILE",
34575
+ message,
34576
+ severity: "error" /* Error */,
34577
+ line: 0,
34578
+ column: 0
34579
+ };
34580
+ }
34581
+ function analyzeDiagnostics(code, fileName, options) {
34582
+ const warnings = [];
34583
+ const pluginOptions = {
34584
+ dev: true,
34585
+ filename: fileName,
34586
+ emitModuleMetadata: false,
34587
+ strictGuarantee: false,
34588
+ warningLevels: {
34589
+ ...options.compilerOptions?.warningLevels ?? {},
34590
+ "FICT-R004": "warn"
34591
+ },
34592
+ ...options.compilerOptions,
34593
+ onWarn: (warning) => warnings.push(warning)
34594
+ };
34595
+ try {
34596
+ transformSync(code, {
34597
+ filename: fileName,
34598
+ configFile: false,
34599
+ babelrc: false,
34600
+ sourceType: "module",
34601
+ parserOpts: {
34602
+ sourceType: "module",
34603
+ plugins: ["typescript", "jsx"],
34604
+ allowReturnOutsideFunction: true
34605
+ },
34606
+ plugins: [[index_default, pluginOptions]],
34607
+ generatorOpts: {
34608
+ compact: false
34609
+ }
34610
+ });
34611
+ } catch (error) {
34612
+ return [...warnings.map(normalizeWarningToDiagnostic), normalizeThrownError(error)];
34613
+ }
34614
+ return warnings.map(normalizeWarningToDiagnostic);
34615
+ }
34616
+ function shouldIncludeFunction(fn) {
34617
+ return functionContainsJSX(fn) || functionUsesMacro(fn, "$state") || functionUsesMacro(fn, "$effect");
34618
+ }
34619
+ function parseFileAst(code, fileName) {
34620
+ const ast = parseSync(code, {
34621
+ filename: fileName,
34622
+ sourceType: "module",
34623
+ parserOpts: {
34624
+ sourceType: "module",
34625
+ plugins: ["typescript", "jsx"],
34626
+ allowReturnOutsideFunction: true
34627
+ }
34628
+ });
34629
+ if (!ast || ast.type !== "File") {
34630
+ throw new Error("Failed to parse source file for Fict analysis.");
34631
+ }
34632
+ return ast;
34633
+ }
34634
+ function analyzeFictFile(code, fileName, options = {}) {
34635
+ const includeRegions = options.includeRegions ?? true;
34636
+ const includeDiagnostics = options.includeDiagnostics ?? true;
34637
+ const verbosity = options.verbosity ?? "minimal";
34638
+ const ast = parseFileAst(code, fileName);
34639
+ const hir = buildHIR(
34640
+ ast,
34641
+ {
34642
+ state: /* @__PURE__ */ new Set(["$state"]),
34643
+ effect: /* @__PURE__ */ new Set(["$effect"])
34644
+ },
34645
+ {
34646
+ dev: true,
34647
+ fileName
34648
+ }
34649
+ );
34650
+ const sourceLines = code.split(/\r?\n/);
34651
+ const components = [];
34652
+ for (const fn of hir.functions) {
34653
+ if (!fn.loc || !shouldIncludeFunction(fn)) continue;
34654
+ const startLine = fn.loc.start.line;
34655
+ const endLine = fn.loc.end.line;
34656
+ const scopeResult = analyzeReactiveScopesWithSSA(fn);
34657
+ const regionResult = generateRegions(fn, scopeResult);
34658
+ const regions = includeRegions ? regionResult.topLevelRegions.map((region) => regionToSerializable(region, fn)) : void 0;
34659
+ const trace = inferTraceMarkersForComponent({
34660
+ fn,
34661
+ sourceLines,
34662
+ startLine,
34663
+ endLine,
34664
+ verbosity,
34665
+ regions
34666
+ });
34667
+ components.push({
34668
+ name: fn.name ?? "<anonymous>",
34669
+ startLine,
34670
+ endLine,
34671
+ trace,
34672
+ regions
34673
+ });
34674
+ }
34675
+ const diagnostics = includeDiagnostics ? analyzeDiagnostics(code, fileName, options) : [];
34676
+ return {
34677
+ fileName,
34678
+ components,
34679
+ diagnostics
34680
+ };
34681
+ }
34682
+
33806
34683
  // src/index.ts
33807
34684
  function stripMacroImports(path2, t4) {
33808
34685
  path2.traverse({
@@ -34696,46 +35573,77 @@ function createHIREntrypointVisitor(t4, options) {
34696
35573
  path2.traverse({
34697
35574
  JSXExpressionContainer(exprPath) {
34698
35575
  const expr = exprPath.node.expression;
34699
- if (!t4.isCallExpression(expr)) return;
34700
- if (!t4.isMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
35576
+ if (!t4.isCallExpression(expr) && !t4.isOptionalCallExpression(expr)) return;
35577
+ if (!t4.isMemberExpression(expr.callee) && !t4.isOptionalMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
34701
35578
  return;
34702
35579
  }
34703
- const cb = expr.arguments[0];
34704
- if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
34705
- const getReturnedJsx = (fn) => {
34706
- if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
34707
- if (t4.isBlockStatement(fn.body)) {
34708
- const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
34709
- if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
34710
- return ret.argument;
34711
- }
34712
- }
34713
- return null;
34714
- };
34715
- const jsx = getReturnedJsx(cb);
34716
- if (!jsx) return;
34717
- if (t4.isJSXFragment(jsx)) {
34718
- warn({
34719
- code: "FICT-J002",
34720
- message: "Missing key prop in list rendering.",
34721
- fileName,
34722
- line: expr.loc?.start.line ?? 0,
34723
- column: expr.loc ? expr.loc.start.column + 1 : 0
34724
- });
35580
+ const callExprPath = exprPath.get("expression");
35581
+ if (!callExprPath.isCallExpression() && !callExprPath.isOptionalCallExpression()) return;
35582
+ const argPaths = callExprPath.get("arguments");
35583
+ const cbPath = Array.isArray(argPaths) ? argPaths[0] : void 0;
35584
+ if (!cbPath || !cbPath.isArrowFunctionExpression() && !cbPath.isFunctionExpression()) {
34725
35585
  return;
34726
35586
  }
34727
- let hasKey = false;
34728
- let hasUnknownSpread = false;
34729
- for (const attr of jsx.openingElement.attributes) {
34730
- if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
34731
- hasKey = true;
34732
- break;
35587
+ const getReturnedJsx = (fnPath) => {
35588
+ const collectReturnedJsxFromExpression = (node, returned2) => {
35589
+ if (!node) return;
35590
+ if (t4.isJSXElement(node) || t4.isJSXFragment(node)) {
35591
+ returned2.push(node);
35592
+ return;
35593
+ }
35594
+ if (t4.isConditionalExpression(node)) {
35595
+ collectReturnedJsxFromExpression(node.consequent, returned2);
35596
+ collectReturnedJsxFromExpression(node.alternate, returned2);
35597
+ return;
35598
+ }
35599
+ if (t4.isLogicalExpression(node)) {
35600
+ collectReturnedJsxFromExpression(node.left, returned2);
35601
+ collectReturnedJsxFromExpression(node.right, returned2);
35602
+ return;
35603
+ }
35604
+ if (t4.isSequenceExpression(node)) {
35605
+ const tail = node.expressions[node.expressions.length - 1];
35606
+ collectReturnedJsxFromExpression(tail, returned2);
35607
+ return;
35608
+ }
35609
+ if (t4.isParenthesizedExpression(node)) {
35610
+ collectReturnedJsxFromExpression(node.expression, returned2);
35611
+ return;
35612
+ }
35613
+ if (t4.isTSAsExpression(node) || t4.isTSTypeAssertion(node) || t4.isTSNonNullExpression(node) || t4.isTSSatisfiesExpression(node) || t4.isTypeCastExpression(node)) {
35614
+ collectReturnedJsxFromExpression(node.expression, returned2);
35615
+ }
35616
+ };
35617
+ const fn = fnPath.node;
35618
+ const returned = [];
35619
+ if (!t4.isBlockStatement(fn.body)) {
35620
+ collectReturnedJsxFromExpression(fn.body, returned);
35621
+ return returned;
34733
35622
  }
34734
- if (t4.isJSXSpreadAttribute(attr)) {
34735
- hasUnknownSpread = true;
35623
+ fnPath.get("body").traverse({
35624
+ Function(innerFnPath) {
35625
+ innerFnPath.skip();
35626
+ },
35627
+ ReturnStatement(retPath) {
35628
+ collectReturnedJsxFromExpression(retPath.node.argument, returned);
35629
+ }
35630
+ });
35631
+ return returned;
35632
+ };
35633
+ const returnedJsx = getReturnedJsx(cbPath);
35634
+ if (returnedJsx.length === 0) return;
35635
+ const hasMissingKeyBranch = returnedJsx.some((jsx) => {
35636
+ if (t4.isJSXFragment(jsx)) return true;
35637
+ let hasKey = false;
35638
+ for (const attr of jsx.openingElement.attributes) {
35639
+ if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
35640
+ hasKey = true;
35641
+ break;
35642
+ }
34736
35643
  }
34737
- }
34738
- if (hasKey || hasUnknownSpread) return;
35644
+ return !hasKey;
35645
+ });
35646
+ if (!hasMissingKeyBranch) return;
34739
35647
  warn({
34740
35648
  code: "FICT-J002",
34741
35649
  message: "Missing key prop in list rendering.",
@@ -35297,9 +36205,11 @@ var createFictPlugin = declare(
35297
36205
  );
35298
36206
  var index_default = createFictPlugin;
35299
36207
  export {
36208
+ analyzeFictFile,
35300
36209
  clearModuleMetadata,
35301
36210
  createFictPlugin,
35302
36211
  index_default as default,
36212
+ inferTraceMarkersForComponent,
35303
36213
  resolveModuleMetadata,
35304
36214
  setModuleMetadata
35305
36215
  };