@shohojdhara/atomix 0.3.12 → 0.3.14
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/CHANGELOG.md +19 -0
- package/README.md +2 -0
- package/dist/atomix.css +101 -88
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +5 -15258
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +17 -19
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +41 -11
- package/dist/core.js +55 -41
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +28 -11
- package/dist/forms.js +25 -24
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +32 -25
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +122 -46
- package/dist/index.esm.js +865 -200
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +870 -204
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +27 -2
- package/dist/theme.js +721 -108
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/scripts/atomix-cli.js +610 -1111
- package/scripts/cli/component-generator.js +610 -0
- package/scripts/cli/documentation-sync.js +542 -0
- package/scripts/cli/interactive-init.js +84 -288
- package/scripts/cli/mappings.js +211 -0
- package/scripts/cli/migration-tools.js +95 -288
- package/scripts/cli/template-manager.js +107 -0
- package/scripts/cli/templates/README.md +123 -0
- package/scripts/cli/templates/composable-templates.js +149 -0
- package/scripts/cli/templates/config-templates.js +126 -0
- package/scripts/cli/templates/index.js +95 -0
- package/scripts/cli/templates/project-templates.js +214 -0
- package/scripts/cli/templates/react-templates.js +261 -0
- package/scripts/cli/templates/scss-templates.js +156 -0
- package/scripts/cli/templates/storybook-templates.js +236 -0
- package/scripts/cli/templates/testing-templates.js +45 -0
- package/scripts/cli/templates/token-templates.js +447 -0
- package/scripts/cli/templates/types-templates.js +133 -0
- package/scripts/cli/templates-original-backup.js +1655 -0
- package/scripts/cli/templates.js +35 -0
- package/scripts/cli/templates_backup.js +684 -0
- package/scripts/cli/theme-bridge.js +20 -14
- package/scripts/cli/token-manager.js +150 -77
- package/scripts/cli/utils.js +37 -25
- package/src/components/Accordion/Accordion.stories.tsx +5 -5
- package/src/components/Accordion/Accordion.test.tsx +57 -0
- package/src/components/Accordion/Accordion.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +41 -44
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +37 -37
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -2
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -51
- package/src/components/Avatar/Avatar.stories.tsx +26 -26
- package/src/components/Badge/Badge.stories.tsx +31 -31
- package/src/components/Badge/Badge.test.tsx +51 -0
- package/src/components/Badge/Badge.tsx +20 -1
- package/src/components/Block/Block.stories.tsx +5 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -1
- package/src/components/Breadcrumb/Breadcrumb.tsx +2 -2
- package/src/components/Button/Button.stories.tsx +13 -13
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Button/ButtonGroup.stories.tsx +2 -2
- package/src/components/Button/README.md +5 -0
- package/src/components/Callout/Callout.stories.tsx +11 -11
- package/src/components/Callout/Callout.test.tsx +10 -10
- package/src/components/Callout/Callout.tsx +7 -7
- package/src/components/Callout/README.md +9 -8
- package/src/components/Card/Card.tsx +2 -2
- package/src/components/Chart/Chart.stories.tsx +6 -6
- package/src/components/Chart/Chart.tsx +1 -1
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +14 -12
- package/src/components/DatePicker/DatePicker.stories.tsx +6 -6
- package/src/components/Dropdown/Dropdown.stories.tsx +4 -4
- package/src/components/Form/Checkbox.stories.tsx +3 -3
- package/src/components/Form/Checkbox.tsx +4 -2
- package/src/components/Form/Form.stories.tsx +3 -3
- package/src/components/Form/FormGroup.stories.tsx +1 -1
- package/src/components/Form/Input.stories.tsx +28 -16
- package/src/components/Form/Input.test.tsx +59 -0
- package/src/components/Form/Input.tsx +97 -95
- package/src/components/Form/Radio.stories.tsx +94 -94
- package/src/components/Form/Radio.tsx +2 -2
- package/src/components/Form/Select.stories.tsx +4 -4
- package/src/components/Form/Select.tsx +2 -2
- package/src/components/Form/Textarea.stories.tsx +22 -7
- package/src/components/Form/Textarea.test.tsx +45 -0
- package/src/components/Form/Textarea.tsx +88 -86
- package/src/components/List/List.stories.tsx +2 -2
- package/src/components/Modal/Modal.stories.tsx +4 -4
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +5 -5
- package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
- package/src/components/Navigation/README.md +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +5 -2
- package/src/components/Pagination/Pagination.tsx +1 -1
- package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -10
- package/src/components/Popover/Popover.stories.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +1 -1
- package/src/components/Progress/Progress.tsx +46 -46
- package/src/components/Rating/Rating.stories.tsx +4 -4
- package/src/components/Rating/Rating.tsx +8 -8
- package/src/components/Slider/Slider.stories.tsx +63 -63
- package/src/components/Spinner/Spinner.stories.tsx +2 -2
- package/src/components/Spinner/Spinner.test.tsx +35 -0
- package/src/components/Spinner/Spinner.tsx +9 -2
- package/src/components/Testimonial/Testimonial.stories.tsx +1 -1
- package/src/components/Toggle/Toggle.stories.tsx +32 -9
- package/src/components/Toggle/Toggle.test.tsx +91 -0
- package/src/components/Toggle/Toggle.tsx +44 -27
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/layouts/Grid/Grid.stories.tsx +49 -49
- package/src/layouts/MasonryGrid/MasonryGrid.stories.tsx +2 -2
- package/src/lib/composables/useAccordion.ts +12 -3
- package/src/lib/composables/useBreadcrumb.ts +2 -2
- package/src/lib/composables/useCallout.ts +7 -7
- package/src/lib/composables/useNavbar.ts +1 -1
- package/src/lib/constants/components.ts +1 -1
- package/src/lib/storybook/InteractiveDemo.tsx +113 -0
- package/src/lib/storybook/PreviewContainer.tsx +36 -0
- package/src/lib/storybook/VariantsGrid.tsx +21 -0
- package/src/lib/storybook/index.ts +3 -0
- package/src/lib/theme/core/createThemeObject.ts +9 -5
- package/src/lib/theme/devtools/CLI.ts +155 -0
- package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +213 -0
- package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +566 -0
- package/src/lib/theme/devtools/LiveEditor.tsx +2 -1
- package/src/lib/theme/devtools/index.ts +3 -0
- package/src/lib/theme/errors/errors.ts +8 -0
- package/src/lib/theme/runtime/ThemeProvider.tsx +117 -57
- package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +305 -0
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +588 -0
- package/src/lib/theme/utils/__tests__/themeValidation.test.ts +264 -0
- package/src/lib/theme/utils/index.ts +1 -0
- package/src/lib/theme/utils/themeValidation.ts +501 -0
- package/src/lib/theme-tools.ts +32 -3
- package/src/lib/types/components.ts +81 -26
- package/src/lib/utils/themeNaming.ts +1 -1
- package/src/styles/06-components/_components.atomix-glass.scss +14 -15
- package/src/styles/06-components/_components.callout.scss +29 -33
- package/src/styles/06-components/_index.scss +1 -1
- package/src/styles/99-utilities/_utilities.display.scss +14 -3
- package/src/styles/99-utilities/_utilities.flex.scss +10 -10
- package/src/styles/99-utilities/_utilities.text.scss +28 -8
- package/scripts/cli/__tests__/cli-commands.test.js +0 -204
- package/scripts/cli/__tests__/utils.test.js +0 -201
- package/scripts/cli/__tests__/vitest.config.js +0 -26
package/scripts/atomix-cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Atomix CLI
|
|
4
|
+
* Atomix CLI
|
|
5
5
|
* Design System Development Tools
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -20,17 +20,18 @@ import chokidar from 'chokidar';
|
|
|
20
20
|
import inquirer from 'inquirer';
|
|
21
21
|
import boxen from 'boxen';
|
|
22
22
|
import { runInitWizard } from './cli/interactive-init.js';
|
|
23
|
-
import {
|
|
24
|
-
migrateTailwind,
|
|
25
|
-
migrateBootstrap,
|
|
26
|
-
migrateSCSSVariables,
|
|
27
|
-
displayMigrationReport
|
|
23
|
+
import {
|
|
24
|
+
migrateTailwind,
|
|
25
|
+
migrateBootstrap,
|
|
26
|
+
migrateSCSSVariables,
|
|
27
|
+
displayMigrationReport
|
|
28
28
|
} from './cli/migration-tools.js';
|
|
29
29
|
import {
|
|
30
30
|
listTokens,
|
|
31
31
|
validateTokens,
|
|
32
32
|
exportTokens,
|
|
33
|
-
importTokens
|
|
33
|
+
importTokens,
|
|
34
|
+
fixTokens
|
|
34
35
|
} from './cli/token-manager.js';
|
|
35
36
|
import { createThemeCLIBridge } from './cli/theme-bridge.js';
|
|
36
37
|
import {
|
|
@@ -39,8 +40,32 @@ import {
|
|
|
39
40
|
validateThemeName,
|
|
40
41
|
sanitizeInput,
|
|
41
42
|
fileExists,
|
|
42
|
-
isDebug as checkDebugMode
|
|
43
|
+
isDebug as checkDebugMode,
|
|
44
|
+
checkNodeVersion,
|
|
45
|
+
AtomixCLIError
|
|
43
46
|
} from './cli/utils.js';
|
|
47
|
+
import {
|
|
48
|
+
componentTemplates,
|
|
49
|
+
generateColorTokens,
|
|
50
|
+
generateSpacingTokens,
|
|
51
|
+
generateTypographyTokens,
|
|
52
|
+
generateShadowTokens,
|
|
53
|
+
generateRadiusTokens,
|
|
54
|
+
generateAnimationTokens
|
|
55
|
+
} from './cli/templates.js';
|
|
56
|
+
import {
|
|
57
|
+
COMPLEXITY_LEVELS,
|
|
58
|
+
COMPONENT_FEATURES,
|
|
59
|
+
generateComponentByComplexity,
|
|
60
|
+
interactiveComponentGeneration,
|
|
61
|
+
validateGeneratedComponent,
|
|
62
|
+
displayValidationReport
|
|
63
|
+
} from './cli/component-generator.js';
|
|
64
|
+
import {
|
|
65
|
+
syncDocumentation,
|
|
66
|
+
validateDocumentation,
|
|
67
|
+
generateCLIDocumentation
|
|
68
|
+
} from './cli/documentation-sync.js';
|
|
44
69
|
|
|
45
70
|
const __filename = fileURLToPath(import.meta.url);
|
|
46
71
|
const __dirname = dirname(__filename);
|
|
@@ -53,18 +78,6 @@ const packageJson = JSON.parse(
|
|
|
53
78
|
// CLI Configuration
|
|
54
79
|
const DEBUG = process.env.ATOMIX_DEBUG === 'true' || process.argv.includes('--debug');
|
|
55
80
|
|
|
56
|
-
/**
|
|
57
|
-
* Enhanced Error Class
|
|
58
|
-
*/
|
|
59
|
-
class AtomixCLIError extends Error {
|
|
60
|
-
constructor(message, code, suggestions = []) {
|
|
61
|
-
super(message);
|
|
62
|
-
this.name = 'AtomixCLIError';
|
|
63
|
-
this.code = code;
|
|
64
|
-
this.suggestions = suggestions;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
81
|
/**
|
|
69
82
|
* Debug logger
|
|
70
83
|
*/
|
|
@@ -72,7 +85,7 @@ function debug(message, data = null) {
|
|
|
72
85
|
if (DEBUG) {
|
|
73
86
|
console.log(chalk.gray(`[DEBUG] ${message}`));
|
|
74
87
|
if (data) {
|
|
75
|
-
console.log(chalk.gray(JSON.stringify(data, null, 2)));
|
|
88
|
+
console.log(chalk.gray(JSON.stringify(data, null, 2),));
|
|
76
89
|
}
|
|
77
90
|
}
|
|
78
91
|
}
|
|
@@ -82,497 +95,25 @@ function debug(message, data = null) {
|
|
|
82
95
|
*/
|
|
83
96
|
function handleError(error, spinner = null) {
|
|
84
97
|
if (spinner) spinner.fail(chalk.red('Operation failed'));
|
|
85
|
-
|
|
98
|
+
|
|
86
99
|
console.error(chalk.bold.red(`\n❌ ${error.message}`));
|
|
87
|
-
|
|
100
|
+
|
|
88
101
|
if (error instanceof AtomixCLIError && error.suggestions.length > 0) {
|
|
89
102
|
console.log(chalk.yellow('\n💡 Suggestions:'));
|
|
90
103
|
error.suggestions.forEach((suggestion, index) => {
|
|
91
104
|
console.log(chalk.gray(` ${index + 1}. ${suggestion}`));
|
|
92
105
|
});
|
|
93
106
|
}
|
|
94
|
-
|
|
107
|
+
|
|
95
108
|
if (DEBUG && error.stack) {
|
|
96
109
|
console.error(chalk.gray('\nStack trace:'));
|
|
97
110
|
console.error(chalk.gray(error.stack));
|
|
98
111
|
}
|
|
99
|
-
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Component templates for design system
|
|
105
|
-
*/
|
|
106
|
-
const componentTemplates = {
|
|
107
|
-
react: {
|
|
108
|
-
component: (name, options = {}) => `import React, { forwardRef, memo } from 'react';
|
|
109
|
-
${options.scssModule ? `import styles from './${name}.module.scss';` : ''}
|
|
110
|
-
${options.types ? `import type { ${name}Props } from '../../lib/types/components';` : ''}
|
|
111
|
-
|
|
112
|
-
${options.types ? '' : `export interface ${name}Props {
|
|
113
|
-
/**
|
|
114
|
-
* Content to be rendered
|
|
115
|
-
*/
|
|
116
|
-
children?: React.ReactNode;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Additional CSS classes
|
|
120
|
-
*/
|
|
121
|
-
className?: string;
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Size variant
|
|
125
|
-
*/
|
|
126
|
-
size?: 'sm' | 'md' | 'lg';
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Color variant
|
|
130
|
-
*/
|
|
131
|
-
variant?: 'primary' | 'secondary' | 'success' | 'error';
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Disabled state
|
|
135
|
-
*/
|
|
136
|
-
disabled?: boolean;
|
|
137
|
-
}`}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* ${name} component
|
|
141
|
-
*
|
|
142
|
-
* @component
|
|
143
|
-
* @example
|
|
144
|
-
* <${name} variant="primary" size="md">
|
|
145
|
-
* Content
|
|
146
|
-
* </${name}>
|
|
147
|
-
*/
|
|
148
|
-
export const ${name} = memo(
|
|
149
|
-
forwardRef<HTMLDivElement, ${name}Props>(
|
|
150
|
-
({ children, className = '', size = 'md', variant = 'primary', disabled = false, ...props }, ref) => {
|
|
151
|
-
${options.scssModule ? `const componentClasses = [
|
|
152
|
-
styles.${name.toLowerCase()},
|
|
153
|
-
styles[\`${name.toLowerCase()}--\${size}\`],
|
|
154
|
-
styles[\`${name.toLowerCase()}--\${variant}\`],
|
|
155
|
-
disabled && styles['${name.toLowerCase()}--disabled'],
|
|
156
|
-
className
|
|
157
|
-
].filter(Boolean).join(' ');` : `const componentClasses = \`c-${name.toLowerCase()} c-${name.toLowerCase()}--\${size} c-${name.toLowerCase()}--\${variant} \${disabled ? 'c-${name.toLowerCase()}--disabled' : ''} \${className}\`.trim();`}
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
<div
|
|
161
|
-
ref={ref}
|
|
162
|
-
className={componentClasses}
|
|
163
|
-
aria-disabled={disabled}
|
|
164
|
-
{...props}
|
|
165
|
-
>
|
|
166
|
-
{children}
|
|
167
|
-
</div>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
)
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
${name}.displayName = '${name}';
|
|
174
|
-
|
|
175
|
-
export default ${name};`,
|
|
176
|
-
|
|
177
|
-
index: (name) => `export { default as ${name} } from './${name}';
|
|
178
|
-
export type { ${name}Props } from './${name}';`,
|
|
179
|
-
|
|
180
|
-
story: (name) => `import type { Meta, StoryObj } from '@storybook/react';
|
|
181
|
-
import { ${name} } from './${name}';
|
|
182
|
-
|
|
183
|
-
const meta: Meta<typeof ${name}> = {
|
|
184
|
-
title: 'Components/${name}',
|
|
185
|
-
component: ${name},
|
|
186
|
-
parameters: {
|
|
187
|
-
layout: 'centered',
|
|
188
|
-
},
|
|
189
|
-
tags: ['autodocs'],
|
|
190
|
-
argTypes: {
|
|
191
|
-
size: {
|
|
192
|
-
control: 'select',
|
|
193
|
-
options: ['sm', 'md', 'lg'],
|
|
194
|
-
},
|
|
195
|
-
variant: {
|
|
196
|
-
control: 'select',
|
|
197
|
-
options: ['primary', 'secondary', 'success', 'error'],
|
|
198
|
-
},
|
|
199
|
-
disabled: {
|
|
200
|
-
control: 'boolean',
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
export default meta;
|
|
206
|
-
type Story = StoryObj<typeof meta>;
|
|
207
|
-
|
|
208
|
-
export const Default: Story = {
|
|
209
|
-
args: {
|
|
210
|
-
children: '${name} Component',
|
|
211
|
-
size: 'md',
|
|
212
|
-
variant: 'primary',
|
|
213
|
-
},
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
export const Small: Story = {
|
|
217
|
-
args: {
|
|
218
|
-
...Default.args,
|
|
219
|
-
size: 'sm',
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
export const Large: Story = {
|
|
224
|
-
args: {
|
|
225
|
-
...Default.args,
|
|
226
|
-
size: 'lg',
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
export const Secondary: Story = {
|
|
231
|
-
args: {
|
|
232
|
-
...Default.args,
|
|
233
|
-
variant: 'secondary',
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
export const Success: Story = {
|
|
238
|
-
args: {
|
|
239
|
-
...Default.args,
|
|
240
|
-
variant: 'success',
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
export const Error: Story = {
|
|
245
|
-
args: {
|
|
246
|
-
...Default.args,
|
|
247
|
-
variant: 'error',
|
|
248
|
-
},
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export const Disabled: Story = {
|
|
252
|
-
args: {
|
|
253
|
-
...Default.args,
|
|
254
|
-
disabled: true,
|
|
255
|
-
},
|
|
256
|
-
};`,
|
|
257
|
-
|
|
258
|
-
test: (name) => `import { describe, it, expect } from 'vitest';
|
|
259
|
-
import { render, screen } from '@testing-library/react';
|
|
260
|
-
import { ${name} } from './${name}';
|
|
261
|
-
|
|
262
|
-
describe('${name}', () => {
|
|
263
|
-
it('renders children correctly', () => {
|
|
264
|
-
render(<${name}>Test Content</${name}>);
|
|
265
|
-
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('applies size variant classes', () => {
|
|
269
|
-
const { container } = render(<${name} size="lg">Content</${name}>);
|
|
270
|
-
const element = container.firstChild;
|
|
271
|
-
expect(element).toHaveClass('c-${name.toLowerCase()}--lg');
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('applies variant classes', () => {
|
|
275
|
-
const { container } = render(<${name} variant="success">Content</${name}>);
|
|
276
|
-
const element = container.firstChild;
|
|
277
|
-
expect(element).toHaveClass('c-${name.toLowerCase()}--success');
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('handles disabled state', () => {
|
|
281
|
-
const { container } = render(<${name} disabled>Content</${name}>);
|
|
282
|
-
const element = container.firstChild;
|
|
283
|
-
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
284
|
-
expect(element).toHaveClass('c-${name.toLowerCase()}--disabled');
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('forwards ref correctly', () => {
|
|
288
|
-
const ref = React.createRef<HTMLDivElement>();
|
|
289
|
-
render(<${name} ref={ref}>Content</${name}>);
|
|
290
|
-
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
291
|
-
});
|
|
292
|
-
});`,
|
|
293
|
-
|
|
294
|
-
scss: (name) => `// Component: ${name}
|
|
295
|
-
// =============================================================================
|
|
296
|
-
// Design system component following ITCSS architecture
|
|
297
|
-
|
|
298
|
-
@import '../../styles/01-settings';
|
|
299
|
-
@import '../../styles/02-tools';
|
|
300
|
-
|
|
301
|
-
// Block: Base component
|
|
302
|
-
// =============================================================================
|
|
303
|
-
.c-${name.toLowerCase()} {
|
|
304
|
-
// Layout
|
|
305
|
-
display: flex;
|
|
306
|
-
align-items: center;
|
|
307
|
-
justify-content: center;
|
|
308
|
-
|
|
309
|
-
// Spacing
|
|
310
|
-
padding: var(--atomix-space-3) var(--atomix-space-4);
|
|
311
|
-
gap: var(--atomix-space-2);
|
|
312
|
-
|
|
313
|
-
// Typography
|
|
314
|
-
font-family: var(--atomix-font-family-base);
|
|
315
|
-
font-size: var(--atomix-font-size-base);
|
|
316
|
-
font-weight: var(--atomix-font-weight-normal);
|
|
317
|
-
line-height: var(--atomix-line-height-base);
|
|
318
|
-
|
|
319
|
-
// Appearance
|
|
320
|
-
background-color: var(--atomix-color-background);
|
|
321
|
-
color: var(--atomix-color-text);
|
|
322
|
-
border: 1px solid var(--atomix-color-border);
|
|
323
|
-
border-radius: var(--atomix-radius-md);
|
|
324
|
-
|
|
325
|
-
// Interaction
|
|
326
|
-
cursor: default;
|
|
327
|
-
user-select: none;
|
|
328
|
-
transition: all 0.2s ease-in-out;
|
|
329
|
-
|
|
330
|
-
// Focus
|
|
331
|
-
&:focus-visible {
|
|
332
|
-
@include focus-ring;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Size Modifiers
|
|
337
|
-
// =============================================================================
|
|
338
|
-
.c-${name.toLowerCase()}--sm {
|
|
339
|
-
padding: var(--atomix-space-2) var(--atomix-space-3);
|
|
340
|
-
font-size: var(--atomix-font-size-sm);
|
|
341
|
-
gap: var(--atomix-space-1);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.c-${name.toLowerCase()}--md {
|
|
345
|
-
// Default size - explicitly defined for clarity
|
|
346
|
-
padding: var(--atomix-space-3) var(--atomix-space-4);
|
|
347
|
-
font-size: var(--atomix-font-size-base);
|
|
348
|
-
gap: var(--atomix-space-2);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.c-${name.toLowerCase()}--lg {
|
|
352
|
-
padding: var(--atomix-space-4) var(--atomix-space-5);
|
|
353
|
-
font-size: var(--atomix-font-size-lg);
|
|
354
|
-
gap: var(--atomix-space-3);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Color/Variant Modifiers
|
|
358
|
-
// =============================================================================
|
|
359
|
-
.c-${name.toLowerCase()}--primary {
|
|
360
|
-
background-color: var(--atomix-color-primary);
|
|
361
|
-
color: var(--atomix-color-primary-text);
|
|
362
|
-
border-color: var(--atomix-color-primary-dark);
|
|
363
|
-
|
|
364
|
-
&:hover:not(:disabled) {
|
|
365
|
-
background-color: var(--atomix-color-primary-dark);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
.c-${name.toLowerCase()}--secondary {
|
|
370
|
-
background-color: var(--atomix-color-secondary);
|
|
371
|
-
color: var(--atomix-color-secondary-text);
|
|
372
|
-
border-color: var(--atomix-color-secondary-dark);
|
|
373
|
-
|
|
374
|
-
&:hover:not(:disabled) {
|
|
375
|
-
background-color: var(--atomix-color-secondary-dark);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.c-${name.toLowerCase()}--success {
|
|
380
|
-
background-color: var(--atomix-color-success);
|
|
381
|
-
color: var(--atomix-color-success-text);
|
|
382
|
-
border-color: var(--atomix-color-success-dark);
|
|
383
|
-
|
|
384
|
-
&:hover:not(:disabled) {
|
|
385
|
-
background-color: var(--atomix-color-success-dark);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.c-${name.toLowerCase()}--error {
|
|
390
|
-
background-color: var(--atomix-color-error);
|
|
391
|
-
color: var(--atomix-color-error-text);
|
|
392
|
-
border-color: var(--atomix-color-error-dark);
|
|
393
|
-
|
|
394
|
-
&:hover:not(:disabled) {
|
|
395
|
-
background-color: var(--atomix-color-error-dark);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// State Modifiers
|
|
400
|
-
// =============================================================================
|
|
401
|
-
.c-${name.toLowerCase()}--disabled {
|
|
402
|
-
@include disabled;
|
|
403
|
-
cursor: not-allowed;
|
|
404
|
-
|
|
405
|
-
&:hover {
|
|
406
|
-
transform: none;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Elements (if component has child elements)
|
|
411
|
-
// =============================================================================
|
|
412
|
-
.c-${name.toLowerCase()}__icon {
|
|
413
|
-
display: flex;
|
|
414
|
-
align-items: center;
|
|
415
|
-
justify-content: center;
|
|
416
|
-
flex-shrink: 0;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.c-${name.toLowerCase()}__content {
|
|
420
|
-
flex: 1;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Responsive Design
|
|
424
|
-
// =============================================================================
|
|
425
|
-
@include respond-to('tablet') {
|
|
426
|
-
.c-${name.toLowerCase()} {
|
|
427
|
-
// Tablet adjustments
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
@include respond-to('mobile') {
|
|
432
|
-
.c-${name.toLowerCase()} {
|
|
433
|
-
// Mobile adjustments
|
|
434
|
-
padding: var(--atomix-space-2) var(--atomix-space-3);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Accessibility
|
|
439
|
-
// =============================================================================
|
|
440
|
-
@media (prefers-reduced-motion: reduce) {
|
|
441
|
-
.c-${name.toLowerCase()} {
|
|
442
|
-
transition: none;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
@media (prefers-contrast: high) {
|
|
447
|
-
.c-${name.toLowerCase()} {
|
|
448
|
-
border-width: 2px;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Dark Mode Support
|
|
453
|
-
// =============================================================================
|
|
454
|
-
[data-theme="dark"] {
|
|
455
|
-
.c-${name.toLowerCase()} {
|
|
456
|
-
background-color: var(--atomix-color-background-dark);
|
|
457
|
-
color: var(--atomix-color-text-dark);
|
|
458
|
-
border-color: var(--atomix-color-border-dark);
|
|
459
|
-
}
|
|
460
|
-
}`,
|
|
461
|
-
|
|
462
|
-
scssModule: (name) => `// Component: ${name}
|
|
463
|
-
// =============================================================================
|
|
464
|
-
// Design system component using CSS Modules
|
|
465
|
-
|
|
466
|
-
@import '../../styles/01-settings';
|
|
467
|
-
@import '../../styles/02-tools';
|
|
468
|
-
|
|
469
|
-
// Block: Base component
|
|
470
|
-
// =============================================================================
|
|
471
|
-
.${name.toLowerCase()} {
|
|
472
|
-
// Layout
|
|
473
|
-
display: flex;
|
|
474
|
-
align-items: center;
|
|
475
|
-
justify-content: center;
|
|
476
|
-
|
|
477
|
-
// Spacing
|
|
478
|
-
padding: var(--atomix-space-3) var(--atomix-space-4);
|
|
479
|
-
gap: var(--atomix-space-2);
|
|
480
|
-
|
|
481
|
-
// Typography
|
|
482
|
-
font-family: var(--atomix-font-family-base);
|
|
483
|
-
font-size: var(--atomix-font-size-base);
|
|
484
|
-
font-weight: var(--atomix-font-weight-normal);
|
|
485
|
-
line-height: var(--atomix-line-height-base);
|
|
486
|
-
|
|
487
|
-
// Appearance
|
|
488
|
-
background-color: var(--atomix-color-background);
|
|
489
|
-
color: var(--atomix-color-text);
|
|
490
|
-
border: 1px solid var(--atomix-color-border);
|
|
491
|
-
border-radius: var(--atomix-radius-md);
|
|
492
|
-
|
|
493
|
-
// Interaction
|
|
494
|
-
cursor: default;
|
|
495
|
-
user-select: none;
|
|
496
|
-
transition: all 0.2s ease-in-out;
|
|
497
|
-
|
|
498
|
-
// Focus
|
|
499
|
-
&:focus-visible {
|
|
500
|
-
outline: 2px solid var(--atomix-color-focus);
|
|
501
|
-
outline-offset: 2px;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Size Modifiers
|
|
506
|
-
// =============================================================================
|
|
507
|
-
.${name.toLowerCase()}--sm {
|
|
508
|
-
composes: ${name.toLowerCase()};
|
|
509
|
-
padding: var(--atomix-space-2) var(--atomix-space-3);
|
|
510
|
-
font-size: var(--atomix-font-size-sm);
|
|
511
|
-
gap: var(--atomix-space-1);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
.${name.toLowerCase()}--md {
|
|
515
|
-
composes: ${name.toLowerCase()};
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
.${name.toLowerCase()}--lg {
|
|
519
|
-
composes: ${name.toLowerCase()};
|
|
520
|
-
padding: var(--atomix-space-4) var(--atomix-space-5);
|
|
521
|
-
font-size: var(--atomix-font-size-lg);
|
|
522
|
-
gap: var(--atomix-space-3);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Color/Variant Modifiers
|
|
526
|
-
// =============================================================================
|
|
527
|
-
.${name.toLowerCase()}--primary {
|
|
528
|
-
background-color: var(--atomix-color-primary);
|
|
529
|
-
color: var(--atomix-color-primary-text);
|
|
530
|
-
border-color: var(--atomix-color-primary-dark);
|
|
531
|
-
|
|
532
|
-
&:hover:not([aria-disabled="true"]) {
|
|
533
|
-
background-color: var(--atomix-color-primary-dark);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
112
|
|
|
537
|
-
|
|
538
|
-
background-color: var(--atomix-color-secondary);
|
|
539
|
-
color: var(--atomix-color-secondary-text);
|
|
540
|
-
border-color: var(--atomix-color-secondary-dark);
|
|
541
|
-
|
|
542
|
-
&:hover:not([aria-disabled="true"]) {
|
|
543
|
-
background-color: var(--atomix-color-secondary-dark);
|
|
544
|
-
}
|
|
113
|
+
process.exit(1);
|
|
545
114
|
}
|
|
546
115
|
|
|
547
|
-
.${name.toLowerCase()}--success {
|
|
548
|
-
background-color: var(--atomix-color-success);
|
|
549
|
-
color: var(--atomix-color-success-text);
|
|
550
|
-
border-color: var(--atomix-color-success-dark);
|
|
551
|
-
|
|
552
|
-
&:hover:not([aria-disabled="true"]) {
|
|
553
|
-
background-color: var(--atomix-color-success-dark);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
116
|
|
|
557
|
-
.${name.toLowerCase()}--error {
|
|
558
|
-
background-color: var(--atomix-color-error);
|
|
559
|
-
color: var(--atomix-color-error-text);
|
|
560
|
-
border-color: var(--atomix-color-error-dark);
|
|
561
|
-
|
|
562
|
-
&:hover:not([aria-disabled="true"]) {
|
|
563
|
-
background-color: var(--atomix-color-error-dark);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// State Modifiers
|
|
568
|
-
// =============================================================================
|
|
569
|
-
.${name.toLowerCase()}--disabled {
|
|
570
|
-
opacity: 0.5;
|
|
571
|
-
cursor: not-allowed;
|
|
572
|
-
pointer-events: none;
|
|
573
|
-
}`
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
117
|
|
|
577
118
|
// Initialize program
|
|
578
119
|
program
|
|
@@ -599,15 +140,40 @@ program
|
|
|
599
140
|
.option('--analyze', 'Analyze bundle size', false)
|
|
600
141
|
.action(async (themePath, options) => {
|
|
601
142
|
let spinner = ora('Initializing theme build...').start();
|
|
602
|
-
|
|
143
|
+
|
|
603
144
|
try {
|
|
145
|
+
const sanitizedThemePath = sanitizeInput(themePath);
|
|
146
|
+
const themePathValidation = validatePath(sanitizedThemePath);
|
|
147
|
+
if (!themePathValidation.isValid) {
|
|
148
|
+
throw new AtomixCLIError(
|
|
149
|
+
themePathValidation.error,
|
|
150
|
+
'INVALID_PATH',
|
|
151
|
+
[
|
|
152
|
+
'Ensure theme path is within the project directory',
|
|
153
|
+
'Avoid sensitive or absolute system paths',
|
|
154
|
+
'Example: atomix build-theme themes/my-theme'
|
|
155
|
+
]
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const sanitizedOutput = sanitizeInput(options.output);
|
|
159
|
+
const outputValidation = validatePath(sanitizedOutput);
|
|
160
|
+
if (!outputValidation.isValid) {
|
|
161
|
+
throw new AtomixCLIError(
|
|
162
|
+
outputValidation.error,
|
|
163
|
+
'INVALID_PATH',
|
|
164
|
+
[
|
|
165
|
+
'Use a project-relative directory for output',
|
|
166
|
+
'Example: --output ./dist'
|
|
167
|
+
]
|
|
168
|
+
);
|
|
169
|
+
}
|
|
604
170
|
// Resolve paths
|
|
605
|
-
const indexPath =
|
|
606
|
-
? resolve(
|
|
607
|
-
: resolve(
|
|
608
|
-
|
|
171
|
+
const indexPath = sanitizedThemePath.endsWith('.scss')
|
|
172
|
+
? resolve(themePathValidation.safePath)
|
|
173
|
+
: resolve(themePathValidation.safePath, 'index.scss');
|
|
174
|
+
|
|
609
175
|
debug(`Building theme from: ${indexPath}`);
|
|
610
|
-
|
|
176
|
+
|
|
611
177
|
// Check if path exists
|
|
612
178
|
try {
|
|
613
179
|
await access(indexPath);
|
|
@@ -622,16 +188,16 @@ program
|
|
|
622
188
|
]
|
|
623
189
|
);
|
|
624
190
|
}
|
|
625
|
-
|
|
191
|
+
|
|
626
192
|
// Build function
|
|
627
193
|
const buildTheme = async () => {
|
|
628
194
|
const startTime = Date.now();
|
|
629
|
-
|
|
195
|
+
|
|
630
196
|
try {
|
|
631
197
|
// Compile SCSS
|
|
632
198
|
spinner.text = 'Compiling SCSS...';
|
|
633
199
|
debug('Starting SCSS compilation');
|
|
634
|
-
|
|
200
|
+
|
|
635
201
|
const result = sass.compile(indexPath, {
|
|
636
202
|
loadPaths: [
|
|
637
203
|
process.cwd(),
|
|
@@ -643,7 +209,7 @@ program
|
|
|
643
209
|
sourceMap: options.sourcemap,
|
|
644
210
|
style: 'expanded',
|
|
645
211
|
});
|
|
646
|
-
|
|
212
|
+
|
|
647
213
|
// Process with PostCSS
|
|
648
214
|
spinner.text = 'Processing with PostCSS...';
|
|
649
215
|
const processed = await postcss([
|
|
@@ -654,23 +220,23 @@ program
|
|
|
654
220
|
from: indexPath,
|
|
655
221
|
map: options.sourcemap,
|
|
656
222
|
});
|
|
657
|
-
|
|
223
|
+
|
|
658
224
|
// Ensure output directory exists
|
|
659
|
-
await mkdir(
|
|
660
|
-
|
|
225
|
+
await mkdir(outputValidation.safePath, { recursive: true });
|
|
226
|
+
|
|
661
227
|
// Get theme name
|
|
662
228
|
const themeName = basename(dirname(indexPath));
|
|
663
|
-
|
|
229
|
+
|
|
664
230
|
// Write expanded CSS
|
|
665
|
-
const outputPath = join(
|
|
231
|
+
const outputPath = join(outputValidation.safePath, `${themeName}.css`);
|
|
666
232
|
await writeFile(outputPath, processed.css, 'utf8');
|
|
667
|
-
|
|
233
|
+
|
|
668
234
|
// Get file size
|
|
669
235
|
const stats = await stat(outputPath);
|
|
670
236
|
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
671
|
-
|
|
237
|
+
|
|
672
238
|
spinner.succeed(chalk.green(`✓ Built ${outputPath} (${sizeKB} KB)`));
|
|
673
|
-
|
|
239
|
+
|
|
674
240
|
// Write minified if requested
|
|
675
241
|
if (options.minify) {
|
|
676
242
|
const minifySpinner = ora('Minifying CSS...').start();
|
|
@@ -680,22 +246,22 @@ program
|
|
|
680
246
|
]).process(result.css, {
|
|
681
247
|
from: indexPath,
|
|
682
248
|
});
|
|
683
|
-
|
|
684
|
-
const minPath = join(
|
|
249
|
+
|
|
250
|
+
const minPath = join(outputValidation.safePath, `${themeName}.min.css`);
|
|
685
251
|
await writeFile(minPath, minified.css, 'utf8');
|
|
686
|
-
|
|
252
|
+
|
|
687
253
|
const minStats = await stat(minPath);
|
|
688
254
|
const minSizeKB = (minStats.size / 1024).toFixed(2);
|
|
689
|
-
|
|
255
|
+
|
|
690
256
|
minifySpinner.succeed(chalk.green(`✓ Built ${minPath} (${minSizeKB} KB)`));
|
|
691
257
|
}
|
|
692
|
-
|
|
258
|
+
|
|
693
259
|
// Analyze if requested
|
|
694
260
|
if (options.analyze) {
|
|
695
261
|
console.log(chalk.cyan('\n📊 Theme Analysis:'));
|
|
696
262
|
console.log(chalk.gray(` Original size: ${sizeKB} KB`));
|
|
697
263
|
if (options.minify) {
|
|
698
|
-
const minPath = join(
|
|
264
|
+
const minPath = join(outputValidation.safePath, `${themeName}.min.css`);
|
|
699
265
|
const minStats = await stat(minPath);
|
|
700
266
|
const minSizeKB = (minStats.size / 1024).toFixed(2);
|
|
701
267
|
const reduction = ((1 - minStats.size / stats.size) * 100).toFixed(1);
|
|
@@ -703,11 +269,11 @@ program
|
|
|
703
269
|
}
|
|
704
270
|
console.log(chalk.gray(` Build time: ${Date.now() - startTime}ms`));
|
|
705
271
|
}
|
|
706
|
-
|
|
272
|
+
|
|
707
273
|
if (!options.watch) {
|
|
708
274
|
console.log(chalk.bold.green('\n✨ Theme build complete!'));
|
|
709
275
|
}
|
|
710
|
-
|
|
276
|
+
|
|
711
277
|
} catch (error) {
|
|
712
278
|
if (options.watch) {
|
|
713
279
|
console.error(chalk.red(`Build error: ${error.message}`));
|
|
@@ -717,35 +283,35 @@ program
|
|
|
717
283
|
}
|
|
718
284
|
}
|
|
719
285
|
};
|
|
720
|
-
|
|
286
|
+
|
|
721
287
|
// Initial build
|
|
722
288
|
await buildTheme();
|
|
723
289
|
spinner.stop();
|
|
724
|
-
|
|
290
|
+
|
|
725
291
|
// Watch mode
|
|
726
292
|
if (options.watch) {
|
|
727
293
|
console.log(chalk.cyan('\n👁️ Watch mode enabled. Press Ctrl+C to exit.\n'));
|
|
728
|
-
|
|
729
|
-
const watcher = chokidar.watch([
|
|
294
|
+
|
|
295
|
+
const watcher = chokidar.watch([themePathValidation.safePath], {
|
|
730
296
|
ignored: /node_modules/,
|
|
731
297
|
persistent: true,
|
|
732
298
|
ignoreInitial: true
|
|
733
299
|
});
|
|
734
|
-
|
|
300
|
+
|
|
735
301
|
watcher.on('change', async (path) => {
|
|
736
302
|
console.log(chalk.gray(`\n[${new Date().toLocaleTimeString()}] File changed: ${relative(process.cwd(), path)}`));
|
|
737
303
|
spinner = ora('Rebuilding theme...').start();
|
|
738
304
|
await buildTheme();
|
|
739
305
|
spinner.stop();
|
|
740
306
|
});
|
|
741
|
-
|
|
307
|
+
|
|
742
308
|
watcher.on('add', async (path) => {
|
|
743
309
|
console.log(chalk.gray(`\n[${new Date().toLocaleTimeString()}] File added: ${relative(process.cwd(), path)}`));
|
|
744
310
|
spinner = ora('Rebuilding theme...').start();
|
|
745
311
|
await buildTheme();
|
|
746
312
|
spinner.stop();
|
|
747
313
|
});
|
|
748
|
-
|
|
314
|
+
|
|
749
315
|
// Handle graceful shutdown
|
|
750
316
|
process.on('SIGINT', () => {
|
|
751
317
|
console.log(chalk.yellow('\n\nShutting down watch mode...'));
|
|
@@ -753,7 +319,7 @@ program
|
|
|
753
319
|
process.exit(0);
|
|
754
320
|
});
|
|
755
321
|
}
|
|
756
|
-
|
|
322
|
+
|
|
757
323
|
} catch (error) {
|
|
758
324
|
handleError(error, spinner);
|
|
759
325
|
}
|
|
@@ -772,30 +338,43 @@ program
|
|
|
772
338
|
.option('--scss-module', 'Use SCSS modules', false)
|
|
773
339
|
.option('--path <path>', 'Custom output path', './src/components')
|
|
774
340
|
.option('-f, --force', 'Overwrite existing files', false)
|
|
341
|
+
.option('-i, --interactive', 'Interactive component generation', false)
|
|
342
|
+
.option('--complexity <level>', 'Component complexity level (simple|medium|complex)', 'medium')
|
|
343
|
+
.option('--validate', 'Validate component after generation', true)
|
|
775
344
|
.action(async (type, name, options) => {
|
|
776
345
|
const spinner = ora(`Generating ${type}: ${name}...`).start();
|
|
777
|
-
|
|
346
|
+
|
|
778
347
|
try {
|
|
779
348
|
debug(`Generating ${type} with name: ${name}`, options);
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const nameValidation = validateComponentName(name);
|
|
783
|
-
if (!nameValidation.isValid) {
|
|
784
|
-
throw new AtomixCLIError(
|
|
785
|
-
nameValidation.error,
|
|
786
|
-
'INVALID_NAME',
|
|
787
|
-
[
|
|
788
|
-
'Use PascalCase naming (e.g., MyComponent)',
|
|
789
|
-
'Start with an uppercase letter',
|
|
790
|
-
'Use only letters and numbers',
|
|
791
|
-
'Avoid reserved words'
|
|
792
|
-
]
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
|
|
349
|
+
const safeName = sanitizeInput(name);
|
|
350
|
+
|
|
796
351
|
if (type === 'component' || type === 'c') {
|
|
352
|
+
// Handle interactive generation
|
|
353
|
+
if (options.interactive) {
|
|
354
|
+
const interactiveResult = await interactiveComponentGeneration();
|
|
355
|
+
if (!interactiveResult) {
|
|
356
|
+
return; // User cancelled
|
|
357
|
+
}
|
|
358
|
+
// Update options with interactive results
|
|
359
|
+
Object.assign(options, interactiveResult.options);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const nameValidation = validateComponentName(safeName);
|
|
363
|
+
if (!nameValidation.isValid) {
|
|
364
|
+
throw new AtomixCLIError(
|
|
365
|
+
nameValidation.error,
|
|
366
|
+
'INVALID_NAME',
|
|
367
|
+
[
|
|
368
|
+
'Use PascalCase naming (e.g., MyComponent)',
|
|
369
|
+
'Start with an uppercase letter',
|
|
370
|
+
'Use only letters and numbers',
|
|
371
|
+
'Avoid reserved words'
|
|
372
|
+
]
|
|
373
|
+
);
|
|
374
|
+
}
|
|
797
375
|
// Validate output path
|
|
798
|
-
const
|
|
376
|
+
const sanitizedPath = sanitizeInput(options.path);
|
|
377
|
+
const pathValidation = validatePath(sanitizedPath);
|
|
799
378
|
if (!pathValidation.isValid) {
|
|
800
379
|
throw new AtomixCLIError(
|
|
801
380
|
pathValidation.error,
|
|
@@ -807,13 +386,13 @@ program
|
|
|
807
386
|
]
|
|
808
387
|
);
|
|
809
388
|
}
|
|
810
|
-
|
|
811
|
-
const componentPath = join(pathValidation.safePath,
|
|
812
|
-
|
|
389
|
+
|
|
390
|
+
const componentPath = join(pathValidation.safePath, safeName);
|
|
391
|
+
|
|
813
392
|
// Check if component already exists
|
|
814
393
|
if (existsSync(componentPath) && !options.force) {
|
|
815
394
|
throw new AtomixCLIError(
|
|
816
|
-
`Component ${
|
|
395
|
+
`Component ${safeName} already exists`,
|
|
817
396
|
'COMPONENT_EXISTS',
|
|
818
397
|
[
|
|
819
398
|
`Delete the existing component at ${componentPath}`,
|
|
@@ -822,77 +401,215 @@ program
|
|
|
822
401
|
]
|
|
823
402
|
);
|
|
824
403
|
}
|
|
825
|
-
|
|
404
|
+
|
|
826
405
|
// Create component directory
|
|
827
406
|
await mkdir(componentPath, { recursive: true });
|
|
828
|
-
|
|
407
|
+
|
|
408
|
+
// Generate composable hook
|
|
409
|
+
const hookPath = join(process.cwd(), 'src/lib/composables');
|
|
410
|
+
if (existsSync(hookPath)) {
|
|
411
|
+
const hookContent = componentTemplates.composable.hook(safeName);
|
|
412
|
+
const hookFilePath = join(hookPath, `use${safeName}.ts`);
|
|
413
|
+
|
|
414
|
+
if (!existsSync(hookFilePath) || options.force) {
|
|
415
|
+
await writeFile(hookFilePath, hookContent, 'utf8');
|
|
416
|
+
console.log(chalk.green(` ✓ Created use${safeName}.ts in src/lib/composables`));
|
|
417
|
+
} else {
|
|
418
|
+
console.log(chalk.yellow(` ⚠️ Hook file already exists: use${safeName}.ts`));
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
console.log(chalk.yellow(` ⚠️ Composables directory not found: ${hookPath}`));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Generate types in lib/types/components.ts
|
|
425
|
+
const typesPath = join(process.cwd(), 'src/lib/types');
|
|
426
|
+
if (existsSync(typesPath)) {
|
|
427
|
+
const componentsTypesPath = join(typesPath, 'components.ts');
|
|
428
|
+
if (existsSync(componentsTypesPath)) {
|
|
429
|
+
let typesContent = await readFile(componentsTypesPath, 'utf8');
|
|
430
|
+
const newTypeContent = componentTemplates.react.types(safeName);
|
|
431
|
+
|
|
432
|
+
// Check if type already exists
|
|
433
|
+
if (!typesContent.includes(`interface ${safeName}Props`)) {
|
|
434
|
+
// Insert before the last export
|
|
435
|
+
const lastExportIndex = typesContent.lastIndexOf('export');
|
|
436
|
+
const insertionPoint = typesContent.lastIndexOf('\n\n', lastExportIndex) + 2;
|
|
437
|
+
typesContent =
|
|
438
|
+
typesContent.slice(0, insertionPoint) +
|
|
439
|
+
'\n' + newTypeContent + '\n' +
|
|
440
|
+
typesContent.slice(insertionPoint);
|
|
441
|
+
|
|
442
|
+
await writeFile(componentsTypesPath, typesContent, 'utf8');
|
|
443
|
+
console.log(chalk.green(` ✓ Added ${safeName}Props to src/lib/types/components.ts`));
|
|
444
|
+
} else {
|
|
445
|
+
console.log(chalk.yellow(` ⚠️ ${safeName}Props already exists in types`));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Generate constants in lib/constants/components.ts
|
|
451
|
+
const constantsPath = join(process.cwd(), 'src/lib/constants');
|
|
452
|
+
if (existsSync(constantsPath)) {
|
|
453
|
+
const constantsFilePath = join(constantsPath, 'components.ts');
|
|
454
|
+
if (existsSync(constantsFilePath)) {
|
|
455
|
+
let constantsContent = await readFile(constantsFilePath, 'utf8');
|
|
456
|
+
const newConstantsContent = componentTemplates.react.constants(safeName);
|
|
457
|
+
|
|
458
|
+
// Check if constants already exist
|
|
459
|
+
if (!constantsContent.includes(`export const ${safeName.toUpperCase()}`)) {
|
|
460
|
+
// Insert before the last export
|
|
461
|
+
const lastExportIndex = constantsContent.lastIndexOf('export');
|
|
462
|
+
const insertionPoint = constantsContent.lastIndexOf('\n\n', lastExportIndex) + 2;
|
|
463
|
+
constantsContent =
|
|
464
|
+
constantsContent.slice(0, insertionPoint) +
|
|
465
|
+
'\n' + newConstantsContent + '\n' +
|
|
466
|
+
constantsContent.slice(insertionPoint);
|
|
467
|
+
|
|
468
|
+
await writeFile(constantsFilePath, constantsContent, 'utf8');
|
|
469
|
+
console.log(chalk.green(` ✓ Added ${safeName.toUpperCase()} constants to src/lib/constants/components.ts`));
|
|
470
|
+
} else {
|
|
471
|
+
console.log(chalk.yellow(` ⚠️ ${safeName.toUpperCase()} constants already exist`));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
829
476
|
// Generate component file
|
|
830
|
-
const componentContent = componentTemplates.react.component(
|
|
831
|
-
|
|
832
|
-
types: false // We'll generate inline types for now
|
|
833
|
-
});
|
|
834
|
-
|
|
477
|
+
const componentContent = componentTemplates.react.component(safeName);
|
|
478
|
+
|
|
835
479
|
await writeFile(
|
|
836
|
-
join(componentPath, `${
|
|
480
|
+
join(componentPath, `${safeName}.tsx`),
|
|
837
481
|
componentContent,
|
|
838
482
|
'utf8'
|
|
839
483
|
);
|
|
840
|
-
spinner.succeed(chalk.green(`✓ Created ${
|
|
841
|
-
|
|
484
|
+
spinner.succeed(chalk.green(`✓ Created ${safeName}.tsx`));
|
|
485
|
+
|
|
842
486
|
// Generate index file
|
|
843
|
-
const indexContent = componentTemplates.react.index(
|
|
487
|
+
const indexContent = componentTemplates.react.index(safeName);
|
|
844
488
|
await writeFile(
|
|
845
489
|
join(componentPath, 'index.ts'),
|
|
846
490
|
indexContent,
|
|
847
491
|
'utf8'
|
|
848
492
|
);
|
|
849
493
|
console.log(chalk.green(` ✓ Created index.ts`));
|
|
850
|
-
|
|
851
|
-
// Generate SCSS
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
494
|
+
|
|
495
|
+
// Generate SCSS files
|
|
496
|
+
if (!options.scssModule) {
|
|
497
|
+
// Generate settings file first
|
|
498
|
+
const settingsPath = join(process.cwd(), 'src/styles/01-settings');
|
|
499
|
+
if (existsSync(settingsPath)) {
|
|
500
|
+
const settingsContent = componentTemplates.react.scssSettings(safeName);
|
|
501
|
+
const settingsFilename = `_settings.${safeName.toLowerCase()}.scss`;
|
|
502
|
+
const settingsFilePath = join(settingsPath, settingsFilename);
|
|
503
|
+
|
|
504
|
+
if (!existsSync(settingsFilePath) || options.force) {
|
|
505
|
+
await writeFile(settingsFilePath, settingsContent, 'utf8');
|
|
506
|
+
console.log(chalk.green(` ✓ Created ${settingsFilename} in src/styles/01-settings`));
|
|
507
|
+
|
|
508
|
+
// Update _index.scss in settings
|
|
509
|
+
const settingsIndexPath = join(settingsPath, '_index.scss');
|
|
510
|
+
if (existsSync(settingsIndexPath)) {
|
|
511
|
+
let indexContent = await readFile(settingsIndexPath, 'utf8');
|
|
512
|
+
const forwardStatement = `@forward 'settings.${safeName.toLowerCase()}';`;
|
|
513
|
+
|
|
514
|
+
if (!indexContent.includes(forwardStatement)) {
|
|
515
|
+
if (!indexContent.endsWith('\n')) indexContent += '\n';
|
|
516
|
+
indexContent += `${forwardStatement}\n`;
|
|
517
|
+
await writeFile(settingsIndexPath, indexContent, 'utf8');
|
|
518
|
+
console.log(chalk.green(` ✓ Updated settings _index.scss`));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
console.log(chalk.yellow(` ⚠️ Settings file already exists: ${settingsFilename}`));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Generate component SCSS file
|
|
527
|
+
const scssContent = componentTemplates.react.scss(safeName);
|
|
528
|
+
const scssPath = join(process.cwd(), 'src/styles/06-components');
|
|
529
|
+
const scssFilename = `_components.${safeName.toLowerCase()}.scss`;
|
|
530
|
+
const scssFilePath = join(scssPath, scssFilename);
|
|
531
|
+
|
|
532
|
+
// Ensure styles directory exists
|
|
533
|
+
if (existsSync(scssPath)) {
|
|
534
|
+
// Check if SCSS file already exists
|
|
535
|
+
if (!existsSync(scssFilePath) || options.force) {
|
|
536
|
+
await writeFile(scssFilePath, scssContent, 'utf8');
|
|
537
|
+
console.log(chalk.green(` ✓ Created ${scssFilename} in src/styles/06-components`));
|
|
538
|
+
|
|
539
|
+
// Update _index.scss
|
|
540
|
+
const indexPath = join(scssPath, '_index.scss');
|
|
541
|
+
if (existsSync(indexPath)) {
|
|
542
|
+
let indexContent = await readFile(indexPath, 'utf8');
|
|
543
|
+
const forwardStatement = `@forward 'components.${safeName.toLowerCase()}';`;
|
|
544
|
+
|
|
545
|
+
if (!indexContent.includes(forwardStatement)) {
|
|
546
|
+
// Append to end of file, ensuring newline
|
|
547
|
+
if (!indexContent.endsWith('\n')) indexContent += '\n';
|
|
548
|
+
indexContent += `${forwardStatement}\n`;
|
|
549
|
+
await writeFile(indexPath, indexContent, 'utf8');
|
|
550
|
+
console.log(chalk.green(` ✓ Updated _index.scss`));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
console.log(chalk.yellow(` ⚠️ SCSS file already exists: ${scssFilename}`));
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
console.log(chalk.yellow(` ⚠️ Styles directory not found: ${scssPath}`));
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
// Fallback for modules if strictly requested (though we discourage it)
|
|
561
|
+
const scssContent = componentTemplates.react.scssModule(safeName);
|
|
562
|
+
await writeFile(
|
|
563
|
+
join(componentPath, `${safeName}.module.scss`),
|
|
564
|
+
scssContent,
|
|
565
|
+
'utf8'
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
864
569
|
// Generate Storybook story
|
|
865
570
|
if (options.story) {
|
|
866
|
-
const storyContent = componentTemplates.react.
|
|
571
|
+
const storyContent = componentTemplates.react.storyEnhanced(safeName);
|
|
867
572
|
await writeFile(
|
|
868
|
-
join(componentPath, `${
|
|
573
|
+
join(componentPath, `${safeName}.stories.tsx`),
|
|
869
574
|
storyContent,
|
|
870
575
|
'utf8'
|
|
871
576
|
);
|
|
872
|
-
console.log(chalk.green(` ✓ Created ${
|
|
577
|
+
console.log(chalk.green(` ✓ Created ${safeName}.stories.tsx`));
|
|
873
578
|
}
|
|
874
|
-
|
|
579
|
+
|
|
875
580
|
// Generate test file
|
|
876
581
|
if (options.test) {
|
|
877
|
-
const testContent = componentTemplates.react.test(
|
|
582
|
+
const testContent = componentTemplates.react.test(safeName);
|
|
878
583
|
await writeFile(
|
|
879
|
-
join(componentPath, `${
|
|
584
|
+
join(componentPath, `${safeName}.test.tsx`),
|
|
880
585
|
testContent,
|
|
881
586
|
'utf8'
|
|
882
587
|
);
|
|
883
|
-
console.log(chalk.green(` ✓ Created ${
|
|
588
|
+
console.log(chalk.green(` ✓ Created ${safeName}.test.tsx`));
|
|
884
589
|
}
|
|
885
|
-
|
|
590
|
+
|
|
591
|
+
// Post-generation validation if requested
|
|
592
|
+
if (options.validate) {
|
|
593
|
+
const validationResult = await validateGeneratedComponent(safeName, componentPath);
|
|
594
|
+
const isValid = displayValidationReport(validationResult.issues, validationResult.warnings, safeName);
|
|
595
|
+
|
|
596
|
+
if (!isValid) {
|
|
597
|
+
console.log(chalk.yellow('\n💡 Some issues were found. Consider addressing them for better component quality.'));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
886
601
|
// Success message with next steps
|
|
887
602
|
console.log(boxen(
|
|
888
|
-
chalk.bold.green(`🎉 Component ${
|
|
603
|
+
chalk.bold.green(`🎉 Component ${safeName} created successfully!\n\n`) +
|
|
889
604
|
chalk.cyan('Next steps:\n') +
|
|
890
605
|
chalk.gray(`1. Import in your app:\n`) +
|
|
891
|
-
chalk.white(` import { ${
|
|
606
|
+
chalk.white(` import { ${safeName} } from '${options.path}/${safeName}';\n\n`) +
|
|
892
607
|
chalk.gray(`2. Add to design system exports:\n`) +
|
|
893
|
-
chalk.white(` export { ${
|
|
608
|
+
chalk.white(` export { ${safeName} } from './${safeName}';\n\n`) +
|
|
894
609
|
chalk.gray(`3. Run Storybook to see your component:\n`) +
|
|
895
|
-
chalk.white(` npm run storybook`)
|
|
610
|
+
chalk.white(` npm run storybook\n\n`) +
|
|
611
|
+
chalk.gray(`4. Validate component quality:\n`) +
|
|
612
|
+
chalk.white(` atomix validate component ${safeName}`),
|
|
896
613
|
{
|
|
897
614
|
padding: 1,
|
|
898
615
|
margin: 1,
|
|
@@ -900,11 +617,62 @@ program
|
|
|
900
617
|
borderColor: 'green'
|
|
901
618
|
}
|
|
902
619
|
));
|
|
903
|
-
|
|
620
|
+
|
|
621
|
+
} else if (type === 'hook' || type === 'h') {
|
|
622
|
+
const componentPath = join(options.path, `use${safeName}`);
|
|
623
|
+
await mkdir(componentPath, { recursive: true });
|
|
624
|
+
|
|
625
|
+
const hookContent = componentTemplates.hook.hook(safeName);
|
|
626
|
+
await writeFile(join(componentPath, `use${safeName}.ts`), hookContent, 'utf8');
|
|
627
|
+
|
|
628
|
+
if (options.test) {
|
|
629
|
+
const testContent = componentTemplates.hook.test(safeName);
|
|
630
|
+
await writeFile(join(componentPath, `use${safeName}.test.ts`), testContent, 'utf8');
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
spinner.succeed(chalk.green(`✓ Created hook use${safeName}`));
|
|
634
|
+
|
|
635
|
+
} else if (type === 'layout' || type === 'l') {
|
|
636
|
+
const layoutPath = join(options.path, safeName);
|
|
637
|
+
await mkdir(layoutPath, { recursive: true });
|
|
638
|
+
|
|
639
|
+
const componentContent = componentTemplates.layout.component(safeName);
|
|
640
|
+
await writeFile(join(layoutPath, `${safeName}.tsx`), componentContent, 'utf8');
|
|
641
|
+
|
|
642
|
+
const scssContent = componentTemplates.layout.scss(safeName);
|
|
643
|
+
const scssFilename = `_layouts.${safeName.toLowerCase()}.scss`;
|
|
644
|
+
const scssPath = join(process.cwd(), 'src/styles/05-layouts');
|
|
645
|
+
|
|
646
|
+
if (existsSync(scssPath)) {
|
|
647
|
+
await writeFile(join(scssPath, scssFilename), scssContent, 'utf8');
|
|
648
|
+
|
|
649
|
+
// Update layouts index
|
|
650
|
+
const indexPath = join(scssPath, '_index.scss');
|
|
651
|
+
if (existsSync(indexPath)) {
|
|
652
|
+
let indexContent = await readFile(indexPath, 'utf8');
|
|
653
|
+
const forwardStatement = `@forward 'layouts.${safeName.toLowerCase()}';`;
|
|
654
|
+
if (!indexContent.includes(forwardStatement)) {
|
|
655
|
+
indexContent += `\n${forwardStatement}`;
|
|
656
|
+
await writeFile(indexPath, indexContent, 'utf8');
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
spinner.succeed(chalk.green(`✓ Created layout ${safeName}`));
|
|
662
|
+
|
|
663
|
+
} else if (type === 'context' || type === 'ctx') {
|
|
664
|
+
const contextPath = join(options.path, `${safeName}Context`);
|
|
665
|
+
await mkdir(contextPath, { recursive: true });
|
|
666
|
+
|
|
667
|
+
const contextContent = componentTemplates.context.context(safeName);
|
|
668
|
+
await writeFile(join(contextPath, `${safeName}Context.tsx`), contextContent, 'utf8');
|
|
669
|
+
|
|
670
|
+
spinner.succeed(chalk.green(`✓ Created context ${safeName}Context`));
|
|
671
|
+
|
|
904
672
|
} else if (type === 'token' || type === 't') {
|
|
905
673
|
// Token generation
|
|
906
674
|
const validCategories = ['colors', 'spacing', 'typography', 'shadows', 'radius', 'animations'];
|
|
907
|
-
|
|
675
|
+
|
|
908
676
|
if (!validCategories.includes(name.toLowerCase())) {
|
|
909
677
|
throw new AtomixCLIError(
|
|
910
678
|
`Invalid token category: ${name}`,
|
|
@@ -916,9 +684,9 @@ program
|
|
|
916
684
|
]
|
|
917
685
|
);
|
|
918
686
|
}
|
|
919
|
-
|
|
687
|
+
|
|
920
688
|
const tokenPath = join(process.cwd(), 'src/styles/01-settings');
|
|
921
|
-
|
|
689
|
+
|
|
922
690
|
// Check if settings directory exists
|
|
923
691
|
if (!existsSync(tokenPath)) {
|
|
924
692
|
throw new AtomixCLIError(
|
|
@@ -931,11 +699,11 @@ program
|
|
|
931
699
|
]
|
|
932
700
|
);
|
|
933
701
|
}
|
|
934
|
-
|
|
702
|
+
|
|
935
703
|
// Generate token file based on category
|
|
936
704
|
let tokenContent = '';
|
|
937
705
|
let filename = '';
|
|
938
|
-
|
|
706
|
+
|
|
939
707
|
switch (name.toLowerCase()) {
|
|
940
708
|
case 'colors':
|
|
941
709
|
filename = '_settings.colors.custom.scss';
|
|
@@ -962,9 +730,9 @@ program
|
|
|
962
730
|
tokenContent = generateAnimationTokens();
|
|
963
731
|
break;
|
|
964
732
|
}
|
|
965
|
-
|
|
733
|
+
|
|
966
734
|
const filePath = join(tokenPath, filename);
|
|
967
|
-
|
|
735
|
+
|
|
968
736
|
// Check if file already exists
|
|
969
737
|
if (existsSync(filePath) && !options.force) {
|
|
970
738
|
throw new AtomixCLIError(
|
|
@@ -977,11 +745,11 @@ program
|
|
|
977
745
|
]
|
|
978
746
|
);
|
|
979
747
|
}
|
|
980
|
-
|
|
748
|
+
|
|
981
749
|
// Write token file
|
|
982
750
|
await writeFile(filePath, tokenContent, 'utf8');
|
|
983
751
|
spinner.succeed(chalk.green(`✓ Created token file: ${filename}`));
|
|
984
|
-
|
|
752
|
+
|
|
985
753
|
// Success message
|
|
986
754
|
console.log(boxen(
|
|
987
755
|
chalk.bold.green(`🎨 ${name} tokens generated successfully!\n\n`) +
|
|
@@ -1010,7 +778,7 @@ program
|
|
|
1010
778
|
]
|
|
1011
779
|
);
|
|
1012
780
|
}
|
|
1013
|
-
|
|
781
|
+
|
|
1014
782
|
} catch (error) {
|
|
1015
783
|
handleError(error, spinner);
|
|
1016
784
|
}
|
|
@@ -1021,36 +789,37 @@ program
|
|
|
1021
789
|
*/
|
|
1022
790
|
program
|
|
1023
791
|
.command('validate [target]')
|
|
1024
|
-
.description('Validate themes, design tokens, or accessibility')
|
|
792
|
+
.description('Validate themes, design tokens, components, or accessibility')
|
|
1025
793
|
.option('--tokens', 'Validate design tokens', false)
|
|
1026
794
|
.option('--theme <path>', 'Validate specific theme', '')
|
|
795
|
+
.option('--component <name>', 'Validate specific component', '')
|
|
1027
796
|
.option('--a11y, --accessibility', 'Check accessibility compliance', false)
|
|
1028
797
|
.option('--fix', 'Attempt to fix issues automatically', false)
|
|
1029
798
|
.action(async (target, options) => {
|
|
1030
799
|
const spinner = ora('Running validation...').start();
|
|
1031
|
-
|
|
800
|
+
|
|
1032
801
|
try {
|
|
1033
802
|
debug('Validation options:', options);
|
|
1034
|
-
|
|
803
|
+
|
|
1035
804
|
const issues = [];
|
|
1036
805
|
const warnings = [];
|
|
1037
|
-
|
|
806
|
+
|
|
1038
807
|
// Token validation
|
|
1039
808
|
if (options.tokens || target === 'tokens') {
|
|
1040
809
|
spinner.text = 'Validating design tokens...';
|
|
1041
|
-
|
|
810
|
+
|
|
1042
811
|
const tokenFiles = [
|
|
1043
812
|
'src/styles/01-settings/_settings.colors.scss',
|
|
1044
813
|
'src/styles/01-settings/_settings.typography.scss',
|
|
1045
814
|
'src/styles/01-settings/_settings.spacing.scss',
|
|
1046
815
|
'src/styles/01-settings/_settings.radius.scss'
|
|
1047
816
|
];
|
|
1048
|
-
|
|
817
|
+
|
|
1049
818
|
for (const file of tokenFiles) {
|
|
1050
819
|
const filePath = join(process.cwd(), file);
|
|
1051
820
|
if (existsSync(filePath)) {
|
|
1052
821
|
const content = await readFile(filePath, 'utf8');
|
|
1053
|
-
|
|
822
|
+
|
|
1054
823
|
// Check for hardcoded values
|
|
1055
824
|
const hardcodedColors = content.match(/#[0-9a-fA-F]{3,8}(?![0-9a-fA-F])/g);
|
|
1056
825
|
if (hardcodedColors && hardcodedColors.length > 0) {
|
|
@@ -1060,7 +829,7 @@ program
|
|
|
1060
829
|
suggestion: 'Use CSS custom properties or SCSS variables'
|
|
1061
830
|
});
|
|
1062
831
|
}
|
|
1063
|
-
|
|
832
|
+
|
|
1064
833
|
// Check for missing default flags
|
|
1065
834
|
const variables = content.match(/\$[a-z-]+:/gi);
|
|
1066
835
|
const defaultFlags = content.match(/!default/g);
|
|
@@ -1080,11 +849,36 @@ program
|
|
|
1080
849
|
}
|
|
1081
850
|
}
|
|
1082
851
|
}
|
|
1083
|
-
|
|
852
|
+
|
|
853
|
+
// Component validation
|
|
854
|
+
if (options.component) {
|
|
855
|
+
spinner.text = `Validating component: ${options.component}...`;
|
|
856
|
+
|
|
857
|
+
const componentPath = join('./src/components', options.component);
|
|
858
|
+
const validationResult = await validateGeneratedComponent(options.component, componentPath);
|
|
859
|
+
|
|
860
|
+
// Convert validation results to issue/warning format
|
|
861
|
+
validationResult.issues.forEach(issue => {
|
|
862
|
+
issues.push({
|
|
863
|
+
file: options.component,
|
|
864
|
+
issue: issue,
|
|
865
|
+
suggestion: 'Check component structure and implementation'
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
validationResult.warnings.forEach(warning => {
|
|
870
|
+
warnings.push({
|
|
871
|
+
file: options.component,
|
|
872
|
+
issue: warning,
|
|
873
|
+
suggestion: 'Improve component quality by addressing warnings'
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
|
|
1084
878
|
// Theme validation
|
|
1085
879
|
if (options.theme) {
|
|
1086
880
|
spinner.text = `Validating theme: ${options.theme}...`;
|
|
1087
|
-
|
|
881
|
+
|
|
1088
882
|
const themePath = resolve(options.theme);
|
|
1089
883
|
if (!existsSync(themePath)) {
|
|
1090
884
|
issues.push({
|
|
@@ -1094,7 +888,7 @@ program
|
|
|
1094
888
|
});
|
|
1095
889
|
} else {
|
|
1096
890
|
const content = await readFile(themePath, 'utf8');
|
|
1097
|
-
|
|
891
|
+
|
|
1098
892
|
// Check for required imports
|
|
1099
893
|
const requiredImports = [
|
|
1100
894
|
'@import.*settings',
|
|
@@ -1102,7 +896,7 @@ program
|
|
|
1102
896
|
'@import.*tools',
|
|
1103
897
|
'@use.*tools'
|
|
1104
898
|
];
|
|
1105
|
-
|
|
899
|
+
|
|
1106
900
|
let hasSettings = false;
|
|
1107
901
|
for (const pattern of requiredImports) {
|
|
1108
902
|
if (new RegExp(pattern).test(content)) {
|
|
@@ -1110,7 +904,7 @@ program
|
|
|
1110
904
|
break;
|
|
1111
905
|
}
|
|
1112
906
|
}
|
|
1113
|
-
|
|
907
|
+
|
|
1114
908
|
if (!hasSettings) {
|
|
1115
909
|
issues.push({
|
|
1116
910
|
file: options.theme,
|
|
@@ -1120,16 +914,16 @@ program
|
|
|
1120
914
|
}
|
|
1121
915
|
}
|
|
1122
916
|
}
|
|
1123
|
-
|
|
917
|
+
|
|
1124
918
|
// Accessibility validation
|
|
1125
919
|
if (options.a11y || options.accessibility) {
|
|
1126
920
|
spinner.text = 'Checking accessibility compliance...';
|
|
1127
|
-
|
|
921
|
+
|
|
1128
922
|
// Check for focus styles
|
|
1129
923
|
const componentFiles = [
|
|
1130
924
|
'src/styles/06-components'
|
|
1131
925
|
];
|
|
1132
|
-
|
|
926
|
+
|
|
1133
927
|
for (const dir of componentFiles) {
|
|
1134
928
|
const dirPath = join(process.cwd(), dir);
|
|
1135
929
|
if (existsSync(dirPath)) {
|
|
@@ -1142,9 +936,9 @@ program
|
|
|
1142
936
|
}
|
|
1143
937
|
}
|
|
1144
938
|
}
|
|
1145
|
-
|
|
939
|
+
|
|
1146
940
|
spinner.stop();
|
|
1147
|
-
|
|
941
|
+
|
|
1148
942
|
// Display results
|
|
1149
943
|
if (issues.length === 0 && warnings.length === 0) {
|
|
1150
944
|
console.log(boxen(
|
|
@@ -1166,7 +960,7 @@ program
|
|
|
1166
960
|
console.log(chalk.yellow(` Fix: ${issue.suggestion}\n`));
|
|
1167
961
|
});
|
|
1168
962
|
}
|
|
1169
|
-
|
|
963
|
+
|
|
1170
964
|
if (warnings.length > 0) {
|
|
1171
965
|
console.log(chalk.bold.yellow(`\n⚠️ Found ${warnings.length} warning(s):\n`));
|
|
1172
966
|
warnings.forEach((warning, index) => {
|
|
@@ -1175,12 +969,27 @@ program
|
|
|
1175
969
|
console.log(chalk.cyan(` Suggestion: ${warning.suggestion}\n`));
|
|
1176
970
|
});
|
|
1177
971
|
}
|
|
1178
|
-
|
|
1179
|
-
if (options.fix && issues.length > 0) {
|
|
1180
|
-
console.log(chalk.cyan('\n🔧
|
|
972
|
+
|
|
973
|
+
if (options.fix && (issues.length > 0 || warnings.length > 0)) {
|
|
974
|
+
console.log(chalk.cyan('\n🔧 Attempting to fix issues...'));
|
|
975
|
+
|
|
976
|
+
let fixedCount = 0;
|
|
977
|
+
|
|
978
|
+
// Run token fixes
|
|
979
|
+
if (options.tokens || target === 'tokens' || !target) {
|
|
980
|
+
const tokenFixResult = await fixTokens(options);
|
|
981
|
+
fixedCount += tokenFixResult.totalFixed;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (fixedCount > 0) {
|
|
985
|
+
console.log(chalk.green(`\n✨ Fixed ${fixedCount} issue(s). Please run validate again to verify.`));
|
|
986
|
+
} else {
|
|
987
|
+
console.log(chalk.yellow('\nCould not automatically fix all reported issues. Manual intervention required.'));
|
|
988
|
+
}
|
|
989
|
+
} else {
|
|
990
|
+
console.log(chalk.yellow('\nCould not automatically fix reported issues. Manual intervention required.'));
|
|
1181
991
|
}
|
|
1182
992
|
}
|
|
1183
|
-
|
|
1184
993
|
} catch (error) {
|
|
1185
994
|
handleError(error, spinner);
|
|
1186
995
|
}
|
|
@@ -1206,7 +1015,7 @@ program
|
|
|
1206
1015
|
borderColor: 'cyan'
|
|
1207
1016
|
}
|
|
1208
1017
|
));
|
|
1209
|
-
|
|
1018
|
+
|
|
1210
1019
|
// Reuse build command with watch flag
|
|
1211
1020
|
await program.parseAsync([
|
|
1212
1021
|
...process.argv.slice(0, 2),
|
|
@@ -1231,10 +1040,10 @@ program
|
|
|
1231
1040
|
.option('--create-backup', 'Create backup before migration', true)
|
|
1232
1041
|
.action(async (from, options) => {
|
|
1233
1042
|
const spinner = ora('Preparing migration...').start();
|
|
1234
|
-
|
|
1043
|
+
|
|
1235
1044
|
try {
|
|
1236
1045
|
debug(`Migrating from ${from}`, options);
|
|
1237
|
-
|
|
1046
|
+
|
|
1238
1047
|
// Validate migration type
|
|
1239
1048
|
const validMigrations = ['tailwind', 'bootstrap', 'scss-variables'];
|
|
1240
1049
|
if (!validMigrations.includes(from.toLowerCase())) {
|
|
@@ -1248,9 +1057,21 @@ program
|
|
|
1248
1057
|
]
|
|
1249
1058
|
);
|
|
1250
1059
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
const
|
|
1060
|
+
|
|
1061
|
+
const sanitizedSource = sanitizeInput(options.source);
|
|
1062
|
+
const sourceValidation = validatePath(sanitizedSource);
|
|
1063
|
+
if (!sourceValidation.isValid) {
|
|
1064
|
+
throw new AtomixCLIError(
|
|
1065
|
+
sourceValidation.error,
|
|
1066
|
+
'INVALID_PATH',
|
|
1067
|
+
[
|
|
1068
|
+
'Ensure source path is within the project directory',
|
|
1069
|
+
'Avoid sensitive or absolute system paths',
|
|
1070
|
+
'Example: --source ./src'
|
|
1071
|
+
]
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
const sourcePath = resolve(sourceValidation.safePath);
|
|
1254
1075
|
if (!existsSync(sourcePath)) {
|
|
1255
1076
|
throw new AtomixCLIError(
|
|
1256
1077
|
`Source directory not found: ${sourcePath}`,
|
|
@@ -1262,9 +1083,9 @@ program
|
|
|
1262
1083
|
]
|
|
1263
1084
|
);
|
|
1264
1085
|
}
|
|
1265
|
-
|
|
1086
|
+
|
|
1266
1087
|
spinner.stop();
|
|
1267
|
-
|
|
1088
|
+
|
|
1268
1089
|
// Show migration preview
|
|
1269
1090
|
console.log(boxen(
|
|
1270
1091
|
chalk.bold.cyan(`🔄 Migration Preview\n\n`) +
|
|
@@ -1279,7 +1100,7 @@ program
|
|
|
1279
1100
|
borderColor: 'cyan'
|
|
1280
1101
|
}
|
|
1281
1102
|
));
|
|
1282
|
-
|
|
1103
|
+
|
|
1283
1104
|
// Confirm migration
|
|
1284
1105
|
if (!options.dryRun) {
|
|
1285
1106
|
const { confirmMigration } = await inquirer.prompt([
|
|
@@ -1290,18 +1111,18 @@ program
|
|
|
1290
1111
|
default: false
|
|
1291
1112
|
}
|
|
1292
1113
|
]);
|
|
1293
|
-
|
|
1114
|
+
|
|
1294
1115
|
if (!confirmMigration) {
|
|
1295
1116
|
console.log(chalk.yellow('\n Migration cancelled.'));
|
|
1296
1117
|
return;
|
|
1297
1118
|
}
|
|
1298
1119
|
}
|
|
1299
|
-
|
|
1120
|
+
|
|
1300
1121
|
// Create backup if requested
|
|
1301
1122
|
if (options.createBackup && !options.dryRun) {
|
|
1302
1123
|
const backupSpinner = ora('Creating backup...').start();
|
|
1303
1124
|
const backupDir = `${sourcePath}.backup.${Date.now()}`;
|
|
1304
|
-
|
|
1125
|
+
|
|
1305
1126
|
try {
|
|
1306
1127
|
const { execSync } = await import('child_process');
|
|
1307
1128
|
execSync(`cp -r "${sourcePath}" "${backupDir}"`, { stdio: 'ignore' });
|
|
@@ -1310,27 +1131,27 @@ program
|
|
|
1310
1131
|
backupSpinner.warn(chalk.yellow('Could not create backup, continuing anyway...'));
|
|
1311
1132
|
}
|
|
1312
1133
|
}
|
|
1313
|
-
|
|
1134
|
+
|
|
1314
1135
|
// Run migration
|
|
1315
1136
|
let report;
|
|
1316
|
-
|
|
1137
|
+
|
|
1317
1138
|
switch (from.toLowerCase()) {
|
|
1318
1139
|
case 'tailwind':
|
|
1319
1140
|
report = await migrateTailwind(sourcePath, options);
|
|
1320
1141
|
break;
|
|
1321
|
-
|
|
1142
|
+
|
|
1322
1143
|
case 'bootstrap':
|
|
1323
1144
|
report = await migrateBootstrap(sourcePath, options);
|
|
1324
1145
|
break;
|
|
1325
|
-
|
|
1146
|
+
|
|
1326
1147
|
case 'scss-variables':
|
|
1327
1148
|
report = await migrateSCSSVariables(sourcePath, options);
|
|
1328
1149
|
break;
|
|
1329
1150
|
}
|
|
1330
|
-
|
|
1151
|
+
|
|
1331
1152
|
// Display report
|
|
1332
1153
|
displayMigrationReport(report);
|
|
1333
|
-
|
|
1154
|
+
|
|
1334
1155
|
// Next steps
|
|
1335
1156
|
if (!options.dryRun && report.filesProcessed > 0) {
|
|
1336
1157
|
console.log(chalk.cyan('\n📝 Next Steps:'));
|
|
@@ -1340,7 +1161,7 @@ program
|
|
|
1340
1161
|
console.log(chalk.gray(' 4. Test your application thoroughly'));
|
|
1341
1162
|
console.log(chalk.gray(' 5. Customize with your theme: atomix create-theme custom'));
|
|
1342
1163
|
}
|
|
1343
|
-
|
|
1164
|
+
|
|
1344
1165
|
} catch (error) {
|
|
1345
1166
|
handleError(error, spinner);
|
|
1346
1167
|
}
|
|
@@ -1359,7 +1180,7 @@ program
|
|
|
1359
1180
|
if (options.skipInstall) {
|
|
1360
1181
|
process.env.ATOMIX_SKIP_INSTALL = 'true';
|
|
1361
1182
|
}
|
|
1362
|
-
|
|
1183
|
+
|
|
1363
1184
|
// Run the interactive wizard
|
|
1364
1185
|
await runInitWizard();
|
|
1365
1186
|
} catch (error) {
|
|
@@ -1380,21 +1201,22 @@ program
|
|
|
1380
1201
|
.action(async (action, options) => {
|
|
1381
1202
|
try {
|
|
1382
1203
|
debug(`Token action: ${action}`, options);
|
|
1383
|
-
|
|
1204
|
+
|
|
1384
1205
|
switch (action.toLowerCase()) {
|
|
1385
1206
|
case 'list':
|
|
1386
1207
|
case 'ls':
|
|
1387
1208
|
await listTokens(options.category);
|
|
1388
1209
|
break;
|
|
1389
|
-
|
|
1210
|
+
|
|
1390
1211
|
case 'validate':
|
|
1391
|
-
case 'check':
|
|
1212
|
+
case 'check': {
|
|
1392
1213
|
const validationResult = await validateTokens(options);
|
|
1393
1214
|
if (validationResult.issues.length > 0) {
|
|
1394
1215
|
process.exit(1); // Exit with error if issues found
|
|
1395
1216
|
}
|
|
1396
1217
|
break;
|
|
1397
|
-
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1398
1220
|
case 'export':
|
|
1399
1221
|
if (!options.format) {
|
|
1400
1222
|
throw new AtomixCLIError(
|
|
@@ -1407,9 +1229,24 @@ program
|
|
|
1407
1229
|
]
|
|
1408
1230
|
);
|
|
1409
1231
|
}
|
|
1410
|
-
|
|
1232
|
+
if (options.output) {
|
|
1233
|
+
const outValidation = validatePath(sanitizeInput(options.output));
|
|
1234
|
+
if (!outValidation.isValid) {
|
|
1235
|
+
throw new AtomixCLIError(
|
|
1236
|
+
outValidation.error,
|
|
1237
|
+
'INVALID_PATH',
|
|
1238
|
+
[
|
|
1239
|
+
'Use a project-relative output file path',
|
|
1240
|
+
'Example: --output ./tokens.json'
|
|
1241
|
+
]
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
await exportTokens(options.format, outValidation.safePath);
|
|
1245
|
+
} else {
|
|
1246
|
+
await exportTokens(options.format, options.output);
|
|
1247
|
+
}
|
|
1411
1248
|
break;
|
|
1412
|
-
|
|
1249
|
+
|
|
1413
1250
|
case 'import':
|
|
1414
1251
|
if (!options.output) {
|
|
1415
1252
|
throw new AtomixCLIError(
|
|
@@ -1421,9 +1258,22 @@ program
|
|
|
1421
1258
|
]
|
|
1422
1259
|
);
|
|
1423
1260
|
}
|
|
1424
|
-
|
|
1261
|
+
{
|
|
1262
|
+
const inValidation = validatePath(sanitizeInput(options.output));
|
|
1263
|
+
if (!inValidation.isValid) {
|
|
1264
|
+
throw new AtomixCLIError(
|
|
1265
|
+
inValidation.error,
|
|
1266
|
+
'INVALID_PATH',
|
|
1267
|
+
[
|
|
1268
|
+
'Use a project-relative input file path',
|
|
1269
|
+
'Example: --output ./tokens.json'
|
|
1270
|
+
]
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
await importTokens(inValidation.safePath, { dryRun: options.dryRun });
|
|
1274
|
+
}
|
|
1425
1275
|
break;
|
|
1426
|
-
|
|
1276
|
+
|
|
1427
1277
|
default:
|
|
1428
1278
|
throw new AtomixCLIError(
|
|
1429
1279
|
`Unknown token action: ${action}`,
|
|
@@ -1435,7 +1285,7 @@ program
|
|
|
1435
1285
|
]
|
|
1436
1286
|
);
|
|
1437
1287
|
}
|
|
1438
|
-
|
|
1288
|
+
|
|
1439
1289
|
} catch (error) {
|
|
1440
1290
|
handleError(error);
|
|
1441
1291
|
}
|
|
@@ -1526,12 +1376,13 @@ themeCommand
|
|
|
1526
1376
|
.option('--template <name>', 'Use template (dark|light|high-contrast)')
|
|
1527
1377
|
.option('--interactive', 'Interactive mode', false)
|
|
1528
1378
|
.option('-o, --output <path>', 'Output directory', './themes')
|
|
1379
|
+
.option('-f, --force', 'Overwrite existing theme', false)
|
|
1529
1380
|
.action(async (name, options) => {
|
|
1530
1381
|
const spinner = ora('Creating theme...').start();
|
|
1531
|
-
|
|
1382
|
+
|
|
1532
1383
|
try {
|
|
1533
1384
|
debug(`Creating theme: ${name}`, options);
|
|
1534
|
-
|
|
1385
|
+
|
|
1535
1386
|
// Validate name
|
|
1536
1387
|
const nameValidation = validateThemeName(name);
|
|
1537
1388
|
if (!nameValidation.isValid) {
|
|
@@ -1546,25 +1397,31 @@ themeCommand
|
|
|
1546
1397
|
]
|
|
1547
1398
|
);
|
|
1548
1399
|
}
|
|
1549
|
-
|
|
1400
|
+
|
|
1550
1401
|
const themePath = join(options.output, name);
|
|
1551
|
-
|
|
1402
|
+
|
|
1552
1403
|
// Check if theme already exists
|
|
1553
1404
|
if (existsSync(themePath)) {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1405
|
+
if (options.force) {
|
|
1406
|
+
await rm(themePath, { recursive: true, force: true });
|
|
1407
|
+
await mkdir(themePath, { recursive: true });
|
|
1408
|
+
spinner.info(chalk.yellow(`Overwriting existing theme: ${name}`));
|
|
1409
|
+
} else {
|
|
1410
|
+
throw new AtomixCLIError(
|
|
1411
|
+
`Theme ${name} already exists`,
|
|
1412
|
+
'THEME_EXISTS',
|
|
1413
|
+
[
|
|
1414
|
+
`Delete the existing theme at ${themePath}`,
|
|
1415
|
+
'Choose a different theme name',
|
|
1416
|
+
'Use --force flag to overwrite'
|
|
1417
|
+
]
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
} else {
|
|
1421
|
+
// Create theme directory
|
|
1422
|
+
await mkdir(themePath, { recursive: true });
|
|
1563
1423
|
}
|
|
1564
|
-
|
|
1565
|
-
// Create theme directory
|
|
1566
|
-
await mkdir(themePath, { recursive: true });
|
|
1567
|
-
|
|
1424
|
+
|
|
1568
1425
|
// Generate theme files based on type
|
|
1569
1426
|
if (options.type === 'css') {
|
|
1570
1427
|
// Create SCSS theme
|
|
@@ -1616,10 +1473,10 @@ themeCommand
|
|
|
1616
1473
|
}
|
|
1617
1474
|
}
|
|
1618
1475
|
`;
|
|
1619
|
-
|
|
1476
|
+
|
|
1620
1477
|
await writeFile(join(themePath, 'index.scss'), scssContent, 'utf8');
|
|
1621
1478
|
spinner.succeed(chalk.green(`✓ Created ${name}/index.scss`));
|
|
1622
|
-
|
|
1479
|
+
|
|
1623
1480
|
} else if (options.type === 'js') {
|
|
1624
1481
|
// Create JavaScript theme
|
|
1625
1482
|
const jsContent = `/**
|
|
@@ -1689,11 +1546,11 @@ export const ${name.replace(/-([a-z])/g, (_, c) => c.toUpperCase())}Theme = crea
|
|
|
1689
1546
|
|
|
1690
1547
|
export default ${name.replace(/-([a-z])/g, (_, c) => c.toUpperCase())}Theme;
|
|
1691
1548
|
`;
|
|
1692
|
-
|
|
1549
|
+
|
|
1693
1550
|
await writeFile(join(themePath, 'index.ts'), jsContent, 'utf8');
|
|
1694
1551
|
spinner.succeed(chalk.green(`✓ Created ${name}/index.ts`));
|
|
1695
1552
|
}
|
|
1696
|
-
|
|
1553
|
+
|
|
1697
1554
|
// Create README
|
|
1698
1555
|
const readmeContent = `# ${name} Theme
|
|
1699
1556
|
|
|
@@ -1734,17 +1591,17 @@ Edit the theme variables in \`index.${options.type === 'css' ? 'scss' : 'ts'}\`
|
|
|
1734
1591
|
atomix build-theme themes/${name}
|
|
1735
1592
|
\`\`\`
|
|
1736
1593
|
`;
|
|
1737
|
-
|
|
1594
|
+
|
|
1738
1595
|
await writeFile(join(themePath, 'README.md'), readmeContent, 'utf8');
|
|
1739
1596
|
console.log(chalk.green(` ✓ Created ${name}/README.md`));
|
|
1740
|
-
|
|
1597
|
+
|
|
1741
1598
|
// Success message
|
|
1742
1599
|
console.log(boxen(
|
|
1743
1600
|
chalk.bold.green(`🎨 Theme "${name}" created successfully!\n\n`) +
|
|
1744
1601
|
chalk.cyan('Next steps:\n') +
|
|
1745
1602
|
chalk.gray(`1. Customize your theme:\n`) +
|
|
1746
1603
|
chalk.white(` Edit ${themePath}/index.${options.type === 'css' ? 'scss' : 'ts'}\n\n`) +
|
|
1747
|
-
(options.type === 'css'
|
|
1604
|
+
(options.type === 'css'
|
|
1748
1605
|
? chalk.gray(`2. Build your theme:\n`) + chalk.white(` atomix build-theme ${themePath}\n\n`)
|
|
1749
1606
|
: chalk.gray(`2. Use in your app:\n`) + chalk.white(` import theme from './themes/${name}';\n\n`)
|
|
1750
1607
|
) +
|
|
@@ -1757,7 +1614,62 @@ atomix build-theme themes/${name}
|
|
|
1757
1614
|
borderColor: 'green'
|
|
1758
1615
|
}
|
|
1759
1616
|
));
|
|
1760
|
-
|
|
1617
|
+
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
handleError(error, spinner);
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Docs Command Group - NEW (Documentation Management)
|
|
1625
|
+
*/
|
|
1626
|
+
const docsCommand = program
|
|
1627
|
+
.command('docs')
|
|
1628
|
+
.description('Documentation management commands');
|
|
1629
|
+
|
|
1630
|
+
// Docs sync
|
|
1631
|
+
docsCommand
|
|
1632
|
+
.command('sync')
|
|
1633
|
+
.description('Sync documentation with component guidelines')
|
|
1634
|
+
.option('--validate', 'Validate documentation after sync', true)
|
|
1635
|
+
.action(async (options) => {
|
|
1636
|
+
const spinner = ora('Syncing documentation...').start();
|
|
1637
|
+
|
|
1638
|
+
try {
|
|
1639
|
+
await syncDocumentation();
|
|
1640
|
+
spinner.stop();
|
|
1641
|
+
|
|
1642
|
+
if (options.validate) {
|
|
1643
|
+
await validateDocumentation();
|
|
1644
|
+
}
|
|
1645
|
+
} catch (error) {
|
|
1646
|
+
handleError(error, spinner);
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
// Docs validate
|
|
1651
|
+
docsCommand
|
|
1652
|
+
.command('validate')
|
|
1653
|
+
.description('Validate documentation completeness')
|
|
1654
|
+
.action(async () => {
|
|
1655
|
+
try {
|
|
1656
|
+
await validateDocumentation();
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
handleError(error);
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
// Docs generate CLI
|
|
1663
|
+
docsCommand
|
|
1664
|
+
.command('generate-cli')
|
|
1665
|
+
.description('Generate CLI documentation from commands')
|
|
1666
|
+
.action(async () => {
|
|
1667
|
+
const spinner = ora('Generating CLI documentation...').start();
|
|
1668
|
+
|
|
1669
|
+
try {
|
|
1670
|
+
const result = await generateCLIDocumentation();
|
|
1671
|
+
spinner.succeed(chalk.green('CLI documentation generated'));
|
|
1672
|
+
console.log(chalk.gray(` → ${result.file}`));
|
|
1761
1673
|
} catch (error) {
|
|
1762
1674
|
handleError(error, spinner);
|
|
1763
1675
|
}
|
|
@@ -1771,21 +1683,20 @@ program
|
|
|
1771
1683
|
.description('Diagnose common issues with your Atomix setup')
|
|
1772
1684
|
.action(async () => {
|
|
1773
1685
|
const spinner = ora('Running diagnostics...').start();
|
|
1774
|
-
|
|
1686
|
+
|
|
1775
1687
|
try {
|
|
1776
1688
|
const checks = [];
|
|
1777
|
-
|
|
1689
|
+
|
|
1778
1690
|
// Check Node version
|
|
1779
|
-
const
|
|
1780
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
1691
|
+
const versionCheck = checkNodeVersion('18.0.0');
|
|
1781
1692
|
checks.push({
|
|
1782
1693
|
name: 'Node.js Version',
|
|
1783
|
-
status:
|
|
1784
|
-
message:
|
|
1785
|
-
?
|
|
1786
|
-
:
|
|
1694
|
+
status: versionCheck.compatible ? '✅' : '❌',
|
|
1695
|
+
message: versionCheck.compatible
|
|
1696
|
+
? `v${versionCheck.current} (supported)`
|
|
1697
|
+
: `v${versionCheck.current} (requires Node ${versionCheck.required}+)`,
|
|
1787
1698
|
});
|
|
1788
|
-
|
|
1699
|
+
|
|
1789
1700
|
// Check Atomix installation
|
|
1790
1701
|
const atomixPath = join(process.cwd(), 'node_modules', '@shohojdhara', 'atomix');
|
|
1791
1702
|
checks.push({
|
|
@@ -1795,7 +1706,7 @@ program
|
|
|
1795
1706
|
? 'Installed correctly'
|
|
1796
1707
|
: 'Not found - run: npm install @shohojdhara/atomix',
|
|
1797
1708
|
});
|
|
1798
|
-
|
|
1709
|
+
|
|
1799
1710
|
// Check for required dependencies
|
|
1800
1711
|
const requiredDeps = ['react', 'react-dom', 'sass'];
|
|
1801
1712
|
for (const dep of requiredDeps) {
|
|
@@ -1808,7 +1719,7 @@ program
|
|
|
1808
1719
|
: 'Missing (may be required for some features)',
|
|
1809
1720
|
});
|
|
1810
1721
|
}
|
|
1811
|
-
|
|
1722
|
+
|
|
1812
1723
|
// Check for configuration files
|
|
1813
1724
|
const configFiles = ['.atomixrc', 'atomix.config.js', 'atomix.config.json', 'theme.config.ts'];
|
|
1814
1725
|
let hasConfig = false;
|
|
@@ -1820,7 +1731,7 @@ program
|
|
|
1820
1731
|
break;
|
|
1821
1732
|
}
|
|
1822
1733
|
}
|
|
1823
|
-
|
|
1734
|
+
|
|
1824
1735
|
checks.push({
|
|
1825
1736
|
name: 'Configuration File',
|
|
1826
1737
|
status: hasConfig ? '✅' : '💡',
|
|
@@ -1828,12 +1739,12 @@ program
|
|
|
1828
1739
|
? `Configuration found (${configFile})`
|
|
1829
1740
|
: 'No config file (using defaults)',
|
|
1830
1741
|
});
|
|
1831
|
-
|
|
1742
|
+
|
|
1832
1743
|
// Check theme CLI availability
|
|
1833
1744
|
const themeCLIAvailable = await import('./cli/theme-bridge.js')
|
|
1834
1745
|
.then(m => m.isThemeCLIAvailable())
|
|
1835
1746
|
.catch(() => false);
|
|
1836
|
-
|
|
1747
|
+
|
|
1837
1748
|
checks.push({
|
|
1838
1749
|
name: 'Theme CLI',
|
|
1839
1750
|
status: themeCLIAvailable ? '✅' : '⚠️',
|
|
@@ -1841,21 +1752,21 @@ program
|
|
|
1841
1752
|
? 'Available'
|
|
1842
1753
|
: 'Theme devtools not found',
|
|
1843
1754
|
});
|
|
1844
|
-
|
|
1755
|
+
|
|
1845
1756
|
spinner.stop();
|
|
1846
|
-
|
|
1757
|
+
|
|
1847
1758
|
// Display results
|
|
1848
1759
|
console.log(chalk.bold('\n🏥 Atomix Doctor Report\n'));
|
|
1849
|
-
console.log(chalk.gray('='
|
|
1850
|
-
|
|
1760
|
+
console.log(chalk.gray('='.repeat(50)));
|
|
1761
|
+
|
|
1851
1762
|
checks.forEach(check => {
|
|
1852
1763
|
console.log(`${check.status} ${chalk.bold(check.name)}`);
|
|
1853
1764
|
console.log(` ${chalk.gray(check.message)}\n`);
|
|
1854
1765
|
});
|
|
1855
|
-
|
|
1766
|
+
|
|
1856
1767
|
const hasIssues = checks.some(c => c.status === '❌');
|
|
1857
1768
|
const hasWarnings = checks.some(c => c.status === '⚠️');
|
|
1858
|
-
|
|
1769
|
+
|
|
1859
1770
|
if (hasIssues) {
|
|
1860
1771
|
console.log(chalk.red('\n❌ Some issues need attention'));
|
|
1861
1772
|
} else if (hasWarnings) {
|
|
@@ -1863,425 +1774,13 @@ program
|
|
|
1863
1774
|
} else {
|
|
1864
1775
|
console.log(chalk.green('\n✅ Everything looks good!'));
|
|
1865
1776
|
}
|
|
1866
|
-
|
|
1777
|
+
|
|
1867
1778
|
} catch (error) {
|
|
1868
1779
|
handleError(error, spinner);
|
|
1869
1780
|
}
|
|
1870
1781
|
});
|
|
1871
1782
|
|
|
1872
|
-
// Token generation functions
|
|
1873
|
-
function generateColorTokens() {
|
|
1874
|
-
return `// Custom Color Tokens
|
|
1875
|
-
// Generated by Atomix CLI
|
|
1876
|
-
// =============================================================================
|
|
1877
|
-
|
|
1878
|
-
// Brand Colors
|
|
1879
|
-
// Customize these to match your brand identity
|
|
1880
|
-
$custom-primary-1: #fff9e6 !default;
|
|
1881
|
-
$custom-primary-2: #fff4cc !default;
|
|
1882
|
-
$custom-primary-3: #ffe699 !default;
|
|
1883
|
-
$custom-primary-4: #ffd966 !default;
|
|
1884
|
-
$custom-primary-5: #ffcc33 !default;
|
|
1885
|
-
$custom-primary-6: #ffb800 !default; // Main brand color
|
|
1886
|
-
$custom-primary-7: #e6a600 !default;
|
|
1887
|
-
$custom-primary-8: #cc9400 !default;
|
|
1888
|
-
$custom-primary-9: #b38200 !default;
|
|
1889
|
-
$custom-primary-10: #997000 !default;
|
|
1890
|
-
|
|
1891
|
-
// Semantic Colors
|
|
1892
|
-
$custom-success: #22c55e !default;
|
|
1893
|
-
$custom-warning: #eab308 !default;
|
|
1894
|
-
$custom-error: #ef4444 !default;
|
|
1895
|
-
$custom-info: #3b82f6 !default;
|
|
1896
|
-
|
|
1897
|
-
// Neutral Colors
|
|
1898
|
-
$custom-gray-1: #f9fafb !default;
|
|
1899
|
-
$custom-gray-2: #f3f4f6 !default;
|
|
1900
|
-
$custom-gray-3: #e5e7eb !default;
|
|
1901
|
-
$custom-gray-4: #d1d5db !default;
|
|
1902
|
-
$custom-gray-5: #9ca3af !default;
|
|
1903
|
-
$custom-gray-6: #6b7280 !default;
|
|
1904
|
-
$custom-gray-7: #4b5563 !default;
|
|
1905
|
-
$custom-gray-8: #374151 !default;
|
|
1906
|
-
$custom-gray-9: #1f2937 !default;
|
|
1907
|
-
$custom-gray-10: #111827 !default;
|
|
1908
|
-
|
|
1909
|
-
// Background Colors
|
|
1910
|
-
$custom-body-bg: #ffffff !default;
|
|
1911
|
-
$custom-body-bg-dark: #1f2937 !default;
|
|
1912
|
-
|
|
1913
|
-
// Text Colors
|
|
1914
|
-
$custom-body-color: $custom-gray-10 !default;
|
|
1915
|
-
$custom-body-color-dark: #ffffff !default;
|
|
1916
|
-
|
|
1917
|
-
// Link Colors
|
|
1918
|
-
$custom-link-color: $custom-primary-6 !default;
|
|
1919
|
-
$custom-link-hover-color: $custom-primary-7 !default;
|
|
1920
|
-
|
|
1921
|
-
// Border Colors
|
|
1922
|
-
$custom-border-color: $custom-gray-3 !default;
|
|
1923
|
-
$custom-border-color-dark: $custom-gray-7 !default;
|
|
1924
|
-
|
|
1925
|
-
// Focus Colors
|
|
1926
|
-
$custom-focus-color: $custom-primary-5 !default;
|
|
1927
|
-
$custom-focus-color-dark: $custom-primary-4 !default;
|
|
1928
|
-
|
|
1929
|
-
// Export custom colors to override defaults
|
|
1930
|
-
$primary: $custom-primary-6 !default;
|
|
1931
|
-
$success: $custom-success !default;
|
|
1932
|
-
$warning: $custom-warning !default;
|
|
1933
|
-
$error: $custom-error !default;
|
|
1934
|
-
$info: $custom-info !default;
|
|
1935
|
-
|
|
1936
|
-
// Dark mode overrides
|
|
1937
|
-
$body-bg-dark: $custom-body-bg-dark !default;
|
|
1938
|
-
$body-color-dark: $custom-body-color-dark !default;
|
|
1939
|
-
$border-color-dark: $custom-border-color-dark !default;
|
|
1940
|
-
`;
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
function generateSpacingTokens() {
|
|
1944
|
-
return `// Custom Spacing Tokens
|
|
1945
|
-
// Generated by Atomix CLI
|
|
1946
|
-
// =============================================================================
|
|
1947
|
-
|
|
1948
|
-
// Base spacing unit (change this to scale all spacing)
|
|
1949
|
-
$custom-spacing-base: 0.25rem !default; // 4px
|
|
1950
|
-
|
|
1951
|
-
// Spacing scale
|
|
1952
|
-
$custom-spacing-0: 0 !default;
|
|
1953
|
-
$custom-spacing-1: $custom-spacing-base !default; // 4px
|
|
1954
|
-
$custom-spacing-2: calc($custom-spacing-base * 2) !default; // 8px
|
|
1955
|
-
$custom-spacing-3: calc($custom-spacing-base * 3) !default; // 12px
|
|
1956
|
-
$custom-spacing-4: calc($custom-spacing-base * 4) !default; // 16px
|
|
1957
|
-
$custom-spacing-5: calc($custom-spacing-base * 5) !default; // 20px
|
|
1958
|
-
$custom-spacing-6: calc($custom-spacing-base * 6) !default; // 24px
|
|
1959
|
-
$custom-spacing-7: calc($custom-spacing-base * 7) !default; // 28px
|
|
1960
|
-
$custom-spacing-8: calc($custom-spacing-base * 8) !default; // 32px
|
|
1961
|
-
$custom-spacing-9: calc($custom-spacing-base * 9) !default; // 36px
|
|
1962
|
-
$custom-spacing-10: calc($custom-spacing-base * 10) !default; // 40px
|
|
1963
|
-
$custom-spacing-11: calc($custom-spacing-base * 11) !default; // 44px
|
|
1964
|
-
$custom-spacing-12: calc($custom-spacing-base * 12) !default; // 48px
|
|
1965
|
-
$custom-spacing-14: calc($custom-spacing-base * 14) !default; // 56px
|
|
1966
|
-
$custom-spacing-16: calc($custom-spacing-base * 16) !default; // 64px
|
|
1967
|
-
$custom-spacing-20: calc($custom-spacing-base * 20) !default; // 80px
|
|
1968
|
-
$custom-spacing-24: calc($custom-spacing-base * 24) !default; // 96px
|
|
1969
|
-
$custom-spacing-28: calc($custom-spacing-base * 28) !default; // 112px
|
|
1970
|
-
$custom-spacing-32: calc($custom-spacing-base * 32) !default; // 128px
|
|
1971
|
-
$custom-spacing-36: calc($custom-spacing-base * 36) !default; // 144px
|
|
1972
|
-
$custom-spacing-40: calc($custom-spacing-base * 40) !default; // 160px
|
|
1973
|
-
$custom-spacing-44: calc($custom-spacing-base * 44) !default; // 176px
|
|
1974
|
-
$custom-spacing-48: calc($custom-spacing-base * 48) !default; // 192px
|
|
1975
|
-
$custom-spacing-52: calc($custom-spacing-base * 52) !default; // 208px
|
|
1976
|
-
$custom-spacing-56: calc($custom-spacing-base * 56) !default; // 224px
|
|
1977
|
-
$custom-spacing-60: calc($custom-spacing-base * 60) !default; // 240px
|
|
1978
|
-
$custom-spacing-64: calc($custom-spacing-base * 64) !default; // 256px
|
|
1979
|
-
|
|
1980
|
-
// Component-specific spacing
|
|
1981
|
-
$custom-button-padding-x: $custom-spacing-4 !default;
|
|
1982
|
-
$custom-button-padding-y: $custom-spacing-2 !default;
|
|
1983
|
-
$custom-card-padding: $custom-spacing-6 !default;
|
|
1984
|
-
$custom-modal-padding: $custom-spacing-8 !default;
|
|
1985
|
-
|
|
1986
|
-
// Layout spacing
|
|
1987
|
-
$custom-container-padding: $custom-spacing-4 !default;
|
|
1988
|
-
$custom-grid-gap: $custom-spacing-6 !default;
|
|
1989
|
-
$custom-section-spacing: $custom-spacing-16 !default;
|
|
1990
|
-
|
|
1991
|
-
// Export to override defaults
|
|
1992
|
-
$spacing-sizes: (
|
|
1993
|
-
0: $custom-spacing-0,
|
|
1994
|
-
1: $custom-spacing-1,
|
|
1995
|
-
2: $custom-spacing-2,
|
|
1996
|
-
3: $custom-spacing-3,
|
|
1997
|
-
4: $custom-spacing-4,
|
|
1998
|
-
5: $custom-spacing-5,
|
|
1999
|
-
6: $custom-spacing-6,
|
|
2000
|
-
7: $custom-spacing-7,
|
|
2001
|
-
8: $custom-spacing-8,
|
|
2002
|
-
9: $custom-spacing-9,
|
|
2003
|
-
10: $custom-spacing-10,
|
|
2004
|
-
12: $custom-spacing-12,
|
|
2005
|
-
16: $custom-spacing-16,
|
|
2006
|
-
20: $custom-spacing-20,
|
|
2007
|
-
24: $custom-spacing-24,
|
|
2008
|
-
32: $custom-spacing-32,
|
|
2009
|
-
40: $custom-spacing-40,
|
|
2010
|
-
48: $custom-spacing-48,
|
|
2011
|
-
56: $custom-spacing-56,
|
|
2012
|
-
64: $custom-spacing-64,
|
|
2013
|
-
) !default;
|
|
2014
|
-
`;
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
function generateTypographyTokens() {
|
|
2018
|
-
return `// Custom Typography Tokens
|
|
2019
|
-
// Generated by Atomix CLI
|
|
2020
|
-
// =============================================================================
|
|
2021
|
-
|
|
2022
|
-
// Font Families
|
|
2023
|
-
$custom-font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default;
|
|
2024
|
-
$custom-font-family-serif: Georgia, "Times New Roman", Times, serif !default;
|
|
2025
|
-
$custom-font-family-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
|
|
2026
|
-
|
|
2027
|
-
// Font Size Scale
|
|
2028
|
-
$custom-font-size-xs: 0.75rem !default; // 12px
|
|
2029
|
-
$custom-font-size-sm: 0.875rem !default; // 14px
|
|
2030
|
-
$custom-font-size-base: 1rem !default; // 16px
|
|
2031
|
-
$custom-font-size-lg: 1.125rem !default; // 18px
|
|
2032
|
-
$custom-font-size-xl: 1.25rem !default; // 20px
|
|
2033
|
-
$custom-font-size-2xl: 1.5rem !default; // 24px
|
|
2034
|
-
$custom-font-size-3xl: 1.875rem !default; // 30px
|
|
2035
|
-
$custom-font-size-4xl: 2.25rem !default; // 36px
|
|
2036
|
-
$custom-font-size-5xl: 3rem !default; // 48px
|
|
2037
|
-
$custom-font-size-6xl: 3.75rem !default; // 60px
|
|
2038
|
-
$custom-font-size-7xl: 4.5rem !default; // 72px
|
|
2039
|
-
$custom-font-size-8xl: 6rem !default; // 96px
|
|
2040
|
-
|
|
2041
|
-
// Line Heights
|
|
2042
|
-
$custom-line-height-tight: 1.2 !default;
|
|
2043
|
-
$custom-line-height-base: 1.5 !default;
|
|
2044
|
-
$custom-line-height-relaxed: 1.75 !default;
|
|
2045
|
-
$custom-line-height-loose: 2 !default;
|
|
2046
|
-
|
|
2047
|
-
// Font Weights
|
|
2048
|
-
$custom-font-weight-light: 300 !default;
|
|
2049
|
-
$custom-font-weight-normal: 400 !default;
|
|
2050
|
-
$custom-font-weight-medium: 500 !default;
|
|
2051
|
-
$custom-font-weight-semibold: 600 !default;
|
|
2052
|
-
$custom-font-weight-bold: 700 !default;
|
|
2053
|
-
$custom-font-weight-heavy: 800 !default;
|
|
2054
|
-
$custom-font-weight-black: 900 !default;
|
|
2055
|
-
|
|
2056
|
-
// Letter Spacing
|
|
2057
|
-
$custom-letter-spacing-tight: -0.05em !default;
|
|
2058
|
-
$custom-letter-spacing-normal: 0 !default;
|
|
2059
|
-
$custom-letter-spacing-wide: 0.025em !default;
|
|
2060
|
-
$custom-letter-spacing-wider: 0.05em !default;
|
|
2061
|
-
$custom-letter-spacing-widest: 0.1em !default;
|
|
2062
|
-
|
|
2063
|
-
// Heading Sizes
|
|
2064
|
-
$custom-h1-font-size: $custom-font-size-5xl !default;
|
|
2065
|
-
$custom-h2-font-size: $custom-font-size-4xl !default;
|
|
2066
|
-
$custom-h3-font-size: $custom-font-size-3xl !default;
|
|
2067
|
-
$custom-h4-font-size: $custom-font-size-2xl !default;
|
|
2068
|
-
$custom-h5-font-size: $custom-font-size-xl !default;
|
|
2069
|
-
$custom-h6-font-size: $custom-font-size-lg !default;
|
|
2070
|
-
|
|
2071
|
-
// Export to override defaults
|
|
2072
|
-
$font-family-base: $custom-font-family-sans !default;
|
|
2073
|
-
$font-family-monospace: $custom-font-family-mono !default;
|
|
2074
|
-
$font-size-base: $custom-font-size-base !default;
|
|
2075
|
-
$font-size-sm: $custom-font-size-sm !default;
|
|
2076
|
-
$font-size-lg: $custom-font-size-lg !default;
|
|
2077
|
-
$line-height-base: $custom-line-height-base !default;
|
|
2078
|
-
$font-weight-base: $custom-font-weight-normal !default;
|
|
2079
|
-
|
|
2080
|
-
// Heading overrides
|
|
2081
|
-
$h1-font-size: $custom-h1-font-size !default;
|
|
2082
|
-
$h2-font-size: $custom-h2-font-size !default;
|
|
2083
|
-
$h3-font-size: $custom-h3-font-size !default;
|
|
2084
|
-
$h4-font-size: $custom-h4-font-size !default;
|
|
2085
|
-
$h5-font-size: $custom-h5-font-size !default;
|
|
2086
|
-
$h6-font-size: $custom-h6-font-size !default;
|
|
2087
|
-
`;
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
function generateShadowTokens() {
|
|
2091
|
-
return `// Custom Box Shadow Tokens
|
|
2092
|
-
// Generated by Atomix CLI
|
|
2093
|
-
// =============================================================================
|
|
2094
|
-
|
|
2095
|
-
// Shadow Colors
|
|
2096
|
-
$custom-shadow-color: rgba(0, 0, 0, 0.1) !default;
|
|
2097
|
-
$custom-shadow-color-dark: rgba(0, 0, 0, 0.2) !default;
|
|
2098
|
-
|
|
2099
|
-
// Shadow Sizes
|
|
2100
|
-
$custom-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !default;
|
|
2101
|
-
$custom-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06) !default;
|
|
2102
|
-
$custom-shadow-base: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !default;
|
|
2103
|
-
$custom-shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) !default;
|
|
2104
|
-
$custom-shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) !default;
|
|
2105
|
-
$custom-shadow-xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !default;
|
|
2106
|
-
$custom-shadow-2xl: 0 35px 60px -15px rgba(0, 0, 0, 0.3) !default;
|
|
2107
|
-
$custom-shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06) !default;
|
|
2108
|
-
$custom-shadow-none: none !default;
|
|
2109
|
-
|
|
2110
|
-
// Component-specific shadows
|
|
2111
|
-
$custom-button-shadow: $custom-shadow-sm !default;
|
|
2112
|
-
$custom-button-shadow-hover: $custom-shadow-md !default;
|
|
2113
|
-
$custom-card-shadow: $custom-shadow-base !default;
|
|
2114
|
-
$custom-dropdown-shadow: $custom-shadow-lg !default;
|
|
2115
|
-
$custom-modal-shadow: $custom-shadow-xl !default;
|
|
2116
|
-
$custom-popover-shadow: $custom-shadow-lg !default;
|
|
2117
|
-
$custom-tooltip-shadow: $custom-shadow-md !default;
|
|
2118
|
-
|
|
2119
|
-
// Dark mode shadows
|
|
2120
|
-
$custom-shadow-xs-dark: 0 1px 2px 0 rgba(0, 0, 0, 0.3) !default;
|
|
2121
|
-
$custom-shadow-sm-dark: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.3) !default;
|
|
2122
|
-
$custom-shadow-base-dark: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3) !default;
|
|
2123
|
-
$custom-shadow-lg-dark: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.4) !default;
|
|
2124
|
-
$custom-shadow-xl-dark: 0 25px 50px -12px rgba(0, 0, 0, 0.6) !default;
|
|
2125
|
-
|
|
2126
|
-
// Export to override defaults
|
|
2127
|
-
$box-shadow: $custom-shadow-base !default;
|
|
2128
|
-
$box-shadow-xs: $custom-shadow-xs !default;
|
|
2129
|
-
$box-shadow-sm: $custom-shadow-sm !default;
|
|
2130
|
-
$box-shadow-lg: $custom-shadow-lg !default;
|
|
2131
|
-
$box-shadow-xl: $custom-shadow-xl !default;
|
|
2132
|
-
$box-shadow-inset: $custom-shadow-inner !default;
|
|
2133
|
-
|
|
2134
|
-
// Dark mode exports
|
|
2135
|
-
$box-shadow-dark: $custom-shadow-base-dark !default;
|
|
2136
|
-
$box-shadow-xs-dark: $custom-shadow-xs-dark !default;
|
|
2137
|
-
$box-shadow-sm-dark: $custom-shadow-sm-dark !default;
|
|
2138
|
-
$box-shadow-lg-dark: $custom-shadow-lg-dark !default;
|
|
2139
|
-
$box-shadow-xl-dark: $custom-shadow-xl-dark !default;
|
|
2140
|
-
`;
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
function generateRadiusTokens() {
|
|
2144
|
-
return `// Custom Border Radius Tokens
|
|
2145
|
-
// Generated by Atomix CLI
|
|
2146
|
-
// =============================================================================
|
|
2147
|
-
|
|
2148
|
-
// Base radius unit
|
|
2149
|
-
$custom-radius-base: 0.25rem !default; // 4px
|
|
2150
|
-
|
|
2151
|
-
// Radius Scale
|
|
2152
|
-
$custom-radius-none: 0 !default;
|
|
2153
|
-
$custom-radius-sm: calc($custom-radius-base * 0.5) !default; // 2px
|
|
2154
|
-
$custom-radius-base: $custom-radius-base !default; // 4px
|
|
2155
|
-
$custom-radius-md: calc($custom-radius-base * 1.5) !default; // 6px
|
|
2156
|
-
$custom-radius-lg: calc($custom-radius-base * 2) !default; // 8px
|
|
2157
|
-
$custom-radius-xl: calc($custom-radius-base * 3) !default; // 12px
|
|
2158
|
-
$custom-radius-2xl: calc($custom-radius-base * 4) !default; // 16px
|
|
2159
|
-
$custom-radius-3xl: calc($custom-radius-base * 6) !default; // 24px
|
|
2160
|
-
$custom-radius-4xl: calc($custom-radius-base * 8) !default; // 32px
|
|
2161
|
-
$custom-radius-full: 9999px !default; // Fully rounded
|
|
2162
|
-
|
|
2163
|
-
// Component-specific radius
|
|
2164
|
-
$custom-button-radius: $custom-radius-md !default;
|
|
2165
|
-
$custom-button-radius-sm: $custom-radius-sm !default;
|
|
2166
|
-
$custom-button-radius-lg: $custom-radius-lg !default;
|
|
2167
|
-
$custom-card-radius: $custom-radius-lg !default;
|
|
2168
|
-
$custom-input-radius: $custom-radius-md !default;
|
|
2169
|
-
$custom-badge-radius: $custom-radius-full !default;
|
|
2170
|
-
$custom-chip-radius: $custom-radius-full !default;
|
|
2171
|
-
$custom-tooltip-radius: $custom-radius-md !default;
|
|
2172
|
-
$custom-modal-radius: $custom-radius-xl !default;
|
|
2173
|
-
$custom-dropdown-radius: $custom-radius-lg !default;
|
|
2174
|
-
|
|
2175
|
-
// Export to override defaults
|
|
2176
|
-
$border-radius: $custom-radius-md !default;
|
|
2177
|
-
$border-radius-sm: $custom-radius-sm !default;
|
|
2178
|
-
$border-radius-lg: $custom-radius-lg !default;
|
|
2179
|
-
$border-radius-xl: $custom-radius-xl !default;
|
|
2180
|
-
$border-radius-xxl: $custom-radius-2xl !default;
|
|
2181
|
-
$border-radius-3xl: $custom-radius-3xl !default;
|
|
2182
|
-
$border-radius-4xl: $custom-radius-4xl !default;
|
|
2183
|
-
$border-radius-pill: $custom-radius-full !default;
|
|
2184
|
-
|
|
2185
|
-
// Component radius exports
|
|
2186
|
-
$btn-border-radius: $custom-button-radius !default;
|
|
2187
|
-
$btn-border-radius-sm: $custom-button-radius-sm !default;
|
|
2188
|
-
$btn-border-radius-lg: $custom-button-radius-lg !default;
|
|
2189
|
-
$card-border-radius: $custom-card-radius !default;
|
|
2190
|
-
$input-border-radius: $custom-input-radius !default;
|
|
2191
|
-
$badge-border-radius: $custom-badge-radius !default;
|
|
2192
|
-
`;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
function generateAnimationTokens() {
|
|
2196
|
-
return `// Custom Animation Tokens
|
|
2197
|
-
// Generated by Atomix CLI
|
|
2198
|
-
// =============================================================================
|
|
2199
|
-
|
|
2200
|
-
// Transition Durations
|
|
2201
|
-
$custom-duration-instant: 0s !default;
|
|
2202
|
-
$custom-duration-fast: 0.15s !default;
|
|
2203
|
-
$custom-duration-base: 0.3s !default;
|
|
2204
|
-
$custom-duration-slow: 0.5s !default;
|
|
2205
|
-
$custom-duration-slower: 0.7s !default;
|
|
2206
|
-
$custom-duration-slowest: 1s !default;
|
|
2207
|
-
|
|
2208
|
-
// Easing Functions
|
|
2209
|
-
$custom-ease-linear: linear !default;
|
|
2210
|
-
$custom-ease-in: cubic-bezier(0.4, 0, 1, 1) !default;
|
|
2211
|
-
$custom-ease-out: cubic-bezier(0, 0, 0.2, 1) !default;
|
|
2212
|
-
$custom-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1) !default;
|
|
2213
|
-
$custom-ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55) !default;
|
|
2214
|
-
$custom-ease-smooth: cubic-bezier(0.23, 1, 0.32, 1) !default;
|
|
2215
|
-
|
|
2216
|
-
// Transition Properties
|
|
2217
|
-
$custom-transition-all: all $custom-duration-base $custom-ease-smooth !default;
|
|
2218
|
-
$custom-transition-colors: background-color $custom-duration-base $custom-ease-smooth,
|
|
2219
|
-
border-color $custom-duration-base $custom-ease-smooth,
|
|
2220
|
-
color $custom-duration-base $custom-ease-smooth,
|
|
2221
|
-
fill $custom-duration-base $custom-ease-smooth,
|
|
2222
|
-
stroke $custom-duration-base $custom-ease-smooth !default;
|
|
2223
|
-
$custom-transition-opacity: opacity $custom-duration-base $custom-ease-smooth !default;
|
|
2224
|
-
$custom-transition-shadow: box-shadow $custom-duration-base $custom-ease-smooth !default;
|
|
2225
|
-
$custom-transition-transform: transform $custom-duration-base $custom-ease-smooth !default;
|
|
2226
|
-
|
|
2227
|
-
// Component-specific transitions
|
|
2228
|
-
$custom-button-transition: $custom-transition-colors, $custom-transition-shadow, $custom-transition-transform !default;
|
|
2229
|
-
$custom-link-transition: $custom-transition-colors, text-decoration-color $custom-duration-base $custom-ease-smooth !default;
|
|
2230
|
-
$custom-input-transition: $custom-transition-colors, $custom-transition-shadow !default;
|
|
2231
|
-
$custom-card-transition: $custom-transition-shadow, $custom-transition-transform !default;
|
|
2232
|
-
$custom-modal-transition: $custom-transition-opacity, $custom-transition-transform !default;
|
|
2233
|
-
$custom-dropdown-transition: $custom-transition-opacity, $custom-transition-transform !default;
|
|
2234
|
-
|
|
2235
|
-
// Animation Keyframes (examples)
|
|
2236
|
-
@keyframes custom-fade-in {
|
|
2237
|
-
from { opacity: 0; }
|
|
2238
|
-
to { opacity: 1; }
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
@keyframes custom-slide-in-up {
|
|
2242
|
-
from {
|
|
2243
|
-
transform: translateY(10px);
|
|
2244
|
-
opacity: 0;
|
|
2245
|
-
}
|
|
2246
|
-
to {
|
|
2247
|
-
transform: translateY(0);
|
|
2248
|
-
opacity: 1;
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
@keyframes custom-scale-in {
|
|
2253
|
-
from {
|
|
2254
|
-
transform: scale(0.95);
|
|
2255
|
-
opacity: 0;
|
|
2256
|
-
}
|
|
2257
|
-
to {
|
|
2258
|
-
transform: scale(1);
|
|
2259
|
-
opacity: 1;
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
@keyframes custom-spin {
|
|
2264
|
-
from { transform: rotate(0deg); }
|
|
2265
|
-
to { transform: rotate(360deg); }
|
|
2266
|
-
}
|
|
2267
1783
|
|
|
2268
|
-
// Export to override defaults
|
|
2269
|
-
$transition-fast: $custom-transition-all !default;
|
|
2270
|
-
$transition-base: $custom-transition-all !default;
|
|
2271
|
-
$transition-slow: all $custom-duration-slow $custom-ease-smooth !default;
|
|
2272
|
-
|
|
2273
|
-
// Duration exports
|
|
2274
|
-
$transition-duration-fast: $custom-duration-fast !default;
|
|
2275
|
-
$transition-duration-base: $custom-duration-base !default;
|
|
2276
|
-
$transition-duration-slow: $custom-duration-slow !default;
|
|
2277
|
-
|
|
2278
|
-
// Easing exports
|
|
2279
|
-
$easing-base: $custom-ease-smooth !default;
|
|
2280
|
-
$easing-ease-in-out: $custom-ease-in-out !default;
|
|
2281
|
-
$easing-ease-out: $custom-ease-out !default;
|
|
2282
|
-
$easing-ease-in: $custom-ease-in !default;
|
|
2283
|
-
`;
|
|
2284
|
-
}
|
|
2285
1784
|
|
|
2286
1785
|
// Parse arguments
|
|
2287
1786
|
program.parse(process.argv);
|