@melcanz85/chaincss 1.11.0 → 1.11.1
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/README.md +114 -45
- package/browser/index.js +1 -1
- package/browser/react-hooks.jsx +1 -63
- package/browser/rtt.js +1 -45
- package/node/atomic-optimizer.js +2 -67
- package/node/btt.js +348 -84
- package/node/cache-manager.js +0 -12
- package/node/chaincss.js +14 -99
- package/node/index.js +2 -13
- package/node/prefixer.js +2 -61
- package/node/strVal.js +1 -1
- package/package.json +1 -1
- package/types.d.ts +0 -26
- package/node/css-properties.json +0 -633
package/node/btt.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const https = require('https');
|
|
3
|
+
const https = require('https');
|
|
4
4
|
const { tokens, createTokens, responsive } = require('../shared/tokens.cjs');
|
|
5
|
-
|
|
6
5
|
const chain = {
|
|
7
6
|
cssOutput: undefined,
|
|
8
7
|
catcher: {},
|
|
@@ -22,35 +21,25 @@ const chain = {
|
|
|
22
21
|
}
|
|
23
22
|
},
|
|
24
23
|
|
|
25
|
-
// Helper function to fetch with https
|
|
26
24
|
fetchWithHttps(url) {
|
|
27
25
|
return new Promise((resolve, reject) => {
|
|
28
26
|
https.get(url, (response) => {
|
|
29
27
|
let data = '';
|
|
30
|
-
|
|
31
|
-
response.on('data', (chunk) => {
|
|
32
|
-
data += chunk;
|
|
33
|
-
});
|
|
34
|
-
|
|
28
|
+
response.on('data', (chunk) => data += chunk);
|
|
35
29
|
response.on('end', () => {
|
|
36
30
|
try {
|
|
37
|
-
|
|
38
|
-
resolve(jsonData);
|
|
31
|
+
resolve(JSON.parse(data));
|
|
39
32
|
} catch (error) {
|
|
40
33
|
reject(error);
|
|
41
34
|
}
|
|
42
35
|
});
|
|
43
|
-
}).on('error',
|
|
44
|
-
reject(error);
|
|
45
|
-
});
|
|
36
|
+
}).on('error', reject);
|
|
46
37
|
});
|
|
47
38
|
},
|
|
48
39
|
|
|
49
40
|
async getCSSProperties() {
|
|
50
41
|
try {
|
|
51
42
|
const jsonPath = path.join(__dirname, 'css-properties.json');
|
|
52
|
-
|
|
53
|
-
// Check if file already exists
|
|
54
43
|
try {
|
|
55
44
|
await fs.promises.access(jsonPath);
|
|
56
45
|
const existingData = await fs.promises.readFile(jsonPath, 'utf8');
|
|
@@ -58,26 +47,16 @@ const chain = {
|
|
|
58
47
|
this.cachedValidProperties = objProp;
|
|
59
48
|
return objProp;
|
|
60
49
|
} catch {
|
|
61
|
-
// Use https instead of fetch
|
|
62
50
|
const url = 'https://raw.githubusercontent.com/mdn/data/main/css/properties.json';
|
|
63
51
|
const data = await this.fetchWithHttps(url);
|
|
64
52
|
const allProperties = Object.keys(data);
|
|
65
|
-
|
|
66
|
-
// Strip vendor prefixes and remove duplicates
|
|
67
53
|
const baseProperties = new Set();
|
|
68
|
-
|
|
69
54
|
allProperties.forEach(prop => {
|
|
70
|
-
// Remove vendor prefixes (-webkit-, -moz-, -ms-, -o-)
|
|
71
55
|
const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
|
|
72
56
|
baseProperties.add(baseProp);
|
|
73
57
|
});
|
|
74
|
-
|
|
75
|
-
// Convert Set back to array and sort
|
|
76
58
|
const cleanProperties = Array.from(baseProperties).sort();
|
|
77
|
-
|
|
78
|
-
// Save cleaned properties
|
|
79
59
|
await fs.promises.writeFile(jsonPath, JSON.stringify(cleanProperties, null, 2));
|
|
80
|
-
|
|
81
60
|
this.cachedValidProperties = cleanProperties;
|
|
82
61
|
return cleanProperties;
|
|
83
62
|
}
|
|
@@ -91,130 +70,415 @@ const chain = {
|
|
|
91
70
|
return this.cachedValidProperties;
|
|
92
71
|
}
|
|
93
72
|
};
|
|
94
|
-
|
|
95
|
-
// Initialize properties synchronously when module loads
|
|
96
73
|
chain.initializeProperties();
|
|
97
|
-
|
|
98
74
|
const resolveToken = (value, useTokens) => {
|
|
99
75
|
if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
|
|
100
76
|
return value;
|
|
101
77
|
}
|
|
102
|
-
|
|
103
78
|
const tokenPath = value.slice(1);
|
|
104
79
|
const tokenValue = tokens.get(tokenPath);
|
|
105
|
-
|
|
106
|
-
if (!tokenValue) {
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return tokenValue;
|
|
80
|
+
return tokenValue || value;
|
|
111
81
|
};
|
|
112
|
-
|
|
113
|
-
function $(useTokens = true){
|
|
82
|
+
function $(useTokens = true) {
|
|
114
83
|
const catcher = {};
|
|
115
|
-
|
|
116
|
-
// Use cached properties if available
|
|
117
84
|
const validProperties = chain.cachedValidProperties;
|
|
118
|
-
|
|
119
85
|
const handler = {
|
|
120
86
|
get: (target, prop) => {
|
|
121
87
|
if (prop === 'block') {
|
|
122
88
|
return function(...args) {
|
|
123
|
-
// If no args, just return current catcher
|
|
124
89
|
if (args.length === 0) {
|
|
125
90
|
const result = { ...catcher };
|
|
126
|
-
// Clear catcher
|
|
127
91
|
Object.keys(catcher).forEach(key => delete catcher[key]);
|
|
128
92
|
return result;
|
|
129
93
|
}
|
|
130
|
-
|
|
131
|
-
// Create result with selectors
|
|
132
94
|
const result = {
|
|
133
95
|
selectors: args,
|
|
134
96
|
...catcher
|
|
135
97
|
};
|
|
136
|
-
|
|
137
|
-
// Clear catcher
|
|
138
98
|
Object.keys(catcher).forEach(key => delete catcher[key]);
|
|
139
|
-
|
|
140
99
|
return result;
|
|
141
100
|
};
|
|
142
101
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
102
|
+
if (prop === '$') {
|
|
103
|
+
return function(selector) {
|
|
104
|
+
const props = {};
|
|
105
|
+
const selectorProxy = new Proxy({}, {
|
|
106
|
+
get: (target, methodProp) => {
|
|
107
|
+
if (methodProp === 'block') {
|
|
108
|
+
return function() {
|
|
109
|
+
return {
|
|
110
|
+
selectors: [selector],
|
|
111
|
+
...props
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return function(value) {
|
|
116
|
+
props[methodProp] = resolveToken(value, useTokens);
|
|
117
|
+
return selectorProxy; // Return the proxy for chaining
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return selectorProxy;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (prop === 'media') {
|
|
126
|
+
return function(query, callback) {
|
|
127
|
+
const subChain = $(useTokens);
|
|
128
|
+
const result = callback(subChain);
|
|
129
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
130
|
+
catcher.atRules.push({
|
|
131
|
+
type: 'media',
|
|
132
|
+
query: query,
|
|
133
|
+
styles: Array.isArray(result) ? result : [result]
|
|
134
|
+
});
|
|
135
|
+
return proxy;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (prop === 'keyframes') {
|
|
139
|
+
return function(name, callback) {
|
|
140
|
+
const keyframeContext = {
|
|
141
|
+
_keyframeSteps: {}
|
|
142
|
+
};
|
|
143
|
+
const keyframeProxy = new Proxy(keyframeContext, {
|
|
144
|
+
get: (target, stepProp) => {
|
|
145
|
+
if (stepProp === 'from' || stepProp === 'to' || stepProp === 'percent') {
|
|
146
|
+
return function(...args) {
|
|
147
|
+
if (stepProp === 'percent') {
|
|
148
|
+
const value = args[0];
|
|
149
|
+
const stepCallback = args[1];
|
|
150
|
+
const subChain = $(useTokens);
|
|
151
|
+
const properties = stepCallback(subChain).block();
|
|
152
|
+
target._keyframeSteps[`${value}%`] = properties;
|
|
153
|
+
} else {
|
|
154
|
+
const stepCallback = args[0];
|
|
155
|
+
const subChain = $(useTokens);
|
|
156
|
+
const properties = stepCallback(subChain).block();
|
|
157
|
+
target._keyframeSteps[stepProp] = properties;
|
|
158
|
+
}
|
|
159
|
+
return keyframeProxy;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
callback(keyframeProxy);
|
|
166
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
167
|
+
catcher.atRules.push({
|
|
168
|
+
type: 'keyframes',
|
|
169
|
+
name: name,
|
|
170
|
+
steps: keyframeContext._keyframeSteps,
|
|
171
|
+
atomic: false
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return proxy;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (prop === 'fontFace') {
|
|
178
|
+
return function(callback) {
|
|
179
|
+
const subChain = $(useTokens);
|
|
180
|
+
const fontProps = callback(subChain).block();
|
|
181
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
182
|
+
catcher.atRules.push({
|
|
183
|
+
type: 'font-face',
|
|
184
|
+
properties: fontProps,
|
|
185
|
+
atomic: false
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return proxy;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (prop === 'supports') {
|
|
192
|
+
return function(condition, callback) {
|
|
193
|
+
const subChain = $(useTokens);
|
|
194
|
+
const styles = callback(subChain).block();
|
|
195
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
196
|
+
catcher.atRules.push({
|
|
197
|
+
type: 'supports',
|
|
198
|
+
condition: condition,
|
|
199
|
+
styles: styles
|
|
200
|
+
});
|
|
201
|
+
return proxy;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (prop === 'container') {
|
|
205
|
+
return function(condition, callback) {
|
|
206
|
+
const subChain = $(useTokens);
|
|
207
|
+
const styles = callback(subChain).block();
|
|
208
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
209
|
+
catcher.atRules.push({
|
|
210
|
+
type: 'container',
|
|
211
|
+
condition: condition,
|
|
212
|
+
styles: styles
|
|
213
|
+
});
|
|
214
|
+
return proxy;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (prop === 'layer') {
|
|
218
|
+
return function(name, callback) {
|
|
219
|
+
const subChain = $(useTokens);
|
|
220
|
+
const styles = callback(subChain).block();
|
|
221
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
222
|
+
catcher.atRules.push({
|
|
223
|
+
type: 'layer',
|
|
224
|
+
name: name,
|
|
225
|
+
styles: styles
|
|
226
|
+
});
|
|
227
|
+
return proxy;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (prop === 'counterStyle') {
|
|
231
|
+
return function(name, callback) {
|
|
232
|
+
const subChain = $(useTokens);
|
|
233
|
+
const properties = callback(subChain).block();
|
|
234
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
235
|
+
catcher.atRules.push({
|
|
236
|
+
type: 'counter-style',
|
|
237
|
+
name: name,
|
|
238
|
+
properties: properties,
|
|
239
|
+
atomic: false
|
|
240
|
+
});
|
|
241
|
+
return proxy;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (prop === 'property') {
|
|
245
|
+
return function(name, callback) {
|
|
246
|
+
const subChain = $(useTokens);
|
|
247
|
+
const descriptors = callback(subChain).block();
|
|
248
|
+
if (!catcher.atRules) catcher.atRules = [];
|
|
249
|
+
catcher.atRules.push({
|
|
250
|
+
type: 'property',
|
|
251
|
+
name: name,
|
|
252
|
+
descriptors: descriptors,
|
|
253
|
+
atomic: false
|
|
254
|
+
});
|
|
255
|
+
return proxy;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (prop === 'from' || prop === 'to' || prop === 'percent') {
|
|
259
|
+
return function(...args) {
|
|
260
|
+
// Handle percent case: .percent(50, callback)
|
|
261
|
+
if (prop === 'percent') {
|
|
262
|
+
const value = args[0];
|
|
263
|
+
const callback = args[1];
|
|
264
|
+
const subChain = $(useTokens);
|
|
265
|
+
const properties = callback(subChain).block();
|
|
266
|
+
if (!this._keyframeSteps) this._keyframeSteps = {};
|
|
267
|
+
this._keyframeSteps[`${value}%`] = properties;
|
|
268
|
+
return this; // Return this for chaining
|
|
269
|
+
}
|
|
270
|
+
const callback = args[0];
|
|
271
|
+
const subChain = $(useTokens);
|
|
272
|
+
const properties = callback(subChain).block();
|
|
273
|
+
if (!this._keyframeSteps) this._keyframeSteps = {};
|
|
274
|
+
this._keyframeSteps[prop] = properties;
|
|
275
|
+
return this;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
146
279
|
if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
|
|
147
280
|
console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
|
|
148
281
|
}
|
|
149
|
-
// Return a function that sets the value
|
|
150
282
|
return function(value) {
|
|
151
283
|
catcher[prop] = resolveToken(value, useTokens);
|
|
152
284
|
return proxy;
|
|
153
285
|
};
|
|
154
286
|
}
|
|
155
287
|
};
|
|
156
|
-
|
|
157
|
-
// Create the proxy
|
|
158
288
|
const proxy = new Proxy({}, handler);
|
|
159
|
-
|
|
160
|
-
// Trigger async load if needed (don't await)
|
|
161
289
|
if (chain.cachedValidProperties.length === 0) {
|
|
162
290
|
chain.getCSSProperties().catch(err => {
|
|
163
291
|
console.error('Failed to load CSS properties:', err.message);
|
|
164
292
|
});
|
|
165
293
|
}
|
|
166
|
-
|
|
167
294
|
return proxy;
|
|
168
295
|
}
|
|
169
|
-
|
|
170
296
|
const run = (...args) => {
|
|
171
297
|
let cssOutput = '';
|
|
172
|
-
|
|
173
298
|
args.forEach((value) => {
|
|
174
|
-
if (value
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
299
|
+
if (!value) return;
|
|
300
|
+
if (value.selectors) {
|
|
301
|
+
let mainRuleBody = '';
|
|
302
|
+
let atRulesOutput = '';
|
|
178
303
|
for (let key in value) {
|
|
179
|
-
if (key
|
|
304
|
+
if (key === 'selectors' || !value.hasOwnProperty(key)) continue;
|
|
305
|
+
if (key === 'atRules' && Array.isArray(value[key])) {
|
|
306
|
+
value[key].forEach(rule => {
|
|
307
|
+
atRulesOutput += processAtRule(rule, value.selectors);
|
|
308
|
+
});
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
180
312
|
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
181
|
-
|
|
313
|
+
mainRuleBody += ` ${kebabKey}: ${value[key]};\n`;
|
|
182
314
|
}
|
|
183
315
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
316
|
+
if (mainRuleBody.trim()) {
|
|
317
|
+
cssOutput += `${value.selectors.join(', ')} {\n${mainRuleBody}}\n`;
|
|
318
|
+
}
|
|
319
|
+
cssOutput += atRulesOutput;
|
|
320
|
+
}
|
|
321
|
+
else if (value.type) {
|
|
322
|
+
cssOutput += processStandaloneAtRule(value);
|
|
187
323
|
}
|
|
188
324
|
});
|
|
189
|
-
|
|
190
|
-
chain.cssOutput = cssOutput
|
|
191
|
-
return cssOutput
|
|
325
|
+
cssOutput = cssOutput.replace(/\n{3,}/g, '\n\n').trim();
|
|
326
|
+
chain.cssOutput = cssOutput;
|
|
327
|
+
return cssOutput;
|
|
192
328
|
};
|
|
193
|
-
|
|
329
|
+
function processAtRule(rule, parentSelectors = null) {
|
|
330
|
+
let output = '';
|
|
331
|
+
switch(rule.type) {
|
|
332
|
+
case 'media':
|
|
333
|
+
if (parentSelectors) {
|
|
334
|
+
let mediaBody = '';
|
|
335
|
+
if (rule.styles) {
|
|
336
|
+
for (let prop in rule.styles) {
|
|
337
|
+
if (prop !== 'selectors' && rule.styles.hasOwnProperty(prop)) {
|
|
338
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
339
|
+
mediaBody += ` ${kebabKey}: ${rule.styles[prop]};\n`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (mediaBody.trim()) {
|
|
344
|
+
output = `@media ${rule.query} {\n ${parentSelectors.join(', ')} {\n${mediaBody} }\n}\n`;
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
output = `@media ${rule.query} {\n`;
|
|
348
|
+
if (Array.isArray(rule.styles)) {
|
|
349
|
+
rule.styles.forEach(styleObj => {
|
|
350
|
+
if (styleObj.selectors) {
|
|
351
|
+
let ruleBody = '';
|
|
352
|
+
for (let prop in styleObj) {
|
|
353
|
+
if (prop !== 'selectors' && styleObj.hasOwnProperty(prop)) {
|
|
354
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
355
|
+
ruleBody += ` ${kebabKey}: ${styleObj[prop]};\n`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (ruleBody.trim()) {
|
|
359
|
+
output += ` ${styleObj.selectors.join(', ')} {\n${ruleBody} }\n`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else if (rule.styles && rule.styles.selectors) {
|
|
365
|
+
let ruleBody = '';
|
|
366
|
+
for (let prop in rule.styles) {
|
|
367
|
+
if (prop !== 'selectors' && rule.styles.hasOwnProperty(prop)) {
|
|
368
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
369
|
+
ruleBody += ` ${kebabKey}: ${rule.styles[prop]};\n`;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (ruleBody.trim()) {
|
|
373
|
+
output += ` ${rule.styles.selectors.join(', ')} {\n${ruleBody} }\n`;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
output += '}\n';
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
case 'keyframes':
|
|
380
|
+
output = `@keyframes ${rule.name} {\n`;
|
|
381
|
+
for (let step in rule.steps) {
|
|
382
|
+
output += ` ${step} {\n`;
|
|
383
|
+
for (let prop in rule.steps[step]) {
|
|
384
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
385
|
+
output += ` ${kebabKey}: ${rule.steps[step][prop]};\n`;
|
|
386
|
+
}
|
|
387
|
+
output += ' }\n';
|
|
388
|
+
}
|
|
389
|
+
output += '}\n';
|
|
390
|
+
break;
|
|
391
|
+
case 'font-face':
|
|
392
|
+
output = '@font-face {\n';
|
|
393
|
+
for (let prop in rule.properties) {
|
|
394
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
395
|
+
output += ` ${kebabKey}: ${rule.properties[prop]};\n`;
|
|
396
|
+
}
|
|
397
|
+
output += '}\n';
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
return output;
|
|
401
|
+
}
|
|
402
|
+
function processStandaloneAtRule(rule) {
|
|
403
|
+
let output = '';
|
|
404
|
+
switch(rule.type) {
|
|
405
|
+
case 'font-face':
|
|
406
|
+
output = '@font-face {\n';
|
|
407
|
+
for (let prop in rule.properties) {
|
|
408
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
409
|
+
output += ` ${kebabKey}: ${rule.properties[prop]};\n`;
|
|
410
|
+
}
|
|
411
|
+
output += '}\n';
|
|
412
|
+
break;
|
|
413
|
+
case 'keyframes':
|
|
414
|
+
output = `@keyframes ${rule.name} {\n`;
|
|
415
|
+
for (let step in rule.steps) {
|
|
416
|
+
output += ` ${step} {\n`;
|
|
417
|
+
for (let prop in rule.steps[step]) {
|
|
418
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
419
|
+
output += ` ${kebabKey}: ${rule.steps[step][prop]};\n`;
|
|
420
|
+
}
|
|
421
|
+
output += ' }\n';
|
|
422
|
+
}
|
|
423
|
+
output += '}\n';
|
|
424
|
+
break;
|
|
425
|
+
case 'counter-style':
|
|
426
|
+
output = `@counter-style ${rule.name} {\n`;
|
|
427
|
+
for (let prop in rule.properties) {
|
|
428
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
429
|
+
output += ` ${kebabKey}: ${rule.properties[prop]};\n`;
|
|
430
|
+
}
|
|
431
|
+
output += '}\n';
|
|
432
|
+
break;
|
|
433
|
+
case 'property':
|
|
434
|
+
output = `@property ${rule.name} {\n`;
|
|
435
|
+
for (let desc in rule.descriptors) {
|
|
436
|
+
const kebabKey = desc.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
437
|
+
output += ` ${kebabKey}: ${rule.descriptors[desc]};\n`;
|
|
438
|
+
}
|
|
439
|
+
output += '}\n';
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
return output;
|
|
443
|
+
}
|
|
194
444
|
const compile = (obj) => {
|
|
195
445
|
let cssString = '';
|
|
196
|
-
|
|
197
446
|
for (const key in obj) {
|
|
198
447
|
if (obj.hasOwnProperty(key)) {
|
|
199
448
|
const element = obj[key];
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
449
|
+
if (element.atRules && Array.isArray(element.atRules)) {
|
|
450
|
+
element.atRules.forEach(rule => {
|
|
451
|
+
cssString += processAtRule(rule, null);
|
|
452
|
+
});
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (element.selectors) {
|
|
456
|
+
let elementCSS = '';
|
|
457
|
+
let atRulesCSS = '';
|
|
458
|
+
for (let prop in element) {
|
|
459
|
+
if (prop === 'selectors' || !element.hasOwnProperty(prop)) continue;
|
|
460
|
+
if (prop === 'atRules' && Array.isArray(element[prop])) {
|
|
461
|
+
element[prop].forEach(rule => {
|
|
462
|
+
atRulesCSS += processAtRule(rule, element.selectors);
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
466
|
+
elementCSS += ` ${kebabKey}: ${element[prop]};\n`;
|
|
467
|
+
}
|
|
208
468
|
}
|
|
469
|
+
if (elementCSS.trim()) {
|
|
470
|
+
cssString += `${element.selectors.join(', ')} {\n${elementCSS}}\n`;
|
|
471
|
+
}
|
|
472
|
+
cssString += atRulesCSS;
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
console.log(`Warning: Unknown element type for key "${key}":`, element);
|
|
209
476
|
}
|
|
210
|
-
|
|
211
|
-
cssString += `${selectors.join(', ')} {\n${elementCSS}}\n`;
|
|
212
477
|
}
|
|
213
478
|
}
|
|
214
|
-
|
|
215
479
|
chain.cssOutput = cssString.trim();
|
|
480
|
+
return chain.cssOutput;
|
|
216
481
|
};
|
|
217
|
-
|
|
218
482
|
module.exports = {
|
|
219
483
|
chain,
|
|
220
484
|
$,
|
package/node/cache-manager.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
|
|
4
3
|
class CacheManager {
|
|
5
4
|
constructor(cachePath = './.chaincss-cache') {
|
|
6
5
|
this.cachePath = path.resolve(process.cwd(), cachePath);
|
|
@@ -8,8 +7,6 @@ class CacheManager {
|
|
|
8
7
|
this.cache = {};
|
|
9
8
|
this.load();
|
|
10
9
|
}
|
|
11
|
-
|
|
12
|
-
// Load cache from disk
|
|
13
10
|
load() {
|
|
14
11
|
try {
|
|
15
12
|
if (fs.existsSync(this.cachePath)) {
|
|
@@ -33,18 +30,12 @@ class CacheManager {
|
|
|
33
30
|
this.cache = {};
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
|
-
|
|
37
|
-
// Get value from cache
|
|
38
33
|
get(key) {
|
|
39
34
|
return this.cache[key];
|
|
40
35
|
}
|
|
41
|
-
|
|
42
|
-
// Set value in cache
|
|
43
36
|
set(key, value) {
|
|
44
37
|
this.cache[key] = value;
|
|
45
38
|
}
|
|
46
|
-
|
|
47
|
-
// Save cache to disk
|
|
48
39
|
save() {
|
|
49
40
|
try {
|
|
50
41
|
const data = JSON.stringify(this.cache, null, 2);
|
|
@@ -54,8 +45,6 @@ class CacheManager {
|
|
|
54
45
|
console.warn('Could not save cache:', error.message);
|
|
55
46
|
}
|
|
56
47
|
}
|
|
57
|
-
|
|
58
|
-
// Clear cache
|
|
59
48
|
clear() {
|
|
60
49
|
this.cache = {};
|
|
61
50
|
if (fs.existsSync(this.cachePath)) {
|
|
@@ -64,5 +53,4 @@ class CacheManager {
|
|
|
64
53
|
console.log('Cache cleared');
|
|
65
54
|
}
|
|
66
55
|
}
|
|
67
|
-
|
|
68
56
|
module.exports = { CacheManager };
|