css2class 2.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.
Files changed (57) hide show
  1. package/API.md +1143 -0
  2. package/CHANGELOG.md +291 -0
  3. package/CONFIG.md +1096 -0
  4. package/CONTRIBUTING.md +571 -0
  5. package/MIGRATION.md +402 -0
  6. package/README.md +634 -0
  7. package/bin/class2css.js +380 -0
  8. package/class2css.config.js +124 -0
  9. package/common.css +3 -0
  10. package/configs/colors.config.js +62 -0
  11. package/configs/layout.config.js +110 -0
  12. package/configs/spacing.config.js +37 -0
  13. package/configs/typography.config.js +41 -0
  14. package/docs/.vitepress/config.mjs +65 -0
  15. package/docs/.vitepress/theme/custom.css +74 -0
  16. package/docs/.vitepress/theme/index.js +7 -0
  17. package/docs/guide/cli.md +97 -0
  18. package/docs/guide/concepts.md +63 -0
  19. package/docs/guide/config-template.md +365 -0
  20. package/docs/guide/config.md +275 -0
  21. package/docs/guide/faq.md +202 -0
  22. package/docs/guide/getting-started.md +83 -0
  23. package/docs/guide/important-and-static.md +67 -0
  24. package/docs/guide/incremental.md +162 -0
  25. package/docs/guide/rules-reference.md +354 -0
  26. package/docs/guide/units.md +57 -0
  27. package/docs/index.md +68 -0
  28. package/package.json +49 -0
  29. package/run.js +90 -0
  30. package/src/README.md +571 -0
  31. package/src/core/CacheManager.js +650 -0
  32. package/src/core/CompatibilityAdapter.js +264 -0
  33. package/src/core/ConfigManager.js +431 -0
  34. package/src/core/ConfigValidator.js +350 -0
  35. package/src/core/EventBus.js +77 -0
  36. package/src/core/FullScanManager.js +430 -0
  37. package/src/core/StateManager.js +631 -0
  38. package/src/docs/DocsServer.js +179 -0
  39. package/src/example.js +106 -0
  40. package/src/generators/DynamicClassGenerator.js +674 -0
  41. package/src/index.js +1046 -0
  42. package/src/parsers/ClassParser.js +572 -0
  43. package/src/parsers/ImportantParser.js +279 -0
  44. package/src/parsers/RegexCompiler.js +200 -0
  45. package/src/utils/ClassChangeTracker.js +366 -0
  46. package/src/utils/ConfigDiagnostics.js +673 -0
  47. package/src/utils/CssFormatter.js +261 -0
  48. package/src/utils/FileUtils.js +230 -0
  49. package/src/utils/Logger.js +150 -0
  50. package/src/utils/Throttle.js +172 -0
  51. package/src/utils/UnitProcessor.js +334 -0
  52. package/src/utils/WxssClassExtractor.js +137 -0
  53. package/src/watchers/ConfigWatcher.js +413 -0
  54. package/src/watchers/FileWatcher.js +133 -0
  55. package/src/writers/FileWriter.js +302 -0
  56. package/src/writers/UnifiedWriter.js +370 -0
  57. package/styles.config.js +250 -0
@@ -0,0 +1,650 @@
1
+ const fs = require('fs').promises;
2
+
3
+ class CacheManager {
4
+ constructor(eventBus, maxSize = 1000) {
5
+ this.eventBus = eventBus;
6
+ this.maxSize = maxSize;
7
+
8
+ // 文件缓存
9
+ this.fileCache = new Map();
10
+ this.fileStats = new Map();
11
+ this.fileSizes = new Map();
12
+
13
+ // 全量扫描缓存
14
+ this.fullScanCache = {
15
+ classListSet: new Set(),
16
+ userStaticClassListSet: new Set(),
17
+ userBaseClassListSet: new Set(),
18
+ scanTime: null,
19
+ isLocked: false,
20
+ };
21
+
22
+ // 全局样式缓存
23
+ this.globalStyleCache = {
24
+ classListSet: new Set(),
25
+ userStaticClassListSet: new Set(),
26
+ userBaseClassListSet: new Set(),
27
+ fileClassMap: new Map(),
28
+ lastUpdateTime: Date.now(),
29
+ };
30
+
31
+ // 配置缓存
32
+ this.configCache = {
33
+ config: null,
34
+ hash: null,
35
+ lastModified: null,
36
+ validationResults: null,
37
+ dependencyGraph: new Map(),
38
+ };
39
+
40
+ // CSS生成缓存
41
+ this.cssGenerationCache = new Map();
42
+ this.cssGenerationStats = {
43
+ hits: 0,
44
+ misses: 0,
45
+ totalGenerations: 0,
46
+ };
47
+
48
+ // 增量更新追踪
49
+ this.incrementalTracker = {
50
+ changedFiles: new Set(),
51
+ deletedFiles: new Set(),
52
+ lastIncrementalUpdate: null,
53
+ pendingUpdates: new Map(),
54
+ };
55
+
56
+ // 缓存策略配置
57
+ this.cacheStrategy = {
58
+ enableFileCache: true,
59
+ enableConfigCache: true,
60
+ enableCssGenerationCache: true,
61
+ enableIncrementalUpdates: true,
62
+ maxCssGenerationCacheSize: 5000,
63
+ maxFileAge: 24 * 60 * 60 * 1000, // 24小时
64
+ compressionEnabled: false,
65
+ };
66
+ }
67
+
68
+ // 文件缓存方法
69
+ async getFileContent(filePath) {
70
+ const maxRetries = 3;
71
+ const baseDelayMs = 80;
72
+
73
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
74
+
75
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
76
+ try {
77
+ const stat = await fs.stat(filePath);
78
+ const mtimeMs = stat.mtimeMs ?? stat.mtime.getTime();
79
+ const size = stat.size ?? 0;
80
+
81
+ const cached = this.fileCache.get(filePath);
82
+ const cachedMtime = this.fileStats.get(filePath);
83
+ const cachedSize = this.fileSizes.get(filePath);
84
+
85
+ // 同时校验 mtime 与 size,降低保存风暴/mtime 粒度导致的误命中
86
+ if (cached && cachedMtime && cachedSize !== undefined && mtimeMs === cachedMtime && size === cachedSize) {
87
+ return cached;
88
+ }
89
+
90
+ const content = await fs.readFile(filePath, 'utf-8');
91
+
92
+ // 保存过程可能短暂读到空内容;若文件 size>0,则短暂重试
93
+ if (content === '' && size > 0 && attempt < maxRetries) {
94
+ await sleep(baseDelayMs * Math.pow(2, attempt));
95
+ continue;
96
+ }
97
+
98
+ // LRU缓存清理
99
+ if (this.fileCache.size >= this.maxSize) {
100
+ const oldestKey = this.fileCache.keys().next().value;
101
+ this.fileCache.delete(oldestKey);
102
+ this.fileStats.delete(oldestKey);
103
+ this.fileSizes.delete(oldestKey);
104
+ }
105
+
106
+ this.fileCache.set(filePath, content);
107
+ this.fileStats.set(filePath, mtimeMs);
108
+ this.fileSizes.set(filePath, size);
109
+
110
+ this.eventBus.emit('cache:file:updated', filePath);
111
+ return content;
112
+ } catch (error) {
113
+ // 保存/替换时常见的瞬时错误:ENOENT/EBUSY/EPERM 等,做短暂重试
114
+ const code = error && error.code;
115
+ const retryableCodes = new Set(['ENOENT', 'EBUSY', 'EPERM', 'EACCES']);
116
+ if (attempt < maxRetries && retryableCodes.has(code)) {
117
+ await sleep(baseDelayMs * Math.pow(2, attempt));
118
+ continue;
119
+ }
120
+
121
+ this.eventBus.emit('cache:file:error', { filePath, error });
122
+ return null;
123
+ }
124
+ }
125
+
126
+ // 理论上不会走到这里
127
+ return null;
128
+ }
129
+
130
+ // 全量扫描缓存方法
131
+ updateFullScanCache(classList, staticClassList, baseClassList) {
132
+ this.fullScanCache.classListSet.clear();
133
+ this.fullScanCache.userStaticClassListSet.clear();
134
+ this.fullScanCache.userBaseClassListSet.clear();
135
+
136
+ classList.forEach((cls) => this.fullScanCache.classListSet.add(cls));
137
+ staticClassList.forEach((cls) => this.fullScanCache.userStaticClassListSet.add(cls));
138
+ baseClassList.forEach((cls) => this.fullScanCache.userBaseClassListSet.add(cls));
139
+
140
+ this.fullScanCache.scanTime = Date.now();
141
+ this.fullScanCache.isLocked = true;
142
+
143
+ this.eventBus.emit('cache:fullScan:updated', this.fullScanCache);
144
+ }
145
+
146
+ getFullScanCache() {
147
+ return this.fullScanCache;
148
+ }
149
+
150
+ isFullScanCacheLocked() {
151
+ return this.fullScanCache.isLocked;
152
+ }
153
+
154
+ // 全局样式缓存方法
155
+ updateGlobalStyleCache(classList, staticClassList, baseClassList, fileClassMap) {
156
+ this.globalStyleCache.classListSet.clear();
157
+ this.globalStyleCache.userStaticClassListSet.clear();
158
+ this.globalStyleCache.userBaseClassListSet.clear();
159
+ this.globalStyleCache.fileClassMap.clear();
160
+
161
+ classList.forEach((cls) => this.globalStyleCache.classListSet.add(cls));
162
+ staticClassList.forEach((cls) => this.globalStyleCache.userStaticClassListSet.add(cls));
163
+ baseClassList.forEach((cls) => this.globalStyleCache.userBaseClassListSet.add(cls));
164
+
165
+ if (fileClassMap) {
166
+ fileClassMap.forEach((value, key) => {
167
+ this.globalStyleCache.fileClassMap.set(key, value);
168
+ });
169
+ }
170
+
171
+ this.globalStyleCache.lastUpdateTime = Date.now();
172
+
173
+ this.eventBus.emit('cache:globalStyle:updated', this.globalStyleCache);
174
+ }
175
+
176
+ getGlobalStyleCache() {
177
+ return this.globalStyleCache;
178
+ }
179
+
180
+ // 缓存清理方法
181
+ clearFileCache() {
182
+ this.fileCache.clear();
183
+ this.fileStats.clear();
184
+ this.eventBus.emit('cache:file:cleared');
185
+ }
186
+
187
+ clearFullScanCache() {
188
+ this.fullScanCache.classListSet.clear();
189
+ this.fullScanCache.userStaticClassListSet.clear();
190
+ this.fullScanCache.userBaseClassListSet.clear();
191
+ this.fullScanCache.scanTime = null;
192
+ this.fullScanCache.isLocked = false;
193
+ this.eventBus.emit('cache:fullScan:cleared');
194
+ }
195
+
196
+ clearGlobalStyleCache() {
197
+ this.globalStyleCache.classListSet.clear();
198
+ this.globalStyleCache.userStaticClassListSet.clear();
199
+ this.globalStyleCache.userBaseClassListSet.clear();
200
+ this.globalStyleCache.fileClassMap.clear();
201
+ this.globalStyleCache.lastUpdateTime = Date.now();
202
+ this.eventBus.emit('cache:globalStyle:cleared');
203
+ }
204
+
205
+ clearAll() {
206
+ this.clearFileCache();
207
+ this.clearFullScanCache();
208
+ this.clearGlobalStyleCache();
209
+ this.eventBus.emit('cache:all:cleared');
210
+ }
211
+
212
+ // 缓存统计
213
+ getCacheStats() {
214
+ return {
215
+ fileCacheSize: this.fileCache.size,
216
+ fileCacheMaxSize: this.maxSize,
217
+ fullScanCacheLocked: this.fullScanCache.isLocked,
218
+ fullScanCacheTime: this.fullScanCache.scanTime,
219
+ fullScanClassCount: this.fullScanCache.classListSet.size,
220
+ fullScanStaticCount: this.fullScanCache.userStaticClassListSet.size,
221
+ globalStyleCacheTime: this.globalStyleCache.lastUpdateTime,
222
+ globalStyleClassCount: this.globalStyleCache.classListSet.size,
223
+ globalStyleStaticCount: this.globalStyleCache.userStaticClassListSet.size,
224
+ globalStyleFileMapCount: this.globalStyleCache.fileClassMap.size,
225
+ };
226
+ }
227
+
228
+ // 缓存优化
229
+ optimizeCache() {
230
+ const stats = this.getCacheStats();
231
+
232
+ // 如果文件缓存接近最大大小,清理最旧的文件
233
+ if (stats.fileCacheSize > this.maxSize * 0.8) {
234
+ const entries = Array.from(this.fileCache.entries());
235
+ const sortedEntries = entries.sort((a, b) => {
236
+ const statA = this.fileStats.get(a[0]) || 0;
237
+ const statB = this.fileStats.get(b[0]) || 0;
238
+ return statA - statB;
239
+ });
240
+
241
+ const toRemove = Math.floor(this.maxSize * 0.2);
242
+ for (let i = 0; i < toRemove && i < sortedEntries.length; i++) {
243
+ const [key] = sortedEntries[i];
244
+ this.fileCache.delete(key);
245
+ this.fileStats.delete(key);
246
+ }
247
+
248
+ this.eventBus.emit('cache:optimized', { removedCount: toRemove });
249
+ }
250
+ }
251
+ // ==================== 配置缓存管理 ====================
252
+
253
+ // 缓存配置
254
+ cacheConfig(config, configHash) {
255
+ if (!this.cacheStrategy.enableConfigCache) {
256
+ return;
257
+ }
258
+
259
+ this.configCache.config = config;
260
+ this.configCache.hash = configHash;
261
+ this.configCache.lastModified = Date.now();
262
+
263
+ this.eventBus.emit('cache:config:updated', {
264
+ hash: configHash,
265
+ timestamp: this.configCache.lastModified,
266
+ });
267
+ }
268
+
269
+ // 获取缓存的配置
270
+ getCachedConfig() {
271
+ return this.configCache.config;
272
+ }
273
+
274
+ // 检查配置是否已缓存且有效
275
+ isConfigCacheValid(configHash) {
276
+ return this.configCache.hash === configHash && this.configCache.config !== null;
277
+ }
278
+
279
+ // 缓存配置验证结果
280
+ cacheConfigValidation(validationResults) {
281
+ this.configCache.validationResults = validationResults;
282
+ this.eventBus.emit('cache:config:validation:cached', validationResults);
283
+ }
284
+
285
+ // 获取缓存的配置验证结果
286
+ getCachedConfigValidation() {
287
+ return this.configCache.validationResults;
288
+ }
289
+
290
+ // 更新配置依赖图
291
+ updateConfigDependencyGraph(dependencies) {
292
+ this.configCache.dependencyGraph.clear();
293
+ Object.entries(dependencies).forEach(([key, deps]) => {
294
+ this.configCache.dependencyGraph.set(key, deps);
295
+ });
296
+ }
297
+
298
+ // ==================== CSS生成缓存管理 ====================
299
+
300
+ // 缓存CSS生成结果
301
+ cacheCssGeneration(classSignature, cssResult) {
302
+ if (!this.cacheStrategy.enableCssGenerationCache) {
303
+ return;
304
+ }
305
+
306
+ // 检查缓存大小限制
307
+ if (this.cssGenerationCache.size >= this.cacheStrategy.maxCssGenerationCacheSize) {
308
+ this.evictOldestCssCache();
309
+ }
310
+
311
+ this.cssGenerationCache.set(classSignature, {
312
+ result: cssResult,
313
+ timestamp: Date.now(),
314
+ accessCount: 1,
315
+ });
316
+
317
+ this.cssGenerationStats.totalGenerations++;
318
+ this.eventBus.emit('cache:css:cached', { classSignature });
319
+ }
320
+
321
+ // 获取缓存的CSS生成结果
322
+ getCachedCssGeneration(classSignature) {
323
+ if (!this.cacheStrategy.enableCssGenerationCache) {
324
+ return null;
325
+ }
326
+
327
+ const cached = this.cssGenerationCache.get(classSignature);
328
+ if (cached) {
329
+ cached.accessCount++;
330
+ this.cssGenerationStats.hits++;
331
+ this.eventBus.emit('cache:css:hit', { classSignature });
332
+ return cached.result;
333
+ }
334
+
335
+ this.cssGenerationStats.misses++;
336
+ this.eventBus.emit('cache:css:miss', { classSignature });
337
+ return null;
338
+ }
339
+
340
+ // 清除过期的CSS缓存
341
+ evictOldestCssCache() {
342
+ let oldestKey = null;
343
+ let oldestTime = Date.now();
344
+
345
+ for (const [key, value] of this.cssGenerationCache) {
346
+ if (value.timestamp < oldestTime) {
347
+ oldestTime = value.timestamp;
348
+ oldestKey = key;
349
+ }
350
+ }
351
+
352
+ if (oldestKey) {
353
+ this.cssGenerationCache.delete(oldestKey);
354
+ this.eventBus.emit('cache:css:evicted', { key: oldestKey });
355
+ }
356
+ }
357
+
358
+ // 清除CSS生成缓存
359
+ clearCssGenerationCache() {
360
+ this.cssGenerationCache.clear();
361
+ this.cssGenerationStats = { hits: 0, misses: 0, totalGenerations: 0 };
362
+ this.eventBus.emit('cache:css:cleared');
363
+ }
364
+
365
+ // ==================== 增量更新管理 ====================
366
+
367
+ // 标记文件为已更改
368
+ markFileChanged(filePath) {
369
+ if (!this.cacheStrategy.enableIncrementalUpdates) {
370
+ return;
371
+ }
372
+
373
+ this.incrementalTracker.changedFiles.add(filePath);
374
+ this.incrementalTracker.deletedFiles.delete(filePath); // 如果之前标记为删除,现在移除
375
+
376
+ this.eventBus.emit('cache:incremental:fileChanged', { filePath });
377
+ }
378
+
379
+ // 标记文件为已删除
380
+ markFileDeleted(filePath) {
381
+ if (!this.cacheStrategy.enableIncrementalUpdates) {
382
+ return;
383
+ }
384
+
385
+ this.incrementalTracker.deletedFiles.add(filePath);
386
+ this.incrementalTracker.changedFiles.delete(filePath); // 如果之前标记为更改,现在移除
387
+
388
+ // 从文件缓存中删除
389
+ this.fileCache.delete(filePath);
390
+ this.fileStats.delete(filePath);
391
+
392
+ this.eventBus.emit('cache:incremental:fileDeleted', { filePath });
393
+ }
394
+
395
+ // 获取已更改的文件
396
+ getChangedFiles() {
397
+ return Array.from(this.incrementalTracker.changedFiles);
398
+ }
399
+
400
+ // 获取已删除的文件
401
+ getDeletedFiles() {
402
+ return Array.from(this.incrementalTracker.deletedFiles);
403
+ }
404
+
405
+ // 处理增量更新
406
+ processIncrementalUpdate() {
407
+ if (!this.cacheStrategy.enableIncrementalUpdates) {
408
+ return null;
409
+ }
410
+
411
+ const changedFiles = this.getChangedFiles();
412
+ const deletedFiles = this.getDeletedFiles();
413
+
414
+ if (changedFiles.length === 0 && deletedFiles.length === 0) {
415
+ return null; // 无需更新
416
+ }
417
+
418
+ const updateInfo = {
419
+ changedFiles,
420
+ deletedFiles,
421
+ timestamp: Date.now(),
422
+ updateId: this.generateUpdateId(),
423
+ };
424
+
425
+ // 清除增量追踪
426
+ this.incrementalTracker.changedFiles.clear();
427
+ this.incrementalTracker.deletedFiles.clear();
428
+ this.incrementalTracker.lastIncrementalUpdate = updateInfo.timestamp;
429
+
430
+ this.eventBus.emit('cache:incremental:processed', updateInfo);
431
+ return updateInfo;
432
+ }
433
+
434
+ // 生成更新ID
435
+ generateUpdateId() {
436
+ return `update_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
437
+ }
438
+
439
+ // 添加待处理的更新
440
+ addPendingUpdate(updateId, updateData) {
441
+ this.incrementalTracker.pendingUpdates.set(updateId, {
442
+ data: updateData,
443
+ timestamp: Date.now(),
444
+ });
445
+ }
446
+
447
+ // 完成待处理的更新
448
+ completePendingUpdate(updateId) {
449
+ const updated = this.incrementalTracker.pendingUpdates.delete(updateId);
450
+ if (updated) {
451
+ this.eventBus.emit('cache:incremental:updateCompleted', { updateId });
452
+ }
453
+ return updated;
454
+ }
455
+
456
+ // ==================== 缓存策略管理 ====================
457
+
458
+ // 更新缓存策略
459
+ updateCacheStrategy(newStrategy) {
460
+ const oldStrategy = { ...this.cacheStrategy };
461
+ this.cacheStrategy = { ...this.cacheStrategy, ...newStrategy };
462
+
463
+ // 如果禁用了某些缓存,清除对应的缓存
464
+ if (!this.cacheStrategy.enableFileCache && oldStrategy.enableFileCache) {
465
+ this.clearFileCache();
466
+ }
467
+
468
+ if (!this.cacheStrategy.enableConfigCache && oldStrategy.enableConfigCache) {
469
+ this.clearConfigCache();
470
+ }
471
+
472
+ if (!this.cacheStrategy.enableCssGenerationCache && oldStrategy.enableCssGenerationCache) {
473
+ this.clearCssGenerationCache();
474
+ }
475
+
476
+ this.eventBus.emit('cache:strategy:updated', {
477
+ oldStrategy,
478
+ newStrategy: this.cacheStrategy,
479
+ });
480
+ }
481
+
482
+ // 获取缓存策略
483
+ getCacheStrategy() {
484
+ return { ...this.cacheStrategy };
485
+ }
486
+
487
+ // 清除配置缓存
488
+ clearConfigCache() {
489
+ this.configCache = {
490
+ config: null,
491
+ hash: null,
492
+ lastModified: null,
493
+ validationResults: null,
494
+ dependencyGraph: new Map(),
495
+ };
496
+ this.eventBus.emit('cache:config:cleared');
497
+ }
498
+
499
+ // ==================== 缓存统计和分析 ====================
500
+
501
+ // 获取缓存统计
502
+ getCacheStats() {
503
+ const now = Date.now();
504
+
505
+ return {
506
+ file: {
507
+ size: this.fileCache.size,
508
+ maxSize: this.maxSize,
509
+ hitRate: this.calculateFileHitRate(),
510
+ },
511
+ config: {
512
+ isCached: this.configCache.config !== null,
513
+ lastModified: this.configCache.lastModified,
514
+ hasValidation: this.configCache.validationResults !== null,
515
+ dependencyCount: this.configCache.dependencyGraph.size,
516
+ },
517
+ cssGeneration: {
518
+ size: this.cssGenerationCache.size,
519
+ maxSize: this.cacheStrategy.maxCssGenerationCacheSize,
520
+ hits: this.cssGenerationStats.hits,
521
+ misses: this.cssGenerationStats.misses,
522
+ hitRate: this.calculateCssHitRate(),
523
+ totalGenerations: this.cssGenerationStats.totalGenerations,
524
+ },
525
+ incremental: {
526
+ changedFiles: this.incrementalTracker.changedFiles.size,
527
+ deletedFiles: this.incrementalTracker.deletedFiles.size,
528
+ pendingUpdates: this.incrementalTracker.pendingUpdates.size,
529
+ lastUpdate: this.incrementalTracker.lastIncrementalUpdate,
530
+ },
531
+ fullScan: {
532
+ isLocked: this.fullScanCache.isLocked,
533
+ scanTime: this.fullScanCache.scanTime,
534
+ classCount: this.fullScanCache.classListSet.size,
535
+ staticClassCount: this.fullScanCache.userStaticClassListSet.size,
536
+ baseClassCount: this.fullScanCache.userBaseClassListSet.size,
537
+ },
538
+ strategy: this.cacheStrategy,
539
+ memoryUsage: this.estimateMemoryUsage(),
540
+ };
541
+ }
542
+
543
+ // 计算文件缓存命中率
544
+ calculateFileHitRate() {
545
+ // 这是一个简化的计算,实际应用中可能需要更复杂的统计
546
+ return this.fileCache.size > 0 ? 0.8 : 0; // 示例值
547
+ }
548
+
549
+ // 计算CSS生成缓存命中率
550
+ calculateCssHitRate() {
551
+ const total = this.cssGenerationStats.hits + this.cssGenerationStats.misses;
552
+ return total > 0 ? (this.cssGenerationStats.hits / total) * 100 : 0;
553
+ }
554
+
555
+ // 估算内存使用
556
+ estimateMemoryUsage() {
557
+ let totalSize = 0;
558
+
559
+ // 文件缓存大小估算
560
+ for (const content of this.fileCache.values()) {
561
+ totalSize += content.length * 2; // 假设UTF-16编码
562
+ }
563
+
564
+ // CSS生成缓存大小估算
565
+ for (const [key, value] of this.cssGenerationCache) {
566
+ totalSize += key.length * 2;
567
+ totalSize += JSON.stringify(value.result).length * 2;
568
+ }
569
+
570
+ return {
571
+ bytes: totalSize,
572
+ kb: Math.round(totalSize / 1024),
573
+ mb: Math.round(totalSize / (1024 * 1024)),
574
+ };
575
+ }
576
+
577
+ // 清理过期缓存
578
+ cleanupExpiredCache() {
579
+ const now = Date.now();
580
+ const maxAge = this.cacheStrategy.maxFileAge;
581
+
582
+ // 清理过期的CSS生成缓存
583
+ for (const [key, value] of this.cssGenerationCache) {
584
+ if (now - value.timestamp > maxAge) {
585
+ this.cssGenerationCache.delete(key);
586
+ this.eventBus.emit('cache:css:expired', { key });
587
+ }
588
+ }
589
+
590
+ // 清理过期的待处理更新
591
+ for (const [id, update] of this.incrementalTracker.pendingUpdates) {
592
+ if (now - update.timestamp > maxAge) {
593
+ this.incrementalTracker.pendingUpdates.delete(id);
594
+ this.eventBus.emit('cache:incremental:updateExpired', { updateId: id });
595
+ }
596
+ }
597
+
598
+ this.eventBus.emit('cache:cleanup:completed', {
599
+ timestamp: now,
600
+ removedItems: 0, // 这里应该统计实际删除的项目数
601
+ });
602
+ }
603
+
604
+ // 优化缓存
605
+ optimizeCache() {
606
+ // 根据访问频率优化CSS缓存
607
+ const cssEntries = Array.from(this.cssGenerationCache.entries());
608
+ cssEntries.sort((a, b) => b[1].accessCount - a[1].accessCount);
609
+
610
+ // 保留访问频率高的前80%
611
+ const keepCount = Math.floor(cssEntries.length * 0.8);
612
+ this.cssGenerationCache.clear();
613
+
614
+ for (let i = 0; i < keepCount; i++) {
615
+ const [key, value] = cssEntries[i];
616
+ this.cssGenerationCache.set(key, value);
617
+ }
618
+
619
+ this.eventBus.emit('cache:optimized', {
620
+ cssKeep: keepCount,
621
+ cssRemoved: cssEntries.length - keepCount,
622
+ });
623
+ }
624
+
625
+ // 重置所有缓存
626
+ resetAllCaches() {
627
+ this.clearFileCache();
628
+ this.clearConfigCache();
629
+ this.clearCssGenerationCache();
630
+ this.clearFullScanCache();
631
+ this.clearGlobalStyleCache();
632
+
633
+ this.incrementalTracker = {
634
+ changedFiles: new Set(),
635
+ deletedFiles: new Set(),
636
+ lastIncrementalUpdate: null,
637
+ pendingUpdates: new Map(),
638
+ };
639
+
640
+ this.cssGenerationStats = {
641
+ hits: 0,
642
+ misses: 0,
643
+ totalGenerations: 0,
644
+ };
645
+
646
+ this.eventBus.emit('cache:reset:completed', { timestamp: Date.now() });
647
+ }
648
+ }
649
+
650
+ module.exports = CacheManager;