Skip to content

fix(unplugin): detect pre-compiled StyleX packages and prevent Next.js App Router runtime injection#1695

Open
JhaSourav07 wants to merge 3 commits into
facebook:mainfrom
JhaSourav07:issue1207/fix-css-priority
Open

fix(unplugin): detect pre-compiled StyleX packages and prevent Next.js App Router runtime injection#1695
JhaSourav07 wants to merge 3 commits into
facebook:mainfrom
JhaSourav07:issue1207/fix-css-priority

Conversation

@JhaSourav07
Copy link
Copy Markdown

@JhaSourav07 JhaSourav07 commented Jun 3, 2026

What changed / motivation ?

Addresses inconsistent CSS priority and ordering behavior when importing pre-compiled third-party StyleX libraries, as well as debugging issues caused by runtime style injection in Next.js.

Specifically, this PR introduces the following changes to @stylexjs/unplugin:

  1. Pre-compiled StyleX Package Detection:
    • Scans package.json manifests of project dependencies (and explicitly configured packages) using a recursive traversal of the exports tree to detect pre-compiled CSS files (handles nested targets, subpaths, and conditional configurations).
    • Emits a deduplicated warning per process/package recommending transpilation (transpilePackages) or CSS Layers (useCSSLayers: true).
    • Resolves manifests for explicitPackages even if they are missing from the project's direct root dependencies (e.g. inside monorepos/workspaces).
  2. Next.js Guard:
    • Detects Next.js environments via environment variables (NEXT_RUNTIME, NEXT_PHASE) and webpack package contexts.
    • Blocks runtimeInjection: true within Next.js by throwing in production and console-erroring in development.

Linked PR/Issues

Fixes #1207

Additional Context

  • Unit tests added to packages/@stylexjs/unplugin/__tests__/unplugin.test.js cover:
    • Deduplication of warning logs.
    • Manifest resolution of explicitPackages missing from dependencies.
    • Nested exports scans (exports.style, exports.css, nested conditional exports, and direct subpath string exports).
    • Next.js runtime injection guards.
  • All 22 unit tests for @stylexjs/unplugin are fully passing (yarn workspace @stylexjs/unplugin test).

Pre-flight checklist

Copilot AI review requested due to automatic review settings June 3, 2026 09:28
@JhaSourav07 JhaSourav07 requested review from mellyeliu and nmn as code owners June 3, 2026 09:28
@meta-cla
Copy link
Copy Markdown

meta-cla Bot commented Jun 3, 2026

Hi @JhaSourav07!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 3, 2026

@JhaSourav07 is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds runtime diagnostics to help users avoid CSS ordering conflicts when consuming StyleX libraries that ship precompiled CSS, and blocks an unsafe config combination in Next.js environments.

Changes:

  • Detect StyleX dependencies that publish precompiled CSS and emit a detailed warning listing affected packages.
  • Add a guard that errors/throws when runtimeInjection is enabled in a detected Next.js App Router environment.
  • Add Jest tests covering the new warning behavior and the runtimeInjection + Next guard.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/@stylexjs/unplugin/src/core.js Detects precompiled CSS in StyleX deps, changes package discovery to return {name, precompiled}, prints warning, and adds the Next + runtimeInjection guard.
packages/@stylexjs/unplugin/tests/unplugin.test.js Adds tests for precompiled-package warning scenarios and for the Next + runtimeInjection guard behavior.

Comment thread packages/@stylexjs/unplugin/src/core.js Outdated
Comment on lines +211 to +217
const found = new Map((explicitPackages || []).map((name) => [name, false]));

const pkgJsonPath = findNearestPackageJson(rootDir);
if (!pkgJsonPath) return Array.from(found);
if (!pkgJsonPath) return mapToPackageInfos(found);
const pkgDir = path.dirname(pkgJsonPath);
const pkgJson = readJSON(pkgJsonPath);
if (!pkgJson) return Array.from(found);
if (!pkgJson) return mapToPackageInfos(found);
Comment on lines +319 to +341
if (precompiledPackages.length > 0) {
const packageList = precompiledPackages.map((p) => ` • ${p}`).join('\n');
console.warn(`
[StyleX] ⚠️ Potential CSS ordering issue detected.

The following packages use StyleX and ship pre-compiled CSS:
${packageList}

Because these packages are not being re-compiled by the StyleX plugin, both
the library CSS and your app-local CSS are emitted as separate files. They
share the same atomic class names (e.g. .xuxw1ft for white-space:nowrap) but
your app's CSS is loaded last and therefore unconditionally overrides the
library's CSS for any colliding rule — regardless of intended priority.

Recommended fixes (pick one):
1. Publish the library without pre-compiled CSS and add it to Next.js
\`transpilePackages\` so a single compiler handles everything.
2. Enable \`useCSSLayers: true\` in BOTH the library build and this plugin
so @layer order governs priority instead of stylesheet load order.
3. Ensure the library <link> appears after your app <link> in <head>
(workaround only — this is fragile and not recommended long-term).
`);
}
Comment thread packages/@stylexjs/unplugin/src/core.js Outdated
Comment on lines +174 to +179
const conditions = [
exports['.'],
...Object.values(exports).filter(
(v) => v !== null && typeof v === 'object',
),
];
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 3, 2026
@JhaSourav07 JhaSourav07 requested a review from Copilot June 3, 2026 09:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

Comment on lines +165 to +190
function hasPrecompiledCss(manifest) {
if (!manifest || typeof manifest !== 'object') return false;

if (typeof manifest.style === 'string' && manifest.style.endsWith('.css')) {
return true;
}

const { exports } = manifest;
if (exports && typeof exports === 'object') {
if (
(typeof exports.style === 'string' && exports.style.endsWith('.css')) ||
(typeof exports.css === 'string' && exports.css.endsWith('.css'))
) {
return true;
}
for (const val of Object.values(exports)) {
if (val && typeof val === 'object' && !Array.isArray(val)) {
if (
(typeof val.style === 'string' && val.style.endsWith('.css')) ||
(typeof val.css === 'string' && val.css.endsWith('.css'))
) {
return true;
}
}
}
}
Comment thread packages/@stylexjs/unplugin/src/core.js Outdated
Comment on lines +362 to +372
const isNextAppRouter =
!!process.env.NEXT_RUNTIME ||
!!process.env.NEXT_PHASE ||
(framework === 'webpack' &&
!!(
process.env.npm_package_dependencies_next ||
process.env.npm_package_devDependencies_next
));

if (userOptions.runtimeInjection && isNextAppRouter) {
const msg = `[StyleX] ❌ \`runtimeInjection\` must not be used with the Next.js App Router.
}
});

test('warns when a StyleX dep ships CSS via exports.style', () => {
}
});

test('warns when a StyleX dep ships CSS via exports.css', () => {
@JhaSourav07 JhaSourav07 requested a review from Copilot June 3, 2026 10:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

packages/@stylexjs/unplugin/src/core.js:1

  • The warning text includes a Next.js-specific recommendation (transpilePackages) even when the plugin runs under non-Next frameworks (e.g. Rollup/Vite). This can be misleading for those users; consider tailoring the recommendations based on isNext / framework (e.g., show a bundler-agnostic “ensure the library is compiled by the same StyleX pipeline” tip, and only mention transpilePackages when isNext is true).
/**

Comment on lines +343 to +347
const warnedSet = (globalThis.__stylex_warned_packages =
globalThis.__stylex_warned_packages || new Set());
const newWarnPackages = precompiledPackages.filter(
(name) => !warnedSet.has(name),
);
Comment on lines +165 to +170
function hasPrecompiledCss(manifest) {
if (!manifest || typeof manifest !== 'object') return false;

if (typeof manifest.style === 'string' && manifest.style.endsWith('.css')) {
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inconsistent priority/ordering when using stylex based 3rd party library

2 participants