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/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 { tokens, createTokens, responsive } = require('../shared/tokens.cjs');
5
- const { AtomicOptimizer } = require('./atomic-optimizer');
4
+ const {tokens, DesignTokens } = require('../shared/tokens.cjs');
5
+ const { COMMON_CSS_PROPERTIES } = require('../browser/commonProps.js');
6
6
 
7
- const atomicOptimizer = new AtomicOptimizer({
8
- enabled: false,
9
- alwaysAtomic: [],
10
- neverAtomic: ['content', 'animation']
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
- Object.assign(atomicOptimizer.options, opts);
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
- try {
26
- const jsonPath = path.join(__dirname, 'css-properties.json');
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
- const resolveToken = (value, useTokens) => {
91
- if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
92
- return value;
93
- }
94
- const tokenPath = value.slice(1);
95
- const tokenValue = tokens.get(tokenPath);
96
- return tokenValue || value;
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
- const validProperties = chain.cachedValidProperties;
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
- if (atomicOptimizer.options.enabled) {
750
+ // Use the injected atomic optimizer
751
+ if (atomicOptimizer && atomicOptimizer.options.enabled) {
718
752
  const result = atomicOptimizer.optimize(styleObjs);
719
- chain.cssOutput = result.css;
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 result.css;
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
- if (atomicOptimizer.options.enabled) {
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
- atomicOptimizer,
1006
+ setAtomicOptimizer, // Export this for dependency injection
1007
+ atomicOptimizer, // Will be null until set
961
1008
  recipe
962
1009
  };