Skip to content

Commit dbe3cc9

Browse files
feat(css): implement full CSS pipeline as Rolldown plugin (#787)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 04a9d75 commit dbe3cc9

28 files changed

+1408
-139
lines changed

docs/options/css.md

Lines changed: 199 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,190 @@
11
# CSS Support
22

3-
CSS support in `tsdown` is still in a very early, experimental stage. While you can use some basic features, please be aware that the API and behavior may change in future releases.
3+
CSS support in `tsdown` is still an experimental feature. While it covers the core use cases, the API and behavior may change in future releases.
44

55
> [!WARNING] Experimental Feature
6-
> CSS support is highly experimental. Please test thoroughly and report any issues you encounter. The API and behavior may change as the feature matures.
6+
> CSS support is experimental. Please test thoroughly and report any issues you encounter. The API and behavior may change as the feature matures.
77
8-
## Options
8+
## CSS Import
99

10-
### Disabling CSS Code Splitting
10+
Importing `.css` files from your TypeScript or JavaScript entry points is supported out of the box. The CSS content is extracted and emitted as a separate `.css` asset file:
1111

12-
By default, CSS may be split into multiple files based on your entry points. If you want to disable CSS code splitting and generate a single CSS file, you can set `css.splitting` to `false` in your configuration:
12+
```ts
13+
// src/index.ts
14+
import './style.css'
15+
16+
export function greet() {
17+
return 'Hello'
18+
}
19+
```
20+
21+
This produces both `index.mjs` and `index.css` in the output directory.
22+
23+
## CSS Pre-processors
24+
25+
`tsdown` provides built-in support for `.scss`, `.sass`, `.less`, `.styl`, and `.stylus` files. The corresponding pre-processor must be installed as a dev dependency:
26+
27+
::: code-group
28+
29+
```sh [Sass]
30+
# Either sass-embedded (recommended, faster) or sass
31+
npm install -D sass-embedded
32+
# or
33+
npm install -D sass
34+
```
35+
36+
```sh [Less]
37+
npm install -D less
38+
```
39+
40+
```sh [Stylus]
41+
npm install -D stylus
42+
```
43+
44+
:::
45+
46+
Once installed, you can import preprocessor files directly:
47+
48+
```ts
49+
import './style.scss'
50+
import './theme.less'
51+
import './global.styl'
52+
```
53+
54+
### Preprocessor Options
55+
56+
You can pass options to each preprocessor via `css.preprocessorOptions`:
1357

1458
```ts
1559
export default defineConfig({
1660
css: {
17-
splitting: false,
61+
preprocessorOptions: {
62+
scss: {
63+
additionalData: `$brand-color: #ff7e17;`,
64+
},
65+
less: {
66+
math: 'always',
67+
},
68+
},
1869
},
1970
})
2071
```
2172

22-
### Setting the Output CSS File Name
73+
#### `additionalData`
2374

24-
You can customize the name of the merged CSS file using the `css.fileName` option:
75+
Each preprocessor supports an `additionalData` option to inject extra code at the beginning of every processed file. This is useful for global variables or mixins:
76+
77+
```ts
78+
export default defineConfig({
79+
css: {
80+
preprocessorOptions: {
81+
scss: {
82+
// String — prepended to every .scss file
83+
additionalData: `@use "src/styles/variables" as *;`,
84+
},
85+
},
86+
},
87+
})
88+
```
89+
90+
You can also use a function for dynamic injection:
91+
92+
```ts
93+
export default defineConfig({
94+
css: {
95+
preprocessorOptions: {
96+
scss: {
97+
additionalData: (source, filename) => {
98+
if (filename.includes('theme')) return source
99+
return `@use "src/styles/variables" as *;\n${source}`
100+
},
101+
},
102+
},
103+
},
104+
})
105+
```
106+
107+
## Lightning CSS
108+
109+
`tsdown` uses [Lightning CSS](https://lightningcss.dev/) for CSS syntax lowering — transforming modern CSS features into syntax compatible with older browsers based on your `target` setting.
110+
111+
To enable CSS syntax lowering, install `lightningcss`:
112+
113+
::: code-group
114+
115+
```sh [npm]
116+
npm install -D lightningcss
117+
```
118+
119+
```sh [pnpm]
120+
pnpm add -D lightningcss
121+
```
122+
123+
```sh [yarn]
124+
yarn add -D lightningcss
125+
```
126+
127+
```sh [bun]
128+
bun add -D lightningcss
129+
```
130+
131+
:::
132+
133+
Once installed, CSS lowering is enabled automatically when a `target` is set. For example, with `target: 'chrome108'`, CSS nesting `&` selectors will be flattened:
134+
135+
```css
136+
/* Input */
137+
.foo {
138+
& .bar {
139+
color: red;
140+
}
141+
}
142+
143+
/* Output (chrome108) */
144+
.foo .bar {
145+
color: red;
146+
}
147+
```
148+
149+
### Lightning CSS Options
150+
151+
You can pass additional options to Lightning CSS via `css.lightningcss`:
152+
153+
```ts
154+
import { Features } from 'lightningcss'
155+
156+
export default defineConfig({
157+
css: {
158+
lightningcss: {
159+
// Override browser targets directly (instead of using `target`)
160+
targets: { chrome: 100 << 16 },
161+
// Include/exclude specific features
162+
include: Features.Nesting,
163+
},
164+
},
165+
})
166+
```
167+
168+
> [!TIP]
169+
> When `css.lightningcss.targets` is set, it takes precedence over the top-level `target` option for CSS transformations.
170+
171+
For more information on available options, refer to the [Lightning CSS documentation](https://lightningcss.dev/).
172+
173+
## CSS Code Splitting
174+
175+
### Merged Mode (Default)
176+
177+
By default, all CSS is merged into a single file (default: `style.css`):
178+
179+
```
180+
dist/
181+
index.mjs
182+
style.css ← all CSS merged
183+
```
184+
185+
### Custom File Name
186+
187+
You can customize the merged CSS file name:
25188

26189
```ts
27190
export default defineConfig({
@@ -31,4 +194,31 @@ export default defineConfig({
31194
})
32195
```
33196

34-
This will output your combined CSS as `my-library.css` in the output directory.
197+
### Splitting Mode
198+
199+
To split CSS per chunk — so each JavaScript chunk that imports CSS has a corresponding `.css` file — enable splitting:
200+
201+
```ts
202+
export default defineConfig({
203+
css: {
204+
splitting: true,
205+
},
206+
})
207+
```
208+
209+
```
210+
dist/
211+
index.mjs
212+
index.css ← CSS from index.ts
213+
async-abc123.mjs
214+
async-abc123.css ← CSS from async chunk
215+
```
216+
217+
## Options Reference
218+
219+
| Option | Type | Default | Description |
220+
| ------------------------- | --------- | ------------- | -------------------------------------------------------------- |
221+
| `css.splitting` | `boolean` | `false` | Enable CSS code splitting per chunk |
222+
| `css.fileName` | `string` | `'style.css'` | File name for the merged CSS file (when `splitting: false`) |
223+
| `css.preprocessorOptions` | `object` || Options for CSS preprocessors (scss, sass, less, styl, stylus) |
224+
| `css.lightningcss` | `object` || Options passed to Lightning CSS for syntax lowering |

docs/options/target.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,28 +103,30 @@ If you need to use the **latest TC39 Stage 3 decorators**, please note that `tsd
103103

104104
`tsdown` can also downlevel CSS features to match your specified browser targets. For example, a CSS nesting `&` selector will be flattened if the target is `chrome108` or lower.
105105

106-
To enable CSS downleveling, you need to manually install [`unplugin-lightningcss`](https://github.com/unplugin/unplugin-lightningcss):
106+
To enable CSS downleveling, install [`lightningcss`](https://lightningcss.dev/):
107107

108108
::: code-group
109109

110110
```sh [npm]
111-
npm install -D unplugin-lightningcss
111+
npm install -D lightningcss
112112
```
113113

114114
```sh [pnpm]
115-
pnpm add -D unplugin-lightningcss
115+
pnpm add -D lightningcss
116116
```
117117

118118
```sh [yarn]
119-
yarn add -D unplugin-lightningcss
119+
yarn add -D lightningcss
120120
```
121121

122122
```sh [bun]
123-
bun add -D unplugin-lightningcss
123+
bun add -D lightningcss
124124
```
125125

126126
:::
127127

128128
Once installed, simply set your browser target (for example, `target: 'chrome100'`) in your configuration or CLI options, and CSS downleveling will be enabled automatically.
129129

130+
You can also pass additional Lightning CSS options via `css.lightningcss`. See the [CSS documentation](/options/css.md#lightning-css) for details.
131+
130132
For more information on browser targets and CSS compatibility, refer to the [Lightning CSS documentation](https://lightningcss.dev/).

0 commit comments

Comments
 (0)