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.
- package/LICENSE +21 -0
- package/README.md +844 -0
- package/bin/index.js +8 -0
- package/package.json +79 -0
- package/src/cli.js +264 -0
- package/src/commands/create.js +264 -0
- package/src/index.js +9 -0
- package/src/prompts/questions.js +358 -0
- package/src/templates/express.js +915 -0
- package/src/templates/fullstack.js +1236 -0
- package/src/templates/nextjs.js +620 -0
- package/src/templates/react.js +586 -0
- package/src/templates/vue.js +545 -0
- package/src/utils/errorHandler.js +275 -0
- package/src/utils/git.js +69 -0
- package/src/utils/packageManager.js +90 -0
- package/src/utils/templateGenerator.js +365 -0
- package/src/utils/validation.js +186 -0
- package/src/utils/versionFetcher.js +128 -0
|
@@ -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 };
|