@shohojdhara/atomix 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/atomix.config.ts +58 -1
- package/dist/atomix.css +172 -157
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +4 -4
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1274 -164
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1099 -83
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2106 -1050
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1663 -638
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +442 -270
- package/dist/index.esm.js +1947 -680
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1982 -712
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +6 -3
- package/scripts/atomix-cli.js +136 -1827
- package/scripts/cli/__tests__/basic.test.js +3 -2
- package/scripts/cli/__tests__/clean.test.js +278 -0
- package/scripts/cli/__tests__/component-validator.test.js +433 -0
- package/scripts/cli/__tests__/generator.test.js +613 -0
- package/scripts/cli/__tests__/glass-motion.test.js +256 -0
- package/scripts/cli/__tests__/integration.test.js +719 -108
- package/scripts/cli/__tests__/migrate.test.js +74 -0
- package/scripts/cli/__tests__/security.test.js +206 -0
- package/scripts/cli/__tests__/test-setup.js +3 -1
- package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
- package/scripts/cli/__tests__/token-provider.test.js +361 -0
- package/scripts/cli/__tests__/utils.test.js +5 -5
- package/scripts/cli/commands/benchmark.js +105 -0
- package/scripts/cli/commands/build-theme.js +115 -0
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +218 -0
- package/scripts/cli/commands/init.js +73 -0
- package/scripts/cli/commands/migrate.js +106 -0
- package/scripts/cli/commands/sync-tokens.js +206 -0
- package/scripts/cli/commands/theme-bridge.js +248 -0
- package/scripts/cli/commands/tokens.js +157 -0
- package/scripts/cli/commands/validate.js +194 -0
- package/scripts/cli/internal/ai-engine.js +156 -0
- package/scripts/cli/internal/compiler.js +114 -0
- package/scripts/cli/internal/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +158 -0
- package/scripts/cli/internal/generator.js +430 -0
- package/scripts/cli/internal/glass-generator.js +398 -0
- package/scripts/cli/internal/hook-generator.js +369 -0
- package/scripts/cli/internal/hooks.js +61 -0
- package/scripts/cli/internal/itcss-generator.js +565 -0
- package/scripts/cli/internal/motion-generator.js +679 -0
- package/scripts/cli/internal/template-engine.js +301 -0
- package/scripts/cli/internal/theme-bridge.js +664 -0
- package/scripts/cli/internal/tokens/engine.js +122 -0
- package/scripts/cli/internal/tokens/provider.js +34 -0
- package/scripts/cli/internal/tokens/providers/figma.js +50 -0
- package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
- package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
- package/scripts/cli/internal/tokens/token-provider.js +443 -0
- package/scripts/cli/internal/tokens/token-validator.js +513 -0
- package/scripts/cli/internal/validator.js +276 -0
- package/scripts/cli/internal/wizard.js +115 -0
- package/scripts/cli/mappings.js +23 -0
- package/scripts/cli/migration-tools.js +164 -94
- package/scripts/cli/plugins/style-dictionary.js +46 -0
- package/scripts/cli/templates/README.md +525 -95
- package/scripts/cli/templates/common-templates.js +40 -14
- package/scripts/cli/templates/components/react-component.ts +282 -0
- package/scripts/cli/templates/config/project-config.ts +112 -0
- package/scripts/cli/templates/hooks/use-component.ts +477 -0
- package/scripts/cli/templates/index.js +19 -4
- package/scripts/cli/templates/index.ts +171 -0
- package/scripts/cli/templates/next-templates.js +72 -0
- package/scripts/cli/templates/react-templates.js +70 -126
- package/scripts/cli/templates/scss-templates.js +35 -35
- package/scripts/cli/templates/stories/storybook-story.ts +241 -0
- package/scripts/cli/templates/styles/scss-component.ts +255 -0
- package/scripts/cli/templates/tests/vitest-test.ts +229 -0
- package/scripts/cli/templates/token-templates.js +337 -1
- package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
- package/scripts/cli/templates/types/component-types.ts +145 -0
- package/scripts/cli/templates/utils/testing-utils.ts +144 -0
- package/scripts/cli/templates/vanilla-templates.js +39 -0
- package/scripts/cli/token-manager.js +8 -2
- package/scripts/cli/utils/cache-manager.js +240 -0
- package/scripts/cli/utils/detector.js +46 -0
- package/scripts/cli/utils/diagnostics.js +289 -0
- package/scripts/cli/utils/error.js +89 -0
- package/scripts/cli/utils/helpers.js +67 -0
- package/scripts/cli/utils/logger.js +75 -0
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +37 -0
- package/scripts/cli/utils.js +28 -341
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.test.tsx +0 -17
- package/src/components/Accordion/Accordion.tsx +0 -4
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
- package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
- package/src/components/AtomixGlass/animation-system.ts +578 -0
- package/src/components/AtomixGlass/shader-utils.ts +4 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
- package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
- package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
- package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
- package/src/components/Button/Button.stories.tsx +10 -0
- package/src/components/Button/Button.test.tsx +16 -11
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +12 -12
- package/src/components/Form/Select.tsx +62 -3
- package/src/components/Modal/Modal.tsx +14 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +38 -0
- package/src/components/Steps/Steps.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +77 -8
- package/src/components/Testimonial/Testimonial.tsx +1 -1
- package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
- package/src/components/TypedButton/TypedButton.tsx +39 -0
- package/src/components/TypedButton/index.ts +2 -0
- package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
- package/src/lib/composables/index.ts +4 -7
- package/src/lib/composables/types.ts +45 -0
- package/src/lib/composables/useAccordion.ts +0 -7
- package/src/lib/composables/useAtomixGlass.ts +148 -6
- package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
- package/src/lib/composables/useChartExport.ts +3 -13
- package/src/lib/composables/useDropdown.ts +66 -0
- package/src/lib/composables/useFocusTrap.ts +80 -0
- package/src/lib/composables/usePerformanceMonitor.ts +448 -0
- package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
- package/src/lib/composables/useResponsiveGlass.ts +441 -0
- package/src/lib/composables/useTooltip.ts +16 -0
- package/src/lib/composables/useTypedButton.ts +66 -0
- package/src/lib/config/index.ts +62 -5
- package/src/lib/constants/components.ts +62 -7
- package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
- package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
- package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
- package/src/lib/types/components.ts +37 -11
- package/src/lib/types/glass.ts +35 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/utils/displacement-generator.ts +1 -1
- package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
- package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
- package/src/styles/06-components/_components.atomix-glass.scss +17 -21
- package/src/styles/06-components/_components.edge-panel.scss +1 -5
- package/src/styles/06-components/_components.modal.scss +1 -4
- package/src/styles/06-components/_components.navbar.scss +1 -1
- package/src/styles/06-components/_components.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- package/src/styles/06-components/_components.tooltip.scss +9 -5
- package/src/styles/06-components/_components.typedbutton.scss +212 -0
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
- package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
- package/scripts/cli/component-generator.js +0 -564
- package/scripts/cli/interactive-init.js +0 -357
- package/src/styles/06-components/old.chart.styles.scss +0 -2788
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Tokens Command
|
|
3
|
+
* Synchronizes design tokens from external sources (Figma, Style Dictionary, etc.)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import { AtomixCLIError } from '../utils/error.js';
|
|
10
|
+
import { tokenProvider, TOKEN_FORMATS } from '../internal/tokens/token-provider.js';
|
|
11
|
+
import { tokenValidator } from '../internal/tokens/token-validator.js';
|
|
12
|
+
import { filesystem } from '../internal/filesystem.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Token source configurations
|
|
16
|
+
*/
|
|
17
|
+
const TOKEN_SOURCES = {
|
|
18
|
+
FIGMA: {
|
|
19
|
+
name: 'Figma',
|
|
20
|
+
format: TOKEN_FORMATS.FIGMA,
|
|
21
|
+
defaultPath: './design-tokens/figma-tokens.json'
|
|
22
|
+
},
|
|
23
|
+
STYLE_DICTIONARY: {
|
|
24
|
+
name: 'Style Dictionary',
|
|
25
|
+
format: TOKEN_FORMATS.STYLE_DICTIONARY,
|
|
26
|
+
defaultPath: './design-tokens/style-dictionary.json'
|
|
27
|
+
},
|
|
28
|
+
W3C_DTCG: {
|
|
29
|
+
name: 'W3C DTCG',
|
|
30
|
+
format: TOKEN_FORMATS.W3C_DTCG,
|
|
31
|
+
defaultPath: './design-tokens/tokens.json'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Action logic for syncing tokens
|
|
37
|
+
* @param {string} source - Token source (figma, style-dictionary, w3c)
|
|
38
|
+
* @param {Object} options - CLI options
|
|
39
|
+
*/
|
|
40
|
+
export async function syncTokensAction(source, options) {
|
|
41
|
+
const spinner = logger.spinner(`Syncing tokens from ${source}...`).start();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Determine source configuration
|
|
45
|
+
const sourceConfig = TOKEN_SOURCES[source.toUpperCase()];
|
|
46
|
+
if (!sourceConfig) {
|
|
47
|
+
throw new AtomixCLIError(
|
|
48
|
+
`Unknown token source: ${source}`,
|
|
49
|
+
'UNKNOWN_SOURCE',
|
|
50
|
+
[
|
|
51
|
+
'Available sources: figma, style-dictionary, w3c',
|
|
52
|
+
'Use `atomix sync-tokens --help` for usage information'
|
|
53
|
+
]
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Determine file path
|
|
58
|
+
const filePath = options.file || sourceConfig.defaultPath;
|
|
59
|
+
const absolutePath = join(process.cwd(), filePath);
|
|
60
|
+
|
|
61
|
+
if (!existsSync(absolutePath)) {
|
|
62
|
+
throw new AtomixCLIError(
|
|
63
|
+
`Token file not found: ${absolutePath}`,
|
|
64
|
+
'TOKEN_FILE_NOT_FOUND',
|
|
65
|
+
[
|
|
66
|
+
'Verify the file path is correct',
|
|
67
|
+
'Export tokens from your design tool first',
|
|
68
|
+
'Use --file flag to specify custom path'
|
|
69
|
+
]
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Load tokens using provider
|
|
74
|
+
logger.debug(`Loading tokens from ${filePath}`);
|
|
75
|
+
const loadedTokens = await tokenProvider.loadTokens(filePath, {
|
|
76
|
+
format: sourceConfig.format
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Validate tokens
|
|
80
|
+
logger.debug('Validating tokens');
|
|
81
|
+
const validationResults = tokenValidator.validate(loadedTokens);
|
|
82
|
+
|
|
83
|
+
if (!validationResults.valid) {
|
|
84
|
+
spinner.warn('Token sync completed with validation issues');
|
|
85
|
+
logger.info(tokenValidator.getReport(validationResults));
|
|
86
|
+
|
|
87
|
+
if (!options.force && validationResults.summary.errors > 0) {
|
|
88
|
+
throw new AtomixCLIError(
|
|
89
|
+
'Token validation failed with errors',
|
|
90
|
+
'VALIDATION_FAILED',
|
|
91
|
+
[
|
|
92
|
+
'Fix the validation errors listed above',
|
|
93
|
+
'Use --force flag to sync anyway (not recommended)'
|
|
94
|
+
]
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
spinner.succeed(`Successfully synced ${countTokens(loadedTokens)} tokens from ${sourceConfig.name}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Export tokens to desired formats
|
|
102
|
+
if (options.output) {
|
|
103
|
+
await exportTokens(options.output, loadedTokens);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Show summary
|
|
107
|
+
showSyncSummary(loadedTokens, validationResults, sourceConfig);
|
|
108
|
+
|
|
109
|
+
} catch (error) {
|
|
110
|
+
spinner.fail('Token sync failed');
|
|
111
|
+
|
|
112
|
+
if (error instanceof AtomixCLIError) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw new AtomixCLIError(
|
|
117
|
+
`Failed to sync tokens: ${error.message}`,
|
|
118
|
+
'SYNC_FAILED',
|
|
119
|
+
[
|
|
120
|
+
'Check that the token file is valid JSON',
|
|
121
|
+
'Ensure you have read permissions for the file',
|
|
122
|
+
'Try validating the file manually'
|
|
123
|
+
]
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Export tokens to multiple formats
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
async function exportTokens(outputDir, tokens) {
|
|
133
|
+
const exports = [];
|
|
134
|
+
|
|
135
|
+
// Export to JSON
|
|
136
|
+
const jsonPath = join(outputDir, 'tokens.json');
|
|
137
|
+
const jsonContent = tokenProvider.exportTokens(TOKEN_FORMATS.JSON, { pretty: true });
|
|
138
|
+
await filesystem.writeFile(jsonPath, jsonContent, 'utf8');
|
|
139
|
+
exports.push(jsonPath);
|
|
140
|
+
|
|
141
|
+
// Export to CSS
|
|
142
|
+
const cssPath = join(outputDir, 'tokens.css');
|
|
143
|
+
const cssContent = tokenProvider.exportTokens(TOKEN_FORMATS.CSS, { selector: ':root' });
|
|
144
|
+
await filesystem.writeFile(cssPath, cssContent, 'utf8');
|
|
145
|
+
exports.push(cssPath);
|
|
146
|
+
|
|
147
|
+
logger.debug(`Exported tokens to: ${exports.join(', ')}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Count total tokens across all categories
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
function countTokens(tokens) {
|
|
155
|
+
let count = 0;
|
|
156
|
+
for (const category of Object.values(tokens)) {
|
|
157
|
+
count += Object.keys(category).length;
|
|
158
|
+
}
|
|
159
|
+
return count;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Display sync summary
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
function showSyncSummary(tokens, validation, source) {
|
|
167
|
+
const totalTokens = countTokens(tokens);
|
|
168
|
+
const categories = Object.keys(tokens);
|
|
169
|
+
|
|
170
|
+
const summary = `
|
|
171
|
+
✅ Token Sync Complete
|
|
172
|
+
|
|
173
|
+
Source: ${source.name}
|
|
174
|
+
Total Tokens: ${totalTokens}
|
|
175
|
+
Categories: ${categories.join(', ')}
|
|
176
|
+
|
|
177
|
+
Validation:
|
|
178
|
+
Errors: ${validation.summary.errors}
|
|
179
|
+
Warnings: ${validation.summary.warnings}
|
|
180
|
+
Info: ${validation.summary.info}
|
|
181
|
+
|
|
182
|
+
Next Steps:
|
|
183
|
+
• Review validation warnings with \`atomix validate tokens\`
|
|
184
|
+
• Export tokens with \`atomix build-theme\`
|
|
185
|
+
• Use tokens in components with \`atomix generate component\`
|
|
186
|
+
`.trim();
|
|
187
|
+
|
|
188
|
+
logger.box(summary);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Interactive mode for token sync
|
|
193
|
+
* @private
|
|
194
|
+
*/
|
|
195
|
+
export async function interactiveSync() {
|
|
196
|
+
logger.info('🔄 Interactive Token Sync\n');
|
|
197
|
+
|
|
198
|
+
// In a full implementation, this would use inquirer to prompt for:
|
|
199
|
+
// 1. Source selection (Figma, Style Dictionary, etc.)
|
|
200
|
+
// 2. File path
|
|
201
|
+
// 3. Export options
|
|
202
|
+
// 4. Validation preferences
|
|
203
|
+
|
|
204
|
+
logger.info('Interactive mode coming soon!');
|
|
205
|
+
logger.info('For now, use: atomix sync-tokens <source> --file <path>\n');
|
|
206
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Theme Bridge Command
|
|
3
|
+
* Syncs design tokens with theme providers (Tailwind, CSS-in-JS, CSS Variables)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import {
|
|
10
|
+
createThemePackage,
|
|
11
|
+
syncToTailwind,
|
|
12
|
+
syncToCssInJs,
|
|
13
|
+
syncToCssVariables,
|
|
14
|
+
validateThemeSync,
|
|
15
|
+
THEME_PROVIDERS
|
|
16
|
+
} from '../internal/theme-bridge.js';
|
|
17
|
+
import { filesystem } from '../internal/filesystem.js';
|
|
18
|
+
import { AtomixCLIError } from '../utils/error.js';
|
|
19
|
+
import { detectFramework } from '../utils/detector.js';
|
|
20
|
+
|
|
21
|
+
export const SYNC_PROVIDERS = {
|
|
22
|
+
TAILWIND: 'tailwind',
|
|
23
|
+
EMOTION: 'emotion',
|
|
24
|
+
STYLED_COMPONENTS: 'styled-components',
|
|
25
|
+
VANILLA_EXTRACT: 'vanilla-extract',
|
|
26
|
+
CSS_VARIABLES: 'css-variables'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Execute theme bridge synchronization (exported for CLI integration)
|
|
31
|
+
* @param {string} source - Source tokens file path
|
|
32
|
+
* @param {Object} options - Command options
|
|
33
|
+
*/
|
|
34
|
+
export async function themeBridgeAction(source = './design-tokens/tokens.json', options = {}) {
|
|
35
|
+
await executeThemeBridge(source, options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates the theme-bridge command
|
|
40
|
+
* @param {Command} program - Commander program instance
|
|
41
|
+
*/
|
|
42
|
+
export function createThemeBridgeCommand(program) {
|
|
43
|
+
const cmd = program
|
|
44
|
+
.command('theme-bridge')
|
|
45
|
+
.description('Sync design tokens with theme providers')
|
|
46
|
+
.argument('[source]', 'Path to design tokens file', './design-tokens/tokens.json')
|
|
47
|
+
.option('-o, --output <dir>', 'Output directory for theme files', './src/theme')
|
|
48
|
+
.option('-f, --format <format>', 'Theme format (tailwind, emotion, styled-components, vanilla-extract, css-variables, all)', 'all')
|
|
49
|
+
.option('--prefix <prefix>', 'CSS variable prefix', 'atomix')
|
|
50
|
+
.option('--selector <selector>', 'CSS selector for variables', ':root')
|
|
51
|
+
.option('--no-typescript', 'Skip TypeScript type generation')
|
|
52
|
+
.option('--validate', 'Validate generated theme files')
|
|
53
|
+
.option('--dry-run', 'Show what would be generated without writing files')
|
|
54
|
+
.action(async (source, options) => {
|
|
55
|
+
try {
|
|
56
|
+
await executeThemeBridge(source, options);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof AtomixCLIError) {
|
|
59
|
+
console.error(chalk.red('\nTheme bridge failed:'));
|
|
60
|
+
console.error(chalk.yellow(error.message));
|
|
61
|
+
if (error.suggestions?.length) {
|
|
62
|
+
console.error(chalk.dim('\nSuggestions:'));
|
|
63
|
+
error.suggestions.forEach(s => console.error(` - ${s}`));
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return cmd;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Execute theme bridge synchronization
|
|
76
|
+
* @param {string} source - Source tokens file path
|
|
77
|
+
* @param {Object} options - Command options
|
|
78
|
+
*/
|
|
79
|
+
async function executeThemeBridge(source, options) {
|
|
80
|
+
const { output, format, prefix, selector, typescript, validate, dryRun } = options;
|
|
81
|
+
|
|
82
|
+
// Set dry run mode
|
|
83
|
+
if (dryRun) {
|
|
84
|
+
process.env.ATOMIX_DRY_RUN = 'true';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(chalk.cyan('\n🎨 Atomix Theme Bridge\n'));
|
|
88
|
+
|
|
89
|
+
// Resolve paths
|
|
90
|
+
const tokenPath = join(process.cwd(), source);
|
|
91
|
+
const outputDir = join(process.cwd(), output);
|
|
92
|
+
|
|
93
|
+
// Check if source file exists
|
|
94
|
+
const exists = await filesystem.exists(tokenPath);
|
|
95
|
+
if (!exists) {
|
|
96
|
+
throw new AtomixCLIError(
|
|
97
|
+
`Design tokens file not found: ${tokenPath}`,
|
|
98
|
+
'TOKEN_FILE_NOT_FOUND',
|
|
99
|
+
[
|
|
100
|
+
'Run `atomix init` to create design tokens',
|
|
101
|
+
'Check the source path is correct',
|
|
102
|
+
'Ensure tokens file has been created'
|
|
103
|
+
]
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(chalk.bold('Source:'), tokenPath);
|
|
108
|
+
console.log(chalk.bold('Output:'), outputDir);
|
|
109
|
+
console.log(chalk.bold('Format:'), format);
|
|
110
|
+
console.log();
|
|
111
|
+
|
|
112
|
+
// Generate theme package or single format
|
|
113
|
+
let result;
|
|
114
|
+
|
|
115
|
+
if (format === 'all' || !format) {
|
|
116
|
+
result = await createThemePackage(tokenPath, outputDir, {
|
|
117
|
+
prefix,
|
|
118
|
+
selector,
|
|
119
|
+
typescript: typescript !== false
|
|
120
|
+
});
|
|
121
|
+
} else if (format === 'tailwind') {
|
|
122
|
+
const outputPath = join(outputDir, 'tailwind.theme.js');
|
|
123
|
+
result = await syncToTailwind(tokenPath, outputPath);
|
|
124
|
+
} else if (['emotion', 'styled-components'].includes(format)) {
|
|
125
|
+
const outputPath = join(outputDir, 'theme.ts');
|
|
126
|
+
result = await syncToCssInJs(tokenPath, outputPath, format);
|
|
127
|
+
} else if (format === 'vanilla-extract') {
|
|
128
|
+
const outputPath = join(outputDir, 'theme.ts');
|
|
129
|
+
result = await syncToCssInJs(tokenPath, outputPath, 'vanilla-extract');
|
|
130
|
+
} else if (format === 'css-variables') {
|
|
131
|
+
const outputPath = join(outputDir, 'variables.css');
|
|
132
|
+
result = await syncToCssVariables(tokenPath, outputPath, { prefix, selector });
|
|
133
|
+
} else {
|
|
134
|
+
throw new AtomixCLIError(
|
|
135
|
+
`Unknown format: ${format}`,
|
|
136
|
+
'UNKNOWN_FORMAT',
|
|
137
|
+
['Use: tailwind, emotion, styled-components, vanilla-extract, css-variables, or all']
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Display results
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
console.log(chalk.yellow('\n[DRY RUN] Files that would be created:'));
|
|
144
|
+
result.created.forEach(file => console.log(` ${file}`));
|
|
145
|
+
} else {
|
|
146
|
+
console.log(chalk.green('\n✓ Theme synchronization complete!\n'));
|
|
147
|
+
console.log(chalk.bold('Created files:'));
|
|
148
|
+
result.created.forEach(file => console.log(` ${chalk.cyan(file)}`));
|
|
149
|
+
|
|
150
|
+
if (result.formats) {
|
|
151
|
+
console.log(chalk.bold('\nFormats generated:'));
|
|
152
|
+
result.formats.forEach(f => console.log(` ${chalk.cyan(f)}`));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(chalk.bold('\nTokens synced:'), result.tokensSynced);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Validate if requested
|
|
159
|
+
if (validate && !dryRun) {
|
|
160
|
+
console.log(chalk.cyan('\n🔍 Validating theme files...\n'));
|
|
161
|
+
|
|
162
|
+
for (const file of result.created) {
|
|
163
|
+
const provider = detectProviderFromFile(file);
|
|
164
|
+
const validation = await validateThemeSync(tokenPath, file, provider);
|
|
165
|
+
|
|
166
|
+
if (validation.valid) {
|
|
167
|
+
console.log(chalk.green(` ✓ ${file}`));
|
|
168
|
+
} else {
|
|
169
|
+
console.log(chalk.yellow(` ⚠ ${file}`));
|
|
170
|
+
validation.issues.forEach(issue => {
|
|
171
|
+
const icon = issue.severity === 'error' ? '✗' : '⚠';
|
|
172
|
+
console.log(chalk.dim(` ${icon} ${issue.message}`));
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Show usage instructions
|
|
181
|
+
showUsageInstructions(format, outputDir);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Detect theme provider from file path/extension
|
|
186
|
+
* @param {string} filePath - Path to theme file
|
|
187
|
+
* @returns {string} Provider type
|
|
188
|
+
*/
|
|
189
|
+
function detectProviderFromFile(filePath) {
|
|
190
|
+
if (filePath.includes('tailwind')) return 'tailwind';
|
|
191
|
+
if (filePath.endsWith('.css')) return 'css-variables';
|
|
192
|
+
if (filePath.includes('vanilla-extract')) return 'vanilla-extract';
|
|
193
|
+
if (filePath.includes('emotion')) return 'emotion';
|
|
194
|
+
if (filePath.includes('styled-components')) return 'styled-components';
|
|
195
|
+
return 'css-in-js';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Show usage instructions for different theme providers
|
|
200
|
+
* @param {string} format - Generated format
|
|
201
|
+
* @param {string} outputDir - Output directory
|
|
202
|
+
*/
|
|
203
|
+
function showUsageInstructions(format, outputDir) {
|
|
204
|
+
console.log(chalk.bold('\n📚 Usage Instructions:\n'));
|
|
205
|
+
|
|
206
|
+
if (format === 'all' || format === 'tailwind') {
|
|
207
|
+
console.log(chalk.cyan('Tailwind CSS:'));
|
|
208
|
+
console.log(chalk.dim(' // tailwind.config.js'));
|
|
209
|
+
console.log(chalk.dim(' module.exports = {'));
|
|
210
|
+
console.log(chalk.dim(' presets: [require(\'./src/theme/tailwind.theme\')],'));
|
|
211
|
+
console.log(chalk.dim(' };'));
|
|
212
|
+
console.log();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (format === 'all' || format === 'emotion') {
|
|
216
|
+
console.log(chalk.cyan('Emotion:'));
|
|
217
|
+
console.log(chalk.dim(' import { ThemeProvider } from \'@emotion/react\';'));
|
|
218
|
+
console.log(chalk.dim(' import { theme } from \'./src/theme/theme\';'));
|
|
219
|
+
console.log(chalk.dim(' '));
|
|
220
|
+
console.log(chalk.dim(' function App() {'));
|
|
221
|
+
console.log(chalk.dim(' return <ThemeProvider theme={theme}>...</ThemeProvider>;'));
|
|
222
|
+
console.log(chalk.dim(' }'));
|
|
223
|
+
console.log();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (format === 'all' || format === 'css-variables') {
|
|
227
|
+
console.log(chalk.cyan('CSS Variables:'));
|
|
228
|
+
console.log(chalk.dim(' // Import in your main CSS file'));
|
|
229
|
+
console.log(chalk.dim(' @import \'./src/theme/variables.css\';'));
|
|
230
|
+
console.log(chalk.dim(' '));
|
|
231
|
+
console.log(chalk.dim(' // Use in components'));
|
|
232
|
+
console.log(chalk.dim(' color: var(--atomix-primary);'));
|
|
233
|
+
console.log(chalk.dim(' padding: var(--atomix-space-md);'));
|
|
234
|
+
console.log();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (format === 'all' || format === 'vanilla-extract') {
|
|
238
|
+
console.log(chalk.cyan('Vanilla Extract:'));
|
|
239
|
+
console.log(chalk.dim(' import { style } from \'@vanilla-extract/css\';'));
|
|
240
|
+
console.log(chalk.dim(' import { theme } from \'./src/theme/theme.css\';'));
|
|
241
|
+
console.log(chalk.dim(' '));
|
|
242
|
+
console.log(chalk.dim(' const myStyle = style({'));
|
|
243
|
+
console.log(chalk.dim(' color: theme.colors.primary,'));
|
|
244
|
+
console.log(chalk.dim(' padding: theme.space.md'));
|
|
245
|
+
console.log(chalk.dim(' });'));
|
|
246
|
+
console.log();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Tokens Command
|
|
3
|
+
* Design token management and export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { listTokens, exportTokens } from '../token-manager.js';
|
|
9
|
+
import { tokenEngine } from '../internal/tokens/engine.js';
|
|
10
|
+
import { isNonInteractive } from '../utils/helpers.js';
|
|
11
|
+
import { AtomixCLIError, ErrorCategory } from '../utils/error.js';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
|
|
14
|
+
const SUPPORTED_PROVIDER_TYPES = 'figma, style-dictionary, w3c';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get provider for pull/push: from options, or prompt in interactive mode, or throw
|
|
18
|
+
* @param {object} options - Command options
|
|
19
|
+
* @param {string} subcommand - 'pull' | 'push'
|
|
20
|
+
* @returns {Promise<string>} - Provider name
|
|
21
|
+
*/
|
|
22
|
+
async function resolveProvider(options, subcommand) {
|
|
23
|
+
if (options.provider) return options.provider;
|
|
24
|
+
|
|
25
|
+
const names = tokenEngine.getProviderNames();
|
|
26
|
+
const message = names.length === 0
|
|
27
|
+
? `No token providers configured. Add tokenEngine.providers in atomix.config. Supported types: ${SUPPORTED_PROVIDER_TYPES}.`
|
|
28
|
+
: `Missing required option: --provider. Supported (from config): ${names.join(', ')}. Use --provider <name>.`;
|
|
29
|
+
|
|
30
|
+
if (isNonInteractive() || !process.stdin.isTTY) {
|
|
31
|
+
throw new AtomixCLIError(message, ErrorCategory.CONFIG, [
|
|
32
|
+
'Set --provider <name> or configure tokenEngine.providers in atomix.config.'
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (names.length === 0) {
|
|
37
|
+
throw new AtomixCLIError(message, ErrorCategory.CONFIG, [
|
|
38
|
+
'Add tokenEngine.providers in atomix.config. Supported types: figma, style-dictionary, w3c.'
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { provider } = await inquirer.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: 'list',
|
|
45
|
+
name: 'provider',
|
|
46
|
+
message: `Select provider for ${subcommand}:`,
|
|
47
|
+
choices: names
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
return provider;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Action for the `atomix tokens` command
|
|
55
|
+
* @param {string} subcommand - list | export | pull | push
|
|
56
|
+
* @param {object} options - Command options
|
|
57
|
+
*/
|
|
58
|
+
export async function tokensAction(subcommand, options = {}) {
|
|
59
|
+
// Initialize token engine
|
|
60
|
+
await tokenEngine.initialize();
|
|
61
|
+
|
|
62
|
+
switch (subcommand) {
|
|
63
|
+
case 'list':
|
|
64
|
+
await listTokensAction(options);
|
|
65
|
+
break;
|
|
66
|
+
case 'export':
|
|
67
|
+
await exportTokensAction(options);
|
|
68
|
+
break;
|
|
69
|
+
case 'pull': {
|
|
70
|
+
const provider = await resolveProvider(options, 'pull');
|
|
71
|
+
if (!provider) break;
|
|
72
|
+
await pullTokensAction(provider, options);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'push': {
|
|
76
|
+
const provider = await resolveProvider(options, 'push');
|
|
77
|
+
if (!provider) break;
|
|
78
|
+
await pushTokensAction(provider, options);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
default:
|
|
82
|
+
throw new AtomixCLIError(
|
|
83
|
+
`Unknown subcommand: ${subcommand}. Available: list, export, pull, push`,
|
|
84
|
+
ErrorCategory.VALIDATION,
|
|
85
|
+
['Use one of: atomix tokens list | export | pull | push']
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Pull tokens from a provider
|
|
92
|
+
*/
|
|
93
|
+
async function pullTokensAction(provider, options) {
|
|
94
|
+
try {
|
|
95
|
+
const tokens = await tokenEngine.pull(provider);
|
|
96
|
+
logger.box(`Tokens pulled successfully from ${provider}!\nSource: ${tokens.source}\nTokens: ${Object.keys(tokens.tokens).join(', ')}`);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw new AtomixCLIError(
|
|
99
|
+
error.message || `Token pull failed`,
|
|
100
|
+
ErrorCategory.CONFIG,
|
|
101
|
+
['Check tokenEngine.providers in atomix.config and network access.']
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Push tokens to a provider
|
|
108
|
+
*/
|
|
109
|
+
async function pushTokensAction(provider, options) {
|
|
110
|
+
try {
|
|
111
|
+
const localTokens = {};
|
|
112
|
+
const success = await tokenEngine.push(provider, localTokens);
|
|
113
|
+
if (success) {
|
|
114
|
+
logger.succeed(`Tokens pushed successfully to ${provider}`);
|
|
115
|
+
} else {
|
|
116
|
+
throw new AtomixCLIError(
|
|
117
|
+
'Token push failed',
|
|
118
|
+
ErrorCategory.CONFIG,
|
|
119
|
+
['Check provider configuration and credentials.']
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof AtomixCLIError) throw error;
|
|
124
|
+
throw new AtomixCLIError(
|
|
125
|
+
error.message || 'Token push failed',
|
|
126
|
+
ErrorCategory.CONFIG,
|
|
127
|
+
['Check tokenEngine.providers in atomix.config.']
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* List all tokens
|
|
134
|
+
*/
|
|
135
|
+
async function listTokensAction(options) {
|
|
136
|
+
try {
|
|
137
|
+
// listTokens already logs everything
|
|
138
|
+
await listTokens();
|
|
139
|
+
} catch (error) {
|
|
140
|
+
logger.error(`Failed to list tokens: ${error.message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Export tokens
|
|
146
|
+
*/
|
|
147
|
+
async function exportTokensAction(options) {
|
|
148
|
+
const format = options.format || 'css';
|
|
149
|
+
const outputPath = options.output || './tokens';
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// exportTokens already logs everything
|
|
153
|
+
await exportTokens(format, outputPath);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error(`Token export failed: ${error.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|