@shohojdhara/atomix 0.4.7 → 0.4.9

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 (176) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +172 -157
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +4 -4
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1274 -164
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1099 -83
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2106 -1050
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1663 -638
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +442 -270
  19. package/dist/index.esm.js +1947 -680
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1982 -712
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.min.js +1 -1
  24. package/dist/index.min.js.map +1 -1
  25. package/package.json +6 -3
  26. package/scripts/atomix-cli.js +136 -1827
  27. package/scripts/cli/__tests__/basic.test.js +3 -2
  28. package/scripts/cli/__tests__/clean.test.js +278 -0
  29. package/scripts/cli/__tests__/component-validator.test.js +433 -0
  30. package/scripts/cli/__tests__/generator.test.js +613 -0
  31. package/scripts/cli/__tests__/glass-motion.test.js +256 -0
  32. package/scripts/cli/__tests__/integration.test.js +719 -108
  33. package/scripts/cli/__tests__/migrate.test.js +74 -0
  34. package/scripts/cli/__tests__/security.test.js +206 -0
  35. package/scripts/cli/__tests__/test-setup.js +3 -1
  36. package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
  37. package/scripts/cli/__tests__/token-provider.test.js +361 -0
  38. package/scripts/cli/__tests__/utils.test.js +5 -5
  39. package/scripts/cli/commands/benchmark.js +105 -0
  40. package/scripts/cli/commands/build-theme.js +115 -0
  41. package/scripts/cli/commands/clean.js +109 -0
  42. package/scripts/cli/commands/doctor.js +88 -0
  43. package/scripts/cli/commands/generate.js +218 -0
  44. package/scripts/cli/commands/init.js +73 -0
  45. package/scripts/cli/commands/migrate.js +106 -0
  46. package/scripts/cli/commands/sync-tokens.js +206 -0
  47. package/scripts/cli/commands/theme-bridge.js +248 -0
  48. package/scripts/cli/commands/tokens.js +157 -0
  49. package/scripts/cli/commands/validate.js +194 -0
  50. package/scripts/cli/internal/ai-engine.js +156 -0
  51. package/scripts/cli/internal/compiler.js +114 -0
  52. package/scripts/cli/internal/component-validator.js +443 -0
  53. package/scripts/cli/internal/config-loader.js +162 -0
  54. package/scripts/cli/internal/filesystem.js +158 -0
  55. package/scripts/cli/internal/generator.js +430 -0
  56. package/scripts/cli/internal/glass-generator.js +398 -0
  57. package/scripts/cli/internal/hook-generator.js +369 -0
  58. package/scripts/cli/internal/hooks.js +61 -0
  59. package/scripts/cli/internal/itcss-generator.js +565 -0
  60. package/scripts/cli/internal/motion-generator.js +679 -0
  61. package/scripts/cli/internal/template-engine.js +301 -0
  62. package/scripts/cli/internal/theme-bridge.js +664 -0
  63. package/scripts/cli/internal/tokens/engine.js +122 -0
  64. package/scripts/cli/internal/tokens/provider.js +34 -0
  65. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  66. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  67. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  68. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  69. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  70. package/scripts/cli/internal/validator.js +276 -0
  71. package/scripts/cli/internal/wizard.js +115 -0
  72. package/scripts/cli/mappings.js +23 -0
  73. package/scripts/cli/migration-tools.js +164 -94
  74. package/scripts/cli/plugins/style-dictionary.js +46 -0
  75. package/scripts/cli/templates/README.md +525 -95
  76. package/scripts/cli/templates/common-templates.js +40 -14
  77. package/scripts/cli/templates/components/react-component.ts +282 -0
  78. package/scripts/cli/templates/config/project-config.ts +112 -0
  79. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  80. package/scripts/cli/templates/index.js +19 -4
  81. package/scripts/cli/templates/index.ts +171 -0
  82. package/scripts/cli/templates/next-templates.js +72 -0
  83. package/scripts/cli/templates/react-templates.js +70 -126
  84. package/scripts/cli/templates/scss-templates.js +35 -35
  85. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  86. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  87. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  88. package/scripts/cli/templates/token-templates.js +337 -1
  89. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  90. package/scripts/cli/templates/types/component-types.ts +145 -0
  91. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  92. package/scripts/cli/templates/vanilla-templates.js +39 -0
  93. package/scripts/cli/token-manager.js +8 -2
  94. package/scripts/cli/utils/cache-manager.js +240 -0
  95. package/scripts/cli/utils/detector.js +46 -0
  96. package/scripts/cli/utils/diagnostics.js +289 -0
  97. package/scripts/cli/utils/error.js +89 -0
  98. package/scripts/cli/utils/helpers.js +67 -0
  99. package/scripts/cli/utils/logger.js +75 -0
  100. package/scripts/cli/utils/security.js +302 -0
  101. package/scripts/cli/utils/telemetry.js +115 -0
  102. package/scripts/cli/utils/validation.js +37 -0
  103. package/scripts/cli/utils.js +28 -341
  104. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  105. package/src/components/Accordion/Accordion.test.tsx +0 -17
  106. package/src/components/Accordion/Accordion.tsx +0 -4
  107. package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
  108. package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
  109. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
  110. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  111. package/src/components/AtomixGlass/README.md +25 -10
  112. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
  113. package/src/components/AtomixGlass/animation-system.ts +578 -0
  114. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  115. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  116. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  117. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  118. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  119. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  120. package/src/components/Avatar/Avatar.tsx +1 -1
  121. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  122. package/src/components/Button/Button.stories.tsx +10 -0
  123. package/src/components/Button/Button.test.tsx +16 -11
  124. package/src/components/Button/Button.tsx +4 -4
  125. package/src/components/Card/Card.tsx +1 -1
  126. package/src/components/Dropdown/Dropdown.tsx +12 -12
  127. package/src/components/Form/Select.tsx +62 -3
  128. package/src/components/Modal/Modal.tsx +14 -3
  129. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  130. package/src/components/Slider/Slider.stories.tsx +3 -3
  131. package/src/components/Slider/Slider.tsx +38 -0
  132. package/src/components/Steps/Steps.tsx +3 -3
  133. package/src/components/Tabs/Tabs.tsx +77 -8
  134. package/src/components/Testimonial/Testimonial.tsx +1 -1
  135. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  136. package/src/components/TypedButton/TypedButton.tsx +39 -0
  137. package/src/components/TypedButton/index.ts +2 -0
  138. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  139. package/src/lib/composables/index.ts +4 -7
  140. package/src/lib/composables/types.ts +45 -0
  141. package/src/lib/composables/useAccordion.ts +0 -7
  142. package/src/lib/composables/useAtomixGlass.ts +148 -6
  143. package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
  144. package/src/lib/composables/useChartExport.ts +3 -13
  145. package/src/lib/composables/useDropdown.ts +66 -0
  146. package/src/lib/composables/useFocusTrap.ts +80 -0
  147. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  148. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  149. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  150. package/src/lib/composables/useTooltip.ts +16 -0
  151. package/src/lib/composables/useTypedButton.ts +66 -0
  152. package/src/lib/config/index.ts +62 -5
  153. package/src/lib/constants/components.ts +62 -7
  154. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  155. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  156. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  157. package/src/lib/types/components.ts +37 -11
  158. package/src/lib/types/glass.ts +35 -0
  159. package/src/lib/types/index.ts +1 -0
  160. package/src/lib/utils/displacement-generator.ts +1 -1
  161. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  162. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  163. package/src/styles/06-components/_components.atomix-glass.scss +17 -21
  164. package/src/styles/06-components/_components.edge-panel.scss +1 -5
  165. package/src/styles/06-components/_components.modal.scss +1 -4
  166. package/src/styles/06-components/_components.navbar.scss +1 -1
  167. package/src/styles/06-components/_components.testbutton.scss +212 -0
  168. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  169. package/src/styles/06-components/_components.tooltip.scss +9 -5
  170. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  171. package/src/styles/99-utilities/_index.scss +1 -0
  172. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  173. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  174. package/scripts/cli/component-generator.js +0 -564
  175. package/scripts/cli/interactive-init.js +0 -357
  176. package/src/styles/06-components/old.chart.styles.scss +0 -2788
@@ -4,30 +4,57 @@
4
4
  */
5
5
 
6
6
  import { readFile, writeFile, readdir, lstat } from 'fs/promises';
7
- import { join, extname, relative } from 'path';
7
+ import { join, extname, relative, basename } from 'path';
8
8
  import chalk from 'chalk';
9
9
  import ora from 'ora';
10
- import { validatePath, sanitizeInput, AtomixCLIError } from './utils.js';
11
- import {
12
- tailwindToAtomix,
13
- bootstrapToAtomix,
14
- scssVariableMigration
15
- } from './mappings.js';
10
+ import { filesystem } from './internal/filesystem.js';
11
+ import { AtomixCLIError } from './utils/error.js';
12
+ import { tailwindToAtomix, bootstrapToAtomix, scssVariableMigration } from './mappings.js';
13
+ import { logger } from './utils/logger.js';
16
14
 
15
+ const sanitizeInput = (input) => input.trim(); // Simple mock if sanitizeInput is missing
17
16
 
17
+ /**
18
+ * Show a simple side-by-side diff for migration preview
19
+ */
20
+ function showPreviewDiff(file, original, modified) {
21
+ const originalLines = original.split('\n');
22
+ const modifiedLines = modified.split('\n');
23
+ const diffs = [];
24
+
25
+ for (let i = 0; i < originalLines.length; i++) {
26
+ if (originalLines[i] !== modifiedLines[i]) {
27
+ diffs.push({
28
+ line: i + 1,
29
+ old: originalLines[i].trim(),
30
+ new: modifiedLines[i].trim()
31
+ });
32
+ }
33
+ }
34
+
35
+ if (diffs.length > 0) {
36
+ console.log(`\n${chalk.bold.blue('Preview for:')} ${chalk.cyan(relative(process.cwd(), file))}`);
37
+ diffs.slice(0, 5).forEach(d => {
38
+ console.log(`${chalk.gray(d.line + ':')} ${chalk.red('- ' + d.old)}`);
39
+ console.log(`${chalk.gray(' ')} ${chalk.green('+ ' + d.new)}`);
40
+ });
41
+ if (diffs.length > 5) {
42
+ console.log(chalk.gray(` ... and ${diffs.length - 5} more changes`));
43
+ }
44
+ }
45
+ }
18
46
 
19
47
  /**
20
48
  * Migrate Tailwind classes to Atomix
21
49
  */
22
50
  export async function migrateTailwind(sourcePath, options = {}) {
23
51
  const sanitizedSource = sanitizeInput(sourcePath);
24
- const sourceValidation = validatePath(sanitizedSource);
52
+ const sourceValidation = filesystem.validatePath(sanitizedSource);
25
53
  if (!sourceValidation.isValid) {
26
- throw new AtomixCLIError(
27
- sourceValidation.error,
28
- 'INVALID_PATH',
29
- ['Provide a valid path within the project', 'Check for typos']
30
- );
54
+ throw new AtomixCLIError(sourceValidation.error, 'INVALID_PATH', [
55
+ 'Provide a valid path within the project',
56
+ 'Check for typos',
57
+ ]);
31
58
  }
32
59
  const safeSource = sourceValidation.safePath;
33
60
  const spinner = ora('Migrating from Tailwind CSS...').start();
@@ -35,19 +62,23 @@ export async function migrateTailwind(sourcePath, options = {}) {
35
62
  filesProcessed: 0,
36
63
  classesReplaced: 0,
37
64
  warnings: [],
38
- errors: []
65
+ errors: [],
39
66
  };
40
67
 
41
68
  try {
42
69
  const files = await getAllFiles(safeSource, ['.jsx', '.tsx', '.js', '.ts', '.html']);
43
70
 
44
- for (const file of files) {
45
- spinner.text = `Processing ${relative(process.cwd(), file)}...`;
71
+ let processedCount = 0;
46
72
 
47
- try {
48
- let content = await readFile(file, 'utf8');
49
- const originalContent = content;
50
- let replacementCount = 0;
73
+ // Process files in batches to prevent "too many open files" errors
74
+ const batchSize = 50;
75
+ for (let i = 0; i < files.length; i += batchSize) {
76
+ const batch = files.slice(i, i + batchSize);
77
+ await Promise.all(batch.map(async (file) => {
78
+ try {
79
+ let content = await readFile(file, 'utf8');
80
+ const originalContent = content;
81
+ let replacementCount = 0;
51
82
 
52
83
  // Replace className attributes
53
84
  content = content.replace(/className=["']([^"']+)["']/g, (match, classes) => {
@@ -71,11 +102,16 @@ export async function migrateTailwind(sourcePath, options = {}) {
71
102
  }
72
103
 
73
104
  // If no mapping found, keep original
74
- if (trimmed && !trimmed.startsWith('c-') && !trimmed.startsWith('u-') && !trimmed.startsWith('o-')) {
105
+ if (
106
+ trimmed &&
107
+ !trimmed.startsWith('c-') &&
108
+ !trimmed.startsWith('u-') &&
109
+ !trimmed.startsWith('o-')
110
+ ) {
75
111
  report.warnings.push({
76
112
  file: relative(process.cwd(), file),
77
113
  class: trimmed,
78
- message: 'No Atomix equivalent found'
114
+ message: 'No Atomix equivalent found',
79
115
  });
80
116
  }
81
117
 
@@ -101,8 +137,12 @@ export async function migrateTailwind(sourcePath, options = {}) {
101
137
  });
102
138
 
103
139
  if (content !== originalContent) {
104
- if (options.dryRun) {
105
- console.log(chalk.yellow(` Would update: ${file}`));
140
+ if (options.dryRun || options.preview) {
141
+ if (options.preview) {
142
+ showPreviewDiff(file, originalContent, content);
143
+ } else {
144
+ console.log(chalk.yellow(` Would update: ${file}`));
145
+ }
106
146
  } else {
107
147
  await writeFile(file, content, 'utf8');
108
148
  report.filesProcessed++;
@@ -110,18 +150,21 @@ export async function migrateTailwind(sourcePath, options = {}) {
110
150
  }
111
151
  }
112
152
 
113
- } catch (error) {
114
- report.errors.push({
115
- file: relative(process.cwd(), file),
116
- error: error.message
117
- });
118
- }
153
+ processedCount++;
154
+ spinner.text = `Processing files (${processedCount}/${files.length})...`;
155
+
156
+ } catch (error) {
157
+ report.errors.push({
158
+ file: relative(process.cwd(), file),
159
+ error: error.message
160
+ });
161
+ }
162
+ }));
119
163
  }
120
164
 
121
165
  spinner.succeed(chalk.green('Tailwind migration complete!'));
122
166
 
123
167
  return report;
124
-
125
168
  } catch (error) {
126
169
  spinner.fail(chalk.red('Migration failed'));
127
170
  throw error;
@@ -133,13 +176,12 @@ export async function migrateTailwind(sourcePath, options = {}) {
133
176
  */
134
177
  export async function migrateBootstrap(sourcePath, options = {}) {
135
178
  const sanitizedSource = sanitizeInput(sourcePath);
136
- const sourceValidation = validatePath(sanitizedSource);
179
+ const sourceValidation = filesystem.validatePath(sanitizedSource);
137
180
  if (!sourceValidation.isValid) {
138
- throw new AtomixCLIError(
139
- sourceValidation.error,
140
- 'INVALID_PATH',
141
- ['Provide a valid path within the project', 'Check for typos']
142
- );
181
+ throw new AtomixCLIError(sourceValidation.error, 'INVALID_PATH', [
182
+ 'Provide a valid path within the project',
183
+ 'Check for typos',
184
+ ]);
143
185
  }
144
186
  const safeSource = sourceValidation.safePath;
145
187
  const spinner = ora('Migrating from Bootstrap...').start();
@@ -147,19 +189,22 @@ export async function migrateBootstrap(sourcePath, options = {}) {
147
189
  filesProcessed: 0,
148
190
  classesReplaced: 0,
149
191
  warnings: [],
150
- errors: []
192
+ errors: [],
151
193
  };
152
194
 
153
195
  try {
154
196
  const files = await getAllFiles(safeSource, ['.jsx', '.tsx', '.js', '.ts', '.html']);
155
197
 
156
- for (const file of files) {
157
- spinner.text = `Processing ${relative(process.cwd(), file)}...`;
198
+ let processedCount = 0;
158
199
 
159
- try {
160
- let content = await readFile(file, 'utf8');
161
- const originalContent = content;
162
- let replacementCount = 0;
200
+ const batchSize = 50;
201
+ for (let i = 0; i < files.length; i += batchSize) {
202
+ const batch = files.slice(i, i + batchSize);
203
+ await Promise.all(batch.map(async (file) => {
204
+ try {
205
+ let content = await readFile(file, 'utf8');
206
+ const originalContent = content;
207
+ let replacementCount = 0;
163
208
 
164
209
  // Replace className/class attributes
165
210
  const classPattern = /(className|class)=["']([^"']+)["']/g;
@@ -191,11 +236,16 @@ export async function migrateBootstrap(sourcePath, options = {}) {
191
236
  }
192
237
 
193
238
  // If no mapping found, keep original
194
- if (trimmed && !trimmed.startsWith('c-') && !trimmed.startsWith('u-') && !trimmed.startsWith('o-')) {
239
+ if (
240
+ trimmed &&
241
+ !trimmed.startsWith('c-') &&
242
+ !trimmed.startsWith('u-') &&
243
+ !trimmed.startsWith('o-')
244
+ ) {
195
245
  report.warnings.push({
196
246
  file: relative(process.cwd(), file),
197
247
  class: trimmed,
198
- message: 'No Atomix equivalent found'
248
+ message: 'No Atomix equivalent found',
199
249
  });
200
250
  }
201
251
 
@@ -206,8 +256,12 @@ export async function migrateBootstrap(sourcePath, options = {}) {
206
256
  });
207
257
 
208
258
  if (content !== originalContent) {
209
- if (options.dryRun) {
210
- console.log(chalk.yellow(` Would update: ${file}`));
259
+ if (options.dryRun || options.preview) {
260
+ if (options.preview) {
261
+ showPreviewDiff(file, originalContent, content);
262
+ } else {
263
+ console.log(chalk.yellow(` Would update: ${file}`));
264
+ }
211
265
  } else {
212
266
  await writeFile(file, content, 'utf8');
213
267
  report.filesProcessed++;
@@ -215,18 +269,21 @@ export async function migrateBootstrap(sourcePath, options = {}) {
215
269
  }
216
270
  }
217
271
 
218
- } catch (error) {
219
- report.errors.push({
220
- file: relative(process.cwd(), file),
221
- error: error.message
222
- });
223
- }
272
+ processedCount++;
273
+ spinner.text = `Processing files (${processedCount}/${files.length})...`;
274
+
275
+ } catch (error) {
276
+ report.errors.push({
277
+ file: relative(process.cwd(), file),
278
+ error: error.message
279
+ });
280
+ }
281
+ }));
224
282
  }
225
283
 
226
284
  spinner.succeed(chalk.green('Bootstrap migration complete!'));
227
285
 
228
286
  return report;
229
-
230
287
  } catch (error) {
231
288
  spinner.fail(chalk.red('Migration failed'));
232
289
  throw error;
@@ -238,13 +295,12 @@ export async function migrateBootstrap(sourcePath, options = {}) {
238
295
  */
239
296
  export async function migrateSCSSVariables(sourcePath, options = {}) {
240
297
  const sanitizedSource = sanitizeInput(sourcePath);
241
- const sourceValidation = validatePath(sanitizedSource);
298
+ const sourceValidation = filesystem.validatePath(sanitizedSource);
242
299
  if (!sourceValidation.isValid) {
243
- throw new AtomixCLIError(
244
- sourceValidation.error,
245
- 'INVALID_PATH',
246
- ['Provide a valid path within the project', 'Check for typos']
247
- );
300
+ throw new AtomixCLIError(sourceValidation.error, 'INVALID_PATH', [
301
+ 'Provide a valid path within the project',
302
+ 'Check for typos',
303
+ ]);
248
304
  }
249
305
  const safeSource = sourceValidation.safePath;
250
306
  const spinner = ora('Migrating SCSS variables to design tokens...').start();
@@ -252,19 +308,22 @@ export async function migrateSCSSVariables(sourcePath, options = {}) {
252
308
  filesProcessed: 0,
253
309
  variablesReplaced: 0,
254
310
  warnings: [],
255
- errors: []
311
+ errors: [],
256
312
  };
257
313
 
258
314
  try {
259
315
  const files = await getAllFiles(safeSource, ['.scss', '.sass', '.css']);
260
316
 
261
- for (const file of files) {
262
- spinner.text = `Processing ${relative(process.cwd(), file)}...`;
317
+ let processedCount = 0;
263
318
 
264
- try {
265
- let content = await readFile(file, 'utf8');
266
- const originalContent = content;
267
- let replacementCount = 0;
319
+ const batchSize = 50;
320
+ for (let i = 0; i < files.length; i += batchSize) {
321
+ const batch = files.slice(i, i + batchSize);
322
+ await Promise.all(batch.map(async (file) => {
323
+ try {
324
+ let content = await readFile(file, 'utf8');
325
+ const originalContent = content;
326
+ let replacementCount = 0;
268
327
 
269
328
  // Replace SCSS variables with CSS custom properties
270
329
  for (const [scssVar, cssVar] of Object.entries(scssVariableMigration)) {
@@ -284,15 +343,19 @@ export async function migrateSCSSVariables(sourcePath, options = {}) {
284
343
  report.warnings.push({
285
344
  file: relative(process.cwd(), file),
286
345
  variable: varName,
287
- message: 'No design token equivalent found'
346
+ message: 'No design token equivalent found',
288
347
  });
289
348
  }
290
349
  });
291
350
  }
292
351
 
293
352
  if (content !== originalContent) {
294
- if (options.dryRun) {
295
- console.log(chalk.yellow(` Would update: ${file}`));
353
+ if (options.dryRun || options.preview) {
354
+ if (options.preview) {
355
+ showPreviewDiff(file, originalContent, content);
356
+ } else {
357
+ console.log(chalk.yellow(` Would update: ${file}`));
358
+ }
296
359
  } else {
297
360
  await writeFile(file, content, 'utf8');
298
361
  report.filesProcessed++;
@@ -300,18 +363,21 @@ export async function migrateSCSSVariables(sourcePath, options = {}) {
300
363
  }
301
364
  }
302
365
 
303
- } catch (error) {
304
- report.errors.push({
305
- file: relative(process.cwd(), file),
306
- error: error.message
307
- });
308
- }
366
+ processedCount++;
367
+ spinner.text = `Processing files (${processedCount}/${files.length})...`;
368
+
369
+ } catch (error) {
370
+ report.errors.push({
371
+ file: relative(process.cwd(), file),
372
+ error: error.message
373
+ });
374
+ }
375
+ }));
309
376
  }
310
377
 
311
378
  spinner.succeed(chalk.green('SCSS variable migration complete!'));
312
379
 
313
380
  return report;
314
-
315
381
  } catch (error) {
316
382
  spinner.fail(chalk.red('Migration failed'));
317
383
  throw error;
@@ -327,22 +393,24 @@ async function getAllFiles(dir, extensions = []) {
327
393
  async function walk(currentPath) {
328
394
  const entries = await readdir(currentPath);
329
395
 
330
- for (const entry of entries) {
331
- const fullPath = join(currentPath, entry);
332
- const stats = await lstat(fullPath);
396
+ await Promise.all(
397
+ entries.map(async entry => {
398
+ const fullPath = join(currentPath, entry);
399
+ const stats = await lstat(fullPath);
333
400
 
334
- if (stats.isDirectory()) {
335
- // Skip node_modules and hidden directories
336
- if (!entry.startsWith('.') && entry !== 'node_modules') {
337
- await walk(fullPath);
338
- }
339
- } else if (stats.isFile()) {
340
- const ext = extname(fullPath);
341
- if (extensions.length === 0 || extensions.includes(ext)) {
342
- files.push(fullPath);
401
+ if (stats.isDirectory()) {
402
+ // Skip node_modules and hidden directories
403
+ if (!entry.startsWith('.') && entry !== 'node_modules') {
404
+ await walk(fullPath);
405
+ }
406
+ } else if (stats.isFile()) {
407
+ const ext = extname(fullPath);
408
+ if (extensions.length === 0 || extensions.includes(ext)) {
409
+ files.push(fullPath);
410
+ }
343
411
  }
344
- }
345
- }
412
+ })
413
+ );
346
414
  }
347
415
 
348
416
  await walk(dir);
@@ -357,7 +425,9 @@ export function displayMigrationReport(report) {
357
425
  console.log(chalk.gray('='.repeat(50)));
358
426
 
359
427
  console.log(chalk.cyan(`Files processed: ${report.filesProcessed}`));
360
- console.log(chalk.cyan(`Classes/Variables replaced: ${report.classesReplaced || report.variablesReplaced}`));
428
+ console.log(
429
+ chalk.cyan(`Classes/Variables replaced: ${report.classesReplaced || report.variablesReplaced}`)
430
+ );
361
431
 
362
432
  if (report.warnings.length > 0) {
363
433
  console.log(chalk.yellow(`\n⚠️ Warnings (${report.warnings.length}):\n`));
@@ -406,5 +476,5 @@ export default {
406
476
  migrateTailwind,
407
477
  migrateBootstrap,
408
478
  migrateSCSSVariables,
409
- displayMigrationReport
479
+ displayMigrationReport,
410
480
  };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Atomix Plugin: Style Dictionary
3
+ * Example plugin showing how to extend CLI functionality
4
+ */
5
+
6
+ export default async function styleDictionaryPlugin(api, options = {}) {
7
+ const { logger, hooks } = api;
8
+
9
+ logger.info('Initializing Style Dictionary plugin...');
10
+
11
+ // Register a hook to run before generation
12
+ hooks.register('preGenerate', async (data) => {
13
+ logger.debug(`[StyleDictionary] Pre-generation hook for ${data.name}`);
14
+
15
+ // Example: Add a custom property to the generation data
16
+ return {
17
+ ...data,
18
+ styleDictionary: {
19
+ enabled: true,
20
+ version: options.version || 'latest'
21
+ }
22
+ };
23
+ });
24
+
25
+ // Register a hook for validation
26
+ hooks.register('onValidate', async (report) => {
27
+ logger.debug('[StyleDictionary] Validation hook running');
28
+
29
+ // Add a custom validation check
30
+ if (options.strictMode && !report.styleDictionary) {
31
+ report.issues.push('Missing Style Dictionary configuration');
32
+ report.valid = false;
33
+ }
34
+
35
+ return report;
36
+ });
37
+
38
+ // Register a hook to run after build
39
+ hooks.register('postBuild', async (assets) => {
40
+ logger.info('[StyleDictionary] Post-build processing...');
41
+ // In a real plugin, you might minify assets or upload them here
42
+ return assets;
43
+ });
44
+
45
+ logger.debug('Style Dictionary plugin initialized successfully.');
46
+ }