@fictjs/compiler 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3065,7 +3065,7 @@ var require_identifier = __commonJS({
3065
3065
  value: true
3066
3066
  });
3067
3067
  exports2.isIdentifierChar = isIdentifierChar;
3068
- exports2.isIdentifierName = isIdentifierName;
3068
+ exports2.isIdentifierName = isIdentifierName2;
3069
3069
  exports2.isIdentifierStart = isIdentifierStart;
3070
3070
  var nonASCIIidentifierStartChars = "\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
3071
3071
  var nonASCIIidentifierChars = "\xB7\u0300-\u036F\u0387\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u0897-\u089F\u08CA-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B55-\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C04\u0C3C\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0CF3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D81-\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EBC\u0EC8-\u0ECE\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u180F-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19D0-\u19DA\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1ABF-\u1ADD\u1AE0-\u1AEB\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\u30FB\uA620-\uA629\uA66F\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA82C\uA880\uA881\uA8B4-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F1\uA8FF-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F\uFF65";
@@ -3106,7 +3106,7 @@ var require_identifier = __commonJS({
3106
3106
  }
3107
3107
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
3108
3108
  }
3109
- function isIdentifierName(name) {
3109
+ function isIdentifierName2(name) {
3110
3110
  let isFirst = true;
3111
3111
  for (let i = 0; i < name.length; i++) {
3112
3112
  let cp = name.charCodeAt(i);
@@ -14098,9 +14098,11 @@ var require_lib3 = __commonJS({
14098
14098
  // src/index.ts
14099
14099
  var index_exports = {};
14100
14100
  __export(index_exports, {
14101
+ analyzeFictFile: () => analyzeFictFile,
14101
14102
  clearModuleMetadata: () => clearModuleMetadata,
14102
14103
  createFictPlugin: () => createFictPlugin,
14103
14104
  default: () => index_default,
14105
+ inferTraceMarkersForComponent: () => inferTraceMarkersForComponent,
14104
14106
  resolveModuleMetadata: () => resolveModuleMetadata,
14105
14107
  setModuleMetadata: () => setModuleMetadata
14106
14108
  });
@@ -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) {
@@ -24267,34 +24310,138 @@ function extractKeyFromAttributes(attributes) {
24267
24310
  }
24268
24311
  return void 0;
24269
24312
  }
24313
+ function collectReturnedJSXFromExpression(expression, returned) {
24314
+ if (expression.kind === "JSXElement") {
24315
+ returned.push(expression);
24316
+ return;
24317
+ }
24318
+ if (expression.kind === "ConditionalExpression") {
24319
+ collectReturnedJSXFromExpression(expression.consequent, returned);
24320
+ collectReturnedJSXFromExpression(expression.alternate, returned);
24321
+ return;
24322
+ }
24323
+ if (expression.kind === "LogicalExpression") {
24324
+ collectReturnedJSXFromExpression(expression.left, returned);
24325
+ collectReturnedJSXFromExpression(expression.right, returned);
24326
+ return;
24327
+ }
24328
+ if (expression.kind === "SequenceExpression") {
24329
+ const tail = expression.expressions[expression.expressions.length - 1];
24330
+ if (tail) collectReturnedJSXFromExpression(tail, returned);
24331
+ }
24332
+ }
24333
+ function extractKeyExpressionFromReturnedExpression(expression) {
24334
+ if (expression.kind === "JSXElement") {
24335
+ return extractKeyFromAttributes(expression.attributes);
24336
+ }
24337
+ if (expression.kind === "ConditionalExpression") {
24338
+ const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
24339
+ const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
24340
+ if (!consequentKey || !alternateKey) return void 0;
24341
+ return {
24342
+ kind: "ConditionalExpression",
24343
+ test: expression.test,
24344
+ consequent: consequentKey,
24345
+ alternate: alternateKey,
24346
+ loc: expression.loc
24347
+ };
24348
+ }
24349
+ if (expression.kind === "SequenceExpression") {
24350
+ const tail = expression.expressions[expression.expressions.length - 1];
24351
+ return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
24352
+ }
24353
+ return void 0;
24354
+ }
24355
+ function getReturnedKeyExpressionsFromCallback(callback) {
24356
+ const returned = [];
24357
+ if (callback.kind === "ArrowFunction") {
24358
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24359
+ const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
24360
+ if (keyExpr) returned.push(keyExpr);
24361
+ return returned;
24362
+ }
24363
+ if (Array.isArray(callback.body)) {
24364
+ for (const block of callback.body) {
24365
+ const term = block.terminator;
24366
+ if (term.kind !== "Return" || !term.argument) continue;
24367
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24368
+ if (keyExpr) returned.push(keyExpr);
24369
+ }
24370
+ }
24371
+ return returned;
24372
+ }
24373
+ if (callback.kind === "FunctionExpression") {
24374
+ for (const block of callback.body ?? []) {
24375
+ const term = block.terminator;
24376
+ if (term.kind !== "Return" || !term.argument) continue;
24377
+ const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
24378
+ if (keyExpr) returned.push(keyExpr);
24379
+ }
24380
+ }
24381
+ return returned;
24382
+ }
24270
24383
  function getReturnedJSXFromCallback(callback) {
24384
+ const returned = [];
24271
24385
  if (callback.kind === "ArrowFunction") {
24272
- if (callback.isExpression && !Array.isArray(callback.body) && callback.body.kind === "JSXElement") {
24273
- return callback.body;
24386
+ if (callback.isExpression && !Array.isArray(callback.body)) {
24387
+ collectReturnedJSXFromExpression(callback.body, returned);
24388
+ return returned;
24274
24389
  }
24275
24390
  if (Array.isArray(callback.body)) {
24276
24391
  for (const block of callback.body) {
24277
24392
  const term = block.terminator;
24278
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24279
- return term.argument;
24393
+ if (term.kind === "Return" && term.argument) {
24394
+ collectReturnedJSXFromExpression(term.argument, returned);
24280
24395
  }
24281
24396
  }
24282
24397
  }
24398
+ return returned;
24283
24399
  }
24284
24400
  if (callback.kind === "FunctionExpression") {
24285
24401
  for (const block of callback.body ?? []) {
24286
24402
  const term = block.terminator;
24287
- if (term.kind === "Return" && term.argument?.kind === "JSXElement") {
24288
- return term.argument;
24403
+ if (term.kind === "Return" && term.argument) {
24404
+ collectReturnedJSXFromExpression(term.argument, returned);
24289
24405
  }
24290
24406
  }
24291
24407
  }
24292
- return null;
24408
+ return returned;
24409
+ }
24410
+ function keyExpressionSignature(expression) {
24411
+ try {
24412
+ return JSON.stringify(expression, (key, value) => {
24413
+ if (key === "loc") return void 0;
24414
+ if (typeof value === "bigint") return `__bigint:${value.toString()}`;
24415
+ return value;
24416
+ }) ?? "";
24417
+ } catch {
24418
+ return "";
24419
+ }
24293
24420
  }
24294
24421
  function extractKeyFromMapCallback(callback) {
24295
- const jsx = getReturnedJSXFromCallback(callback);
24296
- if (!jsx) return void 0;
24297
- return extractKeyFromAttributes(jsx.attributes);
24422
+ const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
24423
+ if (returnedKeyExpressions.length === 1) {
24424
+ return returnedKeyExpressions[0];
24425
+ }
24426
+ if (returnedKeyExpressions.length > 1) {
24427
+ const [firstKey2, ...restKeys2] = returnedKeyExpressions;
24428
+ const firstSignature2 = keyExpressionSignature(firstKey2);
24429
+ if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
24430
+ return firstKey2;
24431
+ }
24432
+ }
24433
+ const returned = getReturnedJSXFromCallback(callback);
24434
+ if (returned.length === 0) return void 0;
24435
+ const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
24436
+ if (keyExpressions.some((expr) => !expr)) return void 0;
24437
+ const [firstKey, ...restKeys] = keyExpressions;
24438
+ const firstSignature = keyExpressionSignature(firstKey);
24439
+ if (!firstSignature) return void 0;
24440
+ const allBranchesSameKey = restKeys.every(
24441
+ (keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
24442
+ );
24443
+ if (!allBranchesSameKey) return void 0;
24444
+ return firstKey;
24298
24445
  }
24299
24446
 
24300
24447
  // src/ir/codegen-overrides.ts
@@ -24899,6 +25046,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24899
25046
  ctx.inListRender = true;
24900
25047
  let callbackExpr = ops.lowerExpression(mapCallback, ctx);
24901
25048
  ctx.inListRender = prevInListRender;
25049
+ const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
25050
+ let deferredCallbackId = null;
25051
+ let deferredCallbackInitId = null;
25052
+ let deferredItemsId = null;
25053
+ if (shouldDeferOptionalCallbackEvaluation) {
25054
+ deferredCallbackId = ops.genTemp(ctx, "mapCb");
25055
+ deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
25056
+ deferredItemsId = ops.genTemp(ctx, "mapItems");
25057
+ }
24902
25058
  const capturedKeyParamName = ctx.listKeyParamName;
24903
25059
  ctx.listKeyExpr = prevListKeyExpr;
24904
25060
  ctx.listItemParamName = prevListItemParamName;
@@ -24998,7 +25154,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
24998
25154
  ],
24999
25155
  keyExprAst
25000
25156
  );
25001
- const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25157
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25002
25158
  if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
25003
25159
  const newParams = [...callbackExpr.params];
25004
25160
  while (newParams.length < 2) {
@@ -25018,25 +25174,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
25018
25174
  }
25019
25175
  }
25020
25176
  statements.push(...hoistedStatements);
25177
+ if (shouldDeferOptionalCallbackEvaluation) {
25178
+ statements.push(
25179
+ t4.variableDeclaration("let", [
25180
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25181
+ ]),
25182
+ t4.variableDeclaration("let", [
25183
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25184
+ ])
25185
+ );
25186
+ }
25187
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25188
+ [],
25189
+ t4.blockStatement([
25190
+ t4.variableDeclaration("const", [
25191
+ t4.variableDeclarator(
25192
+ t4.cloneNode(deferredItemsId, true),
25193
+ t4.cloneNode(arrayExprBase, true)
25194
+ )
25195
+ ]),
25196
+ t4.ifStatement(
25197
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25198
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25199
+ ),
25200
+ t4.ifStatement(
25201
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25202
+ t4.blockStatement([
25203
+ t4.expressionStatement(
25204
+ t4.assignmentExpression(
25205
+ "=",
25206
+ t4.cloneNode(deferredCallbackId, true),
25207
+ t4.cloneNode(callbackExpr, true)
25208
+ )
25209
+ ),
25210
+ t4.expressionStatement(
25211
+ t4.assignmentExpression(
25212
+ "=",
25213
+ t4.cloneNode(deferredCallbackInitId, true),
25214
+ t4.booleanLiteral(true)
25215
+ )
25216
+ )
25217
+ ])
25218
+ ),
25219
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25220
+ ])
25221
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25222
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25223
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25224
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25225
+ t4.identifier("__item"),
25226
+ t4.identifier("__index"),
25227
+ t4.identifier("__key")
25228
+ ])
25229
+ ) : callbackExpr;
25021
25230
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25022
- t4.arrowFunctionExpression([], arrayExpr),
25231
+ getItemsExpr,
25023
25232
  keyFn,
25024
- callbackExpr,
25233
+ renderExpr,
25025
25234
  t4.booleanLiteral(hasIndexParam)
25026
25235
  ]);
25027
25236
  } else {
25028
25237
  statements.push(...hoistedStatements);
25238
+ if (shouldDeferOptionalCallbackEvaluation) {
25239
+ statements.push(
25240
+ t4.variableDeclaration("let", [
25241
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
25242
+ ]),
25243
+ t4.variableDeclaration("let", [
25244
+ t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
25245
+ ])
25246
+ );
25247
+ }
25029
25248
  const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
25030
25249
  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;
25250
+ const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
25251
+ const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25252
+ [],
25253
+ t4.blockStatement([
25254
+ t4.variableDeclaration("const", [
25255
+ t4.variableDeclarator(
25256
+ t4.cloneNode(deferredItemsId, true),
25257
+ t4.cloneNode(arrayExprBase, true)
25258
+ )
25259
+ ]),
25260
+ t4.ifStatement(
25261
+ t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
25262
+ t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
25263
+ ),
25264
+ t4.ifStatement(
25265
+ t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
25266
+ t4.blockStatement([
25267
+ t4.expressionStatement(
25268
+ t4.assignmentExpression(
25269
+ "=",
25270
+ t4.cloneNode(deferredCallbackId, true),
25271
+ t4.cloneNode(callbackExpr, true)
25272
+ )
25273
+ ),
25274
+ t4.expressionStatement(
25275
+ t4.assignmentExpression(
25276
+ "=",
25277
+ t4.cloneNode(deferredCallbackInitId, true),
25278
+ t4.booleanLiteral(true)
25279
+ )
25280
+ )
25281
+ ])
25282
+ ),
25283
+ t4.returnStatement(t4.cloneNode(deferredItemsId, true))
25284
+ ])
25285
+ ) : t4.arrowFunctionExpression([], arrayExpr);
25286
+ const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
25287
+ [t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
25288
+ t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
25289
+ t4.identifier("__item"),
25290
+ t4.identifier("__index"),
25291
+ t4.identifier("__key")
25292
+ ])
25293
+ ) : callbackExpr;
25032
25294
  const keyFn = t4.arrowFunctionExpression(
25033
25295
  [t4.identifier(itemParamName), t4.identifier(indexParamName)],
25034
25296
  t4.identifier(indexParamName)
25035
25297
  );
25036
25298
  listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
25037
- t4.arrowFunctionExpression([], arrayExpr),
25299
+ getItemsExpr,
25038
25300
  keyFn,
25039
- callbackExpr,
25301
+ renderExpr,
25040
25302
  t4.booleanLiteral(hasIndexParam)
25041
25303
  ]);
25042
25304
  }
@@ -33935,6 +34197,506 @@ function getRootIdentifier(expr, t4) {
33935
34197
  return null;
33936
34198
  }
33937
34199
 
34200
+ // src/tooling/analyze.ts
34201
+ var import_core2 = require("@babel/core");
34202
+
34203
+ // src/tooling/trace-infer.ts
34204
+ var TRACE_REGEX_ESCAPES = /[.*+?^${}()|[\]\\]/g;
34205
+ var IDENTIFIER_NAME = /^[A-Za-z_$][\w$]*$/;
34206
+ function isIdentifierName(name) {
34207
+ return IDENTIFIER_NAME.test(name);
34208
+ }
34209
+ function lineContainsIdentifier(lineText, identifier2) {
34210
+ const pattern = new RegExp(`\\b${identifier2.replace(TRACE_REGEX_ESCAPES, "\\$&")}\\b`);
34211
+ return pattern.test(lineText);
34212
+ }
34213
+ function lineContainsAnyIdentifier(lineText, identifiers) {
34214
+ for (const id of identifiers) {
34215
+ if (lineContainsIdentifier(lineText, id)) return true;
34216
+ }
34217
+ return false;
34218
+ }
34219
+ function pushTraceMarker(markersByLine, line, marker) {
34220
+ const markers = markersByLine.get(line);
34221
+ if (!markers) {
34222
+ markersByLine.set(line, [marker]);
34223
+ return;
34224
+ }
34225
+ const duplicate = markers.some(
34226
+ (existing) => existing.kind === marker.kind && existing.label === marker.label
34227
+ );
34228
+ if (!duplicate) markers.push(marker);
34229
+ }
34230
+ function inferReactiveLocalNames(startLine, endLine, sourceLines, baseReactiveNames) {
34231
+ const reactiveNames = new Set(baseReactiveNames);
34232
+ const declarationLines = [];
34233
+ const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?)(?:;)?$/;
34234
+ for (let line = startLine; line <= endLine; line++) {
34235
+ const text = (sourceLines[line - 1] ?? "").trim();
34236
+ if (!text) continue;
34237
+ const withoutComment = text.replace(/\/\/.*$/, "").trim();
34238
+ if (!withoutComment) continue;
34239
+ const match = withoutComment.match(declarationPattern);
34240
+ if (!match) continue;
34241
+ const name = match[1];
34242
+ const expression = match[2];
34243
+ if (!name || !expression) continue;
34244
+ declarationLines.push({ name, expression });
34245
+ }
34246
+ let changed = true;
34247
+ while (changed) {
34248
+ changed = false;
34249
+ for (const declaration of declarationLines) {
34250
+ if (reactiveNames.has(declaration.name)) continue;
34251
+ if (!lineContainsAnyIdentifier(declaration.expression, reactiveNames)) continue;
34252
+ reactiveNames.add(declaration.name);
34253
+ changed = true;
34254
+ }
34255
+ }
34256
+ return reactiveNames;
34257
+ }
34258
+ function isStateCallInstruction(instr) {
34259
+ if (instr.kind !== "Assign") return false;
34260
+ const value = instr.value;
34261
+ return value.kind === "CallExpression" && value.callee.kind === "Identifier" && value.callee.name === "$state";
34262
+ }
34263
+ function collectStateDeclNames(fn) {
34264
+ const result = [];
34265
+ for (const block of fn.blocks) {
34266
+ for (const instr of block.instructions) {
34267
+ if (!isStateCallInstruction(instr) || !instr.loc) continue;
34268
+ const name = deSSAVarName(instr.target.name);
34269
+ if (!isIdentifierName(name)) continue;
34270
+ result.push({ name, line: instr.loc.start.line });
34271
+ }
34272
+ }
34273
+ return result;
34274
+ }
34275
+ function expressionContainsEffectCall(instr) {
34276
+ const value = instr.kind === "Assign" || instr.kind === "Expression" ? instr.value : null;
34277
+ if (!value || !instr.loc) return null;
34278
+ let found = false;
34279
+ walkExpression(value, (expr) => {
34280
+ if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier" && deSSAVarName(expr.callee.name) === "$effect") {
34281
+ found = true;
34282
+ }
34283
+ });
34284
+ return found ? instr.loc.start.line : null;
34285
+ }
34286
+ function flattenRegions2(regions) {
34287
+ if (!regions || regions.length === 0) return [];
34288
+ const result = [];
34289
+ const visit = (region) => {
34290
+ result.push(region);
34291
+ region.children?.forEach((child) => visit(child));
34292
+ };
34293
+ regions.forEach((region) => visit(region));
34294
+ return result;
34295
+ }
34296
+ function findContainingRegion2(line, flatRegions) {
34297
+ let best;
34298
+ for (const region of flatRegions) {
34299
+ if (region.startLine === void 0 || region.endLine === void 0 || line < region.startLine || line > region.endLine) {
34300
+ continue;
34301
+ }
34302
+ const bestSpan = best && best.startLine !== void 0 && best.endLine !== void 0 ? best.endLine - best.startLine : Number.POSITIVE_INFINITY;
34303
+ const span = region.endLine - region.startLine;
34304
+ if (span <= bestSpan) best = region;
34305
+ }
34306
+ return best;
34307
+ }
34308
+ function inferTraceMarkersForComponent(input) {
34309
+ const { fn, sourceLines, startLine, endLine, verbosity, regions } = input;
34310
+ const markersByLine = /* @__PURE__ */ new Map();
34311
+ pushTraceMarker(markersByLine, startLine, {
34312
+ kind: "once",
34313
+ label: "Component setup runs on mount"
34314
+ });
34315
+ const stateDecls = collectStateDeclNames(fn);
34316
+ const reactiveNames = inferReactiveLocalNames(
34317
+ startLine,
34318
+ endLine,
34319
+ sourceLines,
34320
+ stateDecls.map((item) => item.name)
34321
+ );
34322
+ for (const stateDecl of stateDecls) {
34323
+ pushTraceMarker(markersByLine, stateDecl.line, {
34324
+ kind: "once",
34325
+ label: "Signal initialization runs once"
34326
+ });
34327
+ }
34328
+ for (const block of fn.blocks) {
34329
+ for (const instr of block.instructions) {
34330
+ const line = expressionContainsEffectCall(instr);
34331
+ if (!line || line < startLine || line > endLine) continue;
34332
+ pushTraceMarker(markersByLine, line, {
34333
+ kind: "effect",
34334
+ label: "Effect reruns when dependencies change"
34335
+ });
34336
+ }
34337
+ }
34338
+ const flatRegions = flattenRegions2(regions);
34339
+ for (let line = startLine; line <= endLine; line++) {
34340
+ const lineText = sourceLines[line - 1] ?? "";
34341
+ if (!lineText) continue;
34342
+ const hasReactiveName = lineContainsAnyIdentifier(lineText, reactiveNames);
34343
+ if (hasReactiveName && /\{[^}]*\b[A-Za-z_$][\w$]*\b[^}]*\}/.test(lineText)) {
34344
+ pushTraceMarker(markersByLine, line, {
34345
+ kind: "reactive",
34346
+ label: "JSX expression updates with reactive values"
34347
+ });
34348
+ }
34349
+ if (hasReactiveName && /\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText)) {
34350
+ pushTraceMarker(markersByLine, line, {
34351
+ kind: "reactive",
34352
+ label: "Statement reruns when reactive values change"
34353
+ });
34354
+ }
34355
+ if (/\b(?:\$effect|effect)\s*\(/.test(lineText)) {
34356
+ pushTraceMarker(markersByLine, line, {
34357
+ kind: "effect",
34358
+ label: "Effect callback executes reactively"
34359
+ });
34360
+ }
34361
+ if (verbosity === "verbose" && !hasReactiveName && /\{[^}]*\}/.test(lineText)) {
34362
+ pushTraceMarker(markersByLine, line, {
34363
+ kind: "once",
34364
+ label: "JSX expression runs during setup only"
34365
+ });
34366
+ }
34367
+ if (/\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText) && !hasReactiveName && line !== startLine) {
34368
+ pushTraceMarker(markersByLine, line, {
34369
+ kind: "once",
34370
+ label: "Statement runs only during setup"
34371
+ });
34372
+ }
34373
+ const containingRegion = findContainingRegion2(line, flatRegions);
34374
+ if (!containingRegion) continue;
34375
+ const markers = markersByLine.get(line);
34376
+ if (!markers) continue;
34377
+ for (const marker of markers) {
34378
+ marker.regionId = containingRegion.id;
34379
+ marker.deps = containingRegion.dependencies;
34380
+ }
34381
+ }
34382
+ return [...markersByLine.entries()].sort((a, b) => a[0] - b[0]).map(([line, markers]) => ({ line, markers }));
34383
+ }
34384
+
34385
+ // src/tooling/analyze.ts
34386
+ function mergeLoc(a, b) {
34387
+ if (!a) return b ?? null;
34388
+ if (!b) return a;
34389
+ const start = b.start.line < a.start.line || b.start.line === a.start.line && b.start.column < a.start.column ? b.start : a.start;
34390
+ const end = b.end.line > a.end.line || b.end.line === a.end.line && b.end.column > a.end.column ? b.end : a.end;
34391
+ return {
34392
+ start,
34393
+ end,
34394
+ filename: a.filename ?? b.filename,
34395
+ identifierName: a.identifierName ?? b.identifierName
34396
+ };
34397
+ }
34398
+ function expressionContainsMacroCall(expr, macroName) {
34399
+ let found = false;
34400
+ const visit = (value) => {
34401
+ if (found) return;
34402
+ if (value.kind === "CallExpression" && value.callee.kind === "Identifier" && deSSAVarName(value.callee.name) === macroName) {
34403
+ found = true;
34404
+ return;
34405
+ }
34406
+ switch (value.kind) {
34407
+ case "CallExpression":
34408
+ case "OptionalCallExpression":
34409
+ visit(value.callee);
34410
+ value.arguments.forEach((arg) => visit(arg));
34411
+ return;
34412
+ case "MemberExpression":
34413
+ case "OptionalMemberExpression":
34414
+ visit(value.object);
34415
+ if (value.computed) visit(value.property);
34416
+ return;
34417
+ case "BinaryExpression":
34418
+ case "LogicalExpression":
34419
+ visit(value.left);
34420
+ visit(value.right);
34421
+ return;
34422
+ case "UnaryExpression":
34423
+ case "AwaitExpression":
34424
+ case "UpdateExpression":
34425
+ case "SpreadElement":
34426
+ visit(value.argument);
34427
+ return;
34428
+ case "ConditionalExpression":
34429
+ visit(value.test);
34430
+ visit(value.consequent);
34431
+ visit(value.alternate);
34432
+ return;
34433
+ case "ArrayExpression":
34434
+ value.elements.forEach((el) => visit(el));
34435
+ return;
34436
+ case "ObjectExpression":
34437
+ value.properties.forEach((prop) => {
34438
+ if (prop.kind === "SpreadElement") {
34439
+ visit(prop.argument);
34440
+ return;
34441
+ }
34442
+ if (prop.computed) visit(prop.key);
34443
+ visit(prop.value);
34444
+ });
34445
+ return;
34446
+ case "TemplateLiteral":
34447
+ value.expressions.forEach((item) => visit(item));
34448
+ return;
34449
+ case "AssignmentExpression":
34450
+ visit(value.left);
34451
+ visit(value.right);
34452
+ return;
34453
+ case "SequenceExpression":
34454
+ value.expressions.forEach((item) => visit(item));
34455
+ return;
34456
+ case "YieldExpression":
34457
+ if (value.argument) visit(value.argument);
34458
+ return;
34459
+ case "TaggedTemplateExpression":
34460
+ visit(value.tag);
34461
+ value.quasi.expressions.forEach((item) => visit(item));
34462
+ return;
34463
+ case "ImportExpression":
34464
+ visit(value.source);
34465
+ return;
34466
+ case "ArrowFunction":
34467
+ if (Array.isArray(value.body)) {
34468
+ value.body.forEach((block) => {
34469
+ block.instructions.forEach((instr) => {
34470
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34471
+ visit(instr.value);
34472
+ }
34473
+ });
34474
+ });
34475
+ } else {
34476
+ visit(value.body);
34477
+ }
34478
+ return;
34479
+ case "FunctionExpression":
34480
+ value.body.forEach((block) => {
34481
+ block.instructions.forEach((instr) => {
34482
+ if (instr.kind === "Assign" || instr.kind === "Expression") {
34483
+ visit(instr.value);
34484
+ }
34485
+ });
34486
+ });
34487
+ return;
34488
+ case "JSXElement":
34489
+ if (typeof value.tagName !== "string") visit(value.tagName);
34490
+ value.attributes.forEach((attr) => {
34491
+ if (attr.isSpread && attr.spreadExpr) {
34492
+ visit(attr.spreadExpr);
34493
+ } else if (attr.value) {
34494
+ visit(attr.value);
34495
+ }
34496
+ });
34497
+ value.children.forEach((child) => {
34498
+ if (child.kind === "expression") visit(child.value);
34499
+ if (child.kind === "element") visit(child.value);
34500
+ });
34501
+ return;
34502
+ case "Identifier":
34503
+ case "Literal":
34504
+ case "MetaProperty":
34505
+ case "NewExpression":
34506
+ case "ClassExpression":
34507
+ case "ThisExpression":
34508
+ case "SuperExpression":
34509
+ if (value.kind === "NewExpression") {
34510
+ visit(value.callee);
34511
+ value.arguments.forEach((arg) => visit(arg));
34512
+ }
34513
+ if (value.kind === "ClassExpression" && value.superClass) visit(value.superClass);
34514
+ return;
34515
+ default:
34516
+ return;
34517
+ }
34518
+ };
34519
+ visit(expr);
34520
+ return found;
34521
+ }
34522
+ function instructionContainsMacroCall(instruction, macroName) {
34523
+ if (instruction.kind !== "Assign" && instruction.kind !== "Expression") return false;
34524
+ return expressionContainsMacroCall(instruction.value, macroName);
34525
+ }
34526
+ function functionUsesMacro(fn, macroName) {
34527
+ for (const block of fn.blocks) {
34528
+ for (const instruction of block.instructions) {
34529
+ if (instructionContainsMacroCall(instruction, macroName)) return true;
34530
+ }
34531
+ const term = block.terminator;
34532
+ if ("argument" in term && term.argument && expressionContainsMacroCall(term.argument, macroName)) {
34533
+ return true;
34534
+ }
34535
+ if (term.kind === "Branch" && expressionContainsMacroCall(term.test, macroName)) {
34536
+ return true;
34537
+ }
34538
+ if (term.kind === "Switch") {
34539
+ if (expressionContainsMacroCall(term.discriminant, macroName)) return true;
34540
+ if (term.cases.some((entry) => entry.test && expressionContainsMacroCall(entry.test, macroName))) {
34541
+ return true;
34542
+ }
34543
+ }
34544
+ }
34545
+ return false;
34546
+ }
34547
+ function computeRegionLoc(region, fn) {
34548
+ let loc = null;
34549
+ for (const instruction of region.instructions) {
34550
+ loc = mergeLoc(loc, instruction.loc);
34551
+ }
34552
+ for (const blockId of region.blocks) {
34553
+ const block = fn.blocks.find((entry) => entry.id === blockId);
34554
+ if (!block) continue;
34555
+ loc = mergeLoc(loc, block.terminator.loc);
34556
+ }
34557
+ return loc;
34558
+ }
34559
+ function regionToSerializable(region, fn) {
34560
+ const loc = computeRegionLoc(region, fn);
34561
+ return {
34562
+ id: region.id,
34563
+ startLine: loc?.start.line,
34564
+ startColumn: loc?.start.column,
34565
+ endLine: loc?.end.line,
34566
+ endColumn: loc?.end.column,
34567
+ dependencies: [...region.dependencies].map(deSSAVarName),
34568
+ declarations: [...region.declarations].map(deSSAVarName),
34569
+ hasControlFlow: region.hasControlFlow,
34570
+ hasReactiveWrites: region.declarations.size > 0,
34571
+ children: region.children.map((child) => regionToSerializable(child, fn))
34572
+ };
34573
+ }
34574
+ function warningSeverity(code) {
34575
+ const diagnosticCodes = new Set(getAllDiagnosticCodes());
34576
+ if (!diagnosticCodes.has(code)) return "warning" /* Warning */;
34577
+ return getDiagnosticInfo(code).severity;
34578
+ }
34579
+ function normalizeWarningToDiagnostic(warning) {
34580
+ return {
34581
+ code: warning.code,
34582
+ message: warning.message,
34583
+ severity: warningSeverity(warning.code),
34584
+ line: warning.line,
34585
+ column: warning.column
34586
+ };
34587
+ }
34588
+ function normalizeThrownError(error) {
34589
+ const message = error instanceof Error ? error.message : String(error);
34590
+ return {
34591
+ code: "FICT-COMPILE",
34592
+ message,
34593
+ severity: "error" /* Error */,
34594
+ line: 0,
34595
+ column: 0
34596
+ };
34597
+ }
34598
+ function analyzeDiagnostics(code, fileName, options) {
34599
+ const warnings = [];
34600
+ const pluginOptions = {
34601
+ dev: true,
34602
+ filename: fileName,
34603
+ emitModuleMetadata: false,
34604
+ strictGuarantee: false,
34605
+ warningLevels: {
34606
+ ...options.compilerOptions?.warningLevels ?? {},
34607
+ "FICT-R004": "warn"
34608
+ },
34609
+ ...options.compilerOptions,
34610
+ onWarn: (warning) => warnings.push(warning)
34611
+ };
34612
+ try {
34613
+ (0, import_core2.transformSync)(code, {
34614
+ filename: fileName,
34615
+ configFile: false,
34616
+ babelrc: false,
34617
+ sourceType: "module",
34618
+ parserOpts: {
34619
+ sourceType: "module",
34620
+ plugins: ["typescript", "jsx"],
34621
+ allowReturnOutsideFunction: true
34622
+ },
34623
+ plugins: [[index_default, pluginOptions]],
34624
+ generatorOpts: {
34625
+ compact: false
34626
+ }
34627
+ });
34628
+ } catch (error) {
34629
+ return [...warnings.map(normalizeWarningToDiagnostic), normalizeThrownError(error)];
34630
+ }
34631
+ return warnings.map(normalizeWarningToDiagnostic);
34632
+ }
34633
+ function shouldIncludeFunction(fn) {
34634
+ return functionContainsJSX(fn) || functionUsesMacro(fn, "$state") || functionUsesMacro(fn, "$effect");
34635
+ }
34636
+ function parseFileAst(code, fileName) {
34637
+ const ast = (0, import_core2.parseSync)(code, {
34638
+ filename: fileName,
34639
+ sourceType: "module",
34640
+ parserOpts: {
34641
+ sourceType: "module",
34642
+ plugins: ["typescript", "jsx"],
34643
+ allowReturnOutsideFunction: true
34644
+ }
34645
+ });
34646
+ if (!ast || ast.type !== "File") {
34647
+ throw new Error("Failed to parse source file for Fict analysis.");
34648
+ }
34649
+ return ast;
34650
+ }
34651
+ function analyzeFictFile(code, fileName, options = {}) {
34652
+ const includeRegions = options.includeRegions ?? true;
34653
+ const includeDiagnostics = options.includeDiagnostics ?? true;
34654
+ const verbosity = options.verbosity ?? "minimal";
34655
+ const ast = parseFileAst(code, fileName);
34656
+ const hir = buildHIR(
34657
+ ast,
34658
+ {
34659
+ state: /* @__PURE__ */ new Set(["$state"]),
34660
+ effect: /* @__PURE__ */ new Set(["$effect"])
34661
+ },
34662
+ {
34663
+ dev: true,
34664
+ fileName
34665
+ }
34666
+ );
34667
+ const sourceLines = code.split(/\r?\n/);
34668
+ const components = [];
34669
+ for (const fn of hir.functions) {
34670
+ if (!fn.loc || !shouldIncludeFunction(fn)) continue;
34671
+ const startLine = fn.loc.start.line;
34672
+ const endLine = fn.loc.end.line;
34673
+ const scopeResult = analyzeReactiveScopesWithSSA(fn);
34674
+ const regionResult = generateRegions(fn, scopeResult);
34675
+ const regions = includeRegions ? regionResult.topLevelRegions.map((region) => regionToSerializable(region, fn)) : void 0;
34676
+ const trace = inferTraceMarkersForComponent({
34677
+ fn,
34678
+ sourceLines,
34679
+ startLine,
34680
+ endLine,
34681
+ verbosity,
34682
+ regions
34683
+ });
34684
+ components.push({
34685
+ name: fn.name ?? "<anonymous>",
34686
+ startLine,
34687
+ endLine,
34688
+ trace,
34689
+ regions
34690
+ });
34691
+ }
34692
+ const diagnostics = includeDiagnostics ? analyzeDiagnostics(code, fileName, options) : [];
34693
+ return {
34694
+ fileName,
34695
+ components,
34696
+ diagnostics
34697
+ };
34698
+ }
34699
+
33938
34700
  // src/index.ts
33939
34701
  function stripMacroImports(path2, t4) {
33940
34702
  path2.traverse({
@@ -34828,46 +35590,77 @@ function createHIREntrypointVisitor(t4, options) {
34828
35590
  path2.traverse({
34829
35591
  JSXExpressionContainer(exprPath) {
34830
35592
  const expr = exprPath.node.expression;
34831
- if (!t4.isCallExpression(expr)) return;
34832
- 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" })) {
34833
35595
  return;
34834
35596
  }
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
- });
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()) {
34857
35602
  return;
34858
35603
  }
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;
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;
34865
35639
  }
34866
- if (t4.isJSXSpreadAttribute(attr)) {
34867
- 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
+ }
34868
35660
  }
34869
- }
34870
- if (hasKey || hasUnknownSpread) return;
35661
+ return !hasKey;
35662
+ });
35663
+ if (!hasMissingKeyBranch) return;
34871
35664
  warn({
34872
35665
  code: "FICT-J002",
34873
35666
  message: "Missing key prop in list rendering.",
@@ -35430,8 +36223,10 @@ var createFictPlugin = (0, import_helper_plugin_utils.declare)(
35430
36223
  var index_default = createFictPlugin;
35431
36224
  // Annotate the CommonJS export names for ESM import in node:
35432
36225
  0 && (module.exports = {
36226
+ analyzeFictFile,
35433
36227
  clearModuleMetadata,
35434
36228
  createFictPlugin,
36229
+ inferTraceMarkersForComponent,
35435
36230
  resolveModuleMetadata,
35436
36231
  setModuleMetadata
35437
36232
  });