@csszyx/compiler 0.3.0 → 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.cjs CHANGED
@@ -30,12 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ BOOLEAN_SHORTHANDS: () => BOOLEAN_SHORTHANDS,
33
34
  COLOR_PROPERTIES: () => COLOR_PROPERTIES,
34
35
  CsszyxCompiler: () => CsszyxCompiler,
35
36
  DEFAULT_COMPILER_OPTIONS: () => DEFAULT_COMPILER_OPTIONS,
37
+ KNOWN_VARIANTS: () => KNOWN_VARIANTS,
36
38
  ManifestBuilder: () => ManifestBuilder,
37
39
  PROPERTY_CATEGORY_MAP: () => PROPERTY_CATEGORY_MAP,
40
+ PROPERTY_MAP: () => PROPERTY_MAP,
38
41
  PropertyCategory: () => PropertyCategory,
42
+ SUGGESTION_MAP: () => SUGGESTION_MAP,
39
43
  VERSION: () => VERSION,
40
44
  buildParentMap: () => buildParentMap,
41
45
  createRecoveryToken: () => createRecoveryToken,
@@ -230,7 +234,8 @@ var PROPERTY_CATEGORY_MAP = {
230
234
  skewY: 4 /* ANGLE */,
231
235
  // ---- DURATION ----
232
236
  duration: 5 /* DURATION */,
233
- delay: 5 /* DURATION */
237
+ delay: 5 /* DURATION */,
238
+ animationDelay: 5 /* DURATION */
234
239
  };
235
240
  function getPropertyCategory(property) {
236
241
  return PROPERTY_CATEGORY_MAP[property] ?? 7 /* PASSTHROUGH */;
@@ -274,6 +279,9 @@ function isValidColorString(value) {
274
279
  if (/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*-\d+$/.test(value)) {
275
280
  return true;
276
281
  }
282
+ if (/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z][a-zA-Z0-9]*)*$/.test(value)) {
283
+ return true;
284
+ }
277
285
  return false;
278
286
  }
279
287
  function hasSlashOpacity(value) {
@@ -376,11 +384,13 @@ var PROPERTY_MAP = {
376
384
  outlineColor: "outline",
377
385
  outlineOffset: "outline-offset",
378
386
  outlineStyle: "outline",
379
- // Ring (v3 keep for future v3 support)
387
+ // Ring (v4: outset ring + inset ring)
380
388
  ring: "ring",
381
389
  ringColor: "ring",
382
390
  ringOffset: "ring-offset",
383
391
  ringOffsetColor: "ring-offset",
392
+ insetRing: "inset-ring",
393
+ insetRingColor: "inset-ring",
384
394
  // Spacing (canonical shorthand only)
385
395
  p: "p",
386
396
  pt: "pt",
@@ -560,6 +570,7 @@ var PROPERTY_MAP = {
560
570
  rotateX: "rotate-x",
561
571
  rotateY: "rotate-y",
562
572
  rotateZ: "rotate-z",
573
+ translate: "translate",
563
574
  translateX: "translate-x",
564
575
  translateY: "translate-y",
565
576
  translateZ: "translate-z",
@@ -578,6 +589,8 @@ var PROPERTY_MAP = {
578
589
  ease: "ease",
579
590
  delay: "delay",
580
591
  animate: "animate",
592
+ animationDelay: "animation-delay",
593
+ // animation-delay — distinct from transition delay
581
594
  // Masks
582
595
  mask: "mask",
583
596
  maskSize: "mask-size",
@@ -984,13 +997,7 @@ var BOOLEAN_SHORTHANDS = /* @__PURE__ */ new Set([
984
997
  // Ring (v3 future)
985
998
  "ring",
986
999
  // Outline
987
- "outline",
988
- // Transforms
989
- "scale3d",
990
- "rotate3d",
991
- "translate3d",
992
- "transformGpu",
993
- "transformCpu"
1000
+ "outline"
994
1001
  ]);
995
1002
  var BOOLEAN_TO_CLASS = {
996
1003
  inlineBlock: "inline-block",
@@ -1023,9 +1030,6 @@ var BOOLEAN_TO_CLASS = {
1023
1030
  diagonalFractions: "diagonal-fractions",
1024
1031
  stackedFractions: "stacked-fractions",
1025
1032
  // Transforms
1026
- scale3d: "scale-3d",
1027
- rotate3d: "rotate-3d",
1028
- translate3d: "translate-3d",
1029
1033
  transformGpu: "transform-gpu",
1030
1034
  transformCpu: "transform-cpu",
1031
1035
  // Misc
@@ -1092,6 +1096,7 @@ var NEGATIVE_ALLOWED = /* @__PURE__ */ new Set([
1092
1096
  "scale-z",
1093
1097
  "skew-x",
1094
1098
  "skew-y",
1099
+ "translate",
1095
1100
  "translate-x",
1096
1101
  "translate-y",
1097
1102
  "translate-z",
@@ -1118,7 +1123,10 @@ function handleImportant(value) {
1118
1123
  }
1119
1124
  function formatOpacity(op) {
1120
1125
  if (typeof op === "number") {
1121
- return String(op);
1126
+ if (Number.isInteger(op * 2)) {
1127
+ return String(op);
1128
+ }
1129
+ return `[${op}]`;
1122
1130
  }
1123
1131
  if (typeof op === "string") {
1124
1132
  if (op.startsWith("--")) {
@@ -1135,7 +1143,8 @@ function normalizeArbitraryVariant(key) {
1135
1143
  return key.replace(/\s+/g, "");
1136
1144
  }
1137
1145
  function normalizeArbitraryValue(value) {
1138
- return value.trim().replace(/\s+/g, "_");
1146
+ const stripped = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1147
+ return stripped.trim().replace(/\s+/g, "_");
1139
1148
  }
1140
1149
  var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1141
1150
  // Sizing (both rawKey and resolved key forms)
@@ -1173,6 +1182,7 @@ var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1173
1182
  "start",
1174
1183
  "end",
1175
1184
  // Translate
1185
+ "translate",
1176
1186
  "translate-x",
1177
1187
  "translateX",
1178
1188
  "translate-y",
@@ -1181,21 +1191,22 @@ var FRACTION_SUPPORTED_PROPS = /* @__PURE__ */ new Set([
1181
1191
  "aspect"
1182
1192
  ]);
1183
1193
  function needsArbitraryBrackets(value) {
1184
- 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
1185
- /^-\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
1186
- /^\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(value) || // Values starting with . like .25em
1187
- /^-\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(value) || // Negative values starting with -. like -.25em
1188
- value.startsWith("#") || // Hex colors
1189
- value.startsWith("rgb") || // RGB colors
1190
- value.startsWith("hsl") || // HSL colors
1191
- value.includes("calc(") || // Calculations
1192
- value.includes("var(") || // CSS variables (old syntax)
1193
- value.includes("attr(") || // attr() function
1194
- value.includes("url(") || // URLs
1195
- value.includes("clamp(") || // Clamp
1196
- value.includes("min(") || // Min
1197
- value.includes("max(") || // Max
1198
- value.includes(" ");
1194
+ const v = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1195
+ 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
1196
+ /^-\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
1197
+ /^\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(v) || // Values starting with . like .25em
1198
+ /^-\.\d+(px|rem|em|%|vh|vw|ch)?$/.test(v) || // Negative values starting with -. like -.25em
1199
+ v.startsWith("#") || // Hex colors
1200
+ v.startsWith("rgb") || // RGB colors
1201
+ v.startsWith("hsl") || // HSL colors
1202
+ v.includes("calc(") || // Calculations
1203
+ v.includes("var(") || // CSS variables (old syntax)
1204
+ v.includes("attr(") || // attr() function
1205
+ v.includes("url(") || // URLs
1206
+ v.includes("clamp(") || // Clamp
1207
+ v.includes("min(") || // Min
1208
+ v.includes("max(") || // Max
1209
+ v.includes(" ");
1199
1210
  }
1200
1211
  var LIST_STYLE_STANDARD = /* @__PURE__ */ new Set(["none", "disc", "decimal"]);
1201
1212
  var FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set([
@@ -1209,11 +1220,12 @@ var FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set([
1209
1220
  "extra-expanded",
1210
1221
  "ultra-expanded"
1211
1222
  ]);
1212
- var NEEDS_ARBITRARY_PROPERTY = /* @__PURE__ */ new Set([
1213
- "mask-type",
1214
- "mask-composite",
1215
- "mask-mode"
1216
- ]);
1223
+ function camelToKebab(prop) {
1224
+ if (prop.startsWith("--")) {
1225
+ return prop;
1226
+ }
1227
+ return prop.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1228
+ }
1217
1229
  function getVariantPrefix(key) {
1218
1230
  if (VARIANT_MAP[key]) {
1219
1231
  return VARIANT_MAP[key];
@@ -1239,6 +1251,32 @@ function handleGroupPeer(type, nestedObj, prefix) {
1239
1251
  }
1240
1252
  continue;
1241
1253
  }
1254
+ if (nestedKey === "data" && typeof nestedValue === "object") {
1255
+ for (const [attr, attrValue] of Object.entries(nestedValue)) {
1256
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1257
+ continue;
1258
+ }
1259
+ const variantPrefix = `${prefix}${type}-data-[${attr}]:`;
1260
+ const result = transform(attrValue, variantPrefix);
1261
+ if (result.className) {
1262
+ classes.push(result.className);
1263
+ }
1264
+ }
1265
+ continue;
1266
+ }
1267
+ if (nestedKey === "aria" && typeof nestedValue === "object") {
1268
+ for (const [attr, attrValue] of Object.entries(nestedValue)) {
1269
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1270
+ continue;
1271
+ }
1272
+ const variantPrefix = ARIA_STATES.has(attr) ? `${prefix}${type}-aria-${attr}:` : `${prefix}${type}-aria-[${attr}]:`;
1273
+ const result = transform(attrValue, variantPrefix);
1274
+ if (result.className) {
1275
+ classes.push(result.className);
1276
+ }
1277
+ }
1278
+ continue;
1279
+ }
1242
1280
  const isVariant = KNOWN_VARIANTS.has(nestedKey) || KNOWN_VARIANTS.has(getVariantPrefix(nestedKey));
1243
1281
  const isArbitrary = nestedKey.startsWith(".") || nestedKey.startsWith("#") || nestedKey.startsWith("[") || nestedKey.startsWith(":");
1244
1282
  if (isArbitrary) {
@@ -1259,6 +1297,33 @@ function handleGroupPeer(type, nestedObj, prefix) {
1259
1297
  if (stateValue === null || stateValue === void 0 || stateValue === false) {
1260
1298
  continue;
1261
1299
  }
1300
+ if (state === "data" && typeof stateValue === "object") {
1301
+ for (const [attr, attrValue] of Object.entries(stateValue)) {
1302
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1303
+ continue;
1304
+ }
1305
+ const variantPrefix2 = `${prefix}${type}-data-[${attr}]/${nestedKey}:`;
1306
+ const result2 = transform(attrValue, variantPrefix2);
1307
+ if (result2.className) {
1308
+ classes.push(result2.className);
1309
+ }
1310
+ }
1311
+ continue;
1312
+ }
1313
+ if (state === "aria" && typeof stateValue === "object") {
1314
+ for (const [attr, attrValue] of Object.entries(stateValue)) {
1315
+ if (attrValue === null || attrValue === void 0 || attrValue === false) {
1316
+ continue;
1317
+ }
1318
+ const ariaSegment = ARIA_STATES.has(attr) ? `aria-${attr}` : `aria-[${attr}]`;
1319
+ const variantPrefix2 = `${prefix}${type}-${ariaSegment}/${nestedKey}:`;
1320
+ const result2 = transform(attrValue, variantPrefix2);
1321
+ if (result2.className) {
1322
+ classes.push(result2.className);
1323
+ }
1324
+ }
1325
+ continue;
1326
+ }
1262
1327
  const mappedState = getVariantPrefix(state);
1263
1328
  const variantPrefix = `${prefix}${type}-${mappedState}/${nestedKey}:`;
1264
1329
  const result = transform(stateValue, variantPrefix);
@@ -1380,6 +1445,18 @@ function transform(szProp, prefix = "", mangleMap) {
1380
1445
  if (value === null || value === void 0) {
1381
1446
  continue;
1382
1447
  }
1448
+ if (rawKey === "css") {
1449
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1450
+ for (const [cssProp, cssVal] of Object.entries(value)) {
1451
+ if (cssVal === null || cssVal === void 0) {
1452
+ continue;
1453
+ }
1454
+ const kebab = camelToKebab(cssProp);
1455
+ classes.push(`${prefix}[${kebab}:${normalizeArbitraryValue(String(cssVal))}]`);
1456
+ }
1457
+ }
1458
+ continue;
1459
+ }
1383
1460
  if (rawKey.startsWith("@") && typeof value === "string") {
1384
1461
  const mappedKey = VARIANT_MAP[rawKey] || rawKey;
1385
1462
  classes.push(`${prefix}${mappedKey}/${value}`);
@@ -1450,10 +1527,11 @@ function transform(szProp, prefix = "", mangleMap) {
1450
1527
  classes.push(`${prefix}${rawKey}/${value}`);
1451
1528
  continue;
1452
1529
  }
1453
- if (typeof value === "object" && value !== null && !Array.isArray(value) && "color" in value) {
1530
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && rawKey in PROPERTY_MAP && "color" in value) {
1454
1531
  const colorObj = value;
1455
1532
  const twPrefix = PROPERTY_MAP[rawKey] || rawKey.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1456
- const colorBase = String(colorObj.color);
1533
+ const rawColorBase = String(colorObj.color);
1534
+ const colorBase = rawColorBase.startsWith("--") ? `(${rawColorBase})` : needsArbitraryBrackets(rawColorBase) ? `[${normalizeArbitraryValue(rawColorBase)}]` : normalizeArbitraryValue(rawColorBase);
1457
1535
  if (colorObj.op !== void 0) {
1458
1536
  const opStr = formatOpacity(colorObj.op);
1459
1537
  classes.push(`${prefix}${twPrefix}-${colorBase}/${opStr}`);
@@ -1812,7 +1890,7 @@ function transform(szProp, prefix = "", mangleMap) {
1812
1890
  }
1813
1891
  if (rawKey === "decorationThickness" || rawKey === "textDecorationThickness") {
1814
1892
  if (needsArbitraryBrackets(value)) {
1815
- className += `decoration-[${value}]`;
1893
+ className += `decoration-[${normalizeArbitraryValue(value)}]`;
1816
1894
  } else if (value.startsWith("--")) {
1817
1895
  className += `decoration-(${value})`;
1818
1896
  } else {
@@ -1863,7 +1941,9 @@ function transform(szProp, prefix = "", mangleMap) {
1863
1941
  if (rawKey === "brightness" || rawKey === "contrast" || rawKey === "saturate" || rawKey === "scale" || rawKey === "backdropBrightness" || rawKey === "backdropContrast" || rawKey === "backdropSaturate") {
1864
1942
  const prop = rawKey.startsWith("backdrop") ? `backdrop-${rawKey.slice(8).toLowerCase()}` : rawKey;
1865
1943
  const sValue = String(value);
1866
- if (sValue.startsWith("--")) {
1944
+ if (sValue === "3d" && rawKey === "scale") {
1945
+ classes.push(`${prefix}scale-3d`);
1946
+ } else if (sValue.startsWith("--")) {
1867
1947
  classes.push(`${prop}-(${sValue})`);
1868
1948
  } else {
1869
1949
  classes.push(`${prop}-[${sValue}]`);
@@ -1903,38 +1983,53 @@ function transform(szProp, prefix = "", mangleMap) {
1903
1983
  if (rawKey === "bgImg") {
1904
1984
  const v = String(value).trim();
1905
1985
  if (v === "none") {
1906
- classes.push("bg-none");
1986
+ classes.push(`${prefix}bg-none`);
1907
1987
  continue;
1908
1988
  }
1909
1989
  const vNorm = v.startsWith("-") ? v.slice(1) : v;
1990
+ if (vNorm.startsWith("repeating-")) {
1991
+ classes.push(`${prefix}bg-[${normalizeArbitraryValue(v)}]`);
1992
+ continue;
1993
+ }
1910
1994
  if (vNorm.startsWith("linear-") || vNorm.startsWith("radial") || vNorm.startsWith("conic") || vNorm.startsWith("gradient-to-")) {
1911
1995
  const vMapped = vNorm.startsWith("gradient-to-") ? vNorm.replace("gradient-to-", "linear-to-") : vNorm;
1912
1996
  if (v.startsWith("-")) {
1913
- classes.push(`-bg-${vMapped}`);
1997
+ classes.push(`${prefix}-bg-${vMapped}`);
1914
1998
  } else {
1915
- classes.push(`bg-${vMapped}`);
1999
+ classes.push(`${prefix}bg-${vMapped}`);
1916
2000
  }
1917
2001
  continue;
1918
2002
  }
1919
2003
  if (v.startsWith("--")) {
1920
- classes.push(`bg-(image:${v})`);
2004
+ classes.push(`${prefix}bg-(image:${v})`);
1921
2005
  continue;
1922
2006
  }
1923
2007
  if (v.startsWith("url(")) {
1924
- classes.push(`bg-[${v}]`);
2008
+ classes.push(`${prefix}bg-[${v}]`);
1925
2009
  continue;
1926
2010
  }
1927
- classes.push(`bg-[url(${v})]`);
2011
+ classes.push(`${prefix}bg-[url(${v})]`);
1928
2012
  continue;
1929
2013
  }
1930
2014
  if (rawKey === "bgPos") {
1931
2015
  const sVal = String(value);
1932
2016
  if (sVal.startsWith("--")) {
1933
- classes.push(`bg-(${sVal})`);
2017
+ classes.push(`${prefix}bg-(${sVal})`);
1934
2018
  } else if (sVal.includes("_") || needsArbitraryBrackets(sVal)) {
1935
- classes.push(`bg-[${normalizeArbitraryValue(sVal)}]`);
2019
+ classes.push(`${prefix}bg-[${normalizeArbitraryValue(sVal)}]`);
1936
2020
  } else {
1937
- classes.push(`bg-${sVal}`);
2021
+ classes.push(`${prefix}bg-${sVal}`);
2022
+ }
2023
+ continue;
2024
+ }
2025
+ if (rawKey === "bgSize") {
2026
+ const sVal = String(value);
2027
+ if (sVal === "auto" || sVal === "cover" || sVal === "contain") {
2028
+ classes.push(`${prefix}bg-${sVal}`);
2029
+ } else if (sVal.startsWith("--")) {
2030
+ classes.push(`${prefix}bg-size-(${sVal})`);
2031
+ } else {
2032
+ classes.push(`${prefix}bg-size-[${normalizeArbitraryValue(sVal)}]`);
1938
2033
  }
1939
2034
  continue;
1940
2035
  }
@@ -1960,7 +2055,8 @@ function transform(szProp, prefix = "", mangleMap) {
1960
2055
  } else if (value === "no-repeat") {
1961
2056
  className += "bg-no-repeat";
1962
2057
  } else {
1963
- className += `bg-repeat-${value}`;
2058
+ const suffix = value.startsWith("repeat-") ? value.slice(7) : value;
2059
+ className += `bg-repeat-${suffix}`;
1964
2060
  }
1965
2061
  classes.push(className);
1966
2062
  continue;
@@ -1990,16 +2086,19 @@ function transform(szProp, prefix = "", mangleMap) {
1990
2086
  classes.push(className);
1991
2087
  continue;
1992
2088
  }
1993
- if (rawKey === "content" || rawKey === "alignContent") {
1994
- const ALIGN_CONTENT_KEYWORDS = /* @__PURE__ */ new Set(["normal", "center", "start", "end", "between", "around", "evenly", "baseline", "stretch"]);
1995
- if (ALIGN_CONTENT_KEYWORDS.has(value)) {
1996
- className += `content-${value}`;
2089
+ if (rawKey === "alignContent") {
2090
+ className += `content-${value}`;
2091
+ classes.push(className);
2092
+ continue;
2093
+ }
2094
+ if (rawKey === "content") {
2095
+ if (value === "none") {
2096
+ className += "content-none";
1997
2097
  } else if (value.startsWith("--")) {
1998
2098
  className += `content-(${value})`;
1999
- } else if (!["none", "empty"].includes(value)) {
2000
- className += `content-[${value}]`;
2001
2099
  } else {
2002
- className += `content-${value}`;
2100
+ const inner = value.startsWith('"') && value.endsWith('"') && value.length >= 2 ? `'${value.slice(1, -1)}'` : value;
2101
+ className += `content-[${inner}]`;
2003
2102
  }
2004
2103
  classes.push(className);
2005
2104
  continue;
@@ -2052,16 +2151,12 @@ function transform(szProp, prefix = "", mangleMap) {
2052
2151
  const sVal = String(value);
2053
2152
  const prop = PROPERTY_MAP[rawKey] || rawKey;
2054
2153
  if (needsArbitraryBrackets(sVal) || sVal.includes("(") || sVal.includes("_") || sVal.includes("%")) {
2055
- classes.push(`${prop}-[${normalizeArbitraryValue(sVal)}]`);
2154
+ classes.push(`${className}${prop}-[${normalizeArbitraryValue(sVal)}]`);
2056
2155
  continue;
2057
2156
  }
2058
2157
  }
2059
2158
  if (rawKey === "transformStyle") {
2060
- if (value === "preserve-3d" || value === "3d") {
2061
- className += "transform-3d";
2062
- } else {
2063
- className += `transform-${value}`;
2064
- }
2159
+ className += `transform-${value}`;
2065
2160
  classes.push(className);
2066
2161
  continue;
2067
2162
  }
@@ -2072,7 +2167,7 @@ function transform(szProp, prefix = "", mangleMap) {
2072
2167
  } else if (value.startsWith("--")) {
2073
2168
  className += `perspective-(${value})`;
2074
2169
  } else if (needsArbitraryBrackets(value)) {
2075
- className += `perspective-[${value}]`;
2170
+ className += `perspective-[${normalizeArbitraryValue(value)}]`;
2076
2171
  } else {
2077
2172
  className += `perspective-${value}`;
2078
2173
  }
@@ -2096,7 +2191,7 @@ function transform(szProp, prefix = "", mangleMap) {
2096
2191
  }
2097
2192
  }
2098
2193
  if (process.env.NODE_ENV !== "production" && typeof window === "undefined") {
2099
- 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)
2194
+ 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)
2100
2195
  KNOWN_VARIANTS.has(rawKey) || // Groups/Peers
2101
2196
  rawKey === "group" || rawKey === "peer" || // Special variant objects
2102
2197
  rawKey === "has" || rawKey === "not" || rawKey === "data" || rawKey === "aria" || rawKey === "supports" || rawKey === "min" || rawKey === "max";
@@ -2123,6 +2218,11 @@ function transform(szProp, prefix = "", mangleMap) {
2123
2218
  classes.push(className);
2124
2219
  continue;
2125
2220
  }
2221
+ if (rawKey === "animationDelay") {
2222
+ const ms = typeof value === "number" ? `${value}ms` : String(value);
2223
+ classes.push(`${className}[animation-delay:${ms}]`);
2224
+ continue;
2225
+ }
2126
2226
  if (typeof value === "number") {
2127
2227
  if (value < 0 && NEGATIVE_ALLOWED.has(key)) {
2128
2228
  className += `-${key}-${Math.abs(value)}`;
@@ -2135,15 +2235,6 @@ function transform(szProp, prefix = "", mangleMap) {
2135
2235
  if (typeof value === "string") {
2136
2236
  const { value: cleanValue, important } = handleImportant(value);
2137
2237
  let finalValue = cleanValue;
2138
- if (NEEDS_ARBITRARY_PROPERTY.has(key)) {
2139
- const arbitraryProp = `[${key}:${finalValue}]`;
2140
- className += arbitraryProp;
2141
- if (important) {
2142
- className += "!";
2143
- }
2144
- classes.push(className);
2145
- continue;
2146
- }
2147
2238
  if (finalValue.startsWith("--")) {
2148
2239
  const typeHint = CSS_VAR_TYPE_HINTS[rawKey];
2149
2240
  if (typeHint) {
@@ -2177,7 +2268,7 @@ function transform(szProp, prefix = "", mangleMap) {
2177
2268
  }
2178
2269
  }
2179
2270
  let mergedClasses = classes;
2180
- const textSizePattern = /^((?:[a-z0-9\-[\]@/:]*:)*)text-(.+)$/;
2271
+ const textSizePattern = /^((?:[a-z0-9\-[\]@/:]*:)*)text-(xs|sm|base|lg|[2-9]?xl|\[.+\]|\(.+\))$/;
2181
2272
  const leadingPattern = /^((?:[a-z0-9\-[\]@/:]*:)*)leading-(.+)$/;
2182
2273
  const textEntries = [];
2183
2274
  const leadingEntries = [];
@@ -2194,11 +2285,15 @@ function transform(szProp, prefix = "", mangleMap) {
2194
2285
  }
2195
2286
  if (textEntries.length > 0 && leadingEntries.length > 0) {
2196
2287
  const removeIndices = /* @__PURE__ */ new Set();
2288
+ const consumedLeading = /* @__PURE__ */ new Set();
2197
2289
  for (const te of textEntries) {
2198
- const matchingLeading = leadingEntries.find((le) => le.prefix === te.prefix);
2290
+ const matchingLeading = leadingEntries.find(
2291
+ (le) => le.prefix === te.prefix && !consumedLeading.has(le.index)
2292
+ );
2199
2293
  if (matchingLeading) {
2200
2294
  mergedClasses[te.index] = `${te.prefix}text-${te.size}/${matchingLeading.value}`;
2201
2295
  removeIndices.add(matchingLeading.index);
2296
+ consumedLeading.add(matchingLeading.index);
2202
2297
  }
2203
2298
  }
2204
2299
  if (removeIndices.size > 0) {
@@ -2228,11 +2323,14 @@ function normalizeClassName(className) {
2228
2323
  // src/transform.ts
2229
2324
  function transformSourceCode(source) {
2230
2325
  let usesRuntime = false;
2326
+ let usesMerge = false;
2231
2327
  let usesColorVar = false;
2232
2328
  let transformed = false;
2233
2329
  const collectedClasses = /* @__PURE__ */ new Set();
2330
+ const rawClassNames = /* @__PURE__ */ new Set();
2331
+ const diagnostics = [];
2234
2332
  if (!source.includes("sz")) {
2235
- return { code: source, transformed: false, usesRuntime: false, usesColorVar: false, classes: collectedClasses };
2333
+ return { code: source, transformed: false, usesRuntime: false, usesMerge: false, usesColorVar: false, classes: collectedClasses, rawClassNames, diagnostics };
2236
2334
  }
2237
2335
  try {
2238
2336
  const result = babel.transformSync(source, {
@@ -2256,7 +2354,7 @@ function transformSourceCode(source) {
2256
2354
  if (t.isStringLiteral(val)) {
2257
2355
  for (const c of val.value.split(/\s+/)) {
2258
2356
  if (c) {
2259
- collectedClasses.add(c);
2357
+ rawClassNames.add(c);
2260
2358
  }
2261
2359
  }
2262
2360
  }
@@ -2266,6 +2364,99 @@ function transformSourceCode(source) {
2266
2364
  return;
2267
2365
  }
2268
2366
  const value = path.node.value;
2367
+ let existingClassNameNode = null;
2368
+ let existingClassExpr = null;
2369
+ let existingStyleNode = null;
2370
+ let existingStyleExpr = null;
2371
+ if (path.parentPath?.isJSXOpeningElement()) {
2372
+ for (const attr of path.parentPath.node.attributes) {
2373
+ if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
2374
+ const aName = attr.name;
2375
+ if (aName.name === "className" || aName.name === "class") {
2376
+ existingClassNameNode = attr;
2377
+ const aVal = attr.value;
2378
+ if (t.isStringLiteral(aVal)) {
2379
+ existingClassExpr = aVal;
2380
+ } else if (t.isJSXExpressionContainer(aVal)) {
2381
+ if (t.isExpression(aVal.expression)) {
2382
+ existingClassExpr = aVal.expression;
2383
+ }
2384
+ }
2385
+ } else if (aName.name === "style") {
2386
+ existingStyleNode = attr;
2387
+ const aVal = attr.value;
2388
+ if (t.isJSXExpressionContainer(aVal)) {
2389
+ if (t.isExpression(aVal.expression)) {
2390
+ existingStyleExpr = aVal.expression;
2391
+ }
2392
+ } else if (t.isStringLiteral(aVal)) {
2393
+ existingStyleExpr = aVal;
2394
+ }
2395
+ }
2396
+ }
2397
+ }
2398
+ }
2399
+ const createMergedClassNameValue = (szExpr) => {
2400
+ if (!existingClassExpr) {
2401
+ return t.isStringLiteral(szExpr) ? szExpr : t.jsxExpressionContainer(szExpr);
2402
+ }
2403
+ if (existingClassNameNode && path.parentPath?.isJSXOpeningElement()) {
2404
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2405
+ (a) => a !== existingClassNameNode
2406
+ );
2407
+ existingClassNameNode = null;
2408
+ }
2409
+ if (t.isStringLiteral(existingClassExpr) && t.isStringLiteral(szExpr)) {
2410
+ const merged = `${existingClassExpr.value} ${szExpr.value}`.trim();
2411
+ return t.stringLiteral(merged);
2412
+ }
2413
+ usesRuntime = true;
2414
+ usesMerge = true;
2415
+ return t.jsxExpressionContainer(
2416
+ t.callExpression(t.identifier("_szMerge"), [existingClassExpr, szExpr])
2417
+ );
2418
+ };
2419
+ const mergeAndInjectStyle = (newStyleProps) => {
2420
+ if (newStyleProps.length === 0) {
2421
+ return;
2422
+ }
2423
+ if (!path.parentPath?.isJSXOpeningElement()) {
2424
+ return;
2425
+ }
2426
+ if (existingStyleNode && existingStyleExpr) {
2427
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2428
+ (a) => a !== existingStyleNode
2429
+ );
2430
+ existingStyleNode = null;
2431
+ if (t.isObjectExpression(existingStyleExpr)) {
2432
+ existingStyleExpr.properties.push(...newStyleProps);
2433
+ path.parentPath.node.attributes.push(
2434
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(existingStyleExpr))
2435
+ );
2436
+ } else if (t.isStringLiteral(existingStyleExpr)) {
2437
+ const parsedOldProps = parseStyleStringToObjectExpr(existingStyleExpr.value).properties;
2438
+ path.parentPath.node.attributes.push(
2439
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(
2440
+ t.objectExpression([...parsedOldProps, ...newStyleProps])
2441
+ ))
2442
+ );
2443
+ } else {
2444
+ const mergedStyle = t.objectExpression([
2445
+ t.spreadElement(existingStyleExpr),
2446
+ ...newStyleProps
2447
+ ]);
2448
+ path.parentPath.node.attributes.push(
2449
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(mergedStyle))
2450
+ );
2451
+ }
2452
+ } else {
2453
+ path.parentPath.node.attributes.push(
2454
+ t.jsxAttribute(t.jsxIdentifier("style"), t.jsxExpressionContainer(t.objectExpression(newStyleProps)))
2455
+ );
2456
+ existingStyleExpr = t.objectExpression(newStyleProps);
2457
+ existingStyleNode = path.parentPath.node.attributes[path.parentPath.node.attributes.length - 1];
2458
+ }
2459
+ };
2269
2460
  if (t.isStringLiteral(value)) {
2270
2461
  path.node.name.name = "className";
2271
2462
  for (const c of value.value.split(/\s+/)) {
@@ -2273,13 +2464,16 @@ function transformSourceCode(source) {
2273
2464
  collectedClasses.add(c);
2274
2465
  }
2275
2466
  }
2467
+ path.node.value = createMergedClassNameValue(value);
2276
2468
  transformed = true;
2277
2469
  return;
2278
2470
  }
2279
2471
  if (t.isJSXExpressionContainer(value)) {
2280
2472
  const expression = value.expression;
2281
2473
  if (t.isObjectExpression(expression)) {
2282
- const staticObject = evaluateStaticObject(expression);
2474
+ const getBinding = (name) => path.scope.getBinding(name);
2475
+ const flatExpression = resolveObjectSpreads(expression, getBinding) ?? expression;
2476
+ const staticObject = evaluateStaticObject(flatExpression);
2283
2477
  if (staticObject !== null) {
2284
2478
  const { className, attributes } = transform(staticObject);
2285
2479
  for (const c of className.split(/\s+/)) {
@@ -2288,22 +2482,35 @@ function transformSourceCode(source) {
2288
2482
  }
2289
2483
  }
2290
2484
  path.node.name.name = "className";
2291
- path.node.value = t.stringLiteral(className);
2485
+ path.node.value = createMergedClassNameValue(t.stringLiteral(className));
2292
2486
  Object.entries(attributes).forEach(([key, val]) => {
2293
2487
  if (path.parentPath?.isJSXOpeningElement()) {
2294
- path.parentPath.node.attributes.push(
2295
- t.jsxAttribute(
2296
- t.jsxIdentifier(key),
2297
- t.stringLiteral(val)
2298
- )
2299
- );
2488
+ if (key === "style") {
2489
+ const newProps = parseStyleStringToObjectExpr(val).properties;
2490
+ mergeAndInjectStyle(newProps);
2491
+ } else {
2492
+ path.parentPath.node.attributes.push(
2493
+ t.jsxAttribute(
2494
+ t.jsxIdentifier(key),
2495
+ t.stringLiteral(val)
2496
+ )
2497
+ );
2498
+ }
2300
2499
  }
2301
2500
  });
2302
2501
  transformed = true;
2303
2502
  return;
2304
2503
  }
2305
- const partial = evaluatePartialObject(expression);
2306
- if (partial !== null && !partial.hasSpread && partial.dynamicProps.size > 0) {
2504
+ const hoisted = tryHoistConditionalSpread(expression, getBinding);
2505
+ if (hoisted !== null) {
2506
+ path.node.name.name = "className";
2507
+ path.node.value = createMergedClassNameValue(hoisted);
2508
+ collectFromExpr(hoisted, collectedClasses);
2509
+ transformed = true;
2510
+ return;
2511
+ }
2512
+ const partial = evaluatePartialObject(flatExpression);
2513
+ if (partial !== null && !partial.hasSpread && (partial.dynamicProps.size > 0 || partial.conditionalClasses.length > 0)) {
2307
2514
  const staticClasses = [];
2308
2515
  if (Object.keys(partial.staticProps).length > 0) {
2309
2516
  const { className: sc } = transform(partial.staticProps);
@@ -2324,23 +2531,28 @@ function transformSourceCode(source) {
2324
2531
  )
2325
2532
  );
2326
2533
  }
2327
- const allClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2328
- for (const c of allClasses.split(/\s+/)) {
2534
+ const baseClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2535
+ for (const c of baseClasses.split(/\s+/)) {
2329
2536
  if (c) {
2330
2537
  collectedClasses.add(c);
2331
2538
  }
2332
2539
  }
2333
- path.node.name.name = "className";
2334
- path.node.value = t.stringLiteral(allClasses);
2335
- if (styleProps.length > 0 && path.parentPath?.isJSXOpeningElement()) {
2336
- const styleAttr = t.jsxAttribute(
2337
- t.jsxIdentifier("style"),
2338
- t.jsxExpressionContainer(
2339
- t.objectExpression(styleProps)
2340
- )
2341
- );
2342
- path.parentPath.node.attributes.push(styleAttr);
2540
+ for (const cc of partial.conditionalClasses) {
2541
+ for (const c of cc.consequent.split(/\s+/)) {
2542
+ if (c) {
2543
+ collectedClasses.add(c);
2544
+ }
2545
+ }
2546
+ for (const c of cc.alternate.split(/\s+/)) {
2547
+ if (c) {
2548
+ collectedClasses.add(c);
2549
+ }
2550
+ }
2343
2551
  }
2552
+ const classExpr = partial.conditionalClasses.length > 0 ? buildConditionalClassExpr(baseClasses, partial.conditionalClasses) : t.stringLiteral(baseClasses);
2553
+ path.node.name.name = "className";
2554
+ path.node.value = createMergedClassNameValue(classExpr);
2555
+ mergeAndInjectStyle(styleProps);
2344
2556
  if (partial.usesColorVar) {
2345
2557
  usesColorVar = true;
2346
2558
  }
@@ -2353,18 +2565,19 @@ function transformSourceCode(source) {
2353
2565
  if (binding && binding.path.isVariableDeclarator()) {
2354
2566
  const init2 = binding.path.node.init;
2355
2567
  if (init2) {
2356
- const resolved = tryStaticTransformNode(init2);
2568
+ const gbIdent = (name) => path.scope.getBinding(name);
2569
+ const resolved = tryStaticTransformNode(init2, gbIdent);
2357
2570
  if (resolved !== null) {
2358
2571
  path.node.name.name = "className";
2359
2572
  if (t.isStringLiteral(resolved)) {
2360
- path.node.value = resolved;
2573
+ path.node.value = createMergedClassNameValue(resolved);
2361
2574
  for (const c of resolved.value.split(/\s+/)) {
2362
2575
  if (c) {
2363
2576
  collectedClasses.add(c);
2364
2577
  }
2365
2578
  }
2366
2579
  } else {
2367
- value.expression = resolved;
2580
+ path.node.value = createMergedClassNameValue(resolved);
2368
2581
  collectFromExpr(resolved, collectedClasses);
2369
2582
  }
2370
2583
  transformed = true;
@@ -2374,33 +2587,267 @@ function transformSourceCode(source) {
2374
2587
  }
2375
2588
  }
2376
2589
  if (t.isConditionalExpression(expression)) {
2377
- const resolved = tryStaticTransformNode(expression);
2590
+ const gbCond = (name) => path.scope.getBinding(name);
2591
+ const resolved = tryStaticTransformNode(expression, gbCond);
2378
2592
  if (resolved !== null) {
2379
2593
  path.node.name.name = "className";
2380
2594
  if (t.isStringLiteral(resolved)) {
2381
- path.node.value = resolved;
2595
+ path.node.value = createMergedClassNameValue(resolved);
2382
2596
  for (const c of resolved.value.split(/\s+/)) {
2383
2597
  if (c) {
2384
2598
  collectedClasses.add(c);
2385
2599
  }
2386
2600
  }
2387
2601
  } else {
2388
- value.expression = resolved;
2602
+ path.node.value = createMergedClassNameValue(resolved);
2389
2603
  collectFromExpr(resolved, collectedClasses);
2390
2604
  }
2391
2605
  transformed = true;
2392
2606
  return;
2393
2607
  }
2394
2608
  }
2609
+ if (t.isArrayExpression(expression)) {
2610
+ const parts = [];
2611
+ let hasRuntime = false;
2612
+ const getBindingForArray = (name) => path.scope.getBinding(name);
2613
+ for (const element of expression.elements) {
2614
+ if (element === null) {
2615
+ continue;
2616
+ }
2617
+ if (t.isBooleanLiteral(element) && !element.value) {
2618
+ continue;
2619
+ }
2620
+ if (t.isNullLiteral(element)) {
2621
+ continue;
2622
+ }
2623
+ if (t.isIdentifier(element) && element.name === "undefined") {
2624
+ continue;
2625
+ }
2626
+ if (t.isLogicalExpression(element) && element.operator === "&&") {
2627
+ const resolved2 = tryStaticTransformNode(element.right, getBindingForArray);
2628
+ if (resolved2 !== null && t.isStringLiteral(resolved2)) {
2629
+ if (resolved2.value) {
2630
+ parts.push(t.logicalExpression("&&", element.left, resolved2));
2631
+ for (const c of resolved2.value.split(/\s+/)) {
2632
+ if (c) {
2633
+ collectedClasses.add(c);
2634
+ }
2635
+ }
2636
+ hasRuntime = true;
2637
+ }
2638
+ continue;
2639
+ }
2640
+ parts.push(element);
2641
+ hasRuntime = true;
2642
+ continue;
2643
+ }
2644
+ const resolved = tryStaticTransformNode(element, getBindingForArray);
2645
+ if (resolved !== null) {
2646
+ if (t.isStringLiteral(resolved)) {
2647
+ if (resolved.value) {
2648
+ parts.push(resolved);
2649
+ for (const c of resolved.value.split(/\s+/)) {
2650
+ if (c) {
2651
+ collectedClasses.add(c);
2652
+ }
2653
+ }
2654
+ }
2655
+ } else {
2656
+ parts.push(resolved);
2657
+ collectFromExpr(resolved, collectedClasses);
2658
+ hasRuntime = true;
2659
+ }
2660
+ } else {
2661
+ parts.push(element);
2662
+ hasRuntime = true;
2663
+ }
2664
+ }
2665
+ path.node.name.name = "className";
2666
+ if (parts.length === 0) {
2667
+ path.node.value = createMergedClassNameValue(t.stringLiteral(""));
2668
+ } else if (!hasRuntime) {
2669
+ const merged = parts.map((p) => p.value).filter(Boolean).join(" ");
2670
+ path.node.value = createMergedClassNameValue(t.stringLiteral(merged));
2671
+ } else {
2672
+ if (existingClassExpr) {
2673
+ parts.unshift(existingClassExpr);
2674
+ if (existingClassNameNode && path.parentPath?.isJSXOpeningElement()) {
2675
+ path.parentPath.node.attributes = path.parentPath.node.attributes.filter(
2676
+ (a) => a !== existingClassNameNode
2677
+ );
2678
+ existingClassNameNode = null;
2679
+ }
2680
+ }
2681
+ const szCall2 = t.callExpression(
2682
+ t.identifier("_szMerge"),
2683
+ parts
2684
+ );
2685
+ path.node.value = t.jsxExpressionContainer(szCall2);
2686
+ usesMerge = true;
2687
+ usesRuntime = true;
2688
+ }
2689
+ transformed = true;
2690
+ return;
2691
+ }
2692
+ const loc = expression.loc;
2693
+ const lineCol = loc ? `${loc.start.line}:${loc.start.column + 1}` : "?";
2694
+ let reason, suggestion;
2695
+ if (t.isCallExpression(expression)) {
2696
+ const callee = expression.callee;
2697
+ const name = t.isIdentifier(callee) ? callee.name : t.isMemberExpression(callee) && t.isIdentifier(callee.property) ? callee.property.name : "?";
2698
+ reason = `function call \`${name}()\` result is unknown at build time`;
2699
+ suggestion = "If it returns static variants \u2192 convert to szv(). If it depends on runtime data \u2192 use dynamic().";
2700
+ } else if (t.isIdentifier(expression)) {
2701
+ reason = `identifier \`${expression.name}\` could not be resolved to a static value`;
2702
+ 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().";
2703
+ } else if (t.isMemberExpression(expression)) {
2704
+ reason = "member expression is not statically resolvable";
2705
+ suggestion = "Extract the value to a module-level const. For variant-based styling \u2192 szv(). For true runtime values \u2192 dynamic().";
2706
+ } else {
2707
+ reason = `expression of type \`${expression.type}\` is not statically analyzable`;
2708
+ suggestion = "Use a literal sz object or a module-level const. For variant-based styling \u2192 szv(). For true runtime values \u2192 dynamic().";
2709
+ }
2710
+ diagnostics.push(`sz fallback at ${lineCol}: ${reason}.
2711
+ Suggestion: ${suggestion}`);
2395
2712
  path.node.name.name = "className";
2396
2713
  const szCall = t.callExpression(
2397
2714
  t.identifier("_sz"),
2398
2715
  [expression]
2399
2716
  );
2400
- value.expression = szCall;
2717
+ path.node.value = createMergedClassNameValue(szCall);
2401
2718
  usesRuntime = true;
2402
2719
  transformed = true;
2403
2720
  }
2721
+ },
2722
+ // ── szv catalog extraction ────────────────────────────────────────
2723
+ // When the compiler sees `const X = szv({...})` with a static config,
2724
+ // it emits a no-op catalog array so Tailwind JIT can scan all variant
2725
+ // class strings — even when szv is called at runtime with dynamic args.
2726
+ VariableDeclarator(path) {
2727
+ const init2 = path.node.init;
2728
+ if (!t.isCallExpression(init2)) {
2729
+ return;
2730
+ }
2731
+ if (!t.isIdentifier(init2.callee) || init2.callee.name !== "szv") {
2732
+ return;
2733
+ }
2734
+ if (init2.arguments.length === 0) {
2735
+ return;
2736
+ }
2737
+ if (!t.isIdentifier(path.node.id)) {
2738
+ return;
2739
+ }
2740
+ const configArg = init2.arguments[0];
2741
+ if (!t.isObjectExpression(configArg)) {
2742
+ return;
2743
+ }
2744
+ const config = evaluateStaticObject(configArg);
2745
+ if (!config) {
2746
+ return;
2747
+ }
2748
+ const base = config.base ?? {};
2749
+ const variants = config.variants ?? {};
2750
+ const classStrings = [];
2751
+ const baseResult = transform(base);
2752
+ const baseCls = typeof baseResult === "string" ? baseResult : baseResult.className;
2753
+ if (baseCls) {
2754
+ classStrings.push(baseCls);
2755
+ }
2756
+ for (const variantValues of Object.values(variants)) {
2757
+ for (const variantObj of Object.values(variantValues)) {
2758
+ if (!variantObj || typeof variantObj !== "object") {
2759
+ continue;
2760
+ }
2761
+ const merged = { ...base, ...variantObj };
2762
+ const result2 = transform(merged);
2763
+ const cls = typeof result2 === "string" ? result2 : result2.className;
2764
+ if (cls) {
2765
+ classStrings.push(cls);
2766
+ }
2767
+ }
2768
+ }
2769
+ if (classStrings.length === 0) {
2770
+ return;
2771
+ }
2772
+ for (const combined of classStrings) {
2773
+ for (const c of combined.split(/\s+/)) {
2774
+ if (c) {
2775
+ collectedClasses.add(c);
2776
+ }
2777
+ }
2778
+ }
2779
+ const catalogDecl = t.variableDeclaration("const", [
2780
+ t.variableDeclarator(
2781
+ t.identifier(`_szv_catalog_${path.node.id.name}`),
2782
+ t.arrayExpression(classStrings.map((s) => t.stringLiteral(s)))
2783
+ )
2784
+ ]);
2785
+ const parentPath = path.parentPath;
2786
+ if (parentPath && t.isVariableDeclaration(parentPath.node)) {
2787
+ parentPath.insertAfter(catalogDecl);
2788
+ transformed = true;
2789
+ }
2790
+ },
2791
+ // ── dynamic() literal extraction ──────────────────────────────────
2792
+ // Detects `dynamic({...})` and `dynamic(CONST_IDENTIFIER)` calls
2793
+ // with statically-analyzable arguments and adds the resulting
2794
+ // class tokens to collectedClasses so prescanAndWriteClasses()
2795
+ // includes them in csszyx-classes.html for Tailwind to scan.
2796
+ // This means dynamic() with static/const args works in Astro SSR
2797
+ // without needing client:* directives.
2798
+ CallExpression(path) {
2799
+ const callee = path.node.callee;
2800
+ if (!t.isIdentifier(callee) || callee.name !== "dynamic") {
2801
+ return;
2802
+ }
2803
+ if (path.node.arguments.length === 0) {
2804
+ return;
2805
+ }
2806
+ const arg = path.node.arguments[0];
2807
+ if (t.isObjectExpression(arg)) {
2808
+ const staticObj = evaluateStaticObject(arg);
2809
+ if (!staticObj) {
2810
+ return;
2811
+ }
2812
+ const { className } = transform(staticObj);
2813
+ for (const c of className.split(/\s+/)) {
2814
+ if (c) {
2815
+ collectedClasses.add(c);
2816
+ }
2817
+ }
2818
+ return;
2819
+ }
2820
+ let argExpr = arg;
2821
+ while (t.isTSAsExpression(argExpr) || t.isTSSatisfiesExpression(argExpr)) {
2822
+ argExpr = argExpr.expression;
2823
+ }
2824
+ if (t.isIdentifier(argExpr)) {
2825
+ const binding = path.scope.getBinding(argExpr.name);
2826
+ if (!binding) {
2827
+ return;
2828
+ }
2829
+ const declarator = binding.path.node;
2830
+ if (!t.isVariableDeclarator(declarator) || !declarator.init) {
2831
+ return;
2832
+ }
2833
+ let initExpr = declarator.init;
2834
+ while (t.isTSAsExpression(initExpr) || t.isTSSatisfiesExpression(initExpr)) {
2835
+ initExpr = initExpr.expression;
2836
+ }
2837
+ if (!t.isObjectExpression(initExpr)) {
2838
+ return;
2839
+ }
2840
+ const staticObj = evaluateStaticObject(initExpr);
2841
+ if (!staticObj) {
2842
+ return;
2843
+ }
2844
+ const { className } = transform(staticObj);
2845
+ for (const c of className.split(/\s+/)) {
2846
+ if (c) {
2847
+ collectedClasses.add(c);
2848
+ }
2849
+ }
2850
+ }
2404
2851
  }
2405
2852
  }
2406
2853
  };
@@ -2411,29 +2858,72 @@ function transformSourceCode(source) {
2411
2858
  code: result?.code || source,
2412
2859
  transformed,
2413
2860
  usesRuntime,
2861
+ usesMerge,
2414
2862
  usesColorVar,
2415
- classes: collectedClasses
2863
+ classes: collectedClasses,
2864
+ rawClassNames,
2865
+ diagnostics
2416
2866
  };
2417
2867
  } catch (e) {
2418
2868
  console.warn("[csszyx] AST transform failed, falling back to original code:", e);
2419
- return { code: source, transformed: false, usesRuntime: false, usesColorVar: false, classes: collectedClasses };
2869
+ return { code: source, transformed: false, usesRuntime: false, usesMerge: false, usesColorVar: false, classes: collectedClasses, rawClassNames, diagnostics };
2870
+ }
2871
+ }
2872
+ function parseStyleStringToObjectExpr(styleStr) {
2873
+ const props = styleStr.split(";").map((s) => s.trim()).filter(Boolean);
2874
+ const objProps = [];
2875
+ for (const prop of props) {
2876
+ const idx = prop.indexOf(":");
2877
+ if (idx > -1) {
2878
+ const k = prop.slice(0, idx).trim();
2879
+ const v = prop.slice(idx + 1).trim();
2880
+ let keyNode;
2881
+ if (k.startsWith("--")) {
2882
+ keyNode = t.stringLiteral(k);
2883
+ } else {
2884
+ const camel = k.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
2885
+ keyNode = t.identifier(camel);
2886
+ }
2887
+ objProps.push(t.objectProperty(keyNode, t.stringLiteral(v)));
2888
+ }
2420
2889
  }
2890
+ return t.objectExpression(objProps);
2421
2891
  }
2422
- function tryStaticTransformNode(node) {
2892
+ function tryStaticTransformNode(node, getBinding) {
2893
+ if (t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node)) {
2894
+ return tryStaticTransformNode(node.expression, getBinding);
2895
+ }
2423
2896
  if (t.isObjectExpression(node)) {
2424
- const staticObj = evaluateStaticObject(node);
2897
+ const resolved = getBinding ? resolveObjectSpreads(node, getBinding) ?? node : node;
2898
+ const staticObj = evaluateStaticObject(resolved);
2425
2899
  if (staticObj !== null) {
2426
2900
  const { className } = transform(staticObj);
2427
2901
  return t.stringLiteral(className);
2428
2902
  }
2903
+ if (getBinding) {
2904
+ const hoisted = tryHoistConditionalSpread(node, getBinding);
2905
+ if (hoisted !== null) {
2906
+ return hoisted;
2907
+ }
2908
+ }
2429
2909
  return null;
2430
2910
  }
2431
2911
  if (t.isStringLiteral(node)) {
2432
2912
  return node;
2433
2913
  }
2914
+ if (t.isIdentifier(node) && getBinding) {
2915
+ const binding = getBinding(node.name);
2916
+ if (binding && binding.path.isVariableDeclarator()) {
2917
+ const init2 = binding.path.node.init;
2918
+ if (init2) {
2919
+ return tryStaticTransformNode(init2, getBinding);
2920
+ }
2921
+ }
2922
+ return null;
2923
+ }
2434
2924
  if (t.isConditionalExpression(node)) {
2435
- const consequent = tryStaticTransformNode(node.consequent);
2436
- const alternate = tryStaticTransformNode(node.alternate);
2925
+ const consequent = tryStaticTransformNode(node.consequent, getBinding);
2926
+ const alternate = tryStaticTransformNode(node.alternate, getBinding);
2437
2927
  if (consequent !== null && alternate !== null) {
2438
2928
  return t.conditionalExpression(node.test, consequent, alternate);
2439
2929
  }
@@ -2441,6 +2931,39 @@ function tryStaticTransformNode(node) {
2441
2931
  }
2442
2932
  return null;
2443
2933
  }
2934
+ function tryHoistConditionalSpread(node, getBinding) {
2935
+ let conditionalSpreadIdx = -1;
2936
+ let conditionalExpr = null;
2937
+ for (let i = 0; i < node.properties.length; i++) {
2938
+ const prop = node.properties[i];
2939
+ if (!t.isSpreadElement(prop)) {
2940
+ continue;
2941
+ }
2942
+ if (t.isConditionalExpression(prop.argument)) {
2943
+ if (conditionalSpreadIdx !== -1) {
2944
+ return null;
2945
+ }
2946
+ conditionalSpreadIdx = i;
2947
+ conditionalExpr = prop.argument;
2948
+ } else {
2949
+ return null;
2950
+ }
2951
+ }
2952
+ if (conditionalSpreadIdx === -1 || conditionalExpr === null) {
2953
+ return null;
2954
+ }
2955
+ const otherProps = node.properties.filter((_, i) => i !== conditionalSpreadIdx);
2956
+ const mkObj = (branch) => t.objectExpression([t.spreadElement(branch), ...otherProps]);
2957
+ const resolvedA = tryStaticTransformNode(mkObj(conditionalExpr.consequent), getBinding);
2958
+ const resolvedB = tryStaticTransformNode(mkObj(conditionalExpr.alternate), getBinding);
2959
+ if (!resolvedA || !resolvedB) {
2960
+ return null;
2961
+ }
2962
+ if (!t.isStringLiteral(resolvedA) || !t.isStringLiteral(resolvedB)) {
2963
+ return null;
2964
+ }
2965
+ return t.conditionalExpression(conditionalExpr.test, resolvedA, resolvedB);
2966
+ }
2444
2967
  function evaluateStaticObject(node) {
2445
2968
  const result = {};
2446
2969
  for (const prop of node.properties) {
@@ -2455,6 +2978,8 @@ function evaluateStaticObject(node) {
2455
2978
  key = prop.key.name;
2456
2979
  } else if (t.isStringLiteral(prop.key)) {
2457
2980
  key = prop.key.value;
2981
+ } else if (t.isNumericLiteral(prop.key)) {
2982
+ key = String(prop.key.value);
2458
2983
  } else {
2459
2984
  return null;
2460
2985
  }
@@ -2479,10 +3004,82 @@ function evaluateStaticObject(node) {
2479
3004
  }
2480
3005
  return result;
2481
3006
  }
3007
+ function resolveObjectSpreads(node, getBinding) {
3008
+ const newProps = [];
3009
+ for (const prop of node.properties) {
3010
+ if (!t.isSpreadElement(prop)) {
3011
+ if (t.isObjectProperty(prop) && t.isObjectExpression(prop.value)) {
3012
+ const resolvedValue = resolveObjectSpreads(prop.value, getBinding);
3013
+ if (resolvedValue === null) {
3014
+ return null;
3015
+ }
3016
+ newProps.push(t.objectProperty(prop.key, resolvedValue, prop.computed, prop.shorthand));
3017
+ } else {
3018
+ newProps.push(prop);
3019
+ }
3020
+ continue;
3021
+ }
3022
+ const arg = prop.argument;
3023
+ if (!t.isIdentifier(arg)) {
3024
+ return null;
3025
+ }
3026
+ const binding = getBinding(arg.name);
3027
+ if (!binding || !binding.path.isVariableDeclarator()) {
3028
+ return null;
3029
+ }
3030
+ let init2 = binding.path.node.init;
3031
+ if (t.isTSAsExpression(init2) || t.isTSSatisfiesExpression(init2)) {
3032
+ init2 = init2.expression;
3033
+ }
3034
+ if (!t.isObjectExpression(init2)) {
3035
+ return null;
3036
+ }
3037
+ const inner = resolveObjectSpreads(init2, getBinding);
3038
+ if (inner === null) {
3039
+ return null;
3040
+ }
3041
+ newProps.push(...inner.properties);
3042
+ }
3043
+ return t.objectExpression(newProps);
3044
+ }
3045
+ function extractStaticLiteralValue(node) {
3046
+ if (t.isStringLiteral(node)) {
3047
+ return node.value;
3048
+ }
3049
+ if (t.isNumericLiteral(node)) {
3050
+ return node.value;
3051
+ }
3052
+ if (t.isBooleanLiteral(node)) {
3053
+ return node.value;
3054
+ }
3055
+ if (t.isUnaryExpression(node) && node.operator === "-" && t.isNumericLiteral(node.argument)) {
3056
+ return -node.argument.value;
3057
+ }
3058
+ return null;
3059
+ }
3060
+ function buildConditionalClassExpr(baseClasses, conditionalClasses) {
3061
+ if (conditionalClasses.length === 0) {
3062
+ return t.stringLiteral(baseClasses);
3063
+ }
3064
+ const makeCondExpr = (cc) => t.conditionalExpression(cc.test, t.stringLiteral(cc.consequent), t.stringLiteral(cc.alternate));
3065
+ if (conditionalClasses.length === 1 && !baseClasses) {
3066
+ return makeCondExpr(conditionalClasses[0]);
3067
+ }
3068
+ const quasis = [];
3069
+ const exprs = [];
3070
+ for (let i = 0; i < conditionalClasses.length; i++) {
3071
+ const prefix = i === 0 ? baseClasses ? baseClasses + " " : "" : " ";
3072
+ quasis.push(t.templateElement({ raw: prefix, cooked: prefix }, false));
3073
+ exprs.push(makeCondExpr(conditionalClasses[i]));
3074
+ }
3075
+ quasis.push(t.templateElement({ raw: "", cooked: "" }, true));
3076
+ return t.templateLiteral(quasis, exprs);
3077
+ }
2482
3078
  function evaluatePartialObject(node, variantChain = "") {
2483
3079
  const staticProps = {};
2484
3080
  const dynamicProps = /* @__PURE__ */ new Map();
2485
3081
  const rawClasses = [];
3082
+ const conditionalClasses = [];
2486
3083
  let usesColorVar = false;
2487
3084
  for (const prop of node.properties) {
2488
3085
  if (t.isSpreadElement(prop)) {
@@ -2499,6 +3096,8 @@ function evaluatePartialObject(node, variantChain = "") {
2499
3096
  key = prop.key.name;
2500
3097
  } else if (t.isStringLiteral(prop.key)) {
2501
3098
  key = prop.key.value;
3099
+ } else if (t.isNumericLiteral(prop.key)) {
3100
+ key = String(prop.key.value);
2502
3101
  } else {
2503
3102
  return null;
2504
3103
  }
@@ -2541,7 +3140,7 @@ function evaluatePartialObject(node, variantChain = "") {
2541
3140
  staticProps[key] = { color: colorStr, op: opVal };
2542
3141
  } else if (t.isExpression(opProp.value)) {
2543
3142
  const variantPfx = variantChain ? `${variantChain}:` : "";
2544
- rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/[var(${opVarName})]`);
3143
+ rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/(${opVarName})`);
2545
3144
  dynamicProps.set(uniqueKey, {
2546
3145
  expression: opProp.value,
2547
3146
  category: 3 /* UNITLESS */,
@@ -2580,6 +3179,7 @@ function evaluatePartialObject(node, variantChain = "") {
2580
3179
  dynamicProps.set(k, v);
2581
3180
  }
2582
3181
  rawClasses.push(...nestedResult.rawClasses);
3182
+ conditionalClasses.push(...nestedResult.conditionalClasses);
2583
3183
  if (nestedResult.usesColorVar) {
2584
3184
  usesColorVar = true;
2585
3185
  }
@@ -2588,6 +3188,25 @@ function evaluatePartialObject(node, variantChain = "") {
2588
3188
  }
2589
3189
  }
2590
3190
  }
3191
+ } else if (t.isConditionalExpression(value)) {
3192
+ const consVal = extractStaticLiteralValue(value.consequent);
3193
+ const altVal = extractStaticLiteralValue(value.alternate);
3194
+ if (consVal !== null && altVal !== null) {
3195
+ const { className: classA } = transform({ [key]: consVal });
3196
+ const { className: classB } = transform({ [key]: altVal });
3197
+ const vPfx = variantChain ? getVariantPrefix(variantChain) + ":" : "";
3198
+ const prefixed = (cls) => vPfx ? cls.split(/\s+/).filter(Boolean).map((c) => vPfx + c).join(" ") : cls;
3199
+ conditionalClasses.push({ test: value.test, consequent: prefixed(classA), alternate: prefixed(classB) });
3200
+ } else {
3201
+ const twPrefix = PROPERTY_MAP[key] || key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
3202
+ const category = getPropertyCategory(key);
3203
+ const varName = getCSSVariableName(key, variantChain || void 0);
3204
+ const uniqueKey = variantChain ? `${variantChain}-${key}` : key;
3205
+ if (COLOR_PROPERTIES.has(key)) {
3206
+ usesColorVar = true;
3207
+ }
3208
+ dynamicProps.set(uniqueKey, { expression: value, category, varName, twPrefix, variantChain: variantChain || "" });
3209
+ }
2591
3210
  } else if (t.isExpression(value)) {
2592
3211
  const twPrefix = PROPERTY_MAP[key] || key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2593
3212
  const category = getPropertyCategory(key);
@@ -2607,7 +3226,7 @@ function evaluatePartialObject(node, variantChain = "") {
2607
3226
  return null;
2608
3227
  }
2609
3228
  }
2610
- return { staticProps, dynamicProps, rawClasses, hasSpread: false, usesColorVar };
3229
+ return { staticProps, dynamicProps, rawClasses, conditionalClasses, hasSpread: false, usesColorVar };
2611
3230
  }
2612
3231
  function generateStyleValueExpression(info) {
2613
3232
  const { expression, category } = info;
@@ -2670,7 +3289,7 @@ function collectFromExpr(node, classes) {
2670
3289
  function buildCSSVarClassName(info) {
2671
3290
  const { twPrefix, varName, variantChain } = info;
2672
3291
  const variantPrefix = variantChain ? `${getVariantPrefix(variantChain)}:` : "";
2673
- return `${variantPrefix}${twPrefix}-[var(${varName})]`;
3292
+ return `${variantPrefix}${twPrefix}-(${varName})`;
2674
3293
  }
2675
3294
 
2676
3295
  // src/compiler.ts
@@ -3136,12 +3755,16 @@ function mergeOptions(options = {}) {
3136
3755
  }
3137
3756
  // Annotate the CommonJS export names for ESM import in node:
3138
3757
  0 && (module.exports = {
3758
+ BOOLEAN_SHORTHANDS,
3139
3759
  COLOR_PROPERTIES,
3140
3760
  CsszyxCompiler,
3141
3761
  DEFAULT_COMPILER_OPTIONS,
3762
+ KNOWN_VARIANTS,
3142
3763
  ManifestBuilder,
3143
3764
  PROPERTY_CATEGORY_MAP,
3765
+ PROPERTY_MAP,
3144
3766
  PropertyCategory,
3767
+ SUGGESTION_MAP,
3145
3768
  VERSION,
3146
3769
  buildParentMap,
3147
3770
  createRecoveryToken,