chaincss 2.0.7 → 2.1.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/CHANGELOG.md +30 -0
- package/CODE_OF_CONDUCT.md +21 -0
- package/CONTRIBUTING.md +28 -0
- package/README.md +455 -226
- package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
- package/demo/index.html +16 -0
- package/demo/package.json +20 -0
- package/demo/src/App.tsx +117 -0
- package/demo/src/chaincss-barrel.ts +9 -0
- package/demo/src/main.tsx +8 -0
- package/demo/src/styles.chain.ts +300 -0
- package/demo/vite.config.ts +46 -0
- package/dist/cli/commands/build.d.ts +0 -1
- package/dist/cli/commands/cache.d.ts +1 -0
- package/dist/cli/commands/init.d.ts +6 -3
- package/dist/cli/commands/timeline.d.ts +0 -1
- package/dist/cli/commands/watch.d.ts +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +3213 -5296
- package/dist/cli/types.d.ts +51 -20
- package/dist/cli/utils/config-loader.d.ts +0 -1
- package/dist/cli/utils/file-utils.d.ts +27 -3
- package/dist/cli/utils/logger.d.ts +0 -1
- package/dist/compiler/Chain.d.ts +215 -0
- package/dist/compiler/animations.d.ts +76 -0
- package/dist/compiler/atomic-optimizer.d.ts +47 -12
- package/dist/compiler/breakpoints.d.ts +46 -0
- package/dist/compiler/btt.d.ts +36 -60
- package/dist/compiler/cache-manager.d.ts +58 -4
- package/dist/compiler/commonProps.d.ts +0 -1
- package/dist/compiler/content-addressable-cache.d.ts +78 -0
- package/dist/compiler/helpers.d.ts +54 -0
- package/dist/compiler/index.d.ts +16 -9
- package/dist/compiler/index.js +4450 -4316
- package/dist/compiler/prefixer.d.ts +17 -1
- package/dist/compiler/shorthands.d.ts +28 -0
- package/dist/compiler/suggestions.d.ts +43 -0
- package/dist/compiler/theme-contract.d.ts +16 -27
- package/dist/compiler/token-resolver.d.ts +69 -0
- package/dist/compiler/tokens.d.ts +33 -8
- package/dist/core/auto-detector.d.ts +34 -0
- package/dist/core/common-utils.d.ts +97 -0
- package/dist/core/compiler.d.ts +63 -23
- package/dist/core/constants.d.ts +137 -36
- package/dist/core/smart-chain.d.ts +3 -0
- package/dist/core/types.d.ts +122 -15
- package/dist/core/utils.d.ts +134 -17
- package/dist/index.d.ts +52 -8
- package/dist/index.js +7090 -5578
- package/dist/plugins/vite.d.ts +7 -5
- package/dist/plugins/vite.js +2964 -25641
- package/dist/plugins/webpack.d.ts +24 -1
- package/dist/plugins/webpack.js +209 -72
- package/dist/runtime/Chain.d.ts +32 -0
- package/dist/runtime/auto-hooks.d.ts +11 -0
- package/dist/runtime/hmr.d.ts +22 -2
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +3648 -301
- package/dist/runtime/injector.d.ts +39 -72
- package/dist/runtime/react.d.ts +17 -12
- package/dist/runtime/svelte.d.ts +15 -0
- package/dist/runtime/types.d.ts +126 -4
- package/dist/runtime/utils.d.ts +0 -1
- package/dist/runtime/vue.d.ts +34 -14
- package/package.json +59 -66
- package/src/cli/commands/build.ts +133 -0
- package/src/cli/commands/cache.ts +371 -0
- package/src/cli/commands/init.ts +230 -0
- package/src/cli/commands/timeline.ts +435 -0
- package/src/cli/commands/watch.ts +211 -0
- package/src/cli/index.ts +226 -0
- package/src/cli/types.ts +100 -0
- package/src/cli/utils/config-loader.ts +174 -0
- package/src/cli/utils/file-utils.ts +139 -0
- package/src/cli/utils/logger.ts +74 -0
- package/src/compiler/Chain.ts +831 -0
- package/src/compiler/animations.ts +517 -0
- package/src/compiler/atomic-optimizer.ts +786 -0
- package/src/compiler/breakpoints.ts +347 -0
- package/src/compiler/btt.ts +1147 -0
- package/src/compiler/cache-manager.ts +446 -0
- package/src/compiler/commonProps.ts +18 -0
- package/src/compiler/content-addressable-cache.ts +478 -0
- package/src/compiler/helpers.ts +407 -0
- package/src/compiler/index.ts +72 -0
- package/src/compiler/prefixer.ts +724 -0
- package/src/compiler/shorthands.ts +558 -0
- package/src/compiler/suggestions.ts +436 -0
- package/src/compiler/theme-contract.ts +197 -0
- package/src/compiler/token-resolver.ts +241 -0
- package/src/compiler/tokens.ts +612 -0
- package/src/core/auto-detector.ts +187 -0
- package/src/core/common-utils.ts +423 -0
- package/src/core/compiler.ts +835 -0
- package/src/core/constants.ts +424 -0
- package/src/core/index.ts +107 -0
- package/src/core/smart-chain.ts +163 -0
- package/src/core/types.ts +257 -0
- package/src/core/utils.ts +598 -0
- package/src/index.ts +208 -0
- package/src/plugins/vite.d.ts +316 -0
- package/src/plugins/vite.ts +424 -0
- package/src/plugins/webpack.d.ts +289 -0
- package/src/plugins/webpack.ts +416 -0
- package/src/runtime/Chain.ts +242 -0
- package/src/runtime/auto-hooks.tsx +127 -0
- package/src/runtime/auto-vue.ts +72 -0
- package/src/runtime/hmr.ts +212 -0
- package/src/runtime/index.ts +82 -0
- package/src/runtime/injector.ts +273 -0
- package/src/runtime/react.tsx +269 -0
- package/src/runtime/svelte.ts +15 -0
- package/src/runtime/types.ts +256 -0
- package/src/runtime/utils.ts +128 -0
- package/src/runtime/vite-env.d.ts +120 -0
- package/src/runtime/vue.ts +231 -0
- package/tsconfig.build.json +41 -0
- package/tsconfig.json +25 -0
- package/tsconfig.runtimes.json +18 -0
- package/dist/cli/cli.cjs +0 -7
- package/dist/cli/commands/build.d.ts.map +0 -1
- package/dist/cli/commands/compile.d.ts +0 -3
- package/dist/cli/commands/compile.d.ts.map +0 -1
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/timeline.d.ts.map +0 -1
- package/dist/cli/commands/watch.d.ts.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/types.d.ts.map +0 -1
- package/dist/cli/utils/config-loader.d.ts.map +0 -1
- package/dist/cli/utils/file-utils.d.ts.map +0 -1
- package/dist/cli/utils/logger.d.ts.map +0 -1
- package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
- package/dist/compiler/btt.d.ts.map +0 -1
- package/dist/compiler/cache-manager.d.ts.map +0 -1
- package/dist/compiler/commonProps.d.ts.map +0 -1
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/prefixer.d.ts.map +0 -1
- package/dist/compiler/theme-contract.d.ts.map +0 -1
- package/dist/compiler/tokens.d.ts.map +0 -1
- package/dist/compiler/types.d.ts +0 -57
- package/dist/compiler/types.d.ts.map +0 -1
- package/dist/core/compiler.d.ts.map +0 -1
- package/dist/core/constants.d.ts.map +0 -1
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/utils.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/plugins/vite.d.ts.map +0 -1
- package/dist/plugins/webpack.d.ts.map +0 -1
- package/dist/runtime/hmr.d.ts.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/injector.d.ts.map +0 -1
- package/dist/runtime/react.d.ts.map +0 -1
- package/dist/runtime/react.js +0 -324
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/utils.d.ts.map +0 -1
- package/dist/runtime/vue.d.ts.map +0 -1
- package/dist/runtime/vue.js +0 -286
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// src/runtime/injector.ts (fixed version)
|
|
2
|
+
|
|
3
|
+
import { shorthandMap, macros } from '../compiler/shorthands.js';
|
|
4
|
+
import { processStyleObject } from '../core/common-utils.js';
|
|
5
|
+
|
|
6
|
+
const TOKEN_V2_KEY = '__CHAINCSS_V2_TOKENS__';
|
|
7
|
+
|
|
8
|
+
export interface StyleDefinition {
|
|
9
|
+
selectors?: string[];
|
|
10
|
+
hover?: Record<string, string | number>;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TokenStore {
|
|
15
|
+
colors?: Record<string, string>;
|
|
16
|
+
spacing?: Record<string, string>;
|
|
17
|
+
typography?: Record<string, any>;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class StyleInjector {
|
|
22
|
+
private styleElement: HTMLStyleElement | null = null;
|
|
23
|
+
private injectedHashes = new Set<string>();
|
|
24
|
+
private moduleMap = new Map<string, Set<string>>();
|
|
25
|
+
private debugMode = false;
|
|
26
|
+
|
|
27
|
+
private get tokenStore(): TokenStore {
|
|
28
|
+
// Handle non-browser environments (SSR)
|
|
29
|
+
if (typeof window === 'undefined') {
|
|
30
|
+
return (this as any)._internalFallbackStore || {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Defensive initialization
|
|
34
|
+
if (!(window as any)[TOKEN_V2_KEY]) {
|
|
35
|
+
Object.defineProperty(window, TOKEN_V2_KEY, {
|
|
36
|
+
value: {},
|
|
37
|
+
writable: true,
|
|
38
|
+
enumerable: false,
|
|
39
|
+
configurable: true
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (window as any)[TOKEN_V2_KEY];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
if (typeof document !== 'undefined') {
|
|
48
|
+
const existing = document.getElementById('chaincss-runtime-styles');
|
|
49
|
+
if (existing) {
|
|
50
|
+
this.styleElement = existing as HTMLStyleElement;
|
|
51
|
+
} else {
|
|
52
|
+
const style = document.createElement('style');
|
|
53
|
+
style.id = 'chaincss-runtime-styles';
|
|
54
|
+
style.setAttribute('data-chaincss', 'runtime');
|
|
55
|
+
document.head.appendChild(style);
|
|
56
|
+
this.styleElement = style;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Enable debug logging
|
|
63
|
+
*/
|
|
64
|
+
enableDebug(enable: boolean = true): void {
|
|
65
|
+
this.debugMode = enable;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Set global tokens (e.g., brand colors, spacing scales)
|
|
70
|
+
*/
|
|
71
|
+
setTokens(tokens: TokenStore): void {
|
|
72
|
+
if (this.debugMode) {
|
|
73
|
+
console.log('[ChainCSS] Tokens set:', Object.keys(tokens));
|
|
74
|
+
}
|
|
75
|
+
Object.assign(this.tokenStore, tokens);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a token value by path
|
|
80
|
+
*/
|
|
81
|
+
getToken(path: string): any {
|
|
82
|
+
const parts = path.split('.');
|
|
83
|
+
let current: any = this.tokenStore;
|
|
84
|
+
for (const part of parts) {
|
|
85
|
+
if (current && current[part] !== undefined) {
|
|
86
|
+
current = current[part];
|
|
87
|
+
} else {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolves $variables within a string using the tokenStore
|
|
96
|
+
*/
|
|
97
|
+
resolveToken(value: any): any {
|
|
98
|
+
if (typeof value !== 'string') return value;
|
|
99
|
+
|
|
100
|
+
// Exact match: "$colors.primary"
|
|
101
|
+
if (value.startsWith('$')) {
|
|
102
|
+
const tokenValue = this.getToken(value.slice(1));
|
|
103
|
+
return tokenValue !== undefined ? tokenValue : value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Partial match: "1px solid $colors.primary"
|
|
107
|
+
return value.replace(/\$([a-zA-Z0-9.-]+)/g, (match, path) => {
|
|
108
|
+
const tokenValue = this.getToken(path);
|
|
109
|
+
return tokenValue !== undefined ? String(tokenValue) : match;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private generateCSS(style: StyleDefinition, className: string): string {
|
|
114
|
+
let css = '';
|
|
115
|
+
const selector = `.${className}`;
|
|
116
|
+
|
|
117
|
+
// Process style object (handles shorthands and tokens)
|
|
118
|
+
const mainBody = processStyleObject(style, this.tokenStore, { useTokens: true, debug: false });
|
|
119
|
+
if (mainBody && Object.keys(mainBody).length > 0) {
|
|
120
|
+
let rules = '';
|
|
121
|
+
for (const [prop, val] of Object.entries(mainBody)) {
|
|
122
|
+
rules += ` ${prop}: ${val};\n`;
|
|
123
|
+
}
|
|
124
|
+
css += `${selector} {\n${rules}}\n`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle Hover
|
|
128
|
+
if (style.hover) {
|
|
129
|
+
const hoverBody = processStyleObject(style.hover, this.tokenStore, { useTokens: true, debug: false });
|
|
130
|
+
if (hoverBody && Object.keys(hoverBody).length > 0) {
|
|
131
|
+
let rules = '';
|
|
132
|
+
for (const [prop, val] of Object.entries(hoverBody)) {
|
|
133
|
+
rules += ` ${prop}: ${val};\n`;
|
|
134
|
+
}
|
|
135
|
+
css += `${selector}:hover {\n${rules}}\n`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return css;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
injectMultiple(styles: Record<string, StyleDefinition>, moduleId?: string): Record<string, string> {
|
|
143
|
+
const result: Record<string, string> = {};
|
|
144
|
+
let newCSS = '';
|
|
145
|
+
const moduleClasses = new Set<string>();
|
|
146
|
+
|
|
147
|
+
for (const [name, style] of Object.entries(styles)) {
|
|
148
|
+
const className = name;
|
|
149
|
+
result[name] = className;
|
|
150
|
+
|
|
151
|
+
if (!this.injectedHashes.has(className)) {
|
|
152
|
+
const generatedCSS = this.generateCSS(style, className);
|
|
153
|
+
if (generatedCSS) {
|
|
154
|
+
newCSS += generatedCSS;
|
|
155
|
+
this.injectedHashes.add(className);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
moduleClasses.add(className);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (moduleId && moduleClasses.size > 0) {
|
|
162
|
+
this.moduleMap.set(moduleId, moduleClasses);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (newCSS) {
|
|
166
|
+
this.writeToDOM(newCSS);
|
|
167
|
+
if (this.debugMode) {
|
|
168
|
+
console.log(`[ChainCSS] Injected ${newCSS.length} bytes of CSS (${moduleId || 'anonymous'})`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
writeToDOM(css: string): void {
|
|
176
|
+
if (css && this.styleElement) {
|
|
177
|
+
this.styleElement.textContent += css;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
removeModule(moduleId: string): void {
|
|
182
|
+
const classes = this.moduleMap.get(moduleId);
|
|
183
|
+
if (!classes || !this.styleElement?.sheet) return;
|
|
184
|
+
|
|
185
|
+
const sheet = this.styleElement.sheet;
|
|
186
|
+
let removedCount = 0;
|
|
187
|
+
|
|
188
|
+
for (let i = sheet.cssRules.length - 1; i >= 0; i--) {
|
|
189
|
+
const rule = sheet.cssRules[i] as CSSStyleRule;
|
|
190
|
+
if (rule.selectorText) {
|
|
191
|
+
const match = rule.selectorText.match(/\.([a-zA-Z0-9_-]+)/);
|
|
192
|
+
if (match && classes.has(match[1])) {
|
|
193
|
+
sheet.deleteRule(i);
|
|
194
|
+
this.injectedHashes.delete(match[1]);
|
|
195
|
+
removedCount++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.moduleMap.delete(moduleId);
|
|
201
|
+
|
|
202
|
+
if (this.debugMode) {
|
|
203
|
+
console.log(`[ChainCSS] Removed ${removedCount} rules for module ${moduleId}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
removeAll(): void {
|
|
208
|
+
if (this.styleElement) {
|
|
209
|
+
this.styleElement.textContent = '';
|
|
210
|
+
this.injectedHashes.clear();
|
|
211
|
+
this.moduleMap.clear();
|
|
212
|
+
|
|
213
|
+
if (this.debugMode) {
|
|
214
|
+
console.log('[ChainCSS] All runtime styles removed');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getStyleElement(): HTMLStyleElement | null {
|
|
220
|
+
return this.styleElement;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getStats(): { injectedStyles: number; modules: number } {
|
|
224
|
+
return {
|
|
225
|
+
injectedStyles: this.injectedHashes.size,
|
|
226
|
+
modules: this.moduleMap.size
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// --- SINGLETON INSTANCE ---
|
|
232
|
+
export const styleInjector = new StyleInjector();
|
|
233
|
+
|
|
234
|
+
// --- PUBLIC API ---
|
|
235
|
+
export const setTokens = (t: TokenStore) => styleInjector.setTokens(t);
|
|
236
|
+
export const compileRuntime = (s: Record<string, StyleDefinition>, moduleId?: string) =>
|
|
237
|
+
styleInjector.injectMultiple(s, moduleId);
|
|
238
|
+
export const removeRuntimeModule = (moduleId: string) => styleInjector.removeModule(moduleId);
|
|
239
|
+
export const clearRuntimeStyles = () => styleInjector.removeAll();
|
|
240
|
+
export const enableRuntimeDebug = (enabled: boolean) => styleInjector.enableDebug(enabled);
|
|
241
|
+
|
|
242
|
+
export function runRuntime(...styles: StyleDefinition[]): string {
|
|
243
|
+
let css = '';
|
|
244
|
+
for (const style of styles) {
|
|
245
|
+
if (style.selectors && style.selectors.length > 0) {
|
|
246
|
+
const combinedSelector = style.selectors.join(', ');
|
|
247
|
+
|
|
248
|
+
// Use styleInjector directly instead of 'this'
|
|
249
|
+
const mainBody = processStyleObject(style, styleInjector['tokenStore'], { useTokens: true, debug: false });
|
|
250
|
+
|
|
251
|
+
if (mainBody && Object.keys(mainBody).length > 0) {
|
|
252
|
+
let rules = '';
|
|
253
|
+
for (const [prop, val] of Object.entries(mainBody)) {
|
|
254
|
+
rules += ` ${prop}: ${val};\n`;
|
|
255
|
+
}
|
|
256
|
+
css += `${combinedSelector} {\n${rules}}\n`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (style.hover) {
|
|
260
|
+
const hoverBody = processStyleObject(style.hover, styleInjector['tokenStore'], { useTokens: true, debug: false });
|
|
261
|
+
if (hoverBody && Object.keys(hoverBody).length > 0) {
|
|
262
|
+
let rules = '';
|
|
263
|
+
for (const [prop, val] of Object.entries(hoverBody)) {
|
|
264
|
+
rules += ` ${prop}: ${val};\n`;
|
|
265
|
+
}
|
|
266
|
+
css += `${combinedSelector}:hover {\n${rules}}\n`;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
styleInjector.writeToDOM(css);
|
|
272
|
+
return css;
|
|
273
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// src/runtime/react.tsx (fixed version)
|
|
2
|
+
|
|
3
|
+
import React, { useMemo, useRef, useState, useEffect } from 'react';
|
|
4
|
+
import { compileRuntime, setTokens as setGlobalTokens, removeRuntimeModule } from './injector.js';
|
|
5
|
+
import { RuntimeChain } from './Chain.js';
|
|
6
|
+
|
|
7
|
+
export interface UseChainStylesOptions {
|
|
8
|
+
cache?: boolean;
|
|
9
|
+
namespace?: string;
|
|
10
|
+
watch?: boolean;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
ssr?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Better hash function with lower collision risk
|
|
16
|
+
function hashStyleObject(obj: Record<string, any>): string {
|
|
17
|
+
const str = JSON.stringify(obj);
|
|
18
|
+
let hash = 0;
|
|
19
|
+
for (let i = 0; i < str.length; i++) {
|
|
20
|
+
const char = str.charCodeAt(i);
|
|
21
|
+
hash = ((hash << 5) - hash) + char;
|
|
22
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
23
|
+
}
|
|
24
|
+
return Math.abs(hash).toString(36);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* React hook for ChainCSS runtime styles
|
|
29
|
+
*/
|
|
30
|
+
export function useChainStyles(
|
|
31
|
+
styles: Record<string, any>,
|
|
32
|
+
deps: any[] = [],
|
|
33
|
+
options: UseChainStylesOptions = {}
|
|
34
|
+
): Record<string, string> {
|
|
35
|
+
const { namespace = 'c', debug = false, ssr = false } = options;
|
|
36
|
+
const instanceId = useRef(Math.random().toString(36).substring(2, 7));
|
|
37
|
+
const moduleId = useRef(`chaincss-module-${instanceId.current}`);
|
|
38
|
+
const [forceUpdate, setForceUpdate] = useState(0);
|
|
39
|
+
|
|
40
|
+
// Cleanup on unmount
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
return () => {
|
|
43
|
+
if (!ssr && moduleId.current) {
|
|
44
|
+
removeRuntimeModule(moduleId.current);
|
|
45
|
+
if (debug) {
|
|
46
|
+
console.log(`[ChainCSS] Cleaned up module: ${moduleId.current}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}, [ssr]);
|
|
51
|
+
|
|
52
|
+
return useMemo(() => {
|
|
53
|
+
const finalClassMap: Record<string, string> = {};
|
|
54
|
+
const injectionBundle: Record<string, any> = {};
|
|
55
|
+
|
|
56
|
+
for (const [key, styleDef] of Object.entries(styles)) {
|
|
57
|
+
let styleObject: Record<string, any> = {};
|
|
58
|
+
|
|
59
|
+
if (styleDef && typeof (styleDef as any).$el === 'function') {
|
|
60
|
+
styleObject = (styleDef as any).$el();
|
|
61
|
+
if (debug) {
|
|
62
|
+
console.log(`[ChainCSS] Processing style: ${key}`, styleObject);
|
|
63
|
+
}
|
|
64
|
+
} else if (styleDef && typeof styleDef === 'object') {
|
|
65
|
+
styleObject = { ...styleDef };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Remove internal keys
|
|
69
|
+
const staticClasses = Array.isArray(styleObject._classes) ? styleObject._classes : [];
|
|
70
|
+
const internalKeys = ['catcher', 'proxy', 'useTokens', 'componentName', '_isChain', '_classes', '_name'];
|
|
71
|
+
internalKeys.forEach(k => delete styleObject[k]);
|
|
72
|
+
|
|
73
|
+
// Generate stable hash
|
|
74
|
+
const hash = hashStyleObject(styleObject);
|
|
75
|
+
const dynamicClassName = `${namespace}-${key}-${hash}`;
|
|
76
|
+
|
|
77
|
+
// Check if there are actual styles
|
|
78
|
+
const hasStyles = Object.keys(styleObject).length > 0;
|
|
79
|
+
|
|
80
|
+
if (!ssr && hasStyles) {
|
|
81
|
+
injectionBundle[dynamicClassName] = styleObject;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Combine static and dynamic classes
|
|
85
|
+
const classParts = [...staticClasses];
|
|
86
|
+
if (hasStyles) {
|
|
87
|
+
classParts.push(dynamicClassName);
|
|
88
|
+
}
|
|
89
|
+
finalClassMap[key] = classParts.join(' ').trim();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Inject all styles at once
|
|
93
|
+
if (!ssr && Object.keys(injectionBundle).length > 0) {
|
|
94
|
+
compileRuntime(injectionBundle, moduleId.current);
|
|
95
|
+
if (debug) {
|
|
96
|
+
console.log(`[ChainCSS] Injected ${Object.keys(injectionBundle).length} styles for module ${moduleId.current}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return finalClassMap;
|
|
101
|
+
}, [forceUpdate, ...deps]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Dynamic styles hook - re-runs when deps change
|
|
106
|
+
*/
|
|
107
|
+
export function useDynamicChainStyles(
|
|
108
|
+
styleFactory: () => Record<string, any>,
|
|
109
|
+
deps: any[] = [],
|
|
110
|
+
options?: UseChainStylesOptions
|
|
111
|
+
): Record<string, string> {
|
|
112
|
+
const styles = useMemo(() => styleFactory(), deps);
|
|
113
|
+
return useChainStyles(styles, deps, options);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Theme-aware styles hook
|
|
118
|
+
*/
|
|
119
|
+
export function useThemeChainStyles(
|
|
120
|
+
styles: Record<string, any> | ((theme: any) => Record<string, any>),
|
|
121
|
+
theme: any,
|
|
122
|
+
options?: UseChainStylesOptions
|
|
123
|
+
): Record<string, string> {
|
|
124
|
+
const themedStyles = useMemo(() => {
|
|
125
|
+
if (typeof styles === 'function') return styles(theme);
|
|
126
|
+
return styles;
|
|
127
|
+
}, [styles, theme]);
|
|
128
|
+
return useChainStyles(themedStyles, [theme], options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Global style injection component
|
|
133
|
+
*/
|
|
134
|
+
export function ChainCSSGlobal({ styles, tokens, children }: {
|
|
135
|
+
styles?: Record<string, any>;
|
|
136
|
+
tokens?: any;
|
|
137
|
+
children?: React.ReactNode;
|
|
138
|
+
}) {
|
|
139
|
+
if (tokens) {
|
|
140
|
+
setGlobalTokens(tokens);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (styles) {
|
|
144
|
+
useChainStyles(styles, [], { watch: true });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return <>{children}</>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Class name utility (like clsx)
|
|
152
|
+
*/
|
|
153
|
+
export function cx(...classes: (string | undefined | null | false | Record<string, boolean>)[]): string {
|
|
154
|
+
const result: string[] = [];
|
|
155
|
+
|
|
156
|
+
for (const cls of classes) {
|
|
157
|
+
if (!cls) continue;
|
|
158
|
+
if (typeof cls === 'string') {
|
|
159
|
+
result.push(cls);
|
|
160
|
+
} else if (typeof cls === 'object') {
|
|
161
|
+
for (const [key, value] of Object.entries(cls)) {
|
|
162
|
+
if (value) result.push(key);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result.join(' ');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* HOC for class components
|
|
172
|
+
*/
|
|
173
|
+
export function withChainStyles<P extends object>(
|
|
174
|
+
styles: Record<string, any> | ((props: P) => Record<string, any>),
|
|
175
|
+
options?: UseChainStylesOptions
|
|
176
|
+
) {
|
|
177
|
+
return function WrappedComponent(props: P & { chainStyles?: Record<string, string> }) {
|
|
178
|
+
const styleProps = typeof styles === 'function' ? styles(props) : styles;
|
|
179
|
+
const classNames = useChainStyles(styleProps, [], options);
|
|
180
|
+
const Component = (props as any).component || (props as any).wrappedComponent;
|
|
181
|
+
return <Component {...props} chainStyles={classNames} />;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Create a styled component (React)
|
|
187
|
+
*/
|
|
188
|
+
export function createStyledComponent<T extends keyof React.JSX.IntrinsicElements = "div">(
|
|
189
|
+
elementType: T,
|
|
190
|
+
styles: Record<string, any> | (() => Record<string, any>),
|
|
191
|
+
options?: UseChainStylesOptions
|
|
192
|
+
): React.FC<React.ComponentProps<T> & { className?: string }> {
|
|
193
|
+
const StyledComponent: React.FC<any> = (props) => {
|
|
194
|
+
const { className: additionalClassName, ...rest } = props;
|
|
195
|
+
const styleDef = typeof styles === 'function' ? styles() : styles;
|
|
196
|
+
const classNames = useChainStyles({ root: styleDef }, [], options);
|
|
197
|
+
|
|
198
|
+
const combinedClassName = cx(classNames.root, additionalClassName);
|
|
199
|
+
|
|
200
|
+
return React.createElement(elementType, {
|
|
201
|
+
...rest,
|
|
202
|
+
className: combinedClassName
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const displayName = typeof elementType === 'string' ? elementType : (elementType as any).displayName || 'Component';
|
|
207
|
+
StyledComponent.displayName = `ChainCSS.${displayName}`;
|
|
208
|
+
|
|
209
|
+
return StyledComponent;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Create multiple styled components at once
|
|
214
|
+
*/
|
|
215
|
+
export function createStyledComponents(components: Record<string, any>): Record<string, React.FC> {
|
|
216
|
+
const result: Record<string, React.FC> = {};
|
|
217
|
+
|
|
218
|
+
for (const [name, config] of Object.entries(components)) {
|
|
219
|
+
const { element = 'div', styles, options } = config as any;
|
|
220
|
+
result[name] = createStyledComponent(element, styles, options);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* CSS-in-JS hook with computed styles
|
|
228
|
+
*/
|
|
229
|
+
export function useComputedStyles<T extends Record<string, any>>(
|
|
230
|
+
styles: (props: T) => Record<string, any>,
|
|
231
|
+
props: T,
|
|
232
|
+
deps: any[] = [],
|
|
233
|
+
options?: UseChainStylesOptions
|
|
234
|
+
): Record<string, string> {
|
|
235
|
+
const computedStyles = useMemo(() => styles(props), [props, ...deps]);
|
|
236
|
+
return useChainStyles(computedStyles, deps, options);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Set global tokens from React
|
|
241
|
+
*/
|
|
242
|
+
export function setTokens(tokens: any): void {
|
|
243
|
+
setGlobalTokens(tokens);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Debug utilities
|
|
248
|
+
*/
|
|
249
|
+
let debugEnabled = false;
|
|
250
|
+
|
|
251
|
+
export function enableChainCSSDebug(): void {
|
|
252
|
+
debugEnabled = true;
|
|
253
|
+
if (typeof window !== 'undefined') {
|
|
254
|
+
(window as any).__CHAINCSS_DEBUG__ = true;
|
|
255
|
+
console.log('🔍 ChainCSS Debug Mode Enabled');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function disableChainCSSDebug(): void {
|
|
260
|
+
debugEnabled = false;
|
|
261
|
+
if (typeof window !== 'undefined') {
|
|
262
|
+
(window as any).__CHAINCSS_DEBUG__ = false;
|
|
263
|
+
console.log('🔍 ChainCSS Debug Mode Disabled');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function isDebugEnabled(): boolean {
|
|
268
|
+
return debugEnabled || (typeof window !== 'undefined' && (window as any).__CHAINCSS_DEBUG__);
|
|
269
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Svelte integration — optional peer dependency
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { onDestroy, getContext, setContext } from 'svelte';
|
|
4
|
+
import { writable, derived, type Writable, type Readable, get } from 'svelte/store';
|
|
5
|
+
import { compileRuntime, styleInjector, removeRuntimeModule } from './injector.js';
|
|
6
|
+
|
|
7
|
+
export function useAtomicClasses() { return { subscribe: () => {}, get: () => ({}) }; }
|
|
8
|
+
export function cx(...args: any[]) { return args.filter(Boolean).join(' '); }
|
|
9
|
+
export function ChainCSSGlobal() { return null; }
|
|
10
|
+
export function createStyledComponent() { return () => null; }
|
|
11
|
+
export function createStyledComponents() { return {}; }
|
|
12
|
+
export function useComputedStyles() { return { subscribe: () => {}, get: () => '' }; }
|
|
13
|
+
export function provideStyleContext() {}
|
|
14
|
+
export function injectStyleContext() { return {}; }
|
|
15
|
+
export function chainStyles() { return {}; }
|