@homebound/truss 2.24.0 → 2.25.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.
@@ -1360,6 +1360,19 @@ function normalizePseudoIdentifier(pseudo) {
1360
1360
  return `${prefix}${name}`;
1361
1361
  }
1362
1362
 
1363
+ // src/css-custom-property.ts
1364
+ function maybeCssVar(value) {
1365
+ if (typeof value !== "string") return value;
1366
+ if (value.startsWith("--")) return `var(${value})`;
1367
+ return value;
1368
+ }
1369
+ function variableValueNeedsMaybeCssVar(opts) {
1370
+ return !opts.appendPx;
1371
+ }
1372
+ function isCustomPropertyName(value) {
1373
+ return value.startsWith("--");
1374
+ }
1375
+
1363
1376
  // src/spacing-css-var.ts
1364
1377
  var SPACING_CUSTOM_PROPERTY = "--t-spacing";
1365
1378
  function incrementCssValue(multiplier) {
@@ -1458,6 +1471,7 @@ function computeStaticBaseName(seg, cssProp, cssValue, isMultiProp, mapping) {
1458
1471
  function collectAtomicRules(chains, mapping) {
1459
1472
  const rules = /* @__PURE__ */ new Map();
1460
1473
  let needsMaybeInc = false;
1474
+ let needsMaybeCssVar = false;
1461
1475
  function collectSegment(seg) {
1462
1476
  if (seg.error || seg.styleArrayArg || seg.classNameArg || seg.styleArg) return;
1463
1477
  if (seg.typographyLookup) {
@@ -1469,6 +1483,9 @@ function collectAtomicRules(chains, mapping) {
1469
1483
  return;
1470
1484
  }
1471
1485
  if (seg.incremented) needsMaybeInc = true;
1486
+ if (seg.variableProps && seg.argResolved === void 0 && variableValueNeedsMaybeCssVar(seg)) {
1487
+ needsMaybeCssVar = true;
1488
+ }
1472
1489
  collectSegmentRules(rules, seg, mapping);
1473
1490
  }
1474
1491
  for (const chain of chains) {
@@ -1479,7 +1496,7 @@ function collectAtomicRules(chains, mapping) {
1479
1496
  }
1480
1497
  }
1481
1498
  }
1482
- return { rules, needsMaybeInc };
1499
+ return { rules, needsMaybeInc, needsMaybeCssVar };
1483
1500
  }
1484
1501
  function segmentContext(seg, mapping) {
1485
1502
  const prefix = `${conditionPrefix(seg.pseudoClass, seg.mediaQuery, seg.pseudoElement, mapping.breakpoints)}${seg.whenPseudo ? whenPrefix(seg.whenPseudo) : ""}`;
@@ -1565,6 +1582,7 @@ function variableStyleEntries(seg, mapping, prefix, isConditional) {
1565
1582
  cssValue: `var(${varName})`,
1566
1583
  varName,
1567
1584
  argNode: seg.argNode,
1585
+ argResolved: seg.argResolved,
1568
1586
  incremented: seg.incremented,
1569
1587
  appendPx: seg.appendPx
1570
1588
  });
@@ -1654,7 +1672,7 @@ function formatNestedRuleBlock(wrapper, selector, rule) {
1654
1672
  }).join(" ");
1655
1673
  return `${wrapper} { ${selector} { ${body} } }`;
1656
1674
  }
1657
- function buildStyleHashProperties(segments, mapping, maybeIncHelperName) {
1675
+ function buildStyleHashProperties(segments, mapping, maybeIncHelperName, maybeCssVarHelperName) {
1658
1676
  const propGroups = /* @__PURE__ */ new Map();
1659
1677
  function pushEntry(entry) {
1660
1678
  const cssProp = entry.cssProp;
@@ -1682,14 +1700,22 @@ function buildStyleHashProperties(segments, mapping, maybeIncHelperName) {
1682
1700
  if (variableEntries.length > 0) {
1683
1701
  const varsProps = [];
1684
1702
  for (const dyn of variableEntries) {
1685
- let valueExpr = dyn.argNode;
1686
- if (dyn.incremented) {
1687
- valueExpr = t3.callExpression(t3.identifier(maybeIncHelperName ?? "__maybeInc"), [valueExpr]);
1688
- } else if (dyn.appendPx) {
1689
- valueExpr = t3.templateLiteral(
1690
- [t3.templateElement({ raw: "", cooked: "" }, false), t3.templateElement({ raw: "px", cooked: "px" }, true)],
1691
- [valueExpr]
1692
- );
1703
+ let valueExpr;
1704
+ if (dyn.argResolved !== void 0) {
1705
+ valueExpr = t3.stringLiteral(dyn.argResolved);
1706
+ } else {
1707
+ valueExpr = dyn.argNode;
1708
+ if (dyn.incremented) {
1709
+ valueExpr = t3.callExpression(t3.identifier(maybeIncHelperName ?? "__maybeInc"), [valueExpr]);
1710
+ } else if (dyn.appendPx) {
1711
+ valueExpr = t3.templateLiteral(
1712
+ [t3.templateElement({ raw: "", cooked: "" }, false), t3.templateElement({ raw: "px", cooked: "px" }, true)],
1713
+ [valueExpr]
1714
+ );
1715
+ }
1716
+ if (maybeCssVarHelperName && variableValueNeedsMaybeCssVar(dyn)) {
1717
+ valueExpr = t3.callExpression(t3.identifier(maybeCssVarHelperName), [valueExpr]);
1718
+ }
1693
1719
  }
1694
1720
  varsProps.push(t3.objectProperty(t3.stringLiteral(dyn.varName), valueExpr));
1695
1721
  }
@@ -2340,7 +2366,7 @@ function resolveVariableCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
2340
2366
  if (node.args.length !== 1) {
2341
2367
  throw new UnsupportedPatternError(`${abbr}() expects exactly 1 argument, got ${node.args.length}`);
2342
2368
  }
2343
- const literalValue = tryEvaluateLiteral(node.args[0], entry.incremented);
2369
+ const literalValue = tryEvaluatePropertyLiteral(node.args[0], mapping, entry.incremented);
2344
2370
  return buildParameterizedSegment({
2345
2371
  abbr,
2346
2372
  props: entry.props,
@@ -2348,6 +2374,7 @@ function resolveVariableCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
2348
2374
  extraDefs: entry.extraDefs,
2349
2375
  argAst: node.args[0],
2350
2376
  literalValue,
2377
+ mapping,
2351
2378
  mediaQuery,
2352
2379
  pseudoClass,
2353
2380
  pseudoElement,
@@ -2371,6 +2398,7 @@ function resolveDelegateCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
2371
2398
  extraDefs: targetEntry.extraDefs,
2372
2399
  argAst: node.args[0],
2373
2400
  literalValue,
2401
+ mapping,
2374
2402
  mediaQuery,
2375
2403
  pseudoClass,
2376
2404
  pseudoElement,
@@ -2378,14 +2406,44 @@ function resolveDelegateCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
2378
2406
  });
2379
2407
  }
2380
2408
  function buildParameterizedSegment(params) {
2381
- const { abbr, props, incremented, appendPx, extraDefs, argAst, literalValue, whenPseudo } = params;
2409
+ const { abbr, props, incremented, appendPx, extraDefs, argAst, literalValue, mapping, whenPseudo } = params;
2382
2410
  const context = {
2383
2411
  mediaQuery: params.mediaQuery,
2384
2412
  pseudoClass: params.pseudoClass,
2385
2413
  pseudoElement: params.pseudoElement,
2386
2414
  whenPseudo
2387
2415
  };
2416
+ return resolveLiteralOrVariableSegment({
2417
+ abbr,
2418
+ props,
2419
+ incremented,
2420
+ appendPx,
2421
+ extraDefs,
2422
+ argAst,
2423
+ literalValue,
2424
+ mapping,
2425
+ context
2426
+ });
2427
+ }
2428
+ function resolveLiteralOrVariableSegment(params) {
2429
+ const { abbr, props, incremented, appendPx, extraDefs, argAst, literalValue, mapping, context } = params;
2388
2430
  if (literalValue !== null) {
2431
+ const raw = tryResolveValueLiteral(argAst, mapping);
2432
+ if (raw !== null && isCustomPropertyName(raw)) {
2433
+ const base2 = segmentWithConditionContext(
2434
+ {
2435
+ abbr,
2436
+ defs: {},
2437
+ variableProps: props,
2438
+ incremented,
2439
+ variableExtraDefs: extraDefs,
2440
+ argResolved: literalValue
2441
+ },
2442
+ context
2443
+ );
2444
+ if (appendPx) base2.appendPx = true;
2445
+ return base2;
2446
+ }
2389
2447
  const defs = {};
2390
2448
  for (const prop of props) {
2391
2449
  defs[prop] = literalValue;
@@ -2498,23 +2556,18 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
2498
2556
  }
2499
2557
  const propName = propArg.value;
2500
2558
  const valueArg = node.args[1];
2501
- const literalValue = tryEvaluateAddLiteral(valueArg);
2502
- if (literalValue !== null) {
2503
- return [segmentWithConditionContext(
2504
- { abbr: propName, defs: { [propName]: literalValue }, argResolved: literalValue },
2505
- context
2506
- )];
2507
- }
2508
- return [segmentWithConditionContext(
2509
- {
2559
+ const literalValue = tryEvaluatePropertyLiteral(valueArg, mapping, false);
2560
+ return [
2561
+ resolveLiteralOrVariableSegment({
2510
2562
  abbr: propName,
2511
- defs: {},
2512
- variableProps: [propName],
2563
+ props: [propName],
2513
2564
  incremented: false,
2514
- argNode: valueArg
2515
- },
2516
- context
2517
- )];
2565
+ argAst: valueArg,
2566
+ literalValue,
2567
+ mapping,
2568
+ context
2569
+ })
2570
+ ];
2518
2571
  }
2519
2572
  function resolveAddObjectLiteral(obj, mapping, context) {
2520
2573
  const segments = [];
@@ -2534,21 +2587,31 @@ function resolveAddObjectLiteral(obj, mapping, context) {
2534
2587
  throw new UnsupportedPatternError(`add({...}) property keys must be identifiers or string literals`);
2535
2588
  }
2536
2589
  const valueNode = property.value;
2537
- const literalValue = tryEvaluateAddLiteral(valueNode);
2590
+ const literalValue = tryEvaluatePropertyLiteral(valueNode, mapping, false);
2538
2591
  if (literalValue !== null) {
2539
- const canonicalAbbr = findCanonicalAbbreviation(mapping, propName, literalValue);
2540
- if (canonicalAbbr) {
2541
- const entry = mapping.abbreviations[canonicalAbbr];
2542
- segments.push(segmentWithConditionContext(
2543
- { abbr: canonicalAbbr, defs: entry.defs },
2544
- context
2545
- ));
2546
- } else {
2547
- segments.push(segmentWithConditionContext(
2548
- { abbr: propName, defs: { [propName]: literalValue }, argResolved: literalValue },
2549
- context
2550
- ));
2592
+ const raw = tryResolveValueLiteral(valueNode, mapping);
2593
+ if (raw === null || !isCustomPropertyName(raw)) {
2594
+ const canonicalAbbr = findCanonicalAbbreviation(mapping, propName, literalValue);
2595
+ if (canonicalAbbr) {
2596
+ const entry = mapping.abbreviations[canonicalAbbr];
2597
+ segments.push(segmentWithConditionContext(
2598
+ { abbr: canonicalAbbr, defs: entry.defs },
2599
+ context
2600
+ ));
2601
+ continue;
2602
+ }
2551
2603
  }
2604
+ segments.push(
2605
+ resolveLiteralOrVariableSegment({
2606
+ abbr: propName,
2607
+ props: [propName],
2608
+ incremented: false,
2609
+ argAst: valueNode,
2610
+ literalValue,
2611
+ mapping,
2612
+ context
2613
+ })
2614
+ );
2552
2615
  } else {
2553
2616
  segments.push(segmentWithConditionContext(
2554
2617
  { abbr: propName, defs: {}, variableProps: [propName], incremented: false, argNode: valueNode },
@@ -2561,7 +2624,11 @@ function resolveAddObjectLiteral(obj, mapping, context) {
2561
2624
  function findCanonicalAbbreviation(mapping, prop, value) {
2562
2625
  return getLonghandLookup(mapping).get(`${prop}\0${value}`) ?? null;
2563
2626
  }
2564
- function tryEvaluateAddLiteral(node) {
2627
+ function tryResolveValueLiteral(node, mapping) {
2628
+ if (mapping) {
2629
+ const token = tryResolveTokensMember(node, mapping);
2630
+ if (token !== null) return token;
2631
+ }
2565
2632
  if (node.type === "StringLiteral") {
2566
2633
  return node.value;
2567
2634
  }
@@ -2573,6 +2640,20 @@ function tryEvaluateAddLiteral(node) {
2573
2640
  }
2574
2641
  return null;
2575
2642
  }
2643
+ function tryResolveTokensMember(node, mapping) {
2644
+ if (node.type !== "MemberExpression") return null;
2645
+ if (node.object.type !== "Identifier" || node.object.name !== "Tokens") return null;
2646
+ const memberName = !node.computed && node.property.type === "Identifier" ? node.property.name : node.computed && node.property.type === "StringLiteral" ? node.property.value : null;
2647
+ if (memberName == null) return null;
2648
+ const tokenMap = mapping.tokens;
2649
+ if (!tokenMap) {
2650
+ throw new UnsupportedPatternError(`Tokens.* requires config.tokens`);
2651
+ }
2652
+ if (!(memberName in tokenMap)) {
2653
+ throw new UnsupportedPatternError(`Unknown token "${memberName}" - add it to config.tokens`);
2654
+ }
2655
+ return tokenMap[memberName];
2656
+ }
2576
2657
  var WHEN_RELATIONSHIPS = /* @__PURE__ */ new Set(["ancestor", "descendant", "anySibling", "siblingBefore", "siblingAfter"]);
2577
2658
  function resolveWhenCall(node) {
2578
2659
  if (node.args.length !== 1 && node.args.length !== 3) {
@@ -2623,16 +2704,13 @@ function isDefaultMarkerNode(node) {
2623
2704
  function isLegacyDefaultMarkerExpression(node) {
2624
2705
  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";
2625
2706
  }
2626
- function tryEvaluateLiteral(node, incremented) {
2707
+ function tryEvaluatePropertyLiteral(node, mapping, incremented) {
2627
2708
  if (node.type === "NumericLiteral") {
2628
2709
  if (incremented) {
2629
2710
  return incrementCssValue(node.value);
2630
2711
  }
2631
2712
  return String(node.value);
2632
2713
  }
2633
- if (node.type === "StringLiteral") {
2634
- return node.value;
2635
- }
2636
2714
  if (node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "NumericLiteral") {
2637
2715
  const val = -node.argument.value;
2638
2716
  if (incremented) {
@@ -2640,6 +2718,10 @@ function tryEvaluateLiteral(node, incremented) {
2640
2718
  }
2641
2719
  return String(val);
2642
2720
  }
2721
+ const raw = tryResolveValueLiteral(node, mapping);
2722
+ if (raw !== null) {
2723
+ return maybeCssVar(raw);
2724
+ }
2643
2725
  return null;
2644
2726
  }
2645
2727
  function tryEvaluatePxLiteral(node) {
@@ -2791,7 +2873,7 @@ function resolveSetVarPropertyKey(prop, mapping) {
2791
2873
  }
2792
2874
  function expandSetVarValueToLeaves(valueNode, mapping, baseCtx) {
2793
2875
  const unwrapped = unwrapExpression(valueNode);
2794
- const scalar = tryEvaluateAddLiteral(unwrapped);
2876
+ const scalar = tryResolveValueLiteral(unwrapped);
2795
2877
  if (scalar !== null) {
2796
2878
  return [{ literal: scalar, ctx: cloneConditionContext(baseCtx) }];
2797
2879
  }
@@ -2815,7 +2897,7 @@ function expandSetVarValueToLeaves(valueNode, mapping, baseCtx) {
2815
2897
  throw new UnsupportedPatternError(`setVar() responsive object: invalid property key`);
2816
2898
  }
2817
2899
  if (name === "default") {
2818
- const v = tryEvaluateAddLiteral(unwrapExpression(prop.value));
2900
+ const v = tryResolveValueLiteral(unwrapExpression(prop.value));
2819
2901
  if (v === null) {
2820
2902
  throw new UnsupportedPatternError(`setVar().default must be a string or number literal`);
2821
2903
  }
@@ -2860,7 +2942,7 @@ function expandSetVarValueToLeaves(valueNode, mapping, baseCtx) {
2860
2942
  `Unknown breakpoint "${bpName}" in setVar().media - use a Breakpoint name from truss-config`
2861
2943
  );
2862
2944
  }
2863
- const v = tryEvaluateAddLiteral(unwrapExpression(mprop.value));
2945
+ const v = tryResolveValueLiteral(unwrapExpression(mprop.value));
2864
2946
  if (v === null) {
2865
2947
  throw new UnsupportedPatternError(`setVar().media[${bpName}] must be a string or number literal`);
2866
2948
  }
@@ -2894,7 +2976,7 @@ function expandSetVarValueToLeaves(valueNode, mapping, baseCtx) {
2894
2976
  }
2895
2977
  const rv = rprop.value;
2896
2978
  if (rk === "value") {
2897
- const lit = tryEvaluateAddLiteral(unwrapExpression(rv));
2979
+ const lit = tryResolveValueLiteral(unwrapExpression(rv));
2898
2980
  if (lit === null) {
2899
2981
  throw new UnsupportedPatternError(`setVar().container row "value" must be a string or number literal`);
2900
2982
  }
@@ -3178,6 +3260,12 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
3178
3260
  if (seg.whenPseudo) {
3179
3261
  return { error: `when() modifiers are not supported in .css.ts files` };
3180
3262
  }
3263
+ if (seg.variableProps && seg.argResolved) {
3264
+ for (const prop of seg.variableProps) {
3265
+ declarations.push({ property: camelToKebab(prop), value: seg.argResolved });
3266
+ }
3267
+ continue;
3268
+ }
3181
3269
  for (const [prop, value] of Object.entries(seg.defs)) {
3182
3270
  if (typeof value === "string" || typeof value === "number") {
3183
3271
  declarations.push({ property: camelToKebab(prop), value: String(value) });
@@ -3304,7 +3392,14 @@ function buildStyleHashMembers(segments, options) {
3304
3392
  const styleKeyCounts = /* @__PURE__ */ new Map();
3305
3393
  function flushNormal() {
3306
3394
  if (normalSegs.length > 0) {
3307
- members.push(...buildStyleHashProperties(normalSegs, options.mapping, options.maybeIncHelperName));
3395
+ members.push(
3396
+ ...buildStyleHashProperties(
3397
+ normalSegs,
3398
+ options.mapping,
3399
+ options.maybeIncHelperName,
3400
+ options.maybeCssVarHelperName
3401
+ )
3402
+ );
3308
3403
  normalSegs.length = 0;
3309
3404
  }
3310
3405
  }
@@ -3690,10 +3785,12 @@ function transformTruss(code, filename, mapping, options = {}) {
3690
3785
  });
3691
3786
  if (sites.length === 0 && !hasCssPropsCall && !hasBuildtimeJsxCssAttribute) return null;
3692
3787
  const chains = sites.map((s) => s.resolvedChain);
3693
- const { rules, needsMaybeInc } = collectAtomicRules(chains, mapping);
3788
+ const { rules, needsMaybeInc, needsMaybeCssVar } = collectAtomicRules(chains, mapping);
3694
3789
  const cssText = generateCssText(rules);
3695
3790
  const usedTopLevelNames = collectTopLevelBindings(ast);
3696
3791
  const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
3792
+ const existingMaybeCssVarHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "maybeCssVar");
3793
+ const maybeCssVarHelperName = needsMaybeCssVar ? existingMaybeCssVarHelperName ?? reservePreferredName(usedTopLevelNames, "maybeCssVar") : null;
3697
3794
  const existingMergePropsHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "mergeProps");
3698
3795
  const mergePropsHelperName = existingMergePropsHelperName ?? reservePreferredName(usedTopLevelNames, "mergeProps");
3699
3796
  const needsMergePropsHelper = { current: false };
@@ -3716,6 +3813,7 @@ function transformTruss(code, filename, mapping, options = {}) {
3716
3813
  debug: options.debug ?? false,
3717
3814
  mapping,
3718
3815
  maybeIncHelperName,
3816
+ maybeCssVarHelperName,
3719
3817
  mergePropsHelperName,
3720
3818
  needsMergePropsHelper,
3721
3819
  trussPropsHelperName,
@@ -3734,6 +3832,9 @@ function transformTruss(code, filename, mapping, options = {}) {
3734
3832
  if (needsTrussDebugInfo.current && !existingTrussDebugInfoName) {
3735
3833
  runtimeImports.push({ importedName: "TrussDebugInfo", localName: trussDebugInfoName });
3736
3834
  }
3835
+ if (needsMaybeCssVar && !existingMaybeCssVarHelperName && maybeCssVarHelperName) {
3836
+ runtimeImports.push({ importedName: "maybeCssVar", localName: maybeCssVarHelperName });
3837
+ }
3737
3838
  if (options.injectCss) {
3738
3839
  runtimeImports.push({ importedName: "__injectTrussCSS", localName: "__injectTrussCSS" });
3739
3840
  }