initkit 1.1.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.
@@ -0,0 +1,586 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { getLatestVersion } from '../utils/versionFetcher.js';
6
+
7
+ /**
8
+ * Fetch latest version with fallback
9
+ */
10
+ async function fetchVersion(packageName, fallback = 'latest') {
11
+ try {
12
+ const version = await getLatestVersion(packageName);
13
+ return `^${version}`;
14
+ } catch {
15
+ return fallback;
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Generate React + Vite project with modern build tooling
21
+ *
22
+ * Creates a React single-page application using Vite for:
23
+ * - Fast HMR (Hot Module Replacement)
24
+ * - Optimized production builds
25
+ * - TypeScript or JavaScript support
26
+ * - Multiple folder structure patterns
27
+ *
28
+ * Generated project includes:
29
+ * - Vite configuration with React plugin
30
+ * - Folder structure based on preference
31
+ * - Package.json with React 18+ and Vite dependencies
32
+ * - README with getting started instructions
33
+ *
34
+ * @param {string} projectPath - Absolute path to the project directory
35
+ * @param {Object} config - User configuration object
36
+ * @param {string} config.projectName - Name of the project
37
+ * @param {string} config.language - Programming language ('typescript'|'javascript')
38
+ * @param {string} [config.folderStructure='feature-based'] - Folder organization pattern
39
+ * - 'feature-based': Organize by features/modules (recommended)
40
+ * - 'component-based': Organize by component types (pages, components, layouts)
41
+ * - 'type-based': Organize by file type (components, hooks, utils, services)
42
+ * @param {string} config.packageManager - Package manager to use
43
+ *
44
+ * @returns {Promise<void>}
45
+ *
46
+ * @example
47
+ * // Create React + Vite project with feature-based structure
48
+ * await generateReactTemplate('/path/to/project', {
49
+ * projectName: 'my-react-app',
50
+ * language: 'typescript',
51
+ * folderStructure: 'feature-based',
52
+ * packageManager: 'npm'
53
+ * });
54
+ */
55
+ export async function generateReactTemplate(projectPath, config) {
56
+ // Create folder structure only
57
+ await createReactFolderStructure(projectPath, config);
58
+
59
+ // Generate package.json
60
+ await generateReactPackageJson(projectPath, config);
61
+
62
+ // Generate essential files (App.jsx, main.jsx, index.css, vite.config)
63
+ await generateReactEssentialFiles(projectPath, config);
64
+
65
+ // Generate README
66
+ await generateReactReadme(projectPath, config);
67
+ }
68
+
69
+ async function createReactFolderStructure(projectPath, config) {
70
+ const srcPath = path.join(projectPath, 'src');
71
+ const folderStructure = config.folderStructure || 'feature-based';
72
+
73
+ if (folderStructure === 'feature-based') {
74
+ // Feature-based structure
75
+ const features = ['auth', 'dashboard', 'users'];
76
+ for (const feature of features) {
77
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'components'));
78
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'hooks'));
79
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'services'));
80
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'types'));
81
+
82
+ // Barrel export
83
+ await fs.writeFile(
84
+ path.join(srcPath, 'features', feature, 'index.ts'),
85
+ generateFeatureExport(feature)
86
+ );
87
+ }
88
+
89
+ // Shared directory
90
+ await fs.ensureDir(path.join(srcPath, 'shared', 'components', 'ui'));
91
+ await fs.ensureDir(path.join(srcPath, 'shared', 'components', 'layout'));
92
+ await fs.ensureDir(path.join(srcPath, 'shared', 'hooks'));
93
+ await fs.ensureDir(path.join(srcPath, 'shared', 'utils'));
94
+ await fs.ensureDir(path.join(srcPath, 'shared', 'types'));
95
+
96
+ await fs.writeFile(
97
+ path.join(srcPath, 'shared', 'components', 'index.ts'),
98
+ `// Export shared components\n// TODO: Add your shared components here\n`
99
+ );
100
+ } else if (folderStructure === 'component-based') {
101
+ // Component-based structure
102
+ await fs.ensureDir(path.join(srcPath, 'components', 'ui'));
103
+ await fs.ensureDir(path.join(srcPath, 'components', 'layout'));
104
+ await fs.ensureDir(path.join(srcPath, 'components', 'forms'));
105
+ await fs.ensureDir(path.join(srcPath, 'hooks'));
106
+ await fs.ensureDir(path.join(srcPath, 'services'));
107
+ await fs.ensureDir(path.join(srcPath, 'utils'));
108
+ await fs.ensureDir(path.join(srcPath, 'types'));
109
+ } else if (folderStructure === 'atomic') {
110
+ // Atomic design structure
111
+ await fs.ensureDir(path.join(srcPath, 'components', 'atoms'));
112
+ await fs.ensureDir(path.join(srcPath, 'components', 'molecules'));
113
+ await fs.ensureDir(path.join(srcPath, 'components', 'organisms'));
114
+ await fs.ensureDir(path.join(srcPath, 'components', 'templates'));
115
+ await fs.ensureDir(path.join(srcPath, 'pages'));
116
+ await fs.ensureDir(path.join(srcPath, 'hooks'));
117
+ await fs.ensureDir(path.join(srcPath, 'services'));
118
+ }
119
+
120
+ // Common directories
121
+ await fs.ensureDir(path.join(srcPath, 'routes'));
122
+ await fs.ensureDir(path.join(srcPath, 'lib'));
123
+ await fs.ensureDir(path.join(projectPath, 'public'));
124
+ }
125
+
126
+ async function generateReactPackageJson(projectPath, config) {
127
+ const { language, styling, additionalLibraries = [] } = config;
128
+ const isTypeScript = language === 'typescript';
129
+
130
+ const spinner = ora('Fetching latest package versions...').start();
131
+
132
+ try {
133
+ // Fetch core dependencies
134
+ const [reactVer, reactDomVer, viteVer, pluginVer] = await Promise.all([
135
+ fetchVersion('react'),
136
+ fetchVersion('react-dom'),
137
+ fetchVersion('vite'),
138
+ fetchVersion('@vitejs/plugin-react'),
139
+ ]);
140
+
141
+ const dependencies = {
142
+ react: reactVer,
143
+ 'react-dom': reactDomVer,
144
+ };
145
+
146
+ const devDependencies = {
147
+ '@vitejs/plugin-react': pluginVer,
148
+ vite: viteVer,
149
+ };
150
+
151
+ // TypeScript dependencies
152
+ if (isTypeScript) {
153
+ const [tsVer, typesReactVer, typesReactDomVer] = await Promise.all([
154
+ fetchVersion('typescript'),
155
+ fetchVersion('@types/react'),
156
+ fetchVersion('@types/react-dom'),
157
+ ]);
158
+ devDependencies['typescript'] = tsVer;
159
+ devDependencies['@types/react'] = typesReactVer;
160
+ devDependencies['@types/react-dom'] = typesReactDomVer;
161
+ }
162
+
163
+ // Add router
164
+ if (additionalLibraries.includes('react-router')) {
165
+ dependencies['react-router-dom'] = await fetchVersion('react-router-dom');
166
+ }
167
+
168
+ // Add Tailwind
169
+ if (styling === 'tailwind') {
170
+ devDependencies['tailwindcss'] = await fetchVersion('tailwindcss');
171
+ }
172
+
173
+ // Add libraries
174
+ if (additionalLibraries.includes('tanstack-query')) {
175
+ dependencies['@tanstack/react-query'] = await fetchVersion('@tanstack/react-query');
176
+ }
177
+
178
+ if (additionalLibraries.includes('redux-toolkit')) {
179
+ const [reduxVer, reactReduxVer] = await Promise.all([
180
+ fetchVersion('@reduxjs/toolkit'),
181
+ fetchVersion('react-redux'),
182
+ ]);
183
+ dependencies['@reduxjs/toolkit'] = reduxVer;
184
+ dependencies['react-redux'] = reactReduxVer;
185
+ }
186
+
187
+ if (additionalLibraries.includes('zustand')) {
188
+ dependencies['zustand'] = await fetchVersion('zustand');
189
+ }
190
+
191
+ if (additionalLibraries.includes('jotai')) {
192
+ dependencies['jotai'] = await fetchVersion('jotai');
193
+ }
194
+
195
+ if (additionalLibraries.includes('axios')) {
196
+ dependencies['axios'] = await fetchVersion('axios');
197
+ }
198
+
199
+ if (additionalLibraries.includes('zod')) {
200
+ dependencies['zod'] = await fetchVersion('zod');
201
+ }
202
+
203
+ if (additionalLibraries.includes('react-hook-form')) {
204
+ const [formVer, resolversVer] = await Promise.all([
205
+ fetchVersion('react-hook-form'),
206
+ fetchVersion('@hookform/resolvers'),
207
+ ]);
208
+ dependencies['react-hook-form'] = formVer;
209
+ dependencies['@hookform/resolvers'] = resolversVer;
210
+ }
211
+
212
+ if (additionalLibraries.includes('framer-motion')) {
213
+ dependencies['framer-motion'] = await fetchVersion('framer-motion');
214
+ }
215
+
216
+ if (additionalLibraries.includes('react-icons')) {
217
+ dependencies['react-icons'] = await fetchVersion('react-icons');
218
+ }
219
+
220
+ if (additionalLibraries.includes('radix-ui')) {
221
+ const [dialogVer, dropdownVer, selectVer, slotVer] = await Promise.all([
222
+ fetchVersion('@radix-ui/react-dialog'),
223
+ fetchVersion('@radix-ui/react-dropdown-menu'),
224
+ fetchVersion('@radix-ui/react-select'),
225
+ fetchVersion('@radix-ui/react-slot'),
226
+ ]);
227
+ Object.assign(dependencies, {
228
+ '@radix-ui/react-dialog': dialogVer,
229
+ '@radix-ui/react-dropdown-menu': dropdownVer,
230
+ '@radix-ui/react-select': selectVer,
231
+ '@radix-ui/react-slot': slotVer,
232
+ });
233
+ }
234
+
235
+ spinner.succeed(chalk.green('Fetched latest versions'));
236
+
237
+ const packageJson = {
238
+ name: config.projectName,
239
+ private: true,
240
+ version: '0.1.0',
241
+ type: 'module',
242
+ scripts: {
243
+ dev: 'vite',
244
+ build: isTypeScript ? 'tsc && vite build' : 'vite build',
245
+ preview: 'vite preview',
246
+ },
247
+ dependencies,
248
+ devDependencies,
249
+ };
250
+
251
+ await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
252
+ } catch (error) {
253
+ spinner.fail(chalk.yellow('Could not fetch versions, using fallbacks'));
254
+
255
+ // Fallback with latest tag
256
+ const dependencies = { react: 'latest', 'react-dom': 'latest' };
257
+ const devDependencies = {
258
+ '@vitejs/plugin-react': 'latest',
259
+ vite: 'latest',
260
+ ...(isTypeScript && {
261
+ typescript: 'latest',
262
+ '@types/react': 'latest',
263
+ '@types/react-dom': 'latest',
264
+ }),
265
+ };
266
+
267
+ const packageJson = {
268
+ name: config.projectName,
269
+ private: true,
270
+ version: '0.1.0',
271
+ type: 'module',
272
+ scripts: {
273
+ dev: 'vite',
274
+ build: isTypeScript ? 'tsc && vite build' : 'vite build',
275
+ preview: 'vite preview',
276
+ },
277
+ dependencies,
278
+ devDependencies,
279
+ };
280
+
281
+ await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
282
+ }
283
+ }
284
+
285
+ async function generateReactEssentialFiles(projectPath, config) {
286
+ const { language } = config;
287
+ const isTypeScript = language === 'typescript';
288
+ const ext = isTypeScript ? 'tsx' : 'jsx';
289
+ const srcPath = path.join(projectPath, 'src');
290
+
291
+ // Create App component
292
+ const appContent = `import { useState } from 'react'
293
+ import './App.css'
294
+
295
+ function App() {
296
+ const [count, setCount] = useState(0)
297
+
298
+ return (
299
+ <>
300
+ <div>
301
+ <h1>Welcome to ${config.projectName}</h1>
302
+ <div className="card">
303
+ <button onClick={() => setCount((count) => count + 1)}>
304
+ count is {count}
305
+ </button>
306
+ <p>
307
+ Edit <code>src/App.${ext}</code> and save to test HMR
308
+ </p>
309
+ </div>
310
+ <p className="read-the-docs">
311
+ Click on the Vite and React logos to learn more
312
+ </p>
313
+ </div>
314
+ </>
315
+ )
316
+ }
317
+
318
+ export default App
319
+ `;
320
+
321
+ await fs.writeFile(path.join(srcPath, `App.${ext}`), appContent);
322
+
323
+ // Create main entry point
324
+ const mainContent = `import { StrictMode } from 'react'
325
+ import { createRoot } from 'react-dom/client'
326
+ import './index.css'
327
+ import App from './App.${ext}'
328
+
329
+ createRoot(document.getElementById('root')${isTypeScript ? '!' : ''}).render(
330
+ <StrictMode>
331
+ <App />
332
+ </StrictMode>,
333
+ )
334
+ `;
335
+
336
+ await fs.writeFile(path.join(srcPath, `main.${ext}`), mainContent);
337
+
338
+ // Create index.css
339
+ const indexCssContent = `:root {
340
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
341
+ line-height: 1.5;
342
+ font-weight: 400;
343
+
344
+ color-scheme: light dark;
345
+ color: rgba(255, 255, 255, 0.87);
346
+ background-color: #242424;
347
+
348
+ font-synthesis: none;
349
+ text-rendering: optimizeLegibility;
350
+ -webkit-font-smoothing: antialiased;
351
+ -moz-osx-font-smoothing: grayscale;
352
+ }
353
+
354
+ a {
355
+ font-weight: 500;
356
+ color: #646cff;
357
+ text-decoration: inherit;
358
+ }
359
+ a:hover {
360
+ color: #535bf2;
361
+ }
362
+
363
+ body {
364
+ margin: 0;
365
+ display: flex;
366
+ place-items: center;
367
+ min-width: 320px;
368
+ min-height: 100vh;
369
+ }
370
+
371
+ h1 {
372
+ font-size: 3.2em;
373
+ line-height: 1.1;
374
+ }
375
+
376
+ button {
377
+ border-radius: 8px;
378
+ border: 1px solid transparent;
379
+ padding: 0.6em 1.2em;
380
+ font-size: 1em;
381
+ font-weight: 500;
382
+ font-family: inherit;
383
+ background-color: #1a1a1a;
384
+ cursor: pointer;
385
+ transition: border-color 0.25s;
386
+ }
387
+ button:hover {
388
+ border-color: #646cff;
389
+ }
390
+ button:focus,
391
+ button:focus-visible {
392
+ outline: 4px auto -webkit-focus-ring-color;
393
+ }
394
+
395
+ @media (prefers-color-scheme: light) {
396
+ :root {
397
+ color: #213547;
398
+ background-color: #ffffff;
399
+ }
400
+ a:hover {
401
+ color: #747bff;
402
+ }
403
+ button {
404
+ background-color: #f9f9f9;
405
+ }
406
+ }
407
+ `;
408
+
409
+ await fs.writeFile(path.join(srcPath, 'index.css'), indexCssContent);
410
+
411
+ // Create App.css
412
+ const appCssContent = `#root {
413
+ max-width: 1280px;
414
+ margin: 0 auto;
415
+ padding: 2rem;
416
+ text-align: center;
417
+ }
418
+
419
+ .card {
420
+ padding: 2em;
421
+ }
422
+
423
+ .read-the-docs {
424
+ color: #888;
425
+ }
426
+ `;
427
+
428
+ await fs.writeFile(path.join(srcPath, 'App.css'), appCssContent);
429
+
430
+ // Create vite.config
431
+ const viteConfigContent = isTypeScript
432
+ ? `import { defineConfig } from 'vite'
433
+ import react from '@vitejs/plugin-react'
434
+
435
+ // https://vite.dev/config/
436
+ export default defineConfig({
437
+ plugins: [react()],
438
+ })
439
+ `
440
+ : `import { defineConfig } from 'vite'
441
+ import react from '@vitejs/plugin-react'
442
+
443
+ // https://vite.dev/config/
444
+ export default defineConfig({
445
+ plugins: [react()],
446
+ })
447
+ `;
448
+
449
+ await fs.writeFile(
450
+ path.join(projectPath, `vite.config.${isTypeScript ? 'ts' : 'js'}`),
451
+ viteConfigContent
452
+ );
453
+
454
+ // Create index.html in project root
455
+ const indexHtmlContent = `<!doctype html>
456
+ <html lang="en">
457
+ <head>
458
+ <meta charset="UTF-8" />
459
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
460
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
461
+ <title>${config.projectName}</title>
462
+ </head>
463
+ <body>
464
+ <div id="root"></div>
465
+ <script type="module" src="/src/main.${ext}"></script>
466
+ </body>
467
+ </html>
468
+ `;
469
+
470
+ await fs.writeFile(path.join(projectPath, 'index.html'), indexHtmlContent);
471
+
472
+ // Create .gitignore
473
+ const gitignoreContent = `# Logs
474
+ logs
475
+ *.log
476
+ npm-debug.log*
477
+ yarn-debug.log*
478
+ yarn-error.log*
479
+ pnpm-debug.log*
480
+ lerna-debug.log*
481
+
482
+ node_modules
483
+ dist
484
+ dist-ssr
485
+ *.local
486
+
487
+ # Editor directories and files
488
+ .vscode/*
489
+ !.vscode/extensions.json
490
+ .idea
491
+ .DS_Store
492
+ *.suo
493
+ *.ntvs*
494
+ *.njsproj
495
+ *.sln
496
+ *.sw?
497
+ `;
498
+
499
+ await fs.writeFile(path.join(projectPath, '.gitignore'), gitignoreContent);
500
+ }
501
+
502
+ async function generateReactReadme(projectPath, config) {
503
+ const { projectName, folderStructure, language, styling, packageManager } = config;
504
+
505
+ const readme = `# ${projectName}
506
+
507
+ Created with InitKit CLI
508
+
509
+ ## Setup
510
+
511
+ 1. Install dependencies:
512
+ \`\`\`bash
513
+ ${packageManager} install
514
+ \`\`\`
515
+
516
+ 2. Run the development server:
517
+ \`\`\`bash
518
+ ${packageManager} ${packageManager === 'npm' ? 'run ' : ''}dev
519
+ \`\`\`
520
+
521
+ 3. Open [http://localhost:3000](http://localhost:3000)
522
+
523
+ ## Tech Stack
524
+
525
+ - **React 18** - UI library
526
+ - **Vite 6** - Build tool${language === 'typescript' ? '\n- **TypeScript** - Type safety' : ''}${styling === 'tailwind' ? '\n- **Tailwind CSS v4** - Styling' : ''}
527
+
528
+ ## Folder Structure
529
+
530
+ \`\`\`
531
+ src/
532
+ ${
533
+ folderStructure === 'feature-based'
534
+ ? `├── features/ # Feature modules
535
+ │ ├── auth/ # Authentication
536
+ │ ├── dashboard/ # Dashboard
537
+ │ └── users/ # Users
538
+ ├── shared/ # Shared code`
539
+ : folderStructure === 'atomic'
540
+ ? `├── components/ # Atomic design
541
+ │ ├── atoms/ # Basic UI elements
542
+ │ ├── molecules/ # Simple components
543
+ │ ├── organisms/ # Complex components
544
+ │ └── templates/ # Page templates
545
+ ├── pages/ # Page components`
546
+ : `├── components/ # React components
547
+ │ ├── ui/ # UI components
548
+ │ └── layout/ # Layout components
549
+ ├── hooks/ # Custom hooks
550
+ ├── services/ # API services`
551
+ }
552
+ ├── routes/ # Routing configuration
553
+ ├── lib/ # Utilities
554
+ └── public/ # Static files
555
+ \`\`\`
556
+
557
+ ## Next Steps
558
+
559
+ 1. Run \`npm create vite@latest . -- --template react${language === 'typescript' ? '-ts' : ''}\` to initialize Vite${styling === 'tailwind' ? '\n2. Install Tailwind v4: `' + packageManager + (packageManager === 'npm' ? ' install' : ' add') + ' tailwindcss@next`' : ''}
560
+ 3. Start building in \`src/features/\`
561
+ 4. Add environment variables in \`.env\`
562
+
563
+ ---
564
+
565
+ Built with InitKit
566
+ `;
567
+
568
+ await fs.writeFile(path.join(projectPath, 'README.md'), readme);
569
+ }
570
+
571
+ function generateFeatureExport(featureName) {
572
+ return `// ${featureName.toUpperCase()} Feature
573
+
574
+ // TODO: Export your components
575
+ // export { ${featureName.charAt(0).toUpperCase() + featureName.slice(1)}Component } from './components/${featureName.charAt(0).toUpperCase() + featureName.slice(1)}Component';
576
+
577
+ // TODO: Export your hooks
578
+ // export { use${featureName.charAt(0).toUpperCase() + featureName.slice(1)} } from './hooks/use${featureName.charAt(0).toUpperCase() + featureName.slice(1)}';
579
+
580
+ // TODO: Export your services
581
+ // export * from './services/${featureName}Service';
582
+
583
+ // TODO: Export your types
584
+ // export type * from './types/${featureName}.types';
585
+ `;
586
+ }