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,358 @@
1
+ import chalk from 'chalk';
2
+ import validateNpmName from 'validate-npm-package-name';
3
+ import {
4
+ validateProjectName,
5
+ checkDirectoryExists,
6
+ suggestProjectName,
7
+ } from '../utils/validation.js';
8
+
9
+ /**
10
+ * Get interactive prompts for project configuration
11
+ *
12
+ * Generates a dynamic question flow based on user selections:
13
+ * 1. Project Type (Frontend/Backend/Full Stack/Library)
14
+ * 2. Project Name (with real-time validation)
15
+ * 3. Framework Selection (conditional based on project type)
16
+ * 4. Language Choice (TypeScript/JavaScript)
17
+ * 5. Additional Features (styling, testing, linting, etc.)
18
+ * 6. Configuration Options (package manager, Git, etc.)
19
+ *
20
+ * Uses conditional logic with 'when' to show/hide questions based on previous answers.
21
+ * Provides real-time validation feedback with colored icons and suggestions.
22
+ *
23
+ * @param {string|null} initialProjectName - Project name from CLI argument (e.g., 'initkit my-app')
24
+ * If provided, skips the project name prompt
25
+ *
26
+ * @returns {Array<Object>} Array of Inquirer.js question objects with:
27
+ * - type: Question type ('list'|'input'|'confirm'|'checkbox')
28
+ * - name: Answer key to store the value
29
+ * - message: Question text displayed to user
30
+ * - choices: Array of options for list/checkbox questions
31
+ * - when: Function to conditionally show question based on previous answers
32
+ * - validate: Function to validate user input with helpful error messages
33
+ * - transformer: Function to provide real-time visual feedback during input
34
+ * - default: Default value or function to compute default based on previous answers
35
+ *
36
+ * @example
37
+ * // Without initial project name (user will be prompted)
38
+ * const questions = getQuestions(null);
39
+ * const answers = await inquirer.prompt(questions);
40
+ *
41
+ * @example
42
+ * // With initial project name from CLI
43
+ * const questions = getQuestions('my-awesome-app');
44
+ * const answers = await inquirer.prompt(questions);
45
+ * // User will NOT be asked for project name
46
+ */
47
+ function getQuestions(initialProjectName) {
48
+ const questions = [
49
+ // Project Type - First question to determine the flow
50
+ {
51
+ type: 'list',
52
+ name: 'projectType',
53
+ message: 'What type of project do you want to create?',
54
+ choices: [
55
+ { name: 'Frontend Only', value: 'frontend' },
56
+ { name: 'Backend Only', value: 'backend' },
57
+ { name: 'Full Stack', value: 'fullstack' },
58
+ { name: 'Node.js Library/Package', value: 'library' },
59
+ ],
60
+ },
61
+ // Project Name - With enhanced validation
62
+ {
63
+ type: 'input',
64
+ name: 'projectName',
65
+ message: 'What is your project name?',
66
+ default: (answers) => {
67
+ if (initialProjectName) return initialProjectName;
68
+ const typeDefaults = {
69
+ frontend: 'my-frontend-app',
70
+ backend: 'my-backend-api',
71
+ fullstack: 'my-fullstack-app',
72
+ library: 'my-package',
73
+ };
74
+ return typeDefaults[answers.projectType] || 'my-awesome-project';
75
+ },
76
+ when: !initialProjectName,
77
+ validate: (input) => {
78
+ const validation = validateProjectName(input);
79
+ if (!validation.valid) {
80
+ const suggestion = suggestProjectName(input);
81
+ return `${chalk.red('✗')} ${validation.errors[0]}\n ${chalk.cyan('💡 Try:')} ${chalk.green(suggestion)}`;
82
+ }
83
+
84
+ // Check if directory exists
85
+ const dirCheck = checkDirectoryExists(input);
86
+ if (dirCheck.exists) {
87
+ return `${chalk.red('✗')} Directory "${input}" already exists.\n ${chalk.cyan('💡 Try:')} Choose a different name or remove the existing folder`;
88
+ }
89
+
90
+ return true;
91
+ },
92
+ transformer: (input) => {
93
+ // Show real-time validation feedback with icons
94
+ if (!input || input.trim() === '') {
95
+ return chalk.gray(input);
96
+ }
97
+
98
+ const validation = validateProjectName(input);
99
+ const dirCheck = checkDirectoryExists(input);
100
+
101
+ if (validation.valid && !dirCheck.exists) {
102
+ return chalk.green('✓ ') + chalk.green(input);
103
+ } else if (!validation.valid) {
104
+ return chalk.red('✗ ') + chalk.red(input);
105
+ } else if (dirCheck.exists) {
106
+ return chalk.yellow('⚠ ') + chalk.yellow(input) + chalk.gray(' (exists)');
107
+ }
108
+
109
+ return input;
110
+ },
111
+ },
112
+ // Frontend framework selection
113
+ {
114
+ type: 'list',
115
+ name: 'frontend',
116
+ message: 'Choose your frontend framework:',
117
+ choices: [
118
+ { name: 'React + Vite', value: 'react' },
119
+ { name: 'Next.js (React)', value: 'nextjs' },
120
+ { name: 'Vue.js + Vite', value: 'vue' },
121
+ ],
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',
157
+ },
158
+ // Backend framework selection
159
+ {
160
+ type: 'list',
161
+ name: 'backend',
162
+ message: 'Choose your backend framework:',
163
+ choices: [{ name: 'Express.js', value: 'express' }],
164
+ when: (answers) => answers.projectType === 'backend',
165
+ },
166
+ // Database selection (for backend only)
167
+ {
168
+ type: 'list',
169
+ name: 'database',
170
+ message: 'Choose your database:',
171
+ choices: [
172
+ { name: 'Prisma (PostgreSQL/MySQL)', value: 'prisma' },
173
+ { name: 'MongoDB', value: 'mongodb' },
174
+ { name: 'PostgreSQL', value: 'postgresql' },
175
+ { name: 'MySQL', value: 'mysql' },
176
+ { name: 'None', value: 'none' },
177
+ ],
178
+ when: (answers) => answers.projectType === 'backend',
179
+ },
180
+ // Language Choice - TypeScript or JavaScript
181
+ {
182
+ type: 'list',
183
+ name: 'language',
184
+ message: 'Choose your programming language:',
185
+ choices: [
186
+ { name: 'TypeScript (Recommended)', value: 'typescript' },
187
+ { name: 'JavaScript', value: 'javascript' },
188
+ ],
189
+ default: 'typescript',
190
+ },
191
+ // Folder Structure Preference - Frontend
192
+ {
193
+ type: 'list',
194
+ name: 'folderStructure',
195
+ message: 'Choose your folder structure preference:',
196
+ choices: [
197
+ {
198
+ name: 'Feature-based (Organize by feature/module)',
199
+ value: 'feature-based',
200
+ },
201
+ {
202
+ name: 'Component-based (Organize by component type)',
203
+ value: 'component-based',
204
+ },
205
+ {
206
+ name: 'Atomic Design (atoms, molecules, organisms)',
207
+ value: 'atomic',
208
+ },
209
+ ],
210
+ default: 'feature-based',
211
+ when: (answers) => ['frontend'].includes(answers.projectType),
212
+ },
213
+ // Folder Structure Preference - Backend
214
+ {
215
+ type: 'list',
216
+ name: 'folderStructure',
217
+ message: 'Choose your backend architecture:',
218
+ choices: [
219
+ {
220
+ name: 'MVC (Model-View-Controller)',
221
+ value: 'mvc',
222
+ },
223
+ {
224
+ name: 'Clean Architecture (Domain-driven layers)',
225
+ value: 'clean-architecture',
226
+ },
227
+ {
228
+ name: 'Feature-based (Organize by feature/module)',
229
+ value: 'feature-based',
230
+ },
231
+ {
232
+ name: 'Layered (Controller-Service-Repository)',
233
+ value: 'layered',
234
+ },
235
+ ],
236
+ default: 'mvc',
237
+ when: (answers) => ['backend'].includes(answers.projectType),
238
+ },
239
+ // TypeScript Configuration Level (if TypeScript selected)
240
+ {
241
+ type: 'list',
242
+ name: 'typescriptStrict',
243
+ message: 'TypeScript strictness level:',
244
+ choices: [
245
+ { name: 'Strict (Recommended for new projects)', value: 'strict' },
246
+ { name: 'Moderate (Balanced type checking)', value: 'moderate' },
247
+ { name: 'Relaxed (Minimal type checking)', value: 'relaxed' },
248
+ ],
249
+ default: 'strict',
250
+ when: (answers) => answers.language === 'typescript',
251
+ },
252
+ // CSS framework/preprocessor
253
+ {
254
+ type: 'list',
255
+ name: 'styling',
256
+ message: 'Choose your styling solution:',
257
+ choices: [
258
+ { name: 'Tailwind CSS', value: 'tailwind' },
259
+ { name: 'CSS Modules', value: 'css-modules' },
260
+ { name: 'Styled Components', value: 'styled-components' },
261
+ { name: 'Emotion', value: 'emotion' },
262
+ { name: 'Sass/SCSS', value: 'sass' },
263
+ { name: 'Plain CSS', value: 'css' },
264
+ ],
265
+ when: (answers) => answers.projectType === 'frontend',
266
+ },
267
+ // Additional Libraries (Multi-select)
268
+ {
269
+ type: 'checkbox',
270
+ name: 'additionalLibraries',
271
+ message: 'Select additional libraries to include:',
272
+ choices: (answers) => {
273
+ const commonChoices = [
274
+ { name: 'Axios (HTTP client)', value: 'axios', checked: false },
275
+ { name: 'Lodash (Utility library)', value: 'lodash', checked: false },
276
+ { name: 'Date-fns (Date utility)', value: 'date-fns', checked: false },
277
+ { name: 'Zod (Schema validation)', value: 'zod', checked: false },
278
+ ];
279
+
280
+ const frontendChoices = [
281
+ { name: 'TanStack Query (Data fetching)', value: 'tanstack-query', checked: false },
282
+ { name: 'React Router (Routing)', value: 'react-router', checked: false },
283
+ { name: 'Redux Toolkit (State management)', value: 'redux-toolkit', checked: false },
284
+ { name: 'Zustand (State management)', value: 'zustand', checked: false },
285
+ { name: 'Jotai (State management)', value: 'jotai', checked: false },
286
+ { name: 'React Hook Form (Forms)', value: 'react-hook-form', checked: false },
287
+ { name: 'Framer Motion (Animations)', value: 'framer-motion', checked: false },
288
+ { name: 'React Icons (Icon library)', value: 'react-icons', checked: false },
289
+ { name: 'Radix UI (Headless components)', value: 'radix-ui', checked: false },
290
+ { name: 'ShadCN UI (Component library)', value: 'shadcn-ui', checked: false },
291
+ ];
292
+
293
+ const backendChoices = [
294
+ { name: 'JWT (Authentication)', value: 'jwt', checked: false },
295
+ { name: 'Bcrypt (Password hashing)', value: 'bcrypt', checked: false },
296
+ { name: 'Winston (Logging)', value: 'winston', checked: false },
297
+ { name: 'Swagger/OpenAPI (API docs)', value: 'swagger', checked: false },
298
+ { name: 'Rate Limiting', value: 'rate-limit', checked: false },
299
+ { name: 'Docker Setup', value: 'docker', checked: false },
300
+ ];
301
+
302
+ let choices = [...commonChoices];
303
+
304
+ if (['frontend', 'fullstack'].includes(answers.projectType)) {
305
+ choices = [...choices, ...frontendChoices];
306
+ }
307
+
308
+ if (['backend', 'fullstack'].includes(answers.projectType)) {
309
+ choices = [...choices, ...backendChoices];
310
+ }
311
+
312
+ return choices;
313
+ },
314
+ },
315
+ // Development Tools & Features
316
+ {
317
+ type: 'checkbox',
318
+ name: 'features',
319
+ message: 'Select development tools and features:',
320
+ choices: [
321
+ { name: 'ESLint (Code linting)', value: 'eslint', checked: true },
322
+ { name: 'Prettier (Code formatting)', value: 'prettier', checked: true },
323
+ { name: 'Husky (Git hooks)', value: 'husky', checked: false },
324
+ { name: 'Lint-staged (Run linters on staged files)', value: 'lint-staged', checked: false },
325
+ { name: 'Jest (Unit testing)', value: 'jest', checked: false },
326
+ { name: 'Vitest (Fast unit testing)', value: 'vitest', checked: false },
327
+ { name: 'Docker configuration', value: 'docker', checked: false },
328
+ { name: 'GitHub Actions CI/CD', value: 'github-actions', checked: false },
329
+ { name: 'Environment variables (.env)', value: 'dotenv', checked: true },
330
+ { name: 'EditorConfig', value: 'editorconfig', checked: true },
331
+ ],
332
+ },
333
+ // Package manager
334
+ {
335
+ type: 'list',
336
+ name: 'packageManager',
337
+ message: 'Choose your package manager:',
338
+ choices: [
339
+ { name: 'npm', value: 'npm' },
340
+ { name: 'yarn', value: 'yarn' },
341
+ { name: 'pnpm', value: 'pnpm' },
342
+ { name: 'bun', value: 'bun' },
343
+ ],
344
+ default: 'npm',
345
+ },
346
+ // Git initialization
347
+ {
348
+ type: 'confirm',
349
+ name: 'useGit',
350
+ message: 'Initialize Git repository?',
351
+ default: true,
352
+ },
353
+ ];
354
+
355
+ return questions;
356
+ }
357
+
358
+ export { getQuestions };