@homebound/truss 2.15.1 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -544,7 +544,7 @@ var RELATIONSHIP_BASE = {
544
544
  siblingAfter: 40
545
545
  };
546
546
  function computeRulePriority(rule) {
547
- let priority = getPropertyPriority(rule.cssProperty);
547
+ let priority = getPropertyPriority(rule.declarations[0].cssProperty);
548
548
  if (rule.pseudoElement) {
549
549
  priority += PSEUDO_ELEMENT_PRIORITY;
550
550
  }
@@ -565,10 +565,7 @@ function computeRulePriority(rule) {
565
565
  return priority;
566
566
  }
567
567
  function isVariableRule(rule) {
568
- if (rule.declarations) {
569
- return rule.declarations.some((d) => d.cssVarName !== void 0);
570
- }
571
- return rule.cssVarName !== void 0;
568
+ return rule.declarations.some((d) => d.cssVarName !== void 0);
572
569
  }
573
570
  function sortRulesByPriority(rules) {
574
571
  const decorated = rules.map((rule, i) => {
@@ -1013,6 +1010,27 @@ function whenPrefix(whenPseudo) {
1013
1010
  const markerPart = whenPseudo.markerNode?.type === "Identifier" ? `${whenPseudo.markerNode.name}_` : "";
1014
1011
  return `wh_${rel}_${pseudoTag}_${markerPart}`;
1015
1012
  }
1013
+ function conditionPrefix(pseudoClass, mediaQuery, pseudoElement, breakpoints) {
1014
+ const parts = [];
1015
+ if (pseudoElement) {
1016
+ parts.push(`${pseudoElement.replace(/^::/, "")}_`);
1017
+ }
1018
+ if (mediaQuery && breakpoints) {
1019
+ const bpKey = Object.entries(breakpoints).find(([, v]) => v === mediaQuery)?.[0];
1020
+ if (bpKey) {
1021
+ const shortName = bpKey.replace(/^if/, "").toLowerCase();
1022
+ parts.push(`${shortName}_`);
1023
+ } else {
1024
+ parts.push("mq_");
1025
+ }
1026
+ } else if (mediaQuery) {
1027
+ parts.push("mq_");
1028
+ }
1029
+ if (pseudoClass) {
1030
+ parts.push(`${pseudoSelectorTag(pseudoClass)}_`);
1031
+ }
1032
+ return parts.join("");
1033
+ }
1016
1034
  function pseudoSelectorTag(pseudo) {
1017
1035
  const replaced = pseudo.trim().replace(/::?[a-zA-Z-]+/g, (match) => {
1018
1036
  return `_${pseudoIdentifierTag(match)}_`;
@@ -1036,27 +1054,6 @@ function normalizePseudoIdentifier(pseudo) {
1036
1054
  });
1037
1055
  return `${prefix}${name}`;
1038
1056
  }
1039
- function conditionPrefix(pseudoClass, mediaQuery, pseudoElement, breakpoints) {
1040
- const parts = [];
1041
- if (pseudoElement) {
1042
- parts.push(`${pseudoElement.replace(/^::/, "")}_`);
1043
- }
1044
- if (mediaQuery && breakpoints) {
1045
- const bpKey = Object.entries(breakpoints).find(([, v]) => v === mediaQuery)?.[0];
1046
- if (bpKey) {
1047
- const shortName = bpKey.replace(/^if/, "").toLowerCase();
1048
- parts.push(`${shortName}_`);
1049
- } else {
1050
- parts.push("mq_");
1051
- }
1052
- } else if (mediaQuery) {
1053
- parts.push("mq_");
1054
- }
1055
- if (pseudoClass) {
1056
- parts.push(`${pseudoSelectorTag(pseudoClass)}_`);
1057
- }
1058
- return parts.join("");
1059
- }
1060
1057
  function camelToKebab(s) {
1061
1058
  return s.replace(/^(Webkit|Moz|Ms|O)/, (m) => `-${m.toLowerCase()}`).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
1062
1059
  }
@@ -1067,6 +1064,9 @@ function cleanValueForClassName(value) {
1067
1064
  }
1068
1065
  return cleaned.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
1069
1066
  }
1067
+ function getPropertyAbbreviation(cssProp) {
1068
+ return cssPropertyAbbreviations[cssProp] ?? cssProp;
1069
+ }
1070
1070
  function buildLonghandLookup(mapping) {
1071
1071
  const lookup = /* @__PURE__ */ new Map();
1072
1072
  for (const [abbrev, entry] of Object.entries(mapping.abbreviations)) {
@@ -1091,9 +1091,6 @@ function getLonghandLookup(mapping) {
1091
1091
  }
1092
1092
  return cachedLookup;
1093
1093
  }
1094
- function getPropertyAbbreviation(cssProp) {
1095
- return cssPropertyAbbreviations[cssProp] ?? cssProp;
1096
- }
1097
1094
  function computeStaticBaseName(seg, cssProp, cssValue, isMultiProp, mapping) {
1098
1095
  const abbr = seg.abbr;
1099
1096
  if (seg.argResolved !== void 0) {
@@ -1118,7 +1115,7 @@ function collectAtomicRules(chains, mapping) {
1118
1115
  const rules = /* @__PURE__ */ new Map();
1119
1116
  let needsMaybeInc = false;
1120
1117
  function collectSegment(seg) {
1121
- if (seg.error || seg.styleArrayArg || seg.classNameArg) return;
1118
+ if (seg.error || seg.styleArrayArg || seg.classNameArg || seg.styleArg) return;
1122
1119
  if (seg.typographyLookup) {
1123
1120
  for (const segments of Object.values(seg.typographyLookup.segmentsByName)) {
1124
1121
  for (const nestedSeg of segments) {
@@ -1145,10 +1142,11 @@ function collectAtomicRules(chains, mapping) {
1145
1142
  return { rules, needsMaybeInc };
1146
1143
  }
1147
1144
  function segmentContext(seg, mapping) {
1145
+ const prefix = `${conditionPrefix(seg.pseudoClass, seg.mediaQuery, seg.pseudoElement, mapping.breakpoints)}${seg.whenPseudo ? whenPrefix(seg.whenPseudo) : ""}`;
1148
1146
  if (seg.whenPseudo) {
1149
1147
  const wp = seg.whenPseudo;
1150
1148
  return {
1151
- prefix: whenPrefix(wp),
1149
+ prefix,
1152
1150
  whenSelector: {
1153
1151
  relationship: wp.relationship ?? "ancestor",
1154
1152
  markerClass: markerClassName(wp.markerNode),
@@ -1156,7 +1154,7 @@ function segmentContext(seg, mapping) {
1156
1154
  }
1157
1155
  };
1158
1156
  }
1159
- return { prefix: conditionPrefix(seg.pseudoClass, seg.mediaQuery, seg.pseudoElement, mapping.breakpoints) };
1157
+ return { prefix };
1160
1158
  }
1161
1159
  function baseRuleFields(seg) {
1162
1160
  return {
@@ -1175,9 +1173,8 @@ function collectStaticRules(rules, seg, mapping) {
1175
1173
  if (!rules.has(className)) {
1176
1174
  rules.set(className, {
1177
1175
  className,
1178
- cssProperty: camelToKebab(cssProp),
1179
- cssValue,
1180
- ...!whenSelector && baseRuleFields(seg),
1176
+ declarations: [{ cssProperty: camelToKebab(cssProp), cssValue }],
1177
+ ...baseRuleFields(seg),
1181
1178
  whenSelector
1182
1179
  });
1183
1180
  }
@@ -1193,22 +1190,12 @@ function collectVariableRules(rules, seg, mapping) {
1193
1190
  if (!existingRule) {
1194
1191
  rules.set(className, {
1195
1192
  className,
1196
- cssProperty: declaration.cssProperty,
1197
- cssValue: declaration.cssValue,
1198
1193
  declarations: [declaration],
1199
- cssVarName: varName,
1200
- ...!whenSelector && baseRuleFields(seg),
1194
+ ...baseRuleFields(seg),
1201
1195
  whenSelector
1202
1196
  });
1203
1197
  continue;
1204
1198
  }
1205
- existingRule.declarations ??= [
1206
- {
1207
- cssProperty: existingRule.cssProperty,
1208
- cssValue: existingRule.cssValue,
1209
- cssVarName: existingRule.cssVarName
1210
- }
1211
- ];
1212
1199
  if (!existingRule.declarations.some((entry) => {
1213
1200
  return entry.cssProperty === declaration.cssProperty;
1214
1201
  })) {
@@ -1225,9 +1212,8 @@ function collectVariableRules(rules, seg, mapping) {
1225
1212
  if (!rules.has(extraName)) {
1226
1213
  rules.set(extraName, {
1227
1214
  className: extraName,
1228
- cssProperty: camelToKebab(cssProp),
1229
- cssValue,
1230
- ...!whenSelector && baseRuleFields(seg),
1215
+ declarations: [{ cssProperty: camelToKebab(cssProp), cssValue }],
1216
+ ...baseRuleFields(seg),
1231
1217
  whenSelector
1232
1218
  });
1233
1219
  }
@@ -1256,61 +1242,51 @@ function generateCssText(rules) {
1256
1242
  return lines.join("\n");
1257
1243
  }
1258
1244
  function formatRule(rule) {
1259
- if (rule.whenSelector) return formatWhenRule(rule);
1260
- if (rule.mediaQuery && rule.pseudoClass) return formatMediaPseudoRule(rule);
1261
- if (rule.mediaQuery && rule.pseudoElement) return formatMediaPseudoElementRule(rule);
1262
- if (rule.mediaQuery) return formatMediaRule(rule);
1263
- if (rule.pseudoClass && rule.pseudoElement) return formatPseudoRule(rule);
1264
- if (rule.pseudoElement) return formatPseudoElementRule(rule);
1265
- if (rule.pseudoClass) return formatPseudoRule(rule);
1266
- return formatBaseRule(rule);
1267
- }
1268
- function formatBaseRule(rule) {
1269
- return formatRuleBlock(`.${rule.className}`, rule);
1270
- }
1271
- function formatPseudoRule(rule) {
1272
- const pe = rule.pseudoElement ? rule.pseudoElement : "";
1273
- return formatRuleBlock(`.${rule.className}${rule.pseudoClass}${pe}`, rule);
1274
- }
1275
- function formatPseudoElementRule(rule) {
1276
- return formatRuleBlock(`.${rule.className}${rule.pseudoElement}`, rule);
1277
- }
1278
- function formatWhenRule(rule) {
1279
1245
  const whenSelector = rule.whenSelector;
1280
- if (!whenSelector) {
1281
- return formatBaseRule(rule);
1282
- }
1246
+ if (whenSelector) return formatWhenRule(rule, whenSelector);
1247
+ const selector = buildTargetSelector(rule, !!rule.mediaQuery);
1248
+ return formatRuleWithOptionalMedia(rule, selector);
1249
+ }
1250
+ function formatWhenRule(rule, whenSelector) {
1283
1251
  const markerSelector = `.${whenSelector.markerClass}${whenSelector.pseudo}`;
1284
- const targetSelector = `.${rule.className}`;
1252
+ const duplicateClassName = !!rule.mediaQuery;
1285
1253
  if (whenSelector.relationship === "ancestor") {
1286
- return formatRuleBlock(`${markerSelector} ${targetSelector}`, rule);
1254
+ return formatRuleWithOptionalMedia(rule, `${markerSelector} ${buildTargetSelector(rule, duplicateClassName)}`);
1287
1255
  }
1288
1256
  if (whenSelector.relationship === "descendant") {
1289
- return formatRuleBlock(`${targetSelector}:has(${markerSelector})`, rule);
1257
+ return formatRuleWithOptionalMedia(rule, buildTargetSelector(rule, duplicateClassName, `:has(${markerSelector})`));
1290
1258
  }
1291
1259
  if (whenSelector.relationship === "siblingAfter") {
1292
- return formatRuleBlock(`${targetSelector}:has(~ ${markerSelector})`, rule);
1260
+ return formatRuleWithOptionalMedia(
1261
+ rule,
1262
+ buildTargetSelector(rule, duplicateClassName, `:has(~ ${markerSelector})`)
1263
+ );
1293
1264
  }
1294
1265
  if (whenSelector.relationship === "siblingBefore") {
1295
- return formatRuleBlock(`${markerSelector} ~ ${targetSelector}`, rule);
1266
+ return formatRuleWithOptionalMedia(rule, `${markerSelector} ~ ${buildTargetSelector(rule, duplicateClassName)}`);
1296
1267
  }
1297
1268
  if (whenSelector.relationship === "anySibling") {
1298
- return formatRuleBlock(`${targetSelector}:has(~ ${markerSelector}), ${markerSelector} ~ ${targetSelector}`, rule);
1269
+ const afterSelector = buildTargetSelector(rule, duplicateClassName, `:has(~ ${markerSelector})`);
1270
+ const beforeSelector = `${markerSelector} ~ ${buildTargetSelector(rule, duplicateClassName)}`;
1271
+ return formatRuleWithOptionalMedia(rule, `${afterSelector}, ${beforeSelector}`);
1299
1272
  }
1300
- return formatRuleBlock(`${markerSelector} ${targetSelector}`, rule);
1273
+ return formatRuleWithOptionalMedia(rule, `${markerSelector} ${buildTargetSelector(rule, duplicateClassName)}`);
1301
1274
  }
1302
- function formatMediaRule(rule) {
1303
- return formatNestedRuleBlock(rule.mediaQuery, `.${rule.className}.${rule.className}`, rule);
1275
+ function buildTargetSelector(rule, duplicateClassName, extraPseudoClass) {
1276
+ const classSelector = duplicateClassName ? `.${rule.className}.${rule.className}` : `.${rule.className}`;
1277
+ const pseudoClass = rule.pseudoClass ?? "";
1278
+ const relationshipPseudoClass = extraPseudoClass ?? "";
1279
+ const pseudoElement = rule.pseudoElement ?? "";
1280
+ return `${classSelector}${pseudoClass}${relationshipPseudoClass}${pseudoElement}`;
1304
1281
  }
1305
- function formatMediaPseudoRule(rule) {
1306
- return formatNestedRuleBlock(rule.mediaQuery, `.${rule.className}.${rule.className}${rule.pseudoClass}`, rule);
1307
- }
1308
- function formatMediaPseudoElementRule(rule) {
1309
- const pe = rule.pseudoElement ?? "";
1310
- return formatNestedRuleBlock(rule.mediaQuery, `.${rule.className}.${rule.className}${pe}`, rule);
1282
+ function formatRuleWithOptionalMedia(rule, selector) {
1283
+ if (rule.mediaQuery) {
1284
+ return formatNestedRuleBlock(rule.mediaQuery, selector, rule);
1285
+ }
1286
+ return formatRuleBlock(selector, rule);
1311
1287
  }
1312
1288
  function getRuleDeclarations(rule) {
1313
- return rule.declarations ?? [{ cssProperty: rule.cssProperty, cssValue: rule.cssValue, cssVarName: rule.cssVarName }];
1289
+ return rule.declarations;
1314
1290
  }
1315
1291
  function formatRuleBlock(selector, rule) {
1316
1292
  const body = getRuleDeclarations(rule).map((declaration) => {
@@ -1339,7 +1315,7 @@ function buildStyleHashProperties(segments, mapping, maybeIncHelperName) {
1339
1315
  entries.push(entry);
1340
1316
  }
1341
1317
  for (const seg of segments) {
1342
- if (seg.error || seg.styleArrayArg || seg.typographyLookup || seg.classNameArg) continue;
1318
+ if (seg.error || seg.styleArrayArg || seg.typographyLookup || seg.classNameArg || seg.styleArg) continue;
1343
1319
  const { prefix } = segmentContext(seg, mapping);
1344
1320
  const isConditional = prefix !== "";
1345
1321
  if (seg.variableProps) {
@@ -1449,6 +1425,78 @@ import * as t4 from "@babel/types";
1449
1425
  import { basename } from "path";
1450
1426
 
1451
1427
  // src/plugin/resolve-chain.ts
1428
+ function emptyConditionContext() {
1429
+ return {
1430
+ mediaQuery: null,
1431
+ pseudoClass: null,
1432
+ pseudoElement: null,
1433
+ whenPseudo: null
1434
+ };
1435
+ }
1436
+ function cloneConditionContext(context) {
1437
+ return {
1438
+ mediaQuery: context.mediaQuery,
1439
+ pseudoClass: context.pseudoClass,
1440
+ pseudoElement: context.pseudoElement,
1441
+ whenPseudo: context.whenPseudo ? { ...context.whenPseudo } : null
1442
+ };
1443
+ }
1444
+ function segmentWithConditionContext(segment, context) {
1445
+ return {
1446
+ ...segment,
1447
+ mediaQuery: context.mediaQuery,
1448
+ pseudoClass: context.pseudoClass,
1449
+ pseudoElement: context.pseudoElement,
1450
+ whenPseudo: context.whenPseudo
1451
+ };
1452
+ }
1453
+ function applyModifierNodeToConditionContext(context, node, mapping) {
1454
+ if (node.type === "__mediaQuery") {
1455
+ context.mediaQuery = node.mediaQuery;
1456
+ return;
1457
+ }
1458
+ if (node.type === "getter") {
1459
+ if (isPseudoMethod(node.name)) {
1460
+ context.pseudoClass = pseudoSelector(node.name);
1461
+ return;
1462
+ }
1463
+ if (mapping.breakpoints && node.name in mapping.breakpoints) {
1464
+ context.mediaQuery = mapping.breakpoints[node.name];
1465
+ }
1466
+ return;
1467
+ }
1468
+ if (node.type !== "call") {
1469
+ return;
1470
+ }
1471
+ if (node.name === "ifContainer") {
1472
+ try {
1473
+ context.mediaQuery = containerSelectorFromCall(node);
1474
+ } catch {
1475
+ }
1476
+ return;
1477
+ }
1478
+ if (node.name === "element") {
1479
+ if (node.args.length === 1 && node.args[0].type === "StringLiteral") {
1480
+ context.pseudoElement = node.args[0].value;
1481
+ }
1482
+ return;
1483
+ }
1484
+ if (node.name === "when") {
1485
+ try {
1486
+ const resolved = resolveWhenCall(node);
1487
+ if (resolved.kind === "selector") {
1488
+ context.pseudoClass = resolved.pseudo;
1489
+ } else {
1490
+ context.whenPseudo = resolved;
1491
+ }
1492
+ } catch {
1493
+ }
1494
+ return;
1495
+ }
1496
+ if (isPseudoMethod(node.name)) {
1497
+ context.pseudoClass = pseudoSelector(node.name);
1498
+ }
1499
+ }
1452
1500
  function resolveFullChain(chain, mapping) {
1453
1501
  const parts = [];
1454
1502
  const markers = [];
@@ -1470,20 +1518,38 @@ function resolveFullChain(chain, mapping) {
1470
1518
  }
1471
1519
  let i = 0;
1472
1520
  let currentNodes = [];
1521
+ let currentContext = emptyConditionContext();
1522
+ let currentNodesStartContext = emptyConditionContext();
1523
+ function flushCurrentNodes() {
1524
+ if (currentNodes.length === 0) {
1525
+ return;
1526
+ }
1527
+ parts.push({
1528
+ type: "unconditional",
1529
+ segments: resolveChain(currentNodes, mapping, currentNodesStartContext)
1530
+ });
1531
+ currentNodes = [];
1532
+ currentNodesStartContext = cloneConditionContext(currentContext);
1533
+ }
1534
+ function pushCurrentNode(nodeToPush) {
1535
+ if (currentNodes.length === 0) {
1536
+ currentNodesStartContext = cloneConditionContext(currentContext);
1537
+ }
1538
+ currentNodes.push(nodeToPush);
1539
+ applyModifierNodeToConditionContext(currentContext, nodeToPush, mapping);
1540
+ }
1473
1541
  while (i < filteredChain.length) {
1474
1542
  const node = filteredChain[i];
1475
1543
  const mediaStart = getMediaConditionalStartNode(node, mapping);
1476
1544
  if (mediaStart) {
1477
1545
  const elseIndex = findElseIndex(filteredChain, i + 1);
1478
1546
  if (elseIndex !== -1) {
1479
- if (currentNodes.length > 0) {
1480
- parts.push({ type: "unconditional", segments: resolveChain(currentNodes, mapping) });
1481
- currentNodes = [];
1482
- }
1547
+ flushCurrentNodes();
1548
+ const branchContext = cloneConditionContext(currentContext);
1483
1549
  const thenNodes = mediaStart.thenNodes ? [...mediaStart.thenNodes, ...filteredChain.slice(i + 1, elseIndex)] : filteredChain.slice(i, elseIndex);
1484
1550
  const elseNodes = [makeMediaQueryNode(mediaStart.inverseMediaQuery), ...filteredChain.slice(elseIndex + 1)];
1485
- const thenSegs = resolveChain(thenNodes, mapping);
1486
- const elseSegs = resolveChain(elseNodes, mapping);
1551
+ const thenSegs = resolveChain(thenNodes, mapping, branchContext);
1552
+ const elseSegs = resolveChain(elseNodes, mapping, branchContext);
1487
1553
  parts.push({ type: "unconditional", segments: [...thenSegs, ...elseSegs] });
1488
1554
  i = filteredChain.length;
1489
1555
  break;
@@ -1492,14 +1558,12 @@ function resolveFullChain(chain, mapping) {
1492
1558
  if (node.type === "if") {
1493
1559
  if (node.conditionNode.type === "StringLiteral") {
1494
1560
  const mediaQuery = node.conditionNode.value;
1495
- currentNodes.push({ type: "__mediaQuery", mediaQuery });
1561
+ pushCurrentNode({ type: "__mediaQuery", mediaQuery });
1496
1562
  i++;
1497
1563
  continue;
1498
1564
  }
1499
- if (currentNodes.length > 0) {
1500
- parts.push({ type: "unconditional", segments: resolveChain(currentNodes, mapping) });
1501
- currentNodes = [];
1502
- }
1565
+ flushCurrentNodes();
1566
+ const branchContext = cloneConditionContext(currentContext);
1503
1567
  const thenNodes = [];
1504
1568
  const elseNodes = [];
1505
1569
  i++;
@@ -1520,8 +1584,8 @@ function resolveFullChain(chain, mapping) {
1520
1584
  }
1521
1585
  i++;
1522
1586
  }
1523
- const thenSegs = resolveChain(thenNodes, mapping);
1524
- const elseSegs = resolveChain(elseNodes, mapping);
1587
+ const thenSegs = resolveChain(thenNodes, mapping, branchContext);
1588
+ const elseSegs = resolveChain(elseNodes, mapping, branchContext);
1525
1589
  parts.push({
1526
1590
  type: "conditional",
1527
1591
  conditionNode: node.conditionNode,
@@ -1529,13 +1593,11 @@ function resolveFullChain(chain, mapping) {
1529
1593
  elseSegments: elseSegs
1530
1594
  });
1531
1595
  } else {
1532
- currentNodes.push(node);
1596
+ pushCurrentNode(node);
1533
1597
  i++;
1534
1598
  }
1535
1599
  }
1536
- if (currentNodes.length > 0) {
1537
- parts.push({ type: "unconditional", segments: resolveChain(currentNodes, mapping) });
1538
- }
1600
+ flushCurrentNodes();
1539
1601
  const segmentErrors = [];
1540
1602
  for (const part of parts) {
1541
1603
  const segs = part.type === "unconditional" ? part.segments : [...part.thenSegments, ...part.elseSegments];
@@ -1594,29 +1656,23 @@ function invertMediaQuery(query) {
1594
1656
  }
1595
1657
  return query.replace("@media", "@media not");
1596
1658
  }
1597
- function resolveChain(chain, mapping) {
1659
+ function resolveChain(chain, mapping, initialContext = emptyConditionContext()) {
1598
1660
  const segments = [];
1599
- let currentMediaQuery = null;
1600
- let currentPseudoClass = null;
1601
- let currentPseudoElement = null;
1602
- let currentWhenPseudo = null;
1661
+ const context = cloneConditionContext(initialContext);
1603
1662
  for (const node of chain) {
1604
1663
  try {
1605
1664
  if (node.type === "__mediaQuery") {
1606
- currentMediaQuery = node.mediaQuery;
1607
- currentWhenPseudo = null;
1665
+ context.mediaQuery = node.mediaQuery;
1608
1666
  continue;
1609
1667
  }
1610
1668
  if (node.type === "getter") {
1611
1669
  const abbr = node.name;
1612
1670
  if (isPseudoMethod(abbr)) {
1613
- currentPseudoClass = pseudoSelector(abbr);
1614
- currentWhenPseudo = null;
1671
+ context.pseudoClass = pseudoSelector(abbr);
1615
1672
  continue;
1616
1673
  }
1617
1674
  if (mapping.breakpoints && abbr in mapping.breakpoints) {
1618
- currentMediaQuery = mapping.breakpoints[abbr];
1619
- currentWhenPseudo = null;
1675
+ context.mediaQuery = mapping.breakpoints[abbr];
1620
1676
  continue;
1621
1677
  }
1622
1678
  const entry = mapping.abbreviations[abbr];
@@ -1627,27 +1683,26 @@ function resolveChain(chain, mapping) {
1627
1683
  abbr,
1628
1684
  entry,
1629
1685
  mapping,
1630
- currentMediaQuery,
1631
- currentPseudoClass,
1632
- currentPseudoElement,
1633
- currentWhenPseudo
1686
+ context.mediaQuery,
1687
+ context.pseudoClass,
1688
+ context.pseudoElement,
1689
+ context.whenPseudo
1634
1690
  );
1635
1691
  segments.push(...resolved);
1636
1692
  } else if (node.type === "call") {
1637
1693
  const abbr = node.name;
1638
1694
  if (abbr === "ifContainer") {
1639
- currentMediaQuery = containerSelectorFromCall(node);
1640
- currentWhenPseudo = null;
1695
+ context.mediaQuery = containerSelectorFromCall(node);
1641
1696
  continue;
1642
1697
  }
1643
1698
  if (abbr === "add" || abbr === "addCss") {
1644
1699
  const seg = resolveAddCall(
1645
1700
  node,
1646
1701
  mapping,
1647
- currentMediaQuery,
1648
- currentPseudoClass,
1649
- currentPseudoElement,
1650
- currentWhenPseudo
1702
+ context.mediaQuery,
1703
+ context.pseudoClass,
1704
+ context.pseudoElement,
1705
+ context.whenPseudo
1651
1706
  );
1652
1707
  segments.push(seg);
1653
1708
  continue;
@@ -1655,10 +1710,21 @@ function resolveChain(chain, mapping) {
1655
1710
  if (abbr === "className") {
1656
1711
  const seg = resolveClassNameCall(
1657
1712
  node,
1658
- currentMediaQuery,
1659
- currentPseudoClass,
1660
- currentPseudoElement,
1661
- currentWhenPseudo
1713
+ context.mediaQuery,
1714
+ context.pseudoClass,
1715
+ context.pseudoElement,
1716
+ context.whenPseudo
1717
+ );
1718
+ segments.push(seg);
1719
+ continue;
1720
+ }
1721
+ if (abbr === "style") {
1722
+ const seg = resolveStyleCall(
1723
+ node,
1724
+ context.mediaQuery,
1725
+ context.pseudoClass,
1726
+ context.pseudoElement,
1727
+ context.whenPseudo
1662
1728
  );
1663
1729
  segments.push(seg);
1664
1730
  continue;
@@ -1667,9 +1733,10 @@ function resolveChain(chain, mapping) {
1667
1733
  const resolved = resolveTypographyCall(
1668
1734
  node,
1669
1735
  mapping,
1670
- currentMediaQuery,
1671
- currentPseudoClass,
1672
- currentPseudoElement
1736
+ context.mediaQuery,
1737
+ context.pseudoClass,
1738
+ context.pseudoElement,
1739
+ context.whenPseudo
1673
1740
  );
1674
1741
  segments.push(...resolved);
1675
1742
  continue;
@@ -1680,24 +1747,20 @@ function resolveChain(chain, mapping) {
1680
1747
  `element() requires exactly one string literal argument (e.g. "::placeholder")`
1681
1748
  );
1682
1749
  }
1683
- currentPseudoElement = node.args[0].value;
1750
+ context.pseudoElement = node.args[0].value;
1684
1751
  continue;
1685
1752
  }
1686
1753
  if (abbr === "when") {
1687
1754
  const resolved = resolveWhenCall(node);
1688
1755
  if (resolved.kind === "selector") {
1689
- currentPseudoClass = resolved.pseudo;
1690
- currentWhenPseudo = null;
1756
+ context.pseudoClass = resolved.pseudo;
1691
1757
  } else {
1692
- currentPseudoClass = null;
1693
- currentMediaQuery = null;
1694
- currentWhenPseudo = resolved;
1758
+ context.whenPseudo = resolved;
1695
1759
  }
1696
1760
  continue;
1697
1761
  }
1698
1762
  if (isPseudoMethod(abbr)) {
1699
- currentPseudoClass = pseudoSelector(abbr);
1700
- currentWhenPseudo = null;
1763
+ context.pseudoClass = pseudoSelector(abbr);
1701
1764
  if (node.args.length > 0) {
1702
1765
  throw new UnsupportedPatternError(
1703
1766
  `${abbr}() does not take arguments -- use when(marker, "ancestor", ":hover") for relationship selectors`
@@ -1715,10 +1778,10 @@ function resolveChain(chain, mapping) {
1715
1778
  entry,
1716
1779
  node,
1717
1780
  mapping,
1718
- currentMediaQuery,
1719
- currentPseudoClass,
1720
- currentPseudoElement,
1721
- currentWhenPseudo
1781
+ context.mediaQuery,
1782
+ context.pseudoClass,
1783
+ context.pseudoElement,
1784
+ context.whenPseudo
1722
1785
  );
1723
1786
  segments.push(seg);
1724
1787
  } else if (entry.kind === "delegate") {
@@ -1727,10 +1790,10 @@ function resolveChain(chain, mapping) {
1727
1790
  entry,
1728
1791
  node,
1729
1792
  mapping,
1730
- currentMediaQuery,
1731
- currentPseudoClass,
1732
- currentPseudoElement,
1733
- currentWhenPseudo
1793
+ context.mediaQuery,
1794
+ context.pseudoClass,
1795
+ context.pseudoElement,
1796
+ context.whenPseudo
1734
1797
  );
1735
1798
  segments.push(seg);
1736
1799
  } else {
@@ -1747,7 +1810,7 @@ function resolveChain(chain, mapping) {
1747
1810
  }
1748
1811
  return segments;
1749
1812
  }
1750
- function typographyLookupKeySuffix(mediaQuery, pseudoClass, pseudoElement, breakpoints) {
1813
+ function typographyLookupKeySuffix(mediaQuery, pseudoClass, pseudoElement, whenPseudo, breakpoints) {
1751
1814
  const parts = [];
1752
1815
  if (pseudoElement) parts.push(pseudoElement.replace(/^::/, ""));
1753
1816
  if (mediaQuery && breakpoints) {
@@ -1757,25 +1820,36 @@ function typographyLookupKeySuffix(mediaQuery, pseudoClass, pseudoElement, break
1757
1820
  parts.push("mq");
1758
1821
  }
1759
1822
  if (pseudoClass) parts.push(pseudoClass.replace(/^:+/, "").replace(/-/g, "_"));
1823
+ if (whenPseudo) parts.push(whenLookupKeyPart(whenPseudo));
1760
1824
  return parts.join("_");
1761
1825
  }
1762
- function resolveTypographyCall(node, mapping, mediaQuery, pseudoClass, pseudoElement) {
1826
+ function whenLookupKeyPart(whenPseudo) {
1827
+ const parts = ["when", whenPseudo.relationship ?? "ancestor", sanitizeLookupToken(whenPseudo.pseudo)];
1828
+ if (whenPseudo.markerNode?.type === "Identifier" && whenPseudo.markerNode.name) {
1829
+ parts.push(whenPseudo.markerNode.name);
1830
+ }
1831
+ return parts.join("_");
1832
+ }
1833
+ function sanitizeLookupToken(value) {
1834
+ return value.replace(/[^a-zA-Z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "") || "value";
1835
+ }
1836
+ function resolveTypographyCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
1763
1837
  if (node.args.length !== 1) {
1764
1838
  throw new UnsupportedPatternError(`typography() expects exactly 1 argument, got ${node.args.length}`);
1765
1839
  }
1766
1840
  const argAst = node.args[0];
1767
1841
  if (argAst.type === "StringLiteral") {
1768
- return resolveTypographyEntry(argAst.value, mapping, mediaQuery, pseudoClass, pseudoElement);
1842
+ return resolveTypographyEntry(argAst.value, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo);
1769
1843
  }
1770
1844
  const typography = mapping.typography ?? [];
1771
1845
  if (typography.length === 0) {
1772
1846
  throw new UnsupportedPatternError(`typography() is unavailable because no typography abbreviations were generated`);
1773
1847
  }
1774
- const suffix = typographyLookupKeySuffix(mediaQuery, pseudoClass, pseudoElement, mapping.breakpoints);
1848
+ const suffix = typographyLookupKeySuffix(mediaQuery, pseudoClass, pseudoElement, whenPseudo, mapping.breakpoints);
1775
1849
  const lookupKey = suffix ? `typography__${suffix}` : "typography";
1776
1850
  const segmentsByName = {};
1777
1851
  for (const name of typography) {
1778
- segmentsByName[name] = resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement);
1852
+ segmentsByName[name] = resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo);
1779
1853
  }
1780
1854
  return [
1781
1855
  {
@@ -1789,7 +1863,7 @@ function resolveTypographyCall(node, mapping, mediaQuery, pseudoClass, pseudoEle
1789
1863
  }
1790
1864
  ];
1791
1865
  }
1792
- function resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement) {
1866
+ function resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
1793
1867
  if (!(mapping.typography ?? []).includes(name)) {
1794
1868
  throw new UnsupportedPatternError(`Unknown typography abbreviation "${name}"`);
1795
1869
  }
@@ -1797,21 +1871,24 @@ function resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoEl
1797
1871
  if (!entry) {
1798
1872
  throw new UnsupportedPatternError(`Unknown typography abbreviation "${name}"`);
1799
1873
  }
1800
- const resolved = resolveEntry(name, entry, mapping, mediaQuery, pseudoClass, pseudoElement, null);
1874
+ const resolved = resolveEntry(name, entry, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo);
1801
1875
  for (const segment of resolved) {
1802
- if (segment.variableProps || segment.whenPseudo) {
1876
+ if (segment.variableProps) {
1803
1877
  throw new UnsupportedPatternError(`Typography abbreviation "${name}" cannot require runtime arguments`);
1804
1878
  }
1805
1879
  }
1806
1880
  return resolved;
1807
1881
  }
1808
1882
  function resolveEntry(abbr, entry, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
1883
+ const context = {
1884
+ mediaQuery,
1885
+ pseudoClass,
1886
+ pseudoElement,
1887
+ whenPseudo
1888
+ };
1809
1889
  switch (entry.kind) {
1810
1890
  case "static": {
1811
- if (whenPseudo) {
1812
- return [{ abbr, defs: entry.defs, whenPseudo }];
1813
- }
1814
- return [{ abbr, defs: entry.defs, mediaQuery, pseudoClass, pseudoElement }];
1891
+ return [segmentWithConditionContext({ abbr, defs: entry.defs }, context)];
1815
1892
  }
1816
1893
  case "alias": {
1817
1894
  const result = [];
@@ -1874,40 +1951,32 @@ function resolveDelegateCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
1874
1951
  }
1875
1952
  function buildParameterizedSegment(params) {
1876
1953
  const { abbr, props, incremented, appendPx, extraDefs, argAst, literalValue, whenPseudo } = params;
1954
+ const context = {
1955
+ mediaQuery: params.mediaQuery,
1956
+ pseudoClass: params.pseudoClass,
1957
+ pseudoElement: params.pseudoElement,
1958
+ whenPseudo
1959
+ };
1877
1960
  if (literalValue !== null) {
1878
1961
  const defs = {};
1879
1962
  for (const prop of props) {
1880
1963
  defs[prop] = literalValue;
1881
1964
  }
1882
1965
  if (extraDefs) Object.assign(defs, extraDefs);
1883
- if (whenPseudo) {
1884
- return { abbr, defs, whenPseudo, argResolved: literalValue };
1885
- }
1886
- return {
1887
- abbr,
1888
- defs,
1889
- mediaQuery: params.mediaQuery,
1890
- pseudoClass: params.pseudoClass,
1891
- pseudoElement: params.pseudoElement,
1892
- argResolved: literalValue
1893
- };
1966
+ return segmentWithConditionContext({ abbr, defs, argResolved: literalValue }, context);
1894
1967
  }
1895
- const base = {
1896
- abbr,
1897
- defs: {},
1898
- variableProps: props,
1899
- incremented,
1900
- variableExtraDefs: extraDefs,
1901
- argNode: argAst
1902
- };
1968
+ const base = segmentWithConditionContext(
1969
+ {
1970
+ abbr,
1971
+ defs: {},
1972
+ variableProps: props,
1973
+ incremented,
1974
+ variableExtraDefs: extraDefs,
1975
+ argNode: argAst
1976
+ },
1977
+ context
1978
+ );
1903
1979
  if (appendPx) base.appendPx = true;
1904
- if (whenPseudo) {
1905
- base.whenPseudo = whenPseudo;
1906
- } else {
1907
- base.mediaQuery = params.mediaQuery;
1908
- base.pseudoClass = params.pseudoClass;
1909
- base.pseudoElement = params.pseudoElement;
1910
- }
1911
1980
  return base;
1912
1981
  }
1913
1982
  function resolveClassNameCall(node, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
@@ -1930,6 +1999,25 @@ function resolveClassNameCall(node, mediaQuery, pseudoClass, pseudoElement, when
1930
1999
  classNameArg: arg
1931
2000
  };
1932
2001
  }
2002
+ function resolveStyleCall(node, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
2003
+ if (node.args.length !== 1) {
2004
+ throw new UnsupportedPatternError(`style() expects exactly 1 argument, got ${node.args.length}`);
2005
+ }
2006
+ const arg = node.args[0];
2007
+ if (arg.type === "SpreadElement") {
2008
+ throw new UnsupportedPatternError(`style() does not support spread arguments`);
2009
+ }
2010
+ if (mediaQuery || pseudoClass || pseudoElement || whenPseudo) {
2011
+ throw new UnsupportedPatternError(
2012
+ `style() cannot be used inside media query, pseudo-class, pseudo-element, or when() contexts`
2013
+ );
2014
+ }
2015
+ return {
2016
+ abbr: "style",
2017
+ defs: {},
2018
+ styleArg: arg
2019
+ };
2020
+ }
1933
2021
  function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
1934
2022
  const isAddCss = node.name === "addCss";
1935
2023
  if (isAddCss) {
@@ -1977,34 +2065,28 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
1977
2065
  const propName = propArg.value;
1978
2066
  const valueArg = node.args[1];
1979
2067
  const literalValue = tryEvaluateAddLiteral(valueArg);
1980
- if (whenPseudo) {
1981
- if (literalValue !== null) {
1982
- return { abbr: propName, defs: { [propName]: literalValue }, whenPseudo, argResolved: literalValue };
1983
- } else {
1984
- return { abbr: propName, defs: {}, whenPseudo, variableProps: [propName], incremented: false, argNode: valueArg };
1985
- }
1986
- }
2068
+ const context = {
2069
+ mediaQuery,
2070
+ pseudoClass,
2071
+ pseudoElement,
2072
+ whenPseudo
2073
+ };
1987
2074
  if (literalValue !== null) {
1988
- return {
1989
- abbr: propName,
1990
- defs: { [propName]: literalValue },
1991
- mediaQuery,
1992
- pseudoClass,
1993
- pseudoElement,
1994
- argResolved: literalValue
1995
- };
1996
- } else {
1997
- return {
2075
+ return segmentWithConditionContext(
2076
+ { abbr: propName, defs: { [propName]: literalValue }, argResolved: literalValue },
2077
+ context
2078
+ );
2079
+ }
2080
+ return segmentWithConditionContext(
2081
+ {
1998
2082
  abbr: propName,
1999
2083
  defs: {},
2000
- mediaQuery,
2001
- pseudoClass,
2002
- pseudoElement,
2003
2084
  variableProps: [propName],
2004
2085
  incremented: false,
2005
2086
  argNode: valueArg
2006
- };
2007
- }
2087
+ },
2088
+ context
2089
+ );
2008
2090
  }
2009
2091
  function tryEvaluateAddLiteral(node) {
2010
2092
  if (node.type === "StringLiteral") {
@@ -2509,6 +2591,7 @@ function buildStyleHashMembers(segments, options) {
2509
2591
  const members = [];
2510
2592
  const normalSegs = [];
2511
2593
  const classNameArgs = [];
2594
+ const styleKeyCounts = /* @__PURE__ */ new Map();
2512
2595
  function flushNormal() {
2513
2596
  if (normalSegs.length > 0) {
2514
2597
  members.push(...buildStyleHashProperties(normalSegs, options.mapping, options.maybeIncHelperName));
@@ -2521,6 +2604,11 @@ function buildStyleHashMembers(segments, options) {
2521
2604
  classNameArgs.push(t3.cloneNode(seg.classNameArg, true));
2522
2605
  continue;
2523
2606
  }
2607
+ if (seg.styleArg) {
2608
+ flushNormal();
2609
+ members.push(buildInlineStyleMember(seg.styleArg, styleKeyCounts));
2610
+ continue;
2611
+ }
2524
2612
  if (seg.styleArrayArg) {
2525
2613
  flushNormal();
2526
2614
  if (seg.isAddCss && t3.isObjectExpression(seg.styleArrayArg)) {
@@ -2543,7 +2631,7 @@ function buildStyleHashMembers(segments, options) {
2543
2631
  }
2544
2632
  continue;
2545
2633
  }
2546
- if (options.debug && !seg.classNameArg && !seg.styleArrayArg && !seg.typographyLookup) {
2634
+ if (options.debug && !seg.classNameArg && !seg.styleArg && !seg.styleArrayArg && !seg.typographyLookup) {
2547
2635
  const isMultiProp = Object.keys(seg.defs).length > 1;
2548
2636
  const hasExtraDefs = seg.variableExtraDefs && Object.keys(seg.variableExtraDefs).length > 0;
2549
2637
  if (isMultiProp || hasExtraDefs) {
@@ -2561,14 +2649,21 @@ function buildStyleHashMembers(segments, options) {
2561
2649
  function buildCustomClassNameMembers(classNameArgs) {
2562
2650
  const counts = /* @__PURE__ */ new Map();
2563
2651
  return classNameArgs.map((arg) => {
2564
- const baseKey = `className_${sanitizeClassNameKey(arg)}`;
2652
+ const baseKey = `className_${sanitizeMetadataKey(arg)}`;
2565
2653
  const count = (counts.get(baseKey) ?? 0) + 1;
2566
2654
  counts.set(baseKey, count);
2567
2655
  const key = count === 1 ? baseKey : `${baseKey}_${count}`;
2568
2656
  return t3.objectProperty(t3.identifier(key), t3.cloneNode(arg, true));
2569
2657
  });
2570
2658
  }
2571
- function sanitizeClassNameKey(arg) {
2659
+ function buildInlineStyleMember(arg, counts) {
2660
+ const baseKey = `style_${sanitizeMetadataKey(arg)}`;
2661
+ const count = (counts.get(baseKey) ?? 0) + 1;
2662
+ counts.set(baseKey, count);
2663
+ const key = count === 1 ? baseKey : `${baseKey}_${count}`;
2664
+ return t3.objectProperty(t3.identifier(key), t3.cloneNode(arg, true));
2665
+ }
2666
+ function sanitizeMetadataKey(arg) {
2572
2667
  const raw = t3.isStringLiteral(arg) ? arg.value : t3.isTemplateLiteral(arg) && arg.expressions.length === 0 && arg.quasis.length === 1 ? arg.quasis[0].value.cooked ?? "" : generate(arg).code;
2573
2668
  const sanitized = raw.replace(/[^a-zA-Z0-9_$]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
2574
2669
  return sanitized || "value";
@@ -2604,7 +2699,7 @@ function buildAddCssObjectMembers(styleObject) {
2604
2699
  function collectConditionalOnlyProps(segments) {
2605
2700
  const allProps = /* @__PURE__ */ new Map();
2606
2701
  for (const seg of segments) {
2607
- if (seg.error || seg.styleArrayArg || seg.typographyLookup || seg.classNameArg) continue;
2702
+ if (seg.error || seg.styleArrayArg || seg.typographyLookup || seg.classNameArg || seg.styleArg) continue;
2608
2703
  const hasCondition = !!(seg.pseudoClass || seg.mediaQuery || seg.pseudoElement || seg.whenPseudo);
2609
2704
  const props = seg.variableProps ?? Object.keys(seg.defs);
2610
2705
  for (const prop of props) {
@@ -2698,7 +2793,7 @@ function clonePropertyKey(key) {
2698
2793
  function injectDebugInfo(expr, line, options) {
2699
2794
  if (!options.debug) return;
2700
2795
  const firstProp = expr.properties.find((p) => {
2701
- return t3.isObjectProperty(p) && !(t3.isIdentifier(p.key) && p.key.name.startsWith("className_") || t3.isStringLiteral(p.key) && p.key.value.startsWith("className_") || t3.isIdentifier(p.key) && p.key.name === "__marker" || t3.isStringLiteral(p.key) && p.key.value === "__marker");
2796
+ return t3.isObjectProperty(p) && !(t3.isIdentifier(p.key) && p.key.name.startsWith("className_") || t3.isStringLiteral(p.key) && p.key.value.startsWith("className_") || t3.isIdentifier(p.key) && p.key.name.startsWith("style_") || t3.isStringLiteral(p.key) && p.key.value.startsWith("style_") || t3.isIdentifier(p.key) && p.key.name === "__marker" || t3.isStringLiteral(p.key) && p.key.value === "__marker");
2702
2797
  });
2703
2798
  if (!firstProp) return;
2704
2799
  options.needsTrussDebugInfo.current = true;
@@ -3261,6 +3356,9 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
3261
3356
  if (seg.styleArrayArg) {
3262
3357
  return { error: `add(cssProp) is not supported in .css.ts files` };
3263
3358
  }
3359
+ if (seg.styleArg) {
3360
+ return { error: `style() is not supported in .css.ts files` };
3361
+ }
3264
3362
  if (seg.mediaQuery) {
3265
3363
  return { error: `media query modifiers (ifSm, ifMd, etc.) are not supported in .css.ts files` };
3266
3364
  }