@sanity/vanilla-extract-rolldown-plugin
v0.4.8
Published
rolldown plugin for vanilla-extract that extracts CSS into a lightningcss-optimized file, modeled after @tsdown/css
Readme
@sanity/vanilla-extract-rolldown-plugin
A rolldown plugin for vanilla-extract,
built for bundling libraries that ship pre-extracted CSS. Unlike
@vanilla-extract/rollup-plugin
it doesn't declare rollup as a peer dependency, so it doesn't pull a second bundler into
rolldown projects. It also declares
plugin hook filters, so rolldown skips
the Rust ↔ JS roundtrip for modules that aren't vanilla-extract related
(vanilla-extract#1641).
Head-to-head numbers against the official Rollup pipeline (across minify/target variants) live in
the vanilla-extract benchmarks.
The plugin compiles all .css.ts modules and extracts their CSS into a single file (bundle.css
by default), optionally lowered and minified with lightningcss,
following the same architecture (and option vocabulary and defaults) as
@tsdown/css.
Two independent options control what happens to the extracted CSS, both disabled by default like
css.inject in @tsdown/css:
injectprepends an import of the CSS to every entry chunk that uses vanilla-extract styles, through rolldown's native magic-string, so sourcemaps stay intact.exportspublishes the CSS as the"./bundle.css"export subpath of the package. Any injected import then uses the self-referential"<pkg>/bundle.css"bare specifier instead of a relative path.
exports: true declares a plain "./bundle.css": "./dist/bundle.css" export, which is enough for
packages that only ever run in browsers or bundlers. exports: {nodeCompat: true} declares a
conditional export instead, and emits a no-op bundle-css.js shim (plus bundle-css.d.ts for the
export's types condition) for its node/default conditions to point at, so the subpath stays
resolvable in runtimes that cannot import .css files. The shim is named with a hyphen
(bundle-css.js) rather than a .css.js suffix so it does not match vanilla-extract's
cssFileFilter.
Writing the export to package.json is the host tool's job — with tsdown,
@sanity/vanilla-extract-tsdown-plugin
maintains it automatically.
[!NOTE]
inject: {nodeCompat: true}is deprecated. It means{inject: true, exports: {nodeCompat: true}}and still works, with a warning:nodeCompatconfigures how the CSS file is published, not how the import is injected, so it moved toexports.
Usage
pnpm add --save-dev @sanity/vanilla-extract-rolldown-plugin @vanilla-extract/css// rolldown.config.ts
import {vanillaExtractPlugin} from '@sanity/vanilla-extract-rolldown-plugin'
import {defineConfig} from 'rolldown'
export default defineConfig({
input: 'src/index.ts',
plugins: [vanillaExtractPlugin()],
})If you're bundling with tsdown, prefer
@sanity/vanilla-extract-tsdown-plugin:
it wraps this plugin with tsdown's config hooks, defaulting the CSS syntax lowering target to
tsdown's top-level target and writing the conditional "./bundle.css" export to package.json
through tsdown's exports feature. With
@sanity/tsdown-config,
its vanillaExtract option wires all of that up with the defaults most Sanity libraries want.
The extract model is for library builds: from Vite it only makes sense in build-only library
setups (build.rolldownOptions.plugins), not as an application plugin — Vite's dev server never
runs the output-phase hooks the extraction relies on. For Vite 8 apps, use
@sanity/vanilla-extract-vite-plugin,
which feeds the CSS through Vite's own pipeline (with HMR and SSR support) instead.
Options
The options are modeled after the css options of @tsdown/css,
so they feel familiar in a rolldown-based toolchain:
vanillaExtractPlugin({
/**
* Formatting of identifiers (class names, keyframes, CSS vars, etc).
* @defaultValue 'short'
*/
identifiers: 'short',
/**
* Name of the emitted CSS file, like `css.fileName` (which defaults to 'style.css').
* @defaultValue 'bundle.css'
*/
fileName: 'bundle.css',
/**
* Minify the extracted CSS with lightningcss, matching `css.minify`.
* @defaultValue false
*/
minify: false,
/**
* CSS syntax lowering target, in esbuild-style strings like `css.target`. Matching
* `@tsdown/css`, lowering is skipped when no target is configured, or when the targets
* don't include any browsers (e.g. `'node20'`, which speaks to the JS runtime, not the
* browsers the CSS runs in). Set to `false` to disable lowering explicitly.
* (`@sanity/tsdown-config` layers a `@sanity/browserslist-config` default on top for
* browserless targets, through `lightningcss.targets`.)
*/
target: 'chrome90',
/**
* Options passed through to lightningcss's `transform()`, like `css.lightningcss`.
* `lightningcss.targets` takes precedence over the esbuild-style `target`, while the
* plugin-managed fields (`minify`, `cssModules`) win over their lightningcss counterparts.
*/
lightningcss: {errorRecovery: true},
/**
* Inject an import of the extracted CSS into the JS output, like `css.inject` (and matching
* its default of `false`). The specifier is relative unless `exports` publishes the CSS,
* in which case it is the self-referential `import "<pkg>/<fileName>"`.
* @defaultValue false
*/
inject: true,
/**
* Publish the CSS as the `"./<fileName>"` export subpath. `true` declares a plain string
* export; `{nodeCompat: true}` declares a conditional export and emits the no-op JS shim
* plus its `.d.ts`, so the subpath also resolves in runtimes that cannot load `.css`.
* @defaultValue false
*/
exports: {nodeCompat: true},
})CSS sourcemaps are not emitted, matching @tsdown/css — which
intentionally skips them
on the grounds that Vite's build mode doesn't support CSS sourcemaps either
(vitejs/vite#2830).
Adapter API
Host-specific adapters can provide resolved defaults through the plugin's
api property — this is how
@sanity/vanilla-extract-tsdown-plugin forwards tsdown's resolved config:
const plugin = vanillaExtractPlugin(options)
plugin.api.setBuildContext({
// Default for the `target` option (e.g. the host's resolved top-level target)
target: ['chrome90'],
// Package name for the self-referential import of `exports`
packageName: 'my-library',
// Working directory the `.css.ts` modules are compiled from
cwd: process.cwd(),
})Acknowledgements
The plugin combines a port of
@vanilla-extract/rollup-plugin
(MIT licensed, Copyright (c) 2021 SEEK) with the CSS collection and emission architecture of
@tsdown/css (MIT licensed,
Copyright (c) 2025-present VoidZero Inc. & Contributors, Copyright (c) 2024 Kevin Deng). The full
combined license notices are in this package's LICENSE file.
