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,673 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class ConfigDiagnostics {
|
|
5
|
+
constructor(eventBus, configManager) {
|
|
6
|
+
this.eventBus = eventBus;
|
|
7
|
+
this.configManager = configManager;
|
|
8
|
+
this.diagnosticResults = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 执行完整诊断
|
|
12
|
+
async runFullDiagnostics() {
|
|
13
|
+
this.eventBus.emit('diagnostics:started', { timestamp: Date.now() });
|
|
14
|
+
|
|
15
|
+
const results = {
|
|
16
|
+
timestamp: Date.now(),
|
|
17
|
+
overall: { status: 'unknown', score: 0, maxScore: 100 },
|
|
18
|
+
sections: {},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// 1. 配置文件诊断
|
|
23
|
+
results.sections.configFile = await this.diagnoseConfigFile();
|
|
24
|
+
|
|
25
|
+
// 2. 配置内容诊断
|
|
26
|
+
results.sections.configContent = this.diagnoseConfigContent();
|
|
27
|
+
|
|
28
|
+
// 3. 性能诊断
|
|
29
|
+
results.sections.performance = this.diagnosePerformance();
|
|
30
|
+
|
|
31
|
+
// 4. 兼容性诊断
|
|
32
|
+
results.sections.compatibility = this.diagnoseCompatibility();
|
|
33
|
+
|
|
34
|
+
// 5. 最佳实践诊断
|
|
35
|
+
results.sections.bestPractices = this.diagnoseBestPractices();
|
|
36
|
+
|
|
37
|
+
// 6. 安全性诊断
|
|
38
|
+
results.sections.security = this.diagnoseSecurity();
|
|
39
|
+
|
|
40
|
+
// 计算总分
|
|
41
|
+
results.overall = this.calculateOverallScore(results.sections);
|
|
42
|
+
|
|
43
|
+
this.diagnosticResults = results;
|
|
44
|
+
|
|
45
|
+
this.eventBus.emit('diagnostics:completed', {
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
score: results.overall.score,
|
|
48
|
+
status: results.overall.status,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return results;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
this.eventBus.emit('diagnostics:error', { error: error.message });
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 诊断配置文件
|
|
59
|
+
async diagnoseConfigFile() {
|
|
60
|
+
const section = {
|
|
61
|
+
title: 'Configuration File',
|
|
62
|
+
status: 'pass',
|
|
63
|
+
score: 0,
|
|
64
|
+
maxScore: 20,
|
|
65
|
+
checks: [],
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// 检查主配置文件是否存在
|
|
69
|
+
const configPath = path.join(process.cwd(), 'class2css.config.js');
|
|
70
|
+
const fileExists = fs.existsSync(configPath);
|
|
71
|
+
|
|
72
|
+
section.checks.push({
|
|
73
|
+
name: 'Config file exists',
|
|
74
|
+
status: fileExists ? 'pass' : 'fail',
|
|
75
|
+
score: fileExists ? 5 : 0,
|
|
76
|
+
maxScore: 5,
|
|
77
|
+
message: fileExists ? 'Configuration file found' : 'class2css.config.js not found',
|
|
78
|
+
suggestion: fileExists ? null : 'Create class2css.config.js file',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (fileExists) {
|
|
82
|
+
// 检查文件可读性
|
|
83
|
+
try {
|
|
84
|
+
const stats = fs.statSync(configPath);
|
|
85
|
+
const isReadable = stats.isFile();
|
|
86
|
+
|
|
87
|
+
section.checks.push({
|
|
88
|
+
name: 'Config file readable',
|
|
89
|
+
status: isReadable ? 'pass' : 'fail',
|
|
90
|
+
score: isReadable ? 5 : 0,
|
|
91
|
+
maxScore: 5,
|
|
92
|
+
message: isReadable
|
|
93
|
+
? 'Configuration file is readable'
|
|
94
|
+
: 'Configuration file is not readable',
|
|
95
|
+
suggestion: isReadable ? null : 'Check file permissions',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 检查文件大小
|
|
99
|
+
const fileSizeKB = stats.size / 1024;
|
|
100
|
+
const isSizeReasonable = fileSizeKB < 500; // 500KB限制
|
|
101
|
+
|
|
102
|
+
section.checks.push({
|
|
103
|
+
name: 'Config file size',
|
|
104
|
+
status: isSizeReasonable ? 'pass' : 'warning',
|
|
105
|
+
score: isSizeReasonable ? 5 : 3,
|
|
106
|
+
maxScore: 5,
|
|
107
|
+
message: `File size: ${fileSizeKB.toFixed(2)}KB`,
|
|
108
|
+
suggestion: isSizeReasonable
|
|
109
|
+
? null
|
|
110
|
+
: 'Consider splitting large configuration into modules',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 检查模块化配置文件
|
|
114
|
+
const configsDir = path.join(process.cwd(), 'configs');
|
|
115
|
+
const hasModularConfigs = fs.existsSync(configsDir);
|
|
116
|
+
|
|
117
|
+
section.checks.push({
|
|
118
|
+
name: 'Modular configs',
|
|
119
|
+
status: hasModularConfigs ? 'pass' : 'info',
|
|
120
|
+
score: hasModularConfigs ? 5 : 3,
|
|
121
|
+
maxScore: 5,
|
|
122
|
+
message: hasModularConfigs
|
|
123
|
+
? 'Modular configuration detected'
|
|
124
|
+
: 'Using monolithic configuration',
|
|
125
|
+
suggestion: hasModularConfigs
|
|
126
|
+
? null
|
|
127
|
+
: 'Consider using modular configuration for better maintainability',
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
section.checks.push({
|
|
131
|
+
name: 'Config file access',
|
|
132
|
+
status: 'fail',
|
|
133
|
+
score: 0,
|
|
134
|
+
maxScore: 15,
|
|
135
|
+
message: `Error accessing config file: ${error.message}`,
|
|
136
|
+
suggestion: 'Check file permissions and path',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
142
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
143
|
+
|
|
144
|
+
return section;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 诊断配置内容
|
|
148
|
+
diagnoseConfigContent() {
|
|
149
|
+
const section = {
|
|
150
|
+
title: 'Configuration Content',
|
|
151
|
+
status: 'pass',
|
|
152
|
+
score: 0,
|
|
153
|
+
maxScore: 25,
|
|
154
|
+
checks: [],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (!this.configManager) {
|
|
158
|
+
section.checks.push({
|
|
159
|
+
name: 'Config manager availability',
|
|
160
|
+
status: 'fail',
|
|
161
|
+
score: 0,
|
|
162
|
+
maxScore: 25,
|
|
163
|
+
message: 'Configuration manager not available',
|
|
164
|
+
suggestion: 'Initialize configuration manager',
|
|
165
|
+
});
|
|
166
|
+
section.status = 'fail';
|
|
167
|
+
return section;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const config = this.configManager.getConfig();
|
|
171
|
+
|
|
172
|
+
// 检查必需的配置项
|
|
173
|
+
const requiredFields = ['output', 'cssName', 'baseClassName'];
|
|
174
|
+
requiredFields.forEach((field) => {
|
|
175
|
+
const hasField = config[field] !== undefined;
|
|
176
|
+
section.checks.push({
|
|
177
|
+
name: `Required field: ${field}`,
|
|
178
|
+
status: hasField ? 'pass' : 'fail',
|
|
179
|
+
score: hasField ? 3 : 0,
|
|
180
|
+
maxScore: 3,
|
|
181
|
+
message: hasField ? `${field} is configured` : `${field} is missing`,
|
|
182
|
+
suggestion: hasField ? null : `Add ${field} configuration`,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 检查系统配置
|
|
187
|
+
const hasSystemConfig = config.system !== undefined;
|
|
188
|
+
section.checks.push({
|
|
189
|
+
name: 'System configuration',
|
|
190
|
+
status: hasSystemConfig ? 'pass' : 'info',
|
|
191
|
+
score: hasSystemConfig ? 4 : 2,
|
|
192
|
+
maxScore: 4,
|
|
193
|
+
message: hasSystemConfig
|
|
194
|
+
? 'System configuration found'
|
|
195
|
+
: 'Using legacy configuration structure',
|
|
196
|
+
suggestion: hasSystemConfig ? null : 'Consider migrating to system configuration',
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// 检查单位配置
|
|
200
|
+
const baseUnit = config.system?.baseUnit || config.baseUnit;
|
|
201
|
+
const unitConversion = config.system?.unitConversion || config.unitConversion;
|
|
202
|
+
|
|
203
|
+
section.checks.push({
|
|
204
|
+
name: 'Unit configuration',
|
|
205
|
+
status: baseUnit && unitConversion ? 'pass' : 'warning',
|
|
206
|
+
score: baseUnit && unitConversion ? 3 : 1,
|
|
207
|
+
maxScore: 3,
|
|
208
|
+
message: `Base unit: ${baseUnit || 'undefined'}, Conversion: ${unitConversion || 'undefined'}`,
|
|
209
|
+
suggestion: baseUnit && unitConversion ? null : 'Configure baseUnit and unitConversion',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// 检查CSS类映射
|
|
213
|
+
const cssNameMap = this.configManager.getCssNameMap();
|
|
214
|
+
const hasClasses = cssNameMap && cssNameMap.size > 0;
|
|
215
|
+
|
|
216
|
+
section.checks.push({
|
|
217
|
+
name: 'CSS class mappings',
|
|
218
|
+
status: hasClasses ? 'pass' : 'fail',
|
|
219
|
+
score: hasClasses ? 5 : 0,
|
|
220
|
+
maxScore: 5,
|
|
221
|
+
message: hasClasses
|
|
222
|
+
? `${cssNameMap.size} class mappings found`
|
|
223
|
+
: 'No CSS class mappings found',
|
|
224
|
+
suggestion: hasClasses ? null : 'Add CSS class mappings to cssName configuration',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// 检查重要标识配置
|
|
228
|
+
const importantFlags = config.importantFlags;
|
|
229
|
+
const hasImportantFlags =
|
|
230
|
+
importantFlags && importantFlags.suffix && importantFlags.suffix.length > 0;
|
|
231
|
+
|
|
232
|
+
section.checks.push({
|
|
233
|
+
name: 'Important flags',
|
|
234
|
+
status: hasImportantFlags ? 'pass' : 'warning',
|
|
235
|
+
score: hasImportantFlags ? 2 : 1,
|
|
236
|
+
maxScore: 2,
|
|
237
|
+
message: hasImportantFlags ? 'Important flags configured' : 'No important flags configured',
|
|
238
|
+
suggestion: hasImportantFlags
|
|
239
|
+
? null
|
|
240
|
+
: 'Consider configuring important flags for !important support',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
244
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
245
|
+
|
|
246
|
+
return section;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 诊断性能
|
|
250
|
+
diagnosePerformance() {
|
|
251
|
+
const section = {
|
|
252
|
+
title: 'Performance',
|
|
253
|
+
status: 'pass',
|
|
254
|
+
score: 0,
|
|
255
|
+
maxScore: 20,
|
|
256
|
+
checks: [],
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const config = this.configManager?.getConfig();
|
|
260
|
+
|
|
261
|
+
// 检查压缩配置
|
|
262
|
+
const compression = config?.system?.compression || config?.compression;
|
|
263
|
+
section.checks.push({
|
|
264
|
+
name: 'CSS compression',
|
|
265
|
+
status: compression ? 'pass' : 'warning',
|
|
266
|
+
score: compression ? 5 : 2,
|
|
267
|
+
maxScore: 5,
|
|
268
|
+
message: compression ? 'CSS compression enabled' : 'CSS compression disabled',
|
|
269
|
+
suggestion: compression ? null : 'Enable compression for better performance',
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// 检查缓存配置
|
|
273
|
+
const cacheConfig = config?.cache;
|
|
274
|
+
section.checks.push({
|
|
275
|
+
name: 'Cache configuration',
|
|
276
|
+
status: cacheConfig ? 'pass' : 'info',
|
|
277
|
+
score: cacheConfig ? 5 : 3,
|
|
278
|
+
maxScore: 5,
|
|
279
|
+
message: cacheConfig ? 'Cache configuration found' : 'Using default cache settings',
|
|
280
|
+
suggestion: cacheConfig
|
|
281
|
+
? null
|
|
282
|
+
: 'Consider configuring cache settings for optimal performance',
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// 检查多文件模式
|
|
286
|
+
const multiFile = config?.multiFile;
|
|
287
|
+
const isMultiFileOptimal = multiFile && multiFile.enabled;
|
|
288
|
+
|
|
289
|
+
section.checks.push({
|
|
290
|
+
name: 'Multi-file mode',
|
|
291
|
+
status: isMultiFileOptimal ? 'pass' : 'info',
|
|
292
|
+
score: isMultiFileOptimal ? 5 : 3,
|
|
293
|
+
maxScore: 5,
|
|
294
|
+
message: isMultiFileOptimal ? 'Multi-file mode enabled' : 'Single file mode',
|
|
295
|
+
suggestion: isMultiFileOptimal ? null : 'Consider multi-file mode for large projects',
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// 检查CSS类数量
|
|
299
|
+
const cssNameMap = this.configManager?.getCssNameMap();
|
|
300
|
+
const classCount = cssNameMap ? cssNameMap.size : 0;
|
|
301
|
+
const isClassCountReasonable = classCount < 1000;
|
|
302
|
+
|
|
303
|
+
section.checks.push({
|
|
304
|
+
name: 'CSS class count',
|
|
305
|
+
status: isClassCountReasonable ? 'pass' : 'warning',
|
|
306
|
+
score: isClassCountReasonable ? 5 : 2,
|
|
307
|
+
maxScore: 5,
|
|
308
|
+
message: `${classCount} CSS classes configured`,
|
|
309
|
+
suggestion: isClassCountReasonable
|
|
310
|
+
? null
|
|
311
|
+
: 'Large number of CSS classes may impact performance',
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
315
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
316
|
+
|
|
317
|
+
return section;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 诊断兼容性
|
|
321
|
+
diagnoseCompatibility() {
|
|
322
|
+
const section = {
|
|
323
|
+
title: 'Compatibility',
|
|
324
|
+
status: 'pass',
|
|
325
|
+
score: 0,
|
|
326
|
+
maxScore: 15,
|
|
327
|
+
checks: [],
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const config = this.configManager?.getConfig();
|
|
331
|
+
|
|
332
|
+
// 检查版本兼容性
|
|
333
|
+
const hasLegacyStructure =
|
|
334
|
+
config && (config.baseUnit || config.unitConversion) && !config.system;
|
|
335
|
+
const hasNewStructure = config && config.system;
|
|
336
|
+
|
|
337
|
+
let compatibilityStatus = 'unknown';
|
|
338
|
+
let compatibilityScore = 0;
|
|
339
|
+
let compatibilityMessage = '';
|
|
340
|
+
|
|
341
|
+
if (hasNewStructure && hasLegacyStructure) {
|
|
342
|
+
compatibilityStatus = 'pass';
|
|
343
|
+
compatibilityScore = 5;
|
|
344
|
+
compatibilityMessage = 'Both legacy and new configuration structures detected';
|
|
345
|
+
} else if (hasNewStructure) {
|
|
346
|
+
compatibilityStatus = 'pass';
|
|
347
|
+
compatibilityScore = 5;
|
|
348
|
+
compatibilityMessage = 'Using new configuration structure';
|
|
349
|
+
} else if (hasLegacyStructure) {
|
|
350
|
+
compatibilityStatus = 'warning';
|
|
351
|
+
compatibilityScore = 3;
|
|
352
|
+
compatibilityMessage = 'Using legacy configuration structure';
|
|
353
|
+
} else {
|
|
354
|
+
compatibilityStatus = 'fail';
|
|
355
|
+
compatibilityScore = 0;
|
|
356
|
+
compatibilityMessage = 'No valid configuration structure detected';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
section.checks.push({
|
|
360
|
+
name: 'Configuration structure',
|
|
361
|
+
status: compatibilityStatus,
|
|
362
|
+
score: compatibilityScore,
|
|
363
|
+
maxScore: 5,
|
|
364
|
+
message: compatibilityMessage,
|
|
365
|
+
suggestion:
|
|
366
|
+
compatibilityStatus === 'warning'
|
|
367
|
+
? 'Consider migrating to new configuration structure'
|
|
368
|
+
: null,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// 检查单位兼容性
|
|
372
|
+
const unitStrategy = config?.system?.unitStrategy;
|
|
373
|
+
section.checks.push({
|
|
374
|
+
name: 'Unit strategy',
|
|
375
|
+
status: unitStrategy ? 'pass' : 'info',
|
|
376
|
+
score: unitStrategy ? 5 : 3,
|
|
377
|
+
maxScore: 5,
|
|
378
|
+
message: unitStrategy ? 'Advanced unit strategy configured' : 'Using basic unit handling',
|
|
379
|
+
suggestion: unitStrategy ? null : 'Configure unit strategy for better unit handling',
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// 检查变体支持
|
|
383
|
+
const variants = config?.variants;
|
|
384
|
+
section.checks.push({
|
|
385
|
+
name: 'Variant support',
|
|
386
|
+
status: variants ? 'pass' : 'info',
|
|
387
|
+
score: variants ? 5 : 3,
|
|
388
|
+
maxScore: 5,
|
|
389
|
+
message: variants ? 'Variant configuration found' : 'No variant configuration',
|
|
390
|
+
suggestion: variants ? null : 'Configure variants for responsive and pseudo-class support',
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
394
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
395
|
+
|
|
396
|
+
return section;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 诊断最佳实践
|
|
400
|
+
diagnoseBestPractices() {
|
|
401
|
+
const section = {
|
|
402
|
+
title: 'Best Practices',
|
|
403
|
+
status: 'pass',
|
|
404
|
+
score: 0,
|
|
405
|
+
maxScore: 15,
|
|
406
|
+
checks: [],
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const config = this.configManager?.getConfig();
|
|
410
|
+
|
|
411
|
+
// 检查配置组织
|
|
412
|
+
const hasSystemSection = config?.system;
|
|
413
|
+
section.checks.push({
|
|
414
|
+
name: 'Configuration organization',
|
|
415
|
+
status: hasSystemSection ? 'pass' : 'warning',
|
|
416
|
+
score: hasSystemSection ? 5 : 2,
|
|
417
|
+
maxScore: 5,
|
|
418
|
+
message: hasSystemSection
|
|
419
|
+
? 'Well-organized configuration structure'
|
|
420
|
+
: 'Configuration could be better organized',
|
|
421
|
+
suggestion: hasSystemSection
|
|
422
|
+
? null
|
|
423
|
+
: 'Use system section for better configuration organization',
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// 检查命名约定
|
|
427
|
+
const cssNameMap = this.configManager?.getCssNameMap();
|
|
428
|
+
let namingScore = 0;
|
|
429
|
+
let namingMessage = '';
|
|
430
|
+
|
|
431
|
+
if (cssNameMap && cssNameMap.size > 0) {
|
|
432
|
+
const classNames = Array.from(cssNameMap.keys());
|
|
433
|
+
const hasConsistentNaming = classNames.every((name) => /^[a-z]+(-[a-z]+)*$/.test(name));
|
|
434
|
+
|
|
435
|
+
if (hasConsistentNaming) {
|
|
436
|
+
namingScore = 5;
|
|
437
|
+
namingMessage = 'Consistent kebab-case naming detected';
|
|
438
|
+
} else {
|
|
439
|
+
namingScore = 3;
|
|
440
|
+
namingMessage = 'Inconsistent naming convention detected';
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
namingScore = 0;
|
|
444
|
+
namingMessage = 'No CSS classes to analyze';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
section.checks.push({
|
|
448
|
+
name: 'Naming convention',
|
|
449
|
+
status: namingScore >= 4 ? 'pass' : namingScore >= 2 ? 'warning' : 'fail',
|
|
450
|
+
score: namingScore,
|
|
451
|
+
maxScore: 5,
|
|
452
|
+
message: namingMessage,
|
|
453
|
+
suggestion: namingScore >= 4 ? null : 'Use consistent kebab-case naming for CSS classes',
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// 检查文档化
|
|
457
|
+
const hasComments = this.checkConfigComments();
|
|
458
|
+
section.checks.push({
|
|
459
|
+
name: 'Configuration documentation',
|
|
460
|
+
status: hasComments ? 'pass' : 'info',
|
|
461
|
+
score: hasComments ? 5 : 3,
|
|
462
|
+
maxScore: 5,
|
|
463
|
+
message: hasComments
|
|
464
|
+
? 'Configuration includes comments'
|
|
465
|
+
: 'Configuration lacks documentation',
|
|
466
|
+
suggestion: hasComments ? null : 'Add comments to explain configuration sections',
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
470
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
471
|
+
|
|
472
|
+
return section;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 诊断安全性
|
|
476
|
+
diagnoseSecurity() {
|
|
477
|
+
const section = {
|
|
478
|
+
title: 'Security',
|
|
479
|
+
status: 'pass',
|
|
480
|
+
score: 0,
|
|
481
|
+
maxScore: 5,
|
|
482
|
+
checks: [],
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// 检查输出路径安全性
|
|
486
|
+
const config = this.configManager?.getConfig();
|
|
487
|
+
const outputPath = config?.output?.path;
|
|
488
|
+
|
|
489
|
+
let pathSecurity = 'unknown';
|
|
490
|
+
let pathScore = 0;
|
|
491
|
+
let pathMessage = '';
|
|
492
|
+
|
|
493
|
+
if (outputPath) {
|
|
494
|
+
const hasTraversal = outputPath.includes('..') || outputPath.includes('~');
|
|
495
|
+
if (hasTraversal) {
|
|
496
|
+
pathSecurity = 'warning';
|
|
497
|
+
pathScore = 2;
|
|
498
|
+
pathMessage = 'Output path contains potentially unsafe traversal';
|
|
499
|
+
} else {
|
|
500
|
+
pathSecurity = 'pass';
|
|
501
|
+
pathScore = 5;
|
|
502
|
+
pathMessage = 'Output path appears secure';
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
pathSecurity = 'fail';
|
|
506
|
+
pathScore = 0;
|
|
507
|
+
pathMessage = 'No output path configured';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
section.checks.push({
|
|
511
|
+
name: 'Output path security',
|
|
512
|
+
status: pathSecurity,
|
|
513
|
+
score: pathScore,
|
|
514
|
+
maxScore: 5,
|
|
515
|
+
message: pathMessage,
|
|
516
|
+
suggestion:
|
|
517
|
+
pathSecurity === 'warning'
|
|
518
|
+
? 'Avoid path traversal in output configuration'
|
|
519
|
+
: pathSecurity === 'fail'
|
|
520
|
+
? 'Configure output path'
|
|
521
|
+
: null,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
section.score = section.checks.reduce((sum, check) => sum + check.score, 0);
|
|
525
|
+
section.status = this.getSectionStatus(section.score, section.maxScore);
|
|
526
|
+
|
|
527
|
+
return section;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// 计算总分
|
|
531
|
+
calculateOverallScore(sections) {
|
|
532
|
+
const totalScore = Object.values(sections).reduce((sum, section) => sum + section.score, 0);
|
|
533
|
+
const maxScore = Object.values(sections).reduce((sum, section) => sum + section.maxScore, 0);
|
|
534
|
+
const percentage = maxScore > 0 ? Math.round((totalScore / maxScore) * 100) : 0;
|
|
535
|
+
|
|
536
|
+
let status = 'fail';
|
|
537
|
+
if (percentage >= 90) status = 'excellent';
|
|
538
|
+
else if (percentage >= 80) status = 'good';
|
|
539
|
+
else if (percentage >= 70) status = 'pass';
|
|
540
|
+
else if (percentage >= 50) status = 'warning';
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
status,
|
|
544
|
+
score: totalScore,
|
|
545
|
+
maxScore,
|
|
546
|
+
percentage,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// 获取区块状态
|
|
551
|
+
getSectionStatus(score, maxScore) {
|
|
552
|
+
const percentage = maxScore > 0 ? (score / maxScore) * 100 : 0;
|
|
553
|
+
|
|
554
|
+
if (percentage >= 90) return 'excellent';
|
|
555
|
+
if (percentage >= 75) return 'pass';
|
|
556
|
+
if (percentage >= 50) return 'warning';
|
|
557
|
+
return 'fail';
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// 检查配置文件是否有注释
|
|
561
|
+
checkConfigComments() {
|
|
562
|
+
try {
|
|
563
|
+
const configPath = path.join(process.cwd(), 'class2css.config.js');
|
|
564
|
+
if (fs.existsSync(configPath)) {
|
|
565
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
566
|
+
return content.includes('//') || content.includes('/*');
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// 生成优化建议
|
|
575
|
+
generateOptimizationSuggestions() {
|
|
576
|
+
if (!this.diagnosticResults) {
|
|
577
|
+
return {
|
|
578
|
+
error: 'No diagnostic results available. Run diagnostics first.',
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const suggestions = {
|
|
583
|
+
high: [],
|
|
584
|
+
medium: [],
|
|
585
|
+
low: [],
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
Object.values(this.diagnosticResults.sections).forEach((section) => {
|
|
589
|
+
section.checks.forEach((check) => {
|
|
590
|
+
if (check.suggestion) {
|
|
591
|
+
const priority =
|
|
592
|
+
check.status === 'fail' ? 'high' : check.status === 'warning' ? 'medium' : 'low';
|
|
593
|
+
|
|
594
|
+
suggestions[priority].push({
|
|
595
|
+
section: section.title,
|
|
596
|
+
check: check.name,
|
|
597
|
+
suggestion: check.suggestion,
|
|
598
|
+
impact: check.maxScore,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
return suggestions;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// 生成报告
|
|
608
|
+
generateReport(format = 'text') {
|
|
609
|
+
if (!this.diagnosticResults) {
|
|
610
|
+
return 'No diagnostic results available. Run diagnostics first.';
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (format === 'json') {
|
|
614
|
+
return JSON.stringify(this.diagnosticResults, null, 2);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// 文本格式报告
|
|
618
|
+
let report = '';
|
|
619
|
+
report += '='.repeat(60) + '\n';
|
|
620
|
+
report += 'CLASS2CSS CONFIGURATION DIAGNOSTICS REPORT\n';
|
|
621
|
+
report += '='.repeat(60) + '\n';
|
|
622
|
+
report += `Generated: ${new Date(this.diagnosticResults.timestamp).toLocaleString()}\n`;
|
|
623
|
+
report += `Overall Score: ${this.diagnosticResults.overall.score}/${this.diagnosticResults.overall.maxScore} (${this.diagnosticResults.overall.percentage}%)\n`;
|
|
624
|
+
report += `Status: ${this.diagnosticResults.overall.status.toUpperCase()}\n\n`;
|
|
625
|
+
|
|
626
|
+
Object.values(this.diagnosticResults.sections).forEach((section) => {
|
|
627
|
+
report += `-`.repeat(40) + '\n';
|
|
628
|
+
report += `${section.title}: ${section.score}/${section.maxScore} [${section.status.toUpperCase()}]\n`;
|
|
629
|
+
report += `-`.repeat(40) + '\n';
|
|
630
|
+
|
|
631
|
+
section.checks.forEach((check) => {
|
|
632
|
+
const status = check.status.toUpperCase().padEnd(8);
|
|
633
|
+
report += `[${status}] ${check.name}: ${check.message}\n`;
|
|
634
|
+
if (check.suggestion) {
|
|
635
|
+
report += ` → ${check.suggestion}\n`;
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
report += '\n';
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
const suggestions = this.generateOptimizationSuggestions();
|
|
642
|
+
if (suggestions.high.length > 0 || suggestions.medium.length > 0) {
|
|
643
|
+
report += '='.repeat(60) + '\n';
|
|
644
|
+
report += 'OPTIMIZATION SUGGESTIONS\n';
|
|
645
|
+
report += '='.repeat(60) + '\n';
|
|
646
|
+
|
|
647
|
+
if (suggestions.high.length > 0) {
|
|
648
|
+
report += 'HIGH PRIORITY:\n';
|
|
649
|
+
suggestions.high.forEach((s) => {
|
|
650
|
+
report += `• ${s.suggestion} (${s.section}: ${s.check})\n`;
|
|
651
|
+
});
|
|
652
|
+
report += '\n';
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (suggestions.medium.length > 0) {
|
|
656
|
+
report += 'MEDIUM PRIORITY:\n';
|
|
657
|
+
suggestions.medium.forEach((s) => {
|
|
658
|
+
report += `• ${s.suggestion} (${s.section}: ${s.check})\n`;
|
|
659
|
+
});
|
|
660
|
+
report += '\n';
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return report;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// 获取最新诊断结果
|
|
668
|
+
getLatestResults() {
|
|
669
|
+
return this.diagnosticResults;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
module.exports = ConfigDiagnostics;
|