chaincss 1.13.3 → 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/CHANGELOG.md +81 -0
- package/LICENSE +2 -3
- package/README.md +238 -105
- package/dist/cli/commands/build.d.ts +3 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/compile.d.ts +3 -0
- package/dist/cli/commands/compile.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/timeline.d.ts +2 -0
- package/dist/cli/commands/timeline.d.ts.map +1 -0
- package/dist/cli/commands/watch.d.ts +6 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +5960 -0
- package/dist/cli/types.d.ts +51 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/utils/config-loader.d.ts +8 -0
- package/dist/cli/utils/config-loader.d.ts.map +1 -0
- package/dist/cli/utils/file-utils.d.ts +9 -0
- package/dist/cli/utils/file-utils.d.ts.map +1 -0
- package/dist/cli/utils/logger.d.ts +17 -0
- package/dist/cli/utils/logger.d.ts.map +1 -0
- package/dist/compiler/atomic-optimizer.d.ts +76 -0
- package/dist/compiler/atomic-optimizer.d.ts.map +1 -0
- package/dist/compiler/btt.d.ts +138 -0
- package/dist/compiler/btt.d.ts.map +1 -0
- package/dist/compiler/cache-manager.d.ts +20 -0
- package/dist/compiler/cache-manager.d.ts.map +1 -0
- package/dist/compiler/commonProps.d.ts +2 -0
- package/dist/compiler/commonProps.d.ts.map +1 -0
- package/dist/compiler/index.d.ts +12 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +5177 -0
- package/dist/compiler/prefixer.d.ts +42 -0
- package/dist/compiler/prefixer.d.ts.map +1 -0
- package/dist/compiler/theme-contract.d.ts +61 -0
- package/dist/compiler/theme-contract.d.ts.map +1 -0
- package/dist/compiler/tokens.d.ts +52 -0
- package/dist/compiler/tokens.d.ts.map +1 -0
- package/dist/compiler/types.d.ts +57 -0
- package/dist/compiler/types.d.ts.map +1 -0
- package/dist/core/compiler.d.ts +32 -0
- package/dist/core/compiler.d.ts.map +1 -0
- package/dist/core/constants.d.ts +129 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/types.d.ts +88 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/utils.d.ts +37 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5667 -0
- package/dist/plugins/vite.d.ts +11 -0
- package/dist/plugins/vite.d.ts.map +1 -0
- package/dist/plugins/vite.js +25839 -0
- package/dist/plugins/webpack.d.ts +45 -0
- package/dist/plugins/webpack.d.ts.map +1 -0
- package/dist/plugins/webpack.js +107 -0
- package/dist/runtime/hmr.d.ts +3 -0
- package/dist/runtime/hmr.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +15 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +552 -0
- package/dist/runtime/injector.d.ts +85 -0
- package/dist/runtime/injector.d.ts.map +1 -0
- package/dist/runtime/react.d.ts +54 -0
- package/dist/runtime/react.d.ts.map +1 -0
- package/dist/runtime/react.js +270 -0
- package/dist/runtime/types.d.ts +45 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/utils.d.ts +62 -0
- package/dist/runtime/utils.d.ts.map +1 -0
- package/dist/runtime/vue.d.ts +52 -0
- package/dist/runtime/vue.d.ts.map +1 -0
- package/dist/runtime/vue.js +232 -0
- package/package.json +90 -119
- package/browser/commonProps.js +0 -14
- package/browser/index.js +0 -3
- package/browser/react-hooks.js +0 -162
- package/browser/rtt.js +0 -400
- package/browser/vue-composables.js +0 -200
- package/node/atomic-optimizer.js +0 -526
- package/node/btt.js +0 -1009
- package/node/cache-manager.js +0 -56
- package/node/chaincss.js +0 -642
- package/node/index.js +0 -2
- package/node/loaders/chaincss-loader.js +0 -62
- package/node/plugins/next-plugin.js +0 -120
- package/node/plugins/vite-plugin.js +0 -383
- package/node/plugins/webpack-plugin.js +0 -41
- package/node/prefixer.js +0 -237
- package/node/strVal.js +0 -92
- package/node/theme-validator.js +0 -32
- package/shared/theme-contract.js +0 -98
- package/shared/tokens.cjs +0 -256
- package/shared/tokens.mjs +0 -320
- package/types.d.ts +0 -325
package/node/atomic-optimizer.js
DELETED
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
function hashKey(key) {
|
|
6
|
-
return crypto.createHash('sha1').update(key).digest('hex').slice(0, 6);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function kebab(s) {
|
|
10
|
-
return s.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
class AtomicOptimizer {
|
|
14
|
-
constructor(options = {}) {
|
|
15
|
-
// Ensure arrays are arrays (fix for config merging issues)
|
|
16
|
-
if (options.alwaysAtomic && !Array.isArray(options.alwaysAtomic)) {
|
|
17
|
-
options.alwaysAtomic = Object.values(options.alwaysAtomic);
|
|
18
|
-
}
|
|
19
|
-
if (options.neverAtomic && !Array.isArray(options.neverAtomic)) {
|
|
20
|
-
options.neverAtomic = Object.values(options.neverAtomic);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
this.options = {
|
|
24
|
-
enabled: true,
|
|
25
|
-
threshold: 3,
|
|
26
|
-
naming: 'hash',
|
|
27
|
-
cache: true,
|
|
28
|
-
cachePath: './.chaincss-cache',
|
|
29
|
-
minify: true,
|
|
30
|
-
mode: 'hybrid',
|
|
31
|
-
outputStrategy: 'component-first',
|
|
32
|
-
alwaysAtomic: [],
|
|
33
|
-
neverAtomic: [
|
|
34
|
-
'content', 'animation', 'transition', 'keyframes',
|
|
35
|
-
'counterIncrement', 'counterReset'
|
|
36
|
-
],
|
|
37
|
-
frameworkOutput: {
|
|
38
|
-
react: false,
|
|
39
|
-
vue: false,
|
|
40
|
-
vanilla: true
|
|
41
|
-
},
|
|
42
|
-
preserveSelectors: false,
|
|
43
|
-
verbose: false,
|
|
44
|
-
...options
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
this.usageCount = new Map();
|
|
48
|
-
this.atomicClasses = new Map();
|
|
49
|
-
this.componentClassMap = new Map();
|
|
50
|
-
this.stats = {
|
|
51
|
-
totalStyles: 0,
|
|
52
|
-
atomicStyles: 0,
|
|
53
|
-
standardStyles: 0,
|
|
54
|
-
uniqueProperties: 0,
|
|
55
|
-
savings: 0
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
if (this.options.cache) {
|
|
59
|
-
this.loadCache();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// Cache Management
|
|
65
|
-
// ============================================================================
|
|
66
|
-
|
|
67
|
-
loadCache() {
|
|
68
|
-
try {
|
|
69
|
-
if (!fs.existsSync(this.options.cachePath)) return;
|
|
70
|
-
|
|
71
|
-
const data = JSON.parse(fs.readFileSync(this.options.cachePath, 'utf8'));
|
|
72
|
-
|
|
73
|
-
if (data.version !== '1.0.0') {
|
|
74
|
-
//if (this.options.verbose) console.log('Cache version mismatch, creating new cache');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (data.config?.threshold !== this.options.threshold) {
|
|
79
|
-
//if (this.options.verbose) console.log(`Cache threshold (${data.config?.threshold}) differs from current (${this.options.threshold})`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this.atomicClasses = new Map(data.atomicClasses || []);
|
|
84
|
-
this.componentClassMap = new Map(data.componentClassMap || []);
|
|
85
|
-
this.stats = data.stats || this.stats;
|
|
86
|
-
|
|
87
|
-
const cacheTime = new Date(data.timestamp).toLocaleString();
|
|
88
|
-
|
|
89
|
-
} catch (err) {
|
|
90
|
-
console.log('Could not load cache:', err.message);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
saveCache() {
|
|
95
|
-
if (!this.options.cache) return;
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
const cacheDir = path.dirname(this.options.cachePath);
|
|
99
|
-
if (!fs.existsSync(cacheDir)) {
|
|
100
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const cache = {
|
|
104
|
-
version: '1.0.0',
|
|
105
|
-
timestamp: Date.now(),
|
|
106
|
-
atomicClasses: Array.from(this.atomicClasses.entries()),
|
|
107
|
-
componentClassMap: Array.from(this.componentClassMap.entries()),
|
|
108
|
-
stats: this.stats,
|
|
109
|
-
config: {
|
|
110
|
-
threshold: this.options.threshold,
|
|
111
|
-
naming: this.options.naming,
|
|
112
|
-
mode: this.options.mode,
|
|
113
|
-
outputStrategy: this.options.outputStrategy
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
fs.writeFileSync(this.options.cachePath, JSON.stringify(cache, null, 2), 'utf8');
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.log('Could not save cache:', err.message);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ============================================================================
|
|
124
|
-
// Style Tracking
|
|
125
|
-
// ============================================================================
|
|
126
|
-
|
|
127
|
-
trackStyles(styles) {
|
|
128
|
-
const styleArray = Array.isArray(styles) ? styles : Object.values(styles);
|
|
129
|
-
|
|
130
|
-
for (const style of styleArray) {
|
|
131
|
-
if (!style || !style.selectors) continue;
|
|
132
|
-
|
|
133
|
-
for (const [prop, value] of Object.entries(style)) {
|
|
134
|
-
if (prop === 'selectors' || prop === 'atRules') continue;
|
|
135
|
-
|
|
136
|
-
if (prop === 'hover' && typeof value === 'object') {
|
|
137
|
-
for (const [hoverProp, hoverValue] of Object.entries(value)) {
|
|
138
|
-
this.incrementUsage(hoverProp, hoverValue);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
this.incrementUsage(prop, value);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
this.stats.uniqueProperties = this.usageCount.size;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
incrementUsage(prop, value) {
|
|
150
|
-
const key = `${prop}:${value}`;
|
|
151
|
-
const count = (this.usageCount.get(key) || 0) + 1;
|
|
152
|
-
this.usageCount.set(key, count);
|
|
153
|
-
this.stats.totalStyles++;
|
|
154
|
-
|
|
155
|
-
if (this.atomicClasses.has(key)) {
|
|
156
|
-
this.atomicClasses.get(key).usageCount = count;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
shouldBeAtomic(prop, value) {
|
|
161
|
-
if (this.options.mode === 'standard') return false;
|
|
162
|
-
if (this.options.mode === 'atomic') return true;
|
|
163
|
-
|
|
164
|
-
if (this.options.neverAtomic.includes(prop)) return false;
|
|
165
|
-
if (this.options.alwaysAtomic.includes(prop)) return true;
|
|
166
|
-
|
|
167
|
-
const criticalProps = ['position', 'display', 'flex', 'grid', 'zIndex', 'top', 'left', 'right', 'bottom'];
|
|
168
|
-
const isCritical = criticalProps.includes(prop);
|
|
169
|
-
|
|
170
|
-
const key = `${prop}:${value}`;
|
|
171
|
-
const usage = this.usageCount.get(key) || 0;
|
|
172
|
-
|
|
173
|
-
if (isCritical && usage < this.options.threshold * 2) return false;
|
|
174
|
-
return usage >= this.options.threshold;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
generateClassName(prop, value) {
|
|
178
|
-
const key = `${prop}:${value}`;
|
|
179
|
-
|
|
180
|
-
if (this.options.naming === 'hash') {
|
|
181
|
-
return `c_${hashKey(key)}`;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const kebabProp = kebab(prop);
|
|
185
|
-
const safeValue = String(value).replace(/[^a-z0-9_-]/gi, '-').slice(0, 30);
|
|
186
|
-
return `${kebabProp}-${safeValue}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
getOrCreateAtomic(prop, value) {
|
|
190
|
-
const key = `${prop}:${value}`;
|
|
191
|
-
|
|
192
|
-
if (!this.atomicClasses.has(key)) {
|
|
193
|
-
const className = this.generateClassName(prop, value);
|
|
194
|
-
this.atomicClasses.set(key, {
|
|
195
|
-
className,
|
|
196
|
-
prop,
|
|
197
|
-
value,
|
|
198
|
-
usageCount: this.usageCount.get(key) || 0
|
|
199
|
-
});
|
|
200
|
-
this.stats.atomicStyles++;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return this.atomicClasses.get(key).className;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
getKeyFromClassName(className) {
|
|
207
|
-
for (const [key, atomic] of this.atomicClasses) {
|
|
208
|
-
if (atomic.className === className) return key;
|
|
209
|
-
}
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ============================================================================
|
|
214
|
-
// CSS Generation
|
|
215
|
-
// ============================================================================
|
|
216
|
-
|
|
217
|
-
generateAtomicCSS() {
|
|
218
|
-
let css = '';
|
|
219
|
-
const sortedClasses = Array.from(this.atomicClasses.values())
|
|
220
|
-
.sort((a, b) => b.usageCount - a.usageCount);
|
|
221
|
-
|
|
222
|
-
for (const atomic of sortedClasses) {
|
|
223
|
-
const kebabProp = kebab(atomic.prop);
|
|
224
|
-
if (this.options.minify) {
|
|
225
|
-
css += `.${atomic.className}{${kebabProp}:${atomic.value}}`;
|
|
226
|
-
} else {
|
|
227
|
-
css += `.${atomic.className} {\n ${kebabProp}: ${atomic.value};\n}\n`;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Debug: Log atomic CSS generation
|
|
232
|
-
if (this.options.verbose && css) {
|
|
233
|
-
//console.log(` Generated ${this.atomicClasses.size} atomic classes (${css.length} bytes)`);
|
|
234
|
-
if (css.length > 0) {
|
|
235
|
-
//console.log(`First atomic class: ${css.substring(0, 50)}...`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return css;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
generateComponentCSS(style, selectors) {
|
|
243
|
-
const atomicClasses = [];
|
|
244
|
-
const hoverAtomicClasses = [];
|
|
245
|
-
|
|
246
|
-
// Track which properties become atomic
|
|
247
|
-
for (const [prop, value] of Object.entries(style)) {
|
|
248
|
-
if (prop === 'selectors' || prop === 'atRules') continue;
|
|
249
|
-
|
|
250
|
-
if (prop === 'hover' && typeof value === 'object') {
|
|
251
|
-
for (const [hoverProp, hoverValue] of Object.entries(value)) {
|
|
252
|
-
if (this.shouldBeAtomic(hoverProp, hoverValue)) {
|
|
253
|
-
hoverAtomicClasses.push(this.getOrCreateAtomic(hoverProp, hoverValue));
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
} else if (this.shouldBeAtomic(prop, value)) {
|
|
257
|
-
atomicClasses.push(this.getOrCreateAtomic(prop, value));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
let componentCSS = '';
|
|
262
|
-
const selectorStr = selectors.join(', ');
|
|
263
|
-
|
|
264
|
-
// COMPONENT-FIRST STRATEGY (Default)
|
|
265
|
-
if (this.options.outputStrategy === 'component-first') {
|
|
266
|
-
// Include ALL properties in component CSS
|
|
267
|
-
const allStyles = {};
|
|
268
|
-
|
|
269
|
-
// Add all properties (both atomic and non-atomic)
|
|
270
|
-
for (const [prop, value] of Object.entries(style)) {
|
|
271
|
-
if (prop !== 'selectors' && prop !== 'atRules' && prop !== 'hover') {
|
|
272
|
-
allStyles[prop] = value;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Generate CSS with ALL properties
|
|
277
|
-
if (Object.keys(allStyles).length > 0) {
|
|
278
|
-
if (this.options.minify) {
|
|
279
|
-
componentCSS += `${selectorStr}{`;
|
|
280
|
-
for (const [prop, value] of Object.entries(allStyles)) {
|
|
281
|
-
const kebabProp = kebab(prop);
|
|
282
|
-
componentCSS += `${kebabProp}:${value};`;
|
|
283
|
-
}
|
|
284
|
-
componentCSS += `}`;
|
|
285
|
-
} else {
|
|
286
|
-
componentCSS += `${selectorStr} {\n`;
|
|
287
|
-
for (const [prop, value] of Object.entries(allStyles)) {
|
|
288
|
-
const kebabProp = kebab(prop);
|
|
289
|
-
componentCSS += ` ${kebabProp}: ${value};\n`;
|
|
290
|
-
}
|
|
291
|
-
componentCSS += `}\n`;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Add hover styles (all hover properties)
|
|
296
|
-
if (style.hover && typeof style.hover === 'object') {
|
|
297
|
-
const allHoverStyles = {};
|
|
298
|
-
for (const [prop, value] of Object.entries(style.hover)) {
|
|
299
|
-
allHoverStyles[prop] = value;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (Object.keys(allHoverStyles).length > 0) {
|
|
303
|
-
if (this.options.minify) {
|
|
304
|
-
componentCSS += `${selectorStr}:hover{`;
|
|
305
|
-
for (const [prop, value] of Object.entries(allHoverStyles)) {
|
|
306
|
-
const kebabProp = kebab(prop);
|
|
307
|
-
componentCSS += `${kebabProp}:${value};`;
|
|
308
|
-
}
|
|
309
|
-
componentCSS += `}`;
|
|
310
|
-
} else {
|
|
311
|
-
componentCSS += `${selectorStr}:hover {\n`;
|
|
312
|
-
for (const [prop, value] of Object.entries(allHoverStyles)) {
|
|
313
|
-
const kebabProp = kebab(prop);
|
|
314
|
-
componentCSS += ` ${kebabProp}: ${value};\n`;
|
|
315
|
-
}
|
|
316
|
-
componentCSS += `}\n`;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// UTILITY-FIRST STRATEGY (Advanced)
|
|
323
|
-
else {
|
|
324
|
-
const standardStyles = {};
|
|
325
|
-
const hoverStandardStyles = {};
|
|
326
|
-
|
|
327
|
-
for (const [prop, value] of Object.entries(style)) {
|
|
328
|
-
if (prop === 'selectors' || prop === 'atRules') continue;
|
|
329
|
-
|
|
330
|
-
if (prop === 'hover' && typeof value === 'object') {
|
|
331
|
-
for (const [hoverProp, hoverValue] of Object.entries(value)) {
|
|
332
|
-
if (!this.shouldBeAtomic(hoverProp, hoverValue)) {
|
|
333
|
-
hoverStandardStyles[hoverProp] = hoverValue;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
} else if (!this.shouldBeAtomic(prop, value)) {
|
|
337
|
-
standardStyles[prop] = value;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (Object.keys(standardStyles).length > 0) {
|
|
342
|
-
if (this.options.minify) {
|
|
343
|
-
componentCSS += `${selectorStr}{`;
|
|
344
|
-
for (const [prop, value] of Object.entries(standardStyles)) {
|
|
345
|
-
const kebabProp = kebab(prop);
|
|
346
|
-
componentCSS += `${kebabProp}:${value};`;
|
|
347
|
-
}
|
|
348
|
-
componentCSS += `}`;
|
|
349
|
-
} else {
|
|
350
|
-
componentCSS += `${selectorStr} {\n`;
|
|
351
|
-
for (const [prop, value] of Object.entries(standardStyles)) {
|
|
352
|
-
const kebabProp = kebab(prop);
|
|
353
|
-
componentCSS += ` ${kebabProp}: ${value};\n`;
|
|
354
|
-
}
|
|
355
|
-
componentCSS += `}\n`;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (Object.keys(hoverStandardStyles).length > 0) {
|
|
360
|
-
if (this.options.minify) {
|
|
361
|
-
componentCSS += `${selectorStr}:hover{`;
|
|
362
|
-
for (const [prop, value] of Object.entries(hoverStandardStyles)) {
|
|
363
|
-
const kebabProp = kebab(prop);
|
|
364
|
-
componentCSS += `${kebabProp}:${value};`;
|
|
365
|
-
}
|
|
366
|
-
componentCSS += `}`;
|
|
367
|
-
} else {
|
|
368
|
-
componentCSS += `${selectorStr}:hover {\n`;
|
|
369
|
-
for (const [prop, value] of Object.entries(hoverStandardStyles)) {
|
|
370
|
-
const kebabProp = kebab(prop);
|
|
371
|
-
componentCSS += ` ${kebabProp}: ${value};\n`;
|
|
372
|
-
}
|
|
373
|
-
componentCSS += `}\n`;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return {
|
|
379
|
-
css: componentCSS,
|
|
380
|
-
atomicClasses,
|
|
381
|
-
hoverAtomicClasses
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// ============================================================================
|
|
386
|
-
// Main Optimize Method
|
|
387
|
-
// ============================================================================
|
|
388
|
-
|
|
389
|
-
optimize(stylesInput) {
|
|
390
|
-
if (!this.options.enabled) {
|
|
391
|
-
return {
|
|
392
|
-
css: '',
|
|
393
|
-
map: {},
|
|
394
|
-
stats: this.getStats(),
|
|
395
|
-
atomicCSS: ''
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Reset stats
|
|
400
|
-
this.stats = {
|
|
401
|
-
totalStyles: 0,
|
|
402
|
-
atomicStyles: 0,
|
|
403
|
-
standardStyles: 0,
|
|
404
|
-
uniqueProperties: 0,
|
|
405
|
-
savings: 0
|
|
406
|
-
};
|
|
407
|
-
this.usageCount.clear();
|
|
408
|
-
this.componentClassMap.clear();
|
|
409
|
-
|
|
410
|
-
// Normalize input
|
|
411
|
-
let styleArray = [];
|
|
412
|
-
if (Array.isArray(stylesInput)) {
|
|
413
|
-
styleArray = stylesInput;
|
|
414
|
-
} else if (typeof stylesInput === 'object') {
|
|
415
|
-
styleArray = Object.values(stylesInput).filter(v => v && typeof v === 'object');
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (styleArray.length === 0) {
|
|
419
|
-
return { css: '', map: {}, stats: this.getStats(), atomicCSS: '', componentCSS: '' };
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// FIRST: Track usage counts
|
|
423
|
-
this.trackStyles(styleArray);
|
|
424
|
-
|
|
425
|
-
// SECOND: Generate component CSS (this populates atomicClasses via getOrCreateAtomic)
|
|
426
|
-
let componentCSS = '';
|
|
427
|
-
const classMap = {};
|
|
428
|
-
|
|
429
|
-
for (const style of styleArray) {
|
|
430
|
-
if (!style || !style.selectors) continue;
|
|
431
|
-
|
|
432
|
-
const selectors = style.selectors;
|
|
433
|
-
const selectorKey = selectors.join(', ');
|
|
434
|
-
const { css, atomicClasses, hoverAtomicClasses } = this.generateComponentCSS(style, selectors);
|
|
435
|
-
componentCSS += css;
|
|
436
|
-
|
|
437
|
-
if (this.options.outputStrategy === 'utility-first') {
|
|
438
|
-
if (atomicClasses.length > 0) {
|
|
439
|
-
classMap[selectorKey] = atomicClasses.join(' ');
|
|
440
|
-
}
|
|
441
|
-
if (hoverAtomicClasses.length > 0) {
|
|
442
|
-
classMap[`${selectorKey}:hover`] = hoverAtomicClasses.join(' ');
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
this.componentClassMap.set(selectorKey, {
|
|
447
|
-
atomicClasses,
|
|
448
|
-
hoverAtomicClasses,
|
|
449
|
-
selectors
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// THIRD: Generate atomic CSS (now atomicClasses is populated!)
|
|
454
|
-
const atomicCSS = this.generateAtomicCSS();
|
|
455
|
-
|
|
456
|
-
// Combine CSS
|
|
457
|
-
const finalCSS = atomicCSS + componentCSS;
|
|
458
|
-
|
|
459
|
-
// Log stats if verbose
|
|
460
|
-
if (this.options.verbose) {
|
|
461
|
-
const stats = this.getStats();
|
|
462
|
-
//console.log(` Atomic Optimization Stats:`);
|
|
463
|
-
//console.log(` Output strategy: ${this.options.outputStrategy}`);
|
|
464
|
-
//console.log(` Total styles tracked: ${stats.totalStyles}`);
|
|
465
|
-
//console.log(` Atomic classes created: ${this.atomicClasses.size}`);
|
|
466
|
-
//console.log(` Atomic CSS length: ${atomicCSS.length} bytes`);
|
|
467
|
-
//console.log(` Component CSS length: ${componentCSS.length} bytes`);
|
|
468
|
-
//console.log(` Total CSS length: ${finalCSS.length} bytes`);
|
|
469
|
-
//console.log(` Savings: ${stats.savings}`);
|
|
470
|
-
|
|
471
|
-
if (atomicCSS.length === 0 && this.atomicClasses.size > 0) {
|
|
472
|
-
//console.log(` WARNING: ${this.atomicClasses.size} atomic classes exist but generated CSS is empty!`);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// Save cache
|
|
477
|
-
if (this.options.cache) {
|
|
478
|
-
this.saveCache();
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return {
|
|
482
|
-
css: finalCSS,
|
|
483
|
-
map: classMap,
|
|
484
|
-
stats: this.getStats(),
|
|
485
|
-
atomicCSS: atomicCSS,
|
|
486
|
-
componentCSS: componentCSS,
|
|
487
|
-
componentMap: this.componentClassMap
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
getStats() {
|
|
492
|
-
const savings = this.stats.totalStyles > 0
|
|
493
|
-
? ((this.stats.totalStyles - this.stats.atomicStyles) / this.stats.totalStyles * 100).toFixed(1)
|
|
494
|
-
: 0;
|
|
495
|
-
|
|
496
|
-
return {
|
|
497
|
-
totalStyles: this.stats.totalStyles,
|
|
498
|
-
atomicStyles: this.stats.atomicStyles,
|
|
499
|
-
standardStyles: this.stats.standardStyles,
|
|
500
|
-
uniqueProperties: this.stats.uniqueProperties,
|
|
501
|
-
savings: `${savings}%`
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
getAtomicClass(prop, value) {
|
|
506
|
-
const key = `${prop}:${value}`;
|
|
507
|
-
const atomic = this.atomicClasses.get(key);
|
|
508
|
-
return atomic ? atomic.className : null;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
getAllAtomicClasses() {
|
|
512
|
-
return Array.from(this.atomicClasses.values());
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
clearCache() {
|
|
516
|
-
this.atomicClasses.clear();
|
|
517
|
-
this.componentClassMap.clear();
|
|
518
|
-
this.usageCount.clear();
|
|
519
|
-
if (this.options.cache && fs.existsSync(this.options.cachePath)) {
|
|
520
|
-
fs.unlinkSync(this.options.cachePath);
|
|
521
|
-
}
|
|
522
|
-
//console.log('Atomic optimizer cache cleared');
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
module.exports = { AtomicOptimizer };
|