create-template-html-css 2.0.4 → 2.2.0

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +436 -0
  2. package/CODE-SPLITTING-GUIDE.md +274 -0
  3. package/COMPONENTS-GALLERY.html +143 -8
  4. package/HTML-VS-REACT.md +289 -0
  5. package/QUICKSTART-REACT.md +293 -0
  6. package/REACT-SUPPORT-SUMMARY.md +235 -0
  7. package/README.md +261 -12
  8. package/bin/cli.js +100 -759
  9. package/bin/commands/create.js +288 -0
  10. package/bin/commands/gallery.js +42 -0
  11. package/bin/commands/insert.js +123 -0
  12. package/bin/commands/list.js +73 -0
  13. package/package.json +10 -3
  14. package/src/component-choices.js +7 -0
  15. package/src/components-registry.js +112 -0
  16. package/src/format-utils.js +49 -0
  17. package/src/generator.js +83 -594
  18. package/src/generators/color-schemes.js +78 -0
  19. package/src/generators/color-utils.js +108 -0
  20. package/src/generators/component-filters.js +151 -0
  21. package/src/generators/html-generators.js +180 -0
  22. package/src/generators/validation.js +43 -0
  23. package/src/index.js +2 -1
  24. package/src/inserter.js +55 -233
  25. package/src/inserters/backup-utils.js +20 -0
  26. package/src/inserters/component-loader.js +68 -0
  27. package/src/inserters/html-utils.js +31 -0
  28. package/src/inserters/indentation-utils.js +90 -0
  29. package/src/inserters/validation-utils.js +49 -0
  30. package/src/react-component-choices.js +97 -0
  31. package/src/react-component-templates.js +182 -0
  32. package/src/react-file-operations.js +172 -0
  33. package/src/react-generator.js +219 -0
  34. package/src/react-templates.js +418 -0
  35. package/src/templates/basic-components-templates.js +157 -0
  36. package/src/templates/form-components-templates.js +194 -0
  37. package/src/templates/interactive-components-templates.js +139 -0
  38. package/src/utils/file-utils.js +97 -0
  39. package/src/utils/path-utils.js +32 -0
  40. package/src/utils/string-utils.js +51 -0
  41. package/src/utils/template-loader.js +91 -0
  42. package/templates/_shared/PATTERNS.md +246 -0
  43. package/templates/_shared/README.md +74 -0
  44. package/templates/_shared/base.css +18 -0
  45. package/templates/blackjack/index.html +1 -1
  46. package/templates/breakout/index.html +1 -1
  47. package/templates/connect-four/index.html +1 -1
  48. package/templates/dice-game/index.html +1 -1
  49. package/templates/flappy-bird/index.html +1 -1
  50. package/templates/pong/index.html +1 -1
  51. package/templates/skeleton/index.html +4 -4
  52. package/templates/slot-machine/index.html +1 -1
  53. package/templates/tetris/index.html +1 -1
  54. package/templates-react/README.md +126 -0
  55. package/templates-react/alert/Alert.css +158 -0
  56. package/templates-react/alert/Alert.example.jsx +106 -0
  57. package/templates-react/alert/Alert.jsx +61 -0
  58. package/templates-react/badge/Badge.css +196 -0
  59. package/templates-react/badge/Badge.example.jsx +182 -0
  60. package/templates-react/badge/Badge.jsx +44 -0
  61. package/templates-react/button/Button.css +88 -0
  62. package/templates-react/button/Button.example.jsx +40 -0
  63. package/templates-react/button/Button.jsx +29 -0
  64. package/templates-react/card/Card.css +86 -0
  65. package/templates-react/card/Card.example.jsx +49 -0
  66. package/templates-react/card/Card.jsx +35 -0
  67. package/templates-react/checkbox/Checkbox.css +217 -0
  68. package/templates-react/checkbox/Checkbox.example.jsx +141 -0
  69. package/templates-react/checkbox/Checkbox.jsx +82 -0
  70. package/templates-react/counter/Counter.css +99 -0
  71. package/templates-react/counter/Counter.example.jsx +45 -0
  72. package/templates-react/counter/Counter.jsx +70 -0
  73. package/templates-react/dropdown/Dropdown.css +237 -0
  74. package/templates-react/dropdown/Dropdown.example.jsx +98 -0
  75. package/templates-react/dropdown/Dropdown.jsx +154 -0
  76. package/templates-react/form/Form.css +128 -0
  77. package/templates-react/form/Form.example.jsx +64 -0
  78. package/templates-react/form/Form.jsx +125 -0
  79. package/templates-react/input/Input.css +113 -0
  80. package/templates-react/input/Input.example.jsx +82 -0
  81. package/templates-react/input/Input.jsx +87 -0
  82. package/templates-react/modal/Modal.css +152 -0
  83. package/templates-react/modal/Modal.example.jsx +90 -0
  84. package/templates-react/modal/Modal.jsx +46 -0
  85. package/templates-react/navbar/Navbar.css +139 -0
  86. package/templates-react/navbar/Navbar.example.jsx +37 -0
  87. package/templates-react/navbar/Navbar.jsx +62 -0
  88. package/templates-react/progress/Progress.css +247 -0
  89. package/templates-react/progress/Progress.example.jsx +244 -0
  90. package/templates-react/progress/Progress.jsx +79 -0
  91. package/templates-react/switch/Switch.css +244 -0
  92. package/templates-react/switch/Switch.example.jsx +221 -0
  93. package/templates-react/switch/Switch.jsx +98 -0
  94. package/templates-react/todo-list/TodoList.css +236 -0
  95. package/templates-react/todo-list/TodoList.example.jsx +15 -0
  96. package/templates-react/todo-list/TodoList.jsx +84 -0
  97. package/templates-react/tooltip/Tooltip.css +165 -0
  98. package/templates-react/tooltip/Tooltip.example.jsx +166 -0
  99. package/templates-react/tooltip/Tooltip.jsx +176 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * React Component choices for interactive CLI prompts
3
+ */
4
+
5
+ export const REACT_COMPONENT_CHOICES = [
6
+ {
7
+ name: "� Alert - Alert/notification component with variants",
8
+ value: "alert",
9
+ short: "Alert",
10
+ },
11
+ { name: "🏷️ Badge - Status badge with counts, dots, and variants",
12
+ value: "badge",
13
+ short: "Badge",
14
+ },
15
+ { name: "🔘 Button - Customizable button with variants and sizes",
16
+ value: "button",
17
+ short: "Button",
18
+ },
19
+ {
20
+ name: "🎴 Card - Display content in an elegant card",
21
+ value: "card",
22
+ short: "Card",
23
+ },
24
+ {
25
+ name: "☑️ Checkbox - Checkbox with sizes, colors, and states",
26
+ value: "checkbox",
27
+ short: "Checkbox",
28
+ },
29
+ {
30
+ name: "🔢 Counter - Interactive counter with increment/decrement",
31
+ value: "counter",
32
+ short: "Counter",
33
+ },
34
+ {
35
+ name: "📋 Dropdown - Select dropdown with search and multi-select",
36
+ value: "dropdown",
37
+ short: "Dropdown",
38
+ },
39
+ {
40
+ name: "📝 Form - Flexible form with validation",
41
+ value: "form",
42
+ short: "Form",
43
+ },
44
+ {
45
+ name: "📄 Input - Text input with validation and states",
46
+ value: "input",
47
+ short: "Input",
48
+ },
49
+ {
50
+ name: "🪟 Modal - Dialog modal component",
51
+ value: "modal",
52
+ short: "Modal",
53
+ },
54
+ {
55
+ name: "🧭 Navbar - Navigation bar with mobile menu",
56
+ value: "navbar",
57
+ short: "Navbar",
58
+ },
59
+ {
60
+ name: "📊 Progress - Progress bar with variants and animations",
61
+ value: "progress",
62
+ short: "Progress",
63
+ },
64
+ {
65
+ name: "🔀 Switch - Toggle switch for on/off states",
66
+ value: "switch",
67
+ short: "Switch",
68
+ },
69
+ {
70
+ name: "✅ Todo List - Complete todo list with CRUD operations",
71
+ value: "todo-list",
72
+ short: "Todo List",
73
+ },
74
+ {
75
+ name: "💬 Tooltip - Contextual tooltip with positions and triggers",
76
+ value: "tooltip",
77
+ short: "Tooltip",
78
+ },
79
+ ];
80
+
81
+ export const REACT_COMPONENT_DESCRIPTIONS = {
82
+ alert: "An alert component for displaying notifications with different types (success, error, warning, info). Supports dismissible and custom icons",
83
+ badge: "A status badge component for displaying labels, counts, and indicators. Supports sizes, colors, pill shape, dot badges, and position variants",
84
+ button: "A customizable button component with multiple variants (primary, secondary, success, danger) and sizes (small, medium, large)",
85
+ card: "A versatile card component for displaying content with image, title, description, and footer support",
86
+ checkbox: "A checkbox component with different sizes, colors (primary, secondary, success, error), indeterminate state, and validation support",
87
+ counter: "A simple counter component with increment, decrement, and reset functionality. Supports min/max limits and custom steps",
88
+ dropdown: "A dropdown select component with searchable options, multi-select support, custom positioning, and keyboard navigation",
89
+ form: "A flexible form component with built-in validation, supporting multiple field types (text, email, textarea, select)",
90
+ input: "A text input component with validation, error messages, icons, and different states (focused, error, disabled)",
91
+ modal: "A modal dialog component with overlay, customizable sizes, and close handlers",
92
+ navbar: "A responsive navigation bar with logo, menu items, and mobile hamburger menu. Supports sticky positioning",
93
+ progress: "A progress bar component with colors, sizes, labels, striped patterns, animations, and indeterminate state for loading",
94
+ switch: "A toggle switch component for on/off states. Supports colors, sizes, icons, disabled and loading states",
95
+ "todo-list": "A complete todo list component with add, toggle, and delete functionality. Includes task statistics",
96
+ tooltip: "A contextual tooltip component with multiple positions (top, bottom, left, right), triggers (hover, click, focus), and customizable delays",
97
+ };
@@ -0,0 +1,182 @@
1
+ /**
2
+ * React Component Templates Module
3
+ *
4
+ * Central registry for all React component templates organized by category.
5
+ * This module aggregates templates from specialized sub-modules and provides
6
+ * a unified API for accessing component templates and their metadata.
7
+ *
8
+ * @module react-component-templates
9
+ * @author create-template-html-css
10
+ * @version 2.0.0
11
+ */
12
+
13
+ import { BASIC_TEMPLATES } from './templates/basic-components-templates.js';
14
+ import { FORM_TEMPLATES } from './templates/form-components-templates.js';
15
+ import { INTERACTIVE_TEMPLATES } from './templates/interactive-components-templates.js';
16
+
17
+ // ============================================================================
18
+ // COMPONENT TEMPLATES REGISTRY
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Unified component template registry
23
+ *
24
+ * Aggregates all component templates from different categories into a single
25
+ * accessible object. Templates are organized by component name (kebab-case).
26
+ *
27
+ * Categories:
28
+ * - Basic components: alert, badge, button, card, counter
29
+ * - Form components: form, input, checkbox, dropdown, switch
30
+ * - Interactive components: modal, navbar, tooltip, progress, todo-list
31
+ *
32
+ * @constant {Object.<string, string>}
33
+ */
34
+ export const COMPONENT_TEMPLATES = {
35
+ ...BASIC_TEMPLATES,
36
+ ...FORM_TEMPLATES,
37
+ ...INTERACTIVE_TEMPLATES,
38
+ };
39
+
40
+ // ============================================================================
41
+ // REACT IMPORTS MAPPING
42
+ // ============================================================================
43
+
44
+ /**
45
+ * React hooks import requirements for components
46
+ *
47
+ * Maps component names (kebab-case) to the React hooks they require.
48
+ * Components not listed here don't need any React hooks.
49
+ *
50
+ * @constant {Object.<string, string>}
51
+ * @property {string} modal - Requires useState for open/close state
52
+ * @property {string} input - Requires useState for controlled inputs
53
+ * @property {string} checkbox - Requires useState for checked state
54
+ * @property {string} dropdown - Requires useState for selection state
55
+ * @property {string} switch - Requires useState for toggle state
56
+ * @property {string} progress - Requires useState and useEffect for animations
57
+ *
58
+ * @example
59
+ * // Get imports for a component
60
+ * const imports = COMPONENT_IMPORTS['modal']; // Returns 'useState'
61
+ */
62
+ export const COMPONENT_IMPORTS = {
63
+ modal: 'useState',
64
+ input: 'useState',
65
+ checkbox: 'useState',
66
+ dropdown: 'useState',
67
+ switch: 'useState',
68
+ progress: 'useState, useEffect',
69
+ };
70
+
71
+ // ============================================================================
72
+ // HELPER FUNCTIONS
73
+ // ============================================================================
74
+
75
+ /**
76
+ * Get template content for a component with placeholders replaced
77
+ *
78
+ * Retrieves the JSX template for the specified component and replaces
79
+ * placeholder tokens with the actual component name.
80
+ *
81
+ * @param {string} componentKebab - Component name in kebab-case (e.g., 'my-button')
82
+ * @param {string} componentName - Component name in PascalCase (e.g., 'MyButton')
83
+ * @returns {string|null} Template content with placeholders replaced, or null if not found
84
+ *
85
+ * @example
86
+ * const template = getComponentTemplate('button', 'Button');
87
+ * // Returns JSX string with <Button> tags instead of {ComponentName}
88
+ */
89
+ export function getComponentTemplate(componentKebab, componentName) {
90
+ const template = COMPONENT_TEMPLATES[componentKebab];
91
+ if (!template) {
92
+ return null;
93
+ }
94
+
95
+ // Replace {ComponentName} placeholder with actual component name
96
+ return template.replace(/\{ComponentName\}/g, componentName);
97
+ }
98
+
99
+ /**
100
+ * Get required React imports for a component
101
+ *
102
+ * Returns a comma-separated string of React hooks that the component needs.
103
+ * If the component doesn't require any hooks, returns an empty string.
104
+ *
105
+ * @param {string} componentKebab - Component name in kebab-case
106
+ * @returns {string} Comma-separated imports (e.g., 'useState, useEffect') or empty string
107
+ *
108
+ * @example
109
+ * const imports = getComponentImports('progress');
110
+ * // Returns 'useState, useEffect'
111
+ *
112
+ * const imports2 = getComponentImports('button');
113
+ * // Returns '' (no hooks needed)
114
+ */
115
+ export function getComponentImports(componentKebab) {
116
+ return COMPONENT_IMPORTS[componentKebab] || '';
117
+ }
118
+
119
+ /**
120
+ * Get all available component names
121
+ *
122
+ * Returns an array of all component names (in kebab-case) that have templates available.
123
+ * Useful for validation, CLI autocomplete, or displaying available options.
124
+ *
125
+ * @returns {string[]} Array of component names in kebab-case
126
+ *
127
+ * @example
128
+ * const components = getAllComponentNames();
129
+ * // Returns ['alert', 'badge', 'button', 'card', ...]
130
+ */
131
+ export function getAllComponentNames() {
132
+ return Object.keys(COMPONENT_TEMPLATES);
133
+ }
134
+
135
+ /**
136
+ * Check if a component template exists
137
+ *
138
+ * Validates whether a template is available for the specified component name.
139
+ * Case-sensitive - expects kebab-case.
140
+ *
141
+ * @param {string} componentKebab - Component name in kebab-case
142
+ * @returns {boolean} True if template exists, false otherwise
143
+ *
144
+ * @example
145
+ * if (hasComponent('button')) {
146
+ * console.log('Button template is available');
147
+ * }
148
+ */
149
+ export function hasComponent(componentKebab) {
150
+ return componentKebab in COMPONENT_TEMPLATES;
151
+ }
152
+
153
+ /**
154
+ * Get all components that require React imports
155
+ *
156
+ * Returns an array of component names that need React hooks.
157
+ * Useful for documenting which components have state management.
158
+ *
159
+ * @returns {string[]} Array of component names that require React imports
160
+ *
161
+ * @example
162
+ * const statefulComponents = getComponentsWithImports();
163
+ * // Returns ['modal', 'input', 'checkbox', 'dropdown', 'switch', 'progress']
164
+ */
165
+ export function getComponentsWithImports() {
166
+ return Object.keys(COMPONENT_IMPORTS);
167
+ }
168
+
169
+ /**
170
+ * Get component count
171
+ *
172
+ * Returns the total number of available component templates.
173
+ *
174
+ * @returns {number} Total number of components
175
+ *
176
+ * @example
177
+ * console.log(`${getComponentCount()} components available`);
178
+ * // Output: "15 components available"
179
+ */
180
+ export function getComponentCount() {
181
+ return Object.keys(COMPONENT_TEMPLATES).length;
182
+ }
@@ -0,0 +1,172 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import { getDirname } from "./utils/path-utils.js";
4
+ import { COLOR_SCHEMES } from "./generators/color-schemes.js";
5
+ import { applyCustomColors } from "./generators/color-utils.js";
6
+
7
+ const __dirname = getDirname(import.meta.url);
8
+
9
+ // ============================================================================
10
+ // CONSTANTS
11
+ // ============================================================================
12
+
13
+ // Default colors
14
+ const DEFAULT_PRIMARY_COLOR = "#667eea";
15
+ const DEFAULT_SECONDARY_COLOR = "#764ba2";
16
+
17
+ // ============================================================================
18
+ // PATH UTILITIES
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Get template path for React components
23
+ * @param {string} component - Component name
24
+ * @returns {string} Absolute path to template
25
+ */
26
+ function getReactTemplatePath(component) {
27
+ return path.join(__dirname, "..", "templates-react", component);
28
+ }
29
+
30
+ // ============================================================================
31
+ // FILE READING
32
+ // ============================================================================
33
+
34
+ /**
35
+ * Read React component file (.jsx)
36
+ * @param {string} component - Component name
37
+ * @param {string} filename - File name
38
+ * @returns {Promise<string>} File content
39
+ */
40
+ async function readReactFile(component, filename) {
41
+ const templatePath = getReactTemplatePath(component);
42
+ const filePath = path.join(templatePath, filename);
43
+
44
+ try {
45
+ return await fs.readFile(filePath, "utf-8");
46
+ } catch (error) {
47
+ if (error.code === "ENOENT") {
48
+ return null;
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Read and process component template files
56
+ * @param {string} component - Component name (kebab-case)
57
+ * @param {string} componentName - Component name (PascalCase)
58
+ * @param {string} primaryColor - Primary color
59
+ * @param {string} secondaryColor - Secondary color
60
+ * @returns {Promise<Object>} Processed JSX and CSS content
61
+ */
62
+ async function readComponentFiles(component, componentName, primaryColor, secondaryColor) {
63
+ const jsxContent = await readReactFile(component, `${componentName}.jsx`);
64
+ let cssContent = await readReactFile(component, `${componentName}.css`);
65
+
66
+ if (!jsxContent) {
67
+ throw new Error(`React template not found for component: ${component}`);
68
+ }
69
+
70
+ // Apply custom colors to CSS
71
+ if (cssContent && (primaryColor || secondaryColor)) {
72
+ cssContent = applyCustomColors(cssContent, primaryColor, secondaryColor);
73
+ }
74
+
75
+ return { jsxContent, cssContent };
76
+ }
77
+
78
+ // ============================================================================
79
+ // FILE WRITING
80
+ // ============================================================================
81
+
82
+ /**
83
+ * Write component files to directory
84
+ * @param {string} componentDir - Component directory path
85
+ * @param {string} componentName - Component name (PascalCase)
86
+ * @param {string} jsxContent - JSX file content
87
+ * @param {string} cssContent - CSS file content
88
+ */
89
+ async function writeComponentFiles(componentDir, componentName, jsxContent, cssContent) {
90
+ await fs.writeFile(
91
+ path.join(componentDir, `${componentName}.jsx`),
92
+ jsxContent,
93
+ "utf-8"
94
+ );
95
+
96
+ if (cssContent) {
97
+ await fs.writeFile(
98
+ path.join(componentDir, `${componentName}.css`),
99
+ cssContent,
100
+ "utf-8"
101
+ );
102
+ }
103
+ }
104
+
105
+ // ============================================================================
106
+ // PROJECT STRUCTURE
107
+ // ============================================================================
108
+
109
+ /**
110
+ * Create React project structure
111
+ * @param {string} outputDir - Output directory path
112
+ * @returns {Promise<Object>} Directory paths
113
+ */
114
+ async function createReactProjectStructure(outputDir) {
115
+ await fs.mkdir(outputDir, { recursive: true });
116
+
117
+ const srcDir = path.join(outputDir, "src");
118
+ await fs.mkdir(srcDir, { recursive: true });
119
+
120
+ const componentsDir = path.join(srcDir, "components");
121
+ await fs.mkdir(componentsDir, { recursive: true });
122
+
123
+ return { outputDir, srcDir, componentsDir };
124
+ }
125
+
126
+ // ============================================================================
127
+ // UTILITY FUNCTIONS
128
+ // ============================================================================
129
+
130
+ /**
131
+ * Convert component name to PascalCase
132
+ * @param {string} name - Component name (kebab-case)
133
+ * @returns {string} PascalCase name
134
+ */
135
+ function toPascalCase(name) {
136
+ return name
137
+ .split("-")
138
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
139
+ .join("");
140
+ }
141
+
142
+ /**
143
+ * Resolve color scheme to actual colors
144
+ * @param {Object} options - Color options
145
+ * @returns {Object} Resolved colors
146
+ */
147
+ function resolveColors(options) {
148
+ const { colorScheme, primaryColor, secondaryColor } = options;
149
+
150
+ let finalPrimaryColor = primaryColor || DEFAULT_PRIMARY_COLOR;
151
+ let finalSecondaryColor = secondaryColor || DEFAULT_SECONDARY_COLOR;
152
+
153
+ if (colorScheme && COLOR_SCHEMES[colorScheme]) {
154
+ const scheme = COLOR_SCHEMES[colorScheme];
155
+ finalPrimaryColor = scheme.primary;
156
+ finalSecondaryColor = scheme.secondary;
157
+ }
158
+
159
+ return { finalPrimaryColor, finalSecondaryColor };
160
+ }
161
+
162
+ // ============================================================================
163
+ // EXPORTS
164
+ // ============================================================================
165
+
166
+ export {
167
+ readComponentFiles,
168
+ writeComponentFiles,
169
+ createReactProjectStructure,
170
+ toPascalCase,
171
+ resolveColors,
172
+ };
@@ -0,0 +1,219 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import { sanitizeFilename } from "./generators/validation.js";
4
+ import {
5
+ readComponentFiles,
6
+ writeComponentFiles,
7
+ createReactProjectStructure,
8
+ toPascalCase,
9
+ resolveColors,
10
+ } from "./react-file-operations.js";
11
+ import {
12
+ generateAppJsx,
13
+ generateIndexJs,
14
+ generateIndexHtml,
15
+ generatePackageJson,
16
+ generateGitignore,
17
+ generateViteConfig,
18
+ generateReadme,
19
+ } from "./react-templates.js";
20
+
21
+ // ============================================================================
22
+ // CONSTANTS
23
+ // ============================================================================
24
+
25
+ // Valid React components
26
+ export const VALID_REACT_COMPONENTS = [
27
+ "alert",
28
+ "badge",
29
+ "button",
30
+ "card",
31
+ "checkbox",
32
+ "counter",
33
+ "dropdown",
34
+ "form",
35
+ "input",
36
+ "modal",
37
+ "navbar",
38
+ "progress",
39
+ "switch",
40
+ "todo-list",
41
+ "tooltip",
42
+ ];
43
+
44
+ // ============================================================================
45
+ // VALIDATORS
46
+ // ============================================================================
47
+
48
+ /**
49
+ * Validate React component name
50
+ * @param {string} component - Component name to validate
51
+ * @throws {Error} If component is invalid
52
+ */
53
+ function validateComponent(component) {
54
+ if (!VALID_REACT_COMPONENTS.includes(component)) {
55
+ throw new Error(
56
+ `Invalid React component: ${component}. Must be one of: ${VALID_REACT_COMPONENTS.join(", ")}`
57
+ );
58
+ }
59
+ }
60
+
61
+ // ============================================================================
62
+ // MAIN EXPORT FUNCTIONS
63
+ // ============================================================================
64
+
65
+ /**
66
+ * Generate React component files
67
+ * @param {Object} options - Generation options
68
+ * @returns {Promise<string>} Path to generated directory
69
+ */
70
+ async function generateReactTemplate(options) {
71
+ const {
72
+ component,
73
+ name,
74
+ colorScheme,
75
+ primaryColor,
76
+ secondaryColor,
77
+ darkMode,
78
+ lazyLoad = false,
79
+ optimizeBuild = false,
80
+ } = options;
81
+
82
+ // Resolve colors
83
+ const { finalPrimaryColor, finalSecondaryColor } = resolveColors({
84
+ colorScheme,
85
+ primaryColor,
86
+ secondaryColor,
87
+ });
88
+
89
+ // Security: Validate component name
90
+ validateComponent(component);
91
+
92
+ // Security: Sanitize name to prevent path traversal
93
+ const safeName = sanitizeFilename(name);
94
+ if (!safeName || safeName.length === 0) {
95
+ throw new Error("Invalid name provided");
96
+ }
97
+
98
+ // Create project structure
99
+ const outputDir = path.join(process.cwd(), safeName);
100
+ const { srcDir, componentsDir } = await createReactProjectStructure(outputDir);
101
+
102
+ // Get component name in PascalCase
103
+ const componentName = toPascalCase(component);
104
+ const componentDir = path.join(componentsDir, componentName);
105
+ await fs.mkdir(componentDir, { recursive: true });
106
+
107
+ // Read and process component files
108
+ const { jsxContent, cssContent } = await readComponentFiles(
109
+ component,
110
+ componentName,
111
+ finalPrimaryColor,
112
+ finalSecondaryColor
113
+ );
114
+
115
+ // Write component files
116
+ await writeComponentFiles(componentDir, componentName, jsxContent, cssContent);
117
+
118
+ // Create App.jsx (with optional lazy loading)
119
+ const appContent = generateAppJsx(componentName, component, { lazyLoad });
120
+ await fs.writeFile(path.join(srcDir, "App.jsx"), appContent, "utf-8");
121
+
122
+ // Create index.jsx
123
+ const indexContent = generateIndexJs();
124
+ await fs.writeFile(path.join(srcDir, "index.jsx"), indexContent, "utf-8");
125
+
126
+ // Create index.html
127
+ const htmlContent = generateIndexHtml(safeName);
128
+ await fs.writeFile(path.join(outputDir, "index.html"), htmlContent, "utf-8");
129
+
130
+ // Create package.json
131
+ const packageJson = generatePackageJson(safeName);
132
+ await fs.writeFile(
133
+ path.join(outputDir, "package.json"),
134
+ JSON.stringify(packageJson, null, 2),
135
+ "utf-8"
136
+ );
137
+
138
+ // Create .gitignore
139
+ const gitignoreContent = generateGitignore();
140
+ await fs.writeFile(path.join(outputDir, ".gitignore"), gitignoreContent, "utf-8");
141
+
142
+ // Create vite.config.js (with optional optimizations)
143
+ const viteConfig = generateViteConfig(optimizeBuild);
144
+ await fs.writeFile(path.join(outputDir, "vite.config.js"), viteConfig, "utf-8");
145
+
146
+ // Create README.md
147
+ const readmeContent = generateReadme(safeName, componentName);
148
+ await fs.writeFile(path.join(outputDir, "README.md"), readmeContent, "utf-8");
149
+
150
+ return outputDir;
151
+ }
152
+
153
+ /**
154
+ * Add React component only (without full project structure)
155
+ * @param {Object} options - Generation options
156
+ * @returns {Promise<string>} Path to generated component directory
157
+ */
158
+ async function addReactComponentOnly(options) {
159
+ const {
160
+ component,
161
+ outputDir = process.cwd(),
162
+ colorScheme,
163
+ primaryColor,
164
+ secondaryColor,
165
+ } = options;
166
+
167
+ // Resolve colors
168
+ const { finalPrimaryColor, finalSecondaryColor } = resolveColors({
169
+ colorScheme,
170
+ primaryColor,
171
+ secondaryColor,
172
+ });
173
+
174
+ // Security: Validate component name
175
+ validateComponent(component);
176
+
177
+ // Get component name in PascalCase
178
+ const componentName = toPascalCase(component);
179
+ const componentDir = path.join(outputDir, componentName);
180
+
181
+ // Check if component already exists
182
+ try {
183
+ await fs.access(componentDir);
184
+ throw new Error(`Component directory already exists: ${componentDir}`);
185
+ } catch (error) {
186
+ if (error.code !== "ENOENT") {
187
+ throw error;
188
+ }
189
+ }
190
+
191
+ await fs.mkdir(componentDir, { recursive: true });
192
+
193
+ // Read and process component files
194
+ const { jsxContent, cssContent } = await readComponentFiles(
195
+ component,
196
+ componentName,
197
+ finalPrimaryColor,
198
+ finalSecondaryColor
199
+ );
200
+
201
+ // Write component files
202
+ await writeComponentFiles(componentDir, componentName, jsxContent, cssContent);
203
+
204
+ console.log(`✓ Created ${componentName} component in ${componentDir}`);
205
+ console.log(`\nFiles created:`);
206
+ console.log(` - ${componentName}.jsx`);
207
+ console.log(` - ${componentName}.css`);
208
+ console.log(`\nUsage example:`);
209
+ console.log(` import ${componentName} from './${componentName}/${componentName}';`);
210
+ console.log(` import './${componentName}/${componentName}.css';`);
211
+
212
+ return componentDir;
213
+ }
214
+
215
+ // ============================================================================
216
+ // EXPORTS
217
+ // ============================================================================
218
+
219
+ export { generateReactTemplate, addReactComponentOnly };