@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.cjs CHANGED
@@ -3065,7 +3065,7 @@ var require_identifier = __commonJS({
3065
3065
  value: true
3066
3066
  });
3067
3067
  exports2.isIdentifierChar = isIdentifierChar;
3068
- exports2.isIdentifierName = isIdentifierName;
3068
+ exports2.isIdentifierName = isIdentifierName2;
3069
3069
  exports2.isIdentifierStart = isIdentifierStart;
3070
3070
  var nonASCIIidentifierStartChars = "\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
3071
3071
  var nonASCIIidentifierChars = "\xB7\u0300-\u036F\u0387\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u0897-\u089F\u08CA-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B55-\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C04\u0C3C\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0CF3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D81-\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EBC\u0EC8-\u0ECE\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u180F-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19D0-\u19DA\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1ABF-\u1ADD\u1AE0-\u1AEB\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\u30FB\uA620-\uA629\uA66F\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA82C\uA880\uA881\uA8B4-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F1\uA8FF-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F\uFF65";
@@ -3106,7 +3106,7 @@ var require_identifier = __commonJS({
3106
3106
  }
3107
3107
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
3108
3108
  }
3109
- function isIdentifierName(name) {
3109
+ function isIdentifierName2(name) {
3110
3110
  let isFirst = true;
3111
3111
  for (let i = 0; i < name.length; i++) {
3112
3112
  let cp = name.charCodeAt(i);
@@ -14098,9 +14098,11 @@ var require_lib3 = __commonJS({
14098
14098
  // src/index.ts
14099
14099
  var index_exports = {};
14100
14100
  __export(index_exports, {
14101
+ analyzeFictFile: () => analyzeFictFile,
14101
14102
  clearModuleMetadata: () => clearModuleMetadata,
14102
14103
  createFictPlugin: () => createFictPlugin,
14103
14104
  default: () => index_default,
14105
+ inferTraceMarkersForComponent: () => inferTraceMarkersForComponent,
14104
14106
  resolveModuleMetadata: () => resolveModuleMetadata,
14105
14107
  setModuleMetadata: () => setModuleMetadata
14106
14108
  });
@@ -17088,6 +17090,37 @@ function clearModuleMetadata(options) {
17088
17090
  }
17089
17091
 
17090
17092
  // src/validation.ts
17093
+ var DiagnosticCode = /* @__PURE__ */ ((DiagnosticCode2) => {
17094
+ DiagnosticCode2["FICT_P001"] = "FICT-P001";
17095
+ DiagnosticCode2["FICT_P002"] = "FICT-P002";
17096
+ DiagnosticCode2["FICT_P003"] = "FICT-P003";
17097
+ DiagnosticCode2["FICT_P004"] = "FICT-P004";
17098
+ DiagnosticCode2["FICT_P005"] = "FICT-P005";
17099
+ DiagnosticCode2["FICT_S001"] = "FICT-S001";
17100
+ DiagnosticCode2["FICT_S002"] = "FICT-S002";
17101
+ DiagnosticCode2["FICT_E001"] = "FICT-E001";
17102
+ DiagnosticCode2["FICT_E002"] = "FICT-E002";
17103
+ DiagnosticCode2["FICT_E003"] = "FICT-E003";
17104
+ DiagnosticCode2["FICT_M001"] = "FICT-M001";
17105
+ DiagnosticCode2["FICT_M002"] = "FICT-M002";
17106
+ DiagnosticCode2["FICT_M003"] = "FICT-M003";
17107
+ DiagnosticCode2["FICT_C001"] = "FICT-C001";
17108
+ DiagnosticCode2["FICT_C002"] = "FICT-C002";
17109
+ DiagnosticCode2["FICT_C003"] = "FICT-C003";
17110
+ DiagnosticCode2["FICT_C004"] = "FICT-C004";
17111
+ DiagnosticCode2["FICT_J001"] = "FICT-J001";
17112
+ DiagnosticCode2["FICT_J002"] = "FICT-J002";
17113
+ DiagnosticCode2["FICT_J003"] = "FICT-J003";
17114
+ DiagnosticCode2["FICT_R001"] = "FICT-R001";
17115
+ DiagnosticCode2["FICT_R002"] = "FICT-R002";
17116
+ DiagnosticCode2["FICT_R003"] = "FICT-R003";
17117
+ DiagnosticCode2["FICT_R004"] = "FICT-R004";
17118
+ DiagnosticCode2["FICT_R005"] = "FICT-R005";
17119
+ DiagnosticCode2["FICT_X001"] = "FICT-X001";
17120
+ DiagnosticCode2["FICT_X002"] = "FICT-X002";
17121
+ DiagnosticCode2["FICT_X003"] = "FICT-X003";
17122
+ return DiagnosticCode2;
17123
+ })(DiagnosticCode || {});
17091
17124
  var DiagnosticMessages = {
17092
17125
  ["FICT-P001" /* FICT_P001 */]: "Props destructuring falls back to non-reactive binding.",
17093
17126
  ["FICT-P002" /* FICT_P002 */]: "Array rest in props destructuring falls back to non-reactive binding.",
@@ -17175,6 +17208,16 @@ function reportDiagnostic(ctx, code, node, context) {
17175
17208
  });
17176
17209
  }
17177
17210
  }
17211
+ function getAllDiagnosticCodes() {
17212
+ return Object.values(DiagnosticCode);
17213
+ }
17214
+ function getDiagnosticInfo(code) {
17215
+ return {
17216
+ code,
17217
+ severity: DiagnosticSeverities[code],
17218
+ message: DiagnosticMessages[code]
17219
+ };
17220
+ }
17178
17221
 
17179
17222
  // src/ir/ssa.ts
17180
17223
  function enterSSA(program) {
@@ -18895,6 +18938,22 @@ function structurizeBlockUntilJoin(ctx, blockId, joinBlock) {
18895
18938
  if (branchNode) nodes.push(branchNode);
18896
18939
  break;
18897
18940
  }
18941
+ case "Switch": {
18942
+ nodes.push(structurizeSwitch(ctx, block, term));
18943
+ break;
18944
+ }
18945
+ case "ForOf": {
18946
+ nodes.push(structurizeForOf(ctx, block, term));
18947
+ break;
18948
+ }
18949
+ case "ForIn": {
18950
+ nodes.push(structurizeForIn(ctx, block, term));
18951
+ break;
18952
+ }
18953
+ case "Try": {
18954
+ nodes.push(structurizeTry(ctx, block, term));
18955
+ break;
18956
+ }
18898
18957
  case "Break":
18899
18958
  nodes.push({ kind: "break", label: term.label });
18900
18959
  break;
@@ -19029,7 +19088,9 @@ function isSwitchCaseTerminated(node) {
19029
19088
  }
19030
19089
  }
19031
19090
  function structurizeForOf(ctx, block, term) {
19091
+ ctx.reservedBlocks.add(term.exit);
19032
19092
  const body = structurizeBlock(ctx, term.body);
19093
+ ctx.reservedBlocks.delete(term.exit);
19033
19094
  const exit = !ctx.emitted.has(term.exit) ? structurizeBlock(ctx, term.exit) : null;
19034
19095
  const forOfNode = {
19035
19096
  kind: "forOf",
@@ -19045,7 +19106,9 @@ function structurizeForOf(ctx, block, term) {
19045
19106
  return forOfNode;
19046
19107
  }
19047
19108
  function structurizeForIn(ctx, block, term) {
19109
+ ctx.reservedBlocks.add(term.exit);
19048
19110
  const body = structurizeBlock(ctx, term.body);
19111
+ ctx.reservedBlocks.delete(term.exit);
19049
19112
  const exit = !ctx.emitted.has(term.exit) ? structurizeBlock(ctx, term.exit) : null;
19050
19113
  const forInNode = {
19051
19114
  kind: "forIn",
@@ -24267,34 +24330,138 @@ function extractKeyFromAttributes(attributes) {
24267
24330
  }
24268
24331
  return void 0;
24269
24332
  }
24333
+ function collectReturnedJSXFromExpression(expression, returned) {
24334
+ if (expression.kind === "JSXElement") {
24335
+ returned.push(expression);
24336
+ return;
24337
+ }
24338
+ if (expression.kind === "ConditionalExpression") {
24339
+ collectReturnedJSXFromExpression(expression.consequent, returned);
24340
+ collectReturnedJSXFromExpression(expression.alternate, returned);
24341
+ return;
24342
+ }
24343
+ if (expression.kind === "LogicalExpression") {
24344
+ collectReturnedJSXFromExpression(expression.left, returned);
24345
+ collectReturnedJSXFromExpression(expression.right, returned);
24346
+ return;
24347
+ }
24348
+ if (expression.kind === "SequenceExpression") {
24349
+ const tail = expression.expressions[expression.expressions.length - 1];
24350
+ if (tail) collectReturnedJSXFromExpression(tail, returned);
24351
+ }
24352
+ }
24353
+ function extractKeyExpressionFromReturnedExpression(expression) {
24354
+ if (expression.kind === "JSXElement") {
24355
+ return extractKeyFromAttributes(expression.attributes);
24356
+ }
24357
+ if (expression.kind === "ConditionalExpression") {
24358
+ const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
24359
+ const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
24360
+ if (!consequentKey || !alternateKey) return void 0;
24361
+ return {
24362
+ kind: "ConditionalExpression",
24363
+ test: expression.test,
24364
+ consequent: consequentKey,
24365
+ alternate: alternateKey,
24366
+ loc: expression.loc
24367
+ };
24368
+ }
24369
+ if (expression.kind === "SequenceExpression") {
24370
+ const tail = expression.expressions[expression.expressions.length - 1];
24371
+ return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
24372
+ }
24373
+ return void 0;
24374
+ }
24375
+ function getReturnedKeyExpressionsFromCallback(callback) {
24376
+ const returned = [];
24377
+ if (callback.kind === "ArrowFunction") {
24378
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24379
+ const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
24380
+ if (keyExpr) returned.push(keyExpr);
24381
+ return returned;
24382
+ }
24383
+ if (Array.isArray(callback.body)) {
24384
+ for (const block of callback.body) {
24385
+ const term = block.terminator;
24386
+ if (term.kind !== "Return" || !term.argument) continue;
24387
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24388
+ if (keyExpr) returned.push(keyExpr);
24389
+ }
24390
+ }
24391
+ return returned;
24392
+ }
24393
+ if (callback.kind === "FunctionExpression") {
24394
+ for (const block of callback.body ?? []) {
24395
+ const term = block.terminator;
24396
+ if (term.kind !== "Return" || !term.argument) continue;
24397
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24398
+ if (keyExpr) returned.push(keyExpr);
24399
+ }
24400
+ }
24401
+ return returned;
24402
+ }
24270
24403
  function getReturnedJSXFromCallback(callback) {
24404
+ const returned = [];
24271
24405
  if (callback.kind === "ArrowFunction") {
24272
- if (callback.isExpression && !Array.isArray(callback.body) && callback.body.kind === "JSXElement") {
24273
- return callback.body;
24406
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24407
+ collectReturnedJSXFromExpression(callback.body, returned);
24408
+ return returned;
24274
24409
  }
24275
24410
  if (Array.isArray(callback.body)) {
24276
24411
  for (const block of callback.body) {
24277
24412
  const term = block.terminator;
24278
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24279
- return term.argument;
24413
+ if (term.kind === "Return" && term.argument) {
24414
+ collectReturnedJSXFromExpression(term.argument, returned);
24280
24415
  }
24281
24416
  }
24282
24417
  }
24418
+ return returned;
24283
24419
  }
24284
24420
  if (callback.kind === "FunctionExpression") {
24285
24421
  for (const block of callback.body ?? []) {
24286
24422
  const term = block.terminator;
24287
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24288
- return term.argument;
24423
+ if (term.kind === "Return" && term.argument) {
24424
+ collectReturnedJSXFromExpression(term.argument, returned);
24289
24425
  }
24290
24426
  }
24291
24427
  }
24292
- return null;
24428
+ return returned;
24429
+ }
24430
+ function keyExpressionSignature(expression) {
24431
+ try {
24432
+ return JSON.stringify(expression, (key, value) => {
24433
+ if (key === "loc") return void 0;
24434
+ if (typeof value === "bigint") return `__bigint:${value.toString()}`;
24435
+ return value;
24436
+ }) ?? "";
24437
+ } catch {
24438
+ return "";
24439
+ }
24293
24440
  }
24294
24441
  function extractKeyFromMapCallback(callback) {
24295
- const jsx = getReturnedJSXFromCallback(callback);
24296
- if (!jsx) return void 0;
24297
- return extractKeyFromAttributes(jsx.attributes);
24442
+ const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
24443
+ if (returnedKeyExpressions.length === 1) {
24444
+ return returnedKeyExpressions[0];
24445
+ }
24446
+ if (returnedKeyExpressions.length > 1) {
24447
+ const [firstKey2, ...restKeys2] = returnedKeyExpressions;
24448
+ const firstSignature2 = keyExpressionSignature(firstKey2);
24449
+ if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
24450
+ return firstKey2;
24451
+ }
24452
+ }
24453
+ const returned = getReturnedJSXFromCallback(callback);
24454
+ if (returned.length === 0) return void 0;
24455
+ const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
24456
+ if (keyExpressions.some((expr) => !expr)) return void 0;
24457
+ const [firstKey, ...restKeys] = keyExpressions;
24458
+ const firstSignature = keyExpressionSignature(firstKey);
24459
+ if (!firstSignature) return void 0;
24460
+ const allBranchesSameKey = restKeys.every(
24461
+ (keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
24462
+ );
24463
+ if (!allBranchesSameKey) return void 0;
24464
+ return firstKey;
24298
24465
  }
24299
24466
 
24300
24467
  // src/ir/codegen-overrides.ts
@@ -24899,6 +25066,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24899
25066
  ctx.inListRender = true;
24900
25067
  let callbackExpr = ops.lowerExpression(mapCallback, ctx);
24901
25068
  ctx.inListRender = prevInListRender;
25069
+ const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
25070
+ let deferredCallbackId = null;
25071
+ let deferredCallbackInitId = null;
25072
+ let deferredItemsId = null;
25073
+ if (shouldDeferOptionalCallbackEvaluation) {
25074
+ deferredCallbackId = ops.genTemp(ctx, "mapCb");
25075
+ deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
25076
+ deferredItemsId = ops.genTemp(ctx, "mapItems");
25077
+ }
24902
25078
  const capturedKeyParamName = ctx.listKeyParamName;
24903
25079
  ctx.listKeyExpr = prevListKeyExpr;
24904
25080
  ctx.listItemParamName = prevListItemParamName;
@@ -24998,7 +25174,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24998
25174
  ],
24999
25175
  keyExprAst
25000
25176
  );
25001
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25177
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25002
25178
  if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
25003
25179
  const newParams = [...callbackExpr.params];
25004
25180
  while (newParams.length < 2) {
@@ -25018,25 +25194,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
25018
25194
  }
25019
25195
  }
25020
25196
  statements.push(...hoistedStatements);
25197
+ if (shouldDeferOptionalCallbackEvaluation) {
25198
+ statements.push(
25199
+ t4.variableDeclaration("let", [
25200
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25201
+ ]),
25202
+ t4.variableDeclaration("let", [
25203
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25204
+ ])
25205
+ );
25206
+ }
25207
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25208
+ [],
25209
+ t4.blockStatement([
25210
+ t4.variableDeclaration("const", [
25211
+ t4.variableDeclarator(
25212
+ t4.cloneNode(deferredItemsId, true),
25213
+ t4.cloneNode(arrayExprBase, true)
25214
+ )
25215
+ ]),
25216
+ t4.ifStatement(
25217
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25218
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25219
+ ),
25220
+ t4.ifStatement(
25221
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25222
+ t4.blockStatement([
25223
+ t4.expressionStatement(
25224
+ t4.assignmentExpression(
25225
+ "=",
25226
+ t4.cloneNode(deferredCallbackId, true),
25227
+ t4.cloneNode(callbackExpr, true)
25228
+ )
25229
+ ),
25230
+ t4.expressionStatement(
25231
+ t4.assignmentExpression(
25232
+ "=",
25233
+ t4.cloneNode(deferredCallbackInitId, true),
25234
+ t4.booleanLiteral(true)
25235
+ )
25236
+ )
25237
+ ])
25238
+ ),
25239
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25240
+ ])
25241
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25242
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25243
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25244
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25245
+ t4.identifier("__item"),
25246
+ t4.identifier("__index"),
25247
+ t4.identifier("__key")
25248
+ ])
25249
+ ) : callbackExpr;
25021
25250
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25022
- t4.arrowFunctionExpression([], arrayExpr),
25251
+ getItemsExpr,
25023
25252
  keyFn,
25024
- callbackExpr,
25253
+ renderExpr,
25025
25254
  t4.booleanLiteral(hasIndexParam)
25026
25255
  ]);
25027
25256
  } else {
25028
25257
  statements.push(...hoistedStatements);
25258
+ if (shouldDeferOptionalCallbackEvaluation) {
25259
+ statements.push(
25260
+ t4.variableDeclaration("let", [
25261
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25262
+ ]),
25263
+ t4.variableDeclaration("let", [
25264
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25265
+ ])
25266
+ );
25267
+ }
25029
25268
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
25030
25269
  const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[1]) ? callbackExpr.params[1].name : "__index" : "__index";
25031
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25270
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25271
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25272
+ [],
25273
+ t4.blockStatement([
25274
+ t4.variableDeclaration("const", [
25275
+ t4.variableDeclarator(
25276
+ t4.cloneNode(deferredItemsId, true),
25277
+ t4.cloneNode(arrayExprBase, true)
25278
+ )
25279
+ ]),
25280
+ t4.ifStatement(
25281
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25282
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25283
+ ),
25284
+ t4.ifStatement(
25285
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25286
+ t4.blockStatement([
25287
+ t4.expressionStatement(
25288
+ t4.assignmentExpression(
25289
+ "=",
25290
+ t4.cloneNode(deferredCallbackId, true),
25291
+ t4.cloneNode(callbackExpr, true)
25292
+ )
25293
+ ),
25294
+ t4.expressionStatement(
25295
+ t4.assignmentExpression(
25296
+ "=",
25297
+ t4.cloneNode(deferredCallbackInitId, true),
25298
+ t4.booleanLiteral(true)
25299
+ )
25300
+ )
25301
+ ])
25302
+ ),
25303
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25304
+ ])
25305
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25306
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25307
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25308
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25309
+ t4.identifier("__item"),
25310
+ t4.identifier("__index"),
25311
+ t4.identifier("__key")
25312
+ ])
25313
+ ) : callbackExpr;
25032
25314
  const keyFn = t4.arrowFunctionExpression(
25033
25315
  [t4.identifier(itemParamName), t4.identifier(indexParamName)],
25034
25316
  t4.identifier(indexParamName)
25035
25317
  );
25036
25318
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25037
- t4.arrowFunctionExpression([], arrayExpr),
25319
+ getItemsExpr,
25038
25320
  keyFn,
25039
- callbackExpr,
25321
+ renderExpr,
25040
25322
  t4.booleanLiteral(hasIndexParam)
25041
25323
  ]);
25042
25324
  }
@@ -25499,6 +25781,10 @@ function registerResumableComponent(componentName, ctx) {
25499
25781
  const hostParam = t4.identifier("host");
25500
25782
  const snapshotId = t4.identifier("snapshot");
25501
25783
  const ctxId = t4.identifier("ctx");
25784
+ const runtimeModuleUrlExpr = t4.memberExpression(
25785
+ t4.metaProperty(t4.identifier("import"), t4.identifier("meta")),
25786
+ t4.identifier("url")
25787
+ );
25502
25788
  ctx.helpersUsed.add("getSSRScope");
25503
25789
  ctx.helpersUsed.add("ensureScope");
25504
25790
  ctx.helpersUsed.add("prepareContext");
@@ -25572,7 +25858,10 @@ function registerResumableComponent(componentName, ctx) {
25572
25858
  ctx.helpersUsed.add("registerResume");
25573
25859
  const registerCall = t4.expressionStatement(
25574
25860
  t4.callExpression(t4.identifier(RUNTIME_ALIASES.registerResume), [
25575
- t4.stringLiteral(resumeExport),
25861
+ t4.callExpression(t4.identifier(RUNTIME_ALIASES.qrl), [
25862
+ runtimeModuleUrlExpr,
25863
+ t4.stringLiteral(resumeExport)
25864
+ ]),
25576
25865
  resumeFnId
25577
25866
  ])
25578
25867
  );
@@ -27362,18 +27651,8 @@ function lowerExpressionImpl(expr, ctx, valueUsed = true) {
27362
27651
  blocks,
27363
27652
  meta: { fromExpression: true }
27364
27653
  };
27365
- const cfg = analyzeCFG(fn.blocks);
27366
- const hasLoop = cfg.loopHeaders.size > 0 || cfg.backEdges.size > 0;
27367
27654
  const { node, diagnostics } = structurizeCFGWithDiagnostics(fn);
27368
- const structured = node.kind === "stateMachine" || hasLoop ? node.kind === "stateMachine" ? node : {
27369
- kind: "stateMachine",
27370
- blocks: fn.blocks.map((block) => ({
27371
- blockId: block.id,
27372
- instructions: block.instructions,
27373
- terminator: block.terminator
27374
- })),
27375
- entryBlock: fn.blocks[0]?.id ?? 0
27376
- } : diagnostics.isComplete ? node : {
27655
+ const structured = node.kind === "stateMachine" ? node : diagnostics.isComplete ? node : {
27377
27656
  kind: "stateMachine",
27378
27657
  blocks: fn.blocks.map((block) => ({
27379
27658
  blockId: block.id,
@@ -33935,6 +34214,506 @@ function getRootIdentifier(expr, t4) {
33935
34214
  return null;
33936
34215
  }
33937
34216
 
34217
+ // src/tooling/analyze.ts
34218
+ var import_core2 = require("@babel/core");
34219
+
34220
+ // src/tooling/trace-infer.ts
34221
+ var TRACE_REGEX_ESCAPES = /[.*+?^${}()|[\]\\]/g;
34222
+ var IDENTIFIER_NAME = /^[A-Za-z_$][\w$]*$/;
34223
+ function isIdentifierName(name) {
34224
+ return IDENTIFIER_NAME.test(name);
34225
+ }
34226
+ function lineContainsIdentifier(lineText, identifier2) {
34227
+ const pattern = new RegExp(`\\b${identifier2.replace(TRACE_REGEX_ESCAPES, "\\$&")}\\b`);
34228
+ return pattern.test(lineText);
34229
+ }
34230
+ function lineContainsAnyIdentifier(lineText, identifiers) {
34231
+ for (const id of identifiers) {
34232
+ if (lineContainsIdentifier(lineText, id)) return true;
34233
+ }
34234
+ return false;
34235
+ }
34236
+ function pushTraceMarker(markersByLine, line, marker) {
34237
+ const markers = markersByLine.get(line);
34238
+ if (!markers) {
34239
+ markersByLine.set(line, [marker]);
34240
+ return;
34241
+ }
34242
+ const duplicate = markers.some(
34243
+ (existing) => existing.kind === marker.kind && existing.label === marker.label
34244
+ );
34245
+ if (!duplicate) markers.push(marker);
34246
+ }
34247
+ function inferReactiveLocalNames(startLine, endLine, sourceLines, baseReactiveNames) {
34248
+ const reactiveNames = new Set(baseReactiveNames);
34249
+ const declarationLines = [];
34250
+ const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?)(?:;)?$/;
34251
+ for (let line = startLine; line <= endLine; line++) {
34252
+ const text = (sourceLines[line - 1] ?? "").trim();
34253
+ if (!text) continue;
34254
+ const withoutComment = text.replace(/\/\/.*$/, "").trim();
34255
+ if (!withoutComment) continue;
34256
+ const match = withoutComment.match(declarationPattern);
34257
+ if (!match) continue;
34258
+ const name = match[1];
34259
+ const expression = match[2];
34260
+ if (!name || !expression) continue;
34261
+ declarationLines.push({ name, expression });
34262
+ }
34263
+ let changed = true;
34264
+ while (changed) {
34265
+ changed = false;
34266
+ for (const declaration of declarationLines) {
34267
+ if (reactiveNames.has(declaration.name)) continue;
34268
+ if (!lineContainsAnyIdentifier(declaration.expression, reactiveNames)) continue;
34269
+ reactiveNames.add(declaration.name);
34270
+ changed = true;
34271
+ }
34272
+ }
34273
+ return reactiveNames;
34274
+ }
34275
+ function isStateCallInstruction(instr) {
34276
+ if (instr.kind !== "Assign") return false;
34277
+ const value = instr.value;
34278
+ return value.kind === "CallExpression" && value.callee.kind === "Identifier" && value.callee.name === "$state";
34279
+ }
34280
+ function collectStateDeclNames(fn) {
34281
+ const result = [];
34282
+ for (const block of fn.blocks) {
34283
+ for (const instr of block.instructions) {
34284
+ if (!isStateCallInstruction(instr) || !instr.loc) continue;
34285
+ const name = deSSAVarName(instr.target.name);
34286
+ if (!isIdentifierName(name)) continue;
34287
+ result.push({ name, line: instr.loc.start.line });
34288
+ }
34289
+ }
34290
+ return result;
34291
+ }
34292
+ function expressionContainsEffectCall(instr) {
34293
+ const value = instr.kind === "Assign" || instr.kind === "Expression" ? instr.value : null;
34294
+ if (!value || !instr.loc) return null;
34295
+ let found = false;
34296
+ walkExpression(value, (expr) => {
34297
+ if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier" && deSSAVarName(expr.callee.name) === "$effect") {
34298
+ found = true;
34299
+ }
34300
+ });
34301
+ return found ? instr.loc.start.line : null;
34302
+ }
34303
+ function flattenRegions2(regions) {
34304
+ if (!regions || regions.length === 0) return [];
34305
+ const result = [];
34306
+ const visit = (region) => {
34307
+ result.push(region);
34308
+ region.children?.forEach((child) => visit(child));
34309
+ };
34310
+ regions.forEach((region) => visit(region));
34311
+ return result;
34312
+ }
34313
+ function findContainingRegion2(line, flatRegions) {
34314
+ let best;
34315
+ for (const region of flatRegions) {
34316
+ if (region.startLine === void 0 || region.endLine === void 0 || line < region.startLine || line > region.endLine) {
34317
+ continue;
34318
+ }
34319
+ const bestSpan = best && best.startLine !== void 0 && best.endLine !== void 0 ? best.endLine - best.startLine : Number.POSITIVE_INFINITY;
34320
+ const span = region.endLine - region.startLine;
34321
+ if (span <= bestSpan) best = region;
34322
+ }
34323
+ return best;
34324
+ }
34325
+ function inferTraceMarkersForComponent(input) {
34326
+ const { fn, sourceLines, startLine, endLine, verbosity, regions } = input;
34327
+ const markersByLine = /* @__PURE__ */ new Map();
34328
+ pushTraceMarker(markersByLine, startLine, {
34329
+ kind: "once",
34330
+ label: "Component setup runs on mount"
34331
+ });
34332
+ const stateDecls = collectStateDeclNames(fn);
34333
+ const reactiveNames = inferReactiveLocalNames(
34334
+ startLine,
34335
+ endLine,
34336
+ sourceLines,
34337
+ stateDecls.map((item) => item.name)
34338
+ );
34339
+ for (const stateDecl of stateDecls) {
34340
+ pushTraceMarker(markersByLine, stateDecl.line, {
34341
+ kind: "once",
34342
+ label: "Signal initialization runs once"
34343
+ });
34344
+ }
34345
+ for (const block of fn.blocks) {
34346
+ for (const instr of block.instructions) {
34347
+ const line = expressionContainsEffectCall(instr);
34348
+ if (!line || line < startLine || line > endLine) continue;
34349
+ pushTraceMarker(markersByLine, line, {
34350
+ kind: "effect",
34351
+ label: "Effect reruns when dependencies change"
34352
+ });
34353
+ }
34354
+ }
34355
+ const flatRegions = flattenRegions2(regions);
34356
+ for (let line = startLine; line <= endLine; line++) {
34357
+ const lineText = sourceLines[line - 1] ?? "";
34358
+ if (!lineText) continue;
34359
+ const hasReactiveName = lineContainsAnyIdentifier(lineText, reactiveNames);
34360
+ if (hasReactiveName && /\{[^}]*\b[A-Za-z_$][\w$]*\b[^}]*\}/.test(lineText)) {
34361
+ pushTraceMarker(markersByLine, line, {
34362
+ kind: "reactive",
34363
+ label: "JSX expression updates with reactive values"
34364
+ });
34365
+ }
34366
+ if (hasReactiveName && /\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText)) {
34367
+ pushTraceMarker(markersByLine, line, {
34368
+ kind: "reactive",
34369
+ label: "Statement reruns when reactive values change"
34370
+ });
34371
+ }
34372
+ if (/\b(?:\$effect|effect)\s*\(/.test(lineText)) {
34373
+ pushTraceMarker(markersByLine, line, {
34374
+ kind: "effect",
34375
+ label: "Effect callback executes reactively"
34376
+ });
34377
+ }
34378
+ if (verbosity === "verbose" && !hasReactiveName && /\{[^}]*\}/.test(lineText)) {
34379
+ pushTraceMarker(markersByLine, line, {
34380
+ kind: "once",
34381
+ label: "JSX expression runs during setup only"
34382
+ });
34383
+ }
34384
+ if (/\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText) && !hasReactiveName && line !== startLine) {
34385
+ pushTraceMarker(markersByLine, line, {
34386
+ kind: "once",
34387
+ label: "Statement runs only during setup"
34388
+ });
34389
+ }
34390
+ const containingRegion = findContainingRegion2(line, flatRegions);
34391
+ if (!containingRegion) continue;
34392
+ const markers = markersByLine.get(line);
34393
+ if (!markers) continue;
34394
+ for (const marker of markers) {
34395
+ marker.regionId = containingRegion.id;
34396
+ marker.deps = containingRegion.dependencies;
34397
+ }
34398
+ }
34399
+ return [...markersByLine.entries()].sort((a, b) => a[0] - b[0]).map(([line, markers]) => ({ line, markers }));
34400
+ }
34401
+
34402
+ // src/tooling/analyze.ts
34403
+ function mergeLoc(a, b) {
34404
+ if (!a) return b ?? null;
34405
+ if (!b) return a;
34406
+ const start = b.start.line < a.start.line || b.start.line === a.start.line && b.start.column < a.start.column ? b.start : a.start;
34407
+ const end = b.end.line > a.end.line || b.end.line === a.end.line && b.end.column > a.end.column ? b.end : a.end;
34408
+ return {
34409
+ start,
34410
+ end,
34411
+ filename: a.filename ?? b.filename,
34412
+ identifierName: a.identifierName ?? b.identifierName
34413
+ };
34414
+ }
34415
+ function expressionContainsMacroCall(expr, macroName) {
34416
+ let found = false;
34417
+ const visit = (value) => {
34418
+ if (found) return;
34419
+ if (value.kind === "CallExpression" && value.callee.kind === "Identifier" && deSSAVarName(value.callee.name) === macroName) {
34420
+ found = true;
34421
+ return;
34422
+ }
34423
+ switch (value.kind) {
34424
+ case "CallExpression":
34425
+ case "OptionalCallExpression":
34426
+ visit(value.callee);
34427
+ value.arguments.forEach((arg) => visit(arg));
34428
+ return;
34429
+ case "MemberExpression":
34430
+ case "OptionalMemberExpression":
34431
+ visit(value.object);
34432
+ if (value.computed) visit(value.property);
34433
+ return;
34434
+ case "BinaryExpression":
34435
+ case "LogicalExpression":
34436
+ visit(value.left);
34437
+ visit(value.right);
34438
+ return;
34439
+ case "UnaryExpression":
34440
+ case "AwaitExpression":
34441
+ case "UpdateExpression":
34442
+ case "SpreadElement":
34443
+ visit(value.argument);
34444
+ return;
34445
+ case "ConditionalExpression":
34446
+ visit(value.test);
34447
+ visit(value.consequent);
34448
+ visit(value.alternate);
34449
+ return;
34450
+ case "ArrayExpression":
34451
+ value.elements.forEach((el) => visit(el));
34452
+ return;
34453
+ case "ObjectExpression":
34454
+ value.properties.forEach((prop) => {
34455
+ if (prop.kind === "SpreadElement") {
34456
+ visit(prop.argument);
34457
+ return;
34458
+ }
34459
+ if (prop.computed) visit(prop.key);
34460
+ visit(prop.value);
34461
+ });
34462
+ return;
34463
+ case "TemplateLiteral":
34464
+ value.expressions.forEach((item) => visit(item));
34465
+ return;
34466
+ case "AssignmentExpression":
34467
+ visit(value.left);
34468
+ visit(value.right);
34469
+ return;
34470
+ case "SequenceExpression":
34471
+ value.expressions.forEach((item) => visit(item));
34472
+ return;
34473
+ case "YieldExpression":
34474
+ if (value.argument) visit(value.argument);
34475
+ return;
34476
+ case "TaggedTemplateExpression":
34477
+ visit(value.tag);
34478
+ value.quasi.expressions.forEach((item) => visit(item));
34479
+ return;
34480
+ case "ImportExpression":
34481
+ visit(value.source);
34482
+ return;
34483
+ case "ArrowFunction":
34484
+ if (Array.isArray(value.body)) {
34485
+ value.body.forEach((block) => {
34486
+ block.instructions.forEach((instr) => {
34487
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34488
+ visit(instr.value);
34489
+ }
34490
+ });
34491
+ });
34492
+ } else {
34493
+ visit(value.body);
34494
+ }
34495
+ return;
34496
+ case "FunctionExpression":
34497
+ value.body.forEach((block) => {
34498
+ block.instructions.forEach((instr) => {
34499
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34500
+ visit(instr.value);
34501
+ }
34502
+ });
34503
+ });
34504
+ return;
34505
+ case "JSXElement":
34506
+ if (typeof value.tagName !== "string") visit(value.tagName);
34507
+ value.attributes.forEach((attr) => {
34508
+ if (attr.isSpread && attr.spreadExpr) {
34509
+ visit(attr.spreadExpr);
34510
+ } else if (attr.value) {
34511
+ visit(attr.value);
34512
+ }
34513
+ });
34514
+ value.children.forEach((child) => {
34515
+ if (child.kind === "expression") visit(child.value);
34516
+ if (child.kind === "element") visit(child.value);
34517
+ });
34518
+ return;
34519
+ case "Identifier":
34520
+ case "Literal":
34521
+ case "MetaProperty":
34522
+ case "NewExpression":
34523
+ case "ClassExpression":
34524
+ case "ThisExpression":
34525
+ case "SuperExpression":
34526
+ if (value.kind === "NewExpression") {
34527
+ visit(value.callee);
34528
+ value.arguments.forEach((arg) => visit(arg));
34529
+ }
34530
+ if (value.kind === "ClassExpression" && value.superClass) visit(value.superClass);
34531
+ return;
34532
+ default:
34533
+ return;
34534
+ }
34535
+ };
34536
+ visit(expr);
34537
+ return found;
34538
+ }
34539
+ function instructionContainsMacroCall(instruction, macroName) {
34540
+ if (instruction.kind !== "Assign" && instruction.kind !== "Expression") return false;
34541
+ return expressionContainsMacroCall(instruction.value, macroName);
34542
+ }
34543
+ function functionUsesMacro(fn, macroName) {
34544
+ for (const block of fn.blocks) {
34545
+ for (const instruction of block.instructions) {
34546
+ if (instructionContainsMacroCall(instruction, macroName)) return true;
34547
+ }
34548
+ const term = block.terminator;
34549
+ if ("argument" in term && term.argument && expressionContainsMacroCall(term.argument, macroName)) {
34550
+ return true;
34551
+ }
34552
+ if (term.kind === "Branch" && expressionContainsMacroCall(term.test, macroName)) {
34553
+ return true;
34554
+ }
34555
+ if (term.kind === "Switch") {
34556
+ if (expressionContainsMacroCall(term.discriminant, macroName)) return true;
34557
+ if (term.cases.some((entry) => entry.test && expressionContainsMacroCall(entry.test, macroName))) {
34558
+ return true;
34559
+ }
34560
+ }
34561
+ }
34562
+ return false;
34563
+ }
34564
+ function computeRegionLoc(region, fn) {
34565
+ let loc = null;
34566
+ for (const instruction of region.instructions) {
34567
+ loc = mergeLoc(loc, instruction.loc);
34568
+ }
34569
+ for (const blockId of region.blocks) {
34570
+ const block = fn.blocks.find((entry) => entry.id === blockId);
34571
+ if (!block) continue;
34572
+ loc = mergeLoc(loc, block.terminator.loc);
34573
+ }
34574
+ return loc;
34575
+ }
34576
+ function regionToSerializable(region, fn) {
34577
+ const loc = computeRegionLoc(region, fn);
34578
+ return {
34579
+ id: region.id,
34580
+ startLine: loc?.start.line,
34581
+ startColumn: loc?.start.column,
34582
+ endLine: loc?.end.line,
34583
+ endColumn: loc?.end.column,
34584
+ dependencies: [...region.dependencies].map(deSSAVarName),
34585
+ declarations: [...region.declarations].map(deSSAVarName),
34586
+ hasControlFlow: region.hasControlFlow,
34587
+ hasReactiveWrites: region.declarations.size > 0,
34588
+ children: region.children.map((child) => regionToSerializable(child, fn))
34589
+ };
34590
+ }
34591
+ function warningSeverity(code) {
34592
+ const diagnosticCodes = new Set(getAllDiagnosticCodes());
34593
+ if (!diagnosticCodes.has(code)) return "warning" /* Warning */;
34594
+ return getDiagnosticInfo(code).severity;
34595
+ }
34596
+ function normalizeWarningToDiagnostic(warning) {
34597
+ return {
34598
+ code: warning.code,
34599
+ message: warning.message,
34600
+ severity: warningSeverity(warning.code),
34601
+ line: warning.line,
34602
+ column: warning.column
34603
+ };
34604
+ }
34605
+ function normalizeThrownError(error) {
34606
+ const message = error instanceof Error ? error.message : String(error);
34607
+ return {
34608
+ code: "FICT-COMPILE",
34609
+ message,
34610
+ severity: "error" /* Error */,
34611
+ line: 0,
34612
+ column: 0
34613
+ };
34614
+ }
34615
+ function analyzeDiagnostics(code, fileName, options) {
34616
+ const warnings = [];
34617
+ const pluginOptions = {
34618
+ dev: true,
34619
+ filename: fileName,
34620
+ emitModuleMetadata: false,
34621
+ strictGuarantee: false,
34622
+ warningLevels: {
34623
+ ...options.compilerOptions?.warningLevels ?? {},
34624
+ "FICT-R004": "warn"
34625
+ },
34626
+ ...options.compilerOptions,
34627
+ onWarn: (warning) => warnings.push(warning)
34628
+ };
34629
+ try {
34630
+ (0, import_core2.transformSync)(code, {
34631
+ filename: fileName,
34632
+ configFile: false,
34633
+ babelrc: false,
34634
+ sourceType: "module",
34635
+ parserOpts: {
34636
+ sourceType: "module",
34637
+ plugins: ["typescript", "jsx"],
34638
+ allowReturnOutsideFunction: true
34639
+ },
34640
+ plugins: [[index_default, pluginOptions]],
34641
+ generatorOpts: {
34642
+ compact: false
34643
+ }
34644
+ });
34645
+ } catch (error) {
34646
+ return [...warnings.map(normalizeWarningToDiagnostic), normalizeThrownError(error)];
34647
+ }
34648
+ return warnings.map(normalizeWarningToDiagnostic);
34649
+ }
34650
+ function shouldIncludeFunction(fn) {
34651
+ return functionContainsJSX(fn) || functionUsesMacro(fn, "$state") || functionUsesMacro(fn, "$effect");
34652
+ }
34653
+ function parseFileAst(code, fileName) {
34654
+ const ast = (0, import_core2.parseSync)(code, {
34655
+ filename: fileName,
34656
+ sourceType: "module",
34657
+ parserOpts: {
34658
+ sourceType: "module",
34659
+ plugins: ["typescript", "jsx"],
34660
+ allowReturnOutsideFunction: true
34661
+ }
34662
+ });
34663
+ if (!ast || ast.type !== "File") {
34664
+ throw new Error("Failed to parse source file for Fict analysis.");
34665
+ }
34666
+ return ast;
34667
+ }
34668
+ function analyzeFictFile(code, fileName, options = {}) {
34669
+ const includeRegions = options.includeRegions ?? true;
34670
+ const includeDiagnostics = options.includeDiagnostics ?? true;
34671
+ const verbosity = options.verbosity ?? "minimal";
34672
+ const ast = parseFileAst(code, fileName);
34673
+ const hir = buildHIR(
34674
+ ast,
34675
+ {
34676
+ state: /* @__PURE__ */ new Set(["$state"]),
34677
+ effect: /* @__PURE__ */ new Set(["$effect"])
34678
+ },
34679
+ {
34680
+ dev: true,
34681
+ fileName
34682
+ }
34683
+ );
34684
+ const sourceLines = code.split(/\r?\n/);
34685
+ const components = [];
34686
+ for (const fn of hir.functions) {
34687
+ if (!fn.loc || !shouldIncludeFunction(fn)) continue;
34688
+ const startLine = fn.loc.start.line;
34689
+ const endLine = fn.loc.end.line;
34690
+ const scopeResult = analyzeReactiveScopesWithSSA(fn);
34691
+ const regionResult = generateRegions(fn, scopeResult);
34692
+ const regions = includeRegions ? regionResult.topLevelRegions.map((region) => regionToSerializable(region, fn)) : void 0;
34693
+ const trace = inferTraceMarkersForComponent({
34694
+ fn,
34695
+ sourceLines,
34696
+ startLine,
34697
+ endLine,
34698
+ verbosity,
34699
+ regions
34700
+ });
34701
+ components.push({
34702
+ name: fn.name ?? "<anonymous>",
34703
+ startLine,
34704
+ endLine,
34705
+ trace,
34706
+ regions
34707
+ });
34708
+ }
34709
+ const diagnostics = includeDiagnostics ? analyzeDiagnostics(code, fileName, options) : [];
34710
+ return {
34711
+ fileName,
34712
+ components,
34713
+ diagnostics
34714
+ };
34715
+ }
34716
+
33938
34717
  // src/index.ts
33939
34718
  function stripMacroImports(path2, t4) {
33940
34719
  path2.traverse({
@@ -34828,46 +35607,77 @@ function createHIREntrypointVisitor(t4, options) {
34828
35607
  path2.traverse({
34829
35608
  JSXExpressionContainer(exprPath) {
34830
35609
  const expr = exprPath.node.expression;
34831
- if (!t4.isCallExpression(expr)) return;
34832
- if (!t4.isMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
35610
+ if (!t4.isCallExpression(expr) && !t4.isOptionalCallExpression(expr)) return;
35611
+ if (!t4.isMemberExpression(expr.callee) && !t4.isOptionalMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
34833
35612
  return;
34834
35613
  }
34835
- const cb = expr.arguments[0];
34836
- if (!cb || !t4.isArrowFunctionExpression(cb) && !t4.isFunctionExpression(cb)) return;
34837
- const getReturnedJsx = (fn) => {
34838
- if (t4.isJSXElement(fn.body) || t4.isJSXFragment(fn.body)) return fn.body;
34839
- if (t4.isBlockStatement(fn.body)) {
34840
- const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
34841
- if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
34842
- return ret.argument;
34843
- }
34844
- }
34845
- return null;
34846
- };
34847
- const jsx = getReturnedJsx(cb);
34848
- if (!jsx) return;
34849
- if (t4.isJSXFragment(jsx)) {
34850
- warn({
34851
- code: "FICT-J002",
34852
- message: "Missing key prop in list rendering.",
34853
- fileName,
34854
- line: expr.loc?.start.line ?? 0,
34855
- column: expr.loc ? expr.loc.start.column + 1 : 0
34856
- });
35614
+ const callExprPath = exprPath.get("expression");
35615
+ if (!callExprPath.isCallExpression() && !callExprPath.isOptionalCallExpression()) return;
35616
+ const argPaths = callExprPath.get("arguments");
35617
+ const cbPath = Array.isArray(argPaths) ? argPaths[0] : void 0;
35618
+ if (!cbPath || !cbPath.isArrowFunctionExpression() && !cbPath.isFunctionExpression()) {
34857
35619
  return;
34858
35620
  }
34859
- let hasKey = false;
34860
- let hasUnknownSpread = false;
34861
- for (const attr of jsx.openingElement.attributes) {
34862
- if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
34863
- hasKey = true;
34864
- break;
35621
+ const getReturnedJsx = (fnPath) => {
35622
+ const collectReturnedJsxFromExpression = (node, returned2) => {
35623
+ if (!node) return;
35624
+ if (t4.isJSXElement(node) || t4.isJSXFragment(node)) {
35625
+ returned2.push(node);
35626
+ return;
35627
+ }
35628
+ if (t4.isConditionalExpression(node)) {
35629
+ collectReturnedJsxFromExpression(node.consequent, returned2);
35630
+ collectReturnedJsxFromExpression(node.alternate, returned2);
35631
+ return;
35632
+ }
35633
+ if (t4.isLogicalExpression(node)) {
35634
+ collectReturnedJsxFromExpression(node.left, returned2);
35635
+ collectReturnedJsxFromExpression(node.right, returned2);
35636
+ return;
35637
+ }
35638
+ if (t4.isSequenceExpression(node)) {
35639
+ const tail = node.expressions[node.expressions.length - 1];
35640
+ collectReturnedJsxFromExpression(tail, returned2);
35641
+ return;
35642
+ }
35643
+ if (t4.isParenthesizedExpression(node)) {
35644
+ collectReturnedJsxFromExpression(node.expression, returned2);
35645
+ return;
35646
+ }
35647
+ if (t4.isTSAsExpression(node) || t4.isTSTypeAssertion(node) || t4.isTSNonNullExpression(node) || t4.isTSSatisfiesExpression(node) || t4.isTypeCastExpression(node)) {
35648
+ collectReturnedJsxFromExpression(node.expression, returned2);
35649
+ }
35650
+ };
35651
+ const fn = fnPath.node;
35652
+ const returned = [];
35653
+ if (!t4.isBlockStatement(fn.body)) {
35654
+ collectReturnedJsxFromExpression(fn.body, returned);
35655
+ return returned;
34865
35656
  }
34866
- if (t4.isJSXSpreadAttribute(attr)) {
34867
- hasUnknownSpread = true;
35657
+ fnPath.get("body").traverse({
35658
+ Function(innerFnPath) {
35659
+ innerFnPath.skip();
35660
+ },
35661
+ ReturnStatement(retPath) {
35662
+ collectReturnedJsxFromExpression(retPath.node.argument, returned);
35663
+ }
35664
+ });
35665
+ return returned;
35666
+ };
35667
+ const returnedJsx = getReturnedJsx(cbPath);
35668
+ if (returnedJsx.length === 0) return;
35669
+ const hasMissingKeyBranch = returnedJsx.some((jsx) => {
35670
+ if (t4.isJSXFragment(jsx)) return true;
35671
+ let hasKey = false;
35672
+ for (const attr of jsx.openingElement.attributes) {
35673
+ if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
35674
+ hasKey = true;
35675
+ break;
35676
+ }
34868
35677
  }
34869
- }
34870
- if (hasKey || hasUnknownSpread) return;
35678
+ return !hasKey;
35679
+ });
35680
+ if (!hasMissingKeyBranch) return;
34871
35681
  warn({
34872
35682
  code: "FICT-J002",
34873
35683
  message: "Missing key prop in list rendering.",
@@ -35430,8 +36240,10 @@ var createFictPlugin = (0, import_helper_plugin_utils.declare)(
35430
36240
  var index_default = createFictPlugin;
35431
36241
  // Annotate the CommonJS export names for ESM import in node:
35432
36242
  0 && (module.exports = {
36243
+ analyzeFictFile,
35433
36244
  clearModuleMetadata,
35434
36245
  createFictPlugin,
36246
+ inferTraceMarkersForComponent,
35435
36247
  resolveModuleMetadata,
35436
36248
  setModuleMetadata
35437
36249
  });