initkit 1.1.0 → 1.2.1

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.
@@ -3,42 +3,42 @@ import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import ora from 'ora';
5
5
  import { generateTemplate } from '../utils/templateGenerator.js';
6
- import { installDependencies } from '../utils/packageManager.js';
7
6
  import { initGit } from '../utils/git.js';
7
+ import { bootstrapWithOfficialCLI } from '../utils/frameworkBootstrap.js';
8
+ import { installAddons, hasAddons } from '../utils/addonInstaller.js';
8
9
  import {
9
10
  CLIError,
10
11
  ERROR_CODES,
11
12
  withErrorHandling,
12
13
  rollbackProject,
13
- safeFileOperation,
14
14
  } from '../utils/errorHandler.js';
15
15
 
16
16
  /**
17
17
  * Create a new project based on user configuration
18
- *
18
+ *
19
19
  * This is the main orchestration function that:
20
20
  * 1. Creates the project directory
21
21
  * 2. Generates template files
22
22
  * 3. Installs dependencies
23
23
  * 4. Initializes Git repository
24
24
  * 5. Handles errors with automatic rollback
25
- *
25
+ *
26
26
  * @param {Object} answers - User's configuration from interactive prompts
27
27
  * @param {string} answers.projectName - Name of the project (used for directory)
28
- * @param {string} answers.projectType - Type of project ('frontend'|'backend'|'fullstack'|'library')
28
+ * @param {string} answers.projectType - Type of project ('frontend'|'backend'|'library')
29
29
  * @param {string} [answers.frontend] - Frontend framework choice
30
30
  * @param {string} [answers.backend] - Backend framework choice
31
31
  * @param {string} answers.language - Programming language ('typescript'|'javascript')
32
32
  * @param {string} answers.packageManager - Package manager ('npm'|'yarn'|'pnpm')
33
33
  * @param {boolean} [answers.gitInit] - Whether to initialize Git repository
34
- *
34
+ *
35
35
  * @param {Object} [options={}] - Additional command-line options
36
36
  * @param {boolean} [options.verbose=false] - Show detailed output during creation
37
37
  * @param {string} [options.projectPath] - Custom path for project (overrides answers.projectName)
38
- *
38
+ *
39
39
  * @returns {Promise<void>}
40
40
  * @throws {CLIError} If project creation fails at any step (automatic rollback triggered)
41
- *
41
+ *
42
42
  * @example
43
43
  * // Basic usage
44
44
  * await createProject({
@@ -49,7 +49,7 @@ import {
49
49
  * packageManager: 'npm',
50
50
  * gitInit: true
51
51
  * });
52
- *
52
+ *
53
53
  * @example
54
54
  * // With verbose output
55
55
  * await createProject(answers, { verbose: true });
@@ -57,10 +57,18 @@ import {
57
57
  async function createProject(answers, options = {}) {
58
58
  const { verbose = false, projectPath: customProjectPath } = options;
59
59
  const projectPath = customProjectPath || path.join(process.cwd(), answers.projectName);
60
- let spinner;
60
+ let spinner = ora();
61
61
 
62
62
  try {
63
- // Step 1: Check if directory already exists
63
+ // ============================================
64
+ // STEP 1: Bootstrap with Official Framework CLI
65
+ // ============================================
66
+ spinner = ora({
67
+ text: 'Bootstrapping project with official CLI...',
68
+ color: 'cyan',
69
+ }).start();
70
+
71
+ // Check if directory already exists
64
72
  if (await fs.pathExists(projectPath)) {
65
73
  throw new CLIError(
66
74
  `Directory "${answers.projectName}" already exists!`,
@@ -69,103 +77,180 @@ async function createProject(answers, options = {}) {
69
77
  );
70
78
  }
71
79
 
72
- // Step 2: Create project directory
73
- spinner = ora({
74
- text: 'Creating project structure...',
75
- color: 'cyan',
76
- }).start();
80
+ await withErrorHandling(() => bootstrapWithOfficialCLI(projectPath, answers), {
81
+ projectPath,
82
+ rollback: true,
83
+ errorCode: ERROR_CODES.CREATION_FAILED,
84
+ context: { step: 'CLI bootstrapping' },
85
+ });
77
86
 
78
- await safeFileOperation(
79
- () => fs.ensureDir(projectPath),
80
- 'Creating project directory'
81
- );
87
+ spinner.succeed(chalk.green('✓ Base project created'));
82
88
 
83
- if (verbose) {
84
- spinner.info(chalk.gray(`Created directory: ${projectPath}`));
85
- spinner.start('Generating project files...');
86
- }
89
+ // ============================================
90
+ // STEP 2: Install Add-ons with Official CLIs/Commands
91
+ // ============================================
92
+ if (hasAddons(answers)) {
93
+ spinner = ora({
94
+ text: 'Installing selected libraries and add-ons...',
95
+ color: 'cyan',
96
+ }).start();
87
97
 
88
- // Step 3: Generate project files from templates
89
- await withErrorHandling(
90
- () => generateTemplate(projectPath, answers),
91
- {
98
+ await withErrorHandling(() => installAddons(projectPath, answers), {
92
99
  projectPath,
93
100
  rollback: true,
94
101
  errorCode: ERROR_CODES.CREATION_FAILED,
95
- context: { step: 'template generation' },
96
- }
97
- );
102
+ context: { step: 'add-on installation' },
103
+ });
104
+
105
+ spinner.succeed(chalk.green('✓ Add-ons installed'));
106
+ }
98
107
 
99
- spinner.succeed(chalk.green('Project structure created'));
108
+ // ============================================
109
+ // STEP 3: Enhance with Custom Folder Structure
110
+ // ============================================
111
+ spinner = ora({
112
+ text: 'Creating custom folder structure...',
113
+ color: 'cyan',
114
+ }).start();
100
115
 
101
- // Step 4: Initialize Git if requested
116
+ await withErrorHandling(() => generateTemplate(projectPath, answers), {
117
+ projectPath,
118
+ rollback: true,
119
+ errorCode: ERROR_CODES.CREATION_FAILED,
120
+ context: { step: 'folder structure enhancement' },
121
+ });
122
+
123
+ spinner.succeed(chalk.green('✓ Folder structure configured'));
124
+
125
+ // ============================================
126
+ // STEP 4: Install Dependencies
127
+ // ============================================
128
+ spinner = ora({
129
+ text: `Installing dependencies with ${answers.packageManager}...`,
130
+ color: 'cyan',
131
+ }).start();
132
+
133
+ try {
134
+ const installCmd = getInstallCommand(answers.packageManager);
135
+ const { execCommand } = await import('../utils/cliRunner.js');
136
+ await execCommand(installCmd, { cwd: projectPath });
137
+ spinner.succeed(chalk.green('✓ Dependencies installed'));
138
+ } catch (error) {
139
+ spinner.warn(chalk.yellow('⚠ Dependency installation had issues'));
140
+ console.log(chalk.gray(` You can run '${answers.packageManager} install' manually\\n`));
141
+ }
142
+
143
+ // ============================================
144
+ // STEP 5: Initialize Git Repository
145
+ // ============================================
102
146
  if (answers.useGit) {
103
- const gitSpinner = ora('Initializing Git repository...').start();
104
-
147
+ spinner = ora({
148
+ text: 'Initializing Git repository...',
149
+ color: 'cyan',
150
+ }).start();
151
+
105
152
  try {
106
153
  await initGit(projectPath);
107
- gitSpinner.succeed(chalk.green('Git repository initialized'));
154
+ spinner.succeed(chalk.green('Git initialized'));
108
155
  } catch (error) {
109
- gitSpinner.warn(chalk.yellow('Git initialization skipped'));
110
-
111
- if (verbose) {
112
- console.log(chalk.gray(` Reason: ${error.message}`));
113
- }
114
- }
115
- }
156
+ spinner.warn(chalk.yellow('Git initialization skipped'));
116
157
 
117
- // Step 5: Install dependencies
118
- if (answers.installDependencies !== false) {
119
- try {
120
- await installDependencies(projectPath, answers.packageManager, {
121
- verbose,
122
- });
123
- } catch (error) {
124
- // Don't fail the entire process if install fails
125
- console.log(chalk.yellow('\nDependency installation failed'));
126
- console.log(chalk.gray(' You can install them manually later\n'));
127
-
128
158
  if (verbose) {
129
- console.log(chalk.gray(` Error: ${error.message}`));
159
+ console.log(chalk.gray(` Reason: ${error.message}`));
130
160
  }
131
161
  }
132
- } else {
133
- console.log(chalk.gray('\n Skipping dependency installation (--no-install)'));
134
162
  }
135
163
 
136
- // Step 6: Display comprehensive success summary
137
- displaySuccessSummary(answers, projectPath, verbose);
138
-
164
+ // ============================================
165
+ // SUCCESS!
166
+ // ============================================
167
+ console.log('\n');
168
+ displaySuccessMessage(answers, projectPath);
139
169
  } catch (error) {
140
170
  // Stop any running spinners
141
171
  if (spinner && spinner.isSpinning) {
142
- spinner.fail(chalk.red('Project creation failed'));
172
+ spinner.fail(chalk.red('Project creation failed'));
143
173
  }
144
174
 
175
+ console.log(chalk.red('\n✗ Error: ' + error.message));
176
+
177
+ // Rollback project directory
178
+ await rollbackProject(projectPath);
179
+
145
180
  // If it's already a CLIError, just re-throw it
146
181
  if (error instanceof CLIError) {
147
182
  throw error;
148
183
  }
149
184
 
150
185
  // Wrap unexpected errors
151
- throw new CLIError(
152
- `Failed to create project: ${error.message}`,
153
- ERROR_CODES.CREATION_FAILED,
154
- { originalError: error.name, path: projectPath }
155
- );
186
+ throw new CLIError(`Failed to create project: ${error.message}`, ERROR_CODES.CREATION_FAILED, {
187
+ originalError: error.name,
188
+ path: projectPath,
189
+ });
156
190
  }
157
191
  }
158
192
 
159
193
  /**
160
- * Display comprehensive success summary after project creation
194
+ * Display success message after project creation
161
195
  * @param {Object} answers - Project configuration
162
196
  * @param {string} projectPath - Path to the project
163
- * @param {boolean} verbose - Show detailed information
164
197
  */
165
- function displaySuccessSummary(answers, projectPath, verbose = false) {
198
+ function displaySuccessMessage(answers, projectPath) {
199
+ const projectName = path.basename(projectPath);
200
+
201
+ console.log(chalk.green.bold('🎉 Project created successfully!\n'));
202
+ console.log(chalk.cyan('Next steps:'));
203
+ console.log(chalk.white(` cd ${projectName}`));
204
+ console.log(chalk.white(` ${answers.packageManager} run dev\n`));
205
+
206
+ // Show installed add-ons
207
+ if (hasAddons(answers)) {
208
+ console.log(chalk.cyan('Installed add-ons:'));
209
+ if (answers.stateManagement && answers.stateManagement !== 'none') {
210
+ console.log(chalk.white(` ✓ State management: ${answers.stateManagement}`));
211
+ }
212
+ if (answers.uiLibrary && answers.uiLibrary !== 'none') {
213
+ console.log(chalk.white(` ✓ UI library: ${answers.uiLibrary}`));
214
+ }
215
+ if (answers.orm && answers.orm !== 'none') {
216
+ console.log(chalk.white(` ✓ ORM: ${answers.orm}`));
217
+ }
218
+ if (answers.authentication && answers.authentication !== 'none') {
219
+ console.log(chalk.white(` ✓ Authentication: ${answers.authentication}`));
220
+ }
221
+ if (answers.testing && answers.testing.length > 0) {
222
+ console.log(chalk.white(` ✓ Testing: ${answers.testing.join(', ')}`));
223
+ }
224
+ console.log();
225
+ }
226
+
227
+ console.log(chalk.gray('Happy coding! 🚀\n'));
228
+ }
229
+
230
+ /** * Get install command for package manager
231
+ */
232
+ function getInstallCommand(packageManager) {
233
+ const commands = {
234
+ npm: 'npm install',
235
+ yarn: 'yarn install',
236
+ pnpm: 'pnpm install',
237
+ bun: 'bun install',
238
+ };
239
+ return commands[packageManager] || 'npm install';
240
+ }
241
+
242
+ /** * Display comprehensive success summary after project creation (LEGACY - DEPRECATED)
243
+ * This function is kept for backward compatibility but is no longer used
244
+ * @deprecated Use displaySuccessMessage instead
245
+ */
246
+ function displaySuccessSummary(answers, projectPath, _verbose = false) {
166
247
  console.log('');
167
248
  console.log(chalk.green('╔════════════════════════════════════════════════════════════╗'));
168
- console.log(chalk.green('║') + chalk.green.bold(' ✨ Project Created Successfully! ✨ ') + chalk.green('║'));
249
+ console.log(
250
+ chalk.green('║') +
251
+ chalk.green.bold(' ✨ Project Created Successfully! ✨ ') +
252
+ chalk.green('║')
253
+ );
169
254
  console.log(chalk.green('╚════════════════════════════════════════════════════════════╝'));
170
255
  console.log('');
171
256
 
@@ -173,20 +258,18 @@ function displaySuccessSummary(answers, projectPath, verbose = false) {
173
258
  console.log(chalk.cyan.bold(' 📦 Project Information'));
174
259
  console.log(chalk.white(` ${chalk.gray('Name:')} ${chalk.bold(answers.projectName)}`));
175
260
  console.log(chalk.white(` ${chalk.gray('Type:')} ${answers.projectType}`));
176
- console.log(chalk.white(` ${chalk.gray('Language:')} ${answers.language || 'JavaScript'}`));
177
-
261
+ console.log(
262
+ chalk.white(` ${chalk.gray('Language:')} ${answers.language || 'JavaScript'}`)
263
+ );
264
+
178
265
  if (answers.frontend) {
179
266
  console.log(chalk.white(` ${chalk.gray('Frontend:')} ${answers.frontend}`));
180
267
  }
181
-
268
+
182
269
  if (answers.backend) {
183
270
  console.log(chalk.white(` ${chalk.gray('Backend:')} ${answers.backend}`));
184
271
  }
185
272
 
186
- if (answers.fullstackType) {
187
- console.log(chalk.white(` ${chalk.gray('Architecture:')} ${answers.fullstackType}`));
188
- }
189
-
190
273
  if (answers.database && answers.database !== 'none') {
191
274
  console.log(chalk.white(` ${chalk.gray('Database:')} ${answers.database}`));
192
275
  }
@@ -200,7 +283,7 @@ function displaySuccessSummary(answers, projectPath, verbose = false) {
200
283
  // Features section
201
284
  if (answers.features && answers.features.length > 0) {
202
285
  console.log(chalk.cyan.bold(' ⚙️ Configured Features'));
203
- answers.features.forEach(feature => {
286
+ answers.features.forEach((feature) => {
204
287
  console.log(chalk.white(` ${chalk.green('✓')} ${feature}`));
205
288
  });
206
289
  console.log('');
@@ -209,7 +292,7 @@ function displaySuccessSummary(answers, projectPath, verbose = false) {
209
292
  // Additional libraries
210
293
  if (answers.additionalLibraries && answers.additionalLibraries.length > 0) {
211
294
  console.log(chalk.cyan.bold(' 📚 Additional Libraries'));
212
- answers.additionalLibraries.slice(0, 5).forEach(lib => {
295
+ answers.additionalLibraries.slice(0, 5).forEach((lib) => {
213
296
  console.log(chalk.white(` ${chalk.green('✓')} ${lib}`));
214
297
  });
215
298
  if (answers.additionalLibraries.length > 5) {
@@ -232,19 +315,25 @@ function displaySuccessSummary(answers, projectPath, verbose = false) {
232
315
  console.log(chalk.white(` ${chalk.yellow('1.')} Navigate to your project:`));
233
316
  console.log(chalk.gray(` cd ${answers.projectName}`));
234
317
  console.log('');
235
-
318
+
236
319
  if (!packagesInstalled) {
237
320
  console.log(chalk.white(` ${chalk.yellow('2.')} Install dependencies:`));
238
321
  console.log(chalk.gray(` ${answers.packageManager} install`));
239
322
  console.log('');
240
323
  }
241
-
324
+
242
325
  const stepNum = packagesInstalled ? 2 : 3;
243
326
  console.log(chalk.white(` ${chalk.yellow(stepNum + '.')} Start development server:`));
244
- console.log(chalk.gray(` ${answers.packageManager} ${answers.packageManager === 'npm' ? 'run ' : ''}dev`));
327
+ console.log(
328
+ chalk.gray(
329
+ ` ${answers.packageManager} ${answers.packageManager === 'npm' ? 'run ' : ''}dev`
330
+ )
331
+ );
245
332
  console.log('');
246
-
247
- console.log(chalk.white(` ${chalk.yellow((stepNum + 1) + '.')} Read the README for more info:`));
333
+
334
+ console.log(
335
+ chalk.white(` ${chalk.yellow(stepNum + 1 + '.')} Read the README for more info:`)
336
+ );
248
337
  console.log(chalk.gray(` cat README.md`));
249
338
  console.log('');
250
339
 
@@ -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