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,674 @@
|
|
|
1
|
+
const UnitProcessor = require('../utils/UnitProcessor');
|
|
2
|
+
const CssFormatter = require('../utils/CssFormatter');
|
|
3
|
+
|
|
4
|
+
class DynamicClassGenerator {
|
|
5
|
+
constructor(eventBus, configManager, importantParser) {
|
|
6
|
+
this.eventBus = eventBus;
|
|
7
|
+
this.configManager = configManager;
|
|
8
|
+
this.importantParser = importantParser;
|
|
9
|
+
|
|
10
|
+
// 初始化单位处理器
|
|
11
|
+
this.unitProcessor = new UnitProcessor(this.configManager.getConfig());
|
|
12
|
+
|
|
13
|
+
// 初始化CSS格式化器
|
|
14
|
+
const cssFormat = this.configManager.getCssFormat();
|
|
15
|
+
this.cssFormatter = new CssFormatter(cssFormat);
|
|
16
|
+
|
|
17
|
+
// CSS生成缓存
|
|
18
|
+
this.cssCache = new Map();
|
|
19
|
+
this.cacheEnabled = true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 生成动态CSS类列表
|
|
23
|
+
getClassList(classArr) {
|
|
24
|
+
if (!Array.isArray(classArr)) {
|
|
25
|
+
this.eventBus.emit('generator:error', { error: 'classArr must be an array' });
|
|
26
|
+
return { cssStr: '', userBaseClassArr: [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const unitConversion = this.configManager.getUnitConversion();
|
|
30
|
+
const cssNameMap = this.configManager.getCssNameMap();
|
|
31
|
+
let cssStr = '';
|
|
32
|
+
const userBaseClassArr = [];
|
|
33
|
+
|
|
34
|
+
this.eventBus.emit('generator:dynamic:started', { classCount: classArr.length });
|
|
35
|
+
|
|
36
|
+
classArr.forEach((className, index) => {
|
|
37
|
+
try {
|
|
38
|
+
const isImportant = this.importantParser.hasImportantFlag(className);
|
|
39
|
+
const cleanClassName = this.importantParser.cleanImportantFlag(className);
|
|
40
|
+
const name = this.parseClassNameIntelligent(cleanClassName);
|
|
41
|
+
|
|
42
|
+
if (!name) {
|
|
43
|
+
this.eventBus.emit('generator:dynamic:skipped', {
|
|
44
|
+
className,
|
|
45
|
+
reason: 'invalid_format',
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// name 现在可能是 [prefix, value] 或 [prefix, value, variant]
|
|
51
|
+
const variant = name.length >= 3 ? name[2] : null;
|
|
52
|
+
|
|
53
|
+
if (cssNameMap.has(name[0])) {
|
|
54
|
+
const classCss = this.getClassListStr([name[0], name[1]], className, isImportant, variant);
|
|
55
|
+
cssStr += classCss;
|
|
56
|
+
this.eventBus.emit('generator:dynamic:generated', {
|
|
57
|
+
className,
|
|
58
|
+
cleanName: cleanClassName,
|
|
59
|
+
isImportant,
|
|
60
|
+
variant,
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
userBaseClassArr.push([name[0], name[1], className, isImportant, variant]);
|
|
64
|
+
this.eventBus.emit('generator:dynamic:userBase', {
|
|
65
|
+
className,
|
|
66
|
+
cleanName: cleanClassName,
|
|
67
|
+
isImportant,
|
|
68
|
+
variant,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.eventBus.emit('generator:dynamic:error', {
|
|
73
|
+
className,
|
|
74
|
+
error: error.message,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.eventBus.emit('generator:dynamic:completed', {
|
|
80
|
+
generatedCount: classArr.length - userBaseClassArr.length,
|
|
81
|
+
userBaseCount: userBaseClassArr.length,
|
|
82
|
+
cssLength: cssStr.length,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return { cssStr, userBaseClassArr };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 解析响应式变体前缀(如 sm:, md: 等)
|
|
89
|
+
parseResponsiveVariant(className) {
|
|
90
|
+
if (!className || typeof className !== 'string') {
|
|
91
|
+
return { variant: null, baseClass: className };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const variants = this.configManager.getVariants();
|
|
95
|
+
const responsiveVariants = variants.responsive || [];
|
|
96
|
+
|
|
97
|
+
// 按 : 拆分,检查第一部分是否是响应式变体
|
|
98
|
+
const parts = className.split(':');
|
|
99
|
+
if (parts.length >= 2) {
|
|
100
|
+
const potentialVariant = parts[0];
|
|
101
|
+
if (responsiveVariants.includes(potentialVariant)) {
|
|
102
|
+
const baseClass = parts.slice(1).join(':'); // 支持嵌套的 :(虽然当前不支持,但保留兼容性)
|
|
103
|
+
return { variant: potentialVariant, baseClass };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { variant: null, baseClass: className };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 智能前缀解析方法 - 支持复合前缀如 max-w, min-h 等,同时支持响应式前缀
|
|
111
|
+
parseClassNameIntelligent(className) {
|
|
112
|
+
// 先解析响应式变体
|
|
113
|
+
const { variant, baseClass } = this.parseResponsiveVariant(className);
|
|
114
|
+
|
|
115
|
+
// 使用 base class 进行解析
|
|
116
|
+
const cssNameMap = this.configManager.getCssNameMap();
|
|
117
|
+
|
|
118
|
+
// 获取所有已知前缀,按长度降序排列
|
|
119
|
+
const prefixes = Array.from(cssNameMap.keys()).sort((a, b) => b.length - a.length);
|
|
120
|
+
|
|
121
|
+
// 尝试匹配每个前缀
|
|
122
|
+
for (const prefix of prefixes) {
|
|
123
|
+
if (baseClass.startsWith(prefix + '-')) {
|
|
124
|
+
const value = baseClass.substring(prefix.length + 1);
|
|
125
|
+
// 确保值部分不为空且不包含连字符
|
|
126
|
+
if (value && !value.includes('-')) {
|
|
127
|
+
this.eventBus.emit('generator:dynamic:prefix_matched', {
|
|
128
|
+
className,
|
|
129
|
+
prefix,
|
|
130
|
+
value,
|
|
131
|
+
variant,
|
|
132
|
+
method: 'intelligent_parsing',
|
|
133
|
+
});
|
|
134
|
+
return [prefix, value, variant]; // 返回包含 variant 的三元组
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 降级到原有逻辑
|
|
140
|
+
const parts = baseClass.split('-');
|
|
141
|
+
if (parts.length === 2) {
|
|
142
|
+
this.eventBus.emit('generator:dynamic:prefix_matched', {
|
|
143
|
+
className,
|
|
144
|
+
prefix: parts[0],
|
|
145
|
+
value: parts[1],
|
|
146
|
+
variant,
|
|
147
|
+
method: 'fallback_split',
|
|
148
|
+
});
|
|
149
|
+
return [parts[0], parts[1], variant]; // 返回包含 variant 的三元组
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.eventBus.emit('generator:dynamic:parse_failed', {
|
|
153
|
+
className,
|
|
154
|
+
reason: 'invalid_format',
|
|
155
|
+
});
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 生成单个类的CSS字符串(优化版)
|
|
160
|
+
getClassListStr(name, originalClassName, isImportant = false, variant = null) {
|
|
161
|
+
// 检查缓存(包含 variant 信息)
|
|
162
|
+
const cacheKey = `${name[0]}-${name[1]}-${isImportant}-${variant || ''}`;
|
|
163
|
+
if (this.cacheEnabled && this.cssCache.has(cacheKey)) {
|
|
164
|
+
const cached = this.cssCache.get(cacheKey);
|
|
165
|
+
// 替换选择器时需要考虑转义
|
|
166
|
+
const escapedSelector = this.cssFormatter.escapeSelector(originalClassName);
|
|
167
|
+
// 修复:将 - 放在字符类末尾,避免被解释为范围操作符
|
|
168
|
+
return cached.replace(/\.[\w\\:-]+\s*{/, `.${escapedSelector} {`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const classNameDefinition = this.configManager.getCssNameMap().get(name[0]);
|
|
172
|
+
|
|
173
|
+
if (!classNameDefinition) {
|
|
174
|
+
this.eventBus.emit('generator:error', {
|
|
175
|
+
error: `CSS name definition not found for: ${name[0]}`,
|
|
176
|
+
});
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let cssResult = '';
|
|
181
|
+
|
|
182
|
+
// 处理对象类型的CSS定义
|
|
183
|
+
if (this.isObject(classNameDefinition)) {
|
|
184
|
+
cssResult = this.generateObjectBasedCSS(
|
|
185
|
+
name,
|
|
186
|
+
originalClassName,
|
|
187
|
+
classNameDefinition,
|
|
188
|
+
isImportant,
|
|
189
|
+
variant
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
// 处理字符串类型的CSS定义
|
|
193
|
+
cssResult = this.generateStringBasedCSS(
|
|
194
|
+
name,
|
|
195
|
+
originalClassName,
|
|
196
|
+
classNameDefinition,
|
|
197
|
+
isImportant,
|
|
198
|
+
variant
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 缓存结果
|
|
203
|
+
if (this.cacheEnabled && cssResult) {
|
|
204
|
+
this.cssCache.set(cacheKey, cssResult);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return cssResult;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 生成基于对象定义的CSS
|
|
211
|
+
generateObjectBasedCSS(name, originalClassName, classNameDefinition, isImportant, variant = null) {
|
|
212
|
+
if (!classNameDefinition.classArr) {
|
|
213
|
+
this.eventBus.emit('generator:warning', {
|
|
214
|
+
warning: `classArr not found in definition for: ${name[0]}`,
|
|
215
|
+
});
|
|
216
|
+
return '';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const rawValue = name[1];
|
|
220
|
+
const processedValues = [];
|
|
221
|
+
|
|
222
|
+
// 为每个CSS属性处理值
|
|
223
|
+
classNameDefinition.classArr.forEach((cssProperty) => {
|
|
224
|
+
// 使用单位处理器智能处理值
|
|
225
|
+
let processedValue;
|
|
226
|
+
|
|
227
|
+
if (this.unitProcessor && !classNameDefinition.skipConversion) {
|
|
228
|
+
processedValue = this.unitProcessor.parseValue(
|
|
229
|
+
rawValue,
|
|
230
|
+
cssProperty,
|
|
231
|
+
classNameDefinition.unit
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
// 使用传统逻辑(支持skipConversion)
|
|
235
|
+
processedValue = this.legacyProcessValue(rawValue, classNameDefinition);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
processedValues.push({
|
|
239
|
+
property: cssProperty,
|
|
240
|
+
value: processedValue,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// 生成CSS字符串
|
|
245
|
+
const processedValuesArray = processedValues.map(({ property, value }) => ({
|
|
246
|
+
property,
|
|
247
|
+
value: isImportant ? `${value} !important` : value,
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
// 使用格式化器格式化CSS
|
|
251
|
+
const cssRule = this.cssFormatter.formatRule(originalClassName, processedValuesArray);
|
|
252
|
+
|
|
253
|
+
// 如果有响应式变体,用 @media 包裹
|
|
254
|
+
if (variant) {
|
|
255
|
+
return this.wrapWithMediaQuery(cssRule, variant);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return cssRule;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 生成基于字符串定义的CSS
|
|
262
|
+
generateStringBasedCSS(name, originalClassName, classNameDefinition, isImportant, variant = null) {
|
|
263
|
+
const rawValue = name[1];
|
|
264
|
+
let processedValue;
|
|
265
|
+
|
|
266
|
+
if (this.unitProcessor) {
|
|
267
|
+
// 使用单位处理器处理值
|
|
268
|
+
processedValue = this.unitProcessor.parseValue(rawValue, classNameDefinition);
|
|
269
|
+
} else {
|
|
270
|
+
// 回退到原有逻辑
|
|
271
|
+
processedValue = this.legacyProcessValue(rawValue);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const finalValue = isImportant ? `${processedValue} !important` : processedValue;
|
|
275
|
+
|
|
276
|
+
// 使用格式化器格式化CSS
|
|
277
|
+
const cssRule = this.cssFormatter.formatRule(originalClassName, `${classNameDefinition}: ${finalValue}`);
|
|
278
|
+
|
|
279
|
+
// 如果有响应式变体,用 @media 包裹
|
|
280
|
+
if (variant) {
|
|
281
|
+
return this.wrapWithMediaQuery(cssRule, variant);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return cssRule;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 用 @media 查询包裹 CSS 规则
|
|
288
|
+
wrapWithMediaQuery(cssRule, variant) {
|
|
289
|
+
const breakpoints = this.configManager.getBreakpoints();
|
|
290
|
+
const breakpoint = breakpoints[variant];
|
|
291
|
+
|
|
292
|
+
if (!breakpoint) {
|
|
293
|
+
this.eventBus.emit('generator:warning', {
|
|
294
|
+
warning: `Breakpoint not found for variant: ${variant}`,
|
|
295
|
+
});
|
|
296
|
+
return cssRule; // 如果没有找到断点,返回原始规则
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const cssFormat = this.configManager.getCssFormat();
|
|
300
|
+
const isCompressed = cssFormat === 'compressed';
|
|
301
|
+
const isSingleLine = cssFormat === 'singleLine';
|
|
302
|
+
|
|
303
|
+
if (isCompressed) {
|
|
304
|
+
// 压缩格式:@media(min-width:640px){.sm\:w-100{width:200rpx}}
|
|
305
|
+
return `@media(min-width:${breakpoint}){${cssRule.trim()}}`;
|
|
306
|
+
} else if (isSingleLine) {
|
|
307
|
+
// 单行格式:@media (min-width: 640px) { .sm\:w-100 { width: 200rpx; } }
|
|
308
|
+
return `@media (min-width: ${breakpoint}) { ${cssRule.trim()} }\n`;
|
|
309
|
+
} else {
|
|
310
|
+
// 多行格式
|
|
311
|
+
return `@media (min-width: ${breakpoint}) {\n${cssRule.trim()}\n}\n`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 传统的值处理逻辑(向后兼容)
|
|
316
|
+
legacyProcessValue(rawValue, classNameDefinition = {}) {
|
|
317
|
+
const unitConversion = this.configManager.getUnitConversion();
|
|
318
|
+
const baseUnit = this.configManager.getBaseUnit();
|
|
319
|
+
|
|
320
|
+
let unit = baseUnit;
|
|
321
|
+
let size = rawValue;
|
|
322
|
+
const sizeArr = size.split('');
|
|
323
|
+
|
|
324
|
+
// 单位处理
|
|
325
|
+
if (classNameDefinition.unit === '-') {
|
|
326
|
+
unit = '';
|
|
327
|
+
} else if (sizeArr[sizeArr.length - 1] === 'b') {
|
|
328
|
+
size = sizeArr.slice(0, sizeArr.length - 1).join('') + '%';
|
|
329
|
+
unit = '';
|
|
330
|
+
} else if (classNameDefinition.skipConversion === true) {
|
|
331
|
+
// 跳过单位转换,直接使用原始值和指定单位
|
|
332
|
+
unit = classNameDefinition.unit || '';
|
|
333
|
+
// size 保持原值,不进行 unitConversion 转换
|
|
334
|
+
} else if (classNameDefinition.unit !== undefined) {
|
|
335
|
+
unit = classNameDefinition.unit;
|
|
336
|
+
size = unitConversion * size;
|
|
337
|
+
} else {
|
|
338
|
+
size = unitConversion * size;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 处理小数点
|
|
342
|
+
if (rawValue[0] === '0' && rawValue.length > 1) {
|
|
343
|
+
size = String(size).replace('0', '0.');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 当值为0时,省略单位
|
|
347
|
+
return size === 0 || size === '0' ? '0' : `${size}${unit}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 生成用户基础类CSS
|
|
351
|
+
createUserBaseClassList(arr) {
|
|
352
|
+
if (!Array.isArray(arr)) {
|
|
353
|
+
this.eventBus.emit('generator:error', { error: 'userBaseClassArr must be an array' });
|
|
354
|
+
return '';
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let str = '';
|
|
358
|
+
const userBaseClass = this.configManager.getUserBaseClass();
|
|
359
|
+
const cssWrite = new Set(); // 临时防重复集合
|
|
360
|
+
|
|
361
|
+
this.eventBus.emit('generator:userBase:started', { classCount: arr.length });
|
|
362
|
+
|
|
363
|
+
arr.forEach((item, index) => {
|
|
364
|
+
try {
|
|
365
|
+
// item 现在可能是 [className, value, originalClassName, isImportant] 或 [className, value, originalClassName, isImportant, variant]
|
|
366
|
+
const [className, value, originalClassName, isImportant, variant] = item;
|
|
367
|
+
const classKey = originalClassName;
|
|
368
|
+
|
|
369
|
+
if (cssWrite.has(classKey)) {
|
|
370
|
+
this.eventBus.emit('generator:userBase:skipped', {
|
|
371
|
+
className: originalClassName,
|
|
372
|
+
reason: 'duplicate',
|
|
373
|
+
});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const baseClassItem = userBaseClass.find(([k, v]) => k === className);
|
|
378
|
+
|
|
379
|
+
if (baseClassItem === undefined) {
|
|
380
|
+
this.eventBus.emit('generator:userBase:skipped', {
|
|
381
|
+
className: originalClassName,
|
|
382
|
+
reason: 'not_found_in_config',
|
|
383
|
+
});
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
cssWrite.add(classKey);
|
|
388
|
+
const cssClassName = className.replaceAll('_', '-');
|
|
389
|
+
|
|
390
|
+
let cssRule = '';
|
|
391
|
+
if (this.isArray(baseClassItem[1])) {
|
|
392
|
+
const cssValue = isImportant ? `${value} !important` : value;
|
|
393
|
+
// 使用格式化器格式化CSS
|
|
394
|
+
cssRule = this.cssFormatter.formatRule(originalClassName, `${cssClassName}: ${cssValue}`);
|
|
395
|
+
} else if (this.isObject(baseClassItem[1]) && baseClassItem[1][value] !== undefined) {
|
|
396
|
+
const cssValue = isImportant
|
|
397
|
+
? `${baseClassItem[1][value]} !important`
|
|
398
|
+
: baseClassItem[1][value];
|
|
399
|
+
const propertyName = baseClassItem[1]['ABBR'] || cssClassName;
|
|
400
|
+
// 使用格式化器格式化CSS
|
|
401
|
+
cssRule = this.cssFormatter.formatRule(originalClassName, `${propertyName}: ${cssValue}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 如果有响应式变体,用 @media 包裹
|
|
405
|
+
if (variant && cssRule) {
|
|
406
|
+
cssRule = this.wrapWithMediaQuery(cssRule, variant);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
str += cssRule;
|
|
410
|
+
|
|
411
|
+
this.eventBus.emit('generator:userBase:generated', {
|
|
412
|
+
className: originalClassName,
|
|
413
|
+
isImportant,
|
|
414
|
+
variant,
|
|
415
|
+
});
|
|
416
|
+
} catch (error) {
|
|
417
|
+
this.eventBus.emit('generator:userBase:error', {
|
|
418
|
+
item,
|
|
419
|
+
error: error.message,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
this.eventBus.emit('generator:userBase:completed', {
|
|
425
|
+
generatedCount: cssWrite.size,
|
|
426
|
+
cssLength: str.length,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return str;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 工具方法
|
|
433
|
+
isObject(obj) {
|
|
434
|
+
return Object.prototype.toString.call(obj) === '[object Object]';
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
isArray(obj) {
|
|
438
|
+
return Object.prototype.toString.call(obj) === '[object Array]';
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// 验证CSS生成结果
|
|
442
|
+
validateGeneratedCSS(cssStr) {
|
|
443
|
+
const errors = [];
|
|
444
|
+
const warnings = [];
|
|
445
|
+
|
|
446
|
+
if (!cssStr || typeof cssStr !== 'string') {
|
|
447
|
+
errors.push('Generated CSS is null or not a string');
|
|
448
|
+
return { errors, warnings, isValid: false };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// 检查基本的CSS语法
|
|
452
|
+
const cssRules = cssStr.split('}').filter((rule) => rule.trim().length > 0);
|
|
453
|
+
|
|
454
|
+
cssRules.forEach((rule, index) => {
|
|
455
|
+
if (!rule.includes('{')) {
|
|
456
|
+
errors.push(`Rule ${index + 1} missing opening brace`);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!rule.includes(':')) {
|
|
460
|
+
warnings.push(`Rule ${index + 1} might be missing property-value pairs`);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// 检查重复的选择器
|
|
465
|
+
const selectors = cssStr.match(/\.([\w\-]+)\s*{/g);
|
|
466
|
+
if (selectors) {
|
|
467
|
+
const selectorNames = selectors.map((s) => s.replace(/[.{]/g, '').trim());
|
|
468
|
+
const duplicates = selectorNames.filter(
|
|
469
|
+
(name, index) => selectorNames.indexOf(name) !== index
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
if (duplicates.length > 0) {
|
|
473
|
+
warnings.push(`Duplicate selectors found: ${duplicates.join(', ')}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return { errors, warnings, isValid: errors.length === 0 };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// 获取生成统计
|
|
481
|
+
|
|
482
|
+
// 调试生成过程
|
|
483
|
+
debugGeneration(classArr) {
|
|
484
|
+
const result = this.getClassList(classArr);
|
|
485
|
+
const validation = this.validateGeneratedCSS(result.cssStr);
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
inputClasses: classArr,
|
|
489
|
+
result,
|
|
490
|
+
validation,
|
|
491
|
+
stats: this.getGenerationStats(),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 缓存管理
|
|
496
|
+
clearCache() {
|
|
497
|
+
this.cssCache.clear();
|
|
498
|
+
this.eventBus.emit('generator:cache:cleared', {
|
|
499
|
+
timestamp: Date.now(),
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
setCacheEnabled(enabled) {
|
|
504
|
+
this.cacheEnabled = enabled;
|
|
505
|
+
if (!enabled) {
|
|
506
|
+
this.clearCache();
|
|
507
|
+
}
|
|
508
|
+
this.eventBus.emit('generator:cache:toggle', {
|
|
509
|
+
enabled,
|
|
510
|
+
timestamp: Date.now(),
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
getCacheStats() {
|
|
515
|
+
return {
|
|
516
|
+
size: this.cssCache.size,
|
|
517
|
+
enabled: this.cacheEnabled,
|
|
518
|
+
memoryUsage: this.estimateCacheMemoryUsage(),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
estimateCacheMemoryUsage() {
|
|
523
|
+
let totalSize = 0;
|
|
524
|
+
for (const [key, value] of this.cssCache) {
|
|
525
|
+
totalSize += key.length + value.length;
|
|
526
|
+
}
|
|
527
|
+
return totalSize * 2; // 估算字符串内存使用(大概)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// 配置更新
|
|
531
|
+
updateConfig(newConfigManager) {
|
|
532
|
+
if (newConfigManager && newConfigManager !== this.configManager) {
|
|
533
|
+
this.configManager = newConfigManager;
|
|
534
|
+
|
|
535
|
+
// 重新初始化单位处理器
|
|
536
|
+
this.unitProcessor = new UnitProcessor(this.configManager.getConfig());
|
|
537
|
+
|
|
538
|
+
// 更新CSS格式化器格式
|
|
539
|
+
const cssFormat = this.configManager.getCssFormat();
|
|
540
|
+
this.cssFormatter.setFormat(cssFormat);
|
|
541
|
+
|
|
542
|
+
// 清空缓存,因为配置可能已更改
|
|
543
|
+
this.clearCache();
|
|
544
|
+
|
|
545
|
+
this.eventBus.emit('generator:config:updated', {
|
|
546
|
+
timestamp: Date.now(),
|
|
547
|
+
hasUnitProcessor: !!this.unitProcessor,
|
|
548
|
+
cssFormat: cssFormat,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// 性能分析
|
|
554
|
+
analyzePerformance(classArr) {
|
|
555
|
+
const startTime = process.hrtime.bigint();
|
|
556
|
+
const initialCacheSize = this.cssCache.size;
|
|
557
|
+
|
|
558
|
+
const result = this.getClassList(classArr);
|
|
559
|
+
|
|
560
|
+
const endTime = process.hrtime.bigint();
|
|
561
|
+
const duration = Number(endTime - startTime) / 1000000; // 转换为毫秒
|
|
562
|
+
const finalCacheSize = this.cssCache.size;
|
|
563
|
+
|
|
564
|
+
return {
|
|
565
|
+
result,
|
|
566
|
+
performance: {
|
|
567
|
+
duration,
|
|
568
|
+
classesPerMs: classArr.length / duration,
|
|
569
|
+
cacheHits: finalCacheSize - initialCacheSize,
|
|
570
|
+
cacheHitRate: initialCacheSize > 0 ? (initialCacheSize / classArr.length) * 100 : 0,
|
|
571
|
+
generatedCssLength: result.cssStr.length,
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// 批量生成CSS
|
|
577
|
+
batchGenerateCSS(classBatches) {
|
|
578
|
+
const results = [];
|
|
579
|
+
let totalDuration = 0;
|
|
580
|
+
|
|
581
|
+
this.eventBus.emit('generator:batch:started', {
|
|
582
|
+
batchCount: classBatches.length,
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
classBatches.forEach((batch, index) => {
|
|
586
|
+
const startTime = process.hrtime.bigint();
|
|
587
|
+
const batchResult = this.getClassList(batch);
|
|
588
|
+
const endTime = process.hrtime.bigint();
|
|
589
|
+
|
|
590
|
+
const duration = Number(endTime - startTime) / 1000000;
|
|
591
|
+
totalDuration += duration;
|
|
592
|
+
|
|
593
|
+
results.push({
|
|
594
|
+
batchIndex: index,
|
|
595
|
+
classCount: batch.length,
|
|
596
|
+
cssLength: batchResult.cssStr.length,
|
|
597
|
+
duration,
|
|
598
|
+
result: batchResult,
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
this.eventBus.emit('generator:batch:completed', {
|
|
603
|
+
batchCount: classBatches.length,
|
|
604
|
+
totalDuration,
|
|
605
|
+
averageDuration: totalDuration / classBatches.length,
|
|
606
|
+
results,
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
return results;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// 优化建议
|
|
613
|
+
getOptimizationSuggestions() {
|
|
614
|
+
const suggestions = [];
|
|
615
|
+
const cacheStats = this.getCacheStats();
|
|
616
|
+
|
|
617
|
+
// 缓存相关建议
|
|
618
|
+
if (!this.cacheEnabled) {
|
|
619
|
+
suggestions.push({
|
|
620
|
+
type: 'cache',
|
|
621
|
+
priority: 'high',
|
|
622
|
+
message: 'Enable CSS generation cache for better performance',
|
|
623
|
+
action: 'setCacheEnabled(true)',
|
|
624
|
+
});
|
|
625
|
+
} else if (cacheStats.size > 1000) {
|
|
626
|
+
suggestions.push({
|
|
627
|
+
type: 'cache',
|
|
628
|
+
priority: 'medium',
|
|
629
|
+
message: 'Cache size is large, consider clearing periodically',
|
|
630
|
+
action: 'clearCache()',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// 单位处理器建议
|
|
635
|
+
if (!this.unitProcessor) {
|
|
636
|
+
suggestions.push({
|
|
637
|
+
type: 'unit_processor',
|
|
638
|
+
priority: 'medium',
|
|
639
|
+
message: 'Unit processor not available, using legacy processing',
|
|
640
|
+
action: 'Check configuration manager setup',
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return suggestions;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// 增强的统计信息
|
|
648
|
+
getGenerationStats() {
|
|
649
|
+
const baseStats = {
|
|
650
|
+
configManagerReady: !!this.configManager,
|
|
651
|
+
importantParserReady: !!this.importantParser,
|
|
652
|
+
cssNameMapSize: this.configManager ? this.configManager.getCssNameMap().size : 0,
|
|
653
|
+
userBaseClassCount: this.configManager ? this.configManager.getUserBaseClass().length : 0,
|
|
654
|
+
unitProcessorReady: !!this.unitProcessor,
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// 添加缓存统计
|
|
658
|
+
baseStats.cache = this.getCacheStats();
|
|
659
|
+
|
|
660
|
+
// 添加单位处理器统计
|
|
661
|
+
if (this.unitProcessor) {
|
|
662
|
+
const unitConfig = this.unitProcessor.getConfig();
|
|
663
|
+
baseStats.unitProcessor = {
|
|
664
|
+
baseUnit: unitConfig.baseUnit,
|
|
665
|
+
unitConversion: unitConfig.unitConversion,
|
|
666
|
+
supportedProperties: Object.keys(unitConfig.propertyUnits).length,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return baseStats;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
module.exports = DynamicClassGenerator;
|