From ba340a97aa7f070c68cf50395fc00c71b2c6e718 Mon Sep 17 00:00:00 2001 From: Naman Goel Date: Sun, 11 Jan 2026 19:50:14 -0800 Subject: [PATCH 1/6] [WIP] Bun bundle support --- examples/example-bun/.eslintrc.js | 18 +++++ examples/example-bun/README.md | 73 ++++++++++++++++++ examples/example-bun/bunfig.toml | 2 + examples/example-bun/package.json | 24 ++++++ examples/example-bun/scripts/build.mjs | 56 ++++++++++++++ examples/example-bun/scripts/dev.mjs | 36 +++++++++ examples/example-bun/scripts/start.mjs | 33 ++++++++ .../example-bun/scripts/stylex-plugin.mjs | 77 +++++++++++++++++++ examples/example-bun/src/App.jsx | 40 ++++++++++ examples/example-bun/src/global.css | 3 + .../example-bun/src/globalTokens.stylex.js | 23 ++++++ examples/example-bun/src/index.dev.html | 21 +++++ examples/example-bun/src/index.html | 21 +++++ examples/example-bun/src/main.jsx | 13 ++++ packages/@stylexjs/unplugin/src/index.js | 6 +- 15 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 examples/example-bun/.eslintrc.js create mode 100644 examples/example-bun/README.md create mode 100644 examples/example-bun/bunfig.toml create mode 100644 examples/example-bun/package.json create mode 100644 examples/example-bun/scripts/build.mjs create mode 100644 examples/example-bun/scripts/dev.mjs create mode 100644 examples/example-bun/scripts/start.mjs create mode 100644 examples/example-bun/scripts/stylex-plugin.mjs create mode 100644 examples/example-bun/src/App.jsx create mode 100644 examples/example-bun/src/global.css create mode 100644 examples/example-bun/src/globalTokens.stylex.js create mode 100644 examples/example-bun/src/index.dev.html create mode 100644 examples/example-bun/src/index.html create mode 100644 examples/example-bun/src/main.jsx diff --git a/examples/example-bun/.eslintrc.js b/examples/example-bun/.eslintrc.js new file mode 100644 index 000000000..6610adfd9 --- /dev/null +++ b/examples/example-bun/.eslintrc.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * + */ + +module.exports = { + plugins: ['@stylexjs'], + rules: { + '@stylexjs/valid-styles': 'error', + 'ft-flow/space-after-type-colon': 0, + 'ft-flow/no-types-missing-file-annotation': 0, + 'ft-flow/generic-spacing': 0, + }, +}; diff --git a/examples/example-bun/README.md b/examples/example-bun/README.md new file mode 100644 index 000000000..9a25a3519 --- /dev/null +++ b/examples/example-bun/README.md @@ -0,0 +1,73 @@ +# StyleX with Bun + +This example bundles a React app with Bun while compiling StyleX via +`@stylexjs/unplugin`. The unplugin uses its esbuild adapter (Bun’s plugin API is +esbuild-compatible) to compile StyleX at build time, aggregate the generated +styles, and append them to the CSS asset emitted from `src/global.css`. + +### Prerequisites + +- Bun 1.1+ +- `@stylexjs/unplugin` + +## Install dependencies + +```bash +npm install +``` + +## Build script (`scripts/build.mjs`) + +The production build script wires the unplugin into `Bun.build`: + +```js +import stylex from '@stylexjs/unplugin'; + +await Bun.build({ + entrypoints: ['src/main.jsx'], + outdir: 'dist', + metafile: true, + plugins: [ + stylex.esbuild({ + useCSSLayers: true, + importSources: ['@stylexjs/stylex'], + }), + ], +}); +``` + +- `metafile: true` lets the plugin locate CSS assets emitted by Bun. +- `useCSSLayers: true` wraps StyleX output in `@layer` declarations for + predictable specificity. + +## CSS entry points + +- Production uses `src/global.css` so Bun emits a CSS asset; StyleX appends + aggregated styles during `npm run example:build`. +- Development writes `dist/stylex.dev.css`, which the Bun plugin rewrites on + every StyleX transform so the dev server can hot-reload CSS. + +## Dev server integration + +The Bun dev server uses `bunfig.toml` to load a Bun plugin that wraps +`@stylexjs/unplugin` and writes generated StyleX rules into +`dist/stylex.dev.css` (marked with `--stylex-injection`). + +`dist/` is generated output and should remain gitignored. + +## Commands + +```bash +# Production bundle + StyleX CSS extraction +npm run example:build + +# Bun dev server with HMR at http://localhost:3000 +npm run example:dev + +# Serve the generated bundle (run example:build first) +npm run example:start +``` + +The dev server uses Bun's fullstack mode, bundling `dist/index.dev.html` on +request with `development: true` and the StyleX Bun plugin. Production builds +write `dist/index.html` alongside the bundled assets. diff --git a/examples/example-bun/bunfig.toml b/examples/example-bun/bunfig.toml new file mode 100644 index 000000000..fc71603d7 --- /dev/null +++ b/examples/example-bun/bunfig.toml @@ -0,0 +1,2 @@ +[serve.static] +plugins = ["./scripts/stylex-plugin.mjs"] diff --git a/examples/example-bun/package.json b/examples/example-bun/package.json new file mode 100644 index 000000000..0828505bf --- /dev/null +++ b/examples/example-bun/package.json @@ -0,0 +1,24 @@ +{ + "private": true, + "name": "example-bun", + "version": "0.17.4", + "description": "Example: StyleX with Bun via @stylexjs/unplugin", + "main": "src/App.jsx", + "scripts": { + "example:build": "bun run scripts/build.mjs", + "example:dev": "bun run scripts/dev.mjs", + "example:start": "bun run scripts/start.mjs", + "example:lint": "eslint \"**/*.{js,jsx}\"" + }, + "license": "MIT", + "dependencies": { + "@stylexjs/stylex": "0.17.4", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@stylexjs/unplugin": "0.17.4", + "@stylexjs/eslint-plugin": "0.17.4", + "eslint": "^8.57.1" + } +} diff --git a/examples/example-bun/scripts/build.mjs b/examples/example-bun/scripts/build.mjs new file mode 100644 index 000000000..304cfc92f --- /dev/null +++ b/examples/example-bun/scripts/build.mjs @@ -0,0 +1,56 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import { fileURLToPath } from 'url'; +import stylex from '@stylexjs/unplugin'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const entrypoint = path.resolve(__dirname, '..', 'src', 'main.jsx'); +const outdir = path.resolve(__dirname, '..', 'dist'); +const htmlTemplate = path.resolve(__dirname, '..', 'src', 'index.html'); +const htmlOutput = path.resolve(outdir, 'index.html'); + +async function build() { + const result = await Bun.build({ + entrypoints: [entrypoint], + outdir, + target: 'browser', + minify: true, + metafile: true, + plugins: [ + stylex.esbuild({ + useCSSLayers: true, + importSources: ['@stylexjs/stylex'], + unstable_moduleResolution: { + type: 'commonJS', + rootDir: path.resolve(__dirname, '../../..'), + }, + }), + ], + }); + + if (!result.success) { + console.error('Bun build failed.'); + for (const message of result.logs) { + console.error(message); + } + process.exit(1); + } + + try { + await Bun.write(htmlOutput, await Bun.file(htmlTemplate).text()); + } catch (error) { + console.error('Failed to write dist/index.html.'); + console.error(error); + process.exit(1); + } +} + +build(); diff --git a/examples/example-bun/scripts/dev.mjs b/examples/example-bun/scripts/dev.mjs new file mode 100644 index 000000000..f17e6e578 --- /dev/null +++ b/examples/example-bun/scripts/dev.mjs @@ -0,0 +1,36 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import { mkdir } from 'fs/promises'; +import { fileURLToPath, pathToFileURL } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const distDir = path.resolve(__dirname, '..', 'dist'); +const htmlTemplate = path.resolve(__dirname, '..', 'src', 'index.dev.html'); +const htmlOutput = path.resolve(distDir, 'index.dev.html'); + +async function dev() { + await mkdir(distDir, { recursive: true }); + await Bun.write(htmlOutput, await Bun.file(htmlTemplate).text()); + const homepage = (await import(pathToFileURL(htmlOutput).href)).default; + + const port = Number(process.env.PORT) || 3000; + + const server = Bun.serve({ + port, + development: true, + routes: { + '/': homepage, + }, + }); + + console.log(`Dev server running at http://localhost:${server.port}`); +} +dev(); diff --git a/examples/example-bun/scripts/start.mjs b/examples/example-bun/scripts/start.mjs new file mode 100644 index 000000000..5e5283cc7 --- /dev/null +++ b/examples/example-bun/scripts/start.mjs @@ -0,0 +1,33 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const distDir = path.resolve(__dirname, '..', 'dist'); +const port = Number(process.env.PORT) || 3000; + +const server = Bun.serve({ + port, + async fetch(request) { + const url = new URL(request.url); + const pathname = url.pathname === '/' ? '/index.html' : url.pathname; + const filePath = path.join(distDir, pathname); + const file = Bun.file(filePath); + + if (await file.exists()) { + return new Response(file); + } + + return new Response('Not found', { status: 404 }); + }, +}); + +console.log(`Server running at http://localhost:${server.port}`); diff --git a/examples/example-bun/scripts/stylex-plugin.mjs b/examples/example-bun/scripts/stylex-plugin.mjs new file mode 100644 index 000000000..6648468b6 --- /dev/null +++ b/examples/example-bun/scripts/stylex-plugin.mjs @@ -0,0 +1,77 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import { mkdir } from 'fs/promises'; +import stylex from '@stylexjs/unplugin'; + +const rawPlugin = stylex.raw({ + dev: true, + useCSSLayers: true, + importSources: ['@stylexjs/stylex'], + runtimeInjection: false, + unstable_moduleResolution: { + type: 'commonJS', + rootDir: process.cwd(), + }, +}); + +const STYLEX_MARKER = '--stylex-injection'; +const STYLEX_DEV_CSS_PATH = path.join(process.cwd(), 'dist', 'stylex.dev.css'); +let lastCssOutput = null; + +const writeStylexCss = async () => { + const css = rawPlugin.__stylexCollectCss?.() || ''; + const next = css + ? `:root { ${STYLEX_MARKER}: 0; }\n${css}` + : `:root { ${STYLEX_MARKER}: 0; }`; + if (next === lastCssOutput) return; + lastCssOutput = next; + await mkdir(path.dirname(STYLEX_DEV_CSS_PATH), { recursive: true }); + await Bun.write(STYLEX_DEV_CSS_PATH, next); +}; + +const loaders = { + '.js': 'js', + '.jsx': 'jsx', + '.ts': 'ts', + '.tsx': 'tsx', +}; + +export default { + name: '@stylexjs/unplugin-bun', + setup(build) { + build.onStart(() => { + rawPlugin.__stylexResetState?.(); + rawPlugin.buildStart?.(); + writeStylexCss(); + }); + + build.onLoad({ filter: /\.[cm]?[jt]sx?(\?|$)/ }, async (args) => { + if (args.path.includes('node_modules')) return; + const code = await Bun.file(args.path).text(); + const result = await rawPlugin.transform?.(code, args.path); + + if (!result || !result.code) { + return { + contents: code, + loader: loaders[path.extname(args.path)] || 'js', + }; + } + + await writeStylexCss(); + return { + contents: result.code, + loader: loaders[path.extname(args.path)] || 'js', + }; + }); + + build.onEnd(() => { + rawPlugin.buildEnd?.(); + }); + }, +}; diff --git a/examples/example-bun/src/App.jsx b/examples/example-bun/src/App.jsx new file mode 100644 index 000000000..362bf4e69 --- /dev/null +++ b/examples/example-bun/src/App.jsx @@ -0,0 +1,40 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import * as stylex from '@stylexjs/stylex'; +import { colors, fonts, sizes } from './globalTokens.stylex'; + +export default function App() { + return ( +
+
+ StyleX + Bun + unplugin +
+
+ ); +} + +const styles = stylex.create({ + main: { + minHeight: '100vh', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: colors.gray0, + }, + card: { + backgroundColor: colors.blue9, + padding: sizes.spacing5, + borderRadius: sizes.spacing2, + justifyContent: 'center', + display: 'flex', + alignItems: 'center', + color: colors.gray0, + fontFamily: fonts.mono, + }, +}); diff --git a/examples/example-bun/src/global.css b/examples/example-bun/src/global.css new file mode 100644 index 000000000..5d7a0446a --- /dev/null +++ b/examples/example-bun/src/global.css @@ -0,0 +1,3 @@ +:root { + --stylex-injection: 0; +} diff --git a/examples/example-bun/src/globalTokens.stylex.js b/examples/example-bun/src/globalTokens.stylex.js new file mode 100644 index 000000000..20dc2f7bb --- /dev/null +++ b/examples/example-bun/src/globalTokens.stylex.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as stylex from '@stylexjs/stylex'; + +export const colors = stylex.defineVars({ + pink7: '#d6336c', + blue9: '#1864ab', + gray0: '#999', +}); + +export const fonts = stylex.defineVars({ + mono: 'Dank Mono,Operator Mono,Inconsolata,Fira Mono,ui-monospace,SF Mono,Monaco,Droid Sans Mono,Source Code Pro,monospace', +}); + +export const sizes = stylex.defineVars({ + spacing5: '1.5rem', + spacing2: '.5rem', +}); diff --git a/examples/example-bun/src/index.dev.html b/examples/example-bun/src/index.dev.html new file mode 100644 index 000000000..1b1dd2bb6 --- /dev/null +++ b/examples/example-bun/src/index.dev.html @@ -0,0 +1,21 @@ + + + + @stylexjs/unplugin (bun dev) + + + + + +
+ + + diff --git a/examples/example-bun/src/index.html b/examples/example-bun/src/index.html new file mode 100644 index 000000000..4c4f87f5d --- /dev/null +++ b/examples/example-bun/src/index.html @@ -0,0 +1,21 @@ + + + + @stylexjs/unplugin (bun) + + + + + +
+ + + diff --git a/examples/example-bun/src/main.jsx b/examples/example-bun/src/main.jsx new file mode 100644 index 000000000..de8580899 --- /dev/null +++ b/examples/example-bun/src/main.jsx @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import './global.css'; +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App.jsx'; + +createRoot(document.getElementById('root')).render(); diff --git a/packages/@stylexjs/unplugin/src/index.js b/packages/@stylexjs/unplugin/src/index.js index 74fc3adb6..52c9b9326 100644 --- a/packages/@stylexjs/unplugin/src/index.js +++ b/packages/@stylexjs/unplugin/src/index.js @@ -399,7 +399,7 @@ const unpluginInstance = createUnplugin((userOptions = {}, metaOptions) => { // No rollup-style virtual module normalize for webpack/rspack stability - return { + const plugin = { name: '@stylexjs/unplugin', apply: (config, env) => { try { @@ -835,6 +835,10 @@ const unpluginInstance = createUnplugin((userOptions = {}, metaOptions) => { // No-op closeBundle; all file writes handled in writeBundle }; + + plugin.__stylexCollectCss = collectCss; + plugin.__stylexResetState = resetState; + return plugin; }); export default unpluginInstance; From 8423cc2f4bd7aa4796a0e4eaea28ca61e6c9330f Mon Sep 17 00:00:00 2001 From: Naman Goel Date: Mon, 12 Jan 2026 23:50:22 -0800 Subject: [PATCH 2/6] [WIP] Refactor unplugin package --- examples/example-bun/README.md | 6 +- examples/example-bun/bunfig.toml | 2 +- .../example-bun/scripts/stylex-plugin.mjs | 77 -- examples/example-esbuild/public/index.html | 2 +- examples/example-esbuild/src/App.jsx | 4 +- examples/example-rollup/src/App.js | 16 +- examples/example-rspack/src/App.jsx | 16 +- examples/example-vite-react/src/App.tsx | 108 ++- examples/example-vite-react/vite.config.ts | 1 - examples/example-vite/src/App.jsx | 22 +- examples/example-waku/src/pages.gen.ts | 9 +- examples/example-webpack/src/App.jsx | 63 +- packages/@stylexjs/unplugin/README.md | 64 +- .../babel-plugins/add-mjs-extension.js | 9 + packages/@stylexjs/unplugin/package.json | 53 +- packages/@stylexjs/unplugin/src/bun.d.ts | 16 + packages/@stylexjs/unplugin/src/bun.js | 69 ++ packages/@stylexjs/unplugin/src/core.d.ts | 27 + packages/@stylexjs/unplugin/src/core.js | 500 ++++++++++ packages/@stylexjs/unplugin/src/esbuild.d.ts | 12 + packages/@stylexjs/unplugin/src/esbuild.js | 80 ++ packages/@stylexjs/unplugin/src/farm.d.ts | 12 + packages/@stylexjs/unplugin/src/farm.js | 12 + packages/@stylexjs/unplugin/src/index.d.ts | 31 +- packages/@stylexjs/unplugin/src/index.js | 875 +----------------- packages/@stylexjs/unplugin/src/rolldown.d.ts | 12 + packages/@stylexjs/unplugin/src/rolldown.js | 15 + packages/@stylexjs/unplugin/src/rollup.d.ts | 12 + packages/@stylexjs/unplugin/src/rollup.js | 90 ++ packages/@stylexjs/unplugin/src/rspack.d.ts | 12 + packages/@stylexjs/unplugin/src/rspack.js | 15 + packages/@stylexjs/unplugin/src/unloader.d.ts | 12 + packages/@stylexjs/unplugin/src/unloader.js | 12 + packages/@stylexjs/unplugin/src/vite.d.ts | 12 + packages/@stylexjs/unplugin/src/vite.js | 213 +++++ packages/@stylexjs/unplugin/src/webpack.d.ts | 12 + packages/@stylexjs/unplugin/src/webpack.js | 68 ++ 37 files changed, 1504 insertions(+), 1067 deletions(-) delete mode 100644 examples/example-bun/scripts/stylex-plugin.mjs create mode 100644 packages/@stylexjs/unplugin/src/bun.d.ts create mode 100644 packages/@stylexjs/unplugin/src/bun.js create mode 100644 packages/@stylexjs/unplugin/src/core.d.ts create mode 100644 packages/@stylexjs/unplugin/src/core.js create mode 100644 packages/@stylexjs/unplugin/src/esbuild.d.ts create mode 100644 packages/@stylexjs/unplugin/src/esbuild.js create mode 100644 packages/@stylexjs/unplugin/src/farm.d.ts create mode 100644 packages/@stylexjs/unplugin/src/farm.js create mode 100644 packages/@stylexjs/unplugin/src/rolldown.d.ts create mode 100644 packages/@stylexjs/unplugin/src/rolldown.js create mode 100644 packages/@stylexjs/unplugin/src/rollup.d.ts create mode 100644 packages/@stylexjs/unplugin/src/rollup.js create mode 100644 packages/@stylexjs/unplugin/src/rspack.d.ts create mode 100644 packages/@stylexjs/unplugin/src/rspack.js create mode 100644 packages/@stylexjs/unplugin/src/unloader.d.ts create mode 100644 packages/@stylexjs/unplugin/src/unloader.js create mode 100644 packages/@stylexjs/unplugin/src/vite.d.ts create mode 100644 packages/@stylexjs/unplugin/src/vite.js create mode 100644 packages/@stylexjs/unplugin/src/webpack.d.ts create mode 100644 packages/@stylexjs/unplugin/src/webpack.js diff --git a/examples/example-bun/README.md b/examples/example-bun/README.md index 9a25a3519..04016d284 100644 --- a/examples/example-bun/README.md +++ b/examples/example-bun/README.md @@ -49,9 +49,9 @@ await Bun.build({ ## Dev server integration -The Bun dev server uses `bunfig.toml` to load a Bun plugin that wraps -`@stylexjs/unplugin` and writes generated StyleX rules into -`dist/stylex.dev.css` (marked with `--stylex-injection`). +The Bun dev server uses `bunfig.toml` with `@stylexjs/unplugin/bun`, which +writes generated StyleX rules into `dist/stylex.dev.css` (marked with +`--stylex-injection`). `dist/` is generated output and should remain gitignored. diff --git a/examples/example-bun/bunfig.toml b/examples/example-bun/bunfig.toml index fc71603d7..0abe92cde 100644 --- a/examples/example-bun/bunfig.toml +++ b/examples/example-bun/bunfig.toml @@ -1,2 +1,2 @@ [serve.static] -plugins = ["./scripts/stylex-plugin.mjs"] +plugins = ["@stylexjs/unplugin/bun"] diff --git a/examples/example-bun/scripts/stylex-plugin.mjs b/examples/example-bun/scripts/stylex-plugin.mjs deleted file mode 100644 index 6648468b6..000000000 --- a/examples/example-bun/scripts/stylex-plugin.mjs +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path'; -import { mkdir } from 'fs/promises'; -import stylex from '@stylexjs/unplugin'; - -const rawPlugin = stylex.raw({ - dev: true, - useCSSLayers: true, - importSources: ['@stylexjs/stylex'], - runtimeInjection: false, - unstable_moduleResolution: { - type: 'commonJS', - rootDir: process.cwd(), - }, -}); - -const STYLEX_MARKER = '--stylex-injection'; -const STYLEX_DEV_CSS_PATH = path.join(process.cwd(), 'dist', 'stylex.dev.css'); -let lastCssOutput = null; - -const writeStylexCss = async () => { - const css = rawPlugin.__stylexCollectCss?.() || ''; - const next = css - ? `:root { ${STYLEX_MARKER}: 0; }\n${css}` - : `:root { ${STYLEX_MARKER}: 0; }`; - if (next === lastCssOutput) return; - lastCssOutput = next; - await mkdir(path.dirname(STYLEX_DEV_CSS_PATH), { recursive: true }); - await Bun.write(STYLEX_DEV_CSS_PATH, next); -}; - -const loaders = { - '.js': 'js', - '.jsx': 'jsx', - '.ts': 'ts', - '.tsx': 'tsx', -}; - -export default { - name: '@stylexjs/unplugin-bun', - setup(build) { - build.onStart(() => { - rawPlugin.__stylexResetState?.(); - rawPlugin.buildStart?.(); - writeStylexCss(); - }); - - build.onLoad({ filter: /\.[cm]?[jt]sx?(\?|$)/ }, async (args) => { - if (args.path.includes('node_modules')) return; - const code = await Bun.file(args.path).text(); - const result = await rawPlugin.transform?.(code, args.path); - - if (!result || !result.code) { - return { - contents: code, - loader: loaders[path.extname(args.path)] || 'js', - }; - } - - await writeStylexCss(); - return { - contents: result.code, - loader: loaders[path.extname(args.path)] || 'js', - }; - }); - - build.onEnd(() => { - rawPlugin.buildEnd?.(); - }); - }, -}; diff --git a/examples/example-esbuild/public/index.html b/examples/example-esbuild/public/index.html index a6839b505..ca998aaaa 100644 --- a/examples/example-esbuild/public/index.html +++ b/examples/example-esbuild/public/index.html @@ -12,7 +12,7 @@ } } - +
diff --git a/examples/example-esbuild/src/App.jsx b/examples/example-esbuild/src/App.jsx index d44868869..748c4f16b 100644 --- a/examples/example-esbuild/src/App.jsx +++ b/examples/example-esbuild/src/App.jsx @@ -11,7 +11,7 @@ import './global.css'; import * as React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import * as stylex from '@stylexjs/stylex'; import { colors, fonts, sizes } from './globalTokens.stylex'; @@ -46,4 +46,4 @@ function App() { ); } -ReactDOM.render(, document.getElementById('root')); +createRoot(document.getElementById('root')).render(); diff --git a/examples/example-rollup/src/App.js b/examples/example-rollup/src/App.js index 227e4b0fe..5563f41ed 100644 --- a/examples/example-rollup/src/App.js +++ b/examples/example-rollup/src/App.js @@ -9,6 +9,14 @@ import * as stylex from '@stylexjs/stylex'; +export default function App() { + return ( +
+
Content
+
+ ); +} + const styles = stylex.create({ main: { width: '100vw', @@ -29,11 +37,3 @@ const styles = stylex.create({ fontFamily: 'Arial', }, }); - -export default function App() { - return ( -
-
Content
-
- ); -} diff --git a/examples/example-rspack/src/App.jsx b/examples/example-rspack/src/App.jsx index d9ef80eed..5acf7b493 100644 --- a/examples/example-rspack/src/App.jsx +++ b/examples/example-rspack/src/App.jsx @@ -7,6 +7,14 @@ import * as stylex from '@stylexjs/stylex'; +export default function App() { + return ( +
+

StyleX + Rspack + unplugin

+
+ ); +} + const styles = stylex.create({ app: { minHeight: '100vh', @@ -20,11 +28,3 @@ const styles = stylex.create({ fontWeight: 700, }, }); - -export default function App() { - return ( -
-

StyleX + Rspack + unplugin

-
- ); -} diff --git a/examples/example-vite-react/src/App.tsx b/examples/example-vite-react/src/App.tsx index 29e13fc75..e95bd77e8 100644 --- a/examples/example-vite-react/src/App.tsx +++ b/examples/example-vite-react/src/App.tsx @@ -11,6 +11,59 @@ import * as stylex from '@stylexjs/stylex'; import { Button } from '@stylexjs/shared-ui'; import { tokens } from '@stylexjs/shared-ui/tokens.stylex'; +export default function App() { + const [count, setCount] = useState(0); + + return ( +
+
+ +

Vite + React

+
+ + +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+
+
+ ); +} + const spin = stylex.keyframes({ from: { transform: 'rotate(0deg)' }, to: { transform: 'rotate(360deg)' }, @@ -79,58 +132,3 @@ const styles = stylex.create({ outline: { ':focus-visible': '4px auto -webkit-focus-ring-color' }, }, }); - -function App() { - const [count, setCount] = useState(0); - - return ( -
-
- -

Vite + React

-
- - -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

-
-
- ); -} - -export default App; diff --git a/examples/example-vite-react/vite.config.ts b/examples/example-vite-react/vite.config.ts index 8d5a98c59..b1ddc3a24 100644 --- a/examples/example-vite-react/vite.config.ts +++ b/examples/example-vite-react/vite.config.ts @@ -10,6 +10,5 @@ import stylex from '@stylexjs/unplugin'; // https://vite.dev/config/ export default defineConfig({ - // @ts-expect-error - ignore for now plugins: [stylex.vite(), react({})], }); diff --git a/examples/example-vite/src/App.jsx b/examples/example-vite/src/App.jsx index 08b55e346..638d927c5 100644 --- a/examples/example-vite/src/App.jsx +++ b/examples/example-vite/src/App.jsx @@ -9,6 +9,17 @@ import * as stylex from '@stylexjs/stylex'; import { Button } from '@stylexjs/shared-ui'; import { tokens } from '@stylexjs/shared-ui/tokens.stylex'; +export default function App() { + return ( +
+

StyleX + Vite + unplugin

+ +
+ ); +} + const styles = stylex.create({ app: { minHeight: '100%', @@ -21,14 +32,3 @@ const styles = stylex.create({ fontWeight: 700, }, }); - -export default function App() { - return ( -
-

StyleX + Vite + unplugin

- -
- ); -} diff --git a/examples/example-waku/src/pages.gen.ts b/examples/example-waku/src/pages.gen.ts index 723661e4c..388f6f4fe 100644 --- a/examples/example-waku/src/pages.gen.ts +++ b/examples/example-waku/src/pages.gen.ts @@ -1,9 +1,6 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ +// deno-fmt-ignore-file +// biome-ignore format: generated types do not need formatting +// prettier-ignore import type { PathsForPages, GetConfigResponse } from 'waku/router'; // prettier-ignore diff --git a/examples/example-webpack/src/App.jsx b/examples/example-webpack/src/App.jsx index 44d51d679..6bea0aa27 100644 --- a/examples/example-webpack/src/App.jsx +++ b/examples/example-webpack/src/App.jsx @@ -13,6 +13,41 @@ import { colors } from './tokens.stylex'; import './app.css'; import CtaButton from './components/CTAButton'; +export default function App() { + return ( +
+ +

Webpack + StyleX

+
+ + Get Started + + + Thinking in StyleX + +
+
+ ); +} + const styles = stylex.create({ main: { padding: '2rem', @@ -34,7 +69,7 @@ const styles = stylex.create({ ':hover': 'drop-shadow(0 0 2em #646cffaa)', }, willChange: 'filter', - height: '6em' , + height: '6em', }, header: { color: colors.textPrimary, @@ -46,29 +81,3 @@ const styles = stylex.create({ display: 'flex', }, }); - -const App = () => { - return ( -
- -

Webpack + StyleX

-
- - Get Started - - - Thinking in StyleX - -
-
- ) -} - -export default App \ No newline at end of file diff --git a/packages/@stylexjs/unplugin/README.md b/packages/@stylexjs/unplugin/README.md index 50f498ca1..1a4e005de 100644 --- a/packages/@stylexjs/unplugin/README.md +++ b/packages/@stylexjs/unplugin/README.md @@ -1,10 +1,15 @@ # @stylexjs/unplugin -Universal bundler plugin for StyleX built on top of `unplugin`. It compiles StyleX at build time, aggregates CSS from all transformed modules, and appends the result into an existing CSS asset produced by your bundler (or emits a stable fallback when none exists). +Universal bundler plugin for StyleX built on top of `unplugin`. It compiles +StyleX at build time, aggregates CSS from all transformed modules, and appends +the result into an existing CSS asset produced by your bundler (or emits a +stable fallback when none exists). - Adapters for Vite/Rollup, Webpack/Rspack, and esbuild. - Designed to keep StyleX output consolidated and deterministic. -- Dev helpers expose virtual modules for hot CSS reloads: `virtual:stylex:runtime` (JS) and `/virtual:stylex.css` (CSS) or `virtual:stylex:css-only` (JS shim). +- Dev helpers expose virtual modules for hot CSS reloads: + `virtual:stylex:runtime` (JS) and `/virtual:stylex.css` (CSS) or + `virtual:stylex:css-only` (JS shim). ## Install @@ -14,6 +19,10 @@ npm i -D @stylexjs/unplugin ## Usage by bundler +Bundler-specific entrypoints are available (for example, +`@stylexjs/unplugin/vite`, `@stylexjs/unplugin/webpack`, +`@stylexjs/unplugin/esbuild`, and `@stylexjs/unplugin/bun`). + ### Vite ```ts @@ -33,10 +42,20 @@ export default defineConfig({ ``` Notes: -- The plugin auto-discovers installed packages that depend on `@stylexjs/stylex` (or any configured `importSources`) and excludes them from `optimizeDeps`/`ssr.optimizeDeps` so their StyleX code is transformed. Use `externalPackages` to force-deopt additional deps. -- `devMode: 'full'` injects a lightweight runtime that refetches the dev CSS endpoint on HMR. `css-only` serves just the CSS endpoint. `off` disables dev middleware/virtual modules. -- In dev, inject the virtual CSS + runtime from your HTML shell. If a `