@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,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Type Templates
|
|
3
|
+
* Templates for generating TypeScript type definitions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates TypeScript type definitions for a component
|
|
8
|
+
*/
|
|
9
|
+
export const typesTemplate = (name: string): string => `import { ReactNode, HTMLAttributes, AriaAttributes } from 'react';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Size variants for components
|
|
13
|
+
*/
|
|
14
|
+
export type ${name}Size = 'sm' | 'md' | 'lg';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Color variants for components
|
|
18
|
+
*/
|
|
19
|
+
export type ${name}Variant = 'primary' | 'secondary' | 'success' | 'error' | 'warning';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Glass effect configuration
|
|
23
|
+
*/
|
|
24
|
+
export interface GlassConfig {
|
|
25
|
+
displacementScale?: number;
|
|
26
|
+
blurAmount?: number;
|
|
27
|
+
saturation?: number;
|
|
28
|
+
elasticity?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Props for the ${name} component
|
|
33
|
+
*/
|
|
34
|
+
export interface ${name}Props extends HTMLAttributes<HTMLDivElement>, AriaAttributes {
|
|
35
|
+
/** Content to be rendered inside the component */
|
|
36
|
+
children?: ReactNode;
|
|
37
|
+
|
|
38
|
+
/** Additional CSS classes */
|
|
39
|
+
className?: string;
|
|
40
|
+
|
|
41
|
+
/** Size variant */
|
|
42
|
+
size?: ${name}Size;
|
|
43
|
+
|
|
44
|
+
/** Color variant */
|
|
45
|
+
variant?: ${name}Variant;
|
|
46
|
+
|
|
47
|
+
/** Whether the component is disabled */
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
|
|
50
|
+
/** Whether to apply glass morphism effect */
|
|
51
|
+
glass?: boolean | GlassConfig;
|
|
52
|
+
|
|
53
|
+
/** Click handler */
|
|
54
|
+
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
55
|
+
|
|
56
|
+
/** Hover handler */
|
|
57
|
+
onHover?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
58
|
+
|
|
59
|
+
/** Focus handler */
|
|
60
|
+
onFocus?: (event: React.FocusEvent<HTMLDivElement>) => void;
|
|
61
|
+
|
|
62
|
+
/** Blur handler */
|
|
63
|
+
onBlur?: (event: React.FocusEvent<HTMLDivElement>) => void;
|
|
64
|
+
|
|
65
|
+
/** State change handler */
|
|
66
|
+
onStateChange?: (state: ${name}State) => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* State interface for ${name} component
|
|
71
|
+
*/
|
|
72
|
+
export interface ${name}State {
|
|
73
|
+
isOpen?: boolean;
|
|
74
|
+
isActive?: boolean;
|
|
75
|
+
isSelected?: boolean;
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Generates constants file for a component
|
|
81
|
+
*/
|
|
82
|
+
export const constantsTemplate = (name: string): string => `/**
|
|
83
|
+
* ${name} Component Constants
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
export const ${name.toUpperCase()} = {
|
|
87
|
+
/** Base CSS class name */
|
|
88
|
+
BASE_CLASS: 'c-${name.toLowerCase()}',
|
|
89
|
+
|
|
90
|
+
/** Class name prefixes */
|
|
91
|
+
PREFIX: 'c-${name.toLowerCase()}--',
|
|
92
|
+
|
|
93
|
+
/** Element prefixes */
|
|
94
|
+
ELEMENT_PREFIX: '${name.toLowerCase()}__',
|
|
95
|
+
|
|
96
|
+
/** Available sizes */
|
|
97
|
+
SIZES: ['sm', 'md', 'lg'] as const,
|
|
98
|
+
|
|
99
|
+
/** Available variants */
|
|
100
|
+
VARIANTS: ['primary', 'secondary', 'success', 'error', 'warning'] as const,
|
|
101
|
+
|
|
102
|
+
/** Default props */
|
|
103
|
+
DEFAULTS: {
|
|
104
|
+
SIZE: 'md' as const,
|
|
105
|
+
VARIANT: 'primary' as const,
|
|
106
|
+
DISABLED: false,
|
|
107
|
+
GLASS: false,
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/** CSS class names */
|
|
111
|
+
CLASSES: {
|
|
112
|
+
LOADING: 'is-loading',
|
|
113
|
+
DISABLED: 'is-disabled',
|
|
114
|
+
ACTIVE: 'is-active',
|
|
115
|
+
SELECTED: 'is-selected',
|
|
116
|
+
OPEN: 'is-open',
|
|
117
|
+
CLOSED: 'is-closed',
|
|
118
|
+
FOCUS: 'has-focus',
|
|
119
|
+
GLASS: 'has-glass',
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/** Data attributes */
|
|
123
|
+
DATA: {
|
|
124
|
+
STATE: 'data-state',
|
|
125
|
+
DISABLED: 'aria-disabled',
|
|
126
|
+
LOADING: 'aria-busy',
|
|
127
|
+
},
|
|
128
|
+
} as const;
|
|
129
|
+
|
|
130
|
+
export type ${name}Sizes = typeof ${name.toUpperCase()}.SIZES[number];
|
|
131
|
+
export type ${name}Variants = typeof ${name.toUpperCase()}.VARIANTS[number];
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* All type templates
|
|
136
|
+
*/
|
|
137
|
+
export const componentTypeTemplates = {
|
|
138
|
+
types: typesTemplate,
|
|
139
|
+
constants: constantsTemplate,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Type for component type templates object
|
|
144
|
+
*/
|
|
145
|
+
export type ComponentTypeTemplates = typeof componentTypeTemplates;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing Utilities Templates
|
|
3
|
+
* Templates for generating test utilities and helpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates a test utilities file with common helpers
|
|
8
|
+
*/
|
|
9
|
+
export const testingUtilsTemplate = (): string => `import { render, RenderOptions, screen, waitFor } from '@testing-library/react';
|
|
10
|
+
import { ReactElement, ReactNode } from 'react';
|
|
11
|
+
import userEvent from '@testing-library/user-event';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Custom render options
|
|
15
|
+
*/
|
|
16
|
+
interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
|
|
17
|
+
wrapper?: React.ComponentType<{ children: ReactElement }>;
|
|
18
|
+
providerProps?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Custom render function with Atomix providers
|
|
23
|
+
*/
|
|
24
|
+
export function renderWithProviders(
|
|
25
|
+
ui: ReactElement,
|
|
26
|
+
{ wrapper: Wrapper, providerProps, ...renderOptions }: CustomRenderOptions = {}
|
|
27
|
+
) {
|
|
28
|
+
function WrapperComponent({ children }: { children: ReactElement }) {
|
|
29
|
+
return Wrapper ? <Wrapper {...providerProps}>{children}</Wrapper> : children;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return render(ui, { wrapper: WrapperComponent, ...renderOptions });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Mock IntersectionObserver
|
|
37
|
+
*/
|
|
38
|
+
export const mockIntersectionObserver = () => {
|
|
39
|
+
global.IntersectionObserver = class IntersectionObserver {
|
|
40
|
+
observe = vi.fn();
|
|
41
|
+
unobserve = vi.fn();
|
|
42
|
+
disconnect = vi.fn();
|
|
43
|
+
} as any;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Mock ResizeObserver
|
|
48
|
+
*/
|
|
49
|
+
export const mockResizeObserver = () => {
|
|
50
|
+
global.ResizeObserver = class ResizeObserver {
|
|
51
|
+
observe = vi.fn();
|
|
52
|
+
unobserve = vi.fn();
|
|
53
|
+
disconnect = vi.fn();
|
|
54
|
+
} as any;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wait for component to be ready
|
|
59
|
+
*/
|
|
60
|
+
export const waitForReady = async (timeout = 100) => {
|
|
61
|
+
await waitFor(() => expect(screen.getByRole('document')).toBeInTheDocument(), { timeout });
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a mock event
|
|
66
|
+
*/
|
|
67
|
+
export const createMockEvent = (type: string, data: any = {}) => {
|
|
68
|
+
return new Event(type, { bubbles: true, cancelable: true, ...data });
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Simulate keyboard events
|
|
73
|
+
*/
|
|
74
|
+
export const simulateKeyboard = async (element: HTMLElement, key: string) => {
|
|
75
|
+
const user = userEvent.setup();
|
|
76
|
+
await user.keyboard(key);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Mock CSS custom properties
|
|
81
|
+
*/
|
|
82
|
+
export const mockCSSCustomProperties = () => {
|
|
83
|
+
Object.defineProperty(document.documentElement.style, '--test-property', {
|
|
84
|
+
value: 'test-value',
|
|
85
|
+
writable: true,
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reset all mocks
|
|
91
|
+
*/
|
|
92
|
+
export const resetMocks = () => {
|
|
93
|
+
vi.clearAllMocks();
|
|
94
|
+
vi.resetAllMocks();
|
|
95
|
+
vi.restoreAllMocks();
|
|
96
|
+
};
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generates a test setup file
|
|
101
|
+
*/
|
|
102
|
+
export const testSetupTemplate = (): string => `import '@testing-library/jest-dom';
|
|
103
|
+
import { cleanup } from '@testing-library/react';
|
|
104
|
+
import { afterEach, vi } from 'vitest';
|
|
105
|
+
|
|
106
|
+
// Cleanup after each test
|
|
107
|
+
afterEach(() => {
|
|
108
|
+
cleanup();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Mock window.matchMedia
|
|
112
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
113
|
+
writable: true,
|
|
114
|
+
value: vi.fn().mockImplementation(query => ({
|
|
115
|
+
matches: false,
|
|
116
|
+
media: query,
|
|
117
|
+
onchange: null,
|
|
118
|
+
addListener: vi.fn(),
|
|
119
|
+
removeListener: vi.fn(),
|
|
120
|
+
addEventListener: vi.fn(),
|
|
121
|
+
removeEventListener: vi.fn(),
|
|
122
|
+
dispatchEvent: vi.fn(),
|
|
123
|
+
})),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Mock requestAnimationFrame
|
|
127
|
+
global.requestAnimationFrame = (callback) => setTimeout(callback, 0);
|
|
128
|
+
|
|
129
|
+
// Mock cancelAnimationFrame
|
|
130
|
+
global.cancelAnimationFrame = (id) => clearTimeout(id);
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* All testing utilities templates
|
|
135
|
+
*/
|
|
136
|
+
export const testingUtilsTemplates = {
|
|
137
|
+
utils: testingUtilsTemplate,
|
|
138
|
+
setup: testSetupTemplate,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Type for testing utilities templates object
|
|
143
|
+
*/
|
|
144
|
+
export type TestingUtilsTemplates = typeof testingUtilsTemplates;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanilla Component Templates
|
|
3
|
+
* Pure HTML/CSS/JS snippets
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const vanillaTemplates = {
|
|
7
|
+
/**
|
|
8
|
+
* Simple HTML Snippet
|
|
9
|
+
*/
|
|
10
|
+
simple: (name) => `<div class="${name.toLowerCase()}">
|
|
11
|
+
<h1>${name} Component</h1>
|
|
12
|
+
</div>
|
|
13
|
+
`,
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Full Vanilla Component (HTML + JS)
|
|
17
|
+
*/
|
|
18
|
+
component: (name) => `<div class="${name.toLowerCase()}" id="${name.toLowerCase()}-root">
|
|
19
|
+
<h1>${name} Component</h1>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
(function() {
|
|
24
|
+
const root = document.getElementById('${name.toLowerCase()}-root');
|
|
25
|
+
console.log('${name} initialized');
|
|
26
|
+
})();
|
|
27
|
+
</script>
|
|
28
|
+
`,
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* CSS Snippet
|
|
32
|
+
*/
|
|
33
|
+
styles: (name) => `.${name.toLowerCase()} {
|
|
34
|
+
display: block;
|
|
35
|
+
padding: 1rem;
|
|
36
|
+
border: 1px solid var(--atomix-border-color, #ccc);
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
};
|
|
@@ -280,7 +280,7 @@ export async function listTokens(categories = Object.keys(tokenCategories)) {
|
|
|
280
280
|
// Show summary
|
|
281
281
|
if (categoryCount === 0) {
|
|
282
282
|
console.log(safeChalkCall(chalk, 'yellow', '\n⚠️ No design tokens found'));
|
|
283
|
-
console.log(safeChalkCall(chalk, 'gray', '
|
|
283
|
+
console.log(safeChalkCall(chalk, 'gray', 'Create a theme or add token files in src/styles/01-settings (e.g. _settings.colors.scss). See Atomix token docs.'));
|
|
284
284
|
} else {
|
|
285
285
|
console.log(safeChalkCall(chalk, 'bold.cyan', '\n📐 Design Tokens\n'));
|
|
286
286
|
|
|
@@ -430,6 +430,13 @@ export async function exportTokens(format = 'json', outputPath = null) {
|
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
const categoryCount = Object.keys(allTokens).length;
|
|
434
|
+
if (categoryCount === 0) {
|
|
435
|
+
safeSpinnerCall(spinner, 'warn', safeChalkCall(chalk, 'yellow', 'No design tokens found'));
|
|
436
|
+
console.log(safeChalkCall(chalk, 'gray', 'Create a theme or add token files in src/styles/01-settings. See Atomix token docs.'));
|
|
437
|
+
return { path: null, tokens: allTokens };
|
|
438
|
+
}
|
|
439
|
+
|
|
433
440
|
let output;
|
|
434
441
|
let filename;
|
|
435
442
|
|
|
@@ -501,7 +508,6 @@ export async function exportTokens(format = 'json', outputPath = null) {
|
|
|
501
508
|
safeSpinnerCall(spinner, 'succeed', safeChalkCall(chalk, 'green', `✓ Exported tokens to ${finalPath}`));
|
|
502
509
|
|
|
503
510
|
// Show summary
|
|
504
|
-
const categoryCount = Object.keys(allTokens).length;
|
|
505
511
|
const tokenCount = Object.values(allTokens).reduce(
|
|
506
512
|
(sum, cat) => sum + Object.keys(cat).length,
|
|
507
513
|
0
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Cache Manager
|
|
3
|
+
* Utilities for identifying and managing cache files and build artifacts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdir, stat, rm } from 'fs/promises';
|
|
7
|
+
import { join, extname } from 'path';
|
|
8
|
+
import { filesystem } from '../internal/filesystem.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
|
|
12
|
+
// File extensions that should NEVER be deleted (source files)
|
|
13
|
+
const PROTECTED_EXTENSIONS = new Set([
|
|
14
|
+
'.js', '.jsx', '.ts', '.tsx',
|
|
15
|
+
'.scss', '.css', '.sass',
|
|
16
|
+
'.json', '.md', '.html',
|
|
17
|
+
'.yml', '.yaml'
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
// Default directories to clean
|
|
21
|
+
const DEFAULT_CLEAN_TARGETS = [
|
|
22
|
+
'dist',
|
|
23
|
+
'.atomix',
|
|
24
|
+
'node_modules/.cache'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Additional targets for --all flag
|
|
28
|
+
const ALL_CLEAN_TARGETS = [
|
|
29
|
+
...DEFAULT_CLEAN_TARGETS,
|
|
30
|
+
'node_modules'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Cache Manager - Safe file cleanup utilities
|
|
35
|
+
*/
|
|
36
|
+
export const cacheManager = {
|
|
37
|
+
/**
|
|
38
|
+
* Identify files and directories that can be safely cleaned
|
|
39
|
+
* @param {object} options - Clean options
|
|
40
|
+
* @returns {Promise<string[]>} List of paths to clean
|
|
41
|
+
*/
|
|
42
|
+
async identifyTargets(options = {}) {
|
|
43
|
+
const targets = [];
|
|
44
|
+
const cleanScope = options.all ? ALL_CLEAN_TARGETS : DEFAULT_CLEAN_TARGETS;
|
|
45
|
+
|
|
46
|
+
for (const target of cleanScope) {
|
|
47
|
+
const fullPath = join(process.cwd(), target);
|
|
48
|
+
const exists = await filesystem.exists(fullPath);
|
|
49
|
+
|
|
50
|
+
if (exists) {
|
|
51
|
+
targets.push({
|
|
52
|
+
path: fullPath,
|
|
53
|
+
relativePath: target,
|
|
54
|
+
type: await this.getPathType(fullPath)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add log files if not in cache-only mode
|
|
60
|
+
if (!options.cache) {
|
|
61
|
+
const logFiles = await this.findLogFiles();
|
|
62
|
+
targets.push(...logFiles);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return targets;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Determine if a path is a file or directory
|
|
70
|
+
* @param {string} path - Path to check
|
|
71
|
+
* @returns {Promise<'file'|'directory'>}
|
|
72
|
+
*/
|
|
73
|
+
async getPathType(path) {
|
|
74
|
+
try {
|
|
75
|
+
const stats = await stat(path);
|
|
76
|
+
return stats.isDirectory() ? 'directory' : 'file';
|
|
77
|
+
} catch {
|
|
78
|
+
return 'unknown';
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Find all log files in the project
|
|
84
|
+
* @returns {Promise<Array>} List of log file paths
|
|
85
|
+
*/
|
|
86
|
+
async findLogFiles() {
|
|
87
|
+
const logFiles = [];
|
|
88
|
+
const root = process.cwd();
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
92
|
+
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
if (entry.isFile() && extname(entry.name) === '.log') {
|
|
95
|
+
logFiles.push({
|
|
96
|
+
path: join(root, entry.name),
|
|
97
|
+
relativePath: entry.name,
|
|
98
|
+
type: 'file'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.debug('Error scanning for log files:', error.message);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return logFiles;
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if a file extension is protected (source file)
|
|
111
|
+
* @param {string} filePath - Path to check
|
|
112
|
+
* @returns {boolean} True if protected
|
|
113
|
+
*/
|
|
114
|
+
isProtected(filePath) {
|
|
115
|
+
const ext = extname(filePath);
|
|
116
|
+
return PROTECTED_EXTENSIONS.has(ext);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Safely delete a file or directory
|
|
121
|
+
* @param {string} path - Path to delete
|
|
122
|
+
* @param {object} options - Delete options
|
|
123
|
+
* @param {boolean} options.skipValidation - Skip path validation (for tests)
|
|
124
|
+
*/
|
|
125
|
+
async deletePath(path, options = {}) {
|
|
126
|
+
// Safety check: never delete protected files
|
|
127
|
+
if (this.isProtected(path)) {
|
|
128
|
+
throw new Error(`Cannot delete protected source file: ${path}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate path is within project (unless skipping for tests)
|
|
132
|
+
if (!options.skipValidation) {
|
|
133
|
+
const validation = filesystem.validatePath(path);
|
|
134
|
+
if (!validation.isValid) {
|
|
135
|
+
throw new Error(`Invalid path: ${validation.error}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (process.env.ATOMIX_DRY_RUN === 'true') {
|
|
140
|
+
logger.info(`[DRY RUN] Would delete: ${path}`);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await rm(path, {
|
|
146
|
+
recursive: true,
|
|
147
|
+
force: true,
|
|
148
|
+
maxRetries: 3,
|
|
149
|
+
retryDelay: 100
|
|
150
|
+
});
|
|
151
|
+
return true;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`Failed to delete ${path}: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Calculate total size of files/directories
|
|
159
|
+
* @param {Array} targets - List of target paths
|
|
160
|
+
* @returns {Promise<number>} Total size in bytes
|
|
161
|
+
*/
|
|
162
|
+
async calculateSize(targets) {
|
|
163
|
+
let totalSize = 0;
|
|
164
|
+
|
|
165
|
+
const calculateDirSize = async (dirPath) => {
|
|
166
|
+
try {
|
|
167
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
168
|
+
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
const fullPath = join(dirPath, entry.name);
|
|
171
|
+
|
|
172
|
+
if (entry.isDirectory()) {
|
|
173
|
+
totalSize += await calculateDirSize(fullPath);
|
|
174
|
+
} else {
|
|
175
|
+
const stats = await stat(fullPath);
|
|
176
|
+
totalSize += stats.size;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.debug('Error calculating size:', error.message);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return totalSize;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
for (const target of targets) {
|
|
187
|
+
if (target.type === 'directory') {
|
|
188
|
+
await calculateDirSize(target.path);
|
|
189
|
+
} else {
|
|
190
|
+
const stats = await stat(target.path);
|
|
191
|
+
totalSize += stats.size;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return totalSize;
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Format bytes to human-readable string
|
|
200
|
+
* @param {number} bytes - Size in bytes
|
|
201
|
+
* @returns {string} Formatted size
|
|
202
|
+
*/
|
|
203
|
+
formatBytes(bytes) {
|
|
204
|
+
if (bytes === 0) return '0 Bytes';
|
|
205
|
+
|
|
206
|
+
const k = 1024;
|
|
207
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
208
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
209
|
+
|
|
210
|
+
// Handle edge case where size is larger than TB
|
|
211
|
+
const unit = sizes[i] || 'PB';
|
|
212
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + unit;
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Display dry-run preview of what would be cleaned
|
|
217
|
+
* @param {Array} targets - Files/dirs to clean
|
|
218
|
+
*/
|
|
219
|
+
displayDryRun(targets) {
|
|
220
|
+
if (targets.length === 0) {
|
|
221
|
+
logger.box('✨ Nothing to clean! Your project is already tidy.', {
|
|
222
|
+
borderColor: 'green'
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const fileList = targets
|
|
228
|
+
.map(t => ` • ${t.relativePath} (${t.type})`)
|
|
229
|
+
.join('\n');
|
|
230
|
+
|
|
231
|
+
logger.box(
|
|
232
|
+
`⚠️ Dry Run Mode - The following would be deleted:\n\n${fileList}`,
|
|
233
|
+
{
|
|
234
|
+
borderColor: 'yellow'
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
logger.info(`\nTotal: ${targets.length} item(s)\n`);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix CLI Project Detector
|
|
3
|
+
* Automatically detects the framework environment (React, Next.js, Vanilla)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from 'fs/promises';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect the framework from package.json and project structure
|
|
12
|
+
* @param {string} projectRoot - The root directory of the project
|
|
13
|
+
* @returns {Promise<'react' | 'next' | 'vanilla'>}
|
|
14
|
+
*/
|
|
15
|
+
export async function detectFramework(projectRoot = process.cwd()) {
|
|
16
|
+
try {
|
|
17
|
+
const packageJsonPath = join(projectRoot, 'package.json');
|
|
18
|
+
|
|
19
|
+
if (!existsSync(packageJsonPath)) {
|
|
20
|
+
return 'vanilla';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
|
24
|
+
const allDeps = {
|
|
25
|
+
...(packageJson.dependencies || {}),
|
|
26
|
+
...(packageJson.devDependencies || {})
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (allDeps.next) {
|
|
30
|
+
return 'next';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (allDeps.react) {
|
|
34
|
+
return 'react';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for framework specific files
|
|
38
|
+
if (existsSync(join(projectRoot, 'next.config.js')) || existsSync(join(projectRoot, 'next.config.mjs'))) {
|
|
39
|
+
return 'next';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return 'vanilla';
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return 'vanilla';
|
|
45
|
+
}
|
|
46
|
+
}
|