@ikkin/plugin-unocss 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,407 @@
1
+ # @ikkin/plugin-unocss
2
+
3
+ [![npm version](https://badge.fury.io/js/@ikkin%2Fplugin-unocss.svg)](https://www.npmjs.com/package/@ikkin/plugin-unocss)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ > UnoCSS plugin for Rsbuild with CLI pre-generation and automatic injection
7
+
8
+ 一个标准的 Rsbuild 插件,用于在项目中集成 UnoCSS,支持 CLI 预生成模式。
9
+
10
+ ## 特性
11
+
12
+ ✅ **CLI 预生成模式**: 扫描源文件并生成独立的 CSS 文件
13
+ ✅ **自动注入**: 通过 HTML 标签注入 CSS 链接,不污染源码
14
+ ✅ **Watch 模式**: 开发环境监听文件变化并自动重新生成 CSS
15
+ ✅ **浏览器自动刷新**: CSS 变化后自动刷新浏览器,无需手动刷新页面
16
+ ✅ **零配置**: 开箱即用,完全自动化
17
+ ✅ **类型安全**: 完整的 TypeScript 支持
18
+ ✅ **灵活配置**: 支持自定义监听目录和扫描模式
19
+ ✅ **开箱即用**: 内置 `globby` 和 `chokidar`,用户无需额外安装
20
+
21
+ ## 安装
22
+
23
+ ### 通过 NPM 安装(推荐)
24
+
25
+ ```bash
26
+ # npm
27
+ npm install @ikkin/plugin-unocss unocss
28
+
29
+ # pnpm
30
+ pnpm add @ikkin/plugin-unocss unocss
31
+
32
+ # yarn
33
+ yarn add @ikkin/plugin-unocss unocss
34
+ ```
35
+
36
+ > **注意**: 插件已内置 `globby` 和 `chokidar`,你不需要额外安装这些依赖!
37
+
38
+ ### 本地开发
39
+
40
+ 如果你想在本地开发或修改插件:
41
+
42
+ ```bash
43
+ # 在项目根目录
44
+ pnpm install
45
+ ```
46
+
47
+ ## 使用方法
48
+
49
+ ### 1. 基础配置
50
+
51
+ 在 `rsbuild.config.ts` 中添加插件:
52
+
53
+ ```typescript
54
+ import { defineConfig } from '@rsbuild/core';
55
+ import { pluginReact } from '@rsbuild/plugin-react';
56
+ import { pluginUnocss } from '@ikkin/plugin-unocss';
57
+
58
+ export default defineConfig({
59
+ plugins: [
60
+ pluginReact(),
61
+ pluginUnocss(),
62
+ ],
63
+ });
64
+ ```
65
+
66
+ ### 2. 创建 UnoCSS 配置文件
67
+
68
+ 在项目根目录创建 `uno.config.ts`:
69
+
70
+ ```typescript
71
+ import { defineConfig, presetWind4 } from 'unocss';
72
+
73
+ export default defineConfig({
74
+ content: {
75
+ filesystem: ['./src/**/*.{html,js,ts,jsx,tsx}', './index.html'],
76
+ },
77
+ presets: [presetWind4()],
78
+ shortcuts: {
79
+ 'flex-center': 'flex items-center justify-center',
80
+ },
81
+ });
82
+ ```
83
+
84
+ ### 3. 直接使用
85
+
86
+ 在组件中直接使用 UnoCSS 工具类:
87
+
88
+ ```tsx
89
+ const App = () => {
90
+ return (
91
+ <div className="flex-center p-4 bg-blue-500 text-white">
92
+ Hello UnoCSS!
93
+ </div>
94
+ );
95
+ };
96
+ ```
97
+
98
+ ## 工作原理
99
+
100
+ ### 构建流程
101
+
102
+ 1. **扫描文件**: 插件扫描配置的文件路径,提取所有使用的 UnoCSS 类名
103
+ 2. **生成 CSS**: 根据实际使用情况生成 CSS 文件
104
+ - **生产环境**: 生成带 hash 的文件名(如 `uno.a1b2c3d4.css`)
105
+ - **开发环境**: 生成固定文件名(`uno.css`)以支持热更新
106
+ 3. **自动注入**: 通过 HTML 标签注入 CSS 链接(不污染源码)
107
+ - **生产环境**: 复制 CSS 到 `dist/uno.[hash].css` 并在 HTML 中添加 `<link rel="stylesheet" href="/uno.[hash].css">`
108
+ - **开发环境**: 通过 dev server 中间件在 `/uno.css` 路径提供 CSS 文件
109
+ 4. **零配置**: 用户无需手动导入 CSS 文件
110
+
111
+ ### 为什么不污染源码?
112
+
113
+ 传统方案会修改 `src/index.tsx` 添加 import 语句,这样会:
114
+ - ❌ 污染项目的源代码
115
+ - ❌ 用户可能会误提交这个自动生成的导入
116
+ - ❌ 代码审查时会看到不相关的修改
117
+
118
+ 本插件采用 HTML 注入方案:
119
+ - ✅ 完全不修改源代码
120
+ - ✅ 通过 HTML 标签自动注入 CSS 链接
121
+ - ✅ 用户体验更加简洁
122
+
123
+ ### Watch 模式与自动刷新
124
+
125
+ 开发环境下的 watch 模式提供以下功能:
126
+
127
+ 1. **文件监听**: 监听指定目录(默认 `src`)下的文件变化
128
+ 2. **CSS 自动生成**: 文件添加、修改、删除时自动重新生成 CSS
129
+ 3. **浏览器自动刷新**: 使用 `devServer.sockWrite('static-changed', '/uno.css')` 触发浏览器 CSS 刷新
130
+ 4. **防抖机制**: 300ms 防抖,避免频繁重新生成
131
+
132
+ 这样你在开发时添加新组件或修改样式后,浏览器会立即显示最新的样式,无需手动刷新页面。
133
+
134
+ ### 输出路径
135
+
136
+ 默认情况下,生成的 CSS 文件位于:
137
+
138
+ ```
139
+ plugins/generated/uno.css
140
+ ```
141
+
142
+ 你可以通过 `outputPath` 选项自定义输出路径:
143
+
144
+ ```typescript
145
+ // 相对于项目根目录
146
+ pluginUnocss({
147
+ outputPath: './src/styles/uno-generated.css',
148
+ })
149
+ ```
150
+
151
+ **路径解析规则**:
152
+ - **以 `./` 前缀**: 相对于项目根目录(推荐,更明确)
153
+ - 例如:`./src/styles/uno.css` → `项目根目录/src/styles/uno.css`
154
+ - **相对路径(不带 `./`)**: 相对于插件目录(更简洁)
155
+ - 例如:`generated/uno.css` → `plugins/generated/uno.css`
156
+ - **绝对路径**: 直接使用绝对路径
157
+ - 例如:`/custom/path/uno.css` → `/custom/path/uno.css`
158
+ - **未配置**: 默认相对于插件目录
159
+ - 默认:`plugins/generated/uno.css`
160
+
161
+ **默认路径的好处**:
162
+ - 在插件目录内,不会污染项目源码
163
+ - 当插件作为 npm 包发布时,用户看不到生成的文件
164
+ - 可以通过 `.gitignore` 忽略
165
+
166
+ ## 配置选项
167
+
168
+ ```typescript
169
+ pluginUnocss({
170
+ // UnoCSS 配置文件路径或直接配置对象
171
+ unocssConfig: './uno.config.ts',
172
+
173
+ // 生成 CSS 的输出路径
174
+ // 以 `./` 开头:相对于项目根目录(推荐,更明确)
175
+ // 相对路径(不带 `./`):相对于插件目录(更简洁)
176
+ // 绝对路径:使用绝对路径
177
+ // 未配置:默认相对于插件目录
178
+ outputPath: './generated/uno.css',
179
+
180
+ // 扫描的文件路径模式
181
+ contentPatterns: [
182
+ './src/**/*.{html,js,ts,jsx,tsx}',
183
+ './index.html',
184
+ ],
185
+
186
+ // 监听的目录路径(用于 watch 模式)
187
+ watchDirectory: 'src',
188
+
189
+ // 是否自动注入 CSS
190
+ autoInject: true,
191
+
192
+ // 是否启用 watch 模式
193
+ watch: true,
194
+
195
+ // 是否在文件名中添加 hash 值(仅生产环境)
196
+ enableHash: true,
197
+ })
198
+ ```
199
+
200
+ ### 选项说明
201
+
202
+ #### `unocssConfig`
203
+
204
+ - **类型**: `string | UserConfig`
205
+ - **默认值**: `'./uno.config.ts'`
206
+ - **说明**: UnoCSS 配置文件路径(相对于项目根目录)或直接传入配置对象
207
+
208
+ #### `outputPath`
209
+
210
+ - **类型**: `string`
211
+ - **默认值**: `'./generated/uno.css'`
212
+ - **说明**: 生成的 CSS 文件输出路径
213
+ - **路径解析规则**:
214
+ - 以 `./` 开头:相对于项目根目录(推荐,更明确)
215
+ - 相对路径(不带 `./`):相对于插件目录(更简洁)
216
+ - 绝对路径:使用绝对路径
217
+ - 未配置:默认相对于插件目录
218
+
219
+ **示例**:
220
+ ```typescript
221
+ // 推荐用法:相对于项目根目录
222
+ pluginUnocss({
223
+ outputPath: './src/styles/uno-generated.css',
224
+ // 生成到: 项目根目录/src/styles/uno-generated.css
225
+ })
226
+
227
+ // 相对于插件目录(更简洁)
228
+ pluginUnocss({
229
+ outputPath: 'generated/uno.css',
230
+ // 生成到: plugins/generated/uno.css
231
+ })
232
+
233
+ // 使用绝对路径
234
+ pluginUnocss({
235
+ outputPath: '/absolute/path/to/file.css',
236
+ // 生成到: /absolute/path/to/file.css
237
+ })
238
+ ```
239
+
240
+ #### `contentPatterns`
241
+
242
+ - **类型**: `string[]`
243
+ - **默认值**: `['./src/**/*.{html,js,ts,jsx,tsx}', './index.html']`
244
+ - **说明**: 用于扫描 UnoCSS 类名的文件 glob 模式数组
245
+
246
+ #### `watchDirectory`
247
+
248
+ - **类型**: `string`
249
+ - **默认值**: `'src'`
250
+ - **说明**: Watch 模式下监听的目录路径(相对于项目根目录)
251
+ - **示例**: 如果你的源代码在 `packages` 目录,可以设置为 `'packages'`
252
+
253
+ #### `autoInject`
254
+
255
+ - **类型**: `boolean`
256
+ - **默认值**: `true`
257
+ - **说明**: 是否自动在 HTML 中注入 CSS 链接标签
258
+
259
+ #### `watch`
260
+
261
+ - **类型**: `boolean`
262
+ - **默认值**: `true`
263
+ - **说明**: 是否在开发环境启用 watch 模式
264
+
265
+ #### `enableHash`
266
+
267
+ - **类型**: `boolean`
268
+ - **默认值**: `true`
269
+ - **说明**: 是否在文件名中添加 hash 值(仅生产环境)
270
+ - **作用**:
271
+ - ✅ 浏览器缓存优化:CSS 变化时 hash 也变化,强制浏览器重新加载
272
+ - ✅ CDN 友好:可以设置长期缓存策略
273
+ - ✅ 版本管理:每个版本的 CSS 都有唯一文件名
274
+ - **示例**:
275
+ - 启用:`uno.a1b2c3d4.css`
276
+ - 禁用:`uno.css`
277
+
278
+ ## 开发 vs 生产环境
279
+
280
+ ### 开发环境
281
+
282
+ - ✅ 启动时生成 CSS
283
+ - ✅ Watch 模式监听文件变化(需要 `chokidar`)
284
+ - ✅ 文件添加、修改、删除时自动重新生成 CSS
285
+ - ✅ 浏览器自动刷新 CSS(无需手动刷新页面)
286
+ - ✅ 通过 dev server 中间件提供 CSS 文件
287
+
288
+ ### 生产环境
289
+
290
+ - ✅ 构建前生成 CSS
291
+ - ✅ CSS 文件名包含内容 hash(如 `uno.a1b2c3d4.css`)
292
+ - ✅ CSS 复制到 `dist/uno.[hash].css`
293
+ - ✅ 自动注入带 hash 的 CSS 链接到 HTML
294
+ - ✅ 浏览器缓存优化:文件变化时 hash 自动更新
295
+
296
+ ## 高级用法
297
+
298
+ ### 自定义监听目录
299
+
300
+ 如果你的项目结构比较复杂,可以自定义监听的目录:
301
+
302
+ ```typescript
303
+ // rsbuild.config.ts
304
+ export default defineConfig({
305
+ plugins: [
306
+ pluginUnocss({
307
+ watchDirectory: 'packages', // 监听 packages 目录
308
+ contentPatterns: [
309
+ './packages/**/*.{html,js,ts,jsx,tsx}',
310
+ './index.html',
311
+ ],
312
+ }),
313
+ ],
314
+ });
315
+ ```
316
+
317
+ ### 直接传入 UnoCSS 配置
318
+
319
+ 也可以不使用配置文件,直接传入 UnoCSS 配置对象:
320
+
321
+ ```typescript
322
+ import { presetWind4 } from 'unocss';
323
+
324
+ export default defineConfig({
325
+ plugins: [
326
+ pluginUnocss({
327
+ unocssConfig: {
328
+ presets: [presetWind4()],
329
+ shortcuts: {
330
+ 'flex-center': 'flex items-center justify-center',
331
+ },
332
+ },
333
+ }),
334
+ ],
335
+ });
336
+ ```
337
+
338
+ ### 禁用自动注入
339
+
340
+ 如果你想手动控制 CSS 的导入,可以禁用自动注入:
341
+
342
+ ```typescript
343
+ export default defineConfig({
344
+ plugins: [
345
+ pluginUnocss({
346
+ autoInject: false, // 禁用自动注入
347
+ }),
348
+ ],
349
+ });
350
+ ```
351
+
352
+ 然后手动在入口文件中导入生成的 CSS:
353
+
354
+ ```typescript
355
+ // src/index.tsx
356
+ import '../plugins/generated/uno.css';
357
+ ```
358
+
359
+ ## 注意事项
360
+
361
+ 1. **生成的文件不需要提交到 Git**
362
+ - `plugins/generated/` 目录已加入 `.gitignore`
363
+ - 每次构建时会自动重新生成
364
+
365
+ 2. **Watch 模式需要 `chokidar`**
366
+ - 如果未安装,watch 模式会自动跳过并显示警告
367
+ - 安装命令: `pnpm add -D chokidar`
368
+
369
+ 3. **CSS 自动注入**
370
+ - 生产环境:CSS 会被复制到 `dist/uno.css` 并自动注入到 HTML
371
+ - 开发环境:通过 dev server 中间件提供 CSS 文件
372
+ - 无需手动导入或修改源代码
373
+
374
+ 4. **纯 CSS 输出**
375
+ - 插件生成纯 CSS 文件,包含所有使用的工具类
376
+ - 不会注入虚拟 CSS 模块到构建流程
377
+ - 生成的 CSS 可被浏览器直接缓存
378
+
379
+ 5. **文件变化检测**
380
+ - Watch 模式会监听指定目录下的所有 `.html`、`.js`、`.ts`、`.jsx`、`.tsx` 文件
381
+ - 文件添加、修改、删除都会触发 CSS 重新生成
382
+ - 300ms 防抖机制避免频繁重新生成
383
+
384
+ ## 优势对比
385
+
386
+ | 特性 | UnoCSS Plugin | PostCSS 方案 | CLI 手动方案 |
387
+ |------|---------------|-------------|-------------|
388
+ | 配置复杂度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
389
+ | 开发体验 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
390
+ | 生产优化 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
391
+ | 自动化程度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
392
+ | CSS 可见性 | ✅ | ❌ | ✅ |
393
+ | Watch 支持 | ✅ 自动 | ✅ | ⚠️ 需手动配置 |
394
+ | 浏览器自动刷新 | ✅ | ✅ | ❌ |
395
+ | 源码污染 | ❌ | ❌ | ⚠️ 可能 |
396
+ | 灵活配置 | ✅ | ⚠️ | ⚠️ |
397
+
398
+ ## 许可证
399
+
400
+ MIT
401
+
402
+ ## 相关链接
403
+
404
+ - [UnoCSS 官方文档](https://unocss.dev/)
405
+ - [Rsbuild 官方文档](https://rsbuild.rs/)
406
+ - [插件 GitHub 仓库](https://github.com/yourusername/plugin-unocss)
407
+
@@ -0,0 +1,62 @@
1
+ import { RsbuildPlugin } from '@rsbuild/core';
2
+ import { UserConfig } from 'unocss';
3
+
4
+ interface PluginUnocssOptions {
5
+ /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */
6
+ unocssConfig?: string | UserConfig;
7
+ /**
8
+ * 生成的 CSS 文件输出路径
9
+ *
10
+ * 路径解析规则:
11
+ * - 以 './' 开头: 相对于项目根目录 (推荐)
12
+ * - 相对路径: 相对于插件目录
13
+ * - 绝对路径: 直接使用
14
+ * - 未配置: 默认为 'generated/uno.css' 相对于插件目录
15
+ */
16
+ outputPath?: string;
17
+ /** 扫描的内容文件路径模式 (支持 glob 模式) */
18
+ contentPatterns?: string[];
19
+ /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */
20
+ watchDirectory?: string;
21
+ /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */
22
+ autoInject?: boolean;
23
+ /** 是否在开发环境启用文件监听模式 (默认: true) */
24
+ watch?: boolean;
25
+ /**
26
+ * 是否在生产环境为 CSS 文件名添加 content hash
27
+ *
28
+ * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)
29
+ */
30
+ enableHash?: boolean;
31
+ }
32
+ /**
33
+ * UnoCSS Plugin for Rsbuild
34
+ *
35
+ * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.
36
+ *
37
+ * 特性:
38
+ * - 扫描项目文件并生成独立的 CSS 文件
39
+ * - 开发环境支持文件监听和热更新
40
+ * - 生产环境支持 CSS 文件名 hash
41
+ * - 自动在 HTML 中注入 CSS link 标签
42
+ *
43
+ * @param options - 插件配置选项
44
+ * @returns Rsbuild 插件实例
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { pluginUnocss } from '@ikkin/plugin-unocss';
49
+ *
50
+ * export default {
51
+ * plugins: [
52
+ * pluginUnocss({
53
+ * outputPath: './src/generated/uno.css',
54
+ * enableHash: true,
55
+ * })
56
+ * ]
57
+ * };
58
+ * ```
59
+ */
60
+ declare function pluginUnocss(options?: PluginUnocssOptions): RsbuildPlugin;
61
+
62
+ export { type PluginUnocssOptions, pluginUnocss as default, pluginUnocss };
@@ -0,0 +1,62 @@
1
+ import { RsbuildPlugin } from '@rsbuild/core';
2
+ import { UserConfig } from 'unocss';
3
+
4
+ interface PluginUnocssOptions {
5
+ /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */
6
+ unocssConfig?: string | UserConfig;
7
+ /**
8
+ * 生成的 CSS 文件输出路径
9
+ *
10
+ * 路径解析规则:
11
+ * - 以 './' 开头: 相对于项目根目录 (推荐)
12
+ * - 相对路径: 相对于插件目录
13
+ * - 绝对路径: 直接使用
14
+ * - 未配置: 默认为 'generated/uno.css' 相对于插件目录
15
+ */
16
+ outputPath?: string;
17
+ /** 扫描的内容文件路径模式 (支持 glob 模式) */
18
+ contentPatterns?: string[];
19
+ /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */
20
+ watchDirectory?: string;
21
+ /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */
22
+ autoInject?: boolean;
23
+ /** 是否在开发环境启用文件监听模式 (默认: true) */
24
+ watch?: boolean;
25
+ /**
26
+ * 是否在生产环境为 CSS 文件名添加 content hash
27
+ *
28
+ * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)
29
+ */
30
+ enableHash?: boolean;
31
+ }
32
+ /**
33
+ * UnoCSS Plugin for Rsbuild
34
+ *
35
+ * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.
36
+ *
37
+ * 特性:
38
+ * - 扫描项目文件并生成独立的 CSS 文件
39
+ * - 开发环境支持文件监听和热更新
40
+ * - 生产环境支持 CSS 文件名 hash
41
+ * - 自动在 HTML 中注入 CSS link 标签
42
+ *
43
+ * @param options - 插件配置选项
44
+ * @returns Rsbuild 插件实例
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { pluginUnocss } from '@ikkin/plugin-unocss';
49
+ *
50
+ * export default {
51
+ * plugins: [
52
+ * pluginUnocss({
53
+ * outputPath: './src/generated/uno.css',
54
+ * enableHash: true,
55
+ * })
56
+ * ]
57
+ * };
58
+ * ```
59
+ */
60
+ declare function pluginUnocss(options?: PluginUnocssOptions): RsbuildPlugin;
61
+
62
+ export { type PluginUnocssOptions, pluginUnocss as default, pluginUnocss };
@@ -0,0 +1,255 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var url = require('url');
8
+ var crypto = require('crypto');
9
+
10
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
30
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
31
+
32
+ // plugin-unocss.ts
33
+ var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('plugin-unocss.js', document.baseURI).href)));
34
+ var __dirname$1 = path__namespace.dirname(__filename$1);
35
+ function pluginUnocss(options = {}) {
36
+ const {
37
+ unocssConfig = "./uno.config.ts",
38
+ outputPath = "",
39
+ contentPatterns = [
40
+ "./src/**/*.{html,js,ts,jsx,tsx}",
41
+ "./index.html"
42
+ ],
43
+ watchDirectory = "src",
44
+ autoInject = true,
45
+ watch = true,
46
+ enableHash = true
47
+ } = options;
48
+ return {
49
+ name: "unocss",
50
+ setup(api) {
51
+ const rootContext = api.context.rootPath;
52
+ const pluginDir = __dirname$1;
53
+ let resolvedOutputPath;
54
+ if (!outputPath) {
55
+ resolvedOutputPath = path__namespace.resolve(pluginDir, "./generated/uno.css");
56
+ } else if (path__namespace.isAbsolute(outputPath)) {
57
+ resolvedOutputPath = outputPath;
58
+ } else if (outputPath.startsWith("./")) {
59
+ resolvedOutputPath = path__namespace.resolve(rootContext, outputPath);
60
+ } else {
61
+ resolvedOutputPath = path__namespace.resolve(pluginDir, outputPath);
62
+ }
63
+ let currentCssPath = resolvedOutputPath;
64
+ let devServerInstance = null;
65
+ const isProd = process.env.NODE_ENV === "production";
66
+ const loadConfig = async () => {
67
+ if (typeof unocssConfig === "string") {
68
+ const configPath = path__namespace.resolve(rootContext, unocssConfig);
69
+ try {
70
+ const configModule = await import(`file:///${configPath.replace(/\\/g, "/")}`);
71
+ return configModule.default || configModule;
72
+ } catch (err) {
73
+ console.warn(
74
+ `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`
75
+ );
76
+ return {};
77
+ }
78
+ }
79
+ return unocssConfig;
80
+ };
81
+ const generateCSS = async () => {
82
+ console.log("[UnoCSS Hybrid] Generating CSS...");
83
+ try {
84
+ const config = await loadConfig();
85
+ const { globby } = await import('globby');
86
+ const files = await globby(contentPatterns, {
87
+ cwd: rootContext,
88
+ absolute: true
89
+ });
90
+ if (files.length === 0) {
91
+ console.warn("[UnoCSS Hybrid] No files found to scan");
92
+ return resolvedOutputPath;
93
+ }
94
+ console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);
95
+ console.log(`[UnoCSS Hybrid] Files:`, files.map((f) => path__namespace.relative(rootContext, f)));
96
+ const { createGenerator } = await import('unocss');
97
+ const generator = await createGenerator(config);
98
+ const allContents = files.map((file) => {
99
+ const content = fs__namespace.readFileSync(file, "utf-8");
100
+ return content;
101
+ }).join("\n");
102
+ const result = await generator.generate(allContents);
103
+ if (!result.css || result.css.trim().length === 0) {
104
+ console.warn("[UnoCSS Hybrid] Generated CSS is empty");
105
+ return resolvedOutputPath;
106
+ }
107
+ let finalOutputPath;
108
+ if (isProd && enableHash) {
109
+ const distDir = path__namespace.join(rootContext, "dist");
110
+ const hash = crypto.createHash("md5").update(result.css).digest("hex").substring(0, 8);
111
+ finalOutputPath = path__namespace.join(distDir, `uno.${hash}.css`);
112
+ currentCssPath = finalOutputPath;
113
+ } else {
114
+ finalOutputPath = resolvedOutputPath;
115
+ currentCssPath = resolvedOutputPath;
116
+ }
117
+ const outputDir = path__namespace.dirname(finalOutputPath);
118
+ if (!fs__namespace.existsSync(outputDir)) {
119
+ fs__namespace.mkdirSync(outputDir, { recursive: true });
120
+ }
121
+ fs__namespace.writeFileSync(finalOutputPath, result.css);
122
+ const stats = fs__namespace.statSync(finalOutputPath);
123
+ console.log(
124
+ `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`
125
+ );
126
+ return finalOutputPath;
127
+ } catch (err) {
128
+ console.error("[UnoCSS Hybrid] Failed to generate CSS:", err);
129
+ return resolvedOutputPath;
130
+ }
131
+ };
132
+ if (isProd) {
133
+ api.onBeforeBuild(async () => {
134
+ await generateCSS();
135
+ });
136
+ }
137
+ if (autoInject) {
138
+ api.modifyHTMLTags((tags) => {
139
+ let cssPath;
140
+ if (isProd && enableHash) {
141
+ const cssFileName = path__namespace.basename(currentCssPath);
142
+ cssPath = `/${cssFileName}`;
143
+ } else {
144
+ cssPath = "/uno.css";
145
+ }
146
+ tags.headTags.unshift({
147
+ tag: "link",
148
+ attrs: {
149
+ rel: "stylesheet",
150
+ href: cssPath
151
+ }
152
+ });
153
+ console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
154
+ return tags;
155
+ });
156
+ }
157
+ if (!isProd) {
158
+ api.modifyRsbuildConfig((config) => {
159
+ const existingSetupMiddlewares = config.dev?.setupMiddlewares;
160
+ const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares) ? existingSetupMiddlewares : existingSetupMiddlewares ? [existingSetupMiddlewares] : [];
161
+ return {
162
+ ...config,
163
+ dev: {
164
+ ...config.dev,
165
+ setupMiddlewares: [
166
+ ...setupMiddlewaresArray,
167
+ (middlewares, devServer) => {
168
+ devServerInstance = devServer;
169
+ middlewares.unshift(async (req, res, next) => {
170
+ if (req.url === "/uno.css") {
171
+ try {
172
+ if (!fs__namespace.existsSync(resolvedOutputPath)) {
173
+ res.statusCode = 404;
174
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
175
+ res.end("CSS file not found. Please restart the dev server to generate it.");
176
+ console.warn("[UnoCSS Hybrid] CSS file not found at:", resolvedOutputPath);
177
+ return;
178
+ }
179
+ res.setHeader("Content-Type", "text/css; charset=utf-8");
180
+ res.setHeader("Cache-Control", "no-cache");
181
+ const css = fs__namespace.readFileSync(resolvedOutputPath, "utf-8");
182
+ res.end(css);
183
+ } catch (err) {
184
+ console.error("[UnoCSS Hybrid] Error serving CSS:", err);
185
+ res.statusCode = 500;
186
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
187
+ res.end("Error loading CSS file. Check console for details.");
188
+ }
189
+ } else {
190
+ next();
191
+ }
192
+ });
193
+ }
194
+ ]
195
+ }
196
+ };
197
+ });
198
+ api.onBeforeStartDevServer(async () => {
199
+ console.log("[UnoCSS Hybrid] Development mode: CLI generation enabled");
200
+ await generateCSS();
201
+ });
202
+ api.onAfterStartDevServer(async () => {
203
+ console.log("[UnoCSS Hybrid] Dev server started, CSS available at /uno.css");
204
+ if (watch) {
205
+ try {
206
+ const chokidar = await import('chokidar');
207
+ const watchDir = path__namespace.resolve(rootContext, watchDirectory);
208
+ const watcher = chokidar.watch(watchDir, {
209
+ ignored: /(^|[\/\\])\../,
210
+ // ignore dotfiles
211
+ ignoreInitial: true,
212
+ persistent: true
213
+ });
214
+ let regenerateTimer = null;
215
+ watcher.on("all", async (event, filePath) => {
216
+ const ext = path__namespace.extname(filePath);
217
+ const shouldWatch = [".html", ".js", ".ts", ".jsx", ".tsx"].includes(ext);
218
+ if (shouldWatch && (event === "change" || event === "add" || event === "unlink")) {
219
+ console.log(`[UnoCSS Hybrid] File ${event}: ${path__namespace.relative(rootContext, filePath)}`);
220
+ if (regenerateTimer) {
221
+ clearTimeout(regenerateTimer);
222
+ }
223
+ regenerateTimer = setTimeout(async () => {
224
+ console.log("[UnoCSS Hybrid] Regenerating CSS...");
225
+ await generateCSS();
226
+ console.log("[UnoCSS Hybrid] Refreshing CSS in browser...");
227
+ devServerInstance?.sockWrite("static-changed", "/uno.css");
228
+ }, 300);
229
+ }
230
+ });
231
+ console.log("[UnoCSS Hybrid] Watch mode: enabled");
232
+ } catch (err) {
233
+ console.warn("[UnoCSS Hybrid] Watch mode not available:", err.message);
234
+ }
235
+ }
236
+ });
237
+ }
238
+ api.onAfterBuild(() => {
239
+ if (isProd && fs__namespace.existsSync(currentCssPath)) {
240
+ const stats = fs__namespace.statSync(currentCssPath);
241
+ const fileName = path__namespace.basename(currentCssPath);
242
+ console.log(
243
+ `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`
244
+ );
245
+ }
246
+ });
247
+ }
248
+ };
249
+ }
250
+ var plugin_unocss_default = pluginUnocss;
251
+
252
+ exports.default = plugin_unocss_default;
253
+ exports.pluginUnocss = pluginUnocss;
254
+ //# sourceMappingURL=plugin-unocss.js.map
255
+ //# sourceMappingURL=plugin-unocss.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../plugin-unocss.ts"],"names":["__filename","fileURLToPath","__dirname","path","fs","createHash"],"mappings":"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","file":"plugin-unocss.js","sourcesContent":["import type { RsbuildPlugin } from '@rsbuild/core';\nimport type { UserConfig } from 'unocss';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\nimport { createHash } from 'crypto';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport interface PluginUnocssOptions {\n /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */\n unocssConfig?: string | UserConfig;\n\n /**\n * 生成的 CSS 文件输出路径\n *\n * 路径解析规则:\n * - 以 './' 开头: 相对于项目根目录 (推荐)\n * - 相对路径: 相对于插件目录\n * - 绝对路径: 直接使用\n * - 未配置: 默认为 'generated/uno.css' 相对于插件目录\n */\n outputPath?: string;\n\n /** 扫描的内容文件路径模式 (支持 glob 模式) */\n contentPatterns?: string[];\n\n /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */\n watchDirectory?: string;\n\n /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */\n autoInject?: boolean;\n\n /** 是否在开发环境启用文件监听模式 (默认: true) */\n watch?: boolean;\n\n /**\n * 是否在生产环境为 CSS 文件名添加 content hash\n *\n * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)\n */\n enableHash?: boolean;\n}\n\n/**\n * UnoCSS Plugin for Rsbuild\n *\n * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.\n *\n * 特性:\n * - 扫描项目文件并生成独立的 CSS 文件\n * - 开发环境支持文件监听和热更新\n * - 生产环境支持 CSS 文件名 hash\n * - 自动在 HTML 中注入 CSS link 标签\n *\n * @param options - 插件配置选项\n * @returns Rsbuild 插件实例\n *\n * @example\n * ```ts\n * import { pluginUnocss } from '@ikkin/plugin-unocss';\n *\n * export default {\n * plugins: [\n * pluginUnocss({\n * outputPath: './src/generated/uno.css',\n * enableHash: true,\n * })\n * ]\n * };\n * ```\n */\nexport function pluginUnocss(\n options: PluginUnocssOptions = {},\n): RsbuildPlugin {\n const {\n unocssConfig = './uno.config.ts',\n outputPath = '',\n contentPatterns = [\n './src/**/*.{html,js,ts,jsx,tsx}',\n './index.html',\n ],\n watchDirectory = 'src',\n autoInject = true,\n watch = true,\n enableHash = true,\n } = options;\n\n return {\n name: 'unocss',\n\n setup(api) {\n const rootContext = api.context.rootPath;\n\n // 解析输出路径\n const pluginDir = __dirname;\n let resolvedOutputPath: string;\n\n if (!outputPath) {\n // 未配置:使用默认路径(相对于插件目录)\n resolvedOutputPath = path.resolve(pluginDir, './generated/uno.css');\n } else if (path.isAbsolute(outputPath)) {\n // 绝对路径:直接使用\n resolvedOutputPath = outputPath;\n } else if (outputPath.startsWith('./')) {\n // 以 ./ 开头:相对于项目根目录(推荐,更明确)\n resolvedOutputPath = path.resolve(rootContext, outputPath);\n } else {\n // 相对路径(不带 ./):相对于插件目录(更简洁)\n resolvedOutputPath = path.resolve(pluginDir, outputPath);\n }\n\n // 用于保存当前生成的 CSS 文件路径(可能包含 hash)\n let currentCssPath = resolvedOutputPath;\n\n // 用于保存 dev server 实例,以便在文件变化时触发刷新\n let devServerInstance: any = null;\n\n // 通过环境变量判断是否是生产环境\n const isProd = process.env.NODE_ENV === 'production';\n\n // 读取 UnoCSS 配置\n const loadConfig = async (): Promise<UserConfig> => {\n if (typeof unocssConfig === 'string') {\n const configPath = path.resolve(rootContext, unocssConfig);\n try {\n const configModule = await import(\n `file:///${configPath.replace(/\\\\/g, '/')}`\n );\n return configModule.default || configModule;\n } catch (err) {\n console.warn(\n `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`,\n );\n return {};\n }\n }\n return unocssConfig;\n };\n\n // 生成 UnoCSS CSS\n const generateCSS = async (): Promise<string> => {\n console.log('[UnoCSS Hybrid] Generating CSS...');\n\n try {\n const config = await loadConfig();\n\n // 扫描文件\n const { globby } = await import('globby');\n const files = await globby(contentPatterns, {\n cwd: rootContext,\n absolute: true,\n });\n\n if (files.length === 0) {\n console.warn('[UnoCSS Hybrid] No files found to scan');\n return resolvedOutputPath;\n }\n\n console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);\n console.log(`[UnoCSS Hybrid] Files:`, files.map(f => path.relative(rootContext, f)));\n\n // 使用 UnoCSS generator\n const { createGenerator } = await import('unocss');\n const generator = await createGenerator(config);\n\n // 读取所有文件内容并生成\n const allContents = files.map(file => {\n const content = fs.readFileSync(file, 'utf-8');\n return content;\n }).join('\\n');\n\n // 生成 CSS\n const result = await generator.generate(allContents);\n\n if (!result.css || result.css.trim().length === 0) {\n console.warn('[UnoCSS Hybrid] Generated CSS is empty');\n return resolvedOutputPath;\n }\n\n // 计算输出路径\n let finalOutputPath: string;\n if (isProd && enableHash) {\n // 生产环境:直接生成到 dist 目录,文件名带 hash\n const distDir = path.join(rootContext, 'dist');\n const hash = createHash('md5').update(result.css).digest('hex').substring(0, 8);\n finalOutputPath = path.join(distDir, `uno.${hash}.css`);\n currentCssPath = finalOutputPath;\n } else {\n // 开发环境:生成到插件目录的 generated 子目录,文件名固定\n finalOutputPath = resolvedOutputPath;\n currentCssPath = resolvedOutputPath;\n }\n\n // 确保输出目录存在\n const outputDir = path.dirname(finalOutputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n // 写入 CSS 文件\n fs.writeFileSync(finalOutputPath, result.css);\n\n const stats = fs.statSync(finalOutputPath);\n console.log(\n `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`,\n );\n\n return finalOutputPath;\n } catch (err) {\n console.error('[UnoCSS Hybrid] Failed to generate CSS:', err);\n return resolvedOutputPath;\n }\n };\n\n // 生产环境:构建前生成 CSS\n if (isProd) {\n api.onBeforeBuild(async () => {\n await generateCSS();\n });\n }\n\n // 自动注入:通过修改 HTML 标签来注入 CSS(不污染源码)\n if (autoInject) {\n api.modifyHTMLTags((tags) => {\n let cssPath: string;\n\n if (isProd && enableHash) {\n // 生产环境:使用带 hash 的文件名\n const cssFileName = path.basename(currentCssPath);\n cssPath = `/${cssFileName}`;\n } else {\n // 开发环境或未启用 hash:使用固定文件名\n cssPath = '/uno.css';\n }\n\n tags.headTags.unshift({\n tag: 'link',\n attrs: {\n rel: 'stylesheet',\n href: cssPath,\n },\n });\n\n console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);\n return tags;\n });\n }\n\n // 开发环境:配置静态文件服务\n if (!isProd) {\n api.modifyRsbuildConfig((config) => {\n const existingSetupMiddlewares = config.dev?.setupMiddlewares;\n const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares)\n ? existingSetupMiddlewares\n : existingSetupMiddlewares\n ? [existingSetupMiddlewares]\n : [];\n\n return {\n ...config,\n dev: {\n ...config.dev,\n setupMiddlewares: [\n ...setupMiddlewaresArray,\n (middlewares, devServer) => {\n // 保存 dev server 实例\n devServerInstance = devServer;\n\n // 添加自定义中间件来提供 CSS 文件\n middlewares.unshift(async (req, res, next) => {\n if (req.url === '/uno.css') {\n try {\n // 检查文件是否存在\n if (!fs.existsSync(resolvedOutputPath)) {\n res.statusCode = 404;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end('CSS file not found. Please restart the dev server to generate it.');\n console.warn('[UnoCSS Hybrid] CSS file not found at:', resolvedOutputPath);\n return;\n }\n\n // 设置正确的 Content-Type 和缓存控制\n res.setHeader('Content-Type', 'text/css; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache');\n\n const css = fs.readFileSync(resolvedOutputPath, 'utf-8');\n res.end(css);\n } catch (err) {\n console.error('[UnoCSS Hybrid] Error serving CSS:', err);\n res.statusCode = 500;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end('Error loading CSS file. Check console for details.');\n }\n } else {\n next();\n }\n });\n },\n ],\n },\n };\n });\n\n // 启动时生成 CSS\n api.onBeforeStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\n await generateCSS();\n });\n\n // 合并启动后的操作\n api.onAfterStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Dev server started, CSS available at /uno.css');\n\n // Watch 模式\n if (watch) {\n try {\n // 动态导入 chokidar\n const chokidar = await import('chokidar');\n\n // 监听指定目录\n const watchDir = path.resolve(rootContext, watchDirectory);\n\n // 监听内容文件变化\n const watcher = chokidar.watch(watchDir, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n ignoreInitial: true,\n persistent: true,\n });\n\n // 防抖:避免频繁重新生成\n let regenerateTimer: NodeJS.Timeout | null = null;\n\n watcher.on('all', async (event: string, filePath: string) => {\n // 检查文件扩展名\n const ext = path.extname(filePath);\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\n\n // 监听文件添加、修改和删除\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);\n\n if (regenerateTimer) {\n clearTimeout(regenerateTimer);\n }\n\n regenerateTimer = setTimeout(async () => {\n console.log('[UnoCSS Hybrid] Regenerating CSS...');\n await generateCSS();\n\n // 触发浏览器刷新 CSS\n console.log('[UnoCSS Hybrid] Refreshing CSS in browser...');\n devServerInstance?.sockWrite('static-changed', '/uno.css');\n }, 300);\n }\n });\n\n console.log('[UnoCSS Hybrid] Watch mode: enabled');\n } catch (err) {\n console.warn('[UnoCSS Hybrid] Watch mode not available:', (err as Error).message);\n }\n }\n });\n }\n\n // 构建完成后的日志\n api.onAfterBuild(() => {\n if (isProd && fs.existsSync(currentCssPath)) {\n const stats = fs.statSync(currentCssPath);\n const fileName = path.basename(currentCssPath);\n console.log(\n `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`,\n );\n }\n });\n },\n };\n}\n\nexport default pluginUnocss;\n"]}
@@ -0,0 +1,228 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { createHash } from 'crypto';
5
+
6
+ // plugin-unocss.ts
7
+ var __filename$1 = fileURLToPath(import.meta.url);
8
+ var __dirname$1 = path.dirname(__filename$1);
9
+ function pluginUnocss(options = {}) {
10
+ const {
11
+ unocssConfig = "./uno.config.ts",
12
+ outputPath = "",
13
+ contentPatterns = [
14
+ "./src/**/*.{html,js,ts,jsx,tsx}",
15
+ "./index.html"
16
+ ],
17
+ watchDirectory = "src",
18
+ autoInject = true,
19
+ watch = true,
20
+ enableHash = true
21
+ } = options;
22
+ return {
23
+ name: "unocss",
24
+ setup(api) {
25
+ const rootContext = api.context.rootPath;
26
+ const pluginDir = __dirname$1;
27
+ let resolvedOutputPath;
28
+ if (!outputPath) {
29
+ resolvedOutputPath = path.resolve(pluginDir, "./generated/uno.css");
30
+ } else if (path.isAbsolute(outputPath)) {
31
+ resolvedOutputPath = outputPath;
32
+ } else if (outputPath.startsWith("./")) {
33
+ resolvedOutputPath = path.resolve(rootContext, outputPath);
34
+ } else {
35
+ resolvedOutputPath = path.resolve(pluginDir, outputPath);
36
+ }
37
+ let currentCssPath = resolvedOutputPath;
38
+ let devServerInstance = null;
39
+ const isProd = process.env.NODE_ENV === "production";
40
+ const loadConfig = async () => {
41
+ if (typeof unocssConfig === "string") {
42
+ const configPath = path.resolve(rootContext, unocssConfig);
43
+ try {
44
+ const configModule = await import(`file:///${configPath.replace(/\\/g, "/")}`);
45
+ return configModule.default || configModule;
46
+ } catch (err) {
47
+ console.warn(
48
+ `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`
49
+ );
50
+ return {};
51
+ }
52
+ }
53
+ return unocssConfig;
54
+ };
55
+ const generateCSS = async () => {
56
+ console.log("[UnoCSS Hybrid] Generating CSS...");
57
+ try {
58
+ const config = await loadConfig();
59
+ const { globby } = await import('globby');
60
+ const files = await globby(contentPatterns, {
61
+ cwd: rootContext,
62
+ absolute: true
63
+ });
64
+ if (files.length === 0) {
65
+ console.warn("[UnoCSS Hybrid] No files found to scan");
66
+ return resolvedOutputPath;
67
+ }
68
+ console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);
69
+ console.log(`[UnoCSS Hybrid] Files:`, files.map((f) => path.relative(rootContext, f)));
70
+ const { createGenerator } = await import('unocss');
71
+ const generator = await createGenerator(config);
72
+ const allContents = files.map((file) => {
73
+ const content = fs.readFileSync(file, "utf-8");
74
+ return content;
75
+ }).join("\n");
76
+ const result = await generator.generate(allContents);
77
+ if (!result.css || result.css.trim().length === 0) {
78
+ console.warn("[UnoCSS Hybrid] Generated CSS is empty");
79
+ return resolvedOutputPath;
80
+ }
81
+ let finalOutputPath;
82
+ if (isProd && enableHash) {
83
+ const distDir = path.join(rootContext, "dist");
84
+ const hash = createHash("md5").update(result.css).digest("hex").substring(0, 8);
85
+ finalOutputPath = path.join(distDir, `uno.${hash}.css`);
86
+ currentCssPath = finalOutputPath;
87
+ } else {
88
+ finalOutputPath = resolvedOutputPath;
89
+ currentCssPath = resolvedOutputPath;
90
+ }
91
+ const outputDir = path.dirname(finalOutputPath);
92
+ if (!fs.existsSync(outputDir)) {
93
+ fs.mkdirSync(outputDir, { recursive: true });
94
+ }
95
+ fs.writeFileSync(finalOutputPath, result.css);
96
+ const stats = fs.statSync(finalOutputPath);
97
+ console.log(
98
+ `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`
99
+ );
100
+ return finalOutputPath;
101
+ } catch (err) {
102
+ console.error("[UnoCSS Hybrid] Failed to generate CSS:", err);
103
+ return resolvedOutputPath;
104
+ }
105
+ };
106
+ if (isProd) {
107
+ api.onBeforeBuild(async () => {
108
+ await generateCSS();
109
+ });
110
+ }
111
+ if (autoInject) {
112
+ api.modifyHTMLTags((tags) => {
113
+ let cssPath;
114
+ if (isProd && enableHash) {
115
+ const cssFileName = path.basename(currentCssPath);
116
+ cssPath = `/${cssFileName}`;
117
+ } else {
118
+ cssPath = "/uno.css";
119
+ }
120
+ tags.headTags.unshift({
121
+ tag: "link",
122
+ attrs: {
123
+ rel: "stylesheet",
124
+ href: cssPath
125
+ }
126
+ });
127
+ console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
128
+ return tags;
129
+ });
130
+ }
131
+ if (!isProd) {
132
+ api.modifyRsbuildConfig((config) => {
133
+ const existingSetupMiddlewares = config.dev?.setupMiddlewares;
134
+ const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares) ? existingSetupMiddlewares : existingSetupMiddlewares ? [existingSetupMiddlewares] : [];
135
+ return {
136
+ ...config,
137
+ dev: {
138
+ ...config.dev,
139
+ setupMiddlewares: [
140
+ ...setupMiddlewaresArray,
141
+ (middlewares, devServer) => {
142
+ devServerInstance = devServer;
143
+ middlewares.unshift(async (req, res, next) => {
144
+ if (req.url === "/uno.css") {
145
+ try {
146
+ if (!fs.existsSync(resolvedOutputPath)) {
147
+ res.statusCode = 404;
148
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
149
+ res.end("CSS file not found. Please restart the dev server to generate it.");
150
+ console.warn("[UnoCSS Hybrid] CSS file not found at:", resolvedOutputPath);
151
+ return;
152
+ }
153
+ res.setHeader("Content-Type", "text/css; charset=utf-8");
154
+ res.setHeader("Cache-Control", "no-cache");
155
+ const css = fs.readFileSync(resolvedOutputPath, "utf-8");
156
+ res.end(css);
157
+ } catch (err) {
158
+ console.error("[UnoCSS Hybrid] Error serving CSS:", err);
159
+ res.statusCode = 500;
160
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
161
+ res.end("Error loading CSS file. Check console for details.");
162
+ }
163
+ } else {
164
+ next();
165
+ }
166
+ });
167
+ }
168
+ ]
169
+ }
170
+ };
171
+ });
172
+ api.onBeforeStartDevServer(async () => {
173
+ console.log("[UnoCSS Hybrid] Development mode: CLI generation enabled");
174
+ await generateCSS();
175
+ });
176
+ api.onAfterStartDevServer(async () => {
177
+ console.log("[UnoCSS Hybrid] Dev server started, CSS available at /uno.css");
178
+ if (watch) {
179
+ try {
180
+ const chokidar = await import('chokidar');
181
+ const watchDir = path.resolve(rootContext, watchDirectory);
182
+ const watcher = chokidar.watch(watchDir, {
183
+ ignored: /(^|[\/\\])\../,
184
+ // ignore dotfiles
185
+ ignoreInitial: true,
186
+ persistent: true
187
+ });
188
+ let regenerateTimer = null;
189
+ watcher.on("all", async (event, filePath) => {
190
+ const ext = path.extname(filePath);
191
+ const shouldWatch = [".html", ".js", ".ts", ".jsx", ".tsx"].includes(ext);
192
+ if (shouldWatch && (event === "change" || event === "add" || event === "unlink")) {
193
+ console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);
194
+ if (regenerateTimer) {
195
+ clearTimeout(regenerateTimer);
196
+ }
197
+ regenerateTimer = setTimeout(async () => {
198
+ console.log("[UnoCSS Hybrid] Regenerating CSS...");
199
+ await generateCSS();
200
+ console.log("[UnoCSS Hybrid] Refreshing CSS in browser...");
201
+ devServerInstance?.sockWrite("static-changed", "/uno.css");
202
+ }, 300);
203
+ }
204
+ });
205
+ console.log("[UnoCSS Hybrid] Watch mode: enabled");
206
+ } catch (err) {
207
+ console.warn("[UnoCSS Hybrid] Watch mode not available:", err.message);
208
+ }
209
+ }
210
+ });
211
+ }
212
+ api.onAfterBuild(() => {
213
+ if (isProd && fs.existsSync(currentCssPath)) {
214
+ const stats = fs.statSync(currentCssPath);
215
+ const fileName = path.basename(currentCssPath);
216
+ console.log(
217
+ `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`
218
+ );
219
+ }
220
+ });
221
+ }
222
+ };
223
+ }
224
+ var plugin_unocss_default = pluginUnocss;
225
+
226
+ export { plugin_unocss_default as default, pluginUnocss };
227
+ //# sourceMappingURL=plugin-unocss.mjs.map
228
+ //# sourceMappingURL=plugin-unocss.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../plugin-unocss.ts"],"names":["__filename","__dirname"],"mappings":"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","file":"plugin-unocss.mjs","sourcesContent":["import type { RsbuildPlugin } from '@rsbuild/core';\nimport type { UserConfig } from 'unocss';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\nimport { createHash } from 'crypto';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport interface PluginUnocssOptions {\n /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */\n unocssConfig?: string | UserConfig;\n\n /**\n * 生成的 CSS 文件输出路径\n *\n * 路径解析规则:\n * - 以 './' 开头: 相对于项目根目录 (推荐)\n * - 相对路径: 相对于插件目录\n * - 绝对路径: 直接使用\n * - 未配置: 默认为 'generated/uno.css' 相对于插件目录\n */\n outputPath?: string;\n\n /** 扫描的内容文件路径模式 (支持 glob 模式) */\n contentPatterns?: string[];\n\n /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */\n watchDirectory?: string;\n\n /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */\n autoInject?: boolean;\n\n /** 是否在开发环境启用文件监听模式 (默认: true) */\n watch?: boolean;\n\n /**\n * 是否在生产环境为 CSS 文件名添加 content hash\n *\n * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)\n */\n enableHash?: boolean;\n}\n\n/**\n * UnoCSS Plugin for Rsbuild\n *\n * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.\n *\n * 特性:\n * - 扫描项目文件并生成独立的 CSS 文件\n * - 开发环境支持文件监听和热更新\n * - 生产环境支持 CSS 文件名 hash\n * - 自动在 HTML 中注入 CSS link 标签\n *\n * @param options - 插件配置选项\n * @returns Rsbuild 插件实例\n *\n * @example\n * ```ts\n * import { pluginUnocss } from '@ikkin/plugin-unocss';\n *\n * export default {\n * plugins: [\n * pluginUnocss({\n * outputPath: './src/generated/uno.css',\n * enableHash: true,\n * })\n * ]\n * };\n * ```\n */\nexport function pluginUnocss(\n options: PluginUnocssOptions = {},\n): RsbuildPlugin {\n const {\n unocssConfig = './uno.config.ts',\n outputPath = '',\n contentPatterns = [\n './src/**/*.{html,js,ts,jsx,tsx}',\n './index.html',\n ],\n watchDirectory = 'src',\n autoInject = true,\n watch = true,\n enableHash = true,\n } = options;\n\n return {\n name: 'unocss',\n\n setup(api) {\n const rootContext = api.context.rootPath;\n\n // 解析输出路径\n const pluginDir = __dirname;\n let resolvedOutputPath: string;\n\n if (!outputPath) {\n // 未配置:使用默认路径(相对于插件目录)\n resolvedOutputPath = path.resolve(pluginDir, './generated/uno.css');\n } else if (path.isAbsolute(outputPath)) {\n // 绝对路径:直接使用\n resolvedOutputPath = outputPath;\n } else if (outputPath.startsWith('./')) {\n // 以 ./ 开头:相对于项目根目录(推荐,更明确)\n resolvedOutputPath = path.resolve(rootContext, outputPath);\n } else {\n // 相对路径(不带 ./):相对于插件目录(更简洁)\n resolvedOutputPath = path.resolve(pluginDir, outputPath);\n }\n\n // 用于保存当前生成的 CSS 文件路径(可能包含 hash)\n let currentCssPath = resolvedOutputPath;\n\n // 用于保存 dev server 实例,以便在文件变化时触发刷新\n let devServerInstance: any = null;\n\n // 通过环境变量判断是否是生产环境\n const isProd = process.env.NODE_ENV === 'production';\n\n // 读取 UnoCSS 配置\n const loadConfig = async (): Promise<UserConfig> => {\n if (typeof unocssConfig === 'string') {\n const configPath = path.resolve(rootContext, unocssConfig);\n try {\n const configModule = await import(\n `file:///${configPath.replace(/\\\\/g, '/')}`\n );\n return configModule.default || configModule;\n } catch (err) {\n console.warn(\n `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`,\n );\n return {};\n }\n }\n return unocssConfig;\n };\n\n // 生成 UnoCSS CSS\n const generateCSS = async (): Promise<string> => {\n console.log('[UnoCSS Hybrid] Generating CSS...');\n\n try {\n const config = await loadConfig();\n\n // 扫描文件\n const { globby } = await import('globby');\n const files = await globby(contentPatterns, {\n cwd: rootContext,\n absolute: true,\n });\n\n if (files.length === 0) {\n console.warn('[UnoCSS Hybrid] No files found to scan');\n return resolvedOutputPath;\n }\n\n console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);\n console.log(`[UnoCSS Hybrid] Files:`, files.map(f => path.relative(rootContext, f)));\n\n // 使用 UnoCSS generator\n const { createGenerator } = await import('unocss');\n const generator = await createGenerator(config);\n\n // 读取所有文件内容并生成\n const allContents = files.map(file => {\n const content = fs.readFileSync(file, 'utf-8');\n return content;\n }).join('\\n');\n\n // 生成 CSS\n const result = await generator.generate(allContents);\n\n if (!result.css || result.css.trim().length === 0) {\n console.warn('[UnoCSS Hybrid] Generated CSS is empty');\n return resolvedOutputPath;\n }\n\n // 计算输出路径\n let finalOutputPath: string;\n if (isProd && enableHash) {\n // 生产环境:直接生成到 dist 目录,文件名带 hash\n const distDir = path.join(rootContext, 'dist');\n const hash = createHash('md5').update(result.css).digest('hex').substring(0, 8);\n finalOutputPath = path.join(distDir, `uno.${hash}.css`);\n currentCssPath = finalOutputPath;\n } else {\n // 开发环境:生成到插件目录的 generated 子目录,文件名固定\n finalOutputPath = resolvedOutputPath;\n currentCssPath = resolvedOutputPath;\n }\n\n // 确保输出目录存在\n const outputDir = path.dirname(finalOutputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n // 写入 CSS 文件\n fs.writeFileSync(finalOutputPath, result.css);\n\n const stats = fs.statSync(finalOutputPath);\n console.log(\n `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`,\n );\n\n return finalOutputPath;\n } catch (err) {\n console.error('[UnoCSS Hybrid] Failed to generate CSS:', err);\n return resolvedOutputPath;\n }\n };\n\n // 生产环境:构建前生成 CSS\n if (isProd) {\n api.onBeforeBuild(async () => {\n await generateCSS();\n });\n }\n\n // 自动注入:通过修改 HTML 标签来注入 CSS(不污染源码)\n if (autoInject) {\n api.modifyHTMLTags((tags) => {\n let cssPath: string;\n\n if (isProd && enableHash) {\n // 生产环境:使用带 hash 的文件名\n const cssFileName = path.basename(currentCssPath);\n cssPath = `/${cssFileName}`;\n } else {\n // 开发环境或未启用 hash:使用固定文件名\n cssPath = '/uno.css';\n }\n\n tags.headTags.unshift({\n tag: 'link',\n attrs: {\n rel: 'stylesheet',\n href: cssPath,\n },\n });\n\n console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);\n return tags;\n });\n }\n\n // 开发环境:配置静态文件服务\n if (!isProd) {\n api.modifyRsbuildConfig((config) => {\n const existingSetupMiddlewares = config.dev?.setupMiddlewares;\n const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares)\n ? existingSetupMiddlewares\n : existingSetupMiddlewares\n ? [existingSetupMiddlewares]\n : [];\n\n return {\n ...config,\n dev: {\n ...config.dev,\n setupMiddlewares: [\n ...setupMiddlewaresArray,\n (middlewares, devServer) => {\n // 保存 dev server 实例\n devServerInstance = devServer;\n\n // 添加自定义中间件来提供 CSS 文件\n middlewares.unshift(async (req, res, next) => {\n if (req.url === '/uno.css') {\n try {\n // 检查文件是否存在\n if (!fs.existsSync(resolvedOutputPath)) {\n res.statusCode = 404;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end('CSS file not found. Please restart the dev server to generate it.');\n console.warn('[UnoCSS Hybrid] CSS file not found at:', resolvedOutputPath);\n return;\n }\n\n // 设置正确的 Content-Type 和缓存控制\n res.setHeader('Content-Type', 'text/css; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache');\n\n const css = fs.readFileSync(resolvedOutputPath, 'utf-8');\n res.end(css);\n } catch (err) {\n console.error('[UnoCSS Hybrid] Error serving CSS:', err);\n res.statusCode = 500;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end('Error loading CSS file. Check console for details.');\n }\n } else {\n next();\n }\n });\n },\n ],\n },\n };\n });\n\n // 启动时生成 CSS\n api.onBeforeStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\n await generateCSS();\n });\n\n // 合并启动后的操作\n api.onAfterStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Dev server started, CSS available at /uno.css');\n\n // Watch 模式\n if (watch) {\n try {\n // 动态导入 chokidar\n const chokidar = await import('chokidar');\n\n // 监听指定目录\n const watchDir = path.resolve(rootContext, watchDirectory);\n\n // 监听内容文件变化\n const watcher = chokidar.watch(watchDir, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n ignoreInitial: true,\n persistent: true,\n });\n\n // 防抖:避免频繁重新生成\n let regenerateTimer: NodeJS.Timeout | null = null;\n\n watcher.on('all', async (event: string, filePath: string) => {\n // 检查文件扩展名\n const ext = path.extname(filePath);\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\n\n // 监听文件添加、修改和删除\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);\n\n if (regenerateTimer) {\n clearTimeout(regenerateTimer);\n }\n\n regenerateTimer = setTimeout(async () => {\n console.log('[UnoCSS Hybrid] Regenerating CSS...');\n await generateCSS();\n\n // 触发浏览器刷新 CSS\n console.log('[UnoCSS Hybrid] Refreshing CSS in browser...');\n devServerInstance?.sockWrite('static-changed', '/uno.css');\n }, 300);\n }\n });\n\n console.log('[UnoCSS Hybrid] Watch mode: enabled');\n } catch (err) {\n console.warn('[UnoCSS Hybrid] Watch mode not available:', (err as Error).message);\n }\n }\n });\n }\n\n // 构建完成后的日志\n api.onAfterBuild(() => {\n if (isProd && fs.existsSync(currentCssPath)) {\n const stats = fs.statSync(currentCssPath);\n const fileName = path.basename(currentCssPath);\n console.log(\n `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`,\n );\n }\n });\n },\n };\n}\n\nexport default pluginUnocss;\n"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@ikkin/plugin-unocss",
3
+ "version": "1.0.0",
4
+ "description": "UnoCSS plugin for Rsbuild with CLI pre-generation and automatic injection",
5
+ "main": "./dist/plugin-unocss.js",
6
+ "module": "./dist/plugin-unocss.mjs",
7
+ "types": "./dist/plugin-unocss.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/plugin-unocss.d.ts",
11
+ "import": "./dist/plugin-unocss.mjs",
12
+ "require": "./dist/plugin-unocss.js",
13
+ "default": "./dist/plugin-unocss.mjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "prepublishOnly": "npm run build",
25
+ "prepack": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "rsbuild",
29
+ "plugin",
30
+ "unocss",
31
+ "css",
32
+ "atomic-css",
33
+ "utility-class",
34
+ "windi-css",
35
+ "tailwind",
36
+ "cli",
37
+ "atomic-css-engine"
38
+ ],
39
+ "author": "ikkin <your.email@example.com>",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/ikkin/plugin-unocss.git"
44
+ },
45
+ "homepage": "https://github.com/ikkin/plugin-unocss#readme",
46
+ "bugs": {
47
+ "url": "https://github.com/ikkin/plugin-unocss/issues"
48
+ },
49
+ "sideEffects": false,
50
+ "peerDependencies": {
51
+ "@rsbuild/core": ">=1.0.0",
52
+ "unocss": ">=0.60.0"
53
+ },
54
+ "dependencies": {
55
+ "globby": "^14.0.0",
56
+ "chokidar": "^4.0.0"
57
+ },
58
+ "devDependencies": {
59
+ "@rsbuild/core": "^1.1.0",
60
+ "@types/node": "^22.0.0",
61
+ "tsup": "^8.0.0",
62
+ "typescript": "^5.6.0",
63
+ "unocss": "^66.6.0"
64
+ }
65
+ }