@csszyx/compiler 0.9.0 → 0.9.2

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
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  const core = require('@csszyx/core');
4
- const transformCore = require('./shared/compiler.DZgazEy8.cjs');
4
+ const transformCore = require('./shared/compiler.BfDLUvcf.cjs');
5
+ const oxcParser = require('oxc-parser');
5
6
  const t = require('@babel/types');
6
7
  const node_crypto = require('node:crypto');
7
8
  const babel = require('@babel/core');
8
9
  const MagicString = require('magic-string');
9
- const oxcParser = require('oxc-parser');
10
10
  const native = require('@csszyx/core/native');
11
11
 
12
12
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -84,6 +84,7 @@ function transformSourceCode(source, filename, options) {
84
84
  const rawClassNames = /* @__PURE__ */ new Set();
85
85
  const diagnostics = [];
86
86
  const recoveryTokens = /* @__PURE__ */ new Map();
87
+ const cssVariableMap = /* @__PURE__ */ new Map();
87
88
  if (!source.includes("sz")) {
88
89
  return {
89
90
  code: source,
@@ -94,7 +95,8 @@ function transformSourceCode(source, filename, options) {
94
95
  classes: collectedClasses,
95
96
  rawClassNames,
96
97
  diagnostics,
97
- recoveryTokens
98
+ recoveryTokens,
99
+ cssVariableMap
98
100
  };
99
101
  }
100
102
  try {
@@ -756,7 +758,8 @@ function transformSourceCode(source, filename, options) {
756
758
  classes: collectedClasses,
757
759
  rawClassNames,
758
760
  diagnostics,
759
- recoveryTokens
761
+ recoveryTokens,
762
+ cssVariableMap
760
763
  };
761
764
  } catch (e) {
762
765
  if (e instanceof ASTBudgetExceededError) {
@@ -772,7 +775,8 @@ function transformSourceCode(source, filename, options) {
772
775
  classes: collectedClasses,
773
776
  rawClassNames,
774
777
  diagnostics,
775
- recoveryTokens
778
+ recoveryTokens,
779
+ cssVariableMap
776
780
  };
777
781
  }
778
782
  }
@@ -1324,6 +1328,271 @@ class CsszyxCompiler {
1324
1328
  }
1325
1329
  }
1326
1330
 
1331
+ const CSS_VAR_REFERENCE_RE = /var\(\s*(--[\w-]+)/g;
1332
+ function scanGlobalVarUsages(source, filename = "file.tsx", options = {}) {
1333
+ if (!source.includes("--") && !source.includes("var(")) {
1334
+ return [];
1335
+ }
1336
+ const parsed = oxcParser.parseSync(filename, source);
1337
+ if (parsed.errors.length > 0) {
1338
+ throw new Error(
1339
+ `oxc-parser errors in ${filename}: ${parsed.errors.map((error) => error.message).join("; ")}`
1340
+ );
1341
+ }
1342
+ const tokenFilter = options.tokens ? new Set(options.tokens) : null;
1343
+ const constantStrings = collectStringConstants(parsed.program);
1344
+ const diagnostics = [];
1345
+ walk$1(parsed.program, (node, ancestors) => {
1346
+ if (node.type === "CallExpression") {
1347
+ collectStyleMethodDiagnostic(
1348
+ node,
1349
+ source,
1350
+ filename,
1351
+ constantStrings,
1352
+ tokenFilter,
1353
+ diagnostics
1354
+ );
1355
+ return;
1356
+ }
1357
+ if (node.type !== "JSXAttribute") {
1358
+ return;
1359
+ }
1360
+ const attrName = jsxAttributeName(node);
1361
+ if (attrName === "style") {
1362
+ collectJsxStyleDiagnostics(
1363
+ node,
1364
+ source,
1365
+ filename,
1366
+ constantStrings,
1367
+ tokenFilter,
1368
+ diagnostics
1369
+ );
1370
+ return;
1371
+ }
1372
+ if (attrName === "className") {
1373
+ collectClassNameDiagnostics(node, source, filename, tokenFilter, diagnostics);
1374
+ return;
1375
+ }
1376
+ if (attrName === "sz" && ancestors.some((parent) => parent.type === "JSXAttribute")) {
1377
+ return;
1378
+ }
1379
+ });
1380
+ return diagnostics;
1381
+ }
1382
+ function collectStringConstants(root) {
1383
+ const constants = /* @__PURE__ */ new Map();
1384
+ walk$1(root, (node) => {
1385
+ if (node.type !== "VariableDeclarator") {
1386
+ return;
1387
+ }
1388
+ const id = node.id;
1389
+ const init = node.init;
1390
+ if (id?.type !== "Identifier" || !init) {
1391
+ return;
1392
+ }
1393
+ const name = id.name;
1394
+ const value = literalStringValue$1(init);
1395
+ if (typeof name === "string" && value !== null) {
1396
+ constants.set(name, value);
1397
+ }
1398
+ });
1399
+ return constants;
1400
+ }
1401
+ function collectStyleMethodDiagnostic(node, source, filename, constants, tokenFilter, diagnostics) {
1402
+ const method = memberMethodName(node.callee);
1403
+ if (!method || !["setProperty", "getPropertyValue", "removeProperty"].includes(method)) {
1404
+ return;
1405
+ }
1406
+ const firstArg = node.arguments?.[0];
1407
+ const name = firstArg ? resolveString(firstArg, constants) : null;
1408
+ if (!name?.startsWith("--") || !shouldReportToken(name, tokenFilter)) {
1409
+ return;
1410
+ }
1411
+ const kind = method === "setProperty" ? "style-set-property" : method === "getPropertyValue" ? "style-get-property" : "style-remove-property";
1412
+ diagnostics.push(createDiagnostic(kind, name, node, source, filename));
1413
+ }
1414
+ function collectJsxStyleDiagnostics(attr, source, filename, constants, tokenFilter, diagnostics) {
1415
+ const expression = jsxExpression(attr);
1416
+ if (expression?.type !== "ObjectExpression") {
1417
+ return;
1418
+ }
1419
+ for (const prop of expression.properties ?? []) {
1420
+ if (prop.type !== "Property") {
1421
+ continue;
1422
+ }
1423
+ const name = propertyKeyString(prop, constants);
1424
+ if (name?.startsWith("--") && shouldReportToken(name, tokenFilter)) {
1425
+ diagnostics.push(createDiagnostic("jsx-style-key", name, prop, source, filename));
1426
+ }
1427
+ }
1428
+ }
1429
+ function collectClassNameDiagnostics(attr, source, filename, tokenFilter, diagnostics) {
1430
+ for (const value of jsxAttributeStringValues(attr)) {
1431
+ for (const name of extractVarReferences(value)) {
1432
+ if (shouldReportToken(name, tokenFilter)) {
1433
+ diagnostics.push(
1434
+ createDiagnostic("class-string-var-reference", name, attr, source, filename)
1435
+ );
1436
+ }
1437
+ }
1438
+ }
1439
+ }
1440
+ function jsxAttributeName(attr) {
1441
+ const name = attr.name;
1442
+ return name?.type === "JSXIdentifier" ? name.name ?? null : null;
1443
+ }
1444
+ function jsxExpression(attr) {
1445
+ const value = attr.value;
1446
+ if (value?.type !== "JSXExpressionContainer") {
1447
+ return null;
1448
+ }
1449
+ return value.expression ?? null;
1450
+ }
1451
+ function jsxAttributeStringValues(attr) {
1452
+ const value = attr.value;
1453
+ if (!value) {
1454
+ return [];
1455
+ }
1456
+ const direct = literalStringValue$1(value);
1457
+ if (direct !== null) {
1458
+ return [direct];
1459
+ }
1460
+ const expression = jsxExpression(attr);
1461
+ if (!expression) {
1462
+ return [];
1463
+ }
1464
+ const exprValue = literalStringValue$1(expression);
1465
+ if (exprValue !== null) {
1466
+ return [exprValue];
1467
+ }
1468
+ if (expression.type === "TemplateLiteral") {
1469
+ return templateStaticParts(expression);
1470
+ }
1471
+ return [];
1472
+ }
1473
+ function resolveString(node, constants) {
1474
+ const literal = literalStringValue$1(node);
1475
+ if (literal !== null) {
1476
+ return literal;
1477
+ }
1478
+ if (node.type === "Identifier") {
1479
+ const name = node.name;
1480
+ return typeof name === "string" ? constants.get(name) ?? null : null;
1481
+ }
1482
+ return null;
1483
+ }
1484
+ function literalStringValue$1(node) {
1485
+ if (node.type !== "Literal") {
1486
+ return null;
1487
+ }
1488
+ const value = node.value;
1489
+ return typeof value === "string" ? value : null;
1490
+ }
1491
+ function propertyKeyString(property, constants) {
1492
+ const key = property.key;
1493
+ const computed = property.computed === true;
1494
+ if (!key) {
1495
+ return null;
1496
+ }
1497
+ if (computed) {
1498
+ return resolveString(key, constants);
1499
+ }
1500
+ if (key.type === "Identifier") {
1501
+ const name = key.name;
1502
+ return typeof name === "string" ? name : null;
1503
+ }
1504
+ return literalStringValue$1(key);
1505
+ }
1506
+ function memberMethodName(callee) {
1507
+ if (callee?.type !== "MemberExpression") {
1508
+ return null;
1509
+ }
1510
+ const property = callee.property;
1511
+ if (!property) {
1512
+ return null;
1513
+ }
1514
+ if (property.type === "Identifier") {
1515
+ const name = property.name;
1516
+ return typeof name === "string" ? name : null;
1517
+ }
1518
+ return literalStringValue$1(property);
1519
+ }
1520
+ function templateStaticParts(node) {
1521
+ const quasis = node.quasis ?? [];
1522
+ const parts = [];
1523
+ for (const quasi of quasis) {
1524
+ const value = quasi.value;
1525
+ const cooked = value?.cooked;
1526
+ const raw = value?.raw;
1527
+ if (typeof cooked === "string") {
1528
+ parts.push(cooked);
1529
+ } else if (typeof raw === "string") {
1530
+ parts.push(raw);
1531
+ }
1532
+ }
1533
+ return parts;
1534
+ }
1535
+ function extractVarReferences(value) {
1536
+ const refs = /* @__PURE__ */ new Set();
1537
+ for (const match of value.matchAll(CSS_VAR_REFERENCE_RE)) {
1538
+ refs.add(match[1]);
1539
+ }
1540
+ return [...refs].sort();
1541
+ }
1542
+ function shouldReportToken(name, tokenFilter) {
1543
+ return tokenFilter === null || tokenFilter.has(name);
1544
+ }
1545
+ function createDiagnostic(kind, name, node, source, filename) {
1546
+ return {
1547
+ kind,
1548
+ name,
1549
+ location: offsetToLocation(source, node.start, filename),
1550
+ message: `${name} is used outside csszyx-owned global variable alias rewrites (${kind}).`
1551
+ };
1552
+ }
1553
+ function offsetToLocation(source, offset, filename) {
1554
+ let line = 1;
1555
+ let column = 1;
1556
+ for (let index = 0; index < offset; index++) {
1557
+ if (source.charCodeAt(index) === 10) {
1558
+ line++;
1559
+ column = 1;
1560
+ } else {
1561
+ column++;
1562
+ }
1563
+ }
1564
+ return { filePath: filename, line, column };
1565
+ }
1566
+ function walk$1(node, visit, ancestors = []) {
1567
+ if (!isOxcNode$1(node)) {
1568
+ return;
1569
+ }
1570
+ visit(node, ancestors);
1571
+ ancestors.push(node);
1572
+ for (const key of Object.keys(node)) {
1573
+ if (isAstMetadataKey$1(key)) {
1574
+ continue;
1575
+ }
1576
+ const child = node[key];
1577
+ if (Array.isArray(child)) {
1578
+ for (const item of child) {
1579
+ walk$1(item, visit, ancestors);
1580
+ }
1581
+ } else if (child && typeof child === "object") {
1582
+ walk$1(child, visit, ancestors);
1583
+ }
1584
+ }
1585
+ ancestors.pop();
1586
+ }
1587
+ function isOxcNode$1(value) {
1588
+ return Boolean(
1589
+ value && typeof value === "object" && typeof value.type === "string"
1590
+ );
1591
+ }
1592
+ function isAstMetadataKey$1(key) {
1593
+ return key === "loc" || key === "range" || key === "start" || key === "end" || key === "type";
1594
+ }
1595
+
1327
1596
  function findLCA(nodeA, nodeB, parentMap) {
1328
1597
  const ancestorsA = /* @__PURE__ */ new Set();
1329
1598
  let current = nodeA;
@@ -1657,6 +1926,155 @@ function injectRecoveryToken(attributes, token) {
1657
1926
  };
1658
1927
  }
1659
1928
 
1929
+ const DEFAULT_MAX_DEPTH = 5;
1930
+ function planComponentVariableHoistsWithDiagnostics(nodes, usages, options = {}) {
1931
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
1932
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
1933
+ const groups = /* @__PURE__ */ new Map();
1934
+ for (const usage of usages) {
1935
+ const groupKey = `${usage.name}\0${usage.valueKey}`;
1936
+ const group = groups.get(groupKey);
1937
+ if (group) {
1938
+ group.push(usage);
1939
+ } else {
1940
+ groups.set(groupKey, [usage]);
1941
+ }
1942
+ }
1943
+ const plans = [];
1944
+ const diagnostics = [];
1945
+ for (const group of groups.values()) {
1946
+ if (group.length < 2) {
1947
+ continue;
1948
+ }
1949
+ const targetElementId = findLowestCommonAncestor(
1950
+ group.map((usage) => usage.elementId),
1951
+ nodeById
1952
+ );
1953
+ if (!targetElementId) {
1954
+ diagnostics.push(buildHoistDiagnostic(group, "no-lca"));
1955
+ continue;
1956
+ }
1957
+ const target = nodeById.get(targetElementId);
1958
+ if (!target || target.canHost === false) {
1959
+ diagnostics.push(buildHoistDiagnostic(group, "non-host-ancestor"));
1960
+ continue;
1961
+ }
1962
+ if (group.some(
1963
+ (usage) => distanceToAncestor(usage.elementId, targetElementId, nodeById) > maxDepth
1964
+ )) {
1965
+ diagnostics.push(buildHoistDiagnostic(group, "max-depth", maxDepth));
1966
+ continue;
1967
+ }
1968
+ plans.push({
1969
+ name: group[0].name,
1970
+ valueKey: group[0].valueKey,
1971
+ targetElementId,
1972
+ usageIds: group.map((usage) => usage.id)
1973
+ });
1974
+ }
1975
+ return { plans, diagnostics };
1976
+ }
1977
+ function buildHoistDiagnostic(group, reason, maxDepth) {
1978
+ return {
1979
+ name: group[0]?.name ?? "",
1980
+ reason,
1981
+ usageCount: group.length,
1982
+ ...maxDepth === void 0 ? {} : { maxDepth }
1983
+ };
1984
+ }
1985
+ function findLowestCommonAncestor(elementIds, nodeById) {
1986
+ const [first, ...rest] = elementIds;
1987
+ if (!first) {
1988
+ return null;
1989
+ }
1990
+ let currentAncestors = ancestorChain(first, nodeById);
1991
+ for (const elementId of rest) {
1992
+ const nextAncestors = new Set(ancestorChain(elementId, nodeById));
1993
+ currentAncestors = currentAncestors.filter((id) => nextAncestors.has(id));
1994
+ if (currentAncestors.length === 0) {
1995
+ return null;
1996
+ }
1997
+ }
1998
+ return currentAncestors[0] ?? null;
1999
+ }
2000
+ function ancestorChain(elementId, nodeById) {
2001
+ const chain = [];
2002
+ let current = elementId;
2003
+ while (current) {
2004
+ const node = nodeById.get(current);
2005
+ if (!node) {
2006
+ break;
2007
+ }
2008
+ chain.push(current);
2009
+ current = node.parentId;
2010
+ }
2011
+ return chain;
2012
+ }
2013
+ function distanceToAncestor(elementId, ancestorId, nodeById) {
2014
+ let distance = 0;
2015
+ let current = elementId;
2016
+ while (current) {
2017
+ if (current === ancestorId) {
2018
+ return distance;
2019
+ }
2020
+ const node = nodeById.get(current);
2021
+ if (!node) {
2022
+ break;
2023
+ }
2024
+ current = node.parentId;
2025
+ distance++;
2026
+ }
2027
+ return Number.POSITIVE_INFINITY;
2028
+ }
2029
+
2030
+ function planCSSVariableNames(usages, options = {}) {
2031
+ const componentNames = /* @__PURE__ */ new Map();
2032
+ const scopedNamesByElement = /* @__PURE__ */ new Map();
2033
+ const reservedNames = options.reservedNames ?? /* @__PURE__ */ new Set();
2034
+ return usages.map((usage) => {
2035
+ if (usage.tier === "component") {
2036
+ const key2 = canonicalUsageKey(usage);
2037
+ let name2 = componentNames.get(key2);
2038
+ if (!name2) {
2039
+ name2 = allocateVariableName("--c", componentNames.size, reservedNames);
2040
+ componentNames.set(key2, name2);
2041
+ }
2042
+ return { ...usage, name: name2 };
2043
+ }
2044
+ const elementId = usage.elementId ?? "";
2045
+ const elementNames = getOrCreate(scopedNamesByElement, elementId, () => /* @__PURE__ */ new Map());
2046
+ const key = canonicalUsageKey(usage);
2047
+ let name = elementNames.get(key);
2048
+ if (!name) {
2049
+ name = allocateVariableName("--s", elementNames.size, reservedNames);
2050
+ elementNames.set(key, name);
2051
+ }
2052
+ return { ...usage, name };
2053
+ });
2054
+ }
2055
+ function allocateVariableName(prefix, startIndex, reservedNames) {
2056
+ let index = startIndex;
2057
+ while (true) {
2058
+ const name = `${prefix}${core.encode(index)}`;
2059
+ if (!reservedNames.has(name)) {
2060
+ return name;
2061
+ }
2062
+ index++;
2063
+ }
2064
+ }
2065
+ function canonicalUsageKey(usage) {
2066
+ return `${usage.variantChain ?? ""}\0${usage.propertyKey}`;
2067
+ }
2068
+ function getOrCreate(map, key, create) {
2069
+ const existing = map.get(key);
2070
+ if (existing) {
2071
+ return existing;
2072
+ }
2073
+ const value = create();
2074
+ map.set(key, value);
2075
+ return value;
2076
+ }
2077
+
1660
2078
  class OxcNotImplementedError extends Error {
1661
2079
  /**
1662
2080
  * @param slice The Phase D slice expected to implement this path.
@@ -1672,6 +2090,8 @@ function transformOxc(source, filename, options) {
1672
2090
  const rawClassNames = /* @__PURE__ */ new Set();
1673
2091
  const diagnostics = [];
1674
2092
  const recoveryTokens = /* @__PURE__ */ new Map();
2093
+ const cssVariableMap = /* @__PURE__ */ new Map();
2094
+ const globalVarAliases = normalizeGlobalVarAliases$1(options?.globalVarAliases);
1675
2095
  if (!source.includes("sz")) {
1676
2096
  return {
1677
2097
  code: source,
@@ -1682,7 +2102,8 @@ function transformOxc(source, filename, options) {
1682
2102
  classes,
1683
2103
  rawClassNames,
1684
2104
  diagnostics,
1685
- recoveryTokens
2105
+ recoveryTokens,
2106
+ cssVariableMap
1686
2107
  };
1687
2108
  }
1688
2109
  const effectiveFilename = filename ?? "file.tsx";
@@ -1697,6 +2118,18 @@ function transformOxc(source, filename, options) {
1697
2118
  const edits = new MagicString__default(source);
1698
2119
  const objectBindings = collectObjectBindings(parsed.program);
1699
2120
  const conditionalBindings = collectConditionalBindings(parsed.program);
2121
+ const reservedCSSVariableNames = options?.mangleVars ? collectStaticStyleCustomPropertyNames(parsed.program) : void 0;
2122
+ const componentHoists = options?.mangleVars ? planOxcComponentVariableHoists(
2123
+ parsed.program,
2124
+ effectiveFilename,
2125
+ objectBindings,
2126
+ source,
2127
+ options.mangleVarHoistMaxDepth,
2128
+ reservedCSSVariableNames
2129
+ ) : null;
2130
+ if (componentHoists) {
2131
+ diagnostics.push(...componentHoists.diagnostics);
2132
+ }
1700
2133
  let transformed = false;
1701
2134
  let usesRuntime = false;
1702
2135
  let usesMerge = false;
@@ -1722,6 +2155,24 @@ function transformOxc(source, filename, options) {
1722
2155
  let szRecoverAttr = null;
1723
2156
  let alreadyTagged = false;
1724
2157
  let lastAttr = null;
2158
+ let appliedHoistedStyleProps = false;
2159
+ const elementId = elementIdForOpening(openingNode);
2160
+ const hoistedStyleProps = componentHoists?.stylePropsByTarget.get(elementId) ?? [];
2161
+ const applyHoistedStyleProps = () => {
2162
+ if (appliedHoistedStyleProps || hoistedStyleProps.length === 0) {
2163
+ return;
2164
+ }
2165
+ applyStyleProps(
2166
+ edits,
2167
+ source,
2168
+ styleAttr,
2169
+ lastAttr,
2170
+ hoistedStyleProps,
2171
+ openingNode.name.end
2172
+ );
2173
+ appliedHoistedStyleProps = true;
2174
+ transformed = true;
2175
+ };
1725
2176
  for (const attrRaw of attrs) {
1726
2177
  if (attrRaw.type !== "JSXAttribute") {
1727
2178
  continue;
@@ -1782,6 +2233,7 @@ function transformOxc(source, filename, options) {
1782
2233
  }
1783
2234
  }
1784
2235
  if (szAttrs.length === 0) {
2236
+ applyHoistedStyleProps();
1785
2237
  return;
1786
2238
  }
1787
2239
  const szDerived = [];
@@ -1818,7 +2270,9 @@ function transformOxc(source, filename, options) {
1818
2270
  effectiveFilename,
1819
2271
  objectBindings,
1820
2272
  source,
1821
- classes
2273
+ classes,
2274
+ globalVarAliases,
2275
+ cssVariableMap
1822
2276
  );
1823
2277
  if (conditionalClassExpr) {
1824
2278
  if (classNameAttr || szAttrs.length > 1) {
@@ -1840,7 +2294,11 @@ function transformOxc(source, filename, options) {
1840
2294
  const bound = objectBindings.get(identifierName);
1841
2295
  if (bound) {
1842
2296
  const result2 = transformCore.transform(
1843
- astObjectToSzObject(bound, effectiveFilename, objectBindings)
2297
+ applyGlobalVarAliasesToSzObject(
2298
+ astObjectToSzObject(bound, effectiveFilename, objectBindings),
2299
+ globalVarAliases,
2300
+ cssVariableMap
2301
+ )
1844
2302
  );
1845
2303
  for (const c of result2.className.split(/\s+/)) {
1846
2304
  if (c) {
@@ -1857,7 +2315,9 @@ function transformOxc(source, filename, options) {
1857
2315
  effectiveFilename,
1858
2316
  objectBindings,
1859
2317
  source,
1860
- classes
2318
+ classes,
2319
+ globalVarAliases,
2320
+ cssVariableMap
1861
2321
  );
1862
2322
  if (conditionalClassExpr) {
1863
2323
  if (classNameAttr || szAttrs.length > 1) {
@@ -1879,7 +2339,9 @@ function transformOxc(source, filename, options) {
1879
2339
  const arrayClasses = astArrayToStaticClasses(
1880
2340
  expression,
1881
2341
  effectiveFilename,
1882
- objectBindings
2342
+ objectBindings,
2343
+ globalVarAliases,
2344
+ cssVariableMap
1883
2345
  );
1884
2346
  if (arrayClasses === null) {
1885
2347
  collectArrayCandidateClasses(
@@ -1917,7 +2379,9 @@ function transformOxc(source, filename, options) {
1917
2379
  effectiveFilename,
1918
2380
  objectBindings,
1919
2381
  source,
1920
- classes
2382
+ classes,
2383
+ globalVarAliases,
2384
+ cssVariableMap
1921
2385
  );
1922
2386
  if (conditionalSpreadClassExpr) {
1923
2387
  if (classNameAttr || szAttrs.length > 1) {
@@ -1937,9 +2401,15 @@ function transformOxc(source, filename, options) {
1937
2401
  expression,
1938
2402
  effectiveFilename,
1939
2403
  objectBindings,
1940
- source
2404
+ source,
2405
+ options,
2406
+ componentHoists?.usageNamesByElement.get(elementId),
2407
+ cssVariableMap,
2408
+ reservedCSSVariableNames,
2409
+ globalVarAliases
1941
2410
  );
1942
2411
  if (partial && szAttrs.length === 1) {
2412
+ const mergedStyleProps = hoistedStyleProps.length > 0 ? [...hoistedStyleProps, ...partial.styleProps] : partial.styleProps;
1943
2413
  if (classNameAttr?.value?.type === "JSXExpressionContainer") {
1944
2414
  const classExpression = classNameAttr.value.expression;
1945
2415
  const classExpressionSource = source.slice(
@@ -1952,7 +2422,15 @@ function transformOxc(source, filename, options) {
1952
2422
  `className={_szMerge(${classExpressionSource}, ${JSON.stringify(partial.className)})}`
1953
2423
  );
1954
2424
  edits.remove(whitespaceStart(source, szAttr.start), szAttr.end);
1955
- applyStyleProps(edits, source, styleAttr, lastAttr, partial.styleProps);
2425
+ applyStyleProps(
2426
+ edits,
2427
+ source,
2428
+ styleAttr,
2429
+ lastAttr,
2430
+ mergedStyleProps,
2431
+ openingNode.name.end
2432
+ );
2433
+ appliedHoistedStyleProps = true;
1956
2434
  for (const c of partial.className.split(/\s+/)) {
1957
2435
  if (c) {
1958
2436
  classes.add(c);
@@ -1976,7 +2454,15 @@ function transformOxc(source, filename, options) {
1976
2454
  } else {
1977
2455
  edits.overwrite(szAttr.start, szAttr.end, partial.classNameAttr);
1978
2456
  }
1979
- applyStyleProps(edits, source, styleAttr, lastAttr, partial.styleProps);
2457
+ applyStyleProps(
2458
+ edits,
2459
+ source,
2460
+ styleAttr,
2461
+ lastAttr,
2462
+ mergedStyleProps,
2463
+ openingNode.name.end
2464
+ );
2465
+ appliedHoistedStyleProps = true;
1980
2466
  for (const c of partial.className.split(/\s+/)) {
1981
2467
  if (c) {
1982
2468
  classes.add(c);
@@ -1992,7 +2478,9 @@ function transformOxc(source, filename, options) {
1992
2478
  }
1993
2479
  throw err;
1994
2480
  }
1995
- const result = transformCore.transform(szObj);
2481
+ const result = transformCore.transform(
2482
+ applyGlobalVarAliasesToSzObject(szObj, globalVarAliases, cssVariableMap)
2483
+ );
1996
2484
  for (const c of result.className.split(/\s+/)) {
1997
2485
  if (c) {
1998
2486
  szDerived.push(c);
@@ -2025,6 +2513,7 @@ function transformOxc(source, filename, options) {
2025
2513
  transformed = true;
2026
2514
  return;
2027
2515
  }
2516
+ applyHoistedStyleProps();
2028
2517
  const existingRaw = classNameAttr ? stringLiteralValue(classNameAttr.value) : null;
2029
2518
  const mergedClasses = [
2030
2519
  ...existingRaw ? existingRaw.split(/\s+/).filter(Boolean) : [],
@@ -2059,7 +2548,8 @@ function transformOxc(source, filename, options) {
2059
2548
  classes,
2060
2549
  rawClassNames,
2061
2550
  diagnostics,
2062
- recoveryTokens
2551
+ recoveryTokens,
2552
+ cssVariableMap
2063
2553
  };
2064
2554
  }
2065
2555
  function stringLiteralValue(value) {
@@ -2168,7 +2658,7 @@ function astObjectToSzObject(node, filename, bindings) {
2168
2658
  }
2169
2659
  return result;
2170
2660
  }
2171
- function astArrayToStaticClasses(node, filename, bindings) {
2661
+ function astArrayToStaticClasses(node, filename, bindings, globalVarAliases, cssVariableMap) {
2172
2662
  const out = [];
2173
2663
  for (const element of node.elements) {
2174
2664
  if (!element || isFalsyLiteral(element)) {
@@ -2185,7 +2675,13 @@ function astArrayToStaticClasses(node, filename, bindings) {
2185
2675
  }
2186
2676
  let result;
2187
2677
  try {
2188
- result = transformCore.transform(astObjectToSzObject(objectNode, filename, bindings));
2678
+ result = transformCore.transform(
2679
+ applyGlobalVarAliasesToSzObject(
2680
+ astObjectToSzObject(objectNode, filename, bindings),
2681
+ globalVarAliases,
2682
+ cssVariableMap
2683
+ )
2684
+ );
2189
2685
  } catch (err) {
2190
2686
  if (err instanceof OxcNotImplementedError) {
2191
2687
  return null;
@@ -2282,7 +2778,7 @@ function resolveObjectExpression(node, bindings) {
2282
2778
  }
2283
2779
  return null;
2284
2780
  }
2285
- function buildConditionalSpreadClassExpression(node, filename, bindings, source, classes) {
2781
+ function buildConditionalSpreadClassExpression(node, filename, bindings, source, classes, globalVarAliases, cssVariableMap) {
2286
2782
  let conditionalSpread = null;
2287
2783
  const otherProps = [];
2288
2784
  for (const prop of node.properties) {
@@ -2305,14 +2801,18 @@ function buildConditionalSpreadClassExpression(node, filename, bindings, source,
2305
2801
  otherProps,
2306
2802
  node,
2307
2803
  filename,
2308
- bindings
2804
+ bindings,
2805
+ globalVarAliases,
2806
+ cssVariableMap
2309
2807
  );
2310
2808
  const alternate = compileConditionalSpreadBranch(
2311
2809
  conditionalSpread.alternate,
2312
2810
  otherProps,
2313
2811
  node,
2314
2812
  filename,
2315
- bindings
2813
+ bindings,
2814
+ globalVarAliases,
2815
+ cssVariableMap
2316
2816
  );
2317
2817
  if (consequent === null || alternate === null) {
2318
2818
  return null;
@@ -2325,7 +2825,7 @@ function buildConditionalSpreadClassExpression(node, filename, bindings, source,
2325
2825
  const testSource = source.slice(conditionalSpread.test.start, conditionalSpread.test.end);
2326
2826
  return `${testSource} ? ${JSON.stringify(consequent)} : ${JSON.stringify(alternate)}`;
2327
2827
  }
2328
- function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename, bindings) {
2828
+ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename, bindings, globalVarAliases, cssVariableMap) {
2329
2829
  const branchObject = resolveObjectExpression(branch, bindings);
2330
2830
  if (!branchObject) {
2331
2831
  return null;
@@ -2337,7 +2837,13 @@ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename
2337
2837
  filename,
2338
2838
  bindings
2339
2839
  );
2340
- return transformCore.transform({ ...branchValue, ...overrides }).className;
2840
+ return transformCore.transform(
2841
+ applyGlobalVarAliasesToSzObject(
2842
+ { ...branchValue, ...overrides },
2843
+ globalVarAliases,
2844
+ cssVariableMap
2845
+ )
2846
+ ).className;
2341
2847
  } catch (err) {
2342
2848
  if (err instanceof OxcNotImplementedError) {
2343
2849
  return null;
@@ -2345,8 +2851,15 @@ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename
2345
2851
  throw err;
2346
2852
  }
2347
2853
  }
2348
- function buildPartialObjectTransform(node, filename, bindings, source) {
2349
- const partial = evaluatePartialObject(node, filename, bindings, source);
2854
+ function buildPartialObjectTransform(node, filename, bindings, source, options, hoistedNames, cssVariableMap, reservedNames, globalVarAliases = /* @__PURE__ */ new Map()) {
2855
+ const partial = evaluatePartialObject(
2856
+ node,
2857
+ filename,
2858
+ bindings,
2859
+ source,
2860
+ globalVarAliases,
2861
+ cssVariableMap
2862
+ );
2350
2863
  if (!partial || partial.dynamicProps.size === 0 && partial.conditionalClasses.length === 0) {
2351
2864
  return null;
2352
2865
  }
@@ -2355,11 +2868,17 @@ function buildPartialObjectTransform(node, filename, bindings, source) {
2355
2868
  }
2356
2869
  const classParts = [];
2357
2870
  if (Object.keys(partial.staticProps).length > 0) {
2358
- const { className: className2 } = transformCore.transform(partial.staticProps);
2871
+ const { className: className2 } = transformCore.transform(
2872
+ applyGlobalVarAliasesToSzObject(partial.staticProps, globalVarAliases, cssVariableMap)
2873
+ );
2359
2874
  if (className2) {
2360
2875
  classParts.push(className2);
2361
2876
  }
2362
2877
  }
2878
+ if (options?.mangleVars) {
2879
+ applyHoistedVariableNames(partial, hoistedNames, cssVariableMap);
2880
+ applyScopedVariablePlan(partial, hoistedNames, cssVariableMap, reservedNames);
2881
+ }
2363
2882
  for (const [, info] of partial.dynamicProps) {
2364
2883
  classParts.push(buildCSSVarClassName(info));
2365
2884
  }
@@ -2368,12 +2887,351 @@ function buildPartialObjectTransform(node, filename, bindings, source) {
2368
2887
  }
2369
2888
  const className = classParts.filter(Boolean).join(" ");
2370
2889
  const classNameAttr = partial.conditionalClasses.length > 0 ? `className={${buildConditionalClassSource(classParts, partial.conditionalClasses, source)}}` : `className="${className}"`;
2371
- const styleProps = [...partial.dynamicProps.values()].map(
2372
- (info) => `${JSON.stringify(info.varName)}: ${generateStyleValueSource(info, source)}`
2890
+ const styleProps = [...partial.dynamicProps.entries()].filter(([id]) => !hoistedNames?.has(id)).map(
2891
+ ([, info]) => `${JSON.stringify(info.varName)}: ${generateStyleValueSource(info, source)}`
2373
2892
  );
2374
2893
  return { className, classNameAttr, styleProps, usesColorVar: partial.usesColorVar };
2375
2894
  }
2376
- function evaluatePartialObject(node, filename, bindings, source, variantChain = "") {
2895
+ function applyHoistedVariableNames(partial, hoistedNames, cssVariableMap) {
2896
+ if (!hoistedNames) {
2897
+ return;
2898
+ }
2899
+ for (const [id, name] of hoistedNames) {
2900
+ const info = partial.dynamicProps.get(id);
2901
+ if (info) {
2902
+ addCssVariableMapping(cssVariableMap, info.varName, name);
2903
+ info.varName = name;
2904
+ }
2905
+ }
2906
+ }
2907
+ function applyScopedVariablePlan(partial, hoistedNames, cssVariableMap, reservedNames) {
2908
+ const entries = [...partial.dynamicProps.entries()].filter(([id]) => !hoistedNames?.has(id));
2909
+ const plan = planCSSVariableNames(
2910
+ entries.map(([id]) => ({
2911
+ id,
2912
+ tier: "scoped",
2913
+ elementId: "self",
2914
+ propertyKey: id
2915
+ })),
2916
+ { reservedNames }
2917
+ );
2918
+ for (const planned of plan) {
2919
+ const info = partial.dynamicProps.get(planned.id);
2920
+ if (info) {
2921
+ addCssVariableMapping(cssVariableMap, info.varName, planned.name);
2922
+ info.varName = planned.name;
2923
+ }
2924
+ }
2925
+ }
2926
+ function addCssVariableMapping(cssVariableMap, original, mangled) {
2927
+ if (!cssVariableMap) {
2928
+ return;
2929
+ }
2930
+ const existing = cssVariableMap.get(original);
2931
+ if (!existing) {
2932
+ cssVariableMap.set(original, mangled);
2933
+ return;
2934
+ }
2935
+ const values = Array.isArray(existing) ? existing : [existing];
2936
+ if (!values.includes(mangled)) {
2937
+ cssVariableMap.set(original, [...values, mangled]);
2938
+ }
2939
+ }
2940
+ function normalizeGlobalVarAliases$1(input) {
2941
+ if (!input) {
2942
+ return /* @__PURE__ */ new Map();
2943
+ }
2944
+ const entries = input instanceof Map ? input.entries() : Array.isArray(input) ? input : Object.entries(input);
2945
+ const aliases = /* @__PURE__ */ new Map();
2946
+ for (const [original, alias] of entries) {
2947
+ if (original.startsWith("--") && alias.startsWith("--")) {
2948
+ aliases.set(original, alias);
2949
+ }
2950
+ }
2951
+ return aliases;
2952
+ }
2953
+ function applyGlobalVarAliasesToSzObject(object, globalVarAliases, cssVariableMap) {
2954
+ if (globalVarAliases.size === 0) {
2955
+ return object;
2956
+ }
2957
+ const rewritten = {};
2958
+ for (const [key, value] of Object.entries(object)) {
2959
+ rewritten[key] = applyGlobalVarAliasesToSzValue(value, globalVarAliases, cssVariableMap);
2960
+ }
2961
+ return rewritten;
2962
+ }
2963
+ function applyGlobalVarAliasesToSzValue(value, globalVarAliases, cssVariableMap) {
2964
+ if (typeof value === "string") {
2965
+ const alias = globalVarAliases.get(value);
2966
+ if (alias) {
2967
+ addCssVariableMapping(cssVariableMap, value, alias);
2968
+ return alias;
2969
+ }
2970
+ return value;
2971
+ }
2972
+ if (typeof value === "object") {
2973
+ return applyGlobalVarAliasesToSzObject(value, globalVarAliases, cssVariableMap);
2974
+ }
2975
+ return value;
2976
+ }
2977
+ function planOxcComponentVariableHoists(root, filename, bindings, source, maxDepth, reservedNames) {
2978
+ const nodes = [];
2979
+ const candidates = [];
2980
+ collectOxcHoistCandidates(root, null, nodes, candidates, filename, bindings, source);
2981
+ if (candidates.length < 2) {
2982
+ return {
2983
+ stylePropsByTarget: /* @__PURE__ */ new Map(),
2984
+ usageNamesByElement: /* @__PURE__ */ new Map(),
2985
+ diagnostics: []
2986
+ };
2987
+ }
2988
+ const plannedNames = planCSSVariableNames(
2989
+ candidates.map((candidate) => ({
2990
+ id: candidate.id,
2991
+ tier: "component",
2992
+ elementId: candidate.elementId,
2993
+ propertyKey: candidate.propertyKey,
2994
+ variantChain: candidate.variantChain || void 0
2995
+ })),
2996
+ { reservedNames }
2997
+ );
2998
+ const nameByUsage = new Map(plannedNames.map((entry) => [entry.id, entry.name]));
2999
+ const candidateById = new Map(candidates.map((candidate) => [candidate.id, candidate]));
3000
+ const hoistUsages = candidates.map((candidate) => ({
3001
+ id: candidate.id,
3002
+ elementId: candidate.elementId,
3003
+ name: nameByUsage.get(candidate.id) ?? candidate.info.varName,
3004
+ valueKey: candidate.valueKey
3005
+ }));
3006
+ const analysis = planComponentVariableHoistsWithDiagnostics(nodes, hoistUsages, {
3007
+ maxDepth
3008
+ });
3009
+ const plans = analysis.plans;
3010
+ const stylePropsByTarget = /* @__PURE__ */ new Map();
3011
+ const usageNamesByElement = /* @__PURE__ */ new Map();
3012
+ for (const plan of plans) {
3013
+ const [firstUsageId] = plan.usageIds;
3014
+ const firstCandidate = firstUsageId ? candidateById.get(firstUsageId) : void 0;
3015
+ if (!firstCandidate) {
3016
+ continue;
3017
+ }
3018
+ appendMapArray(
3019
+ stylePropsByTarget,
3020
+ plan.targetElementId,
3021
+ `${JSON.stringify(plan.name)}: ${firstCandidate.valueSource}`
3022
+ );
3023
+ for (const usageId of plan.usageIds) {
3024
+ const candidate = candidateById.get(usageId);
3025
+ if (!candidate) {
3026
+ continue;
3027
+ }
3028
+ getOrCreateMap(usageNamesByElement, candidate.elementId).set(
3029
+ candidate.dynamicKey,
3030
+ plan.name
3031
+ );
3032
+ }
3033
+ }
3034
+ return {
3035
+ stylePropsByTarget,
3036
+ usageNamesByElement,
3037
+ diagnostics: analysis.diagnostics.map(formatHoistSkipDiagnostic)
3038
+ };
3039
+ }
3040
+ function formatHoistSkipDiagnostic(diagnostic) {
3041
+ const suffix = diagnostic.reason === "max-depth" && diagnostic.maxDepth !== void 0 ? ` (maxDepth ${diagnostic.maxDepth})` : "";
3042
+ return `[csszyx] mangleVars skipped component CSS variable hoist for ${diagnostic.name} across ${diagnostic.usageCount} usages: ${diagnostic.reason}${suffix}`;
3043
+ }
3044
+ function collectOxcHoistCandidates(node, parentElementId, nodes, candidates, filename, bindings, source) {
3045
+ if (node.type === "JSXElement") {
3046
+ const element = node;
3047
+ const opening = element.openingElement;
3048
+ const elementId = elementIdForOpening(opening);
3049
+ nodes.push({
3050
+ id: elementId,
3051
+ parentId: parentElementId,
3052
+ canHost: canHostHoistedStyleProps(opening)
3053
+ });
3054
+ collectOpeningHoistCandidates(opening, elementId, candidates, filename, bindings, source);
3055
+ for (const child of element.children) {
3056
+ collectOxcHoistCandidates(
3057
+ child,
3058
+ elementId,
3059
+ nodes,
3060
+ candidates,
3061
+ filename,
3062
+ bindings,
3063
+ source
3064
+ );
3065
+ }
3066
+ return;
3067
+ }
3068
+ if (node.type === "JSXFragment") {
3069
+ const fragment = node;
3070
+ const elementId = `f${node.start}`;
3071
+ nodes.push({ id: elementId, parentId: parentElementId, canHost: false });
3072
+ for (const child of fragment.children) {
3073
+ collectOxcHoistCandidates(
3074
+ child,
3075
+ elementId,
3076
+ nodes,
3077
+ candidates,
3078
+ filename,
3079
+ bindings,
3080
+ source
3081
+ );
3082
+ }
3083
+ return;
3084
+ }
3085
+ for (const key of Object.keys(node)) {
3086
+ if (isAstMetadataKey(key)) {
3087
+ continue;
3088
+ }
3089
+ const child = node[key];
3090
+ if (Array.isArray(child)) {
3091
+ for (const item of child) {
3092
+ if (isOxcNode(item)) {
3093
+ collectOxcHoistCandidates(
3094
+ item,
3095
+ parentElementId,
3096
+ nodes,
3097
+ candidates,
3098
+ filename,
3099
+ bindings,
3100
+ source
3101
+ );
3102
+ }
3103
+ }
3104
+ } else if (isOxcNode(child)) {
3105
+ collectOxcHoistCandidates(
3106
+ child,
3107
+ parentElementId,
3108
+ nodes,
3109
+ candidates,
3110
+ filename,
3111
+ bindings,
3112
+ source
3113
+ );
3114
+ }
3115
+ }
3116
+ }
3117
+ function collectOpeningHoistCandidates(opening, elementId, candidates, filename, bindings, source) {
3118
+ for (const attrRaw of opening.attributes ?? []) {
3119
+ if (attrRaw.type !== "JSXAttribute") {
3120
+ continue;
3121
+ }
3122
+ const attr = attrRaw;
3123
+ if (attr.name?.name !== "sz" || attr.value?.type !== "JSXExpressionContainer") {
3124
+ continue;
3125
+ }
3126
+ const expression = attr.value.expression;
3127
+ if (expression.type !== "ObjectExpression") {
3128
+ continue;
3129
+ }
3130
+ const partial = evaluatePartialObject(
3131
+ expression,
3132
+ filename,
3133
+ bindings,
3134
+ source,
3135
+ /* @__PURE__ */ new Map(),
3136
+ void 0
3137
+ );
3138
+ if (!partial || partial.conditionalClasses.length > 0) {
3139
+ continue;
3140
+ }
3141
+ for (const [dynamicKey, info] of partial.dynamicProps) {
3142
+ candidates.push({
3143
+ id: `${elementId}:${dynamicKey}`,
3144
+ elementId,
3145
+ dynamicKey,
3146
+ propertyKey: dynamicKey,
3147
+ variantChain: info.variantChain,
3148
+ valueSource: generateStyleValueSource(info, source),
3149
+ valueKey: buildDynamicValueKey(info, source),
3150
+ info
3151
+ });
3152
+ }
3153
+ }
3154
+ }
3155
+ function elementIdForOpening(opening) {
3156
+ return `e${opening.start}`;
3157
+ }
3158
+ function canHostHoistedStyleProps(opening) {
3159
+ if (!isDomJsxOpening(opening)) {
3160
+ return false;
3161
+ }
3162
+ const styleAttr = findJsxAttribute(opening, "style");
3163
+ return !styleAttr || styleAttr.value?.type === "JSXExpressionContainer";
3164
+ }
3165
+ function isDomJsxOpening(opening) {
3166
+ if (opening.name.type !== "JSXIdentifier") {
3167
+ return false;
3168
+ }
3169
+ const name = String(opening.name.name);
3170
+ return name.length > 0 && name.charAt(0) === name.charAt(0).toLowerCase();
3171
+ }
3172
+ function findJsxAttribute(opening, name) {
3173
+ for (const attrRaw of opening.attributes ?? []) {
3174
+ if (attrRaw.type === "JSXAttribute") {
3175
+ const attr = attrRaw;
3176
+ if (attr.name?.name === name) {
3177
+ return attr;
3178
+ }
3179
+ }
3180
+ }
3181
+ return null;
3182
+ }
3183
+ function collectStaticStyleCustomPropertyNames(node) {
3184
+ const names = /* @__PURE__ */ new Set();
3185
+ walk(node, (child) => {
3186
+ if (child.type !== "JSXOpeningElement") {
3187
+ return;
3188
+ }
3189
+ const styleAttr = findJsxAttribute(child, "style");
3190
+ if (styleAttr?.value?.type !== "JSXExpressionContainer") {
3191
+ return;
3192
+ }
3193
+ const expression = styleAttr.value.expression;
3194
+ if (expression.type !== "ObjectExpression") {
3195
+ return;
3196
+ }
3197
+ for (const propRaw of expression.properties ?? []) {
3198
+ if (propRaw.type !== "Property") {
3199
+ continue;
3200
+ }
3201
+ const key = propRaw.key;
3202
+ const name = literalStringValue(key);
3203
+ if (name?.startsWith("--")) {
3204
+ names.add(name);
3205
+ }
3206
+ }
3207
+ });
3208
+ return names;
3209
+ }
3210
+ function literalStringValue(node) {
3211
+ if (node.type !== "Literal") {
3212
+ return null;
3213
+ }
3214
+ const value = node.value;
3215
+ return typeof value === "string" ? value : null;
3216
+ }
3217
+ function appendMapArray(map, key, value) {
3218
+ const existing = map.get(key);
3219
+ if (existing) {
3220
+ existing.push(value);
3221
+ } else {
3222
+ map.set(key, [value]);
3223
+ }
3224
+ }
3225
+ function getOrCreateMap(map, key) {
3226
+ const existing = map.get(key);
3227
+ if (existing) {
3228
+ return existing;
3229
+ }
3230
+ const value = /* @__PURE__ */ new Map();
3231
+ map.set(key, value);
3232
+ return value;
3233
+ }
3234
+ function evaluatePartialObject(node, filename, bindings, source, globalVarAliases, cssVariableMap, variantChain = "") {
2377
3235
  const staticProps = {};
2378
3236
  const dynamicProps = /* @__PURE__ */ new Map();
2379
3237
  const conditionalClasses = [];
@@ -2430,6 +3288,8 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2430
3288
  filename,
2431
3289
  bindings,
2432
3290
  source,
3291
+ globalVarAliases,
3292
+ cssVariableMap,
2433
3293
  nestedVariant
2434
3294
  );
2435
3295
  if (!nested) {
@@ -2450,8 +3310,20 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2450
3310
  const consequent = extractStaticLiteralValue(conditional.consequent);
2451
3311
  const alternate = extractStaticLiteralValue(conditional.alternate);
2452
3312
  if (consequent !== null && alternate !== null) {
2453
- const { className: consequentClasses } = transformCore.transform({ [key]: consequent });
2454
- const { className: alternateClasses } = transformCore.transform({ [key]: alternate });
3313
+ const { className: consequentClasses } = transformCore.transform(
3314
+ applyGlobalVarAliasesToSzObject(
3315
+ { [key]: consequent },
3316
+ globalVarAliases,
3317
+ cssVariableMap
3318
+ )
3319
+ );
3320
+ const { className: alternateClasses } = transformCore.transform(
3321
+ applyGlobalVarAliasesToSzObject(
3322
+ { [key]: alternate },
3323
+ globalVarAliases,
3324
+ cssVariableMap
3325
+ )
3326
+ );
2455
3327
  conditionalClasses.push({
2456
3328
  test: conditional.test,
2457
3329
  consequent: prefixVariantClasses(consequentClasses, variantChain),
@@ -2480,14 +3352,15 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2480
3352
  }
2481
3353
  return { staticProps, dynamicProps, conditionalClasses, usesColorVar };
2482
3354
  }
2483
- function applyStyleProps(edits, source, styleAttr, lastAttr, styleProps) {
3355
+ function applyStyleProps(edits, source, styleAttr, lastAttr, styleProps, fallbackInsertOffset) {
2484
3356
  if (styleProps.length === 0) {
2485
3357
  return;
2486
3358
  }
2487
3359
  const propsSource = styleProps.join(", ");
2488
3360
  if (!styleAttr) {
2489
- if (lastAttr) {
2490
- edits.appendRight(lastAttr.end, ` style={{${propsSource}}}`);
3361
+ const insertOffset = lastAttr?.end ?? fallbackInsertOffset;
3362
+ if (insertOffset !== void 0) {
3363
+ edits.appendRight(insertOffset, ` style={{${propsSource}}}`);
2491
3364
  }
2492
3365
  return;
2493
3366
  }
@@ -2513,6 +3386,67 @@ function generateStyleValueSource(info, source) {
2513
3386
  return `\`\${${expressionSource}}\``;
2514
3387
  }
2515
3388
  }
3389
+ function buildDynamicValueKey(info, source) {
3390
+ const expressionSource = normalizeDynamicExpressionKey(
3391
+ source.slice(info.expression.start, info.expression.end)
3392
+ );
3393
+ switch (info.category) {
3394
+ case transformCore.PropertyCategory.SPACING:
3395
+ return `spacing:${expressionSource}`;
3396
+ case transformCore.PropertyCategory.COLOR:
3397
+ return `color:${expressionSource}`;
3398
+ case transformCore.PropertyCategory.ANGLE:
3399
+ return `angle:${expressionSource}`;
3400
+ case transformCore.PropertyCategory.DURATION:
3401
+ return `duration:${expressionSource}`;
3402
+ default:
3403
+ return `pass:${expressionSource}`;
3404
+ }
3405
+ }
3406
+ function normalizeDynamicExpressionKey(expressionSource) {
3407
+ let normalized = expressionSource.trim();
3408
+ while (hasRedundantOuterParens(normalized)) {
3409
+ normalized = normalized.slice(1, -1).trim();
3410
+ }
3411
+ return normalized;
3412
+ }
3413
+ function hasRedundantOuterParens(expressionSource) {
3414
+ if (!expressionSource.startsWith("(") || !expressionSource.endsWith(")")) {
3415
+ return false;
3416
+ }
3417
+ let depth = 0;
3418
+ let quote = null;
3419
+ let escaped = false;
3420
+ for (let index = 0; index < expressionSource.length; index++) {
3421
+ const char = expressionSource[index];
3422
+ if (quote) {
3423
+ if (escaped) {
3424
+ escaped = false;
3425
+ } else if (char === "\\") {
3426
+ escaped = true;
3427
+ } else if (char === quote) {
3428
+ quote = null;
3429
+ }
3430
+ continue;
3431
+ }
3432
+ if (char === '"' || char === "'" || char === "`") {
3433
+ quote = char;
3434
+ continue;
3435
+ }
3436
+ if (char === "(") {
3437
+ depth++;
3438
+ } else if (char === ")") {
3439
+ depth--;
3440
+ if (depth === 0 && index !== expressionSource.length - 1) {
3441
+ return false;
3442
+ }
3443
+ }
3444
+ if (depth < 0) {
3445
+ return false;
3446
+ }
3447
+ }
3448
+ return depth === 0;
3449
+ }
2516
3450
  function buildCSSVarClassName(info) {
2517
3451
  const variantPrefix = info.variantChain ? `${transformCore.getVariantPrefix(info.variantChain)}:` : "";
2518
3452
  return `${variantPrefix}${info.twPrefix}-(${info.varName})`;
@@ -2545,9 +3479,21 @@ function prefixVariantClasses(className, variantChain) {
2545
3479
  function isRuntimeExpression(node) {
2546
3480
  return node.type === "Identifier" || node.type === "MemberExpression" || node.type === "CallExpression" || node.type === "ConditionalExpression" || node.type === "TemplateLiteral" || node.type === "BinaryExpression" || node.type === "LogicalExpression";
2547
3481
  }
2548
- function buildStaticConditionalClassExpression(node, filename, bindings, source, classes) {
2549
- const consequent = resolveStaticClassString(node.consequent, filename, bindings);
2550
- const alternate = resolveStaticClassString(node.alternate, filename, bindings);
3482
+ function buildStaticConditionalClassExpression(node, filename, bindings, source, classes, globalVarAliases, cssVariableMap) {
3483
+ const consequent = resolveStaticClassString(
3484
+ node.consequent,
3485
+ filename,
3486
+ bindings,
3487
+ globalVarAliases,
3488
+ cssVariableMap
3489
+ );
3490
+ const alternate = resolveStaticClassString(
3491
+ node.alternate,
3492
+ filename,
3493
+ bindings,
3494
+ globalVarAliases,
3495
+ cssVariableMap
3496
+ );
2551
3497
  if (consequent === null || alternate === null) {
2552
3498
  return null;
2553
3499
  }
@@ -2559,7 +3505,7 @@ function buildStaticConditionalClassExpression(node, filename, bindings, source,
2559
3505
  const testSource = source.slice(node.test.start, node.test.end);
2560
3506
  return `${testSource} ? ${JSON.stringify(consequent)} : ${JSON.stringify(alternate)}`;
2561
3507
  }
2562
- function resolveStaticClassString(node, filename, bindings) {
3508
+ function resolveStaticClassString(node, filename, bindings, globalVarAliases, cssVariableMap) {
2563
3509
  const unwrapped = unwrapExpression(node);
2564
3510
  let objectNode = null;
2565
3511
  if (unwrapped.type === "ObjectExpression") {
@@ -2571,7 +3517,13 @@ function resolveStaticClassString(node, filename, bindings) {
2571
3517
  return null;
2572
3518
  }
2573
3519
  try {
2574
- return transformCore.transform(astObjectToSzObject(objectNode, filename, bindings)).className;
3520
+ return transformCore.transform(
3521
+ applyGlobalVarAliasesToSzObject(
3522
+ astObjectToSzObject(objectNode, filename, bindings),
3523
+ globalVarAliases,
3524
+ cssVariableMap
3525
+ )
3526
+ ).className;
2575
3527
  } catch (err) {
2576
3528
  if (err instanceof OxcNotImplementedError) {
2577
3529
  return null;
@@ -2686,17 +3638,22 @@ function unwrapExpression(node) {
2686
3638
  }
2687
3639
  return current;
2688
3640
  }
3641
+ function isOxcNode(value) {
3642
+ return Boolean(
3643
+ value && typeof value === "object" && typeof value.type === "string"
3644
+ );
3645
+ }
3646
+ function isAstMetadataKey(key) {
3647
+ return key === "loc" || key === "range" || key === "start" || key === "end" || key === "type";
3648
+ }
2689
3649
  function walk(node, visit) {
2690
- if (!node || typeof node !== "object") {
3650
+ if (!isOxcNode(node)) {
2691
3651
  return;
2692
3652
  }
2693
3653
  const typed = node;
2694
- if (typeof typed.type !== "string") {
2695
- return;
2696
- }
2697
3654
  visit(typed);
2698
3655
  for (const key of Object.keys(typed)) {
2699
- if (key === "loc" || key === "range" || key === "start" || key === "end" || key === "type") {
3656
+ if (isAstMetadataKey(key)) {
2700
3657
  continue;
2701
3658
  }
2702
3659
  const child = typed[key];
@@ -2720,7 +3677,7 @@ class OxcRustNotImplementedError extends Error {
2720
3677
  }
2721
3678
  }
2722
3679
  function transformRust(source, filename, options) {
2723
- const [result] = transformRustBatch([{ filename, source }]);
3680
+ const [result] = transformRustBatch([{ filename, source }], options);
2724
3681
  if (!result) {
2725
3682
  throw new OxcRustNotImplementedError("native transform returned no result");
2726
3683
  }
@@ -2747,7 +3704,12 @@ function transformRustBatch(files, options) {
2747
3704
  files.map((file, index) => ({
2748
3705
  filename: file.filename ?? `file-${index}.tsx`,
2749
3706
  source: file.source
2750
- }))
3707
+ })),
3708
+ {
3709
+ mangleVars: options?.mangleVars === true,
3710
+ mangleVarHoistMaxDepth: options?.mangleVarHoistMaxDepth,
3711
+ globalVarAliases: normalizeGlobalVarAliases(options?.globalVarAliases)
3712
+ }
2751
3713
  ).map(fromNativeResult);
2752
3714
  } catch (err) {
2753
3715
  if (err instanceof OxcRustNotImplementedError) {
@@ -2761,6 +3723,13 @@ function transformRustBatch(files, options) {
2761
3723
  throw err;
2762
3724
  }
2763
3725
  }
3726
+ function normalizeGlobalVarAliases(input) {
3727
+ if (!input) {
3728
+ return [];
3729
+ }
3730
+ const entries = input instanceof Map ? input.entries() : Array.isArray(input) ? input : Object.entries(input);
3731
+ return [...entries].filter(([original, alias]) => original.startsWith("--") && alias.startsWith("--")).map(([original, alias]) => ({ original, alias }));
3732
+ }
2764
3733
  function fromNativeResult(result) {
2765
3734
  return {
2766
3735
  code: result.code,
@@ -2780,9 +3749,25 @@ function fromNativeResult(result) {
2780
3749
  path: data.path
2781
3750
  }
2782
3751
  ])
2783
- )
3752
+ ),
3753
+ cssVariableMap: aggregateCssVariableMap(result.cssVariableMap ?? [])
2784
3754
  };
2785
3755
  }
3756
+ function aggregateCssVariableMap(entries) {
3757
+ const map = /* @__PURE__ */ new Map();
3758
+ for (const entry of entries) {
3759
+ const existing = map.get(entry.original);
3760
+ if (!existing) {
3761
+ map.set(entry.original, entry.mangled);
3762
+ continue;
3763
+ }
3764
+ const values = Array.isArray(existing) ? existing : [existing];
3765
+ if (!values.includes(entry.mangled)) {
3766
+ map.set(entry.original, [...values, entry.mangled]);
3767
+ }
3768
+ }
3769
+ return map;
3770
+ }
2786
3771
 
2787
3772
  const VERSION = "0.0.0";
2788
3773
  const DEFAULT_COMPILER_OPTIONS = {
@@ -2803,6 +3788,7 @@ exports.KNOWN_VARIANTS = transformCore.KNOWN_VARIANTS;
2803
3788
  exports.PROPERTY_CATEGORY_MAP = transformCore.PROPERTY_CATEGORY_MAP;
2804
3789
  exports.PROPERTY_MAP = transformCore.PROPERTY_MAP;
2805
3790
  exports.PropertyCategory = transformCore.PropertyCategory;
3791
+ exports.SPECIAL_VARIANTS = transformCore.SPECIAL_VARIANTS;
2806
3792
  exports.SUGGESTION_MAP = transformCore.SUGGESTION_MAP;
2807
3793
  exports.getCSSVariableName = transformCore.getCSSVariableName;
2808
3794
  exports.getPropertyCategory = transformCore.getPropertyCategory;
@@ -2824,6 +3810,7 @@ exports.injectRecoveryToken = injectRecoveryToken;
2824
3810
  exports.isValidRecoveryMode = isValidRecoveryMode;
2825
3811
  exports.mergeOptions = mergeOptions;
2826
3812
  exports.parseManifest = parseManifest;
3813
+ exports.scanGlobalVarUsages = scanGlobalVarUsages;
2827
3814
  exports.serializeManifest = serializeManifest;
2828
3815
  exports.transformOxc = transformOxc;
2829
3816
  exports.transformRust = transformRust;