apexcss-cli 0.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.
@@ -0,0 +1,1934 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ApexCSS Configuration Builder
5
+ *
6
+ * This script generates SCSS configuration files from JavaScript config.
7
+ * It allows users to customize the framework via a JS config file.
8
+ *
9
+ * Usage:
10
+ * node tools/config-builder.js --config=apex.config.js
11
+ * node tools/config-builder.js --watch
12
+ *
13
+ * Options:
14
+ * --config Path to configuration file (default: src/apex.config.js)
15
+ * --output Output directory (default: src/config)
16
+ * --watch Watch for changes and rebuild automatically
17
+ * --help Show help
18
+ */
19
+
20
+ import fs from 'node:fs/promises';
21
+ import path from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+
24
+ const __filename = fileURLToPath(import.meta.url);
25
+
26
+ const LOG_PREFIX = '[apex-config-builder]';
27
+
28
+ function logInfo(message) {
29
+ console.log(`${LOG_PREFIX} INFO: ${message}`);
30
+ }
31
+
32
+ function logWarn(message) {
33
+ console.warn(`${LOG_PREFIX} WARN: ${message}`);
34
+ }
35
+
36
+ function logError(message) {
37
+ console.error(`${LOG_PREFIX} ERROR: ${message}`);
38
+ }
39
+
40
+ // Complete default configuration with all feature toggles
41
+ const defaultConfig = {
42
+ // ============================================================================
43
+ // Feature Toggles - Enable/disable utility categories
44
+ // ============================================================================
45
+ features: {
46
+ // Core Layout Utilities
47
+ display: true,
48
+ flexbox: true,
49
+ grid: true,
50
+ positioning: true,
51
+ visibility: true,
52
+
53
+ // Core Spacing Utilities
54
+ spacing: true,
55
+ sizing: true,
56
+
57
+ // Core Typography Utilities
58
+ typography: true,
59
+
60
+ // Core Visual Utilities
61
+ colors: true,
62
+ backgrounds: true,
63
+ borders: true,
64
+ shadows: true,
65
+ opacity: true,
66
+ overflow: true,
67
+ objectFit: true,
68
+
69
+ // Core Interaction Utilities
70
+ cursor: true,
71
+ transitions: true,
72
+
73
+ // Extended Layout Utilities
74
+ flexExtended: true,
75
+ gridExtended: true,
76
+ float: true,
77
+ containerQueries: true,
78
+ isolation: true,
79
+ placeItems: true,
80
+ justifyItems: true,
81
+ spaceBetween: true,
82
+ columns: true,
83
+ columnsExtended: true,
84
+
85
+ // Extended Typography Utilities
86
+ typographyExtended: true,
87
+ fontExtended: true,
88
+ letterSpacing: true,
89
+ lineHeight: true,
90
+ textAlignLast: true,
91
+ textDecorationExtended: true,
92
+ textJustify: true,
93
+ textIndent: true,
94
+ textShadow: true,
95
+ textEmphasis: true,
96
+ textOrientation: true,
97
+ textUnderline: true,
98
+ hangingPunctuation: true,
99
+ hyphenate: true,
100
+ initialLetter: true,
101
+ tabSize: true,
102
+ wordBreak: true,
103
+ wordWrap: true,
104
+ writingMode: true,
105
+ unicodeBidi: true,
106
+
107
+ // Extended Visual Utilities
108
+ backgroundExtended: true,
109
+ colorModifiers: false,
110
+ blendModes: true,
111
+ masks: true,
112
+ borderRadiusLogical: true,
113
+ ring: true,
114
+ outline: true,
115
+ appearance: true,
116
+ accentColor: true,
117
+ colorScheme: true,
118
+
119
+ // Extended Interaction Utilities
120
+ interaction: true,
121
+ userSelect: true,
122
+ willChange: true,
123
+ all: true,
124
+ caret: true,
125
+ scroll: true,
126
+ overscrollBehavior: true,
127
+ overscrollBehaviorExtended: true,
128
+ overflowExtended: true,
129
+
130
+ // Effects (Animations, Transforms, Filters)
131
+ animations: true,
132
+ transforms: true,
133
+ transforms3d: true,
134
+ filters: true,
135
+ aspectRatio: true,
136
+ imageRendering: true,
137
+ transitionBehavior: true,
138
+
139
+ // Content Utilities (Lists, Tables, Print)
140
+ list: true,
141
+ listStyleExtended: true,
142
+ table: true,
143
+ counter: true,
144
+ caption: true,
145
+ quotes: true,
146
+ orphans: true,
147
+ widows: true,
148
+ pageBreak: true,
149
+ break: true,
150
+ verticalAlign: true,
151
+
152
+ // Advanced/Specialized Utilities
153
+ arbitrary: false,
154
+ logicalProperties: true,
155
+ sizingLogical: true,
156
+ offset: true,
157
+ shapeOutside: true,
158
+ markerExtended: true,
159
+ zoom: true,
160
+ fieldSizing: true,
161
+ svg: true,
162
+ box: true,
163
+ divide: true,
164
+
165
+ // State Variants
166
+ states: true,
167
+ hover: true,
168
+ focus: true,
169
+ active: true,
170
+ disabled: true,
171
+
172
+ // Theme Support
173
+ darkMode: true,
174
+ rtl: true,
175
+ accessibility: true,
176
+ zIndex: true
177
+ },
178
+
179
+ // ============================================================================
180
+ // Breakpoints - Responsive design breakpoints
181
+ // ============================================================================
182
+ breakpoints: {
183
+ sm: '320px',
184
+ md: '768px',
185
+ lg: '1024px',
186
+ xl: '1280px',
187
+ xxl: '2560px',
188
+ xxxl: '3840px'
189
+ },
190
+
191
+ // ============================================================================
192
+ // Spacing Scale - Margin, padding, and gap values
193
+ // ============================================================================
194
+ spacing: {
195
+ 0: '0',
196
+ px: '1px',
197
+ 0.5: '0.125rem',
198
+ 1: '0.25rem',
199
+ 1.5: '0.375rem',
200
+ 2: '0.5rem',
201
+ 2.5: '0.625rem',
202
+ 3: '0.75rem',
203
+ 3.5: '0.875rem',
204
+ 4: '1rem',
205
+ 5: '1.25rem',
206
+ 6: '1.5rem',
207
+ 7: '1.75rem',
208
+ 8: '2rem',
209
+ 9: '2.25rem',
210
+ 10: '2.5rem',
211
+ 11: '2.75rem',
212
+ 12: '3rem',
213
+ 14: '3.5rem',
214
+ 16: '4rem',
215
+ 20: '5rem',
216
+ 24: '6rem',
217
+ 28: '7rem',
218
+ 32: '8rem',
219
+ 36: '9rem',
220
+ 40: '10rem',
221
+ 44: '11rem',
222
+ 48: '12rem',
223
+ 52: '13rem',
224
+ 56: '14rem',
225
+ 60: '15rem',
226
+ 64: '16rem',
227
+ 72: '18rem',
228
+ 80: '20rem',
229
+ 96: '24rem'
230
+ },
231
+
232
+ // ============================================================================
233
+ // Fractional Widths - Percentage-based width utilities (w-1/2, w-1/3, etc.)
234
+ // ============================================================================
235
+ fractionalWidths: {
236
+ // Enable/disable specific denominators
237
+ // When enabled, generates utilities like w-1/2, w-1/3, w-2/3, etc.
238
+ halves: true,
239
+ thirds: true,
240
+ quarters: true,
241
+ fifths: true,
242
+ sixths: true,
243
+ twelfths: true
244
+ },
245
+
246
+ // ============================================================================
247
+ // Column Configuration - CSS Multi-Column Layout
248
+ // ============================================================================
249
+ columns: {
250
+ // Column counts to generate utilities for
251
+ counts: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
252
+ // Column widths
253
+ widths: {
254
+ 'xs': '20rem',
255
+ 'sm': '24rem',
256
+ 'md': '28rem',
257
+ 'lg': '32rem',
258
+ 'xl': '36rem',
259
+ '2xl': '42rem',
260
+ '3xl': '48rem',
261
+ '4xl': '56rem',
262
+ '5xl': '64rem',
263
+ '6xl': '72rem',
264
+ '7xl': '80rem'
265
+ },
266
+ // Column rule styles
267
+ ruleStyles: {
268
+ 'solid': 'solid',
269
+ 'dashed': 'dashed',
270
+ 'dotted': 'dotted',
271
+ 'double': 'double',
272
+ 'none': 'none'
273
+ },
274
+ // Column rule widths
275
+ ruleWidths: {
276
+ '0': '0px',
277
+ 'default': '1px',
278
+ '2': '2px',
279
+ '4': '4px',
280
+ '8': '8px'
281
+ }
282
+ },
283
+
284
+ // ============================================================================
285
+ // Text Shadow Values
286
+ // ============================================================================
287
+ textShadow: {
288
+ 'none': 'none',
289
+ 'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
290
+ 'default': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
291
+ 'md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
292
+ 'lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
293
+ 'xl': '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
294
+ '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
295
+ 'inner': 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)'
296
+ },
297
+
298
+ // ============================================================================
299
+ // Ring Widths - Focus ring widths
300
+ // ============================================================================
301
+ ringWidths: {
302
+ '0': '0px',
303
+ '1': '1px',
304
+ '2': '2px',
305
+ '4': '4px',
306
+ '8': '8px',
307
+ 'default': '3px'
308
+ },
309
+
310
+ // ============================================================================
311
+ // Ring Colors - Focus ring colors
312
+ // ============================================================================
313
+ ringColors: {
314
+ "inherit": "inherit",
315
+ "current": "currentcolor",
316
+ "transparent": "transparent",
317
+ "white": "#fff",
318
+ "black": "#000",
319
+ "primary": "var(--color-primary-500)",
320
+ "gray": "var(--color-gray-500)"
321
+ },
322
+
323
+ // ============================================================================
324
+ // Ring Opacity Values
325
+ // ============================================================================
326
+ ringOpacity: {
327
+ "0": "0",
328
+ "5": "0.05",
329
+ "10": "0.1",
330
+ "20": "0.2",
331
+ "25": "0.25",
332
+ "30": "0.3",
333
+ "40": "0.4",
334
+ "50": "0.5",
335
+ "60": "0.6",
336
+ "70": "0.7",
337
+ "75": "0.75",
338
+ "80": "0.8",
339
+ "90": "0.9",
340
+ "95": "0.95",
341
+ "100": "1"
342
+ },
343
+
344
+ // ============================================================================
345
+ // Outline Widths - Focus outline widths
346
+ // ============================================================================
347
+ outlineWidths: {
348
+ "0": "0px",
349
+ "1": "1px",
350
+ "2": "2px",
351
+ "4": "4px",
352
+ "8": "8px",
353
+ "default": "3px"
354
+ },
355
+
356
+ // ============================================================================
357
+ // Outline Offsets
358
+ // ============================================================================
359
+ outlineOffsets: {
360
+ "0": "0px",
361
+ "1": "1px",
362
+ "2": "2px",
363
+ "4": "4px",
364
+ "8": "8px",
365
+ "default": "0px"
366
+ },
367
+
368
+ // ============================================================================
369
+ // Blend Modes - CSS blend modes for mix-blend-mode and background-blend-mode
370
+ // ============================================================================
371
+ blendModes: {
372
+ 'normal': 'normal',
373
+ 'multiply': 'multiply',
374
+ 'screen': 'screen',
375
+ 'overlay': 'overlay',
376
+ 'darken': 'darken',
377
+ 'lighten': 'lighten',
378
+ 'color-dodge': 'color-dodge',
379
+ 'color-burn': 'color-burn',
380
+ 'hard-light': 'hard-light',
381
+ 'soft-light': 'soft-light',
382
+ 'difference': 'difference',
383
+ 'exclusion': 'exclusion',
384
+ 'hue': 'hue',
385
+ 'saturation': 'saturation',
386
+ 'color': 'color',
387
+ 'luminosity': 'luminosity'
388
+ },
389
+
390
+ // ============================================================================
391
+ // Colors - OKLCH Color Scales (Perceptually uniform color space)
392
+ // Define base colors with OKLCH values, and full scales will be generated
393
+ // Format: { lightness: 0-100%, chroma: 0-0.4, hue: 0-360 }
394
+ // ============================================================================
395
+ colors: {
396
+ // Primary Blue
397
+ primary: {
398
+ hue: 250,
399
+ chroma: 0.18,
400
+ // Scale will be generated from these lightness values
401
+ lightnessScale: {
402
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
403
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
404
+ }
405
+ },
406
+ // Gray (neutral, low chroma)
407
+ gray: {
408
+ hue: 250,
409
+ chroma: 0.02,
410
+ lightnessScale: {
411
+ 50: 96, 100: 90, 200: 85, 300: 78, 400: 70,
412
+ 500: 65, 600: 55, 700: 45, 800: 35, 900: 25, 950: 18
413
+ }
414
+ },
415
+ // Success Green
416
+ success: {
417
+ hue: 145,
418
+ chroma: 0.20,
419
+ lightnessScale: {
420
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
421
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
422
+ }
423
+ },
424
+ // Warning Amber
425
+ warning: {
426
+ hue: 80,
427
+ chroma: 0.16,
428
+ lightnessScale: {
429
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
430
+ 500: 72, 600: 60, 700: 50, 800: 40, 900: 30, 950: 25
431
+ }
432
+ },
433
+ // Danger Red
434
+ danger: {
435
+ hue: 25,
436
+ chroma: 0.22,
437
+ lightnessScale: {
438
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
439
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
440
+ }
441
+ },
442
+ // Info Sky
443
+ info: {
444
+ hue: 220,
445
+ chroma: 0.14,
446
+ lightnessScale: {
447
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
448
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
449
+ }
450
+ },
451
+ // Extended palette
452
+ extended: {
453
+ blue: { hue: 250, chroma: 0.18 },
454
+ green: { hue: 145, chroma: 0.20 },
455
+ red: { hue: 25, chroma: 0.22 },
456
+ yellow: { hue: 90, chroma: 0.18 },
457
+ purple: { hue: 300, chroma: 0.22 },
458
+ orange: { hue: 55, chroma: 0.18 },
459
+ teal: { hue: 180, chroma: 0.16 },
460
+ pink: { hue: 340, chroma: 0.18 }
461
+ }
462
+ },
463
+
464
+ // ============================================================================
465
+ // Color Configuration Maps - For programmatic utility generation
466
+ // These maps drive the generation of color utilities in the framework
467
+ // ============================================================================
468
+ colorConfig: {
469
+ // Color shades used for scale generation
470
+ shades: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950],
471
+
472
+ // Color families for utility generation
473
+ families: {
474
+ gray: 'gray',
475
+ primary: 'primary',
476
+ success: 'success',
477
+ warning: 'warning',
478
+ danger: 'danger',
479
+ info: 'info',
480
+ blue: 'blue',
481
+ green: 'green',
482
+ red: 'red',
483
+ yellow: 'yellow',
484
+ purple: 'purple',
485
+ orange: 'orange',
486
+ teal: 'teal',
487
+ pink: 'pink'
488
+ },
489
+
490
+ // Background opacity values for color modifiers
491
+ bgOpacityValues: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100],
492
+
493
+ // Gradient color stops
494
+ gradientColors: {
495
+ primary: 'primary',
496
+ secondary: 'secondary',
497
+ success: 'success',
498
+ danger: 'danger',
499
+ warning: 'warning',
500
+ info: 'info',
501
+ light: 'light',
502
+ dark: 'dark',
503
+ white: 'white',
504
+ black: 'black',
505
+ transparent: 'transparent',
506
+ current: 'current'
507
+ }
508
+ },
509
+
510
+ // ============================================================================
511
+ // Typography - Font families, sizes, weights, spacing
512
+ // ============================================================================
513
+ typography: {
514
+ fontFamily: {
515
+ sans: ['ui-sans-serif', 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'sans-serif'],
516
+ serif: ['ui-serif', 'Georgia', 'Cambria', 'Times New Roman', 'Times', 'serif'],
517
+ mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', 'monospace']
518
+ },
519
+ fontSize: {
520
+ xs: ['0.75rem', { lineHeight: '1rem' }],
521
+ sm: ['0.875rem', { lineHeight: '1.25rem' }],
522
+ base: ['1rem', { lineHeight: '1.5rem' }],
523
+ lg: ['1.125rem', { lineHeight: '1.75rem' }],
524
+ xl: ['1.25rem', { lineHeight: '1.75rem' }],
525
+ '2xl': ['1.5rem', { lineHeight: '2rem' }],
526
+ '3xl': ['1.875rem', { lineHeight: '2.25rem' }],
527
+ '4xl': ['2.25rem', { lineHeight: '2.5rem' }],
528
+ '5xl': ['3rem', { lineHeight: '1' }],
529
+ '6xl': ['3.75rem', { lineHeight: '1' }],
530
+ '7xl': ['4.5rem', { lineHeight: '1' }],
531
+ '8xl': ['6rem', { lineHeight: '1' }],
532
+ '9xl': ['8rem', { lineHeight: '1' }]
533
+ },
534
+ fontWeight: {
535
+ thin: '100',
536
+ extralight: '200',
537
+ light: '300',
538
+ normal: '400',
539
+ medium: '500',
540
+ semibold: '600',
541
+ bold: '700',
542
+ extrabold: '800',
543
+ black: '900'
544
+ },
545
+ letterSpacing: {
546
+ tighter: '-0.05em',
547
+ tight: '-0.025em',
548
+ normal: '0em',
549
+ wide: '0.025em',
550
+ wider: '0.05em',
551
+ widest: '0.1em'
552
+ },
553
+ lineHeight: {
554
+ none: '1',
555
+ tight: '1.25',
556
+ snug: '1.375',
557
+ normal: '1.5',
558
+ relaxed: '1.625',
559
+ loose: '2'
560
+ }
561
+ },
562
+
563
+ // ============================================================================
564
+ // Border Radius - Corner roundness values
565
+ // ============================================================================
566
+ borderRadius: {
567
+ none: '0',
568
+ sm: '0.125rem',
569
+ default: '0.25rem',
570
+ md: '0.375rem',
571
+ lg: '0.5rem',
572
+ xl: '0.75rem',
573
+ '2xl': '1rem',
574
+ '3xl': '1.5rem',
575
+ full: '9999px'
576
+ },
577
+
578
+ // ============================================================================
579
+ // Shadows - Box shadow values
580
+ // ============================================================================
581
+ shadows: {
582
+ sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
583
+ default: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
584
+ md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
585
+ lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
586
+ xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
587
+ '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
588
+ inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
589
+ none: 'none'
590
+ },
591
+
592
+ // ============================================================================
593
+ // Transitions - Animation timing
594
+ // ============================================================================
595
+ transition: {
596
+ duration: {
597
+ 75: '75ms',
598
+ 100: '100ms',
599
+ 150: '150ms',
600
+ 200: '200ms',
601
+ 300: '300ms',
602
+ 500: '500ms',
603
+ 700: '700ms',
604
+ 1000: '1000ms'
605
+ },
606
+ timing: {
607
+ linear: 'linear',
608
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
609
+ in: 'cubic-bezier(0.4, 0, 1, 1)',
610
+ out: 'cubic-bezier(0, 0, 0.2, 1)',
611
+ 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)'
612
+ }
613
+ },
614
+
615
+ // ============================================================================
616
+ // Z-Index - Stacking order values
617
+ // ============================================================================
618
+ zIndex: {
619
+ auto: 'auto',
620
+ 0: '0',
621
+ 10: '10',
622
+ 20: '20',
623
+ 30: '30',
624
+ 40: '40',
625
+ 50: '50'
626
+ },
627
+
628
+ // ============================================================================
629
+ // Opacity - Transparency values
630
+ // ============================================================================
631
+ opacity: {
632
+ 0: '0',
633
+ 5: '0.05',
634
+ 10: '0.1',
635
+ 20: '0.2',
636
+ 25: '0.25',
637
+ 30: '0.3',
638
+ 40: '0.4',
639
+ 50: '0.5',
640
+ 60: '0.6',
641
+ 70: '0.7',
642
+ 75: '0.75',
643
+ 80: '0.8',
644
+ 90: '0.9',
645
+ 95: '0.95',
646
+ 100: '1'
647
+ },
648
+
649
+ // ============================================================================
650
+ // Filter Configuration
651
+ // ============================================================================
652
+ filterBlurValues: {
653
+ 0: '0',
654
+ default: '8px',
655
+ sm: '4px',
656
+ md: '12px',
657
+ lg: '16px',
658
+ xl: '24px',
659
+ '2xl': '40px',
660
+ '3xl': '64px'
661
+ },
662
+
663
+ filterBrightnessValues: {
664
+ 0: '0',
665
+ 50: '0.5',
666
+ 75: '0.75',
667
+ 90: '0.9',
668
+ 95: '0.95',
669
+ 100: '1',
670
+ 105: '1.05',
671
+ 110: '1.1',
672
+ 125: '1.25',
673
+ 150: '1.5',
674
+ 200: '2'
675
+ },
676
+
677
+ filterContrastValues: {
678
+ 0: '0',
679
+ 50: '0.5',
680
+ 75: '0.75',
681
+ 100: '1',
682
+ 125: '1.25',
683
+ 150: '1.5',
684
+ 200: '2'
685
+ },
686
+
687
+ filterGrayscaleValues: {
688
+ 0: '0',
689
+ default: '100%'
690
+ },
691
+
692
+ filterHueRotateValues: {
693
+ 0: '0deg',
694
+ 15: '15deg',
695
+ 30: '30deg',
696
+ 60: '60deg',
697
+ 90: '90deg',
698
+ 180: '180deg'
699
+ },
700
+
701
+ filterInvertValues: {
702
+ 0: '0',
703
+ default: '100%'
704
+ },
705
+
706
+ filterSaturateValues: {
707
+ 0: '0',
708
+ 50: '0.5',
709
+ 100: '1',
710
+ 150: '1.5',
711
+ 200: '2'
712
+ },
713
+
714
+ filterSepiaValues: {
715
+ 0: '0',
716
+ default: '100%'
717
+ },
718
+
719
+ // ============================================================================
720
+ // Divide Configuration
721
+ // ============================================================================
722
+ divideWidths: {
723
+ 0: '0px',
724
+ 1: '1px',
725
+ 2: '2px',
726
+ 4: '4px',
727
+ 8: '8px'
728
+ },
729
+
730
+ divideColors: {
731
+ current: 'currentcolor',
732
+ transparent: 'transparent',
733
+ primary: 'var(--color-primary-500)',
734
+ secondary: 'var(--color-secondary-500)',
735
+ gray: 'var(--color-gray-500)'
736
+ },
737
+
738
+ divideStyles: {
739
+ solid: 'solid',
740
+ dashed: 'dashed',
741
+ dotted: 'dotted',
742
+ double: 'double',
743
+ none: 'none'
744
+ },
745
+
746
+ dropShadowValues: {
747
+ none: '0 0 #0000',
748
+ default: '0 1px 2px 0 rgb(0 0 0 / 0.1)',
749
+ sm: '0 1px 1px 0 rgb(0 0 0 / 0.05)',
750
+ md: '0 4px 6px -1px rgb(0 0 0 / 0.1)',
751
+ lg: '0 10px 15px -3px rgb(0 0 0 / 0.1)',
752
+ xl: '0 20px 25px -5px rgb(0 0 0 / 0.1)',
753
+ '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)'
754
+ },
755
+
756
+ backdropOpacityValues: {
757
+ 0: '0',
758
+ 25: '0.25',
759
+ 50: '0.5',
760
+ 75: '0.75',
761
+ 100: '1'
762
+ }
763
+ };
764
+
765
+ /**
766
+ * Generate SCSS configuration from user config
767
+ */
768
+ /**
769
+ * Generate SCSS configuration from user config
770
+ * @param {object} config - User configuration
771
+ * @returns {string} - Generated SCSS content
772
+ */
773
+ function generateSCSS(config) {
774
+ const merged = mergeDeep(defaultConfig, config);
775
+
776
+ const sections = [
777
+ generateHeader(),
778
+ generateFeatureToggles(merged.features),
779
+ generateBreakpoints(merged.breakpoints),
780
+ generateSpacing(merged.spacing),
781
+ generateFractionalWidths(merged.fractionalWidths),
782
+ generateColumns(merged.columns),
783
+ generateTextShadow(merged.textShadow),
784
+ generateBlendModes(merged.blendModes),
785
+ generateFilterConfig(merged),
786
+ generateRingWidths(merged.ringWidths),
787
+ generateRingColors(merged.ringColors),
788
+ generateRingOpacity(merged.ringOpacity),
789
+ generateOutlineWidths(merged.outlineWidths),
790
+ generateOutlineOffsets(merged.outlineOffsets),
791
+ generateColors(merged.colors),
792
+ generateColorConfig(merged.colorConfig),
793
+ generateTypography(merged.typography),
794
+ generateBorderRadius(merged.borderRadius),
795
+ generateShadows(merged.shadows),
796
+ generateTransitions(merged.transition),
797
+ generateZIndex(merged.zIndex),
798
+ generateOpacity(merged.opacity),
799
+ generateDivideConfig(merged),
800
+ generateFooter()
801
+ ];
802
+
803
+ return sections.join('\n\n');
804
+ }
805
+
806
+ /**
807
+ * Generate file header
808
+ * @returns {string} - Header content
809
+ */
810
+ function generateHeader() {
811
+ return `// ============================================================================
812
+ // ApexCSS Configuration - Auto-generated by config-builder.js
813
+ // ============================================================================
814
+ // This file is automatically generated. Do not edit directly.
815
+ // Instead, modify your apex.config.js and run: npm run config:build
816
+ // ============================================================================`;
817
+ }
818
+
819
+ /**
820
+ * Generate file footer
821
+ * @returns {string} - Footer content
822
+ */
823
+ function generateFooter() {
824
+ return `// ============================================================================
825
+ // End of ApexCSS Configuration
826
+ // ============================================================================`;
827
+ }
828
+
829
+ /**
830
+ * Generate feature toggle variables with categorization
831
+ */
832
+ function generateFeatureToggles(features) {
833
+ const categories = {
834
+ 'Core Layout': ['display', 'flexbox', 'grid', 'positioning', 'visibility'],
835
+ 'Core Spacing': ['spacing', 'sizing'],
836
+ 'Core Typography': ['typography'],
837
+ 'Core Visual': ['colors', 'backgrounds', 'borders', 'shadows', 'opacity', 'overflow', 'objectFit'],
838
+ 'Core Interaction': ['cursor', 'transitions'],
839
+ 'Extended Layout': ['flexExtended', 'gridExtended', 'float', 'containerQueries', 'isolation', 'placeItems', 'justifyItems', 'spaceBetween', 'columns', 'columnsExtended'],
840
+ 'Extended Typography': ['typographyExtended', 'fontExtended', 'letterSpacing', 'lineHeight', 'textAlignLast', 'textDecorationExtended', 'textJustify', 'textIndent', 'textShadow', 'textEmphasis', 'textOrientation', 'textUnderline', 'hangingPunctuation', 'hyphenate', 'initialLetter', 'tabSize', 'wordBreak', 'wordWrap', 'writingMode', 'unicodeBidi'],
841
+ 'Extended Visual': ['backgroundExtended', 'colorModifiers', 'blendModes', 'masks', 'borderRadiusLogical', 'ring', 'outline', 'appearance', 'accentColor', 'colorScheme'],
842
+ 'Extended Interaction': ['interaction', 'userSelect', 'willChange', 'all', 'caret', 'scroll', 'overscrollBehavior', 'overscrollBehaviorExtended', 'overflowExtended'],
843
+ 'Effects': ['animations', 'transforms', 'transforms3d', 'filters', 'aspectRatio', 'imageRendering', 'transitionBehavior'],
844
+ 'Content': ['list', 'listStyleExtended', 'table', 'counter', 'caption', 'quotes', 'orphans', 'widows', 'pageBreak', 'break', 'verticalAlign'],
845
+ 'Advanced': ['arbitrary', 'logicalProperties', 'sizingLogical', 'offset', 'shapeOutside', 'markerExtended', 'zoom', 'fieldSizing', 'svg', 'box', 'divide'],
846
+ 'State Variants': ['states', 'hover', 'focus', 'active', 'disabled'],
847
+ 'Theme Support': ['darkMode', 'rtl', 'accessibility', 'zIndex']
848
+ };
849
+
850
+ let output = [];
851
+
852
+ for (const [category, keys] of Object.entries(categories)) {
853
+ output.push(`// ${category}`);
854
+ keys.forEach(key => {
855
+ if (key in features) {
856
+ const varName = key.replace(/([A-Z])/g, '-$1').toLowerCase();
857
+ const comment = features[key] ? 'enabled' : 'disabled';
858
+ output.push(`$enable-${varName}: ${features[key]} !default; // ${comment}`);
859
+ }
860
+ });
861
+ output.push('');
862
+ }
863
+
864
+ return output.join('\n');
865
+ }
866
+
867
+ /**
868
+ * Generate breakpoint variables and CSS custom properties
869
+ */
870
+ function generateBreakpoints(breakpoints) {
871
+ const lines = [];
872
+
873
+ // Individual SCSS variables
874
+ lines.push('// Individual breakpoint variables');
875
+ Object.entries(breakpoints).forEach(([key, value]) => {
876
+ lines.push(`$breakpoint-${key}: ${value} !default;`);
877
+ });
878
+
879
+ // SCSS map for iteration
880
+ lines.push('');
881
+ lines.push('// Breakpoints map for SCSS iteration');
882
+ lines.push('$breakpoints: (');
883
+ Object.entries(breakpoints).forEach(([key, value], index, arr) => {
884
+ const comma = index < arr.length - 1 ? ',' : '';
885
+ lines.push(` "${key}": ${value}${comma}`);
886
+ });
887
+ lines.push(') !default;');
888
+
889
+ // Breakpoint class name prefixes (for responsive utility class naming)
890
+ lines.push('');
891
+ lines.push('// Breakpoint prefixes for responsive class names');
892
+ lines.push('$breakpoint-prefixes: (');
893
+ Object.entries(breakpoints).forEach(([key, value], index, arr) => {
894
+ const comma = index < arr.length - 1 ? ',' : '';
895
+ // For 'xxl', use 'xxl' directly. For others, use the key as-is
896
+ const prefix = key === 'xxl' ? 'xxl' : key;
897
+ lines.push(` "${key}": ${prefix}${comma}`);
898
+ });
899
+ lines.push(') !default;');
900
+
901
+ // Alias for backwards compatibility with code that uses $breakpoint-class-names
902
+ lines.push('');
903
+ lines.push('// Alias for backwards compatibility');
904
+ lines.push('$breakpoint-class-names: $breakpoint-prefixes !default;');
905
+
906
+ return lines.join('\n');
907
+ }
908
+
909
+ /**
910
+ * Generate spacing scale
911
+ */
912
+ function generateSpacing(spacing) {
913
+ const lines = [];
914
+
915
+ lines.push('$spacing-scale: (');
916
+ Object.entries(spacing).forEach(([key, value], index, arr) => {
917
+ const comma = index < arr.length - 1 ? ',' : '';
918
+ // Keep numeric keys unquoted for proper numeric comparisons in Sass
919
+ lines.push(` ${key}: ${value}${comma}`);
920
+ });
921
+ lines.push(') !default;');
922
+
923
+ return lines.join('\n');
924
+ }
925
+
926
+ /**
927
+ * Generate fractional widths configuration
928
+ * Outputs a Sass map with enabled fractional width values
929
+ */
930
+ function generateFractionalWidths(config) {
931
+ const widths = [];
932
+
933
+ if (config.halves) {
934
+ widths.push(' "1\\\\/2": 50%');
935
+ }
936
+
937
+ if (config.thirds) {
938
+ widths.push(' "1\\\\/3": 33.3333%');
939
+ widths.push(' "2\\\\/3": 66.6667%');
940
+ }
941
+
942
+ if (config.quarters) {
943
+ widths.push(' "1\\\\/4": 25%');
944
+ widths.push(' "2\\\\/4": 50%');
945
+ widths.push(' "3\\\\/4": 75%');
946
+ }
947
+
948
+ if (config.fifths) {
949
+ widths.push(' "1\\\\/5": 20%');
950
+ widths.push(' "2\\\\/5": 40%');
951
+ widths.push(' "3\\\\/5": 60%');
952
+ widths.push(' "4\\\\/5": 80%');
953
+ }
954
+
955
+ if (config.sixths) {
956
+ widths.push(' "1\\\\/6": 16.6667%');
957
+ widths.push(' "2\\\\/6": 33.3333%');
958
+ widths.push(' "3\\\\/6": 50%');
959
+ widths.push(' "4\\\\/6": 66.6667%');
960
+ widths.push(' "5\\\\/6": 83.3333%');
961
+ }
962
+
963
+ if (config.twelfths) {
964
+ widths.push(' "1\\\\/12": 8.3333%');
965
+ widths.push(' "2\\\\/12": 16.6667%');
966
+ widths.push(' "3\\\\/12": 25%');
967
+ widths.push(' "4\\\\/12": 33.3333%');
968
+ widths.push(' "5\\\\/12": 41.6667%');
969
+ widths.push(' "6\\\\/12": 50%');
970
+ widths.push(' "7\\\\/12": 58.3333%');
971
+ widths.push(' "8\\\\/12": 66.6667%');
972
+ widths.push(' "9\\\\/12": 75%');
973
+ widths.push(' "10\\\\/12": 83.3333%');
974
+ widths.push(' "11\\\\/12": 91.6667%');
975
+ }
976
+
977
+ const lines = ['$fractional-widths: ('];
978
+ widths.forEach((line, index) => {
979
+ const comma = index < widths.length - 1 ? ',' : '';
980
+ lines.push(line + comma);
981
+ });
982
+ lines.push(') !default;');
983
+
984
+ return lines.join('\n');
985
+ }
986
+
987
+ /**
988
+ * Generate column configuration
989
+ */
990
+ function generateColumns(columns) {
991
+ const lines = [];
992
+
993
+ // Column counts
994
+ lines.push('$column-counts: (');
995
+ columns.counts.forEach((count, index) => {
996
+ const comma = index < columns.counts.length - 1 ? ',' : '';
997
+ lines.push(` ${count}${comma}`);
998
+ });
999
+ lines.push(') !default;');
1000
+
1001
+ // Column widths
1002
+ lines.push('$column-widths: (');
1003
+ Object.entries(columns.widths).forEach(([key, value], index, arr) => {
1004
+ const comma = index < arr.length - 1 ? ',' : '';
1005
+ lines.push(` "${key}": ${value}${comma}`);
1006
+ });
1007
+ lines.push(') !default;');
1008
+
1009
+ // Column rule styles
1010
+ lines.push('$column-rule-styles: (');
1011
+ Object.entries(columns.ruleStyles).forEach(([key, value], index, arr) => {
1012
+ const comma = index < arr.length - 1 ? ',' : '';
1013
+ lines.push(` "${key}": ${value}${comma}`);
1014
+ });
1015
+ lines.push(') !default;');
1016
+
1017
+ // Column rule widths
1018
+ lines.push('$column-rule-widths: (');
1019
+ Object.entries(columns.ruleWidths).forEach(([key, value], index, arr) => {
1020
+ const comma = index < arr.length - 1 ? ',' : '';
1021
+ lines.push(` "${key}": ${value}${comma}`);
1022
+ });
1023
+ lines.push(') !default;');
1024
+
1025
+ return lines.join('\n');
1026
+ }
1027
+
1028
+ /**
1029
+ * Generate text shadow values
1030
+ */
1031
+ function generateTextShadow(textShadow) {
1032
+ const lines = ['$text-shadow-values: ('];
1033
+ Object.entries(textShadow).forEach(([key, value], index, arr) => {
1034
+ const comma = index < arr.length - 1 ? ',' : '';
1035
+ // Quote the value to prevent Sass from parsing rgb() / as division
1036
+ lines.push(` "${key}": "${value}"${comma}`);
1037
+ });
1038
+ lines.push(') !default;');
1039
+ return lines.join('\n');
1040
+ }
1041
+
1042
+ /**
1043
+ * Generate ring widths configuration
1044
+ */
1045
+ function generateRingWidths(ringWidths) {
1046
+ const lines = ['$ring-widths: ('];
1047
+ Object.entries(ringWidths).forEach(([key, value], index, arr) => {
1048
+ const comma = index < arr.length - 1 ? ',' : '';
1049
+ lines.push(` "${key}": ${value}${comma}`);
1050
+ });
1051
+ lines.push(') !default;');
1052
+ return lines.join('\n');
1053
+ }
1054
+
1055
+ /**
1056
+ * Generate ring colors configuration
1057
+ */
1058
+ function generateRingColors(ringColors) {
1059
+ const lines = ["$ring-colors: ("];
1060
+ Object.entries(ringColors).forEach(([key, value], index, arr) => {
1061
+ const comma = index < arr.length - 1 ? "," : "";
1062
+ lines.push(` "${key}": ${value}${comma}`);
1063
+ });
1064
+ lines.push(") !default;");
1065
+ return lines.join("\n");
1066
+ }
1067
+
1068
+ /**
1069
+ * Generate ring opacity configuration
1070
+ */
1071
+ function generateRingOpacity(ringOpacity) {
1072
+ const lines = ["$ring-opacity-values: ("];
1073
+ Object.entries(ringOpacity).forEach(([key, value], index, arr) => {
1074
+ const comma = index < arr.length - 1 ? "," : "";
1075
+ lines.push(` "${key}": ${value}${comma}`);
1076
+ });
1077
+ lines.push(") !default;");
1078
+ return lines.join("\n");
1079
+ }
1080
+
1081
+ /**
1082
+ * Generate outline widths configuration
1083
+ */
1084
+ function generateOutlineWidths(outlineWidths) {
1085
+ const lines = ["$outline-widths: ("];
1086
+ Object.entries(outlineWidths).forEach(([key, value], index, arr) => {
1087
+ const comma = index < arr.length - 1 ? "," : "";
1088
+ lines.push(` "${key}": ${value}${comma}`);
1089
+ });
1090
+ lines.push(") !default;");
1091
+ return lines.join("\n");
1092
+ }
1093
+
1094
+ /**
1095
+ * Generate outline offsets configuration
1096
+ */
1097
+ function generateOutlineOffsets(outlineOffsets) {
1098
+ const lines = ["$outline-offsets: ("];
1099
+ Object.entries(outlineOffsets).forEach(([key, value], index, arr) => {
1100
+ const comma = index < arr.length - 1 ? "," : "";
1101
+ lines.push(` "${key}": ${value}${comma}`);
1102
+ });
1103
+ lines.push(") !default;");
1104
+ return lines.join("\n");
1105
+ }
1106
+
1107
+ /**
1108
+ * Generate blend modes configuration
1109
+ */
1110
+ function generateBlendModes(blendModes) {
1111
+ const lines = ['$blend-modes: ('];
1112
+ Object.entries(blendModes).forEach(([key, value], index, arr) => {
1113
+ const comma = index < arr.length - 1 ? ',' : '';
1114
+ lines.push(` "${key}": ${value}${comma}`);
1115
+ });
1116
+ lines.push(') !default;');
1117
+ return lines.join('\n');
1118
+ }
1119
+
1120
+ /**
1121
+ * Generate divide configuration
1122
+ */
1123
+ function generateDivideConfig(config) {
1124
+ const lines = [];
1125
+
1126
+ // Divide widths
1127
+ lines.push('$divide-widths: (');
1128
+ Object.entries(config.divideWidths).forEach(([key, value], index, arr) => {
1129
+ const comma = index < arr.length - 1 ? ',' : '';
1130
+ lines.push(` "${key}": ${value}${comma}`);
1131
+ });
1132
+ lines.push(') !default;');
1133
+
1134
+ // Divide colors
1135
+ lines.push('$divide-colors: (');
1136
+ Object.entries(config.divideColors).forEach(([key, value], index, arr) => {
1137
+ const comma = index < arr.length - 1 ? ',' : '';
1138
+ lines.push(` "${key}": ${value}${comma}`);
1139
+ });
1140
+ lines.push(') !default;');
1141
+
1142
+ // Divide styles
1143
+ lines.push('$divide-styles: (');
1144
+ Object.entries(config.divideStyles).forEach(([key, value], index, arr) => {
1145
+ const comma = index < arr.length - 1 ? ',' : '';
1146
+ lines.push(` "${key}": ${value}${comma}`);
1147
+ });
1148
+ lines.push(') !default;');
1149
+
1150
+ return lines.join('\n');
1151
+ }
1152
+
1153
+ /**
1154
+ * Generate filter configuration
1155
+ */
1156
+ function generateFilterConfig(config) {
1157
+ const lines = [];
1158
+
1159
+ // Filter blur values
1160
+ lines.push('$filter-blur-values: (');
1161
+ Object.entries(config.filterBlurValues).forEach(([key, value], index, arr) => {
1162
+ const comma = index < arr.length - 1 ? ',' : '';
1163
+ lines.push(` "${key}": ${value}${comma}`);
1164
+ });
1165
+ lines.push(') !default;');
1166
+
1167
+ // Filter brightness values
1168
+ lines.push('$filter-brightness-values: (');
1169
+ Object.entries(config.filterBrightnessValues).forEach(([key, value], index, arr) => {
1170
+ const comma = index < arr.length - 1 ? ',' : '';
1171
+ lines.push(` "${key}": ${value}${comma}`);
1172
+ });
1173
+ lines.push(') !default;');
1174
+
1175
+ // Filter contrast values
1176
+ lines.push('$filter-contrast-values: (');
1177
+ Object.entries(config.filterContrastValues).forEach(([key, value], index, arr) => {
1178
+ const comma = index < arr.length - 1 ? ',' : '';
1179
+ lines.push(` "${key}": ${value}${comma}`);
1180
+ });
1181
+ lines.push(') !default;');
1182
+
1183
+ // Filter grayscale values
1184
+ lines.push('$filter-grayscale-values: (');
1185
+ Object.entries(config.filterGrayscaleValues).forEach(([key, value], index, arr) => {
1186
+ const comma = index < arr.length - 1 ? ',' : '';
1187
+ lines.push(` "${key}": ${value}${comma}`);
1188
+ });
1189
+ lines.push(') !default;');
1190
+
1191
+ // Filter hue-rotate values
1192
+ lines.push('$filter-hue-rotate-values: (');
1193
+ Object.entries(config.filterHueRotateValues).forEach(([key, value], index, arr) => {
1194
+ const comma = index < arr.length - 1 ? ',' : '';
1195
+ lines.push(` "${key}": ${value}${comma}`);
1196
+ });
1197
+ lines.push(') !default;');
1198
+
1199
+ // Filter invert values
1200
+ lines.push('$filter-invert-values: (');
1201
+ Object.entries(config.filterInvertValues).forEach(([key, value], index, arr) => {
1202
+ const comma = index < arr.length - 1 ? ',' : '';
1203
+ lines.push(` "${key}": ${value}${comma}`);
1204
+ });
1205
+ lines.push(') !default;');
1206
+
1207
+ // Filter saturate values
1208
+ lines.push('$filter-saturate-values: (');
1209
+ Object.entries(config.filterSaturateValues).forEach(([key, value], index, arr) => {
1210
+ const comma = index < arr.length - 1 ? ',' : '';
1211
+ lines.push(` "${key}": ${value}${comma}`);
1212
+ });
1213
+ lines.push(') !default;');
1214
+
1215
+ // Filter sepia values
1216
+ lines.push('$filter-sepia-values: (');
1217
+ Object.entries(config.filterSepiaValues).forEach(([key, value], index, arr) => {
1218
+ const comma = index < arr.length - 1 ? ',' : '';
1219
+ lines.push(` "${key}": ${value}${comma}`);
1220
+ });
1221
+ lines.push(') !default;');
1222
+
1223
+ // Drop shadow values
1224
+ lines.push('$drop-shadow-values: (');
1225
+ Object.entries(config.dropShadowValues).forEach(([key, value], index, arr) => {
1226
+ const comma = index < arr.length - 1 ? ',' : '';
1227
+ lines.push(` "${key}": "${value}"${comma}`);
1228
+ });
1229
+ lines.push(') !default;');
1230
+
1231
+ // Backdrop opacity values
1232
+ lines.push('$backdrop-opacity-values: (');
1233
+ Object.entries(config.backdropOpacityValues).forEach(([key, value], index, arr) => {
1234
+ const comma = index < arr.length - 1 ? ',' : '';
1235
+ lines.push(` "${key}": ${value}${comma}`);
1236
+ });
1237
+ lines.push(') !default;');
1238
+
1239
+ return lines.join('\n');
1240
+ }
1241
+
1242
+ /**
1243
+ * Generate OKLCH color variables from configuration
1244
+ * Colors are now defined with OKLCH format: { hue, chroma, lightnessScale }
1245
+ */
1246
+ function generateColors(colors) {
1247
+ const lines = [];
1248
+ const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
1249
+ const defaultLightnessScale = {
1250
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
1251
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
1252
+ };
1253
+
1254
+ // Primary colors
1255
+ if (colors.primary) {
1256
+ lines.push('// Primary Color Scale (OKLCH)');
1257
+ const { hue, chroma, lightnessScale } = colors.primary;
1258
+ shades.forEach(shade => {
1259
+ const lightness = lightnessScale[shade];
1260
+ lines.push(`$color-primary-${shade}: oklch(${lightness}% ${chroma} ${hue}deg) !default;`);
1261
+ });
1262
+ }
1263
+
1264
+ // Gray scale
1265
+ if (colors.gray) {
1266
+ lines.push('');
1267
+ lines.push('// Gray Scale (OKLCH)');
1268
+ const { hue, chroma, lightnessScale } = colors.gray;
1269
+ shades.forEach(shade => {
1270
+ const lightness = lightnessScale[shade];
1271
+ lines.push(`$color-gray-${shade}: oklch(${lightness}% ${chroma} ${hue}deg) !default;`);
1272
+ });
1273
+ }
1274
+
1275
+ // Semantic colors
1276
+ const semanticColors = ['success', 'warning', 'danger', 'info'];
1277
+ semanticColors.forEach(colorName => {
1278
+ if (colors[colorName]) {
1279
+ lines.push('');
1280
+ lines.push(`// ${colorName.charAt(0).toUpperCase() + colorName.slice(1)} Color Scale (OKLCH)`);
1281
+ const { hue, chroma, lightnessScale } = colors[colorName];
1282
+ shades.forEach(shade => {
1283
+ const lightness = lightnessScale[shade];
1284
+ lines.push(`$color-${colorName}-${shade}: oklch(${lightness}% ${chroma} ${hue}deg) !default;`);
1285
+ });
1286
+ }
1287
+ });
1288
+
1289
+ // Extended color palette
1290
+ if (colors.extended) {
1291
+ lines.push('');
1292
+ lines.push('// Extended Color Palette (OKLCH)');
1293
+ Object.entries(colors.extended).forEach(([colorName, config]) => {
1294
+ const { hue, chroma } = config;
1295
+ shades.forEach(shade => {
1296
+ const lightness = defaultLightnessScale[shade];
1297
+ lines.push(`$color-${colorName}-${shade}: oklch(${lightness}% ${chroma} ${hue}deg) !default;`);
1298
+ });
1299
+ });
1300
+ }
1301
+
1302
+ // Semantic aliases
1303
+ lines.push('');
1304
+ lines.push('// Semantic Color Aliases');
1305
+ lines.push('$color-primary: $color-primary-500 !default;');
1306
+ lines.push('$color-success: $color-success-500 !default;');
1307
+ lines.push('$color-warning: $color-warning-500 !default;');
1308
+ lines.push('$color-danger: $color-danger-500 !default;');
1309
+ lines.push('$color-info: $color-info-500 !default;');
1310
+
1311
+ return lines.join('\n');
1312
+ }
1313
+
1314
+ /**
1315
+ * Generate color configuration maps for utility generation
1316
+ */
1317
+ function generateColorConfig(colorConfig) {
1318
+ const lines = [];
1319
+
1320
+ // Color shades
1321
+ lines.push('');
1322
+ lines.push('// ============================================================================');
1323
+ lines.push('// Color Scale Maps (for programmatic utility generation)');
1324
+ lines.push('// ============================================================================');
1325
+ lines.push('');
1326
+ lines.push('// Color shades used for scale generation');
1327
+ lines.push(`$color-shades: (${colorConfig.shades.join(', ')}) !default;`);
1328
+ lines.push('');
1329
+
1330
+ // Color families
1331
+ lines.push('// Color families for utility generation');
1332
+ lines.push('$color-families: (');
1333
+ Object.entries(colorConfig.families).forEach(([key, value], index, arr) => {
1334
+ const comma = index < arr.length - 1 ? ',' : '';
1335
+ lines.push(` "${key}": "${value}"${comma}`);
1336
+ });
1337
+ lines.push(') !default;');
1338
+ lines.push('');
1339
+
1340
+ // Background opacity values
1341
+ lines.push('// Background opacity values for color modifiers');
1342
+ lines.push(`$bg-opacity-values: (${colorConfig.bgOpacityValues.join(', ')}) !default;`);
1343
+ lines.push('');
1344
+
1345
+ // Gradient colors
1346
+ lines.push('// Gradient color stops');
1347
+ lines.push('$gradient-colors: (');
1348
+ Object.entries(colorConfig.gradientColors).forEach(([key, value], index, arr) => {
1349
+ const comma = index < arr.length - 1 ? ',' : '';
1350
+ lines.push(` "${key}": "${value}"${comma}`);
1351
+ });
1352
+ lines.push(') !default;');
1353
+
1354
+ return lines.join('\n');
1355
+ }
1356
+
1357
+ /**
1358
+ * Generate typography configuration
1359
+ */
1360
+ function generateTypography(typography) {
1361
+ const lines = [];
1362
+
1363
+ // Font families
1364
+ lines.push('// Font Families');
1365
+ lines.push('$font-families: (');
1366
+ Object.entries(typography.fontFamily).forEach(([key, value], index, arr) => {
1367
+ const comma = index < arr.length - 1 ? ',' : '';
1368
+ const fontList = Array.isArray(value) ? value.join(', ') : value;
1369
+ lines.push(` "${key}": "${fontList}"${comma}`);
1370
+ });
1371
+ lines.push(') !default;');
1372
+ lines.push('');
1373
+
1374
+ // Font sizes
1375
+ lines.push('// Font Sizes');
1376
+ lines.push('$font-sizes: (');
1377
+ Object.entries(typography.fontSize).forEach(([key, value], index, arr) => {
1378
+ const comma = index < arr.length - 1 ? ',' : '';
1379
+ const size = Array.isArray(value) ? value[0] : value;
1380
+ lines.push(` "${key}": ${size}${comma}`);
1381
+ });
1382
+ lines.push(') !default;');
1383
+ lines.push('');
1384
+
1385
+ // Font weights
1386
+ lines.push('// Font Weights');
1387
+ lines.push('$font-weights: (');
1388
+ const sassColorNames = new Set(['black', 'white', 'red', 'green', 'blue', 'gray', 'orange', 'purple', 'yellow', 'pink', 'brown', 'cyan', 'magenta', 'navy', 'olive', 'silver', 'teal', 'maroon', 'aqua', 'fuchsia', 'lime']);
1389
+ Object.entries(typography.fontWeight).forEach(([key, value], index, arr) => {
1390
+ const comma = index < arr.length - 1 ? ',' : '';
1391
+ const safeKey = sassColorNames.has(key) ? `"${key}"` : key;
1392
+ lines.push(` ${safeKey}: ${value}${comma}`);
1393
+ });
1394
+ lines.push(') !default;');
1395
+ lines.push('');
1396
+
1397
+ // Letter spacing
1398
+ lines.push('// Letter Spacing');
1399
+ lines.push('$letter-spacing: (');
1400
+ Object.entries(typography.letterSpacing).forEach(([key, value], index, arr) => {
1401
+ const comma = index < arr.length - 1 ? ',' : '';
1402
+ lines.push(` "${key}": ${value}${comma}`);
1403
+ });
1404
+ lines.push(') !default;');
1405
+ lines.push('');
1406
+
1407
+ // Line heights
1408
+ lines.push('// Line Heights');
1409
+ lines.push('$line-heights: (');
1410
+ Object.entries(typography.lineHeight).forEach(([key, value], index, arr) => {
1411
+ const comma = index < arr.length - 1 ? ',' : '';
1412
+ lines.push(` "${key}": ${value}${comma}`);
1413
+ });
1414
+ lines.push(') !default;');
1415
+
1416
+ return lines.join('\n');
1417
+ }
1418
+
1419
+ /**
1420
+ * Generate border radius scale
1421
+ */
1422
+ function generateBorderRadius(borderRadius) {
1423
+ const lines = [];
1424
+
1425
+ lines.push('$border-radius-scale: (');
1426
+ Object.entries(borderRadius).forEach(([key, value], index, arr) => {
1427
+ const comma = index < arr.length - 1 ? ',' : '';
1428
+ lines.push(` "${key}": ${value}${comma}`);
1429
+ });
1430
+ lines.push(') !default;');
1431
+
1432
+ return lines.join('\n');
1433
+ }
1434
+
1435
+ /**
1436
+ * Generate shadow variables
1437
+ */
1438
+ function generateShadows(shadows) {
1439
+ const lines = [];
1440
+
1441
+ lines.push('$shadows: (');
1442
+ Object.entries(shadows).forEach(([key, value], index, arr) => {
1443
+ const comma = index < arr.length - 1 ? ',' : '';
1444
+ const needsQuotes = value.includes(',');
1445
+ lines.push(` "${key}": ${needsQuotes ? `"${value}"` : value}${comma}`);
1446
+ });
1447
+ lines.push(') !default;');
1448
+
1449
+ return lines.join('\n');
1450
+ }
1451
+
1452
+ /**
1453
+ * Generate transition configuration
1454
+ */
1455
+ function generateTransitions(transition) {
1456
+ const lines = [];
1457
+
1458
+ // Durations
1459
+ lines.push('// Transition Durations');
1460
+ lines.push('$transition-duration: (');
1461
+ Object.entries(transition.duration).forEach(([key, value], index, arr) => {
1462
+ const comma = index < arr.length - 1 ? ',' : '';
1463
+ lines.push(` "${key}": ${value}${comma}`);
1464
+ });
1465
+ lines.push(') !default;');
1466
+ lines.push('');
1467
+
1468
+ // Timing functions
1469
+ lines.push('// Transition Timing Functions');
1470
+ lines.push('$transition-timing: (');
1471
+ Object.entries(transition.timing).forEach(([key, value], index, arr) => {
1472
+ const comma = index < arr.length - 1 ? ',' : '';
1473
+ lines.push(` "${key}": ${value}${comma}`);
1474
+ });
1475
+ lines.push(') !default;');
1476
+
1477
+ return lines.join('\n');
1478
+ }
1479
+
1480
+ /**
1481
+ * Generate z-index scale
1482
+ */
1483
+ function generateZIndex(zIndex) {
1484
+ const lines = [];
1485
+
1486
+ lines.push('$z-index: (');
1487
+ Object.entries(zIndex).forEach(([key, value], index, arr) => {
1488
+ const comma = index < arr.length - 1 ? ',' : '';
1489
+ lines.push(` "${key}": ${value}${comma}`);
1490
+ });
1491
+ lines.push(') !default;');
1492
+
1493
+ return lines.join('\n');
1494
+ }
1495
+
1496
+ /**
1497
+ * Generate opacity scale
1498
+ */
1499
+ function generateOpacity(opacity) {
1500
+ const lines = [];
1501
+
1502
+ lines.push('$opacity-scale: (');
1503
+ Object.entries(opacity).forEach(([key, value], index, arr) => {
1504
+ const comma = index < arr.length - 1 ? ',' : '';
1505
+ // Keep numeric keys unquoted for proper numeric comparisons in Sass
1506
+ lines.push(` ${key}: ${value}${comma}`);
1507
+ });
1508
+ lines.push(') !default;');
1509
+
1510
+ return lines.join('\n');
1511
+ }
1512
+
1513
+ /**
1514
+ * Generate a complete sample config file
1515
+ */
1516
+ function generateSampleConfig() {
1517
+ // Generate the features section from defaultConfig
1518
+ const featureCategories = {
1519
+ 'Core Layout': ['display', 'flexbox', 'grid', 'positioning', 'visibility'],
1520
+ 'Core Spacing': ['spacing', 'sizing'],
1521
+ 'Core Typography': ['typography'],
1522
+ 'Core Visual': ['colors', 'backgrounds', 'borders', 'shadows', 'opacity', 'overflow', 'objectFit'],
1523
+ 'Core Interaction': ['cursor', 'transitions'],
1524
+ 'Extended Layout': ['flexExtended', 'gridExtended', 'float', 'containerQueries', 'isolation', 'placeItems', 'justifyItems', 'spaceBetween', 'columns', 'columnsExtended'],
1525
+ 'Extended Typography': ['typographyExtended', 'fontExtended', 'letterSpacing', 'lineHeight', 'textAlignLast', 'textDecorationExtended', 'textJustify', 'textIndent', 'textShadow', 'textEmphasis', 'textOrientation', 'textUnderline', 'hangingPunctuation', 'hyphenate', 'initialLetter', 'tabSize', 'wordBreak', 'wordWrap', 'writingMode', 'unicodeBidi'],
1526
+ 'Extended Visual': ['backgroundExtended', 'colorModifiers', 'blendModes', 'masks', 'borderRadiusLogical', 'ring', 'outline', 'appearance', 'accentColor', 'colorScheme'],
1527
+ 'Extended Interaction': ['interaction', 'userSelect', 'willChange', 'all', 'caret', 'scroll', 'overscrollBehavior', 'overscrollBehaviorExtended', 'overflowExtended'],
1528
+ 'Effects': ['animations', 'transforms', 'transforms3d', 'filters', 'aspectRatio', 'imageRendering', 'transitionBehavior'],
1529
+ 'Content': ['list', 'listStyleExtended', 'table', 'counter', 'caption', 'quotes', 'orphans', 'widows', 'pageBreak', 'break', 'verticalAlign'],
1530
+ 'Advanced': ['arbitrary', 'logicalProperties', 'sizingLogical', 'offset', 'shapeOutside', 'markerExtended', 'zoom', 'fieldSizing', 'svg', 'box', 'divide'],
1531
+ 'State Variants': ['states', 'hover', 'focus', 'active', 'disabled'],
1532
+ 'Theme Support': ['darkMode', 'rtl', 'accessibility', 'zIndex']
1533
+ };
1534
+
1535
+ let featuresSection = '';
1536
+ for (const [category, keys] of Object.entries(featureCategories)) {
1537
+ featuresSection += ` // ${category}\n`;
1538
+ keys.forEach(key => {
1539
+ if (key in defaultConfig.features) {
1540
+ featuresSection += ` ${key}: true,\n`;
1541
+ }
1542
+ });
1543
+ featuresSection += '\n';
1544
+ }
1545
+
1546
+ return `/**
1547
+ * ApexCSS Configuration File
1548
+ *
1549
+ * This is your configuration file for customizing ApexCSS.
1550
+ * All features are enabled by default. Set any feature to false to disable it
1551
+ * and reduce your bundle size.
1552
+ *
1553
+ * Usage:
1554
+ * 1. Modify the values below to customize the framework
1555
+ * 2. Run: npm run config:build
1556
+ * 3. The SCSS files will be regenerated with your customizations
1557
+ *
1558
+ */
1559
+
1560
+ export default {
1561
+ // ============================================================================
1562
+ // Feature Toggles - Enable/disable utility categories
1563
+ // ============================================================================
1564
+ features: {
1565
+ ${featuresSection.trim()}
1566
+ },
1567
+
1568
+ // ============================================================================
1569
+ // Breakpoints - Customize responsive breakpoints
1570
+ // ============================================================================
1571
+ breakpoints: {
1572
+ sm: '320px',
1573
+ md: '768px',
1574
+ lg: '1024px',
1575
+ xl: '1280px',
1576
+ xxl: '1536px'
1577
+ },
1578
+
1579
+ // ============================================================================
1580
+ // Spacing Scale - Customize margin/padding values
1581
+ // ============================================================================
1582
+ spacing: {
1583
+ 0: '0',
1584
+ 1: '0.25rem',
1585
+ 2: '0.5rem',
1586
+ 4: '1rem',
1587
+ 8: '2rem',
1588
+ 16: '4rem'
1589
+ // Add or remove values as needed
1590
+ },
1591
+
1592
+ // ============================================================================
1593
+ // Colors - Customize your color palette (OKLCH format)
1594
+ // ============================================================================
1595
+ // Define colors with OKLCH format: { hue, chroma, lightnessScale }
1596
+ // - hue: 0-360 (color wheel angle)
1597
+ // - chroma: 0-0.4 (color intensity)
1598
+ // - lightnessScale: { 50: 95, 100: 90, ... 950: 20 } (perceptual lightness values)
1599
+ //
1600
+ // Example: Change primary from blue (250) to purple (300):
1601
+ // primary: { hue: 300, chroma: 0.18, lightnessScale: { ... } }
1602
+ // ============================================================================
1603
+ colors: {
1604
+ primary: {
1605
+ hue: 250, // Blue
1606
+ chroma: 0.18, // Moderate saturation
1607
+ lightnessScale: {
1608
+ 50: 95, 100: 90, 200: 85, 300: 78, 400: 70,
1609
+ 500: 62, 600: 55, 700: 45, 800: 35, 900: 25, 950: 20
1610
+ }
1611
+ }
1612
+ // Add your brand colors here:
1613
+ // brand: {
1614
+ // hue: 340, // Pink
1615
+ // chroma: 0.20,
1616
+ // lightnessScale: { 50: 95, 100: 90, ... 950: 20 }
1617
+ // }
1618
+ }
1619
+ };
1620
+ `;
1621
+ }
1622
+
1623
+ /**
1624
+ * Deep merge two objects
1625
+ * @param {object} target - Target object
1626
+ * @param {object} source - Source object
1627
+ * @returns {object} - Merged object
1628
+ */
1629
+ function mergeDeep(target, source) {
1630
+ const output = { ...target };
1631
+ if (isObject(target) && isObject(source)) {
1632
+ Object.keys(source).forEach(key => {
1633
+ if (isObject(source[key])) {
1634
+ if (!(key in target)) {
1635
+ output[key] = source[key];
1636
+ } else {
1637
+ output[key] = mergeDeep(target[key], source[key]);
1638
+ }
1639
+ } else {
1640
+ output[key] = source[key];
1641
+ }
1642
+ });
1643
+ }
1644
+ return output;
1645
+ }
1646
+
1647
+ /**
1648
+ * Check if value is a plain object
1649
+ * @param {*} item - Value to check
1650
+ * @returns {boolean}
1651
+ */
1652
+ function isObject(item) {
1653
+ return Boolean(item && typeof item === 'object' && !Array.isArray(item));
1654
+ }
1655
+
1656
+ /**
1657
+ * Get the type of a value
1658
+ * @param {*} value - Value to check
1659
+ * @returns {string} - Type name
1660
+ */
1661
+ function getType(value) {
1662
+ if (Array.isArray(value)) {
1663
+ return 'array';
1664
+ }
1665
+
1666
+ if (value === null) {
1667
+ return 'null';
1668
+ }
1669
+
1670
+ return typeof value;
1671
+ }
1672
+
1673
+ /**
1674
+ * Validate user config shape and value types against defaults.
1675
+ * Unknown keys are warnings to keep customization flexible, but type mismatches
1676
+ * are treated as hard errors so generated SCSS remains valid.
1677
+ */
1678
+ function validateUserConfig(userConfig, defaults = defaultConfig, currentPath = '') {
1679
+ const errors = [];
1680
+ const warnings = [];
1681
+
1682
+ if (!isObject(userConfig)) {
1683
+ if (userConfig === undefined || userConfig === null) {
1684
+ return { errors, warnings };
1685
+ }
1686
+
1687
+ errors.push(`Configuration root must be an object, received ${getType(userConfig)}.`);
1688
+ return { errors, warnings };
1689
+ }
1690
+
1691
+ Object.entries(userConfig).forEach(([key, value]) => {
1692
+ const pathKey = currentPath ? `${currentPath}.${key}` : key;
1693
+ const isTopLevel = currentPath === '';
1694
+ const isFeatureKey = currentPath === 'features';
1695
+
1696
+ if (!(key in defaults)) {
1697
+ if (isTopLevel || isFeatureKey) {
1698
+ warnings.push(`Unknown config key \`${pathKey}\` will be ignored by generators.`);
1699
+ }
1700
+ return;
1701
+ }
1702
+
1703
+ const defaultValue = defaults[key];
1704
+ const expectedType = getType(defaultValue);
1705
+ const actualType = getType(value);
1706
+
1707
+ if (expectedType !== actualType) {
1708
+ errors.push(
1709
+ `Invalid type for \`${pathKey}\`: expected ${expectedType}, received ${actualType}.`
1710
+ );
1711
+ return;
1712
+ }
1713
+
1714
+ if (expectedType === 'object') {
1715
+ const nested = validateUserConfig(value, defaultValue, pathKey);
1716
+ errors.push(...nested.errors);
1717
+ warnings.push(...nested.warnings);
1718
+ }
1719
+ });
1720
+
1721
+ if (isObject(userConfig.features)) {
1722
+ Object.entries(userConfig.features).forEach(([featureKey, featureValue]) => {
1723
+ if (typeof featureValue !== 'boolean') {
1724
+ errors.push(
1725
+ `Invalid feature toggle \`features.${featureKey}\`: expected boolean, received ${getType(featureValue)}.`
1726
+ );
1727
+ }
1728
+ });
1729
+ }
1730
+
1731
+ return { errors, warnings };
1732
+ }
1733
+
1734
+ /**
1735
+ * Parse command line arguments
1736
+ */
1737
+ function parseArgs() {
1738
+ const args = process.argv.slice(2);
1739
+ const options = {
1740
+ config: 'src/apex.config.js',
1741
+ output: 'src/config',
1742
+ watch: false,
1743
+ init: false
1744
+ };
1745
+
1746
+ args.forEach(arg => {
1747
+ if (arg.startsWith('--config=')) {
1748
+ options.config = arg.split('=')[1];
1749
+ } else if (arg.startsWith('--output=')) {
1750
+ options.output = arg.split('=')[1];
1751
+ } else if (arg === '--watch' || arg === '-w') {
1752
+ options.watch = true;
1753
+ } else if (arg === '--init') {
1754
+ options.init = true;
1755
+ } else if (arg === '--help' || arg === '-h') {
1756
+ showHelp();
1757
+ process.exit(0);
1758
+ }
1759
+ });
1760
+
1761
+ return options;
1762
+ }
1763
+
1764
+ /**
1765
+ * Show help message
1766
+ */
1767
+ function showHelp() {
1768
+ console.log(`
1769
+ ApexCSS Configuration Builder
1770
+
1771
+ Usage: node config-builder.js [options]
1772
+
1773
+ Options:
1774
+ --config=<file> Config file path (default: src/apex.config.js)
1775
+ --output=<dir> Output directory (default: src/config)
1776
+ --watch, -w Watch for changes and rebuild
1777
+ --init Create sample config file
1778
+ --help, -h Show this help
1779
+
1780
+ Examples:
1781
+ node config-builder.js
1782
+ node config-builder.js --config=custom.config.js
1783
+ node config-builder.js --watch
1784
+ node config-builder.js --init
1785
+ `);
1786
+ }
1787
+
1788
+ /**
1789
+ * Load user configuration (optional)
1790
+ * Users can directly edit src/config/_custom-config.scss instead
1791
+ */
1792
+ async function loadConfig(configPath) {
1793
+ const fullPath = path.resolve(process.cwd(), configPath);
1794
+
1795
+ try {
1796
+ await fs.access(fullPath);
1797
+ const module = await import(fullPath + '?t=' + Date.now());
1798
+ const loaded = module.default || module;
1799
+
1800
+ if (!isObject(loaded)) {
1801
+ throw new Error(`Expected exported config object but received ${getType(loaded)}.`);
1802
+ }
1803
+
1804
+ return loaded;
1805
+ } catch (err) {
1806
+ if (err?.code === 'ENOENT') {
1807
+ logInfo(`Config file not found at ${configPath}; using defaults.`);
1808
+ return {};
1809
+ }
1810
+
1811
+ throw new Error(`Failed to load config file ${configPath}: ${err.message}`);
1812
+ }
1813
+ }
1814
+
1815
+ /**
1816
+ * Main build function
1817
+ */
1818
+ async function build(options) {
1819
+ try {
1820
+ // Load user config
1821
+ const userConfig = await loadConfig(options.config);
1822
+ const validation = validateUserConfig(userConfig);
1823
+
1824
+ validation.warnings.forEach(logWarn);
1825
+
1826
+ if (validation.errors.length > 0) {
1827
+ const details = validation.errors.map(error => `- ${error}`).join('\n');
1828
+ throw new Error(`Configuration validation failed:\n${details}`);
1829
+ }
1830
+
1831
+ const mergedConfig = mergeDeep(defaultConfig, userConfig);
1832
+
1833
+ // Generate SCSS
1834
+ const scss = generateSCSS(userConfig);
1835
+
1836
+ // Ensure output directory exists
1837
+ await fs.mkdir(options.output, { recursive: true });
1838
+
1839
+ // Write SCSS file
1840
+ const scssPath = path.join(options.output, '_custom-config.scss');
1841
+ await fs.writeFile(scssPath, scss);
1842
+ logInfo(`Generated: ${scssPath}`);
1843
+
1844
+ if (isObject(mergedConfig.features)) {
1845
+ const featureEntries = Object.values(mergedConfig.features);
1846
+ const enabled = featureEntries.filter(Boolean).length;
1847
+ const total = featureEntries.length;
1848
+ logInfo(`Feature toggles enabled: ${enabled}/${total}`);
1849
+ }
1850
+
1851
+ console.log('\nšŸŽ‰ Configuration built successfully!');
1852
+ console.log(' Run "npm run build" to compile the framework.\n');
1853
+
1854
+ } catch (err) {
1855
+ logError(`Build failed: ${err.message}`);
1856
+ process.exit(1);
1857
+ }
1858
+ }
1859
+
1860
+ /**
1861
+ * Initialize config file
1862
+ */
1863
+ async function init() {
1864
+ const configPath = path.resolve(process.cwd(), 'src/apex.config.js');
1865
+
1866
+ const sample = generateSampleConfig();
1867
+ await fs.writeFile(configPath, sample);
1868
+ console.log('āœ… Created: src/apex.config.js\\n');
1869
+ console.log(' Edit this file to customize ApexCSS, then run:');
1870
+ console.log(' npm run config:build\\n');
1871
+ }
1872
+
1873
+ /**
1874
+ * Watch for changes
1875
+ */
1876
+ async function watch(options) {
1877
+ const configPath = path.resolve(process.cwd(), options.config);
1878
+
1879
+ logInfo(`Watching ${options.config} for changes...`);
1880
+ console.log('');
1881
+
1882
+ // Initial build
1883
+ await build(options);
1884
+
1885
+ // Watch for changes
1886
+ const { watchFile } = await import('fs');
1887
+ watchFile(configPath, async (curr, prev) => {
1888
+ if (curr.mtime !== prev.mtime) {
1889
+ console.log(`šŸ“ Config changed, rebuilding...\\n`);
1890
+ await build(options);
1891
+ }
1892
+ });
1893
+ }
1894
+
1895
+ // Main execution
1896
+ async function main() {
1897
+ const options = parseArgs();
1898
+
1899
+ if (options.init) {
1900
+ await init();
1901
+ return;
1902
+ }
1903
+
1904
+ if (options.watch) {
1905
+ await watch(options);
1906
+ } else {
1907
+ await build(options);
1908
+ }
1909
+ }
1910
+
1911
+ /**
1912
+ * Check if this module is being run directly
1913
+ * @returns {boolean}
1914
+ */
1915
+ function isDirectExecution() {
1916
+ if (!process.argv[1]) {
1917
+ return false;
1918
+ }
1919
+
1920
+ return path.resolve(process.argv[1]) === __filename;
1921
+ }
1922
+
1923
+ if (isDirectExecution()) {
1924
+ main();
1925
+ }
1926
+
1927
+ export {
1928
+ build,
1929
+ defaultConfig,
1930
+ generateSCSS,
1931
+ isObject,
1932
+ mergeDeep,
1933
+ validateUserConfig
1934
+ };