@melcanz85/chaincss 1.9.5 → 1.10.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/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  [![npm version](https://img.shields.io/npm/v/@melcanz85/chaincss.svg)](https://www.npmjs.com/package/@melcanz85/chaincss)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
+ [![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://melcanz08.github.io/chaincss_react_website/)
8
+
7
9
  **Write CSS with JavaScript. The only CSS-in-JS library that lets you CHOOSE your runtime cost.**
8
10
 
9
11
  ChainCSS is a revolutionary CSS-in-JS solution that gives you **two powerful modes** in one package:
@@ -293,6 +295,7 @@ compile({ hello });" > chaincss/main.jcss
293
295
  }
294
296
  ```
295
297
 
298
+ See ChainCSS in action! Visit our interactive demo site - [https://melcanz08.github.io/chaincss_react_website/]](https://melcanz08.github.io/chaincss_react_website/)
296
299
 
297
300
  ## Performance Comparison
298
301
 
@@ -0,0 +1,2 @@
1
+ export * from './react-hooks.jsx';
2
+ export { $, run, compile, chain, createTokens, responsive, tokens } from './rtt.js';
@@ -1,5 +1,5 @@
1
1
  import { useMemo, useEffect, useRef, useState } from 'react';
2
- import { $, compile } from './transpiler';
2
+ import { $, compile, chain } from './rtt';
3
3
 
4
4
  // Cache for generated styles to avoid duplication
5
5
  const styleCache = new Map();
@@ -37,7 +37,7 @@ const updateStyles = (css) => {
37
37
  };
38
38
 
39
39
  // Main hook for using ChainCSS styles in React
40
- export function useChainStyles(styles, options = {}) {
40
+ export function useChainStyles(styles, deps = [], options = {}) {
41
41
  const {
42
42
  cache = true,
43
43
  namespace = 'chain',
@@ -52,10 +52,15 @@ export function useChainStyles(styles, options = {}) {
52
52
 
53
53
  // Process styles and generate CSS
54
54
  const processed = useMemo(() => {
55
- if (!styles || Object.keys(styles).length === 0) return { classNames: {}, css: '' };
55
+ // FIRST: Resolve styles if it's a function
56
+ const resolvedStyles = typeof styles === 'function' ? styles() : styles;
56
57
 
57
- // Check cache first
58
- const cacheKey = JSON.stringify(styles);
58
+ if (!resolvedStyles || Object.keys(resolvedStyles).length === 0) {
59
+ return { classNames: {}, css: '' };
60
+ }
61
+
62
+ // ✅ NOW use resolvedStyles for cache key
63
+ const cacheKey = JSON.stringify(resolvedStyles);
59
64
  if (cache && styleCache.has(cacheKey)) {
60
65
  return styleCache.get(cacheKey);
61
66
  }
@@ -64,7 +69,8 @@ export function useChainStyles(styles, options = {}) {
64
69
  const newClassNames = {};
65
70
  const compiledStyles = {};
66
71
 
67
- Object.entries(styles).forEach(([key, styleDef]) => {
72
+ // Use resolvedStyles here
73
+ Object.entries(resolvedStyles).forEach(([key, styleDef]) => {
68
74
  // Generate a unique class name
69
75
  const className = `${namespace}-${key}-${id.current}`;
70
76
 
@@ -94,7 +100,7 @@ export function useChainStyles(styles, options = {}) {
94
100
  }
95
101
 
96
102
  return result;
97
- }, [styles, namespace]);
103
+ }, [styles, namespace, id.current, ...deps]);
98
104
 
99
105
  // Update the style sheet when styles change
100
106
  useEffect(() => {
package/browser/rtt.js ADDED
@@ -0,0 +1,180 @@
1
+ import { tokens, createTokens, responsive } from '../shared/tokens.mjs';
2
+
3
+ let cssProperties = [];
4
+
5
+ // Try to import the JSON file, fallback to fetch if it fails
6
+ try {
7
+ // Dynamic import - will fail gracefully if file doesn't exist
8
+ const module = await import('../node/css-properties.json', {
9
+ assert: { type: 'json' },
10
+ // This prevents the error from breaking the build
11
+ ignore: true
12
+ });
13
+ cssProperties = module.default;
14
+ } catch (e) {
15
+ console.log('CSS properties file not found, will fetch from CDN');
16
+ }
17
+
18
+ const chain = {
19
+ cssOutput: undefined,
20
+ catcher: {},
21
+ cachedValidProperties: [],
22
+
23
+ async initializeProperties() {
24
+ if (cssProperties && cssProperties.length > 0) {
25
+ this.cachedValidProperties = cssProperties;
26
+ return;
27
+ }else{
28
+ try {
29
+ let CDNfallBackProp = [];
30
+ const response = await fetch('https://raw.githubusercontent.com/mdn/data/main/css/properties.json');
31
+ const data = await response.json();
32
+ const allProperties = Object.keys(data);
33
+
34
+ // Strip vendor prefixes and remove duplicates
35
+ const baseProperties = new Set();
36
+ allProperties.forEach(prop => {
37
+ const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
38
+ baseProperties.add(baseProp);
39
+ });
40
+ CDNfallBackProp = Array.from(baseProperties).sort();
41
+ this.cachedValidProperties = CDNfallBackProp;
42
+ } catch (error) {
43
+ console.error('Error loading from CDN:', error);
44
+ return [];
45
+ }
46
+ }
47
+ },
48
+
49
+ // Synchronous version for internal use
50
+ getCachedProperties() {
51
+ return this.cachedValidProperties;
52
+ }
53
+ };
54
+
55
+ // Initialize properties synchronously when module loads
56
+ chain.initializeProperties();
57
+
58
+ const resolveToken = (value, useTokens) => {
59
+ if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
60
+ return value;
61
+ }
62
+
63
+ const tokenPath = value.slice(1);
64
+ const tokenValue = tokens.get(tokenPath);
65
+
66
+ if (!tokenValue) {
67
+ return value;
68
+ }
69
+
70
+ return tokenValue;
71
+ };
72
+
73
+ function $(useTokens = true){
74
+ const catcher = {};
75
+
76
+ // Use cached properties if available
77
+ const validProperties = chain.cachedValidProperties;
78
+
79
+ const handler = {
80
+ get: (target, prop) => {
81
+ if (prop === 'block') {
82
+ return function(...args) {
83
+ // If no args, just return current catcher
84
+ if (args.length === 0) {
85
+ const result = { ...catcher };
86
+ // Clear catcher
87
+ Object.keys(catcher).forEach(key => delete catcher[key]);
88
+ return result;
89
+ }
90
+
91
+ // Create result with selectors
92
+ const result = {
93
+ selectors: args,
94
+ ...catcher
95
+ };
96
+
97
+ // Clear catcher
98
+ Object.keys(catcher).forEach(key => delete catcher[key]);
99
+
100
+ return result;
101
+ };
102
+ }
103
+ // Convert camelCase to kebab-case for CSS property
104
+ const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
105
+ // Validate property exists (optional) - use cached properties
106
+ if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
107
+ console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
108
+ }
109
+ // Return a function that sets the value
110
+ return function(value) {
111
+ catcher[prop] = resolveToken(value, useTokens);
112
+ return proxy;
113
+ };
114
+ }
115
+ };
116
+
117
+ // Create the proxy
118
+ const proxy = new Proxy({}, handler);
119
+
120
+ return proxy;
121
+ }
122
+
123
+ const run = (...args) => {
124
+ let cssOutput = '';
125
+
126
+ args.forEach((value) => {
127
+ if (value && value.selectors) {
128
+ let rule = `${value.selectors.join(', ')} {\n`;
129
+
130
+ // Add all properties (excluding 'selectors')
131
+ for (let key in value) {
132
+ if (key !== 'selectors' && value.hasOwnProperty(key)) {
133
+ const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
134
+ rule += ` ${kebabKey}: ${value[key]};\n`;
135
+ }
136
+ }
137
+
138
+ rule += `}\n\n`;
139
+ cssOutput += rule;
140
+ }
141
+ });
142
+
143
+ chain.cssOutput = cssOutput.trim();
144
+ return cssOutput.trim();
145
+ };
146
+
147
+ const compile = (obj) => {
148
+ let cssString = '';
149
+
150
+ for (const key in obj) {
151
+ if (obj.hasOwnProperty(key)) {
152
+ const element = obj[key];
153
+ let selectors = element.selectors || [];
154
+ let elementCSS = '';
155
+
156
+ for (let prop in element) {
157
+ if (element.hasOwnProperty(prop) && prop !== 'selectors') {
158
+ // Convert camelCase to kebab-case
159
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
160
+ elementCSS += ` ${kebabKey}: ${element[prop]};\n`;
161
+ }
162
+ }
163
+
164
+ cssString += `${selectors.join(', ')} {\n${elementCSS}}\n`;
165
+ }
166
+ }
167
+
168
+ chain.cssOutput = cssString.trim();
169
+ return cssString.trim(); // Added return for consistency
170
+ };
171
+
172
+ export {
173
+ chain,
174
+ $,
175
+ run,
176
+ compile,
177
+ tokens,
178
+ createTokens,
179
+ responsive
180
+ };
@@ -1,21 +1,19 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
- const tokenModule = require('./tokens');
4
- const tokens = tokenModule.tokens;
3
+ const https = require('https'); // Built-in, no install needed!
4
+ const { tokens, createTokens, responsive } = require('../shared/tokens.cjs');
5
5
 
6
6
  const chain = {
7
7
  cssOutput: undefined,
8
8
  catcher: {},
9
9
  cachedValidProperties: [],
10
10
 
11
- // Initialize properties synchronously
12
11
  initializeProperties() {
13
12
  try {
14
13
  const jsonPath = path.join(__dirname, 'css-properties.json');
15
14
  if (fs.existsSync(jsonPath)) {
16
15
  const data = fs.readFileSync(jsonPath, 'utf8');
17
16
  this.cachedValidProperties = JSON.parse(data);
18
-
19
17
  } else {
20
18
  console.log('⚠️ CSS properties not cached, will load on first use');
21
19
  }
@@ -24,6 +22,30 @@ const chain = {
24
22
  }
25
23
  },
26
24
 
25
+ // Helper function to fetch with https
26
+ fetchWithHttps(url) {
27
+ return new Promise((resolve, reject) => {
28
+ https.get(url, (response) => {
29
+ let data = '';
30
+
31
+ response.on('data', (chunk) => {
32
+ data += chunk;
33
+ });
34
+
35
+ response.on('end', () => {
36
+ try {
37
+ const jsonData = JSON.parse(data);
38
+ resolve(jsonData);
39
+ } catch (error) {
40
+ reject(error);
41
+ }
42
+ });
43
+ }).on('error', (error) => {
44
+ reject(error);
45
+ });
46
+ });
47
+ },
48
+
27
49
  async getCSSProperties() {
28
50
  try {
29
51
  const jsonPath = path.join(__dirname, 'css-properties.json');
@@ -36,12 +58,12 @@ const chain = {
36
58
  this.cachedValidProperties = objProp;
37
59
  return objProp;
38
60
  } catch {
61
+ // Use https instead of fetch
39
62
  const url = 'https://raw.githubusercontent.com/mdn/data/main/css/properties.json';
40
- const response = await fetch(url);
41
- const data = await response.json();
63
+ const data = await this.fetchWithHttps(url);
42
64
  const allProperties = Object.keys(data);
43
65
 
44
- // Strip vendor prefixes and remove duplicates
66
+ // Strip vendor prefixes and remove duplicates
45
67
  const baseProperties = new Set();
46
68
 
47
69
  allProperties.forEach(prop => {
@@ -65,7 +87,6 @@ const chain = {
65
87
  }
66
88
  },
67
89
 
68
- // Synchronous version for internal use
69
90
  getCachedProperties() {
70
91
  return this.cachedValidProperties;
71
92
  }
@@ -98,7 +119,7 @@ function $(useTokens = true){
98
119
  const handler = {
99
120
  get: (target, prop) => {
100
121
  if (prop === 'block') {
101
- return function(...args) {chain
122
+ return function(...args) {
102
123
  // If no args, just return current catcher
103
124
  if (args.length === 0) {
104
125
  const result = { ...catcher };
@@ -127,7 +148,7 @@ function $(useTokens = true){
127
148
  }
128
149
  // Return a function that sets the value
129
150
  return function(value) {
130
- catcher[prop] = resolveToken(value, useTokens); // ← USE IT HERE
151
+ catcher[prop] = resolveToken(value, useTokens);
131
152
  return proxy;
132
153
  };
133
154
  }
@@ -144,7 +165,7 @@ function $(useTokens = true){
144
165
  }
145
166
 
146
167
  return proxy;
147
- };
168
+ }
148
169
 
149
170
  const run = (...args) => {
150
171
  let cssOutput = '';
@@ -199,6 +220,6 @@ module.exports = {
199
220
  $,
200
221
  run,
201
222
  compile,
202
- createTokens: tokenModule.createTokens,
203
- responsive: tokenModule.responsive
223
+ createTokens,
224
+ responsive
204
225
  };
@@ -9,7 +9,7 @@ const fileCache = new Map();
9
9
  const strVal = require('./strVal.js');
10
10
  const { AtomicOptimizer } = require('./atomic-optimizer');
11
11
  const { CacheManager } = require('./cache-manager');
12
- const { $, run, compile: originalCompile, chain } = require('./transpiler');
12
+ const { $, run, compile: originalCompile, chain } = require('./btt');
13
13
 
14
14
  // Atomic optimizer instance (will be initialized after config)
15
15
  let atomicOptimizer = null;