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.
- package/package.json +2 -5
- package/src/cli.js +77 -34
- package/src/commands/create.js +173 -84
- package/src/prompts/questions.js +95 -39
- package/src/utils/addonInstaller.js +402 -0
- package/src/utils/cliRunner.js +146 -0
- package/src/utils/frameworkBootstrap.js +304 -0
- package/src/utils/templateGenerator.js +191 -171
- package/src/templates/express.js +0 -915
- package/src/templates/fullstack.js +0 -1236
- package/src/templates/nextjs.js +0 -620
- package/src/templates/react.js +0 -586
- package/src/templates/vue.js +0 -545
package/src/prompts/questions.js
CHANGED
|
@@ -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) =>
|
|
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 (
|
|
360
|
+
if (answers.projectType === 'frontend') {
|
|
305
361
|
choices = [...choices, ...frontendChoices];
|
|
306
362
|
}
|
|
307
363
|
|
|
308
|
-
if (
|
|
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
|
+
}
|