@melcanz85/chaincss 1.8.0 → 1.9.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.
- package/README.md +298 -311
- package/atomic-optimizer.js +275 -0
- package/cache-manager.js +68 -0
- package/chaincss.js +182 -17
- package/index.js +24 -0
- package/index.react.js +4 -0
- package/package.json +27 -3
- package/prefixer.js +2 -2
- package/react-hooks.js +175 -0
- package/tokens.js +256 -0
- package/transpiler.js +24 -35
- package/types.d.ts +148 -0
- package/.github/workflows/publish.yml +0 -22
- package/publish.sh +0 -7
package/react-hooks.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { useMemo, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { $, compile } from './transpiler';
|
|
3
|
+
|
|
4
|
+
// Cache for generated styles to avoid duplication
|
|
5
|
+
const styleCache = new Map();
|
|
6
|
+
let styleSheet = null;
|
|
7
|
+
|
|
8
|
+
// Initialize style sheet (add to document head)
|
|
9
|
+
const initStyleSheet = () => {
|
|
10
|
+
if (typeof document === 'undefined') return null; // SSR safety
|
|
11
|
+
|
|
12
|
+
if (!styleSheet) {
|
|
13
|
+
// Check if already exists
|
|
14
|
+
const existing = document.getElementById('chaincss-styles');
|
|
15
|
+
if (existing) {
|
|
16
|
+
styleSheet = existing;
|
|
17
|
+
return styleSheet;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Create new style element
|
|
21
|
+
const style = document.createElement('style');
|
|
22
|
+
style.id = 'chaincss-styles';
|
|
23
|
+
style.setAttribute('data-chaincss', 'true');
|
|
24
|
+
document.head.appendChild(style);
|
|
25
|
+
styleSheet = style;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return styleSheet;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Update styles in the style sheet
|
|
32
|
+
const updateStyles = (css) => {
|
|
33
|
+
const sheet = initStyleSheet();
|
|
34
|
+
if (sheet) {
|
|
35
|
+
sheet.textContent = css;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Main hook for using ChainCSS styles in React
|
|
40
|
+
export function useChainStyles(styles, options = {}) {
|
|
41
|
+
const {
|
|
42
|
+
cache = true,
|
|
43
|
+
namespace = 'chain',
|
|
44
|
+
watch = false
|
|
45
|
+
} = options;
|
|
46
|
+
|
|
47
|
+
// Generate a unique ID for this component instance
|
|
48
|
+
const id = useRef(`chain-${Math.random().toString(36).substr(2, 9)}`);
|
|
49
|
+
|
|
50
|
+
// Store the generated class names
|
|
51
|
+
const [classNames, setClassNames] = useState({});
|
|
52
|
+
|
|
53
|
+
// Process styles and generate CSS
|
|
54
|
+
const processed = useMemo(() => {
|
|
55
|
+
if (!styles || Object.keys(styles).length === 0) return { classNames: {}, css: '' };
|
|
56
|
+
|
|
57
|
+
// Check cache first
|
|
58
|
+
const cacheKey = JSON.stringify(styles);
|
|
59
|
+
if (cache && styleCache.has(cacheKey)) {
|
|
60
|
+
return styleCache.get(cacheKey);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Generate unique class names for each style
|
|
64
|
+
const newClassNames = {};
|
|
65
|
+
const compiledStyles = {};
|
|
66
|
+
|
|
67
|
+
Object.entries(styles).forEach(([key, styleDef]) => {
|
|
68
|
+
// Generate a unique class name
|
|
69
|
+
const className = `${namespace}-${key}-${id.current}`;
|
|
70
|
+
|
|
71
|
+
// Create a style definition with the unique class
|
|
72
|
+
const styleObj = typeof styleDef === 'function'
|
|
73
|
+
? styleDef()
|
|
74
|
+
: styleDef;
|
|
75
|
+
|
|
76
|
+
// Store the class name mapping
|
|
77
|
+
newClassNames[key] = className;
|
|
78
|
+
|
|
79
|
+
// Create the style rule
|
|
80
|
+
compiledStyles[`${key}_${id.current}`] = {
|
|
81
|
+
selectors: [`.${className}`],
|
|
82
|
+
...styleObj
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Compile to CSS
|
|
87
|
+
compile(compiledStyles);
|
|
88
|
+
const css = chain.cssOutput;
|
|
89
|
+
|
|
90
|
+
const result = { classNames: newClassNames, css };
|
|
91
|
+
|
|
92
|
+
if (cache) {
|
|
93
|
+
styleCache.set(cacheKey, result);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}, [styles, namespace]);
|
|
98
|
+
|
|
99
|
+
// Update the style sheet when styles change
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (processed.css) {
|
|
102
|
+
// For simple apps, just append
|
|
103
|
+
if (!watch) {
|
|
104
|
+
const sheet = initStyleSheet();
|
|
105
|
+
if (sheet) {
|
|
106
|
+
// Remove old styles for this component
|
|
107
|
+
const existingStyles = sheet.textContent || '';
|
|
108
|
+
const styleRegex = new RegExp(`\\.[\\w-]*${id.current}[\\s\\S]*?}`, 'g');
|
|
109
|
+
const cleanedStyles = existingStyles.replace(styleRegex, '');
|
|
110
|
+
|
|
111
|
+
// Add new styles
|
|
112
|
+
sheet.textContent = cleanedStyles + processed.css;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
// For watch mode, update everything
|
|
116
|
+
updateStyles(processed.css);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Cleanup on unmount
|
|
121
|
+
return () => {
|
|
122
|
+
if (!watch && styleSheet) {
|
|
123
|
+
const existingStyles = styleSheet.textContent || '';
|
|
124
|
+
const styleRegex = new RegExp(`\\.[\\w-]*${id.current}[\\s\\S]*?}`, 'g');
|
|
125
|
+
styleSheet.textContent = existingStyles.replace(styleRegex, '');
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}, [processed.css, watch]);
|
|
129
|
+
|
|
130
|
+
return processed.classNames;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Hook for dynamic styles that depend on props/state
|
|
134
|
+
export function useDynamicChainStyles(styleFactory, deps = [], options = {}) {
|
|
135
|
+
const styles = useMemo(() => {
|
|
136
|
+
return styleFactory();
|
|
137
|
+
}, deps);
|
|
138
|
+
|
|
139
|
+
return useChainStyles(styles, options);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Hook for theme-aware styles
|
|
143
|
+
export function useThemeChainStyles(styles, theme, options = {}) {
|
|
144
|
+
const themedStyles = useMemo(() => {
|
|
145
|
+
if (typeof styles === 'function') {
|
|
146
|
+
return styles(theme);
|
|
147
|
+
}
|
|
148
|
+
return styles;
|
|
149
|
+
}, [styles, theme]);
|
|
150
|
+
|
|
151
|
+
return useChainStyles(themedStyles, options);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Component for injecting global ChainCSS styles
|
|
155
|
+
export function ChainCSSGlobal({ styles }) {
|
|
156
|
+
useChainStyles(styles, { watch: true });
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// HOC for adding ChainCSS styles to components
|
|
161
|
+
export function withChainStyles(styles, options = {}) {
|
|
162
|
+
return function WrappedComponent(props) {
|
|
163
|
+
const classNames = useChainStyles(
|
|
164
|
+
typeof styles === 'function' ? styles(props) : styles,
|
|
165
|
+
options
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return <WrappedComponent {...props} chainStyles={classNames} />;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Utility to combine multiple class names
|
|
173
|
+
export function cx(...classes) {
|
|
174
|
+
return classes.filter(Boolean).join(' ');
|
|
175
|
+
}
|
package/tokens.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
class DesignTokens {
|
|
2
|
+
constructor(tokens = {}) {
|
|
3
|
+
this.tokens = this.deepFreeze({
|
|
4
|
+
colors: {},
|
|
5
|
+
spacing: {},
|
|
6
|
+
typography: {},
|
|
7
|
+
breakpoints: {},
|
|
8
|
+
zIndex: {},
|
|
9
|
+
shadows: {},
|
|
10
|
+
borderRadius: {},
|
|
11
|
+
...tokens
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
this.flattened = this.flattenTokens(this.tokens);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Deep freeze to prevent accidental modifications
|
|
18
|
+
deepFreeze(obj) {
|
|
19
|
+
Object.keys(obj).forEach(key => {
|
|
20
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
21
|
+
this.deepFreeze(obj[key]);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return Object.freeze(obj);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Flatten nested tokens for easy access
|
|
28
|
+
flattenTokens(obj, prefix = '') {
|
|
29
|
+
return Object.keys(obj).reduce((acc, key) => {
|
|
30
|
+
const prefixed = prefix ? `${prefix}.${key}` : key;
|
|
31
|
+
|
|
32
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
33
|
+
Object.assign(acc, this.flattenTokens(obj[key], prefixed));
|
|
34
|
+
} else {
|
|
35
|
+
acc[prefixed] = obj[key];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return acc;
|
|
39
|
+
}, {});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Get token value by path (e.g., 'colors.primary')
|
|
43
|
+
get(path, defaultValue = '') {
|
|
44
|
+
return this.flattened[path] || defaultValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate CSS variables from tokens
|
|
48
|
+
toCSSVariables(prefix = 'chain') {
|
|
49
|
+
let css = ':root {\n';
|
|
50
|
+
|
|
51
|
+
Object.entries(this.flattened).forEach(([key, value]) => {
|
|
52
|
+
const varName = `--${prefix}-${key.replace(/\./g, '-')}`;
|
|
53
|
+
css += ` ${varName}: ${value};\n`;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
css += '}\n';
|
|
57
|
+
return css;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Create a theme variant
|
|
61
|
+
createTheme(name, overrides) {
|
|
62
|
+
const themeTokens = { ...this.flattened };
|
|
63
|
+
|
|
64
|
+
Object.entries(overrides).forEach(([key, value]) => {
|
|
65
|
+
if (themeTokens[key]) {
|
|
66
|
+
themeTokens[key] = value;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return new DesignTokens(this.expandTokens(themeTokens));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Expand flattened tokens back to nested structure
|
|
74
|
+
expandTokens(flattened) {
|
|
75
|
+
const result = {};
|
|
76
|
+
|
|
77
|
+
Object.entries(flattened).forEach(([key, value]) => {
|
|
78
|
+
const parts = key.split('.');
|
|
79
|
+
let current = result;
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
82
|
+
current[parts[i]] = current[parts[i]] || {};
|
|
83
|
+
current = current[parts[i]];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
current[parts[parts.length - 1]] = value;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Default tokens
|
|
94
|
+
const defaultTokens = {
|
|
95
|
+
colors: {
|
|
96
|
+
primary: '#667eea',
|
|
97
|
+
secondary: '#764ba2',
|
|
98
|
+
success: '#48bb78',
|
|
99
|
+
danger: '#f56565',
|
|
100
|
+
warning: '#ed8936',
|
|
101
|
+
info: '#4299e1',
|
|
102
|
+
light: '#f7fafc',
|
|
103
|
+
dark: '#1a202c',
|
|
104
|
+
white: '#ffffff',
|
|
105
|
+
black: '#000000',
|
|
106
|
+
gray: {
|
|
107
|
+
100: '#f7fafc',
|
|
108
|
+
200: '#edf2f7',
|
|
109
|
+
300: '#e2e8f0',
|
|
110
|
+
400: '#cbd5e0',
|
|
111
|
+
500: '#a0aec0',
|
|
112
|
+
600: '#718096',
|
|
113
|
+
700: '#4a5568',
|
|
114
|
+
800: '#2d3748',
|
|
115
|
+
900: '#1a202c'
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
spacing: {
|
|
120
|
+
0: '0',
|
|
121
|
+
1: '0.25rem',
|
|
122
|
+
2: '0.5rem',
|
|
123
|
+
3: '0.75rem',
|
|
124
|
+
4: '1rem',
|
|
125
|
+
5: '1.25rem',
|
|
126
|
+
6: '1.5rem',
|
|
127
|
+
8: '2rem',
|
|
128
|
+
10: '2.5rem',
|
|
129
|
+
12: '3rem',
|
|
130
|
+
16: '4rem',
|
|
131
|
+
20: '5rem',
|
|
132
|
+
24: '6rem',
|
|
133
|
+
32: '8rem',
|
|
134
|
+
40: '10rem',
|
|
135
|
+
48: '12rem',
|
|
136
|
+
56: '14rem',
|
|
137
|
+
64: '16rem',
|
|
138
|
+
xs: '0.5rem',
|
|
139
|
+
sm: '1rem',
|
|
140
|
+
md: '1.5rem',
|
|
141
|
+
lg: '2rem',
|
|
142
|
+
xl: '3rem',
|
|
143
|
+
'2xl': '4rem',
|
|
144
|
+
'3xl': '6rem'
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
typography: {
|
|
148
|
+
fontFamily: {
|
|
149
|
+
sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
150
|
+
serif: 'Georgia, Cambria, "Times New Roman", Times, serif',
|
|
151
|
+
mono: 'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
152
|
+
},
|
|
153
|
+
fontSize: {
|
|
154
|
+
xs: '0.75rem',
|
|
155
|
+
sm: '0.875rem',
|
|
156
|
+
base: '1rem',
|
|
157
|
+
lg: '1.125rem',
|
|
158
|
+
xl: '1.25rem',
|
|
159
|
+
'2xl': '1.5rem',
|
|
160
|
+
'3xl': '1.875rem',
|
|
161
|
+
'4xl': '2.25rem',
|
|
162
|
+
'5xl': '3rem'
|
|
163
|
+
},
|
|
164
|
+
fontWeight: {
|
|
165
|
+
hairline: '100',
|
|
166
|
+
thin: '200',
|
|
167
|
+
light: '300',
|
|
168
|
+
normal: '400',
|
|
169
|
+
medium: '500',
|
|
170
|
+
semibold: '600',
|
|
171
|
+
bold: '700',
|
|
172
|
+
extrabold: '800',
|
|
173
|
+
black: '900'
|
|
174
|
+
},
|
|
175
|
+
lineHeight: {
|
|
176
|
+
none: '1',
|
|
177
|
+
tight: '1.25',
|
|
178
|
+
snug: '1.375',
|
|
179
|
+
normal: '1.5',
|
|
180
|
+
relaxed: '1.625',
|
|
181
|
+
loose: '2'
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
breakpoints: {
|
|
186
|
+
sm: '640px',
|
|
187
|
+
md: '768px',
|
|
188
|
+
lg: '1024px',
|
|
189
|
+
xl: '1280px',
|
|
190
|
+
'2xl': '1536px'
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
zIndex: {
|
|
194
|
+
0: '0',
|
|
195
|
+
10: '10',
|
|
196
|
+
20: '20',
|
|
197
|
+
30: '30',
|
|
198
|
+
40: '40',
|
|
199
|
+
50: '50',
|
|
200
|
+
auto: 'auto',
|
|
201
|
+
dropdown: '1000',
|
|
202
|
+
sticky: '1020',
|
|
203
|
+
fixed: '1030',
|
|
204
|
+
modal: '1040',
|
|
205
|
+
popover: '1050',
|
|
206
|
+
tooltip: '1060'
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
shadows: {
|
|
210
|
+
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
211
|
+
base: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
|
|
212
|
+
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
|
|
213
|
+
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
|
214
|
+
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
215
|
+
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
|
216
|
+
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
|
|
217
|
+
none: 'none'
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
borderRadius: {
|
|
221
|
+
none: '0',
|
|
222
|
+
sm: '0.125rem',
|
|
223
|
+
base: '0.25rem',
|
|
224
|
+
md: '0.375rem',
|
|
225
|
+
lg: '0.5rem',
|
|
226
|
+
xl: '0.75rem',
|
|
227
|
+
'2xl': '1rem',
|
|
228
|
+
'3xl': '1.5rem',
|
|
229
|
+
full: '9999px'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Create and export tokens instance
|
|
234
|
+
const tokens = new DesignTokens(defaultTokens);
|
|
235
|
+
|
|
236
|
+
// Token utility functions
|
|
237
|
+
const createTokens = (customTokens) => {
|
|
238
|
+
return new DesignTokens(customTokens);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Generate responsive values
|
|
242
|
+
const responsive = (values) => {
|
|
243
|
+
if (typeof values === 'string') return values;
|
|
244
|
+
|
|
245
|
+
return Object.entries(values).map(([breakpoint, value]) => {
|
|
246
|
+
if (breakpoint === 'base') return value;
|
|
247
|
+
return `@media (min-width: ${tokens.get(`breakpoints.${breakpoint}`)}) { ${value} }`;
|
|
248
|
+
}).join(' ');
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
module.exports = {
|
|
252
|
+
tokens,
|
|
253
|
+
createTokens,
|
|
254
|
+
responsive,
|
|
255
|
+
DesignTokens
|
|
256
|
+
};
|
package/transpiler.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
+
const tokenModule = require('./tokens');
|
|
4
|
+
const tokens = tokenModule.tokens;
|
|
3
5
|
|
|
4
6
|
const chain = {
|
|
5
7
|
cssOutput: undefined,
|
|
@@ -72,7 +74,22 @@ const chain = {
|
|
|
72
74
|
// Initialize properties synchronously when module loads
|
|
73
75
|
chain.initializeProperties();
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
const resolveToken = (value, useTokens) => {
|
|
78
|
+
if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const tokenPath = value.slice(1);
|
|
83
|
+
const tokenValue = tokens.get(tokenPath);
|
|
84
|
+
|
|
85
|
+
if (!tokenValue) {
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return tokenValue;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function $(useTokens = true){
|
|
76
93
|
const catcher = {};
|
|
77
94
|
|
|
78
95
|
// Use cached properties if available
|
|
@@ -102,19 +119,16 @@ function $(){
|
|
|
102
119
|
return result;
|
|
103
120
|
};
|
|
104
121
|
}
|
|
105
|
-
|
|
106
122
|
// Convert camelCase to kebab-case for CSS property
|
|
107
|
-
const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
108
|
-
|
|
123
|
+
const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
109
124
|
// Validate property exists (optional) - use cached properties
|
|
110
125
|
if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
|
|
111
|
-
console.warn(
|
|
126
|
+
console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
|
|
112
127
|
}
|
|
113
|
-
|
|
114
128
|
// Return a function that sets the value
|
|
115
129
|
return function(value) {
|
|
116
|
-
catcher[prop] = value;
|
|
117
|
-
return proxy;
|
|
130
|
+
catcher[prop] = resolveToken(value, useTokens); // ← USE IT HERE
|
|
131
|
+
return proxy;
|
|
118
132
|
};
|
|
119
133
|
}
|
|
120
134
|
};
|
|
@@ -178,38 +192,13 @@ const compile = (obj) => {
|
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
chain.cssOutput = cssString.trim();
|
|
181
|
-
return cssString.trim();
|
|
182
195
|
};
|
|
183
196
|
|
|
184
|
-
const get = (filename) => {
|
|
185
|
-
const fileExt = path.extname(filename).toLowerCase();
|
|
186
|
-
if (fileExt !== '.jcss') {
|
|
187
|
-
throw new Error(`Import error: ${filename} must have .jcss extension`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Try to resolve the path
|
|
191
|
-
const resolvedPath = path.resolve(process.cwd(), filename);
|
|
192
|
-
|
|
193
|
-
// Check if file exists
|
|
194
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
195
|
-
throw new Error(`File not found: ${filename} (resolved to: ${resolvedPath})`);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return require(resolvedPath);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// Make chaincss available globally
|
|
202
|
-
if (typeof global !== 'undefined') {
|
|
203
|
-
global.chain = chain;
|
|
204
|
-
global.run = run;
|
|
205
|
-
global.compile = compile;
|
|
206
|
-
global.$ = $;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
197
|
module.exports = {
|
|
210
198
|
chain,
|
|
211
199
|
$,
|
|
212
200
|
run,
|
|
213
201
|
compile,
|
|
214
|
-
|
|
202
|
+
createTokens: tokenModule.createTokens,
|
|
203
|
+
responsive: tokenModule.responsive
|
|
215
204
|
};
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
|
|
3
|
+
declare module '@melcanz85/chaincss' {
|
|
4
|
+
// Style definition returned by .block()
|
|
5
|
+
export interface StyleDefinition {
|
|
6
|
+
selectors: string[];
|
|
7
|
+
[cssProperty: string]: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Base interface for CSS properties (dynamic)
|
|
11
|
+
export interface CSSPropertyBuilder {
|
|
12
|
+
[key: string]: (value: string | number) => ChainBuilder;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Special methods interface
|
|
16
|
+
export interface SpecialMethods {
|
|
17
|
+
block(...selectors: string[]): StyleDefinition;
|
|
18
|
+
token?(path: string): ChainBuilder;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ChainBuilder is the intersection of both
|
|
22
|
+
export type ChainBuilder = CSSPropertyBuilder & SpecialMethods;
|
|
23
|
+
|
|
24
|
+
// The main $ function
|
|
25
|
+
export function $(): ChainBuilder;
|
|
26
|
+
|
|
27
|
+
// Run function for inline styles
|
|
28
|
+
export function run(...styles: StyleDefinition[]): string;
|
|
29
|
+
|
|
30
|
+
// Compile function for objects
|
|
31
|
+
export function compile(styles: Record<string, StyleDefinition>): void;
|
|
32
|
+
|
|
33
|
+
// Get function for importing (VM-safe version)
|
|
34
|
+
export function get(filename: string): any;
|
|
35
|
+
|
|
36
|
+
// Chain object (internal state)
|
|
37
|
+
export const chain: {
|
|
38
|
+
cssOutput: string;
|
|
39
|
+
catcher: any;
|
|
40
|
+
cachedValidProperties: string[];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Processor function
|
|
44
|
+
export function processor(inputFile: string, outputFile: string): Promise<void>;
|
|
45
|
+
|
|
46
|
+
// Watch function
|
|
47
|
+
export function watch(inputFile: string, outputFile: string): void;
|
|
48
|
+
|
|
49
|
+
// Atomic optimizer configuration
|
|
50
|
+
export interface AtomicConfig {
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
threshold?: number;
|
|
53
|
+
naming?: 'hash' | 'readable' | 'short';
|
|
54
|
+
cache?: boolean;
|
|
55
|
+
cachePath?: string;
|
|
56
|
+
minify?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Prefixer configuration
|
|
60
|
+
export interface PrefixerConfig {
|
|
61
|
+
mode?: 'auto' | 'full';
|
|
62
|
+
browsers?: string[];
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
sourceMap?: boolean;
|
|
65
|
+
sourceMapInline?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ChainCSS configuration
|
|
69
|
+
export interface ChainCSSConfig {
|
|
70
|
+
atomic?: AtomicConfig;
|
|
71
|
+
prefixer?: PrefixerConfig;
|
|
72
|
+
sourceMaps?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Function to configure ChainCSS
|
|
76
|
+
export function configure(config: ChainCSSConfig): void;
|
|
77
|
+
|
|
78
|
+
// Atomic optimizer instance
|
|
79
|
+
export const atomicOptimizer: {
|
|
80
|
+
optimize(styles: Record<string, StyleDefinition>): string;
|
|
81
|
+
getStats(): {
|
|
82
|
+
totalStyles: number;
|
|
83
|
+
atomicStyles: number;
|
|
84
|
+
uniqueProperties: number;
|
|
85
|
+
savings?: string;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Token system types
|
|
90
|
+
export interface Tokens {
|
|
91
|
+
colors: Record<string, string | Record<string, string>>;
|
|
92
|
+
spacing: Record<string, string>;
|
|
93
|
+
typography: {
|
|
94
|
+
fontFamily: Record<string, string>;
|
|
95
|
+
fontSize: Record<string, string>;
|
|
96
|
+
fontWeight: Record<string, string>;
|
|
97
|
+
lineHeight: Record<string, string>;
|
|
98
|
+
};
|
|
99
|
+
breakpoints: Record<string, string>;
|
|
100
|
+
zIndex: Record<string, string>;
|
|
101
|
+
shadows: Record<string, string>;
|
|
102
|
+
borderRadius: Record<string, string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class DesignTokens {
|
|
106
|
+
constructor(tokens: Partial<Tokens>);
|
|
107
|
+
get(path: string, defaultValue?: string): string;
|
|
108
|
+
toCSSVariables(prefix?: string): string;
|
|
109
|
+
createTheme(name: string, overrides: Record<string, any>): DesignTokens;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const tokens: DesignTokens;
|
|
113
|
+
export function createTokens(customTokens: Partial<Tokens>): DesignTokens;
|
|
114
|
+
export function responsive(values: Record<string, string> | string): string;
|
|
115
|
+
|
|
116
|
+
// React hooks types (add to your existing declare module)
|
|
117
|
+
export interface UseChainStylesOptions {
|
|
118
|
+
cache?: boolean;
|
|
119
|
+
namespace?: string;
|
|
120
|
+
watch?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function useChainStyles(
|
|
124
|
+
styles: Record<string, any> | (() => Record<string, any>),
|
|
125
|
+
options?: UseChainStylesOptions
|
|
126
|
+
): Record<string, string>;
|
|
127
|
+
|
|
128
|
+
export function useDynamicChainStyles(
|
|
129
|
+
styleFactory: () => Record<string, any>,
|
|
130
|
+
deps?: any[],
|
|
131
|
+
options?: UseChainStylesOptions
|
|
132
|
+
): Record<string, string>;
|
|
133
|
+
|
|
134
|
+
export function useThemeChainStyles(
|
|
135
|
+
styles: Record<string, any> | ((theme: any) => Record<string, any>),
|
|
136
|
+
theme: any,
|
|
137
|
+
options?: UseChainStylesOptions
|
|
138
|
+
): Record<string, string>;
|
|
139
|
+
|
|
140
|
+
export const ChainCSSGlobal: React.FC<{ styles: Record<string, any> }>;
|
|
141
|
+
|
|
142
|
+
export function withChainStyles(
|
|
143
|
+
styles: Record<string, any> | ((props: any) => Record<string, any>),
|
|
144
|
+
options?: UseChainStylesOptions
|
|
145
|
+
): <P extends object>(Component: React.ComponentType<P>) => React.FC<P & { chainStyles?: Record<string, string> }>;
|
|
146
|
+
|
|
147
|
+
export function cx(...classes: (string | undefined | null | false)[]): string;
|
|
148
|
+
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: Publish Package
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- 'v*'
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
id-token: write
|
|
10
|
-
contents: read
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
publish:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
- uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: '24'
|
|
20
|
-
registry-url: 'https://registry.npmjs.org'
|
|
21
|
-
- run: npm ci
|
|
22
|
-
- run: npm publish
|