@sanity/vanilla-extract-tsdown-plugin
v0.4.0
Published
tsdown plugin for vanilla-extract that extracts CSS into a lightningcss-optimized file, modeled after @tsdown/css
Downloads
24,252
Readme
@sanity/vanilla-extract-tsdown-plugin
A tsdown plugin for vanilla-extract, built
for bundling libraries that ship pre-extracted CSS. It wraps the rolldown-generic
@sanity/vanilla-extract-rolldown-plugin —
which 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 — and adds the tsdown specifics on top:
- the CSS syntax lowering
targetdefaults to tsdown's resolved top-leveltarget(and, matchingcss.target, lowering is skipped when the targets name no browsers — e.g. anode20target resolved fromengines.node), - the self-referential import of
exportsuses the package name tsdown resolved, and - the
"./bundle.css"export is written topackage.jsonthrough the plugin'stsdownConfighook when tsdown'sexportsfeature is enabled — conditional withnodeCompat(types→ the shim's.d.ts,browser/style→ the real CSS,node/default→ the no-op shim), a plain string otherwise.
Unlike @vanilla-extract/rollup-plugin it doesn't declare rollup as a peer dependency, so it
doesn't pull a second bundler into tsdown 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 for the underlying rolldown plugin against the official Rollup pipeline live
in the vanilla-extract benchmarks.
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, writing it topackage.json(andpublishConfig.exports) when tsdown'sexportsfeature is enabled. Any injected import then uses the self-referential"<pkg>/bundle.css"bare specifier.
exports: {nodeCompat: true} is the flavor most libraries want: the export becomes conditional and
a no-op bundle-css.js shim (plus bundle-css.d.ts for its types condition) is emitted, so the
subpath stays resolvable in runtimes that cannot import .css files. exports: true declares a
plain string export without the shim, for packages that only ever run in browsers or bundlers.
[!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-tsdown-plugin @vanilla-extract/css// tsdown.config.ts
import {vanillaExtractPlugin} from '@sanity/vanilla-extract-tsdown-plugin'
import {defineConfig} from 'tsdown'
export default defineConfig({
entry: 'src/index.ts',
plugins: [vanillaExtractPlugin()],
})If you're using @sanity/tsdown-config,
prefer its vanillaExtract option instead: it uses this plugin under the hood with the defaults
most Sanity libraries want - inject: true with exports: {nodeCompat: true}, and tsdown's
exports feature already enabled so the conditional "./bundle.css" export is maintained
automatically.
If you're bundling with raw rolldown (or a Vite build-only library setup)
instead of tsdown, use
@sanity/vanilla-extract-rolldown-plugin
directly - it provides everything except the tsdown config wiring described above.
Options
The options are the @sanity/vanilla-extract-rolldown-plugin options,
modeled after the css options of @tsdown/css, so they feel
familiar in a tsdown config:
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`. Defaults to
* tsdown's resolved top-level `target`. Matching `@tsdown/css`, lowering is skipped when no
* target is configured anywhere, 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.)
*/
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, written to `package.json` when
* tsdown's `exports` feature is enabled. `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).
