@homebound/truss 2.17.0 → 2.19.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 +17 -1
- package/build/index.js.map +1 -1
- package/build/plugin/index.js +524 -393
- 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 {
|
|
@@ -1441,6 +1683,12 @@ function cloneConditionContext(context) {
|
|
|
1441
1683
|
whenPseudo: context.whenPseudo ? { ...context.whenPseudo } : null
|
|
1442
1684
|
};
|
|
1443
1685
|
}
|
|
1686
|
+
function resetConditionContext(context) {
|
|
1687
|
+
context.mediaQuery = null;
|
|
1688
|
+
context.pseudoClass = null;
|
|
1689
|
+
context.pseudoElement = null;
|
|
1690
|
+
context.whenPseudo = null;
|
|
1691
|
+
}
|
|
1444
1692
|
function segmentWithConditionContext(segment, context) {
|
|
1445
1693
|
return {
|
|
1446
1694
|
...segment,
|
|
@@ -1456,6 +1704,10 @@ function applyModifierNodeToConditionContext(context, node, mapping) {
|
|
|
1456
1704
|
return;
|
|
1457
1705
|
}
|
|
1458
1706
|
if (node.type === "getter") {
|
|
1707
|
+
if (node.name === "end") {
|
|
1708
|
+
resetConditionContext(context);
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1459
1711
|
if (isPseudoMethod(node.name)) {
|
|
1460
1712
|
context.pseudoClass = pseudoSelector(node.name);
|
|
1461
1713
|
return;
|
|
@@ -1497,9 +1749,10 @@ function applyModifierNodeToConditionContext(context, node, mapping) {
|
|
|
1497
1749
|
context.pseudoClass = pseudoSelector(node.name);
|
|
1498
1750
|
}
|
|
1499
1751
|
}
|
|
1500
|
-
function resolveFullChain(chain, mapping) {
|
|
1752
|
+
function resolveFullChain(chain, mapping, cssBindingName, initialContext = emptyConditionContext()) {
|
|
1501
1753
|
const parts = [];
|
|
1502
1754
|
const markers = [];
|
|
1755
|
+
const nestedErrors = [];
|
|
1503
1756
|
const filteredChain = [];
|
|
1504
1757
|
const scanErrors = [];
|
|
1505
1758
|
for (let j = 0; j < chain.length; j++) {
|
|
@@ -1518,15 +1771,15 @@ function resolveFullChain(chain, mapping) {
|
|
|
1518
1771
|
}
|
|
1519
1772
|
let i = 0;
|
|
1520
1773
|
let currentNodes = [];
|
|
1521
|
-
let currentContext =
|
|
1522
|
-
let currentNodesStartContext =
|
|
1774
|
+
let currentContext = cloneConditionContext(initialContext);
|
|
1775
|
+
let currentNodesStartContext = cloneConditionContext(initialContext);
|
|
1523
1776
|
function flushCurrentNodes() {
|
|
1524
1777
|
if (currentNodes.length === 0) {
|
|
1525
1778
|
return;
|
|
1526
1779
|
}
|
|
1527
1780
|
parts.push({
|
|
1528
1781
|
type: "unconditional",
|
|
1529
|
-
segments: resolveChain(currentNodes, mapping, currentNodesStartContext)
|
|
1782
|
+
segments: resolveChain(currentNodes, mapping, currentNodesStartContext, cssBindingName)
|
|
1530
1783
|
});
|
|
1531
1784
|
currentNodes = [];
|
|
1532
1785
|
currentNodesStartContext = cloneConditionContext(currentContext);
|
|
@@ -1548,13 +1801,22 @@ function resolveFullChain(chain, mapping) {
|
|
|
1548
1801
|
const branchContext = cloneConditionContext(currentContext);
|
|
1549
1802
|
const thenNodes = mediaStart.thenNodes ? [...mediaStart.thenNodes, ...filteredChain.slice(i + 1, elseIndex)] : filteredChain.slice(i, elseIndex);
|
|
1550
1803
|
const elseNodes = [makeMediaQueryNode(mediaStart.inverseMediaQuery), ...filteredChain.slice(elseIndex + 1)];
|
|
1551
|
-
const thenSegs = resolveChain(thenNodes, mapping, branchContext);
|
|
1552
|
-
const elseSegs = resolveChain(elseNodes, mapping, branchContext);
|
|
1804
|
+
const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName);
|
|
1805
|
+
const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName);
|
|
1553
1806
|
parts.push({ type: "unconditional", segments: [...thenSegs, ...elseSegs] });
|
|
1554
1807
|
i = filteredChain.length;
|
|
1555
1808
|
break;
|
|
1556
1809
|
}
|
|
1557
1810
|
}
|
|
1811
|
+
if (isWhenObjectCall(node)) {
|
|
1812
|
+
flushCurrentNodes();
|
|
1813
|
+
const resolved = resolveWhenObjectSelectors(node, mapping, cssBindingName, currentContext);
|
|
1814
|
+
parts.push(...resolved.parts);
|
|
1815
|
+
markers.push(...resolved.markers);
|
|
1816
|
+
nestedErrors.push(...resolved.errors);
|
|
1817
|
+
i++;
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1558
1820
|
if (node.type === "if") {
|
|
1559
1821
|
if (node.conditionNode.type === "StringLiteral") {
|
|
1560
1822
|
const mediaQuery = node.conditionNode.value;
|
|
@@ -1584,8 +1846,8 @@ function resolveFullChain(chain, mapping) {
|
|
|
1584
1846
|
}
|
|
1585
1847
|
i++;
|
|
1586
1848
|
}
|
|
1587
|
-
const thenSegs = resolveChain(thenNodes, mapping, branchContext);
|
|
1588
|
-
const elseSegs = resolveChain(elseNodes, mapping, branchContext);
|
|
1849
|
+
const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName);
|
|
1850
|
+
const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName);
|
|
1589
1851
|
parts.push({
|
|
1590
1852
|
type: "conditional",
|
|
1591
1853
|
conditionNode: node.conditionNode,
|
|
@@ -1607,17 +1869,83 @@ function resolveFullChain(chain, mapping) {
|
|
|
1607
1869
|
}
|
|
1608
1870
|
}
|
|
1609
1871
|
}
|
|
1610
|
-
return { parts, markers, errors: [...scanErrors, ...segmentErrors] };
|
|
1872
|
+
return { parts, markers, errors: [.../* @__PURE__ */ new Set([...scanErrors, ...nestedErrors, ...segmentErrors])] };
|
|
1611
1873
|
}
|
|
1612
|
-
function
|
|
1613
|
-
|
|
1874
|
+
function isWhenObjectCall(node) {
|
|
1875
|
+
return node.type === "call" && node.name === "when" && node.args.length === 1 && node.args[0].type === "ObjectExpression";
|
|
1876
|
+
}
|
|
1877
|
+
function resolveWhenObjectSelectors(node, mapping, cssBindingName, initialContext) {
|
|
1878
|
+
if (!cssBindingName) {
|
|
1614
1879
|
return {
|
|
1615
|
-
|
|
1616
|
-
|
|
1880
|
+
parts: [],
|
|
1881
|
+
markers: [],
|
|
1882
|
+
errors: [new UnsupportedPatternError(`when({ ... }) requires a resolvable Css binding`).message]
|
|
1617
1883
|
};
|
|
1618
1884
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1885
|
+
const objectArg = node.args[0];
|
|
1886
|
+
if (objectArg.type !== "ObjectExpression") {
|
|
1887
|
+
throw new UnsupportedPatternError(`when({ ... }) requires an object literal argument`);
|
|
1888
|
+
}
|
|
1889
|
+
const parts = [];
|
|
1890
|
+
const markers = [];
|
|
1891
|
+
const errors = [];
|
|
1892
|
+
for (const property of objectArg.properties) {
|
|
1893
|
+
try {
|
|
1894
|
+
if (property.type === "SpreadElement") {
|
|
1895
|
+
throw new UnsupportedPatternError(`when({ ... }) does not support spread properties`);
|
|
1896
|
+
}
|
|
1897
|
+
if (property.type !== "ObjectProperty") {
|
|
1898
|
+
throw new UnsupportedPatternError(`when({ ... }) only supports plain object properties`);
|
|
1899
|
+
}
|
|
1900
|
+
if (property.computed || property.key.type !== "StringLiteral") {
|
|
1901
|
+
throw new UnsupportedPatternError(`when({ ... }) selector keys must be string literals`);
|
|
1902
|
+
}
|
|
1903
|
+
const value = unwrapExpression(property.value);
|
|
1904
|
+
if (value.type !== "MemberExpression" || value.computed || value.property.type !== "Identifier" || value.property.name !== "$") {
|
|
1905
|
+
throw new UnsupportedPatternError(`when({ ... }) values must be Css.*.$ expressions`);
|
|
1906
|
+
}
|
|
1907
|
+
const innerChain = extractChain(value.object, cssBindingName);
|
|
1908
|
+
if (!innerChain) {
|
|
1909
|
+
throw new UnsupportedPatternError(`when({ ... }) values must be Css.*.$ expressions`);
|
|
1910
|
+
}
|
|
1911
|
+
const selectorContext = cloneConditionContext(initialContext);
|
|
1912
|
+
selectorContext.pseudoClass = property.key.value;
|
|
1913
|
+
const resolved = resolveFullChain(innerChain, mapping, cssBindingName, selectorContext);
|
|
1914
|
+
parts.push(...resolved.parts);
|
|
1915
|
+
markers.push(...resolved.markers);
|
|
1916
|
+
errors.push(...resolved.errors);
|
|
1917
|
+
} catch (err) {
|
|
1918
|
+
if (err instanceof UnsupportedPatternError) {
|
|
1919
|
+
errors.push(err.message);
|
|
1920
|
+
} else {
|
|
1921
|
+
throw err;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return { parts, markers, errors: [...new Set(errors)] };
|
|
1926
|
+
}
|
|
1927
|
+
function flattenWhenObjectParts(resolved) {
|
|
1928
|
+
const segments = [];
|
|
1929
|
+
for (const part of resolved.parts) {
|
|
1930
|
+
if (part.type !== "unconditional") {
|
|
1931
|
+
throw new UnsupportedPatternError(`when({ ... }) values cannot use if()/else in this context`);
|
|
1932
|
+
}
|
|
1933
|
+
segments.push(...part.segments);
|
|
1934
|
+
}
|
|
1935
|
+
for (const err of resolved.errors) {
|
|
1936
|
+
segments.push({ abbr: "__error", defs: {}, error: err });
|
|
1937
|
+
}
|
|
1938
|
+
return segments;
|
|
1939
|
+
}
|
|
1940
|
+
function getMediaConditionalStartNode(node, mapping) {
|
|
1941
|
+
if (node.type === "if" && node.conditionNode.type === "StringLiteral") {
|
|
1942
|
+
return {
|
|
1943
|
+
inverseMediaQuery: invertMediaQuery(node.conditionNode.value),
|
|
1944
|
+
thenNodes: [makeMediaQueryNode(node.conditionNode.value)]
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
if (node.type === "getter" && mapping.breakpoints && node.name in mapping.breakpoints) {
|
|
1948
|
+
return { inverseMediaQuery: invertMediaQuery(mapping.breakpoints[node.name]) };
|
|
1621
1949
|
}
|
|
1622
1950
|
return null;
|
|
1623
1951
|
}
|
|
@@ -1656,7 +1984,7 @@ function invertMediaQuery(query) {
|
|
|
1656
1984
|
}
|
|
1657
1985
|
return query.replace("@media", "@media not");
|
|
1658
1986
|
}
|
|
1659
|
-
function resolveChain(chain, mapping, initialContext = emptyConditionContext()) {
|
|
1987
|
+
function resolveChain(chain, mapping, initialContext = emptyConditionContext(), cssBindingName) {
|
|
1660
1988
|
const segments = [];
|
|
1661
1989
|
const context = cloneConditionContext(initialContext);
|
|
1662
1990
|
for (const node of chain) {
|
|
@@ -1667,6 +1995,10 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext())
|
|
|
1667
1995
|
}
|
|
1668
1996
|
if (node.type === "getter") {
|
|
1669
1997
|
const abbr = node.name;
|
|
1998
|
+
if (abbr === "end") {
|
|
1999
|
+
resetConditionContext(context);
|
|
2000
|
+
continue;
|
|
2001
|
+
}
|
|
1670
2002
|
if (isPseudoMethod(abbr)) {
|
|
1671
2003
|
context.pseudoClass = pseudoSelector(abbr);
|
|
1672
2004
|
continue;
|
|
@@ -1751,6 +2083,11 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext())
|
|
|
1751
2083
|
continue;
|
|
1752
2084
|
}
|
|
1753
2085
|
if (abbr === "when") {
|
|
2086
|
+
if (isWhenObjectCall(node)) {
|
|
2087
|
+
const resolved2 = resolveWhenObjectSelectors(node, mapping, cssBindingName, context);
|
|
2088
|
+
segments.push(...flattenWhenObjectParts(resolved2));
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
1754
2091
|
const resolved = resolveWhenCall(node);
|
|
1755
2092
|
if (resolved.kind === "selector") {
|
|
1756
2093
|
context.pseudoClass = resolved.pseudo;
|
|
@@ -2120,399 +2457,167 @@ function resolveWhenCall(node) {
|
|
|
2120
2457
|
if (relationshipArg.type !== "StringLiteral") {
|
|
2121
2458
|
throw new UnsupportedPatternError(`when() relationship argument must be a string literal`);
|
|
2122
2459
|
}
|
|
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
|
-
}
|
|
2460
|
+
const relationship = relationshipArg.value;
|
|
2461
|
+
if (!WHEN_RELATIONSHIPS.has(relationship)) {
|
|
2462
|
+
throw new UnsupportedPatternError(
|
|
2463
|
+
`when() relationship must be one of: ${[...WHEN_RELATIONSHIPS].join(", ")} -- got "${relationship}"`
|
|
2464
|
+
);
|
|
2384
2465
|
}
|
|
2385
|
-
|
|
2466
|
+
const pseudoArg = node.args[2];
|
|
2467
|
+
if (pseudoArg.type !== "StringLiteral") {
|
|
2468
|
+
throw new UnsupportedPatternError(`when() pseudo selector (3rd argument) must be a string literal`);
|
|
2469
|
+
}
|
|
2470
|
+
return { kind: "relationship", pseudo: pseudoArg.value, markerNode, relationship };
|
|
2386
2471
|
}
|
|
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
|
-
}
|
|
2472
|
+
function resolveWhenMarker(node) {
|
|
2473
|
+
if (isDefaultMarkerNode(node)) {
|
|
2474
|
+
return void 0;
|
|
2395
2475
|
}
|
|
2396
|
-
|
|
2476
|
+
if (node.type === "Identifier") {
|
|
2477
|
+
return node;
|
|
2478
|
+
}
|
|
2479
|
+
throw new UnsupportedPatternError(`when() marker must be a marker variable or marker`);
|
|
2397
2480
|
}
|
|
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;
|
|
2481
|
+
function isDefaultMarkerNode(node) {
|
|
2482
|
+
if (node.type === "Identifier" && (node.name === "marker" || node.name === "defaultMarker")) {
|
|
2483
|
+
return true;
|
|
2410
2484
|
}
|
|
2485
|
+
return isLegacyDefaultMarkerExpression(node);
|
|
2411
2486
|
}
|
|
2412
|
-
function
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2487
|
+
function isLegacyDefaultMarkerExpression(node) {
|
|
2488
|
+
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";
|
|
2489
|
+
}
|
|
2490
|
+
var PSEUDO_METHODS = {
|
|
2491
|
+
onHover: ":hover",
|
|
2492
|
+
onFocus: ":focus",
|
|
2493
|
+
onFocusVisible: ":focus-visible",
|
|
2494
|
+
onFocusWithin: ":focus-within",
|
|
2495
|
+
onActive: ":active",
|
|
2496
|
+
onDisabled: ":disabled",
|
|
2497
|
+
ifFirstOfType: ":first-of-type",
|
|
2498
|
+
ifLastOfType: ":last-of-type"
|
|
2499
|
+
};
|
|
2500
|
+
function isPseudoMethod(name) {
|
|
2501
|
+
return name in PSEUDO_METHODS;
|
|
2502
|
+
}
|
|
2503
|
+
function pseudoSelector(name) {
|
|
2504
|
+
return PSEUDO_METHODS[name];
|
|
2505
|
+
}
|
|
2506
|
+
function tryEvaluateLiteral(node, incremented, increment) {
|
|
2507
|
+
if (node.type === "NumericLiteral") {
|
|
2508
|
+
if (incremented) {
|
|
2509
|
+
return `${node.value * increment}px`;
|
|
2417
2510
|
}
|
|
2511
|
+
return String(node.value);
|
|
2418
2512
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
return spec.local.name;
|
|
2427
|
-
}
|
|
2513
|
+
if (node.type === "StringLiteral") {
|
|
2514
|
+
return node.value;
|
|
2515
|
+
}
|
|
2516
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2517
|
+
const val = -node.argument.value;
|
|
2518
|
+
if (incremented) {
|
|
2519
|
+
return `${val * increment}px`;
|
|
2428
2520
|
}
|
|
2521
|
+
return String(val);
|
|
2429
2522
|
}
|
|
2430
2523
|
return null;
|
|
2431
2524
|
}
|
|
2432
|
-
function
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
return node;
|
|
2436
|
-
}
|
|
2525
|
+
function tryEvaluatePxLiteral(node) {
|
|
2526
|
+
if (node.type === "NumericLiteral") {
|
|
2527
|
+
return `${node.value}px`;
|
|
2437
2528
|
}
|
|
2438
2529
|
return null;
|
|
2439
2530
|
}
|
|
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;
|
|
2531
|
+
function containerSelectorFromCall(node) {
|
|
2532
|
+
if (node.args.length !== 1) {
|
|
2533
|
+
throw new UnsupportedPatternError(`ifContainer() expects exactly 1 argument, got ${node.args.length}`);
|
|
2452
2534
|
}
|
|
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)));
|
|
2535
|
+
const arg = node.args[0];
|
|
2536
|
+
if (!arg || arg.type !== "ObjectExpression") {
|
|
2537
|
+
throw new UnsupportedPatternError("ifContainer() expects an object literal argument");
|
|
2538
|
+
}
|
|
2539
|
+
let lt;
|
|
2540
|
+
let gt;
|
|
2541
|
+
let name;
|
|
2542
|
+
for (const prop of arg.properties) {
|
|
2543
|
+
if (prop.type === "SpreadElement") {
|
|
2544
|
+
throw new UnsupportedPatternError("ifContainer() does not support spread properties");
|
|
2465
2545
|
}
|
|
2466
|
-
|
|
2546
|
+
if (prop.type !== "ObjectProperty" || prop.computed) {
|
|
2547
|
+
throw new UnsupportedPatternError("ifContainer() expects plain object properties");
|
|
2548
|
+
}
|
|
2549
|
+
const key = objectPropertyName(prop.key);
|
|
2550
|
+
if (!key) {
|
|
2551
|
+
throw new UnsupportedPatternError("ifContainer() only supports identifier/string keys");
|
|
2552
|
+
}
|
|
2553
|
+
const valueNode = prop.value;
|
|
2554
|
+
if (key === "lt") {
|
|
2555
|
+
lt = numericLiteralValue(valueNode, "ifContainer().lt must be a numeric literal");
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
if (key === "gt") {
|
|
2559
|
+
gt = numericLiteralValue(valueNode, "ifContainer().gt must be a numeric literal");
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
if (key === "name") {
|
|
2563
|
+
name = stringLiteralValue(valueNode, "ifContainer().name must be a string literal");
|
|
2564
|
+
continue;
|
|
2565
|
+
}
|
|
2566
|
+
throw new UnsupportedPatternError(`ifContainer() does not support property "${key}"`);
|
|
2467
2567
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2568
|
+
if (lt === void 0 && gt === void 0) {
|
|
2569
|
+
throw new UnsupportedPatternError('ifContainer() requires at least one of "lt" or "gt"');
|
|
2570
|
+
}
|
|
2571
|
+
const parts = [];
|
|
2572
|
+
if (gt !== void 0) {
|
|
2573
|
+
parts.push(`(min-width: ${gt + 1}px)`);
|
|
2574
|
+
}
|
|
2575
|
+
if (lt !== void 0) {
|
|
2576
|
+
parts.push(`(max-width: ${lt}px)`);
|
|
2577
|
+
}
|
|
2578
|
+
const query = parts.join(" and ");
|
|
2579
|
+
const namePrefix = name ? `${name} ` : "";
|
|
2580
|
+
return `@container ${namePrefix}${query}`;
|
|
2476
2581
|
}
|
|
2477
|
-
function
|
|
2478
|
-
|
|
2582
|
+
function objectPropertyName(node) {
|
|
2583
|
+
if (node.type === "Identifier") return node.name;
|
|
2584
|
+
if (node.type === "StringLiteral") return node.value;
|
|
2585
|
+
return null;
|
|
2586
|
+
}
|
|
2587
|
+
function unwrapExpression(node) {
|
|
2479
2588
|
let current = node;
|
|
2480
2589
|
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;
|
|
2590
|
+
if (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression") {
|
|
2591
|
+
current = current.expression;
|
|
2511
2592
|
continue;
|
|
2512
2593
|
}
|
|
2513
|
-
return
|
|
2594
|
+
return current;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
function numericLiteralValue(node, errorMessage) {
|
|
2598
|
+
if (node.type === "NumericLiteral") {
|
|
2599
|
+
return node.value;
|
|
2600
|
+
}
|
|
2601
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
|
|
2602
|
+
return -node.argument.value;
|
|
2603
|
+
}
|
|
2604
|
+
throw new UnsupportedPatternError(errorMessage);
|
|
2605
|
+
}
|
|
2606
|
+
function stringLiteralValue(node, errorMessage) {
|
|
2607
|
+
if (node.type === "StringLiteral") {
|
|
2608
|
+
return node.value;
|
|
2609
|
+
}
|
|
2610
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
2611
|
+
return node.quasis[0].value.cooked ?? "";
|
|
2514
2612
|
}
|
|
2613
|
+
throw new UnsupportedPatternError(errorMessage);
|
|
2515
2614
|
}
|
|
2615
|
+
var UnsupportedPatternError = class extends Error {
|
|
2616
|
+
constructor(message) {
|
|
2617
|
+
super(`[truss] Unsupported pattern: ${message}`);
|
|
2618
|
+
this.name = "UnsupportedPatternError";
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2516
2621
|
|
|
2517
2622
|
// src/plugin/rewrite-sites.ts
|
|
2518
2623
|
import _traverse from "@babel/traverse";
|
|
@@ -2552,6 +2657,20 @@ function getCssAttributePath(path) {
|
|
|
2552
2657
|
function buildStyleHashFromChain(chain, options) {
|
|
2553
2658
|
const members = [];
|
|
2554
2659
|
const previousProperties = /* @__PURE__ */ new Map();
|
|
2660
|
+
const pendingUnconditionalSegments = [];
|
|
2661
|
+
function flushPendingUnconditionalSegments() {
|
|
2662
|
+
if (pendingUnconditionalSegments.length === 0) {
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
const partMembers = buildStyleHashMembers(pendingUnconditionalSegments, options);
|
|
2666
|
+
members.push(...partMembers);
|
|
2667
|
+
for (const member of partMembers) {
|
|
2668
|
+
if (t3.isObjectProperty(member)) {
|
|
2669
|
+
previousProperties.set(propertyName(member.key), member);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
pendingUnconditionalSegments.length = 0;
|
|
2673
|
+
}
|
|
2555
2674
|
if (chain.markers.length > 0) {
|
|
2556
2675
|
const markerClasses = chain.markers.map((marker) => {
|
|
2557
2676
|
return markerClassName(marker.markerNode);
|
|
@@ -2560,14 +2679,9 @@ function buildStyleHashFromChain(chain, options) {
|
|
|
2560
2679
|
}
|
|
2561
2680
|
for (const part of chain.parts) {
|
|
2562
2681
|
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
|
-
}
|
|
2682
|
+
pendingUnconditionalSegments.push(...part.segments);
|
|
2570
2683
|
} else {
|
|
2684
|
+
flushPendingUnconditionalSegments();
|
|
2571
2685
|
const thenMembers = mergeConditionalBranchMembers(
|
|
2572
2686
|
buildStyleHashMembers(part.thenSegments, options),
|
|
2573
2687
|
previousProperties,
|
|
@@ -2585,6 +2699,7 @@ function buildStyleHashFromChain(chain, options) {
|
|
|
2585
2699
|
);
|
|
2586
2700
|
}
|
|
2587
2701
|
}
|
|
2702
|
+
flushPendingUnconditionalSegments();
|
|
2588
2703
|
return t3.objectExpression(members);
|
|
2589
2704
|
}
|
|
2590
2705
|
function buildStyleHashMembers(segments, options) {
|
|
@@ -2976,11 +3091,14 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
2976
3091
|
hasRuntimeStyleCssUsage = true;
|
|
2977
3092
|
return;
|
|
2978
3093
|
}
|
|
3094
|
+
if (isInsideWhenObjectValue(path, cssBindingName)) {
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
2979
3097
|
const parentPath = path.parentPath;
|
|
2980
3098
|
if (parentPath && parentPath.isMemberExpression() && t4.isIdentifier(parentPath.node.property, { name: "$" })) {
|
|
2981
3099
|
return;
|
|
2982
3100
|
}
|
|
2983
|
-
const resolvedChain = resolveFullChain(chain, mapping);
|
|
3101
|
+
const resolvedChain = resolveFullChain(chain, mapping, cssBindingName);
|
|
2984
3102
|
sites.push({ path, resolvedChain });
|
|
2985
3103
|
const line = path.node.loc?.start.line ?? null;
|
|
2986
3104
|
for (const err of resolvedChain.errors) {
|
|
@@ -3124,6 +3242,19 @@ function isRuntimeStyleCssAttribute2(path) {
|
|
|
3124
3242
|
if (!openingElementPath || !openingElementPath.isJSXOpeningElement()) return false;
|
|
3125
3243
|
return t4.isJSXIdentifier(openingElementPath.node.name, { name: "RuntimeStyle" });
|
|
3126
3244
|
}
|
|
3245
|
+
function isInsideWhenObjectValue(path, cssBindingName) {
|
|
3246
|
+
let current = path.parentPath;
|
|
3247
|
+
while (current) {
|
|
3248
|
+
if (current.isObjectExpression()) {
|
|
3249
|
+
const parent = current.parentPath;
|
|
3250
|
+
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)) {
|
|
3251
|
+
return true;
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
current = current.parentPath;
|
|
3255
|
+
}
|
|
3256
|
+
return false;
|
|
3257
|
+
}
|
|
3127
3258
|
function collectRuntimeLookups(chains) {
|
|
3128
3259
|
const lookups = /* @__PURE__ */ new Map();
|
|
3129
3260
|
for (const chain of chains) {
|
|
@@ -3331,7 +3462,7 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
|
|
|
3331
3462
|
if (n.type === "if") return { error: "if() conditionals are not supported in .css.ts files" };
|
|
3332
3463
|
if (n.type === "else") return { error: "else is not supported in .css.ts files" };
|
|
3333
3464
|
}
|
|
3334
|
-
const resolved = resolveFullChain(chain, mapping);
|
|
3465
|
+
const resolved = resolveFullChain(chain, mapping, cssBindingName);
|
|
3335
3466
|
if (resolved.errors.length > 0) {
|
|
3336
3467
|
return { error: resolved.errors[0] };
|
|
3337
3468
|
}
|