@fictjs/compiler 0.15.0 → 0.17.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);
@@ -17073,6 +17073,37 @@ function clearModuleMetadata(options) {
17073
17073
  }
17074
17074
 
17075
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 || {});
17076
17107
  var DiagnosticMessages = {
17077
17108
  ["FICT-P001" /* FICT_P001 */]: "Props destructuring falls back to non-reactive binding.",
17078
17109
  ["FICT-P002" /* FICT_P002 */]: "Array rest in props destructuring falls back to non-reactive binding.",
@@ -17160,6 +17191,16 @@ function reportDiagnostic(ctx, code, node, context) {
17160
17191
  });
17161
17192
  }
17162
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
+ }
17163
17204
 
17164
17205
  // src/ir/ssa.ts
17165
17206
  function enterSSA(program) {
@@ -18880,6 +18921,22 @@ function structurizeBlockUntilJoin(ctx, blockId, joinBlock) {
18880
18921
  if (branchNode) nodes.push(branchNode);
18881
18922
  break;
18882
18923
  }
18924
+ case "Switch": {
18925
+ nodes.push(structurizeSwitch(ctx, block, term));
18926
+ break;
18927
+ }
18928
+ case "ForOf": {
18929
+ nodes.push(structurizeForOf(ctx, block, term));
18930
+ break;
18931
+ }
18932
+ case "ForIn": {
18933
+ nodes.push(structurizeForIn(ctx, block, term));
18934
+ break;
18935
+ }
18936
+ case "Try": {
18937
+ nodes.push(structurizeTry(ctx, block, term));
18938
+ break;
18939
+ }
18883
18940
  case "Break":
18884
18941
  nodes.push({ kind: "break", label: term.label });
18885
18942
  break;
@@ -19014,7 +19071,9 @@ function isSwitchCaseTerminated(node) {
19014
19071
  }
19015
19072
  }
19016
19073
  function structurizeForOf(ctx, block, term) {
19074
+ ctx.reservedBlocks.add(term.exit);
19017
19075
  const body = structurizeBlock(ctx, term.body);
19076
+ ctx.reservedBlocks.delete(term.exit);
19018
19077
  const exit = !ctx.emitted.has(term.exit) ? structurizeBlock(ctx, term.exit) : null;
19019
19078
  const forOfNode = {
19020
19079
  kind: "forOf",
@@ -19030,7 +19089,9 @@ function structurizeForOf(ctx, block, term) {
19030
19089
  return forOfNode;
19031
19090
  }
19032
19091
  function structurizeForIn(ctx, block, term) {
19092
+ ctx.reservedBlocks.add(term.exit);
19033
19093
  const body = structurizeBlock(ctx, term.body);
19094
+ ctx.reservedBlocks.delete(term.exit);
19034
19095
  const exit = !ctx.emitted.has(term.exit) ? structurizeBlock(ctx, term.exit) : null;
19035
19096
  const forInNode = {
19036
19097
  kind: "forIn",
@@ -24252,34 +24313,138 @@ function extractKeyFromAttributes(attributes) {
24252
24313
  }
24253
24314
  return void 0;
24254
24315
  }
24316
+ function collectReturnedJSXFromExpression(expression, returned) {
24317
+ if (expression.kind === "JSXElement") {
24318
+ returned.push(expression);
24319
+ return;
24320
+ }
24321
+ if (expression.kind === "ConditionalExpression") {
24322
+ collectReturnedJSXFromExpression(expression.consequent, returned);
24323
+ collectReturnedJSXFromExpression(expression.alternate, returned);
24324
+ return;
24325
+ }
24326
+ if (expression.kind === "LogicalExpression") {
24327
+ collectReturnedJSXFromExpression(expression.left, returned);
24328
+ collectReturnedJSXFromExpression(expression.right, returned);
24329
+ return;
24330
+ }
24331
+ if (expression.kind === "SequenceExpression") {
24332
+ const tail = expression.expressions[expression.expressions.length - 1];
24333
+ if (tail) collectReturnedJSXFromExpression(tail, returned);
24334
+ }
24335
+ }
24336
+ function extractKeyExpressionFromReturnedExpression(expression) {
24337
+ if (expression.kind === "JSXElement") {
24338
+ return extractKeyFromAttributes(expression.attributes);
24339
+ }
24340
+ if (expression.kind === "ConditionalExpression") {
24341
+ const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
24342
+ const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
24343
+ if (!consequentKey || !alternateKey) return void 0;
24344
+ return {
24345
+ kind: "ConditionalExpression",
24346
+ test: expression.test,
24347
+ consequent: consequentKey,
24348
+ alternate: alternateKey,
24349
+ loc: expression.loc
24350
+ };
24351
+ }
24352
+ if (expression.kind === "SequenceExpression") {
24353
+ const tail = expression.expressions[expression.expressions.length - 1];
24354
+ return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
24355
+ }
24356
+ return void 0;
24357
+ }
24358
+ function getReturnedKeyExpressionsFromCallback(callback) {
24359
+ const returned = [];
24360
+ if (callback.kind === "ArrowFunction") {
24361
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24362
+ const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
24363
+ if (keyExpr) returned.push(keyExpr);
24364
+ return returned;
24365
+ }
24366
+ if (Array.isArray(callback.body)) {
24367
+ for (const block of callback.body) {
24368
+ const term = block.terminator;
24369
+ if (term.kind !== "Return" || !term.argument) continue;
24370
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24371
+ if (keyExpr) returned.push(keyExpr);
24372
+ }
24373
+ }
24374
+ return returned;
24375
+ }
24376
+ if (callback.kind === "FunctionExpression") {
24377
+ for (const block of callback.body ?? []) {
24378
+ const term = block.terminator;
24379
+ if (term.kind !== "Return" || !term.argument) continue;
24380
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24381
+ if (keyExpr) returned.push(keyExpr);
24382
+ }
24383
+ }
24384
+ return returned;
24385
+ }
24255
24386
  function getReturnedJSXFromCallback(callback) {
24387
+ const returned = [];
24256
24388
  if (callback.kind === "ArrowFunction") {
24257
- if (callback.isExpression && !Array.isArray(callback.body) && callback.body.kind === "JSXElement") {
24258
- return callback.body;
24389
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24390
+ collectReturnedJSXFromExpression(callback.body, returned);
24391
+ return returned;
24259
24392
  }
24260
24393
  if (Array.isArray(callback.body)) {
24261
24394
  for (const block of callback.body) {
24262
24395
  const term = block.terminator;
24263
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24264
- return term.argument;
24396
+ if (term.kind === "Return" && term.argument) {
24397
+ collectReturnedJSXFromExpression(term.argument, returned);
24265
24398
  }
24266
24399
  }
24267
24400
  }
24401
+ return returned;
24268
24402
  }
24269
24403
  if (callback.kind === "FunctionExpression") {
24270
24404
  for (const block of callback.body ?? []) {
24271
24405
  const term = block.terminator;
24272
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24273
- return term.argument;
24406
+ if (term.kind === "Return" && term.argument) {
24407
+ collectReturnedJSXFromExpression(term.argument, returned);
24274
24408
  }
24275
24409
  }
24276
24410
  }
24277
- return null;
24411
+ return returned;
24412
+ }
24413
+ function keyExpressionSignature(expression) {
24414
+ try {
24415
+ return JSON.stringify(expression, (key, value) => {
24416
+ if (key === "loc") return void 0;
24417
+ if (typeof value === "bigint") return `__bigint:${value.toString()}`;
24418
+ return value;
24419
+ }) ?? "";
24420
+ } catch {
24421
+ return "";
24422
+ }
24278
24423
  }
24279
24424
  function extractKeyFromMapCallback(callback) {
24280
- const jsx = getReturnedJSXFromCallback(callback);
24281
- if (!jsx) return void 0;
24282
- return extractKeyFromAttributes(jsx.attributes);
24425
+ const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
24426
+ if (returnedKeyExpressions.length === 1) {
24427
+ return returnedKeyExpressions[0];
24428
+ }
24429
+ if (returnedKeyExpressions.length > 1) {
24430
+ const [firstKey2, ...restKeys2] = returnedKeyExpressions;
24431
+ const firstSignature2 = keyExpressionSignature(firstKey2);
24432
+ if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
24433
+ return firstKey2;
24434
+ }
24435
+ }
24436
+ const returned = getReturnedJSXFromCallback(callback);
24437
+ if (returned.length === 0) return void 0;
24438
+ const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
24439
+ if (keyExpressions.some((expr) => !expr)) return void 0;
24440
+ const [firstKey, ...restKeys] = keyExpressions;
24441
+ const firstSignature = keyExpressionSignature(firstKey);
24442
+ if (!firstSignature) return void 0;
24443
+ const allBranchesSameKey = restKeys.every(
24444
+ (keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
24445
+ );
24446
+ if (!allBranchesSameKey) return void 0;
24447
+ return firstKey;
24283
24448
  }
24284
24449
 
24285
24450
  // src/ir/codegen-overrides.ts
@@ -24884,6 +25049,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24884
25049
  ctx.inListRender = true;
24885
25050
  let callbackExpr = ops.lowerExpression(mapCallback, ctx);
24886
25051
  ctx.inListRender = prevInListRender;
25052
+ const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
25053
+ let deferredCallbackId = null;
25054
+ let deferredCallbackInitId = null;
25055
+ let deferredItemsId = null;
25056
+ if (shouldDeferOptionalCallbackEvaluation) {
25057
+ deferredCallbackId = ops.genTemp(ctx, "mapCb");
25058
+ deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
25059
+ deferredItemsId = ops.genTemp(ctx, "mapItems");
25060
+ }
24887
25061
  const capturedKeyParamName = ctx.listKeyParamName;
24888
25062
  ctx.listKeyExpr = prevListKeyExpr;
24889
25063
  ctx.listItemParamName = prevListItemParamName;
@@ -24983,7 +25157,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24983
25157
  ],
24984
25158
  keyExprAst
24985
25159
  );
24986
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25160
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
24987
25161
  if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
24988
25162
  const newParams = [...callbackExpr.params];
24989
25163
  while (newParams.length < 2) {
@@ -25003,25 +25177,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
25003
25177
  }
25004
25178
  }
25005
25179
  statements.push(...hoistedStatements);
25180
+ if (shouldDeferOptionalCallbackEvaluation) {
25181
+ statements.push(
25182
+ t4.variableDeclaration("let", [
25183
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25184
+ ]),
25185
+ t4.variableDeclaration("let", [
25186
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25187
+ ])
25188
+ );
25189
+ }
25190
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25191
+ [],
25192
+ t4.blockStatement([
25193
+ t4.variableDeclaration("const", [
25194
+ t4.variableDeclarator(
25195
+ t4.cloneNode(deferredItemsId, true),
25196
+ t4.cloneNode(arrayExprBase, true)
25197
+ )
25198
+ ]),
25199
+ t4.ifStatement(
25200
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25201
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25202
+ ),
25203
+ t4.ifStatement(
25204
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25205
+ t4.blockStatement([
25206
+ t4.expressionStatement(
25207
+ t4.assignmentExpression(
25208
+ "=",
25209
+ t4.cloneNode(deferredCallbackId, true),
25210
+ t4.cloneNode(callbackExpr, true)
25211
+ )
25212
+ ),
25213
+ t4.expressionStatement(
25214
+ t4.assignmentExpression(
25215
+ "=",
25216
+ t4.cloneNode(deferredCallbackInitId, true),
25217
+ t4.booleanLiteral(true)
25218
+ )
25219
+ )
25220
+ ])
25221
+ ),
25222
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25223
+ ])
25224
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25225
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25226
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25227
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25228
+ t4.identifier("__item"),
25229
+ t4.identifier("__index"),
25230
+ t4.identifier("__key")
25231
+ ])
25232
+ ) : callbackExpr;
25006
25233
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25007
- t4.arrowFunctionExpression([], arrayExpr),
25234
+ getItemsExpr,
25008
25235
  keyFn,
25009
- callbackExpr,
25236
+ renderExpr,
25010
25237
  t4.booleanLiteral(hasIndexParam)
25011
25238
  ]);
25012
25239
  } else {
25013
25240
  statements.push(...hoistedStatements);
25241
+ if (shouldDeferOptionalCallbackEvaluation) {
25242
+ statements.push(
25243
+ t4.variableDeclaration("let", [
25244
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25245
+ ]),
25246
+ t4.variableDeclaration("let", [
25247
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25248
+ ])
25249
+ );
25250
+ }
25014
25251
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
25015
25252
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[1]) ? callbackExpr.params[1].name : "__index" : "__index";
25016
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25253
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25254
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25255
+ [],
25256
+ t4.blockStatement([
25257
+ t4.variableDeclaration("const", [
25258
+ t4.variableDeclarator(
25259
+ t4.cloneNode(deferredItemsId, true),
25260
+ t4.cloneNode(arrayExprBase, true)
25261
+ )
25262
+ ]),
25263
+ t4.ifStatement(
25264
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25265
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25266
+ ),
25267
+ t4.ifStatement(
25268
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25269
+ t4.blockStatement([
25270
+ t4.expressionStatement(
25271
+ t4.assignmentExpression(
25272
+ "=",
25273
+ t4.cloneNode(deferredCallbackId, true),
25274
+ t4.cloneNode(callbackExpr, true)
25275
+ )
25276
+ ),
25277
+ t4.expressionStatement(
25278
+ t4.assignmentExpression(
25279
+ "=",
25280
+ t4.cloneNode(deferredCallbackInitId, true),
25281
+ t4.booleanLiteral(true)
25282
+ )
25283
+ )
25284
+ ])
25285
+ ),
25286
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25287
+ ])
25288
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25289
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25290
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25291
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25292
+ t4.identifier("__item"),
25293
+ t4.identifier("__index"),
25294
+ t4.identifier("__key")
25295
+ ])
25296
+ ) : callbackExpr;
25017
25297
  const keyFn = t4.arrowFunctionExpression(
25018
25298
  [t4.identifier(itemParamName), t4.identifier(indexParamName)],
25019
25299
  t4.identifier(indexParamName)
25020
25300
  );
25021
25301
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25022
- t4.arrowFunctionExpression([], arrayExpr),
25302
+ getItemsExpr,
25023
25303
  keyFn,
25024
- callbackExpr,
25304
+ renderExpr,
25025
25305
  t4.booleanLiteral(hasIndexParam)
25026
25306
  ]);
25027
25307
  }
@@ -25484,6 +25764,10 @@ function registerResumableComponent(componentName, ctx) {
25484
25764
  const hostParam = t4.identifier("host");
25485
25765
  const snapshotId = t4.identifier("snapshot");
25486
25766
  const ctxId = t4.identifier("ctx");
25767
+ const runtimeModuleUrlExpr = t4.memberExpression(
25768
+ t4.metaProperty(t4.identifier("import"), t4.identifier("meta")),
25769
+ t4.identifier("url")
25770
+ );
25487
25771
  ctx.helpersUsed.add("getSSRScope");
25488
25772
  ctx.helpersUsed.add("ensureScope");
25489
25773
  ctx.helpersUsed.add("prepareContext");
@@ -25557,7 +25841,10 @@ function registerResumableComponent(componentName, ctx) {
25557
25841
  ctx.helpersUsed.add("registerResume");
25558
25842
  const registerCall = t4.expressionStatement(
25559
25843
  t4.callExpression(t4.identifier(RUNTIME_ALIASES.registerResume), [
25560
- t4.stringLiteral(resumeExport),
25844
+ t4.callExpression(t4.identifier(RUNTIME_ALIASES.qrl), [
25845
+ runtimeModuleUrlExpr,
25846
+ t4.stringLiteral(resumeExport)
25847
+ ]),
25561
25848
  resumeFnId
25562
25849
  ])
25563
25850
  );
@@ -27347,18 +27634,8 @@ function lowerExpressionImpl(expr, ctx, valueUsed = true) {
27347
27634
  blocks,
27348
27635
  meta: { fromExpression: true }
27349
27636
  };
27350
- const cfg = analyzeCFG(fn.blocks);
27351
- const hasLoop = cfg.loopHeaders.size > 0 || cfg.backEdges.size > 0;
27352
27637
  const { node, diagnostics } = structurizeCFGWithDiagnostics(fn);
27353
- const structured = node.kind === "stateMachine" || hasLoop ? node.kind === "stateMachine" ? node : {
27354
- kind: "stateMachine",
27355
- blocks: fn.blocks.map((block) => ({
27356
- blockId: block.id,
27357
- instructions: block.instructions,
27358
- terminator: block.terminator
27359
- })),
27360
- entryBlock: fn.blocks[0]?.id ?? 0
27361
- } : diagnostics.isComplete ? node : {
27638
+ const structured = node.kind === "stateMachine" ? node : diagnostics.isComplete ? node : {
27362
27639
  kind: "stateMachine",
27363
27640
  blocks: fn.blocks.map((block) => ({
27364
27641
  blockId: block.id,
@@ -33920,6 +34197,506 @@ function getRootIdentifier(expr, t4) {
33920
34197
  return null;
33921
34198
  }
33922
34199
 
34200
+ // src/tooling/analyze.ts
34201
+ import { parseSync, transformSync } from "@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
+ 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 = 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
+
33923
34700
  // src/index.ts
33924
34701
  function stripMacroImports(path2, t4) {
33925
34702
  path2.traverse({
@@ -34813,46 +35590,77 @@ function createHIREntrypointVisitor(t4, options) {
34813
35590
  path2.traverse({
34814
35591
  JSXExpressionContainer(exprPath) {
34815
35592
  const expr = exprPath.node.expression;
34816
- if (!t4.isCallExpression(expr)) return;
34817
- 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" })) {
34818
35595
  return;
34819
35596
  }
34820
- const cb = expr.arguments[0];
34821
- if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
34822
- const getReturnedJsx = (fn) => {
34823
- if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
34824
- if (t4.isBlockStatement(fn.body)) {
34825
- const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
34826
- if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
34827
- return ret.argument;
34828
- }
34829
- }
34830
- return null;
34831
- };
34832
- const jsx = getReturnedJsx(cb);
34833
- if (!jsx) return;
34834
- if (t4.isJSXFragment(jsx)) {
34835
- warn({
34836
- code: "FICT-J002",
34837
- message: "Missing key prop in list rendering.",
34838
- fileName,
34839
- line: expr.loc?.start.line ?? 0,
34840
- column: expr.loc ? expr.loc.start.column + 1 : 0
34841
- });
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()) {
34842
35602
  return;
34843
35603
  }
34844
- let hasKey = false;
34845
- let hasUnknownSpread = false;
34846
- for (const attr of jsx.openingElement.attributes) {
34847
- if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
34848
- hasKey = true;
34849
- 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;
34850
35639
  }
34851
- if (t4.isJSXSpreadAttribute(attr)) {
34852
- 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
+ }
34853
35660
  }
34854
- }
34855
- if (hasKey || hasUnknownSpread) return;
35661
+ return !hasKey;
35662
+ });
35663
+ if (!hasMissingKeyBranch) return;
34856
35664
  warn({
34857
35665
  code: "FICT-J002",
34858
35666
  message: "Missing key prop in list rendering.",
@@ -35414,9 +36222,11 @@ var createFictPlugin = declare(
35414
36222
  );
35415
36223
  var index_default = createFictPlugin;
35416
36224
  export {
36225
+ analyzeFictFile,
35417
36226
  clearModuleMetadata,
35418
36227
  createFictPlugin,
35419
36228
  index_default as default,
36229
+ inferTraceMarkersForComponent,
35420
36230
  resolveModuleMetadata,
35421
36231
  setModuleMetadata
35422
36232
  };