@homebound/truss 2.17.0 → 2.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/build/index.js +7 -1
- package/build/index.js.map +1 -1
- package/build/plugin/index.js +513 -396
- package/build/plugin/index.js.map +1 -1
- package/package.json +1 -1
package/build/plugin/index.js
CHANGED
|
@@ -1424,6 +1424,248 @@ import _generate2 from "@babel/generator";
|
|
|
1424
1424
|
import * as t4 from "@babel/types";
|
|
1425
1425
|
import { basename } from "path";
|
|
1426
1426
|
|
|
1427
|
+
// src/plugin/ast-utils.ts
|
|
1428
|
+
import * as t2 from "@babel/types";
|
|
1429
|
+
function collectTopLevelBindings(ast) {
|
|
1430
|
+
const used = /* @__PURE__ */ new Set();
|
|
1431
|
+
for (const node of ast.program.body) {
|
|
1432
|
+
if (t2.isImportDeclaration(node)) {
|
|
1433
|
+
for (const spec of node.specifiers) {
|
|
1434
|
+
used.add(spec.local.name);
|
|
1435
|
+
}
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
if (t2.isVariableDeclaration(node)) {
|
|
1439
|
+
for (const decl of node.declarations) {
|
|
1440
|
+
collectPatternBindings(decl.id, used);
|
|
1441
|
+
}
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
if (t2.isFunctionDeclaration(node) && node.id) {
|
|
1445
|
+
used.add(node.id.name);
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
if (t2.isClassDeclaration(node) && node.id) {
|
|
1449
|
+
used.add(node.id.name);
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
if (t2.isExportNamedDeclaration(node) && node.declaration) {
|
|
1453
|
+
const decl = node.declaration;
|
|
1454
|
+
if (t2.isVariableDeclaration(decl)) {
|
|
1455
|
+
for (const varDecl of decl.declarations) {
|
|
1456
|
+
collectPatternBindings(varDecl.id, used);
|
|
1457
|
+
}
|
|
1458
|
+
} else if ((t2.isFunctionDeclaration(decl) || t2.isClassDeclaration(decl)) && decl.id) {
|
|
1459
|
+
used.add(decl.id.name);
|
|
1460
|
+
}
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
if (t2.isExportDefaultDeclaration(node)) {
|
|
1464
|
+
const decl = node.declaration;
|
|
1465
|
+
if ((t2.isFunctionDeclaration(decl) || t2.isClassDeclaration(decl)) && decl.id) {
|
|
1466
|
+
used.add(decl.id.name);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return used;
|
|
1471
|
+
}
|
|
1472
|
+
function collectPatternBindings(pattern, used) {
|
|
1473
|
+
if (t2.isVoidPattern(pattern)) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (t2.isIdentifier(pattern)) {
|
|
1477
|
+
used.add(pattern.name);
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
if (t2.isAssignmentPattern(pattern)) {
|
|
1481
|
+
collectPatternBindings(pattern.left, used);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
if (t2.isRestElement(pattern)) {
|
|
1485
|
+
collectPatternBindings(pattern.argument, used);
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (t2.isObjectPattern(pattern)) {
|
|
1489
|
+
for (const prop of pattern.properties) {
|
|
1490
|
+
if (t2.isObjectProperty(prop)) {
|
|
1491
|
+
collectPatternBindings(prop.value, used);
|
|
1492
|
+
} else if (t2.isRestElement(prop)) {
|
|
1493
|
+
collectPatternBindings(prop.argument, used);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (t2.isArrayPattern(pattern)) {
|
|
1499
|
+
for (const el of pattern.elements) {
|
|
1500
|
+
if (!el) continue;
|
|
1501
|
+
if (t2.isIdentifier(el) || t2.isAssignmentPattern(el) || t2.isObjectPattern(el) || t2.isArrayPattern(el)) {
|
|
1502
|
+
collectPatternBindings(el, used);
|
|
1503
|
+
} else if (t2.isRestElement(el)) {
|
|
1504
|
+
collectPatternBindings(el.argument, used);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function reservePreferredName(used, preferred, secondary) {
|
|
1510
|
+
if (!used.has(preferred)) {
|
|
1511
|
+
used.add(preferred);
|
|
1512
|
+
return preferred;
|
|
1513
|
+
}
|
|
1514
|
+
if (secondary && !used.has(secondary)) {
|
|
1515
|
+
used.add(secondary);
|
|
1516
|
+
return secondary;
|
|
1517
|
+
}
|
|
1518
|
+
const base = secondary ?? preferred;
|
|
1519
|
+
let i = 1;
|
|
1520
|
+
let candidate = `${base}_${i}`;
|
|
1521
|
+
while (used.has(candidate)) {
|
|
1522
|
+
i++;
|
|
1523
|
+
candidate = `${base}_${i}`;
|
|
1524
|
+
}
|
|
1525
|
+
used.add(candidate);
|
|
1526
|
+
return candidate;
|
|
1527
|
+
}
|
|
1528
|
+
function findCssImportBinding(ast) {
|
|
1529
|
+
for (const node of ast.program.body) {
|
|
1530
|
+
if (!t2.isImportDeclaration(node)) continue;
|
|
1531
|
+
for (const spec of node.specifiers) {
|
|
1532
|
+
if (t2.isImportSpecifier(spec) && t2.isIdentifier(spec.imported, { name: "Css" })) {
|
|
1533
|
+
return spec.local.name;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
function findCssBuilderBinding(ast) {
|
|
1540
|
+
for (const node of ast.program.body) {
|
|
1541
|
+
if (!t2.isVariableDeclaration(node)) continue;
|
|
1542
|
+
for (const decl of node.declarations) {
|
|
1543
|
+
if (t2.isIdentifier(decl.id) && decl.init && t2.isNewExpression(decl.init) && t2.isIdentifier(decl.init.callee, { name: "CssBuilder" })) {
|
|
1544
|
+
return decl.id.name;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return null;
|
|
1549
|
+
}
|
|
1550
|
+
function removeCssImport(ast, cssBinding) {
|
|
1551
|
+
for (let i = 0; i < ast.program.body.length; i++) {
|
|
1552
|
+
const node = ast.program.body[i];
|
|
1553
|
+
if (!t2.isImportDeclaration(node)) continue;
|
|
1554
|
+
const cssSpecIndex = node.specifiers.findIndex((s) => t2.isImportSpecifier(s) && s.local.name === cssBinding);
|
|
1555
|
+
if (cssSpecIndex === -1) continue;
|
|
1556
|
+
if (node.specifiers.length === 1) {
|
|
1557
|
+
ast.program.body.splice(i, 1);
|
|
1558
|
+
} else {
|
|
1559
|
+
node.specifiers.splice(cssSpecIndex, 1);
|
|
1560
|
+
}
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
function findLastImportIndex(ast) {
|
|
1565
|
+
let lastImportIndex = -1;
|
|
1566
|
+
for (let i = 0; i < ast.program.body.length; i++) {
|
|
1567
|
+
if (t2.isImportDeclaration(ast.program.body[i])) {
|
|
1568
|
+
lastImportIndex = i;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return lastImportIndex;
|
|
1572
|
+
}
|
|
1573
|
+
function findNamedImportBinding(ast, source, importedName) {
|
|
1574
|
+
for (const node of ast.program.body) {
|
|
1575
|
+
if (!t2.isImportDeclaration(node) || node.source.value !== source) continue;
|
|
1576
|
+
for (const spec of node.specifiers) {
|
|
1577
|
+
if (t2.isImportSpecifier(spec) && t2.isIdentifier(spec.imported, { name: importedName })) {
|
|
1578
|
+
return spec.local.name;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
function findImportDeclaration(ast, source) {
|
|
1585
|
+
for (const node of ast.program.body) {
|
|
1586
|
+
if (t2.isImportDeclaration(node) && node.source.value === source) {
|
|
1587
|
+
return node;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1592
|
+
function replaceCssImportWithNamedImports(ast, cssBinding, source, imports) {
|
|
1593
|
+
for (const node of ast.program.body) {
|
|
1594
|
+
if (!t2.isImportDeclaration(node)) continue;
|
|
1595
|
+
const cssSpecIndex = node.specifiers.findIndex((spec) => {
|
|
1596
|
+
return t2.isImportSpecifier(spec) && spec.local.name === cssBinding;
|
|
1597
|
+
});
|
|
1598
|
+
if (cssSpecIndex === -1 || node.specifiers.length !== 1) continue;
|
|
1599
|
+
node.source = t2.stringLiteral(source);
|
|
1600
|
+
node.specifiers = imports.map((entry) => {
|
|
1601
|
+
return t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName));
|
|
1602
|
+
});
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1605
|
+
return false;
|
|
1606
|
+
}
|
|
1607
|
+
function upsertNamedImports(ast, source, imports) {
|
|
1608
|
+
if (imports.length === 0) return;
|
|
1609
|
+
for (const node of ast.program.body) {
|
|
1610
|
+
if (!t2.isImportDeclaration(node) || node.source.value !== source) continue;
|
|
1611
|
+
for (const entry of imports) {
|
|
1612
|
+
const exists = node.specifiers.some((spec) => {
|
|
1613
|
+
return t2.isImportSpecifier(spec) && t2.isIdentifier(spec.imported, { name: entry.importedName });
|
|
1614
|
+
});
|
|
1615
|
+
if (exists) continue;
|
|
1616
|
+
node.specifiers.push(t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName)));
|
|
1617
|
+
}
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const importDecl = t2.importDeclaration(
|
|
1621
|
+
imports.map((entry) => {
|
|
1622
|
+
return t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName));
|
|
1623
|
+
}),
|
|
1624
|
+
t2.stringLiteral(source)
|
|
1625
|
+
);
|
|
1626
|
+
const idx = findLastImportIndex(ast);
|
|
1627
|
+
ast.program.body.splice(idx + 1, 0, importDecl);
|
|
1628
|
+
}
|
|
1629
|
+
function extractChain(node, cssBinding) {
|
|
1630
|
+
const chain = [];
|
|
1631
|
+
let current = node;
|
|
1632
|
+
while (true) {
|
|
1633
|
+
if (t2.isIdentifier(current, { name: cssBinding })) {
|
|
1634
|
+
chain.reverse();
|
|
1635
|
+
return chain;
|
|
1636
|
+
}
|
|
1637
|
+
if (t2.isMemberExpression(current) && !current.computed && t2.isIdentifier(current.property)) {
|
|
1638
|
+
const name = current.property.name;
|
|
1639
|
+
if (name === "else") {
|
|
1640
|
+
chain.push({ type: "else" });
|
|
1641
|
+
} else {
|
|
1642
|
+
chain.push({ type: "getter", name });
|
|
1643
|
+
}
|
|
1644
|
+
current = current.object;
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
if (t2.isCallExpression(current) && t2.isMemberExpression(current.callee) && !current.callee.computed && t2.isIdentifier(current.callee.property)) {
|
|
1648
|
+
const name = current.callee.property.name;
|
|
1649
|
+
if (name === "if") {
|
|
1650
|
+
chain.push({
|
|
1651
|
+
type: "if",
|
|
1652
|
+
conditionNode: current.arguments[0]
|
|
1653
|
+
});
|
|
1654
|
+
current = current.callee.object;
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
chain.push({
|
|
1658
|
+
type: "call",
|
|
1659
|
+
name,
|
|
1660
|
+
args: current.arguments
|
|
1661
|
+
});
|
|
1662
|
+
current = current.callee.object;
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
return null;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1427
1669
|
// src/plugin/resolve-chain.ts
|
|
1428
1670
|
function emptyConditionContext() {
|
|
1429
1671
|
return {
|
|
@@ -1497,9 +1739,10 @@ function applyModifierNodeToConditionContext(context, node, mapping) {
|
|
|
1497
1739
|
context.pseudoClass = pseudoSelector(node.name);
|
|
1498
1740
|
}
|
|
1499
1741
|
}
|
|
1500
|
-
function resolveFullChain(chain, mapping) {
|
|
1742
|
+
function resolveFullChain(chain, mapping, cssBindingName, initialContext = emptyConditionContext()) {
|
|
1501
1743
|
const parts = [];
|
|
1502
1744
|
const markers = [];
|
|
1745
|
+
const nestedErrors = [];
|
|
1503
1746
|
const filteredChain = [];
|
|
1504
1747
|
const scanErrors = [];
|
|
1505
1748
|
for (let j = 0; j < chain.length; j++) {
|
|
@@ -1518,15 +1761,15 @@ function resolveFullChain(chain, mapping) {
|
|
|
1518
1761
|
}
|
|
1519
1762
|
let i = 0;
|
|
1520
1763
|
let currentNodes = [];
|
|
1521
|
-
let currentContext =
|
|
1522
|
-
let currentNodesStartContext =
|
|
1764
|
+
let currentContext = cloneConditionContext(initialContext);
|
|
1765
|
+
let currentNodesStartContext = cloneConditionContext(initialContext);
|
|
1523
1766
|
function flushCurrentNodes() {
|
|
1524
1767
|
if (currentNodes.length === 0) {
|
|
1525
1768
|
return;
|
|
1526
1769
|
}
|
|
1527
1770
|
parts.push({
|
|
1528
1771
|
type: "unconditional",
|
|
1529
|
-
segments: resolveChain(currentNodes, mapping, currentNodesStartContext)
|
|
1772
|
+
segments: resolveChain(currentNodes, mapping, currentNodesStartContext, cssBindingName)
|
|
1530
1773
|
});
|
|
1531
1774
|
currentNodes = [];
|
|
1532
1775
|
currentNodesStartContext = cloneConditionContext(currentContext);
|
|
@@ -1548,13 +1791,22 @@ function resolveFullChain(chain, mapping) {
|
|
|
1548
1791
|
const branchContext = cloneConditionContext(currentContext);
|
|
1549
1792
|
const thenNodes = mediaStart.thenNodes ? [...mediaStart.thenNodes, ...filteredChain.slice(i + 1, elseIndex)] : filteredChain.slice(i, elseIndex);
|
|
1550
1793
|
const elseNodes = [makeMediaQueryNode(mediaStart.inverseMediaQuery), ...filteredChain.slice(elseIndex + 1)];
|
|
1551
|
-
const thenSegs = resolveChain(thenNodes, mapping, branchContext);
|
|
1552
|
-
const elseSegs = resolveChain(elseNodes, mapping, branchContext);
|
|
1794
|
+
const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName);
|
|
1795
|
+
const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName);
|
|
1553
1796
|
parts.push({ type: "unconditional", segments: [...thenSegs, ...elseSegs] });
|
|
1554
1797
|
i = filteredChain.length;
|
|
1555
1798
|
break;
|
|
1556
1799
|
}
|
|
1557
1800
|
}
|
|
1801
|
+
if (isWhenObjectCall(node)) {
|
|
1802
|
+
flushCurrentNodes();
|
|
1803
|
+
const resolved = resolveWhenObjectSelectors(node, mapping, cssBindingName, currentContext);
|
|
1804
|
+
parts.push(...resolved.parts);
|
|
1805
|
+
markers.push(...resolved.markers);
|
|
1806
|
+
nestedErrors.push(...resolved.errors);
|
|
1807
|
+
i++;
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1558
1810
|
if (node.type === "if") {
|
|
1559
1811
|
if (node.conditionNode.type === "StringLiteral") {
|
|
1560
1812
|
const mediaQuery = node.conditionNode.value;
|
|
@@ -1584,8 +1836,8 @@ function resolveFullChain(chain, mapping) {
|
|
|
1584
1836
|
}
|
|
1585
1837
|
i++;
|
|
1586
1838
|
}
|
|
1587
|
-
const thenSegs = resolveChain(thenNodes, mapping, branchContext);
|
|
1588
|
-
const elseSegs = resolveChain(elseNodes, mapping, branchContext);
|
|
1839
|
+
const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName);
|
|
1840
|
+
const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName);
|
|
1589
1841
|
parts.push({
|
|
1590
1842
|
type: "conditional",
|
|
1591
1843
|
conditionNode: node.conditionNode,
|
|
@@ -1607,21 +1859,87 @@ function resolveFullChain(chain, mapping) {
|
|
|
1607
1859
|
}
|
|
1608
1860
|
}
|
|
1609
1861
|
}
|
|
1610
|
-
return { parts, markers, errors: [...scanErrors, ...segmentErrors] };
|
|
1862
|
+
return { parts, markers, errors: [.../* @__PURE__ */ new Set([...scanErrors, ...nestedErrors, ...segmentErrors])] };
|
|
1611
1863
|
}
|
|
1612
|
-
function
|
|
1613
|
-
|
|
1864
|
+
function isWhenObjectCall(node) {
|
|
1865
|
+
return node.type === "call" && node.name === "when" && node.args.length === 1 && node.args[0].type === "ObjectExpression";
|
|
1866
|
+
}
|
|
1867
|
+
function resolveWhenObjectSelectors(node, mapping, cssBindingName, initialContext) {
|
|
1868
|
+
if (!cssBindingName) {
|
|
1614
1869
|
return {
|
|
1615
|
-
|
|
1616
|
-
|
|
1870
|
+
parts: [],
|
|
1871
|
+
markers: [],
|
|
1872
|
+
errors: [new UnsupportedPatternError(`when({ ... }) requires a resolvable Css binding`).message]
|
|
1617
1873
|
};
|
|
1618
1874
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1875
|
+
const objectArg = node.args[0];
|
|
1876
|
+
if (objectArg.type !== "ObjectExpression") {
|
|
1877
|
+
throw new UnsupportedPatternError(`when({ ... }) requires an object literal argument`);
|
|
1621
1878
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1879
|
+
const parts = [];
|
|
1880
|
+
const markers = [];
|
|
1881
|
+
const errors = [];
|
|
1882
|
+
for (const property of objectArg.properties) {
|
|
1883
|
+
try {
|
|
1884
|
+
if (property.type === "SpreadElement") {
|
|
1885
|
+
throw new UnsupportedPatternError(`when({ ... }) does not support spread properties`);
|
|
1886
|
+
}
|
|
1887
|
+
if (property.type !== "ObjectProperty") {
|
|
1888
|
+
throw new UnsupportedPatternError(`when({ ... }) only supports plain object properties`);
|
|
1889
|
+
}
|
|
1890
|
+
if (property.computed || property.key.type !== "StringLiteral") {
|
|
1891
|
+
throw new UnsupportedPatternError(`when({ ... }) selector keys must be string literals`);
|
|
1892
|
+
}
|
|
1893
|
+
const value = unwrapExpression(property.value);
|
|
1894
|
+
if (value.type !== "MemberExpression" || value.computed || value.property.type !== "Identifier" || value.property.name !== "$") {
|
|
1895
|
+
throw new UnsupportedPatternError(`when({ ... }) values must be Css.*.$ expressions`);
|
|
1896
|
+
}
|
|
1897
|
+
const innerChain = extractChain(value.object, cssBindingName);
|
|
1898
|
+
if (!innerChain) {
|
|
1899
|
+
throw new UnsupportedPatternError(`when({ ... }) values must be Css.*.$ expressions`);
|
|
1900
|
+
}
|
|
1901
|
+
const selectorContext = cloneConditionContext(initialContext);
|
|
1902
|
+
selectorContext.pseudoClass = property.key.value;
|
|
1903
|
+
const resolved = resolveFullChain(innerChain, mapping, cssBindingName, selectorContext);
|
|
1904
|
+
parts.push(...resolved.parts);
|
|
1905
|
+
markers.push(...resolved.markers);
|
|
1906
|
+
errors.push(...resolved.errors);
|
|
1907
|
+
} catch (err) {
|
|
1908
|
+
if (err instanceof UnsupportedPatternError) {
|
|
1909
|
+
errors.push(err.message);
|
|
1910
|
+
} else {
|
|
1911
|
+
throw err;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return { parts, markers, errors: [...new Set(errors)] };
|
|
1916
|
+
}
|
|
1917
|
+
function flattenWhenObjectParts(resolved) {
|
|
1918
|
+
const segments = [];
|
|
1919
|
+
for (const part of resolved.parts) {
|
|
1920
|
+
if (part.type !== "unconditional") {
|
|
1921
|
+
throw new UnsupportedPatternError(`when({ ... }) values cannot use if()/else in this context`);
|
|
1922
|
+
}
|
|
1923
|
+
segments.push(...part.segments);
|
|
1924
|
+
}
|
|
1925
|
+
for (const err of resolved.errors) {
|
|
1926
|
+
segments.push({ abbr: "__error", defs: {}, error: err });
|
|
1927
|
+
}
|
|
1928
|
+
return segments;
|
|
1929
|
+
}
|
|
1930
|
+
function getMediaConditionalStartNode(node, mapping) {
|
|
1931
|
+
if (node.type === "if" && node.conditionNode.type === "StringLiteral") {
|
|
1932
|
+
return {
|
|
1933
|
+
inverseMediaQuery: invertMediaQuery(node.conditionNode.value),
|
|
1934
|
+
thenNodes: [makeMediaQueryNode(node.conditionNode.value)]
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
if (node.type === "getter" && mapping.breakpoints && node.name in mapping.breakpoints) {
|
|
1938
|
+
return { inverseMediaQuery: invertMediaQuery(mapping.breakpoints[node.name]) };
|
|
1939
|
+
}
|
|
1940
|
+
return null;
|
|
1941
|
+
}
|
|
1942
|
+
function findElseIndex(chain, start) {
|
|
1625
1943
|
for (let i = start; i < chain.length; i++) {
|
|
1626
1944
|
if (chain[i].type === "if") {
|
|
1627
1945
|
return -1;
|
|
@@ -1656,7 +1974,7 @@ function invertMediaQuery(query) {
|
|
|
1656
1974
|
}
|
|
1657
1975
|
return query.replace("@media", "@media not");
|
|
1658
1976
|
}
|
|
1659
|
-
function resolveChain(chain, mapping, initialContext = emptyConditionContext()) {
|
|
1977
|
+
function resolveChain(chain, mapping, initialContext = emptyConditionContext(), cssBindingName) {
|
|
1660
1978
|
const segments = [];
|
|
1661
1979
|
const context = cloneConditionContext(initialContext);
|
|
1662
1980
|
for (const node of chain) {
|
|
@@ -1751,6 +2069,11 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext())
|
|
|
1751
2069
|
continue;
|
|
1752
2070
|
}
|
|
1753
2071
|
if (abbr === "when") {
|
|
2072
|
+
if (isWhenObjectCall(node)) {
|
|
2073
|
+
const resolved2 = resolveWhenObjectSelectors(node, mapping, cssBindingName, context);
|
|
2074
|
+
segments.push(...flattenWhenObjectParts(resolved2));
|
|
2075
|
+
continue;
|
|
2076
|
+
}
|
|
1754
2077
|
const resolved = resolveWhenCall(node);
|
|
1755
2078
|
if (resolved.kind === "selector") {
|
|
1756
2079
|
context.pseudoClass = resolved.pseudo;
|
|
@@ -2120,399 +2443,167 @@ function resolveWhenCall(node) {
|
|
|
2120
2443
|
if (relationshipArg.type !== "StringLiteral") {
|
|
2121
2444
|
throw new UnsupportedPatternError(`when() relationship argument must be a string literal`);
|
|
2122
2445
|
}
|
|
2123
|
-
const relationship = relationshipArg.value;
|
|
2124
|
-
if (!WHEN_RELATIONSHIPS.has(relationship)) {
|
|
2125
|
-
throw new UnsupportedPatternError(
|
|
2126
|
-
`when() relationship must be one of: ${[...WHEN_RELATIONSHIPS].join(", ")} -- got "${relationship}"`
|
|
2127
|
-
);
|
|
2128
|
-
}
|
|
2129
|
-
const pseudoArg = node.args[2];
|
|
2130
|
-
if (pseudoArg.type !== "StringLiteral") {
|
|
2131
|
-
throw new UnsupportedPatternError(`when() pseudo selector (3rd argument) must be a string literal`);
|
|
2132
|
-
}
|
|
2133
|
-
return { kind: "relationship", pseudo: pseudoArg.value, markerNode, relationship };
|
|
2134
|
-
}
|
|
2135
|
-
function resolveWhenMarker(node) {
|
|
2136
|
-
if (isDefaultMarkerNode(node)) {
|
|
2137
|
-
return void 0;
|
|
2138
|
-
}
|
|
2139
|
-
if (node.type === "Identifier") {
|
|
2140
|
-
return node;
|
|
2141
|
-
}
|
|
2142
|
-
throw new UnsupportedPatternError(`when() marker must be a marker variable or marker`);
|
|
2143
|
-
}
|
|
2144
|
-
function isDefaultMarkerNode(node) {
|
|
2145
|
-
if (node.type === "Identifier" && (node.name === "marker" || node.name === "defaultMarker")) {
|
|
2146
|
-
return true;
|
|
2147
|
-
}
|
|
2148
|
-
return isLegacyDefaultMarkerExpression(node);
|
|
2149
|
-
}
|
|
2150
|
-
function isLegacyDefaultMarkerExpression(node) {
|
|
2151
|
-
return node.type === "CallExpression" && node.arguments.length === 0 && node.callee.type === "MemberExpression" && !node.callee.computed && node.callee.property.type === "Identifier" && node.callee.property.name === "defaultMarker";
|
|
2152
|
-
}
|
|
2153
|
-
var PSEUDO_METHODS = {
|
|
2154
|
-
onHover: ":hover",
|
|
2155
|
-
onFocus: ":focus",
|
|
2156
|
-
onFocusVisible: ":focus-visible",
|
|
2157
|
-
onFocusWithin: ":focus-within",
|
|
2158
|
-
onActive: ":active",
|
|
2159
|
-
onDisabled: ":disabled",
|
|
2160
|
-
ifFirstOfType: ":first-of-type",
|
|
2161
|
-
ifLastOfType: ":last-of-type"
|
|
2162
|
-
};
|
|
2163
|
-
function isPseudoMethod(name) {
|
|
2164
|
-
return name in PSEUDO_METHODS;
|
|
2165
|
-
}
|
|
2166
|
-
function pseudoSelector(name) {
|
|
2167
|
-
return PSEUDO_METHODS[name];
|
|
2168
|
-
}
|
|
2169
|
-
function tryEvaluateLiteral(node, incremented, increment) {
|
|
2170
|
-
if (node.type === "NumericLiteral") {
|
|
2171
|
-
if (incremented) {
|
|
2172
|
-
return `${node.value * increment}px`;
|
|
2173
|
-
}
|
|
2174
|
-
return String(node.value);
|
|
2175
|
-
}
|
|
2176
|
-
if (node.type === "StringLiteral") {
|
|
2177
|
-
return node.value;
|
|
2178
|
-
}
|
|
2179
|
-
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2180
|
-
const val = -node.argument.value;
|
|
2181
|
-
if (incremented) {
|
|
2182
|
-
return `${val * increment}px`;
|
|
2183
|
-
}
|
|
2184
|
-
return String(val);
|
|
2185
|
-
}
|
|
2186
|
-
return null;
|
|
2187
|
-
}
|
|
2188
|
-
function tryEvaluatePxLiteral(node) {
|
|
2189
|
-
if (node.type === "NumericLiteral") {
|
|
2190
|
-
return `${node.value}px`;
|
|
2191
|
-
}
|
|
2192
|
-
return null;
|
|
2193
|
-
}
|
|
2194
|
-
function containerSelectorFromCall(node) {
|
|
2195
|
-
if (node.args.length !== 1) {
|
|
2196
|
-
throw new UnsupportedPatternError(`ifContainer() expects exactly 1 argument, got ${node.args.length}`);
|
|
2197
|
-
}
|
|
2198
|
-
const arg = node.args[0];
|
|
2199
|
-
if (!arg || arg.type !== "ObjectExpression") {
|
|
2200
|
-
throw new UnsupportedPatternError("ifContainer() expects an object literal argument");
|
|
2201
|
-
}
|
|
2202
|
-
let lt;
|
|
2203
|
-
let gt;
|
|
2204
|
-
let name;
|
|
2205
|
-
for (const prop of arg.properties) {
|
|
2206
|
-
if (prop.type === "SpreadElement") {
|
|
2207
|
-
throw new UnsupportedPatternError("ifContainer() does not support spread properties");
|
|
2208
|
-
}
|
|
2209
|
-
if (prop.type !== "ObjectProperty" || prop.computed) {
|
|
2210
|
-
throw new UnsupportedPatternError("ifContainer() expects plain object properties");
|
|
2211
|
-
}
|
|
2212
|
-
const key = objectPropertyName(prop.key);
|
|
2213
|
-
if (!key) {
|
|
2214
|
-
throw new UnsupportedPatternError("ifContainer() only supports identifier/string keys");
|
|
2215
|
-
}
|
|
2216
|
-
const valueNode = prop.value;
|
|
2217
|
-
if (key === "lt") {
|
|
2218
|
-
lt = numericLiteralValue(valueNode, "ifContainer().lt must be a numeric literal");
|
|
2219
|
-
continue;
|
|
2220
|
-
}
|
|
2221
|
-
if (key === "gt") {
|
|
2222
|
-
gt = numericLiteralValue(valueNode, "ifContainer().gt must be a numeric literal");
|
|
2223
|
-
continue;
|
|
2224
|
-
}
|
|
2225
|
-
if (key === "name") {
|
|
2226
|
-
name = stringLiteralValue(valueNode, "ifContainer().name must be a string literal");
|
|
2227
|
-
continue;
|
|
2228
|
-
}
|
|
2229
|
-
throw new UnsupportedPatternError(`ifContainer() does not support property "${key}"`);
|
|
2230
|
-
}
|
|
2231
|
-
if (lt === void 0 && gt === void 0) {
|
|
2232
|
-
throw new UnsupportedPatternError('ifContainer() requires at least one of "lt" or "gt"');
|
|
2233
|
-
}
|
|
2234
|
-
const parts = [];
|
|
2235
|
-
if (gt !== void 0) {
|
|
2236
|
-
parts.push(`(min-width: ${gt + 1}px)`);
|
|
2237
|
-
}
|
|
2238
|
-
if (lt !== void 0) {
|
|
2239
|
-
parts.push(`(max-width: ${lt}px)`);
|
|
2240
|
-
}
|
|
2241
|
-
const query = parts.join(" and ");
|
|
2242
|
-
const namePrefix = name ? `${name} ` : "";
|
|
2243
|
-
return `@container ${namePrefix}${query}`;
|
|
2244
|
-
}
|
|
2245
|
-
function objectPropertyName(node) {
|
|
2246
|
-
if (node.type === "Identifier") return node.name;
|
|
2247
|
-
if (node.type === "StringLiteral") return node.value;
|
|
2248
|
-
return null;
|
|
2249
|
-
}
|
|
2250
|
-
function numericLiteralValue(node, errorMessage) {
|
|
2251
|
-
if (node.type === "NumericLiteral") {
|
|
2252
|
-
return node.value;
|
|
2253
|
-
}
|
|
2254
|
-
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2255
|
-
return -node.argument.value;
|
|
2256
|
-
}
|
|
2257
|
-
throw new UnsupportedPatternError(errorMessage);
|
|
2258
|
-
}
|
|
2259
|
-
function stringLiteralValue(node, errorMessage) {
|
|
2260
|
-
if (node.type === "StringLiteral") {
|
|
2261
|
-
return node.value;
|
|
2262
|
-
}
|
|
2263
|
-
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
2264
|
-
return node.quasis[0].value.cooked ?? "";
|
|
2265
|
-
}
|
|
2266
|
-
throw new UnsupportedPatternError(errorMessage);
|
|
2267
|
-
}
|
|
2268
|
-
var UnsupportedPatternError = class extends Error {
|
|
2269
|
-
constructor(message) {
|
|
2270
|
-
super(`[truss] Unsupported pattern: ${message}`);
|
|
2271
|
-
this.name = "UnsupportedPatternError";
|
|
2272
|
-
}
|
|
2273
|
-
};
|
|
2274
|
-
|
|
2275
|
-
// src/plugin/ast-utils.ts
|
|
2276
|
-
import * as t2 from "@babel/types";
|
|
2277
|
-
function collectTopLevelBindings(ast) {
|
|
2278
|
-
const used = /* @__PURE__ */ new Set();
|
|
2279
|
-
for (const node of ast.program.body) {
|
|
2280
|
-
if (t2.isImportDeclaration(node)) {
|
|
2281
|
-
for (const spec of node.specifiers) {
|
|
2282
|
-
used.add(spec.local.name);
|
|
2283
|
-
}
|
|
2284
|
-
continue;
|
|
2285
|
-
}
|
|
2286
|
-
if (t2.isVariableDeclaration(node)) {
|
|
2287
|
-
for (const decl of node.declarations) {
|
|
2288
|
-
collectPatternBindings(decl.id, used);
|
|
2289
|
-
}
|
|
2290
|
-
continue;
|
|
2291
|
-
}
|
|
2292
|
-
if (t2.isFunctionDeclaration(node) && node.id) {
|
|
2293
|
-
used.add(node.id.name);
|
|
2294
|
-
continue;
|
|
2295
|
-
}
|
|
2296
|
-
if (t2.isClassDeclaration(node) && node.id) {
|
|
2297
|
-
used.add(node.id.name);
|
|
2298
|
-
continue;
|
|
2299
|
-
}
|
|
2300
|
-
if (t2.isExportNamedDeclaration(node) && node.declaration) {
|
|
2301
|
-
const decl = node.declaration;
|
|
2302
|
-
if (t2.isVariableDeclaration(decl)) {
|
|
2303
|
-
for (const varDecl of decl.declarations) {
|
|
2304
|
-
collectPatternBindings(varDecl.id, used);
|
|
2305
|
-
}
|
|
2306
|
-
} else if ((t2.isFunctionDeclaration(decl) || t2.isClassDeclaration(decl)) && decl.id) {
|
|
2307
|
-
used.add(decl.id.name);
|
|
2308
|
-
}
|
|
2309
|
-
continue;
|
|
2310
|
-
}
|
|
2311
|
-
if (t2.isExportDefaultDeclaration(node)) {
|
|
2312
|
-
const decl = node.declaration;
|
|
2313
|
-
if ((t2.isFunctionDeclaration(decl) || t2.isClassDeclaration(decl)) && decl.id) {
|
|
2314
|
-
used.add(decl.id.name);
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
return used;
|
|
2319
|
-
}
|
|
2320
|
-
function collectPatternBindings(pattern, used) {
|
|
2321
|
-
if (t2.isVoidPattern(pattern)) {
|
|
2322
|
-
return;
|
|
2323
|
-
}
|
|
2324
|
-
if (t2.isIdentifier(pattern)) {
|
|
2325
|
-
used.add(pattern.name);
|
|
2326
|
-
return;
|
|
2327
|
-
}
|
|
2328
|
-
if (t2.isAssignmentPattern(pattern)) {
|
|
2329
|
-
collectPatternBindings(pattern.left, used);
|
|
2330
|
-
return;
|
|
2331
|
-
}
|
|
2332
|
-
if (t2.isRestElement(pattern)) {
|
|
2333
|
-
collectPatternBindings(pattern.argument, used);
|
|
2334
|
-
return;
|
|
2335
|
-
}
|
|
2336
|
-
if (t2.isObjectPattern(pattern)) {
|
|
2337
|
-
for (const prop of pattern.properties) {
|
|
2338
|
-
if (t2.isObjectProperty(prop)) {
|
|
2339
|
-
collectPatternBindings(prop.value, used);
|
|
2340
|
-
} else if (t2.isRestElement(prop)) {
|
|
2341
|
-
collectPatternBindings(prop.argument, used);
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
return;
|
|
2345
|
-
}
|
|
2346
|
-
if (t2.isArrayPattern(pattern)) {
|
|
2347
|
-
for (const el of pattern.elements) {
|
|
2348
|
-
if (!el) continue;
|
|
2349
|
-
if (t2.isIdentifier(el) || t2.isAssignmentPattern(el) || t2.isObjectPattern(el) || t2.isArrayPattern(el)) {
|
|
2350
|
-
collectPatternBindings(el, used);
|
|
2351
|
-
} else if (t2.isRestElement(el)) {
|
|
2352
|
-
collectPatternBindings(el.argument, used);
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
function reservePreferredName(used, preferred, secondary) {
|
|
2358
|
-
if (!used.has(preferred)) {
|
|
2359
|
-
used.add(preferred);
|
|
2360
|
-
return preferred;
|
|
2361
|
-
}
|
|
2362
|
-
if (secondary && !used.has(secondary)) {
|
|
2363
|
-
used.add(secondary);
|
|
2364
|
-
return secondary;
|
|
2365
|
-
}
|
|
2366
|
-
const base = secondary ?? preferred;
|
|
2367
|
-
let i = 1;
|
|
2368
|
-
let candidate = `${base}_${i}`;
|
|
2369
|
-
while (used.has(candidate)) {
|
|
2370
|
-
i++;
|
|
2371
|
-
candidate = `${base}_${i}`;
|
|
2372
|
-
}
|
|
2373
|
-
used.add(candidate);
|
|
2374
|
-
return candidate;
|
|
2375
|
-
}
|
|
2376
|
-
function findCssImportBinding(ast) {
|
|
2377
|
-
for (const node of ast.program.body) {
|
|
2378
|
-
if (!t2.isImportDeclaration(node)) continue;
|
|
2379
|
-
for (const spec of node.specifiers) {
|
|
2380
|
-
if (t2.isImportSpecifier(spec) && t2.isIdentifier(spec.imported, { name: "Css" })) {
|
|
2381
|
-
return spec.local.name;
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2446
|
+
const relationship = relationshipArg.value;
|
|
2447
|
+
if (!WHEN_RELATIONSHIPS.has(relationship)) {
|
|
2448
|
+
throw new UnsupportedPatternError(
|
|
2449
|
+
`when() relationship must be one of: ${[...WHEN_RELATIONSHIPS].join(", ")} -- got "${relationship}"`
|
|
2450
|
+
);
|
|
2384
2451
|
}
|
|
2385
|
-
|
|
2452
|
+
const pseudoArg = node.args[2];
|
|
2453
|
+
if (pseudoArg.type !== "StringLiteral") {
|
|
2454
|
+
throw new UnsupportedPatternError(`when() pseudo selector (3rd argument) must be a string literal`);
|
|
2455
|
+
}
|
|
2456
|
+
return { kind: "relationship", pseudo: pseudoArg.value, markerNode, relationship };
|
|
2386
2457
|
}
|
|
2387
|
-
function
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
for (const decl of node.declarations) {
|
|
2391
|
-
if (t2.isIdentifier(decl.id) && decl.init && t2.isNewExpression(decl.init) && t2.isIdentifier(decl.init.callee, { name: "CssBuilder" })) {
|
|
2392
|
-
return decl.id.name;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2458
|
+
function resolveWhenMarker(node) {
|
|
2459
|
+
if (isDefaultMarkerNode(node)) {
|
|
2460
|
+
return void 0;
|
|
2395
2461
|
}
|
|
2396
|
-
|
|
2462
|
+
if (node.type === "Identifier") {
|
|
2463
|
+
return node;
|
|
2464
|
+
}
|
|
2465
|
+
throw new UnsupportedPatternError(`when() marker must be a marker variable or marker`);
|
|
2397
2466
|
}
|
|
2398
|
-
function
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
if (!t2.isImportDeclaration(node)) continue;
|
|
2402
|
-
const cssSpecIndex = node.specifiers.findIndex((s) => t2.isImportSpecifier(s) && s.local.name === cssBinding);
|
|
2403
|
-
if (cssSpecIndex === -1) continue;
|
|
2404
|
-
if (node.specifiers.length === 1) {
|
|
2405
|
-
ast.program.body.splice(i, 1);
|
|
2406
|
-
} else {
|
|
2407
|
-
node.specifiers.splice(cssSpecIndex, 1);
|
|
2408
|
-
}
|
|
2409
|
-
return;
|
|
2467
|
+
function isDefaultMarkerNode(node) {
|
|
2468
|
+
if (node.type === "Identifier" && (node.name === "marker" || node.name === "defaultMarker")) {
|
|
2469
|
+
return true;
|
|
2410
2470
|
}
|
|
2471
|
+
return isLegacyDefaultMarkerExpression(node);
|
|
2411
2472
|
}
|
|
2412
|
-
function
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2473
|
+
function isLegacyDefaultMarkerExpression(node) {
|
|
2474
|
+
return node.type === "CallExpression" && node.arguments.length === 0 && node.callee.type === "MemberExpression" && !node.callee.computed && node.callee.property.type === "Identifier" && node.callee.property.name === "defaultMarker";
|
|
2475
|
+
}
|
|
2476
|
+
var PSEUDO_METHODS = {
|
|
2477
|
+
onHover: ":hover",
|
|
2478
|
+
onFocus: ":focus",
|
|
2479
|
+
onFocusVisible: ":focus-visible",
|
|
2480
|
+
onFocusWithin: ":focus-within",
|
|
2481
|
+
onActive: ":active",
|
|
2482
|
+
onDisabled: ":disabled",
|
|
2483
|
+
ifFirstOfType: ":first-of-type",
|
|
2484
|
+
ifLastOfType: ":last-of-type"
|
|
2485
|
+
};
|
|
2486
|
+
function isPseudoMethod(name) {
|
|
2487
|
+
return name in PSEUDO_METHODS;
|
|
2488
|
+
}
|
|
2489
|
+
function pseudoSelector(name) {
|
|
2490
|
+
return PSEUDO_METHODS[name];
|
|
2491
|
+
}
|
|
2492
|
+
function tryEvaluateLiteral(node, incremented, increment) {
|
|
2493
|
+
if (node.type === "NumericLiteral") {
|
|
2494
|
+
if (incremented) {
|
|
2495
|
+
return `${node.value * increment}px`;
|
|
2417
2496
|
}
|
|
2497
|
+
return String(node.value);
|
|
2418
2498
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
return spec.local.name;
|
|
2427
|
-
}
|
|
2499
|
+
if (node.type === "StringLiteral") {
|
|
2500
|
+
return node.value;
|
|
2501
|
+
}
|
|
2502
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2503
|
+
const val = -node.argument.value;
|
|
2504
|
+
if (incremented) {
|
|
2505
|
+
return `${val * increment}px`;
|
|
2428
2506
|
}
|
|
2507
|
+
return String(val);
|
|
2429
2508
|
}
|
|
2430
2509
|
return null;
|
|
2431
2510
|
}
|
|
2432
|
-
function
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
return node;
|
|
2436
|
-
}
|
|
2511
|
+
function tryEvaluatePxLiteral(node) {
|
|
2512
|
+
if (node.type === "NumericLiteral") {
|
|
2513
|
+
return `${node.value}px`;
|
|
2437
2514
|
}
|
|
2438
2515
|
return null;
|
|
2439
2516
|
}
|
|
2440
|
-
function
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
const cssSpecIndex = node.specifiers.findIndex((spec) => {
|
|
2444
|
-
return t2.isImportSpecifier(spec) && spec.local.name === cssBinding;
|
|
2445
|
-
});
|
|
2446
|
-
if (cssSpecIndex === -1 || node.specifiers.length !== 1) continue;
|
|
2447
|
-
node.source = t2.stringLiteral(source);
|
|
2448
|
-
node.specifiers = imports.map((entry) => {
|
|
2449
|
-
return t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName));
|
|
2450
|
-
});
|
|
2451
|
-
return true;
|
|
2517
|
+
function containerSelectorFromCall(node) {
|
|
2518
|
+
if (node.args.length !== 1) {
|
|
2519
|
+
throw new UnsupportedPatternError(`ifContainer() expects exactly 1 argument, got ${node.args.length}`);
|
|
2452
2520
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
if (exists) continue;
|
|
2464
|
-
node.specifiers.push(t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName)));
|
|
2521
|
+
const arg = node.args[0];
|
|
2522
|
+
if (!arg || arg.type !== "ObjectExpression") {
|
|
2523
|
+
throw new UnsupportedPatternError("ifContainer() expects an object literal argument");
|
|
2524
|
+
}
|
|
2525
|
+
let lt;
|
|
2526
|
+
let gt;
|
|
2527
|
+
let name;
|
|
2528
|
+
for (const prop of arg.properties) {
|
|
2529
|
+
if (prop.type === "SpreadElement") {
|
|
2530
|
+
throw new UnsupportedPatternError("ifContainer() does not support spread properties");
|
|
2465
2531
|
}
|
|
2466
|
-
|
|
2532
|
+
if (prop.type !== "ObjectProperty" || prop.computed) {
|
|
2533
|
+
throw new UnsupportedPatternError("ifContainer() expects plain object properties");
|
|
2534
|
+
}
|
|
2535
|
+
const key = objectPropertyName(prop.key);
|
|
2536
|
+
if (!key) {
|
|
2537
|
+
throw new UnsupportedPatternError("ifContainer() only supports identifier/string keys");
|
|
2538
|
+
}
|
|
2539
|
+
const valueNode = prop.value;
|
|
2540
|
+
if (key === "lt") {
|
|
2541
|
+
lt = numericLiteralValue(valueNode, "ifContainer().lt must be a numeric literal");
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2544
|
+
if (key === "gt") {
|
|
2545
|
+
gt = numericLiteralValue(valueNode, "ifContainer().gt must be a numeric literal");
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
if (key === "name") {
|
|
2549
|
+
name = stringLiteralValue(valueNode, "ifContainer().name must be a string literal");
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
2552
|
+
throw new UnsupportedPatternError(`ifContainer() does not support property "${key}"`);
|
|
2467
2553
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2554
|
+
if (lt === void 0 && gt === void 0) {
|
|
2555
|
+
throw new UnsupportedPatternError('ifContainer() requires at least one of "lt" or "gt"');
|
|
2556
|
+
}
|
|
2557
|
+
const parts = [];
|
|
2558
|
+
if (gt !== void 0) {
|
|
2559
|
+
parts.push(`(min-width: ${gt + 1}px)`);
|
|
2560
|
+
}
|
|
2561
|
+
if (lt !== void 0) {
|
|
2562
|
+
parts.push(`(max-width: ${lt}px)`);
|
|
2563
|
+
}
|
|
2564
|
+
const query = parts.join(" and ");
|
|
2565
|
+
const namePrefix = name ? `${name} ` : "";
|
|
2566
|
+
return `@container ${namePrefix}${query}`;
|
|
2476
2567
|
}
|
|
2477
|
-
function
|
|
2478
|
-
|
|
2568
|
+
function objectPropertyName(node) {
|
|
2569
|
+
if (node.type === "Identifier") return node.name;
|
|
2570
|
+
if (node.type === "StringLiteral") return node.value;
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
function unwrapExpression(node) {
|
|
2479
2574
|
let current = node;
|
|
2480
2575
|
while (true) {
|
|
2481
|
-
if (
|
|
2482
|
-
|
|
2483
|
-
return chain;
|
|
2484
|
-
}
|
|
2485
|
-
if (t2.isMemberExpression(current) && !current.computed && t2.isIdentifier(current.property)) {
|
|
2486
|
-
const name = current.property.name;
|
|
2487
|
-
if (name === "else") {
|
|
2488
|
-
chain.push({ type: "else" });
|
|
2489
|
-
} else {
|
|
2490
|
-
chain.push({ type: "getter", name });
|
|
2491
|
-
}
|
|
2492
|
-
current = current.object;
|
|
2493
|
-
continue;
|
|
2494
|
-
}
|
|
2495
|
-
if (t2.isCallExpression(current) && t2.isMemberExpression(current.callee) && !current.callee.computed && t2.isIdentifier(current.callee.property)) {
|
|
2496
|
-
const name = current.callee.property.name;
|
|
2497
|
-
if (name === "if") {
|
|
2498
|
-
chain.push({
|
|
2499
|
-
type: "if",
|
|
2500
|
-
conditionNode: current.arguments[0]
|
|
2501
|
-
});
|
|
2502
|
-
current = current.callee.object;
|
|
2503
|
-
continue;
|
|
2504
|
-
}
|
|
2505
|
-
chain.push({
|
|
2506
|
-
type: "call",
|
|
2507
|
-
name,
|
|
2508
|
-
args: current.arguments
|
|
2509
|
-
});
|
|
2510
|
-
current = current.callee.object;
|
|
2576
|
+
if (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression") {
|
|
2577
|
+
current = current.expression;
|
|
2511
2578
|
continue;
|
|
2512
2579
|
}
|
|
2513
|
-
return
|
|
2580
|
+
return current;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function numericLiteralValue(node, errorMessage) {
|
|
2584
|
+
if (node.type === "NumericLiteral") {
|
|
2585
|
+
return node.value;
|
|
2586
|
+
}
|
|
2587
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2588
|
+
return -node.argument.value;
|
|
2589
|
+
}
|
|
2590
|
+
throw new UnsupportedPatternError(errorMessage);
|
|
2591
|
+
}
|
|
2592
|
+
function stringLiteralValue(node, errorMessage) {
|
|
2593
|
+
if (node.type === "StringLiteral") {
|
|
2594
|
+
return node.value;
|
|
2595
|
+
}
|
|
2596
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
2597
|
+
return node.quasis[0].value.cooked ?? "";
|
|
2514
2598
|
}
|
|
2599
|
+
throw new UnsupportedPatternError(errorMessage);
|
|
2515
2600
|
}
|
|
2601
|
+
var UnsupportedPatternError = class extends Error {
|
|
2602
|
+
constructor(message) {
|
|
2603
|
+
super(`[truss] Unsupported pattern: ${message}`);
|
|
2604
|
+
this.name = "UnsupportedPatternError";
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2516
2607
|
|
|
2517
2608
|
// src/plugin/rewrite-sites.ts
|
|
2518
2609
|
import _traverse from "@babel/traverse";
|
|
@@ -2552,6 +2643,20 @@ function getCssAttributePath(path) {
|
|
|
2552
2643
|
function buildStyleHashFromChain(chain, options) {
|
|
2553
2644
|
const members = [];
|
|
2554
2645
|
const previousProperties = /* @__PURE__ */ new Map();
|
|
2646
|
+
const pendingUnconditionalSegments = [];
|
|
2647
|
+
function flushPendingUnconditionalSegments() {
|
|
2648
|
+
if (pendingUnconditionalSegments.length === 0) {
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
const partMembers = buildStyleHashMembers(pendingUnconditionalSegments, options);
|
|
2652
|
+
members.push(...partMembers);
|
|
2653
|
+
for (const member of partMembers) {
|
|
2654
|
+
if (t3.isObjectProperty(member)) {
|
|
2655
|
+
previousProperties.set(propertyName(member.key), member);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
pendingUnconditionalSegments.length = 0;
|
|
2659
|
+
}
|
|
2555
2660
|
if (chain.markers.length > 0) {
|
|
2556
2661
|
const markerClasses = chain.markers.map((marker) => {
|
|
2557
2662
|
return markerClassName(marker.markerNode);
|
|
@@ -2560,14 +2665,9 @@ function buildStyleHashFromChain(chain, options) {
|
|
|
2560
2665
|
}
|
|
2561
2666
|
for (const part of chain.parts) {
|
|
2562
2667
|
if (part.type === "unconditional") {
|
|
2563
|
-
|
|
2564
|
-
members.push(...partMembers);
|
|
2565
|
-
for (const member of partMembers) {
|
|
2566
|
-
if (t3.isObjectProperty(member)) {
|
|
2567
|
-
previousProperties.set(propertyName(member.key), member);
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2668
|
+
pendingUnconditionalSegments.push(...part.segments);
|
|
2570
2669
|
} else {
|
|
2670
|
+
flushPendingUnconditionalSegments();
|
|
2571
2671
|
const thenMembers = mergeConditionalBranchMembers(
|
|
2572
2672
|
buildStyleHashMembers(part.thenSegments, options),
|
|
2573
2673
|
previousProperties,
|
|
@@ -2585,6 +2685,7 @@ function buildStyleHashFromChain(chain, options) {
|
|
|
2585
2685
|
);
|
|
2586
2686
|
}
|
|
2587
2687
|
}
|
|
2688
|
+
flushPendingUnconditionalSegments();
|
|
2588
2689
|
return t3.objectExpression(members);
|
|
2589
2690
|
}
|
|
2590
2691
|
function buildStyleHashMembers(segments, options) {
|
|
@@ -2976,11 +3077,14 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
2976
3077
|
hasRuntimeStyleCssUsage = true;
|
|
2977
3078
|
return;
|
|
2978
3079
|
}
|
|
3080
|
+
if (isInsideWhenObjectValue(path, cssBindingName)) {
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
2979
3083
|
const parentPath = path.parentPath;
|
|
2980
3084
|
if (parentPath && parentPath.isMemberExpression() && t4.isIdentifier(parentPath.node.property, { name: "$" })) {
|
|
2981
3085
|
return;
|
|
2982
3086
|
}
|
|
2983
|
-
const resolvedChain = resolveFullChain(chain, mapping);
|
|
3087
|
+
const resolvedChain = resolveFullChain(chain, mapping, cssBindingName);
|
|
2984
3088
|
sites.push({ path, resolvedChain });
|
|
2985
3089
|
const line = path.node.loc?.start.line ?? null;
|
|
2986
3090
|
for (const err of resolvedChain.errors) {
|
|
@@ -3124,6 +3228,19 @@ function isRuntimeStyleCssAttribute2(path) {
|
|
|
3124
3228
|
if (!openingElementPath || !openingElementPath.isJSXOpeningElement()) return false;
|
|
3125
3229
|
return t4.isJSXIdentifier(openingElementPath.node.name, { name: "RuntimeStyle" });
|
|
3126
3230
|
}
|
|
3231
|
+
function isInsideWhenObjectValue(path, cssBindingName) {
|
|
3232
|
+
let current = path.parentPath;
|
|
3233
|
+
while (current) {
|
|
3234
|
+
if (current.isObjectExpression()) {
|
|
3235
|
+
const parent = current.parentPath;
|
|
3236
|
+
if (parent?.isCallExpression() && parent.node.arguments[0] === current.node && t4.isMemberExpression(parent.node.callee) && !parent.node.callee.computed && t4.isIdentifier(parent.node.callee.property, { name: "when" }) && extractChain(parent.node.callee.object, cssBindingName)) {
|
|
3237
|
+
return true;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
current = current.parentPath;
|
|
3241
|
+
}
|
|
3242
|
+
return false;
|
|
3243
|
+
}
|
|
3127
3244
|
function collectRuntimeLookups(chains) {
|
|
3128
3245
|
const lookups = /* @__PURE__ */ new Map();
|
|
3129
3246
|
for (const chain of chains) {
|
|
@@ -3331,7 +3448,7 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
|
|
|
3331
3448
|
if (n.type === "if") return { error: "if() conditionals are not supported in .css.ts files" };
|
|
3332
3449
|
if (n.type === "else") return { error: "else is not supported in .css.ts files" };
|
|
3333
3450
|
}
|
|
3334
|
-
const resolved = resolveFullChain(chain, mapping);
|
|
3451
|
+
const resolved = resolveFullChain(chain, mapping, cssBindingName);
|
|
3335
3452
|
if (resolved.errors.length > 0) {
|
|
3336
3453
|
return { error: resolved.errors[0] };
|
|
3337
3454
|
}
|