chaincss 1.13.2 → 1.13.3
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 +6 -14
- package/browser/index.js +1 -1
- package/browser/rtt.js +39 -9
- package/browser/vue-composables.js +200 -0
- package/node/atomic-optimizer.js +279 -144
- package/node/btt.js +149 -102
- package/node/chaincss.js +197 -44
- package/node/prefixer.js +2 -2
- package/node/strVal.js +37 -51
- package/node/theme-validator.js +4 -4
- package/package.json +13 -3
- package/types.d.ts +51 -3
- package/node/css-properties.json +0 -633
package/node/btt.js
CHANGED
|
@@ -1,19 +1,92 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const https = require('https');
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
4
|
+
const {tokens, DesignTokens } = require('../shared/tokens.cjs');
|
|
5
|
+
const { COMMON_CSS_PROPERTIES } = require('../browser/commonProps.js');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// Remove the hardcoded atomicOptimizer instance
|
|
8
|
+
let atomicOptimizer = null;
|
|
9
|
+
|
|
10
|
+
// Function to set the atomic optimizer from outside
|
|
11
|
+
function setAtomicOptimizer(optimizer) {
|
|
12
|
+
atomicOptimizer = optimizer;
|
|
13
|
+
}
|
|
12
14
|
|
|
13
15
|
function configureAtomic(opts) {
|
|
14
|
-
|
|
16
|
+
if (atomicOptimizer) {
|
|
17
|
+
Object.assign(atomicOptimizer.options, opts);
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
// Helper function for Node.js HTTP requests (for older Node versions)
|
|
22
|
+
const fetchWithHttps = (url) => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const timeout = setTimeout(() => {
|
|
25
|
+
req.destroy();
|
|
26
|
+
reject(new Error('Request timeout'));
|
|
27
|
+
}, 3000);
|
|
28
|
+
|
|
29
|
+
const req = https.get(url, (response) => {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
let data = '';
|
|
32
|
+
response.on('data', (chunk) => data += chunk);
|
|
33
|
+
response.on('end', () => {
|
|
34
|
+
try {
|
|
35
|
+
resolve(JSON.parse(data));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
reject(error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
req.on('error', (error) => {
|
|
43
|
+
clearTimeout(timeout);
|
|
44
|
+
reject(error);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const loadCSSProperties = async () => {
|
|
50
|
+
// Return cached if already loaded
|
|
51
|
+
if (chain.cachedValidProperties !== null) {
|
|
52
|
+
return chain.cachedValidProperties;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Try CDN first (only once) - same as runtime
|
|
56
|
+
try {
|
|
57
|
+
const url = 'https://raw.githubusercontent.com/mdn/data/main/css/properties.json';
|
|
58
|
+
let data;
|
|
59
|
+
|
|
60
|
+
// Use fetch if available (Node 18+), otherwise use https
|
|
61
|
+
if (globalThis.fetch) {
|
|
62
|
+
const controller = new AbortController();
|
|
63
|
+
const timeoutId = setTimeout(() => controller.abort(), 3000);
|
|
64
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
65
|
+
clearTimeout(timeoutId);
|
|
66
|
+
data = await response.json();
|
|
67
|
+
} else {
|
|
68
|
+
// Fallback for older Node versions
|
|
69
|
+
data = await fetchWithHttps(url);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const allProperties = Object.keys(data);
|
|
73
|
+
const baseProperties = new Set();
|
|
74
|
+
|
|
75
|
+
allProperties.forEach(prop => {
|
|
76
|
+
const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
|
|
77
|
+
baseProperties.add(baseProp);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
chain.cachedValidProperties = Array.from(baseProperties).sort();
|
|
81
|
+
return chain.cachedValidProperties;
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// Use imported fallback (clean and separate)
|
|
85
|
+
chain.cachedValidProperties = COMMON_CSS_PROPERTIES;
|
|
86
|
+
return chain.cachedValidProperties;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
17
90
|
const chain = {
|
|
18
91
|
cssOutput: undefined,
|
|
19
92
|
catcher: {},
|
|
@@ -21,63 +94,13 @@ const chain = {
|
|
|
21
94
|
classMap: {},
|
|
22
95
|
atomicStats: null,
|
|
23
96
|
|
|
24
|
-
initializeProperties() {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (fs.existsSync(jsonPath)) {
|
|
28
|
-
const data = fs.readFileSync(jsonPath, 'utf8');
|
|
29
|
-
this.cachedValidProperties = JSON.parse(data);
|
|
30
|
-
} else {
|
|
31
|
-
console.log('⚠️ CSS properties not cached, will load on first use');
|
|
32
|
-
}
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error('Error loading CSS properties:', error.message);
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
fetchWithHttps(url) {
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
https.get(url, (response) => {
|
|
41
|
-
let data = '';
|
|
42
|
-
response.on('data', (chunk) => data += chunk);
|
|
43
|
-
response.on('end', () => {
|
|
44
|
-
try {
|
|
45
|
-
resolve(JSON.parse(data));
|
|
46
|
-
} catch (error) {
|
|
47
|
-
reject(error);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}).on('error', reject);
|
|
51
|
-
});
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
async getCSSProperties() {
|
|
55
|
-
try {
|
|
56
|
-
const jsonPath = path.join(__dirname, 'css-properties.json');
|
|
57
|
-
try {
|
|
58
|
-
await fs.promises.access(jsonPath);
|
|
59
|
-
const existingData = await fs.promises.readFile(jsonPath, 'utf8');
|
|
60
|
-
const objProp = JSON.parse(existingData);
|
|
61
|
-
this.cachedValidProperties = objProp;
|
|
62
|
-
return objProp;
|
|
63
|
-
} catch {
|
|
64
|
-
const url = 'https://raw.githubusercontent.com/mdn/data/main/css/properties.json';
|
|
65
|
-
const data = await this.fetchWithHttps(url);
|
|
66
|
-
const allProperties = Object.keys(data);
|
|
67
|
-
const baseProperties = new Set();
|
|
68
|
-
allProperties.forEach(prop => {
|
|
69
|
-
const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
|
|
70
|
-
baseProperties.add(baseProp);
|
|
71
|
-
});
|
|
72
|
-
const cleanProperties = Array.from(baseProperties).sort();
|
|
73
|
-
await fs.promises.writeFile(jsonPath, JSON.stringify(cleanProperties, null, 2));
|
|
74
|
-
this.cachedValidProperties = cleanProperties;
|
|
75
|
-
return cleanProperties;
|
|
76
|
-
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error('Error loading CSS properties:', error.message);
|
|
79
|
-
return [];
|
|
97
|
+
async initializeProperties() {
|
|
98
|
+
if (this.cachedValidProperties.length > 0) {
|
|
99
|
+
return;
|
|
80
100
|
}
|
|
101
|
+
|
|
102
|
+
const properties = await loadCSSProperties();
|
|
103
|
+
this.cachedValidProperties = properties;
|
|
81
104
|
},
|
|
82
105
|
|
|
83
106
|
getCachedProperties() {
|
|
@@ -87,18 +110,47 @@ const chain = {
|
|
|
87
110
|
|
|
88
111
|
chain.initializeProperties();
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
//token pointer
|
|
114
|
+
const originalToken = tokens;
|
|
115
|
+
|
|
116
|
+
let currentTokenContext = null;
|
|
117
|
+
|
|
118
|
+
// createTokens pointer
|
|
119
|
+
function createTokens(tokenValues) {
|
|
120
|
+
const tokenObj = new DesignTokens(tokenValues);
|
|
121
|
+
currentTokenContext = tokenObj;
|
|
122
|
+
return tokenObj;
|
|
123
|
+
}
|
|
98
124
|
|
|
99
125
|
function $(useTokens = true) {
|
|
100
126
|
const catcher = {};
|
|
101
|
-
|
|
127
|
+
let validProperties = chain.cachedValidProperties;
|
|
128
|
+
const tokenContext = currentTokenContext || null;
|
|
129
|
+
|
|
130
|
+
const resolveToken = (value) => {
|
|
131
|
+
if (!useTokens || typeof value !== 'string') return value;
|
|
132
|
+
|
|
133
|
+
// Check if string contains any token patterns
|
|
134
|
+
if (value.includes('$')) {
|
|
135
|
+
// Replace all $token.path patterns with their resolved values
|
|
136
|
+
return value.replace(/\$([a-zA-Z0-9.-]+)/g, (match, path) => {
|
|
137
|
+
if (tokenContext) {
|
|
138
|
+
const resolved = tokenContext.get(path);
|
|
139
|
+
if (resolved !== undefined) {
|
|
140
|
+
return resolved;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Also try global tokens as fallback
|
|
144
|
+
const globalResolved = tokens.get(path);
|
|
145
|
+
if (globalResolved !== undefined) {
|
|
146
|
+
return globalResolved;
|
|
147
|
+
}
|
|
148
|
+
return match; // Return original if not found
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return value;
|
|
153
|
+
};
|
|
102
154
|
|
|
103
155
|
const handler = {
|
|
104
156
|
get: (target, prop) => {
|
|
@@ -356,24 +408,19 @@ function $(useTokens = true) {
|
|
|
356
408
|
// theme method
|
|
357
409
|
if (prop === 'theme') {
|
|
358
410
|
return function(themeTokens, callback) {
|
|
359
|
-
// Store original tokens to restore later
|
|
360
411
|
const originalTokens = tokens;
|
|
361
412
|
|
|
362
|
-
// Create a temporary token store for this theme
|
|
363
413
|
const themeTokenStore = {
|
|
364
414
|
get: (path) => {
|
|
365
|
-
// Try to get from theme tokens first, fallback to original
|
|
366
415
|
const themeValue = themeTokens.get ? themeTokens.get(path) : null;
|
|
367
416
|
if (themeValue !== null && themeValue !== undefined) {
|
|
368
417
|
return themeValue;
|
|
369
418
|
}
|
|
370
419
|
return originalTokens.get(path);
|
|
371
420
|
},
|
|
372
|
-
// Make it look like the original tokens object
|
|
373
421
|
...themeTokens
|
|
374
422
|
};
|
|
375
423
|
|
|
376
|
-
// Create a proxy to intercept token resolution
|
|
377
424
|
const tokenProxy = new Proxy(themeTokenStore, {
|
|
378
425
|
get: (target, prop) => {
|
|
379
426
|
if (prop === 'get') {
|
|
@@ -383,16 +430,12 @@ function $(useTokens = true) {
|
|
|
383
430
|
}
|
|
384
431
|
});
|
|
385
432
|
|
|
386
|
-
// Temporarily override the global tokens
|
|
387
433
|
const originalTokensRef = globalThis.__CHAINCSS_TOKENS__ || tokens;
|
|
388
434
|
const tempTokens = themeTokenStore;
|
|
389
435
|
|
|
390
|
-
// Create a new $ function that uses the theme tokens
|
|
391
436
|
const themed$ = (useTokens = true) => {
|
|
392
|
-
// Store original resolver
|
|
393
437
|
const originalResolver = resolveToken;
|
|
394
438
|
|
|
395
|
-
// Create temporary token resolver
|
|
396
439
|
const themeResolver = (value, useTokensFlag) => {
|
|
397
440
|
if (!useTokensFlag || typeof value !== 'string' || !value.startsWith('$')) {
|
|
398
441
|
return value;
|
|
@@ -402,21 +445,17 @@ function $(useTokens = true) {
|
|
|
402
445
|
return tokenValue || value;
|
|
403
446
|
};
|
|
404
447
|
|
|
405
|
-
// Temporarily replace resolveToken
|
|
406
448
|
globalThis.__CHAINCSS_TEMP_RESOLVER__ = themeResolver;
|
|
407
449
|
|
|
408
450
|
const result = $(useTokens);
|
|
409
451
|
|
|
410
|
-
// Restore original resolver
|
|
411
452
|
delete globalThis.__CHAINCSS_TEMP_RESOLVER__;
|
|
412
453
|
|
|
413
454
|
return result;
|
|
414
455
|
};
|
|
415
456
|
|
|
416
|
-
// Execute callback with themed chain
|
|
417
457
|
const result = callback(themed$);
|
|
418
458
|
|
|
419
|
-
// Store theme data for CSS generation
|
|
420
459
|
if (!catcher.themes) catcher.themes = [];
|
|
421
460
|
catcher.themes.push({
|
|
422
461
|
name: `theme-${Date.now()}`,
|
|
@@ -443,13 +482,6 @@ function $(useTokens = true) {
|
|
|
443
482
|
};
|
|
444
483
|
|
|
445
484
|
const proxy = new Proxy({}, handler);
|
|
446
|
-
|
|
447
|
-
if (chain.cachedValidProperties.length === 0) {
|
|
448
|
-
chain.getCSSProperties().catch(err => {
|
|
449
|
-
console.error('Failed to load CSS properties:', err.message);
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
485
|
return proxy;
|
|
454
486
|
}
|
|
455
487
|
|
|
@@ -649,6 +681,7 @@ function processStandaloneAtRule(rule) {
|
|
|
649
681
|
return output;
|
|
650
682
|
}
|
|
651
683
|
|
|
684
|
+
// btt.js - Updated run() function
|
|
652
685
|
const run = (...args) => {
|
|
653
686
|
let cssOutput = '';
|
|
654
687
|
const styleObjs = [];
|
|
@@ -714,12 +747,25 @@ const run = (...args) => {
|
|
|
714
747
|
cssOutput = cssOutput.replace(/\n{3,}/g, '\n\n').trim();
|
|
715
748
|
chain.cssOutput = cssOutput;
|
|
716
749
|
|
|
717
|
-
|
|
750
|
+
// Use the injected atomic optimizer
|
|
751
|
+
if (atomicOptimizer && atomicOptimizer.options.enabled) {
|
|
718
752
|
const result = atomicOptimizer.optimize(styleObjs);
|
|
719
|
-
|
|
753
|
+
|
|
754
|
+
// IMPORTANT: In component-first mode, we need to combine
|
|
755
|
+
// atomic utilities with the component CSS
|
|
756
|
+
if (atomicOptimizer.options.outputStrategy === 'component-first') {
|
|
757
|
+
// Component CSS already contains all styles, but we want to add
|
|
758
|
+
// atomic utilities as optional extras. The atomic optimizer's result.css
|
|
759
|
+
// already includes atomicCSS + componentCSS (with all styles)
|
|
760
|
+
chain.cssOutput = result.css;
|
|
761
|
+
} else {
|
|
762
|
+
// utility-first mode
|
|
763
|
+
chain.cssOutput = result.css;
|
|
764
|
+
}
|
|
765
|
+
|
|
720
766
|
chain.classMap = result.map;
|
|
721
767
|
chain.atomicStats = result.stats;
|
|
722
|
-
return
|
|
768
|
+
return chain.cssOutput;
|
|
723
769
|
}
|
|
724
770
|
|
|
725
771
|
return cssOutput;
|
|
@@ -736,7 +782,6 @@ const compile = (obj) => {
|
|
|
736
782
|
// Handle themes
|
|
737
783
|
if (element.themes && Array.isArray(element.themes)) {
|
|
738
784
|
element.themes.forEach(theme => {
|
|
739
|
-
// Generate CSS for each theme variant
|
|
740
785
|
if (theme.styles && theme.styles.selectors) {
|
|
741
786
|
let themeCSS = '';
|
|
742
787
|
let themeSelectors = theme.styles.selectors || [];
|
|
@@ -776,7 +821,6 @@ const compile = (obj) => {
|
|
|
776
821
|
atRulesCSS += processAtRule(rule, element.selectors);
|
|
777
822
|
});
|
|
778
823
|
} else if (prop === 'themes' && Array.isArray(element[prop])) {
|
|
779
|
-
// Process themes (already handled above)
|
|
780
824
|
continue;
|
|
781
825
|
} else if (prop === 'hover' && typeof element[prop] === 'object') {
|
|
782
826
|
let hoverBody = '';
|
|
@@ -802,11 +846,13 @@ const compile = (obj) => {
|
|
|
802
846
|
|
|
803
847
|
chain.cssOutput = cssString.trim();
|
|
804
848
|
|
|
805
|
-
|
|
849
|
+
// Use the injected atomic optimizer instead of a local instance
|
|
850
|
+
if (atomicOptimizer && atomicOptimizer.options.enabled) {
|
|
806
851
|
const result = atomicOptimizer.optimize(collected);
|
|
807
852
|
chain.cssOutput = result.css;
|
|
808
853
|
chain.classMap = result.map;
|
|
809
854
|
chain.atomicStats = result.stats;
|
|
855
|
+
chain.componentMap = result.componentMap;
|
|
810
856
|
return result.css;
|
|
811
857
|
}
|
|
812
858
|
|
|
@@ -931,7 +977,7 @@ function recipe(options) {
|
|
|
931
977
|
if (cv.style) styles.push(cv.style);
|
|
932
978
|
}
|
|
933
979
|
|
|
934
|
-
if (atomicOptimizer.options.enabled) {
|
|
980
|
+
if (atomicOptimizer && atomicOptimizer.options.enabled) {
|
|
935
981
|
const styleObj = {};
|
|
936
982
|
styles.forEach((style, i) => {
|
|
937
983
|
const selectors = style.selectors || [`variant-${i}`];
|
|
@@ -953,10 +999,11 @@ module.exports = {
|
|
|
953
999
|
chain,
|
|
954
1000
|
$,
|
|
955
1001
|
run,
|
|
1002
|
+
tokens : originalToken,
|
|
956
1003
|
compile,
|
|
957
1004
|
createTokens,
|
|
958
|
-
responsive,
|
|
959
1005
|
configureAtomic,
|
|
960
|
-
|
|
1006
|
+
setAtomicOptimizer, // Export this for dependency injection
|
|
1007
|
+
atomicOptimizer, // Will be null until set
|
|
961
1008
|
recipe
|
|
962
1009
|
};
|