@shohojdhara/atomix 0.3.2 → 0.3.4

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 (84) hide show
  1. package/README.md +58 -21
  2. package/dist/atomix.css +96 -121
  3. package/dist/atomix.min.css +3 -3
  4. package/dist/index.d.ts +7937 -7765
  5. package/dist/index.esm.js +3677 -4031
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +3648 -3952
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/package.json +44 -16
  12. package/scripts/atomix-cli.js +1764 -0
  13. package/scripts/build-themes.js +208 -0
  14. package/scripts/cli/interactive-init.js +520 -0
  15. package/scripts/cli/migration-tools.js +603 -0
  16. package/scripts/cli/theme-bridge.js +129 -0
  17. package/scripts/cli/token-manager.js +519 -0
  18. package/scripts/sync-theme-config.js +309 -0
  19. package/src/components/Button/Button.tsx +36 -1
  20. package/src/components/List/ListGroup.tsx +1 -2
  21. package/src/components/Popover/Popover.tsx +2 -2
  22. package/src/components/Tooltip/Tooltip.stories.tsx +49 -12
  23. package/src/components/Tooltip/Tooltip.tsx +32 -58
  24. package/src/lib/composables/useTooltip.ts +285 -0
  25. package/src/lib/config/index.ts +275 -0
  26. package/src/lib/config/loader.ts +105 -0
  27. package/src/lib/constants/cssVariables.ts +390 -0
  28. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +151 -0
  29. package/src/lib/hooks/index.ts +19 -0
  30. package/src/lib/hooks/useComponentCustomization.ts +175 -0
  31. package/src/lib/index.ts +14 -1
  32. package/src/lib/patterns/__tests__/slots.test.ts +108 -0
  33. package/src/lib/patterns/index.ts +35 -0
  34. package/src/lib/patterns/slots.tsx +421 -0
  35. package/src/lib/theme/composeTheme.ts +0 -5
  36. package/src/lib/theme/config/index.ts +1 -1
  37. package/src/lib/theme/config/loader.ts +75 -41
  38. package/src/lib/theme/config/types.ts +21 -7
  39. package/src/lib/theme/config/validator.ts +1 -1
  40. package/src/lib/theme/constants.ts +12 -2
  41. package/src/lib/theme/createTheme.ts +2 -135
  42. package/src/lib/theme/createThemeFromConfig.ts +132 -0
  43. package/src/lib/theme/cssVariableMapper.ts +261 -0
  44. package/src/lib/theme/devtools/CLI.ts +161 -76
  45. package/src/lib/theme/devtools/Comparator.tsx +343 -0
  46. package/src/lib/theme/devtools/IMPROVEMENTS.md +429 -0
  47. package/src/lib/theme/devtools/Inspector.tsx +21 -6
  48. package/src/lib/theme/devtools/LiveEditor.tsx +393 -0
  49. package/src/lib/theme/devtools/README.md +433 -0
  50. package/src/lib/theme/devtools/index.ts +12 -11
  51. package/src/lib/theme/generateCSSVariables.ts +79 -38
  52. package/src/lib/theme/index.ts +45 -246
  53. package/src/lib/theme/runtime/ThemeApplicator.ts +252 -0
  54. package/src/lib/theme/runtime/ThemeManager.test.ts +17 -1
  55. package/src/lib/theme/runtime/ThemeManager.ts +7 -7
  56. package/src/lib/theme/themeUtils.ts +27 -5
  57. package/src/lib/theme/types.ts +59 -1
  58. package/src/lib/theme-tools.ts +125 -0
  59. package/src/lib/types/components.ts +260 -72
  60. package/src/lib/types/partProps.ts +426 -0
  61. package/src/lib/utils/__tests__/componentUtils.test.ts +144 -0
  62. package/src/lib/utils/componentUtils.ts +163 -0
  63. package/src/lib/utils/index.ts +17 -57
  64. package/src/styles/01-settings/_settings.colors.scss +10 -10
  65. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  66. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  67. package/src/styles/03-generic/_generated-root.css +5 -0
  68. package/src/styles/06-components/_components.navbar.scss +12 -5
  69. package/src/styles/06-components/_components.tooltip.scss +31 -81
  70. package/src/themes/README.md +442 -0
  71. package/src/themes/themes.config.js +35 -0
  72. package/src/lib/theme/errors.test.ts +0 -207
  73. package/src/lib/theme/generators/CSSGenerator.ts +0 -311
  74. package/src/lib/theme/generators/ConfigGenerator.ts +0 -287
  75. package/src/lib/theme/generators/TypeGenerator.ts +0 -228
  76. package/src/lib/theme/generators/index.ts +0 -21
  77. package/src/lib/theme/monitoring/ThemeAnalytics.ts +0 -409
  78. package/src/lib/theme/monitoring/index.ts +0 -17
  79. package/src/lib/theme/overrides/ComponentOverrides.ts +0 -243
  80. package/src/lib/theme/overrides/index.ts +0 -15
  81. package/src/lib/theme/studio/ThemeStudio.tsx +0 -312
  82. package/src/lib/theme/studio/index.ts +0 -8
  83. package/src/lib/theme/whitelabel/WhiteLabelManager.ts +0 -364
  84. package/src/lib/theme/whitelabel/index.ts +0 -13
@@ -0,0 +1,603 @@
1
+ /**
2
+ * Migration Tools for Atomix Design System
3
+ * Helps migrate from other design systems and CSS frameworks
4
+ */
5
+
6
+ import { readFile, writeFile, readdir, stat } from 'fs/promises';
7
+ import { join, extname, relative } from 'path';
8
+ import chalk from 'chalk';
9
+ import ora from 'ora';
10
+
11
+ /**
12
+ * Tailwind to Atomix mapping
13
+ */
14
+ const tailwindToAtomix = {
15
+ // Colors
16
+ 'bg-primary': 'c-bg-primary',
17
+ 'text-primary': 'c-text-primary',
18
+ 'border-primary': 'c-border-primary',
19
+
20
+ // Spacing
21
+ 'p-0': 'u-p-0',
22
+ 'p-1': 'u-p-1',
23
+ 'p-2': 'u-p-2',
24
+ 'p-3': 'u-p-3',
25
+ 'p-4': 'u-p-4',
26
+ 'p-5': 'u-p-5',
27
+ 'p-6': 'u-p-6',
28
+ 'p-8': 'u-p-8',
29
+
30
+ 'm-0': 'u-m-0',
31
+ 'm-1': 'u-m-1',
32
+ 'm-2': 'u-m-2',
33
+ 'm-3': 'u-m-3',
34
+ 'm-4': 'u-m-4',
35
+ 'm-auto': 'u-m-auto',
36
+
37
+ // Flexbox
38
+ 'flex': 'u-flex',
39
+ 'flex-row': 'u-flex-row',
40
+ 'flex-col': 'u-flex-column',
41
+ 'items-center': 'u-items-center',
42
+ 'justify-center': 'u-justify-center',
43
+ 'justify-between': 'u-justify-between',
44
+ 'gap-1': 'u-gap-1',
45
+ 'gap-2': 'u-gap-2',
46
+ 'gap-4': 'u-gap-4',
47
+
48
+ // Display
49
+ 'hidden': 'u-hidden',
50
+ 'block': 'u-block',
51
+ 'inline-block': 'u-inline-block',
52
+ 'inline': 'u-inline',
53
+ 'grid': 'u-grid',
54
+
55
+ // Typography
56
+ 'text-xs': 'u-text-xs',
57
+ 'text-sm': 'u-text-sm',
58
+ 'text-base': 'u-text-base',
59
+ 'text-lg': 'u-text-lg',
60
+ 'text-xl': 'u-text-xl',
61
+ 'text-2xl': 'u-text-2xl',
62
+ 'font-bold': 'u-font-bold',
63
+ 'font-semibold': 'u-font-semibold',
64
+ 'font-normal': 'u-font-normal',
65
+
66
+ // Border
67
+ 'border': 'u-border',
68
+ 'border-2': 'u-border-2',
69
+ 'rounded': 'u-rounded',
70
+ 'rounded-md': 'u-rounded-md',
71
+ 'rounded-lg': 'u-rounded-lg',
72
+ 'rounded-full': 'u-rounded-full',
73
+
74
+ // Components
75
+ 'btn': 'c-btn',
76
+ 'btn-primary': 'c-btn c-btn-primary',
77
+ 'btn-secondary': 'c-btn c-btn-secondary',
78
+ 'card': 'c-card',
79
+ 'badge': 'c-badge',
80
+ 'alert': 'c-alert',
81
+ 'input': 'c-input',
82
+ 'select': 'c-select',
83
+ 'checkbox': 'c-checkbox',
84
+ 'radio': 'c-radio',
85
+
86
+ // Responsive prefixes
87
+ 'sm:': '@sm:',
88
+ 'md:': '@md:',
89
+ 'lg:': '@lg:',
90
+ 'xl:': '@xl:',
91
+
92
+ // States
93
+ 'hover:': ':hover ',
94
+ 'focus:': ':focus ',
95
+ 'active:': ':active ',
96
+ 'disabled:': ':disabled '
97
+ };
98
+
99
+ /**
100
+ * Bootstrap to Atomix mapping
101
+ */
102
+ const bootstrapToAtomix = {
103
+ // Components
104
+ 'btn': 'c-btn',
105
+ 'btn-primary': 'c-btn c-btn-primary',
106
+ 'btn-secondary': 'c-btn c-btn-secondary',
107
+ 'btn-success': 'c-btn c-btn-success',
108
+ 'btn-danger': 'c-btn c-btn-error',
109
+ 'btn-warning': 'c-btn c-btn-warning',
110
+ 'btn-info': 'c-btn c-btn-info',
111
+ 'btn-lg': 'c-btn c-btn-lg',
112
+ 'btn-sm': 'c-btn c-btn-sm',
113
+
114
+ 'card': 'c-card',
115
+ 'card-header': 'c-card__header',
116
+ 'card-body': 'c-card__body',
117
+ 'card-footer': 'c-card__footer',
118
+ 'card-title': 'c-card__title',
119
+ 'card-text': 'c-card__text',
120
+
121
+ 'alert': 'c-alert',
122
+ 'alert-primary': 'c-alert c-alert-primary',
123
+ 'alert-success': 'c-alert c-alert-success',
124
+ 'alert-danger': 'c-alert c-alert-error',
125
+ 'alert-warning': 'c-alert c-alert-warning',
126
+
127
+ 'badge': 'c-badge',
128
+ 'badge-primary': 'c-badge c-badge-primary',
129
+ 'badge-secondary': 'c-badge c-badge-secondary',
130
+
131
+ 'form-control': 'c-input',
132
+ 'form-select': 'c-select',
133
+ 'form-check': 'c-form-check',
134
+ 'form-check-input': 'c-checkbox',
135
+ 'form-label': 'c-label',
136
+
137
+ 'modal': 'c-modal',
138
+ 'modal-dialog': 'c-modal__dialog',
139
+ 'modal-content': 'c-modal__content',
140
+ 'modal-header': 'c-modal__header',
141
+ 'modal-body': 'c-modal__body',
142
+ 'modal-footer': 'c-modal__footer',
143
+
144
+ // Grid
145
+ 'container': 'o-container',
146
+ 'container-fluid': 'o-container-fluid',
147
+ 'row': 'o-row',
148
+ 'col': 'o-col',
149
+ 'col-sm': 'o-col-sm',
150
+ 'col-md': 'o-col-md',
151
+ 'col-lg': 'o-col-lg',
152
+ 'col-xl': 'o-col-xl',
153
+
154
+ // Utilities
155
+ 'd-none': 'u-hidden',
156
+ 'd-block': 'u-block',
157
+ 'd-inline': 'u-inline',
158
+ 'd-inline-block': 'u-inline-block',
159
+ 'd-flex': 'u-flex',
160
+ 'd-grid': 'u-grid',
161
+
162
+ 'text-center': 'u-text-center',
163
+ 'text-left': 'u-text-left',
164
+ 'text-right': 'u-text-right',
165
+ 'text-justify': 'u-text-justify',
166
+
167
+ 'text-primary': 'u-text-primary',
168
+ 'text-success': 'u-text-success',
169
+ 'text-danger': 'u-text-error',
170
+ 'text-warning': 'u-text-warning',
171
+ 'text-muted': 'u-text-muted',
172
+
173
+ 'bg-primary': 'u-bg-primary',
174
+ 'bg-success': 'u-bg-success',
175
+ 'bg-danger': 'u-bg-error',
176
+ 'bg-warning': 'u-bg-warning',
177
+
178
+ 'p-0': 'u-p-0',
179
+ 'p-1': 'u-p-1',
180
+ 'p-2': 'u-p-2',
181
+ 'p-3': 'u-p-3',
182
+ 'p-4': 'u-p-4',
183
+ 'p-5': 'u-p-5',
184
+
185
+ 'm-0': 'u-m-0',
186
+ 'm-1': 'u-m-1',
187
+ 'm-2': 'u-m-2',
188
+ 'm-3': 'u-m-3',
189
+ 'm-4': 'u-m-4',
190
+ 'm-5': 'u-m-5',
191
+ 'm-auto': 'u-m-auto',
192
+
193
+ 'rounded': 'u-rounded',
194
+ 'rounded-circle': 'u-rounded-full',
195
+ 'border': 'u-border',
196
+
197
+ 'w-25': 'u-w-25',
198
+ 'w-50': 'u-w-50',
199
+ 'w-75': 'u-w-75',
200
+ 'w-100': 'u-w-100',
201
+ 'h-25': 'u-h-25',
202
+ 'h-50': 'u-h-50',
203
+ 'h-75': 'u-h-75',
204
+ 'h-100': 'u-h-100'
205
+ };
206
+
207
+ /**
208
+ * SCSS Variable Migration
209
+ */
210
+ const scssVariableMigration = {
211
+ // Colors
212
+ '$primary': 'var(--atomix-color-primary)',
213
+ '$secondary': 'var(--atomix-color-secondary)',
214
+ '$success': 'var(--atomix-color-success)',
215
+ '$danger': 'var(--atomix-color-error)',
216
+ '$warning': 'var(--atomix-color-warning)',
217
+ '$info': 'var(--atomix-color-info)',
218
+ '$light': 'var(--atomix-color-light)',
219
+ '$dark': 'var(--atomix-color-dark)',
220
+
221
+ // Spacing
222
+ '$spacer': 'var(--atomix-space-4)',
223
+ '$spacing-xs': 'var(--atomix-space-1)',
224
+ '$spacing-sm': 'var(--atomix-space-2)',
225
+ '$spacing-md': 'var(--atomix-space-4)',
226
+ '$spacing-lg': 'var(--atomix-space-6)',
227
+ '$spacing-xl': 'var(--atomix-space-8)',
228
+
229
+ // Typography
230
+ '$font-family-base': 'var(--atomix-font-family-base)',
231
+ '$font-size-base': 'var(--atomix-font-size-base)',
232
+ '$font-weight-normal': 'var(--atomix-font-weight-normal)',
233
+ '$font-weight-bold': 'var(--atomix-font-weight-bold)',
234
+ '$line-height-base': 'var(--atomix-line-height-base)',
235
+
236
+ // Border
237
+ '$border-radius': 'var(--atomix-radius-md)',
238
+ '$border-width': 'var(--atomix-border-width)',
239
+ '$border-color': 'var(--atomix-color-border)'
240
+ };
241
+
242
+ /**
243
+ * Migrate Tailwind classes to Atomix
244
+ */
245
+ export async function migrateTailwind(sourcePath, options = {}) {
246
+ const spinner = ora('Migrating from Tailwind CSS...').start();
247
+ const report = {
248
+ filesProcessed: 0,
249
+ classesReplaced: 0,
250
+ warnings: [],
251
+ errors: []
252
+ };
253
+
254
+ try {
255
+ const files = await getAllFiles(sourcePath, ['.jsx', '.tsx', '.js', '.ts', '.html']);
256
+
257
+ for (const file of files) {
258
+ spinner.text = `Processing ${relative(process.cwd(), file)}...`;
259
+
260
+ try {
261
+ let content = await readFile(file, 'utf8');
262
+ const originalContent = content;
263
+ let replacementCount = 0;
264
+
265
+ // Replace className attributes
266
+ content = content.replace(/className=["']([^"']+)["']/g, (match, classes) => {
267
+ const classList = classes.split(' ');
268
+ const newClasses = classList.map(cls => {
269
+ const trimmed = cls.trim();
270
+ if (tailwindToAtomix[trimmed]) {
271
+ replacementCount++;
272
+ return tailwindToAtomix[trimmed];
273
+ }
274
+
275
+ // Check for responsive prefixes
276
+ for (const [prefix, replacement] of Object.entries(tailwindToAtomix)) {
277
+ if (trimmed.startsWith(prefix)) {
278
+ const rest = trimmed.substring(prefix.length);
279
+ if (tailwindToAtomix[rest]) {
280
+ replacementCount++;
281
+ return replacement + tailwindToAtomix[rest];
282
+ }
283
+ }
284
+ }
285
+
286
+ // If no mapping found, keep original
287
+ if (trimmed && !trimmed.startsWith('c-') && !trimmed.startsWith('u-') && !trimmed.startsWith('o-')) {
288
+ report.warnings.push({
289
+ file: relative(process.cwd(), file),
290
+ class: trimmed,
291
+ message: 'No Atomix equivalent found'
292
+ });
293
+ }
294
+
295
+ return trimmed;
296
+ });
297
+
298
+ return `className="${newClasses.join(' ')}"`;
299
+ });
300
+
301
+ // Replace class attributes in HTML
302
+ content = content.replace(/class=["']([^"']+)["']/g, (match, classes) => {
303
+ const classList = classes.split(' ');
304
+ const newClasses = classList.map(cls => {
305
+ const trimmed = cls.trim();
306
+ if (tailwindToAtomix[trimmed]) {
307
+ replacementCount++;
308
+ return tailwindToAtomix[trimmed];
309
+ }
310
+ return trimmed;
311
+ });
312
+
313
+ return `class="${newClasses.join(' ')}"`;
314
+ });
315
+
316
+ if (content !== originalContent) {
317
+ if (options.dryRun) {
318
+ console.log(chalk.yellow(` Would update: ${file}`));
319
+ } else {
320
+ await writeFile(file, content, 'utf8');
321
+ report.filesProcessed++;
322
+ report.classesReplaced += replacementCount;
323
+ }
324
+ }
325
+
326
+ } catch (error) {
327
+ report.errors.push({
328
+ file: relative(process.cwd(), file),
329
+ error: error.message
330
+ });
331
+ }
332
+ }
333
+
334
+ spinner.succeed(chalk.green('Tailwind migration complete!'));
335
+
336
+ return report;
337
+
338
+ } catch (error) {
339
+ spinner.fail(chalk.red('Migration failed'));
340
+ throw error;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Migrate Bootstrap classes to Atomix
346
+ */
347
+ export async function migrateBootstrap(sourcePath, options = {}) {
348
+ const spinner = ora('Migrating from Bootstrap...').start();
349
+ const report = {
350
+ filesProcessed: 0,
351
+ classesReplaced: 0,
352
+ warnings: [],
353
+ errors: []
354
+ };
355
+
356
+ try {
357
+ const files = await getAllFiles(sourcePath, ['.jsx', '.tsx', '.js', '.ts', '.html']);
358
+
359
+ for (const file of files) {
360
+ spinner.text = `Processing ${relative(process.cwd(), file)}...`;
361
+
362
+ try {
363
+ let content = await readFile(file, 'utf8');
364
+ const originalContent = content;
365
+ let replacementCount = 0;
366
+
367
+ // Replace className/class attributes
368
+ const classPattern = /(className|class)=["']([^"']+)["']/g;
369
+ content = content.replace(classPattern, (match, attr, classes) => {
370
+ const classList = classes.split(' ');
371
+ const newClasses = classList.map(cls => {
372
+ const trimmed = cls.trim();
373
+
374
+ // Check direct mapping
375
+ if (bootstrapToAtomix[trimmed]) {
376
+ replacementCount++;
377
+ return bootstrapToAtomix[trimmed];
378
+ }
379
+
380
+ // Check for col-{breakpoint}-{size} pattern
381
+ const colPattern = /^col-(sm|md|lg|xl)-(\d+)$/;
382
+ const colMatch = trimmed.match(colPattern);
383
+ if (colMatch) {
384
+ replacementCount++;
385
+ return `o-col-${colMatch[1]}-${colMatch[2]}`;
386
+ }
387
+
388
+ // Check for offset pattern
389
+ const offsetPattern = /^offset-(sm|md|lg|xl)-(\d+)$/;
390
+ const offsetMatch = trimmed.match(offsetPattern);
391
+ if (offsetMatch) {
392
+ replacementCount++;
393
+ return `o-offset-${offsetMatch[1]}-${offsetMatch[2]}`;
394
+ }
395
+
396
+ // If no mapping found, keep original
397
+ if (trimmed && !trimmed.startsWith('c-') && !trimmed.startsWith('u-') && !trimmed.startsWith('o-')) {
398
+ report.warnings.push({
399
+ file: relative(process.cwd(), file),
400
+ class: trimmed,
401
+ message: 'No Atomix equivalent found'
402
+ });
403
+ }
404
+
405
+ return trimmed;
406
+ });
407
+
408
+ return `${attr}="${newClasses.join(' ')}"`;
409
+ });
410
+
411
+ if (content !== originalContent) {
412
+ if (options.dryRun) {
413
+ console.log(chalk.yellow(` Would update: ${file}`));
414
+ } else {
415
+ await writeFile(file, content, 'utf8');
416
+ report.filesProcessed++;
417
+ report.classesReplaced += replacementCount;
418
+ }
419
+ }
420
+
421
+ } catch (error) {
422
+ report.errors.push({
423
+ file: relative(process.cwd(), file),
424
+ error: error.message
425
+ });
426
+ }
427
+ }
428
+
429
+ spinner.succeed(chalk.green('Bootstrap migration complete!'));
430
+
431
+ return report;
432
+
433
+ } catch (error) {
434
+ spinner.fail(chalk.red('Migration failed'));
435
+ throw error;
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Migrate SCSS variables to CSS custom properties
441
+ */
442
+ export async function migrateSCSSVariables(sourcePath, options = {}) {
443
+ const spinner = ora('Migrating SCSS variables to design tokens...').start();
444
+ const report = {
445
+ filesProcessed: 0,
446
+ variablesReplaced: 0,
447
+ warnings: [],
448
+ errors: []
449
+ };
450
+
451
+ try {
452
+ const files = await getAllFiles(sourcePath, ['.scss', '.sass', '.css']);
453
+
454
+ for (const file of files) {
455
+ spinner.text = `Processing ${relative(process.cwd(), file)}...`;
456
+
457
+ try {
458
+ let content = await readFile(file, 'utf8');
459
+ const originalContent = content;
460
+ let replacementCount = 0;
461
+
462
+ // Replace SCSS variables with CSS custom properties
463
+ for (const [scssVar, cssVar] of Object.entries(scssVariableMigration)) {
464
+ const regex = new RegExp(`\\${scssVar}(?![a-z-])`, 'g');
465
+ const matches = content.match(regex);
466
+ if (matches) {
467
+ replacementCount += matches.length;
468
+ content = content.replace(regex, cssVar);
469
+ }
470
+ }
471
+
472
+ // Find remaining SCSS variables that weren't migrated
473
+ const remainingVars = content.match(/\$[a-z-]+/gi);
474
+ if (remainingVars) {
475
+ remainingVars.forEach(varName => {
476
+ if (!scssVariableMigration[varName]) {
477
+ report.warnings.push({
478
+ file: relative(process.cwd(), file),
479
+ variable: varName,
480
+ message: 'No design token equivalent found'
481
+ });
482
+ }
483
+ });
484
+ }
485
+
486
+ if (content !== originalContent) {
487
+ if (options.dryRun) {
488
+ console.log(chalk.yellow(` Would update: ${file}`));
489
+ } else {
490
+ await writeFile(file, content, 'utf8');
491
+ report.filesProcessed++;
492
+ report.variablesReplaced += replacementCount;
493
+ }
494
+ }
495
+
496
+ } catch (error) {
497
+ report.errors.push({
498
+ file: relative(process.cwd(), file),
499
+ error: error.message
500
+ });
501
+ }
502
+ }
503
+
504
+ spinner.succeed(chalk.green('SCSS variable migration complete!'));
505
+
506
+ return report;
507
+
508
+ } catch (error) {
509
+ spinner.fail(chalk.red('Migration failed'));
510
+ throw error;
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Get all files recursively
516
+ */
517
+ async function getAllFiles(dir, extensions = []) {
518
+ const files = [];
519
+
520
+ async function walk(currentPath) {
521
+ const entries = await readdir(currentPath);
522
+
523
+ for (const entry of entries) {
524
+ const fullPath = join(currentPath, entry);
525
+ const stats = await stat(fullPath);
526
+
527
+ if (stats.isDirectory()) {
528
+ // Skip node_modules and hidden directories
529
+ if (!entry.startsWith('.') && entry !== 'node_modules') {
530
+ await walk(fullPath);
531
+ }
532
+ } else if (stats.isFile()) {
533
+ const ext = extname(fullPath);
534
+ if (extensions.length === 0 || extensions.includes(ext)) {
535
+ files.push(fullPath);
536
+ }
537
+ }
538
+ }
539
+ }
540
+
541
+ await walk(dir);
542
+ return files;
543
+ }
544
+
545
+ /**
546
+ * Display migration report
547
+ */
548
+ export function displayMigrationReport(report) {
549
+ console.log(chalk.bold('\nšŸ“Š Migration Report\n'));
550
+ console.log(chalk.gray('=' .repeat(50)));
551
+
552
+ console.log(chalk.cyan(`Files processed: ${report.filesProcessed}`));
553
+ console.log(chalk.cyan(`Classes/Variables replaced: ${report.classesReplaced || report.variablesReplaced}`));
554
+
555
+ if (report.warnings.length > 0) {
556
+ console.log(chalk.yellow(`\nāš ļø Warnings (${report.warnings.length}):\n`));
557
+
558
+ // Group warnings by type
559
+ const groupedWarnings = {};
560
+ report.warnings.forEach(warning => {
561
+ const key = warning.class || warning.variable;
562
+ if (!groupedWarnings[key]) {
563
+ groupedWarnings[key] = [];
564
+ }
565
+ groupedWarnings[key].push(warning.file);
566
+ });
567
+
568
+ Object.entries(groupedWarnings).forEach(([key, files]) => {
569
+ console.log(chalk.yellow(` • ${key}`));
570
+ console.log(chalk.gray(` Found in ${files.length} file(s)`));
571
+ if (files.length <= 3) {
572
+ files.forEach(file => console.log(chalk.gray(` - ${file}`)));
573
+ }
574
+ });
575
+ }
576
+
577
+ if (report.errors.length > 0) {
578
+ console.log(chalk.red(`\nāŒ Errors (${report.errors.length}):\n`));
579
+ report.errors.forEach(error => {
580
+ console.log(chalk.red(` • ${error.file}`));
581
+ console.log(chalk.gray(` ${error.error}`));
582
+ });
583
+ }
584
+
585
+ console.log(chalk.gray('\n' + '=' .repeat(50)));
586
+
587
+ if (report.warnings.length > 0) {
588
+ console.log(chalk.yellow('\nšŸ’” Suggestions:'));
589
+ console.log(chalk.gray(' 1. Review unmapped classes/variables'));
590
+ console.log(chalk.gray(' 2. Create custom mappings for your specific needs'));
591
+ console.log(chalk.gray(' 3. Consider using Atomix utilities for better compatibility'));
592
+ }
593
+
594
+ console.log(chalk.green('\nāœ… Migration complete!'));
595
+ console.log(chalk.gray('Please review the changes and test your application.\n'));
596
+ }
597
+
598
+ export default {
599
+ migrateTailwind,
600
+ migrateBootstrap,
601
+ migrateSCSSVariables,
602
+ displayMigrationReport
603
+ };