@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
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Atomix CLI Internal Filesystem
3
+ * Utilities for safe file and path operations
4
+ */
5
+
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';
15
+
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
+
65
+ /**
66
+ * Validates and resolves a path within the project directory
67
+ * @param {string} inputPath - The path to validate
68
+ * @param {string} basePath - Base directory (defaults to process.cwd())
69
+ * @returns {Object} { isValid: boolean, safePath: string, error?: string }
70
+ */
71
+ validatePath(inputPath, basePath = process.cwd()) {
72
+ try {
73
+ const normalizedBase = normalize(resolve(basePath));
74
+ const normalizedInput = normalize(isAbsolute(inputPath)
75
+ ? inputPath
76
+ : resolve(basePath, inputPath));
77
+
78
+ const relativePath = relative(normalizedBase, normalizedInput);
79
+
80
+ if (relativePath.startsWith('..')) {
81
+ return {
82
+ isValid: false,
83
+ safePath: null,
84
+ error: 'Path is outside the project directory'
85
+ };
86
+ }
87
+
88
+ return {
89
+ isValid: true,
90
+ safePath: normalizedInput,
91
+ error: null
92
+ };
93
+ } catch (error) {
94
+ return {
95
+ isValid: false,
96
+ safePath: null,
97
+ error: `Invalid path: ${error.message}`
98
+ };
99
+ }
100
+ },
101
+
102
+ /**
103
+ * Checks if a file exists
104
+ */
105
+ async exists(path) {
106
+ try {
107
+ await access(path);
108
+ return true;
109
+ } catch {
110
+ return false;
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
+ }
155
+ }
156
+ };
157
+
158
+ export default filesystem;
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Atomix CLI Internal Generator
3
+ * Core logic for scaffolding components and assets
4
+ */
5
+
6
+ import { readFile } from 'fs/promises';
7
+ import { existsSync } from 'fs';
8
+ import { join } from 'path';
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';
24
+
25
+ export { COMPLEXITY_LEVELS };
26
+
27
+ export const COMPONENT_FEATURES = {
28
+ TYPESCRIPT: { name: 'typescript', default: true },
29
+ STORYBOOK: { name: 'storybook', default: true },
30
+ TESTS: { name: 'tests', default: false },
31
+ HOOK: { name: 'hook', default: true },
32
+ STYLES: { name: 'styles', default: true },
33
+ ACCESSIBILITY: { name: 'accessibility', default: true }
34
+ };
35
+
36
+ // Global rate limiter for AI operations
37
+ const aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute
38
+
39
+ export const generator = {
40
+ /**
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
46
+ */
47
+ async generateComponent(name, options = {}) {
48
+ const {
49
+ outputPath,
50
+ complexity = 'medium',
51
+ features = [],
52
+ logger
53
+ } = options;
54
+
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);
108
+
109
+ // 1. Generate Component File
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
+ );
139
+ }
140
+
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
+ }
146
+
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
+ }
169
+
170
+ // 3. Optional Features
171
+ if (features.includes('storybook')) {
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
+ }
188
+ }
189
+
190
+ if (features.includes('tests')) {
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
+ }
207
+ }
208
+
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
+ }
227
+ }
228
+
229
+ // 4. Styles (ITCSS) - Enhanced with auto-generation
230
+ if (features.includes('styles')) {
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
+ }
319
+
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');
387
+ }
388
+
389
+ return componentPath;
390
+ },
391
+
392
+ /**
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[] }
397
+ */
398
+ async validate(name, componentPath) {
399
+ const issues = [];
400
+ const componentFile = join(componentPath, `${name}.tsx`);
401
+
402
+ if (!existsSync(componentFile)) {
403
+ issues.push(`Target file missing: ${name}.tsx`);
404
+ return { valid: false, issues };
405
+ }
406
+
407
+ const content = await readFile(componentFile, 'utf8');
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)
424
+
425
+ return {
426
+ valid: validationResults.valid,
427
+ issues
428
+ };
429
+ }
430
+ };