@shohojdhara/atomix 0.3.4 → 0.3.5
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/dist/atomix.css +9 -10
- package/dist/atomix.css.map +1 -0
- package/dist/atomix.min.css +15108 -11
- package/dist/atomix.min.css.map +1 -0
- package/dist/charts.d.ts +1929 -0
- package/dist/charts.js +6482 -0
- package/dist/charts.js.map +1 -0
- package/dist/core.d.ts +1289 -0
- package/dist/core.js +3357 -0
- package/dist/core.js.map +1 -0
- package/dist/forms.d.ts +1085 -0
- package/dist/forms.js +2450 -0
- package/dist/forms.js.map +1 -0
- package/dist/heavy.d.ts +636 -0
- package/dist/heavy.js +4550 -0
- package/dist/heavy.js.map +1 -0
- package/dist/index.d.ts +5161 -4990
- package/dist/index.esm.js +1457 -784
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1473 -790
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.d.ts +300 -0
- package/dist/layout.js +336 -0
- package/dist/layout.js.map +1 -0
- package/dist/theme.d.ts +1992 -0
- package/dist/theme.js +5348 -0
- package/dist/theme.js.map +1 -0
- package/package.json +66 -20
- package/scripts/atomix-cli.js +544 -16
- package/scripts/cli/__tests__/cli-commands.test.js +204 -0
- package/scripts/cli/__tests__/utils.test.js +201 -0
- package/scripts/cli/__tests__/vitest.config.js +26 -0
- package/scripts/cli/interactive-init.js +1 -1
- package/scripts/cli/token-manager.js +32 -7
- package/scripts/cli/utils.js +347 -0
- package/src/components/Accordion/Accordion.tsx +5 -54
- package/src/components/Accordion/index.ts +1 -1
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Badge/Badge.tsx +3 -3
- package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
- package/src/components/Card/ElevationCard.tsx +1 -1
- package/src/components/Chart/AnimatedChart.tsx +19 -17
- package/src/components/Chart/AreaChart.tsx +5 -1
- package/src/components/Chart/BarChart.tsx +1 -0
- package/src/components/Chart/BubbleChart.tsx +6 -5
- package/src/components/Chart/ChartToolbar.tsx +1 -0
- package/src/components/Chart/FunnelChart.tsx +1 -1
- package/src/components/Chart/RadarChart.tsx +19 -12
- package/src/components/Chart/ScatterChart.tsx +3 -3
- package/src/components/Chart/TreemapChart.tsx +2 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -1
- package/src/components/Chart/types.ts +12 -2
- package/src/components/Chart/utils.ts +4 -3
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/Dropdown/Dropdown.tsx +12 -9
- package/src/components/Footer/FooterSection.tsx +3 -3
- package/src/components/Form/Checkbox.tsx +3 -3
- package/src/components/Form/Input.tsx +4 -2
- package/src/components/Form/Radio.tsx +3 -3
- package/src/components/Form/Select.tsx +3 -3
- package/src/components/Form/Textarea.tsx +4 -2
- package/src/components/List/List.stories.tsx +3 -3
- package/src/components/List/List.tsx +3 -3
- package/src/components/List/ListGroup.tsx +3 -1
- package/src/components/Modal/Modal.tsx +3 -3
- package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
- package/src/components/Navigation/Menu/Menu.tsx +9 -3
- package/src/components/Pagination/Pagination.tsx +6 -5
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
- package/src/components/Popover/Popover.tsx +4 -4
- package/src/components/Progress/Progress.tsx +6 -2
- package/src/components/Rating/Rating.tsx +5 -2
- package/src/components/Slider/Slider.tsx +10 -9
- package/src/components/Spinner/Spinner.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/index.ts +5 -2
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
- package/src/lib/composables/useChartPerformance.ts +102 -78
- package/src/lib/composables/useChartScale.ts +10 -0
- package/src/lib/composables/useHero.ts +9 -2
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
- package/src/lib/composables/useSideMenu.ts +1 -0
- package/src/lib/composables/useVideoPlayer.ts +3 -2
- package/src/lib/config/loader.ts +55 -13
- package/src/lib/hooks/index.ts +0 -1
- package/src/lib/hooks/useComponentCustomization.ts +10 -14
- package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
- package/src/lib/patterns/index.ts +2 -2
- package/src/lib/patterns/slots.tsx +2 -2
- package/src/lib/theme/composeTheme.ts +1 -1
- package/src/lib/theme/core/ThemeEngine.ts +8 -0
- package/src/lib/theme/core/ThemeValidator.ts +5 -2
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +11 -5
- package/src/lib/theme/generateCSSVariables.ts +1 -1
- package/src/lib/theme/i18n/rtl.ts +2 -1
- package/src/lib/theme/runtime/ThemeApplicator.ts +28 -11
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
- package/src/lib/theme/runtime/ThemeManager.ts +4 -0
- package/src/lib/theme-tools.ts +1 -1
- package/src/lib/types/components.ts +183 -34
- package/src/lib/types/partProps.ts +0 -16
- package/src/lib/utils/fontPreloader.ts +148 -0
- package/src/lib/utils/index.ts +11 -0
- package/src/lib/utils/memoryMonitor.ts +189 -0
- package/src/styles/01-settings/_settings.fonts.scss +2 -5
- package/src/styles/03-generic/_generated-root.css +22 -1
- package/src/styles/06-components/_components.navbar.scss +0 -6
- package/src/themes/themes.config.js +37 -4
- package/scripts/build-themes.js +0 -208
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { existsSync, mkdirSync, rmSync, readFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname } from 'path';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const CLI_PATH = join(__dirname, '../../atomix-cli.js');
|
|
11
|
+
const TEST_DIR = join(__dirname, 'test-output');
|
|
12
|
+
|
|
13
|
+
describe('Atomix CLI Commands', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create test directory
|
|
16
|
+
if (!existsSync(TEST_DIR)) {
|
|
17
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
// Clean up test directory
|
|
23
|
+
if (existsSync(TEST_DIR)) {
|
|
24
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('atomix --help', () => {
|
|
29
|
+
it('should display help information', () => {
|
|
30
|
+
const output = execSync(`node ${CLI_PATH} --help`).toString();
|
|
31
|
+
expect(output).toContain('Atomix Design System CLI');
|
|
32
|
+
expect(output).toContain('Commands:');
|
|
33
|
+
expect(output).toContain('build-theme');
|
|
34
|
+
expect(output).toContain('generate');
|
|
35
|
+
expect(output).toContain('validate');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('atomix --version', () => {
|
|
40
|
+
it('should display version number', () => {
|
|
41
|
+
const output = execSync(`node ${CLI_PATH} --version`).toString();
|
|
42
|
+
expect(output).toMatch(/\d+\.\d+\.\d+/);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('atomix generate component', () => {
|
|
47
|
+
const componentPath = join(TEST_DIR, 'TestComponent');
|
|
48
|
+
|
|
49
|
+
it('should create component files with valid name', () => {
|
|
50
|
+
execSync(`node ${CLI_PATH} generate component TestButton --path ${TEST_DIR}`, {
|
|
51
|
+
cwd: process.cwd()
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const buttonPath = join(TEST_DIR, 'TestButton');
|
|
55
|
+
|
|
56
|
+
// Check if files are created
|
|
57
|
+
expect(existsSync(join(buttonPath, 'TestButton.tsx'))).toBe(true);
|
|
58
|
+
expect(existsSync(join(buttonPath, 'index.ts'))).toBe(true);
|
|
59
|
+
expect(existsSync(join(buttonPath, '_testbutton.scss'))).toBe(true);
|
|
60
|
+
expect(existsSync(join(buttonPath, 'TestButton.stories.tsx'))).toBe(true);
|
|
61
|
+
|
|
62
|
+
// Check component content
|
|
63
|
+
const componentContent = readFileSync(join(buttonPath, 'TestButton.tsx'), 'utf8');
|
|
64
|
+
expect(componentContent).toContain('export const TestButton');
|
|
65
|
+
expect(componentContent).toContain('TestButtonProps');
|
|
66
|
+
expect(componentContent).toContain('forwardRef');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should reject invalid component names', () => {
|
|
70
|
+
expect(() => {
|
|
71
|
+
execSync(`node ${CLI_PATH} generate component test-button --path ${TEST_DIR}`, {
|
|
72
|
+
cwd: process.cwd()
|
|
73
|
+
});
|
|
74
|
+
}).toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should create test file when --test flag is used', () => {
|
|
78
|
+
execSync(`node ${CLI_PATH} generate component TestCard --test --path ${TEST_DIR}`, {
|
|
79
|
+
cwd: process.cwd()
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const cardPath = join(TEST_DIR, 'TestCard');
|
|
83
|
+
expect(existsSync(join(cardPath, 'TestCard.test.tsx'))).toBe(true);
|
|
84
|
+
|
|
85
|
+
const testContent = readFileSync(join(cardPath, 'TestCard.test.tsx'), 'utf8');
|
|
86
|
+
expect(testContent).toContain('describe(\'TestCard\'');
|
|
87
|
+
expect(testContent).toContain('vitest');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('atomix generate token', () => {
|
|
92
|
+
it('should generate color tokens', () => {
|
|
93
|
+
const settingsPath = join(TEST_DIR, 'src/styles/01-settings');
|
|
94
|
+
mkdirSync(settingsPath, { recursive: true });
|
|
95
|
+
|
|
96
|
+
execSync(`node ${CLI_PATH} generate token colors`, {
|
|
97
|
+
cwd: TEST_DIR
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const tokenFile = join(settingsPath, '_settings.colors.custom.scss');
|
|
101
|
+
expect(existsSync(tokenFile)).toBe(true);
|
|
102
|
+
|
|
103
|
+
const content = readFileSync(tokenFile, 'utf8');
|
|
104
|
+
expect(content).toContain('Custom Color Tokens');
|
|
105
|
+
expect(content).toContain('$custom-primary-6');
|
|
106
|
+
expect(content).toContain('$custom-success');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should reject invalid token categories', () => {
|
|
110
|
+
expect(() => {
|
|
111
|
+
execSync(`node ${CLI_PATH} generate token invalid-category`, {
|
|
112
|
+
cwd: TEST_DIR
|
|
113
|
+
});
|
|
114
|
+
}).toThrow();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('atomix validate', () => {
|
|
119
|
+
it('should validate tokens when --tokens flag is used', () => {
|
|
120
|
+
// This is a mock test - in real implementation, you'd set up token files
|
|
121
|
+
const output = execSync(`node ${CLI_PATH} validate --tokens`, {
|
|
122
|
+
cwd: process.cwd()
|
|
123
|
+
}).toString();
|
|
124
|
+
|
|
125
|
+
// Should complete without throwing
|
|
126
|
+
expect(output).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('atomix doctor', () => {
|
|
131
|
+
it('should run diagnostics successfully', () => {
|
|
132
|
+
const output = execSync(`node ${CLI_PATH} doctor`).toString();
|
|
133
|
+
expect(output).toContain('Atomix Doctor Report');
|
|
134
|
+
expect(output).toContain('Node.js Version');
|
|
135
|
+
expect(output).toContain('Atomix Installation');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('atomix theme create', () => {
|
|
140
|
+
it('should create CSS theme with valid name', () => {
|
|
141
|
+
const themesPath = join(TEST_DIR, 'themes');
|
|
142
|
+
mkdirSync(themesPath, { recursive: true });
|
|
143
|
+
|
|
144
|
+
execSync(`node ${CLI_PATH} theme create test-theme --output ${themesPath}`, {
|
|
145
|
+
cwd: process.cwd()
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const themePath = join(themesPath, 'test-theme');
|
|
149
|
+
expect(existsSync(join(themePath, 'index.scss'))).toBe(true);
|
|
150
|
+
expect(existsSync(join(themePath, 'README.md'))).toBe(true);
|
|
151
|
+
|
|
152
|
+
const themeContent = readFileSync(join(themePath, 'index.scss'), 'utf8');
|
|
153
|
+
expect(themeContent).toContain('data-theme="test-theme"');
|
|
154
|
+
expect(themeContent).toContain('--atomix-color-primary');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should create JavaScript theme when --type js is used', () => {
|
|
158
|
+
const themesPath = join(TEST_DIR, 'themes');
|
|
159
|
+
mkdirSync(themesPath, { recursive: true });
|
|
160
|
+
|
|
161
|
+
execSync(`node ${CLI_PATH} theme create js-theme --type js --output ${themesPath}`, {
|
|
162
|
+
cwd: process.cwd()
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const themePath = join(themesPath, 'js-theme');
|
|
166
|
+
expect(existsSync(join(themePath, 'index.ts'))).toBe(true);
|
|
167
|
+
|
|
168
|
+
const themeContent = readFileSync(join(themePath, 'index.ts'), 'utf8');
|
|
169
|
+
expect(themeContent).toContain('jsTheme');
|
|
170
|
+
expect(themeContent).toContain('createTheme');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should reject invalid theme names', () => {
|
|
174
|
+
expect(() => {
|
|
175
|
+
execSync(`node ${CLI_PATH} theme create InvalidTheme --output ${TEST_DIR}`, {
|
|
176
|
+
cwd: process.cwd()
|
|
177
|
+
});
|
|
178
|
+
}).toThrow();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('CLI Error Handling', () => {
|
|
184
|
+
it('should show suggestions for invalid commands', () => {
|
|
185
|
+
try {
|
|
186
|
+
execSync(`node ${CLI_PATH} invalidcommand`);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const output = error.stdout?.toString() || error.stderr?.toString();
|
|
189
|
+
expect(output).toContain('unknown command');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should handle missing required arguments', () => {
|
|
194
|
+
expect(() => {
|
|
195
|
+
execSync(`node ${CLI_PATH} generate`);
|
|
196
|
+
}).toThrow();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should respect --debug flag', () => {
|
|
200
|
+
const output = execSync(`node ${CLI_PATH} --debug --help`).toString();
|
|
201
|
+
// Debug mode should be enabled
|
|
202
|
+
expect(process.env.ATOMIX_DEBUG).toBe('true');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
validatePath,
|
|
4
|
+
validateComponentName,
|
|
5
|
+
validateThemeName,
|
|
6
|
+
sanitizeInput,
|
|
7
|
+
isValidColor,
|
|
8
|
+
checkNodeVersion,
|
|
9
|
+
formatFileSize
|
|
10
|
+
} from '../utils.js';
|
|
11
|
+
import { join, resolve } from 'path';
|
|
12
|
+
|
|
13
|
+
describe('CLI Utils', () => {
|
|
14
|
+
describe('validatePath', () => {
|
|
15
|
+
const basePath = '/home/user/project';
|
|
16
|
+
|
|
17
|
+
it('should validate safe paths within project', () => {
|
|
18
|
+
const result = validatePath('src/components', basePath);
|
|
19
|
+
expect(result.isValid).toBe(true);
|
|
20
|
+
expect(result.safePath).toBe(resolve(basePath, 'src/components'));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should reject paths outside project directory', () => {
|
|
24
|
+
const result = validatePath('../../etc/passwd', basePath);
|
|
25
|
+
expect(result.isValid).toBe(false);
|
|
26
|
+
expect(result.error).toContain('outside the project directory');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should reject paths to sensitive files', () => {
|
|
30
|
+
const sensitiveFiles = ['.env', '.git/config', 'private/key.pem', 'secret.key'];
|
|
31
|
+
|
|
32
|
+
sensitiveFiles.forEach(file => {
|
|
33
|
+
const result = validatePath(file, basePath);
|
|
34
|
+
expect(result.isValid).toBe(false);
|
|
35
|
+
expect(result.error).toContain('sensitive path');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should handle absolute paths correctly', () => {
|
|
40
|
+
const absolutePath = join(basePath, 'src/components');
|
|
41
|
+
const result = validatePath(absolutePath, basePath);
|
|
42
|
+
expect(result.isValid).toBe(true);
|
|
43
|
+
expect(result.safePath).toBe(absolutePath);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('validateComponentName', () => {
|
|
48
|
+
it('should accept valid PascalCase names', () => {
|
|
49
|
+
const validNames = ['Button', 'CardHeader', 'MyComponent123'];
|
|
50
|
+
|
|
51
|
+
validNames.forEach(name => {
|
|
52
|
+
const result = validateComponentName(name);
|
|
53
|
+
expect(result.isValid).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should reject invalid component names', () => {
|
|
58
|
+
const invalidNames = [
|
|
59
|
+
'button', // lowercase
|
|
60
|
+
'Button-Test', // contains hyphen
|
|
61
|
+
'Button_Test', // contains underscore
|
|
62
|
+
'123Button', // starts with number
|
|
63
|
+
'A', // too short
|
|
64
|
+
'', // empty
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
invalidNames.forEach(name => {
|
|
68
|
+
const result = validateComponentName(name);
|
|
69
|
+
expect(result.isValid).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should reject reserved words', () => {
|
|
74
|
+
const reserved = ['Component', 'React', 'Fragment'];
|
|
75
|
+
|
|
76
|
+
reserved.forEach(name => {
|
|
77
|
+
const result = validateComponentName(name);
|
|
78
|
+
expect(result.isValid).toBe(false);
|
|
79
|
+
expect(result.error).toContain('reserved word');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('validateThemeName', () => {
|
|
85
|
+
it('should accept valid kebab-case names', () => {
|
|
86
|
+
const validNames = ['dark-theme', 'light-mode', 'custom-theme-2'];
|
|
87
|
+
|
|
88
|
+
validNames.forEach(name => {
|
|
89
|
+
const result = validateThemeName(name);
|
|
90
|
+
expect(result.isValid).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should reject invalid theme names', () => {
|
|
95
|
+
const invalidNames = [
|
|
96
|
+
'DarkTheme', // PascalCase
|
|
97
|
+
'dark_theme', // underscore
|
|
98
|
+
'123-theme', // starts with number
|
|
99
|
+
'theme-', // ends with hyphen
|
|
100
|
+
'theme--dark', // consecutive hyphens
|
|
101
|
+
'', // empty
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
invalidNames.forEach(name => {
|
|
105
|
+
const result = validateThemeName(name);
|
|
106
|
+
expect(result.isValid).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('sanitizeInput', () => {
|
|
112
|
+
it('should remove dangerous shell characters', () => {
|
|
113
|
+
const dangerous = 'test; rm -rf /';
|
|
114
|
+
const sanitized = sanitizeInput(dangerous);
|
|
115
|
+
expect(sanitized).toBe('test rm -rf /');
|
|
116
|
+
expect(sanitized).not.toContain(';');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should remove multiple dangerous characters', () => {
|
|
120
|
+
const input = 'cmd1 && cmd2 | cmd3 `evil` $var > file < input \\ ';
|
|
121
|
+
const sanitized = sanitizeInput(input);
|
|
122
|
+
expect(sanitized).toBe('cmd1 cmd2 cmd3 evil var file input');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle non-string inputs', () => {
|
|
126
|
+
expect(sanitizeInput(123)).toBe('123');
|
|
127
|
+
expect(sanitizeInput(null)).toBe('null');
|
|
128
|
+
expect(sanitizeInput(undefined)).toBe('undefined');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('isValidColor', () => {
|
|
133
|
+
it('should validate hex colors', () => {
|
|
134
|
+
const validHex = ['#FFF', '#FFFF', '#FFFFFF', '#FFFFFFFF', '#abc123'];
|
|
135
|
+
|
|
136
|
+
validHex.forEach(color => {
|
|
137
|
+
expect(isValidColor(color)).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should validate CSS color functions', () => {
|
|
142
|
+
const validFunctions = [
|
|
143
|
+
'rgb(255, 255, 255)',
|
|
144
|
+
'rgba(0, 0, 0, 0.5)',
|
|
145
|
+
'hsl(120, 100%, 50%)',
|
|
146
|
+
'hsla(240, 100%, 50%, 0.3)',
|
|
147
|
+
'var(--atomix-color-primary)',
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
validFunctions.forEach(color => {
|
|
151
|
+
expect(isValidColor(color)).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should reject invalid colors', () => {
|
|
156
|
+
const invalid = ['#GGG', '#12', 'red', '255,255,255', 'notacolor'];
|
|
157
|
+
|
|
158
|
+
invalid.forEach(color => {
|
|
159
|
+
expect(isValidColor(color)).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('checkNodeVersion', () => {
|
|
165
|
+
it('should check Node version compatibility', () => {
|
|
166
|
+
const currentVersion = process.version.substring(1);
|
|
167
|
+
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
|
168
|
+
|
|
169
|
+
// Should pass for current version
|
|
170
|
+
const result1 = checkNodeVersion(currentVersion);
|
|
171
|
+
expect(result1.compatible).toBe(true);
|
|
172
|
+
|
|
173
|
+
// Should fail for higher version
|
|
174
|
+
const higherVersion = `${major + 1}.0.0`;
|
|
175
|
+
const result2 = checkNodeVersion(higherVersion);
|
|
176
|
+
expect(result2.compatible).toBe(false);
|
|
177
|
+
|
|
178
|
+
// Should pass for lower version
|
|
179
|
+
if (major > 0) {
|
|
180
|
+
const lowerVersion = `${major - 1}.0.0`;
|
|
181
|
+
const result3 = checkNodeVersion(lowerVersion);
|
|
182
|
+
expect(result3.compatible).toBe(true);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('formatFileSize', () => {
|
|
188
|
+
it('should format file sizes correctly', () => {
|
|
189
|
+
expect(formatFileSize(0)).toBe('0 B');
|
|
190
|
+
expect(formatFileSize(512)).toBe('512.00 B');
|
|
191
|
+
expect(formatFileSize(1024)).toBe('1.00 KB');
|
|
192
|
+
expect(formatFileSize(1048576)).toBe('1.00 MB');
|
|
193
|
+
expect(formatFileSize(1073741824)).toBe('1.00 GB');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should handle decimal values', () => {
|
|
197
|
+
expect(formatFileSize(1536)).toBe('1.50 KB'); // 1.5 KB
|
|
198
|
+
expect(formatFileSize(2621440)).toBe('2.50 MB'); // 2.5 MB
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: 'v8',
|
|
9
|
+
reporter: ['text', 'json', 'html'],
|
|
10
|
+
exclude: [
|
|
11
|
+
'node_modules/**',
|
|
12
|
+
'**/__tests__/**',
|
|
13
|
+
'**/test-output/**',
|
|
14
|
+
],
|
|
15
|
+
include: [
|
|
16
|
+
'../*.js',
|
|
17
|
+
],
|
|
18
|
+
thresholds: {
|
|
19
|
+
lines: 80,
|
|
20
|
+
functions: 80,
|
|
21
|
+
branches: 80,
|
|
22
|
+
statements: 80,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
7
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
10
|
import boxen from 'boxen';
|
|
@@ -433,13 +433,38 @@ export async function importTokens(filePath, options = {}) {
|
|
|
433
433
|
|
|
434
434
|
case 'js':
|
|
435
435
|
case 'ts':
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
436
|
+
// Parse tokens safely without eval
|
|
437
|
+
try {
|
|
438
|
+
// Try to parse as JSON first (common case)
|
|
439
|
+
const jsonMatch = content.match(/export\s+(?:const|default)\s+\w*\s*=\s*({[\s\S]*})/);
|
|
440
|
+
if (jsonMatch) {
|
|
441
|
+
// Extract the object literal and clean it up
|
|
442
|
+
let objectStr = jsonMatch[1];
|
|
443
|
+
|
|
444
|
+
// Remove trailing semicolon if present
|
|
445
|
+
objectStr = objectStr.replace(/;\s*$/, '');
|
|
446
|
+
|
|
447
|
+
// Try direct JSON parsing first
|
|
448
|
+
try {
|
|
449
|
+
tokens = JSON.parse(objectStr);
|
|
450
|
+
} catch {
|
|
451
|
+
// If that fails, try to convert JS object to JSON
|
|
452
|
+
// This handles single quotes and unquoted keys
|
|
453
|
+
objectStr = objectStr
|
|
454
|
+
// Replace single quotes with double quotes
|
|
455
|
+
.replace(/'/g, '"')
|
|
456
|
+
// Add quotes to unquoted keys
|
|
457
|
+
.replace(/(\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
|
|
458
|
+
// Handle trailing commas
|
|
459
|
+
.replace(/,(\s*[}\]])/g, '$1');
|
|
460
|
+
|
|
461
|
+
tokens = JSON.parse(objectStr);
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
throw new Error('Could not find token export in JavaScript/TypeScript file');
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
throw new Error(`Could not parse JavaScript/TypeScript tokens: ${error.message}`);
|
|
443
468
|
}
|
|
444
469
|
break;
|
|
445
470
|
|