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.
- package/API.md +1143 -0
- package/CHANGELOG.md +291 -0
- package/CONFIG.md +1096 -0
- package/CONTRIBUTING.md +571 -0
- package/MIGRATION.md +402 -0
- package/README.md +634 -0
- package/bin/class2css.js +380 -0
- package/class2css.config.js +124 -0
- package/common.css +3 -0
- package/configs/colors.config.js +62 -0
- package/configs/layout.config.js +110 -0
- package/configs/spacing.config.js +37 -0
- package/configs/typography.config.js +41 -0
- package/docs/.vitepress/config.mjs +65 -0
- package/docs/.vitepress/theme/custom.css +74 -0
- package/docs/.vitepress/theme/index.js +7 -0
- package/docs/guide/cli.md +97 -0
- package/docs/guide/concepts.md +63 -0
- package/docs/guide/config-template.md +365 -0
- package/docs/guide/config.md +275 -0
- package/docs/guide/faq.md +202 -0
- package/docs/guide/getting-started.md +83 -0
- package/docs/guide/important-and-static.md +67 -0
- package/docs/guide/incremental.md +162 -0
- package/docs/guide/rules-reference.md +354 -0
- package/docs/guide/units.md +57 -0
- package/docs/index.md +68 -0
- package/package.json +49 -0
- package/run.js +90 -0
- package/src/README.md +571 -0
- package/src/core/CacheManager.js +650 -0
- package/src/core/CompatibilityAdapter.js +264 -0
- package/src/core/ConfigManager.js +431 -0
- package/src/core/ConfigValidator.js +350 -0
- package/src/core/EventBus.js +77 -0
- package/src/core/FullScanManager.js +430 -0
- package/src/core/StateManager.js +631 -0
- package/src/docs/DocsServer.js +179 -0
- package/src/example.js +106 -0
- package/src/generators/DynamicClassGenerator.js +674 -0
- package/src/index.js +1046 -0
- package/src/parsers/ClassParser.js +572 -0
- package/src/parsers/ImportantParser.js +279 -0
- package/src/parsers/RegexCompiler.js +200 -0
- package/src/utils/ClassChangeTracker.js +366 -0
- package/src/utils/ConfigDiagnostics.js +673 -0
- package/src/utils/CssFormatter.js +261 -0
- package/src/utils/FileUtils.js +230 -0
- package/src/utils/Logger.js +150 -0
- package/src/utils/Throttle.js +172 -0
- package/src/utils/UnitProcessor.js +334 -0
- package/src/utils/WxssClassExtractor.js +137 -0
- package/src/watchers/ConfigWatcher.js +413 -0
- package/src/watchers/FileWatcher.js +133 -0
- package/src/writers/FileWriter.js +302 -0
- package/src/writers/UnifiedWriter.js +370 -0
- package/styles.config.js +250 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
class ClassChangeTracker {
|
|
2
|
+
constructor(eventBus) {
|
|
3
|
+
this.eventBus = eventBus;
|
|
4
|
+
this.changeHistory = new Map(); // 存储文件的变化历史
|
|
5
|
+
this.maxHistorySize = 100; // 最大历史记录数
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 对比class变化
|
|
9
|
+
compareClasses(oldClasses, newClasses, filePath) {
|
|
10
|
+
const oldSet = new Set(oldClasses || []);
|
|
11
|
+
const newSet = new Set(newClasses || []);
|
|
12
|
+
|
|
13
|
+
const added = [...newSet].filter((cls) => !oldSet.has(cls));
|
|
14
|
+
const removed = [...oldSet].filter((cls) => !newSet.has(cls));
|
|
15
|
+
const unchanged = [...oldSet].filter((cls) => newSet.has(cls));
|
|
16
|
+
|
|
17
|
+
const changes = {
|
|
18
|
+
added,
|
|
19
|
+
removed,
|
|
20
|
+
unchanged,
|
|
21
|
+
total: {
|
|
22
|
+
old: oldSet.size,
|
|
23
|
+
new: newSet.size,
|
|
24
|
+
added: added.length,
|
|
25
|
+
removed: removed.length,
|
|
26
|
+
unchanged: unchanged.length,
|
|
27
|
+
},
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
filePath,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// 分析变化类型
|
|
33
|
+
changes.categories = this.categorizeChanges(changes);
|
|
34
|
+
|
|
35
|
+
// 分析变化原因
|
|
36
|
+
changes.reasons = this.analyzeChangeReasons(changes);
|
|
37
|
+
|
|
38
|
+
// 保存到历史记录
|
|
39
|
+
this.saveChangeHistory(changes, filePath);
|
|
40
|
+
|
|
41
|
+
// 发射变化事件
|
|
42
|
+
this.eventBus.emit('classChange:detected', changes);
|
|
43
|
+
|
|
44
|
+
return changes;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 分类变化类型
|
|
48
|
+
categorizeChanges(changes) {
|
|
49
|
+
const categories = {
|
|
50
|
+
structural: [], // 结构变化(新增/删除class)
|
|
51
|
+
content: [], // 内容变化(class值修改)
|
|
52
|
+
style: [], // 样式相关变化
|
|
53
|
+
layout: [], // 布局相关变化
|
|
54
|
+
utility: [], // 工具类变化
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const allChanged = [...changes.added, ...changes.removed];
|
|
58
|
+
|
|
59
|
+
allChanged.forEach((className) => {
|
|
60
|
+
if (this.isLayoutClass(className)) {
|
|
61
|
+
categories.layout.push(className);
|
|
62
|
+
} else if (this.isStyleClass(className)) {
|
|
63
|
+
categories.style.push(className);
|
|
64
|
+
} else if (this.isUtilityClass(className)) {
|
|
65
|
+
categories.utility.push(className);
|
|
66
|
+
} else {
|
|
67
|
+
categories.structural.push(className);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return categories;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 分析变化原因
|
|
75
|
+
analyzeChangeReasons(changes) {
|
|
76
|
+
const reasons = {
|
|
77
|
+
layoutAdjustment: false,
|
|
78
|
+
styleUpdate: false,
|
|
79
|
+
responsiveDesign: false,
|
|
80
|
+
componentRefactor: false,
|
|
81
|
+
bugFix: false,
|
|
82
|
+
featureAddition: false,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const { added, removed } = changes;
|
|
86
|
+
const allChanged = [...added, ...removed];
|
|
87
|
+
|
|
88
|
+
// 检测布局调整
|
|
89
|
+
if (this.hasLayoutClasses(allChanged)) {
|
|
90
|
+
reasons.layoutAdjustment = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 检测样式更新
|
|
94
|
+
if (this.hasStyleClasses(allChanged)) {
|
|
95
|
+
reasons.styleUpdate = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 检测响应式设计
|
|
99
|
+
if (this.hasResponsiveClasses(allChanged)) {
|
|
100
|
+
reasons.responsiveDesign = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 检测组件重构
|
|
104
|
+
if (added.length > 5 || removed.length > 5) {
|
|
105
|
+
reasons.componentRefactor = true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 检测功能添加
|
|
109
|
+
if (added.length > 0 && removed.length === 0) {
|
|
110
|
+
reasons.featureAddition = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return reasons;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 判断是否为布局类
|
|
117
|
+
isLayoutClass(className) {
|
|
118
|
+
const layoutPatterns = [
|
|
119
|
+
/^(m|p|w|h|max-w|min-w|max-h|min-h|top|right|bottom|left|inset)/,
|
|
120
|
+
/^(flex|grid|block|inline|hidden|visible)/,
|
|
121
|
+
/^(absolute|relative|fixed|sticky)/,
|
|
122
|
+
/^(z-|gap-|space-)/,
|
|
123
|
+
/^(container|wrapper|layout)/,
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
return layoutPatterns.some((pattern) => pattern.test(className));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 判断是否为样式类
|
|
130
|
+
isStyleClass(className) {
|
|
131
|
+
const stylePatterns = [
|
|
132
|
+
/^(text-|font-|color-|bg-|border-)/,
|
|
133
|
+
/^(rounded|shadow|opacity|transition)/,
|
|
134
|
+
/^(hover:|focus:|active:|disabled:)/,
|
|
135
|
+
/^(transform|scale|rotate|translate)/,
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
return stylePatterns.some((pattern) => pattern.test(className));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 判断是否为工具类
|
|
142
|
+
isUtilityClass(className) {
|
|
143
|
+
const utilityPatterns = [
|
|
144
|
+
/^(cursor-|pointer-events|user-select)/,
|
|
145
|
+
/^(overflow|resize|select)/,
|
|
146
|
+
/^(whitespace|break|hyphens)/,
|
|
147
|
+
/^(list-|table-|caption)/,
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
return utilityPatterns.some((pattern) => pattern.test(className));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 检测布局类
|
|
154
|
+
hasLayoutClasses(classes) {
|
|
155
|
+
return classes.some((cls) => this.isLayoutClass(cls));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 检测样式类
|
|
159
|
+
hasStyleClasses(classes) {
|
|
160
|
+
return classes.some((cls) => this.isStyleClass(cls));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 检测响应式类
|
|
164
|
+
hasResponsiveClasses(classes) {
|
|
165
|
+
const responsivePatterns = [/^(sm:|md:|lg:|xl:|2xl:)/, /^(mobile:|tablet:|desktop:)/];
|
|
166
|
+
|
|
167
|
+
return classes.some((cls) => responsivePatterns.some((pattern) => pattern.test(cls)));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 生成变化报告
|
|
171
|
+
generateReport(changes, options = {}) {
|
|
172
|
+
const {
|
|
173
|
+
includeDetails = true,
|
|
174
|
+
includeStatistics = true,
|
|
175
|
+
includeCategories = true,
|
|
176
|
+
includeReasons = true,
|
|
177
|
+
maxDisplayItems = 10,
|
|
178
|
+
} = options;
|
|
179
|
+
|
|
180
|
+
const report = {
|
|
181
|
+
summary: this.generateSummary(changes),
|
|
182
|
+
timestamp: changes.timestamp,
|
|
183
|
+
filePath: changes.filePath,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (includeDetails) {
|
|
187
|
+
report.details = {
|
|
188
|
+
added: changes.added.slice(0, maxDisplayItems),
|
|
189
|
+
removed: changes.removed.slice(0, maxDisplayItems),
|
|
190
|
+
unchanged: changes.unchanged.slice(0, maxDisplayItems),
|
|
191
|
+
hasMore: {
|
|
192
|
+
added: changes.added.length > maxDisplayItems,
|
|
193
|
+
removed: changes.removed.length > maxDisplayItems,
|
|
194
|
+
unchanged: changes.unchanged.length > maxDisplayItems,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (includeStatistics) {
|
|
200
|
+
report.statistics = changes.total;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (includeCategories) {
|
|
204
|
+
report.categories = changes.categories;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (includeReasons) {
|
|
208
|
+
report.reasons = changes.reasons;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return report;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 生成摘要
|
|
215
|
+
generateSummary(changes) {
|
|
216
|
+
const { added, removed, total } = changes;
|
|
217
|
+
|
|
218
|
+
if (added.length === 0 && removed.length === 0) {
|
|
219
|
+
return 'No class changes detected';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let summary = '';
|
|
223
|
+
|
|
224
|
+
if (added.length > 0) {
|
|
225
|
+
summary += `Added ${added.length} class${added.length > 1 ? 'es' : ''}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (removed.length > 0) {
|
|
229
|
+
if (summary) summary += ', ';
|
|
230
|
+
summary += `removed ${removed.length} class${removed.length > 1 ? 'es' : ''}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
summary += ` (${total.old} → ${total.new})`;
|
|
234
|
+
|
|
235
|
+
return summary;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 格式化变化显示
|
|
239
|
+
formatChanges(changes, options = {}) {
|
|
240
|
+
const { useColors = true, showDetails = true, maxItems = 5 } = options;
|
|
241
|
+
|
|
242
|
+
const format = {
|
|
243
|
+
summary: this.generateSummary(changes),
|
|
244
|
+
details: {},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
if (showDetails) {
|
|
248
|
+
if (changes.added.length > 0) {
|
|
249
|
+
format.details.added = {
|
|
250
|
+
count: changes.added.length,
|
|
251
|
+
items: changes.added.slice(0, maxItems),
|
|
252
|
+
hasMore: changes.added.length > maxItems,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (changes.removed.length > 0) {
|
|
257
|
+
format.details.removed = {
|
|
258
|
+
count: changes.removed.length,
|
|
259
|
+
items: changes.removed.slice(0, maxItems),
|
|
260
|
+
hasMore: changes.removed.length > maxItems,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (useColors) {
|
|
266
|
+
format.colors = {
|
|
267
|
+
added: '\x1b[32m', // 绿色
|
|
268
|
+
removed: '\x1b[31m', // 红色
|
|
269
|
+
unchanged: '\x1b[37m', // 白色
|
|
270
|
+
reset: '\x1b[0m', // 重置
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return format;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 获取变化统计
|
|
278
|
+
getChangeStatistics(changes) {
|
|
279
|
+
return {
|
|
280
|
+
totalChanges: changes.added.length + changes.removed.length,
|
|
281
|
+
netChange: changes.added.length - changes.removed.length,
|
|
282
|
+
changeRatio:
|
|
283
|
+
changes.total.old > 0
|
|
284
|
+
? (((changes.added.length + changes.removed.length) / changes.total.old) * 100).toFixed(
|
|
285
|
+
2
|
|
286
|
+
) + '%'
|
|
287
|
+
: '0%',
|
|
288
|
+
categories: Object.keys(changes.categories).filter(
|
|
289
|
+
(key) => changes.categories[key].length > 0
|
|
290
|
+
),
|
|
291
|
+
reasons: Object.keys(changes.reasons).filter((key) => changes.reasons[key]),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 保存变化历史
|
|
296
|
+
saveChangeHistory(changes, filePath) {
|
|
297
|
+
if (!this.changeHistory.has(filePath)) {
|
|
298
|
+
this.changeHistory.set(filePath, []);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const history = this.changeHistory.get(filePath);
|
|
302
|
+
history.push({
|
|
303
|
+
...changes,
|
|
304
|
+
id: this.generateChangeId(),
|
|
305
|
+
timestamp: Date.now(),
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// 限制历史记录大小
|
|
309
|
+
if (history.length > this.maxHistorySize) {
|
|
310
|
+
history.shift();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
this.eventBus.emit('classChange:history:updated', {
|
|
314
|
+
filePath,
|
|
315
|
+
historySize: history.length,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// 获取变化历史
|
|
320
|
+
getChangeHistory(filePath, limit = 10) {
|
|
321
|
+
const history = this.changeHistory.get(filePath) || [];
|
|
322
|
+
return history.slice(-limit);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 生成变化ID
|
|
326
|
+
generateChangeId() {
|
|
327
|
+
return `change_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 清理历史记录
|
|
331
|
+
clearHistory(filePath = null) {
|
|
332
|
+
if (filePath) {
|
|
333
|
+
this.changeHistory.delete(filePath);
|
|
334
|
+
} else {
|
|
335
|
+
this.changeHistory.clear();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.eventBus.emit('classChange:history:cleared', { filePath });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 获取统计信息
|
|
342
|
+
getStats() {
|
|
343
|
+
const totalFiles = this.changeHistory.size;
|
|
344
|
+
let totalChanges = 0;
|
|
345
|
+
let totalAdded = 0;
|
|
346
|
+
let totalRemoved = 0;
|
|
347
|
+
|
|
348
|
+
for (const [filePath, history] of this.changeHistory.entries()) {
|
|
349
|
+
totalChanges += history.length;
|
|
350
|
+
history.forEach((change) => {
|
|
351
|
+
totalAdded += change.added.length;
|
|
352
|
+
totalRemoved += change.removed.length;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
totalFiles,
|
|
358
|
+
totalChanges,
|
|
359
|
+
totalAdded,
|
|
360
|
+
totalRemoved,
|
|
361
|
+
averageChangesPerFile: totalFiles > 0 ? (totalChanges / totalFiles).toFixed(2) : 0,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
module.exports = ClassChangeTracker;
|