initkit 1.1.0 → 1.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.
@@ -54,7 +54,6 @@ function getQuestions(initialProjectName) {
54
54
  choices: [
55
55
  { name: 'Frontend Only', value: 'frontend' },
56
56
  { name: 'Backend Only', value: 'backend' },
57
- { name: 'Full Stack', value: 'fullstack' },
58
57
  { name: 'Node.js Library/Package', value: 'library' },
59
58
  ],
60
59
  },
@@ -68,7 +67,6 @@ function getQuestions(initialProjectName) {
68
67
  const typeDefaults = {
69
68
  frontend: 'my-frontend-app',
70
69
  backend: 'my-backend-api',
71
- fullstack: 'my-fullstack-app',
72
70
  library: 'my-package',
73
71
  };
74
72
  return typeDefaults[answers.projectType] || 'my-awesome-project';
@@ -119,41 +117,7 @@ function getQuestions(initialProjectName) {
119
117
  { name: 'Next.js (React)', value: 'nextjs' },
120
118
  { name: 'Vue.js + Vite', value: 'vue' },
121
119
  ],
122
- when: (answers) => ['frontend', 'fullstack'].includes(answers.projectType),
123
- },
124
- // Full-stack architecture type
125
- {
126
- type: 'list',
127
- name: 'fullstackType',
128
- message: 'Choose your full-stack architecture:',
129
- choices: [
130
- { name: 'Monorepo (apps/ + packages/)', value: 'monorepo' },
131
- { name: 'Traditional (separate client/ + server/)', value: 'traditional' },
132
- ],
133
- when: (answers) => answers.projectType === 'fullstack',
134
- },
135
- // Full-stack stack selection
136
- {
137
- type: 'list',
138
- name: 'stack',
139
- message: 'Choose your full-stack:',
140
- choices: (answers) => {
141
- if (answers.fullstackType === 'monorepo') {
142
- return [
143
- { name: 'Next.js + Express + MongoDB', value: 'Next.js + Express + MongoDB' },
144
- { name: 'Next.js + Express + PostgreSQL', value: 'Next.js + Express + PostgreSQL' },
145
- { name: 'React + Express + MongoDB', value: 'React + Express + MongoDB' },
146
- { name: 'React + Express + PostgreSQL', value: 'React + Express + PostgreSQL' },
147
- ];
148
- }
149
- return [
150
- { name: 'MERN (MongoDB + Express + React + Node)', value: 'MERN' },
151
- { name: 'PERN (PostgreSQL + Express + React + Node)', value: 'PERN' },
152
- { name: 'Next.js + Express', value: 'Next.js + Express' },
153
- { name: 'Laravel + React', value: 'Laravel + React' },
154
- ];
155
- },
156
- when: (answers) => answers.projectType === 'fullstack',
120
+ when: (answers) => answers.projectType === 'frontend',
157
121
  },
158
122
  // Backend framework selection
159
123
  {
@@ -264,6 +228,98 @@ function getQuestions(initialProjectName) {
264
228
  ],
265
229
  when: (answers) => answers.projectType === 'frontend',
266
230
  },
231
+ // State Management (Frontend)
232
+ {
233
+ type: 'list',
234
+ name: 'stateManagement',
235
+ message: 'Choose your state management solution:',
236
+ choices: [
237
+ { name: 'None (React Context API only)', value: 'none' },
238
+ { name: 'Redux Toolkit (Official Redux)', value: 'redux-toolkit' },
239
+ { name: 'Zustand (Lightweight)', value: 'zustand' },
240
+ { name: 'Jotai (Atomic)', value: 'jotai' },
241
+ { name: 'Recoil (Atomic)', value: 'recoil' },
242
+ { name: 'Pinia (Vue)', value: 'pinia' },
243
+ ],
244
+ default: 'none',
245
+ when: (answers) => answers.projectType === 'frontend' && answers.frontend !== 'nextjs',
246
+ },
247
+ // UI Component Library (Frontend)
248
+ {
249
+ type: 'list',
250
+ name: 'uiLibrary',
251
+ message: 'Choose a UI component library (optional):',
252
+ choices: [
253
+ { name: 'None', value: 'none' },
254
+ { name: 'shadcn/ui (Radix + Tailwind)', value: 'shadcn' },
255
+ { name: 'Material-UI (MUI)', value: 'mui' },
256
+ { name: 'Ant Design', value: 'antd' },
257
+ { name: 'Chakra UI', value: 'chakra' },
258
+ { name: 'Mantine', value: 'mantine' },
259
+ { name: 'DaisyUI (Tailwind)', value: 'daisyui' },
260
+ ],
261
+ default: 'none',
262
+ when: (answers) => answers.projectType === 'frontend',
263
+ },
264
+ // ORM/Database Tool (Backend)
265
+ {
266
+ type: 'list',
267
+ name: 'orm',
268
+ message: 'Choose an ORM/database tool:',
269
+ choices: [
270
+ { name: 'None', value: 'none' },
271
+ { name: 'Prisma (Type-safe ORM)', value: 'prisma' },
272
+ { name: 'Drizzle (Lightweight ORM)', value: 'drizzle' },
273
+ { name: 'TypeORM', value: 'typeorm' },
274
+ { name: 'Mongoose (MongoDB)', value: 'mongoose' },
275
+ ],
276
+ default: 'none',
277
+ when: (answers) => answers.projectType === 'backend',
278
+ },
279
+ // Database Selection (if ORM selected)
280
+ {
281
+ type: 'list',
282
+ name: 'database',
283
+ message: 'Choose your database:',
284
+ choices: [
285
+ { name: 'PostgreSQL', value: 'postgresql' },
286
+ { name: 'MySQL', value: 'mysql' },
287
+ { name: 'SQLite', value: 'sqlite' },
288
+ { name: 'MongoDB', value: 'mongodb' },
289
+ ],
290
+ default: 'postgresql',
291
+ when: (answers) => answers.orm && answers.orm !== 'none' && answers.orm !== 'mongoose',
292
+ },
293
+ // Authentication (Frontend/Backend)
294
+ {
295
+ type: 'list',
296
+ name: 'authentication',
297
+ message: 'Choose an authentication solution:',
298
+ choices: [
299
+ { name: 'None', value: 'none' },
300
+ { name: 'NextAuth.js', value: 'nextauth' },
301
+ { name: 'Clerk', value: 'clerk' },
302
+ { name: 'Supabase Auth', value: 'supabase' },
303
+ { name: 'Auth0', value: 'auth0' },
304
+ { name: 'Lucia', value: 'lucia' },
305
+ ],
306
+ default: 'none',
307
+ when: (answers) => ['frontend', 'backend'].includes(answers.projectType),
308
+ },
309
+ // Testing Frameworks
310
+ {
311
+ type: 'checkbox',
312
+ name: 'testing',
313
+ message: 'Select testing frameworks:',
314
+ choices: [
315
+ { name: 'Vitest (Unit/Integration)', value: 'vitest', checked: false },
316
+ { name: 'Jest (Unit/Integration)', value: 'jest', checked: false },
317
+ { name: 'Playwright (E2E)', value: 'playwright', checked: false },
318
+ { name: 'Cypress (E2E)', value: 'cypress', checked: false },
319
+ { name: 'React Testing Library', value: 'testing-library', checked: false },
320
+ ],
321
+ when: (answers) => ['frontend', 'backend'].includes(answers.projectType),
322
+ },
267
323
  // Additional Libraries (Multi-select)
268
324
  {
269
325
  type: 'checkbox',
@@ -301,11 +357,11 @@ function getQuestions(initialProjectName) {
301
357
 
302
358
  let choices = [...commonChoices];
303
359
 
304
- if (['frontend', 'fullstack'].includes(answers.projectType)) {
360
+ if (answers.projectType === 'frontend') {
305
361
  choices = [...choices, ...frontendChoices];
306
362
  }
307
363
 
308
- if (['backend', 'fullstack'].includes(answers.projectType)) {
364
+ if (answers.projectType === 'backend') {
309
365
  choices = [...choices, ...backendChoices];
310
366
  }
311
367
 
@@ -0,0 +1,402 @@
1
+ import { execCommand } from './cliRunner.js';
2
+ import chalk from 'chalk';
3
+
4
+ /**
5
+ * Install add-ons/libraries using their official installation methods
6
+ *
7
+ * This module installs additional libraries and tools using their
8
+ * official CLIs and installation commands, ensuring proper setup
9
+ * and configuration.
10
+ *
11
+ * @param {string} projectPath - Project directory
12
+ * @param {Object} config - User configuration with selected add-ons
13
+ * @returns {Promise<void>}
14
+ */
15
+ export async function installAddons(projectPath, config) {
16
+ const {
17
+ stateManagement,
18
+ uiLibrary,
19
+ database,
20
+ orm,
21
+ authentication,
22
+ testing = [],
23
+ styling,
24
+ additionalLibraries = [],
25
+ } = config;
26
+
27
+ // State Management
28
+ if (stateManagement && stateManagement !== 'none') {
29
+ console.log(chalk.cyan('\n📦 Installing state management...'));
30
+ await installStateManagement(projectPath, stateManagement, config);
31
+ }
32
+
33
+ // UI Library
34
+ if (uiLibrary && uiLibrary !== 'none') {
35
+ console.log(chalk.cyan('\n🎨 Installing UI library...'));
36
+ await installUILibrary(projectPath, uiLibrary, config);
37
+ }
38
+
39
+ // Database & ORM
40
+ if (orm && orm !== 'none') {
41
+ console.log(chalk.cyan('\n🗄️ Installing ORM...'));
42
+ await installORM(projectPath, orm, database, config);
43
+ }
44
+
45
+ // Authentication
46
+ if (authentication && authentication !== 'none') {
47
+ console.log(chalk.cyan('\n🔐 Installing authentication...'));
48
+ await installAuthentication(projectPath, authentication, config);
49
+ }
50
+
51
+ // Testing
52
+ if (testing && testing.length > 0) {
53
+ console.log(chalk.cyan('\n🧪 Installing testing frameworks...'));
54
+ await installTesting(projectPath, testing, config);
55
+ }
56
+
57
+ // Additional libraries
58
+ if (additionalLibraries && additionalLibraries.length > 0) {
59
+ console.log(chalk.cyan('\n📦 Installing additional libraries...'));
60
+ for (const lib of additionalLibraries) {
61
+ await installAdditionalLibrary(projectPath, lib, config);
62
+ }
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Install state management library
68
+ */
69
+ async function installStateManagement(projectPath, library, config) {
70
+ const { packageManager } = config;
71
+ const installCmd = getInstallCommand(packageManager);
72
+
73
+ switch (library) {
74
+ case 'redux-toolkit':
75
+ console.log(chalk.dim(' Installing Redux Toolkit and React Redux...'));
76
+ await execCommand(`${installCmd} @reduxjs/toolkit react-redux`, {
77
+ cwd: projectPath,
78
+ });
79
+ // Store setup will be done in template generator
80
+ break;
81
+
82
+ case 'zustand':
83
+ console.log(chalk.dim(' Installing Zustand...'));
84
+ await execCommand(`${installCmd} zustand`, { cwd: projectPath });
85
+ break;
86
+
87
+ case 'jotai':
88
+ console.log(chalk.dim(' Installing Jotai...'));
89
+ await execCommand(`${installCmd} jotai`, { cwd: projectPath });
90
+ break;
91
+
92
+ case 'recoil':
93
+ console.log(chalk.dim(' Installing Recoil...'));
94
+ await execCommand(`${installCmd} recoil`, { cwd: projectPath });
95
+ break;
96
+
97
+ case 'pinia':
98
+ // Pinia is usually included in Vue setup
99
+ console.log(chalk.dim(' Pinia will be configured in Vue setup...'));
100
+ break;
101
+
102
+ default:
103
+ console.log(chalk.yellow(` Unknown state management: ${library}`));
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Install UI library (some have their own CLIs!)
109
+ */
110
+ async function installUILibrary(projectPath, library, config) {
111
+ const { packageManager, language } = config;
112
+ const installCmd = getInstallCommand(packageManager);
113
+
114
+ switch (library) {
115
+ case 'shadcn':
116
+ case 'shadcn-ui':
117
+ console.log(chalk.dim(' Running shadcn-ui init...'));
118
+ // shadcn has its own CLI!
119
+ await execCommand(`npx shadcn@latest init -y`, { cwd: projectPath });
120
+ break;
121
+
122
+ case 'mui':
123
+ case 'material-ui':
124
+ console.log(chalk.dim(' Installing Material-UI...'));
125
+ await execCommand(
126
+ `${installCmd} @mui/material @emotion/react @emotion/styled @mui/icons-material`,
127
+ { cwd: projectPath }
128
+ );
129
+ break;
130
+
131
+ case 'antd':
132
+ case 'ant-design':
133
+ console.log(chalk.dim(' Installing Ant Design...'));
134
+ await execCommand(`${installCmd} antd`, { cwd: projectPath });
135
+ break;
136
+
137
+ case 'chakra':
138
+ case 'chakra-ui':
139
+ console.log(chalk.dim(' Installing Chakra UI...'));
140
+ await execCommand(
141
+ `${installCmd} @chakra-ui/react @emotion/react @emotion/styled framer-motion`,
142
+ { cwd: projectPath }
143
+ );
144
+ break;
145
+
146
+ case 'mantine':
147
+ console.log(chalk.dim(' Installing Mantine...'));
148
+ await execCommand(
149
+ `${installCmd} @mantine/core @mantine/hooks @mantine/form @mantine/notifications`,
150
+ { cwd: projectPath }
151
+ );
152
+ break;
153
+
154
+ case 'daisyui':
155
+ console.log(chalk.dim(' Installing DaisyUI...'));
156
+ await execCommand(`${installCmd} -D daisyui`, { cwd: projectPath });
157
+ break;
158
+
159
+ default:
160
+ console.log(chalk.yellow(` Unknown UI library: ${library}`));
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Install ORM (Prisma has its own CLI!)
166
+ */
167
+ async function installORM(projectPath, orm, database, config) {
168
+ const { packageManager, language } = config;
169
+ const installCmd = getInstallCommand(packageManager);
170
+
171
+ switch (orm) {
172
+ case 'prisma': {
173
+ console.log(chalk.dim(' Installing Prisma...'));
174
+ // Prisma has its own init CLI!
175
+ await execCommand(`${installCmd} -D prisma`, { cwd: projectPath });
176
+ await execCommand(`${installCmd} @prisma/client`, { cwd: projectPath });
177
+
178
+ // Initialize Prisma with database
179
+ const datasource = getDatasourceForPrisma(database);
180
+ console.log(chalk.dim(` Initializing Prisma with ${datasource}...`));
181
+ await execCommand(`npx prisma init --datasource-provider ${datasource}`, {
182
+ cwd: projectPath,
183
+ });
184
+ break;
185
+ }
186
+
187
+ case 'drizzle': {
188
+ console.log(chalk.dim(' Installing Drizzle ORM...'));
189
+ await execCommand(`${installCmd} drizzle-orm`, { cwd: projectPath });
190
+ await execCommand(`${installCmd} -D drizzle-kit`, { cwd: projectPath });
191
+
192
+ // Add database driver
193
+ const driver = getDriverForDrizzle(database);
194
+ console.log(chalk.dim(` Installing database driver: ${driver}...`));
195
+ await execCommand(`${installCmd} ${driver}`, { cwd: projectPath });
196
+ break;
197
+ }
198
+
199
+ case 'typeorm': {
200
+ console.log(chalk.dim(' Installing TypeORM...'));
201
+ await execCommand(`${installCmd} typeorm reflect-metadata`, {
202
+ cwd: projectPath,
203
+ });
204
+ if (language === 'typescript') {
205
+ await execCommand(`${installCmd} -D @types/node`, { cwd: projectPath });
206
+ }
207
+ // Add database driver
208
+ const typeormDriver = getDriverForTypeORM(database);
209
+ if (typeormDriver) {
210
+ console.log(chalk.dim(` Installing database driver: ${typeormDriver}...`));
211
+ await execCommand(`${installCmd} ${typeormDriver}`, {
212
+ cwd: projectPath,
213
+ });
214
+ }
215
+ break;
216
+ }
217
+
218
+ case 'mongoose':
219
+ console.log(chalk.dim(' Installing Mongoose...'));
220
+ await execCommand(`${installCmd} mongoose`, { cwd: projectPath });
221
+ if (language === 'typescript') {
222
+ await execCommand(`${installCmd} -D @types/mongoose`, {
223
+ cwd: projectPath,
224
+ });
225
+ }
226
+ break;
227
+
228
+ default:
229
+ console.log(chalk.yellow(` Unknown ORM: ${orm}`));
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Install authentication library
235
+ */
236
+ async function installAuthentication(projectPath, auth, config) {
237
+ const { packageManager } = config;
238
+ const installCmd = getInstallCommand(packageManager);
239
+
240
+ switch (auth) {
241
+ case 'nextauth':
242
+ case 'next-auth':
243
+ console.log(chalk.dim(' Installing NextAuth.js...'));
244
+ await execCommand(`${installCmd} next-auth`, { cwd: projectPath });
245
+ break;
246
+
247
+ case 'clerk':
248
+ console.log(chalk.dim(' Installing Clerk...'));
249
+ await execCommand(`${installCmd} @clerk/nextjs`, { cwd: projectPath });
250
+ break;
251
+
252
+ case 'supabase':
253
+ console.log(chalk.dim(' Installing Supabase client...'));
254
+ await execCommand(`${installCmd} @supabase/supabase-js`, {
255
+ cwd: projectPath,
256
+ });
257
+ break;
258
+
259
+ case 'auth0':
260
+ console.log(chalk.dim(' Installing Auth0...'));
261
+ await execCommand(`${installCmd} @auth0/nextjs-auth0`, {
262
+ cwd: projectPath,
263
+ });
264
+ break;
265
+
266
+ case 'lucia':
267
+ console.log(chalk.dim(' Installing Lucia...'));
268
+ await execCommand(`${installCmd} lucia`, { cwd: projectPath });
269
+ break;
270
+
271
+ default:
272
+ console.log(chalk.yellow(` Unknown authentication library: ${auth}`));
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Install testing frameworks (Playwright has its own CLI!)
278
+ */
279
+ async function installTesting(projectPath, testingLibs, config) {
280
+ const { packageManager } = config;
281
+ const installCmd = getInstallCommand(packageManager);
282
+
283
+ for (const lib of testingLibs) {
284
+ switch (lib) {
285
+ case 'vitest':
286
+ console.log(chalk.dim(' Installing Vitest...'));
287
+ await execCommand(`${installCmd} -D vitest`, { cwd: projectPath });
288
+ await execCommand(`${installCmd} -D @vitest/ui`, { cwd: projectPath });
289
+ break;
290
+
291
+ case 'jest':
292
+ console.log(chalk.dim(' Installing Jest...'));
293
+ await execCommand(`${installCmd} -D jest @types/jest`, {
294
+ cwd: projectPath,
295
+ });
296
+ break;
297
+
298
+ case 'playwright':
299
+ console.log(chalk.dim(' Installing Playwright...'));
300
+ // Playwright has its own init CLI!
301
+ await execCommand(`npm init playwright@latest`, { cwd: projectPath });
302
+ break;
303
+
304
+ case 'cypress':
305
+ console.log(chalk.dim(' Installing Cypress...'));
306
+ await execCommand(`${installCmd} -D cypress`, { cwd: projectPath });
307
+ break;
308
+
309
+ case 'testing-library':
310
+ case '@testing-library':
311
+ console.log(chalk.dim(' Installing React Testing Library...'));
312
+ await execCommand(
313
+ `${installCmd} -D @testing-library/react @testing-library/jest-dom @testing-library/user-event`,
314
+ { cwd: projectPath }
315
+ );
316
+ break;
317
+
318
+ default:
319
+ console.log(chalk.yellow(` Unknown testing library: ${lib}`));
320
+ }
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Install additional library
326
+ */
327
+ async function installAdditionalLibrary(projectPath, lib, config) {
328
+ const { packageManager } = config;
329
+ const installCmd = getInstallCommand(packageManager);
330
+
331
+ console.log(chalk.dim(` Installing ${lib}...`));
332
+ await execCommand(`${installCmd} ${lib}`, { cwd: projectPath });
333
+ }
334
+
335
+ /**
336
+ * Get install command for package manager
337
+ */
338
+ function getInstallCommand(packageManager) {
339
+ const commands = {
340
+ npm: 'npm install',
341
+ yarn: 'yarn add',
342
+ pnpm: 'pnpm add',
343
+ bun: 'bun add',
344
+ };
345
+ return commands[packageManager] || 'npm install';
346
+ }
347
+
348
+ /**
349
+ * Helper functions for database mappings
350
+ */
351
+ function getDatasourceForPrisma(database) {
352
+ if (!database || database === 'none') return 'postgresql';
353
+
354
+ const mapping = {
355
+ postgresql: 'postgresql',
356
+ postgres: 'postgresql',
357
+ mysql: 'mysql',
358
+ sqlite: 'sqlite',
359
+ mongodb: 'mongodb',
360
+ sqlserver: 'sqlserver',
361
+ };
362
+ return mapping[database.toLowerCase()] || 'postgresql';
363
+ }
364
+
365
+ function getDriverForDrizzle(database) {
366
+ if (!database || database === 'none') return 'pg';
367
+
368
+ const drivers = {
369
+ postgresql: 'pg',
370
+ postgres: 'pg',
371
+ mysql: 'mysql2',
372
+ sqlite: 'better-sqlite3',
373
+ };
374
+ return drivers[database.toLowerCase()] || 'pg';
375
+ }
376
+
377
+ function getDriverForTypeORM(database) {
378
+ if (!database || database === 'none') return null;
379
+
380
+ const drivers = {
381
+ postgresql: 'pg',
382
+ postgres: 'pg',
383
+ mysql: 'mysql2',
384
+ sqlite: 'sqlite3',
385
+ mongodb: 'mongodb',
386
+ };
387
+ return drivers[database.toLowerCase()] || null;
388
+ }
389
+
390
+ /**
391
+ * Check if user selected any add-ons
392
+ */
393
+ export function hasAddons(config) {
394
+ return !!(
395
+ (config.stateManagement && config.stateManagement !== 'none') ||
396
+ (config.uiLibrary && config.uiLibrary !== 'none') ||
397
+ (config.orm && config.orm !== 'none') ||
398
+ (config.authentication && config.authentication !== 'none') ||
399
+ (config.testing && config.testing.length > 0) ||
400
+ (config.additionalLibraries && config.additionalLibraries.length > 0)
401
+ );
402
+ }