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.
- package/README.md +270 -72
- package/bin/index.js +9 -7
- package/lib/prompts.js +132 -150
- package/lib/setup.js +264 -143
- package/package.json +17 -8
- package/templates/base/.env.example +9 -0
- package/templates/base/.eslintrc.cjs +37 -0
- package/templates/base/.prettierrc +11 -0
- package/templates/base/components.json +17 -0
- package/templates/base/index.html +3 -2
- package/templates/base/package.json +34 -15
- package/templates/base/postcss.config.js +6 -0
- package/templates/base/src/App.tsx +7 -22
- package/templates/base/src/components/layout/error-boundary.tsx +60 -0
- package/templates/base/src/components/layout/index.ts +2 -0
- package/templates/base/src/components/layout/root-layout.tsx +36 -0
- package/templates/base/src/components/ui/button.tsx +55 -0
- package/templates/base/src/components/ui/card.tsx +85 -0
- package/templates/base/src/components/ui/index.ts +12 -0
- package/templates/base/src/components/ui/input.tsx +24 -0
- package/templates/base/src/components/ui/separator.tsx +29 -0
- package/templates/base/src/components/ui/skeleton.tsx +15 -0
- package/templates/base/src/hooks/index.ts +3 -0
- package/templates/base/src/hooks/use-cancel-token.ts +63 -0
- package/templates/base/src/hooks/use-debounce.ts +29 -0
- package/templates/base/src/hooks/use-loader.ts +39 -0
- package/templates/base/src/index.css +74 -61
- package/templates/base/src/lib/utils.ts +14 -0
- package/templates/base/src/main.tsx +6 -6
- package/templates/base/src/providers/index.tsx +27 -0
- package/templates/base/src/providers/theme-provider.tsx +92 -0
- package/templates/base/src/routes/index.tsx +40 -0
- package/templates/base/src/routes/routes.ts +36 -0
- package/templates/base/src/screens/home/index.tsx +132 -0
- package/templates/base/src/screens/not-found/index.tsx +29 -0
- package/templates/base/src/services/alertify-services.ts +133 -0
- package/templates/base/src/services/api/api-helpers.ts +130 -0
- package/templates/base/src/services/api/axios-instance.ts +77 -0
- package/templates/base/src/services/api/index.ts +9 -0
- package/templates/base/src/services/index.ts +2 -0
- package/templates/base/src/types/index.ts +55 -0
- package/templates/base/src/vite-env.d.ts +31 -0
- package/templates/base/tailwind.config.js +77 -0
- package/templates/base/tsconfig.json +5 -4
- package/templates/base/tsconfig.node.json +22 -0
- package/templates/base/vite.config.ts +68 -7
- package/templates/optional/antd/config-provider.tsx +33 -0
- package/templates/optional/antd/index.ts +2 -0
- package/templates/optional/antd/styles/antd-overrides.css +104 -0
- package/templates/optional/antd/theme.ts +75 -0
- package/templates/optional/husky/.husky/pre-commit +1 -0
- package/templates/optional/husky/.lintstagedrc.json +6 -0
- package/templates/optional/redux/hooks.ts +17 -0
- package/templates/optional/redux/index.ts +13 -0
- package/templates/optional/redux/provider.tsx +33 -0
- package/templates/optional/redux/store/index.ts +45 -0
- package/templates/optional/redux/store/slices/app-slice.ts +62 -0
- 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
|
-
|
|
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: '
|
|
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))
|
|
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.
|
|
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('
|
|
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
|
-
//
|
|
48
|
-
|
|
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: '
|
|
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
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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: '
|
|
140
|
-
message: '
|
|
89
|
+
name: 'optionalFeatures',
|
|
90
|
+
message: 'Select optional features:',
|
|
141
91
|
choices: [
|
|
142
|
-
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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 (
|
|
188
|
+
if (installDeps) {
|
|
210
189
|
await installDependencies(config);
|
|
211
190
|
}
|
|
212
191
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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(` ${
|
|
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 };
|