create-modern-react 1.0.1 → 2.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.
Files changed (58) hide show
  1. package/README.md +270 -72
  2. package/bin/index.js +9 -7
  3. package/lib/prompts.js +132 -150
  4. package/lib/setup.js +264 -143
  5. package/package.json +17 -8
  6. package/templates/base/.env.example +9 -0
  7. package/templates/base/.eslintrc.cjs +37 -0
  8. package/templates/base/.prettierrc +11 -0
  9. package/templates/base/components.json +17 -0
  10. package/templates/base/index.html +3 -2
  11. package/templates/base/package.json +34 -15
  12. package/templates/base/postcss.config.js +6 -0
  13. package/templates/base/src/App.tsx +7 -22
  14. package/templates/base/src/components/layout/error-boundary.tsx +60 -0
  15. package/templates/base/src/components/layout/index.ts +2 -0
  16. package/templates/base/src/components/layout/root-layout.tsx +36 -0
  17. package/templates/base/src/components/ui/button.tsx +55 -0
  18. package/templates/base/src/components/ui/card.tsx +85 -0
  19. package/templates/base/src/components/ui/index.ts +12 -0
  20. package/templates/base/src/components/ui/input.tsx +24 -0
  21. package/templates/base/src/components/ui/separator.tsx +29 -0
  22. package/templates/base/src/components/ui/skeleton.tsx +15 -0
  23. package/templates/base/src/hooks/index.ts +3 -0
  24. package/templates/base/src/hooks/use-cancel-token.ts +63 -0
  25. package/templates/base/src/hooks/use-debounce.ts +29 -0
  26. package/templates/base/src/hooks/use-loader.ts +39 -0
  27. package/templates/base/src/index.css +74 -61
  28. package/templates/base/src/lib/utils.ts +14 -0
  29. package/templates/base/src/main.tsx +6 -6
  30. package/templates/base/src/providers/index.tsx +27 -0
  31. package/templates/base/src/providers/theme-provider.tsx +92 -0
  32. package/templates/base/src/routes/index.tsx +40 -0
  33. package/templates/base/src/routes/routes.ts +36 -0
  34. package/templates/base/src/screens/home/index.tsx +132 -0
  35. package/templates/base/src/screens/not-found/index.tsx +29 -0
  36. package/templates/base/src/services/alertify-services.ts +133 -0
  37. package/templates/base/src/services/api/api-helpers.ts +130 -0
  38. package/templates/base/src/services/api/axios-instance.ts +77 -0
  39. package/templates/base/src/services/api/index.ts +9 -0
  40. package/templates/base/src/services/index.ts +2 -0
  41. package/templates/base/src/types/index.ts +55 -0
  42. package/templates/base/src/vite-env.d.ts +31 -0
  43. package/templates/base/tailwind.config.js +77 -0
  44. package/templates/base/tsconfig.json +5 -4
  45. package/templates/base/tsconfig.node.json +22 -0
  46. package/templates/base/vite.config.ts +68 -7
  47. package/templates/optional/antd/config-provider.tsx +33 -0
  48. package/templates/optional/antd/index.ts +2 -0
  49. package/templates/optional/antd/styles/antd-overrides.css +104 -0
  50. package/templates/optional/antd/theme.ts +75 -0
  51. package/templates/optional/husky/.husky/pre-commit +1 -0
  52. package/templates/optional/husky/.lintstagedrc.json +6 -0
  53. package/templates/optional/redux/hooks.ts +17 -0
  54. package/templates/optional/redux/index.ts +13 -0
  55. package/templates/optional/redux/provider.tsx +33 -0
  56. package/templates/optional/redux/store/index.ts +45 -0
  57. package/templates/optional/redux/store/slices/app-slice.ts +62 -0
  58. package/templates/base/src/App.css +0 -14
package/lib/prompts.js CHANGED
@@ -5,17 +5,36 @@ const fs = require('fs-extra');
5
5
  const { installDependencies } = require('./install');
6
6
  const { setupProject } = require('./setup');
7
7
 
8
+ /**
9
+ * Core stack (always included):
10
+ * - React 18.3 + TypeScript 5.5 + Vite 5.4 (SWC)
11
+ * - Tailwind CSS 3.4 + tailwind-merge + clsx + CVA
12
+ * - Shadcn/ui components (Button, Input, Card, Skeleton, Separator)
13
+ * - Lucide React icons
14
+ * - Wouter routing (lightweight, 2KB)
15
+ * - Axios with interceptors
16
+ * - ESLint + Prettier (pre-configured)
17
+ * - Path aliases (~/*)
18
+ */
19
+
8
20
  async function createProject(projectName, options) {
9
- // Get project name if not provided
21
+ console.log(chalk.cyan.bold('\nšŸš€ create-modern-react\n'));
22
+ console.log(chalk.gray('Production-ready React + TypeScript + Tailwind in seconds\n'));
23
+
24
+ // ─────────────────────────────────────────────────────────────
25
+ // Prompt 1: Project Name
26
+ // ─────────────────────────────────────────────────────────────
10
27
  if (!projectName) {
11
28
  const nameAnswer = await inquirer.prompt([
12
29
  {
13
30
  type: 'input',
14
31
  name: 'projectName',
15
- message: 'What is the name of your project?',
32
+ message: 'Project name:',
16
33
  validate: (input) => {
17
34
  if (!input.trim()) return 'Project name is required';
18
- if (!/^[a-zA-Z0-9-_]+$/.test(input)) return 'Project name can only contain letters, numbers, hyphens, and underscores';
35
+ if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
36
+ return 'Project name can only contain letters, numbers, hyphens, and underscores';
37
+ }
19
38
  return true;
20
39
  }
21
40
  }
@@ -31,25 +50,27 @@ async function createProject(projectName, options) {
31
50
  {
32
51
  type: 'confirm',
33
52
  name: 'overwrite',
34
- message: `Directory ${projectName} already exists. Do you want to overwrite it?`,
53
+ message: `Directory "${projectName}" already exists. Overwrite?`,
35
54
  default: false
36
55
  }
37
56
  ]);
38
57
 
39
58
  if (!overwriteAnswer.overwrite) {
40
- console.log(chalk.yellow('Operation cancelled.'));
59
+ console.log(chalk.yellow('\nOperation cancelled.'));
41
60
  return;
42
61
  }
43
62
 
44
63
  await fs.remove(projectPath);
45
64
  }
46
65
 
47
- // Package Manager Selection
48
- const packageManagerAnswer = await inquirer.prompt([
66
+ // ─────────────────────────────────────────────────────────────
67
+ // Prompt 2: Package Manager
68
+ // ─────────────────────────────────────────────────────────────
69
+ const { packageManager } = await inquirer.prompt([
49
70
  {
50
71
  type: 'list',
51
72
  name: 'packageManager',
52
- message: 'Which package manager would you like to use?',
73
+ message: 'Package manager:',
53
74
  choices: [
54
75
  { name: 'npm', value: 'npm' },
55
76
  { name: 'yarn', value: 'yarn' },
@@ -58,169 +79,130 @@ async function createProject(projectName, options) {
58
79
  }
59
80
  ]);
60
81
 
61
- // UI Library Selection
62
- const uiLibraryAnswer = await inquirer.prompt([
63
- {
64
- type: 'list',
65
- name: 'uiLibrary',
66
- message: 'Do you want to include a UI component library?',
67
- choices: [
68
- { name: 'Ant Design v5 (with theme customization)', value: 'antd' },
69
- { name: 'Material-UI (MUI)', value: 'mui' },
70
- { name: 'Chakra UI', value: 'chakra' },
71
- { name: 'None (custom components only)', value: 'none' }
72
- ]
73
- }
74
- ]);
75
-
76
- // CSS Framework Selection
77
- const cssFrameworkAnswer = await inquirer.prompt([
78
- {
79
- type: 'list',
80
- name: 'cssFramework',
81
- message: 'Which CSS framework would you like to use?',
82
- choices: [
83
- { name: 'Tailwind CSS (with custom config)', value: 'tailwind' },
84
- { name: 'CSS Modules', value: 'css-modules' },
85
- { name: 'Styled Components', value: 'styled-components' },
86
- { name: 'Plain CSS only', value: 'plain' }
87
- ]
88
- }
89
- ]);
90
-
91
- // State Management Selection
92
- const stateManagementAnswer = await inquirer.prompt([
93
- {
94
- type: 'list',
95
- name: 'stateManagement',
96
- message: 'Do you need state management?',
97
- choices: [
98
- { name: 'Redux Toolkit (with Redux Persist)', value: 'redux-toolkit' },
99
- { name: 'Zustand', value: 'zustand' },
100
- { name: 'Jotai', value: 'jotai' },
101
- { name: 'React state only', value: 'none' }
102
- ]
103
- }
104
- ]);
105
-
106
- // Data Fetching Selection
107
- const dataFetchingAnswer = await inquirer.prompt([
108
- {
109
- type: 'list',
110
- name: 'dataFetching',
111
- message: 'Do you want to include data fetching libraries?',
112
- choices: [
113
- { name: 'React Query (TanStack Query)', value: 'react-query' },
114
- { name: 'SWR', value: 'swr' },
115
- { name: 'Apollo Client (for GraphQL)', value: 'apollo' },
116
- { name: 'Fetch API only', value: 'none' }
117
- ]
118
- }
119
- ]);
120
-
121
- // Routing Selection
122
- const routingAnswer = await inquirer.prompt([
123
- {
124
- type: 'list',
125
- name: 'routing',
126
- message: 'Do you need client-side routing?',
127
- choices: [
128
- { name: 'React Router v6', value: 'react-router' },
129
- { name: 'Wouter (lightweight)', value: 'wouter' },
130
- { name: 'No routing', value: 'none' }
131
- ]
132
- }
133
- ]);
134
-
135
- // Development Tools Selection
136
- const devToolsAnswer = await inquirer.prompt([
82
+ // ─────────────────────────────────────────────────────────────
83
+ // Prompt 3: Optional Features (checkbox - multi-select)
84
+ // ─────────────────────────────────────────────────────────────
85
+ console.log(chalk.gray(' (Press <space> to select, <enter> to confirm)\n'));
86
+ const { optionalFeatures } = await inquirer.prompt([
137
87
  {
138
88
  type: 'checkbox',
139
- name: 'devTools',
140
- message: 'Which development tools would you like?',
89
+ name: 'optionalFeatures',
90
+ message: 'Select optional features:',
141
91
  choices: [
142
- { name: 'Storybook (component development)', value: 'storybook' },
143
- { name: 'ESLint + Prettier (code quality)', value: 'eslint-prettier', checked: true },
144
- { name: 'Husky + lint-staged (git hooks)', value: 'husky', checked: true },
145
- { name: 'Jest + Testing Library (testing)', value: 'testing' }
146
- ]
147
- }
148
- ]);
149
-
150
- // Icons Selection
151
- const iconsAnswer = await inquirer.prompt([
152
- {
153
- type: 'list',
154
- name: 'icons',
155
- message: 'Do you want icon libraries?',
156
- choices: [
157
- { name: 'Lucide React', value: 'lucide' },
158
- { name: 'React Icons', value: 'react-icons' },
159
- { name: 'Heroicons', value: 'heroicons' },
160
- { name: 'No icon library', value: 'none' }
92
+ {
93
+ name: 'Redux Toolkit + Redux Persist (state management)',
94
+ value: 'redux',
95
+ checked: false
96
+ },
97
+ {
98
+ name: 'Ant Design v5 (replaces Shadcn/ui)',
99
+ value: 'antd',
100
+ checked: false
101
+ },
102
+ {
103
+ name: 'Husky + lint-staged (git hooks)',
104
+ value: 'husky',
105
+ checked: false
106
+ }
161
107
  ]
162
108
  }
163
109
  ]);
164
110
 
165
- // PWA Selection
166
- const pwaAnswer = await inquirer.prompt([
167
- {
168
- type: 'confirm',
169
- name: 'pwa',
170
- message: 'Make it a PWA?',
171
- default: false
172
- }
173
- ]);
111
+ // ─────────────────────────────────────────────────────────────
112
+ // Prompt 4: Initialize Git
113
+ // ─────────────────────────────────────────────────────────────
114
+ let initGit = true;
115
+ if (!options.skipGit) {
116
+ const gitAnswer = await inquirer.prompt([
117
+ {
118
+ type: 'confirm',
119
+ name: 'git',
120
+ message: 'Initialize Git repository?',
121
+ default: true
122
+ }
123
+ ]);
124
+ initGit = gitAnswer.git;
125
+ } else {
126
+ initGit = false;
127
+ }
174
128
 
175
- // Git Selection
176
- const gitAnswer = await inquirer.prompt([
177
- {
178
- type: 'confirm',
179
- name: 'git',
180
- message: 'Initialize Git repository?',
181
- default: true
182
- }
183
- ]);
129
+ // ─────────────────────────────────────────────────────────────
130
+ // Prompt 5: Install Dependencies
131
+ // ─────────────────────────────────────────────────────────────
132
+ let installDeps = true;
133
+ if (!options.skipInstall) {
134
+ const installAnswer = await inquirer.prompt([
135
+ {
136
+ type: 'confirm',
137
+ name: 'install',
138
+ message: 'Install dependencies?',
139
+ default: true
140
+ }
141
+ ]);
142
+ installDeps = installAnswer.install;
143
+ } else {
144
+ installDeps = false;
145
+ }
184
146
 
147
+ // ─────────────────────────────────────────────────────────────
148
+ // Build Configuration Object
149
+ // ─────────────────────────────────────────────────────────────
185
150
  const config = {
186
151
  projectName,
187
152
  projectPath,
188
- packageManager: packageManagerAnswer.packageManager,
189
- uiLibrary: uiLibraryAnswer.uiLibrary,
190
- cssFramework: cssFrameworkAnswer.cssFramework,
191
- stateManagement: stateManagementAnswer.stateManagement,
192
- dataFetching: dataFetchingAnswer.dataFetching,
193
- routing: routingAnswer.routing,
194
- devTools: devToolsAnswer.devTools,
195
- icons: iconsAnswer.icons,
196
- pwa: pwaAnswer.pwa,
197
- git: gitAnswer.git,
198
- skipInstall: options.skipInstall,
199
- skipGit: options.skipGit
153
+ packageManager,
154
+ // Optional features
155
+ useRedux: optionalFeatures.includes('redux'),
156
+ useAntd: optionalFeatures.includes('antd'),
157
+ useHusky: optionalFeatures.includes('husky'),
158
+ // Flags
159
+ initGit,
160
+ installDeps
200
161
  };
201
162
 
202
- console.log(chalk.blue('\nšŸ“¦ Creating project with the following configuration:'));
203
- console.log(chalk.gray(JSON.stringify(config, null, 2)));
204
-
205
- // Create project
163
+ // ─────────────────────────────────────────────────────────────
164
+ // Display Configuration Summary
165
+ // ─────────────────────────────────────────────────────────────
166
+ console.log(chalk.cyan('\nā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”'));
167
+ console.log(chalk.cyan('│') + chalk.white.bold(' Configuration Summary ') + chalk.cyan('│'));
168
+ console.log(chalk.cyan('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤'));
169
+ console.log(chalk.cyan('│') + chalk.white(' Core Stack (always included): ') + chalk.cyan('│'));
170
+ console.log(chalk.cyan('│') + chalk.gray(' React 18 + TypeScript + Vite (SWC) ') + chalk.cyan('│'));
171
+ console.log(chalk.cyan('│') + chalk.gray(' Tailwind CSS + clsx + CVA ') + chalk.cyan('│'));
172
+ console.log(chalk.cyan('│') + chalk.gray(` ${config.useAntd ? 'Ant Design v5' : 'Shadcn/ui components'} `) + chalk.cyan('│'));
173
+ console.log(chalk.cyan('│') + chalk.gray(' Wouter routing + Axios ') + chalk.cyan('│'));
174
+ console.log(chalk.cyan('│') + chalk.gray(' Lucide icons + ESLint + Prettier ') + chalk.cyan('│'));
175
+ console.log(chalk.cyan('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤'));
176
+ console.log(chalk.cyan('│') + chalk.white(' Optional Features: ') + chalk.cyan('│'));
177
+ console.log(chalk.cyan('│') + ` Redux Toolkit: ${config.useRedux ? chalk.green('āœ“') : chalk.gray('āœ—')} ` + chalk.cyan('│'));
178
+ console.log(chalk.cyan('│') + ` Ant Design v5: ${config.useAntd ? chalk.green('āœ“') : chalk.gray('āœ—')} ` + chalk.cyan('│'));
179
+ console.log(chalk.cyan('│') + ` Husky hooks: ${config.useHusky ? chalk.green('āœ“') : chalk.gray('āœ—')} ` + chalk.cyan('│'));
180
+ console.log(chalk.cyan('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n'));
181
+
182
+ // ─────────────────────────────────────────────────────────────
183
+ // Execute Setup
184
+ // ─────────────────────────────────────────────────────────────
206
185
  await setupProject(config);
207
186
 
208
187
  // Install dependencies
209
- if (!options.skipInstall) {
188
+ if (installDeps) {
210
189
  await installDependencies(config);
211
190
  }
212
191
 
213
- console.log(chalk.green.bold(`\nšŸŽ‰ Project ${projectName} created successfully!`));
214
- console.log(chalk.blue('\nNext steps:'));
215
- console.log(chalk.gray(` cd ${projectName}`));
216
-
217
- // Use the actual package manager that was used for installation
218
- const actualPackageManager = config.actualPackageManager || config.packageManager;
192
+ // ─────────────────────────────────────────────────────────────
193
+ // Success Message
194
+ // ─────────────────────────────────────────────────────────────
195
+ const pm = config.actualPackageManager || packageManager;
196
+ const runCmd = pm === 'npm' ? 'npm run' : pm;
219
197
 
220
- if (options.skipInstall) {
221
- console.log(chalk.gray(` ${actualPackageManager} install`));
198
+ console.log(chalk.green.bold(`\nāœ… Project "${projectName}" created successfully!\n`));
199
+ console.log(chalk.white('Next steps:\n'));
200
+ console.log(chalk.gray(` cd ${projectName}`));
201
+ if (!installDeps) {
202
+ console.log(chalk.gray(` ${pm} install`));
222
203
  }
223
- console.log(chalk.gray(` ${actualPackageManager === 'npm' ? 'npm run dev' : actualPackageManager + ' dev'}`));
204
+ console.log(chalk.gray(` ${runCmd} dev\n`));
205
+ console.log(chalk.cyan('Happy coding! šŸŽ‰\n'));
224
206
  }
225
207
 
226
- module.exports = { createProject };
208
+ module.exports = { createProject };