|
3 | 3 | [](https://npmjs.com/package/@bosh-code/tsdown-plugin-inject-css) |
4 | 4 |  |
5 | 5 |
|
6 | | -Inject CSS imports at the top of each chunk file in tsdown builds. |
| 6 | +Inject CSS imports at the top of files built with [tsdown](https://tsdown.dev/). |
7 | 7 |
|
8 | 8 | During the build process, tsdown strips CSS imports from your source files. This plugin tracks those imports and |
9 | | -re-injects them into output chunks. |
| 9 | +re-injects them into the built files. |
10 | 10 |
|
11 | | -```js |
12 | | -// Input: foo.ts |
13 | | -import './foo.css'; |
14 | | - |
15 | | -export const Foo = () => <div>Foo</div>; |
16 | | - |
17 | | -// Output: foo.js (with plugin) |
18 | | -import './foo.css'; |
19 | | - |
20 | | -export const Foo = () => <div>Foo</div>; |
21 | | -``` |
| 11 | +## Installation |
22 | 12 |
|
23 | | -## Features |
| 13 | +Install the plugin: |
24 | 14 |
|
25 | | -- 💡 Automatically tracks CSS imports before they're stripped |
26 | | -- 🎯 Injects imports into the correct output chunks |
27 | | -- ⚡️ Sourcemap support |
28 | | -- 🛠 Out-of-box, minimal configuration |
29 | | -- 🔄 Works with tsdown's code splitting and chunking |
| 15 | +```shell |
| 16 | +# npm |
| 17 | +npm install -D @bosh-code/tsdown-plugin-inject-css |
30 | 18 |
|
31 | | -## Installation |
| 19 | +# yarn |
| 20 | +yarn add -D @bosh-code/tsdown-plugin-inject-css |
32 | 21 |
|
33 | | -```bash |
34 | | -npm install @bosh-code/tsdown-plugin-inject-css -D |
35 | | -# or |
36 | | -pnpm add @bosh-code/tsdown-plugin-inject-css -D |
37 | | -# or |
38 | | -yarn add @bosh-code/tsdown-plugin-inject-css -D |
| 22 | +# pnpm |
| 23 | +pnpm add -D @bosh-code/tsdown-plugin-inject-css |
39 | 24 | ``` |
40 | 25 |
|
41 | | -## Usage |
| 26 | +Add it to your `tsdown.config.ts`: |
42 | 27 |
|
43 | 28 | ```ts |
44 | 29 | // tsdown.config.ts |
45 | | -import { defineConfig } from 'tsdown'; |
46 | | -import { libInjectCss } from '@bosh-code/tsdown-plugin-inject-css'; |
| 30 | + |
| 31 | +import { injectCssPlugin } from '@bosh-code/tsdown-plugin-inject-css'; |
47 | 32 |
|
48 | 33 | export default defineConfig({ |
49 | | - entry: ['./src/index.ts'], |
50 | | - format: ['esm'], |
| 34 | + external: ['preact'], |
51 | 35 | plugins: [ |
52 | | - libInjectCss({ |
53 | | - sourcemap: true, // default: true |
54 | | - }), |
55 | | - ], |
| 36 | + injectCssPlugin() |
| 37 | + ] |
56 | 38 | }); |
57 | | -``` |
58 | 39 |
|
59 | | -## How It Works |
60 | | - |
61 | | -The plugin operates in three phases: |
| 40 | +``` |
62 | 41 |
|
63 | | -1. **Transform Phase**: Scans source files for CSS imports (e.g., `import './style.css'`) before they're stripped |
64 | | -2. **Render Phase**: Tracks which source modules end up in which output chunks |
65 | | -3. **Generate Phase**: Re-injects the CSS imports at the top of the appropriate chunks |
| 42 | +___ |
66 | 43 |
|
67 | 44 | ### Example |
68 | 45 |
|
69 | | -Given this structure: |
70 | | - |
71 | | -```ts |
72 | | -// src/foo.ts |
73 | | -import './foo.css'; |
| 46 | +#### Source files: |
74 | 47 |
|
75 | | -export const Foo = () => <div>Foo < /div>; |
| 48 | +Component files: |
76 | 49 |
|
77 | | -// src/bar.ts |
78 | | -import './bar.css'; |
| 50 | +```css |
| 51 | +/* src/greeting.css */ |
79 | 52 |
|
80 | | -export const Bar = () => <div>Bar < /div>; |
| 53 | +/* Add component styles here */ |
81 | 54 |
|
82 | | -// src/index.ts |
83 | | -export { Foo } from './foo'; |
84 | | -export { Bar } from './bar'; |
| 55 | +.greeting { |
| 56 | + color: red; |
| 57 | +} |
85 | 58 | ``` |
86 | 59 |
|
87 | | -The plugin ensures: |
88 | | - |
89 | | -- `foo.js` includes `import './foo.css';` |
90 | | -- `bar.js` includes `import './bar.css';` |
91 | | -- `index.js` imports from `foo.js` and `bar.js` (CSS already handled) |
| 60 | +```tsx |
| 61 | +// src/greeting.tsx |
| 62 | +import './Foo.css'; |
92 | 63 |
|
93 | | -## Options |
| 64 | +export const Greeting = () => <div class="greeting">Hello World</div>; |
| 65 | +``` |
94 | 66 |
|
95 | | -### `sourcemap` |
| 67 | +Library entrypoint: |
96 | 68 |
|
97 | | -- Type: `boolean` |
98 | | -- Default: `true` |
| 69 | +```css |
| 70 | +/* src/index.css */ |
99 | 71 |
|
100 | | -Whether to generate sourcemaps for the modified chunks. |
| 72 | +/* Add global styles here */ |
101 | 73 |
|
102 | | -```ts |
103 | | -libInjectCss({ |
104 | | - sourcemap: false, // Disable sourcemap generation |
105 | | -}) |
| 74 | +html { |
| 75 | + background-color: blue; |
| 76 | +} |
106 | 77 | ``` |
107 | 78 |
|
108 | | -## Why This Plugin? |
109 | | - |
110 | | -When building component libraries with tsdown, CSS imports are typically stripped during the transpilation process. This |
111 | | -plugin solves the problem by: |
| 79 | +```ts |
| 80 | +// src/index.ts |
| 81 | +import './index.css'; |
112 | 82 |
|
113 | | -1. **Preserving CSS imports**: Ensures styles are loaded when components are used |
114 | | -2. **Proper chunking**: Each chunk only imports its required CSS files |
115 | | -3. **Tree-shaking friendly**: Works seamlessly with tsdown's code splitting |
116 | | -4. **Zero configuration**: Works out of the box with sensible defaults |
| 83 | +export { Greeting } from './greeting' |
| 84 | +``` |
117 | 85 |
|
118 | | -## Compatibility |
| 86 | +#### Built files: |
119 | 87 |
|
120 | | -This plugin is designed for: |
| 88 | +```css |
| 89 | +/* dist/index.css */ |
| 90 | +html { |
| 91 | + background-color: blue; |
| 92 | +} |
121 | 93 |
|
122 | | -- ✅ tsdown (primary target) |
123 | | -- ✅ Rolldown (direct compatibility) |
124 | | -- ⚠️ Rollup (may work with limitations) |
| 94 | +.greeting { |
| 95 | + color: red; |
| 96 | +} |
125 | 97 |
|
126 | | -## Configuration Tips |
| 98 | +/* Gorgeous colour theme, I know. */ |
| 99 | +``` |
127 | 100 |
|
128 | | -### For Component Libraries |
| 101 | +```js |
| 102 | +// dist/index.js |
| 103 | +import { jsx as e } from 'preact/jsx-runtime'; |
| 104 | +import './index.css'; // Injected by plugin |
129 | 105 |
|
130 | | -When building a component library, you typically want: |
| 106 | +const t = () => e(`div`, { className: `greeting`, children: `Hello World` }); |
| 107 | +export { t as Greeting }; |
131 | 108 |
|
132 | | -```ts |
133 | | -export default defineConfig({ |
134 | | - entry: ['./src/index.ts'], |
135 | | - format: ['esm'], |
136 | | - dts: true, |
137 | | - sourcemap: true, |
138 | | - plugins: [ |
139 | | - libInjectCss(), |
140 | | - ], |
141 | | -}); |
142 | 109 | ``` |
143 | 110 |
|
144 | | -### With Multiple Entry Points |
| 111 | +___ |
145 | 112 |
|
146 | | -The plugin works seamlessly with multiple entries: |
| 113 | +### The how and why |
147 | 114 |
|
148 | | -```ts |
149 | | -export default defineConfig({ |
150 | | - entry: { |
151 | | - index: './src/index.ts', |
152 | | - button: './src/button/index.ts', |
153 | | - card: './src/card/index.ts', |
154 | | - }, |
155 | | - format: ['esm'], |
156 | | - plugins: [ |
157 | | - libInjectCss(), |
158 | | - ], |
159 | | -}); |
160 | | -``` |
| 115 | +This plugin is *heavily* inspired by [vite-plugin-lib-inject-css](https://github.com/emosheeep/vite-plugin-lib-inject-css) but |
| 116 | +I adapted it to work with tsdown specifically. |
161 | 117 |
|
162 | | -Each entry point will have its CSS imports properly injected. |
| 118 | +I made this because I wanted to use tsdown to build my preact component library. |
163 | 119 |
|
164 | | -## Inspired By |
| 120 | +It *should* work with multiple entrypoint, however I haven't tried that. I got it to work for my project and called it good. |
165 | 121 |
|
166 | | -This plugin is inspired by [vite-plugin-lib-inject-css](https://github.com/emosheeep/vite-plugin-lib-inject-css) but |
167 | | -adapted specifically for tsdown's architecture and build process. |
| 122 | +**Contributions welcome!** |
168 | 123 |
|
169 | | -## License |
| 124 | +### License |
170 | 125 |
|
171 | 126 | MIT © bosh-code |
| 127 | + |
| 128 | +<div align="center"> |
| 129 | + <img style="width: 120px" src="https://raw.githubusercontent.com/bosh-code/tsdown-plugin-inject-css/main/.github/images/nz-made.png"> |
| 130 | +</div> |
0 commit comments