@shohojdhara/atomix 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/atomix.config.ts +58 -1
- package/dist/atomix.css +148 -120
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1227 -122
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1052 -41
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2086 -1035
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1620 -600
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +441 -270
- package/dist/index.esm.js +1900 -638
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1935 -670
- 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 +148 -4
- 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 +4 -1
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +135 -14
- package/scripts/cli/commands/init.js +45 -18
- 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/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +102 -2
- package/scripts/cli/internal/generator.js +359 -39
- 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 +60 -6
- 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 +45 -3
- package/scripts/cli/utils/helpers.js +24 -0
- package/scripts/cli/utils/logger.js +1 -1
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +4 -38
- package/scripts/cli/utils.js +46 -0
- 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.tsx +102 -2
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +125 -12
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- 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 +144 -5
- 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 +55 -0
- 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.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- 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/src/styles/06-components/old.chart.styles.scss +0 -2788
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shohojdhara/atomix",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "Atomix Design System - A modern component library for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -134,8 +134,6 @@
|
|
|
134
134
|
},
|
|
135
135
|
"devDependencies": {
|
|
136
136
|
"@arethetypeswrong/cli": "^0.15.0",
|
|
137
|
-
"jest-axe": "^8.0.0",
|
|
138
|
-
"rollup-plugin-preserve-shebangs": "^0.2.0",
|
|
139
137
|
"@babel/core": "^7.23.0",
|
|
140
138
|
"@babel/plugin-transform-runtime": "^7.23.0",
|
|
141
139
|
"@babel/preset-env": "^7.23.0",
|
|
@@ -143,9 +141,11 @@
|
|
|
143
141
|
"@babel/preset-typescript": "^7.23.0",
|
|
144
142
|
"@babel/runtime-corejs3": "^7.23.0",
|
|
145
143
|
"@changesets/cli": "^2.26.0",
|
|
144
|
+
"@happy-dom/global-registrator": "^20.8.4",
|
|
146
145
|
"@phosphor-icons/react": "2.1.10",
|
|
147
146
|
"@rollup/plugin-babel": "^6.0.0",
|
|
148
147
|
"@rollup/plugin-commonjs": "^25.0.0",
|
|
148
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
149
149
|
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
150
150
|
"@rollup/plugin-terser": "^0.4.0",
|
|
151
151
|
"@rollup/plugin-typescript": "^11.0.0",
|
|
@@ -185,7 +185,9 @@
|
|
|
185
185
|
"eslint-plugin-jsx-a11y": "^6.7.0",
|
|
186
186
|
"eslint-plugin-react": "^7.33.0",
|
|
187
187
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
188
|
+
"happy-dom": "^20.8.4",
|
|
188
189
|
"identity-obj-proxy": "^3.0.0",
|
|
190
|
+
"jest-axe": "^8.0.0",
|
|
189
191
|
"jsdom": "^22.0.0",
|
|
190
192
|
"postcss-flexbugs-fixes": "^5.0.0",
|
|
191
193
|
"postcss-import": "^15.0.0",
|
|
@@ -198,6 +200,7 @@
|
|
|
198
200
|
"rollup-plugin-dts": "^6.0.0",
|
|
199
201
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
200
202
|
"rollup-plugin-postcss": "^4.0.0",
|
|
203
|
+
"rollup-plugin-preserve-shebangs": "^0.2.0",
|
|
201
204
|
"rollup-plugin-visualizer": "^5.12.0",
|
|
202
205
|
"storybook": "^8.6.14",
|
|
203
206
|
"storybook-theme-switch-addon": "^1.0.2",
|
package/scripts/atomix-cli.js
CHANGED
|
@@ -11,11 +11,21 @@ import { join, dirname } from 'path';
|
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { logger } from './cli/utils/logger.js';
|
|
13
13
|
import { handleCLIError } from './cli/utils/error.js';
|
|
14
|
+
import { telemetry } from './cli/utils/telemetry.js';
|
|
15
|
+
import chalk from 'chalk';
|
|
14
16
|
|
|
15
17
|
// Action Modules
|
|
16
18
|
import { initAction } from './cli/commands/init.js';
|
|
17
19
|
import { generateAction } from './cli/commands/generate.js';
|
|
18
20
|
import { buildThemeAction } from './cli/commands/build-theme.js';
|
|
21
|
+
import { doctorAction } from './cli/commands/doctor.js';
|
|
22
|
+
import { validateAction } from './cli/commands/validate.js';
|
|
23
|
+
import { tokensAction } from './cli/commands/tokens.js';
|
|
24
|
+
import { migrateAction } from './cli/commands/migrate.js';
|
|
25
|
+
import { benchmarkAction } from './cli/commands/benchmark.js';
|
|
26
|
+
import { cleanAction } from './cli/commands/clean.js';
|
|
27
|
+
import { themeBridgeAction } from './cli/commands/theme-bridge.js';
|
|
28
|
+
import { configLoader } from './cli/internal/config-loader.js';
|
|
19
29
|
|
|
20
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
31
|
const __dirname = dirname(__filename);
|
|
@@ -30,11 +40,75 @@ program
|
|
|
30
40
|
.description('Atomix Design System CLI - Modular Edition')
|
|
31
41
|
.version(packageJson.version)
|
|
32
42
|
.option('-d, --debug', 'Enable debug mode', false)
|
|
33
|
-
.
|
|
43
|
+
.option('--dry-run', 'Preview changes without modifying files', false)
|
|
44
|
+
.hook('preAction', async (thisCommand) => {
|
|
45
|
+
// Load config
|
|
46
|
+
await configLoader.load();
|
|
47
|
+
|
|
34
48
|
if (thisCommand.opts().debug) {
|
|
35
49
|
process.env.ATOMIX_DEBUG = 'true';
|
|
36
50
|
logger.debug('Debug mode enabled');
|
|
37
51
|
}
|
|
52
|
+
if (thisCommand.opts().dryRun) {
|
|
53
|
+
process.env.ATOMIX_DRY_RUN = 'true';
|
|
54
|
+
logger.info(chalk.yellow('⚠️ Dry-run mode enabled. No files will be modified.'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Start telemetry
|
|
58
|
+
const fullCommand = thisCommand.name() === 'atomix'
|
|
59
|
+
? thisCommand.args[0] || 'atomix'
|
|
60
|
+
: thisCommand.name();
|
|
61
|
+
telemetry.start(fullCommand);
|
|
62
|
+
})
|
|
63
|
+
.hook('postAction', async (thisCommand) => {
|
|
64
|
+
// Stop telemetry
|
|
65
|
+
await telemetry.stop(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Environment Diagnostics
|
|
70
|
+
*/
|
|
71
|
+
program
|
|
72
|
+
.command('doctor')
|
|
73
|
+
.description('Verify the environment and project health. Use --explain to list what each check does.')
|
|
74
|
+
.option('--explain', 'Print short descriptions for each doctor check and exit')
|
|
75
|
+
.action(async (options) => {
|
|
76
|
+
try {
|
|
77
|
+
await doctorAction(options);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
await handleCLIError(error);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Code & Config Validation
|
|
85
|
+
*/
|
|
86
|
+
program
|
|
87
|
+
.command('validate [subcommand] [name]')
|
|
88
|
+
.description('Audit project quality (A11y, Tokens, Performance). Use "validate component <Name>" for component-scoped audit. Run build first for performance analysis (requires dist/).')
|
|
89
|
+
.action(async (subcommand, name, options) => {
|
|
90
|
+
try {
|
|
91
|
+
await validateAction(options, subcommand, name);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
await handleCLIError(error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Design Token Management
|
|
99
|
+
*/
|
|
100
|
+
program
|
|
101
|
+
.command('tokens <subcommand>')
|
|
102
|
+
.description('Manage design tokens (list, export, pull, push). For pull/push, --provider is required (configure tokenEngine.providers in atomix.config; types: figma, style-dictionary, w3c).')
|
|
103
|
+
.option('-f, --format <format>', 'Export format (css|scss|json)', 'css')
|
|
104
|
+
.option('-o, --output <path>', 'Output directory', './tokens')
|
|
105
|
+
.option('-p, --provider <provider>', 'Token provider name (required for pull and push)')
|
|
106
|
+
.action(async (subcommand, options) => {
|
|
107
|
+
try {
|
|
108
|
+
await tokensAction(subcommand, options);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
await handleCLIError(error);
|
|
111
|
+
}
|
|
38
112
|
});
|
|
39
113
|
|
|
40
114
|
/**
|
|
@@ -42,7 +116,9 @@ program
|
|
|
42
116
|
*/
|
|
43
117
|
program
|
|
44
118
|
.command('init')
|
|
45
|
-
.description('Initialize a new Atomix design system project')
|
|
119
|
+
.description('Initialize a new Atomix design system project. For CI/scripts use --type <react|nextjs|vanilla> or --yes.')
|
|
120
|
+
.option('-y, --yes', 'Use default choices (non-interactive)')
|
|
121
|
+
.option('-t, --type <type>', 'Project type: react, nextjs, or vanilla (skips prompt when set)')
|
|
46
122
|
.action(async (options) => {
|
|
47
123
|
try {
|
|
48
124
|
await initAction(options);
|
|
@@ -60,6 +136,7 @@ program
|
|
|
60
136
|
.description('Generate components, tokens, or themes')
|
|
61
137
|
.option('-i, --interactive', 'Interactive mode', false)
|
|
62
138
|
.option('-p, --path <path>', 'Output path', './src/components')
|
|
139
|
+
.option('--prompt <prompt>', 'AI prompt for generating component')
|
|
63
140
|
.option('--complexity <level>', 'Complexity (simple|medium|complex)', 'medium')
|
|
64
141
|
.option('--validate', 'Validate after generation', true)
|
|
65
142
|
.action(async (type, name, options) => {
|
|
@@ -70,13 +147,59 @@ program
|
|
|
70
147
|
}
|
|
71
148
|
});
|
|
72
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Migration Tools
|
|
152
|
+
*/
|
|
153
|
+
program
|
|
154
|
+
.command('migrate <type> <source>')
|
|
155
|
+
.description('Migrate from other frameworks (tailwind|bootstrap). <source> is the project root directory (e.g. . or ./my-tailwind-app).')
|
|
156
|
+
.option('-p, --preview', 'Preview side-by-side diff before applying', false)
|
|
157
|
+
.action(async (type, source, options) => {
|
|
158
|
+
try {
|
|
159
|
+
await migrateAction(type, source, options);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
await handleCLIError(error);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Performance & Benchmarking
|
|
167
|
+
*/
|
|
168
|
+
program
|
|
169
|
+
.command('benchmark')
|
|
170
|
+
.description('Profile CLI performance and show metrics. Benchmark collects metrics from previous CLI runs.')
|
|
171
|
+
.action(async (options) => {
|
|
172
|
+
try {
|
|
173
|
+
await benchmarkAction(options);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
await handleCLIError(error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Build Artifact Cleanup
|
|
181
|
+
*/
|
|
182
|
+
program
|
|
183
|
+
.command('clean')
|
|
184
|
+
.description('Clean build artifacts and cache files. Use --dry-run to preview, --all to include node_modules.')
|
|
185
|
+
.option('--all', 'Include node_modules in cleanup', false)
|
|
186
|
+
.option('--cache', 'Only clean cache directories', false)
|
|
187
|
+
.option('--verbose', 'Show detailed output', false)
|
|
188
|
+
.action(async (options) => {
|
|
189
|
+
try {
|
|
190
|
+
await cleanAction(options);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
await handleCLIError(error);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
73
196
|
/**
|
|
74
197
|
* Theme Building
|
|
75
198
|
*/
|
|
76
199
|
program
|
|
77
200
|
.command('build-theme <path>')
|
|
78
|
-
.description('Build a custom theme from SCSS')
|
|
79
|
-
.option('-o, --output <path>', 'Output directory', './dist')
|
|
201
|
+
.description('Build a custom theme from SCSS. Default output is ./dist (may overwrite app build; use -o dist-theme for custom themes).')
|
|
202
|
+
.option('-o, --output <path>', 'Output directory (default: ./dist)', './dist')
|
|
80
203
|
.option('-m, --minify', 'Minify CSS', true)
|
|
81
204
|
.option('-s, --sourcemap', 'Generate source maps', false)
|
|
82
205
|
.option('-w, --watch', 'Watch mode', false)
|
|
@@ -89,6 +212,27 @@ program
|
|
|
89
212
|
}
|
|
90
213
|
});
|
|
91
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Theme Bridge - Sync Design Tokens with Theme Providers
|
|
217
|
+
*/
|
|
218
|
+
program
|
|
219
|
+
.command('theme-bridge [source]')
|
|
220
|
+
.description('Sync design tokens with theme providers (Tailwind, CSS-in-JS, CSS Variables)')
|
|
221
|
+
.option('-o, --output <dir>', 'Output directory for theme files', './src/theme')
|
|
222
|
+
.option('-f, --format <format>', 'Theme format (tailwind, emotion, styled-components, vanilla-extract, css-variables, all)', 'all')
|
|
223
|
+
.option('--prefix <prefix>', 'CSS variable prefix', 'atomix')
|
|
224
|
+
.option('--selector <selector>', 'CSS selector for variables', ':root')
|
|
225
|
+
.option('--no-typescript', 'Skip TypeScript type generation')
|
|
226
|
+
.option('--validate', 'Validate generated theme files')
|
|
227
|
+
.option('--dry-run', 'Show what would be generated without writing files')
|
|
228
|
+
.action(async (source, options) => {
|
|
229
|
+
try {
|
|
230
|
+
await themeBridgeAction(source, options);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
await handleCLIError(error);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
92
236
|
// Run program
|
|
93
237
|
program.parseAsync(process.argv).catch(async (error) => {
|
|
94
238
|
await handleCLIError(error);
|
|
@@ -21,7 +21,8 @@ describe('CLI Core Utils', () => {
|
|
|
21
21
|
it('should reject paths outside project directory', () => {
|
|
22
22
|
const result = validatePath('../../etc/passwd', '/project');
|
|
23
23
|
expect(result.isValid).toBe(false);
|
|
24
|
-
expect(result.error).
|
|
24
|
+
expect(result.error).toBeTruthy();
|
|
25
|
+
expect(result.error).toMatch(/outside|traversal/);
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
it('should reject sensitive files', () => {
|
|
@@ -42,7 +43,7 @@ describe('CLI Core Utils', () => {
|
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
it('should reject invalid names', () => {
|
|
45
|
-
const invalidNames = ['
|
|
46
|
+
const invalidNames = ['123Button', '', '1', 'a'];
|
|
46
47
|
|
|
47
48
|
invalidNames.forEach(name => {
|
|
48
49
|
const result = validateComponentName(name);
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Atomix CLI Clean Command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { mkdtemp, writeFile, mkdir, rm } from 'fs/promises';
|
|
8
|
+
import { tmpdir } from 'os';
|
|
9
|
+
import { cacheManager } from '../utils/cache-manager.js';
|
|
10
|
+
|
|
11
|
+
describe('cacheManager', () => {
|
|
12
|
+
let testDir;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
// Create isolated temp directory for each test
|
|
16
|
+
testDir = await mkdtemp(join(tmpdir(), 'atomix-clean-test-'));
|
|
17
|
+
process.chdir(testDir);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
// Cleanup temp directory
|
|
22
|
+
await rm(testDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('identifyTargets', () => {
|
|
26
|
+
it('should return empty array when no clean targets exist', async () => {
|
|
27
|
+
const targets = await cacheManager.identifyTargets();
|
|
28
|
+
expect(targets).toEqual([]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should identify dist directory', async () => {
|
|
32
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
33
|
+
const targets = await cacheManager.identifyTargets();
|
|
34
|
+
|
|
35
|
+
expect(targets.some(t => t.relativePath === 'dist')).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should identify .atomix directory', async () => {
|
|
39
|
+
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
40
|
+
const targets = await cacheManager.identifyTargets();
|
|
41
|
+
|
|
42
|
+
expect(targets.some(t => t.relativePath === '.atomix')).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should identify node_modules/.cache with --all flag', async () => {
|
|
46
|
+
await mkdir(join(testDir, 'node_modules', '.cache'), { recursive: true });
|
|
47
|
+
|
|
48
|
+
// Without --all flag, node_modules should not be included
|
|
49
|
+
const targetsDefault = await cacheManager.identifyTargets();
|
|
50
|
+
// Default targets don't include node_modules at all
|
|
51
|
+
expect(targetsDefault.some(t => t.relativePath === 'node_modules')).toBe(false);
|
|
52
|
+
|
|
53
|
+
// With --all flag, node_modules should be included
|
|
54
|
+
const targetsAll = await cacheManager.identifyTargets({ all: true });
|
|
55
|
+
expect(targetsAll.some(t => t.relativePath === 'node_modules')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should only identify cache directories with --cache flag', async () => {
|
|
59
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
60
|
+
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
61
|
+
await mkdir(join(testDir, 'node_modules', '.cache'), { recursive: true });
|
|
62
|
+
|
|
63
|
+
const targets = await cacheManager.identifyTargets({ cache: true });
|
|
64
|
+
|
|
65
|
+
// Should include .atomix and node_modules/.cache but not log files
|
|
66
|
+
expect(targets.some(t => t.relativePath === '.atomix')).toBe(true);
|
|
67
|
+
expect(targets.some(t => t.relativePath.includes('node_modules'))).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('getPathType', () => {
|
|
72
|
+
it('should identify directory type', async () => {
|
|
73
|
+
await mkdir(join(testDir, 'test-dir'), { recursive: true });
|
|
74
|
+
const type = await cacheManager.getPathType(join(testDir, 'test-dir'));
|
|
75
|
+
expect(type).toBe('directory');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should identify file type', async () => {
|
|
79
|
+
await writeFile(join(testDir, 'test.txt'), 'content');
|
|
80
|
+
const type = await cacheManager.getPathType(join(testDir, 'test.txt'));
|
|
81
|
+
expect(type).toBe('file');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return unknown for non-existent path', async () => {
|
|
85
|
+
const type = await cacheManager.getPathType(join(testDir, 'nonexistent'));
|
|
86
|
+
expect(type).toBe('unknown');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('isProtected', () => {
|
|
91
|
+
it('should protect JavaScript files', () => {
|
|
92
|
+
expect(cacheManager.isProtected('file.js')).toBe(true);
|
|
93
|
+
expect(cacheManager.isProtected('file.jsx')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should protect TypeScript files', () => {
|
|
97
|
+
expect(cacheManager.isProtected('file.ts')).toBe(true);
|
|
98
|
+
expect(cacheManager.isProtected('file.tsx')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should protect style files', () => {
|
|
102
|
+
expect(cacheManager.isProtected('file.scss')).toBe(true);
|
|
103
|
+
expect(cacheManager.isProtected('file.css')).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should allow deleting build artifacts', () => {
|
|
107
|
+
expect(cacheManager.isProtected('file.map')).toBe(false);
|
|
108
|
+
expect(cacheManager.isProtected('file.bundle')).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('formatBytes', () => {
|
|
113
|
+
it('should format zero bytes', () => {
|
|
114
|
+
expect(cacheManager.formatBytes(0)).toBe('0 Bytes');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should format bytes to KB', () => {
|
|
118
|
+
expect(cacheManager.formatBytes(1024)).toBe('1 KB');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should format bytes to MB', () => {
|
|
122
|
+
expect(cacheManager.formatBytes(1048576)).toBe('1 MB');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should format large sizes', () => {
|
|
126
|
+
expect(cacheManager.formatBytes(1073741824)).toBe('1 GB');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('calculateSize', () => {
|
|
131
|
+
it('should calculate total size of files', async () => {
|
|
132
|
+
// Create test files
|
|
133
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
134
|
+
await writeFile(join(testDir, 'dist', 'file1.txt'), 'a'.repeat(100));
|
|
135
|
+
await writeFile(join(testDir, 'dist', 'file2.txt'), 'b'.repeat(200));
|
|
136
|
+
|
|
137
|
+
const targets = await cacheManager.identifyTargets();
|
|
138
|
+
const size = await cacheManager.calculateSize(targets);
|
|
139
|
+
|
|
140
|
+
expect(size).toBeGreaterThan(0);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('deletePath', () => {
|
|
145
|
+
it('should delete a directory in dry-run mode without actually deleting', async () => {
|
|
146
|
+
process.env.ATOMIX_DRY_RUN = 'true';
|
|
147
|
+
|
|
148
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
149
|
+
await writeFile(join(testDir, 'dist', 'test.txt'), 'content');
|
|
150
|
+
|
|
151
|
+
// Use base path as testDir since filesystem validates against cwd
|
|
152
|
+
const result = await cacheManager.deletePath(join(testDir, 'dist'), { skipValidation: true });
|
|
153
|
+
|
|
154
|
+
expect(result).toBe(true);
|
|
155
|
+
|
|
156
|
+
// Verify directory still exists
|
|
157
|
+
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
158
|
+
expect(exists).toBe('directory');
|
|
159
|
+
|
|
160
|
+
delete process.env.ATOMIX_DRY_RUN;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should actually delete a directory when not in dry-run mode', async () => {
|
|
164
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
165
|
+
await writeFile(join(testDir, 'dist', 'test.txt'), 'content');
|
|
166
|
+
|
|
167
|
+
const result = await cacheManager.deletePath(join(testDir, 'dist'), { skipValidation: true });
|
|
168
|
+
|
|
169
|
+
expect(result).toBe(true);
|
|
170
|
+
|
|
171
|
+
// Verify directory is deleted
|
|
172
|
+
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
173
|
+
expect(exists).toBe('unknown');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should refuse to delete protected files', async () => {
|
|
177
|
+
await writeFile(join(testDir, 'source.js'), 'console.log("hi")');
|
|
178
|
+
|
|
179
|
+
await expect(
|
|
180
|
+
cacheManager.deletePath(join(testDir, 'source.js'), { skipValidation: true })
|
|
181
|
+
).rejects.toThrow('Cannot delete protected source file');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('displayDryRun', () => {
|
|
186
|
+
it('should show message when nothing to clean', () => {
|
|
187
|
+
// This is mainly a visual test, we just ensure it doesn't throw
|
|
188
|
+
expect(() => {
|
|
189
|
+
cacheManager.displayDryRun([]);
|
|
190
|
+
}).not.toThrow();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should display list of files to be deleted', () => {
|
|
194
|
+
const targets = [
|
|
195
|
+
{ relativePath: 'dist', type: 'directory' },
|
|
196
|
+
{ relativePath: '.atomix', type: 'directory' }
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
expect(() => {
|
|
200
|
+
cacheManager.displayDryRun(targets);
|
|
201
|
+
}).not.toThrow();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('cleanAction', () => {
|
|
207
|
+
let testDir;
|
|
208
|
+
|
|
209
|
+
beforeEach(async () => {
|
|
210
|
+
testDir = await mkdtemp(join(tmpdir(), 'atomix-action-test-'));
|
|
211
|
+
process.chdir(testDir);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
afterEach(async () => {
|
|
215
|
+
await rm(testDir, { recursive: true, force: true });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle empty state gracefully', async () => {
|
|
219
|
+
const { cleanAction } = await import('../commands/clean.js');
|
|
220
|
+
|
|
221
|
+
// Should not throw when nothing to clean
|
|
222
|
+
await expect(cleanAction({})).resolves.not.toThrow();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should respect dry-run mode', async () => {
|
|
226
|
+
const { cleanAction } = await import('../commands/clean.js');
|
|
227
|
+
|
|
228
|
+
// Create test directory
|
|
229
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
230
|
+
|
|
231
|
+
// Run in dry-run mode
|
|
232
|
+
process.env.ATOMIX_DRY_RUN = 'true';
|
|
233
|
+
await cleanAction({ dryRun: true });
|
|
234
|
+
|
|
235
|
+
// Verify dist still exists
|
|
236
|
+
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
237
|
+
expect(exists).toBe('directory');
|
|
238
|
+
|
|
239
|
+
delete process.env.ATOMIX_DRY_RUN;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should clean created directories', async () => {
|
|
243
|
+
const { cleanAction } = await import('../commands/clean.js');
|
|
244
|
+
|
|
245
|
+
// Create test directories
|
|
246
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
247
|
+
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
248
|
+
|
|
249
|
+
// Run clean
|
|
250
|
+
await cleanAction({});
|
|
251
|
+
|
|
252
|
+
// Verify directories are deleted
|
|
253
|
+
const distExists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
254
|
+
const atomixExists = await cacheManager.getPathType(join(testDir, '.atomix'));
|
|
255
|
+
|
|
256
|
+
expect(distExists).toBe('unknown');
|
|
257
|
+
expect(atomixExists).toBe('unknown');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should preserve source files', async () => {
|
|
261
|
+
const { cleanAction } = await import('../commands/clean.js');
|
|
262
|
+
|
|
263
|
+
// Create mix of source and build files
|
|
264
|
+
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
265
|
+
await writeFile(join(testDir, 'src.js'), 'console.log("source")');
|
|
266
|
+
|
|
267
|
+
// Run clean
|
|
268
|
+
await cleanAction({});
|
|
269
|
+
|
|
270
|
+
// Verify source file preserved
|
|
271
|
+
const srcExists = await cacheManager.getPathType(join(testDir, 'src.js'));
|
|
272
|
+
expect(srcExists).toBe('file');
|
|
273
|
+
|
|
274
|
+
// Verify build deleted
|
|
275
|
+
const distExists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
276
|
+
expect(distExists).toBe('unknown');
|
|
277
|
+
});
|
|
278
|
+
});
|