@csszyx/compiler 0.8.0 → 0.9.1

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,13 @@
1
1
  'use strict';
2
2
 
3
3
  const core = require('@csszyx/core');
4
- const transformCore = require('./shared/compiler.C6jT0mcT.cjs');
4
+ const transformCore = require('./shared/compiler.DItEsgH4.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
+ const native = require('@csszyx/core/native');
10
11
 
11
12
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
12
13
 
@@ -83,6 +84,7 @@ function transformSourceCode(source, filename, options) {
83
84
  const rawClassNames = /* @__PURE__ */ new Set();
84
85
  const diagnostics = [];
85
86
  const recoveryTokens = /* @__PURE__ */ new Map();
87
+ const cssVariableMap = /* @__PURE__ */ new Map();
86
88
  if (!source.includes("sz")) {
87
89
  return {
88
90
  code: source,
@@ -93,7 +95,8 @@ function transformSourceCode(source, filename, options) {
93
95
  classes: collectedClasses,
94
96
  rawClassNames,
95
97
  diagnostics,
96
- recoveryTokens
98
+ recoveryTokens,
99
+ cssVariableMap
97
100
  };
98
101
  }
99
102
  try {
@@ -755,7 +758,8 @@ function transformSourceCode(source, filename, options) {
755
758
  classes: collectedClasses,
756
759
  rawClassNames,
757
760
  diagnostics,
758
- recoveryTokens
761
+ recoveryTokens,
762
+ cssVariableMap
759
763
  };
760
764
  } catch (e) {
761
765
  if (e instanceof ASTBudgetExceededError) {
@@ -771,7 +775,8 @@ function transformSourceCode(source, filename, options) {
771
775
  classes: collectedClasses,
772
776
  rawClassNames,
773
777
  diagnostics,
774
- recoveryTokens
778
+ recoveryTokens,
779
+ cssVariableMap
775
780
  };
776
781
  }
777
782
  }
@@ -1323,6 +1328,271 @@ class CsszyxCompiler {
1323
1328
  }
1324
1329
  }
1325
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
+
1326
1596
  function findLCA(nodeA, nodeB, parentMap) {
1327
1597
  const ancestorsA = /* @__PURE__ */ new Set();
1328
1598
  let current = nodeA;
@@ -1656,6 +1926,155 @@ function injectRecoveryToken(attributes, token) {
1656
1926
  };
1657
1927
  }
1658
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
+
1659
2078
  class OxcNotImplementedError extends Error {
1660
2079
  /**
1661
2080
  * @param slice The Phase D slice expected to implement this path.
@@ -1671,6 +2090,8 @@ function transformOxc(source, filename, options) {
1671
2090
  const rawClassNames = /* @__PURE__ */ new Set();
1672
2091
  const diagnostics = [];
1673
2092
  const recoveryTokens = /* @__PURE__ */ new Map();
2093
+ const cssVariableMap = /* @__PURE__ */ new Map();
2094
+ const globalVarAliases = normalizeGlobalVarAliases$1(options?.globalVarAliases);
1674
2095
  if (!source.includes("sz")) {
1675
2096
  return {
1676
2097
  code: source,
@@ -1681,7 +2102,8 @@ function transformOxc(source, filename, options) {
1681
2102
  classes,
1682
2103
  rawClassNames,
1683
2104
  diagnostics,
1684
- recoveryTokens
2105
+ recoveryTokens,
2106
+ cssVariableMap
1685
2107
  };
1686
2108
  }
1687
2109
  const effectiveFilename = filename ?? "file.tsx";
@@ -1696,6 +2118,18 @@ function transformOxc(source, filename, options) {
1696
2118
  const edits = new MagicString__default(source);
1697
2119
  const objectBindings = collectObjectBindings(parsed.program);
1698
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
+ }
1699
2133
  let transformed = false;
1700
2134
  let usesRuntime = false;
1701
2135
  let usesMerge = false;
@@ -1721,6 +2155,24 @@ function transformOxc(source, filename, options) {
1721
2155
  let szRecoverAttr = null;
1722
2156
  let alreadyTagged = false;
1723
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
+ };
1724
2176
  for (const attrRaw of attrs) {
1725
2177
  if (attrRaw.type !== "JSXAttribute") {
1726
2178
  continue;
@@ -1781,6 +2233,7 @@ function transformOxc(source, filename, options) {
1781
2233
  }
1782
2234
  }
1783
2235
  if (szAttrs.length === 0) {
2236
+ applyHoistedStyleProps();
1784
2237
  return;
1785
2238
  }
1786
2239
  const szDerived = [];
@@ -1817,7 +2270,9 @@ function transformOxc(source, filename, options) {
1817
2270
  effectiveFilename,
1818
2271
  objectBindings,
1819
2272
  source,
1820
- classes
2273
+ classes,
2274
+ globalVarAliases,
2275
+ cssVariableMap
1821
2276
  );
1822
2277
  if (conditionalClassExpr) {
1823
2278
  if (classNameAttr || szAttrs.length > 1) {
@@ -1839,7 +2294,11 @@ function transformOxc(source, filename, options) {
1839
2294
  const bound = objectBindings.get(identifierName);
1840
2295
  if (bound) {
1841
2296
  const result2 = transformCore.transform(
1842
- astObjectToSzObject(bound, effectiveFilename, objectBindings)
2297
+ applyGlobalVarAliasesToSzObject(
2298
+ astObjectToSzObject(bound, effectiveFilename, objectBindings),
2299
+ globalVarAliases,
2300
+ cssVariableMap
2301
+ )
1843
2302
  );
1844
2303
  for (const c of result2.className.split(/\s+/)) {
1845
2304
  if (c) {
@@ -1856,7 +2315,9 @@ function transformOxc(source, filename, options) {
1856
2315
  effectiveFilename,
1857
2316
  objectBindings,
1858
2317
  source,
1859
- classes
2318
+ classes,
2319
+ globalVarAliases,
2320
+ cssVariableMap
1860
2321
  );
1861
2322
  if (conditionalClassExpr) {
1862
2323
  if (classNameAttr || szAttrs.length > 1) {
@@ -1878,7 +2339,9 @@ function transformOxc(source, filename, options) {
1878
2339
  const arrayClasses = astArrayToStaticClasses(
1879
2340
  expression,
1880
2341
  effectiveFilename,
1881
- objectBindings
2342
+ objectBindings,
2343
+ globalVarAliases,
2344
+ cssVariableMap
1882
2345
  );
1883
2346
  if (arrayClasses === null) {
1884
2347
  collectArrayCandidateClasses(
@@ -1916,7 +2379,9 @@ function transformOxc(source, filename, options) {
1916
2379
  effectiveFilename,
1917
2380
  objectBindings,
1918
2381
  source,
1919
- classes
2382
+ classes,
2383
+ globalVarAliases,
2384
+ cssVariableMap
1920
2385
  );
1921
2386
  if (conditionalSpreadClassExpr) {
1922
2387
  if (classNameAttr || szAttrs.length > 1) {
@@ -1936,9 +2401,15 @@ function transformOxc(source, filename, options) {
1936
2401
  expression,
1937
2402
  effectiveFilename,
1938
2403
  objectBindings,
1939
- source
2404
+ source,
2405
+ options,
2406
+ componentHoists?.usageNamesByElement.get(elementId),
2407
+ cssVariableMap,
2408
+ reservedCSSVariableNames,
2409
+ globalVarAliases
1940
2410
  );
1941
2411
  if (partial && szAttrs.length === 1) {
2412
+ const mergedStyleProps = hoistedStyleProps.length > 0 ? [...hoistedStyleProps, ...partial.styleProps] : partial.styleProps;
1942
2413
  if (classNameAttr?.value?.type === "JSXExpressionContainer") {
1943
2414
  const classExpression = classNameAttr.value.expression;
1944
2415
  const classExpressionSource = source.slice(
@@ -1951,7 +2422,15 @@ function transformOxc(source, filename, options) {
1951
2422
  `className={_szMerge(${classExpressionSource}, ${JSON.stringify(partial.className)})}`
1952
2423
  );
1953
2424
  edits.remove(whitespaceStart(source, szAttr.start), szAttr.end);
1954
- 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;
1955
2434
  for (const c of partial.className.split(/\s+/)) {
1956
2435
  if (c) {
1957
2436
  classes.add(c);
@@ -1975,7 +2454,15 @@ function transformOxc(source, filename, options) {
1975
2454
  } else {
1976
2455
  edits.overwrite(szAttr.start, szAttr.end, partial.classNameAttr);
1977
2456
  }
1978
- 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;
1979
2466
  for (const c of partial.className.split(/\s+/)) {
1980
2467
  if (c) {
1981
2468
  classes.add(c);
@@ -1991,7 +2478,9 @@ function transformOxc(source, filename, options) {
1991
2478
  }
1992
2479
  throw err;
1993
2480
  }
1994
- const result = transformCore.transform(szObj);
2481
+ const result = transformCore.transform(
2482
+ applyGlobalVarAliasesToSzObject(szObj, globalVarAliases, cssVariableMap)
2483
+ );
1995
2484
  for (const c of result.className.split(/\s+/)) {
1996
2485
  if (c) {
1997
2486
  szDerived.push(c);
@@ -2024,6 +2513,7 @@ function transformOxc(source, filename, options) {
2024
2513
  transformed = true;
2025
2514
  return;
2026
2515
  }
2516
+ applyHoistedStyleProps();
2027
2517
  const existingRaw = classNameAttr ? stringLiteralValue(classNameAttr.value) : null;
2028
2518
  const mergedClasses = [
2029
2519
  ...existingRaw ? existingRaw.split(/\s+/).filter(Boolean) : [],
@@ -2058,7 +2548,8 @@ function transformOxc(source, filename, options) {
2058
2548
  classes,
2059
2549
  rawClassNames,
2060
2550
  diagnostics,
2061
- recoveryTokens
2551
+ recoveryTokens,
2552
+ cssVariableMap
2062
2553
  };
2063
2554
  }
2064
2555
  function stringLiteralValue(value) {
@@ -2167,7 +2658,7 @@ function astObjectToSzObject(node, filename, bindings) {
2167
2658
  }
2168
2659
  return result;
2169
2660
  }
2170
- function astArrayToStaticClasses(node, filename, bindings) {
2661
+ function astArrayToStaticClasses(node, filename, bindings, globalVarAliases, cssVariableMap) {
2171
2662
  const out = [];
2172
2663
  for (const element of node.elements) {
2173
2664
  if (!element || isFalsyLiteral(element)) {
@@ -2184,7 +2675,13 @@ function astArrayToStaticClasses(node, filename, bindings) {
2184
2675
  }
2185
2676
  let result;
2186
2677
  try {
2187
- result = transformCore.transform(astObjectToSzObject(objectNode, filename, bindings));
2678
+ result = transformCore.transform(
2679
+ applyGlobalVarAliasesToSzObject(
2680
+ astObjectToSzObject(objectNode, filename, bindings),
2681
+ globalVarAliases,
2682
+ cssVariableMap
2683
+ )
2684
+ );
2188
2685
  } catch (err) {
2189
2686
  if (err instanceof OxcNotImplementedError) {
2190
2687
  return null;
@@ -2281,7 +2778,7 @@ function resolveObjectExpression(node, bindings) {
2281
2778
  }
2282
2779
  return null;
2283
2780
  }
2284
- function buildConditionalSpreadClassExpression(node, filename, bindings, source, classes) {
2781
+ function buildConditionalSpreadClassExpression(node, filename, bindings, source, classes, globalVarAliases, cssVariableMap) {
2285
2782
  let conditionalSpread = null;
2286
2783
  const otherProps = [];
2287
2784
  for (const prop of node.properties) {
@@ -2304,14 +2801,18 @@ function buildConditionalSpreadClassExpression(node, filename, bindings, source,
2304
2801
  otherProps,
2305
2802
  node,
2306
2803
  filename,
2307
- bindings
2804
+ bindings,
2805
+ globalVarAliases,
2806
+ cssVariableMap
2308
2807
  );
2309
2808
  const alternate = compileConditionalSpreadBranch(
2310
2809
  conditionalSpread.alternate,
2311
2810
  otherProps,
2312
2811
  node,
2313
2812
  filename,
2314
- bindings
2813
+ bindings,
2814
+ globalVarAliases,
2815
+ cssVariableMap
2315
2816
  );
2316
2817
  if (consequent === null || alternate === null) {
2317
2818
  return null;
@@ -2324,7 +2825,7 @@ function buildConditionalSpreadClassExpression(node, filename, bindings, source,
2324
2825
  const testSource = source.slice(conditionalSpread.test.start, conditionalSpread.test.end);
2325
2826
  return `${testSource} ? ${JSON.stringify(consequent)} : ${JSON.stringify(alternate)}`;
2326
2827
  }
2327
- function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename, bindings) {
2828
+ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename, bindings, globalVarAliases, cssVariableMap) {
2328
2829
  const branchObject = resolveObjectExpression(branch, bindings);
2329
2830
  if (!branchObject) {
2330
2831
  return null;
@@ -2336,7 +2837,13 @@ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename
2336
2837
  filename,
2337
2838
  bindings
2338
2839
  );
2339
- return transformCore.transform({ ...branchValue, ...overrides }).className;
2840
+ return transformCore.transform(
2841
+ applyGlobalVarAliasesToSzObject(
2842
+ { ...branchValue, ...overrides },
2843
+ globalVarAliases,
2844
+ cssVariableMap
2845
+ )
2846
+ ).className;
2340
2847
  } catch (err) {
2341
2848
  if (err instanceof OxcNotImplementedError) {
2342
2849
  return null;
@@ -2344,8 +2851,15 @@ function compileConditionalSpreadBranch(branch, otherProps, sourceNode, filename
2344
2851
  throw err;
2345
2852
  }
2346
2853
  }
2347
- function buildPartialObjectTransform(node, filename, bindings, source) {
2348
- 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
+ );
2349
2863
  if (!partial || partial.dynamicProps.size === 0 && partial.conditionalClasses.length === 0) {
2350
2864
  return null;
2351
2865
  }
@@ -2354,11 +2868,17 @@ function buildPartialObjectTransform(node, filename, bindings, source) {
2354
2868
  }
2355
2869
  const classParts = [];
2356
2870
  if (Object.keys(partial.staticProps).length > 0) {
2357
- const { className: className2 } = transformCore.transform(partial.staticProps);
2871
+ const { className: className2 } = transformCore.transform(
2872
+ applyGlobalVarAliasesToSzObject(partial.staticProps, globalVarAliases, cssVariableMap)
2873
+ );
2358
2874
  if (className2) {
2359
2875
  classParts.push(className2);
2360
2876
  }
2361
2877
  }
2878
+ if (options?.mangleVars) {
2879
+ applyHoistedVariableNames(partial, hoistedNames, cssVariableMap);
2880
+ applyScopedVariablePlan(partial, hoistedNames, cssVariableMap, reservedNames);
2881
+ }
2362
2882
  for (const [, info] of partial.dynamicProps) {
2363
2883
  classParts.push(buildCSSVarClassName(info));
2364
2884
  }
@@ -2367,12 +2887,351 @@ function buildPartialObjectTransform(node, filename, bindings, source) {
2367
2887
  }
2368
2888
  const className = classParts.filter(Boolean).join(" ");
2369
2889
  const classNameAttr = partial.conditionalClasses.length > 0 ? `className={${buildConditionalClassSource(classParts, partial.conditionalClasses, source)}}` : `className="${className}"`;
2370
- const styleProps = [...partial.dynamicProps.values()].map(
2371
- (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)}`
2372
2892
  );
2373
2893
  return { className, classNameAttr, styleProps, usesColorVar: partial.usesColorVar };
2374
2894
  }
2375
- 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 = "") {
2376
3235
  const staticProps = {};
2377
3236
  const dynamicProps = /* @__PURE__ */ new Map();
2378
3237
  const conditionalClasses = [];
@@ -2429,6 +3288,8 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2429
3288
  filename,
2430
3289
  bindings,
2431
3290
  source,
3291
+ globalVarAliases,
3292
+ cssVariableMap,
2432
3293
  nestedVariant
2433
3294
  );
2434
3295
  if (!nested) {
@@ -2449,8 +3310,20 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2449
3310
  const consequent = extractStaticLiteralValue(conditional.consequent);
2450
3311
  const alternate = extractStaticLiteralValue(conditional.alternate);
2451
3312
  if (consequent !== null && alternate !== null) {
2452
- const { className: consequentClasses } = transformCore.transform({ [key]: consequent });
2453
- 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
+ );
2454
3327
  conditionalClasses.push({
2455
3328
  test: conditional.test,
2456
3329
  consequent: prefixVariantClasses(consequentClasses, variantChain),
@@ -2479,14 +3352,15 @@ function evaluatePartialObject(node, filename, bindings, source, variantChain =
2479
3352
  }
2480
3353
  return { staticProps, dynamicProps, conditionalClasses, usesColorVar };
2481
3354
  }
2482
- function applyStyleProps(edits, source, styleAttr, lastAttr, styleProps) {
3355
+ function applyStyleProps(edits, source, styleAttr, lastAttr, styleProps, fallbackInsertOffset) {
2483
3356
  if (styleProps.length === 0) {
2484
3357
  return;
2485
3358
  }
2486
3359
  const propsSource = styleProps.join(", ");
2487
3360
  if (!styleAttr) {
2488
- if (lastAttr) {
2489
- edits.appendRight(lastAttr.end, ` style={{${propsSource}}}`);
3361
+ const insertOffset = lastAttr?.end ?? fallbackInsertOffset;
3362
+ if (insertOffset !== void 0) {
3363
+ edits.appendRight(insertOffset, ` style={{${propsSource}}}`);
2490
3364
  }
2491
3365
  return;
2492
3366
  }
@@ -2512,6 +3386,67 @@ function generateStyleValueSource(info, source) {
2512
3386
  return `\`\${${expressionSource}}\``;
2513
3387
  }
2514
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
+ }
2515
3450
  function buildCSSVarClassName(info) {
2516
3451
  const variantPrefix = info.variantChain ? `${transformCore.getVariantPrefix(info.variantChain)}:` : "";
2517
3452
  return `${variantPrefix}${info.twPrefix}-(${info.varName})`;
@@ -2544,9 +3479,21 @@ function prefixVariantClasses(className, variantChain) {
2544
3479
  function isRuntimeExpression(node) {
2545
3480
  return node.type === "Identifier" || node.type === "MemberExpression" || node.type === "CallExpression" || node.type === "ConditionalExpression" || node.type === "TemplateLiteral" || node.type === "BinaryExpression" || node.type === "LogicalExpression";
2546
3481
  }
2547
- function buildStaticConditionalClassExpression(node, filename, bindings, source, classes) {
2548
- const consequent = resolveStaticClassString(node.consequent, filename, bindings);
2549
- 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
+ );
2550
3497
  if (consequent === null || alternate === null) {
2551
3498
  return null;
2552
3499
  }
@@ -2558,7 +3505,7 @@ function buildStaticConditionalClassExpression(node, filename, bindings, source,
2558
3505
  const testSource = source.slice(node.test.start, node.test.end);
2559
3506
  return `${testSource} ? ${JSON.stringify(consequent)} : ${JSON.stringify(alternate)}`;
2560
3507
  }
2561
- function resolveStaticClassString(node, filename, bindings) {
3508
+ function resolveStaticClassString(node, filename, bindings, globalVarAliases, cssVariableMap) {
2562
3509
  const unwrapped = unwrapExpression(node);
2563
3510
  let objectNode = null;
2564
3511
  if (unwrapped.type === "ObjectExpression") {
@@ -2570,7 +3517,13 @@ function resolveStaticClassString(node, filename, bindings) {
2570
3517
  return null;
2571
3518
  }
2572
3519
  try {
2573
- 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;
2574
3527
  } catch (err) {
2575
3528
  if (err instanceof OxcNotImplementedError) {
2576
3529
  return null;
@@ -2685,17 +3638,22 @@ function unwrapExpression(node) {
2685
3638
  }
2686
3639
  return current;
2687
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
+ }
2688
3649
  function walk(node, visit) {
2689
- if (!node || typeof node !== "object") {
3650
+ if (!isOxcNode(node)) {
2690
3651
  return;
2691
3652
  }
2692
3653
  const typed = node;
2693
- if (typeof typed.type !== "string") {
2694
- return;
2695
- }
2696
3654
  visit(typed);
2697
3655
  for (const key of Object.keys(typed)) {
2698
- if (key === "loc" || key === "range" || key === "start" || key === "end" || key === "type") {
3656
+ if (isAstMetadataKey(key)) {
2699
3657
  continue;
2700
3658
  }
2701
3659
  const child = typed[key];
@@ -2709,6 +3667,108 @@ function walk(node, visit) {
2709
3667
  }
2710
3668
  }
2711
3669
 
3670
+ class OxcRustNotImplementedError extends Error {
3671
+ /**
3672
+ * @param detail Native loader or transform failure detail.
3673
+ */
3674
+ constructor(detail) {
3675
+ super(`transformRust: native engine unavailable - ${detail}`);
3676
+ this.name = "OxcRustNotImplementedError";
3677
+ }
3678
+ }
3679
+ function transformRust(source, filename, options) {
3680
+ const [result] = transformRustBatch([{ filename, source }], options);
3681
+ if (!result) {
3682
+ throw new OxcRustNotImplementedError("native transform returned no result");
3683
+ }
3684
+ return result;
3685
+ }
3686
+ function ensureRustTransformAvailable() {
3687
+ try {
3688
+ native.transformBatch([]);
3689
+ } catch (err) {
3690
+ if (err instanceof OxcRustNotImplementedError) {
3691
+ throw err;
3692
+ }
3693
+ if (err instanceof native.CsszyxNativeUnavailableError) {
3694
+ throw new OxcRustNotImplementedError(
3695
+ `${err.message}; native package: ${err.packageName ?? "unsupported platform"}`
3696
+ );
3697
+ }
3698
+ throw err;
3699
+ }
3700
+ }
3701
+ function transformRustBatch(files, options) {
3702
+ try {
3703
+ return native.transformBatch(
3704
+ files.map((file, index) => ({
3705
+ filename: file.filename ?? `file-${index}.tsx`,
3706
+ source: file.source
3707
+ })),
3708
+ {
3709
+ mangleVars: options?.mangleVars === true,
3710
+ mangleVarHoistMaxDepth: options?.mangleVarHoistMaxDepth,
3711
+ globalVarAliases: normalizeGlobalVarAliases(options?.globalVarAliases)
3712
+ }
3713
+ ).map(fromNativeResult);
3714
+ } catch (err) {
3715
+ if (err instanceof OxcRustNotImplementedError) {
3716
+ throw err;
3717
+ }
3718
+ if (err instanceof native.CsszyxNativeUnavailableError) {
3719
+ throw new OxcRustNotImplementedError(
3720
+ `${err.message}; native package: ${err.packageName ?? "unsupported platform"}`
3721
+ );
3722
+ }
3723
+ throw err;
3724
+ }
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
+ }
3733
+ function fromNativeResult(result) {
3734
+ return {
3735
+ code: result.code,
3736
+ transformed: result.metadata.transformed,
3737
+ usesRuntime: result.metadata.usesRuntime,
3738
+ usesMerge: result.metadata.usesMerge,
3739
+ usesColorVar: result.metadata.usesColorVar,
3740
+ classes: new Set(result.classes),
3741
+ rawClassNames: new Set(result.rawClassNames),
3742
+ diagnostics: result.diagnostics,
3743
+ recoveryTokens: new Map(
3744
+ result.recoveryTokens.map(({ token, ...data }) => [
3745
+ token,
3746
+ {
3747
+ mode: data.mode,
3748
+ component: data.component,
3749
+ path: data.path
3750
+ }
3751
+ ])
3752
+ ),
3753
+ cssVariableMap: aggregateCssVariableMap(result.cssVariableMap ?? [])
3754
+ };
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
+ }
3771
+
2712
3772
  const VERSION = "0.0.0";
2713
3773
  const DEFAULT_COMPILER_OPTIONS = {
2714
3774
  buildId: Date.now().toString(),
@@ -2738,17 +3798,22 @@ exports.CsszyxCompiler = CsszyxCompiler;
2738
3798
  exports.DEFAULT_COMPILER_OPTIONS = DEFAULT_COMPILER_OPTIONS;
2739
3799
  exports.ManifestBuilder = ManifestBuilder;
2740
3800
  exports.OxcNotImplementedError = OxcNotImplementedError;
3801
+ exports.OxcRustNotImplementedError = OxcRustNotImplementedError;
2741
3802
  exports.VERSION = VERSION;
2742
3803
  exports.buildParentMap = buildParentMap;
2743
3804
  exports.createRecoveryToken = createRecoveryToken;
3805
+ exports.ensureRustTransformAvailable = ensureRustTransformAvailable;
2744
3806
  exports.generateRecoveryToken = generateRecoveryToken;
2745
3807
  exports.hoistCSSVariables = hoistCSSVariables;
2746
3808
  exports.injectRecoveryToken = injectRecoveryToken;
2747
3809
  exports.isValidRecoveryMode = isValidRecoveryMode;
2748
3810
  exports.mergeOptions = mergeOptions;
2749
3811
  exports.parseManifest = parseManifest;
3812
+ exports.scanGlobalVarUsages = scanGlobalVarUsages;
2750
3813
  exports.serializeManifest = serializeManifest;
2751
3814
  exports.transformOxc = transformOxc;
3815
+ exports.transformRust = transformRust;
3816
+ exports.transformRustBatch = transformRustBatch;
2752
3817
  exports.transformSourceCode = transformSourceCode;
2753
3818
  exports.validateManifest = validateManifest;
2754
3819
  exports.validateSzRecover = validateSzRecover;