Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## master

- - Add `env` compiler option to expose `stylex.env` compile-time constants.

## 0.16.3 (Oct 27, 2025)

- Add configs to `sort-keys` property ordering.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,119 @@ describe('@stylexjs/babel-plugin', () => {
`);
});

test('stylex.env resolves compile-time constants', () => {
const { code, metadata } = transform(
`
import * as stylex from '@stylexjs/stylex';
export const styles = stylex.create({
root: {
color: stylex.env.brandPrimary,
}
});
`,
{ env: { brandPrimary: '#123456' } },
);
expect(code).toMatchInlineSnapshot(`
"import * as stylex from '@stylexjs/stylex';
export const styles = {
root: {
kMwMTN: "x1tfn4g9",
$$css: true
}
};"
`);
expect(metadata).toMatchInlineSnapshot(`
{
"stylex": [
[
"x1tfn4g9",
{
"ltr": ".x1tfn4g9{color:#123456}",
"rtl": null,
},
3000,
],
],
}
`);
});

test('named env import resolves compile-time constants', () => {
const { code, metadata } = transform(
`
import * as stylex from '@stylexjs/stylex';
import { env } from '@stylexjs/stylex';
export const styles = stylex.create({
root: {
color: env.brandPrimary,
}
});
`,
{ env: { brandPrimary: '#654321' } },
);
expect(code).toMatchInlineSnapshot(`
"import * as stylex from '@stylexjs/stylex';
import { env } from '@stylexjs/stylex';
export const styles = {
root: {
kMwMTN: "xa6cz37",
$$css: true
}
};"
`);
expect(metadata).toMatchInlineSnapshot(`
{
"stylex": [
[
"xa6cz37",
{
"ltr": ".xa6cz37{color:#654321}",
"rtl": null,
},
3000,
],
],
}
`);
});

test('named env import resolves compile-time constants', () => {
const { code, metadata } = transform(
`
import {create, env} from '@stylexjs/stylex';
export const styles = create({
root: {
color: env.brandPrimary,
}
});
`,
{ env: { brandPrimary: '#123456' } },
);
expect(code).toMatchInlineSnapshot(`
"import { create, env } from '@stylexjs/stylex';
export const styles = {
root: {
kMwMTN: "x1tfn4g9",
$$css: true
}
};"
`);
expect(metadata).toMatchInlineSnapshot(`
{
"stylex": [
[
"x1tfn4g9",
{
"ltr": ".x1tfn4g9{color:#123456}",
"rtl": null,
},
3000,
],
],
}
`);
});

test('nested referenced style object', () => {
const { code, metadata } = transform(`
import * as stylex from '@stylexjs/stylex';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,56 @@ describe('@stylexjs/babel-plugin', () => {
`);
});

test('stylex.env resolves in inline objects', () => {
expect(
transform(
`
import stylex from 'stylex';
const styles = stylex.create({
red: {
color: stylex.env.primaryColor,
}
});
stylex.props(styles.red);
`,
{ env: { primaryColor: '#ff0000' } },
),
).toMatchInlineSnapshot(`
"import _inject from "@stylexjs/stylex/lib/stylex-inject";
var _inject2 = _inject;
import stylex from 'stylex';
_inject2(".xe4pkkx{color:#ff0000}", 3000);
({
className: "xe4pkkx"
});"
`);
});

test('named env import resolves in inline objects', () => {
expect(
transform(
`
import { props, create, env } from 'stylex';
const styles = create({
red: {
color: env.primaryColor,
}
});
props(styles.red);
`,
{ env: { primaryColor: '#00ffaa' } },
),
).toMatchInlineSnapshot(`
"import _inject from "@stylexjs/stylex/lib/stylex-inject";
var _inject2 = _inject;
import { props, create, env } from 'stylex';
_inject2(".x4iekqp{color:#00ffaa}", 3000);
({
className: "x4iekqp"
});"
`);
});

describe('props calls with jsx', () => {
const options = {
debug: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export type StyleXOptions = $ReadOnly<{
classNamePrefix: string,
debug: ?boolean,
definedStylexCSSVariables?: { [key: string]: mixed },
env?: $ReadOnly<{ [string]: any }>,
dev: boolean,
enableDebugClassNames?: ?boolean,
enableDebugDataProp?: ?boolean,
Expand Down
52 changes: 52 additions & 0 deletions packages/@stylexjs/babel-plugin/src/utils/state-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
StyleXOptions as RuntimeOptions,
} from '../shared';
import type { Check } from './validate';
import type { FunctionConfig } from './evaluate-path';

import * as t from '@babel/types';
import { name } from '@stylexjs/stylex/package.json';
Expand Down Expand Up @@ -113,6 +114,7 @@ export type StyleXOptions = $ReadOnly<{

type StyleXStateOptions = $ReadOnly<{
...StyleXOptions,
env: $ReadOnly<{ [string]: any }>,
runtimeInjection: ?string | $ReadOnly<{ from: string, as: ?string }>,
aliases?: ?$ReadOnly<{ [string]: $ReadOnlyArray<string> }>,
rewriteAliases: boolean,
Expand All @@ -139,6 +141,20 @@ const checkRuntimeInjection: Check<StyleXOptions['runtimeInjection']> =
}),
);

const checkEnvOption: Check<$ReadOnly<{ [string]: mixed }>> = (
value,
name = 'options.env',
) => {
if (typeof value !== 'object' || value == null || Array.isArray(value)) {
return new Error(
`Expected (${name}) to be an object, but got \`${JSON.stringify(
value,
)}\`.`,
);
}
return value;
};

const DEFAULT_INJECT_PATH = '@stylexjs/stylex/lib/stylex-inject';

export default class StateManager {
Expand All @@ -161,6 +177,7 @@ export default class StateManager {
+stylexViewTransitionClassImport: Set<string> = new Set();
+stylexDefaultMarkerImport: Set<string> = new Set();
+stylexWhenImport: Set<string> = new Set();
+stylexEnvImport: Set<string> = new Set();

injectImportInserted: ?t.Identifier = null;

Expand Down Expand Up @@ -353,6 +370,17 @@ export default class StateManager {
'options.treeshakeCompensation',
);

const envInput: StyleXStateOptions['env'] = z.logAndDefault(
checkEnvOption,
options.env ?? {},
{},
'options.env',
);

const env: StyleXStateOptions['env'] = Object.freeze({
...envInput,
});

const aliasesOption: StyleXOptions['aliases'] = z.logAndDefault(
z.unionOf(
z.nullish(),
Expand Down Expand Up @@ -381,6 +409,7 @@ export default class StateManager {
debug,
definedStylexCSSVariables: {},
dev,
env,
enableDebugClassNames,
enableDebugDataProp,
enableDevClassNames,
Expand Down Expand Up @@ -431,6 +460,29 @@ export default class StateManager {
return null;
}

applyStylexEnv(identifiers: FunctionConfig['identifiers']): void {
const env = this.options.env;
this.stylexImport.forEach((importName) => {
const current = identifiers[importName];
if (
current != null &&
typeof current === 'object' &&
!Array.isArray(current)
) {
if ('fn' in current) {
identifiers[importName] = { env };
} else {
identifiers[importName] = { ...current, env };
}
return;
}
identifiers[importName] = { env };
});
this.stylexEnvImport.forEach((importName) => {
identifiers[importName] = env;
});
}

get canReferenceTheme(): boolean {
return !!this.inStyleXCreate;
// || this.isStyleXDefineVars
Expand Down
6 changes: 6 additions & 0 deletions packages/@stylexjs/babel-plugin/src/visitors/imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ export function readImportDeclarations(
if (importedName === 'defaultMarker') {
state.stylexDefaultMarkerImport.add(localName);
}
if (importedName === 'env') {
state.stylexEnvImport.add(localName);
}
}
}
}
Expand Down Expand Up @@ -173,6 +176,9 @@ export function readRequires(
if (prop.key.name === 'defaultMarker') {
state.stylexDefaultMarkerImport.add(value.name);
}
if (prop.key.name === 'env') {
state.stylexEnvImport.add(value.name);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export default function transformStyleXCreateTheme(
memberExpressions[name].positionTry = { fn: positionTry };
identifiers[name] = { ...(identifiers[name] ?? {}), types };
});
state.applyStylexEnv(identifiers);

const { confident: confident2, value: overrides } = evaluate(
secondArg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export default function transformStyleXCreate(
};
identifiers[name] = { ...(identifiers[name] ?? {}), when: stylexWhen };
});
state.applyStylexEnv(identifiers);

const { confident, value, fns, reason, deopt } = evaluateStyleXCreateArg(
firstArg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type { NodePath } from '@babel/traverse';
import type { FunctionConfig } from '../utils/evaluate-path';

import * as t from '@babel/types';
import { evaluate } from '../utils/evaluate-path';
Expand Down Expand Up @@ -49,7 +50,18 @@ export default function transformStyleXDefineConsts(
const args = callExpressionPath.get('arguments');
const firstArg = args[0];

const { confident, value } = evaluate(firstArg, state);
const evaluatePathFnConfig: FunctionConfig = {
identifiers: {},
memberExpressions: {},
disableImports: true,
};
state.applyStylexEnv(evaluatePathFnConfig.identifiers);

const { confident, value } = evaluate(
firstArg,
state,
evaluatePathFnConfig,
);
if (!confident) {
throw callExpressionPath.buildCodeFrameError(
messages.nonStaticValue('defineConsts'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export default function transformStyleXDefineVars(
memberExpressions[name].positionTry = { fn: positionTry };
identifiers[name] = { ...(identifiers[name] ?? {}), types: stylexTypes };
});
state.applyStylexEnv(identifiers);

const { confident, value } = evaluate(firstArg, state, {
identifiers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default function transformStyleXKeyframes(
}
memberExpressions[name].firstThatWorks = { fn: stylexFirstThatWorks };
});
state.applyStylexEnv(identifiers);

const { confident, value } = evaluate(firstArg, state, {
identifiers,
Expand Down
Loading
Loading