@stylexjs/babel-plugin 0.17.5 → 0.18.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.
@@ -1484,6 +1484,12 @@ const checkRuntimeInjection = unionOf3(boolean(), string(), object({
1484
1484
  from: string(),
1485
1485
  as: string()
1486
1486
  }));
1487
+ const checkEnvOption = (value, name = 'options.env') => {
1488
+ if (typeof value !== 'object' || value == null || Array.isArray(value)) {
1489
+ return new Error(`Expected (${name}) to be an object, but got \`${String(JSON.stringify(value))}\`.`);
1490
+ }
1491
+ return value;
1492
+ };
1487
1493
  const DEFAULT_INJECT_PATH = '@stylexjs/stylex/lib/stylex-inject';
1488
1494
  class StateManager {
1489
1495
  importPaths = new Set();
@@ -1503,6 +1509,7 @@ class StateManager {
1503
1509
  stylexViewTransitionClassImport = new Set();
1504
1510
  stylexDefaultMarkerImport = new Set();
1505
1511
  stylexWhenImport = new Set();
1512
+ stylexEnvImport = new Set();
1506
1513
  injectImportInserted = null;
1507
1514
  styleMap = new Map();
1508
1515
  styleVars = new Map();
@@ -1526,6 +1533,7 @@ class StateManager {
1526
1533
  const enableLegacyValueFlipping = logAndDefault(boolean(), options.enableLegacyValueFlipping ?? defaultOptions.enableLegacyValueFlipping, false, 'options.enableLegacyValueFlipping');
1527
1534
  const enableLogicalStylesPolyfill = logAndDefault(boolean(), options.enableLogicalStylesPolyfill ?? defaultOptions.enableLogicalStylesPolyfill, false, 'options.enableLogicalStylesPolyfill');
1528
1535
  const enableLTRRTLComments = logAndDefault(boolean(), options.enableLTRRTLComments ?? defaultOptions.enableLTRRTLComments, false, 'options.enableLTRRTLComments');
1536
+ const sxPropName = logAndDefault(unionOf(string(), literal(false)), options.sxPropName ?? 'sx', 'sx', 'options.sxPropName');
1529
1537
  const test = logAndDefault(boolean(), options.test ?? defaultOptions.test, false, 'options.test');
1530
1538
  const configRuntimeInjection = logAndDefault(checkRuntimeInjection, options.runtimeInjection ?? false, false, 'options.runtimeInjection');
1531
1539
  const runtimeInjection = configRuntimeInjection === true ? DEFAULT_INJECT_PATH : configRuntimeInjection === false ? undefined : configRuntimeInjection;
@@ -1536,6 +1544,10 @@ class StateManager {
1536
1544
  const propertyValidationMode = logAndDefault(unionOf3(literal('throw'), literal('warn'), literal('silent')), options.propertyValidationMode ?? defaultOptions.propertyValidationMode, 'silent', 'options.propertyValidationMode');
1537
1545
  const unstable_moduleResolution = logAndDefault(unionOf(nullish(), CheckModuleResolution), options.unstable_moduleResolution, null, 'options.unstable_moduleResolution');
1538
1546
  const treeshakeCompensation = logAndDefault(boolean(), options.treeshakeCompensation ?? defaultOptions.treeshakeCompensation, false, 'options.treeshakeCompensation');
1547
+ const envInput = logAndDefault(checkEnvOption, options.env ?? {}, {}, 'options.env');
1548
+ const env = Object.freeze({
1549
+ ...envInput
1550
+ });
1539
1551
  const aliasesOption = logAndDefault(unionOf(nullish(), objectOf(unionOf(string(), array(string())))), options.aliases, null, 'options.aliases');
1540
1552
  const aliases = aliasesOption == null ? aliasesOption : Object.fromEntries(Object.entries(aliasesOption).map(([key, value]) => {
1541
1553
  if (typeof value === 'string') {
@@ -1551,6 +1563,7 @@ class StateManager {
1551
1563
  definedStylexCSSVariables: {},
1552
1564
  dev,
1553
1565
  propertyValidationMode,
1566
+ env,
1554
1567
  enableDebugClassNames,
1555
1568
  enableDebugDataProp,
1556
1569
  enableDevClassNames,
@@ -1561,6 +1574,7 @@ class StateManager {
1561
1574
  enableLegacyValueFlipping,
1562
1575
  enableLogicalStylesPolyfill,
1563
1576
  enableLTRRTLComments,
1577
+ sxPropName,
1564
1578
  importSources,
1565
1579
  rewriteAliases: typeof options.rewriteAliases === 'boolean' ? options.rewriteAliases : false,
1566
1580
  runtimeInjection,
@@ -1592,6 +1606,31 @@ class StateManager {
1592
1606
  }
1593
1607
  return null;
1594
1608
  }
1609
+ applyStylexEnv(identifiers) {
1610
+ const env = this.options.env;
1611
+ this.stylexImport.forEach(importName => {
1612
+ const current = identifiers[importName];
1613
+ if (current != null && typeof current === 'object' && !Array.isArray(current)) {
1614
+ if ('fn' in current) {
1615
+ identifiers[importName] = {
1616
+ env
1617
+ };
1618
+ } else {
1619
+ identifiers[importName] = {
1620
+ ...current,
1621
+ env
1622
+ };
1623
+ }
1624
+ return;
1625
+ }
1626
+ identifiers[importName] = {
1627
+ env
1628
+ };
1629
+ });
1630
+ this.stylexEnvImport.forEach(importName => {
1631
+ identifiers[importName] = env;
1632
+ });
1633
+ }
1595
1634
  get canReferenceTheme() {
1596
1635
  return !!this.inStyleXCreate;
1597
1636
  }
@@ -1910,6 +1949,9 @@ function readImportDeclarations(path, state) {
1910
1949
  if (importedName === 'defaultMarker') {
1911
1950
  state.stylexDefaultMarkerImport.add(localName);
1912
1951
  }
1952
+ if (importedName === 'env') {
1953
+ state.stylexEnvImport.add(localName);
1954
+ }
1913
1955
  }
1914
1956
  }
1915
1957
  }
@@ -1976,6 +2018,9 @@ function readRequires(path, state) {
1976
2018
  if (prop.key.name === 'defaultMarker') {
1977
2019
  state.stylexDefaultMarkerImport.add(value.name);
1978
2020
  }
2021
+ if (prop.key.name === 'env') {
2022
+ state.stylexEnvImport.add(value.name);
2023
+ }
1979
2024
  }
1980
2025
  }
1981
2026
  }
@@ -4574,7 +4619,6 @@ const ILLEGAL_NESTED_PSEUDO = "Pseudo objects can't be nested more than one leve
4574
4619
  const ILLEGAL_PROP_VALUE = 'A style value can only contain an array, string or number.';
4575
4620
  const ILLEGAL_PROP_ARRAY_VALUE = 'A style array value can only contain strings or numbers.';
4576
4621
  const ILLEGAL_NAMESPACE_VALUE = 'A StyleX namespace must be an object.';
4577
- const INVALID_CONST_KEY = 'Keys in defineConsts() cannot start with "--".';
4578
4622
  const INVALID_PSEUDO = 'Invalid pseudo selector, not on the whitelist.';
4579
4623
  const INVALID_PSEUDO_OR_AT_RULE = 'Invalid pseudo or at-rule.';
4580
4624
  const INVALID_MEDIA_QUERY_SYNTAX = 'Invalid media query syntax.';
@@ -4597,7 +4641,6 @@ var m = /*#__PURE__*/Object.freeze({
4597
4641
  ILLEGAL_NESTED_PSEUDO: ILLEGAL_NESTED_PSEUDO,
4598
4642
  ILLEGAL_PROP_ARRAY_VALUE: ILLEGAL_PROP_ARRAY_VALUE,
4599
4643
  ILLEGAL_PROP_VALUE: ILLEGAL_PROP_VALUE,
4600
- INVALID_CONST_KEY: INVALID_CONST_KEY,
4601
4644
  INVALID_MEDIA_QUERY_SYNTAX: INVALID_MEDIA_QUERY_SYNTAX,
4602
4645
  INVALID_PSEUDO: INVALID_PSEUDO,
4603
4646
  INVALID_PSEUDO_OR_AT_RULE: INVALID_PSEUDO_OR_AT_RULE,
@@ -5598,6 +5641,16 @@ function requirePropertyPriorities () {
5598
5641
  SIBLING_AFTER: /^:where\(:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/,
5599
5642
  ANY_SIBLING: /^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+~\s+\*,\s+:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/
5600
5643
  };
5644
+ const PSEUDO_PART_REGEX = /::[a-zA-Z-]+|:[a-zA-Z-]+(?:\([^)]*\))?/g;
5645
+ function getCompoundPseudoPriority(key) {
5646
+ const parts = key.match(PSEUDO_PART_REGEX);
5647
+ if (!parts || parts.length <= 1 || parts.some(p => p.includes('('))) return;
5648
+ let total = 0;
5649
+ for (const part of parts) {
5650
+ total += part.startsWith('::') ? PSEUDO_ELEMENT_PRIORITY : PSEUDO_CLASS_PRIORITIES[part] ?? 40;
5651
+ }
5652
+ return total;
5653
+ }
5601
5654
  function getAtRulePriority(key) {
5602
5655
  if (key.startsWith('--')) {
5603
5656
  return 1;
@@ -5638,7 +5691,7 @@ function requirePropertyPriorities () {
5638
5691
  return 40 + pseudoBase(siblingAfterMatch[1]);
5639
5692
  }
5640
5693
  if (key.startsWith(':')) {
5641
- const prop = key.startsWith(':') && key.includes('(') ? key.slice(0, key.indexOf('(')) : key;
5694
+ const prop = key.split('(')[0];
5642
5695
  return PSEUDO_CLASS_PRIORITIES[prop] ?? 40;
5643
5696
  }
5644
5697
  }
@@ -5659,6 +5712,8 @@ function requirePropertyPriorities () {
5659
5712
  function getPriority(key) {
5660
5713
  const atRulePriority = getAtRulePriority(key);
5661
5714
  if (atRulePriority) return atRulePriority;
5715
+ const compoundPriority = getCompoundPseudoPriority(key);
5716
+ if (compoundPriority != null) return compoundPriority;
5662
5717
  const pseudoElementPriority = getPseudoElementPriority(key);
5663
5718
  if (pseudoElementPriority) return pseudoElementPriority;
5664
5719
  const pseudoClassPriority = getPseudoClassPriority(key);
@@ -5869,7 +5924,7 @@ class PreRule {
5869
5924
  this.value = value;
5870
5925
  }
5871
5926
  get pseudos() {
5872
- const unsortedPseudos = this.keyPath.filter(key => key.startsWith(':'));
5927
+ const unsortedPseudos = this.keyPath.filter(key => key.startsWith(':') || key.startsWith('['));
5873
5928
  return sortPseudos(unsortedPseudos);
5874
5929
  }
5875
5930
  get atRules() {
@@ -5974,7 +6029,7 @@ function _flattenRawStyleObject(style, keyPath, options) {
5974
6029
  });
5975
6030
  continue;
5976
6031
  }
5977
- if (typeof value === 'object' && !key.startsWith(':') && !key.startsWith('@')) {
6032
+ if (typeof value === 'object' && !key.startsWith(':') && !key.startsWith('@') && !key.startsWith('[')) {
5978
6033
  const equivalentPairs = {};
5979
6034
  for (const condition in value) {
5980
6035
  const innerValue = value[condition];
@@ -6000,7 +6055,7 @@ function _flattenRawStyleObject(style, keyPath, options) {
6000
6055
  flattened.push([property, PreRuleSet.create(rules)]);
6001
6056
  }
6002
6057
  }
6003
- if (typeof value === 'object' && (key.startsWith(':') || key.startsWith('@'))) {
6058
+ if (typeof value === 'object' && (key.startsWith(':') || key.startsWith('@') || key.startsWith('['))) {
6004
6059
  const pairs = _flattenRawStyleObject(value, [...keyPath, _key], options);
6005
6060
  for (const [property, preRule] of pairs) {
6006
6061
  flattened.push([key + '_' + property, preRule]);
@@ -6030,7 +6085,7 @@ function validateNamespace(namespace, conditions = []) {
6030
6085
  continue;
6031
6086
  }
6032
6087
  if (isPlainObject(val)) {
6033
- if (key.startsWith('@') || key.startsWith(':')) {
6088
+ if (key.startsWith('@') || key.startsWith(':') || key.startsWith('[')) {
6034
6089
  if (conditions.includes(key)) {
6035
6090
  throw new Error(DUPLICATE_CONDITIONAL);
6036
6091
  }
@@ -6046,7 +6101,7 @@ function validateNamespace(namespace, conditions = []) {
6046
6101
  function validateConditionalStyles(val, conditions = []) {
6047
6102
  for (const key in val) {
6048
6103
  const v = val[key];
6049
- if (!(key.startsWith('@') || key.startsWith(':') || key.startsWith('var(--') || key === 'default')) {
6104
+ if (!(key.startsWith('@') || key.startsWith(':') || key.startsWith('[') || key.startsWith('var(--') || key === 'default')) {
6050
6105
  throw new Error(INVALID_PSEUDO_OR_AT_RULE);
6051
6106
  }
6052
6107
  if (conditions.includes(key)) {
@@ -6432,11 +6487,8 @@ function styleXDefineConsts(constants, options) {
6432
6487
  const jsOutput = {};
6433
6488
  const injectableStyles = {};
6434
6489
  for (const [key, value] of Object.entries(constants)) {
6435
- if (key.startsWith('--')) {
6436
- throw new Error(INVALID_CONST_KEY);
6437
- }
6438
6490
  const varSafeKey = (key[0] >= '0' && key[0] <= '9' ? `_${key}` : key).replace(/[^a-zA-Z0-9]/g, '_');
6439
- const constKey = debug && enableDebugClassNames ? `${varSafeKey}-${classNamePrefix}${hash(`${exportId}.${key}`)}` : `${classNamePrefix}${hash(`${exportId}.${key}`)}`;
6491
+ const constKey = key.startsWith('--') ? key.slice(2) : debug && enableDebugClassNames ? `${varSafeKey}-${classNamePrefix}${hash(`${exportId}.${key}`)}` : `${classNamePrefix}${hash(`${exportId}.${key}`)}`;
6440
6492
  jsOutput[key] = value;
6441
6493
  injectableStyles[constKey] = {
6442
6494
  constKey,
@@ -6608,12 +6660,15 @@ function getDefaultMarkerClassName(options = defaultOptions) {
6608
6660
  return `${prefix}default-marker`;
6609
6661
  }
6610
6662
  function validatePseudoSelector(pseudo) {
6611
- if (!pseudo.startsWith(':')) {
6612
- throw new Error('Pseudo selector must start with ":"');
6663
+ if (!(pseudo.startsWith(':') || pseudo.startsWith('['))) {
6664
+ throw new Error('Pseudo selector must start with ":" or "["');
6613
6665
  }
6614
6666
  if (pseudo.startsWith('::')) {
6615
6667
  throw new Error('Pseudo selector cannot start with "::" (pseudo-elements are not supported)');
6616
6668
  }
6669
+ if (pseudo.startsWith('[') && !pseudo.endsWith(']')) {
6670
+ throw new Error('Attribute selector must end with "]"');
6671
+ }
6617
6672
  }
6618
6673
  function ancestor(pseudo, options = defaultOptions) {
6619
6674
  validatePseudoSelector(pseudo);
@@ -7651,6 +7706,18 @@ function isSafeToSkipNullCheck(expr) {
7651
7706
  }
7652
7707
  return false;
7653
7708
  }
7709
+ function hasExplicitNullishFallback(expr) {
7710
+ if (isNullLiteral(expr)) return true;
7711
+ if (isIdentifier(expr) && expr.name === 'undefined') return true;
7712
+ if (isUnaryExpression(expr) && expr.operator === 'void') return true;
7713
+ if (isConditionalExpression(expr)) {
7714
+ return hasExplicitNullishFallback(expr.consequent) || hasExplicitNullishFallback(expr.alternate);
7715
+ }
7716
+ if (isLogicalExpression(expr)) {
7717
+ return hasExplicitNullishFallback(expr.left) || hasExplicitNullishFallback(expr.right);
7718
+ }
7719
+ return false;
7720
+ }
7654
7721
  function transformStyleXCreate(path, state) {
7655
7722
  const {
7656
7723
  node
@@ -7719,6 +7786,7 @@ function transformStyleXCreate(path, state) {
7719
7786
  when: stylexWhen
7720
7787
  };
7721
7788
  });
7789
+ state.applyStylexEnv(identifiers);
7722
7790
  const {
7723
7791
  confident,
7724
7792
  value,
@@ -7746,7 +7814,7 @@ function transformStyleXCreate(path, state) {
7746
7814
  const isPseudoElement = path.some(p => p.startsWith('::'));
7747
7815
  injectedInheritStyles[variableName] = {
7748
7816
  priority: 0,
7749
- ltr: `@property ${variableName} { syntax: "*";${isPseudoElement ? '' : ' inherits: false;'}}`,
7817
+ ltr: `@property ${variableName} { syntax: "*"; inherits: ${isPseudoElement ? 'true' : 'false'};}`,
7750
7818
  rtl: null
7751
7819
  };
7752
7820
  });
@@ -7795,14 +7863,21 @@ function transformStyleXCreate(path, state) {
7795
7863
  for (const [className, classPaths] of Object.entries(classPathsPerNamespace[key])) {
7796
7864
  origClassPaths[className] = classPaths.join('_');
7797
7865
  }
7798
- let dynamicStyles = Object.entries(inlineStyles).map(([_key, v]) => ({
7866
+ let dynamicStyles = Object.entries(inlineStyles).map(([varName, v]) => ({
7799
7867
  expression: v.originalExpression,
7800
7868
  key: v.path.slice(0, v.path.findIndex(p => !p.startsWith(':') && !p.startsWith('@')) + 1).join('_'),
7869
+ varName,
7801
7870
  path: v.path.join('_')
7802
7871
  }));
7803
7872
  if (state.options.styleResolution === 'legacy-expand-shorthands') {
7804
7873
  dynamicStyles = legacyExpandShorthands(dynamicStyles);
7805
7874
  }
7875
+ const nullishVarExpressions = new Map();
7876
+ dynamicStyles.forEach(style => {
7877
+ if (hasExplicitNullishFallback(style.expression)) {
7878
+ nullishVarExpressions.set(style.varName, style.expression);
7879
+ }
7880
+ });
7806
7881
  if (isObjectExpression(prop.value)) {
7807
7882
  const value = prop.value;
7808
7883
  let cssTagValue = booleanLiteral(true);
@@ -7826,9 +7901,23 @@ function transformStyleXCreate(path, state) {
7826
7901
  let isStatic = true;
7827
7902
  const exprList = [];
7828
7903
  classList.forEach((cls, index) => {
7829
- const expr = dynamicStyles.find(({
7904
+ let expr = dynamicStyles.find(({
7830
7905
  path
7831
7906
  }) => origClassPaths[cls] === path)?.expression;
7907
+ if (expr == null && nullishVarExpressions.size > 0) {
7908
+ const injectedStyle = injectedStyles[cls];
7909
+ const rule = injectedStyle != null ? typeof injectedStyle.ltr === 'string' ? injectedStyle.ltr : typeof injectedStyle.rtl === 'string' ? injectedStyle.rtl : null : null;
7910
+ if (rule != null) {
7911
+ const matches = rule.matchAll(/var\((--x-[^,)]+)[^)]*\)/g);
7912
+ for (const match of matches) {
7913
+ const varExpr = nullishVarExpressions.get(match[1]);
7914
+ if (varExpr != null) {
7915
+ expr = varExpr;
7916
+ break;
7917
+ }
7918
+ }
7919
+ }
7920
+ }
7832
7921
  const isLast = index === classList.length - 1;
7833
7922
  const clsWithSpace = isLast ? cls : cls + ' ';
7834
7923
  if (expr && !isSafeToSkipNullCheck(expr)) {
@@ -7989,6 +8078,7 @@ function transformStyleXCreateTheme(callExpressionPath, state) {
7989
8078
  types: stylexTypes
7990
8079
  };
7991
8080
  });
8081
+ state.applyStylexEnv(identifiers);
7992
8082
  const {
7993
8083
  confident: confident2,
7994
8084
  value: overrides
@@ -8103,6 +8193,7 @@ function transformStyleXDefineVars(callExpressionPath, state) {
8103
8193
  types: stylexTypes
8104
8194
  };
8105
8195
  });
8196
+ state.applyStylexEnv(identifiers);
8106
8197
  const {
8107
8198
  confident,
8108
8199
  value
@@ -8165,10 +8256,16 @@ function transformStyleXDefineConsts(callExpressionPath, state) {
8165
8256
  const varId = variableDeclaratorNode.id;
8166
8257
  const args = callExpressionPath.get('arguments');
8167
8258
  const firstArg = args[0];
8259
+ const evaluatePathFnConfig = {
8260
+ identifiers: {},
8261
+ memberExpressions: {},
8262
+ disableImports: true
8263
+ };
8264
+ state.applyStylexEnv(evaluatePathFnConfig.identifiers);
8168
8265
  const {
8169
8266
  confident,
8170
8267
  value
8171
- } = evaluate(firstArg, state);
8268
+ } = evaluate(firstArg, state, evaluatePathFnConfig);
8172
8269
  if (!confident) {
8173
8270
  throw callExpressionPath.buildCodeFrameError(messages.nonStaticValue('defineConsts'), SyntaxError);
8174
8271
  }
@@ -8250,6 +8347,7 @@ function transformStyleXKeyframes(path, state) {
8250
8347
  fn: firstThatWorks
8251
8348
  };
8252
8349
  });
8350
+ state.applyStylexEnv(identifiers);
8253
8351
  const {
8254
8352
  confident,
8255
8353
  value
@@ -8321,6 +8419,7 @@ function transformStyleXPositionTry(path, state) {
8321
8419
  fn: firstThatWorks
8322
8420
  };
8323
8421
  });
8422
+ state.applyStylexEnv(identifiers);
8324
8423
  const {
8325
8424
  confident,
8326
8425
  value
@@ -8375,6 +8474,12 @@ function transformStyleXMerge(path, state) {
8375
8474
  if (node == null || node.callee.type !== 'Identifier' || !state.stylexImport.has(node.callee.name)) {
8376
8475
  return;
8377
8476
  }
8477
+ const evaluatePathFnConfig = {
8478
+ identifiers: {},
8479
+ memberExpressions: {},
8480
+ disableImports: true
8481
+ };
8482
+ state.applyStylexEnv(evaluatePathFnConfig.identifiers);
8378
8483
  let bailOut = false;
8379
8484
  let conditional = 0;
8380
8485
  let currentIndex = -1;
@@ -8481,7 +8586,7 @@ function transformStyleXMerge(path, state) {
8481
8586
  const {
8482
8587
  confident,
8483
8588
  value: styleValue
8484
- } = evaluate(path, state);
8589
+ } = evaluate(path, state, evaluatePathFnConfig);
8485
8590
  if (!confident || styleValue == null) {
8486
8591
  nonNullProps = true;
8487
8592
  styleNonNullProps = true;
@@ -8617,6 +8722,7 @@ function transformStylexProps(path, state) {
8617
8722
  }
8618
8723
  };
8619
8724
  });
8725
+ state.applyStylexEnv(identifiers);
8620
8726
  const evaluatePathFnConfig = {
8621
8727
  identifiers,
8622
8728
  memberExpressions,
@@ -8942,6 +9048,7 @@ function transformStyleXViewTransitionClass(path, state) {
8942
9048
  fn: keyframes$1
8943
9049
  };
8944
9050
  });
9051
+ state.applyStylexEnv(identifiers);
8945
9052
  const {
8946
9053
  confident,
8947
9054
  value
@@ -9203,6 +9310,31 @@ function styleXTransform() {
9203
9310
  });
9204
9311
  }
9205
9312
  },
9313
+ JSXOpeningElement(path) {
9314
+ const sxPropName = state.options.sxPropName;
9315
+ if (sxPropName === false) {
9316
+ return;
9317
+ }
9318
+ const node = path.node;
9319
+ if (node.name.type !== 'JSXIdentifier' || typeof node.name.name !== 'string' || node.name.name[0] !== node.name.name[0].toLowerCase()) {
9320
+ return;
9321
+ }
9322
+ for (const attr of path.get('attributes')) {
9323
+ if (!attr.isJSXAttribute() || !attr.get('name').isJSXIdentifier() || attr.get('name').node.name !== sxPropName) {
9324
+ continue;
9325
+ }
9326
+ const valuePath = attr.get('value');
9327
+ if (!valuePath.isJSXExpressionContainer()) {
9328
+ continue;
9329
+ }
9330
+ const value = valuePath.get('expression');
9331
+ if (value.isJSXEmptyExpression()) {
9332
+ continue;
9333
+ }
9334
+ attr.replaceWith(undefined(callExpression(memberExpression(identifier('stylex'), identifier('props')), [value.node])));
9335
+ break;
9336
+ }
9337
+ },
9206
9338
  CallExpression(path) {
9207
9339
  const parentPath = path.parentPath;
9208
9340
  if (parentPath.isVariableDeclarator()) {
package/lib/index.js CHANGED
@@ -370,6 +370,12 @@ const checkRuntimeInjection = unionOf3(boolean(), string(), object({
370
370
  from: string(),
371
371
  as: string()
372
372
  }));
373
+ const checkEnvOption = (value, name = 'options.env') => {
374
+ if (typeof value !== 'object' || value == null || Array.isArray(value)) {
375
+ return new Error(`Expected (${name}) to be an object, but got \`${String(JSON.stringify(value))}\`.`);
376
+ }
377
+ return value;
378
+ };
373
379
  const DEFAULT_INJECT_PATH = '@stylexjs/stylex/lib/stylex-inject';
374
380
  class StateManager {
375
381
  importPaths = new Set();
@@ -389,6 +395,7 @@ class StateManager {
389
395
  stylexViewTransitionClassImport = new Set();
390
396
  stylexDefaultMarkerImport = new Set();
391
397
  stylexWhenImport = new Set();
398
+ stylexEnvImport = new Set();
392
399
  injectImportInserted = null;
393
400
  styleMap = new Map();
394
401
  styleVars = new Map();
@@ -412,6 +419,7 @@ class StateManager {
412
419
  const enableLegacyValueFlipping = logAndDefault(boolean(), options.enableLegacyValueFlipping ?? defaultOptions.enableLegacyValueFlipping, false, 'options.enableLegacyValueFlipping');
413
420
  const enableLogicalStylesPolyfill = logAndDefault(boolean(), options.enableLogicalStylesPolyfill ?? defaultOptions.enableLogicalStylesPolyfill, false, 'options.enableLogicalStylesPolyfill');
414
421
  const enableLTRRTLComments = logAndDefault(boolean(), options.enableLTRRTLComments ?? defaultOptions.enableLTRRTLComments, false, 'options.enableLTRRTLComments');
422
+ const sxPropName = logAndDefault(unionOf(string(), literal(false)), options.sxPropName ?? 'sx', 'sx', 'options.sxPropName');
415
423
  const test = logAndDefault(boolean(), options.test ?? defaultOptions.test, false, 'options.test');
416
424
  const configRuntimeInjection = logAndDefault(checkRuntimeInjection, options.runtimeInjection ?? false, false, 'options.runtimeInjection');
417
425
  const runtimeInjection = configRuntimeInjection === true ? DEFAULT_INJECT_PATH : configRuntimeInjection === false ? undefined : configRuntimeInjection;
@@ -422,6 +430,10 @@ class StateManager {
422
430
  const propertyValidationMode = logAndDefault(unionOf3(literal('throw'), literal('warn'), literal('silent')), options.propertyValidationMode ?? defaultOptions.propertyValidationMode, 'silent', 'options.propertyValidationMode');
423
431
  const unstable_moduleResolution = logAndDefault(unionOf(nullish(), CheckModuleResolution), options.unstable_moduleResolution, null, 'options.unstable_moduleResolution');
424
432
  const treeshakeCompensation = logAndDefault(boolean(), options.treeshakeCompensation ?? defaultOptions.treeshakeCompensation, false, 'options.treeshakeCompensation');
433
+ const envInput = logAndDefault(checkEnvOption, options.env ?? {}, {}, 'options.env');
434
+ const env = Object.freeze({
435
+ ...envInput
436
+ });
425
437
  const aliasesOption = logAndDefault(unionOf(nullish(), objectOf(unionOf(string(), array(string())))), options.aliases, null, 'options.aliases');
426
438
  const aliases = aliasesOption == null ? aliasesOption : Object.fromEntries(Object.entries(aliasesOption).map(([key, value]) => {
427
439
  if (typeof value === 'string') {
@@ -437,6 +449,7 @@ class StateManager {
437
449
  definedStylexCSSVariables: {},
438
450
  dev,
439
451
  propertyValidationMode,
452
+ env,
440
453
  enableDebugClassNames,
441
454
  enableDebugDataProp,
442
455
  enableDevClassNames,
@@ -447,6 +460,7 @@ class StateManager {
447
460
  enableLegacyValueFlipping,
448
461
  enableLogicalStylesPolyfill,
449
462
  enableLTRRTLComments,
463
+ sxPropName,
450
464
  importSources,
451
465
  rewriteAliases: typeof options.rewriteAliases === 'boolean' ? options.rewriteAliases : false,
452
466
  runtimeInjection,
@@ -478,6 +492,31 @@ class StateManager {
478
492
  }
479
493
  return null;
480
494
  }
495
+ applyStylexEnv(identifiers) {
496
+ const env = this.options.env;
497
+ this.stylexImport.forEach(importName => {
498
+ const current = identifiers[importName];
499
+ if (current != null && typeof current === 'object' && !Array.isArray(current)) {
500
+ if ('fn' in current) {
501
+ identifiers[importName] = {
502
+ env
503
+ };
504
+ } else {
505
+ identifiers[importName] = {
506
+ ...current,
507
+ env
508
+ };
509
+ }
510
+ return;
511
+ }
512
+ identifiers[importName] = {
513
+ env
514
+ };
515
+ });
516
+ this.stylexEnvImport.forEach(importName => {
517
+ identifiers[importName] = env;
518
+ });
519
+ }
481
520
  get canReferenceTheme() {
482
521
  return !!this.inStyleXCreate;
483
522
  }
@@ -796,6 +835,9 @@ function readImportDeclarations(path, state) {
796
835
  if (importedName === 'defaultMarker') {
797
836
  state.stylexDefaultMarkerImport.add(localName);
798
837
  }
838
+ if (importedName === 'env') {
839
+ state.stylexEnvImport.add(localName);
840
+ }
799
841
  }
800
842
  }
801
843
  }
@@ -862,6 +904,9 @@ function readRequires(path, state) {
862
904
  if (prop.key.name === 'defaultMarker') {
863
905
  state.stylexDefaultMarkerImport.add(value.name);
864
906
  }
907
+ if (prop.key.name === 'env') {
908
+ state.stylexEnvImport.add(value.name);
909
+ }
865
910
  }
866
911
  }
867
912
  }
@@ -3460,7 +3505,6 @@ const ILLEGAL_NESTED_PSEUDO = "Pseudo objects can't be nested more than one leve
3460
3505
  const ILLEGAL_PROP_VALUE = 'A style value can only contain an array, string or number.';
3461
3506
  const ILLEGAL_PROP_ARRAY_VALUE = 'A style array value can only contain strings or numbers.';
3462
3507
  const ILLEGAL_NAMESPACE_VALUE = 'A StyleX namespace must be an object.';
3463
- const INVALID_CONST_KEY = 'Keys in defineConsts() cannot start with "--".';
3464
3508
  const INVALID_PSEUDO = 'Invalid pseudo selector, not on the whitelist.';
3465
3509
  const INVALID_PSEUDO_OR_AT_RULE = 'Invalid pseudo or at-rule.';
3466
3510
  const INVALID_MEDIA_QUERY_SYNTAX = 'Invalid media query syntax.';
@@ -3483,7 +3527,6 @@ var m = /*#__PURE__*/Object.freeze({
3483
3527
  ILLEGAL_NESTED_PSEUDO: ILLEGAL_NESTED_PSEUDO,
3484
3528
  ILLEGAL_PROP_ARRAY_VALUE: ILLEGAL_PROP_ARRAY_VALUE,
3485
3529
  ILLEGAL_PROP_VALUE: ILLEGAL_PROP_VALUE,
3486
- INVALID_CONST_KEY: INVALID_CONST_KEY,
3487
3530
  INVALID_MEDIA_QUERY_SYNTAX: INVALID_MEDIA_QUERY_SYNTAX,
3488
3531
  INVALID_PSEUDO: INVALID_PSEUDO,
3489
3532
  INVALID_PSEUDO_OR_AT_RULE: INVALID_PSEUDO_OR_AT_RULE,
@@ -4484,6 +4527,16 @@ function requirePropertyPriorities () {
4484
4527
  SIBLING_AFTER: /^:where\(:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/,
4485
4528
  ANY_SIBLING: /^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+~\s+\*,\s+:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/
4486
4529
  };
4530
+ const PSEUDO_PART_REGEX = /::[a-zA-Z-]+|:[a-zA-Z-]+(?:\([^)]*\))?/g;
4531
+ function getCompoundPseudoPriority(key) {
4532
+ const parts = key.match(PSEUDO_PART_REGEX);
4533
+ if (!parts || parts.length <= 1 || parts.some(p => p.includes('('))) return;
4534
+ let total = 0;
4535
+ for (const part of parts) {
4536
+ total += part.startsWith('::') ? PSEUDO_ELEMENT_PRIORITY : PSEUDO_CLASS_PRIORITIES[part] ?? 40;
4537
+ }
4538
+ return total;
4539
+ }
4487
4540
  function getAtRulePriority(key) {
4488
4541
  if (key.startsWith('--')) {
4489
4542
  return 1;
@@ -4524,7 +4577,7 @@ function requirePropertyPriorities () {
4524
4577
  return 40 + pseudoBase(siblingAfterMatch[1]);
4525
4578
  }
4526
4579
  if (key.startsWith(':')) {
4527
- const prop = key.startsWith(':') && key.includes('(') ? key.slice(0, key.indexOf('(')) : key;
4580
+ const prop = key.split('(')[0];
4528
4581
  return PSEUDO_CLASS_PRIORITIES[prop] ?? 40;
4529
4582
  }
4530
4583
  }
@@ -4545,6 +4598,8 @@ function requirePropertyPriorities () {
4545
4598
  function getPriority(key) {
4546
4599
  const atRulePriority = getAtRulePriority(key);
4547
4600
  if (atRulePriority) return atRulePriority;
4601
+ const compoundPriority = getCompoundPseudoPriority(key);
4602
+ if (compoundPriority != null) return compoundPriority;
4548
4603
  const pseudoElementPriority = getPseudoElementPriority(key);
4549
4604
  if (pseudoElementPriority) return pseudoElementPriority;
4550
4605
  const pseudoClassPriority = getPseudoClassPriority(key);
@@ -4755,7 +4810,7 @@ class PreRule {
4755
4810
  this.value = value;
4756
4811
  }
4757
4812
  get pseudos() {
4758
- const unsortedPseudos = this.keyPath.filter(key => key.startsWith(':'));
4813
+ const unsortedPseudos = this.keyPath.filter(key => key.startsWith(':') || key.startsWith('['));
4759
4814
  return sortPseudos(unsortedPseudos);
4760
4815
  }
4761
4816
  get atRules() {
@@ -4860,7 +4915,7 @@ function _flattenRawStyleObject(style, keyPath, options) {
4860
4915
  });
4861
4916
  continue;
4862
4917
  }
4863
- if (typeof value === 'object' && !key.startsWith(':') && !key.startsWith('@')) {
4918
+ if (typeof value === 'object' && !key.startsWith(':') && !key.startsWith('@') && !key.startsWith('[')) {
4864
4919
  const equivalentPairs = {};
4865
4920
  for (const condition in value) {
4866
4921
  const innerValue = value[condition];
@@ -4886,7 +4941,7 @@ function _flattenRawStyleObject(style, keyPath, options) {
4886
4941
  flattened.push([property, PreRuleSet.create(rules)]);
4887
4942
  }
4888
4943
  }
4889
- if (typeof value === 'object' && (key.startsWith(':') || key.startsWith('@'))) {
4944
+ if (typeof value === 'object' && (key.startsWith(':') || key.startsWith('@') || key.startsWith('['))) {
4890
4945
  const pairs = _flattenRawStyleObject(value, [...keyPath, _key], options);
4891
4946
  for (const [property, preRule] of pairs) {
4892
4947
  flattened.push([key + '_' + property, preRule]);
@@ -4916,7 +4971,7 @@ function validateNamespace(namespace, conditions = []) {
4916
4971
  continue;
4917
4972
  }
4918
4973
  if (isPlainObject(val)) {
4919
- if (key.startsWith('@') || key.startsWith(':')) {
4974
+ if (key.startsWith('@') || key.startsWith(':') || key.startsWith('[')) {
4920
4975
  if (conditions.includes(key)) {
4921
4976
  throw new Error(DUPLICATE_CONDITIONAL);
4922
4977
  }
@@ -4932,7 +4987,7 @@ function validateNamespace(namespace, conditions = []) {
4932
4987
  function validateConditionalStyles(val, conditions = []) {
4933
4988
  for (const key in val) {
4934
4989
  const v = val[key];
4935
- if (!(key.startsWith('@') || key.startsWith(':') || key.startsWith('var(--') || key === 'default')) {
4990
+ if (!(key.startsWith('@') || key.startsWith(':') || key.startsWith('[') || key.startsWith('var(--') || key === 'default')) {
4936
4991
  throw new Error(INVALID_PSEUDO_OR_AT_RULE);
4937
4992
  }
4938
4993
  if (conditions.includes(key)) {
@@ -5318,11 +5373,8 @@ function styleXDefineConsts(constants, options) {
5318
5373
  const jsOutput = {};
5319
5374
  const injectableStyles = {};
5320
5375
  for (const [key, value] of Object.entries(constants)) {
5321
- if (key.startsWith('--')) {
5322
- throw new Error(INVALID_CONST_KEY);
5323
- }
5324
5376
  const varSafeKey = (key[0] >= '0' && key[0] <= '9' ? `_${key}` : key).replace(/[^a-zA-Z0-9]/g, '_');
5325
- const constKey = debug && enableDebugClassNames ? `${varSafeKey}-${classNamePrefix}${hash(`${exportId}.${key}`)}` : `${classNamePrefix}${hash(`${exportId}.${key}`)}`;
5377
+ const constKey = key.startsWith('--') ? key.slice(2) : debug && enableDebugClassNames ? `${varSafeKey}-${classNamePrefix}${hash(`${exportId}.${key}`)}` : `${classNamePrefix}${hash(`${exportId}.${key}`)}`;
5326
5378
  jsOutput[key] = value;
5327
5379
  injectableStyles[constKey] = {
5328
5380
  constKey,
@@ -5494,12 +5546,15 @@ function getDefaultMarkerClassName(options = defaultOptions) {
5494
5546
  return `${prefix}default-marker`;
5495
5547
  }
5496
5548
  function validatePseudoSelector(pseudo) {
5497
- if (!pseudo.startsWith(':')) {
5498
- throw new Error('Pseudo selector must start with ":"');
5549
+ if (!(pseudo.startsWith(':') || pseudo.startsWith('['))) {
5550
+ throw new Error('Pseudo selector must start with ":" or "["');
5499
5551
  }
5500
5552
  if (pseudo.startsWith('::')) {
5501
5553
  throw new Error('Pseudo selector cannot start with "::" (pseudo-elements are not supported)');
5502
5554
  }
5555
+ if (pseudo.startsWith('[') && !pseudo.endsWith(']')) {
5556
+ throw new Error('Attribute selector must end with "]"');
5557
+ }
5503
5558
  }
5504
5559
  function ancestor(pseudo, options = defaultOptions) {
5505
5560
  validatePseudoSelector(pseudo);
@@ -6533,6 +6588,18 @@ function isSafeToSkipNullCheck(expr) {
6533
6588
  }
6534
6589
  return false;
6535
6590
  }
6591
+ function hasExplicitNullishFallback(expr) {
6592
+ if (t__namespace.isNullLiteral(expr)) return true;
6593
+ if (t__namespace.isIdentifier(expr) && expr.name === 'undefined') return true;
6594
+ if (t__namespace.isUnaryExpression(expr) && expr.operator === 'void') return true;
6595
+ if (t__namespace.isConditionalExpression(expr)) {
6596
+ return hasExplicitNullishFallback(expr.consequent) || hasExplicitNullishFallback(expr.alternate);
6597
+ }
6598
+ if (t__namespace.isLogicalExpression(expr)) {
6599
+ return hasExplicitNullishFallback(expr.left) || hasExplicitNullishFallback(expr.right);
6600
+ }
6601
+ return false;
6602
+ }
6536
6603
  function transformStyleXCreate(path, state) {
6537
6604
  const {
6538
6605
  node
@@ -6601,6 +6668,7 @@ function transformStyleXCreate(path, state) {
6601
6668
  when: stylexWhen
6602
6669
  };
6603
6670
  });
6671
+ state.applyStylexEnv(identifiers);
6604
6672
  const {
6605
6673
  confident,
6606
6674
  value,
@@ -6628,7 +6696,7 @@ function transformStyleXCreate(path, state) {
6628
6696
  const isPseudoElement = path.some(p => p.startsWith('::'));
6629
6697
  injectedInheritStyles[variableName] = {
6630
6698
  priority: 0,
6631
- ltr: `@property ${variableName} { syntax: "*";${isPseudoElement ? '' : ' inherits: false;'}}`,
6699
+ ltr: `@property ${variableName} { syntax: "*"; inherits: ${isPseudoElement ? 'true' : 'false'};}`,
6632
6700
  rtl: null
6633
6701
  };
6634
6702
  });
@@ -6677,14 +6745,21 @@ function transformStyleXCreate(path, state) {
6677
6745
  for (const [className, classPaths] of Object.entries(classPathsPerNamespace[key])) {
6678
6746
  origClassPaths[className] = classPaths.join('_');
6679
6747
  }
6680
- let dynamicStyles = Object.entries(inlineStyles).map(([_key, v]) => ({
6748
+ let dynamicStyles = Object.entries(inlineStyles).map(([varName, v]) => ({
6681
6749
  expression: v.originalExpression,
6682
6750
  key: v.path.slice(0, v.path.findIndex(p => !p.startsWith(':') && !p.startsWith('@')) + 1).join('_'),
6751
+ varName,
6683
6752
  path: v.path.join('_')
6684
6753
  }));
6685
6754
  if (state.options.styleResolution === 'legacy-expand-shorthands') {
6686
6755
  dynamicStyles = legacyExpandShorthands(dynamicStyles);
6687
6756
  }
6757
+ const nullishVarExpressions = new Map();
6758
+ dynamicStyles.forEach(style => {
6759
+ if (hasExplicitNullishFallback(style.expression)) {
6760
+ nullishVarExpressions.set(style.varName, style.expression);
6761
+ }
6762
+ });
6688
6763
  if (t__namespace.isObjectExpression(prop.value)) {
6689
6764
  const value = prop.value;
6690
6765
  let cssTagValue = t__namespace.booleanLiteral(true);
@@ -6708,9 +6783,23 @@ function transformStyleXCreate(path, state) {
6708
6783
  let isStatic = true;
6709
6784
  const exprList = [];
6710
6785
  classList.forEach((cls, index) => {
6711
- const expr = dynamicStyles.find(({
6786
+ let expr = dynamicStyles.find(({
6712
6787
  path
6713
6788
  }) => origClassPaths[cls] === path)?.expression;
6789
+ if (expr == null && nullishVarExpressions.size > 0) {
6790
+ const injectedStyle = injectedStyles[cls];
6791
+ const rule = injectedStyle != null ? typeof injectedStyle.ltr === 'string' ? injectedStyle.ltr : typeof injectedStyle.rtl === 'string' ? injectedStyle.rtl : null : null;
6792
+ if (rule != null) {
6793
+ const matches = rule.matchAll(/var\((--x-[^,)]+)[^)]*\)/g);
6794
+ for (const match of matches) {
6795
+ const varExpr = nullishVarExpressions.get(match[1]);
6796
+ if (varExpr != null) {
6797
+ expr = varExpr;
6798
+ break;
6799
+ }
6800
+ }
6801
+ }
6802
+ }
6714
6803
  const isLast = index === classList.length - 1;
6715
6804
  const clsWithSpace = isLast ? cls : cls + ' ';
6716
6805
  if (expr && !isSafeToSkipNullCheck(expr)) {
@@ -6871,6 +6960,7 @@ function transformStyleXCreateTheme(callExpressionPath, state) {
6871
6960
  types: stylexTypes
6872
6961
  };
6873
6962
  });
6963
+ state.applyStylexEnv(identifiers);
6874
6964
  const {
6875
6965
  confident: confident2,
6876
6966
  value: overrides
@@ -6985,6 +7075,7 @@ function transformStyleXDefineVars(callExpressionPath, state) {
6985
7075
  types: stylexTypes
6986
7076
  };
6987
7077
  });
7078
+ state.applyStylexEnv(identifiers);
6988
7079
  const {
6989
7080
  confident,
6990
7081
  value
@@ -7047,10 +7138,16 @@ function transformStyleXDefineConsts(callExpressionPath, state) {
7047
7138
  const varId = variableDeclaratorNode.id;
7048
7139
  const args = callExpressionPath.get('arguments');
7049
7140
  const firstArg = args[0];
7141
+ const evaluatePathFnConfig = {
7142
+ identifiers: {},
7143
+ memberExpressions: {},
7144
+ disableImports: true
7145
+ };
7146
+ state.applyStylexEnv(evaluatePathFnConfig.identifiers);
7050
7147
  const {
7051
7148
  confident,
7052
7149
  value
7053
- } = evaluate(firstArg, state);
7150
+ } = evaluate(firstArg, state, evaluatePathFnConfig);
7054
7151
  if (!confident) {
7055
7152
  throw callExpressionPath.buildCodeFrameError(messages.nonStaticValue('defineConsts'), SyntaxError);
7056
7153
  }
@@ -7132,6 +7229,7 @@ function transformStyleXKeyframes(path, state) {
7132
7229
  fn: firstThatWorks
7133
7230
  };
7134
7231
  });
7232
+ state.applyStylexEnv(identifiers);
7135
7233
  const {
7136
7234
  confident,
7137
7235
  value
@@ -7203,6 +7301,7 @@ function transformStyleXPositionTry(path, state) {
7203
7301
  fn: firstThatWorks
7204
7302
  };
7205
7303
  });
7304
+ state.applyStylexEnv(identifiers);
7206
7305
  const {
7207
7306
  confident,
7208
7307
  value
@@ -7257,6 +7356,12 @@ function transformStyleXMerge(path, state) {
7257
7356
  if (node == null || node.callee.type !== 'Identifier' || !state.stylexImport.has(node.callee.name)) {
7258
7357
  return;
7259
7358
  }
7359
+ const evaluatePathFnConfig = {
7360
+ identifiers: {},
7361
+ memberExpressions: {},
7362
+ disableImports: true
7363
+ };
7364
+ state.applyStylexEnv(evaluatePathFnConfig.identifiers);
7260
7365
  let bailOut = false;
7261
7366
  let conditional = 0;
7262
7367
  let currentIndex = -1;
@@ -7363,7 +7468,7 @@ function transformStyleXMerge(path, state) {
7363
7468
  const {
7364
7469
  confident,
7365
7470
  value: styleValue
7366
- } = evaluate(path, state);
7471
+ } = evaluate(path, state, evaluatePathFnConfig);
7367
7472
  if (!confident || styleValue == null) {
7368
7473
  nonNullProps = true;
7369
7474
  styleNonNullProps = true;
@@ -7499,6 +7604,7 @@ function transformStylexProps(path, state) {
7499
7604
  }
7500
7605
  };
7501
7606
  });
7607
+ state.applyStylexEnv(identifiers);
7502
7608
  const evaluatePathFnConfig = {
7503
7609
  identifiers,
7504
7610
  memberExpressions,
@@ -7824,6 +7930,7 @@ function transformStyleXViewTransitionClass(path, state) {
7824
7930
  fn: keyframes$1
7825
7931
  };
7826
7932
  });
7933
+ state.applyStylexEnv(identifiers);
7827
7934
  const {
7828
7935
  confident,
7829
7936
  value
@@ -8085,6 +8192,31 @@ function styleXTransform() {
8085
8192
  });
8086
8193
  }
8087
8194
  },
8195
+ JSXOpeningElement(path) {
8196
+ const sxPropName = state.options.sxPropName;
8197
+ if (sxPropName === false) {
8198
+ return;
8199
+ }
8200
+ const node = path.node;
8201
+ if (node.name.type !== 'JSXIdentifier' || typeof node.name.name !== 'string' || node.name.name[0] !== node.name.name[0].toLowerCase()) {
8202
+ return;
8203
+ }
8204
+ for (const attr of path.get('attributes')) {
8205
+ if (!attr.isJSXAttribute() || !attr.get('name').isJSXIdentifier() || attr.get('name').node.name !== sxPropName) {
8206
+ continue;
8207
+ }
8208
+ const valuePath = attr.get('value');
8209
+ if (!valuePath.isJSXExpressionContainer()) {
8210
+ continue;
8211
+ }
8212
+ const value = valuePath.get('expression');
8213
+ if (value.isJSXEmptyExpression()) {
8214
+ continue;
8215
+ }
8216
+ attr.replaceWith(t__namespace.jsxSpreadAttribute(t__namespace.callExpression(t__namespace.memberExpression(t__namespace.identifier('stylex'), t__namespace.identifier('props')), [value.node])));
8217
+ break;
8218
+ }
8219
+ },
8088
8220
  CallExpression(path) {
8089
8221
  const parentPath = path.parentPath;
8090
8222
  if (parentPath.isVariableDeclarator()) {
@@ -47,6 +47,7 @@ export type StyleXOptions = $ReadOnly<{
47
47
  classNamePrefix: string,
48
48
  debug: ?boolean,
49
49
  definedStylexCSSVariables?: { [key: string]: mixed },
50
+ env?: $ReadOnly<{ [string]: any }>,
50
51
  dev: boolean,
51
52
  propertyValidationMode?: 'throw' | 'warn' | 'silent',
52
53
  enableDebugClassNames?: ?boolean,
@@ -35,8 +35,6 @@ export declare const ILLEGAL_PROP_ARRAY_VALUE: 'A style array value can only con
35
35
  export declare type ILLEGAL_PROP_ARRAY_VALUE = typeof ILLEGAL_PROP_ARRAY_VALUE;
36
36
  export declare const ILLEGAL_NAMESPACE_VALUE: 'A StyleX namespace must be an object.';
37
37
  export declare type ILLEGAL_NAMESPACE_VALUE = typeof ILLEGAL_NAMESPACE_VALUE;
38
- export declare const INVALID_CONST_KEY: 'Keys in defineConsts() cannot start with "--".';
39
- export declare type INVALID_CONST_KEY = typeof INVALID_CONST_KEY;
40
38
  export declare const INVALID_PSEUDO: 'Invalid pseudo selector, not on the whitelist.';
41
39
  export declare type INVALID_PSEUDO = typeof INVALID_PSEUDO;
42
40
  export declare const INVALID_PSEUDO_OR_AT_RULE: 'Invalid pseudo or at-rule.';
@@ -26,7 +26,6 @@ declare export const ILLEGAL_NESTED_PSEUDO: "Pseudo objects can't be nested more
26
26
  declare export const ILLEGAL_PROP_VALUE: 'A style value can only contain an array, string or number.';
27
27
  declare export const ILLEGAL_PROP_ARRAY_VALUE: 'A style array value can only contain strings or numbers.';
28
28
  declare export const ILLEGAL_NAMESPACE_VALUE: 'A StyleX namespace must be an object.';
29
- declare export const INVALID_CONST_KEY: 'Keys in defineConsts() cannot start with "--".';
30
29
  declare export const INVALID_PSEUDO: 'Invalid pseudo selector, not on the whitelist.';
31
30
  declare export const INVALID_PSEUDO_OR_AT_RULE: 'Invalid pseudo or at-rule.';
32
31
  declare export const INVALID_MEDIA_QUERY_SYNTAX: 'Invalid media query syntax.';
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { StyleXOptions } from '../common-types';
11
+ type WhenSelector = `:${string}` | `[${string}`;
11
12
  /**
12
13
  * Creates selector that observes if the given pseudo-class is
13
14
  * active on an ancestor with the "defaultMarker"
@@ -17,7 +18,7 @@ import type { StyleXOptions } from '../common-types';
17
18
  * @returns A :where() clause for the ancestor observer
18
19
  */
19
20
  export declare function ancestor(
20
- pseudo: `:${string}`,
21
+ pseudo: WhenSelector,
21
22
  options?: string | StyleXOptions,
22
23
  ): string;
23
24
  /**
@@ -28,7 +29,7 @@ export declare function ancestor(
28
29
  * @returns A :has() clause for the descendant observer
29
30
  */
30
31
  export declare function descendant(
31
- pseudo: `:${string}`,
32
+ pseudo: WhenSelector,
32
33
  options?: string | StyleXOptions,
33
34
  ): string;
34
35
  /**
@@ -39,7 +40,7 @@ export declare function descendant(
39
40
  * @returns A :where() clause for the previous sibling observer
40
41
  */
41
42
  export declare function siblingBefore(
42
- pseudo: `:${string}`,
43
+ pseudo: WhenSelector,
43
44
  options?: string | StyleXOptions,
44
45
  ): string;
45
46
  /**
@@ -50,7 +51,7 @@ export declare function siblingBefore(
50
51
  * @returns A :has() clause for the next sibling observer
51
52
  */
52
53
  export declare function siblingAfter(
53
- pseudo: `:${string}`,
54
+ pseudo: WhenSelector,
54
55
  options?: string | StyleXOptions,
55
56
  ): string;
56
57
  /**
@@ -61,6 +62,6 @@ export declare function siblingAfter(
61
62
  * @returns A :where() clause for the any sibling observer
62
63
  */
63
64
  export declare function anySibling(
64
- pseudo: `:${string}`,
65
+ pseudo: WhenSelector,
65
66
  options?: string | StyleXOptions,
66
67
  ): string;
@@ -9,6 +9,8 @@
9
9
 
10
10
  import type { StyleXOptions } from '../common-types';
11
11
 
12
+ type WhenSelector = StringPrefix<':'> | StringPrefix<'['>;
13
+
12
14
  /**
13
15
  * Creates selector that observes if the given pseudo-class is
14
16
  * active on an ancestor with the "defaultMarker"
@@ -18,7 +20,7 @@ import type { StyleXOptions } from '../common-types';
18
20
  * @returns A :where() clause for the ancestor observer
19
21
  */
20
22
  declare export function ancestor(
21
- pseudo: StringPrefix<':'>,
23
+ pseudo: WhenSelector,
22
24
  options?: string | StyleXOptions,
23
25
  ): string;
24
26
 
@@ -30,7 +32,7 @@ declare export function ancestor(
30
32
  * @returns A :has() clause for the descendant observer
31
33
  */
32
34
  declare export function descendant(
33
- pseudo: StringPrefix<':'>,
35
+ pseudo: WhenSelector,
34
36
  options?: string | StyleXOptions,
35
37
  ): string;
36
38
 
@@ -42,7 +44,7 @@ declare export function descendant(
42
44
  * @returns A :where() clause for the previous sibling observer
43
45
  */
44
46
  declare export function siblingBefore(
45
- pseudo: StringPrefix<':'>,
47
+ pseudo: WhenSelector,
46
48
  options?: string | StyleXOptions,
47
49
  ): string;
48
50
 
@@ -54,7 +56,7 @@ declare export function siblingBefore(
54
56
  * @returns A :has() clause for the next sibling observer
55
57
  */
56
58
  declare export function siblingAfter(
57
- pseudo: StringPrefix<':'>,
59
+ pseudo: WhenSelector,
58
60
  options?: string | StyleXOptions,
59
61
  ): string;
60
62
 
@@ -66,6 +68,6 @@ declare export function siblingAfter(
66
68
  * @returns A :where() clause for the any sibling observer
67
69
  */
68
70
  declare export function anySibling(
69
- pseudo: StringPrefix<':'>,
71
+ pseudo: WhenSelector,
70
72
  options?: string | StyleXOptions,
71
73
  ): string;
@@ -13,6 +13,7 @@ import type {
13
13
  CompiledNamespaces,
14
14
  StyleXOptions as RuntimeOptions,
15
15
  } from '../shared';
16
+ import type { FunctionConfig } from './evaluate-path';
16
17
  import * as t from '@babel/types';
17
18
  export type ImportPathResolution =
18
19
  | false
@@ -60,6 +61,7 @@ export type StyleXOptions = Readonly<
60
61
  enableLogicalStylesPolyfill?: boolean;
61
62
  enableLTRRTLComments?: boolean;
62
63
  enableMinifiedKeys?: boolean;
64
+ sxPropName?: string | false;
63
65
  importSources: ReadonlyArray<
64
66
  string | Readonly<{ from: string; as: string }>
65
67
  >;
@@ -87,6 +89,7 @@ export type StyleXOptions = Readonly<
87
89
  enableLogicalStylesPolyfill?: boolean;
88
90
  enableLTRRTLComments?: boolean;
89
91
  enableMinifiedKeys?: boolean;
92
+ sxPropName?: string | false;
90
93
  importSources: ReadonlyArray<
91
94
  string | Readonly<{ from: string; as: string }>
92
95
  >;
@@ -104,6 +107,7 @@ type StyleXStateOptions = Readonly<
104
107
  Omit<
105
108
  StyleXOptions,
106
109
  | keyof {
110
+ env: Readonly<{ [$$Key$$: string]: any }>;
107
111
  runtimeInjection:
108
112
  | (null | undefined | string)
109
113
  | Readonly<{ from: string; as: null | undefined | string }>;
@@ -114,6 +118,7 @@ type StyleXStateOptions = Readonly<
114
118
  rewriteAliases: boolean;
115
119
  }
116
120
  > & {
121
+ env: Readonly<{ [$$Key$$: string]: any }>;
117
122
  runtimeInjection:
118
123
  | (null | undefined | string)
119
124
  | Readonly<{ from: string; as: null | undefined | string }>;
@@ -143,6 +148,7 @@ declare class StateManager {
143
148
  readonly stylexViewTransitionClassImport: Set<string>;
144
149
  readonly stylexDefaultMarkerImport: Set<string>;
145
150
  readonly stylexWhenImport: Set<string>;
151
+ readonly stylexEnvImport: Set<string>;
146
152
  injectImportInserted: null | undefined | t.Identifier;
147
153
  readonly styleMap: Map<string, CompiledNamespaces>;
148
154
  readonly styleVars: Map<string, NodePath>;
@@ -156,6 +162,7 @@ declare class StateManager {
156
162
  get importPathString(): string;
157
163
  get importSources(): ReadonlyArray<string>;
158
164
  importAs(source: string): null | string;
165
+ applyStylexEnv(identifiers: FunctionConfig['identifiers']): void;
159
166
  get canReferenceTheme(): boolean;
160
167
  get metadata(): { [key: string]: any };
161
168
  get runtimeInjection():
@@ -13,6 +13,8 @@ import type {
13
13
  CompiledNamespaces,
14
14
  StyleXOptions as RuntimeOptions,
15
15
  } from '../shared';
16
+ import type { FunctionConfig } from './evaluate-path';
17
+
16
18
  import * as t from '@babel/types';
17
19
  export type ImportPathResolution =
18
20
  | false
@@ -57,6 +59,7 @@ export type StyleXOptions = $ReadOnly<{
57
59
  enableLogicalStylesPolyfill?: boolean,
58
60
  enableLTRRTLComments?: boolean,
59
61
  enableMinifiedKeys?: boolean,
62
+ sxPropName?: string | false,
60
63
  importSources: $ReadOnlyArray<
61
64
  string | $ReadOnly<{ from: string, as: string }>,
62
65
  >,
@@ -70,6 +73,7 @@ export type StyleXOptions = $ReadOnly<{
70
73
 
71
74
  type StyleXStateOptions = $ReadOnly<{
72
75
  ...StyleXOptions,
76
+ env: $ReadOnly<{ [string]: any }>,
73
77
  runtimeInjection: ?string | $ReadOnly<{ from: string, as: ?string }>,
74
78
  aliases?: ?$ReadOnly<{ [string]: $ReadOnlyArray<string> }>,
75
79
  rewriteAliases: boolean,
@@ -96,6 +100,7 @@ declare export default class StateManager {
96
100
  +stylexViewTransitionClassImport: Set<string>;
97
101
  +stylexDefaultMarkerImport: Set<string>;
98
102
  +stylexWhenImport: Set<string>;
103
+ +stylexEnvImport: Set<string>;
99
104
  injectImportInserted: ?t.Identifier;
100
105
  // `stylex.create` calls
101
106
  +styleMap: Map<string, CompiledNamespaces>;
@@ -109,6 +114,7 @@ declare export default class StateManager {
109
114
  get importPathString(): string;
110
115
  get importSources(): $ReadOnlyArray<string>;
111
116
  importAs(source: string): null | string;
117
+ applyStylexEnv(identifiers: FunctionConfig['identifiers']): void;
112
118
  get canReferenceTheme(): boolean;
113
119
  get metadata(): { [key: string]: any };
114
120
  get runtimeInjection(): ?$ReadOnly<{ from: string, as?: ?string }>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+
10
+ import type { NodePath } from '@babel/traverse';
11
+ import * as t from '@babel/types';
12
+ import StateManager from '../utils/state-manager';
13
+ /**
14
+ * Transform inline-css call expressions like `css.color(value)`.
15
+ * These become arrow functions that return [compiledObj, inlineObj].
16
+ */
17
+ export declare function transformInlineCSSCall(
18
+ path: NodePath<t.CallExpression>,
19
+ state: StateManager,
20
+ ): void;
21
+ /**
22
+ * Transform inline-css member expressions like `css.display.flex`.
23
+ * These become compiled namespace objects: { [key]: className, $$css: true }
24
+ */
25
+ export declare function transformInlineCSSMemberExpression(
26
+ path: NodePath<t.MemberExpression>,
27
+ state: StateManager,
28
+ ): void;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type { NodePath } from '@babel/traverse';
11
+
12
+ import * as t from '@babel/types';
13
+ import StateManager from '../utils/state-manager';
14
+ /**
15
+ * Transform inline-css call expressions like `css.color(value)`.
16
+ * These become arrow functions that return [compiledObj, inlineObj].
17
+ */
18
+ declare export function transformInlineCSSCall(
19
+ path: NodePath<t.CallExpression>,
20
+ state: StateManager,
21
+ ): void;
22
+
23
+ /**
24
+ * Transform inline-css member expressions like `css.display.flex`.
25
+ * These become compiled namespace objects: { [key]: className, $$css: true }
26
+ */
27
+ declare export function transformInlineCSSMemberExpression(
28
+ path: NodePath<t.MemberExpression>,
29
+ state: StateManager,
30
+ ): void;
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import type { NodePath } from '@babel/traverse';
11
-
12
11
  import * as t from '@babel/types';
13
12
  import StateManager from '../utils/state-manager';
14
13
  /// This function looks for `stylex.defineConsts` calls and transforms them.
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import type { NodePath } from '@babel/traverse';
11
-
12
11
  import * as t from '@babel/types';
13
12
  import StateManager from '../utils/state-manager';
14
13
  declare export function skipStylexMergeChildren(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stylexjs/babel-plugin",
3
- "version": "0.17.5",
3
+ "version": "0.18.1",
4
4
  "description": "StyleX babel plugin.",
5
5
  "main": "lib/index.js",
6
6
  "browser": "lib/index.browser.js",
@@ -23,8 +23,8 @@
23
23
  "@babel/traverse": "^7.26.8",
24
24
  "@babel/types": "^7.26.8",
25
25
  "@dual-bundle/import-meta-resolve": "^4.1.0",
26
- "@stylexjs/shared": "0.17.5",
27
- "@stylexjs/stylex": "0.17.5",
26
+ "@stylexjs/shared": "0.18.1",
27
+ "@stylexjs/stylex": "0.18.1",
28
28
  "postcss-value-parser": "^4.1.0"
29
29
  },
30
30
  "devDependencies": {
@@ -36,8 +36,8 @@
36
36
  "@rollup/plugin-replace": "^6.0.1",
37
37
  "babel-plugin-syntax-hermes-parser": "^0.32.1",
38
38
  "path-browserify": "^1.0.1",
39
- "rollup": "^4.24.0",
40
- "scripts": "0.17.5"
39
+ "rollup": "^4.59.0",
40
+ "scripts": "0.18.1"
41
41
  },
42
42
  "files": [
43
43
  "flow_modules/*",