@shohojdhara/atomix 0.4.8 → 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 (165) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +148 -120
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +1 -1
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1227 -122
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1052 -41
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2086 -1035
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1620 -600
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +441 -270
  19. package/dist/index.esm.js +1900 -638
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1935 -670
  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 +148 -4
  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 +4 -1
  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 +135 -14
  44. package/scripts/cli/commands/init.js +45 -18
  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/component-validator.js +443 -0
  52. package/scripts/cli/internal/config-loader.js +162 -0
  53. package/scripts/cli/internal/filesystem.js +102 -2
  54. package/scripts/cli/internal/generator.js +359 -39
  55. package/scripts/cli/internal/glass-generator.js +398 -0
  56. package/scripts/cli/internal/hook-generator.js +369 -0
  57. package/scripts/cli/internal/hooks.js +61 -0
  58. package/scripts/cli/internal/itcss-generator.js +565 -0
  59. package/scripts/cli/internal/motion-generator.js +679 -0
  60. package/scripts/cli/internal/template-engine.js +301 -0
  61. package/scripts/cli/internal/theme-bridge.js +664 -0
  62. package/scripts/cli/internal/tokens/engine.js +122 -0
  63. package/scripts/cli/internal/tokens/provider.js +34 -0
  64. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  65. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  66. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  67. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  68. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  69. package/scripts/cli/internal/validator.js +276 -0
  70. package/scripts/cli/internal/wizard.js +60 -6
  71. package/scripts/cli/mappings.js +23 -0
  72. package/scripts/cli/migration-tools.js +164 -94
  73. package/scripts/cli/plugins/style-dictionary.js +46 -0
  74. package/scripts/cli/templates/README.md +525 -95
  75. package/scripts/cli/templates/common-templates.js +40 -14
  76. package/scripts/cli/templates/components/react-component.ts +282 -0
  77. package/scripts/cli/templates/config/project-config.ts +112 -0
  78. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  79. package/scripts/cli/templates/index.js +19 -4
  80. package/scripts/cli/templates/index.ts +171 -0
  81. package/scripts/cli/templates/next-templates.js +72 -0
  82. package/scripts/cli/templates/react-templates.js +70 -126
  83. package/scripts/cli/templates/scss-templates.js +35 -35
  84. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  85. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  86. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  87. package/scripts/cli/templates/token-templates.js +337 -1
  88. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  89. package/scripts/cli/templates/types/component-types.ts +145 -0
  90. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  91. package/scripts/cli/templates/vanilla-templates.js +39 -0
  92. package/scripts/cli/token-manager.js +8 -2
  93. package/scripts/cli/utils/cache-manager.js +240 -0
  94. package/scripts/cli/utils/detector.js +46 -0
  95. package/scripts/cli/utils/diagnostics.js +289 -0
  96. package/scripts/cli/utils/error.js +45 -3
  97. package/scripts/cli/utils/helpers.js +24 -0
  98. package/scripts/cli/utils/logger.js +1 -1
  99. package/scripts/cli/utils/security.js +302 -0
  100. package/scripts/cli/utils/telemetry.js +115 -0
  101. package/scripts/cli/utils/validation.js +4 -38
  102. package/scripts/cli/utils.js +46 -0
  103. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  104. package/src/components/Accordion/Accordion.test.tsx +0 -17
  105. package/src/components/Accordion/Accordion.tsx +0 -4
  106. package/src/components/AtomixGlass/AtomixGlass.tsx +102 -2
  107. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +125 -12
  108. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  109. package/src/components/AtomixGlass/README.md +25 -10
  110. package/src/components/AtomixGlass/animation-system.ts +578 -0
  111. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  112. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  113. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  114. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  115. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  116. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  117. package/src/components/Avatar/Avatar.tsx +1 -1
  118. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  119. package/src/components/Button/Button.stories.tsx +10 -0
  120. package/src/components/Button/Button.test.tsx +16 -11
  121. package/src/components/Button/Button.tsx +4 -4
  122. package/src/components/Card/Card.tsx +1 -1
  123. package/src/components/Dropdown/Dropdown.tsx +12 -12
  124. package/src/components/Form/Select.tsx +62 -3
  125. package/src/components/Modal/Modal.tsx +14 -3
  126. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  127. package/src/components/Slider/Slider.stories.tsx +3 -3
  128. package/src/components/Slider/Slider.tsx +38 -0
  129. package/src/components/Steps/Steps.tsx +3 -3
  130. package/src/components/Tabs/Tabs.tsx +77 -8
  131. package/src/components/Testimonial/Testimonial.tsx +1 -1
  132. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  133. package/src/components/TypedButton/TypedButton.tsx +39 -0
  134. package/src/components/TypedButton/index.ts +2 -0
  135. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  136. package/src/lib/composables/index.ts +4 -7
  137. package/src/lib/composables/types.ts +45 -0
  138. package/src/lib/composables/useAccordion.ts +0 -7
  139. package/src/lib/composables/useAtomixGlass.ts +144 -5
  140. package/src/lib/composables/useChartExport.ts +3 -13
  141. package/src/lib/composables/useDropdown.ts +66 -0
  142. package/src/lib/composables/useFocusTrap.ts +80 -0
  143. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  144. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  145. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  146. package/src/lib/composables/useTooltip.ts +16 -0
  147. package/src/lib/composables/useTypedButton.ts +66 -0
  148. package/src/lib/config/index.ts +62 -5
  149. package/src/lib/constants/components.ts +55 -0
  150. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  151. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  152. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  153. package/src/lib/types/components.ts +37 -11
  154. package/src/lib/types/glass.ts +35 -0
  155. package/src/lib/types/index.ts +1 -0
  156. package/src/lib/utils/displacement-generator.ts +1 -1
  157. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  158. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  159. package/src/styles/06-components/_components.testbutton.scss +212 -0
  160. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  161. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  162. package/src/styles/99-utilities/_index.scss +1 -0
  163. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  164. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  165. package/src/styles/06-components/old.chart.styles.scss +0 -2788
@@ -3,10 +3,65 @@
3
3
  * Utilities for safe file and path operations
4
4
  */
5
5
 
6
- import { resolve, normalize, isAbsolute, relative } from 'path';
7
- import { access } from 'fs/promises';
6
+ import { resolve, normalize, isAbsolute, relative, dirname } from 'path';
7
+ import { access, writeFile, mkdir, readFile } from 'fs/promises';
8
+ import { logger } from '../utils/logger.js';
9
+ import chalk from 'chalk';
10
+ import {
11
+ validateSecurePath,
12
+ createBackup,
13
+ retryWithBackoff
14
+ } from '../utils/security.js';
8
15
 
9
16
  export const filesystem = {
17
+ /**
18
+ * Safe file write with dry-run support, backup, and retry mechanism
19
+ * @param {string} path - Path to write to
20
+ * @param {string} content - Content to write
21
+ * @param {object} options - Options
22
+ */
23
+ async writeFile(path, content, options = {}) {
24
+ if (process.env.ATOMIX_DRY_RUN === 'true') {
25
+ logger.info(`${chalk.cyan('[DRY RUN]')} Would write file: ${chalk.bold(path)}`);
26
+ if (options.debug) {
27
+ logger.debug('Content:', content);
28
+ }
29
+ return true;
30
+ }
31
+
32
+ // Validate path security
33
+ const pathValidation = validateSecurePath(path);
34
+ if (!pathValidation.isValid) {
35
+ throw new Error(`Security validation failed for path ${path}: ${pathValidation.error}`);
36
+ }
37
+
38
+ const safePath = pathValidation.safePath;
39
+
40
+ const writeOperation = async () => {
41
+ try {
42
+ const dir = dirname(safePath);
43
+ await mkdir(dir, { recursive: true });
44
+
45
+ // Create backup if file exists and backup is enabled
46
+ if (options.backup !== false && await this.exists(safePath)) {
47
+ try {
48
+ await createBackup(safePath);
49
+ } catch (backupError) {
50
+ logger.warn(`Backup failed for ${safePath}: ${backupError.message}`);
51
+ }
52
+ }
53
+
54
+ await writeFile(safePath, content, options);
55
+ return true;
56
+ } catch (error) {
57
+ throw new Error(`Failed to write file ${safePath}: ${error.message}`);
58
+ }
59
+ };
60
+
61
+ // Use retry mechanism for file operations
62
+ return retryWithBackoff(writeOperation, options.maxRetries || 2, options.retryDelay || 100);
63
+ },
64
+
10
65
  /**
11
66
  * Validates and resolves a path within the project directory
12
67
  * @param {string} inputPath - The path to validate
@@ -54,5 +109,50 @@ export const filesystem = {
54
109
  } catch {
55
110
  return false;
56
111
  }
112
+ },
113
+
114
+ /**
115
+ * Create directory recursively with dry-run support
116
+ * @param {string} path - Directory path to create
117
+ * @param {object} options - Options
118
+ * @returns {Promise<boolean>} Success status
119
+ */
120
+ async createDirectory(path, options = {}) {
121
+ if (process.env.ATOMIX_DRY_RUN === 'true') {
122
+ logger.info(`${chalk.cyan('[DRY RUN]')} Would create directory: ${chalk.bold(path)}`);
123
+ return true;
124
+ }
125
+
126
+ const pathValidation = validateSecurePath(path);
127
+ if (!pathValidation.isValid) {
128
+ throw new Error(`Security validation failed for path ${path}: ${pathValidation.error}`);
129
+ }
130
+
131
+ const safePath = pathValidation.safePath;
132
+
133
+ try {
134
+ await mkdir(safePath, { recursive: true });
135
+ logger.debug(`Created directory: ${safePath}`);
136
+ return true;
137
+ } catch (error) {
138
+ throw new Error(`Failed to create directory ${safePath}: ${error.message}`);
139
+ }
140
+ },
141
+
142
+ /**
143
+ * Read file content with encoding
144
+ * @param {string} path - File path
145
+ * @param {string} encoding - File encoding (default: utf8)
146
+ * @returns {Promise<string>} File content
147
+ */
148
+ async readFile(path, encoding = 'utf8') {
149
+ try {
150
+ const content = await readFile(path, encoding);
151
+ return content;
152
+ } catch (error) {
153
+ throw new Error(`Failed to read file ${path}: ${error.message}`);
154
+ }
57
155
  }
58
156
  };
157
+
158
+ export default filesystem;
@@ -3,16 +3,26 @@
3
3
  * Core logic for scaffolding components and assets
4
4
  */
5
5
 
6
- import { writeFile, mkdir, readFile } from 'fs/promises';
6
+ import { readFile } from 'fs/promises';
7
7
  import { existsSync } from 'fs';
8
8
  import { join } from 'path';
9
- import { componentTemplates } from '../templates.js';
9
+ import { templateEngine, COMPLEXITY_LEVELS } from './template-engine.js';
10
+ import { detectFramework } from '../utils/detector.js';
11
+ import { filesystem } from './filesystem.js';
12
+ import { aiEngine } from './ai-engine.js';
13
+ import {
14
+ sanitizeInput,
15
+ validateComponentNameSecure,
16
+ RateLimiter
17
+ } from '../utils/security.js';
18
+ import { AtomixCLIError } from '../utils/error.js';
19
+ import { tokenProvider } from './tokens/token-provider.js';
20
+ import { tokenValidator } from './tokens/token-validator.js';
21
+ import { componentValidator } from './component-validator.js';
22
+ import { generateComponentStylesPackage } from './itcss-generator.js';
23
+ import { generateHookFile } from './hook-generator.js';
10
24
 
11
- export const COMPLEXITY_LEVELS = {
12
- SIMPLE: { name: 'simple', template: 'simple' },
13
- MEDIUM: { name: 'medium', template: 'medium' },
14
- COMPLEX: { name: 'complex', template: 'complex' }
15
- };
25
+ export { COMPLEXITY_LEVELS };
16
26
 
17
27
  export const COMPONENT_FEATURES = {
18
28
  TYPESCRIPT: { name: 'typescript', default: true },
@@ -23,9 +33,16 @@ export const COMPONENT_FEATURES = {
23
33
  ACCESSIBILITY: { name: 'accessibility', default: true }
24
34
  };
25
35
 
36
+ // Global rate limiter for AI operations
37
+ const aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute
38
+
26
39
  export const generator = {
27
40
  /**
28
41
  * Generates component files based on options
42
+ * @param {string} name - Component name
43
+ * @param {Object} options - Generation options
44
+ * @returns {Promise<string>} Path to generated component
45
+ * @throws {AtomixCLIError} If generation fails
29
46
  */
30
47
  async generateComponent(name, options = {}) {
31
48
  const {
@@ -35,52 +52,338 @@ export const generator = {
35
52
  logger
36
53
  } = options;
37
54
 
38
- const componentPath = join(outputPath, name);
39
- await mkdir(componentPath, { recursive: true });
55
+ // Sanitize and validate component name
56
+ const sanitizedName = sanitizeInput(name, 'componentName');
57
+ const validation = validateComponentNameSecure(sanitizedName);
58
+ if (!validation.isValid) {
59
+ throw new AtomixCLIError(
60
+ `Component name validation failed: ${validation.error}`,
61
+ 'INVALID_COMPONENT_NAME',
62
+ [
63
+ 'Use PascalCase (e.g., MyComponent, Button)',
64
+ 'Start with a letter (not numbers)',
65
+ 'Avoid special characters except letters and numbers'
66
+ ]
67
+ );
68
+ }
69
+
70
+ // Detect framework
71
+ let framework;
72
+ try {
73
+ framework = await detectFramework();
74
+ if (logger) logger.debug(`Detected framework: ${framework}`);
75
+ } catch (error) {
76
+ throw new AtomixCLIError(
77
+ `Framework detection failed: ${error.message}`,
78
+ 'FRAMEWORK_DETECTION_FAILED',
79
+ [
80
+ 'Ensure package.json exists in project root',
81
+ 'Check for React, Next.js, or vanilla project structure',
82
+ 'Run `atomix doctor` to diagnose environment issues'
83
+ ]
84
+ );
85
+ }
86
+
87
+ // Load design tokens if available
88
+ let availableTokens = {};
89
+ try {
90
+ const tokenPaths = [
91
+ './design-tokens/tokens.json',
92
+ './src/design-tokens/tokens.json',
93
+ './tokens.json'
94
+ ];
95
+
96
+ for (const tokenPath of tokenPaths) {
97
+ if (existsSync(join(process.cwd(), tokenPath))) {
98
+ availableTokens = await tokenProvider.loadTokens(tokenPath);
99
+ if (logger) logger.debug(`Loaded design tokens from ${tokenPath}`);
100
+ break;
101
+ }
102
+ }
103
+ } catch (error) {
104
+ if (logger) logger.debug(`Token loading skipped: ${error.message}`);
105
+ }
106
+
107
+ const componentPath = join(outputPath, sanitizedName);
40
108
 
41
109
  // 1. Generate Component File
42
- const templateName = COMPLEXITY_LEVELS[complexity.toUpperCase()]?.template || 'medium';
43
- let content = '';
44
-
45
- switch (templateName) {
46
- case 'simple': content = componentTemplates.react.simple(name); break;
47
- case 'medium': content = componentTemplates.react.medium(name); break;
48
- case 'complex': content = componentTemplates.react.complex(name); break;
49
- default: content = componentTemplates.react.component(name);
110
+ let content;
111
+ let ext = '.tsx';
112
+
113
+ try {
114
+ if (framework === 'vanilla') {
115
+ const templateFn = templateEngine.selectTemplate('vanilla', complexity, 'component');
116
+ content = templateEngine.render(templateFn, sanitizedName);
117
+ ext = '.html';
118
+ } else if (framework === 'next') {
119
+ const templateFn = templateEngine.selectTemplate('next', complexity, 'component');
120
+ content = templateEngine.render(templateFn, sanitizedName);
121
+ } else {
122
+ // Default to React
123
+ const templateFn = templateEngine.selectTemplate('react', complexity, 'component');
124
+ content = templateEngine.render(templateFn, sanitizedName);
125
+ }
126
+ } catch (error) {
127
+ if (error instanceof AtomixCLIError) {
128
+ throw error;
129
+ }
130
+ throw new AtomixCLIError(
131
+ `Failed to select template: ${error.message}`,
132
+ 'TEMPLATE_SELECTION_FAILED',
133
+ [
134
+ `Check if complexity level '${complexity}' is valid`,
135
+ `Verify framework '${framework}' is supported`,
136
+ 'Run `atomix doctor` to check template availability'
137
+ ]
138
+ );
50
139
  }
51
140
 
52
- await writeFile(join(componentPath, `${name}.tsx`), content, 'utf8');
53
- if (logger) logger.debug(`Created ${name}.tsx`);
141
+ // Validate generated component against design system rules
142
+ const componentValidation = tokenValidator.validateComponent(content, availableTokens);
143
+ if (!componentValidation.valid && logger) {
144
+ logger.debug(`Component validation: ${componentValidation.issues.length} issues found`);
145
+ }
54
146
 
55
- // 2. Index File
56
- await writeFile(join(componentPath, 'index.ts'), componentTemplates.react.index(name), 'utf8');
147
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}${ext}`), content, 'utf8');
148
+ if (logger) logger.debug(`Created ${sanitizedName}${ext}`);
149
+
150
+ // 2. Index File (only for React/Next)
151
+ if (framework !== 'vanilla') {
152
+ try {
153
+ const indexTemplateFn = templateEngine.selectTemplate(framework, complexity, 'index');
154
+ const indexContent = templateEngine.render(indexTemplateFn, sanitizedName);
155
+ await filesystem.writeFile(join(componentPath, 'index.ts'), indexContent, 'utf8');
156
+ if (logger) logger.debug(`Created index.ts`);
157
+ } catch (error) {
158
+ throw new AtomixCLIError(
159
+ `Failed to generate index file: ${error.message}`,
160
+ 'INDEX_GENERATION_FAILED',
161
+ [
162
+ 'Check index template exists for framework',
163
+ 'Verify template exports in template files',
164
+ 'Try generating without index feature'
165
+ ]
166
+ );
167
+ }
168
+ }
57
169
 
58
170
  // 3. Optional Features
59
171
  if (features.includes('storybook')) {
60
- await writeFile(join(componentPath, `${name}.stories.tsx`), componentTemplates.react.story(name), 'utf8');
172
+ try {
173
+ const storyTemplateFn = templateEngine.selectTemplate(framework, complexity, 'story');
174
+ const storyContent = templateEngine.render(storyTemplateFn, sanitizedName);
175
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.stories.tsx`), storyContent, 'utf8');
176
+ if (logger) logger.debug(`Created ${sanitizedName}.stories.tsx`);
177
+ } catch (error) {
178
+ throw new AtomixCLIError(
179
+ `Failed to generate Storybook story: ${error.message}`,
180
+ 'STORYBOOK_GENERATION_FAILED',
181
+ [
182
+ 'Check storybook template exists',
183
+ 'Verify story feature is supported for this framework',
184
+ 'Try generating without --storybook flag'
185
+ ]
186
+ );
187
+ }
61
188
  }
62
189
 
63
190
  if (features.includes('tests')) {
64
- await writeFile(join(componentPath, `${name}.test.tsx`), componentTemplates.react.test(name), 'utf8');
191
+ try {
192
+ const testTemplateFn = templateEngine.selectTemplate(framework, complexity, 'test');
193
+ const testContent = templateEngine.render(testTemplateFn, sanitizedName);
194
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.test.tsx`), testContent, 'utf8');
195
+ if (logger) logger.debug(`Created ${sanitizedName}.test.tsx`);
196
+ } catch (error) {
197
+ throw new AtomixCLIError(
198
+ `Failed to generate test file: ${error.message}`,
199
+ 'TEST_GENERATION_FAILED',
200
+ [
201
+ 'Check test template exists',
202
+ 'Verify test feature is supported for this framework',
203
+ 'Try generating without --tests flag'
204
+ ]
205
+ );
206
+ }
65
207
  }
66
208
 
67
- if (features.includes('hook')) {
68
- const hookDir = join(outputPath, '..', 'lib', 'composables');
69
- await mkdir(hookDir, { recursive: true });
70
- await writeFile(join(hookDir, `use${name}.ts`), componentTemplates.composable.useHook(name), 'utf8');
209
+ if (features.includes('hook') && framework !== 'vanilla') {
210
+ try {
211
+ const hookDir = join(outputPath, '..', 'lib', 'composables');
212
+ const hookTemplateFn = templateEngine.selectTemplate(framework, complexity, 'hook');
213
+ const hookContent = templateEngine.render(hookTemplateFn, sanitizedName);
214
+ await filesystem.writeFile(join(hookDir, `use${sanitizedName}.ts`), hookContent, 'utf8');
215
+ if (logger) logger.debug(`Created use${sanitizedName}.ts`);
216
+ } catch (error) {
217
+ throw new AtomixCLIError(
218
+ `Failed to generate composable hook: ${error.message}`,
219
+ 'HOOK_GENERATION_FAILED',
220
+ [
221
+ 'Check hook template exists',
222
+ 'Verify hook feature is supported for this framework',
223
+ 'Try generating without --hook flag'
224
+ ]
225
+ );
226
+ }
71
227
  }
72
228
 
73
- // 4. Styles (ITCSS)
229
+ // 4. Styles (ITCSS) - Enhanced with auto-generation
74
230
  if (features.includes('styles')) {
75
- const stylesDir = join(outputPath, '..', 'styles');
76
-
77
- const settingsPath = join(stylesDir, '01-settings');
78
- await mkdir(settingsPath, { recursive: true });
79
- await writeFile(join(settingsPath, `_settings.${name.toLowerCase()}.scss`), componentTemplates.scss.settings(name), 'utf8');
231
+ try {
232
+ const stylesResult = await generateComponentStylesPackage(sanitizedName, process.cwd(), {
233
+ force: options.force || false
234
+ });
235
+
236
+ if (logger && stylesResult.created.length > 0) {
237
+ logger.debug(`Created ${stylesResult.created.length} ITCSS style files`);
238
+ }
239
+ } catch (error) {
240
+ throw new AtomixCLIError(
241
+ `Failed to generate ITCSS styles: ${error.message}`,
242
+ 'STYLE_GENERATION_FAILED',
243
+ [
244
+ 'Check SCSS templates exist',
245
+ 'Verify styles directory structure',
246
+ 'Try generating without --styles flag'
247
+ ]
248
+ );
249
+ }
250
+ }
251
+
252
+ // 5. Composable Hook - Enhanced generation
253
+ if (features.includes('hook') && framework !== 'vanilla') {
254
+ try {
255
+ const hookResult = await generateHookFile(sanitizedName, process.cwd(), {
256
+ force: options.force || false
257
+ });
258
+
259
+ if (logger && hookResult.created.length > 0) {
260
+ logger.debug(`Created ${hookResult.created.length} composable hook files`);
261
+ }
262
+ } catch (error) {
263
+ throw new AtomixCLIError(
264
+ `Failed to generate composable hook: ${error.message}`,
265
+ 'HOOK_GENERATION_FAILED',
266
+ [
267
+ 'Check hook template exists',
268
+ 'Verify hook feature is supported for this framework',
269
+ 'Try generating without --hook flag'
270
+ ]
271
+ );
272
+ }
273
+ }
274
+
275
+ return componentPath;
276
+ },
277
+
278
+ /**
279
+ * Generates component files using AI based on a prompt
280
+ * @param {string} name - Component name
281
+ * @param {string} prompt - AI prompt
282
+ * @param {Object} options - Generation options
283
+ * @returns {Promise<string>} Path to generated component
284
+ * @throws {AtomixCLIError} If generation fails
285
+ */
286
+ async generateAIComponent(name, prompt, options = {}) {
287
+ const { outputPath, logger } = options;
288
+
289
+ // Apply rate limiting for AI operations
290
+ const userId = process.env.USER || 'anonymous';
291
+ if (!aiRateLimiter.checkLimit(userId)) {
292
+ throw new AtomixCLIError(
293
+ `Rate limit exceeded. Please wait before generating more AI components. Remaining: ${aiRateLimiter.getRemaining(userId)} seconds`,
294
+ 'RATE_LIMIT_EXCEEDED',
295
+ [
296
+ 'Wait for rate limit to reset (60 seconds)',
297
+ 'Reduce frequency of AI component generation',
298
+ 'Use regular generation instead of AI for simple components'
299
+ ]
300
+ );
301
+ }
302
+
303
+ // Sanitize inputs
304
+ const sanitizedName = sanitizeInput(name, 'componentName');
305
+ sanitizeInput(prompt, 'prompt'); // Sanitize but use original
306
+
307
+ const validation = validateComponentNameSecure(sanitizedName);
308
+ if (!validation.isValid) {
309
+ throw new AtomixCLIError(
310
+ `Component name validation failed: ${validation.error}`,
311
+ 'INVALID_COMPONENT_NAME',
312
+ [
313
+ 'Use PascalCase (e.g., MyComponent, Button)',
314
+ 'Start with a letter (not numbers)',
315
+ 'Avoid special characters except letters and numbers'
316
+ ]
317
+ );
318
+ }
80
319
 
81
- const compStylesPath = join(stylesDir, '06-components');
82
- await mkdir(compStylesPath, { recursive: true });
83
- await writeFile(join(compStylesPath, `_components.${name.toLowerCase()}.scss`), componentTemplates.scss.component(name), 'utf8');
320
+ const componentPath = join(outputPath, sanitizedName);
321
+
322
+ // Call AI Engine
323
+ let generated;
324
+ try {
325
+ generated = await aiEngine.generateComponent(name, prompt);
326
+ } catch (error) {
327
+ throw new AtomixCLIError(
328
+ `AI generation failed: ${error.message}`,
329
+ 'AI_GENERATION_FAILED',
330
+ [
331
+ 'Check your internet connection',
332
+ 'Verify AI engine credentials are configured',
333
+ 'Try again in a few moments',
334
+ 'Use regular generation as fallback'
335
+ ]
336
+ );
337
+ }
338
+
339
+ // Write component file
340
+ try {
341
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.tsx`), generated.component, 'utf8');
342
+ if (logger) logger.debug(`Created ${sanitizedName}.tsx (AI)`);
343
+ } catch (error) {
344
+ throw new AtomixCLIError(
345
+ `Failed to write AI-generated component: ${error.message}`,
346
+ 'FILE_WRITE_FAILED',
347
+ [
348
+ 'Check you have write permissions for the target directory',
349
+ 'Ensure the path is valid and within project root',
350
+ 'Verify disk space is available'
351
+ ]
352
+ );
353
+ }
354
+
355
+ // Index file
356
+ try {
357
+ const indexTemplateFn = templateEngine.selectTemplate('react', 'medium', 'index');
358
+ const indexContent = templateEngine.render(indexTemplateFn, sanitizedName);
359
+ await filesystem.writeFile(join(componentPath, 'index.ts'), indexContent, 'utf8');
360
+ } catch (error) {
361
+ throw new AtomixCLIError(
362
+ `Failed to write index file: ${error.message}`,
363
+ 'FILE_WRITE_FAILED',
364
+ [
365
+ 'Check index template exists',
366
+ 'Verify write permissions',
367
+ 'AI generation may have incomplete templates'
368
+ ]
369
+ );
370
+ }
371
+
372
+ // Optional files from AI
373
+ if (generated.styles) {
374
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.scss`), generated.styles, 'utf8');
375
+ }
376
+
377
+ if (generated.tests) {
378
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.test.tsx`), generated.tests, 'utf8');
379
+ }
380
+
381
+ if (generated.stories) {
382
+ await filesystem.writeFile(join(componentPath, `${sanitizedName}.stories.tsx`), generated.stories, 'utf8');
383
+ }
384
+
385
+ if (generated.readme) {
386
+ await filesystem.writeFile(join(componentPath, 'README.md'), generated.readme, 'utf8');
84
387
  }
85
388
 
86
389
  return componentPath;
@@ -88,6 +391,9 @@ export const generator = {
88
391
 
89
392
  /**
90
393
  * Validates a generated component
394
+ * @param {string} name - Component name
395
+ * @param {string} componentPath - Path to component directory
396
+ * @returns {Promise<Object>} { valid: boolean, issues: string[] }
91
397
  */
92
398
  async validate(name, componentPath) {
93
399
  const issues = [];
@@ -99,11 +405,25 @@ export const generator = {
99
405
  }
100
406
 
101
407
  const content = await readFile(componentFile, 'utf8');
102
- if (!content.includes('displayName')) issues.push('Missing displayName');
103
- if (!content.includes('aria-')) issues.push('Missing accessibility attributes');
408
+
409
+ // Use new component validator for comprehensive checks
410
+ const validationResults = componentValidator.validate(content, name);
411
+
412
+ // Add all issues from component validator
413
+ for (const issue of validationResults.issues) {
414
+ issues.push(`[${issue.rule}] ${issue.message}${issue.suggestion ? ' - ' + issue.suggestion : ''}`);
415
+ }
416
+
417
+ // Legacy checks (keep for backward compatibility)
418
+ // 1. Check for displayName (already covered by componentValidator)
419
+ // 2. Check for JSDoc documentation (already covered)
420
+ // 3. Check for TypeScript type definitions (already covered)
421
+ // 4. Check for forwardRef usage (already covered)
422
+ // 5. Check for Accessibility attributes (already covered)
423
+ // 6. Check for hardcoded colors (already covered)
104
424
 
105
425
  return {
106
- valid: issues.length === 0,
426
+ valid: validationResults.valid,
107
427
  issues
108
428
  };
109
429
  }