@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.
- package/build/index.d.ts +11 -1
- package/build/index.js +52 -3
- package/build/index.js.map +1 -1
- package/build/plugin/index.d.ts +2 -0
- package/build/plugin/index.js +258 -4
- package/build/plugin/index.js.map +1 -1
- package/package.json +1 -1
package/build/plugin/index.js
CHANGED
|
@@ -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.
|
|
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
|
|
2550
|
-
if (
|
|
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",
|
|
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}`);
|