@homebound/truss 2.21.6 → 2.23.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.
@@ -1725,6 +1725,9 @@ function buildRuntimeLookupDeclaration(lookupName, segmentsByName, mapping) {
1725
1725
  // src/plugin/transform-css.ts
1726
1726
  import * as t5 from "@babel/types";
1727
1727
 
1728
+ // src/plugin/resolve-chain.ts
1729
+ import { pascalCase } from "change-case";
1730
+
1728
1731
  // src/media-query.ts
1729
1732
  function invertMediaQuery(query) {
1730
1733
  const screenPrefix = "@media screen and ";
@@ -1820,7 +1823,7 @@ function applyModifierNodeToConditionContext(context, node, mapping) {
1820
1823
  }
1821
1824
  const resolved = resolveWhenCall(node);
1822
1825
  if (resolved.kind === "selector") {
1823
- context.pseudoClass = resolved.pseudo;
1826
+ context.pseudoClass = resolved.selector;
1824
1827
  } else {
1825
1828
  context.whenPseudo = resolved;
1826
1829
  }
@@ -2149,6 +2152,18 @@ function resolveChain(ctx, chain) {
2149
2152
  segments.push(seg);
2150
2153
  continue;
2151
2154
  }
2155
+ if (abbr === "setVar") {
2156
+ const segs = resolveSetVarCall(
2157
+ node,
2158
+ mapping,
2159
+ context.mediaQuery,
2160
+ context.pseudoClass,
2161
+ context.pseudoElement,
2162
+ context.whenPseudo
2163
+ );
2164
+ segments.push(...segs);
2165
+ continue;
2166
+ }
2152
2167
  if (abbr === "typography") {
2153
2168
  const resolved = resolveTypographyCall(
2154
2169
  node,
@@ -2546,11 +2561,11 @@ function resolveWhenCall(node) {
2546
2561
  );
2547
2562
  }
2548
2563
  if (node.args.length === 1) {
2549
- const pseudoArg2 = node.args[0];
2550
- if (pseudoArg2.type !== "StringLiteral") {
2564
+ const selectorArg = node.args[0];
2565
+ if (selectorArg.type !== "StringLiteral") {
2551
2566
  throw new UnsupportedPatternError(`when() selector must be a string literal`);
2552
2567
  }
2553
- return { kind: "selector", pseudo: pseudoArg2.value };
2568
+ return { kind: "selector", selector: selectorArg.value };
2554
2569
  }
2555
2570
  const markerArg = node.args[0];
2556
2571
  const markerNode = resolveWhenMarker(markerArg);
@@ -2697,6 +2712,245 @@ function stringLiteralValue(node, errorMessage) {
2697
2712
  }
2698
2713
  throw new UnsupportedPatternError(errorMessage);
2699
2714
  }
2715
+ function mediaQueryForBreakpointName(mapping, bpName) {
2716
+ if (!mapping.breakpoints) return null;
2717
+ const key = `if${pascalCase(bpName)}`;
2718
+ return mapping.breakpoints[key] ?? null;
2719
+ }
2720
+ function setVarClassBaseFromCssVarName(cssVarName) {
2721
+ const body = (cssVarName.startsWith("--") ? cssVarName.slice(2) : cssVarName).replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
2722
+ return `__${body}`;
2723
+ }
2724
+ function containerQueryStringFromBounds(name, gt, lt) {
2725
+ const parts = [];
2726
+ if (gt !== void 0) {
2727
+ parts.push(`(min-width: ${gt + 1}px)`);
2728
+ }
2729
+ if (lt !== void 0) {
2730
+ parts.push(`(max-width: ${lt}px)`);
2731
+ }
2732
+ const query = parts.join(" and ");
2733
+ const namePrefix = name ? `${name} ` : "";
2734
+ return `@container ${namePrefix}${query}`;
2735
+ }
2736
+ function resolveSetVarPropertyKey(prop, mapping) {
2737
+ const key = prop.key;
2738
+ if (!prop.computed) {
2739
+ if (key.type === "StringLiteral") {
2740
+ if (key.value.startsWith("--")) {
2741
+ return key.value;
2742
+ }
2743
+ throw new UnsupportedPatternError(
2744
+ `setVar() string keys must be CSS variables starting with "--" - got ${JSON.stringify(key.value)}`
2745
+ );
2746
+ }
2747
+ if (key.type === "Identifier") {
2748
+ throw new UnsupportedPatternError(
2749
+ `setVar() requires computed keys like [Tokens.Name] or string keys "--my-var", not bare property names`
2750
+ );
2751
+ }
2752
+ throw new UnsupportedPatternError(`setVar() property keys must be string literals or [Tokens.*] members`);
2753
+ }
2754
+ if (key.type === "MemberExpression") {
2755
+ const mem = key;
2756
+ const memberName = !mem.computed && mem.property.type === "Identifier" ? mem.property.name : mem.computed && mem.property.type === "StringLiteral" ? mem.property.value : null;
2757
+ if (memberName == null) {
2758
+ throw new UnsupportedPatternError(
2759
+ `setVar() [Tokens.name] keys must use a plain .member or ["string"] member access`
2760
+ );
2761
+ }
2762
+ const tokenMap = mapping.tokens;
2763
+ if (!tokenMap || !(memberName in tokenMap)) {
2764
+ throw new UnsupportedPatternError(
2765
+ tokenMap ? `Unknown token "${memberName}" - add it to config.tokens or use a "--" string literal key` : `setVar() [Tokens.*] requires config.tokens; use "--" string literal keys only`
2766
+ );
2767
+ }
2768
+ return tokenMap[memberName];
2769
+ }
2770
+ throw new UnsupportedPatternError(`setVar() computed keys must be Tokens.*-style members`);
2771
+ }
2772
+ function expandSetVarValueToLeaves(valueNode, mapping, baseCtx) {
2773
+ const unwrapped = unwrapExpression(valueNode);
2774
+ const scalar = tryEvaluateAddLiteral(unwrapped);
2775
+ if (scalar !== null) {
2776
+ return [{ literal: scalar, ctx: cloneConditionContext(baseCtx) }];
2777
+ }
2778
+ if (unwrapped.type !== "ObjectExpression") {
2779
+ throw new UnsupportedPatternError(
2780
+ `setVar() values must be string/number literals or a { default?, media?, container? } object`
2781
+ );
2782
+ }
2783
+ let defaultLit;
2784
+ let mediaObj;
2785
+ let containerArr;
2786
+ for (const prop of unwrapped.properties) {
2787
+ if (prop.type === "SpreadElement") {
2788
+ throw new UnsupportedPatternError(`setVar() responsive object does not support spread properties`);
2789
+ }
2790
+ if (prop.type !== "ObjectProperty" || prop.computed) {
2791
+ throw new UnsupportedPatternError(`setVar() responsive object only supports plain data properties`);
2792
+ }
2793
+ const name = objectPropertyName(prop.key);
2794
+ if (!name) {
2795
+ throw new UnsupportedPatternError(`setVar() responsive object: invalid property key`);
2796
+ }
2797
+ if (name === "default") {
2798
+ const v = tryEvaluateAddLiteral(unwrapExpression(prop.value));
2799
+ if (v === null) {
2800
+ throw new UnsupportedPatternError(`setVar().default must be a string or number literal`);
2801
+ }
2802
+ defaultLit = v;
2803
+ continue;
2804
+ }
2805
+ if (name === "media") {
2806
+ if (prop.value.type !== "ObjectExpression") {
2807
+ throw new UnsupportedPatternError(`setVar().media must be an object literal`);
2808
+ }
2809
+ mediaObj = prop.value;
2810
+ continue;
2811
+ }
2812
+ if (name === "container") {
2813
+ if (prop.value.type !== "ArrayExpression") {
2814
+ throw new UnsupportedPatternError(`setVar().container must be an array literal`);
2815
+ }
2816
+ containerArr = prop.value;
2817
+ continue;
2818
+ }
2819
+ throw new UnsupportedPatternError(`setVar() responsive object does not support property "${name}"`);
2820
+ }
2821
+ const leaves = [];
2822
+ if (defaultLit !== void 0) {
2823
+ leaves.push({ literal: defaultLit, ctx: cloneConditionContext(baseCtx) });
2824
+ }
2825
+ if (mediaObj) {
2826
+ for (const mprop of mediaObj.properties) {
2827
+ if (mprop.type === "SpreadElement") {
2828
+ throw new UnsupportedPatternError(`setVar().media does not support spread properties`);
2829
+ }
2830
+ if (mprop.type !== "ObjectProperty" || mprop.computed) {
2831
+ throw new UnsupportedPatternError(`setVar().media only supports plain identifier or string keys`);
2832
+ }
2833
+ const bpName = objectPropertyName(mprop.key);
2834
+ if (!bpName) {
2835
+ throw new UnsupportedPatternError(`setVar().media: invalid breakpoint key`);
2836
+ }
2837
+ const mq = mediaQueryForBreakpointName(mapping, bpName);
2838
+ if (!mq) {
2839
+ throw new UnsupportedPatternError(
2840
+ `Unknown breakpoint "${bpName}" in setVar().media - use a Breakpoint name from truss-config`
2841
+ );
2842
+ }
2843
+ const v = tryEvaluateAddLiteral(unwrapExpression(mprop.value));
2844
+ if (v === null) {
2845
+ throw new UnsupportedPatternError(`setVar().media[${bpName}] must be a string or number literal`);
2846
+ }
2847
+ const ctx = cloneConditionContext(baseCtx);
2848
+ ctx.mediaQuery = mq;
2849
+ leaves.push({ literal: v, ctx });
2850
+ }
2851
+ }
2852
+ if (containerArr) {
2853
+ for (const elt of containerArr.elements) {
2854
+ if (elt === null) {
2855
+ continue;
2856
+ }
2857
+ if (elt.type !== "ObjectExpression") {
2858
+ throw new UnsupportedPatternError(`setVar().container entries must be object literals`);
2859
+ }
2860
+ let rowValue;
2861
+ let cname;
2862
+ let lt;
2863
+ let gt;
2864
+ for (const rprop of elt.properties) {
2865
+ if (rprop.type === "SpreadElement") {
2866
+ throw new UnsupportedPatternError(`setVar().container row does not support spread`);
2867
+ }
2868
+ if (rprop.type !== "ObjectProperty" || rprop.computed) {
2869
+ throw new UnsupportedPatternError(`setVar().container row: use plain properties only`);
2870
+ }
2871
+ const rk = objectPropertyName(rprop.key);
2872
+ if (!rk) {
2873
+ continue;
2874
+ }
2875
+ const rv = rprop.value;
2876
+ if (rk === "value") {
2877
+ const lit = tryEvaluateAddLiteral(unwrapExpression(rv));
2878
+ if (lit === null) {
2879
+ throw new UnsupportedPatternError(`setVar().container row "value" must be a string or number literal`);
2880
+ }
2881
+ rowValue = lit;
2882
+ } else if (rk === "name") {
2883
+ cname = stringLiteralValue(rv, `setVar().container name must be a string literal`);
2884
+ } else if (rk === "lt") {
2885
+ lt = numericLiteralValue(rv, `setVar().container lt must be a numeric literal`);
2886
+ } else if (rk === "gt") {
2887
+ gt = numericLiteralValue(rv, `setVar().container gt must be a numeric literal`);
2888
+ } else {
2889
+ throw new UnsupportedPatternError(`setVar().container row does not support property "${rk}"`);
2890
+ }
2891
+ }
2892
+ if (rowValue === void 0) {
2893
+ throw new UnsupportedPatternError(`setVar().container row requires a "value" property`);
2894
+ }
2895
+ if (lt === void 0 && gt === void 0) {
2896
+ throw new UnsupportedPatternError(`setVar().container row requires at least one of gt or lt`);
2897
+ }
2898
+ const containerMq = containerQueryStringFromBounds(cname, gt, lt);
2899
+ const ctx = cloneConditionContext(baseCtx);
2900
+ ctx.mediaQuery = containerMq;
2901
+ leaves.push({ literal: rowValue, ctx });
2902
+ }
2903
+ }
2904
+ if (leaves.length === 0) {
2905
+ throw new UnsupportedPatternError(
2906
+ `setVar() responsive object must include at least one of default, media entries, or container entries`
2907
+ );
2908
+ }
2909
+ return leaves;
2910
+ }
2911
+ function resolveSetVarCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
2912
+ if (node.args.length !== 1) {
2913
+ throw new UnsupportedPatternError(`setVar() requires exactly 1 argument (an object literal)`);
2914
+ }
2915
+ const arg = node.args[0];
2916
+ if (arg.type === "SpreadElement") {
2917
+ throw new UnsupportedPatternError(`setVar() does not support spread arguments`);
2918
+ }
2919
+ if (arg.type !== "ObjectExpression") {
2920
+ throw new UnsupportedPatternError(`setVar() requires an object literal argument`);
2921
+ }
2922
+ const baseContext = {
2923
+ mediaQuery,
2924
+ pseudoClass,
2925
+ pseudoElement,
2926
+ whenPseudo
2927
+ };
2928
+ const segments = [];
2929
+ for (const prop of arg.properties) {
2930
+ if (prop.type === "SpreadElement") {
2931
+ throw new UnsupportedPatternError(`setVar() does not support spread properties`);
2932
+ }
2933
+ if (prop.type !== "ObjectProperty") {
2934
+ throw new UnsupportedPatternError(`setVar() only supports object properties`);
2935
+ }
2936
+ const cssVarName = resolveSetVarPropertyKey(prop, mapping);
2937
+ const leaves = expandSetVarValueToLeaves(prop.value, mapping, baseContext);
2938
+ for (const leaf of leaves) {
2939
+ const abbr = setVarClassBaseFromCssVarName(cssVarName);
2940
+ segments.push(
2941
+ segmentWithConditionContext(
2942
+ {
2943
+ abbr,
2944
+ defs: { [cssVarName]: leaf.literal },
2945
+ argResolved: leaf.literal
2946
+ },
2947
+ leaf.ctx
2948
+ )
2949
+ );
2950
+ }
2951
+ }
2952
+ return segments;
2953
+ }
2700
2954
  var UnsupportedPatternError = class extends Error {
2701
2955
  constructor(message) {
2702
2956
  super(`[truss] Unsupported pattern: ${message}`);