@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 +847 -52
- package/dist/index.d.cts +393 -1
- package/dist/index.d.ts +393 -1
- package/dist/index.js +845 -52
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3059,7 +3059,7 @@ var require_identifier = __commonJS({
|
|
|
3059
3059
|
value: true
|
|
3060
3060
|
});
|
|
3061
3061
|
exports.isIdentifierChar = isIdentifierChar;
|
|
3062
|
-
exports.isIdentifierName =
|
|
3062
|
+
exports.isIdentifierName = isIdentifierName2;
|
|
3063
3063
|
exports.isIdentifierStart = isIdentifierStart;
|
|
3064
3064
|
var nonASCIIidentifierStartChars = "\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
|
|
3065
3065
|
var nonASCIIidentifierChars = "\xB7\u0300-\u036F\u0387\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u0897-\u089F\u08CA-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B55-\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C04\u0C3C\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0CF3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D81-\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EBC\u0EC8-\u0ECE\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u180F-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19D0-\u19DA\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1ABF-\u1ADD\u1AE0-\u1AEB\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\u30FB\uA620-\uA629\uA66F\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA82C\uA880\uA881\uA8B4-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F1\uA8FF-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F\uFF65";
|
|
@@ -3100,7 +3100,7 @@ var require_identifier = __commonJS({
|
|
|
3100
3100
|
}
|
|
3101
3101
|
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
|
|
3102
3102
|
}
|
|
3103
|
-
function
|
|
3103
|
+
function isIdentifierName2(name) {
|
|
3104
3104
|
let isFirst = true;
|
|
3105
3105
|
for (let i = 0; i < name.length; i++) {
|
|
3106
3106
|
let cp = name.charCodeAt(i);
|
|
@@ -17073,6 +17073,37 @@ function clearModuleMetadata(options) {
|
|
|
17073
17073
|
}
|
|
17074
17074
|
|
|
17075
17075
|
// src/validation.ts
|
|
17076
|
+
var DiagnosticCode = /* @__PURE__ */ ((DiagnosticCode2) => {
|
|
17077
|
+
DiagnosticCode2["FICT_P001"] = "FICT-P001";
|
|
17078
|
+
DiagnosticCode2["FICT_P002"] = "FICT-P002";
|
|
17079
|
+
DiagnosticCode2["FICT_P003"] = "FICT-P003";
|
|
17080
|
+
DiagnosticCode2["FICT_P004"] = "FICT-P004";
|
|
17081
|
+
DiagnosticCode2["FICT_P005"] = "FICT-P005";
|
|
17082
|
+
DiagnosticCode2["FICT_S001"] = "FICT-S001";
|
|
17083
|
+
DiagnosticCode2["FICT_S002"] = "FICT-S002";
|
|
17084
|
+
DiagnosticCode2["FICT_E001"] = "FICT-E001";
|
|
17085
|
+
DiagnosticCode2["FICT_E002"] = "FICT-E002";
|
|
17086
|
+
DiagnosticCode2["FICT_E003"] = "FICT-E003";
|
|
17087
|
+
DiagnosticCode2["FICT_M001"] = "FICT-M001";
|
|
17088
|
+
DiagnosticCode2["FICT_M002"] = "FICT-M002";
|
|
17089
|
+
DiagnosticCode2["FICT_M003"] = "FICT-M003";
|
|
17090
|
+
DiagnosticCode2["FICT_C001"] = "FICT-C001";
|
|
17091
|
+
DiagnosticCode2["FICT_C002"] = "FICT-C002";
|
|
17092
|
+
DiagnosticCode2["FICT_C003"] = "FICT-C003";
|
|
17093
|
+
DiagnosticCode2["FICT_C004"] = "FICT-C004";
|
|
17094
|
+
DiagnosticCode2["FICT_J001"] = "FICT-J001";
|
|
17095
|
+
DiagnosticCode2["FICT_J002"] = "FICT-J002";
|
|
17096
|
+
DiagnosticCode2["FICT_J003"] = "FICT-J003";
|
|
17097
|
+
DiagnosticCode2["FICT_R001"] = "FICT-R001";
|
|
17098
|
+
DiagnosticCode2["FICT_R002"] = "FICT-R002";
|
|
17099
|
+
DiagnosticCode2["FICT_R003"] = "FICT-R003";
|
|
17100
|
+
DiagnosticCode2["FICT_R004"] = "FICT-R004";
|
|
17101
|
+
DiagnosticCode2["FICT_R005"] = "FICT-R005";
|
|
17102
|
+
DiagnosticCode2["FICT_X001"] = "FICT-X001";
|
|
17103
|
+
DiagnosticCode2["FICT_X002"] = "FICT-X002";
|
|
17104
|
+
DiagnosticCode2["FICT_X003"] = "FICT-X003";
|
|
17105
|
+
return DiagnosticCode2;
|
|
17106
|
+
})(DiagnosticCode || {});
|
|
17076
17107
|
var DiagnosticMessages = {
|
|
17077
17108
|
["FICT-P001" /* FICT_P001 */]: "Props destructuring falls back to non-reactive binding.",
|
|
17078
17109
|
["FICT-P002" /* FICT_P002 */]: "Array rest in props destructuring falls back to non-reactive binding.",
|
|
@@ -17160,6 +17191,16 @@ function reportDiagnostic(ctx, code, node, context) {
|
|
|
17160
17191
|
});
|
|
17161
17192
|
}
|
|
17162
17193
|
}
|
|
17194
|
+
function getAllDiagnosticCodes() {
|
|
17195
|
+
return Object.values(DiagnosticCode);
|
|
17196
|
+
}
|
|
17197
|
+
function getDiagnosticInfo(code) {
|
|
17198
|
+
return {
|
|
17199
|
+
code,
|
|
17200
|
+
severity: DiagnosticSeverities[code],
|
|
17201
|
+
message: DiagnosticMessages[code]
|
|
17202
|
+
};
|
|
17203
|
+
}
|
|
17163
17204
|
|
|
17164
17205
|
// src/ir/ssa.ts
|
|
17165
17206
|
function enterSSA(program) {
|
|
@@ -24252,34 +24293,138 @@ function extractKeyFromAttributes(attributes) {
|
|
|
24252
24293
|
}
|
|
24253
24294
|
return void 0;
|
|
24254
24295
|
}
|
|
24296
|
+
function collectReturnedJSXFromExpression(expression, returned) {
|
|
24297
|
+
if (expression.kind === "JSXElement") {
|
|
24298
|
+
returned.push(expression);
|
|
24299
|
+
return;
|
|
24300
|
+
}
|
|
24301
|
+
if (expression.kind === "ConditionalExpression") {
|
|
24302
|
+
collectReturnedJSXFromExpression(expression.consequent, returned);
|
|
24303
|
+
collectReturnedJSXFromExpression(expression.alternate, returned);
|
|
24304
|
+
return;
|
|
24305
|
+
}
|
|
24306
|
+
if (expression.kind === "LogicalExpression") {
|
|
24307
|
+
collectReturnedJSXFromExpression(expression.left, returned);
|
|
24308
|
+
collectReturnedJSXFromExpression(expression.right, returned);
|
|
24309
|
+
return;
|
|
24310
|
+
}
|
|
24311
|
+
if (expression.kind === "SequenceExpression") {
|
|
24312
|
+
const tail = expression.expressions[expression.expressions.length - 1];
|
|
24313
|
+
if (tail) collectReturnedJSXFromExpression(tail, returned);
|
|
24314
|
+
}
|
|
24315
|
+
}
|
|
24316
|
+
function extractKeyExpressionFromReturnedExpression(expression) {
|
|
24317
|
+
if (expression.kind === "JSXElement") {
|
|
24318
|
+
return extractKeyFromAttributes(expression.attributes);
|
|
24319
|
+
}
|
|
24320
|
+
if (expression.kind === "ConditionalExpression") {
|
|
24321
|
+
const consequentKey = extractKeyExpressionFromReturnedExpression(expression.consequent);
|
|
24322
|
+
const alternateKey = extractKeyExpressionFromReturnedExpression(expression.alternate);
|
|
24323
|
+
if (!consequentKey || !alternateKey) return void 0;
|
|
24324
|
+
return {
|
|
24325
|
+
kind: "ConditionalExpression",
|
|
24326
|
+
test: expression.test,
|
|
24327
|
+
consequent: consequentKey,
|
|
24328
|
+
alternate: alternateKey,
|
|
24329
|
+
loc: expression.loc
|
|
24330
|
+
};
|
|
24331
|
+
}
|
|
24332
|
+
if (expression.kind === "SequenceExpression") {
|
|
24333
|
+
const tail = expression.expressions[expression.expressions.length - 1];
|
|
24334
|
+
return tail ? extractKeyExpressionFromReturnedExpression(tail) : void 0;
|
|
24335
|
+
}
|
|
24336
|
+
return void 0;
|
|
24337
|
+
}
|
|
24338
|
+
function getReturnedKeyExpressionsFromCallback(callback) {
|
|
24339
|
+
const returned = [];
|
|
24340
|
+
if (callback.kind === "ArrowFunction") {
|
|
24341
|
+
if (callback.isExpression && !Array.isArray(callback.body)) {
|
|
24342
|
+
const keyExpr = extractKeyExpressionFromReturnedExpression(callback.body);
|
|
24343
|
+
if (keyExpr) returned.push(keyExpr);
|
|
24344
|
+
return returned;
|
|
24345
|
+
}
|
|
24346
|
+
if (Array.isArray(callback.body)) {
|
|
24347
|
+
for (const block of callback.body) {
|
|
24348
|
+
const term = block.terminator;
|
|
24349
|
+
if (term.kind !== "Return" || !term.argument) continue;
|
|
24350
|
+
const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
|
|
24351
|
+
if (keyExpr) returned.push(keyExpr);
|
|
24352
|
+
}
|
|
24353
|
+
}
|
|
24354
|
+
return returned;
|
|
24355
|
+
}
|
|
24356
|
+
if (callback.kind === "FunctionExpression") {
|
|
24357
|
+
for (const block of callback.body ?? []) {
|
|
24358
|
+
const term = block.terminator;
|
|
24359
|
+
if (term.kind !== "Return" || !term.argument) continue;
|
|
24360
|
+
const keyExpr = extractKeyExpressionFromReturnedExpression(term.argument);
|
|
24361
|
+
if (keyExpr) returned.push(keyExpr);
|
|
24362
|
+
}
|
|
24363
|
+
}
|
|
24364
|
+
return returned;
|
|
24365
|
+
}
|
|
24255
24366
|
function getReturnedJSXFromCallback(callback) {
|
|
24367
|
+
const returned = [];
|
|
24256
24368
|
if (callback.kind === "ArrowFunction") {
|
|
24257
|
-
if (callback.isExpression && !Array.isArray(callback.body)
|
|
24258
|
-
|
|
24369
|
+
if (callback.isExpression && !Array.isArray(callback.body)) {
|
|
24370
|
+
collectReturnedJSXFromExpression(callback.body, returned);
|
|
24371
|
+
return returned;
|
|
24259
24372
|
}
|
|
24260
24373
|
if (Array.isArray(callback.body)) {
|
|
24261
24374
|
for (const block of callback.body) {
|
|
24262
24375
|
const term = block.terminator;
|
|
24263
|
-
if (term.kind === "Return" && term.argument
|
|
24264
|
-
|
|
24376
|
+
if (term.kind === "Return" && term.argument) {
|
|
24377
|
+
collectReturnedJSXFromExpression(term.argument, returned);
|
|
24265
24378
|
}
|
|
24266
24379
|
}
|
|
24267
24380
|
}
|
|
24381
|
+
return returned;
|
|
24268
24382
|
}
|
|
24269
24383
|
if (callback.kind === "FunctionExpression") {
|
|
24270
24384
|
for (const block of callback.body ?? []) {
|
|
24271
24385
|
const term = block.terminator;
|
|
24272
|
-
if (term.kind === "Return" && term.argument
|
|
24273
|
-
|
|
24386
|
+
if (term.kind === "Return" && term.argument) {
|
|
24387
|
+
collectReturnedJSXFromExpression(term.argument, returned);
|
|
24274
24388
|
}
|
|
24275
24389
|
}
|
|
24276
24390
|
}
|
|
24277
|
-
return
|
|
24391
|
+
return returned;
|
|
24392
|
+
}
|
|
24393
|
+
function keyExpressionSignature(expression) {
|
|
24394
|
+
try {
|
|
24395
|
+
return JSON.stringify(expression, (key, value) => {
|
|
24396
|
+
if (key === "loc") return void 0;
|
|
24397
|
+
if (typeof value === "bigint") return `__bigint:${value.toString()}`;
|
|
24398
|
+
return value;
|
|
24399
|
+
}) ?? "";
|
|
24400
|
+
} catch {
|
|
24401
|
+
return "";
|
|
24402
|
+
}
|
|
24278
24403
|
}
|
|
24279
24404
|
function extractKeyFromMapCallback(callback) {
|
|
24280
|
-
const
|
|
24281
|
-
if (
|
|
24282
|
-
|
|
24405
|
+
const returnedKeyExpressions = getReturnedKeyExpressionsFromCallback(callback);
|
|
24406
|
+
if (returnedKeyExpressions.length === 1) {
|
|
24407
|
+
return returnedKeyExpressions[0];
|
|
24408
|
+
}
|
|
24409
|
+
if (returnedKeyExpressions.length > 1) {
|
|
24410
|
+
const [firstKey2, ...restKeys2] = returnedKeyExpressions;
|
|
24411
|
+
const firstSignature2 = keyExpressionSignature(firstKey2);
|
|
24412
|
+
if (firstSignature2 && restKeys2.every((keyExpr) => keyExpressionSignature(keyExpr) === firstSignature2)) {
|
|
24413
|
+
return firstKey2;
|
|
24414
|
+
}
|
|
24415
|
+
}
|
|
24416
|
+
const returned = getReturnedJSXFromCallback(callback);
|
|
24417
|
+
if (returned.length === 0) return void 0;
|
|
24418
|
+
const keyExpressions = returned.map((jsx) => extractKeyFromAttributes(jsx.attributes));
|
|
24419
|
+
if (keyExpressions.some((expr) => !expr)) return void 0;
|
|
24420
|
+
const [firstKey, ...restKeys] = keyExpressions;
|
|
24421
|
+
const firstSignature = keyExpressionSignature(firstKey);
|
|
24422
|
+
if (!firstSignature) return void 0;
|
|
24423
|
+
const allBranchesSameKey = restKeys.every(
|
|
24424
|
+
(keyExpr) => keyExpressionSignature(keyExpr) === firstSignature
|
|
24425
|
+
);
|
|
24426
|
+
if (!allBranchesSameKey) return void 0;
|
|
24427
|
+
return firstKey;
|
|
24283
24428
|
}
|
|
24284
24429
|
|
|
24285
24430
|
// src/ir/codegen-overrides.ts
|
|
@@ -24884,6 +25029,15 @@ function buildListCallExpression(expr, statements, ctx, ops) {
|
|
|
24884
25029
|
ctx.inListRender = true;
|
|
24885
25030
|
let callbackExpr = ops.lowerExpression(mapCallback, ctx);
|
|
24886
25031
|
ctx.inListRender = prevInListRender;
|
|
25032
|
+
const shouldDeferOptionalCallbackEvaluation = isOptional && !t4.isArrowFunctionExpression(callbackExpr) && !t4.isFunctionExpression(callbackExpr);
|
|
25033
|
+
let deferredCallbackId = null;
|
|
25034
|
+
let deferredCallbackInitId = null;
|
|
25035
|
+
let deferredItemsId = null;
|
|
25036
|
+
if (shouldDeferOptionalCallbackEvaluation) {
|
|
25037
|
+
deferredCallbackId = ops.genTemp(ctx, "mapCb");
|
|
25038
|
+
deferredCallbackInitId = ops.genTemp(ctx, "mapCbReady");
|
|
25039
|
+
deferredItemsId = ops.genTemp(ctx, "mapItems");
|
|
25040
|
+
}
|
|
24887
25041
|
const capturedKeyParamName = ctx.listKeyParamName;
|
|
24888
25042
|
ctx.listKeyExpr = prevListKeyExpr;
|
|
24889
25043
|
ctx.listItemParamName = prevListItemParamName;
|
|
@@ -24983,7 +25137,7 @@ function buildListCallExpression(expr, statements, ctx, ops) {
|
|
|
24983
25137
|
],
|
|
24984
25138
|
keyExprAst
|
|
24985
25139
|
);
|
|
24986
|
-
const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
|
|
25140
|
+
const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
|
|
24987
25141
|
if (canConstifyKey && (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr))) {
|
|
24988
25142
|
const newParams = [...callbackExpr.params];
|
|
24989
25143
|
while (newParams.length < 2) {
|
|
@@ -25003,25 +25157,131 @@ function buildListCallExpression(expr, statements, ctx, ops) {
|
|
|
25003
25157
|
}
|
|
25004
25158
|
}
|
|
25005
25159
|
statements.push(...hoistedStatements);
|
|
25160
|
+
if (shouldDeferOptionalCallbackEvaluation) {
|
|
25161
|
+
statements.push(
|
|
25162
|
+
t4.variableDeclaration("let", [
|
|
25163
|
+
t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
|
|
25164
|
+
]),
|
|
25165
|
+
t4.variableDeclaration("let", [
|
|
25166
|
+
t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
|
|
25167
|
+
])
|
|
25168
|
+
);
|
|
25169
|
+
}
|
|
25170
|
+
const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
|
|
25171
|
+
[],
|
|
25172
|
+
t4.blockStatement([
|
|
25173
|
+
t4.variableDeclaration("const", [
|
|
25174
|
+
t4.variableDeclarator(
|
|
25175
|
+
t4.cloneNode(deferredItemsId, true),
|
|
25176
|
+
t4.cloneNode(arrayExprBase, true)
|
|
25177
|
+
)
|
|
25178
|
+
]),
|
|
25179
|
+
t4.ifStatement(
|
|
25180
|
+
t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
|
|
25181
|
+
t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
|
|
25182
|
+
),
|
|
25183
|
+
t4.ifStatement(
|
|
25184
|
+
t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
|
|
25185
|
+
t4.blockStatement([
|
|
25186
|
+
t4.expressionStatement(
|
|
25187
|
+
t4.assignmentExpression(
|
|
25188
|
+
"=",
|
|
25189
|
+
t4.cloneNode(deferredCallbackId, true),
|
|
25190
|
+
t4.cloneNode(callbackExpr, true)
|
|
25191
|
+
)
|
|
25192
|
+
),
|
|
25193
|
+
t4.expressionStatement(
|
|
25194
|
+
t4.assignmentExpression(
|
|
25195
|
+
"=",
|
|
25196
|
+
t4.cloneNode(deferredCallbackInitId, true),
|
|
25197
|
+
t4.booleanLiteral(true)
|
|
25198
|
+
)
|
|
25199
|
+
)
|
|
25200
|
+
])
|
|
25201
|
+
),
|
|
25202
|
+
t4.returnStatement(t4.cloneNode(deferredItemsId, true))
|
|
25203
|
+
])
|
|
25204
|
+
) : t4.arrowFunctionExpression([], arrayExpr);
|
|
25205
|
+
const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
|
|
25206
|
+
[t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
|
|
25207
|
+
t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
|
|
25208
|
+
t4.identifier("__item"),
|
|
25209
|
+
t4.identifier("__index"),
|
|
25210
|
+
t4.identifier("__key")
|
|
25211
|
+
])
|
|
25212
|
+
) : callbackExpr;
|
|
25006
25213
|
listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
|
|
25007
|
-
|
|
25214
|
+
getItemsExpr,
|
|
25008
25215
|
keyFn,
|
|
25009
|
-
|
|
25216
|
+
renderExpr,
|
|
25010
25217
|
t4.booleanLiteral(hasIndexParam)
|
|
25011
25218
|
]);
|
|
25012
25219
|
} else {
|
|
25013
25220
|
statements.push(...hoistedStatements);
|
|
25221
|
+
if (shouldDeferOptionalCallbackEvaluation) {
|
|
25222
|
+
statements.push(
|
|
25223
|
+
t4.variableDeclaration("let", [
|
|
25224
|
+
t4.variableDeclarator(t4.cloneNode(deferredCallbackId, true))
|
|
25225
|
+
]),
|
|
25226
|
+
t4.variableDeclaration("let", [
|
|
25227
|
+
t4.variableDeclarator(t4.cloneNode(deferredCallbackInitId, true), t4.booleanLiteral(false))
|
|
25228
|
+
])
|
|
25229
|
+
);
|
|
25230
|
+
}
|
|
25014
25231
|
const itemParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[0]) ? callbackExpr.params[0].name : "__item" : "__item";
|
|
25015
25232
|
const indexParamName = t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr) ? t4.isIdentifier(callbackExpr.params[1]) ? callbackExpr.params[1].name : "__index" : "__index";
|
|
25016
|
-
const hasIndexParam = (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
|
|
25233
|
+
const hasIndexParam = shouldDeferOptionalCallbackEvaluation || (t4.isArrowFunctionExpression(callbackExpr) || t4.isFunctionExpression(callbackExpr)) && callbackExpr.params.length >= 2;
|
|
25234
|
+
const getItemsExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
|
|
25235
|
+
[],
|
|
25236
|
+
t4.blockStatement([
|
|
25237
|
+
t4.variableDeclaration("const", [
|
|
25238
|
+
t4.variableDeclarator(
|
|
25239
|
+
t4.cloneNode(deferredItemsId, true),
|
|
25240
|
+
t4.cloneNode(arrayExprBase, true)
|
|
25241
|
+
)
|
|
25242
|
+
]),
|
|
25243
|
+
t4.ifStatement(
|
|
25244
|
+
t4.binaryExpression("==", t4.cloneNode(deferredItemsId, true), t4.nullLiteral()),
|
|
25245
|
+
t4.blockStatement([t4.returnStatement(t4.arrayExpression([]))])
|
|
25246
|
+
),
|
|
25247
|
+
t4.ifStatement(
|
|
25248
|
+
t4.unaryExpression("!", t4.cloneNode(deferredCallbackInitId, true)),
|
|
25249
|
+
t4.blockStatement([
|
|
25250
|
+
t4.expressionStatement(
|
|
25251
|
+
t4.assignmentExpression(
|
|
25252
|
+
"=",
|
|
25253
|
+
t4.cloneNode(deferredCallbackId, true),
|
|
25254
|
+
t4.cloneNode(callbackExpr, true)
|
|
25255
|
+
)
|
|
25256
|
+
),
|
|
25257
|
+
t4.expressionStatement(
|
|
25258
|
+
t4.assignmentExpression(
|
|
25259
|
+
"=",
|
|
25260
|
+
t4.cloneNode(deferredCallbackInitId, true),
|
|
25261
|
+
t4.booleanLiteral(true)
|
|
25262
|
+
)
|
|
25263
|
+
)
|
|
25264
|
+
])
|
|
25265
|
+
),
|
|
25266
|
+
t4.returnStatement(t4.cloneNode(deferredItemsId, true))
|
|
25267
|
+
])
|
|
25268
|
+
) : t4.arrowFunctionExpression([], arrayExpr);
|
|
25269
|
+
const renderExpr = shouldDeferOptionalCallbackEvaluation ? t4.arrowFunctionExpression(
|
|
25270
|
+
[t4.identifier("__item"), t4.identifier("__index"), t4.identifier("__key")],
|
|
25271
|
+
t4.callExpression(t4.cloneNode(deferredCallbackId, true), [
|
|
25272
|
+
t4.identifier("__item"),
|
|
25273
|
+
t4.identifier("__index"),
|
|
25274
|
+
t4.identifier("__key")
|
|
25275
|
+
])
|
|
25276
|
+
) : callbackExpr;
|
|
25017
25277
|
const keyFn = t4.arrowFunctionExpression(
|
|
25018
25278
|
[t4.identifier(itemParamName), t4.identifier(indexParamName)],
|
|
25019
25279
|
t4.identifier(indexParamName)
|
|
25020
25280
|
);
|
|
25021
25281
|
listCall = t4.callExpression(t4.identifier(RUNTIME_ALIASES.keyedList), [
|
|
25022
|
-
|
|
25282
|
+
getItemsExpr,
|
|
25023
25283
|
keyFn,
|
|
25024
|
-
|
|
25284
|
+
renderExpr,
|
|
25025
25285
|
t4.booleanLiteral(hasIndexParam)
|
|
25026
25286
|
]);
|
|
25027
25287
|
}
|
|
@@ -33920,6 +34180,506 @@ function getRootIdentifier(expr, t4) {
|
|
|
33920
34180
|
return null;
|
|
33921
34181
|
}
|
|
33922
34182
|
|
|
34183
|
+
// src/tooling/analyze.ts
|
|
34184
|
+
import { parseSync, transformSync } from "@babel/core";
|
|
34185
|
+
|
|
34186
|
+
// src/tooling/trace-infer.ts
|
|
34187
|
+
var TRACE_REGEX_ESCAPES = /[.*+?^${}()|[\]\\]/g;
|
|
34188
|
+
var IDENTIFIER_NAME = /^[A-Za-z_$][\w$]*$/;
|
|
34189
|
+
function isIdentifierName(name) {
|
|
34190
|
+
return IDENTIFIER_NAME.test(name);
|
|
34191
|
+
}
|
|
34192
|
+
function lineContainsIdentifier(lineText, identifier2) {
|
|
34193
|
+
const pattern = new RegExp(`\\b${identifier2.replace(TRACE_REGEX_ESCAPES, "\\$&")}\\b`);
|
|
34194
|
+
return pattern.test(lineText);
|
|
34195
|
+
}
|
|
34196
|
+
function lineContainsAnyIdentifier(lineText, identifiers) {
|
|
34197
|
+
for (const id of identifiers) {
|
|
34198
|
+
if (lineContainsIdentifier(lineText, id)) return true;
|
|
34199
|
+
}
|
|
34200
|
+
return false;
|
|
34201
|
+
}
|
|
34202
|
+
function pushTraceMarker(markersByLine, line, marker) {
|
|
34203
|
+
const markers = markersByLine.get(line);
|
|
34204
|
+
if (!markers) {
|
|
34205
|
+
markersByLine.set(line, [marker]);
|
|
34206
|
+
return;
|
|
34207
|
+
}
|
|
34208
|
+
const duplicate = markers.some(
|
|
34209
|
+
(existing) => existing.kind === marker.kind && existing.label === marker.label
|
|
34210
|
+
);
|
|
34211
|
+
if (!duplicate) markers.push(marker);
|
|
34212
|
+
}
|
|
34213
|
+
function inferReactiveLocalNames(startLine, endLine, sourceLines, baseReactiveNames) {
|
|
34214
|
+
const reactiveNames = new Set(baseReactiveNames);
|
|
34215
|
+
const declarationLines = [];
|
|
34216
|
+
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?)(?:;)?$/;
|
|
34217
|
+
for (let line = startLine; line <= endLine; line++) {
|
|
34218
|
+
const text = (sourceLines[line - 1] ?? "").trim();
|
|
34219
|
+
if (!text) continue;
|
|
34220
|
+
const withoutComment = text.replace(/\/\/.*$/, "").trim();
|
|
34221
|
+
if (!withoutComment) continue;
|
|
34222
|
+
const match = withoutComment.match(declarationPattern);
|
|
34223
|
+
if (!match) continue;
|
|
34224
|
+
const name = match[1];
|
|
34225
|
+
const expression = match[2];
|
|
34226
|
+
if (!name || !expression) continue;
|
|
34227
|
+
declarationLines.push({ name, expression });
|
|
34228
|
+
}
|
|
34229
|
+
let changed = true;
|
|
34230
|
+
while (changed) {
|
|
34231
|
+
changed = false;
|
|
34232
|
+
for (const declaration of declarationLines) {
|
|
34233
|
+
if (reactiveNames.has(declaration.name)) continue;
|
|
34234
|
+
if (!lineContainsAnyIdentifier(declaration.expression, reactiveNames)) continue;
|
|
34235
|
+
reactiveNames.add(declaration.name);
|
|
34236
|
+
changed = true;
|
|
34237
|
+
}
|
|
34238
|
+
}
|
|
34239
|
+
return reactiveNames;
|
|
34240
|
+
}
|
|
34241
|
+
function isStateCallInstruction(instr) {
|
|
34242
|
+
if (instr.kind !== "Assign") return false;
|
|
34243
|
+
const value = instr.value;
|
|
34244
|
+
return value.kind === "CallExpression" && value.callee.kind === "Identifier" && value.callee.name === "$state";
|
|
34245
|
+
}
|
|
34246
|
+
function collectStateDeclNames(fn) {
|
|
34247
|
+
const result = [];
|
|
34248
|
+
for (const block of fn.blocks) {
|
|
34249
|
+
for (const instr of block.instructions) {
|
|
34250
|
+
if (!isStateCallInstruction(instr) || !instr.loc) continue;
|
|
34251
|
+
const name = deSSAVarName(instr.target.name);
|
|
34252
|
+
if (!isIdentifierName(name)) continue;
|
|
34253
|
+
result.push({ name, line: instr.loc.start.line });
|
|
34254
|
+
}
|
|
34255
|
+
}
|
|
34256
|
+
return result;
|
|
34257
|
+
}
|
|
34258
|
+
function expressionContainsEffectCall(instr) {
|
|
34259
|
+
const value = instr.kind === "Assign" || instr.kind === "Expression" ? instr.value : null;
|
|
34260
|
+
if (!value || !instr.loc) return null;
|
|
34261
|
+
let found = false;
|
|
34262
|
+
walkExpression(value, (expr) => {
|
|
34263
|
+
if (expr.kind === "CallExpression" && expr.callee.kind === "Identifier" && deSSAVarName(expr.callee.name) === "$effect") {
|
|
34264
|
+
found = true;
|
|
34265
|
+
}
|
|
34266
|
+
});
|
|
34267
|
+
return found ? instr.loc.start.line : null;
|
|
34268
|
+
}
|
|
34269
|
+
function flattenRegions2(regions) {
|
|
34270
|
+
if (!regions || regions.length === 0) return [];
|
|
34271
|
+
const result = [];
|
|
34272
|
+
const visit = (region) => {
|
|
34273
|
+
result.push(region);
|
|
34274
|
+
region.children?.forEach((child) => visit(child));
|
|
34275
|
+
};
|
|
34276
|
+
regions.forEach((region) => visit(region));
|
|
34277
|
+
return result;
|
|
34278
|
+
}
|
|
34279
|
+
function findContainingRegion2(line, flatRegions) {
|
|
34280
|
+
let best;
|
|
34281
|
+
for (const region of flatRegions) {
|
|
34282
|
+
if (region.startLine === void 0 || region.endLine === void 0 || line < region.startLine || line > region.endLine) {
|
|
34283
|
+
continue;
|
|
34284
|
+
}
|
|
34285
|
+
const bestSpan = best && best.startLine !== void 0 && best.endLine !== void 0 ? best.endLine - best.startLine : Number.POSITIVE_INFINITY;
|
|
34286
|
+
const span = region.endLine - region.startLine;
|
|
34287
|
+
if (span <= bestSpan) best = region;
|
|
34288
|
+
}
|
|
34289
|
+
return best;
|
|
34290
|
+
}
|
|
34291
|
+
function inferTraceMarkersForComponent(input) {
|
|
34292
|
+
const { fn, sourceLines, startLine, endLine, verbosity, regions } = input;
|
|
34293
|
+
const markersByLine = /* @__PURE__ */ new Map();
|
|
34294
|
+
pushTraceMarker(markersByLine, startLine, {
|
|
34295
|
+
kind: "once",
|
|
34296
|
+
label: "Component setup runs on mount"
|
|
34297
|
+
});
|
|
34298
|
+
const stateDecls = collectStateDeclNames(fn);
|
|
34299
|
+
const reactiveNames = inferReactiveLocalNames(
|
|
34300
|
+
startLine,
|
|
34301
|
+
endLine,
|
|
34302
|
+
sourceLines,
|
|
34303
|
+
stateDecls.map((item) => item.name)
|
|
34304
|
+
);
|
|
34305
|
+
for (const stateDecl of stateDecls) {
|
|
34306
|
+
pushTraceMarker(markersByLine, stateDecl.line, {
|
|
34307
|
+
kind: "once",
|
|
34308
|
+
label: "Signal initialization runs once"
|
|
34309
|
+
});
|
|
34310
|
+
}
|
|
34311
|
+
for (const block of fn.blocks) {
|
|
34312
|
+
for (const instr of block.instructions) {
|
|
34313
|
+
const line = expressionContainsEffectCall(instr);
|
|
34314
|
+
if (!line || line < startLine || line > endLine) continue;
|
|
34315
|
+
pushTraceMarker(markersByLine, line, {
|
|
34316
|
+
kind: "effect",
|
|
34317
|
+
label: "Effect reruns when dependencies change"
|
|
34318
|
+
});
|
|
34319
|
+
}
|
|
34320
|
+
}
|
|
34321
|
+
const flatRegions = flattenRegions2(regions);
|
|
34322
|
+
for (let line = startLine; line <= endLine; line++) {
|
|
34323
|
+
const lineText = sourceLines[line - 1] ?? "";
|
|
34324
|
+
if (!lineText) continue;
|
|
34325
|
+
const hasReactiveName = lineContainsAnyIdentifier(lineText, reactiveNames);
|
|
34326
|
+
if (hasReactiveName && /\{[^}]*\b[A-Za-z_$][\w$]*\b[^}]*\}/.test(lineText)) {
|
|
34327
|
+
pushTraceMarker(markersByLine, line, {
|
|
34328
|
+
kind: "reactive",
|
|
34329
|
+
label: "JSX expression updates with reactive values"
|
|
34330
|
+
});
|
|
34331
|
+
}
|
|
34332
|
+
if (hasReactiveName && /\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText)) {
|
|
34333
|
+
pushTraceMarker(markersByLine, line, {
|
|
34334
|
+
kind: "reactive",
|
|
34335
|
+
label: "Statement reruns when reactive values change"
|
|
34336
|
+
});
|
|
34337
|
+
}
|
|
34338
|
+
if (/\b(?:\$effect|effect)\s*\(/.test(lineText)) {
|
|
34339
|
+
pushTraceMarker(markersByLine, line, {
|
|
34340
|
+
kind: "effect",
|
|
34341
|
+
label: "Effect callback executes reactively"
|
|
34342
|
+
});
|
|
34343
|
+
}
|
|
34344
|
+
if (verbosity === "verbose" && !hasReactiveName && /\{[^}]*\}/.test(lineText)) {
|
|
34345
|
+
pushTraceMarker(markersByLine, line, {
|
|
34346
|
+
kind: "once",
|
|
34347
|
+
label: "JSX expression runs during setup only"
|
|
34348
|
+
});
|
|
34349
|
+
}
|
|
34350
|
+
if (/\bconsole\.(?:log|debug|info|warn|error)\s*\(/.test(lineText) && !hasReactiveName && line !== startLine) {
|
|
34351
|
+
pushTraceMarker(markersByLine, line, {
|
|
34352
|
+
kind: "once",
|
|
34353
|
+
label: "Statement runs only during setup"
|
|
34354
|
+
});
|
|
34355
|
+
}
|
|
34356
|
+
const containingRegion = findContainingRegion2(line, flatRegions);
|
|
34357
|
+
if (!containingRegion) continue;
|
|
34358
|
+
const markers = markersByLine.get(line);
|
|
34359
|
+
if (!markers) continue;
|
|
34360
|
+
for (const marker of markers) {
|
|
34361
|
+
marker.regionId = containingRegion.id;
|
|
34362
|
+
marker.deps = containingRegion.dependencies;
|
|
34363
|
+
}
|
|
34364
|
+
}
|
|
34365
|
+
return [...markersByLine.entries()].sort((a, b) => a[0] - b[0]).map(([line, markers]) => ({ line, markers }));
|
|
34366
|
+
}
|
|
34367
|
+
|
|
34368
|
+
// src/tooling/analyze.ts
|
|
34369
|
+
function mergeLoc(a, b) {
|
|
34370
|
+
if (!a) return b ?? null;
|
|
34371
|
+
if (!b) return a;
|
|
34372
|
+
const start = b.start.line < a.start.line || b.start.line === a.start.line && b.start.column < a.start.column ? b.start : a.start;
|
|
34373
|
+
const end = b.end.line > a.end.line || b.end.line === a.end.line && b.end.column > a.end.column ? b.end : a.end;
|
|
34374
|
+
return {
|
|
34375
|
+
start,
|
|
34376
|
+
end,
|
|
34377
|
+
filename: a.filename ?? b.filename,
|
|
34378
|
+
identifierName: a.identifierName ?? b.identifierName
|
|
34379
|
+
};
|
|
34380
|
+
}
|
|
34381
|
+
function expressionContainsMacroCall(expr, macroName) {
|
|
34382
|
+
let found = false;
|
|
34383
|
+
const visit = (value) => {
|
|
34384
|
+
if (found) return;
|
|
34385
|
+
if (value.kind === "CallExpression" && value.callee.kind === "Identifier" && deSSAVarName(value.callee.name) === macroName) {
|
|
34386
|
+
found = true;
|
|
34387
|
+
return;
|
|
34388
|
+
}
|
|
34389
|
+
switch (value.kind) {
|
|
34390
|
+
case "CallExpression":
|
|
34391
|
+
case "OptionalCallExpression":
|
|
34392
|
+
visit(value.callee);
|
|
34393
|
+
value.arguments.forEach((arg) => visit(arg));
|
|
34394
|
+
return;
|
|
34395
|
+
case "MemberExpression":
|
|
34396
|
+
case "OptionalMemberExpression":
|
|
34397
|
+
visit(value.object);
|
|
34398
|
+
if (value.computed) visit(value.property);
|
|
34399
|
+
return;
|
|
34400
|
+
case "BinaryExpression":
|
|
34401
|
+
case "LogicalExpression":
|
|
34402
|
+
visit(value.left);
|
|
34403
|
+
visit(value.right);
|
|
34404
|
+
return;
|
|
34405
|
+
case "UnaryExpression":
|
|
34406
|
+
case "AwaitExpression":
|
|
34407
|
+
case "UpdateExpression":
|
|
34408
|
+
case "SpreadElement":
|
|
34409
|
+
visit(value.argument);
|
|
34410
|
+
return;
|
|
34411
|
+
case "ConditionalExpression":
|
|
34412
|
+
visit(value.test);
|
|
34413
|
+
visit(value.consequent);
|
|
34414
|
+
visit(value.alternate);
|
|
34415
|
+
return;
|
|
34416
|
+
case "ArrayExpression":
|
|
34417
|
+
value.elements.forEach((el) => visit(el));
|
|
34418
|
+
return;
|
|
34419
|
+
case "ObjectExpression":
|
|
34420
|
+
value.properties.forEach((prop) => {
|
|
34421
|
+
if (prop.kind === "SpreadElement") {
|
|
34422
|
+
visit(prop.argument);
|
|
34423
|
+
return;
|
|
34424
|
+
}
|
|
34425
|
+
if (prop.computed) visit(prop.key);
|
|
34426
|
+
visit(prop.value);
|
|
34427
|
+
});
|
|
34428
|
+
return;
|
|
34429
|
+
case "TemplateLiteral":
|
|
34430
|
+
value.expressions.forEach((item) => visit(item));
|
|
34431
|
+
return;
|
|
34432
|
+
case "AssignmentExpression":
|
|
34433
|
+
visit(value.left);
|
|
34434
|
+
visit(value.right);
|
|
34435
|
+
return;
|
|
34436
|
+
case "SequenceExpression":
|
|
34437
|
+
value.expressions.forEach((item) => visit(item));
|
|
34438
|
+
return;
|
|
34439
|
+
case "YieldExpression":
|
|
34440
|
+
if (value.argument) visit(value.argument);
|
|
34441
|
+
return;
|
|
34442
|
+
case "TaggedTemplateExpression":
|
|
34443
|
+
visit(value.tag);
|
|
34444
|
+
value.quasi.expressions.forEach((item) => visit(item));
|
|
34445
|
+
return;
|
|
34446
|
+
case "ImportExpression":
|
|
34447
|
+
visit(value.source);
|
|
34448
|
+
return;
|
|
34449
|
+
case "ArrowFunction":
|
|
34450
|
+
if (Array.isArray(value.body)) {
|
|
34451
|
+
value.body.forEach((block) => {
|
|
34452
|
+
block.instructions.forEach((instr) => {
|
|
34453
|
+
if (instr.kind === "Assign" || instr.kind === "Expression") {
|
|
34454
|
+
visit(instr.value);
|
|
34455
|
+
}
|
|
34456
|
+
});
|
|
34457
|
+
});
|
|
34458
|
+
} else {
|
|
34459
|
+
visit(value.body);
|
|
34460
|
+
}
|
|
34461
|
+
return;
|
|
34462
|
+
case "FunctionExpression":
|
|
34463
|
+
value.body.forEach((block) => {
|
|
34464
|
+
block.instructions.forEach((instr) => {
|
|
34465
|
+
if (instr.kind === "Assign" || instr.kind === "Expression") {
|
|
34466
|
+
visit(instr.value);
|
|
34467
|
+
}
|
|
34468
|
+
});
|
|
34469
|
+
});
|
|
34470
|
+
return;
|
|
34471
|
+
case "JSXElement":
|
|
34472
|
+
if (typeof value.tagName !== "string") visit(value.tagName);
|
|
34473
|
+
value.attributes.forEach((attr) => {
|
|
34474
|
+
if (attr.isSpread && attr.spreadExpr) {
|
|
34475
|
+
visit(attr.spreadExpr);
|
|
34476
|
+
} else if (attr.value) {
|
|
34477
|
+
visit(attr.value);
|
|
34478
|
+
}
|
|
34479
|
+
});
|
|
34480
|
+
value.children.forEach((child) => {
|
|
34481
|
+
if (child.kind === "expression") visit(child.value);
|
|
34482
|
+
if (child.kind === "element") visit(child.value);
|
|
34483
|
+
});
|
|
34484
|
+
return;
|
|
34485
|
+
case "Identifier":
|
|
34486
|
+
case "Literal":
|
|
34487
|
+
case "MetaProperty":
|
|
34488
|
+
case "NewExpression":
|
|
34489
|
+
case "ClassExpression":
|
|
34490
|
+
case "ThisExpression":
|
|
34491
|
+
case "SuperExpression":
|
|
34492
|
+
if (value.kind === "NewExpression") {
|
|
34493
|
+
visit(value.callee);
|
|
34494
|
+
value.arguments.forEach((arg) => visit(arg));
|
|
34495
|
+
}
|
|
34496
|
+
if (value.kind === "ClassExpression" && value.superClass) visit(value.superClass);
|
|
34497
|
+
return;
|
|
34498
|
+
default:
|
|
34499
|
+
return;
|
|
34500
|
+
}
|
|
34501
|
+
};
|
|
34502
|
+
visit(expr);
|
|
34503
|
+
return found;
|
|
34504
|
+
}
|
|
34505
|
+
function instructionContainsMacroCall(instruction, macroName) {
|
|
34506
|
+
if (instruction.kind !== "Assign" && instruction.kind !== "Expression") return false;
|
|
34507
|
+
return expressionContainsMacroCall(instruction.value, macroName);
|
|
34508
|
+
}
|
|
34509
|
+
function functionUsesMacro(fn, macroName) {
|
|
34510
|
+
for (const block of fn.blocks) {
|
|
34511
|
+
for (const instruction of block.instructions) {
|
|
34512
|
+
if (instructionContainsMacroCall(instruction, macroName)) return true;
|
|
34513
|
+
}
|
|
34514
|
+
const term = block.terminator;
|
|
34515
|
+
if ("argument" in term && term.argument && expressionContainsMacroCall(term.argument, macroName)) {
|
|
34516
|
+
return true;
|
|
34517
|
+
}
|
|
34518
|
+
if (term.kind === "Branch" && expressionContainsMacroCall(term.test, macroName)) {
|
|
34519
|
+
return true;
|
|
34520
|
+
}
|
|
34521
|
+
if (term.kind === "Switch") {
|
|
34522
|
+
if (expressionContainsMacroCall(term.discriminant, macroName)) return true;
|
|
34523
|
+
if (term.cases.some((entry) => entry.test && expressionContainsMacroCall(entry.test, macroName))) {
|
|
34524
|
+
return true;
|
|
34525
|
+
}
|
|
34526
|
+
}
|
|
34527
|
+
}
|
|
34528
|
+
return false;
|
|
34529
|
+
}
|
|
34530
|
+
function computeRegionLoc(region, fn) {
|
|
34531
|
+
let loc = null;
|
|
34532
|
+
for (const instruction of region.instructions) {
|
|
34533
|
+
loc = mergeLoc(loc, instruction.loc);
|
|
34534
|
+
}
|
|
34535
|
+
for (const blockId of region.blocks) {
|
|
34536
|
+
const block = fn.blocks.find((entry) => entry.id === blockId);
|
|
34537
|
+
if (!block) continue;
|
|
34538
|
+
loc = mergeLoc(loc, block.terminator.loc);
|
|
34539
|
+
}
|
|
34540
|
+
return loc;
|
|
34541
|
+
}
|
|
34542
|
+
function regionToSerializable(region, fn) {
|
|
34543
|
+
const loc = computeRegionLoc(region, fn);
|
|
34544
|
+
return {
|
|
34545
|
+
id: region.id,
|
|
34546
|
+
startLine: loc?.start.line,
|
|
34547
|
+
startColumn: loc?.start.column,
|
|
34548
|
+
endLine: loc?.end.line,
|
|
34549
|
+
endColumn: loc?.end.column,
|
|
34550
|
+
dependencies: [...region.dependencies].map(deSSAVarName),
|
|
34551
|
+
declarations: [...region.declarations].map(deSSAVarName),
|
|
34552
|
+
hasControlFlow: region.hasControlFlow,
|
|
34553
|
+
hasReactiveWrites: region.declarations.size > 0,
|
|
34554
|
+
children: region.children.map((child) => regionToSerializable(child, fn))
|
|
34555
|
+
};
|
|
34556
|
+
}
|
|
34557
|
+
function warningSeverity(code) {
|
|
34558
|
+
const diagnosticCodes = new Set(getAllDiagnosticCodes());
|
|
34559
|
+
if (!diagnosticCodes.has(code)) return "warning" /* Warning */;
|
|
34560
|
+
return getDiagnosticInfo(code).severity;
|
|
34561
|
+
}
|
|
34562
|
+
function normalizeWarningToDiagnostic(warning) {
|
|
34563
|
+
return {
|
|
34564
|
+
code: warning.code,
|
|
34565
|
+
message: warning.message,
|
|
34566
|
+
severity: warningSeverity(warning.code),
|
|
34567
|
+
line: warning.line,
|
|
34568
|
+
column: warning.column
|
|
34569
|
+
};
|
|
34570
|
+
}
|
|
34571
|
+
function normalizeThrownError(error) {
|
|
34572
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34573
|
+
return {
|
|
34574
|
+
code: "FICT-COMPILE",
|
|
34575
|
+
message,
|
|
34576
|
+
severity: "error" /* Error */,
|
|
34577
|
+
line: 0,
|
|
34578
|
+
column: 0
|
|
34579
|
+
};
|
|
34580
|
+
}
|
|
34581
|
+
function analyzeDiagnostics(code, fileName, options) {
|
|
34582
|
+
const warnings = [];
|
|
34583
|
+
const pluginOptions = {
|
|
34584
|
+
dev: true,
|
|
34585
|
+
filename: fileName,
|
|
34586
|
+
emitModuleMetadata: false,
|
|
34587
|
+
strictGuarantee: false,
|
|
34588
|
+
warningLevels: {
|
|
34589
|
+
...options.compilerOptions?.warningLevels ?? {},
|
|
34590
|
+
"FICT-R004": "warn"
|
|
34591
|
+
},
|
|
34592
|
+
...options.compilerOptions,
|
|
34593
|
+
onWarn: (warning) => warnings.push(warning)
|
|
34594
|
+
};
|
|
34595
|
+
try {
|
|
34596
|
+
transformSync(code, {
|
|
34597
|
+
filename: fileName,
|
|
34598
|
+
configFile: false,
|
|
34599
|
+
babelrc: false,
|
|
34600
|
+
sourceType: "module",
|
|
34601
|
+
parserOpts: {
|
|
34602
|
+
sourceType: "module",
|
|
34603
|
+
plugins: ["typescript", "jsx"],
|
|
34604
|
+
allowReturnOutsideFunction: true
|
|
34605
|
+
},
|
|
34606
|
+
plugins: [[index_default, pluginOptions]],
|
|
34607
|
+
generatorOpts: {
|
|
34608
|
+
compact: false
|
|
34609
|
+
}
|
|
34610
|
+
});
|
|
34611
|
+
} catch (error) {
|
|
34612
|
+
return [...warnings.map(normalizeWarningToDiagnostic), normalizeThrownError(error)];
|
|
34613
|
+
}
|
|
34614
|
+
return warnings.map(normalizeWarningToDiagnostic);
|
|
34615
|
+
}
|
|
34616
|
+
function shouldIncludeFunction(fn) {
|
|
34617
|
+
return functionContainsJSX(fn) || functionUsesMacro(fn, "$state") || functionUsesMacro(fn, "$effect");
|
|
34618
|
+
}
|
|
34619
|
+
function parseFileAst(code, fileName) {
|
|
34620
|
+
const ast = parseSync(code, {
|
|
34621
|
+
filename: fileName,
|
|
34622
|
+
sourceType: "module",
|
|
34623
|
+
parserOpts: {
|
|
34624
|
+
sourceType: "module",
|
|
34625
|
+
plugins: ["typescript", "jsx"],
|
|
34626
|
+
allowReturnOutsideFunction: true
|
|
34627
|
+
}
|
|
34628
|
+
});
|
|
34629
|
+
if (!ast || ast.type !== "File") {
|
|
34630
|
+
throw new Error("Failed to parse source file for Fict analysis.");
|
|
34631
|
+
}
|
|
34632
|
+
return ast;
|
|
34633
|
+
}
|
|
34634
|
+
function analyzeFictFile(code, fileName, options = {}) {
|
|
34635
|
+
const includeRegions = options.includeRegions ?? true;
|
|
34636
|
+
const includeDiagnostics = options.includeDiagnostics ?? true;
|
|
34637
|
+
const verbosity = options.verbosity ?? "minimal";
|
|
34638
|
+
const ast = parseFileAst(code, fileName);
|
|
34639
|
+
const hir = buildHIR(
|
|
34640
|
+
ast,
|
|
34641
|
+
{
|
|
34642
|
+
state: /* @__PURE__ */ new Set(["$state"]),
|
|
34643
|
+
effect: /* @__PURE__ */ new Set(["$effect"])
|
|
34644
|
+
},
|
|
34645
|
+
{
|
|
34646
|
+
dev: true,
|
|
34647
|
+
fileName
|
|
34648
|
+
}
|
|
34649
|
+
);
|
|
34650
|
+
const sourceLines = code.split(/\r?\n/);
|
|
34651
|
+
const components = [];
|
|
34652
|
+
for (const fn of hir.functions) {
|
|
34653
|
+
if (!fn.loc || !shouldIncludeFunction(fn)) continue;
|
|
34654
|
+
const startLine = fn.loc.start.line;
|
|
34655
|
+
const endLine = fn.loc.end.line;
|
|
34656
|
+
const scopeResult = analyzeReactiveScopesWithSSA(fn);
|
|
34657
|
+
const regionResult = generateRegions(fn, scopeResult);
|
|
34658
|
+
const regions = includeRegions ? regionResult.topLevelRegions.map((region) => regionToSerializable(region, fn)) : void 0;
|
|
34659
|
+
const trace = inferTraceMarkersForComponent({
|
|
34660
|
+
fn,
|
|
34661
|
+
sourceLines,
|
|
34662
|
+
startLine,
|
|
34663
|
+
endLine,
|
|
34664
|
+
verbosity,
|
|
34665
|
+
regions
|
|
34666
|
+
});
|
|
34667
|
+
components.push({
|
|
34668
|
+
name: fn.name ?? "<anonymous>",
|
|
34669
|
+
startLine,
|
|
34670
|
+
endLine,
|
|
34671
|
+
trace,
|
|
34672
|
+
regions
|
|
34673
|
+
});
|
|
34674
|
+
}
|
|
34675
|
+
const diagnostics = includeDiagnostics ? analyzeDiagnostics(code, fileName, options) : [];
|
|
34676
|
+
return {
|
|
34677
|
+
fileName,
|
|
34678
|
+
components,
|
|
34679
|
+
diagnostics
|
|
34680
|
+
};
|
|
34681
|
+
}
|
|
34682
|
+
|
|
33923
34683
|
// src/index.ts
|
|
33924
34684
|
function stripMacroImports(path2, t4) {
|
|
33925
34685
|
path2.traverse({
|
|
@@ -34813,46 +35573,77 @@ function createHIREntrypointVisitor(t4, options) {
|
|
|
34813
35573
|
path2.traverse({
|
|
34814
35574
|
JSXExpressionContainer(exprPath) {
|
|
34815
35575
|
const expr = exprPath.node.expression;
|
|
34816
|
-
if (!t4.isCallExpression(expr)) return;
|
|
34817
|
-
if (!t4.isMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
|
|
35576
|
+
if (!t4.isCallExpression(expr) && !t4.isOptionalCallExpression(expr)) return;
|
|
35577
|
+
if (!t4.isMemberExpression(expr.callee) && !t4.isOptionalMemberExpression(expr.callee) || !t4.isIdentifier(expr.callee.property, { name: "map" })) {
|
|
34818
35578
|
return;
|
|
34819
35579
|
}
|
|
34820
|
-
const
|
|
34821
|
-
if (!
|
|
34822
|
-
const
|
|
34823
|
-
|
|
34824
|
-
|
|
34825
|
-
const ret = fn.body.body.find((stmt) => t4.isReturnStatement(stmt));
|
|
34826
|
-
if (ret && t4.isReturnStatement(ret) && ret.argument && (t4.isJSXElement(ret.argument) || t4.isJSXFragment(ret.argument))) {
|
|
34827
|
-
return ret.argument;
|
|
34828
|
-
}
|
|
34829
|
-
}
|
|
34830
|
-
return null;
|
|
34831
|
-
};
|
|
34832
|
-
const jsx = getReturnedJsx(cb);
|
|
34833
|
-
if (!jsx) return;
|
|
34834
|
-
if (t4.isJSXFragment(jsx)) {
|
|
34835
|
-
warn({
|
|
34836
|
-
code: "FICT-J002",
|
|
34837
|
-
message: "Missing key prop in list rendering.",
|
|
34838
|
-
fileName,
|
|
34839
|
-
line: expr.loc?.start.line ?? 0,
|
|
34840
|
-
column: expr.loc ? expr.loc.start.column + 1 : 0
|
|
34841
|
-
});
|
|
35580
|
+
const callExprPath = exprPath.get("expression");
|
|
35581
|
+
if (!callExprPath.isCallExpression() && !callExprPath.isOptionalCallExpression()) return;
|
|
35582
|
+
const argPaths = callExprPath.get("arguments");
|
|
35583
|
+
const cbPath = Array.isArray(argPaths) ? argPaths[0] : void 0;
|
|
35584
|
+
if (!cbPath || !cbPath.isArrowFunctionExpression() && !cbPath.isFunctionExpression()) {
|
|
34842
35585
|
return;
|
|
34843
35586
|
}
|
|
34844
|
-
|
|
34845
|
-
|
|
34846
|
-
|
|
34847
|
-
|
|
34848
|
-
|
|
34849
|
-
|
|
35587
|
+
const getReturnedJsx = (fnPath) => {
|
|
35588
|
+
const collectReturnedJsxFromExpression = (node, returned2) => {
|
|
35589
|
+
if (!node) return;
|
|
35590
|
+
if (t4.isJSXElement(node) || t4.isJSXFragment(node)) {
|
|
35591
|
+
returned2.push(node);
|
|
35592
|
+
return;
|
|
35593
|
+
}
|
|
35594
|
+
if (t4.isConditionalExpression(node)) {
|
|
35595
|
+
collectReturnedJsxFromExpression(node.consequent, returned2);
|
|
35596
|
+
collectReturnedJsxFromExpression(node.alternate, returned2);
|
|
35597
|
+
return;
|
|
35598
|
+
}
|
|
35599
|
+
if (t4.isLogicalExpression(node)) {
|
|
35600
|
+
collectReturnedJsxFromExpression(node.left, returned2);
|
|
35601
|
+
collectReturnedJsxFromExpression(node.right, returned2);
|
|
35602
|
+
return;
|
|
35603
|
+
}
|
|
35604
|
+
if (t4.isSequenceExpression(node)) {
|
|
35605
|
+
const tail = node.expressions[node.expressions.length - 1];
|
|
35606
|
+
collectReturnedJsxFromExpression(tail, returned2);
|
|
35607
|
+
return;
|
|
35608
|
+
}
|
|
35609
|
+
if (t4.isParenthesizedExpression(node)) {
|
|
35610
|
+
collectReturnedJsxFromExpression(node.expression, returned2);
|
|
35611
|
+
return;
|
|
35612
|
+
}
|
|
35613
|
+
if (t4.isTSAsExpression(node) || t4.isTSTypeAssertion(node) || t4.isTSNonNullExpression(node) || t4.isTSSatisfiesExpression(node) || t4.isTypeCastExpression(node)) {
|
|
35614
|
+
collectReturnedJsxFromExpression(node.expression, returned2);
|
|
35615
|
+
}
|
|
35616
|
+
};
|
|
35617
|
+
const fn = fnPath.node;
|
|
35618
|
+
const returned = [];
|
|
35619
|
+
if (!t4.isBlockStatement(fn.body)) {
|
|
35620
|
+
collectReturnedJsxFromExpression(fn.body, returned);
|
|
35621
|
+
return returned;
|
|
34850
35622
|
}
|
|
34851
|
-
|
|
34852
|
-
|
|
35623
|
+
fnPath.get("body").traverse({
|
|
35624
|
+
Function(innerFnPath) {
|
|
35625
|
+
innerFnPath.skip();
|
|
35626
|
+
},
|
|
35627
|
+
ReturnStatement(retPath) {
|
|
35628
|
+
collectReturnedJsxFromExpression(retPath.node.argument, returned);
|
|
35629
|
+
}
|
|
35630
|
+
});
|
|
35631
|
+
return returned;
|
|
35632
|
+
};
|
|
35633
|
+
const returnedJsx = getReturnedJsx(cbPath);
|
|
35634
|
+
if (returnedJsx.length === 0) return;
|
|
35635
|
+
const hasMissingKeyBranch = returnedJsx.some((jsx) => {
|
|
35636
|
+
if (t4.isJSXFragment(jsx)) return true;
|
|
35637
|
+
let hasKey = false;
|
|
35638
|
+
for (const attr of jsx.openingElement.attributes) {
|
|
35639
|
+
if (t4.isJSXAttribute(attr) && t4.isJSXIdentifier(attr.name, { name: "key" })) {
|
|
35640
|
+
hasKey = true;
|
|
35641
|
+
break;
|
|
35642
|
+
}
|
|
34853
35643
|
}
|
|
34854
|
-
|
|
34855
|
-
|
|
35644
|
+
return !hasKey;
|
|
35645
|
+
});
|
|
35646
|
+
if (!hasMissingKeyBranch) return;
|
|
34856
35647
|
warn({
|
|
34857
35648
|
code: "FICT-J002",
|
|
34858
35649
|
message: "Missing key prop in list rendering.",
|
|
@@ -35414,9 +36205,11 @@ var createFictPlugin = declare(
|
|
|
35414
36205
|
);
|
|
35415
36206
|
var index_default = createFictPlugin;
|
|
35416
36207
|
export {
|
|
36208
|
+
analyzeFictFile,
|
|
35417
36209
|
clearModuleMetadata,
|
|
35418
36210
|
createFictPlugin,
|
|
35419
36211
|
index_default as default,
|
|
36212
|
+
inferTraceMarkersForComponent,
|
|
35420
36213
|
resolveModuleMetadata,
|
|
35421
36214
|
setModuleMetadata
|
|
35422
36215
|
};
|