@ikkin/plugin-unocss 1.0.1 → 1.0.3

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.
@@ -61,8 +61,122 @@ function pluginUnocss(options = {}) {
61
61
  resolvedOutputPath = path__namespace.resolve(pluginDir, outputPath);
62
62
  }
63
63
  let currentCssPath = resolvedOutputPath;
64
- let devServerInstance = null;
64
+ let wss = null;
65
+ let wsPort = 0;
65
66
  const isProd = process.env.NODE_ENV === "production";
67
+ const fileContentCache = /* @__PURE__ */ new Map();
68
+ let lastGeneratedCSS = "";
69
+ if (isProd) {
70
+ api.onBeforeBuild(async () => {
71
+ await generateCSS();
72
+ });
73
+ }
74
+ if (autoInject) {
75
+ api.modifyHTMLTags((tags) => {
76
+ let cssPath;
77
+ if (isProd && enableHash) {
78
+ const cssFileName = path__namespace.basename(currentCssPath);
79
+ cssPath = `/${cssFileName}`;
80
+ } else {
81
+ cssPath = "/uno.css";
82
+ }
83
+ tags.headTags.unshift({
84
+ tag: "link",
85
+ attrs: {
86
+ rel: "stylesheet",
87
+ href: cssPath
88
+ }
89
+ });
90
+ console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
91
+ if (!isProd) {
92
+ const hotReloadScript = `
93
+ (function() {
94
+ function reloadCSS(version) {
95
+ const oldLink = document.querySelector('link[href*="/uno.css"]');
96
+ if (oldLink && oldLink.parentNode) {
97
+ const newLink = document.createElement('link');
98
+ newLink.rel = 'stylesheet';
99
+ newLink.href = '/uno.css?v=' + version;
100
+ oldLink.parentNode.insertBefore(newLink, oldLink);
101
+
102
+ newLink.onload = function() {
103
+ if (oldLink.parentNode) {
104
+ oldLink.parentNode.removeChild(oldLink);
105
+ }
106
+ };
107
+
108
+ newLink.onerror = function() {
109
+ console.error('[UnoCSS] Failed to reload CSS');
110
+ if (newLink.parentNode) {
111
+ newLink.parentNode.removeChild(newLink);
112
+ }
113
+ };
114
+ }
115
+ }
116
+
117
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
118
+ const host = window.location.hostname;
119
+ const wsUrl = protocol + '//' + host + ':${wsPort}';
120
+
121
+ let socket = null;
122
+ let reconnectCount = 0;
123
+ const maxReconnect = 10;
124
+
125
+ function connect() {
126
+ if (reconnectCount === 0) {
127
+ console.log('[UnoCSS] WebSocket connecting to ' + wsUrl);
128
+ }
129
+
130
+ socket = new WebSocket(wsUrl);
131
+
132
+ socket.onopen = function() {
133
+ console.log('[UnoCSS] WebSocket connected.');
134
+ reconnectCount = 0;
135
+ };
136
+
137
+ socket.onmessage = function(event) {
138
+ try {
139
+ const data = JSON.parse(event.data);
140
+ if (data.type === 'css-update' && data.version) {
141
+ reloadCSS(data.version);
142
+ }
143
+ } catch (e) {
144
+ // \u5FFD\u7565\u89E3\u6790\u9519\u8BEF
145
+ }
146
+ };
147
+
148
+ socket.onclose = function() {
149
+ if (reconnectCount >= maxReconnect) {
150
+ console.warn('[UnoCSS] WebSocket connection failed after maximum retry attempts.');
151
+ return;
152
+ }
153
+
154
+ if (reconnectCount === 0) {
155
+ console.log('[UnoCSS] WebSocket connection lost. Reconnecting...');
156
+ }
157
+
158
+ reconnectCount++;
159
+ const delay = Math.min(1000 * Math.pow(1.5, reconnectCount), 30000);
160
+ setTimeout(connect, delay);
161
+ };
162
+
163
+ socket.onerror = function(error) {
164
+ console.error('[UnoCSS] WebSocket error:', error);
165
+ };
166
+ }
167
+
168
+ connect();
169
+ })();
170
+ `;
171
+ tags.bodyTags.push({
172
+ tag: "script",
173
+ children: hotReloadScript
174
+ });
175
+ console.log("[UnoCSS Hybrid] Hot reload script injected");
176
+ }
177
+ return tags;
178
+ });
179
+ }
66
180
  const loadConfig = async () => {
67
181
  if (typeof unocssConfig === "string") {
68
182
  const configPath = path__namespace.resolve(rootContext, unocssConfig);
@@ -78,8 +192,10 @@ function pluginUnocss(options = {}) {
78
192
  }
79
193
  return unocssConfig;
80
194
  };
81
- const generateCSS = async () => {
82
- console.log("[UnoCSS Hybrid] Generating CSS...");
195
+ const getFileHash = (content) => {
196
+ return crypto.createHash("md5").update(content).digest("hex");
197
+ };
198
+ const generateCSS = async (changedFiles) => {
83
199
  try {
84
200
  const config = await loadConfig();
85
201
  const { globby } = await import('globby');
@@ -91,12 +207,68 @@ function pluginUnocss(options = {}) {
91
207
  console.warn("[UnoCSS Hybrid] No files found to scan");
92
208
  return resolvedOutputPath;
93
209
  }
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)));
210
+ if (!isProd && changedFiles && fileContentCache.size > 0) {
211
+ const changedHashes = /* @__PURE__ */ new Set();
212
+ const changedFilesContent = [];
213
+ for (const file of changedFiles) {
214
+ if (!fs__namespace.existsSync(file)) {
215
+ const relPath2 = path__namespace.relative(rootContext, file);
216
+ fileContentCache.delete(relPath2);
217
+ console.log(`[UnoCSS Hybrid] File removed: ${relPath2}`);
218
+ continue;
219
+ }
220
+ const content = fs__namespace.readFileSync(file, "utf-8");
221
+ const hash = getFileHash(content);
222
+ const relPath = path__namespace.relative(rootContext, file);
223
+ if (fileContentCache.get(relPath) !== hash) {
224
+ fileContentCache.set(relPath, hash);
225
+ changedFilesContent.push(content);
226
+ changedHashes.add(hash);
227
+ console.log(`[UnoCSS Hybrid] File changed: ${relPath} (hash: ${hash.substring(0, 8)})`);
228
+ }
229
+ }
230
+ if (changedFilesContent.length > 0) {
231
+ console.log(`[UnoCSS Hybrid] Incremental generation: ${changedFilesContent.length} changed file(s)`);
232
+ const { createGenerator: createGenerator2 } = await import('unocss');
233
+ const generator2 = await createGenerator2(config);
234
+ const changedContent = changedFilesContent.join("\n");
235
+ const result2 = await generator2.generate(changedContent);
236
+ if (result2.css && result2.css.trim().length > 0) {
237
+ let existingCSS = "";
238
+ if (fs__namespace.existsSync(resolvedOutputPath)) {
239
+ existingCSS = fs__namespace.readFileSync(resolvedOutputPath, "utf-8");
240
+ }
241
+ const newClasses = new Set(
242
+ result2.css.match(/\.(\w[-\w]*)/g)?.map((cls) => cls.slice(1)) || []
243
+ );
244
+ const existingCSSLines = existingCSS.split("\n").filter((line) => {
245
+ const match = line.match(/^\.([\w-]+)/);
246
+ if (match) {
247
+ return !newClasses.has(match[1]);
248
+ }
249
+ return true;
250
+ });
251
+ const mergedCSS = [...existingCSSLines, result2.css].join("\n").replace(/\n\n+/g, "\n");
252
+ fs__namespace.writeFileSync(resolvedOutputPath, mergedCSS);
253
+ const stats2 = fs__namespace.statSync(resolvedOutputPath);
254
+ console.log(
255
+ `[UnoCSS Hybrid] CSS updated incrementally: ${resolvedOutputPath} (${(stats2.size / 1024).toFixed(2)} KB, ${newClasses.size} new classes)`
256
+ );
257
+ return resolvedOutputPath;
258
+ }
259
+ } else {
260
+ console.log("[UnoCSS Hybrid] No content changes detected, skipping generation");
261
+ return resolvedOutputPath;
262
+ }
263
+ }
264
+ console.log("[UnoCSS Hybrid] Full CSS generation...");
96
265
  const { createGenerator } = await import('unocss');
97
266
  const generator = await createGenerator(config);
98
267
  const allContents = files.map((file) => {
99
268
  const content = fs__namespace.readFileSync(file, "utf-8");
269
+ const hash = getFileHash(content);
270
+ const relPath = path__namespace.relative(rootContext, file);
271
+ fileContentCache.set(relPath, hash);
100
272
  return content;
101
273
  }).join("\n");
102
274
  const result = await generator.generate(allContents);
@@ -119,6 +291,7 @@ function pluginUnocss(options = {}) {
119
291
  fs__namespace.mkdirSync(outputDir, { recursive: true });
120
292
  }
121
293
  fs__namespace.writeFileSync(finalOutputPath, result.css);
294
+ lastGeneratedCSS = result.css;
122
295
  const stats = fs__namespace.statSync(finalOutputPath);
123
296
  console.log(
124
297
  `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`
@@ -134,66 +307,6 @@ function pluginUnocss(options = {}) {
134
307
  await generateCSS();
135
308
  });
136
309
  }
137
- let cssVersion = Date.now();
138
- if (autoInject) {
139
- api.modifyHTMLTags((tags) => {
140
- let cssPath;
141
- if (isProd && enableHash) {
142
- const cssFileName = path__namespace.basename(currentCssPath);
143
- cssPath = `/${cssFileName}`;
144
- } else {
145
- cssPath = "/uno.css";
146
- }
147
- tags.headTags.unshift({
148
- tag: "link",
149
- attrs: {
150
- rel: "stylesheet",
151
- href: cssPath
152
- }
153
- });
154
- console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
155
- if (!isProd) {
156
- const hotReloadScript = `
157
- (() => {
158
- window.__unocss_version__ = ${cssVersion};
159
-
160
- // \u76D1\u542C WebSocket \u6D88\u606F
161
- const originalOnMessage = window.__whm__?.fns?.push;
162
- if (window.__whm__?.fns) {
163
- window.__whm__.fns.push(function(data) {
164
- // \u76D1\u542C\u81EA\u5B9A\u4E49\u70ED\u66F4\u65B0\u4E8B\u4EF6
165
- if (data.type === 'unocss-css-update') {
166
- const { version } = data.data;
167
- if (version !== window.__unocss_version__) {
168
- window.__unocss_version__ = version;
169
-
170
- // \u67E5\u627E UnoCSS \u7684 link \u6807\u7B7E
171
- const link = document.querySelector('link[href*="/uno.css"]');
172
- if (link) {
173
- // \u66F4\u65B0 href \u5F3A\u5236\u91CD\u65B0\u52A0\u8F7D
174
- const href = link.getAttribute('href');
175
- if (href) {
176
- // \u6DFB\u52A0\u7248\u672C\u53F7\u4F5C\u4E3A\u67E5\u8BE2\u53C2\u6570
177
- const newHref = href.split('?')[0] + '?v=' + version;
178
- link.setAttribute('href', newHref);
179
- console.log('[UnoCSS] CSS hot updated:', newHref);
180
- }
181
- }
182
- }
183
- }
184
- });
185
- }
186
- })();
187
- `;
188
- tags.bodyTags.push({
189
- tag: "script",
190
- children: hotReloadScript
191
- });
192
- console.log("[UnoCSS Hybrid] Hot reload script injected");
193
- }
194
- return tags;
195
- });
196
- }
197
310
  if (!isProd) {
198
311
  api.modifyRsbuildConfig((config) => {
199
312
  const existingSetupMiddlewares = config.dev?.setupMiddlewares;
@@ -204,8 +317,7 @@ function pluginUnocss(options = {}) {
204
317
  ...config.dev,
205
318
  setupMiddlewares: [
206
319
  ...setupMiddlewaresArray,
207
- (middlewares, devServer) => {
208
- devServerInstance = devServer;
320
+ (middlewares, _devServer) => {
209
321
  middlewares.unshift(async (req, res, next) => {
210
322
  if (req.url?.startsWith("/uno.css")) {
211
323
  try {
@@ -217,7 +329,9 @@ function pluginUnocss(options = {}) {
217
329
  return;
218
330
  }
219
331
  res.setHeader("Content-Type", "text/css; charset=utf-8");
220
- res.setHeader("Cache-Control", "no-cache");
332
+ res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
333
+ res.setHeader("Pragma", "no-cache");
334
+ res.setHeader("Expires", "0");
221
335
  const css = fs__namespace.readFileSync(resolvedOutputPath, "utf-8");
222
336
  res.end(css);
223
337
  } catch (err) {
@@ -238,38 +352,80 @@ function pluginUnocss(options = {}) {
238
352
  api.onBeforeStartDevServer(async () => {
239
353
  console.log("[UnoCSS Hybrid] Development mode: CLI generation enabled");
240
354
  await generateCSS();
355
+ try {
356
+ const { WebSocketServer } = await import('ws');
357
+ wss = new WebSocketServer({ port: 0 });
358
+ wsPort = wss.address().port;
359
+ console.log(`[UnoCSS Hybrid] WebSocket server created on port ${wsPort}`);
360
+ wss.on("connection", (ws) => {
361
+ console.log("[UnoCSS Hybrid] Client connected to WebSocket");
362
+ ws.on("close", () => {
363
+ console.log("[UnoCSS Hybrid] Client disconnected from WebSocket");
364
+ });
365
+ });
366
+ console.log("[UnoCSS Hybrid] UnoCSS HMR WebSocket server ready");
367
+ } catch (err) {
368
+ console.error("[UnoCSS Hybrid] Failed to create WebSocket server:", err);
369
+ }
241
370
  });
242
371
  api.onAfterStartDevServer(async () => {
243
372
  console.log("[UnoCSS Hybrid] Dev server started, CSS available at /uno.css");
244
373
  if (watch) {
245
374
  try {
375
+ console.log("[UnoCSS Hybrid] Initializing watch mode...");
246
376
  const chokidar = await import('chokidar');
377
+ console.log("[UnoCSS Hybrid] chokidar imported successfully");
247
378
  const watchDir = path__namespace.resolve(rootContext, watchDirectory);
379
+ console.log("[UnoCSS Hybrid] Watching directory:", watchDir);
248
380
  const watcher = chokidar.watch(watchDir, {
249
381
  ignored: /(^|[\/\\])\../,
250
382
  // ignore dotfiles
251
383
  ignoreInitial: true,
252
384
  persistent: true
253
385
  });
386
+ console.log("[UnoCSS Hybrid] Watcher created, setting up event handlers...");
387
+ watcher.on("ready", () => {
388
+ console.log("[UnoCSS Hybrid] Watcher ready, scanning for files...");
389
+ });
390
+ watcher.on("error", (error) => {
391
+ console.error("[UnoCSS Hybrid] Watcher error:", error);
392
+ });
254
393
  let regenerateTimer = null;
394
+ let changedFilesInBatch = [];
255
395
  watcher.on("all", async (event, filePath) => {
396
+ console.log(`[UnoCSS Hybrid] Watcher event: ${event} on ${path__namespace.relative(rootContext, filePath)}`);
256
397
  const ext = path__namespace.extname(filePath);
257
398
  const shouldWatch = [".html", ".js", ".ts", ".jsx", ".tsx"].includes(ext);
399
+ console.log(`[UnoCSS Hybrid] File extension: ${ext}, shouldWatch: ${shouldWatch}`);
258
400
  if (shouldWatch && (event === "change" || event === "add" || event === "unlink")) {
259
- console.log(`[UnoCSS Hybrid] File ${event}: ${path__namespace.relative(rootContext, filePath)}`);
401
+ console.log(`[UnoCSS Hybrid] File ${event}: ${path__namespace.relative(rootContext, filePath)} - queuing`);
402
+ changedFilesInBatch.push(filePath);
260
403
  if (regenerateTimer) {
261
404
  clearTimeout(regenerateTimer);
262
405
  }
263
406
  regenerateTimer = setTimeout(async () => {
264
- console.log("[UnoCSS Hybrid] Regenerating CSS...");
265
- await generateCSS();
266
- cssVersion = Date.now();
267
- console.log("[UnoCSS Hybrid] Hot updating CSS in browser...");
268
- devServerInstance?.sockWrite("unocss-css-update", {
269
- version: cssVersion,
270
- path: "/uno.css"
271
- });
407
+ console.log(`[UnoCSS Hybrid] Regenerating CSS... (${changedFilesInBatch.length} file(s) changed)`);
408
+ await generateCSS(changedFilesInBatch);
409
+ const version = Date.now();
410
+ console.log("[UnoCSS Hybrid] Broadcasting CSS update via WebSocket, version:", version);
411
+ if (wss) {
412
+ const message = JSON.stringify({
413
+ type: "css-update",
414
+ version
415
+ });
416
+ wss.clients.forEach((client) => {
417
+ if (client.readyState === 1) {
418
+ client.send(message);
419
+ }
420
+ });
421
+ console.log(`[UnoCSS Hybrid] Broadcasted to ${wss.clients.size} client(s)`);
422
+ } else {
423
+ console.warn("[UnoCSS Hybrid] WebSocket server not available");
424
+ }
425
+ changedFilesInBatch = [];
272
426
  }, 300);
427
+ } else {
428
+ console.log(`[UnoCSS Hybrid] Event ignored - ext: ${ext}, event: ${event}`);
273
429
  }
274
430
  });
275
431
  console.log("[UnoCSS Hybrid] Watch mode: enabled");
@@ -288,6 +444,13 @@ function pluginUnocss(options = {}) {
288
444
  );
289
445
  }
290
446
  });
447
+ api.onCloseDevServer(async () => {
448
+ if (wss) {
449
+ console.log("[UnoCSS Hybrid] Closing WebSocket server...");
450
+ wss.close();
451
+ wss = null;
452
+ }
453
+ });
291
454
  }
292
455
  };
293
456
  }
@@ -1 +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';\r\nimport type { UserConfig } from 'unocss';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { createHash } from 'crypto';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\nexport interface PluginUnocssOptions {\r\n /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */\r\n unocssConfig?: string | UserConfig;\r\n\r\n /**\r\n * 生成的 CSS 文件输出路径\r\n *\r\n * 路径解析规则:\r\n * - 以 './' 开头: 相对于项目根目录 (推荐)\r\n * - 相对路径: 相对于插件目录\r\n * - 绝对路径: 直接使用\r\n * - 未配置: 默认为 'generated/uno.css' 相对于插件目录\r\n */\r\n outputPath?: string;\r\n\r\n /** 扫描的内容文件路径模式 (支持 glob 模式) */\r\n contentPatterns?: string[];\r\n\r\n /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */\r\n watchDirectory?: string;\r\n\r\n /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */\r\n autoInject?: boolean;\r\n\r\n /** 是否在开发环境启用文件监听模式 (默认: true) */\r\n watch?: boolean;\r\n\r\n /**\r\n * 是否在生产环境为 CSS 文件名添加 content hash\r\n *\r\n * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)\r\n */\r\n enableHash?: boolean;\r\n}\r\n\r\n/**\r\n * UnoCSS Plugin for Rsbuild\r\n *\r\n * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.\r\n *\r\n * 特性:\r\n * - 扫描项目文件并生成独立的 CSS 文件\r\n * - 开发环境支持文件监听和热更新\r\n * - 生产环境支持 CSS 文件名 hash\r\n * - 自动在 HTML 中注入 CSS link 标签\r\n *\r\n * @param options - 插件配置选项\r\n * @returns Rsbuild 插件实例\r\n *\r\n * @example\r\n * ```ts\r\n * import { pluginUnocss } from '@ikkin/plugin-unocss';\r\n *\r\n * export default {\r\n * plugins: [\r\n * pluginUnocss({\r\n * outputPath: './src/generated/uno.css',\r\n * enableHash: true,\r\n * })\r\n * ]\r\n * };\r\n * ```\r\n */\r\nexport function pluginUnocss(\r\n options: PluginUnocssOptions = {},\r\n): RsbuildPlugin {\r\n const {\r\n unocssConfig = './uno.config.ts',\r\n outputPath = '',\r\n contentPatterns = [\r\n './src/**/*.{html,js,ts,jsx,tsx}',\r\n './index.html',\r\n ],\r\n watchDirectory = 'src',\r\n autoInject = true,\r\n watch = true,\r\n enableHash = true,\r\n } = options;\r\n\r\n return {\r\n name: 'unocss',\r\n\r\n setup(api) {\r\n const rootContext = api.context.rootPath;\r\n\r\n // 解析输出路径\r\n const pluginDir = __dirname;\r\n let resolvedOutputPath: string;\r\n\r\n if (!outputPath) {\r\n // 未配置:使用默认路径(相对于插件目录)\r\n resolvedOutputPath = path.resolve(pluginDir, './generated/uno.css');\r\n } else if (path.isAbsolute(outputPath)) {\r\n // 绝对路径:直接使用\r\n resolvedOutputPath = outputPath;\r\n } else if (outputPath.startsWith('./')) {\r\n // 以 ./ 开头:相对于项目根目录(推荐,更明确)\r\n resolvedOutputPath = path.resolve(rootContext, outputPath);\r\n } else {\r\n // 相对路径(不带 ./):相对于插件目录(更简洁)\r\n resolvedOutputPath = path.resolve(pluginDir, outputPath);\r\n }\r\n\r\n // 用于保存当前生成的 CSS 文件路径(可能包含 hash)\r\n let currentCssPath = resolvedOutputPath;\r\n\r\n // 用于保存 dev server 实例,以便在文件变化时触发刷新\r\n let devServerInstance: any = null;\r\n\r\n // 通过环境变量判断是否是生产环境\r\n const isProd = process.env.NODE_ENV === 'production';\r\n\r\n // 读取 UnoCSS 配置\r\n const loadConfig = async (): Promise<UserConfig> => {\r\n if (typeof unocssConfig === 'string') {\r\n const configPath = path.resolve(rootContext, unocssConfig);\r\n try {\r\n const configModule = await import(\r\n `file:///${configPath.replace(/\\\\/g, '/')}`\r\n );\r\n return configModule.default || configModule;\r\n } catch (err) {\r\n console.warn(\r\n `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`,\r\n );\r\n return {};\r\n }\r\n }\r\n return unocssConfig;\r\n };\r\n\r\n // 生成 UnoCSS CSS\r\n const generateCSS = async (): Promise<string> => {\r\n console.log('[UnoCSS Hybrid] Generating CSS...');\r\n\r\n try {\r\n const config = await loadConfig();\r\n\r\n // 扫描文件\r\n const { globby } = await import('globby');\r\n const files = await globby(contentPatterns, {\r\n cwd: rootContext,\r\n absolute: true,\r\n });\r\n\r\n if (files.length === 0) {\r\n console.warn('[UnoCSS Hybrid] No files found to scan');\r\n return resolvedOutputPath;\r\n }\r\n\r\n console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);\r\n console.log(`[UnoCSS Hybrid] Files:`, files.map(f => path.relative(rootContext, f)));\r\n\r\n // 使用 UnoCSS generator\r\n const { createGenerator } = await import('unocss');\r\n const generator = await createGenerator(config);\r\n\r\n // 读取所有文件内容并生成\r\n const allContents = files.map(file => {\r\n const content = fs.readFileSync(file, 'utf-8');\r\n return content;\r\n }).join('\\n');\r\n\r\n // 生成 CSS\r\n const result = await generator.generate(allContents);\r\n\r\n if (!result.css || result.css.trim().length === 0) {\r\n console.warn('[UnoCSS Hybrid] Generated CSS is empty');\r\n return resolvedOutputPath;\r\n }\r\n\r\n // 计算输出路径\r\n let finalOutputPath: string;\r\n if (isProd && enableHash) {\r\n // 生产环境:直接生成到 dist 目录,文件名带 hash\r\n const distDir = path.join(rootContext, 'dist');\r\n const hash = createHash('md5').update(result.css).digest('hex').substring(0, 8);\r\n finalOutputPath = path.join(distDir, `uno.${hash}.css`);\r\n currentCssPath = finalOutputPath;\r\n } else {\r\n // 开发环境:生成到插件目录的 generated 子目录,文件名固定\r\n finalOutputPath = resolvedOutputPath;\r\n currentCssPath = resolvedOutputPath;\r\n }\r\n\r\n // 确保输出目录存在\r\n const outputDir = path.dirname(finalOutputPath);\r\n if (!fs.existsSync(outputDir)) {\r\n fs.mkdirSync(outputDir, { recursive: true });\r\n }\r\n\r\n // 写入 CSS 文件\r\n fs.writeFileSync(finalOutputPath, result.css);\r\n\r\n const stats = fs.statSync(finalOutputPath);\r\n console.log(\r\n `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`,\r\n );\r\n\r\n return finalOutputPath;\r\n } catch (err) {\r\n console.error('[UnoCSS Hybrid] Failed to generate CSS:', err);\r\n return resolvedOutputPath;\r\n }\r\n };\r\n\r\n // 生产环境:构建前生成 CSS\r\n if (isProd) {\r\n api.onBeforeBuild(async () => {\r\n await generateCSS();\r\n });\r\n }\r\n\r\n // 用于保存 CSS 版本号,强制浏览器重新加载\r\n let cssVersion = Date.now();\r\n\r\n // 自动注入:通过修改 HTML 标签来注入 CSS(不污染源码)\r\n if (autoInject) {\r\n api.modifyHTMLTags((tags) => {\r\n let cssPath: string;\r\n\r\n if (isProd && enableHash) {\r\n // 生产环境:使用带 hash 的文件名\r\n const cssFileName = path.basename(currentCssPath);\r\n cssPath = `/${cssFileName}`;\r\n } else {\r\n // 开发环境或未启用 hash:使用固定文件名\r\n cssPath = '/uno.css';\r\n }\r\n\r\n tags.headTags.unshift({\r\n tag: 'link',\r\n attrs: {\r\n rel: 'stylesheet',\r\n href: cssPath,\r\n },\r\n });\r\n\r\n console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);\r\n\r\n // 开发环境:注入热更新脚本\r\n if (!isProd) {\r\n const hotReloadScript = `\r\n (() => {\r\n window.__unocss_version__ = ${cssVersion};\r\n\r\n // 监听 WebSocket 消息\r\n const originalOnMessage = window.__whm__?.fns?.push;\r\n if (window.__whm__?.fns) {\r\n window.__whm__.fns.push(function(data) {\r\n // 监听自定义热更新事件\r\n if (data.type === 'unocss-css-update') {\r\n const { version } = data.data;\r\n if (version !== window.__unocss_version__) {\r\n window.__unocss_version__ = version;\r\n\r\n // 查找 UnoCSS 的 link 标签\r\n const link = document.querySelector('link[href*=\"/uno.css\"]');\r\n if (link) {\r\n // 更新 href 强制重新加载\r\n const href = link.getAttribute('href');\r\n if (href) {\r\n // 添加版本号作为查询参数\r\n const newHref = href.split('?')[0] + '?v=' + version;\r\n link.setAttribute('href', newHref);\r\n console.log('[UnoCSS] CSS hot updated:', newHref);\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n })();\r\n `;\r\n tags.bodyTags.push({\r\n tag: 'script',\r\n children: hotReloadScript,\r\n });\r\n console.log('[UnoCSS Hybrid] Hot reload script injected');\r\n }\r\n\r\n return tags;\r\n });\r\n }\r\n\r\n // 开发环境:配置静态文件服务和热更新\r\n if (!isProd) {\r\n\r\n api.modifyRsbuildConfig((config) => {\r\n const existingSetupMiddlewares = config.dev?.setupMiddlewares;\r\n const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares)\r\n ? existingSetupMiddlewares\r\n : existingSetupMiddlewares\r\n ? [existingSetupMiddlewares]\r\n : [];\r\n\r\n return {\r\n ...config,\r\n dev: {\r\n ...config.dev,\r\n setupMiddlewares: [\r\n ...setupMiddlewaresArray,\r\n (middlewares, devServer) => {\r\n // 保存 dev server 实例\r\n devServerInstance = devServer;\r\n\r\n // 添加自定义中间件来提供 CSS 文件\r\n middlewares.unshift(async (req, res, next) => {\r\n if (req.url?.startsWith('/uno.css')) {\r\n try {\r\n // 检查文件是否存在\r\n if (!fs.existsSync(resolvedOutputPath)) {\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('CSS file not found. Please restart the dev server to generate it.');\r\n console.warn('[UnoCSS Hybrid] CSS file not found at:', resolvedOutputPath);\r\n return;\r\n }\r\n\r\n // 设置正确的 Content-Type 和缓存控制\r\n res.setHeader('Content-Type', 'text/css; charset=utf-8');\r\n res.setHeader('Cache-Control', 'no-cache');\r\n\r\n const css = fs.readFileSync(resolvedOutputPath, 'utf-8');\r\n res.end(css);\r\n } catch (err) {\r\n console.error('[UnoCSS Hybrid] Error serving CSS:', err);\r\n res.statusCode = 500;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Error loading CSS file. Check console for details.');\r\n }\r\n } else {\r\n next();\r\n }\r\n });\r\n },\r\n ],\r\n },\r\n };\r\n });\r\n\r\n // 启动时生成 CSS\r\n api.onBeforeStartDevServer(async () => {\r\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\r\n await generateCSS();\r\n });\r\n\r\n // 合并启动后的操作\r\n api.onAfterStartDevServer(async () => {\r\n console.log('[UnoCSS Hybrid] Dev server started, CSS available at /uno.css');\r\n\r\n // Watch 模式\r\n if (watch) {\r\n try {\r\n // 动态导入 chokidar\r\n const chokidar = await import('chokidar');\r\n\r\n // 监听指定目录\r\n const watchDir = path.resolve(rootContext, watchDirectory);\r\n\r\n // 监听内容文件变化\r\n const watcher = chokidar.watch(watchDir, {\r\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\r\n ignoreInitial: true,\r\n persistent: true,\r\n });\r\n\r\n // 防抖:避免频繁重新生成\r\n let regenerateTimer: NodeJS.Timeout | null = null;\r\n\r\n watcher.on('all', async (event: string, filePath: string) => {\r\n // 检查文件扩展名\r\n const ext = path.extname(filePath);\r\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\r\n\r\n // 监听文件添加、修改和删除\r\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\r\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);\r\n\r\n if (regenerateTimer) {\r\n clearTimeout(regenerateTimer);\r\n }\r\n\r\n regenerateTimer = setTimeout(async () => {\r\n console.log('[UnoCSS Hybrid] Regenerating CSS...');\r\n await generateCSS();\r\n\r\n // 更新版本号\r\n cssVersion = Date.now();\r\n\r\n // 触发 CSS 热更新(发送自定义事件)\r\n console.log('[UnoCSS Hybrid] Hot updating CSS in browser...');\r\n devServerInstance?.sockWrite('unocss-css-update', {\r\n version: cssVersion,\r\n path: '/uno.css',\r\n });\r\n }, 300);\r\n }\r\n });\r\n\r\n console.log('[UnoCSS Hybrid] Watch mode: enabled');\r\n } catch (err) {\r\n console.warn('[UnoCSS Hybrid] Watch mode not available:', (err as Error).message);\r\n }\r\n }\r\n });\r\n }\r\n\r\n // 构建完成后的日志\r\n api.onAfterBuild(() => {\r\n if (isProd && fs.existsSync(currentCssPath)) {\r\n const stats = fs.statSync(currentCssPath);\r\n const fileName = path.basename(currentCssPath);\r\n console.log(\r\n `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`,\r\n );\r\n }\r\n });\r\n },\r\n };\r\n}\r\n\r\nexport default pluginUnocss;\r\n"]}
1
+ {"version":3,"sources":["../plugin-unocss.ts"],"names":["__filename","fileURLToPath","__dirname","path","createHash","fs","relPath","createGenerator","generator","result","stats"],"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 // 用于保存 WebSocket 服务器实例和端口\n let wss: any = null;\n // 动态分配的 WebSocket 端口(避免端口冲突)\n let wsPort = 0;\n\n // 通过环境变量判断是否是生产环境\n const isProd = process.env.NODE_ENV === 'production';\n\n // 文件内容哈希缓存(用于增量生成)\n const fileContentCache = new Map<string, string>();\n // 上次生成的完整 CSS 内容(用于比较)\n let lastGeneratedCSS = '';\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\n // 开发环境:注入热更新脚本和 WebSocket 端口\n if (!isProd) {\n const hotReloadScript = `\n (function() {\n function reloadCSS(version) {\n const oldLink = document.querySelector('link[href*=\"/uno.css\"]');\n if (oldLink && oldLink.parentNode) {\n const newLink = document.createElement('link');\n newLink.rel = 'stylesheet';\n newLink.href = '/uno.css?v=' + version;\n oldLink.parentNode.insertBefore(newLink, oldLink);\n\n newLink.onload = function() {\n if (oldLink.parentNode) {\n oldLink.parentNode.removeChild(oldLink);\n }\n };\n\n newLink.onerror = function() {\n console.error('[UnoCSS] Failed to reload CSS');\n if (newLink.parentNode) {\n newLink.parentNode.removeChild(newLink);\n }\n };\n }\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n const host = window.location.hostname;\n const wsUrl = protocol + '//' + host + ':${wsPort}';\n\n let socket = null;\n let reconnectCount = 0;\n const maxReconnect = 10;\n\n function connect() {\n if (reconnectCount === 0) {\n console.log('[UnoCSS] WebSocket connecting to ' + wsUrl);\n }\n\n socket = new WebSocket(wsUrl);\n\n socket.onopen = function() {\n console.log('[UnoCSS] WebSocket connected.');\n reconnectCount = 0;\n };\n\n socket.onmessage = function(event) {\n try {\n const data = JSON.parse(event.data);\n if (data.type === 'css-update' && data.version) {\n reloadCSS(data.version);\n }\n } catch (e) {\n // 忽略解析错误\n }\n };\n\n socket.onclose = function() {\n if (reconnectCount >= maxReconnect) {\n console.warn('[UnoCSS] WebSocket connection failed after maximum retry attempts.');\n return;\n }\n\n if (reconnectCount === 0) {\n console.log('[UnoCSS] WebSocket connection lost. Reconnecting...');\n }\n\n reconnectCount++;\n const delay = Math.min(1000 * Math.pow(1.5, reconnectCount), 30000);\n setTimeout(connect, delay);\n };\n\n socket.onerror = function(error) {\n console.error('[UnoCSS] WebSocket error:', error);\n };\n }\n\n connect();\n })();\n `;\n tags.bodyTags.push({\n tag: 'script',\n children: hotReloadScript,\n });\n console.log('[UnoCSS Hybrid] Hot reload script injected');\n }\n\n return tags;\n });\n }\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 // 生成文件内容的哈希\n const getFileHash = (content: string): string => {\n return createHash('md5').update(content).digest('hex');\n };\n\n // 生成 UnoCSS CSS(开发模式支持增量生成)\n const generateCSS = async (changedFiles?: string[]): Promise<string> => {\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 // 开发模式的增量生成逻辑\n if (!isProd && changedFiles && fileContentCache.size > 0) {\n // 检测变化的文件\n const changedHashes = new Set<string>();\n const changedFilesContent: string[] = [];\n\n for (const file of changedFiles) {\n if (!fs.existsSync(file)) {\n // 文件被删除,从缓存中移除\n const relPath = path.relative(rootContext, file);\n fileContentCache.delete(relPath);\n console.log(`[UnoCSS Hybrid] File removed: ${relPath}`);\n continue;\n }\n\n const content = fs.readFileSync(file, 'utf-8');\n const hash = getFileHash(content);\n const relPath = path.relative(rootContext, file);\n\n if (fileContentCache.get(relPath) !== hash) {\n // 文件内容变化了\n fileContentCache.set(relPath, hash);\n changedFilesContent.push(content);\n changedHashes.add(hash);\n console.log(`[UnoCSS Hybrid] File changed: ${relPath} (hash: ${hash.substring(0, 8)})`);\n }\n }\n\n // 如果有文件变化,进行增量生成\n if (changedFilesContent.length > 0) {\n console.log(`[UnoCSS Hybrid] Incremental generation: ${changedFilesContent.length} changed file(s)`);\n\n const { createGenerator } = await import('unocss');\n const generator = await createGenerator(config);\n\n // 只生成变化文件的新 CSS\n const changedContent = changedFilesContent.join('\\n');\n const result = await generator.generate(changedContent);\n\n if (result.css && result.css.trim().length > 0) {\n // 读取旧的 CSS 文件\n let existingCSS = '';\n if (fs.existsSync(resolvedOutputPath)) {\n existingCSS = fs.readFileSync(resolvedOutputPath, 'utf-8');\n }\n\n // 解析新 CSS 的类名\n const newClasses = new Set(\n result.css.match(/\\.(\\w[-\\w]*)/g)?.map(cls => cls.slice(1)) || []\n );\n\n // 过滤掉旧 CSS 中被新 CSS 覆盖的类\n const existingCSSLines = existingCSS.split('\\n').filter(line => {\n const match = line.match(/^\\.([\\w-]+)/);\n if (match) {\n return !newClasses.has(match[1]);\n }\n return true;\n });\n\n // 合并:保留旧 CSS 中未被新 CSS 覆盖的部分,加上新 CSS\n const mergedCSS = [...existingCSSLines, result.css].join('\\n').replace(/\\n\\n+/g, '\\n');\n\n // 写入合并后的 CSS\n fs.writeFileSync(resolvedOutputPath, mergedCSS);\n\n const stats = fs.statSync(resolvedOutputPath);\n console.log(\n `[UnoCSS Hybrid] CSS updated incrementally: ${resolvedOutputPath} (${(stats.size / 1024).toFixed(2)} KB, ${newClasses.size} new classes)`,\n );\n\n return resolvedOutputPath;\n }\n } else {\n console.log('[UnoCSS Hybrid] No content changes detected, skipping generation');\n return resolvedOutputPath;\n }\n }\n\n // 全量生成(生产环境或首次生成)\n console.log('[UnoCSS Hybrid] Full CSS generation...');\n\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 const hash = getFileHash(content);\n const relPath = path.relative(rootContext, file);\n fileContentCache.set(relPath, hash);\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 lastGeneratedCSS = 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 // 开发环境:配置静态文件服务和热更新\n if (!isProd) {\n\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 // 添加自定义中间件来提供 CSS 文件\n middlewares.unshift(async (req, res, next) => {\n // 处理带查询参数的请求 (如 /uno.css?v=123456)\n if (req.url?.startsWith('/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 // 禁用缓存,确保每次都获取最新内容\n res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\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 并创建 WebSocket 服务器\n api.onBeforeStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\n await generateCSS();\n\n // 提前创建 WebSocket 服务器,以便在 HTML 生成前获取端口\n try {\n const { WebSocketServer } = await import('ws');\n\n // 创建 WebSocket 服务器,使用 port: 0 让系统自动分配可用端口\n wss = new WebSocketServer({ port: 0 });\n wsPort = (wss.address() as any).port;\n\n console.log(`[UnoCSS Hybrid] WebSocket server created on port ${wsPort}`);\n\n // 当客户端连接时\n wss.on('connection', (ws: any) => {\n console.log('[UnoCSS Hybrid] Client connected to WebSocket');\n\n ws.on('close', () => {\n console.log('[UnoCSS Hybrid] Client disconnected from WebSocket');\n });\n });\n\n console.log('[UnoCSS Hybrid] UnoCSS HMR WebSocket server ready');\n } catch (err) {\n console.error('[UnoCSS Hybrid] Failed to create WebSocket server:', err);\n }\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 console.log('[UnoCSS Hybrid] Initializing watch mode...');\n\n // 动态导入 chokidar\n const chokidar = await import('chokidar');\n console.log('[UnoCSS Hybrid] chokidar imported successfully');\n\n // 监听指定目录\n const watchDir = path.resolve(rootContext, watchDirectory);\n console.log('[UnoCSS Hybrid] Watching directory:', watchDir);\n\n // 监听内容文件变化\n const watcher = chokidar.watch(watchDir, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n ignoreInitial: true,\n persistent: true,\n });\n\n console.log('[UnoCSS Hybrid] Watcher created, setting up event handlers...');\n\n watcher.on('ready', () => {\n console.log('[UnoCSS Hybrid] Watcher ready, scanning for files...');\n });\n\n watcher.on('error', (error) => {\n console.error('[UnoCSS Hybrid] Watcher error:', error);\n });\n\n // 防抖:避免频繁重新生成\n let regenerateTimer: NodeJS.Timeout | null = null;\n // 收集本次防抖周期内的变化文件\n let changedFilesInBatch: string[] = [];\n\n watcher.on('all', async (event: string, filePath: string) => {\n console.log(`[UnoCSS Hybrid] Watcher event: ${event} on ${path.relative(rootContext, filePath)}`);\n\n // 检查文件扩展名\n const ext = path.extname(filePath);\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\n\n console.log(`[UnoCSS Hybrid] File extension: ${ext}, shouldWatch: ${shouldWatch}`);\n\n // 监听文件添加、修改和删除\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)} - queuing`);\n\n // 将变化的文件添加到批次中\n changedFilesInBatch.push(filePath);\n\n if (regenerateTimer) {\n clearTimeout(regenerateTimer);\n }\n\n regenerateTimer = setTimeout(async () => {\n console.log(`[UnoCSS Hybrid] Regenerating CSS... (${changedFilesInBatch.length} file(s) changed)`);\n\n // 传递变化文件列表,启用增量生成\n await generateCSS(changedFilesInBatch);\n\n const version = Date.now();\n console.log('[UnoCSS Hybrid] Broadcasting CSS update via WebSocket, version:', version);\n\n // 通过我们自己的 WebSocket 广播更新\n if (wss) {\n const message = JSON.stringify({\n type: 'css-update',\n version: version,\n });\n\n wss.clients.forEach((client: any) => {\n if (client.readyState === 1) { // WebSocket.OPEN\n client.send(message);\n }\n });\n\n console.log(`[UnoCSS Hybrid] Broadcasted to ${wss.clients.size} client(s)`);\n } else {\n console.warn('[UnoCSS Hybrid] WebSocket server not available');\n }\n\n // 清空批次\n changedFilesInBatch = [];\n }, 300);\n } else {\n console.log(`[UnoCSS Hybrid] Event ignored - ext: ${ext}, event: ${event}`);\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 // 关闭 dev server 时清理 WebSocket 服务器\n api.onCloseDevServer(async () => {\n if (wss) {\n console.log('[UnoCSS Hybrid] Closing WebSocket server...');\n wss.close();\n wss = null;\n }\n });\n },\n };\n}\n\nexport default pluginUnocss;\n"]}
@@ -35,8 +35,122 @@ function pluginUnocss(options = {}) {
35
35
  resolvedOutputPath = path.resolve(pluginDir, outputPath);
36
36
  }
37
37
  let currentCssPath = resolvedOutputPath;
38
- let devServerInstance = null;
38
+ let wss = null;
39
+ let wsPort = 0;
39
40
  const isProd = process.env.NODE_ENV === "production";
41
+ const fileContentCache = /* @__PURE__ */ new Map();
42
+ let lastGeneratedCSS = "";
43
+ if (isProd) {
44
+ api.onBeforeBuild(async () => {
45
+ await generateCSS();
46
+ });
47
+ }
48
+ if (autoInject) {
49
+ api.modifyHTMLTags((tags) => {
50
+ let cssPath;
51
+ if (isProd && enableHash) {
52
+ const cssFileName = path.basename(currentCssPath);
53
+ cssPath = `/${cssFileName}`;
54
+ } else {
55
+ cssPath = "/uno.css";
56
+ }
57
+ tags.headTags.unshift({
58
+ tag: "link",
59
+ attrs: {
60
+ rel: "stylesheet",
61
+ href: cssPath
62
+ }
63
+ });
64
+ console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
65
+ if (!isProd) {
66
+ const hotReloadScript = `
67
+ (function() {
68
+ function reloadCSS(version) {
69
+ const oldLink = document.querySelector('link[href*="/uno.css"]');
70
+ if (oldLink && oldLink.parentNode) {
71
+ const newLink = document.createElement('link');
72
+ newLink.rel = 'stylesheet';
73
+ newLink.href = '/uno.css?v=' + version;
74
+ oldLink.parentNode.insertBefore(newLink, oldLink);
75
+
76
+ newLink.onload = function() {
77
+ if (oldLink.parentNode) {
78
+ oldLink.parentNode.removeChild(oldLink);
79
+ }
80
+ };
81
+
82
+ newLink.onerror = function() {
83
+ console.error('[UnoCSS] Failed to reload CSS');
84
+ if (newLink.parentNode) {
85
+ newLink.parentNode.removeChild(newLink);
86
+ }
87
+ };
88
+ }
89
+ }
90
+
91
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
92
+ const host = window.location.hostname;
93
+ const wsUrl = protocol + '//' + host + ':${wsPort}';
94
+
95
+ let socket = null;
96
+ let reconnectCount = 0;
97
+ const maxReconnect = 10;
98
+
99
+ function connect() {
100
+ if (reconnectCount === 0) {
101
+ console.log('[UnoCSS] WebSocket connecting to ' + wsUrl);
102
+ }
103
+
104
+ socket = new WebSocket(wsUrl);
105
+
106
+ socket.onopen = function() {
107
+ console.log('[UnoCSS] WebSocket connected.');
108
+ reconnectCount = 0;
109
+ };
110
+
111
+ socket.onmessage = function(event) {
112
+ try {
113
+ const data = JSON.parse(event.data);
114
+ if (data.type === 'css-update' && data.version) {
115
+ reloadCSS(data.version);
116
+ }
117
+ } catch (e) {
118
+ // \u5FFD\u7565\u89E3\u6790\u9519\u8BEF
119
+ }
120
+ };
121
+
122
+ socket.onclose = function() {
123
+ if (reconnectCount >= maxReconnect) {
124
+ console.warn('[UnoCSS] WebSocket connection failed after maximum retry attempts.');
125
+ return;
126
+ }
127
+
128
+ if (reconnectCount === 0) {
129
+ console.log('[UnoCSS] WebSocket connection lost. Reconnecting...');
130
+ }
131
+
132
+ reconnectCount++;
133
+ const delay = Math.min(1000 * Math.pow(1.5, reconnectCount), 30000);
134
+ setTimeout(connect, delay);
135
+ };
136
+
137
+ socket.onerror = function(error) {
138
+ console.error('[UnoCSS] WebSocket error:', error);
139
+ };
140
+ }
141
+
142
+ connect();
143
+ })();
144
+ `;
145
+ tags.bodyTags.push({
146
+ tag: "script",
147
+ children: hotReloadScript
148
+ });
149
+ console.log("[UnoCSS Hybrid] Hot reload script injected");
150
+ }
151
+ return tags;
152
+ });
153
+ }
40
154
  const loadConfig = async () => {
41
155
  if (typeof unocssConfig === "string") {
42
156
  const configPath = path.resolve(rootContext, unocssConfig);
@@ -52,8 +166,10 @@ function pluginUnocss(options = {}) {
52
166
  }
53
167
  return unocssConfig;
54
168
  };
55
- const generateCSS = async () => {
56
- console.log("[UnoCSS Hybrid] Generating CSS...");
169
+ const getFileHash = (content) => {
170
+ return createHash("md5").update(content).digest("hex");
171
+ };
172
+ const generateCSS = async (changedFiles) => {
57
173
  try {
58
174
  const config = await loadConfig();
59
175
  const { globby } = await import('globby');
@@ -65,12 +181,68 @@ function pluginUnocss(options = {}) {
65
181
  console.warn("[UnoCSS Hybrid] No files found to scan");
66
182
  return resolvedOutputPath;
67
183
  }
68
- console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);
69
- console.log(`[UnoCSS Hybrid] Files:`, files.map((f) => path.relative(rootContext, f)));
184
+ if (!isProd && changedFiles && fileContentCache.size > 0) {
185
+ const changedHashes = /* @__PURE__ */ new Set();
186
+ const changedFilesContent = [];
187
+ for (const file of changedFiles) {
188
+ if (!fs.existsSync(file)) {
189
+ const relPath2 = path.relative(rootContext, file);
190
+ fileContentCache.delete(relPath2);
191
+ console.log(`[UnoCSS Hybrid] File removed: ${relPath2}`);
192
+ continue;
193
+ }
194
+ const content = fs.readFileSync(file, "utf-8");
195
+ const hash = getFileHash(content);
196
+ const relPath = path.relative(rootContext, file);
197
+ if (fileContentCache.get(relPath) !== hash) {
198
+ fileContentCache.set(relPath, hash);
199
+ changedFilesContent.push(content);
200
+ changedHashes.add(hash);
201
+ console.log(`[UnoCSS Hybrid] File changed: ${relPath} (hash: ${hash.substring(0, 8)})`);
202
+ }
203
+ }
204
+ if (changedFilesContent.length > 0) {
205
+ console.log(`[UnoCSS Hybrid] Incremental generation: ${changedFilesContent.length} changed file(s)`);
206
+ const { createGenerator: createGenerator2 } = await import('unocss');
207
+ const generator2 = await createGenerator2(config);
208
+ const changedContent = changedFilesContent.join("\n");
209
+ const result2 = await generator2.generate(changedContent);
210
+ if (result2.css && result2.css.trim().length > 0) {
211
+ let existingCSS = "";
212
+ if (fs.existsSync(resolvedOutputPath)) {
213
+ existingCSS = fs.readFileSync(resolvedOutputPath, "utf-8");
214
+ }
215
+ const newClasses = new Set(
216
+ result2.css.match(/\.(\w[-\w]*)/g)?.map((cls) => cls.slice(1)) || []
217
+ );
218
+ const existingCSSLines = existingCSS.split("\n").filter((line) => {
219
+ const match = line.match(/^\.([\w-]+)/);
220
+ if (match) {
221
+ return !newClasses.has(match[1]);
222
+ }
223
+ return true;
224
+ });
225
+ const mergedCSS = [...existingCSSLines, result2.css].join("\n").replace(/\n\n+/g, "\n");
226
+ fs.writeFileSync(resolvedOutputPath, mergedCSS);
227
+ const stats2 = fs.statSync(resolvedOutputPath);
228
+ console.log(
229
+ `[UnoCSS Hybrid] CSS updated incrementally: ${resolvedOutputPath} (${(stats2.size / 1024).toFixed(2)} KB, ${newClasses.size} new classes)`
230
+ );
231
+ return resolvedOutputPath;
232
+ }
233
+ } else {
234
+ console.log("[UnoCSS Hybrid] No content changes detected, skipping generation");
235
+ return resolvedOutputPath;
236
+ }
237
+ }
238
+ console.log("[UnoCSS Hybrid] Full CSS generation...");
70
239
  const { createGenerator } = await import('unocss');
71
240
  const generator = await createGenerator(config);
72
241
  const allContents = files.map((file) => {
73
242
  const content = fs.readFileSync(file, "utf-8");
243
+ const hash = getFileHash(content);
244
+ const relPath = path.relative(rootContext, file);
245
+ fileContentCache.set(relPath, hash);
74
246
  return content;
75
247
  }).join("\n");
76
248
  const result = await generator.generate(allContents);
@@ -93,6 +265,7 @@ function pluginUnocss(options = {}) {
93
265
  fs.mkdirSync(outputDir, { recursive: true });
94
266
  }
95
267
  fs.writeFileSync(finalOutputPath, result.css);
268
+ lastGeneratedCSS = result.css;
96
269
  const stats = fs.statSync(finalOutputPath);
97
270
  console.log(
98
271
  `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`
@@ -108,66 +281,6 @@ function pluginUnocss(options = {}) {
108
281
  await generateCSS();
109
282
  });
110
283
  }
111
- let cssVersion = Date.now();
112
- if (autoInject) {
113
- api.modifyHTMLTags((tags) => {
114
- let cssPath;
115
- if (isProd && enableHash) {
116
- const cssFileName = path.basename(currentCssPath);
117
- cssPath = `/${cssFileName}`;
118
- } else {
119
- cssPath = "/uno.css";
120
- }
121
- tags.headTags.unshift({
122
- tag: "link",
123
- attrs: {
124
- rel: "stylesheet",
125
- href: cssPath
126
- }
127
- });
128
- console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);
129
- if (!isProd) {
130
- const hotReloadScript = `
131
- (() => {
132
- window.__unocss_version__ = ${cssVersion};
133
-
134
- // \u76D1\u542C WebSocket \u6D88\u606F
135
- const originalOnMessage = window.__whm__?.fns?.push;
136
- if (window.__whm__?.fns) {
137
- window.__whm__.fns.push(function(data) {
138
- // \u76D1\u542C\u81EA\u5B9A\u4E49\u70ED\u66F4\u65B0\u4E8B\u4EF6
139
- if (data.type === 'unocss-css-update') {
140
- const { version } = data.data;
141
- if (version !== window.__unocss_version__) {
142
- window.__unocss_version__ = version;
143
-
144
- // \u67E5\u627E UnoCSS \u7684 link \u6807\u7B7E
145
- const link = document.querySelector('link[href*="/uno.css"]');
146
- if (link) {
147
- // \u66F4\u65B0 href \u5F3A\u5236\u91CD\u65B0\u52A0\u8F7D
148
- const href = link.getAttribute('href');
149
- if (href) {
150
- // \u6DFB\u52A0\u7248\u672C\u53F7\u4F5C\u4E3A\u67E5\u8BE2\u53C2\u6570
151
- const newHref = href.split('?')[0] + '?v=' + version;
152
- link.setAttribute('href', newHref);
153
- console.log('[UnoCSS] CSS hot updated:', newHref);
154
- }
155
- }
156
- }
157
- }
158
- });
159
- }
160
- })();
161
- `;
162
- tags.bodyTags.push({
163
- tag: "script",
164
- children: hotReloadScript
165
- });
166
- console.log("[UnoCSS Hybrid] Hot reload script injected");
167
- }
168
- return tags;
169
- });
170
- }
171
284
  if (!isProd) {
172
285
  api.modifyRsbuildConfig((config) => {
173
286
  const existingSetupMiddlewares = config.dev?.setupMiddlewares;
@@ -178,8 +291,7 @@ function pluginUnocss(options = {}) {
178
291
  ...config.dev,
179
292
  setupMiddlewares: [
180
293
  ...setupMiddlewaresArray,
181
- (middlewares, devServer) => {
182
- devServerInstance = devServer;
294
+ (middlewares, _devServer) => {
183
295
  middlewares.unshift(async (req, res, next) => {
184
296
  if (req.url?.startsWith("/uno.css")) {
185
297
  try {
@@ -191,7 +303,9 @@ function pluginUnocss(options = {}) {
191
303
  return;
192
304
  }
193
305
  res.setHeader("Content-Type", "text/css; charset=utf-8");
194
- res.setHeader("Cache-Control", "no-cache");
306
+ res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
307
+ res.setHeader("Pragma", "no-cache");
308
+ res.setHeader("Expires", "0");
195
309
  const css = fs.readFileSync(resolvedOutputPath, "utf-8");
196
310
  res.end(css);
197
311
  } catch (err) {
@@ -212,38 +326,80 @@ function pluginUnocss(options = {}) {
212
326
  api.onBeforeStartDevServer(async () => {
213
327
  console.log("[UnoCSS Hybrid] Development mode: CLI generation enabled");
214
328
  await generateCSS();
329
+ try {
330
+ const { WebSocketServer } = await import('ws');
331
+ wss = new WebSocketServer({ port: 0 });
332
+ wsPort = wss.address().port;
333
+ console.log(`[UnoCSS Hybrid] WebSocket server created on port ${wsPort}`);
334
+ wss.on("connection", (ws) => {
335
+ console.log("[UnoCSS Hybrid] Client connected to WebSocket");
336
+ ws.on("close", () => {
337
+ console.log("[UnoCSS Hybrid] Client disconnected from WebSocket");
338
+ });
339
+ });
340
+ console.log("[UnoCSS Hybrid] UnoCSS HMR WebSocket server ready");
341
+ } catch (err) {
342
+ console.error("[UnoCSS Hybrid] Failed to create WebSocket server:", err);
343
+ }
215
344
  });
216
345
  api.onAfterStartDevServer(async () => {
217
346
  console.log("[UnoCSS Hybrid] Dev server started, CSS available at /uno.css");
218
347
  if (watch) {
219
348
  try {
349
+ console.log("[UnoCSS Hybrid] Initializing watch mode...");
220
350
  const chokidar = await import('chokidar');
351
+ console.log("[UnoCSS Hybrid] chokidar imported successfully");
221
352
  const watchDir = path.resolve(rootContext, watchDirectory);
353
+ console.log("[UnoCSS Hybrid] Watching directory:", watchDir);
222
354
  const watcher = chokidar.watch(watchDir, {
223
355
  ignored: /(^|[\/\\])\../,
224
356
  // ignore dotfiles
225
357
  ignoreInitial: true,
226
358
  persistent: true
227
359
  });
360
+ console.log("[UnoCSS Hybrid] Watcher created, setting up event handlers...");
361
+ watcher.on("ready", () => {
362
+ console.log("[UnoCSS Hybrid] Watcher ready, scanning for files...");
363
+ });
364
+ watcher.on("error", (error) => {
365
+ console.error("[UnoCSS Hybrid] Watcher error:", error);
366
+ });
228
367
  let regenerateTimer = null;
368
+ let changedFilesInBatch = [];
229
369
  watcher.on("all", async (event, filePath) => {
370
+ console.log(`[UnoCSS Hybrid] Watcher event: ${event} on ${path.relative(rootContext, filePath)}`);
230
371
  const ext = path.extname(filePath);
231
372
  const shouldWatch = [".html", ".js", ".ts", ".jsx", ".tsx"].includes(ext);
373
+ console.log(`[UnoCSS Hybrid] File extension: ${ext}, shouldWatch: ${shouldWatch}`);
232
374
  if (shouldWatch && (event === "change" || event === "add" || event === "unlink")) {
233
- console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);
375
+ console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)} - queuing`);
376
+ changedFilesInBatch.push(filePath);
234
377
  if (regenerateTimer) {
235
378
  clearTimeout(regenerateTimer);
236
379
  }
237
380
  regenerateTimer = setTimeout(async () => {
238
- console.log("[UnoCSS Hybrid] Regenerating CSS...");
239
- await generateCSS();
240
- cssVersion = Date.now();
241
- console.log("[UnoCSS Hybrid] Hot updating CSS in browser...");
242
- devServerInstance?.sockWrite("unocss-css-update", {
243
- version: cssVersion,
244
- path: "/uno.css"
245
- });
381
+ console.log(`[UnoCSS Hybrid] Regenerating CSS... (${changedFilesInBatch.length} file(s) changed)`);
382
+ await generateCSS(changedFilesInBatch);
383
+ const version = Date.now();
384
+ console.log("[UnoCSS Hybrid] Broadcasting CSS update via WebSocket, version:", version);
385
+ if (wss) {
386
+ const message = JSON.stringify({
387
+ type: "css-update",
388
+ version
389
+ });
390
+ wss.clients.forEach((client) => {
391
+ if (client.readyState === 1) {
392
+ client.send(message);
393
+ }
394
+ });
395
+ console.log(`[UnoCSS Hybrid] Broadcasted to ${wss.clients.size} client(s)`);
396
+ } else {
397
+ console.warn("[UnoCSS Hybrid] WebSocket server not available");
398
+ }
399
+ changedFilesInBatch = [];
246
400
  }, 300);
401
+ } else {
402
+ console.log(`[UnoCSS Hybrid] Event ignored - ext: ${ext}, event: ${event}`);
247
403
  }
248
404
  });
249
405
  console.log("[UnoCSS Hybrid] Watch mode: enabled");
@@ -262,6 +418,13 @@ function pluginUnocss(options = {}) {
262
418
  );
263
419
  }
264
420
  });
421
+ api.onCloseDevServer(async () => {
422
+ if (wss) {
423
+ console.log("[UnoCSS Hybrid] Closing WebSocket server...");
424
+ wss.close();
425
+ wss = null;
426
+ }
427
+ });
265
428
  }
266
429
  };
267
430
  }
@@ -1 +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';\r\nimport type { UserConfig } from 'unocss';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { createHash } from 'crypto';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\nexport interface PluginUnocssOptions {\r\n /** UnoCSS 配置文件路径或直接配置对象 (默认: './uno.config.ts') */\r\n unocssConfig?: string | UserConfig;\r\n\r\n /**\r\n * 生成的 CSS 文件输出路径\r\n *\r\n * 路径解析规则:\r\n * - 以 './' 开头: 相对于项目根目录 (推荐)\r\n * - 相对路径: 相对于插件目录\r\n * - 绝对路径: 直接使用\r\n * - 未配置: 默认为 'generated/uno.css' 相对于插件目录\r\n */\r\n outputPath?: string;\r\n\r\n /** 扫描的内容文件路径模式 (支持 glob 模式) */\r\n contentPatterns?: string[];\r\n\r\n /** 监听的目录路径 (用于开发环境的 watch 模式, 默认: 'src') */\r\n watchDirectory?: string;\r\n\r\n /** 是否自动在 HTML 中注入生成的 CSS link 标签 (默认: true) */\r\n autoInject?: boolean;\r\n\r\n /** 是否在开发环境启用文件监听模式 (默认: true) */\r\n watch?: boolean;\r\n\r\n /**\r\n * 是否在生产环境为 CSS 文件名添加 content hash\r\n *\r\n * 启用后, 文件名格式为 'uno.[hash].css', hash 基于 CSS 内容生成 (默认: true)\r\n */\r\n enableHash?: boolean;\r\n}\r\n\r\n/**\r\n * UnoCSS Plugin for Rsbuild\r\n *\r\n * 一个用于 Rsbuild 的 UnoCSS 插件,提供 CLI 预生成和自动注入功能.\r\n *\r\n * 特性:\r\n * - 扫描项目文件并生成独立的 CSS 文件\r\n * - 开发环境支持文件监听和热更新\r\n * - 生产环境支持 CSS 文件名 hash\r\n * - 自动在 HTML 中注入 CSS link 标签\r\n *\r\n * @param options - 插件配置选项\r\n * @returns Rsbuild 插件实例\r\n *\r\n * @example\r\n * ```ts\r\n * import { pluginUnocss } from '@ikkin/plugin-unocss';\r\n *\r\n * export default {\r\n * plugins: [\r\n * pluginUnocss({\r\n * outputPath: './src/generated/uno.css',\r\n * enableHash: true,\r\n * })\r\n * ]\r\n * };\r\n * ```\r\n */\r\nexport function pluginUnocss(\r\n options: PluginUnocssOptions = {},\r\n): RsbuildPlugin {\r\n const {\r\n unocssConfig = './uno.config.ts',\r\n outputPath = '',\r\n contentPatterns = [\r\n './src/**/*.{html,js,ts,jsx,tsx}',\r\n './index.html',\r\n ],\r\n watchDirectory = 'src',\r\n autoInject = true,\r\n watch = true,\r\n enableHash = true,\r\n } = options;\r\n\r\n return {\r\n name: 'unocss',\r\n\r\n setup(api) {\r\n const rootContext = api.context.rootPath;\r\n\r\n // 解析输出路径\r\n const pluginDir = __dirname;\r\n let resolvedOutputPath: string;\r\n\r\n if (!outputPath) {\r\n // 未配置:使用默认路径(相对于插件目录)\r\n resolvedOutputPath = path.resolve(pluginDir, './generated/uno.css');\r\n } else if (path.isAbsolute(outputPath)) {\r\n // 绝对路径:直接使用\r\n resolvedOutputPath = outputPath;\r\n } else if (outputPath.startsWith('./')) {\r\n // 以 ./ 开头:相对于项目根目录(推荐,更明确)\r\n resolvedOutputPath = path.resolve(rootContext, outputPath);\r\n } else {\r\n // 相对路径(不带 ./):相对于插件目录(更简洁)\r\n resolvedOutputPath = path.resolve(pluginDir, outputPath);\r\n }\r\n\r\n // 用于保存当前生成的 CSS 文件路径(可能包含 hash)\r\n let currentCssPath = resolvedOutputPath;\r\n\r\n // 用于保存 dev server 实例,以便在文件变化时触发刷新\r\n let devServerInstance: any = null;\r\n\r\n // 通过环境变量判断是否是生产环境\r\n const isProd = process.env.NODE_ENV === 'production';\r\n\r\n // 读取 UnoCSS 配置\r\n const loadConfig = async (): Promise<UserConfig> => {\r\n if (typeof unocssConfig === 'string') {\r\n const configPath = path.resolve(rootContext, unocssConfig);\r\n try {\r\n const configModule = await import(\r\n `file:///${configPath.replace(/\\\\/g, '/')}`\r\n );\r\n return configModule.default || configModule;\r\n } catch (err) {\r\n console.warn(\r\n `[UnoCSS Hybrid] Failed to load config from ${configPath}, using default config`,\r\n );\r\n return {};\r\n }\r\n }\r\n return unocssConfig;\r\n };\r\n\r\n // 生成 UnoCSS CSS\r\n const generateCSS = async (): Promise<string> => {\r\n console.log('[UnoCSS Hybrid] Generating CSS...');\r\n\r\n try {\r\n const config = await loadConfig();\r\n\r\n // 扫描文件\r\n const { globby } = await import('globby');\r\n const files = await globby(contentPatterns, {\r\n cwd: rootContext,\r\n absolute: true,\r\n });\r\n\r\n if (files.length === 0) {\r\n console.warn('[UnoCSS Hybrid] No files found to scan');\r\n return resolvedOutputPath;\r\n }\r\n\r\n console.log(`[UnoCSS Hybrid] Found ${files.length} files to scan`);\r\n console.log(`[UnoCSS Hybrid] Files:`, files.map(f => path.relative(rootContext, f)));\r\n\r\n // 使用 UnoCSS generator\r\n const { createGenerator } = await import('unocss');\r\n const generator = await createGenerator(config);\r\n\r\n // 读取所有文件内容并生成\r\n const allContents = files.map(file => {\r\n const content = fs.readFileSync(file, 'utf-8');\r\n return content;\r\n }).join('\\n');\r\n\r\n // 生成 CSS\r\n const result = await generator.generate(allContents);\r\n\r\n if (!result.css || result.css.trim().length === 0) {\r\n console.warn('[UnoCSS Hybrid] Generated CSS is empty');\r\n return resolvedOutputPath;\r\n }\r\n\r\n // 计算输出路径\r\n let finalOutputPath: string;\r\n if (isProd && enableHash) {\r\n // 生产环境:直接生成到 dist 目录,文件名带 hash\r\n const distDir = path.join(rootContext, 'dist');\r\n const hash = createHash('md5').update(result.css).digest('hex').substring(0, 8);\r\n finalOutputPath = path.join(distDir, `uno.${hash}.css`);\r\n currentCssPath = finalOutputPath;\r\n } else {\r\n // 开发环境:生成到插件目录的 generated 子目录,文件名固定\r\n finalOutputPath = resolvedOutputPath;\r\n currentCssPath = resolvedOutputPath;\r\n }\r\n\r\n // 确保输出目录存在\r\n const outputDir = path.dirname(finalOutputPath);\r\n if (!fs.existsSync(outputDir)) {\r\n fs.mkdirSync(outputDir, { recursive: true });\r\n }\r\n\r\n // 写入 CSS 文件\r\n fs.writeFileSync(finalOutputPath, result.css);\r\n\r\n const stats = fs.statSync(finalOutputPath);\r\n console.log(\r\n `[UnoCSS Hybrid] CSS generated: ${finalOutputPath} (${(stats.size / 1024).toFixed(2)} KB)`,\r\n );\r\n\r\n return finalOutputPath;\r\n } catch (err) {\r\n console.error('[UnoCSS Hybrid] Failed to generate CSS:', err);\r\n return resolvedOutputPath;\r\n }\r\n };\r\n\r\n // 生产环境:构建前生成 CSS\r\n if (isProd) {\r\n api.onBeforeBuild(async () => {\r\n await generateCSS();\r\n });\r\n }\r\n\r\n // 用于保存 CSS 版本号,强制浏览器重新加载\r\n let cssVersion = Date.now();\r\n\r\n // 自动注入:通过修改 HTML 标签来注入 CSS(不污染源码)\r\n if (autoInject) {\r\n api.modifyHTMLTags((tags) => {\r\n let cssPath: string;\r\n\r\n if (isProd && enableHash) {\r\n // 生产环境:使用带 hash 的文件名\r\n const cssFileName = path.basename(currentCssPath);\r\n cssPath = `/${cssFileName}`;\r\n } else {\r\n // 开发环境或未启用 hash:使用固定文件名\r\n cssPath = '/uno.css';\r\n }\r\n\r\n tags.headTags.unshift({\r\n tag: 'link',\r\n attrs: {\r\n rel: 'stylesheet',\r\n href: cssPath,\r\n },\r\n });\r\n\r\n console.log(`[UnoCSS Hybrid] Auto-injected CSS link: ${cssPath}`);\r\n\r\n // 开发环境:注入热更新脚本\r\n if (!isProd) {\r\n const hotReloadScript = `\r\n (() => {\r\n window.__unocss_version__ = ${cssVersion};\r\n\r\n // 监听 WebSocket 消息\r\n const originalOnMessage = window.__whm__?.fns?.push;\r\n if (window.__whm__?.fns) {\r\n window.__whm__.fns.push(function(data) {\r\n // 监听自定义热更新事件\r\n if (data.type === 'unocss-css-update') {\r\n const { version } = data.data;\r\n if (version !== window.__unocss_version__) {\r\n window.__unocss_version__ = version;\r\n\r\n // 查找 UnoCSS 的 link 标签\r\n const link = document.querySelector('link[href*=\"/uno.css\"]');\r\n if (link) {\r\n // 更新 href 强制重新加载\r\n const href = link.getAttribute('href');\r\n if (href) {\r\n // 添加版本号作为查询参数\r\n const newHref = href.split('?')[0] + '?v=' + version;\r\n link.setAttribute('href', newHref);\r\n console.log('[UnoCSS] CSS hot updated:', newHref);\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n })();\r\n `;\r\n tags.bodyTags.push({\r\n tag: 'script',\r\n children: hotReloadScript,\r\n });\r\n console.log('[UnoCSS Hybrid] Hot reload script injected');\r\n }\r\n\r\n return tags;\r\n });\r\n }\r\n\r\n // 开发环境:配置静态文件服务和热更新\r\n if (!isProd) {\r\n\r\n api.modifyRsbuildConfig((config) => {\r\n const existingSetupMiddlewares = config.dev?.setupMiddlewares;\r\n const setupMiddlewaresArray = Array.isArray(existingSetupMiddlewares)\r\n ? existingSetupMiddlewares\r\n : existingSetupMiddlewares\r\n ? [existingSetupMiddlewares]\r\n : [];\r\n\r\n return {\r\n ...config,\r\n dev: {\r\n ...config.dev,\r\n setupMiddlewares: [\r\n ...setupMiddlewaresArray,\r\n (middlewares, devServer) => {\r\n // 保存 dev server 实例\r\n devServerInstance = devServer;\r\n\r\n // 添加自定义中间件来提供 CSS 文件\r\n middlewares.unshift(async (req, res, next) => {\r\n if (req.url?.startsWith('/uno.css')) {\r\n try {\r\n // 检查文件是否存在\r\n if (!fs.existsSync(resolvedOutputPath)) {\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('CSS file not found. Please restart the dev server to generate it.');\r\n console.warn('[UnoCSS Hybrid] CSS file not found at:', resolvedOutputPath);\r\n return;\r\n }\r\n\r\n // 设置正确的 Content-Type 和缓存控制\r\n res.setHeader('Content-Type', 'text/css; charset=utf-8');\r\n res.setHeader('Cache-Control', 'no-cache');\r\n\r\n const css = fs.readFileSync(resolvedOutputPath, 'utf-8');\r\n res.end(css);\r\n } catch (err) {\r\n console.error('[UnoCSS Hybrid] Error serving CSS:', err);\r\n res.statusCode = 500;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Error loading CSS file. Check console for details.');\r\n }\r\n } else {\r\n next();\r\n }\r\n });\r\n },\r\n ],\r\n },\r\n };\r\n });\r\n\r\n // 启动时生成 CSS\r\n api.onBeforeStartDevServer(async () => {\r\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\r\n await generateCSS();\r\n });\r\n\r\n // 合并启动后的操作\r\n api.onAfterStartDevServer(async () => {\r\n console.log('[UnoCSS Hybrid] Dev server started, CSS available at /uno.css');\r\n\r\n // Watch 模式\r\n if (watch) {\r\n try {\r\n // 动态导入 chokidar\r\n const chokidar = await import('chokidar');\r\n\r\n // 监听指定目录\r\n const watchDir = path.resolve(rootContext, watchDirectory);\r\n\r\n // 监听内容文件变化\r\n const watcher = chokidar.watch(watchDir, {\r\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\r\n ignoreInitial: true,\r\n persistent: true,\r\n });\r\n\r\n // 防抖:避免频繁重新生成\r\n let regenerateTimer: NodeJS.Timeout | null = null;\r\n\r\n watcher.on('all', async (event: string, filePath: string) => {\r\n // 检查文件扩展名\r\n const ext = path.extname(filePath);\r\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\r\n\r\n // 监听文件添加、修改和删除\r\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\r\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)}`);\r\n\r\n if (regenerateTimer) {\r\n clearTimeout(regenerateTimer);\r\n }\r\n\r\n regenerateTimer = setTimeout(async () => {\r\n console.log('[UnoCSS Hybrid] Regenerating CSS...');\r\n await generateCSS();\r\n\r\n // 更新版本号\r\n cssVersion = Date.now();\r\n\r\n // 触发 CSS 热更新(发送自定义事件)\r\n console.log('[UnoCSS Hybrid] Hot updating CSS in browser...');\r\n devServerInstance?.sockWrite('unocss-css-update', {\r\n version: cssVersion,\r\n path: '/uno.css',\r\n });\r\n }, 300);\r\n }\r\n });\r\n\r\n console.log('[UnoCSS Hybrid] Watch mode: enabled');\r\n } catch (err) {\r\n console.warn('[UnoCSS Hybrid] Watch mode not available:', (err as Error).message);\r\n }\r\n }\r\n });\r\n }\r\n\r\n // 构建完成后的日志\r\n api.onAfterBuild(() => {\r\n if (isProd && fs.existsSync(currentCssPath)) {\r\n const stats = fs.statSync(currentCssPath);\r\n const fileName = path.basename(currentCssPath);\r\n console.log(\r\n `[UnoCSS Hybrid] Final CSS: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)`,\r\n );\r\n }\r\n });\r\n },\r\n };\r\n}\r\n\r\nexport default pluginUnocss;\r\n"]}
1
+ {"version":3,"sources":["../plugin-unocss.ts"],"names":["__filename","__dirname","relPath","createGenerator","generator","result","stats"],"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 // 用于保存 WebSocket 服务器实例和端口\n let wss: any = null;\n // 动态分配的 WebSocket 端口(避免端口冲突)\n let wsPort = 0;\n\n // 通过环境变量判断是否是生产环境\n const isProd = process.env.NODE_ENV === 'production';\n\n // 文件内容哈希缓存(用于增量生成)\n const fileContentCache = new Map<string, string>();\n // 上次生成的完整 CSS 内容(用于比较)\n let lastGeneratedCSS = '';\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\n // 开发环境:注入热更新脚本和 WebSocket 端口\n if (!isProd) {\n const hotReloadScript = `\n (function() {\n function reloadCSS(version) {\n const oldLink = document.querySelector('link[href*=\"/uno.css\"]');\n if (oldLink && oldLink.parentNode) {\n const newLink = document.createElement('link');\n newLink.rel = 'stylesheet';\n newLink.href = '/uno.css?v=' + version;\n oldLink.parentNode.insertBefore(newLink, oldLink);\n\n newLink.onload = function() {\n if (oldLink.parentNode) {\n oldLink.parentNode.removeChild(oldLink);\n }\n };\n\n newLink.onerror = function() {\n console.error('[UnoCSS] Failed to reload CSS');\n if (newLink.parentNode) {\n newLink.parentNode.removeChild(newLink);\n }\n };\n }\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n const host = window.location.hostname;\n const wsUrl = protocol + '//' + host + ':${wsPort}';\n\n let socket = null;\n let reconnectCount = 0;\n const maxReconnect = 10;\n\n function connect() {\n if (reconnectCount === 0) {\n console.log('[UnoCSS] WebSocket connecting to ' + wsUrl);\n }\n\n socket = new WebSocket(wsUrl);\n\n socket.onopen = function() {\n console.log('[UnoCSS] WebSocket connected.');\n reconnectCount = 0;\n };\n\n socket.onmessage = function(event) {\n try {\n const data = JSON.parse(event.data);\n if (data.type === 'css-update' && data.version) {\n reloadCSS(data.version);\n }\n } catch (e) {\n // 忽略解析错误\n }\n };\n\n socket.onclose = function() {\n if (reconnectCount >= maxReconnect) {\n console.warn('[UnoCSS] WebSocket connection failed after maximum retry attempts.');\n return;\n }\n\n if (reconnectCount === 0) {\n console.log('[UnoCSS] WebSocket connection lost. Reconnecting...');\n }\n\n reconnectCount++;\n const delay = Math.min(1000 * Math.pow(1.5, reconnectCount), 30000);\n setTimeout(connect, delay);\n };\n\n socket.onerror = function(error) {\n console.error('[UnoCSS] WebSocket error:', error);\n };\n }\n\n connect();\n })();\n `;\n tags.bodyTags.push({\n tag: 'script',\n children: hotReloadScript,\n });\n console.log('[UnoCSS Hybrid] Hot reload script injected');\n }\n\n return tags;\n });\n }\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 // 生成文件内容的哈希\n const getFileHash = (content: string): string => {\n return createHash('md5').update(content).digest('hex');\n };\n\n // 生成 UnoCSS CSS(开发模式支持增量生成)\n const generateCSS = async (changedFiles?: string[]): Promise<string> => {\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 // 开发模式的增量生成逻辑\n if (!isProd && changedFiles && fileContentCache.size > 0) {\n // 检测变化的文件\n const changedHashes = new Set<string>();\n const changedFilesContent: string[] = [];\n\n for (const file of changedFiles) {\n if (!fs.existsSync(file)) {\n // 文件被删除,从缓存中移除\n const relPath = path.relative(rootContext, file);\n fileContentCache.delete(relPath);\n console.log(`[UnoCSS Hybrid] File removed: ${relPath}`);\n continue;\n }\n\n const content = fs.readFileSync(file, 'utf-8');\n const hash = getFileHash(content);\n const relPath = path.relative(rootContext, file);\n\n if (fileContentCache.get(relPath) !== hash) {\n // 文件内容变化了\n fileContentCache.set(relPath, hash);\n changedFilesContent.push(content);\n changedHashes.add(hash);\n console.log(`[UnoCSS Hybrid] File changed: ${relPath} (hash: ${hash.substring(0, 8)})`);\n }\n }\n\n // 如果有文件变化,进行增量生成\n if (changedFilesContent.length > 0) {\n console.log(`[UnoCSS Hybrid] Incremental generation: ${changedFilesContent.length} changed file(s)`);\n\n const { createGenerator } = await import('unocss');\n const generator = await createGenerator(config);\n\n // 只生成变化文件的新 CSS\n const changedContent = changedFilesContent.join('\\n');\n const result = await generator.generate(changedContent);\n\n if (result.css && result.css.trim().length > 0) {\n // 读取旧的 CSS 文件\n let existingCSS = '';\n if (fs.existsSync(resolvedOutputPath)) {\n existingCSS = fs.readFileSync(resolvedOutputPath, 'utf-8');\n }\n\n // 解析新 CSS 的类名\n const newClasses = new Set(\n result.css.match(/\\.(\\w[-\\w]*)/g)?.map(cls => cls.slice(1)) || []\n );\n\n // 过滤掉旧 CSS 中被新 CSS 覆盖的类\n const existingCSSLines = existingCSS.split('\\n').filter(line => {\n const match = line.match(/^\\.([\\w-]+)/);\n if (match) {\n return !newClasses.has(match[1]);\n }\n return true;\n });\n\n // 合并:保留旧 CSS 中未被新 CSS 覆盖的部分,加上新 CSS\n const mergedCSS = [...existingCSSLines, result.css].join('\\n').replace(/\\n\\n+/g, '\\n');\n\n // 写入合并后的 CSS\n fs.writeFileSync(resolvedOutputPath, mergedCSS);\n\n const stats = fs.statSync(resolvedOutputPath);\n console.log(\n `[UnoCSS Hybrid] CSS updated incrementally: ${resolvedOutputPath} (${(stats.size / 1024).toFixed(2)} KB, ${newClasses.size} new classes)`,\n );\n\n return resolvedOutputPath;\n }\n } else {\n console.log('[UnoCSS Hybrid] No content changes detected, skipping generation');\n return resolvedOutputPath;\n }\n }\n\n // 全量生成(生产环境或首次生成)\n console.log('[UnoCSS Hybrid] Full CSS generation...');\n\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 const hash = getFileHash(content);\n const relPath = path.relative(rootContext, file);\n fileContentCache.set(relPath, hash);\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 lastGeneratedCSS = 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 // 开发环境:配置静态文件服务和热更新\n if (!isProd) {\n\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 // 添加自定义中间件来提供 CSS 文件\n middlewares.unshift(async (req, res, next) => {\n // 处理带查询参数的请求 (如 /uno.css?v=123456)\n if (req.url?.startsWith('/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 // 禁用缓存,确保每次都获取最新内容\n res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\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 并创建 WebSocket 服务器\n api.onBeforeStartDevServer(async () => {\n console.log('[UnoCSS Hybrid] Development mode: CLI generation enabled');\n await generateCSS();\n\n // 提前创建 WebSocket 服务器,以便在 HTML 生成前获取端口\n try {\n const { WebSocketServer } = await import('ws');\n\n // 创建 WebSocket 服务器,使用 port: 0 让系统自动分配可用端口\n wss = new WebSocketServer({ port: 0 });\n wsPort = (wss.address() as any).port;\n\n console.log(`[UnoCSS Hybrid] WebSocket server created on port ${wsPort}`);\n\n // 当客户端连接时\n wss.on('connection', (ws: any) => {\n console.log('[UnoCSS Hybrid] Client connected to WebSocket');\n\n ws.on('close', () => {\n console.log('[UnoCSS Hybrid] Client disconnected from WebSocket');\n });\n });\n\n console.log('[UnoCSS Hybrid] UnoCSS HMR WebSocket server ready');\n } catch (err) {\n console.error('[UnoCSS Hybrid] Failed to create WebSocket server:', err);\n }\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 console.log('[UnoCSS Hybrid] Initializing watch mode...');\n\n // 动态导入 chokidar\n const chokidar = await import('chokidar');\n console.log('[UnoCSS Hybrid] chokidar imported successfully');\n\n // 监听指定目录\n const watchDir = path.resolve(rootContext, watchDirectory);\n console.log('[UnoCSS Hybrid] Watching directory:', watchDir);\n\n // 监听内容文件变化\n const watcher = chokidar.watch(watchDir, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n ignoreInitial: true,\n persistent: true,\n });\n\n console.log('[UnoCSS Hybrid] Watcher created, setting up event handlers...');\n\n watcher.on('ready', () => {\n console.log('[UnoCSS Hybrid] Watcher ready, scanning for files...');\n });\n\n watcher.on('error', (error) => {\n console.error('[UnoCSS Hybrid] Watcher error:', error);\n });\n\n // 防抖:避免频繁重新生成\n let regenerateTimer: NodeJS.Timeout | null = null;\n // 收集本次防抖周期内的变化文件\n let changedFilesInBatch: string[] = [];\n\n watcher.on('all', async (event: string, filePath: string) => {\n console.log(`[UnoCSS Hybrid] Watcher event: ${event} on ${path.relative(rootContext, filePath)}`);\n\n // 检查文件扩展名\n const ext = path.extname(filePath);\n const shouldWatch = ['.html', '.js', '.ts', '.jsx', '.tsx'].includes(ext);\n\n console.log(`[UnoCSS Hybrid] File extension: ${ext}, shouldWatch: ${shouldWatch}`);\n\n // 监听文件添加、修改和删除\n if (shouldWatch && (event === 'change' || event === 'add' || event === 'unlink')) {\n console.log(`[UnoCSS Hybrid] File ${event}: ${path.relative(rootContext, filePath)} - queuing`);\n\n // 将变化的文件添加到批次中\n changedFilesInBatch.push(filePath);\n\n if (regenerateTimer) {\n clearTimeout(regenerateTimer);\n }\n\n regenerateTimer = setTimeout(async () => {\n console.log(`[UnoCSS Hybrid] Regenerating CSS... (${changedFilesInBatch.length} file(s) changed)`);\n\n // 传递变化文件列表,启用增量生成\n await generateCSS(changedFilesInBatch);\n\n const version = Date.now();\n console.log('[UnoCSS Hybrid] Broadcasting CSS update via WebSocket, version:', version);\n\n // 通过我们自己的 WebSocket 广播更新\n if (wss) {\n const message = JSON.stringify({\n type: 'css-update',\n version: version,\n });\n\n wss.clients.forEach((client: any) => {\n if (client.readyState === 1) { // WebSocket.OPEN\n client.send(message);\n }\n });\n\n console.log(`[UnoCSS Hybrid] Broadcasted to ${wss.clients.size} client(s)`);\n } else {\n console.warn('[UnoCSS Hybrid] WebSocket server not available');\n }\n\n // 清空批次\n changedFilesInBatch = [];\n }, 300);\n } else {\n console.log(`[UnoCSS Hybrid] Event ignored - ext: ${ext}, event: ${event}`);\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 // 关闭 dev server 时清理 WebSocket 服务器\n api.onCloseDevServer(async () => {\n if (wss) {\n console.log('[UnoCSS Hybrid] Closing WebSocket server...');\n wss.close();\n wss = null;\n }\n });\n },\n };\n}\n\nexport default pluginUnocss;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikkin/plugin-unocss",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "UnoCSS plugin for Rsbuild with CLI pre-generation and automatic injection",
5
5
  "main": "./dist/plugin-unocss.js",
6
6
  "module": "./dist/plugin-unocss.mjs",
@@ -53,11 +53,13 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "globby": "^14.0.0",
56
- "chokidar": "^4.0.0"
56
+ "chokidar": "^4.0.0",
57
+ "ws": "^8.0.0"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@rsbuild/core": "^1.1.0",
60
61
  "@types/node": "^22.0.0",
62
+ "@types/ws": "^8.5.0",
61
63
  "tsup": "^8.0.0",
62
64
  "typescript": "^5.6.0",
63
65
  "unocss": "^66.6.0"