create-modern-react 1.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.
- package/README.md +113 -0
- package/bin/index.js +28 -0
- package/lib/install.js +59 -0
- package/lib/prompts.js +235 -0
- package/lib/setup.js +228 -0
- package/package.json +54 -0
- package/templates/base/index.html +13 -0
- package/templates/base/package.json +28 -0
- package/templates/base/src/App.css +14 -0
- package/templates/base/src/App.tsx +25 -0
- package/templates/base/src/index.css +70 -0
- package/templates/base/src/main.tsx +10 -0
- package/templates/base/tsconfig.json +31 -0
- package/templates/base/vite.config.ts +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# create-modern-react
|
|
2
|
+
|
|
3
|
+
Create a modern React application with Vite, TypeScript, and your choice of modern libraries.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-modern-react my-app
|
|
9
|
+
cd my-app
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
🚀 **Modern Stack**: Vite + React 18 + TypeScript
|
|
16
|
+
📦 **Interactive Setup**: Choose your preferred libraries
|
|
17
|
+
🎨 **UI Libraries**: Ant Design, Material-UI, Chakra UI support
|
|
18
|
+
🎯 **State Management**: Redux Toolkit, Zustand, Jotai options
|
|
19
|
+
🛣️ **Routing**: React Router or Wouter
|
|
20
|
+
🔧 **Development Tools**: ESLint, Prettier, Storybook, Testing
|
|
21
|
+
🎨 **Styling**: Tailwind CSS, CSS Modules, Styled Components
|
|
22
|
+
📱 **PWA Ready**: Optional Progressive Web App support
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx create-modern-react my-project
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### With Options
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx create-modern-react my-project --skip-install --skip-git
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Interactive Prompts
|
|
39
|
+
|
|
40
|
+
The CLI will guide you through selecting:
|
|
41
|
+
|
|
42
|
+
- **Project Name**: Your project directory name
|
|
43
|
+
- **Package Manager**: npm, yarn, or pnpm
|
|
44
|
+
- **UI Library**: Ant Design, Material-UI, Chakra UI, or none
|
|
45
|
+
- **CSS Framework**: Tailwind CSS, CSS Modules, Styled Components, or plain CSS
|
|
46
|
+
- **State Management**: Redux Toolkit, Zustand, Jotai, or React state only
|
|
47
|
+
- **Data Fetching**: React Query, SWR, Apollo Client, or Fetch API
|
|
48
|
+
- **Routing**: React Router, Wouter, or none
|
|
49
|
+
- **Development Tools**: Storybook, ESLint/Prettier, Husky, Testing
|
|
50
|
+
- **Icons**: Lucide React, React Icons, Heroicons, or none
|
|
51
|
+
- **PWA**: Progressive Web App features
|
|
52
|
+
- **Git**: Initialize Git repository
|
|
53
|
+
|
|
54
|
+
## Template Combinations
|
|
55
|
+
|
|
56
|
+
### Minimal Setup
|
|
57
|
+
|
|
58
|
+
- Vite + React + TypeScript + Tailwind CSS
|
|
59
|
+
|
|
60
|
+
### Enterprise Ready
|
|
61
|
+
|
|
62
|
+
- Vite + React + TypeScript + Ant Design + Redux Toolkit + React Query + React Router
|
|
63
|
+
|
|
64
|
+
### Modern Stack
|
|
65
|
+
|
|
66
|
+
- Vite + React + TypeScript + Tailwind + Zustand + React Query + Wouter
|
|
67
|
+
|
|
68
|
+
### Full Featured
|
|
69
|
+
|
|
70
|
+
- All libraries with Storybook, testing, and PWA support
|
|
71
|
+
|
|
72
|
+
## CLI Options
|
|
73
|
+
|
|
74
|
+
- `--skip-install`: Skip automatic dependency installation
|
|
75
|
+
- `--skip-git`: Skip Git repository initialization
|
|
76
|
+
- `--template <name>`: Use a specific template (future feature)
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- Node.js 16.0.0 or higher
|
|
81
|
+
- npm, yarn, or pnpm
|
|
82
|
+
|
|
83
|
+
## Generated Project Structure
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
my-app/
|
|
87
|
+
├── public/
|
|
88
|
+
├── src/
|
|
89
|
+
│ ├── components/
|
|
90
|
+
│ ├── hooks/
|
|
91
|
+
│ ├── utils/
|
|
92
|
+
│ ├── types/
|
|
93
|
+
│ ├── styles/
|
|
94
|
+
│ ├── App.tsx
|
|
95
|
+
│ ├── main.tsx
|
|
96
|
+
│ └── index.css
|
|
97
|
+
├── package.json
|
|
98
|
+
├── vite.config.ts
|
|
99
|
+
├── tsconfig.json
|
|
100
|
+
└── README.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Contributing
|
|
104
|
+
|
|
105
|
+
1. Fork the repository
|
|
106
|
+
2. Create your feature branch
|
|
107
|
+
3. Commit your changes
|
|
108
|
+
4. Push to the branch
|
|
109
|
+
5. Create a Pull Request
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT © [Your Name]
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require("commander");
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
const { createProject } = require("../lib/prompts");
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name("create-modern-react")
|
|
9
|
+
.description(
|
|
10
|
+
"Create a modern React application with Vite, TypeScript, and your choice of modern libraries",
|
|
11
|
+
)
|
|
12
|
+
.version("1.0.0")
|
|
13
|
+
.argument("[project-name]", "name of the project")
|
|
14
|
+
.option("-t, --template <template>", "use a specific template")
|
|
15
|
+
.option("--skip-install", "skip dependency installation")
|
|
16
|
+
.option("--skip-git", "skip git initialization")
|
|
17
|
+
.action(async (projectName, options) => {
|
|
18
|
+
console.log(chalk.blue.bold("\n🚀 Welcome to create-modern-react!\n"));
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await createProject(projectName, options);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(chalk.red("❌ Error creating project:"), error.message);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
program.parse();
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { spawn } = require("child_process");
|
|
2
|
+
const chalk = require("chalk");
|
|
3
|
+
|
|
4
|
+
function runCommand(command, args, cwd) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const child = spawn(command, args, {
|
|
7
|
+
cwd,
|
|
8
|
+
stdio: "inherit",
|
|
9
|
+
shell: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
child.on("close", (code) => {
|
|
13
|
+
if (code === 0) {
|
|
14
|
+
resolve();
|
|
15
|
+
} else {
|
|
16
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
child.on("error", (error) => {
|
|
21
|
+
reject(error);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function installDependencies(config) {
|
|
27
|
+
const { projectPath, packageManager } = config;
|
|
28
|
+
|
|
29
|
+
console.log(chalk.blue("\n📦 Installing dependencies..."));
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const installCommand =
|
|
33
|
+
packageManager === "yarn"
|
|
34
|
+
? "yarn"
|
|
35
|
+
: packageManager === "pnpm"
|
|
36
|
+
? "pnpm"
|
|
37
|
+
: "npm";
|
|
38
|
+
const installArgs =
|
|
39
|
+
packageManager === "npm"
|
|
40
|
+
? ["install"]
|
|
41
|
+
: packageManager === "pnpm"
|
|
42
|
+
? ["install"]
|
|
43
|
+
: [];
|
|
44
|
+
|
|
45
|
+
await runCommand(installCommand, installArgs, projectPath);
|
|
46
|
+
|
|
47
|
+
console.log(chalk.green("✅ Dependencies installed successfully!"));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(
|
|
50
|
+
chalk.red("❌ Failed to install dependencies:"),
|
|
51
|
+
error.message,
|
|
52
|
+
);
|
|
53
|
+
console.log(chalk.yellow("You can install them manually by running:"));
|
|
54
|
+
console.log(chalk.gray(` cd ${config.projectName}`));
|
|
55
|
+
console.log(chalk.gray(` ${packageManager} install`));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { installDependencies, runCommand };
|
package/lib/prompts.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
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
|
+
async function createProject(projectName, options) {
|
|
9
|
+
// Get project name if not provided
|
|
10
|
+
if (!projectName) {
|
|
11
|
+
const nameAnswer = await inquirer.prompt([
|
|
12
|
+
{
|
|
13
|
+
type: "input",
|
|
14
|
+
name: "projectName",
|
|
15
|
+
message: "What is the name of your project?",
|
|
16
|
+
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";
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
24
|
+
projectName = nameAnswer.projectName;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
28
|
+
|
|
29
|
+
// Check if directory exists
|
|
30
|
+
if (fs.existsSync(projectPath)) {
|
|
31
|
+
const overwriteAnswer = await inquirer.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: "confirm",
|
|
34
|
+
name: "overwrite",
|
|
35
|
+
message: `Directory ${projectName} already exists. Do you want to overwrite it?`,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
if (!overwriteAnswer.overwrite) {
|
|
41
|
+
console.log(chalk.yellow("Operation cancelled."));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await fs.remove(projectPath);
|
|
46
|
+
}
|
|
47
|
+
|
|
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([
|
|
124
|
+
{
|
|
125
|
+
type: "list",
|
|
126
|
+
name: "routing",
|
|
127
|
+
message: "Do you need client-side routing?",
|
|
128
|
+
choices: [
|
|
129
|
+
{ name: "React Router v6", value: "react-router" },
|
|
130
|
+
{ name: "Wouter (lightweight)", value: "wouter" },
|
|
131
|
+
{ name: "No routing", value: "none" },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
// Development Tools Selection
|
|
137
|
+
const devToolsAnswer = await inquirer.prompt([
|
|
138
|
+
{
|
|
139
|
+
type: "checkbox",
|
|
140
|
+
name: "devTools",
|
|
141
|
+
message: "Which development tools would you like?",
|
|
142
|
+
choices: [
|
|
143
|
+
{ name: "Storybook (component development)", value: "storybook" },
|
|
144
|
+
{
|
|
145
|
+
name: "ESLint + Prettier (code quality)",
|
|
146
|
+
value: "eslint-prettier",
|
|
147
|
+
checked: true,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "Husky + lint-staged (git hooks)",
|
|
151
|
+
value: "husky",
|
|
152
|
+
checked: true,
|
|
153
|
+
},
|
|
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
|
+
},
|
|
172
|
+
]);
|
|
173
|
+
|
|
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
|
+
]);
|
|
183
|
+
|
|
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
|
+
]);
|
|
193
|
+
|
|
194
|
+
const config = {
|
|
195
|
+
projectName,
|
|
196
|
+
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,
|
|
209
|
+
};
|
|
210
|
+
|
|
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
|
|
217
|
+
await setupProject(config);
|
|
218
|
+
|
|
219
|
+
// Install dependencies
|
|
220
|
+
if (!options.skipInstall) {
|
|
221
|
+
await installDependencies(config);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log(
|
|
225
|
+
chalk.green.bold(`\n🎉 Project ${projectName} created successfully!`),
|
|
226
|
+
);
|
|
227
|
+
console.log(chalk.blue("\nNext steps:"));
|
|
228
|
+
console.log(chalk.gray(` cd ${projectName}`));
|
|
229
|
+
if (options.skipInstall) {
|
|
230
|
+
console.log(chalk.gray(` ${config.packageManager} install`));
|
|
231
|
+
}
|
|
232
|
+
console.log(chalk.gray(` ${config.packageManager} dev`));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = { createProject };
|
package/lib/setup.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
const { runCommand } = require("./install");
|
|
5
|
+
|
|
6
|
+
async function setupProject(config) {
|
|
7
|
+
const { projectPath, projectName } = config;
|
|
8
|
+
|
|
9
|
+
console.log(chalk.blue("\n🏗️ Setting up project structure..."));
|
|
10
|
+
|
|
11
|
+
// Create project directory
|
|
12
|
+
await fs.ensureDir(projectPath);
|
|
13
|
+
|
|
14
|
+
// Copy base template
|
|
15
|
+
const templatePath = path.join(__dirname, "../templates/base");
|
|
16
|
+
await fs.copy(templatePath, projectPath);
|
|
17
|
+
|
|
18
|
+
// Update package.json with project name and selected dependencies
|
|
19
|
+
await updatePackageJson(config);
|
|
20
|
+
|
|
21
|
+
// Configure selected libraries
|
|
22
|
+
await configureLibraries(config);
|
|
23
|
+
|
|
24
|
+
// Initialize git if requested
|
|
25
|
+
if (config.git && !config.skipGit) {
|
|
26
|
+
await initializeGit(config);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(chalk.green("✅ Project structure created successfully!"));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function updatePackageJson(config) {
|
|
33
|
+
const packageJsonPath = path.join(config.projectPath, "package.json");
|
|
34
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
35
|
+
|
|
36
|
+
// Update name
|
|
37
|
+
packageJson.name = config.projectName;
|
|
38
|
+
|
|
39
|
+
// Add dependencies based on configuration
|
|
40
|
+
const dependencies = { ...packageJson.dependencies };
|
|
41
|
+
const devDependencies = { ...packageJson.devDependencies };
|
|
42
|
+
|
|
43
|
+
// UI Library dependencies
|
|
44
|
+
if (config.uiLibrary === "antd") {
|
|
45
|
+
dependencies["antd"] = "^5.0.0";
|
|
46
|
+
} else if (config.uiLibrary === "mui") {
|
|
47
|
+
dependencies["@mui/material"] = "^5.0.0";
|
|
48
|
+
dependencies["@emotion/react"] = "^11.0.0";
|
|
49
|
+
dependencies["@emotion/styled"] = "^11.0.0";
|
|
50
|
+
} else if (config.uiLibrary === "chakra") {
|
|
51
|
+
dependencies["@chakra-ui/react"] = "^2.0.0";
|
|
52
|
+
dependencies["@emotion/react"] = "^11.0.0";
|
|
53
|
+
dependencies["@emotion/styled"] = "^11.0.0";
|
|
54
|
+
dependencies["framer-motion"] = "^6.0.0";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// CSS Framework dependencies
|
|
58
|
+
if (config.cssFramework === "tailwind") {
|
|
59
|
+
devDependencies["tailwindcss"] = "^3.0.0";
|
|
60
|
+
devDependencies["postcss"] = "^8.0.0";
|
|
61
|
+
devDependencies["autoprefixer"] = "^10.0.0";
|
|
62
|
+
} else if (config.cssFramework === "styled-components") {
|
|
63
|
+
dependencies["styled-components"] = "^6.0.0";
|
|
64
|
+
devDependencies["@types/styled-components"] = "^5.1.0";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// State Management dependencies
|
|
68
|
+
if (config.stateManagement === "redux-toolkit") {
|
|
69
|
+
dependencies["@reduxjs/toolkit"] = "^2.0.0";
|
|
70
|
+
dependencies["react-redux"] = "^9.0.0";
|
|
71
|
+
dependencies["redux-persist"] = "^6.0.0";
|
|
72
|
+
} else if (config.stateManagement === "zustand") {
|
|
73
|
+
dependencies["zustand"] = "^4.0.0";
|
|
74
|
+
} else if (config.stateManagement === "jotai") {
|
|
75
|
+
dependencies["jotai"] = "^2.0.0";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Data Fetching dependencies
|
|
79
|
+
if (config.dataFetching === "react-query") {
|
|
80
|
+
dependencies["@tanstack/react-query"] = "^5.0.0";
|
|
81
|
+
dependencies["@tanstack/react-query-devtools"] = "^5.0.0";
|
|
82
|
+
} else if (config.dataFetching === "swr") {
|
|
83
|
+
dependencies["swr"] = "^2.0.0";
|
|
84
|
+
} else if (config.dataFetching === "apollo") {
|
|
85
|
+
dependencies["@apollo/client"] = "^3.0.0";
|
|
86
|
+
dependencies["graphql"] = "^16.0.0";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Routing dependencies
|
|
90
|
+
if (config.routing === "react-router") {
|
|
91
|
+
dependencies["react-router-dom"] = "^6.0.0";
|
|
92
|
+
} else if (config.routing === "wouter") {
|
|
93
|
+
dependencies["wouter"] = "^3.0.0";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Icon dependencies
|
|
97
|
+
if (config.icons === "lucide") {
|
|
98
|
+
dependencies["lucide-react"] = "^0.400.0";
|
|
99
|
+
} else if (config.icons === "react-icons") {
|
|
100
|
+
dependencies["react-icons"] = "^5.0.0";
|
|
101
|
+
} else if (config.icons === "heroicons") {
|
|
102
|
+
dependencies["@heroicons/react"] = "^2.0.0";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Development Tools dependencies
|
|
106
|
+
if (config.devTools.includes("storybook")) {
|
|
107
|
+
devDependencies["@storybook/react-vite"] = "^8.0.0";
|
|
108
|
+
devDependencies["@storybook/addon-essentials"] = "^8.0.0";
|
|
109
|
+
devDependencies["storybook"] = "^8.0.0";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (config.devTools.includes("testing")) {
|
|
113
|
+
devDependencies["jest"] = "^29.0.0";
|
|
114
|
+
devDependencies["@testing-library/react"] = "^14.0.0";
|
|
115
|
+
devDependencies["@testing-library/jest-dom"] = "^6.0.0";
|
|
116
|
+
devDependencies["@testing-library/user-event"] = "^14.0.0";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (config.devTools.includes("husky")) {
|
|
120
|
+
devDependencies["husky"] = "^9.0.0";
|
|
121
|
+
devDependencies["lint-staged"] = "^15.0.0";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// PWA dependencies
|
|
125
|
+
if (config.pwa) {
|
|
126
|
+
devDependencies["vite-plugin-pwa"] = "^0.20.0";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
packageJson.dependencies = dependencies;
|
|
130
|
+
packageJson.devDependencies = devDependencies;
|
|
131
|
+
|
|
132
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function configureLibraries(config) {
|
|
136
|
+
const { projectPath } = config;
|
|
137
|
+
|
|
138
|
+
// Configure Tailwind if selected
|
|
139
|
+
if (config.cssFramework === "tailwind") {
|
|
140
|
+
await configureTailwind(projectPath);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Configure Vite plugins based on selections
|
|
144
|
+
await configureVite(config);
|
|
145
|
+
|
|
146
|
+
// Add configuration files for selected libraries
|
|
147
|
+
await addConfigFiles(config);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function configureTailwind(projectPath) {
|
|
151
|
+
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
|
|
152
|
+
export default {
|
|
153
|
+
content: [
|
|
154
|
+
"./index.html",
|
|
155
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
156
|
+
],
|
|
157
|
+
theme: {
|
|
158
|
+
extend: {},
|
|
159
|
+
},
|
|
160
|
+
plugins: [],
|
|
161
|
+
}`;
|
|
162
|
+
|
|
163
|
+
const postcssConfig = `export default {
|
|
164
|
+
plugins: {
|
|
165
|
+
tailwindcss: {},
|
|
166
|
+
autoprefixer: {},
|
|
167
|
+
},
|
|
168
|
+
}`;
|
|
169
|
+
|
|
170
|
+
await fs.writeFile(
|
|
171
|
+
path.join(projectPath, "tailwind.config.js"),
|
|
172
|
+
tailwindConfig,
|
|
173
|
+
);
|
|
174
|
+
await fs.writeFile(
|
|
175
|
+
path.join(projectPath, "postcss.config.js"),
|
|
176
|
+
postcssConfig,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Add Tailwind directives to CSS
|
|
180
|
+
const cssPath = path.join(projectPath, "src/index.css");
|
|
181
|
+
const tailwindDirectives = `@tailwind base;
|
|
182
|
+
@tailwind components;
|
|
183
|
+
@tailwind utilities;
|
|
184
|
+
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
if (await fs.pathExists(cssPath)) {
|
|
188
|
+
const existingCss = await fs.readFile(cssPath, "utf8");
|
|
189
|
+
await fs.writeFile(cssPath, tailwindDirectives + existingCss);
|
|
190
|
+
} else {
|
|
191
|
+
await fs.writeFile(cssPath, tailwindDirectives);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function configureVite(config) {
|
|
196
|
+
// This would configure vite.config.ts based on selected options
|
|
197
|
+
// For now, we'll use the existing base template configuration
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function addConfigFiles(config) {
|
|
201
|
+
// Add configuration files for selected tools
|
|
202
|
+
// This would add ESLint, Prettier, Jest configs, etc.
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function initializeGit(config) {
|
|
206
|
+
const { projectPath } = config;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
console.log(chalk.blue("🔧 Initializing Git repository..."));
|
|
210
|
+
|
|
211
|
+
await runCommand("git", ["init"], projectPath);
|
|
212
|
+
await runCommand("git", ["add", "."], projectPath);
|
|
213
|
+
await runCommand(
|
|
214
|
+
"git",
|
|
215
|
+
["commit", "-m", "Initial commit from create-modern-react"],
|
|
216
|
+
projectPath,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
console.log(chalk.green("✅ Git repository initialized!"));
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.warn(
|
|
222
|
+
chalk.yellow("⚠️ Could not initialize Git repository:"),
|
|
223
|
+
error.message,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = { setupProject };
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-modern-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a modern React application with Vite, TypeScript, and your choice of modern libraries",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-modern-react": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=16.0.0"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"react",
|
|
14
|
+
"vite",
|
|
15
|
+
"typescript",
|
|
16
|
+
"cli",
|
|
17
|
+
"boilerplate",
|
|
18
|
+
"template",
|
|
19
|
+
"modern",
|
|
20
|
+
"scaffold"
|
|
21
|
+
],
|
|
22
|
+
"author": "Abhay Rana",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/abhay-rana/create-modern-react.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/abhay-rana/create-modern-react/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/abhay-rana/create-modern-react#readme",
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"lib",
|
|
35
|
+
"templates"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"chalk": "^4.1.2",
|
|
39
|
+
"commander": "^11.0.0",
|
|
40
|
+
"fs-extra": "^11.0.0",
|
|
41
|
+
"inquirer": "^8.2.6"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/fs-extra": "^11.0.0",
|
|
45
|
+
"@types/inquirer": "^9.0.0",
|
|
46
|
+
"eslint": "^8.57.0",
|
|
47
|
+
"prettier": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"format": "prettier --write ."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Modern React App</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modern-react-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"react": "^18.2.0",
|
|
14
|
+
"react-dom": "^18.2.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/react": "^18.2.66",
|
|
18
|
+
"@types/react-dom": "^18.2.22",
|
|
19
|
+
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
|
20
|
+
"@typescript-eslint/parser": "^7.2.0",
|
|
21
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
22
|
+
"eslint": "^8.57.0",
|
|
23
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
24
|
+
"eslint-plugin-react-refresh": "^0.4.6",
|
|
25
|
+
"typescript": "^5.2.2",
|
|
26
|
+
"vite": "^5.2.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import "./App.css";
|
|
3
|
+
|
|
4
|
+
function App() {
|
|
5
|
+
const [count, setCount] = useState(0);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="App">
|
|
9
|
+
<div>
|
|
10
|
+
<h1>Modern React App</h1>
|
|
11
|
+
<div className="card">
|
|
12
|
+
<button onClick={() => setCount((count) => count + 1)}>
|
|
13
|
+
count is {count}
|
|
14
|
+
</button>
|
|
15
|
+
<p>
|
|
16
|
+
Edit <code>src/App.tsx</code> and save to test HMR
|
|
17
|
+
</p>
|
|
18
|
+
</div>
|
|
19
|
+
<p className="read-the-docs">Click on the React logo to learn more</p>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default App;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
-webkit-text-size-adjust: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
a {
|
|
18
|
+
font-weight: 500;
|
|
19
|
+
color: #646cff;
|
|
20
|
+
text-decoration: inherit;
|
|
21
|
+
}
|
|
22
|
+
a:hover {
|
|
23
|
+
color: #535bf2;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body {
|
|
27
|
+
margin: 0;
|
|
28
|
+
display: flex;
|
|
29
|
+
place-items: center;
|
|
30
|
+
min-width: 320px;
|
|
31
|
+
min-height: 100vh;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
h1 {
|
|
35
|
+
font-size: 3.2em;
|
|
36
|
+
line-height: 1.1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
button {
|
|
40
|
+
border-radius: 8px;
|
|
41
|
+
border: 1px solid transparent;
|
|
42
|
+
padding: 0.6em 1.2em;
|
|
43
|
+
font-size: 1em;
|
|
44
|
+
font-weight: 500;
|
|
45
|
+
font-family: inherit;
|
|
46
|
+
background-color: #1a1a1a;
|
|
47
|
+
color: inherit;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
transition: border-color 0.25s;
|
|
50
|
+
}
|
|
51
|
+
button:hover {
|
|
52
|
+
border-color: #646cff;
|
|
53
|
+
}
|
|
54
|
+
button:focus,
|
|
55
|
+
button:focus-visible {
|
|
56
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (prefers-color-scheme: light) {
|
|
60
|
+
:root {
|
|
61
|
+
color: #213547;
|
|
62
|
+
background-color: #ffffff;
|
|
63
|
+
}
|
|
64
|
+
a:hover {
|
|
65
|
+
color: #747bff;
|
|
66
|
+
}
|
|
67
|
+
button {
|
|
68
|
+
background-color: #f9f9f9;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
|
|
23
|
+
/* Path mapping */
|
|
24
|
+
"baseUrl": ".",
|
|
25
|
+
"paths": {
|
|
26
|
+
"~/*": ["src/*"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"include": ["src"],
|
|
30
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
31
|
+
}
|