@csszyx/compiler 0.3.1 → 0.4.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/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
1
+ import {
2
+ __require
3
+ } from "./chunk-3RG5ZIWI.js";
7
4
 
8
5
  // src/compiler.ts
9
6
  import { init, transform_sz, version as getWasmVersion } from "@csszyx/core";
@@ -178,7 +175,8 @@ var PROPERTY_CATEGORY_MAP = {
178
175
  skewY: 4 /* ANGLE */,
179
176
  // ---- DURATION ----
180
177
  duration: 5 /* DURATION */,
181
- delay: 5 /* DURATION */
178
+ delay: 5 /* DURATION */,
179
+ animationDelay: 5 /* DURATION */
182
180
  };
183
181
  function getPropertyCategory(property) {
184
182
  return PROPERTY_CATEGORY_MAP[property] ?? 7 /* PASSTHROUGH */;
@@ -222,6 +220,9 @@ function isValidColorString(value) {
222
220
  if (/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*-\d+$/.test(value)) {
223
221
  return true;
224
222
  }
223
+ if (/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z][a-zA-Z0-9]*)*$/.test(value)) {
224
+ return true;
225
+ }
225
226
  return false;
226
227
  }
227
228
  function hasSlashOpacity(value) {
@@ -324,11 +325,13 @@ var PROPERTY_MAP = {
324
325
  outlineColor: "outline",
325
326
  outlineOffset: "outline-offset",
326
327
  outlineStyle: "outline",
327
- // Ring (v3 keep for future v3 support)
328
+ // Ring (v4: outset ring + inset ring)
328
329
  ring: "ring",
329
330
  ringColor: "ring",
330
331
  ringOffset: "ring-offset",
331
332
  ringOffsetColor: "ring-offset",
333
+ insetRing: "inset-ring",
334
+ insetRingColor: "inset-ring",
332
335
  // Spacing (canonical shorthand only)
333
336
  p: "p",
334
337
  pt: "pt",
@@ -508,6 +511,7 @@ var PROPERTY_MAP = {
508
511
  rotateX: "rotate-x",
509
512
  rotateY: "rotate-y",
510
513
  rotateZ: "rotate-z",
514
+ translate: "translate",
511
515
  translateX: "translate-x",
512
516
  translateY: "translate-y",
513
517
  translateZ: "translate-z",
@@ -526,6 +530,8 @@ var PROPERTY_MAP = {
526
530
  ease: "ease",
527
531
  delay: "delay",
528
532
  animate: "animate",
533
+ animationDelay: "animation-delay",
534
+ // animation-delay — distinct from transition delay
529
535
  // Masks
530
536
  mask: "mask",
531
537
  maskSize: "mask-size",
@@ -932,13 +938,7 @@ var BOOLEAN_SHORTHANDS = /* @__PURE__ */ new Set([
932
938
  // Ring (v3 future)
933
939
  "ring",
934
940
  // Outline
935
- "outline",
936
- // Transforms
937
- "scale3d",
938
- "rotate3d",
939
- "translate3d",
940
- "transformGpu",
941
- "transformCpu"
941
+ "outline"
942
942
  ]);
943
943
  var BOOLEAN_TO_CLASS = {
944
944
  inlineBlock: "inline-block",
@@ -971,9 +971,6 @@ var BOOLEAN_TO_CLASS = {
971
971
  diagonalFractions: "diagonal-fractions",
972
972
  stackedFractions: "stacked-fractions",
973
973
  // Transforms
974
- scale3d: "scale-3d",
975
- rotate3d: "rotate-3d",
976
- translate3d: "translate-3d",
977
974
  transformGpu: "transform-gpu",
978
975
  transformCpu: "transform-cpu",
979
976
  // Misc
@@ -1040,6 +1037,7 @@ var NEGATIVE_ALLOWED = /* @__PURE__ */ new Set([
1040
1037
  "scale-z",
1041
1038
  "skew-x",
1042
1039
  "skew-y",
1040
+ "translate",
1043
1041
  "translate-x",
1044
1042
  "translate-y",
1045
1043
  "translate-z",
@@ -1066,7 +1064,10 @@ function handleImportant(value) {
1066
1064
  }
1067
1065
  function formatOpacity(op) {
1068
1066
  if (typeof op === "number") {
1069
- return String(op);
1067
+ if (Number.isInteger(op * 2)) {
1068
+ return String(op);
1069
+ }
1070
+ return `[${op}]`;
1070
1071
  }
1071
1072
  if (typeof op === "string") {
1072
1073
  if (op.startsWith("--")) {
@@ -1083,7 +1084,8 @@ function normalizeArbitraryVariant(key) {
1083
1084
  return key.replace(/\s+/g, "");
1084
1085
  }
1085
1086
  function normalizeArbitraryValue(value) {
1086
- return value.trim().replace(/\s+/g, "_");
1087
+ const stripped = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1088
+ return stripped.trim().replace(/\s+/g, "_");
1087
1089
  }
1088
1090
  var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1089
1091
  // Sizing (both rawKey and resolved key forms)
@@ -1121,6 +1123,7 @@ var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1121
1123
  "start",
1122
1124
  "end",
1123
1125
  // Translate
1126
+ "translate",
1124
1127
  "translate-x",
1125
1128
  "translateX",
1126
1129
  "translate-y",
@@ -1129,21 +1132,22 @@ var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1129
1132
  "aspect"
1130
1133
  ]);
1131
1134
  function needsArbitraryBrackets(value) {
1132
- return /^\d+(\.\d+)?(px|rem|em|%|vh|vw|ch|dvh|dvw|svh|svw|lvh|lvw|cqw|cqh|deg|rad|turn|grad|ms|s|fr)$/.test(value) || // Positive units
1133
- /^-\d+(\.\d+)?(px|rem|em|%|vh|vw|ch|dvh|dvw|svh|svw|lvh|lvw|cqw|cqh|deg|rad|turn|grad|ms|s|fr)$/.test(value) || // Negative units like -1px, -2rem
1134
- /^\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(value) || // Values starting with . like .25em
1135
- /^-\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(value) || // Negative values starting with -. like -.25em
1136
- value.startsWith("#") || // Hex colors
1137
- value.startsWith("rgb") || // RGB colors
1138
- value.startsWith("hsl") || // HSL colors
1139
- value.includes("calc(") || // Calculations
1140
- value.includes("var(") || // CSS variables (old syntax)
1141
- value.includes("attr(") || // attr() function
1142
- value.includes("url(") || // URLs
1143
- value.includes("clamp(") || // Clamp
1144
- value.includes("min(") || // Min
1145
- value.includes("max(") || // Max
1146
- value.includes(" ");
1135
+ const v = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1136
+ return /^\d+(\.\d+)?(px|rem|em|%|vh|vw|ch|dvh|dvw|svh|svw|lvh|lvw|cqw|cqh|deg|rad|turn|grad|ms|s|fr)$/.test(v) || // Positive units
1137
+ /^-\d+(\.\d+)?(px|rem|em|%|vh|vw|ch|dvh|dvw|svh|svw|lvh|lvw|cqw|cqh|deg|rad|turn|grad|ms|s|fr)$/.test(v) || // Negative units like -1px, -2rem
1138
+ /^\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(v) || // Values starting with . like .25em
1139
+ /^-\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(v) || // Negative values starting with -. like -.25em
1140
+ v.startsWith("#") || // Hex colors
1141
+ v.startsWith("rgb") || // RGB colors
1142
+ v.startsWith("hsl") || // HSL colors
1143
+ v.includes("calc(") || // Calculations
1144
+ v.includes("var(") || // CSS variables (old syntax)
1145
+ v.includes("attr(") || // attr() function
1146
+ v.includes("url(") || // URLs
1147
+ v.includes("clamp(") || // Clamp
1148
+ v.includes("min(") || // Min
1149
+ v.includes("max(") || // Max
1150
+ v.includes(" ");
1147
1151
  }
1148
1152
  var LIST_STYLE_STANDARD = /* @__PURE__ */ new Set(["none", "disc", "decimal"]);
1149
1153
  var FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set([
@@ -1157,11 +1161,12 @@ var FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set([
1157
1161
  "extra-expanded",
1158
1162
  "ultra-expanded"
1159
1163
  ]);
1160
- var NEEDS_ARBITRARY_PROPERTY = /* @__PURE__ */ new Set([
1161
- "mask-type",
1162
- "mask-composite",
1163
- "mask-mode"
1164
- ]);
1164
+ function camelToKebab(prop) {
1165
+ if (prop.startsWith("--")) {
1166
+ return prop;
1167
+ }
1168
+ return prop.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1169
+ }
1165
1170
  function getVariantPrefix(key) {
1166
1171
  if (VARIANT_MAP[key]) {
1167
1172
  return VARIANT_MAP[key];
@@ -1187,6 +1192,32 @@ function handleGroupPeer(type, nestedObj, prefix) {
1187
1192
  }
1188
1193
  continue;
1189
1194
  }
1195
+ if (nestedKey === "data" && typeof nestedValue === "object") {
1196
+ for (const [attr, attrValue] of Object.entries(nestedValue)) {
1197
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1198
+ continue;
1199
+ }
1200
+ const variantPrefix = `${prefix}${type}-data-[${attr}]:`;
1201
+ const result = transform(attrValue, variantPrefix);
1202
+ if (result.className) {
1203
+ classes.push(result.className);
1204
+ }
1205
+ }
1206
+ continue;
1207
+ }
1208
+ if (nestedKey === "aria" && typeof nestedValue === "object") {
1209
+ for (const [attr, attrValue] of Object.entries(nestedValue)) {
1210
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1211
+ continue;
1212
+ }
1213
+ const variantPrefix = ARIA_STATES.has(attr) ? `${prefix}${type}-aria-${attr}:` : `${prefix}${type}-aria-[${attr}]:`;
1214
+ const result = transform(attrValue, variantPrefix);
1215
+ if (result.className) {
1216
+ classes.push(result.className);
1217
+ }
1218
+ }
1219
+ continue;
1220
+ }
1190
1221
  const isVariant = KNOWN_VARIANTS.has(nestedKey) || KNOWN_VARIANTS.has(getVariantPrefix(nestedKey));
1191
1222
  const isArbitrary = nestedKey.startsWith(".") || nestedKey.startsWith("#") || nestedKey.startsWith("[") || nestedKey.startsWith(":");
1192
1223
  if (isArbitrary) {
@@ -1207,6 +1238,33 @@ function handleGroupPeer(type, nestedObj, prefix) {
1207
1238
  if (stateValue === null || stateValue === void 0 || stateValue === false) {
1208
1239
  continue;
1209
1240
  }
1241
+ if (state === "data" && typeof stateValue === "object") {
1242
+ for (const [attr, attrValue] of Object.entries(stateValue)) {
1243
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1244
+ continue;
1245
+ }
1246
+ const variantPrefix2 = `${prefix}${type}-data-[${attr}]/${nestedKey}:`;
1247
+ const result2 = transform(attrValue, variantPrefix2);
1248
+ if (result2.className) {
1249
+ classes.push(result2.className);
1250
+ }
1251
+ }
1252
+ continue;
1253
+ }
1254
+ if (state === "aria" && typeof stateValue === "object") {
1255
+ for (const [attr, attrValue] of Object.entries(stateValue)) {
1256
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1257
+ continue;
1258
+ }
1259
+ const ariaSegment = ARIA_STATES.has(attr) ? `aria-${attr}` : `aria-[${attr}]`;
1260
+ const variantPrefix2 = `${prefix}${type}-${ariaSegment}/${nestedKey}:`;
1261
+ const result2 = transform(attrValue, variantPrefix2);
1262
+ if (result2.className) {
1263
+ classes.push(result2.className);
1264
+ }
1265
+ }
1266
+ continue;
1267
+ }
1210
1268
  const mappedState = getVariantPrefix(state);
1211
1269
  const variantPrefix = `${prefix}${type}-${mappedState}/${nestedKey}:`;
1212
1270
  const result = transform(stateValue, variantPrefix);
@@ -1328,6 +1386,18 @@ function transform(szProp, prefix = "", mangleMap) {
1328
1386
  if (value === null || value === void 0) {
1329
1387
  continue;
1330
1388
  }
1389
+ if (rawKey === "css") {
1390
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1391
+ for (const [cssProp, cssVal] of Object.entries(value)) {
1392
+ if (cssVal === null || cssVal === void 0) {
1393
+ continue;
1394
+ }
1395
+ const kebab = camelToKebab(cssProp);
1396
+ classes.push(`${prefix}[${kebab}:${normalizeArbitraryValue(String(cssVal))}]`);
1397
+ }
1398
+ }
1399
+ continue;
1400
+ }
1331
1401
  if (rawKey.startsWith("@") && typeof value === "string") {
1332
1402
  const mappedKey = VARIANT_MAP[rawKey] || rawKey;
1333
1403
  classes.push(`${prefix}${mappedKey}/${value}`);
@@ -1398,10 +1468,11 @@ function transform(szProp, prefix = "", mangleMap) {
1398
1468
  classes.push(`${prefix}${rawKey}/${value}`);
1399
1469
  continue;
1400
1470
  }
1401
- if (typeof value === "object" && value !== null && !Array.isArray(value) && "color" in value) {
1471
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && rawKey in PROPERTY_MAP && "color" in value) {
1402
1472
  const colorObj = value;
1403
1473
  const twPrefix = PROPERTY_MAP[rawKey] || rawKey.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1404
- const colorBase = String(colorObj.color);
1474
+ const rawColorBase = String(colorObj.color);
1475
+ const colorBase = rawColorBase.startsWith("--") ? `(${rawColorBase})` : needsArbitraryBrackets(rawColorBase) ? `[${normalizeArbitraryValue(rawColorBase)}]` : normalizeArbitraryValue(rawColorBase);
1405
1476
  if (colorObj.op !== void 0) {
1406
1477
  const opStr = formatOpacity(colorObj.op);
1407
1478
  classes.push(`${prefix}${twPrefix}-${colorBase}/${opStr}`);
@@ -1760,7 +1831,7 @@ function transform(szProp, prefix = "", mangleMap) {
1760
1831
  }
1761
1832
  if (rawKey === "decorationThickness" || rawKey === "textDecorationThickness") {
1762
1833
  if (needsArbitraryBrackets(value)) {
1763
- className += `decoration-[${value}]`;
1834
+ className += `decoration-[${normalizeArbitraryValue(value)}]`;
1764
1835
  } else if (value.startsWith("--")) {
1765
1836
  className += `decoration-(${value})`;
1766
1837
  } else {
@@ -1811,7 +1882,9 @@ function transform(szProp, prefix = "", mangleMap) {
1811
1882
  if (rawKey === "brightness" || rawKey === "contrast" || rawKey === "saturate" || rawKey === "scale" || rawKey === "backdropBrightness" || rawKey === "backdropContrast" || rawKey === "backdropSaturate") {
1812
1883
  const prop = rawKey.startsWith("backdrop") ? `backdrop-${rawKey.slice(8).toLowerCase()}` : rawKey;
1813
1884
  const sValue = String(value);
1814
- if (sValue.startsWith("--")) {
1885
+ if (sValue === "3d" && rawKey === "scale") {
1886
+ classes.push(`${prefix}scale-3d`);
1887
+ } else if (sValue.startsWith("--")) {
1815
1888
  classes.push(`${prop}-(${sValue})`);
1816
1889
  } else {
1817
1890
  classes.push(`${prop}-[${sValue}]`);
@@ -1851,38 +1924,53 @@ function transform(szProp, prefix = "", mangleMap) {
1851
1924
  if (rawKey === "bgImg") {
1852
1925
  const v = String(value).trim();
1853
1926
  if (v === "none") {
1854
- classes.push("bg-none");
1927
+ classes.push(`${prefix}bg-none`);
1855
1928
  continue;
1856
1929
  }
1857
1930
  const vNorm = v.startsWith("-") ? v.slice(1) : v;
1931
+ if (vNorm.startsWith("repeating-")) {
1932
+ classes.push(`${prefix}bg-[${normalizeArbitraryValue(v)}]`);
1933
+ continue;
1934
+ }
1858
1935
  if (vNorm.startsWith("linear-") || vNorm.startsWith("radial") || vNorm.startsWith("conic") || vNorm.startsWith("gradient-to-")) {
1859
1936
  const vMapped = vNorm.startsWith("gradient-to-") ? vNorm.replace("gradient-to-", "linear-to-") : vNorm;
1860
1937
  if (v.startsWith("-")) {
1861
- classes.push(`-bg-${vMapped}`);
1938
+ classes.push(`${prefix}-bg-${vMapped}`);
1862
1939
  } else {
1863
- classes.push(`bg-${vMapped}`);
1940
+ classes.push(`${prefix}bg-${vMapped}`);
1864
1941
  }
1865
1942
  continue;
1866
1943
  }
1867
1944
  if (v.startsWith("--")) {
1868
- classes.push(`bg-(image:${v})`);
1945
+ classes.push(`${prefix}bg-(image:${v})`);
1869
1946
  continue;
1870
1947
  }
1871
1948
  if (v.startsWith("url(")) {
1872
- classes.push(`bg-[${v}]`);
1949
+ classes.push(`${prefix}bg-[${v}]`);
1873
1950
  continue;
1874
1951
  }
1875
- classes.push(`bg-[url(${v})]`);
1952
+ classes.push(`${prefix}bg-[url(${v})]`);
1876
1953
  continue;
1877
1954
  }
1878
1955
  if (rawKey === "bgPos") {
1879
1956
  const sVal = String(value);
1880
1957
  if (sVal.startsWith("--")) {
1881
- classes.push(`bg-(${sVal})`);
1958
+ classes.push(`${prefix}bg-(${sVal})`);
1882
1959
  } else if (sVal.includes("_") || needsArbitraryBrackets(sVal)) {
1883
- classes.push(`bg-[${normalizeArbitraryValue(sVal)}]`);
1960
+ classes.push(`${prefix}bg-[${normalizeArbitraryValue(sVal)}]`);
1961
+ } else {
1962
+ classes.push(`${prefix}bg-${sVal}`);
1963
+ }
1964
+ continue;
1965
+ }
1966
+ if (rawKey === "bgSize") {
1967
+ const sVal = String(value);
1968
+ if (sVal === "auto" || sVal === "cover" || sVal === "contain") {
1969
+ classes.push(`${prefix}bg-${sVal}`);
1970
+ } else if (sVal.startsWith("--")) {
1971
+ classes.push(`${prefix}bg-size-(${sVal})`);
1884
1972
  } else {
1885
- classes.push(`bg-${sVal}`);
1973
+ classes.push(`${prefix}bg-size-[${normalizeArbitraryValue(sVal)}]`);
1886
1974
  }
1887
1975
  continue;
1888
1976
  }
@@ -1908,7 +1996,8 @@ function transform(szProp, prefix = "", mangleMap) {
1908
1996
  } else if (value === "no-repeat") {
1909
1997
  className += "bg-no-repeat";
1910
1998
  } else {
1911
- className += `bg-repeat-${value}`;
1999
+ const suffix = value.startsWith("repeat-") ? value.slice(7) : value;
2000
+ className += `bg-repeat-${suffix}`;
1912
2001
  }
1913
2002
  classes.push(className);
1914
2003
  continue;
@@ -1949,7 +2038,8 @@ function transform(szProp, prefix = "", mangleMap) {
1949
2038
  } else if (value.startsWith("--")) {
1950
2039
  className += `content-(${value})`;
1951
2040
  } else {
1952
- className += `content-[${value}]`;
2041
+ const inner = value.startsWith('"') && value.endsWith('"') && value.length >= 2 ? `'${value.slice(1, -1)}'` : value;
2042
+ className += `content-[${inner}]`;
1953
2043
  }
1954
2044
  classes.push(className);
1955
2045
  continue;
@@ -2002,16 +2092,12 @@ function transform(szProp, prefix = "", mangleMap) {
2002
2092
  const sVal = String(value);
2003
2093
  const prop = PROPERTY_MAP[rawKey] || rawKey;
2004
2094
  if (needsArbitraryBrackets(sVal) || sVal.includes("(") || sVal.includes("_") || sVal.includes("%")) {
2005
- classes.push(`${prop}-[${normalizeArbitraryValue(sVal)}]`);
2095
+ classes.push(`${className}${prop}-[${normalizeArbitraryValue(sVal)}]`);
2006
2096
  continue;
2007
2097
  }
2008
2098
  }
2009
2099
  if (rawKey === "transformStyle") {
2010
- if (value === "preserve-3d" || value === "3d") {
2011
- className += "transform-3d";
2012
- } else {
2013
- className += `transform-${value}`;
2014
- }
2100
+ className += `transform-${value}`;
2015
2101
  classes.push(className);
2016
2102
  continue;
2017
2103
  }
@@ -2022,7 +2108,7 @@ function transform(szProp, prefix = "", mangleMap) {
2022
2108
  } else if (value.startsWith("--")) {
2023
2109
  className += `perspective-(${value})`;
2024
2110
  } else if (needsArbitraryBrackets(value)) {
2025
- className += `perspective-[${value}]`;
2111
+ className += `perspective-[${normalizeArbitraryValue(value)}]`;
2026
2112
  } else {
2027
2113
  className += `perspective-${value}`;
2028
2114
  }
@@ -2046,7 +2132,7 @@ function transform(szProp, prefix = "", mangleMap) {
2046
2132
  }
2047
2133
  }
2048
2134
  if (process.env.NODE_ENV !== "production" && typeof window === "undefined") {
2049
- const isKnown = PROPERTY_MAP[rawKey] || BOOLEAN_SHORTHANDS.has(rawKey) || SNAP_DIRECT_MAP[rawKey] || NEEDS_ARBITRARY_PROPERTY.has(key) || rawKey === "fromPos" || rawKey === "viaPos" || rawKey === "toPos" || rawKey.startsWith("--") || rawKey.startsWith("[") || rawKey.startsWith("@") || // Variants that fell through (e.g. empty object)
2135
+ const isKnown = PROPERTY_MAP[rawKey] || BOOLEAN_SHORTHANDS.has(rawKey) || SNAP_DIRECT_MAP[rawKey] || rawKey === "fromPos" || rawKey === "viaPos" || rawKey === "toPos" || rawKey.startsWith("--") || rawKey.startsWith("[") || rawKey.startsWith("@") || // Variants that fell through (e.g. empty object)
2050
2136
  KNOWN_VARIANTS.has(rawKey) || // Groups/Peers
2051
2137
  rawKey === "group" || rawKey === "peer" || // Special variant objects
2052
2138
  rawKey === "has" || rawKey === "not" || rawKey === "data" || rawKey === "aria" || rawKey === "supports" || rawKey === "min" || rawKey === "max";
@@ -2073,6 +2159,11 @@ function transform(szProp, prefix = "", mangleMap) {
2073
2159
  classes.push(className);
2074
2160
  continue;
2075
2161
  }
2162
+ if (rawKey === "animationDelay") {
2163
+ const ms = typeof value === "number" ? `${value}ms` : String(value);
2164
+ classes.push(`${className}[animation-delay:${ms}]`);
2165
+ continue;
2166
+ }
2076
2167
  if (typeof value === "number") {
2077
2168
  if (value < 0 && NEGATIVE_ALLOWED.has(key)) {
2078
2169
  className += `-${key}-${Math.abs(value)}`;
@@ -2085,15 +2176,6 @@ function transform(szProp, prefix = "", mangleMap) {
2085
2176
  if (typeof value === "string") {
2086
2177
  const { value: cleanValue, important } = handleImportant(value);
2087
2178
  let finalValue = cleanValue;
2088
- if (NEEDS_ARBITRARY_PROPERTY.has(key)) {
2089
- const arbitraryProp = `[${key}:${finalValue}]`;
2090
- className += arbitraryProp;
2091
- if (important) {
2092
- className += "!";
2093
- }
2094
- classes.push(className);
2095
- continue;
2096
- }
2097
2179
  if (finalValue.startsWith("--")) {
2098
2180
  const typeHint = CSS_VAR_TYPE_HINTS[rawKey];
2099
2181
  if (typeHint) {
@@ -2182,11 +2264,14 @@ function normalizeClassName(className) {
2182
2264
  // src/transform.ts
2183
2265
  function transformSourceCode(source) {
2184
2266
  let usesRuntime = false;
2267
+ let usesMerge = false;
2185
2268
  let usesColorVar = false;
2186
2269
  let transformed = false;
2187
2270
  const collectedClasses = /* @__PURE__ */ new Set();
2271
+ const rawClassNames = /* @__PURE__ */ new Set();
2272
+ const diagnostics = [];
2188
2273
  if (!source.includes("sz")) {
2189
- return { code: source, transformed: false, usesRuntime: false, usesColorVar: false, classes: collectedClasses };
2274
+ return { code: source, transformed: false, usesRuntime: false, usesMerge: false, usesColorVar: false, classes: collectedClasses, rawClassNames, diagnostics };
2190
2275
  }
2191
2276
  try {
2192
2277
  const result = babel.transformSync(source, {
@@ -2210,7 +2295,7 @@ function transformSourceCode(source) {
2210
2295
  if (t.isStringLiteral(val)) {
2211
2296
  for (const c of val.value.split(/\s+/)) {
2212
2297
  if (c) {
2213
- collectedClasses.add(c);
2298
+ rawClassNames.add(c);
2214
2299
  }
2215
2300
  }
2216
2301
  }
@@ -2220,6 +2305,99 @@ function transformSourceCode(source) {
2220
2305
  return;
2221
2306
  }
2222
2307
  const value = path.node.value;
2308
+ let existingClassNameNode = null;
2309
+ let existingClassExpr = null;
2310
+ let existingStyleNode = null;
2311
+ let existingStyleExpr = null;
2312
+ if (path.parentPath?.isJSXOpeningElement()) {
2313
+ for (const attr of path.parentPath.node.attributes) {
2314
+ if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
2315
+ const aName = attr.name;
2316
+ if (aName.name === "className" || aName.name === "class") {
2317
+ existingClassNameNode = attr;
2318
+ const aVal = attr.value;
2319
+ if (t.isStringLiteral(aVal)) {
2320
+ existingClassExpr = aVal;
2321
+ } else if (t.isJSXExpressionContainer(aVal)) {
2322
+ if (t.isExpression(aVal.expression)) {
2323
+ existingClassExpr = aVal.expression;
2324
+ }
2325
+ }
2326
+ } else if (aName.name === "style") {
2327
+ existingStyleNode = attr;
2328
+ const aVal = attr.value;
2329
+ if (t.isJSXExpressionContainer(aVal)) {
2330
+ if (t.isExpression(aVal.expression)) {
2331
+ existingStyleExpr = aVal.expression;
2332
+ }
2333
+ } else if (t.isStringLiteral(aVal)) {
2334
+ existingStyleExpr = aVal;
2335
+ }
2336
+ }
2337
+ }
2338
+ }
2339
+ }
2340
+ const createMergedClassNameValue = (szExpr) => {
2341
+ if (!existingClassExpr) {
2342
+ return t.isStringLiteral(szExpr) ? szExpr : t.jsxExpressionContainer(szExpr);
2343
+ }
2344
+ if (existingClassNameNode && path.parentPath?.isJSXOpeningElement()) {
2345
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2346
+ (a) => a !== existingClassNameNode
2347
+ );
2348
+ existingClassNameNode = null;
2349
+ }
2350
+ if (t.isStringLiteral(existingClassExpr) && t.isStringLiteral(szExpr)) {
2351
+ const merged = `${existingClassExpr.value} ${szExpr.value}`.trim();
2352
+ return t.stringLiteral(merged);
2353
+ }
2354
+ usesRuntime = true;
2355
+ usesMerge = true;
2356
+ return t.jsxExpressionContainer(
2357
+ t.callExpression(t.identifier("_szMerge"), [existingClassExpr, szExpr])
2358
+ );
2359
+ };
2360
+ const mergeAndInjectStyle = (newStyleProps) => {
2361
+ if (newStyleProps.length === 0) {
2362
+ return;
2363
+ }
2364
+ if (!path.parentPath?.isJSXOpeningElement()) {
2365
+ return;
2366
+ }
2367
+ if (existingStyleNode && existingStyleExpr) {
2368
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2369
+ (a) => a !== existingStyleNode
2370
+ );
2371
+ existingStyleNode = null;
2372
+ if (t.isObjectExpression(existingStyleExpr)) {
2373
+ existingStyleExpr.properties.push(...newStyleProps);
2374
+ path.parentPath.node.attributes.push(
2375
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(existingStyleExpr))
2376
+ );
2377
+ } else if (t.isStringLiteral(existingStyleExpr)) {
2378
+ const parsedOldProps = parseStyleStringToObjectExpr(existingStyleExpr.value).properties;
2379
+ path.parentPath.node.attributes.push(
2380
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(
2381
+ t.objectExpression([...parsedOldProps, ...newStyleProps])
2382
+ ))
2383
+ );
2384
+ } else {
2385
+ const mergedStyle = t.objectExpression([
2386
+ t.spreadElement(existingStyleExpr),
2387
+ ...newStyleProps
2388
+ ]);
2389
+ path.parentPath.node.attributes.push(
2390
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(mergedStyle))
2391
+ );
2392
+ }
2393
+ } else {
2394
+ path.parentPath.node.attributes.push(
2395
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(t.objectExpression(newStyleProps)))
2396
+ );
2397
+ existingStyleExpr = t.objectExpression(newStyleProps);
2398
+ existingStyleNode = path.parentPath.node.attributes[path.parentPath.node.attributes.length - 1];
2399
+ }
2400
+ };
2223
2401
  if (t.isStringLiteral(value)) {
2224
2402
  path.node.name.name = "className";
2225
2403
  for (const c of value.value.split(/\s+/)) {
@@ -2227,13 +2405,16 @@ function transformSourceCode(source) {
2227
2405
  collectedClasses.add(c);
2228
2406
  }
2229
2407
  }
2408
+ path.node.value = createMergedClassNameValue(value);
2230
2409
  transformed = true;
2231
2410
  return;
2232
2411
  }
2233
2412
  if (t.isJSXExpressionContainer(value)) {
2234
2413
  const expression = value.expression;
2235
2414
  if (t.isObjectExpression(expression)) {
2236
- const staticObject = evaluateStaticObject(expression);
2415
+ const getBinding = (name) => path.scope.getBinding(name);
2416
+ const flatExpression = resolveObjectSpreads(expression, getBinding) ?? expression;
2417
+ const staticObject = evaluateStaticObject(flatExpression);
2237
2418
  if (staticObject !== null) {
2238
2419
  const { className, attributes } = transform(staticObject);
2239
2420
  for (const c of className.split(/\s+/)) {
@@ -2242,22 +2423,35 @@ function transformSourceCode(source) {
2242
2423
  }
2243
2424
  }
2244
2425
  path.node.name.name = "className";
2245
- path.node.value = t.stringLiteral(className);
2426
+ path.node.value = createMergedClassNameValue(t.stringLiteral(className));
2246
2427
  Object.entries(attributes).forEach(([key, val]) => {
2247
2428
  if (path.parentPath?.isJSXOpeningElement()) {
2248
- path.parentPath.node.attributes.push(
2249
- t.jsxAttribute(
2250
- t.jsxIdentifier(key),
2251
- t.stringLiteral(val)
2252
- )
2253
- );
2429
+ if (key === "style") {
2430
+ const newProps = parseStyleStringToObjectExpr(val).properties;
2431
+ mergeAndInjectStyle(newProps);
2432
+ } else {
2433
+ path.parentPath.node.attributes.push(
2434
+ t.jsxAttribute(
2435
+ t.jsxIdentifier(key),
2436
+ t.stringLiteral(val)
2437
+ )
2438
+ );
2439
+ }
2254
2440
  }
2255
2441
  });
2256
2442
  transformed = true;
2257
2443
  return;
2258
2444
  }
2259
- const partial = evaluatePartialObject(expression);
2260
- if (partial !== null && !partial.hasSpread && partial.dynamicProps.size > 0) {
2445
+ const hoisted = tryHoistConditionalSpread(expression, getBinding);
2446
+ if (hoisted !== null) {
2447
+ path.node.name.name = "className";
2448
+ path.node.value = createMergedClassNameValue(hoisted);
2449
+ collectFromExpr(hoisted, collectedClasses);
2450
+ transformed = true;
2451
+ return;
2452
+ }
2453
+ const partial = evaluatePartialObject(flatExpression);
2454
+ if (partial !== null && !partial.hasSpread && (partial.dynamicProps.size > 0 || partial.conditionalClasses.length > 0)) {
2261
2455
  const staticClasses = [];
2262
2456
  if (Object.keys(partial.staticProps).length > 0) {
2263
2457
  const { className: sc } = transform(partial.staticProps);
@@ -2278,23 +2472,28 @@ function transformSourceCode(source) {
2278
2472
  )
2279
2473
  );
2280
2474
  }
2281
- const allClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2282
- for (const c of allClasses.split(/\s+/)) {
2475
+ const baseClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2476
+ for (const c of baseClasses.split(/\s+/)) {
2283
2477
  if (c) {
2284
2478
  collectedClasses.add(c);
2285
2479
  }
2286
2480
  }
2287
- path.node.name.name = "className";
2288
- path.node.value = t.stringLiteral(allClasses);
2289
- if (styleProps.length > 0 && path.parentPath?.isJSXOpeningElement()) {
2290
- const styleAttr = t.jsxAttribute(
2291
- t.jsxIdentifier("style"),
2292
- t.jsxExpressionContainer(
2293
- t.objectExpression(styleProps)
2294
- )
2295
- );
2296
- path.parentPath.node.attributes.push(styleAttr);
2481
+ for (const cc of partial.conditionalClasses) {
2482
+ for (const c of cc.consequent.split(/\s+/)) {
2483
+ if (c) {
2484
+ collectedClasses.add(c);
2485
+ }
2486
+ }
2487
+ for (const c of cc.alternate.split(/\s+/)) {
2488
+ if (c) {
2489
+ collectedClasses.add(c);
2490
+ }
2491
+ }
2297
2492
  }
2493
+ const classExpr = partial.conditionalClasses.length > 0 ? buildConditionalClassExpr(baseClasses, partial.conditionalClasses) : t.stringLiteral(baseClasses);
2494
+ path.node.name.name = "className";
2495
+ path.node.value = createMergedClassNameValue(classExpr);
2496
+ mergeAndInjectStyle(styleProps);
2298
2497
  if (partial.usesColorVar) {
2299
2498
  usesColorVar = true;
2300
2499
  }
@@ -2307,18 +2506,19 @@ function transformSourceCode(source) {
2307
2506
  if (binding && binding.path.isVariableDeclarator()) {
2308
2507
  const init2 = binding.path.node.init;
2309
2508
  if (init2) {
2310
- const resolved = tryStaticTransformNode(init2);
2509
+ const gbIdent = (name) => path.scope.getBinding(name);
2510
+ const resolved = tryStaticTransformNode(init2, gbIdent);
2311
2511
  if (resolved !== null) {
2312
2512
  path.node.name.name = "className";
2313
2513
  if (t.isStringLiteral(resolved)) {
2314
- path.node.value = resolved;
2514
+ path.node.value = createMergedClassNameValue(resolved);
2315
2515
  for (const c of resolved.value.split(/\s+/)) {
2316
2516
  if (c) {
2317
2517
  collectedClasses.add(c);
2318
2518
  }
2319
2519
  }
2320
2520
  } else {
2321
- value.expression = resolved;
2521
+ path.node.value = createMergedClassNameValue(resolved);
2322
2522
  collectFromExpr(resolved, collectedClasses);
2323
2523
  }
2324
2524
  transformed = true;
@@ -2328,33 +2528,267 @@ function transformSourceCode(source) {
2328
2528
  }
2329
2529
  }
2330
2530
  if (t.isConditionalExpression(expression)) {
2331
- const resolved = tryStaticTransformNode(expression);
2531
+ const gbCond = (name) => path.scope.getBinding(name);
2532
+ const resolved = tryStaticTransformNode(expression, gbCond);
2332
2533
  if (resolved !== null) {
2333
2534
  path.node.name.name = "className";
2334
2535
  if (t.isStringLiteral(resolved)) {
2335
- path.node.value = resolved;
2536
+ path.node.value = createMergedClassNameValue(resolved);
2336
2537
  for (const c of resolved.value.split(/\s+/)) {
2337
2538
  if (c) {
2338
2539
  collectedClasses.add(c);
2339
2540
  }
2340
2541
  }
2341
2542
  } else {
2342
- value.expression = resolved;
2543
+ path.node.value = createMergedClassNameValue(resolved);
2343
2544
  collectFromExpr(resolved, collectedClasses);
2344
2545
  }
2345
2546
  transformed = true;
2346
2547
  return;
2347
2548
  }
2348
2549
  }
2550
+ if (t.isArrayExpression(expression)) {
2551
+ const parts = [];
2552
+ let hasRuntime = false;
2553
+ const getBindingForArray = (name) => path.scope.getBinding(name);
2554
+ for (const element of expression.elements) {
2555
+ if (element === null) {
2556
+ continue;
2557
+ }
2558
+ if (t.isBooleanLiteral(element) && !element.value) {
2559
+ continue;
2560
+ }
2561
+ if (t.isNullLiteral(element)) {
2562
+ continue;
2563
+ }
2564
+ if (t.isIdentifier(element) && element.name === "undefined") {
2565
+ continue;
2566
+ }
2567
+ if (t.isLogicalExpression(element) && element.operator === "&&") {
2568
+ const resolved2 = tryStaticTransformNode(element.right, getBindingForArray);
2569
+ if (resolved2 !== null && t.isStringLiteral(resolved2)) {
2570
+ if (resolved2.value) {
2571
+ parts.push(t.logicalExpression("&&", element.left, resolved2));
2572
+ for (const c of resolved2.value.split(/\s+/)) {
2573
+ if (c) {
2574
+ collectedClasses.add(c);
2575
+ }
2576
+ }
2577
+ hasRuntime = true;
2578
+ }
2579
+ continue;
2580
+ }
2581
+ parts.push(element);
2582
+ hasRuntime = true;
2583
+ continue;
2584
+ }
2585
+ const resolved = tryStaticTransformNode(element, getBindingForArray);
2586
+ if (resolved !== null) {
2587
+ if (t.isStringLiteral(resolved)) {
2588
+ if (resolved.value) {
2589
+ parts.push(resolved);
2590
+ for (const c of resolved.value.split(/\s+/)) {
2591
+ if (c) {
2592
+ collectedClasses.add(c);
2593
+ }
2594
+ }
2595
+ }
2596
+ } else {
2597
+ parts.push(resolved);
2598
+ collectFromExpr(resolved, collectedClasses);
2599
+ hasRuntime = true;
2600
+ }
2601
+ } else {
2602
+ parts.push(element);
2603
+ hasRuntime = true;
2604
+ }
2605
+ }
2606
+ path.node.name.name = "className";
2607
+ if (parts.length === 0) {
2608
+ path.node.value = createMergedClassNameValue(t.stringLiteral(""));
2609
+ } else if (!hasRuntime) {
2610
+ const merged = parts.map((p) => p.value).filter(Boolean).join(" ");
2611
+ path.node.value = createMergedClassNameValue(t.stringLiteral(merged));
2612
+ } else {
2613
+ if (existingClassExpr) {
2614
+ parts.unshift(existingClassExpr);
2615
+ if (existingClassNameNode && path.parentPath?.isJSXOpeningElement()) {
2616
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2617
+ (a) => a !== existingClassNameNode
2618
+ );
2619
+ existingClassNameNode = null;
2620
+ }
2621
+ }
2622
+ const szCall2 = t.callExpression(
2623
+ t.identifier("_szMerge"),
2624
+ parts
2625
+ );
2626
+ path.node.value = t.jsxExpressionContainer(szCall2);
2627
+ usesMerge = true;
2628
+ usesRuntime = true;
2629
+ }
2630
+ transformed = true;
2631
+ return;
2632
+ }
2633
+ const loc = expression.loc;
2634
+ const lineCol = loc ? `${loc.start.line}:${loc.start.column + 1}` : "?";
2635
+ let reason, suggestion;
2636
+ if (t.isCallExpression(expression)) {
2637
+ const callee = expression.callee;
2638
+ const name = t.isIdentifier(callee) ? callee.name : t.isMemberExpression(callee) && t.isIdentifier(callee.property) ? callee.property.name : "?";
2639
+ reason = `function call \`${name}()\` result is unknown at build time`;
2640
+ suggestion = "If it returns static variants \u2192 convert to szv(). If it depends on runtime data \u2192 use dynamic().";
2641
+ } else if (t.isIdentifier(expression)) {
2642
+ reason = `identifier \`${expression.name}\` could not be resolved to a static value`;
2643
+ suggestion = "Make sure it's a module-level or function-body const with a literal object value. For variant-based styling \u2192 szv(). For true runtime values \u2192 dynamic().";
2644
+ } else if (t.isMemberExpression(expression)) {
2645
+ reason = "member expression is not statically resolvable";
2646
+ suggestion = "Extract the value to a module-level const. For variant-based styling \u2192 szv(). For true runtime values \u2192 dynamic().";
2647
+ } else {
2648
+ reason = `expression of type \`${expression.type}\` is not statically analyzable`;
2649
+ suggestion = "Use a literal sz object or a module-level const. For variant-based styling \u2192 szv(). For true runtime values \u2192 dynamic().";
2650
+ }
2651
+ diagnostics.push(`sz fallback at ${lineCol}: ${reason}.
2652
+ Suggestion: ${suggestion}`);
2349
2653
  path.node.name.name = "className";
2350
2654
  const szCall = t.callExpression(
2351
2655
  t.identifier("_sz"),
2352
2656
  [expression]
2353
2657
  );
2354
- value.expression = szCall;
2658
+ path.node.value = createMergedClassNameValue(szCall);
2355
2659
  usesRuntime = true;
2356
2660
  transformed = true;
2357
2661
  }
2662
+ },
2663
+ // ── szv catalog extraction ────────────────────────────────────────
2664
+ // When the compiler sees `const X = szv({...})` with a static config,
2665
+ // it emits a no-op catalog array so Tailwind JIT can scan all variant
2666
+ // class strings — even when szv is called at runtime with dynamic args.
2667
+ VariableDeclarator(path) {
2668
+ const init2 = path.node.init;
2669
+ if (!t.isCallExpression(init2)) {
2670
+ return;
2671
+ }
2672
+ if (!t.isIdentifier(init2.callee) || init2.callee.name !== "szv") {
2673
+ return;
2674
+ }
2675
+ if (init2.arguments.length === 0) {
2676
+ return;
2677
+ }
2678
+ if (!t.isIdentifier(path.node.id)) {
2679
+ return;
2680
+ }
2681
+ const configArg = init2.arguments[0];
2682
+ if (!t.isObjectExpression(configArg)) {
2683
+ return;
2684
+ }
2685
+ const config = evaluateStaticObject(configArg);
2686
+ if (!config) {
2687
+ return;
2688
+ }
2689
+ const base = config.base ?? {};
2690
+ const variants = config.variants ?? {};
2691
+ const classStrings = [];
2692
+ const baseResult = transform(base);
2693
+ const baseCls = typeof baseResult === "string" ? baseResult : baseResult.className;
2694
+ if (baseCls) {
2695
+ classStrings.push(baseCls);
2696
+ }
2697
+ for (const variantValues of Object.values(variants)) {
2698
+ for (const variantObj of Object.values(variantValues)) {
2699
+ if (!variantObj || typeof variantObj !== "object") {
2700
+ continue;
2701
+ }
2702
+ const merged = { ...base, ...variantObj };
2703
+ const result2 = transform(merged);
2704
+ const cls = typeof result2 === "string" ? result2 : result2.className;
2705
+ if (cls) {
2706
+ classStrings.push(cls);
2707
+ }
2708
+ }
2709
+ }
2710
+ if (classStrings.length === 0) {
2711
+ return;
2712
+ }
2713
+ for (const combined of classStrings) {
2714
+ for (const c of combined.split(/\s+/)) {
2715
+ if (c) {
2716
+ collectedClasses.add(c);
2717
+ }
2718
+ }
2719
+ }
2720
+ const catalogDecl = t.variableDeclaration("const", [
2721
+ t.variableDeclarator(
2722
+ t.identifier(`_szv_catalog_${path.node.id.name}`),
2723
+ t.arrayExpression(classStrings.map((s) => t.stringLiteral(s)))
2724
+ )
2725
+ ]);
2726
+ const parentPath = path.parentPath;
2727
+ if (parentPath && t.isVariableDeclaration(parentPath.node)) {
2728
+ parentPath.insertAfter(catalogDecl);
2729
+ transformed = true;
2730
+ }
2731
+ },
2732
+ // ── dynamic() literal extraction ──────────────────────────────────
2733
+ // Detects `dynamic({...})` and `dynamic(CONST_IDENTIFIER)` calls
2734
+ // with statically-analyzable arguments and adds the resulting
2735
+ // class tokens to collectedClasses so prescanAndWriteClasses()
2736
+ // includes them in csszyx-classes.html for Tailwind to scan.
2737
+ // This means dynamic() with static/const args works in Astro SSR
2738
+ // without needing client:* directives.
2739
+ CallExpression(path) {
2740
+ const callee = path.node.callee;
2741
+ if (!t.isIdentifier(callee) || callee.name !== "dynamic") {
2742
+ return;
2743
+ }
2744
+ if (path.node.arguments.length === 0) {
2745
+ return;
2746
+ }
2747
+ const arg = path.node.arguments[0];
2748
+ if (t.isObjectExpression(arg)) {
2749
+ const staticObj = evaluateStaticObject(arg);
2750
+ if (!staticObj) {
2751
+ return;
2752
+ }
2753
+ const { className } = transform(staticObj);
2754
+ for (const c of className.split(/\s+/)) {
2755
+ if (c) {
2756
+ collectedClasses.add(c);
2757
+ }
2758
+ }
2759
+ return;
2760
+ }
2761
+ let argExpr = arg;
2762
+ while (t.isTSAsExpression(argExpr) || t.isTSSatisfiesExpression(argExpr)) {
2763
+ argExpr = argExpr.expression;
2764
+ }
2765
+ if (t.isIdentifier(argExpr)) {
2766
+ const binding = path.scope.getBinding(argExpr.name);
2767
+ if (!binding) {
2768
+ return;
2769
+ }
2770
+ const declarator = binding.path.node;
2771
+ if (!t.isVariableDeclarator(declarator) || !declarator.init) {
2772
+ return;
2773
+ }
2774
+ let initExpr = declarator.init;
2775
+ while (t.isTSAsExpression(initExpr) || t.isTSSatisfiesExpression(initExpr)) {
2776
+ initExpr = initExpr.expression;
2777
+ }
2778
+ if (!t.isObjectExpression(initExpr)) {
2779
+ return;
2780
+ }
2781
+ const staticObj = evaluateStaticObject(initExpr);
2782
+ if (!staticObj) {
2783
+ return;
2784
+ }
2785
+ const { className } = transform(staticObj);
2786
+ for (const c of className.split(/\s+/)) {
2787
+ if (c) {
2788
+ collectedClasses.add(c);
2789
+ }
2790
+ }
2791
+ }
2358
2792
  }
2359
2793
  }
2360
2794
  };
@@ -2365,29 +2799,72 @@ function transformSourceCode(source) {
2365
2799
  code: result?.code || source,
2366
2800
  transformed,
2367
2801
  usesRuntime,
2802
+ usesMerge,
2368
2803
  usesColorVar,
2369
- classes: collectedClasses
2804
+ classes: collectedClasses,
2805
+ rawClassNames,
2806
+ diagnostics
2370
2807
  };
2371
2808
  } catch (e) {
2372
2809
  console.warn("[csszyx] AST transform failed, falling back to original code:", e);
2373
- return { code: source, transformed: false, usesRuntime: false, usesColorVar: false, classes: collectedClasses };
2810
+ return { code: source, transformed: false, usesRuntime: false, usesMerge: false, usesColorVar: false, classes: collectedClasses, rawClassNames, diagnostics };
2374
2811
  }
2375
2812
  }
2376
- function tryStaticTransformNode(node) {
2813
+ function parseStyleStringToObjectExpr(styleStr) {
2814
+ const props = styleStr.split(";").map((s) => s.trim()).filter(Boolean);
2815
+ const objProps = [];
2816
+ for (const prop of props) {
2817
+ const idx = prop.indexOf(":");
2818
+ if (idx > -1) {
2819
+ const k = prop.slice(0, idx).trim();
2820
+ const v = prop.slice(idx + 1).trim();
2821
+ let keyNode;
2822
+ if (k.startsWith("--")) {
2823
+ keyNode = t.stringLiteral(k);
2824
+ } else {
2825
+ const camel = k.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
2826
+ keyNode = t.identifier(camel);
2827
+ }
2828
+ objProps.push(t.objectProperty(keyNode, t.stringLiteral(v)));
2829
+ }
2830
+ }
2831
+ return t.objectExpression(objProps);
2832
+ }
2833
+ function tryStaticTransformNode(node, getBinding) {
2834
+ if (t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node)) {
2835
+ return tryStaticTransformNode(node.expression, getBinding);
2836
+ }
2377
2837
  if (t.isObjectExpression(node)) {
2378
- const staticObj = evaluateStaticObject(node);
2838
+ const resolved = getBinding ? resolveObjectSpreads(node, getBinding) ?? node : node;
2839
+ const staticObj = evaluateStaticObject(resolved);
2379
2840
  if (staticObj !== null) {
2380
2841
  const { className } = transform(staticObj);
2381
2842
  return t.stringLiteral(className);
2382
2843
  }
2844
+ if (getBinding) {
2845
+ const hoisted = tryHoistConditionalSpread(node, getBinding);
2846
+ if (hoisted !== null) {
2847
+ return hoisted;
2848
+ }
2849
+ }
2383
2850
  return null;
2384
2851
  }
2385
2852
  if (t.isStringLiteral(node)) {
2386
2853
  return node;
2387
2854
  }
2855
+ if (t.isIdentifier(node) && getBinding) {
2856
+ const binding = getBinding(node.name);
2857
+ if (binding && binding.path.isVariableDeclarator()) {
2858
+ const init2 = binding.path.node.init;
2859
+ if (init2) {
2860
+ return tryStaticTransformNode(init2, getBinding);
2861
+ }
2862
+ }
2863
+ return null;
2864
+ }
2388
2865
  if (t.isConditionalExpression(node)) {
2389
- const consequent = tryStaticTransformNode(node.consequent);
2390
- const alternate = tryStaticTransformNode(node.alternate);
2866
+ const consequent = tryStaticTransformNode(node.consequent, getBinding);
2867
+ const alternate = tryStaticTransformNode(node.alternate, getBinding);
2391
2868
  if (consequent !== null && alternate !== null) {
2392
2869
  return t.conditionalExpression(node.test, consequent, alternate);
2393
2870
  }
@@ -2395,6 +2872,39 @@ function tryStaticTransformNode(node) {
2395
2872
  }
2396
2873
  return null;
2397
2874
  }
2875
+ function tryHoistConditionalSpread(node, getBinding) {
2876
+ let conditionalSpreadIdx = -1;
2877
+ let conditionalExpr = null;
2878
+ for (let i = 0; i < node.properties.length; i++) {
2879
+ const prop = node.properties[i];
2880
+ if (!t.isSpreadElement(prop)) {
2881
+ continue;
2882
+ }
2883
+ if (t.isConditionalExpression(prop.argument)) {
2884
+ if (conditionalSpreadIdx !== -1) {
2885
+ return null;
2886
+ }
2887
+ conditionalSpreadIdx = i;
2888
+ conditionalExpr = prop.argument;
2889
+ } else {
2890
+ return null;
2891
+ }
2892
+ }
2893
+ if (conditionalSpreadIdx === -1 || conditionalExpr === null) {
2894
+ return null;
2895
+ }
2896
+ const otherProps = node.properties.filter((_, i) => i !== conditionalSpreadIdx);
2897
+ const mkObj = (branch) => t.objectExpression([t.spreadElement(branch), ...otherProps]);
2898
+ const resolvedA = tryStaticTransformNode(mkObj(conditionalExpr.consequent), getBinding);
2899
+ const resolvedB = tryStaticTransformNode(mkObj(conditionalExpr.alternate), getBinding);
2900
+ if (!resolvedA || !resolvedB) {
2901
+ return null;
2902
+ }
2903
+ if (!t.isStringLiteral(resolvedA) || !t.isStringLiteral(resolvedB)) {
2904
+ return null;
2905
+ }
2906
+ return t.conditionalExpression(conditionalExpr.test, resolvedA, resolvedB);
2907
+ }
2398
2908
  function evaluateStaticObject(node) {
2399
2909
  const result = {};
2400
2910
  for (const prop of node.properties) {
@@ -2409,6 +2919,8 @@ function evaluateStaticObject(node) {
2409
2919
  key = prop.key.name;
2410
2920
  } else if (t.isStringLiteral(prop.key)) {
2411
2921
  key = prop.key.value;
2922
+ } else if (t.isNumericLiteral(prop.key)) {
2923
+ key = String(prop.key.value);
2412
2924
  } else {
2413
2925
  return null;
2414
2926
  }
@@ -2433,10 +2945,82 @@ function evaluateStaticObject(node) {
2433
2945
  }
2434
2946
  return result;
2435
2947
  }
2948
+ function resolveObjectSpreads(node, getBinding) {
2949
+ const newProps = [];
2950
+ for (const prop of node.properties) {
2951
+ if (!t.isSpreadElement(prop)) {
2952
+ if (t.isObjectProperty(prop) && t.isObjectExpression(prop.value)) {
2953
+ const resolvedValue = resolveObjectSpreads(prop.value, getBinding);
2954
+ if (resolvedValue === null) {
2955
+ return null;
2956
+ }
2957
+ newProps.push(t.objectProperty(prop.key, resolvedValue, prop.computed, prop.shorthand));
2958
+ } else {
2959
+ newProps.push(prop);
2960
+ }
2961
+ continue;
2962
+ }
2963
+ const arg = prop.argument;
2964
+ if (!t.isIdentifier(arg)) {
2965
+ return null;
2966
+ }
2967
+ const binding = getBinding(arg.name);
2968
+ if (!binding || !binding.path.isVariableDeclarator()) {
2969
+ return null;
2970
+ }
2971
+ let init2 = binding.path.node.init;
2972
+ if (t.isTSAsExpression(init2) || t.isTSSatisfiesExpression(init2)) {
2973
+ init2 = init2.expression;
2974
+ }
2975
+ if (!t.isObjectExpression(init2)) {
2976
+ return null;
2977
+ }
2978
+ const inner = resolveObjectSpreads(init2, getBinding);
2979
+ if (inner === null) {
2980
+ return null;
2981
+ }
2982
+ newProps.push(...inner.properties);
2983
+ }
2984
+ return t.objectExpression(newProps);
2985
+ }
2986
+ function extractStaticLiteralValue(node) {
2987
+ if (t.isStringLiteral(node)) {
2988
+ return node.value;
2989
+ }
2990
+ if (t.isNumericLiteral(node)) {
2991
+ return node.value;
2992
+ }
2993
+ if (t.isBooleanLiteral(node)) {
2994
+ return node.value;
2995
+ }
2996
+ if (t.isUnaryExpression(node) && node.operator === "-" && t.isNumericLiteral(node.argument)) {
2997
+ return -node.argument.value;
2998
+ }
2999
+ return null;
3000
+ }
3001
+ function buildConditionalClassExpr(baseClasses, conditionalClasses) {
3002
+ if (conditionalClasses.length === 0) {
3003
+ return t.stringLiteral(baseClasses);
3004
+ }
3005
+ const makeCondExpr = (cc) => t.conditionalExpression(cc.test, t.stringLiteral(cc.consequent), t.stringLiteral(cc.alternate));
3006
+ if (conditionalClasses.length === 1 && !baseClasses) {
3007
+ return makeCondExpr(conditionalClasses[0]);
3008
+ }
3009
+ const quasis = [];
3010
+ const exprs = [];
3011
+ for (let i = 0; i < conditionalClasses.length; i++) {
3012
+ const prefix = i === 0 ? baseClasses ? baseClasses + " " : "" : " ";
3013
+ quasis.push(t.templateElement({ raw: prefix, cooked: prefix }, false));
3014
+ exprs.push(makeCondExpr(conditionalClasses[i]));
3015
+ }
3016
+ quasis.push(t.templateElement({ raw: "", cooked: "" }, true));
3017
+ return t.templateLiteral(quasis, exprs);
3018
+ }
2436
3019
  function evaluatePartialObject(node, variantChain = "") {
2437
3020
  const staticProps = {};
2438
3021
  const dynamicProps = /* @__PURE__ */ new Map();
2439
3022
  const rawClasses = [];
3023
+ const conditionalClasses = [];
2440
3024
  let usesColorVar = false;
2441
3025
  for (const prop of node.properties) {
2442
3026
  if (t.isSpreadElement(prop)) {
@@ -2453,6 +3037,8 @@ function evaluatePartialObject(node, variantChain = "") {
2453
3037
  key = prop.key.name;
2454
3038
  } else if (t.isStringLiteral(prop.key)) {
2455
3039
  key = prop.key.value;
3040
+ } else if (t.isNumericLiteral(prop.key)) {
3041
+ key = String(prop.key.value);
2456
3042
  } else {
2457
3043
  return null;
2458
3044
  }
@@ -2495,7 +3081,7 @@ function evaluatePartialObject(node, variantChain = "") {
2495
3081
  staticProps[key] = { color: colorStr, op: opVal };
2496
3082
  } else if (t.isExpression(opProp.value)) {
2497
3083
  const variantPfx = variantChain ? `${variantChain}:` : "";
2498
- rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/[var(${opVarName})]`);
3084
+ rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/(${opVarName})`);
2499
3085
  dynamicProps.set(uniqueKey, {
2500
3086
  expression: opProp.value,
2501
3087
  category: 3 /* UNITLESS */,
@@ -2534,6 +3120,7 @@ function evaluatePartialObject(node, variantChain = "") {
2534
3120
  dynamicProps.set(k, v);
2535
3121
  }
2536
3122
  rawClasses.push(...nestedResult.rawClasses);
3123
+ conditionalClasses.push(...nestedResult.conditionalClasses);
2537
3124
  if (nestedResult.usesColorVar) {
2538
3125
  usesColorVar = true;
2539
3126
  }
@@ -2542,6 +3129,25 @@ function evaluatePartialObject(node, variantChain = "") {
2542
3129
  }
2543
3130
  }
2544
3131
  }
3132
+ } else if (t.isConditionalExpression(value)) {
3133
+ const consVal = extractStaticLiteralValue(value.consequent);
3134
+ const altVal = extractStaticLiteralValue(value.alternate);
3135
+ if (consVal !== null && altVal !== null) {
3136
+ const { className: classA } = transform({ [key]: consVal });
3137
+ const { className: classB } = transform({ [key]: altVal });
3138
+ const vPfx = variantChain ? getVariantPrefix(variantChain) + ":" : "";
3139
+ const prefixed = (cls) => vPfx ? cls.split(/\s+/).filter(Boolean).map((c) => vPfx + c).join(" ") : cls;
3140
+ conditionalClasses.push({ test: value.test, consequent: prefixed(classA), alternate: prefixed(classB) });
3141
+ } else {
3142
+ const twPrefix = PROPERTY_MAP[key] || key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
3143
+ const category = getPropertyCategory(key);
3144
+ const varName = getCSSVariableName(key, variantChain || void 0);
3145
+ const uniqueKey = variantChain ? `${variantChain}-${key}` : key;
3146
+ if (COLOR_PROPERTIES.has(key)) {
3147
+ usesColorVar = true;
3148
+ }
3149
+ dynamicProps.set(uniqueKey, { expression: value, category, varName, twPrefix, variantChain: variantChain || "" });
3150
+ }
2545
3151
  } else if (t.isExpression(value)) {
2546
3152
  const twPrefix = PROPERTY_MAP[key] || key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2547
3153
  const category = getPropertyCategory(key);
@@ -2561,7 +3167,7 @@ function evaluatePartialObject(node, variantChain = "") {
2561
3167
  return null;
2562
3168
  }
2563
3169
  }
2564
- return { staticProps, dynamicProps, rawClasses, hasSpread: false, usesColorVar };
3170
+ return { staticProps, dynamicProps, rawClasses, conditionalClasses, hasSpread: false, usesColorVar };
2565
3171
  }
2566
3172
  function generateStyleValueExpression(info) {
2567
3173
  const { expression, category } = info;
@@ -2624,7 +3230,7 @@ function collectFromExpr(node, classes) {
2624
3230
  function buildCSSVarClassName(info) {
2625
3231
  const { twPrefix, varName, variantChain } = info;
2626
3232
  const variantPrefix = variantChain ? `${getVariantPrefix(variantChain)}:` : "";
2627
- return `${variantPrefix}${twPrefix}-[var(${varName})]`;
3233
+ return `${variantPrefix}${twPrefix}-(${varName})`;
2628
3234
  }
2629
3235
 
2630
3236
  // src/compiler.ts
@@ -3089,12 +3695,16 @@ function mergeOptions(options = {}) {
3089
3695
  };
3090
3696
  }
3091
3697
  export {
3698
+ BOOLEAN_SHORTHANDS,
3092
3699
  COLOR_PROPERTIES,
3093
3700
  CsszyxCompiler,
3094
3701
  DEFAULT_COMPILER_OPTIONS,
3702
+ KNOWN_VARIANTS,
3095
3703
  ManifestBuilder,
3096
3704
  PROPERTY_CATEGORY_MAP,
3705
+ PROPERTY_MAP,
3097
3706
  PropertyCategory,
3707
+ SUGGESTION_MAP,
3098
3708
  VERSION,
3099
3709
  buildParentMap,
3100
3710
  createRecoveryToken,