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,350 @@
1
+ const path = require('path');
2
+
3
+ class ConfigValidator {
4
+ constructor(eventBus) {
5
+ this.eventBus = eventBus;
6
+ this.errors = [];
7
+ this.warnings = [];
8
+ }
9
+
10
+ // 验证完整配置
11
+ validateConfig(config) {
12
+ this.errors = [];
13
+ this.warnings = [];
14
+
15
+ // 基础配置验证
16
+ this.validateSystemConfig(config);
17
+
18
+ // 原子化规则验证
19
+ this.validateAtomicRules(config);
20
+
21
+ // 兼容性配置验证
22
+ this.validateCompatibilityConfig(config);
23
+
24
+ // 冲突检测
25
+ this.detectConflicts(config);
26
+
27
+ // 单位一致性检查
28
+ this.checkUnitConsistency(config);
29
+
30
+ return {
31
+ isValid: this.errors.length === 0,
32
+ errors: this.errors,
33
+ warnings: this.warnings,
34
+ };
35
+ }
36
+
37
+ // 验证系统配置
38
+ validateSystemConfig(config) {
39
+ const system = config.system || {};
40
+
41
+ // 检查必需字段
42
+ if (!system.baseUnit) {
43
+ this.errors.push('system.baseUnit is required');
44
+ }
45
+
46
+ if (typeof system.unitConversion !== 'number' && typeof system.unitConversion !== 'string') {
47
+ this.errors.push('system.unitConversion must be a number or string');
48
+ }
49
+
50
+ // 验证单位转换比例
51
+ const conversion = parseFloat(system.unitConversion);
52
+ if (isNaN(conversion) || conversion <= 0) {
53
+ this.errors.push('system.unitConversion must be a positive number');
54
+ }
55
+
56
+ // 验证压缩设置
57
+ if (typeof system.compression !== 'boolean') {
58
+ this.warnings.push('system.compression should be a boolean, defaulting to true');
59
+ }
60
+ }
61
+
62
+ // 验证原子化规则
63
+ validateAtomicRules(config) {
64
+ const atomicRules = config.atomicRules || {};
65
+
66
+ // 检查是否至少有一个规则类别
67
+ const ruleCategories = Object.keys(atomicRules);
68
+ if (ruleCategories.length === 0) {
69
+ this.warnings.push('No atomic rules defined, using legacy configuration');
70
+ return;
71
+ }
72
+
73
+ // 验证每个规则类别
74
+ ruleCategories.forEach((category) => {
75
+ this.validateRuleCategory(category, atomicRules[category]);
76
+ });
77
+ }
78
+
79
+ // 验证规则类别
80
+ validateRuleCategory(categoryName, categoryRules) {
81
+ if (typeof categoryRules !== 'object') {
82
+ this.errors.push(`atomicRules.${categoryName} must be an object`);
83
+ return;
84
+ }
85
+
86
+ Object.entries(categoryRules).forEach(([ruleName, ruleConfig]) => {
87
+ this.validateRule(ruleName, ruleConfig, categoryName);
88
+ });
89
+ }
90
+
91
+ // 验证单个规则
92
+ validateRule(ruleName, ruleConfig, categoryName) {
93
+ if (typeof ruleConfig !== 'object') {
94
+ this.errors.push(`atomicRules.${categoryName}.${ruleName} must be an object`);
95
+ return;
96
+ }
97
+
98
+ // 检查必需字段
99
+ if (!ruleConfig.properties) {
100
+ this.errors.push(`atomicRules.${categoryName}.${ruleName} must have properties field`);
101
+ return;
102
+ }
103
+
104
+ // 验证properties字段
105
+ if (!Array.isArray(ruleConfig.properties) && typeof ruleConfig.properties !== 'string') {
106
+ this.errors.push(
107
+ `atomicRules.${categoryName}.${ruleName}.properties must be string or array`
108
+ );
109
+ return;
110
+ }
111
+
112
+ // 验证defaultUnit
113
+ if (ruleConfig.defaultUnit !== undefined && typeof ruleConfig.defaultUnit !== 'string') {
114
+ this.errors.push(`atomicRules.${categoryName}.${ruleName}.defaultUnit must be a string`);
115
+ }
116
+
117
+ // 验证value字段(用于固定值)
118
+ if (
119
+ ruleConfig.value !== undefined &&
120
+ typeof ruleConfig.value !== 'string' &&
121
+ typeof ruleConfig.value !== 'number'
122
+ ) {
123
+ this.errors.push(`atomicRules.${categoryName}.${ruleName}.value must be string or number`);
124
+ }
125
+ }
126
+
127
+ // 验证兼容性配置
128
+ validateCompatibilityConfig(config) {
129
+ const compatibility = config.compatibility || {};
130
+
131
+ // 验证legacy配置
132
+ if (compatibility.legacy) {
133
+ this.validateLegacyConfig(compatibility.legacy);
134
+ }
135
+
136
+ // 验证unified配置
137
+ if (compatibility.unified) {
138
+ if (typeof compatibility.unified.enabled !== 'boolean') {
139
+ this.warnings.push('compatibility.unified.enabled should be a boolean');
140
+ }
141
+ }
142
+ }
143
+
144
+ // 验证legacy配置
145
+ validateLegacyConfig(legacy) {
146
+ // 验证cssName
147
+ if (legacy.cssName && typeof legacy.cssName !== 'object') {
148
+ this.errors.push('compatibility.legacy.cssName must be an object');
149
+ }
150
+
151
+ // 验证baseClassName
152
+ if (legacy.baseClassName && typeof legacy.baseClassName !== 'object') {
153
+ this.errors.push('compatibility.legacy.baseClassName must be an object');
154
+ }
155
+
156
+ // 验证atomicClassMap
157
+ if (legacy.atomicClassMap && typeof legacy.atomicClassMap !== 'object') {
158
+ this.errors.push('compatibility.legacy.atomicClassMap must be an object');
159
+ }
160
+ }
161
+
162
+ // 检测配置冲突
163
+ detectConflicts(config) {
164
+ const conflicts = [];
165
+
166
+ // 检测text属性冲突
167
+ const textConflicts = this.detectTextConflicts(config);
168
+ conflicts.push(...textConflicts);
169
+
170
+ // 检测重复定义
171
+ const duplicateConflicts = this.detectDuplicateDefinitions(config);
172
+ conflicts.push(...duplicateConflicts);
173
+
174
+ // 报告冲突
175
+ conflicts.forEach((conflict) => {
176
+ this.warnings.push(`Configuration conflict detected: ${conflict}`);
177
+ });
178
+ }
179
+
180
+ // 检测text属性冲突
181
+ detectTextConflicts(config) {
182
+ const conflicts = [];
183
+
184
+ // 检查cssName中的text定义
185
+ const cssNameText = config.cssName?.text;
186
+ const atomicClassMapText = config.atomicClassMap?.text;
187
+
188
+ if (cssNameText && atomicClassMapText) {
189
+ conflicts.push('text property defined in both cssName and atomicClassMap');
190
+ }
191
+
192
+ return conflicts;
193
+ }
194
+
195
+ // 检测重复定义
196
+ detectDuplicateDefinitions(config) {
197
+ const conflicts = [];
198
+ const allDefinitions = new Set();
199
+
200
+ // 收集所有定义
201
+ const collectDefinitions = (obj, prefix = '') => {
202
+ if (!obj || typeof obj !== 'object') return;
203
+
204
+ Object.keys(obj).forEach((key) => {
205
+ const fullKey = prefix ? `${prefix}.${key}` : key;
206
+ if (allDefinitions.has(fullKey)) {
207
+ conflicts.push(`Duplicate definition: ${fullKey}`);
208
+ } else {
209
+ allDefinitions.add(fullKey);
210
+ }
211
+
212
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
213
+ collectDefinitions(obj[key], fullKey);
214
+ }
215
+ });
216
+ };
217
+
218
+ // 检查各个配置段
219
+ collectDefinitions(config.cssName, 'cssName');
220
+ collectDefinitions(config.baseClassName, 'baseClassName');
221
+ collectDefinitions(config.atomicClassMap, 'atomicClassMap');
222
+ collectDefinitions(config.atomicRules, 'atomicRules');
223
+
224
+ return conflicts;
225
+ }
226
+
227
+ // 检查单位一致性
228
+ checkUnitConsistency(config) {
229
+ const inconsistencies = [];
230
+
231
+ // 检查基础单位设置
232
+ const baseUnit = config.system?.baseUnit || config.baseUnit;
233
+ if (!baseUnit) {
234
+ this.warnings.push('No base unit specified, defaulting to px');
235
+ return;
236
+ }
237
+
238
+ // 检查atomicClassMap中的单位一致性
239
+ const atomicClassMap = config.atomicClassMap || {};
240
+ Object.entries(atomicClassMap).forEach(([key, value]) => {
241
+ if (value && typeof value === 'object' && value.unit) {
242
+ // 检查是否与基础单位一致
243
+ if (value.unit !== baseUnit && value.unit !== '') {
244
+ this.warnings.push(
245
+ `Unit inconsistency: ${key} uses ${value.unit} instead of ${baseUnit}`
246
+ );
247
+ }
248
+ }
249
+ });
250
+
251
+ return inconsistencies;
252
+ }
253
+
254
+ // 自动修复配置
255
+ autoFix(config) {
256
+ const fixedConfig = JSON.parse(JSON.stringify(config));
257
+ let fixed = false;
258
+
259
+ // 修复缺失的默认值
260
+ if (!fixedConfig.system) {
261
+ fixedConfig.system = {};
262
+ fixed = true;
263
+ }
264
+
265
+ if (!fixedConfig.system.baseUnit) {
266
+ fixedConfig.system.baseUnit = 'px';
267
+ fixed = true;
268
+ }
269
+
270
+ if (fixedConfig.system.unitConversion === undefined) {
271
+ fixedConfig.system.unitConversion = 1;
272
+ fixed = true;
273
+ }
274
+
275
+ if (fixedConfig.system.compression === undefined) {
276
+ fixedConfig.system.compression = true;
277
+ fixed = true;
278
+ }
279
+
280
+ // 修复配置冲突
281
+ if (this.resolveConflicts(fixedConfig)) {
282
+ fixed = true;
283
+ }
284
+
285
+ return { config: fixedConfig, fixed };
286
+ }
287
+
288
+ // 解决配置冲突
289
+ resolveConflicts(config) {
290
+ let resolved = false;
291
+
292
+ // 解决text属性冲突
293
+ if (config.cssName?.text && config.atomicClassMap?.text) {
294
+ // 保留atomicClassMap中的定义,移除cssName中的
295
+ delete config.cssName.text;
296
+ resolved = true;
297
+ }
298
+
299
+ return resolved;
300
+ }
301
+
302
+ // 生成配置报告
303
+ generateReport(config) {
304
+ const validation = this.validateConfig(config);
305
+ const autoFix = this.autoFix(config);
306
+
307
+ return {
308
+ validation,
309
+ autoFix,
310
+ recommendations: this.generateRecommendations(config),
311
+ summary: {
312
+ totalErrors: validation.errors.length,
313
+ totalWarnings: validation.warnings.length,
314
+ needsAutoFix: autoFix.fixed,
315
+ overallStatus: validation.isValid ? 'valid' : 'invalid',
316
+ },
317
+ };
318
+ }
319
+
320
+ // 生成优化建议
321
+ generateRecommendations(config) {
322
+ const recommendations = [];
323
+
324
+ // 检查是否使用了新的配置结构
325
+ if (!config.atomicRules) {
326
+ recommendations.push(
327
+ 'Consider migrating to the new atomicRules structure for better organization'
328
+ );
329
+ }
330
+
331
+ // 检查单位一致性
332
+ if (config.atomicClassMap) {
333
+ const baseUnit = config.system?.baseUnit || config.baseUnit;
334
+ Object.entries(config.atomicClassMap).forEach(([key, value]) => {
335
+ if (value && typeof value === 'object' && value.unit && value.unit !== baseUnit) {
336
+ recommendations.push(`Consider using consistent units: ${key} should use ${baseUnit}`);
337
+ }
338
+ });
339
+ }
340
+
341
+ // 检查是否启用了验证
342
+ if (!config.validation) {
343
+ recommendations.push('Enable configuration validation for better error detection');
344
+ }
345
+
346
+ return recommendations;
347
+ }
348
+ }
349
+
350
+ module.exports = ConfigValidator;
@@ -0,0 +1,77 @@
1
+ class EventBus {
2
+ constructor() {
3
+ this.events = new Map();
4
+ this.onceEvents = new Map();
5
+ }
6
+
7
+ on(event, callback) {
8
+ if (!this.events.has(event)) {
9
+ this.events.set(event, []);
10
+ }
11
+ this.events.get(event).push(callback);
12
+ }
13
+
14
+ once(event, callback) {
15
+ if (!this.onceEvents.has(event)) {
16
+ this.onceEvents.set(event, []);
17
+ }
18
+ this.onceEvents.get(event).push(callback);
19
+ }
20
+
21
+ off(event, callback) {
22
+ if (this.events.has(event)) {
23
+ const callbacks = this.events.get(event);
24
+ const index = callbacks.indexOf(callback);
25
+ if (index > -1) {
26
+ callbacks.splice(index, 1);
27
+ }
28
+ }
29
+
30
+ if (this.onceEvents.has(event)) {
31
+ const callbacks = this.onceEvents.get(event);
32
+ const index = callbacks.indexOf(callback);
33
+ if (index > -1) {
34
+ callbacks.splice(index, 1);
35
+ }
36
+ }
37
+ }
38
+
39
+ emit(event, ...args) {
40
+ // 处理普通事件
41
+ if (this.events.has(event)) {
42
+ this.events.get(event).forEach((callback) => {
43
+ try {
44
+ callback(...args);
45
+ } catch (error) {
46
+ console.error(`EventBus error in event '${event}':`, error);
47
+ }
48
+ });
49
+ }
50
+
51
+ // 处理一次性事件
52
+ if (this.onceEvents.has(event)) {
53
+ const callbacks = this.onceEvents.get(event);
54
+ callbacks.forEach((callback) => {
55
+ try {
56
+ callback(...args);
57
+ } catch (error) {
58
+ console.error(`EventBus error in once event '${event}':`, error);
59
+ }
60
+ });
61
+ this.onceEvents.delete(event);
62
+ }
63
+ }
64
+
65
+ clear() {
66
+ this.events.clear();
67
+ this.onceEvents.clear();
68
+ }
69
+
70
+ getEventCount(event) {
71
+ const normalCount = this.events.has(event) ? this.events.get(event).length : 0;
72
+ const onceCount = this.onceEvents.has(event) ? this.onceEvents.get(event).length : 0;
73
+ return normalCount + onceCount;
74
+ }
75
+ }
76
+
77
+ module.exports = EventBus;