@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.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)}]`);
1884
1961
  } else {
1885
- classes.push(`bg-${sVal}`);
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})`);
1972
+ } else {
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;
@@ -1938,16 +2027,19 @@ function transform(szProp, prefix = "", mangleMap) {
1938
2027
  classes.push(className);
1939
2028
  continue;
1940
2029
  }
1941
- if (rawKey === "content" || rawKey === "alignContent") {
1942
- const ALIGN_CONTENT_KEYWORDS = /* @__PURE__ */ new Set(["normal", "center", "start", "end", "between", "around", "evenly", "baseline", "stretch"]);
1943
- if (ALIGN_CONTENT_KEYWORDS.has(value)) {
1944
- className += `content-${value}`;
2030
+ if (rawKey === "alignContent") {
2031
+ className += `content-${value}`;
2032
+ classes.push(className);
2033
+ continue;
2034
+ }
2035
+ if (rawKey === "content") {
2036
+ if (value === "none") {
2037
+ className += "content-none";
1945
2038
  } else if (value.startsWith("--")) {
1946
2039
  className += `content-(${value})`;
1947
- } else if (!["none", "empty"].includes(value)) {
1948
- className += `content-[${value}]`;
1949
2040
  } else {
1950
- className += `content-${value}`;
2041
+ const inner = value.startsWith('"') && value.endsWith('"') && value.length >= 2 ? `'${value.slice(1, -1)}'` : value;
2042
+ className += `content-[${inner}]`;
1951
2043
  }
1952
2044
  classes.push(className);
1953
2045
  continue;
@@ -2000,16 +2092,12 @@ function transform(szProp, prefix = "", mangleMap) {
2000
2092
  const sVal = String(value);
2001
2093
  const prop = PROPERTY_MAP[rawKey] || rawKey;
2002
2094
  if (needsArbitraryBrackets(sVal) || sVal.includes("(") || sVal.includes("_") || sVal.includes("%")) {
2003
- classes.push(`${prop}-[${normalizeArbitraryValue(sVal)}]`);
2095
+ classes.push(`${className}${prop}-[${normalizeArbitraryValue(sVal)}]`);
2004
2096
  continue;
2005
2097
  }
2006
2098
  }
2007
2099
  if (rawKey === "transformStyle") {
2008
- if (value === "preserve-3d" || value === "3d") {
2009
- className += "transform-3d";
2010
- } else {
2011
- className += `transform-${value}`;
2012
- }
2100
+ className += `transform-${value}`;
2013
2101
  classes.push(className);
2014
2102
  continue;
2015
2103
  }
@@ -2020,7 +2108,7 @@ function transform(szProp, prefix = "", mangleMap) {
2020
2108
  } else if (value.startsWith("--")) {
2021
2109
  className += `perspective-(${value})`;
2022
2110
  } else if (needsArbitraryBrackets(value)) {
2023
- className += `perspective-[${value}]`;
2111
+ className += `perspective-[${normalizeArbitraryValue(value)}]`;
2024
2112
  } else {
2025
2113
  className += `perspective-${value}`;
2026
2114
  }
@@ -2044,7 +2132,7 @@ function transform(szProp, prefix = "", mangleMap) {
2044
2132
  }
2045
2133
  }
2046
2134
  if (process.env.NODE_ENV !== "production" && typeof window === "undefined") {
2047
- 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)
2048
2136
  KNOWN_VARIANTS.has(rawKey) || // Groups/Peers
2049
2137
  rawKey === "group" || rawKey === "peer" || // Special variant objects
2050
2138
  rawKey === "has" || rawKey === "not" || rawKey === "data" || rawKey === "aria" || rawKey === "supports" || rawKey === "min" || rawKey === "max";
@@ -2071,6 +2159,11 @@ function transform(szProp, prefix = "", mangleMap) {
2071
2159
  classes.push(className);
2072
2160
  continue;
2073
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
+ }
2074
2167
  if (typeof value === "number") {
2075
2168
  if (value < 0 && NEGATIVE_ALLOWED.has(key)) {
2076
2169
  className += `-${key}-${Math.abs(value)}`;
@@ -2083,15 +2176,6 @@ function transform(szProp, prefix = "", mangleMap) {
2083
2176
  if (typeof value === "string") {
2084
2177
  const { value: cleanValue, important } = handleImportant(value);
2085
2178
  let finalValue = cleanValue;
2086
- if (NEEDS_ARBITRARY_PROPERTY.has(key)) {
2087
- const arbitraryProp = `[${key}:${finalValue}]`;
2088
- className += arbitraryProp;
2089
- if (important) {
2090
- className += "!";
2091
- }
2092
- classes.push(className);
2093
- continue;
2094
- }
2095
2179
  if (finalValue.startsWith("--")) {
2096
2180
  const typeHint = CSS_VAR_TYPE_HINTS[rawKey];
2097
2181
  if (typeHint) {
@@ -2125,7 +2209,7 @@ function transform(szProp, prefix = "", mangleMap) {
2125
2209
  }
2126
2210
  }
2127
2211
  let mergedClasses = classes;
2128
- const textSizePattern = /^((?:[a-z0-9\-[\]@/:]*:)*)text-(.+)$/;
2212
+ const textSizePattern = /^((?:[a-z0-9\-[\]@/:]*:)*)text-(xs|sm|base|lg|[2-9]?xl|\[.+\]|\(.+\))$/;
2129
2213
  const leadingPattern = /^((?:[a-z0-9\-[\]@/:]*:)*)leading-(.+)$/;
2130
2214
  const textEntries = [];
2131
2215
  const leadingEntries = [];
@@ -2142,11 +2226,15 @@ function transform(szProp, prefix = "", mangleMap) {
2142
2226
  }
2143
2227
  if (textEntries.length > 0 && leadingEntries.length > 0) {
2144
2228
  const removeIndices = /* @__PURE__ */ new Set();
2229
+ const consumedLeading = /* @__PURE__ */ new Set();
2145
2230
  for (const te of textEntries) {
2146
- const matchingLeading = leadingEntries.find((le) => le.prefix === te.prefix);
2231
+ const matchingLeading = leadingEntries.find(
2232
+ (le) => le.prefix === te.prefix && !consumedLeading.has(le.index)
2233
+ );
2147
2234
  if (matchingLeading) {
2148
2235
  mergedClasses[te.index] = `${te.prefix}text-${te.size}/${matchingLeading.value}`;
2149
2236
  removeIndices.add(matchingLeading.index);
2237
+ consumedLeading.add(matchingLeading.index);
2150
2238
  }
2151
2239
  }
2152
2240
  if (removeIndices.size > 0) {
@@ -2176,11 +2264,14 @@ function normalizeClassName(className) {
2176
2264
  // src/transform.ts
2177
2265
  function transformSourceCode(source) {
2178
2266
  let usesRuntime = false;
2267
+ let usesMerge = false;
2179
2268
  let usesColorVar = false;
2180
2269
  let transformed = false;
2181
2270
  const collectedClasses = /* @__PURE__ */ new Set();
2271
+ const rawClassNames = /* @__PURE__ */ new Set();
2272
+ const diagnostics = [];
2182
2273
  if (!source.includes("sz")) {
2183
- 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 };
2184
2275
  }
2185
2276
  try {
2186
2277
  const result = babel.transformSync(source, {
@@ -2204,7 +2295,7 @@ function transformSourceCode(source) {
2204
2295
  if (t.isStringLiteral(val)) {
2205
2296
  for (const c of val.value.split(/\s+/)) {
2206
2297
  if (c) {
2207
- collectedClasses.add(c);
2298
+ rawClassNames.add(c);
2208
2299
  }
2209
2300
  }
2210
2301
  }
@@ -2214,6 +2305,99 @@ function transformSourceCode(source) {
2214
2305
  return;
2215
2306
  }
2216
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
+ };
2217
2401
  if (t.isStringLiteral(value)) {
2218
2402
  path.node.name.name = "className";
2219
2403
  for (const c of value.value.split(/\s+/)) {
@@ -2221,13 +2405,16 @@ function transformSourceCode(source) {
2221
2405
  collectedClasses.add(c);
2222
2406
  }
2223
2407
  }
2408
+ path.node.value = createMergedClassNameValue(value);
2224
2409
  transformed = true;
2225
2410
  return;
2226
2411
  }
2227
2412
  if (t.isJSXExpressionContainer(value)) {
2228
2413
  const expression = value.expression;
2229
2414
  if (t.isObjectExpression(expression)) {
2230
- const staticObject = evaluateStaticObject(expression);
2415
+ const getBinding = (name) => path.scope.getBinding(name);
2416
+ const flatExpression = resolveObjectSpreads(expression, getBinding) ?? expression;
2417
+ const staticObject = evaluateStaticObject(flatExpression);
2231
2418
  if (staticObject !== null) {
2232
2419
  const { className, attributes } = transform(staticObject);
2233
2420
  for (const c of className.split(/\s+/)) {
@@ -2236,22 +2423,35 @@ function transformSourceCode(source) {
2236
2423
  }
2237
2424
  }
2238
2425
  path.node.name.name = "className";
2239
- path.node.value = t.stringLiteral(className);
2426
+ path.node.value = createMergedClassNameValue(t.stringLiteral(className));
2240
2427
  Object.entries(attributes).forEach(([key, val]) => {
2241
2428
  if (path.parentPath?.isJSXOpeningElement()) {
2242
- path.parentPath.node.attributes.push(
2243
- t.jsxAttribute(
2244
- t.jsxIdentifier(key),
2245
- t.stringLiteral(val)
2246
- )
2247
- );
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
+ }
2248
2440
  }
2249
2441
  });
2250
2442
  transformed = true;
2251
2443
  return;
2252
2444
  }
2253
- const partial = evaluatePartialObject(expression);
2254
- 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)) {
2255
2455
  const staticClasses = [];
2256
2456
  if (Object.keys(partial.staticProps).length > 0) {
2257
2457
  const { className: sc } = transform(partial.staticProps);
@@ -2272,23 +2472,28 @@ function transformSourceCode(source) {
2272
2472
  )
2273
2473
  );
2274
2474
  }
2275
- const allClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2276
- for (const c of allClasses.split(/\s+/)) {
2475
+ const baseClasses = [...staticClasses, ...partial.rawClasses, ...cssVarClasses].join(" ");
2476
+ for (const c of baseClasses.split(/\s+/)) {
2277
2477
  if (c) {
2278
2478
  collectedClasses.add(c);
2279
2479
  }
2280
2480
  }
2281
- path.node.name.name = "className";
2282
- path.node.value = t.stringLiteral(allClasses);
2283
- if (styleProps.length > 0 && path.parentPath?.isJSXOpeningElement()) {
2284
- const styleAttr = t.jsxAttribute(
2285
- t.jsxIdentifier("style"),
2286
- t.jsxExpressionContainer(
2287
- t.objectExpression(styleProps)
2288
- )
2289
- );
2290
- 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
+ }
2291
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);
2292
2497
  if (partial.usesColorVar) {
2293
2498
  usesColorVar = true;
2294
2499
  }
@@ -2301,18 +2506,19 @@ function transformSourceCode(source) {
2301
2506
  if (binding && binding.path.isVariableDeclarator()) {
2302
2507
  const init2 = binding.path.node.init;
2303
2508
  if (init2) {
2304
- const resolved = tryStaticTransformNode(init2);
2509
+ const gbIdent = (name) => path.scope.getBinding(name);
2510
+ const resolved = tryStaticTransformNode(init2, gbIdent);
2305
2511
  if (resolved !== null) {
2306
2512
  path.node.name.name = "className";
2307
2513
  if (t.isStringLiteral(resolved)) {
2308
- path.node.value = resolved;
2514
+ path.node.value = createMergedClassNameValue(resolved);
2309
2515
  for (const c of resolved.value.split(/\s+/)) {
2310
2516
  if (c) {
2311
2517
  collectedClasses.add(c);
2312
2518
  }
2313
2519
  }
2314
2520
  } else {
2315
- value.expression = resolved;
2521
+ path.node.value = createMergedClassNameValue(resolved);
2316
2522
  collectFromExpr(resolved, collectedClasses);
2317
2523
  }
2318
2524
  transformed = true;
@@ -2322,33 +2528,267 @@ function transformSourceCode(source) {
2322
2528
  }
2323
2529
  }
2324
2530
  if (t.isConditionalExpression(expression)) {
2325
- const resolved = tryStaticTransformNode(expression);
2531
+ const gbCond = (name) => path.scope.getBinding(name);
2532
+ const resolved = tryStaticTransformNode(expression, gbCond);
2326
2533
  if (resolved !== null) {
2327
2534
  path.node.name.name = "className";
2328
2535
  if (t.isStringLiteral(resolved)) {
2329
- path.node.value = resolved;
2536
+ path.node.value = createMergedClassNameValue(resolved);
2330
2537
  for (const c of resolved.value.split(/\s+/)) {
2331
2538
  if (c) {
2332
2539
  collectedClasses.add(c);
2333
2540
  }
2334
2541
  }
2335
2542
  } else {
2336
- value.expression = resolved;
2543
+ path.node.value = createMergedClassNameValue(resolved);
2337
2544
  collectFromExpr(resolved, collectedClasses);
2338
2545
  }
2339
2546
  transformed = true;
2340
2547
  return;
2341
2548
  }
2342
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}`);
2343
2653
  path.node.name.name = "className";
2344
2654
  const szCall = t.callExpression(
2345
2655
  t.identifier("_sz"),
2346
2656
  [expression]
2347
2657
  );
2348
- value.expression = szCall;
2658
+ path.node.value = createMergedClassNameValue(szCall);
2349
2659
  usesRuntime = true;
2350
2660
  transformed = true;
2351
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
+ }
2352
2792
  }
2353
2793
  }
2354
2794
  };
@@ -2359,29 +2799,72 @@ function transformSourceCode(source) {
2359
2799
  code: result?.code || source,
2360
2800
  transformed,
2361
2801
  usesRuntime,
2802
+ usesMerge,
2362
2803
  usesColorVar,
2363
- classes: collectedClasses
2804
+ classes: collectedClasses,
2805
+ rawClassNames,
2806
+ diagnostics
2364
2807
  };
2365
2808
  } catch (e) {
2366
2809
  console.warn("[csszyx] AST transform failed, falling back to original code:", e);
2367
- 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 };
2811
+ }
2812
+ }
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
+ }
2368
2830
  }
2831
+ return t.objectExpression(objProps);
2369
2832
  }
2370
- function tryStaticTransformNode(node) {
2833
+ function tryStaticTransformNode(node, getBinding) {
2834
+ if (t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node)) {
2835
+ return tryStaticTransformNode(node.expression, getBinding);
2836
+ }
2371
2837
  if (t.isObjectExpression(node)) {
2372
- const staticObj = evaluateStaticObject(node);
2838
+ const resolved = getBinding ? resolveObjectSpreads(node, getBinding) ?? node : node;
2839
+ const staticObj = evaluateStaticObject(resolved);
2373
2840
  if (staticObj !== null) {
2374
2841
  const { className } = transform(staticObj);
2375
2842
  return t.stringLiteral(className);
2376
2843
  }
2844
+ if (getBinding) {
2845
+ const hoisted = tryHoistConditionalSpread(node, getBinding);
2846
+ if (hoisted !== null) {
2847
+ return hoisted;
2848
+ }
2849
+ }
2377
2850
  return null;
2378
2851
  }
2379
2852
  if (t.isStringLiteral(node)) {
2380
2853
  return node;
2381
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
+ }
2382
2865
  if (t.isConditionalExpression(node)) {
2383
- const consequent = tryStaticTransformNode(node.consequent);
2384
- const alternate = tryStaticTransformNode(node.alternate);
2866
+ const consequent = tryStaticTransformNode(node.consequent, getBinding);
2867
+ const alternate = tryStaticTransformNode(node.alternate, getBinding);
2385
2868
  if (consequent !== null && alternate !== null) {
2386
2869
  return t.conditionalExpression(node.test, consequent, alternate);
2387
2870
  }
@@ -2389,6 +2872,39 @@ function tryStaticTransformNode(node) {
2389
2872
  }
2390
2873
  return null;
2391
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
+ }
2392
2908
  function evaluateStaticObject(node) {
2393
2909
  const result = {};
2394
2910
  for (const prop of node.properties) {
@@ -2403,6 +2919,8 @@ function evaluateStaticObject(node) {
2403
2919
  key = prop.key.name;
2404
2920
  } else if (t.isStringLiteral(prop.key)) {
2405
2921
  key = prop.key.value;
2922
+ } else if (t.isNumericLiteral(prop.key)) {
2923
+ key = String(prop.key.value);
2406
2924
  } else {
2407
2925
  return null;
2408
2926
  }
@@ -2427,10 +2945,82 @@ function evaluateStaticObject(node) {
2427
2945
  }
2428
2946
  return result;
2429
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
+ }
2430
3019
  function evaluatePartialObject(node, variantChain = "") {
2431
3020
  const staticProps = {};
2432
3021
  const dynamicProps = /* @__PURE__ */ new Map();
2433
3022
  const rawClasses = [];
3023
+ const conditionalClasses = [];
2434
3024
  let usesColorVar = false;
2435
3025
  for (const prop of node.properties) {
2436
3026
  if (t.isSpreadElement(prop)) {
@@ -2447,6 +3037,8 @@ function evaluatePartialObject(node, variantChain = "") {
2447
3037
  key = prop.key.name;
2448
3038
  } else if (t.isStringLiteral(prop.key)) {
2449
3039
  key = prop.key.value;
3040
+ } else if (t.isNumericLiteral(prop.key)) {
3041
+ key = String(prop.key.value);
2450
3042
  } else {
2451
3043
  return null;
2452
3044
  }
@@ -2489,7 +3081,7 @@ function evaluatePartialObject(node, variantChain = "") {
2489
3081
  staticProps[key] = { color: colorStr, op: opVal };
2490
3082
  } else if (t.isExpression(opProp.value)) {
2491
3083
  const variantPfx = variantChain ? `${variantChain}:` : "";
2492
- rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/[var(${opVarName})]`);
3084
+ rawClasses.push(`${variantPfx}${twPrefix}-${colorStr}/(${opVarName})`);
2493
3085
  dynamicProps.set(uniqueKey, {
2494
3086
  expression: opProp.value,
2495
3087
  category: 3 /* UNITLESS */,
@@ -2528,6 +3120,7 @@ function evaluatePartialObject(node, variantChain = "") {
2528
3120
  dynamicProps.set(k, v);
2529
3121
  }
2530
3122
  rawClasses.push(...nestedResult.rawClasses);
3123
+ conditionalClasses.push(...nestedResult.conditionalClasses);
2531
3124
  if (nestedResult.usesColorVar) {
2532
3125
  usesColorVar = true;
2533
3126
  }
@@ -2536,6 +3129,25 @@ function evaluatePartialObject(node, variantChain = "") {
2536
3129
  }
2537
3130
  }
2538
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
+ }
2539
3151
  } else if (t.isExpression(value)) {
2540
3152
  const twPrefix = PROPERTY_MAP[key] || key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2541
3153
  const category = getPropertyCategory(key);
@@ -2555,7 +3167,7 @@ function evaluatePartialObject(node, variantChain = "") {
2555
3167
  return null;
2556
3168
  }
2557
3169
  }
2558
- return { staticProps, dynamicProps, rawClasses, hasSpread: false, usesColorVar };
3170
+ return { staticProps, dynamicProps, rawClasses, conditionalClasses, hasSpread: false, usesColorVar };
2559
3171
  }
2560
3172
  function generateStyleValueExpression(info) {
2561
3173
  const { expression, category } = info;
@@ -2618,7 +3230,7 @@ function collectFromExpr(node, classes) {
2618
3230
  function buildCSSVarClassName(info) {
2619
3231
  const { twPrefix, varName, variantChain } = info;
2620
3232
  const variantPrefix = variantChain ? `${getVariantPrefix(variantChain)}:` : "";
2621
- return `${variantPrefix}${twPrefix}-[var(${varName})]`;
3233
+ return `${variantPrefix}${twPrefix}-(${varName})`;
2622
3234
  }
2623
3235
 
2624
3236
  // src/compiler.ts
@@ -3083,12 +3695,16 @@ function mergeOptions(options = {}) {
3083
3695
  };
3084
3696
  }
3085
3697
  export {
3698
+ BOOLEAN_SHORTHANDS,
3086
3699
  COLOR_PROPERTIES,
3087
3700
  CsszyxCompiler,
3088
3701
  DEFAULT_COMPILER_OPTIONS,
3702
+ KNOWN_VARIANTS,
3089
3703
  ManifestBuilder,
3090
3704
  PROPERTY_CATEGORY_MAP,
3705
+ PROPERTY_MAP,
3091
3706
  PropertyCategory,
3707
+ SUGGESTION_MAP,
3092
3708
  VERSION,
3093
3709
  buildParentMap,
3094
3710
  createRecoveryToken,