@homebound/truss 2.19.2 → 2.20.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.
@@ -6,6 +6,23 @@ import { createHash } from "crypto";
6
6
  // src/plugin/emit-truss.ts
7
7
  import * as t from "@babel/types";
8
8
 
9
+ // src/plugin/types.ts
10
+ var _longhandCache = /* @__PURE__ */ new WeakMap();
11
+ function getLonghandLookup(mapping) {
12
+ let lookup = _longhandCache.get(mapping);
13
+ if (lookup) return lookup;
14
+ lookup = /* @__PURE__ */ new Map();
15
+ for (const [abbr, entry] of Object.entries(mapping.abbreviations)) {
16
+ if (entry.kind !== "static") continue;
17
+ const keys = Object.keys(entry.defs);
18
+ if (keys.length !== 1) continue;
19
+ const key = `${keys[0]}\0${entry.defs[keys[0]]}`;
20
+ if (!lookup.has(key)) lookup.set(key, abbr);
21
+ }
22
+ _longhandCache.set(mapping, lookup);
23
+ return lookup;
24
+ }
25
+
9
26
  // src/plugin/property-priorities.ts
10
27
  var longHandPhysical = /* @__PURE__ */ new Set();
11
28
  var longHandLogical = /* @__PURE__ */ new Set();
@@ -1067,30 +1084,6 @@ function cleanValueForClassName(value) {
1067
1084
  function getPropertyAbbreviation(cssProp) {
1068
1085
  return cssPropertyAbbreviations[cssProp] ?? cssProp;
1069
1086
  }
1070
- function buildLonghandLookup(mapping) {
1071
- const lookup = /* @__PURE__ */ new Map();
1072
- for (const [abbrev, entry] of Object.entries(mapping.abbreviations)) {
1073
- if (entry.kind !== "static") continue;
1074
- const props = Object.keys(entry.defs);
1075
- if (props.length !== 1) continue;
1076
- const prop = props[0];
1077
- const value = String(entry.defs[prop]);
1078
- const key = `${prop}\0${value}`;
1079
- if (!lookup.has(key)) {
1080
- lookup.set(key, abbrev);
1081
- }
1082
- }
1083
- return lookup;
1084
- }
1085
- var cachedMapping = null;
1086
- var cachedLookup = null;
1087
- function getLonghandLookup(mapping) {
1088
- if (cachedMapping !== mapping) {
1089
- cachedMapping = mapping;
1090
- cachedLookup = buildLonghandLookup(mapping);
1091
- }
1092
- return cachedLookup;
1093
- }
1094
1087
  function computeStaticBaseName(seg, cssProp, cssValue, isMultiProp, mapping) {
1095
1088
  const abbr = seg.abbr;
1096
1089
  if (seg.argResolved !== void 0) {
@@ -1749,7 +1742,9 @@ function applyModifierNodeToConditionContext(context, node, mapping) {
1749
1742
  context.pseudoClass = pseudoSelector(node.name);
1750
1743
  }
1751
1744
  }
1752
- function resolveFullChain(chain, mapping, cssBindingName, initialContext = emptyConditionContext(), resolveCssChainReference2) {
1745
+ function resolveFullChain(ctx, chain) {
1746
+ const { mapping } = ctx;
1747
+ const initialContext = ctx.initialContext ?? emptyConditionContext();
1753
1748
  const parts = [];
1754
1749
  const markers = [];
1755
1750
  const nestedErrors = [];
@@ -1779,7 +1774,7 @@ function resolveFullChain(chain, mapping, cssBindingName, initialContext = empty
1779
1774
  }
1780
1775
  parts.push({
1781
1776
  type: "unconditional",
1782
- segments: resolveChain(currentNodes, mapping, currentNodesStartContext, cssBindingName, resolveCssChainReference2)
1777
+ segments: resolveChain({ ...ctx, initialContext: currentNodesStartContext }, currentNodes)
1783
1778
  });
1784
1779
  currentNodes = [];
1785
1780
  currentNodesStartContext = cloneConditionContext(currentContext);
@@ -1801,8 +1796,8 @@ function resolveFullChain(chain, mapping, cssBindingName, initialContext = empty
1801
1796
  const branchContext = cloneConditionContext(currentContext);
1802
1797
  const thenNodes = mediaStart.thenNodes ? [...mediaStart.thenNodes, ...filteredChain.slice(i + 1, elseIndex)] : filteredChain.slice(i, elseIndex);
1803
1798
  const elseNodes = [makeMediaQueryNode(mediaStart.inverseMediaQuery), ...filteredChain.slice(elseIndex + 1)];
1804
- const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName, resolveCssChainReference2);
1805
- const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName, resolveCssChainReference2);
1799
+ const thenSegs = resolveChain({ ...ctx, initialContext: branchContext }, thenNodes);
1800
+ const elseSegs = resolveChain({ ...ctx, initialContext: branchContext }, elseNodes);
1806
1801
  parts.push({ type: "unconditional", segments: [...thenSegs, ...elseSegs] });
1807
1802
  i = filteredChain.length;
1808
1803
  break;
@@ -1810,13 +1805,7 @@ function resolveFullChain(chain, mapping, cssBindingName, initialContext = empty
1810
1805
  }
1811
1806
  if (isWhenObjectCall(node)) {
1812
1807
  flushCurrentNodes();
1813
- const resolved = resolveWhenObjectSelectors(
1814
- node,
1815
- mapping,
1816
- cssBindingName,
1817
- currentContext,
1818
- resolveCssChainReference2
1819
- );
1808
+ const resolved = resolveWhenObjectSelectors(ctx, node, currentContext);
1820
1809
  parts.push(...resolved.parts);
1821
1810
  markers.push(...resolved.markers);
1822
1811
  nestedErrors.push(...resolved.errors);
@@ -1852,8 +1841,8 @@ function resolveFullChain(chain, mapping, cssBindingName, initialContext = empty
1852
1841
  }
1853
1842
  i++;
1854
1843
  }
1855
- const thenSegs = resolveChain(thenNodes, mapping, branchContext, cssBindingName, resolveCssChainReference2);
1856
- const elseSegs = resolveChain(elseNodes, mapping, branchContext, cssBindingName, resolveCssChainReference2);
1844
+ const thenSegs = resolveChain({ ...ctx, initialContext: branchContext }, thenNodes);
1845
+ const elseSegs = resolveChain({ ...ctx, initialContext: branchContext }, elseNodes);
1857
1846
  parts.push({
1858
1847
  type: "conditional",
1859
1848
  conditionNode: node.conditionNode,
@@ -1880,7 +1869,8 @@ function resolveFullChain(chain, mapping, cssBindingName, initialContext = empty
1880
1869
  function isWhenObjectCall(node) {
1881
1870
  return node.type === "call" && node.name === "when" && node.args.length === 1 && node.args[0].type === "ObjectExpression";
1882
1871
  }
1883
- function resolveWhenObjectSelectors(node, mapping, cssBindingName, initialContext, resolveCssChainReference2) {
1872
+ function resolveWhenObjectSelectors(ctx, node, initialContext) {
1873
+ const { cssBindingName } = ctx;
1884
1874
  if (!cssBindingName) {
1885
1875
  return {
1886
1876
  parts: [],
@@ -1907,13 +1897,13 @@ function resolveWhenObjectSelectors(node, mapping, cssBindingName, initialContex
1907
1897
  throw new UnsupportedPatternError(`when({ ... }) selector keys must be string literals`);
1908
1898
  }
1909
1899
  const value = unwrapExpression(property.value);
1910
- const innerChain = resolveWhenObjectValueChain(value, cssBindingName, resolveCssChainReference2);
1900
+ const innerChain = resolveWhenObjectValueChain(ctx, value);
1911
1901
  if (!innerChain) {
1912
1902
  throw new UnsupportedPatternError(`when({ ... }) values must be Css.*.$ expressions`);
1913
1903
  }
1914
1904
  const selectorContext = cloneConditionContext(initialContext);
1915
1905
  selectorContext.pseudoClass = property.key.value;
1916
- const resolved = resolveFullChain(innerChain, mapping, cssBindingName, selectorContext, resolveCssChainReference2);
1906
+ const resolved = resolveFullChain({ ...ctx, initialContext: selectorContext }, innerChain);
1917
1907
  parts.push(...resolved.parts);
1918
1908
  markers.push(...resolved.markers);
1919
1909
  errors.push(...resolved.errors);
@@ -1927,7 +1917,8 @@ function resolveWhenObjectSelectors(node, mapping, cssBindingName, initialContex
1927
1917
  }
1928
1918
  return { parts, markers, errors: [...new Set(errors)] };
1929
1919
  }
1930
- function resolveWhenObjectValueChain(value, cssBindingName, resolveCssChainReference2) {
1920
+ function resolveWhenObjectValueChain(ctx, value) {
1921
+ const { cssBindingName, resolveCssChainReference: resolveCssChainReference2 } = ctx;
1931
1922
  if (cssBindingName && value.type === "MemberExpression" && !value.computed && value.property.type === "Identifier" && value.property.name === "$") {
1932
1923
  return extractChain(value.object, cssBindingName);
1933
1924
  }
@@ -1993,7 +1984,9 @@ function invertMediaQuery(query) {
1993
1984
  }
1994
1985
  return query.replace("@media", "@media not");
1995
1986
  }
1996
- function resolveChain(chain, mapping, initialContext = emptyConditionContext(), cssBindingName, resolveCssChainReference2) {
1987
+ function resolveChain(ctx, chain) {
1988
+ const { mapping, cssBindingName } = ctx;
1989
+ const initialContext = ctx.initialContext ?? emptyConditionContext();
1997
1990
  const segments = [];
1998
1991
  const context = cloneConditionContext(initialContext);
1999
1992
  for (const node of chain) {
@@ -2036,10 +2029,9 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext(),
2036
2029
  context.mediaQuery = containerSelectorFromCall(node);
2037
2030
  continue;
2038
2031
  }
2039
- if (abbr === "add" || abbr === "addCss") {
2040
- const seg = resolveAddCall(
2032
+ if (abbr === "with") {
2033
+ const seg = resolveWithCall(
2041
2034
  node,
2042
- mapping,
2043
2035
  context.mediaQuery,
2044
2036
  context.pseudoClass,
2045
2037
  context.pseudoElement,
@@ -2048,6 +2040,18 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext(),
2048
2040
  segments.push(seg);
2049
2041
  continue;
2050
2042
  }
2043
+ if (abbr === "add") {
2044
+ const segs = resolveAddCall(
2045
+ node,
2046
+ mapping,
2047
+ context.mediaQuery,
2048
+ context.pseudoClass,
2049
+ context.pseudoElement,
2050
+ context.whenPseudo
2051
+ );
2052
+ segments.push(...segs);
2053
+ continue;
2054
+ }
2051
2055
  if (abbr === "className") {
2052
2056
  const seg = resolveClassNameCall(
2053
2057
  node,
@@ -2093,13 +2097,7 @@ function resolveChain(chain, mapping, initialContext = emptyConditionContext(),
2093
2097
  }
2094
2098
  if (abbr === "when") {
2095
2099
  if (isWhenObjectCall(node)) {
2096
- const resolved2 = resolveWhenObjectSelectors(
2097
- node,
2098
- mapping,
2099
- cssBindingName,
2100
- context,
2101
- resolveCssChainReference2
2102
- );
2100
+ const resolved2 = resolveWhenObjectSelectors(ctx, node, context);
2103
2101
  segments.push(...flattenWhenObjectParts(resolved2));
2104
2102
  continue;
2105
2103
  }
@@ -2370,18 +2368,15 @@ function resolveStyleCall(node, mediaQuery, pseudoClass, pseudoElement, whenPseu
2370
2368
  styleArg: arg
2371
2369
  };
2372
2370
  }
2373
- function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
2374
- const isAddCss = node.name === "addCss";
2375
- if (isAddCss) {
2376
- if (node.args.length !== 1) {
2377
- throw new UnsupportedPatternError(
2378
- `addCss() requires exactly 1 argument (an existing CssProp/style hash expression)`
2379
- );
2380
- }
2381
- const styleArg = node.args[0];
2382
- if (styleArg.type === "SpreadElement") {
2383
- throw new UnsupportedPatternError(`addCss() does not support spread arguments`);
2384
- }
2371
+ function resolveWithCall(node, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
2372
+ if (node.args.length !== 1) {
2373
+ throw new UnsupportedPatternError(`with() requires exactly 1 argument`);
2374
+ }
2375
+ const styleArg = node.args[0];
2376
+ if (styleArg.type === "SpreadElement") {
2377
+ throw new UnsupportedPatternError(`with() does not support spread arguments`);
2378
+ }
2379
+ if (styleArg.type === "ObjectExpression") {
2385
2380
  return {
2386
2381
  abbr: "__composed_css_prop",
2387
2382
  defs: {},
@@ -2389,25 +2384,34 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
2389
2384
  isAddCss: true
2390
2385
  };
2391
2386
  }
2387
+ return {
2388
+ abbr: "__composed_css_prop",
2389
+ defs: {},
2390
+ styleArrayArg: styleArg
2391
+ };
2392
+ }
2393
+ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, whenPseudo) {
2394
+ const context = {
2395
+ mediaQuery,
2396
+ pseudoClass,
2397
+ pseudoElement,
2398
+ whenPseudo
2399
+ };
2392
2400
  if (node.args.length === 1) {
2393
2401
  const styleArg = node.args[0];
2394
2402
  if (styleArg.type === "SpreadElement") {
2395
2403
  throw new UnsupportedPatternError(`add() does not support spread arguments`);
2396
2404
  }
2397
2405
  if (styleArg.type === "ObjectExpression") {
2398
- throw new UnsupportedPatternError(
2399
- `add(cssProp) does not accept object literals -- pass an existing CssProp expression instead`
2400
- );
2406
+ return resolveAddObjectLiteral(styleArg, mapping, context);
2401
2407
  }
2402
- return {
2403
- abbr: "__composed_css_prop",
2404
- defs: {},
2405
- styleArrayArg: styleArg
2406
- };
2408
+ throw new UnsupportedPatternError(
2409
+ `add() requires 1 or 2 arguments (property name and value, or an object literal), got ${node.args.length}. Supported overloads are add({ prop: value }), add("propName", value), and with(cssProp)`
2410
+ );
2407
2411
  }
2408
2412
  if (node.args.length !== 2) {
2409
2413
  throw new UnsupportedPatternError(
2410
- `add() requires exactly 2 arguments (property name and value), got ${node.args.length}. Supported overloads are add(cssProp), addCss(cssProp), and add("propName", value)`
2414
+ `add() requires 1 or 2 arguments (property name and value, or an object literal), got ${node.args.length}. Supported overloads are add({ prop: value }), add("propName", value), and with(cssProp)`
2411
2415
  );
2412
2416
  }
2413
2417
  const propArg = node.args[0];
@@ -2417,19 +2421,13 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
2417
2421
  const propName = propArg.value;
2418
2422
  const valueArg = node.args[1];
2419
2423
  const literalValue = tryEvaluateAddLiteral(valueArg);
2420
- const context = {
2421
- mediaQuery,
2422
- pseudoClass,
2423
- pseudoElement,
2424
- whenPseudo
2425
- };
2426
2424
  if (literalValue !== null) {
2427
- return segmentWithConditionContext(
2425
+ return [segmentWithConditionContext(
2428
2426
  { abbr: propName, defs: { [propName]: literalValue }, argResolved: literalValue },
2429
2427
  context
2430
- );
2428
+ )];
2431
2429
  }
2432
- return segmentWithConditionContext(
2430
+ return [segmentWithConditionContext(
2433
2431
  {
2434
2432
  abbr: propName,
2435
2433
  defs: {},
@@ -2438,7 +2436,52 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
2438
2436
  argNode: valueArg
2439
2437
  },
2440
2438
  context
2441
- );
2439
+ )];
2440
+ }
2441
+ function resolveAddObjectLiteral(obj, mapping, context) {
2442
+ const segments = [];
2443
+ for (const property of obj.properties) {
2444
+ if (property.type === "SpreadElement") {
2445
+ throw new UnsupportedPatternError(`add({...}) does not support spread properties -- use with() instead`);
2446
+ }
2447
+ if (property.type !== "ObjectProperty" || property.computed) {
2448
+ throw new UnsupportedPatternError(`add({...}) only supports simple property keys`);
2449
+ }
2450
+ let propName;
2451
+ if (property.key.type === "Identifier") {
2452
+ propName = property.key.name;
2453
+ } else if (property.key.type === "StringLiteral") {
2454
+ propName = property.key.value;
2455
+ } else {
2456
+ throw new UnsupportedPatternError(`add({...}) property keys must be identifiers or string literals`);
2457
+ }
2458
+ const valueNode = property.value;
2459
+ const literalValue = tryEvaluateAddLiteral(valueNode);
2460
+ if (literalValue !== null) {
2461
+ const canonicalAbbr = findCanonicalAbbreviation(mapping, propName, literalValue);
2462
+ if (canonicalAbbr) {
2463
+ const entry = mapping.abbreviations[canonicalAbbr];
2464
+ segments.push(segmentWithConditionContext(
2465
+ { abbr: canonicalAbbr, defs: entry.defs },
2466
+ context
2467
+ ));
2468
+ } else {
2469
+ segments.push(segmentWithConditionContext(
2470
+ { abbr: propName, defs: { [propName]: literalValue }, argResolved: literalValue },
2471
+ context
2472
+ ));
2473
+ }
2474
+ } else {
2475
+ segments.push(segmentWithConditionContext(
2476
+ { abbr: propName, defs: {}, variableProps: [propName], incremented: false, argNode: valueNode },
2477
+ context
2478
+ ));
2479
+ }
2480
+ }
2481
+ return segments;
2482
+ }
2483
+ function findCanonicalAbbreviation(mapping, prop, value) {
2484
+ return getLonghandLookup(mapping).get(`${prop}\0${value}`) ?? null;
2442
2485
  }
2443
2486
  function tryEvaluateAddLiteral(node) {
2444
2487
  if (node.type === "StringLiteral") {
@@ -3114,7 +3157,7 @@ function transformTruss(code, filename, mapping, options = {}) {
3114
3157
  return;
3115
3158
  }
3116
3159
  const resolveCssChainReference2 = buildCssChainReferenceResolver(path, cssBindingName);
3117
- const resolvedChain = resolveFullChain(chain, mapping, cssBindingName, void 0, resolveCssChainReference2);
3160
+ const resolvedChain = resolveFullChain({ mapping, cssBindingName, resolveCssChainReference: resolveCssChainReference2 }, chain);
3118
3161
  sites.push({ path, resolvedChain });
3119
3162
  const line = path.node.loc?.start.line ?? null;
3120
3163
  for (const err of resolvedChain.errors) {
@@ -3515,7 +3558,7 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
3515
3558
  return { error: "when() modifiers are not supported in .css.ts files" };
3516
3559
  }
3517
3560
  }
3518
- const resolved = resolveFullChain(chain, mapping, cssBindingName);
3561
+ const resolved = resolveFullChain({ mapping, cssBindingName }, chain);
3519
3562
  if (resolved.errors.length > 0) {
3520
3563
  return { error: resolved.errors[0] };
3521
3564
  }