@csszyx/compiler 0.10.8 → 0.10.10

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
@@ -81,6 +81,7 @@ function transformSourceCode(source, filename, options) {
81
81
  let usesColorVar = false;
82
82
  let transformed = false;
83
83
  const collectedClasses = /* @__PURE__ */ new Set();
84
+ const szsPendingClasses = [];
84
85
  const rawClassNames = /* @__PURE__ */ new Set();
85
86
  const diagnostics = [];
86
87
  const recoveryTokens = /* @__PURE__ */ new Map();
@@ -200,6 +201,70 @@ function transformSourceCode(source, filename, options) {
200
201
  transformed = true;
201
202
  return;
202
203
  }
204
+ if (attrName === "szs") {
205
+ const openingEl = path.parentPath?.isJSXOpeningElement() ? path.parentPath.node : null;
206
+ if (openingEl && isHostElementName(openingEl.name)) {
207
+ diagnostics.push(
208
+ `[csszyx] szs at ${filename ?? "<anonymous>"}: szs has no effect on a host element \u2014 it maps slot names of a custom component. Attribute left unchanged.`
209
+ );
210
+ return;
211
+ }
212
+ const container = path.node.value;
213
+ if (!t__namespace.isJSXExpressionContainer(container) || !t__namespace.isObjectExpression(container.expression)) {
214
+ diagnostics.push(szsUnsupportedMessage$1(filename));
215
+ return;
216
+ }
217
+ const slotMap = container.expression;
218
+ if (!isValidSzsSlotMap$1(slotMap)) {
219
+ diagnostics.push(szsUnsupportedMessage$1(filename));
220
+ return;
221
+ }
222
+ transformCore.setSzWarnLocation(
223
+ transformCore.formatSzWarnLocation(
224
+ filename ?? "file.tsx",
225
+ path.node.loc?.start.line,
226
+ options?.rootDir
227
+ )
228
+ );
229
+ const compiledSlots = [];
230
+ for (const prop of slotMap.properties) {
231
+ const slot = prop;
232
+ if (t__namespace.isStringLiteral(slot.value)) {
233
+ compiledSlots.push({
234
+ slot,
235
+ classes: slot.value.value,
236
+ rewrite: false
237
+ });
238
+ continue;
239
+ }
240
+ const compiled = tryStaticTransformNode(slot.value);
241
+ if (!compiled || !t__namespace.isStringLiteral(compiled)) {
242
+ diagnostics.push(szsUnsupportedMessage$1(filename));
243
+ return;
244
+ }
245
+ compiledSlots.push({
246
+ slot,
247
+ classes: compiled.value,
248
+ rewrite: true
249
+ });
250
+ }
251
+ for (const {
252
+ slot,
253
+ classes: slotClasses,
254
+ rewrite
255
+ } of compiledSlots) {
256
+ if (rewrite) {
257
+ slot.value = t__namespace.stringLiteral(slotClasses);
258
+ transformed = true;
259
+ }
260
+ for (const c of slotClasses.split(/\s+/)) {
261
+ if (c) {
262
+ szsPendingClasses.push(c);
263
+ }
264
+ }
265
+ }
266
+ return;
267
+ }
203
268
  if (attrName !== "sz") {
204
269
  return;
205
270
  }
@@ -390,6 +455,17 @@ function transformSourceCode(source, filename, options) {
390
455
  transformed = true;
391
456
  return;
392
457
  }
458
+ const hoistedNested = tryHoistNestedConditional(
459
+ flatExpression,
460
+ getBinding
461
+ );
462
+ if (hoistedNested !== null) {
463
+ path.node.name.name = "className";
464
+ path.node.value = createMergedClassNameValue(hoistedNested);
465
+ collectFromExpr(hoistedNested, collectedClasses);
466
+ transformed = true;
467
+ return;
468
+ }
393
469
  const partial = evaluatePartialObject$1(flatExpression);
394
470
  if (partial !== null && !partial.hasSpread && (partial.dynamicProps.size > 0 || partial.conditionalClasses.length > 0)) {
395
471
  const staticClasses = [];
@@ -778,6 +854,9 @@ function transformSourceCode(source, filename, options) {
778
854
  })
779
855
  ]
780
856
  });
857
+ for (const c of szsPendingClasses) {
858
+ collectedClasses.add(c);
859
+ }
781
860
  return {
782
861
  code: result?.code || source,
783
862
  transformed,
@@ -829,6 +908,31 @@ function parseStyleStringToObjectExpr(styleStr) {
829
908
  }
830
909
  return t__namespace.objectExpression(objProps);
831
910
  }
911
+ function isHostElementName(name) {
912
+ return t__namespace.isJSXIdentifier(name) && /^[a-z]/.test(name.name);
913
+ }
914
+ function szsUnsupportedMessage$1(filename) {
915
+ return `[csszyx] szs at ${filename ?? "<anonymous>"}: every slot must be an identifier key with a static object literal (or class string) value. Attribute left unchanged.`;
916
+ }
917
+ function isPureLiteralSzValue$1(node) {
918
+ if (t__namespace.isStringLiteral(node) || t__namespace.isNumericLiteral(node) || t__namespace.isBooleanLiteral(node)) {
919
+ return true;
920
+ }
921
+ if (t__namespace.isUnaryExpression(node) && node.operator === "-" && t__namespace.isNumericLiteral(node.argument)) {
922
+ return true;
923
+ }
924
+ if (t__namespace.isObjectExpression(node)) {
925
+ return node.properties.every(
926
+ (prop) => t__namespace.isObjectProperty(prop) && !prop.computed && t__namespace.isIdentifier(prop.key) && isPureLiteralSzValue$1(prop.value)
927
+ );
928
+ }
929
+ return false;
930
+ }
931
+ function isValidSzsSlotMap$1(slotMap) {
932
+ return slotMap.properties.every(
933
+ (prop) => t__namespace.isObjectProperty(prop) && !prop.computed && t__namespace.isIdentifier(prop.key) && (t__namespace.isStringLiteral(prop.value) || t__namespace.isObjectExpression(prop.value) && isPureLiteralSzValue$1(prop.value))
934
+ );
935
+ }
832
936
  function emptyClassToUndefined(node) {
833
937
  return t__namespace.isStringLiteral(node) && node.value === "" ? t__namespace.identifier("undefined") : node;
834
938
  }
@@ -878,6 +982,123 @@ function tryStaticTransformNode(node, getBinding) {
878
982
  }
879
983
  return null;
880
984
  }
985
+ function scanNestedConditionals(node) {
986
+ let topLevel = 0;
987
+ let nested = 0;
988
+ let test = null;
989
+ for (const prop of node.properties) {
990
+ if (!t__namespace.isObjectProperty(prop)) {
991
+ continue;
992
+ }
993
+ const value = prop.value;
994
+ if (t__namespace.isConditionalExpression(value)) {
995
+ topLevel++;
996
+ } else if (t__namespace.isObjectExpression(value)) {
997
+ nested += countAllConditionals(value);
998
+ test ??= firstConditionalTest(value);
999
+ }
1000
+ }
1001
+ return { topLevel, nested, test };
1002
+ }
1003
+ function countAllConditionals(node) {
1004
+ let count = 0;
1005
+ for (const prop of node.properties) {
1006
+ if (!t__namespace.isObjectProperty(prop)) {
1007
+ continue;
1008
+ }
1009
+ const value = prop.value;
1010
+ if (t__namespace.isConditionalExpression(value)) {
1011
+ count++;
1012
+ } else if (t__namespace.isObjectExpression(value)) {
1013
+ count += countAllConditionals(value);
1014
+ }
1015
+ }
1016
+ return count;
1017
+ }
1018
+ function firstConditionalTest(node) {
1019
+ for (const prop of node.properties) {
1020
+ if (!t__namespace.isObjectProperty(prop)) {
1021
+ continue;
1022
+ }
1023
+ const value = prop.value;
1024
+ if (t__namespace.isConditionalExpression(value)) {
1025
+ return value.test;
1026
+ }
1027
+ if (t__namespace.isObjectExpression(value)) {
1028
+ const inner = firstConditionalTest(value);
1029
+ if (inner) {
1030
+ return inner;
1031
+ }
1032
+ }
1033
+ }
1034
+ return null;
1035
+ }
1036
+ function cloneObjectPickingBranch(node, pick) {
1037
+ return t__namespace.objectExpression(
1038
+ node.properties.map((prop) => {
1039
+ if (!t__namespace.isObjectProperty(prop)) {
1040
+ return t__namespace.cloneNode(prop);
1041
+ }
1042
+ const value = prop.value;
1043
+ let nextValue = t__namespace.cloneNode(value);
1044
+ if (t__namespace.isConditionalExpression(value)) {
1045
+ nextValue = t__namespace.cloneNode(value[pick]);
1046
+ } else if (t__namespace.isObjectExpression(value)) {
1047
+ nextValue = cloneObjectPickingBranch(value, pick);
1048
+ }
1049
+ return t__namespace.objectProperty(
1050
+ t__namespace.cloneNode(prop.key),
1051
+ nextValue,
1052
+ prop.computed,
1053
+ prop.shorthand
1054
+ );
1055
+ })
1056
+ );
1057
+ }
1058
+ function tryHoistNestedConditional(node, getBinding) {
1059
+ const { topLevel, nested, test } = scanNestedConditionals(node);
1060
+ if (topLevel !== 0 || nested !== 1 || test === null) {
1061
+ return null;
1062
+ }
1063
+ const condPropIndex = node.properties.findIndex(
1064
+ (prop) => t__namespace.isObjectProperty(prop) && t__namespace.isObjectExpression(prop.value) && countAllConditionals(prop.value) === 1
1065
+ );
1066
+ if (condPropIndex === -1) {
1067
+ return null;
1068
+ }
1069
+ const staticNode = t__namespace.objectExpression(node.properties.filter((_, i) => i !== condPropIndex));
1070
+ const condNode = t__namespace.objectExpression([node.properties[condPropIndex]]);
1071
+ const staticClasses = staticNode.properties.length > 0 ? tryStaticTransformNode(staticNode, getBinding) : null;
1072
+ const consequent = tryStaticTransformNode(
1073
+ cloneObjectPickingBranch(condNode, "consequent"),
1074
+ getBinding
1075
+ );
1076
+ const alternate = tryStaticTransformNode(
1077
+ cloneObjectPickingBranch(condNode, "alternate"),
1078
+ getBinding
1079
+ );
1080
+ if (!consequent || !alternate || !t__namespace.isStringLiteral(consequent) || !t__namespace.isStringLiteral(alternate) || staticNode.properties.length > 0 && (!staticClasses || !t__namespace.isStringLiteral(staticClasses))) {
1081
+ return null;
1082
+ }
1083
+ const ternary = t__namespace.conditionalExpression(
1084
+ test,
1085
+ emptyClassToUndefined(consequent),
1086
+ emptyClassToUndefined(alternate)
1087
+ );
1088
+ if (!staticClasses || !t__namespace.isStringLiteral(staticClasses) || staticClasses.value === "") {
1089
+ return ternary;
1090
+ }
1091
+ return t__namespace.templateLiteral(
1092
+ [
1093
+ t__namespace.templateElement(
1094
+ { raw: `${staticClasses.value} `, cooked: `${staticClasses.value} ` },
1095
+ false
1096
+ ),
1097
+ t__namespace.templateElement({ raw: "", cooked: "" }, true)
1098
+ ],
1099
+ [ternary]
1100
+ );
1101
+ }
881
1102
  function tryHoistConditionalSpread(node, getBinding) {
882
1103
  let conditionalSpreadIdx = -1;
883
1104
  let conditionalExpr = null;
@@ -1286,6 +1507,18 @@ function collectFromExpr(node, classes) {
1286
1507
  } else if (t__namespace.isConditionalExpression(node)) {
1287
1508
  collectFromExpr(node.consequent, classes);
1288
1509
  collectFromExpr(node.alternate, classes);
1510
+ } else if (t__namespace.isTemplateLiteral(node)) {
1511
+ for (let i = 0; i < node.quasis.length; i++) {
1512
+ for (const c of (node.quasis[i].value.cooked ?? "").split(/\s+/)) {
1513
+ if (c) {
1514
+ classes.add(c);
1515
+ }
1516
+ }
1517
+ const expr = node.expressions[i];
1518
+ if (expr && t__namespace.isExpression(expr)) {
1519
+ collectFromExpr(expr, classes);
1520
+ }
1521
+ }
1289
1522
  }
1290
1523
  }
1291
1524
  function collectCandidatesFromBabelExpr(node, path, classes) {
@@ -1536,6 +1769,10 @@ class CsszyxCompiler {
1536
1769
  }
1537
1770
  }
1538
1771
 
1772
+ function sortStrings(values) {
1773
+ return [...values].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
1774
+ }
1775
+
1539
1776
  const CSS_VAR_REFERENCE_RE = /var\(\s*(--[\w-]+)/g;
1540
1777
  function scanGlobalVarUsages(source, filename = "file.tsx", options = {}) {
1541
1778
  if (!source.includes("--") && !source.includes("var(")) {
@@ -1745,7 +1982,7 @@ function extractVarReferences(value) {
1745
1982
  for (const match of value.matchAll(CSS_VAR_REFERENCE_RE)) {
1746
1983
  refs.add(match[1]);
1747
1984
  }
1748
- return [...refs].sort();
1985
+ return sortStrings(refs);
1749
1986
  }
1750
1987
  function shouldReportToken(name, tokenFilter) {
1751
1988
  return tokenFilter === null || tokenFilter.has(name);
@@ -2019,7 +2256,7 @@ class ManifestBuilder {
2019
2256
  * @returns {string} SHA-256 checksum
2020
2257
  */
2021
2258
  computeChecksum(tokens) {
2022
- const sortedKeys = Object.keys(tokens).sort();
2259
+ const sortedKeys = sortStrings(Object.keys(tokens));
2023
2260
  const sortedTokens = {};
2024
2261
  for (const key of sortedKeys) {
2025
2262
  sortedTokens[key] = tokens[key];
@@ -2295,6 +2532,7 @@ class OxcNotImplementedError extends Error {
2295
2532
  }
2296
2533
  function transformOxc(source, filename, options) {
2297
2534
  const classes = /* @__PURE__ */ new Set();
2535
+ const szsPendingClasses = [];
2298
2536
  const rawClassNames = /* @__PURE__ */ new Set();
2299
2537
  const diagnostics = [];
2300
2538
  const recoveryTokens = /* @__PURE__ */ new Map();
@@ -2366,6 +2604,7 @@ function transformOxc(source, filename, options) {
2366
2604
  const openingNode = node;
2367
2605
  const attrs = openingNode.attributes ?? [];
2368
2606
  const szAttrs = [];
2607
+ const szsAttrs = [];
2369
2608
  let classNameAttr = null;
2370
2609
  let styleAttr = null;
2371
2610
  let szRecoverAttr = null;
@@ -2398,6 +2637,8 @@ function transformOxc(source, filename, options) {
2398
2637
  const name = attr.name?.name;
2399
2638
  if (name === "sz") {
2400
2639
  szAttrs.push(attr);
2640
+ } else if (name === "szs") {
2641
+ szsAttrs.push(attr);
2401
2642
  } else if (name === "className" || name === "class") {
2402
2643
  classNameAttr = attr;
2403
2644
  } else if (name === "style") {
@@ -2448,6 +2689,89 @@ function transformOxc(source, filename, options) {
2448
2689
  }
2449
2690
  }
2450
2691
  }
2692
+ for (const szsAttr of szsAttrs) {
2693
+ if (isHostOpeningElementName(openingNode.name)) {
2694
+ diagnostics.push(
2695
+ `[csszyx] szs at ${effectiveFilename}: szs has no effect on a host element \u2014 it maps slot names of a custom component. Attribute left unchanged.`
2696
+ );
2697
+ continue;
2698
+ }
2699
+ const szsValue = szsAttr.value;
2700
+ const szsExpression = szsValue && szsValue.type === "JSXExpressionContainer" ? szsValue.expression : null;
2701
+ if (!szsExpression || szsExpression.type !== "ObjectExpression") {
2702
+ diagnostics.push(szsUnsupportedMessage(effectiveFilename));
2703
+ continue;
2704
+ }
2705
+ const slotMap = szsExpression;
2706
+ if (!isValidSzsSlotMap(slotMap)) {
2707
+ diagnostics.push(szsUnsupportedMessage(effectiveFilename));
2708
+ continue;
2709
+ }
2710
+ const { line: szsWarnLine } = offsetToLineColumn(source, szsAttr.start);
2711
+ transformCore.setSzWarnLocation(
2712
+ transformCore.formatSzWarnLocation(effectiveFilename, szsWarnLine, options?.rootDir)
2713
+ );
2714
+ const slotEntries = [];
2715
+ let anyCompiled = false;
2716
+ let slotFailed = false;
2717
+ for (const propRaw of slotMap.properties) {
2718
+ const prop = propRaw;
2719
+ const keyText = source.slice(prop.key.start, prop.key.end);
2720
+ const propValue = prop.value;
2721
+ const literal = propValue.type === "Literal" ? propValue.value : null;
2722
+ if (typeof literal === "string") {
2723
+ slotEntries.push({
2724
+ keyText,
2725
+ classNames: literal,
2726
+ text: source.slice(propValue.start, propValue.end)
2727
+ });
2728
+ continue;
2729
+ }
2730
+ try {
2731
+ const slotObject = astObjectToSzObject(
2732
+ propValue,
2733
+ effectiveFilename,
2734
+ objectBindings
2735
+ );
2736
+ const compiled = transformCore.transform(
2737
+ applyGlobalVarAliasesToSzObject(
2738
+ slotObject,
2739
+ globalVarAliases,
2740
+ cssVariableMap
2741
+ )
2742
+ ).className;
2743
+ slotEntries.push({
2744
+ keyText,
2745
+ classNames: compiled,
2746
+ text: JSON.stringify(compiled)
2747
+ });
2748
+ anyCompiled = true;
2749
+ } catch (err) {
2750
+ if (err instanceof OxcNotImplementedError) {
2751
+ slotFailed = true;
2752
+ break;
2753
+ }
2754
+ throw err;
2755
+ }
2756
+ }
2757
+ transformCore.setSzWarnLocation(void 0);
2758
+ if (slotFailed) {
2759
+ diagnostics.push(szsUnsupportedMessage(effectiveFilename));
2760
+ continue;
2761
+ }
2762
+ if (anyCompiled) {
2763
+ const body = slotEntries.map((entry) => `${entry.keyText}: ${entry.text}`).join(", ");
2764
+ edits.overwrite(szsAttr.start, szsAttr.end, `szs={{ ${body} }}`);
2765
+ transformed = true;
2766
+ }
2767
+ for (const entry of slotEntries) {
2768
+ for (const c of entry.classNames.split(/\s+/)) {
2769
+ if (c) {
2770
+ szsPendingClasses.push(c);
2771
+ }
2772
+ }
2773
+ }
2774
+ }
2451
2775
  if (szAttrs.length === 0) {
2452
2776
  applyHoistedStyleProps();
2453
2777
  return;
@@ -2645,6 +2969,29 @@ function transformOxc(source, filename, options) {
2645
2969
  transformed = true;
2646
2970
  return;
2647
2971
  }
2972
+ const nestedConditionalClassExpr = buildNestedConditionalClassExpression(
2973
+ expression,
2974
+ effectiveFilename,
2975
+ objectBindings,
2976
+ source,
2977
+ classes,
2978
+ globalVarAliases,
2979
+ cssVariableMap
2980
+ );
2981
+ if (nestedConditionalClassExpr) {
2982
+ if (classNameAttr || szAttrs.length > 1) {
2983
+ runtimeFallbackExpr = expression;
2984
+ runtimeFallbackAttr = szAttr;
2985
+ break;
2986
+ }
2987
+ edits.overwrite(
2988
+ szAttr.start,
2989
+ szAttr.end,
2990
+ `className={${nestedConditionalClassExpr}}`
2991
+ );
2992
+ transformed = true;
2993
+ return;
2994
+ }
2648
2995
  const partial = buildPartialObjectTransform(
2649
2996
  expression,
2650
2997
  effectiveFilename,
@@ -2803,6 +3150,9 @@ function transformOxc(source, filename, options) {
2803
3150
  }
2804
3151
  transformed = true;
2805
3152
  });
3153
+ for (const c of szsPendingClasses) {
3154
+ classes.add(c);
3155
+ }
2806
3156
  return {
2807
3157
  code: transformed ? edits.toString() : source,
2808
3158
  transformed,
@@ -2872,6 +3222,49 @@ function buildRuntimeFallbackDiagnostic(expression, source) {
2872
3222
  return `sz fallback at ${lineCol}: ${reason}.
2873
3223
  Suggestion: ${suggestion}`;
2874
3224
  }
3225
+ function isHostOpeningElementName(nameNode) {
3226
+ return nameNode.type === "JSXIdentifier" && /^[a-z]/.test(String(nameNode.name));
3227
+ }
3228
+ function szsUnsupportedMessage(filename) {
3229
+ return `[csszyx] szs at ${filename}: every slot must be an identifier key with a static object literal (or class string) value. Attribute left unchanged.`;
3230
+ }
3231
+ function isPureLiteralSzValue(node) {
3232
+ if (node.type === "Literal") {
3233
+ const value = node.value;
3234
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
3235
+ }
3236
+ if (node.type === "UnaryExpression") {
3237
+ const unary = node;
3238
+ return unary.operator === "-" && unary.argument.type === "Literal" && typeof unary.argument.value === "number";
3239
+ }
3240
+ if (node.type === "ObjectExpression") {
3241
+ const properties = node.properties;
3242
+ return properties.every((propRaw) => {
3243
+ if (propRaw.type !== "Property") {
3244
+ return false;
3245
+ }
3246
+ const prop = propRaw;
3247
+ return !prop.computed && prop.key.type === "Identifier" && isPureLiteralSzValue(prop.value);
3248
+ });
3249
+ }
3250
+ return false;
3251
+ }
3252
+ function isValidSzsSlotMap(slotMap) {
3253
+ return slotMap.properties.every((propRaw) => {
3254
+ if (propRaw.type !== "Property") {
3255
+ return false;
3256
+ }
3257
+ const prop = propRaw;
3258
+ if (prop.computed || prop.key.type !== "Identifier") {
3259
+ return false;
3260
+ }
3261
+ const value = prop.value;
3262
+ if (value.type === "Literal" && typeof value.value === "string") {
3263
+ return true;
3264
+ }
3265
+ return value.type === "ObjectExpression" && isPureLiteralSzValue(value);
3266
+ });
3267
+ }
2875
3268
  function extractElementName(nameNode) {
2876
3269
  if (nameNode.type === "JSXIdentifier") {
2877
3270
  return String(nameNode.name);
@@ -2881,7 +3274,7 @@ function extractElementName(nameNode) {
2881
3274
  }
2882
3275
  return "<unknown>";
2883
3276
  }
2884
- function astObjectToSzObject(node, filename, bindings) {
3277
+ function astObjectToSzObject(node, filename, bindings, branchPick) {
2885
3278
  const result = {};
2886
3279
  for (const propRaw of node.properties) {
2887
3280
  if (propRaw.type === "SpreadElement") {
@@ -2918,7 +3311,7 @@ function astObjectToSzObject(node, filename, bindings) {
2918
3311
  `unsupported key shape ${prop.key.type} at ${filename}:${prop.key.start}`
2919
3312
  );
2920
3313
  }
2921
- result[key] = astValueToSzValue(prop.value, filename, bindings);
3314
+ result[key] = astValueToSzValue(prop.value, filename, bindings, branchPick);
2922
3315
  }
2923
3316
  return result;
2924
3317
  }
@@ -3350,6 +3743,99 @@ function resolveObjectExpression(node, bindings) {
3350
3743
  }
3351
3744
  return null;
3352
3745
  }
3746
+ function countOxcConditionals(node) {
3747
+ let count = 0;
3748
+ for (const propRaw of node.properties) {
3749
+ if (propRaw.type !== "Property") {
3750
+ continue;
3751
+ }
3752
+ const value = propRaw.value;
3753
+ if (value.type === "ConditionalExpression") {
3754
+ count++;
3755
+ } else if (value.type === "ObjectExpression") {
3756
+ count += countOxcConditionals(value);
3757
+ }
3758
+ }
3759
+ return count;
3760
+ }
3761
+ function firstOxcConditional(node) {
3762
+ for (const propRaw of node.properties) {
3763
+ if (propRaw.type !== "Property") {
3764
+ continue;
3765
+ }
3766
+ const value = propRaw.value;
3767
+ if (value.type === "ConditionalExpression") {
3768
+ return value;
3769
+ }
3770
+ if (value.type === "ObjectExpression") {
3771
+ const found = firstOxcConditional(value);
3772
+ if (found) {
3773
+ return found;
3774
+ }
3775
+ }
3776
+ }
3777
+ return null;
3778
+ }
3779
+ function buildNestedConditionalClassExpression(node, filename, bindings, source, classes, globalVarAliases, cssVariableMap) {
3780
+ let topLevel = 0;
3781
+ for (const propRaw of node.properties) {
3782
+ if (propRaw.type === "Property" && propRaw.value.type === "ConditionalExpression") {
3783
+ topLevel++;
3784
+ }
3785
+ }
3786
+ const first = firstOxcConditional(node);
3787
+ if (topLevel !== 0 || countOxcConditionals(node) !== 1 || !first) {
3788
+ return null;
3789
+ }
3790
+ const condPropIndex = node.properties.findIndex(
3791
+ (prop) => prop.type === "Property" && prop.value.type === "ObjectExpression" && countOxcConditionals(prop.value) === 1
3792
+ );
3793
+ if (condPropIndex === -1) {
3794
+ return null;
3795
+ }
3796
+ const staticNode = {
3797
+ ...node,
3798
+ properties: node.properties.filter((_, i) => i !== condPropIndex)
3799
+ };
3800
+ const condNode = {
3801
+ ...node,
3802
+ properties: [node.properties[condPropIndex]]
3803
+ };
3804
+ const compile = (target, pick) => {
3805
+ try {
3806
+ return transformCore.transform(
3807
+ applyGlobalVarAliasesToSzObject(
3808
+ astObjectToSzObject(target, filename, bindings, pick),
3809
+ globalVarAliases,
3810
+ cssVariableMap
3811
+ )
3812
+ ).className;
3813
+ } catch (err) {
3814
+ if (err instanceof OxcNotImplementedError) {
3815
+ return null;
3816
+ }
3817
+ throw err;
3818
+ }
3819
+ };
3820
+ const staticClasses = staticNode.properties.length > 0 ? compile(staticNode) : "";
3821
+ const consequent = compile(condNode, "consequent");
3822
+ const alternate = compile(condNode, "alternate");
3823
+ if (staticClasses === null || consequent === null || alternate === null) {
3824
+ return null;
3825
+ }
3826
+ for (const cls of `${staticClasses} ${consequent} ${alternate}`.split(/\s+/)) {
3827
+ if (cls) {
3828
+ classes.add(cls);
3829
+ }
3830
+ }
3831
+ const testSource = source.slice(first.test.start, first.test.end);
3832
+ const branch = (cls) => cls === "" ? "undefined" : JSON.stringify(cls);
3833
+ const ternary = `${testSource} ? ${branch(consequent)} : ${branch(alternate)}`;
3834
+ if (staticClasses === "") {
3835
+ return ternary;
3836
+ }
3837
+ return `\`${staticClasses} \${${ternary}}\``;
3838
+ }
3353
3839
  function buildConditionalSpreadClassExpression(node, filename, bindings, source, classes, globalVarAliases, cssVariableMap) {
3354
3840
  let conditionalSpread = null;
3355
3841
  const otherProps = [];
@@ -4134,7 +4620,15 @@ function extractKeyName(key) {
4134
4620
  }
4135
4621
  return null;
4136
4622
  }
4137
- function astValueToSzValue(node, filename, bindings) {
4623
+ function astValueToSzValue(node, filename, bindings, branchPick) {
4624
+ if (branchPick && node.type === "ConditionalExpression") {
4625
+ return astValueToSzValue(
4626
+ node[branchPick],
4627
+ filename,
4628
+ bindings,
4629
+ branchPick
4630
+ );
4631
+ }
4138
4632
  if (node.type === "Literal") {
4139
4633
  const value = node.value;
4140
4634
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
@@ -4160,7 +4654,7 @@ function astValueToSzValue(node, filename, bindings) {
4160
4654
  );
4161
4655
  }
4162
4656
  if (node.type === "ObjectExpression") {
4163
- return astObjectToSzObject(node, filename, bindings);
4657
+ return astObjectToSzObject(node, filename, bindings, branchPick);
4164
4658
  }
4165
4659
  if (node.type === "Identifier" || node.type === "MemberExpression") {
4166
4660
  throw new OxcNotImplementedError(
@@ -4431,6 +4925,7 @@ exports.mergeOptions = mergeOptions;
4431
4925
  exports.parseManifest = parseManifest;
4432
4926
  exports.scanGlobalVarUsages = scanGlobalVarUsages;
4433
4927
  exports.serializeManifest = serializeManifest;
4928
+ exports.sortStrings = sortStrings;
4434
4929
  exports.transformOxc = transformOxc;
4435
4930
  exports.transformRust = transformRust;
4436
4931
  exports.transformRustBatch = transformRustBatch;