@melcanz85/chaincss 1.11.0 → 1.11.2

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.
@@ -1,6 +1,5 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
-
4
3
  class AtomicOptimizer {
5
4
  constructor(options = {}) {
6
5
  this.options = {
@@ -12,7 +11,6 @@ class AtomicOptimizer {
12
11
  minify: true,
13
12
  ...options
14
13
  };
15
-
16
14
  this.usageCount = new Map();
17
15
  this.atomicClasses = new Map();
18
16
  this.stats = {
@@ -21,28 +19,21 @@ class AtomicOptimizer {
21
19
  standardStyles: 0,
22
20
  uniqueProperties: 0
23
21
  };
24
-
25
22
  this.cache = null;
26
23
  if (this.options.cache) {
27
24
  this.loadCache();
28
25
  }
29
26
  }
30
-
31
27
  loadCache() {
32
28
  try {
33
29
  if (fs.existsSync(this.options.cachePath)) {
34
30
  const data = fs.readFileSync(this.options.cachePath, 'utf8');
35
31
  const cache = JSON.parse(data);
36
-
37
- // Check version compatibility
38
32
  if (cache.version === '1.0.0') {
39
33
  this.atomicClasses = new Map(cache.atomicClasses || []);
40
34
  this.stats = cache.stats || this.stats;
41
-
42
35
  const cacheTime = new Date(cache.timestamp).toLocaleString();
43
36
  console.log(`--Loaded ${this.atomicClasses.size} atomic classes from cache (${cacheTime})\n`);
44
-
45
- // Verify config matches
46
37
  if (cache.config) {
47
38
  if (cache.config.threshold !== this.options.threshold) {
48
39
  console.log(`Cache threshold (${cache.config.threshold}) differs from current (${this.options.threshold})`);
@@ -56,7 +47,6 @@ class AtomicOptimizer {
56
47
  console.log('Could not load cache:', err.message);
57
48
  }
58
49
  }
59
-
60
50
  saveCache() {
61
51
  try {
62
52
  const cache = {
@@ -65,8 +55,6 @@ class AtomicOptimizer {
65
55
  atomicClasses: Array.from(this.atomicClasses.entries()),
66
56
  stats: this.stats
67
57
  };
68
-
69
- // Keep only last 5 cache files for rollback
70
58
  const cacheDir = path.dirname(this.options.cachePath);
71
59
  if (fs.existsSync(cacheDir)) {
72
60
  const files = fs.readdirSync(cacheDir)
@@ -76,19 +64,15 @@ class AtomicOptimizer {
76
64
  time: fs.statSync(path.join(cacheDir, f)).mtime.getTime()
77
65
  }))
78
66
  .sort((a, b) => b.time - a.time);
79
-
80
67
  files.slice(4).forEach(f => {
81
68
  fs.unlinkSync(path.join(cacheDir, f.name));
82
69
  });
83
70
  }
84
-
85
71
  fs.writeFileSync(this.options.cachePath, JSON.stringify(cache, null, 2), 'utf8');
86
-
87
72
  } catch (err) {
88
73
  console.log('Could not save cache:', err.message);
89
74
  }
90
75
  }
91
-
92
76
  trackStyles(styles) {
93
77
  Object.values(styles).forEach(style => {
94
78
  Object.entries(style).forEach(([prop, value]) => {
@@ -100,25 +84,18 @@ class AtomicOptimizer {
100
84
  });
101
85
  this.stats.uniqueProperties = this.usageCount.size;
102
86
  }
103
-
104
87
  shouldBeAtomic(prop, value) {
105
88
  const key = `${prop}:${value}`;
106
89
  const usage = this.usageCount.get(key) || 0;
107
-
108
- // Don't atomic-ify important layout properties that affect order
109
90
  const criticalProps = ['position', 'display', 'flex', 'grid', 'z-index'];
110
91
  const isCritical = criticalProps.some(p => prop.includes(p));
111
-
112
92
  if (isCritical && usage < this.options.threshold * 2) {
113
- return false; // Keep critical styles in place
93
+ return false;
114
94
  }
115
-
116
95
  return usage >= this.options.threshold;
117
96
  }
118
-
119
97
  generateClassName(prop, value) {
120
98
  const key = `${prop}:${value}`;
121
-
122
99
  if (this.options.naming === 'hash') {
123
100
  let hash = 0;
124
101
  for (let i = 0; i < key.length; i++) {
@@ -127,15 +104,12 @@ class AtomicOptimizer {
127
104
  }
128
105
  return `_${Math.abs(hash).toString(36).substring(0, 6)}`;
129
106
  }
130
-
131
107
  const kebabProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
132
108
  const safeValue = value.replace(/[^a-zA-Z0-9-]/g, '-');
133
109
  return `${kebabProp}-${safeValue}`;
134
110
  }
135
-
136
111
  getOrCreateAtomic(prop, value) {
137
112
  const key = `${prop}:${value}`;
138
-
139
113
  if (!this.atomicClasses.has(key)) {
140
114
  const className = this.generateClassName(prop, value);
141
115
  this.atomicClasses.set(key, {
@@ -146,36 +120,27 @@ class AtomicOptimizer {
146
120
  });
147
121
  this.stats.atomicStyles++;
148
122
  }
149
-
150
123
  return this.atomicClasses.get(key).className;
151
124
  }
152
-
153
125
  findKeyByClassName(className) {
154
126
  for (let [key, value] of this.atomicClasses.entries()) {
155
127
  if (value.className === className) return key;
156
128
  }
157
129
  return null;
158
130
  }
159
-
160
131
  generateAtomicCSS() {
161
- let css = '';
162
-
132
+ let css = '';
163
133
  const sortedClasses = Array.from(this.atomicClasses.values())
164
134
  .sort((a, b) => b.usageCount - a.usageCount);
165
-
166
135
  sortedClasses.forEach(atomic => {
167
136
  const kebabProp = atomic.prop.replace(/([A-Z])/g, '-$1').toLowerCase();
168
137
  css += `.${atomic.className} { ${kebabProp}: ${atomic.value}; }\n`;
169
138
  });
170
-
171
139
  return css;
172
140
  }
173
-
174
141
  generateComponentCSS(componentName, style, selectors) {
175
142
  const atomicClasses = [];
176
143
  const standardStyles = {};
177
-
178
- // Separate atomic and standard styles
179
144
  Object.entries(style).forEach(([prop, value]) => {
180
145
  if (prop === 'selectors') return;
181
146
 
@@ -186,13 +151,9 @@ class AtomicOptimizer {
186
151
  standardStyles[prop] = value;
187
152
  }
188
153
  });
189
-
190
154
  let componentCSS = '';
191
-
192
155
  if (atomicClasses.length > 0 || Object.keys(standardStyles).length > 0) {
193
156
  componentCSS += `${selectors.join(', ')} {\n`;
194
-
195
- // EXPAND atomic classes into actual properties
196
157
  atomicClasses.forEach(className => {
197
158
  const key = this.findKeyByClassName(className);
198
159
  if (key) {
@@ -201,69 +162,43 @@ class AtomicOptimizer {
201
162
  componentCSS += ` ${kebabProp}: ${atomic.value};\n`;
202
163
  }
203
164
  });
204
-
205
- // Add standard styles (these will override atomic if needed)
206
165
  Object.entries(standardStyles).forEach(([prop, value]) => {
207
166
  const kebabProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
208
167
  componentCSS += ` ${kebabProp}: ${value};\n`;
209
168
  });
210
-
211
169
  componentCSS += `}\n`;
212
170
  }
213
-
214
171
  return componentCSS;
215
172
  }
216
-
217
173
  validateStyleOrder(originalStyles, atomicStyles) {
218
- // Compare original vs atomic to ensure no missing styles
219
174
  const originalProps = new Set();
220
175
  const atomicProps = new Set();
221
-
222
176
  Object.values(originalStyles).forEach(style => {
223
177
  Object.keys(style).forEach(prop => {
224
178
  if (prop !== 'selectors') originalProps.add(prop);
225
179
  });
226
180
  });
227
-
228
- // Check atomic classes for missing props
229
181
  this.atomicClasses.forEach(atomic => {
230
182
  atomicProps.add(atomic.prop);
231
183
  });
232
-
233
184
  const missingProps = [...originalProps].filter(p => !atomicProps.has(p));
234
185
  if (missingProps.length > 0) {
235
186
  console.warn('Missing atomic classes for:', missingProps);
236
187
  }
237
188
  }
238
-
239
189
  optimize(styles) {
240
- // Track usage first
241
190
  this.trackStyles(styles);
242
-
243
- // Generate atomic CSS
244
191
  let atomicCSS = this.generateAtomicCSS();
245
-
246
- // Generate component CSS with expanded styles
247
192
  let componentCSS = '';
248
-
249
- // Process components in original order to maintain specificity
250
193
  Object.entries(styles).forEach(([name, style]) => {
251
194
  const selectors = style.selectors || [`.${name}`];
252
195
  componentCSS += this.generateComponentCSS(name, style, selectors);
253
196
  });
254
-
255
- // Calculate savings
256
197
  const savings = ((this.stats.totalStyles - this.atomicClasses.size) / this.stats.totalStyles * 100).toFixed(1);
257
-
258
- // Save cache if enabled
259
198
  if (this.options.cache) {
260
199
  this.saveCache();
261
200
  }
262
-
263
- // Return atomic CSS first, then component CSS
264
- // This ensures atomic classes are defined before they're used
265
201
  return atomicCSS + componentCSS;
266
202
  }
267
203
  }
268
-
269
204
  module.exports = { AtomicOptimizer };