@barefootjs/jsx 0.17.0 → 0.18.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/adapters/env-signal.d.ts +42 -7
- package/dist/adapters/env-signal.d.ts.map +1 -1
- package/dist/adapters/interface.d.ts +20 -4
- package/dist/adapters/interface.d.ts.map +1 -1
- package/dist/adapters/parsed-expr-emitter.d.ts +3 -1
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/augment-inherited-props.d.ts +19 -0
- package/dist/augment-inherited-props.d.ts.map +1 -1
- package/dist/compiler.d.ts.map +1 -1
- package/dist/expression-parser.d.ts +48 -2
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1728 -969
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/compute-inlinability.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +9 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/template-parse.d.ts +19 -0
- package/dist/ir-to-client-js/control-flow/stringify/template-parse.d.ts.map +1 -1
- package/dist/ir-to-client-js/emit-registration.d.ts +4 -2
- package/dist/ir-to-client-js/emit-registration.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +41 -0
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +22 -1
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/loop-destructure.d.ts +55 -18
- package/dist/loop-destructure.d.ts.map +1 -1
- package/dist/lowering-registry.d.ts +13 -0
- package/dist/lowering-registry.d.ts.map +1 -1
- package/dist/relocate.d.ts +28 -0
- package/dist/relocate.d.ts.map +1 -1
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/ssr-seed-plan.d.ts +84 -0
- package/dist/ssr-seed-plan.d.ts.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +284 -12
- package/src/__tests__/augment-inherited-props.test.ts +96 -0
- package/src/__tests__/compiler-runtime-contract.test.ts +11 -1
- package/src/__tests__/compiler-stress-1244.test.ts +13 -4
- package/src/__tests__/csr-substitution-safety-divergence.test.ts +137 -0
- package/src/__tests__/destructured-map-params.test.ts +11 -1
- package/src/__tests__/expression-parser.test.ts +74 -3
- package/src/__tests__/free-identifiers.test.ts +55 -0
- package/src/__tests__/ir-sort-comparator.test.ts +261 -0
- package/src/__tests__/loop-destructure.test.ts +313 -0
- package/src/__tests__/loop-hoisted-template.test.ts +235 -0
- package/src/__tests__/materialize-getter-calls.test.ts +58 -0
- package/src/__tests__/props-destructuring.test.ts +110 -0
- package/src/__tests__/serialize-parsed-expr.test.ts +88 -2
- package/src/__tests__/ssr-defaults.test.ts +20 -0
- package/src/__tests__/ssr-seed-plan.test.ts +212 -0
- package/src/__tests__/staged-ir/11-template-primitive-registry.test.ts +231 -1
- package/src/__tests__/tagged-template-interleave.test.ts +268 -0
- package/src/__tests__/unsupported-expression.test.ts +194 -7
- package/src/adapters/env-signal.ts +57 -9
- package/src/adapters/interface.ts +20 -4
- package/src/adapters/parsed-expr-emitter.ts +19 -2
- package/src/analyzer-context.ts +20 -0
- package/src/analyzer.ts +74 -1
- package/src/augment-inherited-props.ts +139 -9
- package/src/compiler.ts +10 -1
- package/src/expression-parser.ts +421 -50
- package/src/index.ts +30 -3
- package/src/ir-to-client-js/collect-elements.ts +15 -1
- package/src/ir-to-client-js/compute-inlinability.ts +6 -1
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +1 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +9 -0
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +30 -8
- package/src/ir-to-client-js/control-flow/stringify/template-parse.ts +30 -0
- package/src/ir-to-client-js/emit-registration.ts +4 -2
- package/src/ir-to-client-js/html-template.ts +198 -1
- package/src/ir-to-client-js/index.ts +1 -0
- package/src/ir-to-client-js/types.ts +22 -0
- package/src/jsx-to-ir.ts +409 -24
- package/src/loop-destructure.ts +89 -36
- package/src/lowering-registry.ts +16 -0
- package/src/relocate.ts +201 -14
- package/src/ssr-defaults.ts +34 -32
- package/src/ssr-seed-plan.ts +146 -0
- package/src/types.ts +76 -0
package/dist/index.js
CHANGED
|
@@ -352,6 +352,7 @@ function tsNodeToParsedExpr(node) {
|
|
|
352
352
|
}
|
|
353
353
|
var CALLBACK_METHODS = new Set([
|
|
354
354
|
"filter",
|
|
355
|
+
"map",
|
|
355
356
|
"every",
|
|
356
357
|
"some",
|
|
357
358
|
"find",
|
|
@@ -440,6 +441,7 @@ function convertNode(node, raw) {
|
|
|
440
441
|
if (callee.property === "flat") {
|
|
441
442
|
const depthNode = node.arguments[0];
|
|
442
443
|
let flatDepth;
|
|
444
|
+
let depthExpr;
|
|
443
445
|
if (depthNode === undefined) {
|
|
444
446
|
flatDepth = 1;
|
|
445
447
|
} else if (ts2.isIdentifier(depthNode) && depthNode.text === "Infinity") {
|
|
@@ -452,16 +454,23 @@ function convertNode(node, raw) {
|
|
|
452
454
|
n = -Number(depthNode.operand.text);
|
|
453
455
|
}
|
|
454
456
|
if (n === undefined || Number.isNaN(n)) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
457
|
+
const parsedDepth = convertNode(depthNode, raw);
|
|
458
|
+
if (checkSupport(parsedDepth).supported) {
|
|
459
|
+
depthExpr = parsedDepth;
|
|
460
|
+
flatDepth = 1;
|
|
461
|
+
} else {
|
|
462
|
+
return {
|
|
463
|
+
kind: "unsupported",
|
|
464
|
+
raw,
|
|
465
|
+
reason: `\`.flat(depth)\` needs a literal integer, \`Infinity\`, or a supported dynamic depth expression — this depth can't be resolved. Use a literal depth, a supported expression (prop/signal/arithmetic), or pre-compute the value before the template.`
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
const truncated = Math.trunc(n);
|
|
470
|
+
flatDepth = truncated < 0 ? 0 : truncated;
|
|
460
471
|
}
|
|
461
|
-
const truncated = Math.trunc(n);
|
|
462
|
-
flatDepth = truncated < 0 ? 0 : truncated;
|
|
463
472
|
}
|
|
464
|
-
return { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth };
|
|
473
|
+
return depthExpr !== undefined ? { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth, depthExpr } : { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth };
|
|
465
474
|
}
|
|
466
475
|
if (callee.property === "toLowerCase") {
|
|
467
476
|
return { kind: "array-method", method: "toLowerCase", object: callee.object, args };
|
|
@@ -1006,6 +1015,8 @@ function validateRestUsage(expr, restName, excludedTopKeys) {
|
|
|
1006
1015
|
walk(e.object);
|
|
1007
1016
|
for (const a of e.args)
|
|
1008
1017
|
walk(a);
|
|
1018
|
+
if (e.method === "flat" && e.depthExpr)
|
|
1019
|
+
walk(e.depthExpr);
|
|
1009
1020
|
return;
|
|
1010
1021
|
case "literal":
|
|
1011
1022
|
case "unsupported":
|
|
@@ -1091,6 +1102,8 @@ function collectIdentifiers(expr, out) {
|
|
|
1091
1102
|
case "array-method":
|
|
1092
1103
|
collectIdentifiers(expr.object, out);
|
|
1093
1104
|
expr.args.forEach((e) => collectIdentifiers(e, out));
|
|
1105
|
+
if (expr.method === "flat" && expr.depthExpr)
|
|
1106
|
+
collectIdentifiers(expr.depthExpr, out);
|
|
1094
1107
|
return;
|
|
1095
1108
|
case "literal":
|
|
1096
1109
|
case "regex":
|
|
@@ -1148,7 +1161,14 @@ function substituteDestructuredFields(expr, fieldMap, syntheticParam, restName)
|
|
|
1148
1161
|
return { kind: "array-literal", elements: e.elements.map(walk) };
|
|
1149
1162
|
case "array-method":
|
|
1150
1163
|
if (e.method === "flat") {
|
|
1151
|
-
return {
|
|
1164
|
+
return {
|
|
1165
|
+
kind: "array-method",
|
|
1166
|
+
method: "flat",
|
|
1167
|
+
object: walk(e.object),
|
|
1168
|
+
args: [],
|
|
1169
|
+
flatDepth: e.flatDepth,
|
|
1170
|
+
...e.depthExpr ? { depthExpr: walk(e.depthExpr) } : {}
|
|
1171
|
+
};
|
|
1152
1172
|
}
|
|
1153
1173
|
return { kind: "array-method", method: e.method, object: walk(e.object), args: e.args.map(walk) };
|
|
1154
1174
|
case "literal":
|
|
@@ -1245,6 +1265,11 @@ function checkSupport(expr) {
|
|
|
1245
1265
|
if (!argSupport.supported)
|
|
1246
1266
|
return argSupport;
|
|
1247
1267
|
}
|
|
1268
|
+
if (expr.method === "flat" && expr.depthExpr) {
|
|
1269
|
+
const depthSupport = checkSupport(expr.depthExpr);
|
|
1270
|
+
if (!depthSupport.supported)
|
|
1271
|
+
return depthSupport;
|
|
1272
|
+
}
|
|
1248
1273
|
return { supported: true, level: "L2" };
|
|
1249
1274
|
}
|
|
1250
1275
|
case "call": {
|
|
@@ -1344,6 +1369,9 @@ function checkSupport(expr) {
|
|
|
1344
1369
|
const leftSupport = checkSupport(expr.left);
|
|
1345
1370
|
if (!leftSupport.supported)
|
|
1346
1371
|
return leftSupport;
|
|
1372
|
+
if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
|
|
1373
|
+
return { supported: true, level: "L4" };
|
|
1374
|
+
}
|
|
1347
1375
|
const rightSupport = checkSupport(expr.right);
|
|
1348
1376
|
if (!rightSupport.supported)
|
|
1349
1377
|
return rightSupport;
|
|
@@ -1398,7 +1426,7 @@ function containsHigherOrder(expr) {
|
|
|
1398
1426
|
case "array-literal":
|
|
1399
1427
|
return expr.elements.some(containsHigherOrder);
|
|
1400
1428
|
case "array-method":
|
|
1401
|
-
return containsHigherOrder(expr.object) || expr.args.some(containsHigherOrder);
|
|
1429
|
+
return containsHigherOrder(expr.object) || expr.args.some(containsHigherOrder) || expr.method === "flat" && expr.depthExpr !== undefined && containsHigherOrder(expr.depthExpr);
|
|
1402
1430
|
default:
|
|
1403
1431
|
return false;
|
|
1404
1432
|
}
|
|
@@ -1566,7 +1594,10 @@ function usesPerPath(name, expr) {
|
|
|
1566
1594
|
case "array-literal":
|
|
1567
1595
|
return sum(e.elements);
|
|
1568
1596
|
case "array-method":
|
|
1569
|
-
|
|
1597
|
+
if (e.method === "flat") {
|
|
1598
|
+
return add(walk(e.object), e.depthExpr ? walk(e.depthExpr) : { min: 0, max: 0 });
|
|
1599
|
+
}
|
|
1600
|
+
return add(walk(e.object), sum(e.args));
|
|
1570
1601
|
case "object-literal":
|
|
1571
1602
|
return sum(e.properties.map((p) => p.value));
|
|
1572
1603
|
case "arrow":
|
|
@@ -1621,7 +1652,14 @@ function inlineBinding(expr, name, value) {
|
|
|
1621
1652
|
return { kind: "array-literal", elements: e.elements.map((el) => walk(el, enclosing)) };
|
|
1622
1653
|
case "array-method":
|
|
1623
1654
|
if (e.method === "flat") {
|
|
1624
|
-
return {
|
|
1655
|
+
return {
|
|
1656
|
+
kind: "array-method",
|
|
1657
|
+
method: "flat",
|
|
1658
|
+
object: walk(e.object, enclosing),
|
|
1659
|
+
args: [],
|
|
1660
|
+
flatDepth: e.flatDepth,
|
|
1661
|
+
...e.depthExpr ? { depthExpr: walk(e.depthExpr, enclosing) } : {}
|
|
1662
|
+
};
|
|
1625
1663
|
}
|
|
1626
1664
|
return { kind: "array-method", method: e.method, object: walk(e.object, enclosing), args: e.args.map((a) => walk(a, enclosing)) };
|
|
1627
1665
|
case "object-literal":
|
|
@@ -1752,6 +1790,9 @@ function exprToString(expr) {
|
|
|
1752
1790
|
return `[${expr.elements.map(exprToString).join(", ")}]`;
|
|
1753
1791
|
case "array-method":
|
|
1754
1792
|
if (expr.method === "flat") {
|
|
1793
|
+
if (expr.depthExpr) {
|
|
1794
|
+
return `${exprToString(expr.object)}.flat(${exprToString(expr.depthExpr)})`;
|
|
1795
|
+
}
|
|
1755
1796
|
const d = expr.flatDepth;
|
|
1756
1797
|
const depthSrc = d === "infinity" ? "Infinity" : String(d);
|
|
1757
1798
|
return `${exprToString(expr.object)}.flat(${d === 1 ? "" : depthSrc})`;
|
|
@@ -1803,6 +1844,9 @@ function stringifyParsedExpr(expr) {
|
|
|
1803
1844
|
return `[${expr.elements.map(stringifyParsedExpr).join(", ")}]`;
|
|
1804
1845
|
case "array-method":
|
|
1805
1846
|
if (expr.method === "flat") {
|
|
1847
|
+
if (expr.depthExpr) {
|
|
1848
|
+
return `${stringifyParsedExpr(expr.object)}.flat(${stringifyParsedExpr(expr.depthExpr)})`;
|
|
1849
|
+
}
|
|
1806
1850
|
const d = expr.flatDepth;
|
|
1807
1851
|
const depthSrc = d === "infinity" ? "Infinity" : String(d);
|
|
1808
1852
|
return `${stringifyParsedExpr(expr.object)}.flat(${d === 1 ? "" : depthSrc})`;
|
|
@@ -1813,66 +1857,195 @@ function stringifyParsedExpr(expr) {
|
|
|
1813
1857
|
return expr.raw;
|
|
1814
1858
|
}
|
|
1815
1859
|
}
|
|
1860
|
+
function materializeGetterCalls(expr, names) {
|
|
1861
|
+
const rw = (e) => materializeGetterCalls(e, names);
|
|
1862
|
+
switch (expr.kind) {
|
|
1863
|
+
case "call":
|
|
1864
|
+
if (expr.args.length === 0 && expr.callee.kind === "identifier" && names.has(expr.callee.name)) {
|
|
1865
|
+
return { kind: "identifier", name: expr.callee.name };
|
|
1866
|
+
}
|
|
1867
|
+
return { kind: "call", callee: rw(expr.callee), args: expr.args.map(rw) };
|
|
1868
|
+
case "binary":
|
|
1869
|
+
return { kind: "binary", op: expr.op, left: rw(expr.left), right: rw(expr.right) };
|
|
1870
|
+
case "logical":
|
|
1871
|
+
return { kind: "logical", op: expr.op, left: rw(expr.left), right: rw(expr.right) };
|
|
1872
|
+
case "unary":
|
|
1873
|
+
return { kind: "unary", op: expr.op, argument: rw(expr.argument) };
|
|
1874
|
+
case "conditional":
|
|
1875
|
+
return {
|
|
1876
|
+
kind: "conditional",
|
|
1877
|
+
test: rw(expr.test),
|
|
1878
|
+
consequent: rw(expr.consequent),
|
|
1879
|
+
alternate: rw(expr.alternate)
|
|
1880
|
+
};
|
|
1881
|
+
case "member":
|
|
1882
|
+
return { kind: "member", object: rw(expr.object), property: expr.property, computed: expr.computed };
|
|
1883
|
+
case "index-access":
|
|
1884
|
+
return { kind: "index-access", object: rw(expr.object), index: rw(expr.index) };
|
|
1885
|
+
case "template-literal":
|
|
1886
|
+
return {
|
|
1887
|
+
kind: "template-literal",
|
|
1888
|
+
parts: expr.parts.map((p) => p.type === "string" ? p : { type: "expression", expr: rw(p.expr) })
|
|
1889
|
+
};
|
|
1890
|
+
case "array-literal":
|
|
1891
|
+
return { kind: "array-literal", elements: expr.elements.map(rw) };
|
|
1892
|
+
case "array-method":
|
|
1893
|
+
if (expr.method === "flat") {
|
|
1894
|
+
return { ...expr, object: rw(expr.object), ...expr.depthExpr ? { depthExpr: rw(expr.depthExpr) } : {} };
|
|
1895
|
+
}
|
|
1896
|
+
return { ...expr, object: rw(expr.object), args: expr.args.map(rw) };
|
|
1897
|
+
case "object-literal":
|
|
1898
|
+
return {
|
|
1899
|
+
kind: "object-literal",
|
|
1900
|
+
raw: expr.raw,
|
|
1901
|
+
properties: expr.properties.map((p) => ({ ...p, value: rw(p.value) }))
|
|
1902
|
+
};
|
|
1903
|
+
case "arrow":
|
|
1904
|
+
return { kind: "arrow", params: expr.params, body: rw(expr.body) };
|
|
1905
|
+
case "identifier":
|
|
1906
|
+
case "literal":
|
|
1907
|
+
case "regex":
|
|
1908
|
+
case "unsupported":
|
|
1909
|
+
return expr;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1816
1912
|
function serializeParsedExpr(expr) {
|
|
1817
1913
|
const node = toEvalNode(expr);
|
|
1818
1914
|
return node === null ? null : JSON.stringify(node);
|
|
1819
1915
|
}
|
|
1820
1916
|
function freeVarsInBody(body, params) {
|
|
1821
1917
|
const found = new Set;
|
|
1822
|
-
const visit = (e) => {
|
|
1918
|
+
const visit = (e, bound) => {
|
|
1823
1919
|
switch (e.kind) {
|
|
1824
1920
|
case "identifier":
|
|
1825
|
-
if (!
|
|
1921
|
+
if (!bound.has(e.name))
|
|
1826
1922
|
found.add(e.name);
|
|
1827
1923
|
return;
|
|
1828
1924
|
case "binary":
|
|
1829
1925
|
case "logical":
|
|
1830
|
-
visit(e.left);
|
|
1831
|
-
visit(e.right);
|
|
1926
|
+
visit(e.left, bound);
|
|
1927
|
+
visit(e.right, bound);
|
|
1832
1928
|
return;
|
|
1833
1929
|
case "unary":
|
|
1834
|
-
visit(e.argument);
|
|
1930
|
+
visit(e.argument, bound);
|
|
1835
1931
|
return;
|
|
1836
1932
|
case "conditional":
|
|
1837
|
-
visit(e.test);
|
|
1838
|
-
visit(e.consequent);
|
|
1839
|
-
visit(e.alternate);
|
|
1933
|
+
visit(e.test, bound);
|
|
1934
|
+
visit(e.consequent, bound);
|
|
1935
|
+
visit(e.alternate, bound);
|
|
1840
1936
|
return;
|
|
1841
1937
|
case "member":
|
|
1842
|
-
visit(e.object);
|
|
1938
|
+
visit(e.object, bound);
|
|
1843
1939
|
return;
|
|
1844
1940
|
case "index-access":
|
|
1845
|
-
visit(e.object);
|
|
1846
|
-
visit(e.index);
|
|
1941
|
+
visit(e.object, bound);
|
|
1942
|
+
visit(e.index, bound);
|
|
1847
1943
|
return;
|
|
1848
1944
|
case "call":
|
|
1849
1945
|
if (evalBuiltinCalleeName(e.callee) === null)
|
|
1850
|
-
visit(e.callee);
|
|
1851
|
-
e.args.forEach(visit);
|
|
1946
|
+
visit(e.callee, bound);
|
|
1947
|
+
e.args.forEach((a) => visit(a, bound));
|
|
1852
1948
|
return;
|
|
1853
1949
|
case "template-literal":
|
|
1854
1950
|
for (const p of e.parts)
|
|
1855
1951
|
if (p.type === "expression")
|
|
1856
|
-
visit(p.expr);
|
|
1952
|
+
visit(p.expr, bound);
|
|
1857
1953
|
return;
|
|
1858
1954
|
case "array-literal":
|
|
1859
|
-
e.elements.forEach(visit);
|
|
1955
|
+
e.elements.forEach((el) => visit(el, bound));
|
|
1860
1956
|
return;
|
|
1861
1957
|
case "object-literal":
|
|
1862
1958
|
for (const p of e.properties)
|
|
1863
|
-
visit(p.value);
|
|
1959
|
+
visit(p.value, bound);
|
|
1864
1960
|
return;
|
|
1865
|
-
case "literal":
|
|
1866
1961
|
case "array-method":
|
|
1867
|
-
|
|
1962
|
+
if (e.method === "includes" || e.method === "join") {
|
|
1963
|
+
visit(e.object, bound);
|
|
1964
|
+
e.args.forEach((a) => visit(a, bound));
|
|
1965
|
+
}
|
|
1966
|
+
return;
|
|
1967
|
+
case "arrow": {
|
|
1968
|
+
const inner = e.params.length === 0 ? bound : new Set([...bound, ...e.params]);
|
|
1969
|
+
visit(e.body, inner);
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
case "literal":
|
|
1868
1973
|
case "regex":
|
|
1869
1974
|
case "unsupported":
|
|
1870
1975
|
return;
|
|
1871
1976
|
}
|
|
1872
1977
|
};
|
|
1873
|
-
visit(body);
|
|
1978
|
+
visit(body, params);
|
|
1874
1979
|
return [...found].sort();
|
|
1875
1980
|
}
|
|
1981
|
+
function freeIdentifiers(expr) {
|
|
1982
|
+
const free = new Set;
|
|
1983
|
+
function visit(e, bound) {
|
|
1984
|
+
switch (e.kind) {
|
|
1985
|
+
case "literal":
|
|
1986
|
+
case "regex":
|
|
1987
|
+
return true;
|
|
1988
|
+
case "identifier":
|
|
1989
|
+
if (!bound.has(e.name))
|
|
1990
|
+
free.add(e.name);
|
|
1991
|
+
return true;
|
|
1992
|
+
case "call": {
|
|
1993
|
+
const isBuiltinCallee = evalBuiltinCalleeName(e.callee) !== null;
|
|
1994
|
+
if (!isBuiltinCallee && !visit(e.callee, bound))
|
|
1995
|
+
return false;
|
|
1996
|
+
for (const a of e.args)
|
|
1997
|
+
if (!visit(a, bound))
|
|
1998
|
+
return false;
|
|
1999
|
+
return true;
|
|
2000
|
+
}
|
|
2001
|
+
case "member":
|
|
2002
|
+
return visit(e.object, bound);
|
|
2003
|
+
case "index-access":
|
|
2004
|
+
return visit(e.object, bound) && visit(e.index, bound);
|
|
2005
|
+
case "binary":
|
|
2006
|
+
case "logical":
|
|
2007
|
+
return visit(e.left, bound) && visit(e.right, bound);
|
|
2008
|
+
case "unary":
|
|
2009
|
+
return visit(e.argument, bound);
|
|
2010
|
+
case "conditional":
|
|
2011
|
+
return visit(e.test, bound) && visit(e.consequent, bound) && visit(e.alternate, bound);
|
|
2012
|
+
case "template-literal":
|
|
2013
|
+
for (const p of e.parts) {
|
|
2014
|
+
if (p.type === "expression" && !visit(p.expr, bound))
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
return true;
|
|
2018
|
+
case "array-literal":
|
|
2019
|
+
for (const el of e.elements)
|
|
2020
|
+
if (!visit(el, bound))
|
|
2021
|
+
return false;
|
|
2022
|
+
return true;
|
|
2023
|
+
case "array-method":
|
|
2024
|
+
if (!visit(e.object, bound))
|
|
2025
|
+
return false;
|
|
2026
|
+
for (const a of e.args)
|
|
2027
|
+
if (!visit(a, bound))
|
|
2028
|
+
return false;
|
|
2029
|
+
if (e.method === "flat" && e.depthExpr && !visit(e.depthExpr, bound))
|
|
2030
|
+
return false;
|
|
2031
|
+
return true;
|
|
2032
|
+
case "object-literal":
|
|
2033
|
+
for (const p of e.properties)
|
|
2034
|
+
if (!visit(p.value, bound))
|
|
2035
|
+
return false;
|
|
2036
|
+
return true;
|
|
2037
|
+
case "arrow": {
|
|
2038
|
+
const inner = new Set(bound);
|
|
2039
|
+
for (const p of e.params)
|
|
2040
|
+
inner.add(p);
|
|
2041
|
+
return visit(e.body, inner);
|
|
2042
|
+
}
|
|
2043
|
+
case "unsupported":
|
|
2044
|
+
return false;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return visit(expr, new Set) ? free : null;
|
|
2048
|
+
}
|
|
1876
2049
|
var EVAL_BINARY_OPS = new Set([
|
|
1877
2050
|
"+",
|
|
1878
2051
|
"-",
|
|
@@ -1950,6 +2123,20 @@ function toEvalNode(e) {
|
|
|
1950
2123
|
return object && index ? { kind: "index-access", object, index } : null;
|
|
1951
2124
|
}
|
|
1952
2125
|
case "call": {
|
|
2126
|
+
const cb = asCallbackMethodCall(e);
|
|
2127
|
+
if (cb && (cb.method === "map" || cb.method === "filter")) {
|
|
2128
|
+
const object = toEvalNode(cb.object);
|
|
2129
|
+
if (!object)
|
|
2130
|
+
return null;
|
|
2131
|
+
const body = toEvalNode(cb.arrow.body);
|
|
2132
|
+
if (!body)
|
|
2133
|
+
return null;
|
|
2134
|
+
return {
|
|
2135
|
+
kind: "call",
|
|
2136
|
+
callee: { kind: "member", object, property: cb.method, computed: false },
|
|
2137
|
+
args: [{ kind: "arrow", params: cb.arrow.params, body }]
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
1953
2140
|
if (evalBuiltinCalleeName(e.callee) === null)
|
|
1954
2141
|
return null;
|
|
1955
2142
|
const callee = toEvalNode(e.callee);
|
|
@@ -1998,7 +2185,23 @@ function toEvalNode(e) {
|
|
|
1998
2185
|
}
|
|
1999
2186
|
return { kind: "object-literal", properties };
|
|
2000
2187
|
}
|
|
2001
|
-
case "array-method":
|
|
2188
|
+
case "array-method": {
|
|
2189
|
+
if (e.method === "includes" && e.args.length === 1) {
|
|
2190
|
+
const object = toEvalNode(e.object);
|
|
2191
|
+
const arg = toEvalNode(e.args[0]);
|
|
2192
|
+
return object && arg ? { kind: "array-method", method: "includes", object, args: [arg] } : null;
|
|
2193
|
+
}
|
|
2194
|
+
if (e.method === "join" && e.args.length <= 1) {
|
|
2195
|
+
const object = toEvalNode(e.object);
|
|
2196
|
+
if (!object)
|
|
2197
|
+
return null;
|
|
2198
|
+
if (e.args.length === 0)
|
|
2199
|
+
return { kind: "array-method", method: "join", object, args: [] };
|
|
2200
|
+
const sep = toEvalNode(e.args[0]);
|
|
2201
|
+
return sep ? { kind: "array-method", method: "join", object, args: [sep] } : null;
|
|
2202
|
+
}
|
|
2203
|
+
return null;
|
|
2204
|
+
}
|
|
2002
2205
|
case "arrow":
|
|
2003
2206
|
case "regex":
|
|
2004
2207
|
case "unsupported":
|
|
@@ -3152,6 +3355,89 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3152
3355
|
return assertNever(node);
|
|
3153
3356
|
}
|
|
3154
3357
|
}
|
|
3358
|
+
function buildLoopSkeletonTemplate(node, safe) {
|
|
3359
|
+
switch (node.type) {
|
|
3360
|
+
case "element": {
|
|
3361
|
+
const attrParts = [];
|
|
3362
|
+
for (const a of node.attrs) {
|
|
3363
|
+
if (a.name === "...")
|
|
3364
|
+
return null;
|
|
3365
|
+
if (a.name === "dangerouslySetInnerHTML")
|
|
3366
|
+
return null;
|
|
3367
|
+
if (a.name === "key") {
|
|
3368
|
+
attrParts.push(`${keyAttrName(0)}=""`);
|
|
3369
|
+
continue;
|
|
3370
|
+
}
|
|
3371
|
+
const v = a.value;
|
|
3372
|
+
switch (v.kind) {
|
|
3373
|
+
case "literal":
|
|
3374
|
+
attrParts.push(`${toHtmlAttrName(a.name)}="${v.value}"`);
|
|
3375
|
+
break;
|
|
3376
|
+
case "boolean-attr":
|
|
3377
|
+
attrParts.push(toHtmlAttrName(a.name));
|
|
3378
|
+
break;
|
|
3379
|
+
case "boolean-shorthand":
|
|
3380
|
+
case "jsx-children":
|
|
3381
|
+
break;
|
|
3382
|
+
case "expression":
|
|
3383
|
+
case "template": {
|
|
3384
|
+
const attrKey = node.slotId ? `${node.slotId}::${a.name}` : null;
|
|
3385
|
+
if (!attrKey || !safe.reactiveAttrKeys.has(attrKey))
|
|
3386
|
+
return null;
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
case "spread":
|
|
3390
|
+
return null;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
if (node.slotId)
|
|
3394
|
+
attrParts.push(`bf="${node.slotId}"`);
|
|
3395
|
+
const attrs = attrParts.join(" ");
|
|
3396
|
+
let children = "";
|
|
3397
|
+
for (const child of node.children) {
|
|
3398
|
+
const rendered = buildLoopSkeletonTemplate(child, safe);
|
|
3399
|
+
if (rendered === null)
|
|
3400
|
+
return null;
|
|
3401
|
+
children += rendered;
|
|
3402
|
+
}
|
|
3403
|
+
if (children || !VOID_ELEMENTS.has(node.tag)) {
|
|
3404
|
+
return `<${node.tag}${attrs ? " " + attrs : ""}>${children}</${node.tag}>`;
|
|
3405
|
+
}
|
|
3406
|
+
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
3407
|
+
}
|
|
3408
|
+
case "text":
|
|
3409
|
+
return node.value;
|
|
3410
|
+
case "expression":
|
|
3411
|
+
if (node.expr === "null" || node.expr === "undefined")
|
|
3412
|
+
return "";
|
|
3413
|
+
if (!node.slotId) {
|
|
3414
|
+
return null;
|
|
3415
|
+
}
|
|
3416
|
+
if (!safe.reactiveTextSlotIds.has(node.slotId))
|
|
3417
|
+
return null;
|
|
3418
|
+
return `<!--bf:${node.slotId}--><!--/-->`;
|
|
3419
|
+
case "fragment": {
|
|
3420
|
+
let out = "";
|
|
3421
|
+
for (const child of node.children) {
|
|
3422
|
+
const rendered = buildLoopSkeletonTemplate(child, safe);
|
|
3423
|
+
if (rendered === null)
|
|
3424
|
+
return null;
|
|
3425
|
+
out += rendered;
|
|
3426
|
+
}
|
|
3427
|
+
return out;
|
|
3428
|
+
}
|
|
3429
|
+
case "conditional":
|
|
3430
|
+
case "component":
|
|
3431
|
+
case "loop":
|
|
3432
|
+
case "if-statement":
|
|
3433
|
+
case "provider":
|
|
3434
|
+
case "async":
|
|
3435
|
+
case "slot":
|
|
3436
|
+
return null;
|
|
3437
|
+
default:
|
|
3438
|
+
return assertNever(node);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3155
3441
|
function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParams) {
|
|
3156
3442
|
const recurse = (n) => irToPlaceholderTemplate(n, restSpreadNames, loopDepth, loopParams);
|
|
3157
3443
|
const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
|
|
@@ -3551,7 +3837,23 @@ function generateCsrTemplate(node, inlinableConstants, ctx, insideLoop, restSpre
|
|
|
3551
3837
|
}
|
|
3552
3838
|
}
|
|
3553
3839
|
}
|
|
3554
|
-
|
|
3840
|
+
const effectiveUnsafeLocalNames = mergeCsrNullUnsafe(ctx, unsafeLocalNames);
|
|
3841
|
+
return generateCsrTemplateWithOpts(node, { inlinableConstants, restSpreadNames, propsObjectName, csrEnv, insideLoop, unsafeLocalNames: effectiveUnsafeLocalNames, deferredChildSlots, loopDepth: -1 });
|
|
3842
|
+
}
|
|
3843
|
+
function mergeCsrNullUnsafe(ctx, unsafeLocalNames) {
|
|
3844
|
+
let merged = null;
|
|
3845
|
+
let exemptNames = null;
|
|
3846
|
+
for (const [name, entry] of ctx.csrInlinable) {
|
|
3847
|
+
if (entry !== null || unsafeLocalNames?.has(name))
|
|
3848
|
+
continue;
|
|
3849
|
+
exemptNames ??= new Set(ctx.localConstants.filter((c) => c.isJsx || c.systemConstructKind).map((c) => c.name));
|
|
3850
|
+
if (exemptNames.has(name))
|
|
3851
|
+
continue;
|
|
3852
|
+
if (!merged)
|
|
3853
|
+
merged = new Set(unsafeLocalNames ?? []);
|
|
3854
|
+
merged.add(name);
|
|
3855
|
+
}
|
|
3856
|
+
return merged ?? unsafeLocalNames;
|
|
3555
3857
|
}
|
|
3556
3858
|
function buildCsrEnvForCtx(ctx, inlinableConstants, propsObjectName) {
|
|
3557
3859
|
const base = buildSignalMemoEnv(ctx.signals, ctx.memos, propsObjectName ?? null);
|
|
@@ -3582,8 +3884,8 @@ function propResolvesUnsafe(prop, env, unsafeLocalNames) {
|
|
|
3582
3884
|
}
|
|
3583
3885
|
if (!source)
|
|
3584
3886
|
return false;
|
|
3585
|
-
const { freeIdentifiers } = csrSubstitute(source, env);
|
|
3586
|
-
return setIntersects(
|
|
3887
|
+
const { freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env);
|
|
3888
|
+
return setIntersects(freeIdentifiers2, unsafeLocalNames);
|
|
3587
3889
|
}
|
|
3588
3890
|
function computeDeferredChildSlots(node, ctx, inlinableConstants, unsafeLocalNames, propsObjectName) {
|
|
3589
3891
|
const deferred = new Set;
|
|
@@ -3636,8 +3938,8 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
3636
3938
|
const source = templateExpr ?? expr;
|
|
3637
3939
|
if (!source)
|
|
3638
3940
|
return source;
|
|
3639
|
-
const { rewritten, freeIdentifiers } = csrSubstitute(source, env);
|
|
3640
|
-
if (unsafeLocalNames && unsafeLocalNames.size > 0 && setIntersects(
|
|
3941
|
+
const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env);
|
|
3942
|
+
if (unsafeLocalNames && unsafeLocalNames.size > 0 && setIntersects(freeIdentifiers2, unsafeLocalNames)) {
|
|
3641
3943
|
return UNSAFE_TEMPLATE_EXPR;
|
|
3642
3944
|
}
|
|
3643
3945
|
return applyPropsRewrite(rewritten, propsObjectName ?? null);
|
|
@@ -4139,6 +4441,15 @@ function createAnalyzerContext(sourceFile, filePath) {
|
|
|
4139
4441
|
checker: null,
|
|
4140
4442
|
componentBodyBlock: null,
|
|
4141
4443
|
getJS(node) {
|
|
4444
|
+
let ownSourceFile;
|
|
4445
|
+
try {
|
|
4446
|
+
ownSourceFile = node.getSourceFile();
|
|
4447
|
+
} catch {
|
|
4448
|
+
ownSourceFile = undefined;
|
|
4449
|
+
}
|
|
4450
|
+
if (ownSourceFile && ownSourceFile !== sourceFile) {
|
|
4451
|
+
return node.getText(ownSourceFile);
|
|
4452
|
+
}
|
|
4142
4453
|
return reconstructWithoutTypes(node, sourceFile, this.typeExcludeRanges);
|
|
4143
4454
|
}
|
|
4144
4455
|
};
|
|
@@ -6301,7 +6612,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
|
|
|
6301
6612
|
const baseValue = `${propsName}.${sourceKey}`;
|
|
6302
6613
|
const value2 = defaultValueExpr ? `${baseValue} ?? ${defaultValueExpr}` : baseValue;
|
|
6303
6614
|
const containsArrow2 = el.initializer ? nodeContainsArrow(el.initializer) : false;
|
|
6304
|
-
const
|
|
6615
|
+
const freeIdentifiers3 = el.initializer ? extractFreeIdentifiersFromNode(el.initializer) : new Set([propsName]);
|
|
6305
6616
|
ctx.localConstants.push({
|
|
6306
6617
|
name: localName,
|
|
6307
6618
|
value: value2,
|
|
@@ -6309,7 +6620,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
|
|
|
6309
6620
|
isExported,
|
|
6310
6621
|
type: null,
|
|
6311
6622
|
loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath),
|
|
6312
|
-
freeIdentifiers:
|
|
6623
|
+
freeIdentifiers: freeIdentifiers3,
|
|
6313
6624
|
containsArrow: containsArrow2 || undefined,
|
|
6314
6625
|
origin: { phase: "hydrate", scope: "init", effect: "pure" }
|
|
6315
6626
|
});
|
|
@@ -6393,7 +6704,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
|
|
|
6393
6704
|
} else if (value) {
|
|
6394
6705
|
type = inferTypeFromValue(value);
|
|
6395
6706
|
}
|
|
6396
|
-
const
|
|
6707
|
+
const freeIdentifiers2 = node.initializer ? extractFreeIdentifiersFromNode(node.initializer) : undefined;
|
|
6397
6708
|
const containsArrow = node.initializer ? nodeContainsArrow(node.initializer) : false;
|
|
6398
6709
|
const systemConstructKind = node.initializer ? getSystemConstructKind(node.initializer) : undefined;
|
|
6399
6710
|
let templateValue;
|
|
@@ -6442,7 +6753,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
|
|
|
6442
6753
|
isModule: isModule || undefined,
|
|
6443
6754
|
type,
|
|
6444
6755
|
loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath),
|
|
6445
|
-
freeIdentifiers,
|
|
6756
|
+
freeIdentifiers: freeIdentifiers2,
|
|
6446
6757
|
isJsx,
|
|
6447
6758
|
isJsxFunction: isJsxFunction || undefined,
|
|
6448
6759
|
containsArrow: containsArrow || undefined,
|
|
@@ -6492,6 +6803,7 @@ function extractProps(param, ctx) {
|
|
|
6492
6803
|
loc: getSourceLocation(param, ctx.sourceFile, ctx.filePath),
|
|
6493
6804
|
hasIgnoreDirective: ignored
|
|
6494
6805
|
};
|
|
6806
|
+
const memberTypes = param.type ? collectMemberTypes(param.type, ctx) : null;
|
|
6495
6807
|
for (const element of param.name.elements) {
|
|
6496
6808
|
if (ts8.isBindingElement(element) && ts8.isIdentifier(element.name)) {
|
|
6497
6809
|
const localName = element.name.text;
|
|
@@ -6500,10 +6812,12 @@ function extractProps(param, ctx) {
|
|
|
6500
6812
|
ctx.restPropsName = localName;
|
|
6501
6813
|
continue;
|
|
6502
6814
|
}
|
|
6815
|
+
const sourcePropName = element.propertyName && ts8.isIdentifier(element.propertyName) ? element.propertyName.text : localName;
|
|
6816
|
+
const resolvedType = memberTypes?.get(sourcePropName) ?? { kind: "unknown", raw: "unknown" };
|
|
6503
6817
|
const defaultContainsArrow = element.initializer ? nodeContainsArrow(element.initializer) : false;
|
|
6504
6818
|
ctx.propsParams.push({
|
|
6505
6819
|
name: localName,
|
|
6506
|
-
type:
|
|
6820
|
+
type: resolvedType,
|
|
6507
6821
|
optional: !!element.initializer,
|
|
6508
6822
|
defaultValue,
|
|
6509
6823
|
defaultContainsArrow: defaultContainsArrow || undefined
|
|
@@ -6564,6 +6878,37 @@ function collectKeysFromMembers(members, ctx) {
|
|
|
6564
6878
|
}
|
|
6565
6879
|
return keys;
|
|
6566
6880
|
}
|
|
6881
|
+
function collectMemberTypes(typeNode, ctx) {
|
|
6882
|
+
const isResolvablePrimitive = (info) => info.kind === "primitive" && (info.primitive === "string" || info.primitive === "number" || info.primitive === "boolean");
|
|
6883
|
+
const fromMembers = (members) => {
|
|
6884
|
+
const map = new Map;
|
|
6885
|
+
for (const member of members) {
|
|
6886
|
+
if (ts8.isPropertySignature(member) && member.name && member.type && !member.questionToken) {
|
|
6887
|
+
const info = typeNodeToTypeInfo(member.type, ctx.sourceFile);
|
|
6888
|
+
if (info && isResolvablePrimitive(info)) {
|
|
6889
|
+
map.set(member.name.getText(ctx.sourceFile), info);
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
return map;
|
|
6894
|
+
};
|
|
6895
|
+
if (ts8.isTypeLiteralNode(typeNode)) {
|
|
6896
|
+
return fromMembers(typeNode.members);
|
|
6897
|
+
}
|
|
6898
|
+
if (ts8.isTypeReferenceNode(typeNode)) {
|
|
6899
|
+
const typeName = typeNode.typeName.getText(ctx.sourceFile);
|
|
6900
|
+
const typeDecl = findTypeDeclaration(typeName, ctx.sourceFile);
|
|
6901
|
+
if (!typeDecl)
|
|
6902
|
+
return null;
|
|
6903
|
+
if (ts8.isInterfaceDeclaration(typeDecl)) {
|
|
6904
|
+
return fromMembers(typeDecl.members);
|
|
6905
|
+
}
|
|
6906
|
+
if (ts8.isTypeAliasDeclaration(typeDecl) && ts8.isTypeLiteralNode(typeDecl.type)) {
|
|
6907
|
+
return fromMembers(typeDecl.type.members);
|
|
6908
|
+
}
|
|
6909
|
+
}
|
|
6910
|
+
return null;
|
|
6911
|
+
}
|
|
6567
6912
|
function extractPropsFromType(typeNode, ctx) {
|
|
6568
6913
|
if (ts8.isTypeLiteralNode(typeNode)) {
|
|
6569
6914
|
extractPropsFromTypeMembers(typeNode.members, ctx);
|
|
@@ -7258,58 +7603,171 @@ function pickAttrMetaFromIR(src) {
|
|
|
7258
7603
|
};
|
|
7259
7604
|
}
|
|
7260
7605
|
|
|
7261
|
-
// src/
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
result.push(imp);
|
|
7606
|
+
// src/module-exports.ts
|
|
7607
|
+
function generateModuleExports(ir, extraInlineExported = new Set, rewriteRelativeImport) {
|
|
7608
|
+
const lines = [];
|
|
7609
|
+
for (const constant of ir.metadata.localConstants) {
|
|
7610
|
+
if (!constant.isExported)
|
|
7611
|
+
continue;
|
|
7612
|
+
const keyword = constant.declarationKind ?? "const";
|
|
7613
|
+
if (!constant.value) {
|
|
7614
|
+
lines.push(`export ${keyword} ${constant.name}`);
|
|
7271
7615
|
continue;
|
|
7272
7616
|
}
|
|
7273
|
-
const
|
|
7274
|
-
if (
|
|
7617
|
+
const value = constant.value.trim();
|
|
7618
|
+
if (/^createContext\b/.test(value) || /^new WeakMap\b/.test(value))
|
|
7275
7619
|
continue;
|
|
7276
|
-
|
|
7620
|
+
lines.push(`export ${keyword} ${constant.name} = ${constant.value}`);
|
|
7277
7621
|
}
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
return checker.getTypeAtLocation(node);
|
|
7287
|
-
}
|
|
7288
|
-
function isReactiveType(type) {
|
|
7289
|
-
return type.getProperty(REACTIVE_BRAND) !== undefined;
|
|
7290
|
-
}
|
|
7291
|
-
var NOT_REACTIVE = { isReactive: false, reason: { kind: "not-reactive" } };
|
|
7292
|
-
function safeGetText(node) {
|
|
7293
|
-
try {
|
|
7294
|
-
return node.getText();
|
|
7295
|
-
} catch {
|
|
7296
|
-
return "";
|
|
7622
|
+
for (const func of ir.metadata.localFunctions) {
|
|
7623
|
+
if (!func.isExported)
|
|
7624
|
+
continue;
|
|
7625
|
+
const params = func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
|
|
7626
|
+
const returnAnnotation = func.typedReturnType ? `: ${func.typedReturnType}` : "";
|
|
7627
|
+
const body = func.typedBody ?? func.body;
|
|
7628
|
+
const asyncKw = func.isAsync ? "async " : "";
|
|
7629
|
+
lines.push(`export ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
|
|
7297
7630
|
}
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
const
|
|
7311
|
-
|
|
7312
|
-
return {
|
|
7631
|
+
const inlineExported = collectInlineExportedNames(ir);
|
|
7632
|
+
for (const name of extraInlineExported)
|
|
7633
|
+
inlineExported.add(name);
|
|
7634
|
+
for (const block of ir.metadata.namedExports) {
|
|
7635
|
+
const isReexportFrom = block.source !== null;
|
|
7636
|
+
const survivingSpecs = block.specifiers.filter((spec) => {
|
|
7637
|
+
if (isReexportFrom)
|
|
7638
|
+
return true;
|
|
7639
|
+
return !(inlineExported.has(spec.name) && spec.alias == null);
|
|
7640
|
+
});
|
|
7641
|
+
if (survivingSpecs.length === 0)
|
|
7642
|
+
continue;
|
|
7643
|
+
const specText = survivingSpecs.map((s) => {
|
|
7644
|
+
const prefix = s.isTypeOnly ? "type " : "";
|
|
7645
|
+
return s.alias ? `${prefix}${s.name} as ${s.alias}` : `${prefix}${s.name}`;
|
|
7646
|
+
}).join(", ");
|
|
7647
|
+
const typeKw = block.isTypeOnly ? "type " : "";
|
|
7648
|
+
if (isReexportFrom) {
|
|
7649
|
+
const source = rewriteRelativeImport && block.source.startsWith(".") ? rewriteRelativeImport(block.source) : block.source;
|
|
7650
|
+
lines.push(`export ${typeKw}{ ${specText} } from '${source}'`);
|
|
7651
|
+
} else {
|
|
7652
|
+
lines.push(`export ${typeKw}{ ${specText} }`);
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
return lines.length > 0 ? lines.join(`
|
|
7656
|
+
`) : null;
|
|
7657
|
+
}
|
|
7658
|
+
function collectInlineExportedNames(ir) {
|
|
7659
|
+
const names = new Set;
|
|
7660
|
+
for (const c of ir.metadata.localConstants) {
|
|
7661
|
+
if (c.isExported)
|
|
7662
|
+
names.add(c.name);
|
|
7663
|
+
}
|
|
7664
|
+
for (const f of ir.metadata.localFunctions) {
|
|
7665
|
+
if (f.isExported)
|
|
7666
|
+
names.add(f.name);
|
|
7667
|
+
}
|
|
7668
|
+
if (ir.metadata.isExported && ir.metadata.componentName) {
|
|
7669
|
+
names.add(ir.metadata.componentName);
|
|
7670
|
+
}
|
|
7671
|
+
return names;
|
|
7672
|
+
}
|
|
7673
|
+
function formatParamWithType(p) {
|
|
7674
|
+
const rest = p.isRest ? "..." : "";
|
|
7675
|
+
const optional = p.optional ? "?" : "";
|
|
7676
|
+
const typeAnnotation = p.type?.raw && p.type.raw !== "unknown" ? `: ${p.type.raw}` : "";
|
|
7677
|
+
const defaultPart = p.defaultValue !== undefined ? ` = ${p.defaultValue}` : "";
|
|
7678
|
+
return `${rest}${p.name}${optional}${typeAnnotation}${defaultPart}`;
|
|
7679
|
+
}
|
|
7680
|
+
function findReachableNames(primaryRefs, declarations) {
|
|
7681
|
+
const allNames = new Set(declarations.map((d) => d.name));
|
|
7682
|
+
const bodyMap = new Map(declarations.map((d) => [d.name, d.body]));
|
|
7683
|
+
const reachable = new Set;
|
|
7684
|
+
const queue = [];
|
|
7685
|
+
for (const name of allNames) {
|
|
7686
|
+
if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
|
|
7687
|
+
reachable.add(name);
|
|
7688
|
+
queue.push(name);
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7691
|
+
while (queue.length > 0) {
|
|
7692
|
+
const current = queue.shift();
|
|
7693
|
+
const body = bodyMap.get(current) || "";
|
|
7694
|
+
for (const name of allNames) {
|
|
7695
|
+
if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
|
|
7696
|
+
reachable.add(name);
|
|
7697
|
+
queue.push(name);
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
}
|
|
7701
|
+
return reachable;
|
|
7702
|
+
}
|
|
7703
|
+
function extractFunctionParams(value) {
|
|
7704
|
+
const arrowMatch = value.match(/^(?:async\s*)?\(([^)]*)\)\s*(?::\s*[^=]+)?\s*=>/);
|
|
7705
|
+
if (arrowMatch) {
|
|
7706
|
+
return arrowMatch[1].split(",").map((p) => p.trim().split(":")[0].split("=")[0].trim()).filter(Boolean).join(", ");
|
|
7707
|
+
}
|
|
7708
|
+
const singleMatch = value.match(/^(?:async\s*)?(\w+)\s*=>/);
|
|
7709
|
+
if (singleMatch) {
|
|
7710
|
+
return singleMatch[1];
|
|
7711
|
+
}
|
|
7712
|
+
const funcMatch = value.match(/^(?:async\s*)?function\s*\w*\s*\(([^)]*)\)/);
|
|
7713
|
+
if (funcMatch) {
|
|
7714
|
+
return funcMatch[1].split(",").map((p) => p.trim().split(":")[0].split("=")[0].trim()).filter(Boolean).join(", ");
|
|
7715
|
+
}
|
|
7716
|
+
return "";
|
|
7717
|
+
}
|
|
7718
|
+
|
|
7719
|
+
// src/builtins.ts
|
|
7720
|
+
var CLIENT_BUILTIN_SOURCE = "@barefootjs/client";
|
|
7721
|
+
function isClientBuiltinName(name) {
|
|
7722
|
+
return name === "Async" || name === "Region";
|
|
7723
|
+
}
|
|
7724
|
+
function stripClientBuiltinImports(imports) {
|
|
7725
|
+
const result = [];
|
|
7726
|
+
for (const imp of imports) {
|
|
7727
|
+
if (imp.source !== CLIENT_BUILTIN_SOURCE || imp.isTypeOnly || imp.specifiers.length === 0) {
|
|
7728
|
+
result.push(imp);
|
|
7729
|
+
continue;
|
|
7730
|
+
}
|
|
7731
|
+
const kept = imp.specifiers.filter((spec) => spec.isDefault || spec.isNamespace || spec.isTypeOnly || !isClientBuiltinName(spec.name));
|
|
7732
|
+
if (kept.length === 0)
|
|
7733
|
+
continue;
|
|
7734
|
+
result.push(kept.length === imp.specifiers.length ? imp : { ...imp, specifiers: kept });
|
|
7735
|
+
}
|
|
7736
|
+
return result;
|
|
7737
|
+
}
|
|
7738
|
+
|
|
7739
|
+
// src/reactivity-checker.ts
|
|
7740
|
+
import ts9 from "typescript";
|
|
7741
|
+
var REACTIVE_BRAND = "__reactive";
|
|
7742
|
+
function queryType(checker, node) {
|
|
7743
|
+
incrementCounter("typeCheckerQueries");
|
|
7744
|
+
return checker.getTypeAtLocation(node);
|
|
7745
|
+
}
|
|
7746
|
+
function isReactiveType(type) {
|
|
7747
|
+
return type.getProperty(REACTIVE_BRAND) !== undefined;
|
|
7748
|
+
}
|
|
7749
|
+
var NOT_REACTIVE = { isReactive: false, reason: { kind: "not-reactive" } };
|
|
7750
|
+
function safeGetText(node) {
|
|
7751
|
+
try {
|
|
7752
|
+
return node.getText();
|
|
7753
|
+
} catch {
|
|
7754
|
+
return "";
|
|
7755
|
+
}
|
|
7756
|
+
}
|
|
7757
|
+
function analyze(node, checker) {
|
|
7758
|
+
if (ts9.isPropertyAccessExpression(node)) {
|
|
7759
|
+
try {
|
|
7760
|
+
const type = queryType(checker, node);
|
|
7761
|
+
if (isReactiveType(type)) {
|
|
7762
|
+
return {
|
|
7763
|
+
isReactive: true,
|
|
7764
|
+
reason: { kind: "brand", via: "property-access", nodeText: safeGetText(node) }
|
|
7765
|
+
};
|
|
7766
|
+
}
|
|
7767
|
+
} catch {}
|
|
7768
|
+
const sub = analyze(node.expression, checker);
|
|
7769
|
+
if (sub.isReactive) {
|
|
7770
|
+
return {
|
|
7313
7771
|
isReactive: true,
|
|
7314
7772
|
reason: {
|
|
7315
7773
|
kind: "child",
|
|
@@ -8379,6 +8837,7 @@ function transformExpression(node, ctx) {
|
|
|
8379
8837
|
return transformExpressionInner(expr, ctx, node, isClientOnly);
|
|
8380
8838
|
}
|
|
8381
8839
|
function transformExpressionInner(expr, ctx, node, isClientOnly) {
|
|
8840
|
+
expr = tryDesugarInterleaveTaggedTemplate(expr, ctx);
|
|
8382
8841
|
checkBareSignalOrMemoIdentifier(expr, ctx);
|
|
8383
8842
|
if (ts11.isIdentifier(expr)) {
|
|
8384
8843
|
const jsxNode = ctx.analyzer.jsxConstants.get(expr.text);
|
|
@@ -8920,11 +9379,10 @@ function isIteratorShapeCall(node) {
|
|
|
8920
9379
|
return { array: node.expression.expression, shape: name };
|
|
8921
9380
|
}
|
|
8922
9381
|
function extractSortComparator(callback, _method, ctx) {
|
|
8923
|
-
const
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
unsupportedReason: `Sort comparator '${raw}' is not a supported shape. Accepted:
|
|
9382
|
+
const outerRaw = ctx.getJS(callback);
|
|
9383
|
+
const unsupported = () => ({
|
|
9384
|
+
result: null,
|
|
9385
|
+
unsupportedReason: `Sort comparator '${outerRaw}' is not a supported shape. Accepted:
|
|
8928
9386
|
` + ` (a, b) => a - b
|
|
8929
9387
|
` + ` (a, b) => a.field - b.field
|
|
8930
9388
|
` + ` (a, b) => a.localeCompare(b)
|
|
@@ -8932,15 +9390,25 @@ function extractSortComparator(callback, _method, ctx) {
|
|
|
8932
9390
|
` + ` (a, b) => a.field > b.field ? 1 : a.field < b.field ? -1 : 0
|
|
8933
9391
|
` + ` any of the above '||'-chained for multi-key tie-breaks
|
|
8934
9392
|
` + `(reverse the operands for descending order).`
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
if (
|
|
9393
|
+
});
|
|
9394
|
+
let resolvedNode = callback;
|
|
9395
|
+
if (ts11.isIdentifier(callback)) {
|
|
9396
|
+
const resolved = resolveSortComparatorIdentifier(callback.text, ctx);
|
|
9397
|
+
if (!resolved) {
|
|
9398
|
+
return {
|
|
9399
|
+
result: null,
|
|
9400
|
+
unsupportedReason: `Sort comparator '${outerRaw}' could not be resolved to a local function — ` + `declare it in the same file or inline it.`
|
|
9401
|
+
};
|
|
9402
|
+
}
|
|
9403
|
+
resolvedNode = resolved;
|
|
9404
|
+
}
|
|
9405
|
+
if (!ts11.isArrowFunction(resolvedNode) && !ts11.isFunctionExpression(resolvedNode)) {
|
|
8938
9406
|
return {
|
|
8939
9407
|
result: null,
|
|
8940
9408
|
unsupportedReason: "Sort comparator must be an arrow function or function expression"
|
|
8941
9409
|
};
|
|
8942
9410
|
}
|
|
8943
|
-
const arrow = tsNodeToParsedExpr(
|
|
9411
|
+
const arrow = tsNodeToParsedExpr(resolvedNode);
|
|
8944
9412
|
if (arrow.kind !== "arrow" || arrow.params.length !== 2)
|
|
8945
9413
|
return unsupported();
|
|
8946
9414
|
if (sortComparatorFromArrow(arrow) === null)
|
|
@@ -8954,6 +9422,21 @@ function extractSortComparator(callback, _method, ctx) {
|
|
|
8954
9422
|
}
|
|
8955
9423
|
};
|
|
8956
9424
|
}
|
|
9425
|
+
function resolveSortComparatorIdentifier(name, ctx) {
|
|
9426
|
+
const constInfo = findLocalConst(name, ctx);
|
|
9427
|
+
const fnInfo = findLocalFunction(name, ctx);
|
|
9428
|
+
if (constInfo && fnInfo)
|
|
9429
|
+
return null;
|
|
9430
|
+
if (constInfo) {
|
|
9431
|
+
const ast = parseConstInitializer(constInfo);
|
|
9432
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
9433
|
+
}
|
|
9434
|
+
if (fnInfo) {
|
|
9435
|
+
const ast = parseFunctionInfoAsExpr(fnInfo);
|
|
9436
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
9437
|
+
}
|
|
9438
|
+
return null;
|
|
9439
|
+
}
|
|
8957
9440
|
function extractFilterPredicate(callback, ctx) {
|
|
8958
9441
|
if (!ts11.isArrowFunction(callback))
|
|
8959
9442
|
return { result: null };
|
|
@@ -9026,7 +9509,7 @@ function extractLoopParamBindings(pattern) {
|
|
|
9026
9509
|
const appendDotAccess = (prefix, key) => {
|
|
9027
9510
|
return isIdent(key) ? `${prefix}.${key}` : `${prefix}[${JSON.stringify(key)}]`;
|
|
9028
9511
|
};
|
|
9029
|
-
const walk = (p, prefix) => {
|
|
9512
|
+
const walk = (p, prefix, segments) => {
|
|
9030
9513
|
if (unsupported)
|
|
9031
9514
|
return;
|
|
9032
9515
|
if (ts11.isArrayBindingPattern(p)) {
|
|
@@ -9043,15 +9526,17 @@ function extractLoopParamBindings(pattern) {
|
|
|
9043
9526
|
bindings.push({
|
|
9044
9527
|
name: el.name.text,
|
|
9045
9528
|
path: prefix,
|
|
9046
|
-
rest: { kind: "array", from: index }
|
|
9529
|
+
rest: { kind: "array", from: index },
|
|
9530
|
+
segments
|
|
9047
9531
|
});
|
|
9048
9532
|
return;
|
|
9049
9533
|
}
|
|
9050
9534
|
const path = `${prefix}[${index}]`;
|
|
9535
|
+
const nextSegments = [...segments, { kind: "index", index }];
|
|
9051
9536
|
if (ts11.isIdentifier(el.name)) {
|
|
9052
|
-
bindings.push({ name: el.name.text, path });
|
|
9537
|
+
bindings.push({ name: el.name.text, path, segments: nextSegments });
|
|
9053
9538
|
} else {
|
|
9054
|
-
walk(el.name, path);
|
|
9539
|
+
walk(el.name, path, nextSegments);
|
|
9055
9540
|
}
|
|
9056
9541
|
}
|
|
9057
9542
|
return;
|
|
@@ -9068,7 +9553,8 @@ function extractLoopParamBindings(pattern) {
|
|
|
9068
9553
|
bindings.push({
|
|
9069
9554
|
name: el.name.text,
|
|
9070
9555
|
path: prefix,
|
|
9071
|
-
rest: { kind: "object", exclude: collectedKeys }
|
|
9556
|
+
rest: { kind: "object", exclude: collectedKeys },
|
|
9557
|
+
segments
|
|
9072
9558
|
});
|
|
9073
9559
|
return;
|
|
9074
9560
|
}
|
|
@@ -9091,17 +9577,19 @@ function extractLoopParamBindings(pattern) {
|
|
|
9091
9577
|
unsupported = true;
|
|
9092
9578
|
return;
|
|
9093
9579
|
}
|
|
9094
|
-
|
|
9580
|
+
const keyIsIdent = isIdent(keyText);
|
|
9581
|
+
collectedKeys.push({ key: keyText, isIdent: keyIsIdent });
|
|
9095
9582
|
const path = appendDotAccess(prefix, keyText);
|
|
9583
|
+
const nextSegments = [...segments, { kind: "field", key: keyText, isIdent: keyIsIdent }];
|
|
9096
9584
|
if (ts11.isIdentifier(el.name)) {
|
|
9097
|
-
bindings.push({ name: el.name.text, path });
|
|
9585
|
+
bindings.push({ name: el.name.text, path, segments: nextSegments });
|
|
9098
9586
|
} else {
|
|
9099
|
-
walk(el.name, path);
|
|
9587
|
+
walk(el.name, path, nextSegments);
|
|
9100
9588
|
}
|
|
9101
9589
|
}
|
|
9102
9590
|
};
|
|
9103
9591
|
if (ts11.isArrayBindingPattern(pattern) || ts11.isObjectBindingPattern(pattern)) {
|
|
9104
|
-
walk(pattern, "");
|
|
9592
|
+
walk(pattern, "", []);
|
|
9105
9593
|
if (unsupported)
|
|
9106
9594
|
return { unsupported: true };
|
|
9107
9595
|
return bindings;
|
|
@@ -9843,14 +10331,14 @@ function processAttributes(attributes, ctx) {
|
|
|
9843
10331
|
clientOnly = true;
|
|
9844
10332
|
}
|
|
9845
10333
|
}
|
|
9846
|
-
const
|
|
10334
|
+
const freeIdentifiers2 = attrFreeIdentifiers(attr);
|
|
9847
10335
|
attrs.push({
|
|
9848
10336
|
name,
|
|
9849
10337
|
value,
|
|
9850
10338
|
clientOnly,
|
|
9851
10339
|
loc: getSourceLocation(attr, ctx.sourceFile, ctx.filePath),
|
|
9852
10340
|
...computeReactivityFlags(attr, ctx),
|
|
9853
|
-
...
|
|
10341
|
+
...freeIdentifiers2 !== undefined && { freeIdentifiers: freeIdentifiers2 }
|
|
9854
10342
|
});
|
|
9855
10343
|
}
|
|
9856
10344
|
return { attrs, events, ref };
|
|
@@ -9870,6 +10358,7 @@ function getAttributeValue(attr, ctx) {
|
|
|
9870
10358
|
expr = branchInit;
|
|
9871
10359
|
}
|
|
9872
10360
|
}
|
|
10361
|
+
expr = tryDesugarInterleaveTaggedTemplate(expr, ctx);
|
|
9873
10362
|
if (ts11.isAwaitExpression(expr)) {
|
|
9874
10363
|
ctx.analyzer.errors.push(createError(ErrorCodes.STAGE_AWAIT_IN_TEMPLATE, getSourceLocation(expr, ctx.sourceFile, ctx.filePath)));
|
|
9875
10364
|
return AttrValueOf.expression("undefined");
|
|
@@ -10005,6 +10494,14 @@ function findLocalConst(name, ctx) {
|
|
|
10005
10494
|
const pool = fnScoped.length > 0 ? fnScoped : matches;
|
|
10006
10495
|
return pool[pool.length - 1];
|
|
10007
10496
|
}
|
|
10497
|
+
function findLocalFunction(name, ctx) {
|
|
10498
|
+
const matches = ctx.analyzer.localFunctions.filter((f) => f.name === name);
|
|
10499
|
+
if (matches.length === 0)
|
|
10500
|
+
return;
|
|
10501
|
+
const fnScoped = matches.filter((f) => !f.isModule);
|
|
10502
|
+
const pool = fnScoped.length > 0 ? fnScoped : matches;
|
|
10503
|
+
return pool[pool.length - 1];
|
|
10504
|
+
}
|
|
10008
10505
|
function isDynamicTagLocal(name, ctx) {
|
|
10009
10506
|
if (!hasDynamicTagBinding(name, ctx.sourceFile))
|
|
10010
10507
|
return false;
|
|
@@ -10099,6 +10596,147 @@ function parseConstInitializerImpl(c) {
|
|
|
10099
10596
|
function astText(node) {
|
|
10100
10597
|
return node.getText(node.getSourceFile());
|
|
10101
10598
|
}
|
|
10599
|
+
var functionInfoExprCache = new WeakMap;
|
|
10600
|
+
function parseFunctionInfoAsExpr(fn) {
|
|
10601
|
+
const cached = functionInfoExprCache.get(fn);
|
|
10602
|
+
if (cached !== undefined)
|
|
10603
|
+
return cached;
|
|
10604
|
+
const result = parseFunctionInfoAsExprImpl(fn);
|
|
10605
|
+
functionInfoExprCache.set(fn, result);
|
|
10606
|
+
return result;
|
|
10607
|
+
}
|
|
10608
|
+
function parseFunctionInfoAsExprImpl(fn) {
|
|
10609
|
+
if (!fn.body)
|
|
10610
|
+
return null;
|
|
10611
|
+
const params = fn.typedParams !== undefined ? fn.typedParams : fn.params.map(formatParamWithType).join(", ");
|
|
10612
|
+
const body = fn.typedBody ?? fn.body;
|
|
10613
|
+
const wrapped = `const __bf_resolve_fn__ = function(${params}) ${body}`;
|
|
10614
|
+
const sf = ts11.createSourceFile("__bf_resolve_fn.ts", wrapped, ts11.ScriptTarget.Latest, true, ts11.ScriptKind.TS);
|
|
10615
|
+
const stmt = sf.statements[0];
|
|
10616
|
+
if (!stmt || !ts11.isVariableStatement(stmt))
|
|
10617
|
+
return null;
|
|
10618
|
+
const decl = stmt.declarationList.declarations[0];
|
|
10619
|
+
if (!decl?.initializer)
|
|
10620
|
+
return null;
|
|
10621
|
+
return decl.initializer;
|
|
10622
|
+
}
|
|
10623
|
+
function tryDesugarInterleaveTaggedTemplate(expr, ctx) {
|
|
10624
|
+
if (!ts11.isTaggedTemplateExpression(expr))
|
|
10625
|
+
return expr;
|
|
10626
|
+
if (!ts11.isIdentifier(expr.tag))
|
|
10627
|
+
return expr;
|
|
10628
|
+
const resolvedTag = resolveInterleaveTagIdentifier(expr.tag.text, ctx);
|
|
10629
|
+
if (!resolvedTag)
|
|
10630
|
+
return expr;
|
|
10631
|
+
if (!isInterleaveTagFunction(resolvedTag))
|
|
10632
|
+
return expr;
|
|
10633
|
+
const rewritten = buildUntaggedTemplateLiteral(expr, ctx);
|
|
10634
|
+
return rewritten ?? expr;
|
|
10635
|
+
}
|
|
10636
|
+
function resolveInterleaveTagIdentifier(name, ctx) {
|
|
10637
|
+
const constInfo = findLocalConst(name, ctx);
|
|
10638
|
+
const fnInfo = findLocalFunction(name, ctx);
|
|
10639
|
+
if (constInfo && fnInfo)
|
|
10640
|
+
return null;
|
|
10641
|
+
if (constInfo) {
|
|
10642
|
+
const ast = parseConstInitializer(constInfo);
|
|
10643
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
10644
|
+
}
|
|
10645
|
+
if (fnInfo) {
|
|
10646
|
+
const ast = parseFunctionInfoAsExpr(fnInfo);
|
|
10647
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
10648
|
+
}
|
|
10649
|
+
return null;
|
|
10650
|
+
}
|
|
10651
|
+
function isInterleaveTagFunction(fn) {
|
|
10652
|
+
if (!ts11.isArrowFunction(fn) && !ts11.isFunctionExpression(fn))
|
|
10653
|
+
return false;
|
|
10654
|
+
if (fn.parameters.length !== 2)
|
|
10655
|
+
return false;
|
|
10656
|
+
const [partsParam, argsParam] = fn.parameters;
|
|
10657
|
+
if (!ts11.isIdentifier(partsParam.name) || partsParam.dotDotDotToken)
|
|
10658
|
+
return false;
|
|
10659
|
+
if (!ts11.isIdentifier(argsParam.name) || !argsParam.dotDotDotToken)
|
|
10660
|
+
return false;
|
|
10661
|
+
const parsed = tsNodeToParsedExpr(fn);
|
|
10662
|
+
if (parsed.kind !== "arrow")
|
|
10663
|
+
return false;
|
|
10664
|
+
return isInterleaveReduceCall(parsed.body, partsParam.name.text, argsParam.name.text);
|
|
10665
|
+
}
|
|
10666
|
+
function isInterleaveReduceCall(body, partsName, argsName) {
|
|
10667
|
+
if (body.kind !== "call" || body.args.length !== 2)
|
|
10668
|
+
return false;
|
|
10669
|
+
const { callee, args } = body;
|
|
10670
|
+
if (callee.kind !== "member" || callee.computed || callee.property !== "reduce")
|
|
10671
|
+
return false;
|
|
10672
|
+
if (callee.object.kind !== "identifier" || callee.object.name !== partsName)
|
|
10673
|
+
return false;
|
|
10674
|
+
const [callback, init] = args;
|
|
10675
|
+
if (init.kind !== "literal" || init.literalType !== "string" || init.value !== "")
|
|
10676
|
+
return false;
|
|
10677
|
+
if (callback.kind !== "arrow" || callback.params.length !== 3)
|
|
10678
|
+
return false;
|
|
10679
|
+
const [acc, p, i] = callback.params;
|
|
10680
|
+
return isInterleaveReduceCallbackBody(callback.body, acc, p, i, argsName);
|
|
10681
|
+
}
|
|
10682
|
+
function isInterleaveReduceCallbackBody(body, acc, p, i, argsName) {
|
|
10683
|
+
if (body.kind !== "binary" || body.op !== "+")
|
|
10684
|
+
return false;
|
|
10685
|
+
const { left, right } = body;
|
|
10686
|
+
if (left.kind !== "binary" || left.op !== "+")
|
|
10687
|
+
return false;
|
|
10688
|
+
if (left.left.kind !== "identifier" || left.left.name !== acc)
|
|
10689
|
+
return false;
|
|
10690
|
+
if (left.right.kind !== "identifier" || left.right.name !== p)
|
|
10691
|
+
return false;
|
|
10692
|
+
return isInterleaveSpanExpr(right, i, argsName);
|
|
10693
|
+
}
|
|
10694
|
+
function isInterleaveSpanExpr(expr, i, argsName) {
|
|
10695
|
+
let inner = expr;
|
|
10696
|
+
if (inner.kind === "call" && inner.args.length === 1 && inner.callee.kind === "identifier" && inner.callee.name === "String") {
|
|
10697
|
+
inner = inner.args[0];
|
|
10698
|
+
}
|
|
10699
|
+
if (inner.kind !== "logical" || inner.op !== "??")
|
|
10700
|
+
return false;
|
|
10701
|
+
if (inner.right.kind !== "literal" || inner.right.literalType !== "string" || inner.right.value !== "") {
|
|
10702
|
+
return false;
|
|
10703
|
+
}
|
|
10704
|
+
const idx = inner.left;
|
|
10705
|
+
if (idx.kind !== "index-access")
|
|
10706
|
+
return false;
|
|
10707
|
+
if (idx.object.kind !== "identifier" || idx.object.name !== argsName)
|
|
10708
|
+
return false;
|
|
10709
|
+
if (idx.index.kind !== "identifier" || idx.index.name !== i)
|
|
10710
|
+
return false;
|
|
10711
|
+
return true;
|
|
10712
|
+
}
|
|
10713
|
+
function buildUntaggedTemplateLiteral(node, ctx) {
|
|
10714
|
+
const template = node.template;
|
|
10715
|
+
let text;
|
|
10716
|
+
if (ts11.isNoSubstitutionTemplateLiteral(template)) {
|
|
10717
|
+
text = "`" + (template.rawText ?? template.text) + "`";
|
|
10718
|
+
} else {
|
|
10719
|
+
let body = template.head.rawText ?? template.head.text;
|
|
10720
|
+
for (const span of template.templateSpans) {
|
|
10721
|
+
const spanText = ctx.getJS(span.expression);
|
|
10722
|
+
body += "${(" + spanText + ") ?? ''}";
|
|
10723
|
+
body += span.literal.rawText ?? span.literal.text;
|
|
10724
|
+
}
|
|
10725
|
+
text = "`" + body + "`";
|
|
10726
|
+
}
|
|
10727
|
+
const wrapped = `const __bf_resolve_tagged__ = (${text})`;
|
|
10728
|
+
const sf = ts11.createSourceFile("__bf_resolve_tagged.tsx", wrapped, ts11.ScriptTarget.Latest, true, ts11.ScriptKind.TSX);
|
|
10729
|
+
const stmt = sf.statements[0];
|
|
10730
|
+
if (!stmt || !ts11.isVariableStatement(stmt))
|
|
10731
|
+
return null;
|
|
10732
|
+
const decl = stmt.declarationList.declarations[0];
|
|
10733
|
+
if (!decl?.initializer)
|
|
10734
|
+
return null;
|
|
10735
|
+
const result = ts11.isParenthesizedExpression(decl.initializer) ? decl.initializer.expression : decl.initializer;
|
|
10736
|
+
if (!ts11.isTemplateExpression(result) && !ts11.isNoSubstitutionTemplateLiteral(result))
|
|
10737
|
+
return null;
|
|
10738
|
+
return result;
|
|
10739
|
+
}
|
|
10102
10740
|
function parseTernary(expr, ctx) {
|
|
10103
10741
|
const whenTrueValue = getStringValue(expr.whenTrue);
|
|
10104
10742
|
const whenFalseValue = getStringValue(expr.whenFalse);
|
|
@@ -10173,14 +10811,14 @@ function processComponentProps(attributes, ctx) {
|
|
|
10173
10811
|
clientOnly = true;
|
|
10174
10812
|
}
|
|
10175
10813
|
}
|
|
10176
|
-
const
|
|
10814
|
+
const freeIdentifiers2 = attrFreeIdentifiers(attr);
|
|
10177
10815
|
props.push({
|
|
10178
10816
|
name,
|
|
10179
10817
|
value,
|
|
10180
10818
|
clientOnly,
|
|
10181
10819
|
loc: getSourceLocation(attr, ctx.sourceFile, ctx.filePath),
|
|
10182
10820
|
...computeReactivityFlags(attr, ctx),
|
|
10183
|
-
...
|
|
10821
|
+
...freeIdentifiers2 !== undefined && { freeIdentifiers: freeIdentifiers2 }
|
|
10184
10822
|
});
|
|
10185
10823
|
}
|
|
10186
10824
|
return props;
|
|
@@ -10596,7 +11234,7 @@ function decideWrapForChildProp(expandedValue, ctx, prop) {
|
|
|
10596
11234
|
return { wrap: true, reason: "props-access" };
|
|
10597
11235
|
return decideWrapForAttr(expandedValue, ctx, prop);
|
|
10598
11236
|
}
|
|
10599
|
-
function needsEffectWrapper(expr, ctx,
|
|
11237
|
+
function needsEffectWrapper(expr, ctx, freeIdentifiers2) {
|
|
10600
11238
|
for (const signal of ctx.signals) {
|
|
10601
11239
|
if (new RegExp(`\\b${signal.getter}\\s*\\(`).test(expr)) {
|
|
10602
11240
|
return true;
|
|
@@ -10610,7 +11248,7 @@ function needsEffectWrapper(expr, ctx, freeIdentifiers) {
|
|
|
10610
11248
|
for (const prop of ctx.propsParams) {
|
|
10611
11249
|
if (prop.name === "children")
|
|
10612
11250
|
continue;
|
|
10613
|
-
if (
|
|
11251
|
+
if (freeIdentifiers2 ? freeIdentifiers2.has(prop.name) : tokenContainsIdent(expr, prop.name)) {
|
|
10614
11252
|
return true;
|
|
10615
11253
|
}
|
|
10616
11254
|
}
|
|
@@ -10621,8 +11259,8 @@ function needsEffectWrapper(expr, ctx, freeIdentifiers) {
|
|
|
10621
11259
|
}
|
|
10622
11260
|
return false;
|
|
10623
11261
|
}
|
|
10624
|
-
function classifyReactivity(expr, ctx, loopParam, loopParamBindings,
|
|
10625
|
-
const has = (name) =>
|
|
11262
|
+
function classifyReactivity(expr, ctx, loopParam, loopParamBindings, freeIdentifiers2) {
|
|
11263
|
+
const has = (name) => freeIdentifiers2 ? freeIdentifiers2.has(name) : tokenContainsIdent(expr, name);
|
|
10626
11264
|
if (loopParamBindings && loopParamBindings.length > 0) {
|
|
10627
11265
|
for (const b of loopParamBindings) {
|
|
10628
11266
|
if (has(b.name)) {
|
|
@@ -10632,7 +11270,7 @@ function classifyReactivity(expr, ctx, loopParam, loopParamBindings, freeIdentif
|
|
|
10632
11270
|
} else if (loopParam && has(loopParam)) {
|
|
10633
11271
|
return { kind: "loop-param", param: loopParam };
|
|
10634
11272
|
}
|
|
10635
|
-
if (needsEffectWrapper(expr, ctx,
|
|
11273
|
+
if (needsEffectWrapper(expr, ctx, freeIdentifiers2)) {
|
|
10636
11274
|
return { kind: "signal-or-memo-or-prop" };
|
|
10637
11275
|
}
|
|
10638
11276
|
return { kind: "none" };
|
|
@@ -11214,6 +11852,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11214
11852
|
const { useElementReconciliation, innerLoops } = decideLoopRendering(l, siblingOffsets, ctx);
|
|
11215
11853
|
let template = "";
|
|
11216
11854
|
let staticItemTemplate;
|
|
11855
|
+
let skeletonTemplate;
|
|
11217
11856
|
if (l.childComponent) {
|
|
11218
11857
|
template = "";
|
|
11219
11858
|
if (l.isStaticArray && l.children[0]) {
|
|
@@ -11224,6 +11863,11 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11224
11863
|
template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec);
|
|
11225
11864
|
if (l.isStaticArray) {
|
|
11226
11865
|
staticItemTemplate = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0);
|
|
11866
|
+
} else if (!useElementReconciliation && !l.bodyIsMultiRoot && !l.bodyIsItemConditional) {
|
|
11867
|
+
skeletonTemplate = buildLoopSkeletonTemplate(l.children[0], {
|
|
11868
|
+
reactiveAttrKeys: new Set(bindings.reactiveAttrs.map((a) => `${a.childSlotId}::${a.attrName}`)),
|
|
11869
|
+
reactiveTextSlotIds: new Set(bindings.reactiveTexts.map((t) => t.slotId))
|
|
11870
|
+
}) ?? undefined;
|
|
11227
11871
|
}
|
|
11228
11872
|
}
|
|
11229
11873
|
ctx.loopElements.push({
|
|
@@ -11241,6 +11885,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11241
11885
|
iterationShape: l.iterationShape,
|
|
11242
11886
|
template,
|
|
11243
11887
|
staticItemTemplate,
|
|
11888
|
+
skeletonTemplate,
|
|
11244
11889
|
childEventHandlers: childHandlers,
|
|
11245
11890
|
bindings,
|
|
11246
11891
|
childComponent: l.childComponent,
|
|
@@ -12129,31 +12774,70 @@ function collectComponentNames(node) {
|
|
|
12129
12774
|
|
|
12130
12775
|
// src/relocate.ts
|
|
12131
12776
|
import ts13 from "typescript";
|
|
12132
|
-
|
|
12133
|
-
|
|
12777
|
+
|
|
12778
|
+
// src/lowering-registry.ts
|
|
12779
|
+
var plugins = [];
|
|
12780
|
+
function registerLoweringPlugin(plugin) {
|
|
12781
|
+
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
12782
|
+
if (existing >= 0)
|
|
12783
|
+
plugins[existing] = plugin;
|
|
12784
|
+
else
|
|
12785
|
+
plugins.push(plugin);
|
|
12134
12786
|
}
|
|
12135
|
-
function
|
|
12136
|
-
|
|
12137
|
-
function visit3(n, parent) {
|
|
12138
|
-
if (ts13.isIdentifier(n)) {
|
|
12139
|
-
if (parent && ts13.isPropertyAccessExpression(parent) && parent.name === n)
|
|
12140
|
-
return;
|
|
12141
|
-
if (parent && ts13.isPropertyAssignment(parent) && parent.name === n)
|
|
12142
|
-
return;
|
|
12143
|
-
if (parent && ts13.isShorthandPropertyAssignment(parent) && parent.name === n)
|
|
12144
|
-
return;
|
|
12145
|
-
const list = refs.get(n.text) ?? [];
|
|
12146
|
-
list.push(n);
|
|
12147
|
-
refs.set(n.text, list);
|
|
12148
|
-
return;
|
|
12149
|
-
}
|
|
12150
|
-
ts13.forEachChild(n, (child) => visit3(child, n));
|
|
12151
|
-
}
|
|
12152
|
-
visit3(node);
|
|
12153
|
-
return refs;
|
|
12787
|
+
function getLoweringPlugins() {
|
|
12788
|
+
return [...plugins];
|
|
12154
12789
|
}
|
|
12155
|
-
function
|
|
12156
|
-
|
|
12790
|
+
function prepareLoweringMatchers(metadata) {
|
|
12791
|
+
const matchers = [];
|
|
12792
|
+
for (const plugin of plugins) {
|
|
12793
|
+
const matcher = plugin.prepare(metadata);
|
|
12794
|
+
if (matcher)
|
|
12795
|
+
matchers.push(matcher);
|
|
12796
|
+
}
|
|
12797
|
+
return matchers;
|
|
12798
|
+
}
|
|
12799
|
+
function matchLoweringCall(callee, args, metadata) {
|
|
12800
|
+
for (const matcher of prepareLoweringMatchers(metadata)) {
|
|
12801
|
+
const node = matcher(callee, args);
|
|
12802
|
+
if (node)
|
|
12803
|
+
return node;
|
|
12804
|
+
}
|
|
12805
|
+
return null;
|
|
12806
|
+
}
|
|
12807
|
+
function isValidHelperId(helper) {
|
|
12808
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
|
|
12809
|
+
}
|
|
12810
|
+
function __resetLoweringPluginsForTest(next = []) {
|
|
12811
|
+
plugins.length = 0;
|
|
12812
|
+
plugins.push(...next);
|
|
12813
|
+
}
|
|
12814
|
+
|
|
12815
|
+
// src/relocate.ts
|
|
12816
|
+
function classify(name, env) {
|
|
12817
|
+
return env.bindings.get(name) ?? "global";
|
|
12818
|
+
}
|
|
12819
|
+
function collectFreeRefs(node) {
|
|
12820
|
+
const refs = new Map;
|
|
12821
|
+
function visit3(n, parent) {
|
|
12822
|
+
if (ts13.isIdentifier(n)) {
|
|
12823
|
+
if (parent && ts13.isPropertyAccessExpression(parent) && parent.name === n)
|
|
12824
|
+
return;
|
|
12825
|
+
if (parent && ts13.isPropertyAssignment(parent) && parent.name === n)
|
|
12826
|
+
return;
|
|
12827
|
+
if (parent && ts13.isShorthandPropertyAssignment(parent) && parent.name === n)
|
|
12828
|
+
return;
|
|
12829
|
+
const list = refs.get(n.text) ?? [];
|
|
12830
|
+
list.push(n);
|
|
12831
|
+
refs.set(n.text, list);
|
|
12832
|
+
return;
|
|
12833
|
+
}
|
|
12834
|
+
ts13.forEachChild(n, (child) => visit3(child, n));
|
|
12835
|
+
}
|
|
12836
|
+
visit3(node);
|
|
12837
|
+
return refs;
|
|
12838
|
+
}
|
|
12839
|
+
function decideAction(kind, toScope, env, name) {
|
|
12840
|
+
if (isVisibleIn(toScope, kind)) {
|
|
12157
12841
|
return { action: "pass-through", rewrittenAs: name };
|
|
12158
12842
|
}
|
|
12159
12843
|
if (kind === "prop" && toScope === "template") {
|
|
@@ -12163,6 +12847,14 @@ function decideAction(kind, toScope, env, name) {
|
|
|
12163
12847
|
return { action: "lift-to-prop", rewrittenAs: `${PROPS_PARAM}.${name}` };
|
|
12164
12848
|
}
|
|
12165
12849
|
if ((kind === "init-local" || kind === "sub-init-local") && toScope === "template") {
|
|
12850
|
+
const aliasTarget = env.aliasTargets?.get(name);
|
|
12851
|
+
if (aliasTarget !== undefined) {
|
|
12852
|
+
const targetLeftmost = aliasTarget.includes(".") ? aliasTarget.split(".")[0] : aliasTarget;
|
|
12853
|
+
const targetKind = classify(targetLeftmost, env);
|
|
12854
|
+
if (isVisibleIn(toScope, targetKind)) {
|
|
12855
|
+
return { action: "inline", rewrittenAs: aliasTarget };
|
|
12856
|
+
}
|
|
12857
|
+
}
|
|
12166
12858
|
const inlineForm = env.inlinable.get(name);
|
|
12167
12859
|
if (inlineForm !== undefined) {
|
|
12168
12860
|
return { action: "inline", rewrittenAs: inlineForm };
|
|
@@ -12242,6 +12934,8 @@ function isInlinableInTemplate(value, env) {
|
|
|
12242
12934
|
return { ok: true, rewrittenValue: r.text, decisions: r.decisions };
|
|
12243
12935
|
}
|
|
12244
12936
|
function getCalleeIdentifierPath(callee) {
|
|
12937
|
+
if (ts13.isParenthesizedExpression(callee))
|
|
12938
|
+
return getCalleeIdentifierPath(callee.expression);
|
|
12245
12939
|
if (ts13.isIdentifier(callee))
|
|
12246
12940
|
return callee.text;
|
|
12247
12941
|
if (ts13.isPropertyAccessExpression(callee)) {
|
|
@@ -12253,6 +12947,8 @@ function getCalleeIdentifierPath(callee) {
|
|
|
12253
12947
|
return null;
|
|
12254
12948
|
}
|
|
12255
12949
|
function getCalleeLeftmostIdentifier(callee) {
|
|
12950
|
+
if (ts13.isParenthesizedExpression(callee))
|
|
12951
|
+
return getCalleeLeftmostIdentifier(callee.expression);
|
|
12256
12952
|
if (ts13.isIdentifier(callee))
|
|
12257
12953
|
return callee.text;
|
|
12258
12954
|
if (ts13.isPropertyAccessExpression(callee)) {
|
|
@@ -12266,20 +12962,39 @@ var REGISTRY_SAFE_BINDING_KINDS = new Set([
|
|
|
12266
12962
|
"module-local"
|
|
12267
12963
|
]);
|
|
12268
12964
|
function isCallAcceptedByAdapter(call, env) {
|
|
12269
|
-
const
|
|
12270
|
-
if (
|
|
12965
|
+
const originalPath = getCalleeIdentifierPath(call.expression);
|
|
12966
|
+
if (originalPath === null)
|
|
12271
12967
|
return false;
|
|
12272
12968
|
const leftmost = getCalleeLeftmostIdentifier(call.expression);
|
|
12969
|
+
let resolvedPath = originalPath;
|
|
12970
|
+
let resolvedLeftmost = leftmost;
|
|
12273
12971
|
if (leftmost !== null) {
|
|
12274
|
-
const
|
|
12972
|
+
const aliasTarget = env.aliasTargets?.get(leftmost);
|
|
12973
|
+
if (aliasTarget !== undefined) {
|
|
12974
|
+
resolvedPath = originalPath === leftmost ? aliasTarget : `${aliasTarget}${originalPath.slice(leftmost.length)}`;
|
|
12975
|
+
resolvedLeftmost = aliasTarget.includes(".") ? aliasTarget.split(".")[0] : aliasTarget;
|
|
12976
|
+
}
|
|
12977
|
+
}
|
|
12978
|
+
if (resolvedLeftmost !== null) {
|
|
12979
|
+
const kind = env.bindings.get(resolvedLeftmost);
|
|
12275
12980
|
if (kind !== undefined && !REGISTRY_SAFE_BINDING_KINDS.has(kind)) {
|
|
12276
12981
|
return false;
|
|
12277
12982
|
}
|
|
12278
12983
|
}
|
|
12279
|
-
if (env.templatePrimitives && env.templatePrimitives[
|
|
12984
|
+
if (env.templatePrimitives && env.templatePrimitives[resolvedPath])
|
|
12280
12985
|
return true;
|
|
12281
|
-
if (env.acceptsTemplateCall && env.acceptsTemplateCall(
|
|
12986
|
+
if (env.acceptsTemplateCall && env.acceptsTemplateCall(resolvedPath))
|
|
12282
12987
|
return true;
|
|
12988
|
+
if (env.loweringMatchers && env.loweringMatchers.length > 0) {
|
|
12989
|
+
const parsed = tsNodeToParsedExpr(call);
|
|
12990
|
+
if (parsed.kind === "call") {
|
|
12991
|
+
const calleeForMatch = resolvedPath !== originalPath && !resolvedPath.includes(".") && parsed.callee.kind === "identifier" ? { kind: "identifier", name: resolvedPath } : parsed.callee;
|
|
12992
|
+
for (const matcher of env.loweringMatchers) {
|
|
12993
|
+
if (matcher(calleeForMatch, parsed.args))
|
|
12994
|
+
return true;
|
|
12995
|
+
}
|
|
12996
|
+
}
|
|
12997
|
+
}
|
|
12283
12998
|
return false;
|
|
12284
12999
|
}
|
|
12285
13000
|
function parseExpressionNode(text) {
|
|
@@ -12425,6 +13140,7 @@ function buildRelocateEnvFromIR(metadata, options) {
|
|
|
12425
13140
|
env.templatePrimitives = options.templatePrimitives;
|
|
12426
13141
|
if (options?.acceptsTemplateCall)
|
|
12427
13142
|
env.acceptsTemplateCall = options.acceptsTemplateCall;
|
|
13143
|
+
env.loweringMatchers = prepareLoweringMatchers(metadata);
|
|
12428
13144
|
return env;
|
|
12429
13145
|
}
|
|
12430
13146
|
function buildRelocateEnvFromFields(src) {
|
|
@@ -12473,14 +13189,35 @@ function buildRelocateEnvFromFields(src) {
|
|
|
12473
13189
|
if (kind === "prop")
|
|
12474
13190
|
propsForLift.add(name);
|
|
12475
13191
|
}
|
|
13192
|
+
const aliasTargets = new Map;
|
|
13193
|
+
for (const c of src.localConstants) {
|
|
13194
|
+
const kind = bindings.get(c.name);
|
|
13195
|
+
if (kind !== "init-local" && kind !== "module-local")
|
|
13196
|
+
continue;
|
|
13197
|
+
const target = identifierPathFromParsed(c.parsed);
|
|
13198
|
+
if (target !== null)
|
|
13199
|
+
aliasTargets.set(c.name, target);
|
|
13200
|
+
}
|
|
12476
13201
|
return {
|
|
12477
13202
|
bindings,
|
|
12478
13203
|
inlinable: new Map,
|
|
12479
13204
|
propsForLift,
|
|
12480
13205
|
propsObjectName,
|
|
12481
|
-
allowFallback: true
|
|
13206
|
+
allowFallback: true,
|
|
13207
|
+
aliasTargets
|
|
12482
13208
|
};
|
|
12483
13209
|
}
|
|
13210
|
+
function identifierPathFromParsed(expr) {
|
|
13211
|
+
if (!expr)
|
|
13212
|
+
return null;
|
|
13213
|
+
if (expr.kind === "identifier")
|
|
13214
|
+
return expr.name;
|
|
13215
|
+
if (expr.kind === "member" && !expr.computed) {
|
|
13216
|
+
const object = identifierPathFromParsed(expr.object);
|
|
13217
|
+
return object === null ? null : `${object}.${expr.property}`;
|
|
13218
|
+
}
|
|
13219
|
+
return null;
|
|
13220
|
+
}
|
|
12484
13221
|
|
|
12485
13222
|
// src/ir-to-client-js/compute-inlinability.ts
|
|
12486
13223
|
function buildEnvFromCtx(ctx) {
|
|
@@ -12500,7 +13237,7 @@ function buildEnvFromCtx(ctx) {
|
|
|
12500
13237
|
effects: ctx.effects,
|
|
12501
13238
|
onMounts: ctx.onMounts,
|
|
12502
13239
|
initStatements: ctx.initStatements,
|
|
12503
|
-
imports:
|
|
13240
|
+
imports: ctx.imports,
|
|
12504
13241
|
templateImports: [],
|
|
12505
13242
|
namedExports: [],
|
|
12506
13243
|
localFunctions: ctx.localFunctions,
|
|
@@ -12634,9 +13371,9 @@ function populateCsrInlinable(ctx, relocateEnv) {
|
|
|
12634
13371
|
continue;
|
|
12635
13372
|
const env = buildEnvWithConsts();
|
|
12636
13373
|
const source = c.value.trim();
|
|
12637
|
-
const { rewritten, freeIdentifiers } = csrSubstitute(source, env);
|
|
13374
|
+
const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env);
|
|
12638
13375
|
let pendingDependency = false;
|
|
12639
|
-
for (const id of
|
|
13376
|
+
for (const id of freeIdentifiers2) {
|
|
12640
13377
|
if (id === c.name)
|
|
12641
13378
|
continue;
|
|
12642
13379
|
const dep = ctx.localConstants.find((o) => o.name === id);
|
|
@@ -12652,7 +13389,7 @@ function populateCsrInlinable(ctx, relocateEnv) {
|
|
|
12652
13389
|
ctx.csrInlinable.set(c.name, null);
|
|
12653
13390
|
} else {
|
|
12654
13391
|
const bridgedRewritten = inlineResult.rewrittenValue;
|
|
12655
|
-
const bridgedFreeIdentifiers = recomputeFreeIdentifiers(bridgedRewritten,
|
|
13392
|
+
const bridgedFreeIdentifiers = recomputeFreeIdentifiers(bridgedRewritten, freeIdentifiers2);
|
|
12656
13393
|
ctx.csrInlinable.set(c.name, { rewrittenValue: bridgedRewritten, freeIdentifiers: bridgedFreeIdentifiers });
|
|
12657
13394
|
constSubs.set(c.name, {
|
|
12658
13395
|
kind: "identifier",
|
|
@@ -13199,14 +13936,26 @@ function sortDeclarations(declarations, declNameSet, graph) {
|
|
|
13199
13936
|
var ENV_SIGNAL_CLIENT_FACTORY = {
|
|
13200
13937
|
search: "createSearchParams"
|
|
13201
13938
|
};
|
|
13202
|
-
|
|
13939
|
+
var ENV_SIGNAL_READERS = new Map([
|
|
13940
|
+
["search", { key: "search", canonicalName: "searchParams", methods: new Set(["get"]) }]
|
|
13941
|
+
]);
|
|
13942
|
+
function envSignalReaderFor(key) {
|
|
13943
|
+
if (key === undefined)
|
|
13944
|
+
return null;
|
|
13945
|
+
return ENV_SIGNAL_READERS.get(key) ?? null;
|
|
13946
|
+
}
|
|
13947
|
+
function envSignalLocalNames(metadata, key) {
|
|
13203
13948
|
const names = new Set;
|
|
13204
13949
|
for (const s of metadata.signals) {
|
|
13205
|
-
if (s.envReader ===
|
|
13950
|
+
if (s.envReader !== undefined && (key === undefined || s.envReader === key)) {
|
|
13206
13951
|
names.add(s.getter);
|
|
13952
|
+
}
|
|
13207
13953
|
}
|
|
13208
13954
|
return names;
|
|
13209
13955
|
}
|
|
13956
|
+
function searchParamsLocalNames(metadata) {
|
|
13957
|
+
return envSignalLocalNames(metadata, "search");
|
|
13958
|
+
}
|
|
13210
13959
|
function importsSearchParams(metadata) {
|
|
13211
13960
|
return searchParamsLocalNames(metadata).size > 0;
|
|
13212
13961
|
}
|
|
@@ -15249,6 +15998,15 @@ function emitTemplateCloneInline(template) {
|
|
|
15249
15998
|
}
|
|
15250
15999
|
return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`${template}\`; return __tpl.content.firstElementChild.cloneNode(true)`;
|
|
15251
16000
|
}
|
|
16001
|
+
function emitHoistedTemplateDecl(lines, indent, tplVar, skeletonTemplate) {
|
|
16002
|
+
const isSvg = templateRootIsSvg(skeletonTemplate);
|
|
16003
|
+
const html = isSvg ? `<svg>${skeletonTemplate}</svg>` : skeletonTemplate;
|
|
16004
|
+
lines.push(`${indent}const ${tplVar} = document.createElement('template')`);
|
|
16005
|
+
lines.push(`${indent}${tplVar}.innerHTML = \`${html}\``);
|
|
16006
|
+
}
|
|
16007
|
+
function hoistedCloneExpr(tplVar, skeletonTemplate) {
|
|
16008
|
+
return templateRootIsSvg(skeletonTemplate) ? `${tplVar}.content.firstElementChild.firstElementChild.cloneNode(true)` : `${tplVar}.content.firstElementChild.cloneNode(true)`;
|
|
16009
|
+
}
|
|
15252
16010
|
function emitTemplateCloneLines(template, indent) {
|
|
15253
16011
|
if (templateRootIsSvg(template)) {
|
|
15254
16012
|
return [
|
|
@@ -15547,6 +16305,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
15547
16305
|
indexParam,
|
|
15548
16306
|
mapPreambleWrapped,
|
|
15549
16307
|
template,
|
|
16308
|
+
skeletonTemplate,
|
|
15550
16309
|
reactiveEffects,
|
|
15551
16310
|
childRefs,
|
|
15552
16311
|
bodyIsMultiRoot,
|
|
@@ -15557,11 +16316,16 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
15557
16316
|
stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
|
|
15558
16317
|
return;
|
|
15559
16318
|
}
|
|
16319
|
+
const hoistedTpl = !bodyIsMultiRoot && skeletonTemplate ? skeletonTemplate : null;
|
|
16320
|
+
const tplVar = `__tpl_${markerId.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
16321
|
+
if (hoistedTpl) {
|
|
16322
|
+
emitHoistedTemplateDecl(lines, topIndent, tplVar, hoistedTpl);
|
|
16323
|
+
}
|
|
15560
16324
|
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
15561
16325
|
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
15562
16326
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
15563
16327
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
|
|
15564
|
-
const cloneExpr = emitTemplateCloneInline(template);
|
|
16328
|
+
const cloneExpr = hoistedTpl ? `return ${hoistedCloneExpr(tplVar, hoistedTpl)}` : emitTemplateCloneInline(template);
|
|
15565
16329
|
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}${preamble}if (__existing) return __existing; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
15566
16330
|
return;
|
|
15567
16331
|
}
|
|
@@ -15571,12 +16335,16 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
15571
16335
|
lines.push(`${bodyIndent}${paramUnwrap}`);
|
|
15572
16336
|
if (mapPreambleWrapped)
|
|
15573
16337
|
lines.push(`${bodyIndent}${mapPreambleWrapped}`);
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
16338
|
+
if (hoistedTpl) {
|
|
16339
|
+
lines.push(`${bodyIndent}const __el = __existing ?? ${hoistedCloneExpr(tplVar, hoistedTpl)}`);
|
|
16340
|
+
} else {
|
|
16341
|
+
emitLoopItemElementSetup(lines, {
|
|
16342
|
+
template,
|
|
16343
|
+
bodyIsMultiRoot,
|
|
16344
|
+
indent: bodyIndent,
|
|
16345
|
+
singleRootLayout: "inline"
|
|
16346
|
+
});
|
|
16347
|
+
}
|
|
15580
16348
|
if (reactiveEffects !== null) {
|
|
15581
16349
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
|
|
15582
16350
|
}
|
|
@@ -16262,6 +17030,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
16262
17030
|
indexParam: elem.index || "__idx",
|
|
16263
17031
|
mapPreambleWrapped: elem.mapPreamble ? wrap(elem.mapPreamble) : "",
|
|
16264
17032
|
template: elem.template,
|
|
17033
|
+
skeletonTemplate: elem.skeletonTemplate,
|
|
16265
17034
|
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
16266
17035
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
16267
17036
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
@@ -16868,6 +17637,7 @@ function createContext(ir, scope, adapterCapabilities, profile) {
|
|
|
16868
17637
|
initStatements: ir.metadata.initStatements ?? [],
|
|
16869
17638
|
localFunctions: ir.metadata.localFunctions,
|
|
16870
17639
|
localConstants: ir.metadata.localConstants,
|
|
17640
|
+
imports: ir.metadata.imports,
|
|
16871
17641
|
propsParams: ir.metadata.propsParams,
|
|
16872
17642
|
propsObjectName: ir.metadata.propsObjectName,
|
|
16873
17643
|
restPropsName: ir.metadata.restPropsName,
|
|
@@ -16951,119 +17721,6 @@ ${externalImports.join(`
|
|
|
16951
17721
|
return generatedCode.replace(IMPORT_PLACEHOLDER, allImports);
|
|
16952
17722
|
}
|
|
16953
17723
|
|
|
16954
|
-
// src/module-exports.ts
|
|
16955
|
-
function generateModuleExports(ir, extraInlineExported = new Set, rewriteRelativeImport) {
|
|
16956
|
-
const lines = [];
|
|
16957
|
-
for (const constant of ir.metadata.localConstants) {
|
|
16958
|
-
if (!constant.isExported)
|
|
16959
|
-
continue;
|
|
16960
|
-
const keyword = constant.declarationKind ?? "const";
|
|
16961
|
-
if (!constant.value) {
|
|
16962
|
-
lines.push(`export ${keyword} ${constant.name}`);
|
|
16963
|
-
continue;
|
|
16964
|
-
}
|
|
16965
|
-
const value = constant.value.trim();
|
|
16966
|
-
if (/^createContext\b/.test(value) || /^new WeakMap\b/.test(value))
|
|
16967
|
-
continue;
|
|
16968
|
-
lines.push(`export ${keyword} ${constant.name} = ${constant.value}`);
|
|
16969
|
-
}
|
|
16970
|
-
for (const func of ir.metadata.localFunctions) {
|
|
16971
|
-
if (!func.isExported)
|
|
16972
|
-
continue;
|
|
16973
|
-
const params = func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
|
|
16974
|
-
const returnAnnotation = func.typedReturnType ? `: ${func.typedReturnType}` : "";
|
|
16975
|
-
const body = func.typedBody ?? func.body;
|
|
16976
|
-
const asyncKw = func.isAsync ? "async " : "";
|
|
16977
|
-
lines.push(`export ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
|
|
16978
|
-
}
|
|
16979
|
-
const inlineExported = collectInlineExportedNames(ir);
|
|
16980
|
-
for (const name of extraInlineExported)
|
|
16981
|
-
inlineExported.add(name);
|
|
16982
|
-
for (const block of ir.metadata.namedExports) {
|
|
16983
|
-
const isReexportFrom = block.source !== null;
|
|
16984
|
-
const survivingSpecs = block.specifiers.filter((spec) => {
|
|
16985
|
-
if (isReexportFrom)
|
|
16986
|
-
return true;
|
|
16987
|
-
return !(inlineExported.has(spec.name) && spec.alias == null);
|
|
16988
|
-
});
|
|
16989
|
-
if (survivingSpecs.length === 0)
|
|
16990
|
-
continue;
|
|
16991
|
-
const specText = survivingSpecs.map((s) => {
|
|
16992
|
-
const prefix = s.isTypeOnly ? "type " : "";
|
|
16993
|
-
return s.alias ? `${prefix}${s.name} as ${s.alias}` : `${prefix}${s.name}`;
|
|
16994
|
-
}).join(", ");
|
|
16995
|
-
const typeKw = block.isTypeOnly ? "type " : "";
|
|
16996
|
-
if (isReexportFrom) {
|
|
16997
|
-
const source = rewriteRelativeImport && block.source.startsWith(".") ? rewriteRelativeImport(block.source) : block.source;
|
|
16998
|
-
lines.push(`export ${typeKw}{ ${specText} } from '${source}'`);
|
|
16999
|
-
} else {
|
|
17000
|
-
lines.push(`export ${typeKw}{ ${specText} }`);
|
|
17001
|
-
}
|
|
17002
|
-
}
|
|
17003
|
-
return lines.length > 0 ? lines.join(`
|
|
17004
|
-
`) : null;
|
|
17005
|
-
}
|
|
17006
|
-
function collectInlineExportedNames(ir) {
|
|
17007
|
-
const names = new Set;
|
|
17008
|
-
for (const c of ir.metadata.localConstants) {
|
|
17009
|
-
if (c.isExported)
|
|
17010
|
-
names.add(c.name);
|
|
17011
|
-
}
|
|
17012
|
-
for (const f of ir.metadata.localFunctions) {
|
|
17013
|
-
if (f.isExported)
|
|
17014
|
-
names.add(f.name);
|
|
17015
|
-
}
|
|
17016
|
-
if (ir.metadata.isExported && ir.metadata.componentName) {
|
|
17017
|
-
names.add(ir.metadata.componentName);
|
|
17018
|
-
}
|
|
17019
|
-
return names;
|
|
17020
|
-
}
|
|
17021
|
-
function formatParamWithType(p) {
|
|
17022
|
-
const rest = p.isRest ? "..." : "";
|
|
17023
|
-
const optional = p.optional ? "?" : "";
|
|
17024
|
-
const typeAnnotation = p.type?.raw && p.type.raw !== "unknown" ? `: ${p.type.raw}` : "";
|
|
17025
|
-
const defaultPart = p.defaultValue !== undefined ? ` = ${p.defaultValue}` : "";
|
|
17026
|
-
return `${rest}${p.name}${optional}${typeAnnotation}${defaultPart}`;
|
|
17027
|
-
}
|
|
17028
|
-
function findReachableNames(primaryRefs, declarations) {
|
|
17029
|
-
const allNames = new Set(declarations.map((d) => d.name));
|
|
17030
|
-
const bodyMap = new Map(declarations.map((d) => [d.name, d.body]));
|
|
17031
|
-
const reachable = new Set;
|
|
17032
|
-
const queue = [];
|
|
17033
|
-
for (const name of allNames) {
|
|
17034
|
-
if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
|
|
17035
|
-
reachable.add(name);
|
|
17036
|
-
queue.push(name);
|
|
17037
|
-
}
|
|
17038
|
-
}
|
|
17039
|
-
while (queue.length > 0) {
|
|
17040
|
-
const current = queue.shift();
|
|
17041
|
-
const body = bodyMap.get(current) || "";
|
|
17042
|
-
for (const name of allNames) {
|
|
17043
|
-
if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
|
|
17044
|
-
reachable.add(name);
|
|
17045
|
-
queue.push(name);
|
|
17046
|
-
}
|
|
17047
|
-
}
|
|
17048
|
-
}
|
|
17049
|
-
return reachable;
|
|
17050
|
-
}
|
|
17051
|
-
function extractFunctionParams(value) {
|
|
17052
|
-
const arrowMatch = value.match(/^(?:async\s*)?\(([^)]*)\)\s*(?::\s*[^=]+)?\s*=>/);
|
|
17053
|
-
if (arrowMatch) {
|
|
17054
|
-
return arrowMatch[1].split(",").map((p) => p.trim().split(":")[0].split("=")[0].trim()).filter(Boolean).join(", ");
|
|
17055
|
-
}
|
|
17056
|
-
const singleMatch = value.match(/^(?:async\s*)?(\w+)\s*=>/);
|
|
17057
|
-
if (singleMatch) {
|
|
17058
|
-
return singleMatch[1];
|
|
17059
|
-
}
|
|
17060
|
-
const funcMatch = value.match(/^(?:async\s*)?function\s*\w*\s*\(([^)]*)\)/);
|
|
17061
|
-
if (funcMatch) {
|
|
17062
|
-
return funcMatch[1].split(",").map((p) => p.trim().split(":")[0].split("=")[0].trim()).filter(Boolean).join(", ");
|
|
17063
|
-
}
|
|
17064
|
-
return "";
|
|
17065
|
-
}
|
|
17066
|
-
|
|
17067
17724
|
// src/css-layer-prefixer.ts
|
|
17068
17725
|
function prefixClass(cls, layerName) {
|
|
17069
17726
|
if (!cls || cls.startsWith("layer-"))
|
|
@@ -17571,16 +18228,14 @@ function extractSsrDefaults(metadata) {
|
|
|
17571
18228
|
propsLike.add(metadata.propsObjectName);
|
|
17572
18229
|
for (const p of metadata.propsParams)
|
|
17573
18230
|
propsLike.add(p.name);
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
out[p.name] = { propName: p.name, value: null };
|
|
17583
|
-
}
|
|
18231
|
+
for (const p of metadata.propsParams) {
|
|
18232
|
+
if (p.isRest)
|
|
18233
|
+
continue;
|
|
18234
|
+
if (metadata.propsObjectName === null && p.defaultValue !== undefined) {
|
|
18235
|
+
const value = tryStaticEval(p.defaultValue, { bindings: {}, propsLike });
|
|
18236
|
+
out[p.name] = { propName: p.name, value: resultToJsonable(value) };
|
|
18237
|
+
} else {
|
|
18238
|
+
out[p.name] = { propName: p.name, value: null };
|
|
17584
18239
|
}
|
|
17585
18240
|
}
|
|
17586
18241
|
if (metadata.restPropsName) {
|
|
@@ -17897,47 +18552,501 @@ function evalNode(node, ctx) {
|
|
|
17897
18552
|
return UNRESOLVED;
|
|
17898
18553
|
}
|
|
17899
18554
|
|
|
17900
|
-
// src/
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
const
|
|
17904
|
-
const
|
|
17905
|
-
const
|
|
17906
|
-
const
|
|
17907
|
-
|
|
17908
|
-
const fold = (src, rawNames, idx, names, render) => {
|
|
17909
|
-
if (!idx.has(src)) {
|
|
17910
|
-
idx.set(src, result.length);
|
|
17911
|
-
names.set(src, new Set);
|
|
17912
|
-
result.push("");
|
|
17913
|
-
}
|
|
17914
|
-
const set = names.get(src);
|
|
17915
|
-
for (const n of rawNames.split(",").map((s) => s.trim()).filter(Boolean))
|
|
17916
|
-
set.add(n);
|
|
17917
|
-
result[idx.get(src)] = render(src, set);
|
|
17918
|
-
};
|
|
17919
|
-
for (const raw of lines) {
|
|
17920
|
-
const line = raw.trim();
|
|
17921
|
-
if (!line)
|
|
18555
|
+
// src/augment-inherited-props.ts
|
|
18556
|
+
import ts17 from "typescript";
|
|
18557
|
+
function collectContextConsumers(metadata) {
|
|
18558
|
+
const constants = metadata.localConstants ?? [];
|
|
18559
|
+
const contextDefaults = new Map;
|
|
18560
|
+
const contextDefaultKinds = new Map;
|
|
18561
|
+
for (const c of constants) {
|
|
18562
|
+
if (c.systemConstructKind !== "createContext" || c.value === undefined)
|
|
17922
18563
|
continue;
|
|
17923
|
-
|
|
17924
|
-
|
|
17925
|
-
|
|
17926
|
-
fold(valueMatch[2], valueMatch[1], valueIdx, valueNames, (s, n) => `import { ${[...n].join(", ")} } from '${s}'`);
|
|
17927
|
-
} else if (typeMatch) {
|
|
17928
|
-
fold(typeMatch[2], typeMatch[1], typeIdx, typeNames, (s, n) => `import type { ${[...n].join(", ")} } from '${s}'`);
|
|
17929
|
-
} else if (!seenOther.has(line)) {
|
|
17930
|
-
seenOther.add(line);
|
|
17931
|
-
result.push(line);
|
|
17932
|
-
}
|
|
18564
|
+
contextDefaults.set(c.name, parseCreateContextDefault(c.value));
|
|
18565
|
+
if (isObjectLiteralCreateContextDefault(c.value))
|
|
18566
|
+
contextDefaultKinds.set(c.name, "object");
|
|
17933
18567
|
}
|
|
17934
|
-
|
|
17935
|
-
|
|
17936
|
-
|
|
17937
|
-
|
|
17938
|
-
|
|
17939
|
-
|
|
17940
|
-
|
|
18568
|
+
if (contextDefaults.size === 0)
|
|
18569
|
+
return [];
|
|
18570
|
+
const consumers = [];
|
|
18571
|
+
for (const c of constants) {
|
|
18572
|
+
if (c.value === undefined)
|
|
18573
|
+
continue;
|
|
18574
|
+
const ctxName = parseUseContextArg(c.value);
|
|
18575
|
+
if (ctxName === null || !contextDefaults.has(ctxName))
|
|
18576
|
+
continue;
|
|
18577
|
+
consumers.push({
|
|
18578
|
+
localName: c.name,
|
|
18579
|
+
contextName: ctxName,
|
|
18580
|
+
defaultValue: contextDefaults.get(ctxName) ?? null,
|
|
18581
|
+
defaultKind: contextDefaultKinds.get(ctxName)
|
|
18582
|
+
});
|
|
18583
|
+
}
|
|
18584
|
+
return consumers;
|
|
18585
|
+
}
|
|
18586
|
+
function parseUseContextArg(source) {
|
|
18587
|
+
const expr = parseSingleExpression(source);
|
|
18588
|
+
if (!expr || !ts17.isCallExpression(expr))
|
|
18589
|
+
return null;
|
|
18590
|
+
if (!ts17.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
|
|
18591
|
+
return null;
|
|
18592
|
+
if (expr.arguments.length !== 1)
|
|
18593
|
+
return null;
|
|
18594
|
+
const arg = expr.arguments[0];
|
|
18595
|
+
return ts17.isIdentifier(arg) ? arg.text : null;
|
|
18596
|
+
}
|
|
18597
|
+
function parseCreateContextDefault(source) {
|
|
18598
|
+
const expr = parseSingleExpression(source);
|
|
18599
|
+
if (!expr || !ts17.isCallExpression(expr))
|
|
18600
|
+
return null;
|
|
18601
|
+
if (expr.arguments.length === 0)
|
|
18602
|
+
return null;
|
|
18603
|
+
const arg = expr.arguments[0];
|
|
18604
|
+
if (ts17.isStringLiteral(arg) || ts17.isNoSubstitutionTemplateLiteral(arg))
|
|
18605
|
+
return arg.text;
|
|
18606
|
+
if (ts17.isNumericLiteral(arg))
|
|
18607
|
+
return Number(arg.text);
|
|
18608
|
+
if (arg.kind === ts17.SyntaxKind.TrueKeyword)
|
|
18609
|
+
return true;
|
|
18610
|
+
if (arg.kind === ts17.SyntaxKind.FalseKeyword)
|
|
18611
|
+
return false;
|
|
18612
|
+
return null;
|
|
18613
|
+
}
|
|
18614
|
+
function isObjectLiteralCreateContextDefault(source) {
|
|
18615
|
+
const expr = parseSingleExpression(source);
|
|
18616
|
+
if (!expr || !ts17.isCallExpression(expr))
|
|
18617
|
+
return false;
|
|
18618
|
+
if (expr.arguments.length === 0)
|
|
18619
|
+
return false;
|
|
18620
|
+
return ts17.isObjectLiteralExpression(expr.arguments[0]);
|
|
18621
|
+
}
|
|
18622
|
+
function parseSingleExpression(source) {
|
|
18623
|
+
const sf = ts17.createSourceFile("__ctx.ts", `(${source})`, ts17.ScriptTarget.Latest, false);
|
|
18624
|
+
const stmt = sf.statements[0];
|
|
18625
|
+
if (!stmt || !ts17.isExpressionStatement(stmt))
|
|
18626
|
+
return null;
|
|
18627
|
+
let e = stmt.expression;
|
|
18628
|
+
while (ts17.isParenthesizedExpression(e))
|
|
18629
|
+
e = e.expression;
|
|
18630
|
+
return e;
|
|
18631
|
+
}
|
|
18632
|
+
function augmentInheritedPropAccesses(ir) {
|
|
18633
|
+
const propsObj = ir.metadata.propsObjectName;
|
|
18634
|
+
if (!propsObj)
|
|
18635
|
+
return;
|
|
18636
|
+
const existing = new Set(ir.metadata.propsParams.map((p) => p.name));
|
|
18637
|
+
const bareRefProps = new Set;
|
|
18638
|
+
const booleanAttrProps = new Set;
|
|
18639
|
+
const accessed = new Set;
|
|
18640
|
+
const accessRe = new RegExp(`(?:^|[^\\w$.])${propsObj}\\.([A-Za-z_$][\\w$]*)`, "g");
|
|
18641
|
+
const scan = (s, also) => {
|
|
18642
|
+
if (!s)
|
|
18643
|
+
return;
|
|
18644
|
+
for (const m of s.matchAll(accessRe)) {
|
|
18645
|
+
accessed.add(m[1]);
|
|
18646
|
+
also?.add(m[1]);
|
|
18647
|
+
}
|
|
18648
|
+
};
|
|
18649
|
+
const coalesceLiteralTypes = new Map;
|
|
18650
|
+
const pinCoalesceLiterals = (s) => {
|
|
18651
|
+
if (!s || !s.includes(propsObj))
|
|
18652
|
+
return;
|
|
18653
|
+
const sf = ts17.createSourceFile("__aug.ts", `(${s})`, ts17.ScriptTarget.Latest, false);
|
|
18654
|
+
const visit3 = (n) => {
|
|
18655
|
+
if (ts17.isBinaryExpression(n) && (n.operatorToken.kind === ts17.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts17.SyntaxKind.BarBarToken)) {
|
|
18656
|
+
let left = n.left;
|
|
18657
|
+
while (ts17.isParenthesizedExpression(left))
|
|
18658
|
+
left = left.expression;
|
|
18659
|
+
if (ts17.isPropertyAccessExpression(left) && ts17.isIdentifier(left.expression) && left.expression.text === propsObj) {
|
|
18660
|
+
const name = left.name.text;
|
|
18661
|
+
let right = n.right;
|
|
18662
|
+
while (ts17.isParenthesizedExpression(right))
|
|
18663
|
+
right = right.expression;
|
|
18664
|
+
if (ts17.isPrefixUnaryExpression(right))
|
|
18665
|
+
right = right.operand;
|
|
18666
|
+
const kind = ts17.isNumericLiteral(right) ? "number" : right.kind === ts17.SyntaxKind.TrueKeyword || right.kind === ts17.SyntaxKind.FalseKeyword ? "boolean" : ts17.isStringLiteralLike(right) ? "string" : null;
|
|
18667
|
+
if (kind && !coalesceLiteralTypes.has(name))
|
|
18668
|
+
coalesceLiteralTypes.set(name, kind);
|
|
18669
|
+
}
|
|
18670
|
+
}
|
|
18671
|
+
ts17.forEachChild(n, visit3);
|
|
18672
|
+
};
|
|
18673
|
+
visit3(sf);
|
|
18674
|
+
};
|
|
18675
|
+
for (const memo of ir.metadata.memos) {
|
|
18676
|
+
scan(memo.computation);
|
|
18677
|
+
pinCoalesceLiterals(memo.computation);
|
|
18678
|
+
}
|
|
18679
|
+
for (const signal of ir.metadata.signals) {
|
|
18680
|
+
scan(signal.initialValue);
|
|
18681
|
+
pinCoalesceLiterals(signal.initialValue);
|
|
18682
|
+
}
|
|
18683
|
+
for (const stmt of ir.metadata.initStatements ?? [])
|
|
18684
|
+
scan(stmt.body);
|
|
18685
|
+
for (const eff of ir.metadata.effects ?? [])
|
|
18686
|
+
scan(eff.body);
|
|
18687
|
+
for (const c of ir.metadata.localConstants ?? []) {
|
|
18688
|
+
if (c.isModule)
|
|
18689
|
+
continue;
|
|
18690
|
+
scan(c.value);
|
|
18691
|
+
pinCoalesceLiterals(c.value);
|
|
18692
|
+
}
|
|
18693
|
+
const walk = (node) => {
|
|
18694
|
+
if (!node)
|
|
18695
|
+
return;
|
|
18696
|
+
const carrier = node;
|
|
18697
|
+
if (carrier.type === "expression") {
|
|
18698
|
+
scan(carrier.expr);
|
|
18699
|
+
pinCoalesceLiterals(carrier.expr);
|
|
18700
|
+
}
|
|
18701
|
+
scan(carrier.condition, bareRefProps);
|
|
18702
|
+
pinCoalesceLiterals(carrier.condition);
|
|
18703
|
+
scan(carrier.array, bareRefProps);
|
|
18704
|
+
const el = node;
|
|
18705
|
+
for (const prop of node.props ?? []) {
|
|
18706
|
+
const v = prop.value;
|
|
18707
|
+
if (v?.parts) {
|
|
18708
|
+
for (const part of v.parts) {
|
|
18709
|
+
if (part.type === "string")
|
|
18710
|
+
scan(part.value);
|
|
18711
|
+
else if (part.type === "ternary") {
|
|
18712
|
+
scan(part.condition);
|
|
18713
|
+
scan(part.whenTrue);
|
|
18714
|
+
scan(part.whenFalse);
|
|
18715
|
+
} else if (part.type === "lookup")
|
|
18716
|
+
scan(part.key);
|
|
18717
|
+
}
|
|
18718
|
+
}
|
|
18719
|
+
if (v?.kind === "expression" && typeof v.expr === "string") {
|
|
18720
|
+
scan(v.expr, bareRefProps);
|
|
18721
|
+
pinCoalesceLiterals(v.expr);
|
|
18722
|
+
}
|
|
18723
|
+
}
|
|
18724
|
+
for (const attr of el.attrs ?? []) {
|
|
18725
|
+
const v = attr.value;
|
|
18726
|
+
if (v?.parts) {
|
|
18727
|
+
for (const part of v.parts) {
|
|
18728
|
+
if (part.type === "string")
|
|
18729
|
+
scan(part.value);
|
|
18730
|
+
else if (part.type === "ternary") {
|
|
18731
|
+
scan(part.condition);
|
|
18732
|
+
scan(part.whenTrue);
|
|
18733
|
+
scan(part.whenFalse);
|
|
18734
|
+
} else if (part.type === "lookup")
|
|
18735
|
+
scan(part.key);
|
|
18736
|
+
}
|
|
18737
|
+
}
|
|
18738
|
+
if (v?.kind === "expression" && typeof v.expr === "string") {
|
|
18739
|
+
scan(v.expr);
|
|
18740
|
+
const expr = v.expr.trim();
|
|
18741
|
+
const prefix = `${propsObj}.`;
|
|
18742
|
+
if (isBooleanAttr(attr.name) || v.presenceOrUndefined) {
|
|
18743
|
+
const m = expr.match(new RegExp(`^${propsObj}\\.([A-Za-z_$][\\w$]*)`));
|
|
18744
|
+
if (m)
|
|
18745
|
+
booleanAttrProps.add(m[1]);
|
|
18746
|
+
} else if (expr.startsWith(prefix)) {
|
|
18747
|
+
const rest = expr.slice(prefix.length);
|
|
18748
|
+
if (/^[A-Za-z_$][\w$]*$/.test(rest))
|
|
18749
|
+
bareRefProps.add(rest);
|
|
18750
|
+
}
|
|
18751
|
+
}
|
|
18752
|
+
}
|
|
18753
|
+
for (const child of el.children ?? []) {
|
|
18754
|
+
const c = child;
|
|
18755
|
+
walk(c.element ?? child);
|
|
18756
|
+
}
|
|
18757
|
+
const branchy = node;
|
|
18758
|
+
walk(branchy.whenTrue);
|
|
18759
|
+
walk(branchy.whenFalse);
|
|
18760
|
+
walk(branchy.consequent);
|
|
18761
|
+
walk(branchy.alternate);
|
|
18762
|
+
};
|
|
18763
|
+
walk(ir.root);
|
|
18764
|
+
for (const name of accessed) {
|
|
18765
|
+
if (existing.has(name))
|
|
18766
|
+
continue;
|
|
18767
|
+
let raw;
|
|
18768
|
+
if (booleanAttrProps.has(name))
|
|
18769
|
+
raw = "boolean";
|
|
18770
|
+
else if (coalesceLiteralTypes.has(name))
|
|
18771
|
+
raw = coalesceLiteralTypes.get(name);
|
|
18772
|
+
else if (bareRefProps.has(name))
|
|
18773
|
+
raw = "unknown";
|
|
18774
|
+
else
|
|
18775
|
+
raw = "string";
|
|
18776
|
+
const type = raw === "unknown" ? { kind: "unknown", raw: "unknown" } : { kind: "primitive", raw, primitive: raw };
|
|
18777
|
+
ir.metadata.propsParams.push({ name, type, optional: true });
|
|
18778
|
+
existing.add(name);
|
|
18779
|
+
}
|
|
18780
|
+
}
|
|
18781
|
+
function parseStaticStringConst(source) {
|
|
18782
|
+
const sf = ts17.createSourceFile("__const.ts", `const __x = (${source});`, ts17.ScriptTarget.Latest, false);
|
|
18783
|
+
const stmt = sf.statements[0];
|
|
18784
|
+
if (!stmt || !ts17.isVariableStatement(stmt))
|
|
18785
|
+
return null;
|
|
18786
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
18787
|
+
while (init && ts17.isParenthesizedExpression(init))
|
|
18788
|
+
init = init.expression;
|
|
18789
|
+
if (!init)
|
|
18790
|
+
return null;
|
|
18791
|
+
if (ts17.isStringLiteral(init) || ts17.isNoSubstitutionTemplateLiteral(init)) {
|
|
18792
|
+
return init.text;
|
|
18793
|
+
}
|
|
18794
|
+
return evalStringArrayJoin(source);
|
|
18795
|
+
}
|
|
18796
|
+
function evalTemplateOfStringConsts(source, resolved) {
|
|
18797
|
+
const sf = ts17.createSourceFile("__const.ts", `const __x = (${source});`, ts17.ScriptTarget.Latest, false);
|
|
18798
|
+
const stmt = sf.statements[0];
|
|
18799
|
+
if (!stmt || !ts17.isVariableStatement(stmt))
|
|
18800
|
+
return null;
|
|
18801
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
18802
|
+
while (init && ts17.isParenthesizedExpression(init))
|
|
18803
|
+
init = init.expression;
|
|
18804
|
+
if (!init || !ts17.isTemplateExpression(init))
|
|
18805
|
+
return null;
|
|
18806
|
+
let out = init.head.text;
|
|
18807
|
+
for (const span of init.templateSpans) {
|
|
18808
|
+
if (!ts17.isIdentifier(span.expression))
|
|
18809
|
+
return null;
|
|
18810
|
+
const value = resolved.get(span.expression.text);
|
|
18811
|
+
if (value === undefined)
|
|
18812
|
+
return null;
|
|
18813
|
+
out += value + span.literal.text;
|
|
18814
|
+
}
|
|
18815
|
+
return out;
|
|
18816
|
+
}
|
|
18817
|
+
function collectModuleStringConsts(constants) {
|
|
18818
|
+
const map = new Map;
|
|
18819
|
+
const candidates = (constants ?? []).filter((c) => c.isModule && c.value !== undefined);
|
|
18820
|
+
let progressed = true;
|
|
18821
|
+
while (progressed) {
|
|
18822
|
+
progressed = false;
|
|
18823
|
+
for (const c of candidates) {
|
|
18824
|
+
if (map.has(c.name))
|
|
18825
|
+
continue;
|
|
18826
|
+
const literal = parseStaticStringConst(c.value) ?? evalTemplateOfStringConsts(c.value, map);
|
|
18827
|
+
if (literal !== null) {
|
|
18828
|
+
map.set(c.name, literal);
|
|
18829
|
+
progressed = true;
|
|
18830
|
+
}
|
|
18831
|
+
}
|
|
18832
|
+
}
|
|
18833
|
+
return map;
|
|
18834
|
+
}
|
|
18835
|
+
function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
18836
|
+
const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
|
|
18837
|
+
if (constInfo?.value === undefined)
|
|
18838
|
+
return null;
|
|
18839
|
+
const sf = ts17.createSourceFile("__rec.ts", `(${constInfo.value})`, ts17.ScriptTarget.Latest, true);
|
|
18840
|
+
if (sf.statements.length !== 1)
|
|
18841
|
+
return null;
|
|
18842
|
+
const stmt = sf.statements[0];
|
|
18843
|
+
if (!ts17.isExpressionStatement(stmt))
|
|
18844
|
+
return null;
|
|
18845
|
+
let parsed = stmt.expression;
|
|
18846
|
+
while (ts17.isParenthesizedExpression(parsed))
|
|
18847
|
+
parsed = parsed.expression;
|
|
18848
|
+
if (!ts17.isObjectLiteralExpression(parsed))
|
|
18849
|
+
return null;
|
|
18850
|
+
for (const prop of parsed.properties) {
|
|
18851
|
+
if (!ts17.isPropertyAssignment(prop))
|
|
18852
|
+
continue;
|
|
18853
|
+
const name = prop.name;
|
|
18854
|
+
const propKey = ts17.isIdentifier(name) || ts17.isStringLiteral(name) || ts17.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
|
|
18855
|
+
if (propKey !== key)
|
|
18856
|
+
continue;
|
|
18857
|
+
let v = prop.initializer;
|
|
18858
|
+
while (ts17.isParenthesizedExpression(v))
|
|
18859
|
+
v = v.expression;
|
|
18860
|
+
if (ts17.isNumericLiteral(v))
|
|
18861
|
+
return { kind: "number", text: v.text };
|
|
18862
|
+
if (ts17.isStringLiteral(v) || ts17.isNoSubstitutionTemplateLiteral(v)) {
|
|
18863
|
+
return { kind: "string", text: v.text };
|
|
18864
|
+
}
|
|
18865
|
+
return null;
|
|
18866
|
+
}
|
|
18867
|
+
return null;
|
|
18868
|
+
}
|
|
18869
|
+
function evalStringArrayJoin(source) {
|
|
18870
|
+
const sf = ts17.createSourceFile("__join.ts", `const __x = (${source});`, ts17.ScriptTarget.Latest, false);
|
|
18871
|
+
const stmt = sf.statements[0];
|
|
18872
|
+
if (!stmt || !ts17.isVariableStatement(stmt))
|
|
18873
|
+
return null;
|
|
18874
|
+
let node = stmt.declarationList.declarations[0]?.initializer;
|
|
18875
|
+
while (node && ts17.isParenthesizedExpression(node))
|
|
18876
|
+
node = node.expression;
|
|
18877
|
+
if (!node || !ts17.isCallExpression(node))
|
|
18878
|
+
return null;
|
|
18879
|
+
const callee = node.expression;
|
|
18880
|
+
if (!ts17.isPropertyAccessExpression(callee))
|
|
18881
|
+
return null;
|
|
18882
|
+
if (callee.name.text !== "join")
|
|
18883
|
+
return null;
|
|
18884
|
+
let recv = callee.expression;
|
|
18885
|
+
while (ts17.isParenthesizedExpression(recv))
|
|
18886
|
+
recv = recv.expression;
|
|
18887
|
+
if (!ts17.isArrayLiteralExpression(recv))
|
|
18888
|
+
return null;
|
|
18889
|
+
const parts = [];
|
|
18890
|
+
for (const el of recv.elements) {
|
|
18891
|
+
if (ts17.isStringLiteral(el) || ts17.isNoSubstitutionTemplateLiteral(el)) {
|
|
18892
|
+
parts.push(el.text);
|
|
18893
|
+
} else {
|
|
18894
|
+
return null;
|
|
18895
|
+
}
|
|
18896
|
+
}
|
|
18897
|
+
let sep2 = ",";
|
|
18898
|
+
if (node.arguments.length >= 1) {
|
|
18899
|
+
const arg = node.arguments[0];
|
|
18900
|
+
if (ts17.isStringLiteral(arg) || ts17.isNoSubstitutionTemplateLiteral(arg))
|
|
18901
|
+
sep2 = arg.text;
|
|
18902
|
+
else
|
|
18903
|
+
return null;
|
|
18904
|
+
}
|
|
18905
|
+
return parts.join(sep2);
|
|
18906
|
+
}
|
|
18907
|
+
function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
18908
|
+
if (!ts17.isElementAccessExpression(val))
|
|
18909
|
+
return null;
|
|
18910
|
+
const obj = val.expression;
|
|
18911
|
+
const arg = val.argumentExpression;
|
|
18912
|
+
if (!ts17.isIdentifier(obj) || !ts17.isIdentifier(arg))
|
|
18913
|
+
return null;
|
|
18914
|
+
let indexPropName;
|
|
18915
|
+
let defaultKey;
|
|
18916
|
+
const resolved = resolveKey?.(arg.text);
|
|
18917
|
+
if (resolved) {
|
|
18918
|
+
indexPropName = resolved.propName;
|
|
18919
|
+
defaultKey = resolved.defaultLiteral;
|
|
18920
|
+
} else if (propsParams.some((p) => p.name === arg.text)) {
|
|
18921
|
+
indexPropName = arg.text;
|
|
18922
|
+
} else {
|
|
18923
|
+
return null;
|
|
18924
|
+
}
|
|
18925
|
+
const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
|
|
18926
|
+
if (constInfo?.value === undefined)
|
|
18927
|
+
return null;
|
|
18928
|
+
const sf = ts17.createSourceFile("__rec.ts", `(${constInfo.value})`, ts17.ScriptTarget.Latest, true);
|
|
18929
|
+
if (sf.statements.length !== 1)
|
|
18930
|
+
return null;
|
|
18931
|
+
const stmt = sf.statements[0];
|
|
18932
|
+
if (!ts17.isExpressionStatement(stmt))
|
|
18933
|
+
return null;
|
|
18934
|
+
let parsed = stmt.expression;
|
|
18935
|
+
while (ts17.isParenthesizedExpression(parsed))
|
|
18936
|
+
parsed = parsed.expression;
|
|
18937
|
+
if (!ts17.isObjectLiteralExpression(parsed))
|
|
18938
|
+
return null;
|
|
18939
|
+
const entries = [];
|
|
18940
|
+
for (const prop of parsed.properties) {
|
|
18941
|
+
if (!ts17.isPropertyAssignment(prop))
|
|
18942
|
+
return null;
|
|
18943
|
+
let key;
|
|
18944
|
+
if (ts17.isIdentifier(prop.name)) {
|
|
18945
|
+
key = prop.name.text;
|
|
18946
|
+
} else if (ts17.isStringLiteral(prop.name) || ts17.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
18947
|
+
key = prop.name.text;
|
|
18948
|
+
} else {
|
|
18949
|
+
return null;
|
|
18950
|
+
}
|
|
18951
|
+
let v = prop.initializer;
|
|
18952
|
+
while (ts17.isParenthesizedExpression(v))
|
|
18953
|
+
v = v.expression;
|
|
18954
|
+
if (ts17.isNumericLiteral(v)) {
|
|
18955
|
+
entries.push({ key, value: { kind: "number", text: v.text } });
|
|
18956
|
+
} else if (ts17.isStringLiteral(v) || ts17.isNoSubstitutionTemplateLiteral(v)) {
|
|
18957
|
+
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
18958
|
+
} else {
|
|
18959
|
+
return null;
|
|
18960
|
+
}
|
|
18961
|
+
}
|
|
18962
|
+
return { indexPropName, entries, defaultKey };
|
|
18963
|
+
}
|
|
18964
|
+
|
|
18965
|
+
// src/ssr-seed-plan.ts
|
|
18966
|
+
function classify2(name, origin, expr, parsed, available) {
|
|
18967
|
+
if (!isSupported(parsed).supported)
|
|
18968
|
+
return { kind: "opaque", name, origin };
|
|
18969
|
+
const frees = freeIdentifiers(parsed);
|
|
18970
|
+
if (frees === null)
|
|
18971
|
+
return { kind: "opaque", name, origin };
|
|
18972
|
+
for (const free of frees) {
|
|
18973
|
+
if (!available.has(free))
|
|
18974
|
+
return { kind: "opaque", name, origin };
|
|
18975
|
+
}
|
|
18976
|
+
return { kind: "derived", name, origin, expr, parsed, frees: [...frees] };
|
|
18977
|
+
}
|
|
18978
|
+
function computeSsrSeedPlan(metadata) {
|
|
18979
|
+
const baseScope = metadata.propsParams.map((p) => p.name);
|
|
18980
|
+
if (metadata.propsObjectName)
|
|
18981
|
+
baseScope.push(metadata.propsObjectName);
|
|
18982
|
+
for (const name of collectModuleStringConsts(metadata.localConstants).keys()) {
|
|
18983
|
+
baseScope.push(name);
|
|
18984
|
+
}
|
|
18985
|
+
const available = new Set(baseScope);
|
|
18986
|
+
const steps = [];
|
|
18987
|
+
for (const signal of metadata.signals) {
|
|
18988
|
+
if (signal.envReader) {
|
|
18989
|
+
const reader = envSignalReaderFor(signal.envReader);
|
|
18990
|
+
if (reader) {
|
|
18991
|
+
steps.push({ kind: "env-reader", name: signal.getter, reader });
|
|
18992
|
+
available.add(signal.getter);
|
|
18993
|
+
continue;
|
|
18994
|
+
}
|
|
18995
|
+
}
|
|
18996
|
+
const expr = signal.initialValue.trim();
|
|
18997
|
+
steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify2(signal.getter, "signal", expr, parseExpression(expr), available));
|
|
18998
|
+
available.add(signal.getter);
|
|
18999
|
+
}
|
|
19000
|
+
for (const memo of metadata.memos) {
|
|
19001
|
+
const body = extractArrowBodyExpression(memo.computation);
|
|
19002
|
+
const expr = body?.trim() ?? "";
|
|
19003
|
+
steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify2(memo.name, "memo", expr, memo.parsed ?? parseExpression(expr), available));
|
|
19004
|
+
available.add(memo.name);
|
|
19005
|
+
}
|
|
19006
|
+
return { baseScope, steps };
|
|
19007
|
+
}
|
|
19008
|
+
|
|
19009
|
+
// src/compiler.ts
|
|
19010
|
+
function mergeTemplateImports(lines) {
|
|
19011
|
+
const result = [];
|
|
19012
|
+
const valueIdx = new Map;
|
|
19013
|
+
const valueNames = new Map;
|
|
19014
|
+
const typeIdx = new Map;
|
|
19015
|
+
const typeNames = new Map;
|
|
19016
|
+
const seenOther = new Set;
|
|
19017
|
+
const fold = (src, rawNames, idx, names, render) => {
|
|
19018
|
+
if (!idx.has(src)) {
|
|
19019
|
+
idx.set(src, result.length);
|
|
19020
|
+
names.set(src, new Set);
|
|
19021
|
+
result.push("");
|
|
19022
|
+
}
|
|
19023
|
+
const set = names.get(src);
|
|
19024
|
+
for (const n of rawNames.split(",").map((s) => s.trim()).filter(Boolean))
|
|
19025
|
+
set.add(n);
|
|
19026
|
+
result[idx.get(src)] = render(src, set);
|
|
19027
|
+
};
|
|
19028
|
+
for (const raw of lines) {
|
|
19029
|
+
const line = raw.trim();
|
|
19030
|
+
if (!line)
|
|
19031
|
+
continue;
|
|
19032
|
+
const typeMatch = line.match(/^import\s+type\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]\s*;?$/);
|
|
19033
|
+
const valueMatch = line.match(/^import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]\s*;?$/);
|
|
19034
|
+
if (valueMatch) {
|
|
19035
|
+
fold(valueMatch[2], valueMatch[1], valueIdx, valueNames, (s, n) => `import { ${[...n].join(", ")} } from '${s}'`);
|
|
19036
|
+
} else if (typeMatch) {
|
|
19037
|
+
fold(typeMatch[2], typeMatch[1], typeIdx, typeNames, (s, n) => `import type { ${[...n].join(", ")} } from '${s}'`);
|
|
19038
|
+
} else if (!seenOther.has(line)) {
|
|
19039
|
+
seenOther.add(line);
|
|
19040
|
+
result.push(line);
|
|
19041
|
+
}
|
|
19042
|
+
}
|
|
19043
|
+
return result.filter(Boolean).join(`
|
|
19044
|
+
`);
|
|
19045
|
+
}
|
|
19046
|
+
function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
19047
|
+
const files = [];
|
|
19048
|
+
const errors = [];
|
|
19049
|
+
const adapter = options.adapter;
|
|
17941
19050
|
const entries = [];
|
|
17942
19051
|
const program = options.program ?? (needsTypeBasedDetection(source) ? createProgramForFile(source, filePath)?.program : undefined);
|
|
17943
19052
|
for (const componentName of componentNames) {
|
|
@@ -18036,7 +19145,8 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
18036
19145
|
files.push({
|
|
18037
19146
|
path: dir + output.componentName + adapter.extension,
|
|
18038
19147
|
content: output.rawTemplate,
|
|
18039
|
-
type: "markedTemplate"
|
|
19148
|
+
type: "markedTemplate",
|
|
19149
|
+
componentName: output.componentName
|
|
18040
19150
|
});
|
|
18041
19151
|
const ir = entries.find((e) => e.componentIR.metadata.componentName === output.componentName);
|
|
18042
19152
|
const ssrDefaults = ir ? extractSsrDefaults(ir.componentIR.metadata) : undefined;
|
|
@@ -18044,7 +19154,8 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
18044
19154
|
files.push({
|
|
18045
19155
|
path: dir + output.componentName + ".ssr-defaults.json",
|
|
18046
19156
|
content: JSON.stringify(ssrDefaults),
|
|
18047
|
-
type: "ssrDefaults"
|
|
19157
|
+
type: "ssrDefaults",
|
|
19158
|
+
componentName: output.componentName
|
|
18048
19159
|
});
|
|
18049
19160
|
}
|
|
18050
19161
|
}
|
|
@@ -18209,7 +19320,7 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
18209
19320
|
return { files, errors };
|
|
18210
19321
|
}
|
|
18211
19322
|
function buildMetadata(ctx) {
|
|
18212
|
-
|
|
19323
|
+
const metadata = {
|
|
18213
19324
|
componentName: ctx.componentName || "Unknown",
|
|
18214
19325
|
hasDefaultExport: ctx.hasDefaultExport,
|
|
18215
19326
|
isExported: ctx.isExported,
|
|
@@ -18231,6 +19342,8 @@ function buildMetadata(ctx) {
|
|
|
18231
19342
|
localFunctions: ctx.localFunctions,
|
|
18232
19343
|
localConstants: ctx.localConstants
|
|
18233
19344
|
};
|
|
19345
|
+
metadata.ssrSeedPlan = computeSsrSeedPlan(metadata);
|
|
19346
|
+
return metadata;
|
|
18234
19347
|
}
|
|
18235
19348
|
function compileJSX(source, filePath, options) {
|
|
18236
19349
|
const files = [];
|
|
@@ -18338,7 +19451,8 @@ function compileJSX(source, filePath, options) {
|
|
|
18338
19451
|
files.push({
|
|
18339
19452
|
path: filePath.replace(/\.tsx?$/, adapter.extension),
|
|
18340
19453
|
content,
|
|
18341
|
-
type: "markedTemplate"
|
|
19454
|
+
type: "markedTemplate",
|
|
19455
|
+
componentName: componentIR.metadata.componentName
|
|
18342
19456
|
});
|
|
18343
19457
|
{
|
|
18344
19458
|
const ssrDefaults = extractSsrDefaults(componentIR.metadata);
|
|
@@ -18346,7 +19460,8 @@ function compileJSX(source, filePath, options) {
|
|
|
18346
19460
|
files.push({
|
|
18347
19461
|
path: filePath.replace(/\.tsx?$/, ".ssr-defaults.json"),
|
|
18348
19462
|
content: JSON.stringify(ssrDefaults),
|
|
18349
|
-
type: "ssrDefaults"
|
|
19463
|
+
type: "ssrDefaults",
|
|
19464
|
+
componentName: componentIR.metadata.componentName
|
|
18350
19465
|
});
|
|
18351
19466
|
}
|
|
18352
19467
|
}
|
|
@@ -18393,7 +19508,7 @@ function compileJSX(source, filePath, options) {
|
|
|
18393
19508
|
return { files, errors };
|
|
18394
19509
|
}
|
|
18395
19510
|
// src/shared-program.ts
|
|
18396
|
-
import
|
|
19511
|
+
import ts18 from "typescript";
|
|
18397
19512
|
function commonParent(paths) {
|
|
18398
19513
|
if (paths.length === 0)
|
|
18399
19514
|
return process.cwd();
|
|
@@ -18414,10 +19529,10 @@ function commonParent(paths) {
|
|
|
18414
19529
|
function createProgramForCorpus(files, options = {}) {
|
|
18415
19530
|
const baseUrl = options.baseUrl ?? commonParent(files);
|
|
18416
19531
|
const compilerOptions = {
|
|
18417
|
-
target:
|
|
18418
|
-
module:
|
|
18419
|
-
moduleResolution:
|
|
18420
|
-
jsx:
|
|
19532
|
+
target: ts18.ScriptTarget.Latest,
|
|
19533
|
+
module: ts18.ModuleKind.ESNext,
|
|
19534
|
+
moduleResolution: ts18.ModuleResolutionKind.Bundler,
|
|
19535
|
+
jsx: ts18.JsxEmit.ReactJSX,
|
|
18421
19536
|
strict: true,
|
|
18422
19537
|
skipLibCheck: true,
|
|
18423
19538
|
noEmit: true,
|
|
@@ -18427,7 +19542,7 @@ function createProgramForCorpus(files, options = {}) {
|
|
|
18427
19542
|
...options.compilerOptions
|
|
18428
19543
|
};
|
|
18429
19544
|
const absolute = files.map((f) => path_default.resolve(f));
|
|
18430
|
-
return
|
|
19545
|
+
return ts18.createProgram(absolute, compilerOptions, undefined, options.oldProgram);
|
|
18431
19546
|
}
|
|
18432
19547
|
// src/adapters/interface.ts
|
|
18433
19548
|
class BaseAdapter {
|
|
@@ -18915,7 +20030,7 @@ function emitParsedExpr(expr, emitter) {
|
|
|
18915
20030
|
return emitter.objectLiteral(expr.properties, expr.raw, emit);
|
|
18916
20031
|
case "array-method":
|
|
18917
20032
|
if (expr.method === "flat") {
|
|
18918
|
-
return emitter.flatMethod(expr.object, expr.flatDepth, emit);
|
|
20033
|
+
return emitter.flatMethod(expr.object, expr.depthExpr ? { expr: expr.depthExpr } : expr.flatDepth, emit);
|
|
18919
20034
|
}
|
|
18920
20035
|
return emitter.arrayMethod(expr.method, expr.object, expr.args, emit);
|
|
18921
20036
|
case "unsupported":
|
|
@@ -18984,42 +20099,9 @@ function isOmitBranch(node) {
|
|
|
18984
20099
|
if (node.kind === "identifier")
|
|
18985
20100
|
return node.name === "undefined";
|
|
18986
20101
|
if (node.kind === "literal") {
|
|
18987
|
-
return node.literalType === "null" || node.literalType === "string" && node.value === "";
|
|
18988
|
-
}
|
|
18989
|
-
return false;
|
|
18990
|
-
}
|
|
18991
|
-
// src/lowering-registry.ts
|
|
18992
|
-
var plugins = [];
|
|
18993
|
-
function registerLoweringPlugin(plugin) {
|
|
18994
|
-
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
18995
|
-
if (existing >= 0)
|
|
18996
|
-
plugins[existing] = plugin;
|
|
18997
|
-
else
|
|
18998
|
-
plugins.push(plugin);
|
|
18999
|
-
}
|
|
19000
|
-
function getLoweringPlugins() {
|
|
19001
|
-
return [...plugins];
|
|
19002
|
-
}
|
|
19003
|
-
function prepareLoweringMatchers(metadata) {
|
|
19004
|
-
const matchers = [];
|
|
19005
|
-
for (const plugin of plugins) {
|
|
19006
|
-
const matcher = plugin.prepare(metadata);
|
|
19007
|
-
if (matcher)
|
|
19008
|
-
matchers.push(matcher);
|
|
19009
|
-
}
|
|
19010
|
-
return matchers;
|
|
19011
|
-
}
|
|
19012
|
-
function matchLoweringCall(callee, args, metadata) {
|
|
19013
|
-
for (const matcher of prepareLoweringMatchers(metadata)) {
|
|
19014
|
-
const node = matcher(callee, args);
|
|
19015
|
-
if (node)
|
|
19016
|
-
return node;
|
|
19017
|
-
}
|
|
19018
|
-
return null;
|
|
19019
|
-
}
|
|
19020
|
-
function __resetLoweringPluginsForTest(next = []) {
|
|
19021
|
-
plugins.length = 0;
|
|
19022
|
-
plugins.push(...next);
|
|
20102
|
+
return node.literalType === "null" || node.literalType === "string" && node.value === "";
|
|
20103
|
+
}
|
|
20104
|
+
return false;
|
|
19023
20105
|
}
|
|
19024
20106
|
// src/builtin-lowering-plugins.ts
|
|
19025
20107
|
var queryHrefPlugin = {
|
|
@@ -19095,7 +20177,7 @@ function emitAttrValue(value, emitter, name) {
|
|
|
19095
20177
|
}
|
|
19096
20178
|
}
|
|
19097
20179
|
// src/combine-client-js.ts
|
|
19098
|
-
import
|
|
20180
|
+
import ts19 from "typescript";
|
|
19099
20181
|
var CHILD_PLACEHOLDER_RE = /import '\/\* @bf-child:(\w+) \*\/'/g;
|
|
19100
20182
|
function combineParentChildClientJs(files) {
|
|
19101
20183
|
const result = new Map;
|
|
@@ -19152,10 +20234,10 @@ function combineParentChildClientJs(files) {
|
|
|
19152
20234
|
return result;
|
|
19153
20235
|
}
|
|
19154
20236
|
function parseAndMerge(content, importsBySource, otherImports, codeSections) {
|
|
19155
|
-
const sourceFile =
|
|
20237
|
+
const sourceFile = ts19.createSourceFile("combine.js", content, ts19.ScriptTarget.Latest, false, ts19.ScriptKind.JS);
|
|
19156
20238
|
const importSpans = [];
|
|
19157
20239
|
for (const stmt of sourceFile.statements) {
|
|
19158
|
-
if (!
|
|
20240
|
+
if (!ts19.isImportDeclaration(stmt))
|
|
19159
20241
|
continue;
|
|
19160
20242
|
const start = stmt.getStart(sourceFile);
|
|
19161
20243
|
const end = stmt.getEnd();
|
|
@@ -19165,8 +20247,8 @@ function parseAndMerge(content, importsBySource, otherImports, codeSections) {
|
|
|
19165
20247
|
continue;
|
|
19166
20248
|
const clause = stmt.importClause;
|
|
19167
20249
|
const bindings = clause?.namedBindings;
|
|
19168
|
-
const specifier =
|
|
19169
|
-
if (clause && !clause.name && bindings &&
|
|
20250
|
+
const specifier = ts19.isStringLiteral(stmt.moduleSpecifier) ? stmt.moduleSpecifier.text : "";
|
|
20251
|
+
if (clause && !clause.name && bindings && ts19.isNamedImports(bindings)) {
|
|
19170
20252
|
if (!importsBySource.has(specifier)) {
|
|
19171
20253
|
importsBySource.set(specifier, new Set);
|
|
19172
20254
|
}
|
|
@@ -19194,8 +20276,7 @@ function parseAndMerge(content, importsBySource, otherImports, codeSections) {
|
|
|
19194
20276
|
}
|
|
19195
20277
|
}
|
|
19196
20278
|
// src/loop-destructure.ts
|
|
19197
|
-
|
|
19198
|
-
function isLowerableObjectRestDestructure(loop) {
|
|
20279
|
+
function isLowerableLoopDestructure(loop) {
|
|
19199
20280
|
const bindings = loop.paramBindings;
|
|
19200
20281
|
if (!bindings || bindings.length === 0)
|
|
19201
20282
|
return false;
|
|
@@ -19207,18 +20288,20 @@ function isLowerableObjectRestDestructure(loop) {
|
|
|
19207
20288
|
}
|
|
19208
20289
|
for (const b of bindings) {
|
|
19209
20290
|
if (b.rest) {
|
|
19210
|
-
if (b.
|
|
20291
|
+
if (!b.segments)
|
|
19211
20292
|
return false;
|
|
19212
|
-
} else if (!
|
|
20293
|
+
} else if (!b.segments || b.segments.length === 0) {
|
|
19213
20294
|
return false;
|
|
19214
20295
|
}
|
|
19215
20296
|
}
|
|
19216
|
-
const
|
|
19217
|
-
if (
|
|
20297
|
+
const objectRestNames = bindings.filter((b) => b.rest?.kind === "object").map((b) => b.name);
|
|
20298
|
+
if (objectRestNames.length === 0)
|
|
19218
20299
|
return true;
|
|
19219
|
-
return !restNamesMisused(loop,
|
|
20300
|
+
return !restNamesMisused(loop, objectRestNames);
|
|
19220
20301
|
}
|
|
20302
|
+
var isLowerableObjectRestDestructure = isLowerableLoopDestructure;
|
|
19221
20303
|
function restNamesMisused(loop, names) {
|
|
20304
|
+
const nameSet = new Set(names);
|
|
19222
20305
|
const valueUse = names.map((n) => new RegExp(`(?<![\\w.$])${escapeRe(n)}(?!\\s*\\??\\.)(?![\\w$])`));
|
|
19223
20306
|
let misused = false;
|
|
19224
20307
|
const check = (s) => {
|
|
@@ -19231,7 +20314,10 @@ function restNamesMisused(loop, names) {
|
|
|
19231
20314
|
}
|
|
19232
20315
|
}
|
|
19233
20316
|
};
|
|
19234
|
-
const attr = (v) => {
|
|
20317
|
+
const attr = (v, isIntrinsicElementAttrs) => {
|
|
20318
|
+
if (v.kind === "spread" && isIntrinsicElementAttrs && nameSet.has(v.expr.trim())) {
|
|
20319
|
+
return;
|
|
20320
|
+
}
|
|
19235
20321
|
if (v.kind === "expression" || v.kind === "spread") {
|
|
19236
20322
|
check(v.expr);
|
|
19237
20323
|
check(v.templateExpr);
|
|
@@ -19291,16 +20377,16 @@ function restNamesMisused(loop, names) {
|
|
|
19291
20377
|
visit3(node.alternate);
|
|
19292
20378
|
break;
|
|
19293
20379
|
case "element":
|
|
19294
|
-
node.attrs.forEach((a) => attr(a.value));
|
|
20380
|
+
node.attrs.forEach((a) => attr(a.value, true));
|
|
19295
20381
|
node.events.forEach((e) => check(e.handler));
|
|
19296
20382
|
node.children.forEach(visit3);
|
|
19297
20383
|
break;
|
|
19298
20384
|
case "component":
|
|
19299
|
-
node.props.forEach((p) => attr(p.value));
|
|
20385
|
+
node.props.forEach((p) => attr(p.value, false));
|
|
19300
20386
|
node.children.forEach(visit3);
|
|
19301
20387
|
break;
|
|
19302
20388
|
case "provider":
|
|
19303
|
-
attr(node.valueProp.value);
|
|
20389
|
+
attr(node.valueProp.value, false);
|
|
19304
20390
|
node.children.forEach(visit3);
|
|
19305
20391
|
break;
|
|
19306
20392
|
case "fragment":
|
|
@@ -19325,7 +20411,7 @@ function escapeRe(s) {
|
|
|
19325
20411
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19326
20412
|
}
|
|
19327
20413
|
// src/debug.ts
|
|
19328
|
-
import
|
|
20414
|
+
import ts20 from "typescript";
|
|
19329
20415
|
function buildComponentGraph(source, filePath, componentName) {
|
|
19330
20416
|
const ctx = analyzeComponent(source, filePath, componentName);
|
|
19331
20417
|
if (!ctx.jsxReturn) {
|
|
@@ -20610,7 +21696,7 @@ function truncateExpr(expr, max = 40) {
|
|
|
20610
21696
|
function exprReadsPropMember(expr, propsObjectName) {
|
|
20611
21697
|
let sf;
|
|
20612
21698
|
try {
|
|
20613
|
-
sf =
|
|
21699
|
+
sf = ts20.createSourceFile("__attr.tsx", `(${expr})`, ts20.ScriptTarget.Latest, true, ts20.ScriptKind.TSX);
|
|
20614
21700
|
} catch {
|
|
20615
21701
|
return false;
|
|
20616
21702
|
}
|
|
@@ -20618,11 +21704,11 @@ function exprReadsPropMember(expr, propsObjectName) {
|
|
|
20618
21704
|
const visit3 = (n) => {
|
|
20619
21705
|
if (found)
|
|
20620
21706
|
return;
|
|
20621
|
-
if (
|
|
21707
|
+
if (ts20.isPropertyAccessExpression(n) && ts20.isIdentifier(n.expression) && n.expression.text === propsObjectName && n.name.text !== "children") {
|
|
20622
21708
|
found = true;
|
|
20623
21709
|
return;
|
|
20624
21710
|
}
|
|
20625
|
-
|
|
21711
|
+
ts20.forEachChild(n, visit3);
|
|
20626
21712
|
};
|
|
20627
21713
|
visit3(sf);
|
|
20628
21714
|
return found;
|
|
@@ -20692,7 +21778,7 @@ function findSourceFile2(meta) {
|
|
|
20692
21778
|
return null;
|
|
20693
21779
|
}
|
|
20694
21780
|
// src/profiler.ts
|
|
20695
|
-
import
|
|
21781
|
+
import ts21 from "typescript";
|
|
20696
21782
|
var PROFILE_SCHEMA_VERSION = 1;
|
|
20697
21783
|
var DEFAULT_FANOUT_THRESHOLD = 8;
|
|
20698
21784
|
function buildStaticBudget(source, filePath, componentName, options = {}) {
|
|
@@ -20962,15 +22048,15 @@ function joinProfilerEvents(events, index) {
|
|
|
20962
22048
|
return { joined, unattributed, diagnostics };
|
|
20963
22049
|
}
|
|
20964
22050
|
function findUninstrumentedEffects(source, filePath, instrumentedLines) {
|
|
20965
|
-
const sf =
|
|
22051
|
+
const sf = ts21.createSourceFile(filePath, source, ts21.ScriptTarget.Latest, true, ts21.ScriptKind.TSX);
|
|
20966
22052
|
const out = [];
|
|
20967
22053
|
const visit3 = (node) => {
|
|
20968
|
-
if (
|
|
22054
|
+
if (ts21.isCallExpression(node) && ts21.isIdentifier(node.expression) && node.expression.text === "createEffect") {
|
|
20969
22055
|
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
20970
22056
|
if (!instrumentedLines.has(line))
|
|
20971
22057
|
out.push({ file: filePath, line });
|
|
20972
22058
|
}
|
|
20973
|
-
|
|
22059
|
+
ts21.forEachChild(node, visit3);
|
|
20974
22060
|
};
|
|
20975
22061
|
visit3(sf);
|
|
20976
22062
|
out.sort((a, b) => a.line - b.line);
|
|
@@ -21278,13 +22364,13 @@ function assessBatchSafety(args) {
|
|
|
21278
22364
|
const signalGetters = new Set(args.graph.signals.map((s) => s.name));
|
|
21279
22365
|
let sf;
|
|
21280
22366
|
try {
|
|
21281
|
-
sf =
|
|
22367
|
+
sf = ts21.createSourceFile("__h.ts", `const __h = ${args.handler}`, ts21.ScriptTarget.Latest, true);
|
|
21282
22368
|
} catch {
|
|
21283
22369
|
return "unverified";
|
|
21284
22370
|
}
|
|
21285
22371
|
const calls = [];
|
|
21286
22372
|
const visit3 = (node) => {
|
|
21287
|
-
if (
|
|
22373
|
+
if (ts21.isCallExpression(node) && ts21.isIdentifier(node.expression)) {
|
|
21288
22374
|
const name = node.expression.text;
|
|
21289
22375
|
if (setters.has(name))
|
|
21290
22376
|
calls.push({ pos: node.getStart(sf), kind: "write" });
|
|
@@ -21293,7 +22379,7 @@ function assessBatchSafety(args) {
|
|
|
21293
22379
|
else if (!signalGetters.has(name) && !memoNames.has(name))
|
|
21294
22380
|
calls.push({ pos: node.getStart(sf), kind: "risky" });
|
|
21295
22381
|
}
|
|
21296
|
-
|
|
22382
|
+
ts21.forEachChild(node, visit3);
|
|
21297
22383
|
};
|
|
21298
22384
|
visit3(sf);
|
|
21299
22385
|
calls.sort((a, b) => a.pos - b.pos);
|
|
@@ -21658,608 +22744,273 @@ function computeMetrics(graph, eventSummary, hydrated) {
|
|
|
21658
22744
|
maxSignalFanOut: maxFanOut,
|
|
21659
22745
|
hotSignal,
|
|
21660
22746
|
maxMemoChainDepth,
|
|
21661
|
-
totalSubscriptions,
|
|
21662
|
-
batchCandidateCount
|
|
21663
|
-
};
|
|
21664
|
-
}
|
|
21665
|
-
function computeMaxMemoChainDepth(graph) {
|
|
21666
|
-
if (graph.memos.length === 0)
|
|
21667
|
-
return 0;
|
|
21668
|
-
const memoSet = new Set(graph.memos.map((m) => m.name));
|
|
21669
|
-
const memoDeps = new Map;
|
|
21670
|
-
for (const memo of graph.memos) {
|
|
21671
|
-
memoDeps.set(memo.name, memo.deps.filter((d) => memoSet.has(d)));
|
|
21672
|
-
}
|
|
21673
|
-
const cache = new Map;
|
|
21674
|
-
function depth(name, visited) {
|
|
21675
|
-
if (cache.has(name))
|
|
21676
|
-
return cache.get(name);
|
|
21677
|
-
if (visited.has(name))
|
|
21678
|
-
return 0;
|
|
21679
|
-
const children = memoDeps.get(name) ?? [];
|
|
21680
|
-
if (children.length === 0) {
|
|
21681
|
-
cache.set(name, 1);
|
|
21682
|
-
return 1;
|
|
21683
|
-
}
|
|
21684
|
-
visited.add(name);
|
|
21685
|
-
const d = 1 + Math.max(...children.map((c) => depth(c, new Set(visited))));
|
|
21686
|
-
cache.set(name, d);
|
|
21687
|
-
return d;
|
|
21688
|
-
}
|
|
21689
|
-
let max = 0;
|
|
21690
|
-
for (const memo of graph.memos) {
|
|
21691
|
-
const d = depth(memo.name, new Set);
|
|
21692
|
-
if (d > max)
|
|
21693
|
-
max = d;
|
|
21694
|
-
}
|
|
21695
|
-
return max;
|
|
21696
|
-
}
|
|
21697
|
-
function computeFindings(metrics, graph, eventSummary) {
|
|
21698
|
-
const findings = [];
|
|
21699
|
-
for (const signal of graph.signals) {
|
|
21700
|
-
if (signal.consumers.length > THRESHOLDS.highFanOut) {
|
|
21701
|
-
findings.push({
|
|
21702
|
-
kind: "high-fan-out",
|
|
21703
|
-
severity: "warning",
|
|
21704
|
-
signal: signal.name,
|
|
21705
|
-
message: `${signal.name} has ${signal.consumers.length} consumers (fan-out > ${THRESHOLDS.highFanOut})`,
|
|
21706
|
-
suggestion: `Split ${signal.name} into finer-grained signals, or add a createMemo to shield downstream consumers from unrelated updates`,
|
|
21707
|
-
loc: { file: signal.loc.file, line: signal.loc.line }
|
|
21708
|
-
});
|
|
21709
|
-
}
|
|
21710
|
-
}
|
|
21711
|
-
if (metrics.maxMemoChainDepth > THRESHOLDS.deepMemoChain) {
|
|
21712
|
-
findings.push({
|
|
21713
|
-
kind: "deep-memo-chain",
|
|
21714
|
-
severity: "warning",
|
|
21715
|
-
depth: metrics.maxMemoChainDepth,
|
|
21716
|
-
message: `Memo chain depth ${metrics.maxMemoChainDepth} (threshold: ${THRESHOLDS.deepMemoChain}) — a single signal update cascades through ${metrics.maxMemoChainDepth} memo levels`,
|
|
21717
|
-
suggestion: "Flatten intermediate memos that do not cache expensive computations; deep chains increase propagation latency"
|
|
21718
|
-
});
|
|
21719
|
-
}
|
|
21720
|
-
const batchSeen = new Set;
|
|
21721
|
-
for (const event of eventSummary.events) {
|
|
21722
|
-
const distinct = new Set;
|
|
21723
|
-
const setterNames = [];
|
|
21724
|
-
for (const sc of event.setterCalls) {
|
|
21725
|
-
if (sc.signal) {
|
|
21726
|
-
distinct.add(sc.signal);
|
|
21727
|
-
setterNames.push(sc.setter);
|
|
21728
|
-
}
|
|
21729
|
-
}
|
|
21730
|
-
if (distinct.size >= THRESHOLDS.batchMinSignals) {
|
|
21731
|
-
const dedupeKey = `${event.eventName}|${event.loc.file ?? ""}|${event.loc.start.line}|${[...distinct].sort().join(",")}`;
|
|
21732
|
-
if (batchSeen.has(dedupeKey))
|
|
21733
|
-
continue;
|
|
21734
|
-
batchSeen.add(dedupeKey);
|
|
21735
|
-
findings.push({
|
|
21736
|
-
kind: "batch-candidate",
|
|
21737
|
-
severity: "info",
|
|
21738
|
-
signals: [...distinct],
|
|
21739
|
-
message: `${event.eventName} on <${event.elementContext}> sets ${distinct.size} signals (${[...distinct].join(", ")}) — triggers ${distinct.size} separate update cycles (static; verify setters are not in separate if/else branches)`,
|
|
21740
|
-
suggestion: `If all listed setters fire unconditionally in the same handler path, wrap in batch(() => { ${setterNames.join("; ")}; }) to collapse ${distinct.size} cycles into 1`,
|
|
21741
|
-
loc: event.loc.file ? { file: event.loc.file, line: event.loc.start.line } : undefined
|
|
21742
|
-
});
|
|
21743
|
-
}
|
|
21744
|
-
}
|
|
21745
|
-
if (metrics.dynamicBindings > 0 && metrics.fallbacks >= THRESHOLDS.fallbackHeavyMin && metrics.fallbacks / metrics.dynamicBindings > THRESHOLDS.fallbackHeavyRatio) {
|
|
21746
|
-
findings.push({
|
|
21747
|
-
kind: "fallback-heavy",
|
|
21748
|
-
severity: "info",
|
|
21749
|
-
message: `${metrics.fallbacks}/${metrics.dynamicBindings} bindings (${Math.round(metrics.fallbacks / metrics.dynamicBindings * 100)}%) are fallback-wrapped — reactivity not statically provable`,
|
|
21750
|
-
suggestion: "Run `bf debug fallbacks` to see each expression and fix them so the compiler can prove reactivity without the fallback wrapper"
|
|
21751
|
-
});
|
|
21752
|
-
}
|
|
21753
|
-
return findings;
|
|
21754
|
-
}
|
|
21755
|
-
function diffProfiles(before, after) {
|
|
21756
|
-
const worseWhenHigher = new Set([
|
|
21757
|
-
"fallbacks",
|
|
21758
|
-
"maxSignalFanOut",
|
|
21759
|
-
"maxMemoChainDepth",
|
|
21760
|
-
"totalSubscriptions",
|
|
21761
|
-
"batchCandidateCount"
|
|
21762
|
-
]);
|
|
21763
|
-
const numericKeys = [
|
|
21764
|
-
"signals",
|
|
21765
|
-
"memos",
|
|
21766
|
-
"effects",
|
|
21767
|
-
"loops",
|
|
21768
|
-
"eventHandlers",
|
|
21769
|
-
"dynamicBindings",
|
|
21770
|
-
"fallbacks",
|
|
21771
|
-
"conditionals",
|
|
21772
|
-
"maxSignalFanOut",
|
|
21773
|
-
"maxMemoChainDepth",
|
|
21774
|
-
"totalSubscriptions",
|
|
21775
|
-
"batchCandidateCount"
|
|
21776
|
-
];
|
|
21777
|
-
const regressions = [];
|
|
21778
|
-
const improvements = [];
|
|
21779
|
-
const neutral = [];
|
|
21780
|
-
for (const key of numericKeys) {
|
|
21781
|
-
const b = before[key];
|
|
21782
|
-
const a = after[key];
|
|
21783
|
-
if (a === b)
|
|
21784
|
-
continue;
|
|
21785
|
-
const entry = { metric: key, before: b, after: a, delta: a - b };
|
|
21786
|
-
if (worseWhenHigher.has(key)) {
|
|
21787
|
-
if (a > b)
|
|
21788
|
-
regressions.push(entry);
|
|
21789
|
-
else
|
|
21790
|
-
improvements.push(entry);
|
|
21791
|
-
} else {
|
|
21792
|
-
neutral.push(entry);
|
|
21793
|
-
}
|
|
21794
|
-
}
|
|
21795
|
-
return { componentName: after.componentName, before, after, regressions, improvements, neutral };
|
|
21796
|
-
}
|
|
21797
|
-
function formatSingleProfile(profile) {
|
|
21798
|
-
const m = profile.metrics;
|
|
21799
|
-
const lines = [];
|
|
21800
|
-
lines.push(`${m.componentName} — reactive profile (static)`);
|
|
21801
|
-
if (m.sourceFile)
|
|
21802
|
-
lines.push(` source: ${m.sourceFile}`);
|
|
21803
|
-
lines.push(` hydrated: ${m.hydrated ? "yes" : "no"}`);
|
|
21804
|
-
lines.push("");
|
|
21805
|
-
lines.push(" Counts:");
|
|
21806
|
-
lines.push(` signals: ${m.signals}`);
|
|
21807
|
-
lines.push(` memos: ${m.memos}`);
|
|
21808
|
-
if (m.effects > 0)
|
|
21809
|
-
lines.push(` effects: ${m.effects}`);
|
|
21810
|
-
lines.push(` dynamic bindings: ${m.dynamicBindings}`);
|
|
21811
|
-
if (m.fallbacks > 0)
|
|
21812
|
-
lines.push(` fallbacks: ${m.fallbacks}`);
|
|
21813
|
-
if (m.loops > 0)
|
|
21814
|
-
lines.push(` loops: ${m.loops}`);
|
|
21815
|
-
if (m.conditionals > 0)
|
|
21816
|
-
lines.push(` conditionals: ${m.conditionals}`);
|
|
21817
|
-
if (m.eventHandlers > 0)
|
|
21818
|
-
lines.push(` event handlers: ${m.eventHandlers}`);
|
|
21819
|
-
lines.push("");
|
|
21820
|
-
lines.push(" Reactive budget (SR5):");
|
|
21821
|
-
const fanOutSuffix = m.hotSignal ? ` (${m.hotSignal})` : "";
|
|
21822
|
-
lines.push(` max signal fan-out: ${m.maxSignalFanOut}${fanOutSuffix}`);
|
|
21823
|
-
lines.push(` max memo chain depth: ${m.maxMemoChainDepth}`);
|
|
21824
|
-
lines.push(` total subscriptions: ${m.totalSubscriptions}`);
|
|
21825
|
-
if (m.batchCandidateCount > 0) {
|
|
21826
|
-
lines.push(` batch candidates: ${m.batchCandidateCount} handler(s) set ≥2 signals`);
|
|
21827
|
-
}
|
|
21828
|
-
if (profile.findings.length > 0) {
|
|
21829
|
-
lines.push("");
|
|
21830
|
-
lines.push(" Findings:");
|
|
21831
|
-
for (const f of profile.findings) {
|
|
21832
|
-
const icon = f.severity === "warning" ? "⚠" : "→";
|
|
21833
|
-
lines.push(` ${icon} [${f.kind}] ${f.message}`);
|
|
21834
|
-
lines.push(` fix: ${f.suggestion}`);
|
|
21835
|
-
if (f.loc) {
|
|
21836
|
-
const file = f.loc.file.split("/").pop() ?? f.loc.file;
|
|
21837
|
-
lines.push(` at ${file}:${f.loc.line}`);
|
|
21838
|
-
}
|
|
21839
|
-
}
|
|
21840
|
-
} else {
|
|
21841
|
-
lines.push("");
|
|
21842
|
-
lines.push(" No findings — component is within all thresholds.");
|
|
21843
|
-
}
|
|
21844
|
-
return lines.join(`
|
|
21845
|
-
`);
|
|
21846
|
-
}
|
|
21847
|
-
function formatProfileTable(profiles) {
|
|
21848
|
-
if (profiles.length === 0)
|
|
21849
|
-
return "No components found.";
|
|
21850
|
-
const sorted = [...profiles].sort((a, b) => b.metrics.totalSubscriptions - a.metrics.totalSubscriptions);
|
|
21851
|
-
const lines = [];
|
|
21852
|
-
lines.push("Component sig memo bind fall fanOut chain subs batch findings");
|
|
21853
|
-
lines.push("─".repeat(90));
|
|
21854
|
-
for (const p of sorted) {
|
|
21855
|
-
const m = p.metrics;
|
|
21856
|
-
const name = m.componentName.padEnd(23).slice(0, 23);
|
|
21857
|
-
const findingStr = p.findings.length > 0 ? p.findings.map((f) => f.kind.replace(/-/g, "_")).join(",") : "—";
|
|
21858
|
-
const row = [
|
|
21859
|
-
name,
|
|
21860
|
-
String(m.signals).padStart(3),
|
|
21861
|
-
String(m.memos).padStart(5),
|
|
21862
|
-
String(m.dynamicBindings).padStart(5),
|
|
21863
|
-
String(m.fallbacks).padStart(5),
|
|
21864
|
-
String(m.maxSignalFanOut).padStart(7),
|
|
21865
|
-
String(m.maxMemoChainDepth).padStart(6),
|
|
21866
|
-
String(m.totalSubscriptions).padStart(5),
|
|
21867
|
-
String(m.batchCandidateCount).padStart(6),
|
|
21868
|
-
` ${findingStr}`
|
|
21869
|
-
].join(" ");
|
|
21870
|
-
lines.push(row);
|
|
21871
|
-
}
|
|
21872
|
-
const allFindings = sorted.flatMap((p) => p.findings.map((f) => ({ component: p.metrics.componentName, finding: f })));
|
|
21873
|
-
if (allFindings.length > 0) {
|
|
21874
|
-
lines.push("");
|
|
21875
|
-
lines.push("Findings:");
|
|
21876
|
-
for (const { component, finding } of allFindings) {
|
|
21877
|
-
const icon = finding.severity === "warning" ? "⚠" : "→";
|
|
21878
|
-
lines.push(` ${icon} ${component}: ${finding.message}`);
|
|
21879
|
-
lines.push(` fix: ${finding.suggestion}`);
|
|
21880
|
-
if (finding.loc) {
|
|
21881
|
-
const file = finding.loc.file.split("/").pop() ?? finding.loc.file;
|
|
21882
|
-
lines.push(` at ${file}:${finding.loc.line}`);
|
|
21883
|
-
}
|
|
21884
|
-
}
|
|
21885
|
-
} else {
|
|
21886
|
-
lines.push("");
|
|
21887
|
-
lines.push("No findings across all components.");
|
|
21888
|
-
}
|
|
21889
|
-
return lines.join(`
|
|
21890
|
-
`);
|
|
21891
|
-
}
|
|
21892
|
-
function formatProfileDiff(diff) {
|
|
21893
|
-
const lines = [];
|
|
21894
|
-
lines.push(`${diff.componentName} — reactive profile diff (before → after)`);
|
|
21895
|
-
lines.push("");
|
|
21896
|
-
if (diff.regressions.length === 0 && diff.improvements.length === 0 && diff.neutral.length === 0) {
|
|
21897
|
-
lines.push(" No changes in reactive metrics.");
|
|
21898
|
-
return lines.join(`
|
|
21899
|
-
`);
|
|
21900
|
-
}
|
|
21901
|
-
if (diff.regressions.length > 0) {
|
|
21902
|
-
lines.push(" Regressions (reactive cost increased):");
|
|
21903
|
-
for (const e of diff.regressions) {
|
|
21904
|
-
lines.push(` ${e.metric}: ${e.before} → ${e.after} (+${e.delta})`);
|
|
21905
|
-
}
|
|
22747
|
+
totalSubscriptions,
|
|
22748
|
+
batchCandidateCount
|
|
22749
|
+
};
|
|
22750
|
+
}
|
|
22751
|
+
function computeMaxMemoChainDepth(graph) {
|
|
22752
|
+
if (graph.memos.length === 0)
|
|
22753
|
+
return 0;
|
|
22754
|
+
const memoSet = new Set(graph.memos.map((m) => m.name));
|
|
22755
|
+
const memoDeps = new Map;
|
|
22756
|
+
for (const memo of graph.memos) {
|
|
22757
|
+
memoDeps.set(memo.name, memo.deps.filter((d) => memoSet.has(d)));
|
|
21906
22758
|
}
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
|
|
22759
|
+
const cache = new Map;
|
|
22760
|
+
function depth(name, visited) {
|
|
22761
|
+
if (cache.has(name))
|
|
22762
|
+
return cache.get(name);
|
|
22763
|
+
if (visited.has(name))
|
|
22764
|
+
return 0;
|
|
22765
|
+
const children = memoDeps.get(name) ?? [];
|
|
22766
|
+
if (children.length === 0) {
|
|
22767
|
+
cache.set(name, 1);
|
|
22768
|
+
return 1;
|
|
21911
22769
|
}
|
|
22770
|
+
visited.add(name);
|
|
22771
|
+
const d = 1 + Math.max(...children.map((c) => depth(c, new Set(visited))));
|
|
22772
|
+
cache.set(name, d);
|
|
22773
|
+
return d;
|
|
21912
22774
|
}
|
|
21913
|
-
|
|
21914
|
-
|
|
21915
|
-
|
|
21916
|
-
|
|
21917
|
-
|
|
21918
|
-
}
|
|
22775
|
+
let max = 0;
|
|
22776
|
+
for (const memo of graph.memos) {
|
|
22777
|
+
const d = depth(memo.name, new Set);
|
|
22778
|
+
if (d > max)
|
|
22779
|
+
max = d;
|
|
21919
22780
|
}
|
|
21920
|
-
return
|
|
21921
|
-
`);
|
|
21922
|
-
}
|
|
21923
|
-
function profileToJSON(profile) {
|
|
21924
|
-
return {
|
|
21925
|
-
metrics: profile.metrics,
|
|
21926
|
-
findings: profile.findings
|
|
21927
|
-
};
|
|
22781
|
+
return max;
|
|
21928
22782
|
}
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
22783
|
+
function computeFindings(metrics, graph, eventSummary) {
|
|
22784
|
+
const findings = [];
|
|
22785
|
+
for (const signal of graph.signals) {
|
|
22786
|
+
if (signal.consumers.length > THRESHOLDS.highFanOut) {
|
|
22787
|
+
findings.push({
|
|
22788
|
+
kind: "high-fan-out",
|
|
22789
|
+
severity: "warning",
|
|
22790
|
+
signal: signal.name,
|
|
22791
|
+
message: `${signal.name} has ${signal.consumers.length} consumers (fan-out > ${THRESHOLDS.highFanOut})`,
|
|
22792
|
+
suggestion: `Split ${signal.name} into finer-grained signals, or add a createMemo to shield downstream consumers from unrelated updates`,
|
|
22793
|
+
loc: { file: signal.loc.file, line: signal.loc.line }
|
|
22794
|
+
});
|
|
22795
|
+
}
|
|
21938
22796
|
}
|
|
21939
|
-
if (
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
21945
|
-
|
|
21946
|
-
if (ctxName === null || !contextDefaults.has(ctxName))
|
|
21947
|
-
continue;
|
|
21948
|
-
consumers.push({
|
|
21949
|
-
localName: c.name,
|
|
21950
|
-
contextName: ctxName,
|
|
21951
|
-
defaultValue: contextDefaults.get(ctxName) ?? null
|
|
22797
|
+
if (metrics.maxMemoChainDepth > THRESHOLDS.deepMemoChain) {
|
|
22798
|
+
findings.push({
|
|
22799
|
+
kind: "deep-memo-chain",
|
|
22800
|
+
severity: "warning",
|
|
22801
|
+
depth: metrics.maxMemoChainDepth,
|
|
22802
|
+
message: `Memo chain depth ${metrics.maxMemoChainDepth} (threshold: ${THRESHOLDS.deepMemoChain}) — a single signal update cascades through ${metrics.maxMemoChainDepth} memo levels`,
|
|
22803
|
+
suggestion: "Flatten intermediate memos that do not cache expensive computations; deep chains increase propagation latency"
|
|
21952
22804
|
});
|
|
21953
22805
|
}
|
|
21954
|
-
|
|
21955
|
-
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
if (expr.arguments.length !== 1)
|
|
21963
|
-
return null;
|
|
21964
|
-
const arg = expr.arguments[0];
|
|
21965
|
-
return ts21.isIdentifier(arg) ? arg.text : null;
|
|
21966
|
-
}
|
|
21967
|
-
function parseCreateContextDefault(source) {
|
|
21968
|
-
const expr = parseSingleExpression(source);
|
|
21969
|
-
if (!expr || !ts21.isCallExpression(expr))
|
|
21970
|
-
return null;
|
|
21971
|
-
if (expr.arguments.length === 0)
|
|
21972
|
-
return null;
|
|
21973
|
-
const arg = expr.arguments[0];
|
|
21974
|
-
if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
|
|
21975
|
-
return arg.text;
|
|
21976
|
-
if (ts21.isNumericLiteral(arg))
|
|
21977
|
-
return Number(arg.text);
|
|
21978
|
-
if (arg.kind === ts21.SyntaxKind.TrueKeyword)
|
|
21979
|
-
return true;
|
|
21980
|
-
if (arg.kind === ts21.SyntaxKind.FalseKeyword)
|
|
21981
|
-
return false;
|
|
21982
|
-
return null;
|
|
21983
|
-
}
|
|
21984
|
-
function parseSingleExpression(source) {
|
|
21985
|
-
const sf = ts21.createSourceFile("__ctx.ts", `(${source})`, ts21.ScriptTarget.Latest, false);
|
|
21986
|
-
const stmt = sf.statements[0];
|
|
21987
|
-
if (!stmt || !ts21.isExpressionStatement(stmt))
|
|
21988
|
-
return null;
|
|
21989
|
-
let e = stmt.expression;
|
|
21990
|
-
while (ts21.isParenthesizedExpression(e))
|
|
21991
|
-
e = e.expression;
|
|
21992
|
-
return e;
|
|
21993
|
-
}
|
|
21994
|
-
function augmentInheritedPropAccesses(ir) {
|
|
21995
|
-
const propsObj = ir.metadata.propsObjectName;
|
|
21996
|
-
if (!propsObj)
|
|
21997
|
-
return;
|
|
21998
|
-
const existing = new Set(ir.metadata.propsParams.map((p) => p.name));
|
|
21999
|
-
const bareRefProps = new Set;
|
|
22000
|
-
const booleanAttrProps = new Set;
|
|
22001
|
-
const accessed = new Set;
|
|
22002
|
-
const accessRe = new RegExp(`(?:^|[^\\w$.])${propsObj}\\.([A-Za-z_$][\\w$]*)`, "g");
|
|
22003
|
-
const scan = (s) => {
|
|
22004
|
-
if (!s)
|
|
22005
|
-
return;
|
|
22006
|
-
for (const m of s.matchAll(accessRe))
|
|
22007
|
-
accessed.add(m[1]);
|
|
22008
|
-
};
|
|
22009
|
-
for (const memo of ir.metadata.memos)
|
|
22010
|
-
scan(memo.computation);
|
|
22011
|
-
for (const signal of ir.metadata.signals)
|
|
22012
|
-
scan(signal.initialValue);
|
|
22013
|
-
for (const stmt of ir.metadata.initStatements ?? [])
|
|
22014
|
-
scan(stmt.body);
|
|
22015
|
-
for (const eff of ir.metadata.effects ?? [])
|
|
22016
|
-
scan(eff.body);
|
|
22017
|
-
for (const c of ir.metadata.localConstants ?? []) {
|
|
22018
|
-
if (c.isModule)
|
|
22019
|
-
continue;
|
|
22020
|
-
scan(c.value);
|
|
22021
|
-
}
|
|
22022
|
-
const walk = (node) => {
|
|
22023
|
-
if (!node)
|
|
22024
|
-
return;
|
|
22025
|
-
const el = node;
|
|
22026
|
-
for (const attr of el.attrs ?? []) {
|
|
22027
|
-
const v = attr.value;
|
|
22028
|
-
if (v?.parts) {
|
|
22029
|
-
for (const part of v.parts) {
|
|
22030
|
-
if (part.type === "string")
|
|
22031
|
-
scan(part.value);
|
|
22032
|
-
else if (part.type === "ternary") {
|
|
22033
|
-
scan(part.condition);
|
|
22034
|
-
scan(part.whenTrue);
|
|
22035
|
-
scan(part.whenFalse);
|
|
22036
|
-
} else if (part.type === "lookup")
|
|
22037
|
-
scan(part.key);
|
|
22038
|
-
}
|
|
22039
|
-
}
|
|
22040
|
-
if (v?.kind === "expression" && typeof v.expr === "string") {
|
|
22041
|
-
scan(v.expr);
|
|
22042
|
-
const expr = v.expr.trim();
|
|
22043
|
-
const prefix = `${propsObj}.`;
|
|
22044
|
-
if (isBooleanAttr(attr.name) || v.presenceOrUndefined) {
|
|
22045
|
-
const m = expr.match(new RegExp(`^${propsObj}\\.([A-Za-z_$][\\w$]*)`));
|
|
22046
|
-
if (m)
|
|
22047
|
-
booleanAttrProps.add(m[1]);
|
|
22048
|
-
} else if (expr.startsWith(prefix)) {
|
|
22049
|
-
const rest = expr.slice(prefix.length);
|
|
22050
|
-
if (/^[A-Za-z_$][\w$]*$/.test(rest))
|
|
22051
|
-
bareRefProps.add(rest);
|
|
22052
|
-
}
|
|
22806
|
+
const batchSeen = new Set;
|
|
22807
|
+
for (const event of eventSummary.events) {
|
|
22808
|
+
const distinct = new Set;
|
|
22809
|
+
const setterNames = [];
|
|
22810
|
+
for (const sc of event.setterCalls) {
|
|
22811
|
+
if (sc.signal) {
|
|
22812
|
+
distinct.add(sc.signal);
|
|
22813
|
+
setterNames.push(sc.setter);
|
|
22053
22814
|
}
|
|
22054
22815
|
}
|
|
22055
|
-
|
|
22056
|
-
const
|
|
22057
|
-
|
|
22816
|
+
if (distinct.size >= THRESHOLDS.batchMinSignals) {
|
|
22817
|
+
const dedupeKey = `${event.eventName}|${event.loc.file ?? ""}|${event.loc.start.line}|${[...distinct].sort().join(",")}`;
|
|
22818
|
+
if (batchSeen.has(dedupeKey))
|
|
22819
|
+
continue;
|
|
22820
|
+
batchSeen.add(dedupeKey);
|
|
22821
|
+
findings.push({
|
|
22822
|
+
kind: "batch-candidate",
|
|
22823
|
+
severity: "info",
|
|
22824
|
+
signals: [...distinct],
|
|
22825
|
+
message: `${event.eventName} on <${event.elementContext}> sets ${distinct.size} signals (${[...distinct].join(", ")}) — triggers ${distinct.size} separate update cycles (static; verify setters are not in separate if/else branches)`,
|
|
22826
|
+
suggestion: `If all listed setters fire unconditionally in the same handler path, wrap in batch(() => { ${setterNames.join("; ")}; }) to collapse ${distinct.size} cycles into 1`,
|
|
22827
|
+
loc: event.loc.file ? { file: event.loc.file, line: event.loc.start.line } : undefined
|
|
22828
|
+
});
|
|
22058
22829
|
}
|
|
22059
|
-
const branchy = node;
|
|
22060
|
-
walk(branchy.whenTrue);
|
|
22061
|
-
walk(branchy.whenFalse);
|
|
22062
|
-
walk(branchy.consequent);
|
|
22063
|
-
walk(branchy.alternate);
|
|
22064
|
-
};
|
|
22065
|
-
walk(ir.root);
|
|
22066
|
-
for (const name of accessed) {
|
|
22067
|
-
if (existing.has(name))
|
|
22068
|
-
continue;
|
|
22069
|
-
let raw;
|
|
22070
|
-
if (booleanAttrProps.has(name))
|
|
22071
|
-
raw = "boolean";
|
|
22072
|
-
else if (bareRefProps.has(name))
|
|
22073
|
-
raw = "unknown";
|
|
22074
|
-
else
|
|
22075
|
-
raw = "string";
|
|
22076
|
-
const type = raw === "boolean" ? { kind: "primitive", raw: "boolean", primitive: "boolean" } : raw === "string" ? { kind: "primitive", raw: "string", primitive: "string" } : { kind: "unknown", raw: "unknown" };
|
|
22077
|
-
ir.metadata.propsParams.push({ name, type, optional: true });
|
|
22078
|
-
existing.add(name);
|
|
22079
22830
|
}
|
|
22831
|
+
if (metrics.dynamicBindings > 0 && metrics.fallbacks >= THRESHOLDS.fallbackHeavyMin && metrics.fallbacks / metrics.dynamicBindings > THRESHOLDS.fallbackHeavyRatio) {
|
|
22832
|
+
findings.push({
|
|
22833
|
+
kind: "fallback-heavy",
|
|
22834
|
+
severity: "info",
|
|
22835
|
+
message: `${metrics.fallbacks}/${metrics.dynamicBindings} bindings (${Math.round(metrics.fallbacks / metrics.dynamicBindings * 100)}%) are fallback-wrapped — reactivity not statically provable`,
|
|
22836
|
+
suggestion: "Run `bf debug fallbacks` to see each expression and fix them so the compiler can prove reactivity without the fallback wrapper"
|
|
22837
|
+
});
|
|
22838
|
+
}
|
|
22839
|
+
return findings;
|
|
22080
22840
|
}
|
|
22081
|
-
function
|
|
22082
|
-
const
|
|
22083
|
-
|
|
22084
|
-
|
|
22085
|
-
|
|
22086
|
-
|
|
22087
|
-
|
|
22088
|
-
|
|
22089
|
-
|
|
22090
|
-
|
|
22091
|
-
|
|
22092
|
-
|
|
22841
|
+
function diffProfiles(before, after) {
|
|
22842
|
+
const worseWhenHigher = new Set([
|
|
22843
|
+
"fallbacks",
|
|
22844
|
+
"maxSignalFanOut",
|
|
22845
|
+
"maxMemoChainDepth",
|
|
22846
|
+
"totalSubscriptions",
|
|
22847
|
+
"batchCandidateCount"
|
|
22848
|
+
]);
|
|
22849
|
+
const numericKeys = [
|
|
22850
|
+
"signals",
|
|
22851
|
+
"memos",
|
|
22852
|
+
"effects",
|
|
22853
|
+
"loops",
|
|
22854
|
+
"eventHandlers",
|
|
22855
|
+
"dynamicBindings",
|
|
22856
|
+
"fallbacks",
|
|
22857
|
+
"conditionals",
|
|
22858
|
+
"maxSignalFanOut",
|
|
22859
|
+
"maxMemoChainDepth",
|
|
22860
|
+
"totalSubscriptions",
|
|
22861
|
+
"batchCandidateCount"
|
|
22862
|
+
];
|
|
22863
|
+
const regressions = [];
|
|
22864
|
+
const improvements = [];
|
|
22865
|
+
const neutral = [];
|
|
22866
|
+
for (const key of numericKeys) {
|
|
22867
|
+
const b = before[key];
|
|
22868
|
+
const a = after[key];
|
|
22869
|
+
if (a === b)
|
|
22870
|
+
continue;
|
|
22871
|
+
const entry = { metric: key, before: b, after: a, delta: a - b };
|
|
22872
|
+
if (worseWhenHigher.has(key)) {
|
|
22873
|
+
if (a > b)
|
|
22874
|
+
regressions.push(entry);
|
|
22875
|
+
else
|
|
22876
|
+
improvements.push(entry);
|
|
22877
|
+
} else {
|
|
22878
|
+
neutral.push(entry);
|
|
22879
|
+
}
|
|
22093
22880
|
}
|
|
22094
|
-
return
|
|
22881
|
+
return { componentName: after.componentName, before, after, regressions, improvements, neutral };
|
|
22095
22882
|
}
|
|
22096
|
-
function
|
|
22097
|
-
const
|
|
22098
|
-
const
|
|
22099
|
-
|
|
22100
|
-
|
|
22101
|
-
|
|
22102
|
-
|
|
22103
|
-
|
|
22104
|
-
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22883
|
+
function formatSingleProfile(profile) {
|
|
22884
|
+
const m = profile.metrics;
|
|
22885
|
+
const lines = [];
|
|
22886
|
+
lines.push(`${m.componentName} — reactive profile (static)`);
|
|
22887
|
+
if (m.sourceFile)
|
|
22888
|
+
lines.push(` source: ${m.sourceFile}`);
|
|
22889
|
+
lines.push(` hydrated: ${m.hydrated ? "yes" : "no"}`);
|
|
22890
|
+
lines.push("");
|
|
22891
|
+
lines.push(" Counts:");
|
|
22892
|
+
lines.push(` signals: ${m.signals}`);
|
|
22893
|
+
lines.push(` memos: ${m.memos}`);
|
|
22894
|
+
if (m.effects > 0)
|
|
22895
|
+
lines.push(` effects: ${m.effects}`);
|
|
22896
|
+
lines.push(` dynamic bindings: ${m.dynamicBindings}`);
|
|
22897
|
+
if (m.fallbacks > 0)
|
|
22898
|
+
lines.push(` fallbacks: ${m.fallbacks}`);
|
|
22899
|
+
if (m.loops > 0)
|
|
22900
|
+
lines.push(` loops: ${m.loops}`);
|
|
22901
|
+
if (m.conditionals > 0)
|
|
22902
|
+
lines.push(` conditionals: ${m.conditionals}`);
|
|
22903
|
+
if (m.eventHandlers > 0)
|
|
22904
|
+
lines.push(` event handlers: ${m.eventHandlers}`);
|
|
22905
|
+
lines.push("");
|
|
22906
|
+
lines.push(" Reactive budget (SR5):");
|
|
22907
|
+
const fanOutSuffix = m.hotSignal ? ` (${m.hotSignal})` : "";
|
|
22908
|
+
lines.push(` max signal fan-out: ${m.maxSignalFanOut}${fanOutSuffix}`);
|
|
22909
|
+
lines.push(` max memo chain depth: ${m.maxMemoChainDepth}`);
|
|
22910
|
+
lines.push(` total subscriptions: ${m.totalSubscriptions}`);
|
|
22911
|
+
if (m.batchCandidateCount > 0) {
|
|
22912
|
+
lines.push(` batch candidates: ${m.batchCandidateCount} handler(s) set ≥2 signals`);
|
|
22114
22913
|
}
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
22120
|
-
|
|
22121
|
-
|
|
22122
|
-
|
|
22123
|
-
|
|
22124
|
-
|
|
22125
|
-
continue;
|
|
22126
|
-
const literal = parseStaticStringConst(c.value) ?? evalTemplateOfStringConsts(c.value, map);
|
|
22127
|
-
if (literal !== null) {
|
|
22128
|
-
map.set(c.name, literal);
|
|
22129
|
-
progressed = true;
|
|
22914
|
+
if (profile.findings.length > 0) {
|
|
22915
|
+
lines.push("");
|
|
22916
|
+
lines.push(" Findings:");
|
|
22917
|
+
for (const f of profile.findings) {
|
|
22918
|
+
const icon = f.severity === "warning" ? "⚠" : "→";
|
|
22919
|
+
lines.push(` ${icon} [${f.kind}] ${f.message}`);
|
|
22920
|
+
lines.push(` fix: ${f.suggestion}`);
|
|
22921
|
+
if (f.loc) {
|
|
22922
|
+
const file = f.loc.file.split("/").pop() ?? f.loc.file;
|
|
22923
|
+
lines.push(` at ${file}:${f.loc.line}`);
|
|
22130
22924
|
}
|
|
22131
22925
|
}
|
|
22926
|
+
} else {
|
|
22927
|
+
lines.push("");
|
|
22928
|
+
lines.push(" No findings — component is within all thresholds.");
|
|
22132
22929
|
}
|
|
22133
|
-
return
|
|
22930
|
+
return lines.join(`
|
|
22931
|
+
`);
|
|
22134
22932
|
}
|
|
22135
|
-
function
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
const
|
|
22140
|
-
|
|
22141
|
-
|
|
22142
|
-
const
|
|
22143
|
-
|
|
22144
|
-
|
|
22145
|
-
|
|
22146
|
-
|
|
22147
|
-
|
|
22148
|
-
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
v = v.expression;
|
|
22160
|
-
if (ts21.isNumericLiteral(v))
|
|
22161
|
-
return { kind: "number", text: v.text };
|
|
22162
|
-
if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
|
|
22163
|
-
return { kind: "string", text: v.text };
|
|
22164
|
-
}
|
|
22165
|
-
return null;
|
|
22933
|
+
function formatProfileTable(profiles) {
|
|
22934
|
+
if (profiles.length === 0)
|
|
22935
|
+
return "No components found.";
|
|
22936
|
+
const sorted = [...profiles].sort((a, b) => b.metrics.totalSubscriptions - a.metrics.totalSubscriptions);
|
|
22937
|
+
const lines = [];
|
|
22938
|
+
lines.push("Component sig memo bind fall fanOut chain subs batch findings");
|
|
22939
|
+
lines.push("─".repeat(90));
|
|
22940
|
+
for (const p of sorted) {
|
|
22941
|
+
const m = p.metrics;
|
|
22942
|
+
const name = m.componentName.padEnd(23).slice(0, 23);
|
|
22943
|
+
const findingStr = p.findings.length > 0 ? p.findings.map((f) => f.kind.replace(/-/g, "_")).join(",") : "—";
|
|
22944
|
+
const row = [
|
|
22945
|
+
name,
|
|
22946
|
+
String(m.signals).padStart(3),
|
|
22947
|
+
String(m.memos).padStart(5),
|
|
22948
|
+
String(m.dynamicBindings).padStart(5),
|
|
22949
|
+
String(m.fallbacks).padStart(5),
|
|
22950
|
+
String(m.maxSignalFanOut).padStart(7),
|
|
22951
|
+
String(m.maxMemoChainDepth).padStart(6),
|
|
22952
|
+
String(m.totalSubscriptions).padStart(5),
|
|
22953
|
+
String(m.batchCandidateCount).padStart(6),
|
|
22954
|
+
` ${findingStr}`
|
|
22955
|
+
].join(" ");
|
|
22956
|
+
lines.push(row);
|
|
22166
22957
|
}
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
|
|
22171
|
-
|
|
22172
|
-
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
const callee = node.expression;
|
|
22180
|
-
if (!ts21.isPropertyAccessExpression(callee))
|
|
22181
|
-
return null;
|
|
22182
|
-
if (callee.name.text !== "join")
|
|
22183
|
-
return null;
|
|
22184
|
-
let recv = callee.expression;
|
|
22185
|
-
while (ts21.isParenthesizedExpression(recv))
|
|
22186
|
-
recv = recv.expression;
|
|
22187
|
-
if (!ts21.isArrayLiteralExpression(recv))
|
|
22188
|
-
return null;
|
|
22189
|
-
const parts = [];
|
|
22190
|
-
for (const el of recv.elements) {
|
|
22191
|
-
if (ts21.isStringLiteral(el) || ts21.isNoSubstitutionTemplateLiteral(el)) {
|
|
22192
|
-
parts.push(el.text);
|
|
22193
|
-
} else {
|
|
22194
|
-
return null;
|
|
22958
|
+
const allFindings = sorted.flatMap((p) => p.findings.map((f) => ({ component: p.metrics.componentName, finding: f })));
|
|
22959
|
+
if (allFindings.length > 0) {
|
|
22960
|
+
lines.push("");
|
|
22961
|
+
lines.push("Findings:");
|
|
22962
|
+
for (const { component, finding } of allFindings) {
|
|
22963
|
+
const icon = finding.severity === "warning" ? "⚠" : "→";
|
|
22964
|
+
lines.push(` ${icon} ${component}: ${finding.message}`);
|
|
22965
|
+
lines.push(` fix: ${finding.suggestion}`);
|
|
22966
|
+
if (finding.loc) {
|
|
22967
|
+
const file = finding.loc.file.split("/").pop() ?? finding.loc.file;
|
|
22968
|
+
lines.push(` at ${file}:${finding.loc.line}`);
|
|
22969
|
+
}
|
|
22195
22970
|
}
|
|
22971
|
+
} else {
|
|
22972
|
+
lines.push("");
|
|
22973
|
+
lines.push("No findings across all components.");
|
|
22196
22974
|
}
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
const arg = node.arguments[0];
|
|
22200
|
-
if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
|
|
22201
|
-
sep2 = arg.text;
|
|
22202
|
-
else
|
|
22203
|
-
return null;
|
|
22204
|
-
}
|
|
22205
|
-
return parts.join(sep2);
|
|
22975
|
+
return lines.join(`
|
|
22976
|
+
`);
|
|
22206
22977
|
}
|
|
22207
|
-
function
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
return
|
|
22214
|
-
|
|
22215
|
-
let defaultKey;
|
|
22216
|
-
const resolved = resolveKey?.(arg.text);
|
|
22217
|
-
if (resolved) {
|
|
22218
|
-
indexPropName = resolved.propName;
|
|
22219
|
-
defaultKey = resolved.defaultLiteral;
|
|
22220
|
-
} else if (propsParams.some((p) => p.name === arg.text)) {
|
|
22221
|
-
indexPropName = arg.text;
|
|
22222
|
-
} else {
|
|
22223
|
-
return null;
|
|
22978
|
+
function formatProfileDiff(diff) {
|
|
22979
|
+
const lines = [];
|
|
22980
|
+
lines.push(`${diff.componentName} — reactive profile diff (before → after)`);
|
|
22981
|
+
lines.push("");
|
|
22982
|
+
if (diff.regressions.length === 0 && diff.improvements.length === 0 && diff.neutral.length === 0) {
|
|
22983
|
+
lines.push(" No changes in reactive metrics.");
|
|
22984
|
+
return lines.join(`
|
|
22985
|
+
`);
|
|
22224
22986
|
}
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
if (sf.statements.length !== 1)
|
|
22230
|
-
return null;
|
|
22231
|
-
const stmt = sf.statements[0];
|
|
22232
|
-
if (!ts21.isExpressionStatement(stmt))
|
|
22233
|
-
return null;
|
|
22234
|
-
let parsed = stmt.expression;
|
|
22235
|
-
while (ts21.isParenthesizedExpression(parsed))
|
|
22236
|
-
parsed = parsed.expression;
|
|
22237
|
-
if (!ts21.isObjectLiteralExpression(parsed))
|
|
22238
|
-
return null;
|
|
22239
|
-
const entries = [];
|
|
22240
|
-
for (const prop of parsed.properties) {
|
|
22241
|
-
if (!ts21.isPropertyAssignment(prop))
|
|
22242
|
-
return null;
|
|
22243
|
-
let key;
|
|
22244
|
-
if (ts21.isIdentifier(prop.name)) {
|
|
22245
|
-
key = prop.name.text;
|
|
22246
|
-
} else if (ts21.isStringLiteral(prop.name) || ts21.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
22247
|
-
key = prop.name.text;
|
|
22248
|
-
} else {
|
|
22249
|
-
return null;
|
|
22987
|
+
if (diff.regressions.length > 0) {
|
|
22988
|
+
lines.push(" Regressions (reactive cost increased):");
|
|
22989
|
+
for (const e of diff.regressions) {
|
|
22990
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (+${e.delta})`);
|
|
22250
22991
|
}
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
} else if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
|
|
22257
|
-
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
22258
|
-
} else {
|
|
22259
|
-
return null;
|
|
22992
|
+
}
|
|
22993
|
+
if (diff.improvements.length > 0) {
|
|
22994
|
+
lines.push(" Improvements (reactive cost decreased):");
|
|
22995
|
+
for (const e of diff.improvements) {
|
|
22996
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (${e.delta})`);
|
|
22260
22997
|
}
|
|
22261
22998
|
}
|
|
22262
|
-
|
|
22999
|
+
if (diff.neutral.length > 0) {
|
|
23000
|
+
lines.push(" Structural changes (count changes, no clear direction):");
|
|
23001
|
+
for (const e of diff.neutral) {
|
|
23002
|
+
const sign = (e.delta ?? 0) > 0 ? "+" : "";
|
|
23003
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (${sign}${e.delta})`);
|
|
23004
|
+
}
|
|
23005
|
+
}
|
|
23006
|
+
return lines.join(`
|
|
23007
|
+
`);
|
|
23008
|
+
}
|
|
23009
|
+
function profileToJSON(profile) {
|
|
23010
|
+
return {
|
|
23011
|
+
metrics: profile.metrics,
|
|
23012
|
+
findings: profile.findings
|
|
23013
|
+
};
|
|
22263
23014
|
}
|
|
22264
23015
|
|
|
22265
23016
|
// src/index.ts
|
|
@@ -22292,6 +23043,7 @@ export {
|
|
|
22292
23043
|
parseBlockBodyTolerant,
|
|
22293
23044
|
parseBlockBody,
|
|
22294
23045
|
needsTypeBasedDetection,
|
|
23046
|
+
materializeGetterCalls,
|
|
22295
23047
|
matchSearchParamsMethodCall,
|
|
22296
23048
|
matchQueryHrefCall,
|
|
22297
23049
|
matchLoweringCall,
|
|
@@ -22301,8 +23053,10 @@ export {
|
|
|
22301
23053
|
listComponentFunctions,
|
|
22302
23054
|
jsxToIR,
|
|
22303
23055
|
joinProfilerEvents,
|
|
23056
|
+
isValidHelperId,
|
|
22304
23057
|
isSupported,
|
|
22305
23058
|
isLowerableObjectRestDestructure,
|
|
23059
|
+
isLowerableLoopDestructure,
|
|
22306
23060
|
isBooleanAttr,
|
|
22307
23061
|
importsSearchParams,
|
|
22308
23062
|
identifierPath,
|
|
@@ -22315,6 +23069,7 @@ export {
|
|
|
22315
23069
|
generateClientJsWithSourceMap,
|
|
22316
23070
|
generateClientJs,
|
|
22317
23071
|
freeVarsInBody,
|
|
23072
|
+
freeIdentifiers,
|
|
22318
23073
|
formatWhyUpdate,
|
|
22319
23074
|
formatWastedReReruns,
|
|
22320
23075
|
formatUpdatePath,
|
|
@@ -22343,6 +23098,8 @@ export {
|
|
|
22343
23098
|
exprToString,
|
|
22344
23099
|
evaluateProfileGates,
|
|
22345
23100
|
evalStringArrayJoin,
|
|
23101
|
+
envSignalReaderFor,
|
|
23102
|
+
envSignalLocalNames,
|
|
22346
23103
|
enableCompilerInstrumentation,
|
|
22347
23104
|
emitParsedExpr,
|
|
22348
23105
|
emitIRNode,
|
|
@@ -22355,6 +23112,7 @@ export {
|
|
|
22355
23112
|
createProgramForCorpus,
|
|
22356
23113
|
createError,
|
|
22357
23114
|
containsHigherOrder,
|
|
23115
|
+
computeSsrSeedPlan,
|
|
22358
23116
|
compileJSX,
|
|
22359
23117
|
combineParentChildClientJs,
|
|
22360
23118
|
collectModuleStringConsts,
|
|
@@ -22390,6 +23148,7 @@ export {
|
|
|
22390
23148
|
PROFILE_SCHEMA_VERSION,
|
|
22391
23149
|
JsxAdapter,
|
|
22392
23150
|
ErrorCodes,
|
|
23151
|
+
ENV_SIGNAL_READERS,
|
|
22393
23152
|
CALLBACK_METHODS,
|
|
22394
23153
|
BaseAdapter,
|
|
22395
23154
|
BUILTIN_LOWERING_PLUGINS,
|