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