@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.
- package/atomix.config.ts +58 -1
- package/dist/atomix.css +172 -157
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +4 -4
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1274 -164
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1099 -83
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2106 -1050
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1663 -638
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +442 -270
- package/dist/index.esm.js +1947 -680
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1982 -712
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +6 -3
- package/scripts/atomix-cli.js +136 -1827
- package/scripts/cli/__tests__/basic.test.js +3 -2
- package/scripts/cli/__tests__/clean.test.js +278 -0
- package/scripts/cli/__tests__/component-validator.test.js +433 -0
- package/scripts/cli/__tests__/generator.test.js +613 -0
- package/scripts/cli/__tests__/glass-motion.test.js +256 -0
- package/scripts/cli/__tests__/integration.test.js +719 -108
- package/scripts/cli/__tests__/migrate.test.js +74 -0
- package/scripts/cli/__tests__/security.test.js +206 -0
- package/scripts/cli/__tests__/test-setup.js +3 -1
- package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
- package/scripts/cli/__tests__/token-provider.test.js +361 -0
- package/scripts/cli/__tests__/utils.test.js +5 -5
- package/scripts/cli/commands/benchmark.js +105 -0
- package/scripts/cli/commands/build-theme.js +115 -0
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +218 -0
- package/scripts/cli/commands/init.js +73 -0
- package/scripts/cli/commands/migrate.js +106 -0
- package/scripts/cli/commands/sync-tokens.js +206 -0
- package/scripts/cli/commands/theme-bridge.js +248 -0
- package/scripts/cli/commands/tokens.js +157 -0
- package/scripts/cli/commands/validate.js +194 -0
- package/scripts/cli/internal/ai-engine.js +156 -0
- package/scripts/cli/internal/compiler.js +114 -0
- package/scripts/cli/internal/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +158 -0
- package/scripts/cli/internal/generator.js +430 -0
- package/scripts/cli/internal/glass-generator.js +398 -0
- package/scripts/cli/internal/hook-generator.js +369 -0
- package/scripts/cli/internal/hooks.js +61 -0
- package/scripts/cli/internal/itcss-generator.js +565 -0
- package/scripts/cli/internal/motion-generator.js +679 -0
- package/scripts/cli/internal/template-engine.js +301 -0
- package/scripts/cli/internal/theme-bridge.js +664 -0
- package/scripts/cli/internal/tokens/engine.js +122 -0
- package/scripts/cli/internal/tokens/provider.js +34 -0
- package/scripts/cli/internal/tokens/providers/figma.js +50 -0
- package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
- package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
- package/scripts/cli/internal/tokens/token-provider.js +443 -0
- package/scripts/cli/internal/tokens/token-validator.js +513 -0
- package/scripts/cli/internal/validator.js +276 -0
- package/scripts/cli/internal/wizard.js +115 -0
- package/scripts/cli/mappings.js +23 -0
- package/scripts/cli/migration-tools.js +164 -94
- package/scripts/cli/plugins/style-dictionary.js +46 -0
- package/scripts/cli/templates/README.md +525 -95
- package/scripts/cli/templates/common-templates.js +40 -14
- package/scripts/cli/templates/components/react-component.ts +282 -0
- package/scripts/cli/templates/config/project-config.ts +112 -0
- package/scripts/cli/templates/hooks/use-component.ts +477 -0
- package/scripts/cli/templates/index.js +19 -4
- package/scripts/cli/templates/index.ts +171 -0
- package/scripts/cli/templates/next-templates.js +72 -0
- package/scripts/cli/templates/react-templates.js +70 -126
- package/scripts/cli/templates/scss-templates.js +35 -35
- package/scripts/cli/templates/stories/storybook-story.ts +241 -0
- package/scripts/cli/templates/styles/scss-component.ts +255 -0
- package/scripts/cli/templates/tests/vitest-test.ts +229 -0
- package/scripts/cli/templates/token-templates.js +337 -1
- package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
- package/scripts/cli/templates/types/component-types.ts +145 -0
- package/scripts/cli/templates/utils/testing-utils.ts +144 -0
- package/scripts/cli/templates/vanilla-templates.js +39 -0
- package/scripts/cli/token-manager.js +8 -2
- package/scripts/cli/utils/cache-manager.js +240 -0
- package/scripts/cli/utils/detector.js +46 -0
- package/scripts/cli/utils/diagnostics.js +289 -0
- package/scripts/cli/utils/error.js +89 -0
- package/scripts/cli/utils/helpers.js +67 -0
- package/scripts/cli/utils/logger.js +75 -0
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +37 -0
- package/scripts/cli/utils.js +28 -341
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.test.tsx +0 -17
- package/src/components/Accordion/Accordion.tsx +0 -4
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
- package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
- package/src/components/AtomixGlass/animation-system.ts +578 -0
- package/src/components/AtomixGlass/shader-utils.ts +4 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
- package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
- package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
- package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
- package/src/components/Button/Button.stories.tsx +10 -0
- package/src/components/Button/Button.test.tsx +16 -11
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +12 -12
- package/src/components/Form/Select.tsx +62 -3
- package/src/components/Modal/Modal.tsx +14 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +38 -0
- package/src/components/Steps/Steps.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +77 -8
- package/src/components/Testimonial/Testimonial.tsx +1 -1
- package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
- package/src/components/TypedButton/TypedButton.tsx +39 -0
- package/src/components/TypedButton/index.ts +2 -0
- package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
- package/src/lib/composables/index.ts +4 -7
- package/src/lib/composables/types.ts +45 -0
- package/src/lib/composables/useAccordion.ts +0 -7
- package/src/lib/composables/useAtomixGlass.ts +148 -6
- package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
- package/src/lib/composables/useChartExport.ts +3 -13
- package/src/lib/composables/useDropdown.ts +66 -0
- package/src/lib/composables/useFocusTrap.ts +80 -0
- package/src/lib/composables/usePerformanceMonitor.ts +448 -0
- package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
- package/src/lib/composables/useResponsiveGlass.ts +441 -0
- package/src/lib/composables/useTooltip.ts +16 -0
- package/src/lib/composables/useTypedButton.ts +66 -0
- package/src/lib/config/index.ts +62 -5
- package/src/lib/constants/components.ts +62 -7
- package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
- package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
- package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
- package/src/lib/types/components.ts +37 -11
- package/src/lib/types/glass.ts +35 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/utils/displacement-generator.ts +1 -1
- package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
- package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
- package/src/styles/06-components/_components.atomix-glass.scss +17 -21
- package/src/styles/06-components/_components.edge-panel.scss +1 -5
- package/src/styles/06-components/_components.modal.scss +1 -4
- package/src/styles/06-components/_components.navbar.scss +1 -1
- package/src/styles/06-components/_components.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- package/src/styles/06-components/_components.tooltip.scss +9 -5
- package/src/styles/06-components/_components.typedbutton.scss +212 -0
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
- package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
- package/scripts/cli/component-generator.js +0 -564
- package/scripts/cli/interactive-init.js +0 -357
- package/src/styles/06-components/old.chart.styles.scss +0 -2788
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Clean Command
|
|
3
|
+
* Safely clean build artifacts and cache files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { AtomixCLIError } from '../utils/error.js';
|
|
8
|
+
import { cacheManager } from '../utils/cache-manager.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Action logic for cleaning build artifacts
|
|
12
|
+
* @param {object} options - Command options
|
|
13
|
+
* @param {boolean} options.all - Clean node_modules as well
|
|
14
|
+
* @param {boolean} options.cache - Only clean cache directories
|
|
15
|
+
* @param {boolean} options.dryRun - Preview without deleting
|
|
16
|
+
* @param {boolean} options.verbose - Show detailed output
|
|
17
|
+
*/
|
|
18
|
+
export async function cleanAction(options) {
|
|
19
|
+
const spinner = logger.spinner('Analyzing project...').start();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Step 1: Identify what can be cleaned
|
|
23
|
+
spinner.text = 'Scanning for cleanable files...';
|
|
24
|
+
const targets = await cacheManager.identifyTargets(options);
|
|
25
|
+
|
|
26
|
+
// Filter out protected files
|
|
27
|
+
const safeTargets = targets.filter(target => {
|
|
28
|
+
if (cacheManager.isProtected(target.path)) {
|
|
29
|
+
logger.debug(`Skipping protected file: ${target.path}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Step 2: Handle empty state
|
|
36
|
+
if (safeTargets.length === 0) {
|
|
37
|
+
spinner.succeed('Nothing to clean! Your project is already tidy.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Step 3: Calculate total size
|
|
42
|
+
const totalSize = await cacheManager.calculateSize(safeTargets);
|
|
43
|
+
const formattedSize = cacheManager.formatBytes(totalSize);
|
|
44
|
+
|
|
45
|
+
// Step 4: Handle dry-run mode
|
|
46
|
+
if (options.dryRun || process.env.ATOMIX_DRY_RUN === 'true') {
|
|
47
|
+
spinner.stop();
|
|
48
|
+
cacheManager.displayDryRun(safeTargets);
|
|
49
|
+
logger.info(
|
|
50
|
+
`\nℹ️ Total size: ${formattedSize}\n` +
|
|
51
|
+
`⚠️ Run without --dry-run to actually delete these files.`
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Step 5: Perform cleanup
|
|
57
|
+
spinner.text = `Cleaning ${safeTargets.length} item(s)...`;
|
|
58
|
+
|
|
59
|
+
let successCount = 0;
|
|
60
|
+
let errorCount = 0;
|
|
61
|
+
const errors = [];
|
|
62
|
+
|
|
63
|
+
for (const target of safeTargets) {
|
|
64
|
+
try {
|
|
65
|
+
if (options.verbose) {
|
|
66
|
+
logger.info(`Removing: ${target.relativePath}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await cacheManager.deletePath(target.path, options);
|
|
70
|
+
successCount++;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errorCount++;
|
|
73
|
+
errors.push({
|
|
74
|
+
path: target.relativePath,
|
|
75
|
+
error: error.message
|
|
76
|
+
});
|
|
77
|
+
logger.debug(`Failed to delete ${target.path}: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Step 6: Report results
|
|
82
|
+
if (errorCount > 0) {
|
|
83
|
+
spinner.warn(`Cleanup completed with ${errorCount} warning(s)`);
|
|
84
|
+
|
|
85
|
+
if (options.verbose && errors.length > 0) {
|
|
86
|
+
logger.warn('\nWarnings:');
|
|
87
|
+
errors.forEach(err => {
|
|
88
|
+
logger.warn(` • ${err.path}: ${err.error}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
spinner.succeed(`Cleanup completed! Removed ${successCount} item(s) (${formattedSize})`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
} catch (error) {
|
|
96
|
+
spinner.fail('Cleanup failed');
|
|
97
|
+
|
|
98
|
+
// Re-throw as AtomixCLIError with helpful suggestions
|
|
99
|
+
throw new AtomixCLIError(
|
|
100
|
+
error.message,
|
|
101
|
+
'FILESYSTEM_ERROR',
|
|
102
|
+
[
|
|
103
|
+
'Check if you have write permissions for the target directories',
|
|
104
|
+
'Ensure no files are locked by another process',
|
|
105
|
+
'Try running with --verbose to see which file caused the issue'
|
|
106
|
+
]
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Doctor Command
|
|
3
|
+
* Diagnostic tool to verify the environment and project health
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { checkRuntimes, checkProjectStructure, checkConfig, checkPermissions, checkPlugins, checkTokens } from '../utils/diagnostics.js';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
|
|
10
|
+
/** Short descriptions for each doctor check (for --explain) */
|
|
11
|
+
const DOCTOR_CHECK_DESCRIPTIONS = {
|
|
12
|
+
'Node.js': 'Ensures Node.js >= 18.0.0 for CLI and build compatibility.',
|
|
13
|
+
'NPM': 'Ensures npm >= 8.0.0 for package management.',
|
|
14
|
+
'Directory: src': 'Required project directory for application source.',
|
|
15
|
+
'Directory: scripts/cli': 'Required only for Atomix monorepo development (internal).',
|
|
16
|
+
'Directory: themes (Recommended)': 'Optional; recommended for custom themes.',
|
|
17
|
+
'Directory: docs (Recommended)': 'Optional; recommended for documentation.',
|
|
18
|
+
'Configuration': 'Looks for atomix.config.ts or atomix.config.js in project root.',
|
|
19
|
+
'Permissions: .': 'Read/write access to project root.',
|
|
20
|
+
'Permissions: src': 'Read/write access to src directory.',
|
|
21
|
+
'Plugins': 'Reports plugins registered in atomix.config.',
|
|
22
|
+
'Tokens': 'Design token discovery: src/styles/01-settings/_settings.*.scss (optional).'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Action for the `atomix doctor` command
|
|
27
|
+
* @param {object} options - Command options (--explain)
|
|
28
|
+
*/
|
|
29
|
+
export async function doctorAction(options = {}) {
|
|
30
|
+
if (options.explain) {
|
|
31
|
+
console.log(chalk.bold.cyan('Atomix doctor checks:\n'));
|
|
32
|
+
for (const [name, desc] of Object.entries(DOCTOR_CHECK_DESCRIPTIONS)) {
|
|
33
|
+
console.log(chalk.bold(name));
|
|
34
|
+
console.log(chalk.gray(` ${desc}\n`));
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk.gray('Run `atomix doctor` (without --explain) to run the checks.'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const spinner = logger.spinner('Running diagnostics...').start();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const runtimes = await checkRuntimes();
|
|
44
|
+
const structure = await checkProjectStructure();
|
|
45
|
+
const config = await checkConfig();
|
|
46
|
+
const permissions = await checkPermissions();
|
|
47
|
+
const plugins = await checkPlugins();
|
|
48
|
+
const tokens = await checkTokens();
|
|
49
|
+
|
|
50
|
+
spinner.stop();
|
|
51
|
+
|
|
52
|
+
logger.box('Atomix Diagnostic Report', {
|
|
53
|
+
borderColor: 'cyan',
|
|
54
|
+
padding: 1,
|
|
55
|
+
margin: 1
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const allResults = [...runtimes, ...structure, ...config, ...permissions, ...plugins, ...tokens];
|
|
59
|
+
let issuesFound = false;
|
|
60
|
+
|
|
61
|
+
for (const result of allResults) {
|
|
62
|
+
const icon = result.status === 'pass' ? chalk.green('✓') :
|
|
63
|
+
result.status === 'warn' ? chalk.yellow('!') :
|
|
64
|
+
chalk.red('❌');
|
|
65
|
+
|
|
66
|
+
const statusText = result.status.toUpperCase();
|
|
67
|
+
const nameText = chalk.bold(result.name);
|
|
68
|
+
const messageText = chalk.gray(result.message);
|
|
69
|
+
|
|
70
|
+
console.log(`${icon} [${statusText}] ${nameText}: ${messageText}`);
|
|
71
|
+
|
|
72
|
+
if (result.status !== 'pass' && result.suggestion) {
|
|
73
|
+
issuesFound = true;
|
|
74
|
+
console.log(chalk.blue(` 💡 Suggestion: ${result.suggestion}`));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!issuesFound) {
|
|
79
|
+
console.log(chalk.bold.green('\n🎉 Your environment is healthy! Everything is ready for Atomix.'));
|
|
80
|
+
} else {
|
|
81
|
+
console.log(chalk.bold.yellow('\n⚠️ Some issues or suggestions were found. Review the report above.'));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
spinner.fail('Diagnostic failed');
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Generate Command
|
|
3
|
+
* Orchestrates component generation with validation, hooks, and error handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { AtomixCLIError } from '../utils/error.js';
|
|
9
|
+
import { generator, COMPLEXITY_LEVELS, COMPONENT_FEATURES } from '../internal/generator.js';
|
|
10
|
+
import { filesystem } from '../internal/filesystem.js';
|
|
11
|
+
import { validateComponentName } from '../utils/validation.js';
|
|
12
|
+
import { hookManager } from '../internal/hooks.js';
|
|
13
|
+
import { validateComponent } from '../internal/validator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Action logic for generating components
|
|
17
|
+
* @param {string} type - Component type (component, token, etc.)
|
|
18
|
+
* @param {string} name - Component name
|
|
19
|
+
* @param {Object} options - CLI options
|
|
20
|
+
*/
|
|
21
|
+
export async function generateAction(type, name, options) {
|
|
22
|
+
let config = {
|
|
23
|
+
name,
|
|
24
|
+
complexity: options.complexity || 'medium',
|
|
25
|
+
features: determineFeatures(options),
|
|
26
|
+
outputPath: options.path || './src/components'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (options.interactive) {
|
|
30
|
+
config = await promptInteractive();
|
|
31
|
+
if (!config) return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Pre-generation hook
|
|
35
|
+
try {
|
|
36
|
+
config = await hookManager.trigger('preGenerate', config);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw new AtomixCLIError(
|
|
39
|
+
`Pre-generation hook failed: ${error.message}`,
|
|
40
|
+
'HOOK_EXECUTION_FAILED',
|
|
41
|
+
[
|
|
42
|
+
'Check preGenerate hook implementation',
|
|
43
|
+
'Verify hook returns valid configuration object',
|
|
44
|
+
'Review hook logs with ATOMIX_DEBUG=true'
|
|
45
|
+
]
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const spinner = logger.spinner(`Generating ${type}: ${config.name}...`).start();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Validation
|
|
53
|
+
const nameValidation = await validateComponentName(config.name);
|
|
54
|
+
if (!nameValidation.isValid) {
|
|
55
|
+
throw new AtomixCLIError(
|
|
56
|
+
nameValidation.error,
|
|
57
|
+
'INVALID_NAME',
|
|
58
|
+
[
|
|
59
|
+
'Use PascalCase (e.g., MyComponent, Button)',
|
|
60
|
+
'Start with a letter (not numbers)',
|
|
61
|
+
'Avoid special characters except letters and numbers'
|
|
62
|
+
]
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const pathValidation = filesystem.validatePath(config.outputPath);
|
|
67
|
+
if (!pathValidation.isValid) {
|
|
68
|
+
throw new AtomixCLIError(
|
|
69
|
+
pathValidation.error,
|
|
70
|
+
'INVALID_PATH',
|
|
71
|
+
[
|
|
72
|
+
'Ensure path is within project root',
|
|
73
|
+
'Check you have write permissions for target directory',
|
|
74
|
+
'Use absolute path or path relative to current directory'
|
|
75
|
+
]
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Execution
|
|
80
|
+
let path;
|
|
81
|
+
if (options.prompt) {
|
|
82
|
+
path = await generator.generateAIComponent(config.name, options.prompt, {
|
|
83
|
+
...config,
|
|
84
|
+
logger: { debug: (msg) => logger.debug(msg) }
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
path = await generator.generateComponent(config.name, {
|
|
88
|
+
...config,
|
|
89
|
+
logger: { debug: (msg) => logger.debug(msg) }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
spinner.succeed(`Generated component ${config.name} at ${path}`);
|
|
94
|
+
|
|
95
|
+
if (options.validate) {
|
|
96
|
+
let report = await generator.validate(config.name, path);
|
|
97
|
+
// Component-scoped A11y and token validation (Phase 2: design system creator)
|
|
98
|
+
if (type === 'component') {
|
|
99
|
+
const componentReport = await validateComponent(config.name, process.cwd());
|
|
100
|
+
const componentIssues = componentReport.issues.map(
|
|
101
|
+
(i) => (typeof i === 'string' ? i : `[${i.type}] ${i.file}: ${i.message}`)
|
|
102
|
+
);
|
|
103
|
+
report.issues = [...report.issues, ...componentIssues];
|
|
104
|
+
report.valid = report.valid && componentReport.valid;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Validation hook
|
|
108
|
+
try {
|
|
109
|
+
report = await hookManager.trigger('onValidate', report);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logger.warn(`Validation hook failed: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!report.valid) {
|
|
115
|
+
logger.warn('Validation found issues:');
|
|
116
|
+
report.issues.forEach((i) => logger.info(` - ${i}`));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Post-build hook (using generated path as asset)
|
|
121
|
+
try {
|
|
122
|
+
await hookManager.trigger('postBuild', [path]);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.warn(`Post-build hook failed: ${error.message}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
logger.box(`🎉 Component ${config.name} ready!\nRun: atomix validate component ${config.name}`);
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
spinner.fail('Generation failed');
|
|
131
|
+
|
|
132
|
+
// Enhance error context for known error types
|
|
133
|
+
if (error.code === 'TEMPLATE_NOT_FOUND') {
|
|
134
|
+
error.suggestions.push('Run `atomix doctor` to check template availability');
|
|
135
|
+
} else if (error.code === 'FRAMEWORK_DETECTION_FAILED') {
|
|
136
|
+
error.suggestions.push('Initialize project with `npm init` or `yarn init`');
|
|
137
|
+
} else if (error.code === 'FILE_WRITE_FAILED') {
|
|
138
|
+
error.suggestions.push('Check disk space and file permissions');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Determine which features to enable based on CLI options
|
|
147
|
+
* @param {Object} options - CLI options
|
|
148
|
+
* @returns {string[]} Array of feature names
|
|
149
|
+
*/
|
|
150
|
+
function determineFeatures(options) {
|
|
151
|
+
const features = [];
|
|
152
|
+
|
|
153
|
+
// Default features (always enabled unless explicitly disabled)
|
|
154
|
+
if (options.storybook !== false) {
|
|
155
|
+
features.push('storybook');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (options.hook !== false) {
|
|
159
|
+
features.push('hook');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (options.styles !== false) {
|
|
163
|
+
features.push('styles');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Optional features (enabled by flag)
|
|
167
|
+
if (options.tests === true) {
|
|
168
|
+
features.push('tests');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return features;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Interactive mode handler
|
|
176
|
+
* Prompts user for component configuration
|
|
177
|
+
* @returns {Promise<Object>} Configuration object
|
|
178
|
+
*/
|
|
179
|
+
async function promptInteractive() {
|
|
180
|
+
logger.info('🎨 Interactive Component Generator');
|
|
181
|
+
|
|
182
|
+
const answers = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: 'input',
|
|
185
|
+
name: 'name',
|
|
186
|
+
message: 'Component name (PascalCase):',
|
|
187
|
+
validate: async (val) => {
|
|
188
|
+
const result = await validateComponentName(val);
|
|
189
|
+
return result.isValid || `Invalid: ${result.error}. Use PascalCase (e.g., MyComponent)`;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'list',
|
|
194
|
+
name: 'complexity',
|
|
195
|
+
message: 'Complexity level:',
|
|
196
|
+
choices: Object.keys(COMPLEXITY_LEVELS).map(k => ({
|
|
197
|
+
name: k.toLowerCase(),
|
|
198
|
+
value: k.toLowerCase()
|
|
199
|
+
})),
|
|
200
|
+
default: 'medium'
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: 'checkbox',
|
|
204
|
+
name: 'features',
|
|
205
|
+
message: 'Select features:',
|
|
206
|
+
choices: Object.keys(COMPONENT_FEATURES).map(k => ({
|
|
207
|
+
name: `${k.toLowerCase()}${COMPONENT_FEATURES[k].default ? ' (default)' : ''}`,
|
|
208
|
+
value: COMPONENT_FEATURES[k].name,
|
|
209
|
+
checked: COMPONENT_FEATURES[k].default
|
|
210
|
+
}))
|
|
211
|
+
}
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
...answers,
|
|
216
|
+
outputPath: './src/components'
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Init Command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { wizard } from '../internal/wizard.js';
|
|
8
|
+
import { isNonInteractive } from '../utils/helpers.js';
|
|
9
|
+
import { AtomixCLIError, ErrorCategory } from '../utils/error.js';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_PROJECT_TYPE = 'react';
|
|
12
|
+
const DEFAULT_FEATURES = ['typescript', 'storybook', 'testing'];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Action logic for the init command
|
|
16
|
+
* @param {object} options - Command options (--yes, --type)
|
|
17
|
+
*/
|
|
18
|
+
export async function initAction(options = {}) {
|
|
19
|
+
const useDefaults = options.yes || options.type || isNonInteractive();
|
|
20
|
+
let projectType = (options.type || '').toLowerCase();
|
|
21
|
+
const validTypes = ['react', 'nextjs', 'vanilla'];
|
|
22
|
+
if (options.type && !validTypes.includes(projectType)) {
|
|
23
|
+
throw new AtomixCLIError(
|
|
24
|
+
`Invalid --type: ${options.type}. Use one of: ${validTypes.join(', ')}`,
|
|
25
|
+
ErrorCategory.VALIDATION,
|
|
26
|
+
['Example: atomix init --type react']
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (!projectType) projectType = DEFAULT_PROJECT_TYPE;
|
|
30
|
+
|
|
31
|
+
logger.info('🎨 Atomix Design System Setup Wizard');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
let answers;
|
|
35
|
+
if (useDefaults) {
|
|
36
|
+
answers = {
|
|
37
|
+
projectType,
|
|
38
|
+
features: DEFAULT_FEATURES
|
|
39
|
+
};
|
|
40
|
+
if (options.type) logger.info(`Using project type: ${projectType}`);
|
|
41
|
+
} else {
|
|
42
|
+
answers = await inquirer.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: 'list',
|
|
45
|
+
name: 'projectType',
|
|
46
|
+
message: 'What type of project are you building?',
|
|
47
|
+
choices: ['react', 'nextjs', 'vanilla'],
|
|
48
|
+
default: projectType
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'checkbox',
|
|
52
|
+
name: 'features',
|
|
53
|
+
message: 'Select features:',
|
|
54
|
+
choices: ['typescript', 'storybook', 'testing']
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const spinner = logger.spinner('Setting up project...').start();
|
|
60
|
+
|
|
61
|
+
await wizard.initProject(answers.projectType, {
|
|
62
|
+
...answers,
|
|
63
|
+
logger: { debug: (msg) => logger.debug(msg) }
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
spinner.succeed('Project initialized successfully');
|
|
67
|
+
|
|
68
|
+
logger.box('✨ Setup Complete!\nRun: npm run dev');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.error(`Setup failed: ${error.message}`);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI - Migrate Command
|
|
3
|
+
* Handles migrations from Tailwind, Bootstrap, and other frameworks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { stat } from 'fs/promises';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
import { migrateTailwind, migrateBootstrap } from '../migration-tools.js';
|
|
11
|
+
import { AtomixCLIError, ErrorCategory } from '../utils/error.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ensure source is a directory; throw AtomixCLIError otherwise
|
|
15
|
+
*/
|
|
16
|
+
async function ensureSourceIsDirectory(source) {
|
|
17
|
+
const absolute = resolve(process.cwd(), source);
|
|
18
|
+
let st;
|
|
19
|
+
try {
|
|
20
|
+
st = await stat(absolute);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
if (err.code === 'ENOENT') {
|
|
23
|
+
throw new AtomixCLIError(
|
|
24
|
+
`Source not found: ${source}`,
|
|
25
|
+
ErrorCategory.INVALID_PATH,
|
|
26
|
+
['Source must be a directory. Pass the project root (e.g. . or ./my-app).', 'Check that the path exists.']
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
if (!st.isDirectory()) {
|
|
32
|
+
throw new AtomixCLIError(
|
|
33
|
+
`Source is not a directory: ${source}`,
|
|
34
|
+
ErrorCategory.INVALID_PATH,
|
|
35
|
+
['Source must be the project root directory (e.g. . or ./my-tailwind-app).', 'Do not pass a file path (e.g. package.json).']
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return absolute;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Migrate action handler
|
|
43
|
+
*/
|
|
44
|
+
export async function migrateAction(type, source, options) {
|
|
45
|
+
await ensureSourceIsDirectory(source);
|
|
46
|
+
|
|
47
|
+
logger.info(chalk.blue(`🚀 Starting migration: ${type} from ${source}...`));
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
let report;
|
|
51
|
+
|
|
52
|
+
switch (type.toLowerCase()) {
|
|
53
|
+
case 'tailwind':
|
|
54
|
+
report = await migrateTailwind(source, options);
|
|
55
|
+
break;
|
|
56
|
+
case 'bootstrap':
|
|
57
|
+
report = await migrateBootstrap(source, options);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
throw new AtomixCLIError(
|
|
61
|
+
`Unsupported migration type: ${type}. Use tailwind or bootstrap.`,
|
|
62
|
+
ErrorCategory.VALIDATION,
|
|
63
|
+
['Example: atomix migrate tailwind .']
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Display Migration Report
|
|
68
|
+
displayMigrationReport(report);
|
|
69
|
+
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error instanceof AtomixCLIError) throw error;
|
|
72
|
+
throw new AtomixCLIError(
|
|
73
|
+
error.message || 'Migration failed',
|
|
74
|
+
ErrorCategory.FILESYSTEM,
|
|
75
|
+
['Check source path and permissions. Run with --dry-run to preview.']
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Display migration report in a clean format
|
|
82
|
+
*/
|
|
83
|
+
function displayMigrationReport(report) {
|
|
84
|
+
console.log('\n' + chalk.bold.underline('Migration Report'));
|
|
85
|
+
console.log(`${chalk.green('✔')} Files processed: ${report.filesProcessed}`);
|
|
86
|
+
console.log(`${chalk.green('✔')} Classes replaced: ${report.classesReplaced}`);
|
|
87
|
+
|
|
88
|
+
if (report.warnings.length > 0) {
|
|
89
|
+
console.log('\n' + chalk.yellow.bold('Warnings:'));
|
|
90
|
+
const uniqueWarnings = [...new Set(report.warnings.map(w => `${w.file}: ${w.class}`))];
|
|
91
|
+
uniqueWarnings.slice(0, 10).forEach(w => console.log(` ${chalk.yellow('!')} ${w}`));
|
|
92
|
+
if (uniqueWarnings.length > 10) {
|
|
93
|
+
console.log(` ... and ${uniqueWarnings.length - 10} more`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (report.errors.length > 0) {
|
|
98
|
+
console.log('\n' + chalk.red.bold('Errors:'));
|
|
99
|
+
report.errors.slice(0, 10).forEach(e => console.log(` ${chalk.red('✘')} ${e.file}: ${e.error}`));
|
|
100
|
+
if (report.errors.length > 10) {
|
|
101
|
+
console.log(` ... and ${report.errors.length - 10} more`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('\n' + chalk.green.bold('Migration completed successfully! 🎉'));
|
|
106
|
+
}
|