chaincss 2.0.7 → 2.1.1

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.
Files changed (159) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/CODE_OF_CONDUCT.md +21 -0
  3. package/CONTRIBUTING.md +28 -0
  4. package/README.md +455 -226
  5. package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
  6. package/demo/index.html +16 -0
  7. package/demo/package.json +20 -0
  8. package/demo/src/App.tsx +117 -0
  9. package/demo/src/chaincss-barrel.ts +9 -0
  10. package/demo/src/main.tsx +8 -0
  11. package/demo/src/styles.chain.ts +300 -0
  12. package/demo/vite.config.ts +46 -0
  13. package/dist/cli/commands/build.d.ts +0 -1
  14. package/dist/cli/commands/cache.d.ts +1 -0
  15. package/dist/cli/commands/init.d.ts +6 -3
  16. package/dist/cli/commands/timeline.d.ts +0 -1
  17. package/dist/cli/commands/watch.d.ts +0 -1
  18. package/dist/cli/index.d.ts +0 -1
  19. package/dist/cli/index.js +3213 -5296
  20. package/dist/cli/types.d.ts +51 -20
  21. package/dist/cli/utils/config-loader.d.ts +0 -1
  22. package/dist/cli/utils/file-utils.d.ts +27 -3
  23. package/dist/cli/utils/logger.d.ts +0 -1
  24. package/dist/compiler/Chain.d.ts +215 -0
  25. package/dist/compiler/animations.d.ts +76 -0
  26. package/dist/compiler/atomic-optimizer.d.ts +47 -12
  27. package/dist/compiler/breakpoints.d.ts +46 -0
  28. package/dist/compiler/btt.d.ts +36 -60
  29. package/dist/compiler/cache-manager.d.ts +58 -4
  30. package/dist/compiler/commonProps.d.ts +0 -1
  31. package/dist/compiler/content-addressable-cache.d.ts +78 -0
  32. package/dist/compiler/helpers.d.ts +54 -0
  33. package/dist/compiler/index.d.ts +16 -9
  34. package/dist/compiler/index.js +4450 -4316
  35. package/dist/compiler/prefixer.d.ts +17 -1
  36. package/dist/compiler/shorthands.d.ts +28 -0
  37. package/dist/compiler/suggestions.d.ts +43 -0
  38. package/dist/compiler/theme-contract.d.ts +16 -27
  39. package/dist/compiler/token-resolver.d.ts +69 -0
  40. package/dist/compiler/tokens.d.ts +33 -8
  41. package/dist/core/auto-detector.d.ts +34 -0
  42. package/dist/core/common-utils.d.ts +97 -0
  43. package/dist/core/compiler.d.ts +63 -23
  44. package/dist/core/constants.d.ts +137 -36
  45. package/dist/core/smart-chain.d.ts +3 -0
  46. package/dist/core/types.d.ts +122 -15
  47. package/dist/core/utils.d.ts +134 -17
  48. package/dist/index.d.ts +52 -8
  49. package/dist/index.js +7090 -5578
  50. package/dist/plugins/vite.d.ts +7 -5
  51. package/dist/plugins/vite.js +2964 -25641
  52. package/dist/plugins/webpack.d.ts +24 -1
  53. package/dist/plugins/webpack.js +209 -72
  54. package/dist/runtime/Chain.d.ts +32 -0
  55. package/dist/runtime/auto-hooks.d.ts +11 -0
  56. package/dist/runtime/hmr.d.ts +22 -2
  57. package/dist/runtime/index.d.ts +3 -2
  58. package/dist/runtime/index.js +3648 -301
  59. package/dist/runtime/injector.d.ts +39 -72
  60. package/dist/runtime/react.d.ts +17 -12
  61. package/dist/runtime/svelte.d.ts +15 -0
  62. package/dist/runtime/types.d.ts +126 -4
  63. package/dist/runtime/utils.d.ts +0 -1
  64. package/dist/runtime/vue.d.ts +34 -14
  65. package/package.json +59 -66
  66. package/src/cli/commands/build.ts +133 -0
  67. package/src/cli/commands/cache.ts +371 -0
  68. package/src/cli/commands/init.ts +230 -0
  69. package/src/cli/commands/timeline.ts +435 -0
  70. package/src/cli/commands/watch.ts +211 -0
  71. package/src/cli/index.ts +226 -0
  72. package/src/cli/types.ts +100 -0
  73. package/src/cli/utils/config-loader.ts +174 -0
  74. package/src/cli/utils/file-utils.ts +139 -0
  75. package/src/cli/utils/logger.ts +74 -0
  76. package/src/compiler/Chain.ts +831 -0
  77. package/src/compiler/animations.ts +517 -0
  78. package/src/compiler/atomic-optimizer.ts +786 -0
  79. package/src/compiler/breakpoints.ts +347 -0
  80. package/src/compiler/btt.ts +1147 -0
  81. package/src/compiler/cache-manager.ts +446 -0
  82. package/src/compiler/commonProps.ts +18 -0
  83. package/src/compiler/content-addressable-cache.ts +478 -0
  84. package/src/compiler/helpers.ts +407 -0
  85. package/src/compiler/index.ts +72 -0
  86. package/src/compiler/prefixer.ts +720 -0
  87. package/src/compiler/shorthands.ts +558 -0
  88. package/src/compiler/suggestions.ts +436 -0
  89. package/src/compiler/theme-contract.ts +197 -0
  90. package/src/compiler/token-resolver.ts +241 -0
  91. package/src/compiler/tokens.ts +612 -0
  92. package/src/core/auto-detector.ts +187 -0
  93. package/src/core/common-utils.ts +423 -0
  94. package/src/core/compiler.ts +835 -0
  95. package/src/core/constants.ts +424 -0
  96. package/src/core/index.ts +107 -0
  97. package/src/core/smart-chain.ts +163 -0
  98. package/src/core/types.ts +257 -0
  99. package/src/core/utils.ts +598 -0
  100. package/src/index.ts +208 -0
  101. package/src/plugins/vite.d.ts +316 -0
  102. package/src/plugins/vite.ts +424 -0
  103. package/src/plugins/webpack.d.ts +289 -0
  104. package/src/plugins/webpack.ts +416 -0
  105. package/src/runtime/Chain.ts +242 -0
  106. package/src/runtime/auto-hooks.tsx +127 -0
  107. package/src/runtime/auto-vue.ts +72 -0
  108. package/src/runtime/hmr.ts +212 -0
  109. package/src/runtime/index.ts +82 -0
  110. package/src/runtime/injector.ts +273 -0
  111. package/src/runtime/react.tsx +269 -0
  112. package/src/runtime/svelte.ts +15 -0
  113. package/src/runtime/types.ts +256 -0
  114. package/src/runtime/utils.ts +128 -0
  115. package/src/runtime/vite-env.d.ts +120 -0
  116. package/src/runtime/vue.ts +231 -0
  117. package/tsconfig.build.json +41 -0
  118. package/tsconfig.json +25 -0
  119. package/tsconfig.runtimes.json +18 -0
  120. package/dist/cli/cli.cjs +0 -7
  121. package/dist/cli/commands/build.d.ts.map +0 -1
  122. package/dist/cli/commands/compile.d.ts +0 -3
  123. package/dist/cli/commands/compile.d.ts.map +0 -1
  124. package/dist/cli/commands/init.d.ts.map +0 -1
  125. package/dist/cli/commands/timeline.d.ts.map +0 -1
  126. package/dist/cli/commands/watch.d.ts.map +0 -1
  127. package/dist/cli/index.d.ts.map +0 -1
  128. package/dist/cli/types.d.ts.map +0 -1
  129. package/dist/cli/utils/config-loader.d.ts.map +0 -1
  130. package/dist/cli/utils/file-utils.d.ts.map +0 -1
  131. package/dist/cli/utils/logger.d.ts.map +0 -1
  132. package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
  133. package/dist/compiler/btt.d.ts.map +0 -1
  134. package/dist/compiler/cache-manager.d.ts.map +0 -1
  135. package/dist/compiler/commonProps.d.ts.map +0 -1
  136. package/dist/compiler/index.d.ts.map +0 -1
  137. package/dist/compiler/prefixer.d.ts.map +0 -1
  138. package/dist/compiler/theme-contract.d.ts.map +0 -1
  139. package/dist/compiler/tokens.d.ts.map +0 -1
  140. package/dist/compiler/types.d.ts +0 -57
  141. package/dist/compiler/types.d.ts.map +0 -1
  142. package/dist/core/compiler.d.ts.map +0 -1
  143. package/dist/core/constants.d.ts.map +0 -1
  144. package/dist/core/index.d.ts +0 -4
  145. package/dist/core/index.d.ts.map +0 -1
  146. package/dist/core/types.d.ts.map +0 -1
  147. package/dist/core/utils.d.ts.map +0 -1
  148. package/dist/index.d.ts.map +0 -1
  149. package/dist/plugins/vite.d.ts.map +0 -1
  150. package/dist/plugins/webpack.d.ts.map +0 -1
  151. package/dist/runtime/hmr.d.ts.map +0 -1
  152. package/dist/runtime/index.d.ts.map +0 -1
  153. package/dist/runtime/injector.d.ts.map +0 -1
  154. package/dist/runtime/react.d.ts.map +0 -1
  155. package/dist/runtime/react.js +0 -324
  156. package/dist/runtime/types.d.ts.map +0 -1
  157. package/dist/runtime/utils.d.ts.map +0 -1
  158. package/dist/runtime/vue.d.ts.map +0 -1
  159. package/dist/runtime/vue.js +0 -286
@@ -0,0 +1,1147 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import https from 'https';
4
+ import chalk from 'chalk';
5
+ import { fileURLToPath } from 'url';
6
+ import { tokens as originalToken, DesignTokens } from './tokens.js';
7
+ import { COMMON_CSS_PROPERTIES } from './commonProps.js';
8
+ import type { AtomicOptimizer } from './atomic-optimizer.js';
9
+ import { shorthandMap, handleShorthand } from './shorthands.js';
10
+ import { getSuggestion } from './suggestions.js';
11
+ import { resolveToken } from './token-resolver.js';
12
+ import { currentBreakpoints, setBreakpoints } from './breakpoints.js';
13
+ import { animationPresets, createAnimation } from './animations.js';
14
+ import { helpers } from './helpers.js';
15
+ import type { AnimationConfig } from './animations.js';
16
+ import { chain, setTokenContext } from './Chain.js';
17
+
18
+ // ============================================================================
19
+ // Re-export Chain API from Chain.ts
20
+ // ============================================================================
21
+ export { setBreakpoints } from './breakpoints.js';
22
+ export { chain, enableDebug } from './Chain.js';
23
+
24
+ // ============================================================================
25
+ // Style Timeline / Diff Viewer
26
+ // ============================================================================
27
+
28
+ interface StyleSnapshot {
29
+ id: string;
30
+ timestamp: number;
31
+ selector: string;
32
+ styles: Record<string, any>;
33
+ source: string;
34
+ hash: string;
35
+ }
36
+
37
+ interface StyleChange {
38
+ id: string;
39
+ timestamp: number;
40
+ selector: string;
41
+ property: string;
42
+ oldValue: any;
43
+ newValue: any;
44
+ type: 'add' | 'remove' | 'modify';
45
+ }
46
+
47
+ let styleHistory: StyleSnapshot[] = [];
48
+ let styleChanges: StyleChange[] = [];
49
+ let timelineEnabled = false;
50
+ let currentSnapshotId = 0;
51
+
52
+ // Enable/disable timeline tracking
53
+ export function enableTimeline(enable: boolean = true): void {
54
+ timelineEnabled = enable;
55
+ if (!enable) {
56
+ styleHistory = [];
57
+ styleChanges = [];
58
+ }
59
+ }
60
+
61
+ export function getStyleHistory(): StyleSnapshot[] {
62
+ return styleHistory;
63
+ }
64
+
65
+ export function getStyleChanges(): StyleChange[] {
66
+ return styleChanges;
67
+ }
68
+
69
+ export function getStyleDiff(snapshotId1: string, snapshotId2: string): Record<string, any> {
70
+ const snapshot1 = styleHistory.find(s => s.id === snapshotId1);
71
+ const snapshot2 = styleHistory.find(s => s.id === snapshotId2);
72
+
73
+ if (!snapshot1 || !snapshot2) {
74
+ return { error: 'Snapshot not found' };
75
+ }
76
+
77
+ const diff: Record<string, any> = {
78
+ added: {},
79
+ removed: {},
80
+ modified: {}
81
+ };
82
+
83
+ // Find added and modified properties
84
+ for (const [key, value] of Object.entries(snapshot2.styles)) {
85
+ if (!(key in snapshot1.styles)) {
86
+ diff.added[key] = value;
87
+ } else if (JSON.stringify(snapshot1.styles[key]) !== JSON.stringify(value)) {
88
+ diff.modified[key] = {
89
+ old: snapshot1.styles[key],
90
+ new: value
91
+ };
92
+ }
93
+ }
94
+
95
+ // Find removed properties
96
+ for (const [key, value] of Object.entries(snapshot1.styles)) {
97
+ if (!(key in snapshot2.styles)) {
98
+ diff.removed[key] = value;
99
+ }
100
+ }
101
+
102
+ return diff;
103
+ }
104
+
105
+ function takeSnapshot(selector: string, styles: Record<string, any>, source: string): string {
106
+ if (!timelineEnabled) return '';
107
+
108
+ const hash = JSON.stringify(styles);
109
+ const existing = styleHistory.find(s => s.selector === selector && s.hash === hash);
110
+ if (existing) return existing.id;
111
+
112
+ const id = `snapshot_${currentSnapshotId++}`;
113
+ const snapshot: StyleSnapshot = {
114
+ id,
115
+ timestamp: Date.now(),
116
+ selector,
117
+ styles: { ...styles },
118
+ source,
119
+ hash
120
+ };
121
+
122
+ styleHistory.push(snapshot);
123
+
124
+ const previousSnapshot = styleHistory.slice(-2)[0];
125
+ if (previousSnapshot && previousSnapshot.selector === selector) {
126
+ for (const [key, value] of Object.entries(styles)) {
127
+ const oldValue = previousSnapshot.styles[key];
128
+ if (!(key in previousSnapshot.styles)) {
129
+ styleChanges.push({
130
+ id: `change_${Date.now()}_${Math.random()}`,
131
+ timestamp: Date.now(),
132
+ selector,
133
+ property: key,
134
+ oldValue: undefined,
135
+ newValue: value,
136
+ type: 'add'
137
+ });
138
+ } else if (JSON.stringify(oldValue) !== JSON.stringify(value)) {
139
+ styleChanges.push({
140
+ id: `change_${Date.now()}_${Math.random()}`,
141
+ timestamp: Date.now(),
142
+ selector,
143
+ property: key,
144
+ oldValue,
145
+ newValue: value,
146
+ type: 'modify'
147
+ });
148
+ }
149
+ }
150
+
151
+ for (const [key] of Object.entries(previousSnapshot.styles)) {
152
+ if (!(key in styles)) {
153
+ styleChanges.push({
154
+ id: `change_${Date.now()}_${Math.random()}`,
155
+ timestamp: Date.now(),
156
+ selector,
157
+ property: key,
158
+ oldValue: previousSnapshot.styles[key],
159
+ newValue: undefined,
160
+ type: 'remove'
161
+ });
162
+ }
163
+ }
164
+ }
165
+
166
+ return id;
167
+ }
168
+
169
+ export function exportTimeline(): string {
170
+ return JSON.stringify({
171
+ history: styleHistory,
172
+ changes: styleChanges,
173
+ exportedAt: Date.now()
174
+ }, null, 2);
175
+ }
176
+
177
+ export function clearTimeline(): void {
178
+ styleHistory = [];
179
+ styleChanges = [];
180
+ currentSnapshotId = 0;
181
+ }
182
+
183
+ // ============================================================================
184
+ // Framework Component Generators
185
+ // ============================================================================
186
+
187
+ interface ComponentInfo {
188
+ name: string;
189
+ selector: string;
190
+ styles: Record<string, any>;
191
+ propsDefinition?: Record<string, any>;
192
+ framework: 'react' | 'vue' | 'svelte' | 'solid' | 'auto';
193
+ }
194
+
195
+ function detectFrameworkFromProject(): 'react' | 'vue' | 'svelte' | 'solid' {
196
+ try {
197
+ require.resolve('react/package.json');
198
+ return 'react';
199
+ } catch (e) {}
200
+ try {
201
+ require.resolve('vue/package.json');
202
+ return 'vue';
203
+ } catch (e) {}
204
+ try {
205
+ require.resolve('svelte/package.json');
206
+ return 'svelte';
207
+ } catch (e) {}
208
+ try {
209
+ require.resolve('solid-js/package.json');
210
+ return 'solid';
211
+ } catch (e) {}
212
+ return 'react';
213
+ }
214
+
215
+ function generateReactComponent(info: ComponentInfo): string {
216
+ const propsInterface = info.propsDefinition
217
+ ? Object.entries(info.propsDefinition)
218
+ .map(([key, type]) => ` ${key}?: ${type};`)
219
+ .join('\n')
220
+ : ' [key: string]: any;';
221
+
222
+ return `// Auto-generated by ChainCSS
223
+ import React from 'react';
224
+ import styles from './${info.name}.class.js';
225
+ import './${info.name}.css';
226
+
227
+ export interface ${info.name}Props {
228
+ className?: string;
229
+ children?: React.ReactNode;
230
+ ${propsInterface}
231
+ }
232
+
233
+ export const ${info.name}: React.FC<${info.name}Props> = ({
234
+ className,
235
+ children,
236
+ ...props
237
+ }) => {
238
+ const combinedClassName = [styles.${info.selector.replace(/^\./, '')}, className]
239
+ .filter(Boolean)
240
+ .join(' ');
241
+
242
+ return (
243
+ <div className={combinedClassName} {...props}>
244
+ {children}
245
+ </div>
246
+ );
247
+ };
248
+
249
+ ${info.name}.displayName = 'ChainCSS${info.name}';
250
+
251
+ export default ${info.name};
252
+ `;
253
+ }
254
+
255
+ function generateVueComponent(info: ComponentInfo): string {
256
+ const propsDefinition = info.propsDefinition
257
+ ? Object.entries(info.propsDefinition)
258
+ .map(([key, type]) => ` ${key}: { type: ${type}, default: null },`)
259
+ .join('\n')
260
+ : '';
261
+
262
+ return `<!-- Auto-generated by ChainCSS -->
263
+ <template>
264
+ <component
265
+ :is="tag"
266
+ :class="combinedClass"
267
+ v-bind="$attrs"
268
+ >
269
+ <slot />
270
+ </component>
271
+ </template>
272
+
273
+ <script>
274
+ import styles from './${info.name}.class.js';
275
+ import './${info.name}.css';
276
+
277
+ export default {
278
+ name: 'ChainCSS${info.name}',
279
+ props: {
280
+ tag: {
281
+ type: String,
282
+ default: 'div'
283
+ },
284
+ className: {
285
+ type: String,
286
+ default: ''
287
+ },
288
+ ${propsDefinition}
289
+ },
290
+ computed: {
291
+ combinedClass() {
292
+ return [styles.${info.selector.replace(/^\./, '')}, this.className]
293
+ .filter(Boolean)
294
+ .join(' ');
295
+ }
296
+ }
297
+ };
298
+ </script>
299
+ `;
300
+ }
301
+
302
+ function generateSvelteComponent(info: ComponentInfo): string {
303
+ return `<!-- Auto-generated by ChainCSS -->
304
+ <script>
305
+ import styles from './${info.name}.class.js';
306
+ import './${info.name}.css';
307
+
308
+ export let className = '';
309
+ export let tag = 'div';
310
+
311
+ $: combinedClass = [styles.${info.selector.replace(/^\./, '')}, className]
312
+ .filter(Boolean)
313
+ .join(' ');
314
+ </script>
315
+
316
+ <svelte:element this={tag} class={combinedClass}>
317
+ <slot />
318
+ </svelte:element>
319
+ `;
320
+ }
321
+
322
+ function generateSolidComponent(info: ComponentInfo): string {
323
+ return `// Auto-generated by ChainCSS
324
+ import { splitProps } from 'solid-js';
325
+ import styles from './${info.name}.class.js';
326
+ import './${info.name}.css';
327
+
328
+ export function ${info.name}(props) {
329
+ const [local, others] = splitProps(props, ['class', 'children']);
330
+ const combinedClass = () => [styles.${info.selector.replace(/^\./, '')}, local.class]
331
+ .filter(Boolean)
332
+ .join(' ');
333
+
334
+ return (
335
+ <div class={combinedClass()} {...others}>
336
+ {local.children}
337
+ </div>
338
+ );
339
+ }
340
+ `;
341
+ }
342
+
343
+ // Main function to generate component code
344
+ export function generateComponentCode(info: ComponentInfo): string {
345
+ let framework = info.framework;
346
+ if (framework === 'auto') {
347
+ framework = detectFrameworkFromProject();
348
+ }
349
+
350
+ switch (framework) {
351
+ case 'react':
352
+ return generateReactComponent(info);
353
+ case 'vue':
354
+ return generateVueComponent(info);
355
+ case 'svelte':
356
+ return generateSvelteComponent(info);
357
+ case 'solid':
358
+ return generateSolidComponent(info);
359
+ default:
360
+ return generateReactComponent(info);
361
+ }
362
+ }
363
+
364
+ // ============================================================================
365
+ // Style Versioning / Source Maps
366
+ // ============================================================================
367
+
368
+ let enableSourceComments = true;
369
+
370
+ function getSourceLocation(): string | null {
371
+ if (!enableSourceComments) return null;
372
+
373
+ const stack = new Error().stack;
374
+ if (!stack) return null;
375
+
376
+ const stackLines = stack.split('\n');
377
+
378
+ for (let i = 0; i < stackLines.length; i++) {
379
+ const line = stackLines[i];
380
+ const match = line.match(/([^/]+\.chain\.js):(\d+):\d+/);
381
+ if (match) {
382
+ const fileName = match[1];
383
+ const lineNumber = match[2];
384
+ return `${fileName}:${lineNumber}`;
385
+ }
386
+ }
387
+
388
+ return null;
389
+ }
390
+
391
+ export function setSourceComments(enabled: boolean): void {
392
+ enableSourceComments = enabled;
393
+ }
394
+
395
+ function addSourceComment(css: string, sourceLocation: string | null): string {
396
+ if (!enableSourceComments || !sourceLocation) return css;
397
+ return `/* Generated from: ${sourceLocation} */\n${css}`;
398
+ }
399
+
400
+ // ============================================================================
401
+ // CSS Property Loading
402
+ // ============================================================================
403
+
404
+ const fetchWithHttps = (url: string): Promise<any> => {
405
+ return new Promise((resolve, reject) => {
406
+ const timeout = setTimeout(() => {
407
+ req.destroy();
408
+ reject(new Error('Request timeout'));
409
+ }, 3000);
410
+
411
+ const req = https.get(url, (response) => {
412
+ clearTimeout(timeout);
413
+ let data = '';
414
+ response.on('data', (chunk: string) => data += chunk);
415
+ response.on('end', () => {
416
+ try {
417
+ resolve(JSON.parse(data));
418
+ } catch (error) {
419
+ reject(error);
420
+ }
421
+ });
422
+ });
423
+
424
+ req.on('error', (error) => {
425
+ clearTimeout(timeout);
426
+ reject(error);
427
+ });
428
+ });
429
+ };
430
+
431
+ const loadCSSProperties = async (): Promise<string[]> => {
432
+ if (chains.cachedValidProperties !== null && chains.cachedValidProperties.length > 0) {
433
+ return chains.cachedValidProperties;
434
+ }
435
+
436
+ try {
437
+ const url = 'https://raw.githubusercontent.com/mdn/data/main/css/properties.json';
438
+ let data: any;
439
+
440
+ if (typeof fetch !== 'undefined') {
441
+ const controller = new AbortController();
442
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
443
+ const response = await fetch(url, { signal: controller.signal });
444
+ clearTimeout(timeoutId);
445
+ data = await response.json();
446
+ } else {
447
+ data = await fetchWithHttps(url);
448
+ }
449
+
450
+ const allProperties = Object.keys(data);
451
+ const baseProperties = new Set<string>();
452
+
453
+ allProperties.forEach(prop => {
454
+ const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
455
+ baseProperties.add(baseProp);
456
+ });
457
+
458
+ chains.cachedValidProperties = Array.from(baseProperties).sort();
459
+ return chains.cachedValidProperties;
460
+
461
+ } catch (error) {
462
+ chains.cachedValidProperties = COMMON_CSS_PROPERTIES;
463
+ return chains.cachedValidProperties;
464
+ }
465
+ };
466
+
467
+ // ============================================================================
468
+ // Chain Object & Properties
469
+ // ============================================================================
470
+
471
+ export interface ChainObject {
472
+ cssOutput: string;
473
+ cachedValidProperties: string[];
474
+ classMap: Record<string, string>;
475
+ atomicStats: any;
476
+ componentMap?: Map<string, any>;
477
+ initializeProperties: () => Promise<void>;
478
+ getCachedProperties: () => string[] | null;
479
+ }
480
+
481
+ export const chains: ChainObject = {
482
+ cssOutput: undefined as any,
483
+ cachedValidProperties: [],
484
+ classMap: {},
485
+ atomicStats: null,
486
+
487
+ async initializeProperties() {
488
+ if (this.cachedValidProperties && this.cachedValidProperties.length > 0) {
489
+ return;
490
+ }
491
+ const properties = await loadCSSProperties();
492
+ this.cachedValidProperties = properties;
493
+ },
494
+
495
+ getCachedProperties() {
496
+ return this.cachedValidProperties;
497
+ }
498
+ };
499
+
500
+ let atomicOptimizer: AtomicOptimizer | null = null;
501
+
502
+ export function setAtomicOptimizer(optimizer: AtomicOptimizer | null): void {
503
+ atomicOptimizer = optimizer;
504
+ }
505
+
506
+ export function configureAtomic(opts: Record<string, any>): void {
507
+ if (atomicOptimizer) {
508
+ Object.assign(atomicOptimizer.options, opts);
509
+ }
510
+ }
511
+
512
+ // ============================================================================
513
+ // Tokens
514
+ // ============================================================================
515
+
516
+ export const tokens = originalToken;
517
+
518
+ export function createTokens(tokenValues: Record<string, any>): DesignTokens {
519
+ const tokenObj = new DesignTokens(tokenValues);
520
+ // Also set the token context in Chain.ts
521
+ setTokenContext(tokenObj);
522
+ return tokenObj;
523
+ }
524
+
525
+ // ============================================================================
526
+ // AT-Rule Processing
527
+ // ============================================================================
528
+
529
+ export interface AtRule {
530
+ type: 'media' | 'keyframes' | 'font-face' | 'supports' | 'container' | 'layer' | 'counter-style' | 'property';
531
+ query?: string;
532
+ condition?: string;
533
+ name?: string;
534
+ styles?: any;
535
+ steps?: Record<string, Record<string, string>>;
536
+ properties?: Record<string, string>;
537
+ descriptors?: Record<string, string>;
538
+ }
539
+
540
+ export interface NestedRule {
541
+ selector: string;
542
+ styles: Record<string, string | number>;
543
+ }
544
+
545
+ export interface ThemeBlock {
546
+ name: string;
547
+ styles: StyleDefinition;
548
+ tokens: any;
549
+ fallback: any;
550
+ }
551
+
552
+ export interface StyleDefinition {
553
+ selectors: string[];
554
+ hover?: Record<string, string | number>;
555
+ atRules?: AtRule[];
556
+ nestedRules?: NestedRule[];
557
+ themes?: ThemeBlock[];
558
+ [cssProperty: string]: any;
559
+ }
560
+
561
+ function processAtRule(rule: AtRule, parentSelectors: string[] | null = null): string {
562
+ let output = '';
563
+
564
+ switch(rule.type) {
565
+ case 'media':
566
+ output = `@media ${rule.query} {\n`;
567
+ if (rule.styles && typeof rule.styles === 'object') {
568
+ let ruleBody = '';
569
+ for (const prop in rule.styles) {
570
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
571
+ ruleBody += ` ${kebabKey}: ${rule.styles[prop]};\n`;
572
+ }
573
+ if (ruleBody.trim()) {
574
+ const selector = (parentSelectors && parentSelectors.length > 0)
575
+ ? parentSelectors.join(', ')
576
+ : '.unknown-selector';
577
+ const sourceLocation = getSourceLocation();
578
+ if (enableSourceComments && sourceLocation) {
579
+ output += ` /* Generated from: ${sourceLocation} */\n`;
580
+ }
581
+ output += ` ${selector} {\n${ruleBody} }\n`;
582
+ }
583
+ }
584
+ output += '}\n';
585
+ break;
586
+
587
+ case 'keyframes':
588
+ output = `@keyframes ${rule.name} {\n`;
589
+ for (const step in rule.steps) {
590
+ output += ` ${step} {\n`;
591
+ for (const prop in rule.steps[step]) {
592
+ if (prop !== 'selectors') {
593
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
594
+ output += ` ${kebabKey}: ${rule.steps[step][prop]};\n`;
595
+ }
596
+ }
597
+ output += ' }\n';
598
+ }
599
+ output += '}\n';
600
+ break;
601
+
602
+ case 'font-face':
603
+ output = '@font-face {\n';
604
+ for (const prop in rule.properties) {
605
+ if (prop !== 'selectors') {
606
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
607
+ output += ` ${kebabKey}: ${rule.properties[prop]};\n`;
608
+ }
609
+ }
610
+ output += '}\n';
611
+ break;
612
+
613
+ default:
614
+ // Handle other AT-rules
615
+ output = '';
616
+ break;
617
+ }
618
+
619
+ return output;
620
+ }
621
+
622
+ // ============================================================================
623
+ // Run & Compile Functions
624
+ // ============================================================================
625
+
626
+ export const run = (...args: any[]): string => {
627
+ // Validate inputs
628
+ if (args.length === 0) return '';
629
+
630
+ const validStyles = args.filter(value => value && typeof value === 'object');
631
+ if (validStyles.length === 0) return '';
632
+
633
+ let cssOutput = '';
634
+ const styleObjs: any[] = [];
635
+
636
+ args.forEach((value) => {
637
+ if (!value) return;
638
+ styleObjs.push(value);
639
+
640
+ // Standalone at-rules (keyframes, etc.)
641
+ if (value.type && !value.selectors) {
642
+ cssOutput += processAtRule(value) + '\n';
643
+ return;
644
+ }
645
+
646
+ if (value.selectors) {
647
+ let mainRuleBody = '';
648
+ let subRulesOutput = '';
649
+
650
+ for (const key in value) {
651
+ if (!value.hasOwnProperty(key)) continue;
652
+
653
+ // Skip metadata and handled special keys
654
+ if ([
655
+ 'selectors', 'atRules', 'hover', 'nestedRules', 'use', 'nest', 'themes',
656
+ '_componentName', '_generateComponent', '_framework', '_propsDefinition'
657
+ ].includes(key)) continue;
658
+
659
+ // Handle AT-rules
660
+ if (key === 'atRules' && Array.isArray(value[key])) {
661
+ value[key].forEach((rule: any) => {
662
+ subRulesOutput += processAtRule(rule, value.selectors);
663
+ });
664
+ continue;
665
+ }
666
+
667
+ // Handle Nested Rules
668
+ if (key === 'nestedRules' && Array.isArray(value[key])) {
669
+ value[key].forEach((rule: any) => {
670
+ let nestedBody = '';
671
+ for (const prop in rule.styles) {
672
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
673
+ nestedBody += ` ${kebabKey}: ${rule.styles[prop]};\n`;
674
+ }
675
+ if (nestedBody) {
676
+ subRulesOutput += `${value.selectors.join(', ')} ${rule.selector} {\n${nestedBody} }\n`;
677
+ }
678
+ });
679
+ continue;
680
+ }
681
+
682
+ // Handle Hover State
683
+ if (key === 'hover' && typeof value[key] === 'object') {
684
+ let hoverBody = '';
685
+ for (const hoverKey in value[key]) {
686
+ const kebabKey = hoverKey.replace(/([A-Z])/g, '-$1').toLowerCase();
687
+ hoverBody += ` ${kebabKey}: ${value[key][hoverKey]};\n`;
688
+ }
689
+ if (hoverBody) {
690
+ subRulesOutput += `${value.selectors.join(', ')}:hover {\n${hoverBody}}\n`;
691
+ }
692
+ continue;
693
+ }
694
+
695
+ // Standard CSS Property
696
+ const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
697
+ mainRuleBody += ` ${kebabKey}: ${value[key]};\n`;
698
+ }
699
+
700
+ if (mainRuleBody.trim()) {
701
+ cssOutput += `${value.selectors.join(', ')} {\n${mainRuleBody}}\n`;
702
+ }
703
+ cssOutput += subRulesOutput;
704
+ }
705
+ });
706
+
707
+ // Cleanup whitespace
708
+ cssOutput = cssOutput.replace(/\n{3,}/g, '\n\n').trim();
709
+
710
+ // Handle Atomic Optimization inside recipes/runs
711
+ if (atomicOptimizer && atomicOptimizer.options.enabled) {
712
+ const result = atomicOptimizer.optimize(styleObjs);
713
+ return result.css;
714
+ }
715
+
716
+ return cssOutput;
717
+ };
718
+
719
+ function generateCSSFromCollected(collected: StyleDefinition[]): string {
720
+ let css = '';
721
+ for (const style of collected) {
722
+ if (!style.selectors) continue;
723
+
724
+ let normalStyles = '';
725
+ let hoverStyles = '';
726
+
727
+ for (const [key, value] of Object.entries(style)) {
728
+ if (key === 'selectors') continue;
729
+
730
+ if (key === 'hover' && typeof value === 'object') {
731
+ for (const [hoverKey, hoverValue] of Object.entries(value)) {
732
+ const kebabKey = hoverKey.replace(/([A-Z])/g, '-$1').toLowerCase();
733
+ hoverStyles += ` ${kebabKey}: ${hoverValue};\n`;
734
+ }
735
+ } else {
736
+ const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
737
+ normalStyles += ` ${kebabKey}: ${value};\n`;
738
+ }
739
+ }
740
+
741
+ if (normalStyles) {
742
+ css += `${style.selectors.join(', ')} {\n${normalStyles}}\n`;
743
+ }
744
+ if (hoverStyles) {
745
+ css += `${style.selectors.join(', ')}:hover {\n${hoverStyles}}\n`;
746
+ }
747
+ }
748
+ return css;
749
+ }
750
+
751
+ export const compile = (obj: Record<string, StyleDefinition>): string => {
752
+ let cssString = '';
753
+ const collected: StyleDefinition[] = [];
754
+ const processedSelectors = new Set<string>();
755
+
756
+ for (const key in obj) {
757
+ if (!obj.hasOwnProperty(key)) continue;
758
+ const element = obj[key];
759
+
760
+ if (element && (element as any).variants && typeof (element as any).compileAll === 'function') {
761
+ const cleanKey = key.includes('_') ? key.split('_').pop() : key;
762
+ const recipeOutput = (element as any).compileAll(cleanKey);
763
+ cssString += recipeOutput + '\n';
764
+ continue;
765
+ }
766
+
767
+ // 1. Basic Validation
768
+ if (!element || !element.selectors || !element.selectors[0]) continue;
769
+
770
+ const selectorKey = element.selectors.join(',');
771
+ if (processedSelectors.has(selectorKey)) continue;
772
+
773
+ processedSelectors.add(selectorKey);
774
+ collected.push(element);
775
+
776
+ const sourceLocation = getSourceLocation();
777
+ let elementCSS = '';
778
+ let subRulesCSS = '';
779
+
780
+ // 2. Timeline Snapshot (Internal Debugging)
781
+ if (timelineEnabled) {
782
+ const styles: Record<string, any> = {};
783
+ for (const prop in element) {
784
+ if (!['selectors', 'atRules', 'hover', 'nestedRules', 'use', 'nest', 'themes'].includes(prop)) {
785
+ styles[prop] = element[prop];
786
+ }
787
+ }
788
+ takeSnapshot(element.selectors[0], styles, sourceLocation || 'unknown');
789
+ }
790
+
791
+ // 3. Process Standard CSS Properties
792
+ for (const prop in element) {
793
+ if (prop.startsWith('.') || prop.startsWith('&')) continue;
794
+ // Skip metadata and special blocks
795
+ if (['selectors', 'atRules', 'hover', 'use', 'nest', 'themes', 'nestedRules', '_componentName', '_generateComponent', '_framework'].includes(prop)) continue;
796
+ if (prop.startsWith('_') || !element.hasOwnProperty(prop)) continue;
797
+
798
+ const value = element[prop];
799
+ if (value === undefined || value === null) continue;
800
+
801
+ const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
802
+ elementCSS += ` ${kebabKey}: ${value};\n`;
803
+ }
804
+
805
+ // 4. Generate Main Selector Block
806
+ if (elementCSS.trim()) {
807
+ let block = `${element.selectors.join(', ')} {\n${elementCSS}}\n`;
808
+ cssString += addSourceComment(block, sourceLocation);
809
+ }
810
+
811
+ // 5. Process Hover State
812
+ if (element.hover && typeof element.hover === 'object') {
813
+ let hoverBody = '';
814
+ for (const hProp in element.hover) {
815
+ const hKebab = hProp.replace(/([A-Z])/g, '-$1').toLowerCase();
816
+ hoverBody += ` ${hKebab}: ${element.hover[hProp]};\n`;
817
+ }
818
+ if (hoverBody) {
819
+ let block = `${element.selectors.join(', ')}:hover {\n${hoverBody}}\n`;
820
+ cssString += addSourceComment(block, sourceLocation);
821
+ }
822
+ }
823
+
824
+ // 5.5 Process Nested Selectors (The missing link!)
825
+ for (const prop in element) {
826
+ // If the property starts with . or &, it's a nested selector
827
+ if ((prop.startsWith('.') || prop.startsWith('&')) && typeof element[prop] === 'object') {
828
+ const subElement = element[prop];
829
+
830
+ // Resolve selector: replace '&' with parent selector or prepend parent
831
+ const parentSelector = element.selectors[0];
832
+ const subSelector = prop.startsWith('&')
833
+ ? prop.replace('&', parentSelector)
834
+ : `${parentSelector} ${prop}`;
835
+
836
+ // Recursively compile this sub-block
837
+ // We wrap it in a mock StyleDefinition object so compile can eat it
838
+ cssString += compile({
839
+ [subSelector]: {
840
+ selectors: [subSelector],
841
+ ...subElement
842
+ }
843
+ }) + '\n';
844
+ }
845
+ }
846
+
847
+ // 6. Process At-Rules (Media Queries, Keyframes)
848
+ if (element.atRules && Array.isArray(element.atRules)) {
849
+ element.atRules.forEach((rule: AtRule) => {
850
+ subRulesCSS += processAtRule(rule, element.selectors);
851
+ });
852
+ }
853
+
854
+ // 7. Process Themes
855
+ if (element.themes && Array.isArray(element.themes)) {
856
+ element.themes.forEach((theme: ThemeBlock) => {
857
+ if (theme.styles) {
858
+ let themeCSS = '';
859
+ for (const tProp in theme.styles) {
860
+ if (tProp === 'selectors') continue;
861
+ const tKebab = tProp.replace(/([A-Z])/g, '-$1').toLowerCase();
862
+ themeCSS += ` ${tKebab}: ${theme.styles[tProp]};\n`;
863
+ }
864
+ if (themeCSS) {
865
+ let block = `${theme.styles.selectors?.join(', ') || element.selectors.join(', ')} {\n${themeCSS}}\n`;
866
+ subRulesCSS += addSourceComment(block, sourceLocation);
867
+ }
868
+ }
869
+ });
870
+ }
871
+
872
+ cssString += subRulesCSS;
873
+ }
874
+
875
+ // 8. Handle Atomic Optimization or Final Output
876
+ if (atomicOptimizer && atomicOptimizer.options.enabled) {
877
+ const result = atomicOptimizer.optimize(collected);
878
+ chains.cssOutput = result.css;
879
+ return result.css;
880
+ }
881
+
882
+ chains.cssOutput = cssString.trim();
883
+ return chains.cssOutput;
884
+ };
885
+
886
+ // ============================================================================
887
+ // Recipe System
888
+ // ============================================================================
889
+
890
+ export interface RecipeOptions<TVariants extends Record<string, Record<string, any>>> {
891
+ base?: StyleDefinition | (() => StyleDefinition);
892
+ variants?: TVariants;
893
+ defaultVariants?: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>;
894
+ compoundVariants?: Array<{
895
+ variants: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>;
896
+ style: StyleDefinition | (() => StyleDefinition);
897
+ }>;
898
+ }
899
+
900
+ export type Recipe<TVariants extends Record<string, Record<string, any>>> = {
901
+ (selection?: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>): StyleDefinition;
902
+ variants: TVariants;
903
+ defaultVariants: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>;
904
+ base: StyleDefinition;
905
+ getAllVariants: () => Array<Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>>;
906
+ compileAll: () => string;
907
+ getVariantClassNames: () => Record<string, string>;
908
+ };
909
+
910
+ export function recipe<TVariants extends Record<string, Record<string, any>>>(
911
+ options: RecipeOptions<TVariants>
912
+ ): Recipe<TVariants> {
913
+ const {
914
+ base,
915
+ variants = {} as TVariants,
916
+ defaultVariants = {},
917
+ compoundVariants = []
918
+ } = options;
919
+
920
+ const baseStyle = typeof base === 'function' ? (base as () => StyleDefinition)() : base;
921
+ const variantStyles: Record<string, Record<string, StyleDefinition>> = {};
922
+
923
+ for (const [variantName, variantMap] of Object.entries(variants)) {
924
+ variantStyles[variantName] = {};
925
+ for (const [variantKey, variantStyle] of Object.entries(variantMap as Record<string, any>)) {
926
+ variantStyles[variantName][variantKey] = typeof variantStyle === 'function'
927
+ ? (variantStyle as () => StyleDefinition)()
928
+ : variantStyle;
929
+ }
930
+ }
931
+
932
+ const compoundStyles = compoundVariants.map(cv => ({
933
+ condition: cv.variants || cv,
934
+ style: typeof cv.style === 'function' ? (cv.style as () => StyleDefinition)() : cv.style
935
+ }));
936
+
937
+ function mergeStyles(...styles: (StyleDefinition | undefined)[]): StyleDefinition {
938
+ const merged: StyleDefinition = { selectors: [] } as StyleDefinition;
939
+ for (const style of styles) {
940
+ if (!style) continue;
941
+ for (const [key, value] of Object.entries(style)) {
942
+ if (key === 'selectors') {
943
+ // Prevent duplicate selectors
944
+ const newSelectors = Array.isArray(value) ? value : [value];
945
+ merged.selectors = [...new Set([...(merged.selectors || []), ...newSelectors])];
946
+ } else if (key === 'hover' && typeof value === 'object') {
947
+ if (!merged.hover) merged.hover = {};
948
+ Object.assign(merged.hover, value);
949
+ } else if (key !== 'selectors') {
950
+ (merged as any)[key] = value;
951
+ }
952
+ }
953
+ }
954
+ return merged;
955
+ }
956
+
957
+ function pick(variantSelection: Partial<Record<keyof TVariants, any>> = {}): StyleDefinition {
958
+ const selected = { ...defaultVariants, ...variantSelection } as Record<string, any>;
959
+ const stylesToMerge: StyleDefinition[] = [];
960
+
961
+ if (baseStyle) stylesToMerge.push(baseStyle);
962
+ for (const [variantName, variantValue] of Object.entries(selected)) {
963
+ const variantStyle = variantStyles[variantName]?.[variantValue];
964
+ if (variantStyle) stylesToMerge.push(variantStyle);
965
+ }
966
+ for (const cv of compoundStyles) {
967
+ const matches = Object.entries(cv.condition).every(
968
+ ([key, value]) => selected[key] === value
969
+ );
970
+ if (matches && cv.style) stylesToMerge.push(cv.style);
971
+ }
972
+
973
+ const merged = mergeStyles(...stylesToMerge);
974
+ let styleBuilder: any = chain();
975
+
976
+ for (const [prop, value] of Object.entries(merged)) {
977
+ if (prop === 'selectors' || prop === 'hover') continue;
978
+ if (styleBuilder[prop]) {
979
+ styleBuilder = styleBuilder[prop](value);
980
+ }
981
+ }
982
+
983
+ if (merged.hover) {
984
+ styleBuilder = styleBuilder.hover();
985
+ for (const [hoverProp, hoverValue] of Object.entries(merged.hover)) {
986
+ if (styleBuilder[hoverProp]) {
987
+ styleBuilder = styleBuilder[hoverProp](hoverValue);
988
+ }
989
+ }
990
+ styleBuilder = styleBuilder.end();
991
+ }
992
+
993
+ const selectors = merged.selectors || [];
994
+ return styleBuilder.$el(...selectors);
995
+ }
996
+
997
+ (pick as any).variants = variants;
998
+ (pick as any).defaultVariants = defaultVariants;
999
+ (pick as any).base = baseStyle;
1000
+
1001
+ (pick as any).getAllVariants = (): Array<Partial<Record<keyof TVariants, any>>> => {
1002
+ const result: Array<Partial<Record<keyof TVariants, any>>> = [];
1003
+ const variantKeys = Object.keys(variants) as (keyof TVariants)[];
1004
+
1005
+ function generate(current: Partial<Record<keyof TVariants, any>>, index: number): void {
1006
+ if (index === variantKeys.length) {
1007
+ result.push({ ...current });
1008
+ return;
1009
+ }
1010
+ const variantName = variantKeys[index];
1011
+ for (const variantValue of Object.keys(variants[variantName] as Record<string, any>)) {
1012
+ current[variantName] = variantValue as any;
1013
+ generate(current, index + 1);
1014
+ }
1015
+ }
1016
+
1017
+ generate({}, 0);
1018
+ return result;
1019
+ };
1020
+
1021
+ // Get class names for all variants (useful for component libraries)
1022
+ (pick as any).getVariantClassNames = (): Record<string, string> => {
1023
+ const allVariants = (pick as any).getAllVariants();
1024
+ const classNames: Record<string, string> = {};
1025
+
1026
+ for (const variant of allVariants) {
1027
+ const variantKey = Object.entries(variant).map(([k, v]) => `${k}-${v}`).join('_');
1028
+ const styleDef = pick(variant);
1029
+ // Extract class name from selectors
1030
+ if (styleDef.selectors && styleDef.selectors[0]) {
1031
+ classNames[variantKey] = styleDef.selectors[0].replace(/^\./, '');
1032
+ }
1033
+ }
1034
+
1035
+ return classNames;
1036
+ };
1037
+
1038
+ (pick as any).compileAll = (): string => {
1039
+ const allVariants = (pick as any).getAllVariants();
1040
+ const styles: StyleDefinition[] = [];
1041
+
1042
+ // Add base style
1043
+ if (baseStyle && baseStyle.selectors) {
1044
+ styles.push(baseStyle);
1045
+ }
1046
+
1047
+ // Add all variant styles
1048
+ for (const variant of allVariants) {
1049
+ const styleDef = pick(variant);
1050
+ if (styleDef && styleDef.selectors) {
1051
+ styles.push(styleDef);
1052
+ }
1053
+ }
1054
+
1055
+ // Also add individual variant styles for completeness
1056
+ for (const variantName of Object.keys(variants)) {
1057
+ for (const variantKey of Object.keys(variants[variantName])) {
1058
+ const variantStyle = variantStyles[variantName]?.[variantKey];
1059
+ if (variantStyle && variantStyle.selectors) {
1060
+ styles.push(variantStyle);
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ // Run compilation
1066
+ return run(...styles);
1067
+ };
1068
+
1069
+ return pick as Recipe<TVariants>;
1070
+ }
1071
+
1072
+ /**
1073
+ * The "Brain": Extracts ChainCSS calls from raw text
1074
+ */
1075
+ export const scanContent = (text: string): string[] => {
1076
+ // FIXED: Better regex for matching nested parentheses
1077
+ const regex = /(?:chain|\$t?)\(((?:[^()]|\([^()]*\))*)\)(?:\s*\.\s*[a-zA-Z0-9]+\s*\([^)]*\))*/g;
1078
+ const matches = text.match(regex) || [];
1079
+ return matches.map(m => m.replace(/\s+/g, ''));
1080
+ };
1081
+
1082
+ /**
1083
+ * The "Worker": Reads the file, uses the Brain, feeds the Optimizer
1084
+ */
1085
+ export function scanFileForStyles(
1086
+ filePath: string,
1087
+ optimizer: any,
1088
+ source: string | null = null
1089
+ ): { foundCount: number; errors: Error[] } {
1090
+ const errors: Error[] = [];
1091
+ let foundCount = 0;
1092
+
1093
+ try {
1094
+ const content = source !== null ? source : fs.readFileSync(filePath, 'utf8');
1095
+ if (!content || content.trim().length === 0) {
1096
+ return { foundCount: 0, errors };
1097
+ }
1098
+
1099
+ // FIXED: Better regex that matches nested parentheses
1100
+ const styleRegex = /(?:chain|\$)\(((?:[^()]|\([^()]*\))*)\)/g;
1101
+
1102
+ let match;
1103
+
1104
+ while ((match = styleRegex.exec(content)) !== null) {
1105
+ try {
1106
+ const styleBody = match[1].trim();
1107
+
1108
+ // Clean up quotes if it's a string inside the parens
1109
+ const cleanBody = styleBody.replace(/^['"`]|['"`]$/g, '');
1110
+
1111
+ if (cleanBody) {
1112
+ // Feed into the optimizer
1113
+ if (optimizer && typeof optimizer.trackStyles === 'function') {
1114
+ optimizer.trackStyles([{ selectors: { '&': cleanBody } }]);
1115
+ }
1116
+ foundCount++;
1117
+ }
1118
+ } catch (parseError) {
1119
+ errors.push(parseError as Error);
1120
+ if (process.env.DEBUG) {
1121
+ console.error(`[Scanner] Parse error in ${filePath}:`, parseError);
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ if (foundCount > 0 && process.env.DEBUG) {
1127
+ console.log(chalk.magenta(`[Scanner] Found ${foundCount} styles in ${filePath}`));
1128
+ }
1129
+ } catch (err) {
1130
+ errors.push(err as Error);
1131
+ if (process.env.DEBUG) {
1132
+ console.error(`[Scanner] Error processing ${filePath}:`, err);
1133
+ }
1134
+ }
1135
+
1136
+ return { foundCount, errors };
1137
+ }
1138
+
1139
+ // Initialize properties (non-blocking)
1140
+ chains.initializeProperties().catch((err: Error) => {
1141
+ if (process.env.DEBUG) {
1142
+ console.error('Failed to load CSS properties:', err.message);
1143
+ }
1144
+ });
1145
+
1146
+ // Exports
1147
+ export { atomicOptimizer, chains as chainObject };