create-modern-react 1.0.0 ā 1.0.1
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/bin/index.js +13 -15
- package/lib/install.js +103 -32
- package/lib/prompts.js +109 -118
- package/lib/setup.js +77 -90
- package/package.json +1 -1
- package/templates/base/index.html +1 -1
- package/templates/base/package.json +1 -1
- package/templates/base/src/App.css +1 -1
- package/templates/base/src/App.tsx +8 -6
- package/templates/base/src/index.css +1 -1
- package/templates/base/src/main.tsx +6 -6
- package/templates/base/tsconfig.json +1 -1
- package/templates/base/vite.config.ts +6 -6
package/bin/index.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { program } = require(
|
|
4
|
-
const chalk = require(
|
|
5
|
-
const { createProject } = require(
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { createProject } = require('../lib/prompts');
|
|
6
6
|
|
|
7
7
|
program
|
|
8
|
-
.name(
|
|
9
|
-
.description(
|
|
10
|
-
|
|
11
|
-
)
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
.option(
|
|
15
|
-
.option("--skip-install", "skip dependency installation")
|
|
16
|
-
.option("--skip-git", "skip git initialization")
|
|
8
|
+
.name('create-modern-react')
|
|
9
|
+
.description('Create a modern React application with Vite, TypeScript, and your choice of modern libraries')
|
|
10
|
+
.version('1.0.0')
|
|
11
|
+
.argument('[project-name]', 'name of the project')
|
|
12
|
+
.option('-t, --template <template>', 'use a specific template')
|
|
13
|
+
.option('--skip-install', 'skip dependency installation')
|
|
14
|
+
.option('--skip-git', 'skip git initialization')
|
|
17
15
|
.action(async (projectName, options) => {
|
|
18
|
-
console.log(chalk.blue.bold(
|
|
16
|
+
console.log(chalk.blue.bold('\nš Welcome to create-modern-react!\n'));
|
|
19
17
|
|
|
20
18
|
try {
|
|
21
19
|
await createProject(projectName, options);
|
|
22
20
|
} catch (error) {
|
|
23
|
-
console.error(chalk.red(
|
|
21
|
+
console.error(chalk.red('ā Error creating project:'), error.message);
|
|
24
22
|
process.exit(1);
|
|
25
23
|
}
|
|
26
24
|
});
|
|
27
25
|
|
|
28
|
-
program.parse();
|
|
26
|
+
program.parse();
|
package/lib/install.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const { spawn } = require(
|
|
2
|
-
const chalk = require(
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
3
|
|
|
4
4
|
function runCommand(command, args, cwd) {
|
|
5
5
|
return new Promise((resolve, reject) => {
|
|
6
6
|
const child = spawn(command, args, {
|
|
7
7
|
cwd,
|
|
8
|
-
stdio:
|
|
9
|
-
shell: true
|
|
8
|
+
stdio: 'inherit',
|
|
9
|
+
shell: true
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
child.on(
|
|
12
|
+
child.on('close', (code) => {
|
|
13
13
|
if (code === 0) {
|
|
14
14
|
resolve();
|
|
15
15
|
} else {
|
|
@@ -17,43 +17,114 @@ function runCommand(command, args, cwd) {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
child.on(
|
|
20
|
+
child.on('error', (error) => {
|
|
21
21
|
reject(error);
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// Check if a command is available
|
|
27
|
+
function isCommandAvailable(command) {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const child = spawn(command, ['--version'], {
|
|
30
|
+
stdio: 'ignore',
|
|
31
|
+
shell: true
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
child.on('close', (code) => {
|
|
35
|
+
resolve(code === 0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
child.on('error', () => {
|
|
39
|
+
resolve(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get the best available package manager with fallback
|
|
45
|
+
async function getAvailablePackageManager(preferredManager) {
|
|
46
|
+
console.log(chalk.blue(`š Checking for ${preferredManager}...`));
|
|
47
|
+
|
|
48
|
+
// Check if preferred package manager is available
|
|
49
|
+
if (await isCommandAvailable(preferredManager)) {
|
|
50
|
+
console.log(chalk.green(`ā
${preferredManager} is available`));
|
|
51
|
+
return preferredManager;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fallback logic
|
|
55
|
+
console.log(chalk.yellow(`ā ļø ${preferredManager} is not installed`));
|
|
56
|
+
|
|
57
|
+
if (preferredManager !== 'npm') {
|
|
58
|
+
console.log(chalk.blue('š Falling back to npm...'));
|
|
59
|
+
|
|
60
|
+
if (await isCommandAvailable('npm')) {
|
|
61
|
+
console.log(chalk.green('ā
npm is available, using npm instead'));
|
|
62
|
+
return 'npm';
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error('Neither ' + preferredManager + ' nor npm is available');
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error('npm is not available');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get install command and args for package manager
|
|
72
|
+
function getInstallCommand(packageManager) {
|
|
73
|
+
switch (packageManager) {
|
|
74
|
+
case 'yarn':
|
|
75
|
+
return { command: 'yarn', args: ['install'] };
|
|
76
|
+
case 'pnpm':
|
|
77
|
+
return { command: 'pnpm', args: ['install'] };
|
|
78
|
+
case 'npm':
|
|
79
|
+
default:
|
|
80
|
+
return { command: 'npm', args: ['install'] };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
26
84
|
async function installDependencies(config) {
|
|
27
|
-
const { projectPath, packageManager } = config;
|
|
85
|
+
const { projectPath, packageManager: preferredManager, projectName } = config;
|
|
28
86
|
|
|
29
|
-
console.log(chalk.blue(
|
|
87
|
+
console.log(chalk.blue('\nš¦ Installing dependencies...'));
|
|
30
88
|
|
|
31
89
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
90
|
+
// Get the best available package manager
|
|
91
|
+
const availableManager = await getAvailablePackageManager(preferredManager);
|
|
92
|
+
const { command, args } = getInstallCommand(availableManager);
|
|
93
|
+
|
|
94
|
+
// Update config with actual package manager used
|
|
95
|
+
config.actualPackageManager = availableManager;
|
|
96
|
+
|
|
97
|
+
console.log(chalk.blue(`š„ Running: ${command} ${args.join(' ')}`));
|
|
98
|
+
await runCommand(command, args, projectPath);
|
|
99
|
+
|
|
100
|
+
console.log(chalk.green('ā
Dependencies installed successfully!'));
|
|
101
|
+
|
|
102
|
+
// Show different message if fallback was used
|
|
103
|
+
if (availableManager !== preferredManager) {
|
|
104
|
+
console.log(chalk.yellow(`ā¹ļø Note: Used ${availableManager} instead of ${preferredManager}`));
|
|
105
|
+
}
|
|
106
|
+
|
|
48
107
|
} catch (error) {
|
|
49
|
-
console.error(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
108
|
+
console.error(chalk.red('ā Failed to install dependencies:'), error.message);
|
|
109
|
+
console.log(chalk.yellow('\nš§ You can install them manually by running:'));
|
|
110
|
+
console.log(chalk.gray(` cd ${projectName}`));
|
|
111
|
+
|
|
112
|
+
// Show command for the originally preferred package manager
|
|
113
|
+
if (preferredManager === 'yarn') {
|
|
114
|
+
console.log(chalk.gray(` yarn install`));
|
|
115
|
+
} else if (preferredManager === 'pnpm') {
|
|
116
|
+
console.log(chalk.gray(` pnpm install`));
|
|
117
|
+
} else {
|
|
118
|
+
console.log(chalk.gray(` npm install`));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(chalk.yellow('\nš” Make sure you have the package manager installed:'));
|
|
122
|
+
if (preferredManager === 'yarn') {
|
|
123
|
+
console.log(chalk.gray(` npm install -g yarn`));
|
|
124
|
+
} else if (preferredManager === 'pnpm') {
|
|
125
|
+
console.log(chalk.gray(` npm install -g pnpm`));
|
|
126
|
+
}
|
|
56
127
|
}
|
|
57
128
|
}
|
|
58
129
|
|
|
59
|
-
module.exports = { installDependencies, runCommand };
|
|
130
|
+
module.exports = { installDependencies, runCommand };
|
package/lib/prompts.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
const inquirer = require(
|
|
2
|
-
const chalk = require(
|
|
3
|
-
const path = require(
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { installDependencies } = require(
|
|
6
|
-
const { setupProject } = require(
|
|
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
7
|
|
|
8
8
|
async function createProject(projectName, options) {
|
|
9
9
|
// Get project name if not provided
|
|
10
10
|
if (!projectName) {
|
|
11
11
|
const nameAnswer = await inquirer.prompt([
|
|
12
12
|
{
|
|
13
|
-
type:
|
|
14
|
-
name:
|
|
15
|
-
message:
|
|
13
|
+
type: 'input',
|
|
14
|
+
name: 'projectName',
|
|
15
|
+
message: 'What is the name of your project?',
|
|
16
16
|
validate: (input) => {
|
|
17
|
-
if (!input.trim()) return
|
|
18
|
-
if (!/^[a-zA-Z0-9-_]+$/.test(input))
|
|
19
|
-
return "Project name can only contain letters, numbers, hyphens, and underscores";
|
|
17
|
+
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';
|
|
20
19
|
return true;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
23
22
|
]);
|
|
24
23
|
projectName = nameAnswer.projectName;
|
|
25
24
|
}
|
|
@@ -30,15 +29,15 @@ async function createProject(projectName, options) {
|
|
|
30
29
|
if (fs.existsSync(projectPath)) {
|
|
31
30
|
const overwriteAnswer = await inquirer.prompt([
|
|
32
31
|
{
|
|
33
|
-
type:
|
|
34
|
-
name:
|
|
32
|
+
type: 'confirm',
|
|
33
|
+
name: 'overwrite',
|
|
35
34
|
message: `Directory ${projectName} already exists. Do you want to overwrite it?`,
|
|
36
|
-
default: false
|
|
37
|
-
}
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
38
37
|
]);
|
|
39
38
|
|
|
40
39
|
if (!overwriteAnswer.overwrite) {
|
|
41
|
-
console.log(chalk.yellow(
|
|
40
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
42
41
|
return;
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -48,147 +47,139 @@ async function createProject(projectName, options) {
|
|
|
48
47
|
// Package Manager Selection
|
|
49
48
|
const packageManagerAnswer = await inquirer.prompt([
|
|
50
49
|
{
|
|
51
|
-
type:
|
|
52
|
-
name:
|
|
53
|
-
message:
|
|
50
|
+
type: 'list',
|
|
51
|
+
name: 'packageManager',
|
|
52
|
+
message: 'Which package manager would you like to use?',
|
|
54
53
|
choices: [
|
|
55
|
-
{ name:
|
|
56
|
-
{ name:
|
|
57
|
-
{ name:
|
|
58
|
-
]
|
|
59
|
-
}
|
|
54
|
+
{ name: 'npm', value: 'npm' },
|
|
55
|
+
{ name: 'yarn', value: 'yarn' },
|
|
56
|
+
{ name: 'pnpm', value: 'pnpm' }
|
|
57
|
+
]
|
|
58
|
+
}
|
|
60
59
|
]);
|
|
61
60
|
|
|
62
61
|
// UI Library Selection
|
|
63
62
|
const uiLibraryAnswer = await inquirer.prompt([
|
|
64
63
|
{
|
|
65
|
-
type:
|
|
66
|
-
name:
|
|
67
|
-
message:
|
|
64
|
+
type: 'list',
|
|
65
|
+
name: 'uiLibrary',
|
|
66
|
+
message: 'Do you want to include a UI component library?',
|
|
68
67
|
choices: [
|
|
69
|
-
{ name:
|
|
70
|
-
{ name:
|
|
71
|
-
{ name:
|
|
72
|
-
{ name:
|
|
73
|
-
]
|
|
74
|
-
}
|
|
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
|
+
}
|
|
75
74
|
]);
|
|
76
75
|
|
|
77
76
|
// CSS Framework Selection
|
|
78
77
|
const cssFrameworkAnswer = await inquirer.prompt([
|
|
79
78
|
{
|
|
80
|
-
type:
|
|
81
|
-
name:
|
|
82
|
-
message:
|
|
79
|
+
type: 'list',
|
|
80
|
+
name: 'cssFramework',
|
|
81
|
+
message: 'Which CSS framework would you like to use?',
|
|
83
82
|
choices: [
|
|
84
|
-
{ name:
|
|
85
|
-
{ name:
|
|
86
|
-
{ name:
|
|
87
|
-
{ name:
|
|
88
|
-
]
|
|
89
|
-
}
|
|
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
|
+
}
|
|
90
89
|
]);
|
|
91
90
|
|
|
92
91
|
// State Management Selection
|
|
93
92
|
const stateManagementAnswer = await inquirer.prompt([
|
|
94
93
|
{
|
|
95
|
-
type:
|
|
96
|
-
name:
|
|
97
|
-
message:
|
|
94
|
+
type: 'list',
|
|
95
|
+
name: 'stateManagement',
|
|
96
|
+
message: 'Do you need state management?',
|
|
98
97
|
choices: [
|
|
99
|
-
{ name:
|
|
100
|
-
{ name:
|
|
101
|
-
{ name:
|
|
102
|
-
{ name:
|
|
103
|
-
]
|
|
104
|
-
}
|
|
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
|
+
}
|
|
105
104
|
]);
|
|
106
105
|
|
|
107
106
|
// Data Fetching Selection
|
|
108
107
|
const dataFetchingAnswer = await inquirer.prompt([
|
|
109
108
|
{
|
|
110
|
-
type:
|
|
111
|
-
name:
|
|
112
|
-
message:
|
|
109
|
+
type: 'list',
|
|
110
|
+
name: 'dataFetching',
|
|
111
|
+
message: 'Do you want to include data fetching libraries?',
|
|
113
112
|
choices: [
|
|
114
|
-
{ name:
|
|
115
|
-
{ name:
|
|
116
|
-
{ name:
|
|
117
|
-
{ name:
|
|
118
|
-
]
|
|
119
|
-
}
|
|
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
|
+
}
|
|
120
119
|
]);
|
|
121
120
|
|
|
122
121
|
// Routing Selection
|
|
123
122
|
const routingAnswer = await inquirer.prompt([
|
|
124
123
|
{
|
|
125
|
-
type:
|
|
126
|
-
name:
|
|
127
|
-
message:
|
|
124
|
+
type: 'list',
|
|
125
|
+
name: 'routing',
|
|
126
|
+
message: 'Do you need client-side routing?',
|
|
128
127
|
choices: [
|
|
129
|
-
{ name:
|
|
130
|
-
{ name:
|
|
131
|
-
{ name:
|
|
132
|
-
]
|
|
133
|
-
}
|
|
128
|
+
{ name: 'React Router v6', value: 'react-router' },
|
|
129
|
+
{ name: 'Wouter (lightweight)', value: 'wouter' },
|
|
130
|
+
{ name: 'No routing', value: 'none' }
|
|
131
|
+
]
|
|
132
|
+
}
|
|
134
133
|
]);
|
|
135
134
|
|
|
136
135
|
// Development Tools Selection
|
|
137
136
|
const devToolsAnswer = await inquirer.prompt([
|
|
138
137
|
{
|
|
139
|
-
type:
|
|
140
|
-
name:
|
|
141
|
-
message:
|
|
138
|
+
type: 'checkbox',
|
|
139
|
+
name: 'devTools',
|
|
140
|
+
message: 'Which development tools would you like?',
|
|
142
141
|
choices: [
|
|
143
|
-
{ name:
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
},
|
|
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
|
+
}
|
|
157
148
|
]);
|
|
158
149
|
|
|
159
150
|
// Icons Selection
|
|
160
151
|
const iconsAnswer = await inquirer.prompt([
|
|
161
152
|
{
|
|
162
|
-
type:
|
|
163
|
-
name:
|
|
164
|
-
message:
|
|
153
|
+
type: 'list',
|
|
154
|
+
name: 'icons',
|
|
155
|
+
message: 'Do you want icon libraries?',
|
|
165
156
|
choices: [
|
|
166
|
-
{ name:
|
|
167
|
-
{ name:
|
|
168
|
-
{ name:
|
|
169
|
-
{ name:
|
|
170
|
-
]
|
|
171
|
-
}
|
|
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' }
|
|
161
|
+
]
|
|
162
|
+
}
|
|
172
163
|
]);
|
|
173
164
|
|
|
174
165
|
// PWA Selection
|
|
175
166
|
const pwaAnswer = await inquirer.prompt([
|
|
176
167
|
{
|
|
177
|
-
type:
|
|
178
|
-
name:
|
|
179
|
-
message:
|
|
180
|
-
default: false
|
|
181
|
-
}
|
|
168
|
+
type: 'confirm',
|
|
169
|
+
name: 'pwa',
|
|
170
|
+
message: 'Make it a PWA?',
|
|
171
|
+
default: false
|
|
172
|
+
}
|
|
182
173
|
]);
|
|
183
174
|
|
|
184
175
|
// Git Selection
|
|
185
176
|
const gitAnswer = await inquirer.prompt([
|
|
186
177
|
{
|
|
187
|
-
type:
|
|
188
|
-
name:
|
|
189
|
-
message:
|
|
190
|
-
default: true
|
|
191
|
-
}
|
|
178
|
+
type: 'confirm',
|
|
179
|
+
name: 'git',
|
|
180
|
+
message: 'Initialize Git repository?',
|
|
181
|
+
default: true
|
|
182
|
+
}
|
|
192
183
|
]);
|
|
193
184
|
|
|
194
185
|
const config = {
|
|
@@ -205,12 +196,10 @@ async function createProject(projectName, options) {
|
|
|
205
196
|
pwa: pwaAnswer.pwa,
|
|
206
197
|
git: gitAnswer.git,
|
|
207
198
|
skipInstall: options.skipInstall,
|
|
208
|
-
skipGit: options.skipGit
|
|
199
|
+
skipGit: options.skipGit
|
|
209
200
|
};
|
|
210
201
|
|
|
211
|
-
console.log(
|
|
212
|
-
chalk.blue("\nš¦ Creating project with the following configuration:"),
|
|
213
|
-
);
|
|
202
|
+
console.log(chalk.blue('\nš¦ Creating project with the following configuration:'));
|
|
214
203
|
console.log(chalk.gray(JSON.stringify(config, null, 2)));
|
|
215
204
|
|
|
216
205
|
// Create project
|
|
@@ -221,15 +210,17 @@ async function createProject(projectName, options) {
|
|
|
221
210
|
await installDependencies(config);
|
|
222
211
|
}
|
|
223
212
|
|
|
224
|
-
console.log(
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
console.log(chalk.blue("\nNext steps:"));
|
|
213
|
+
console.log(chalk.green.bold(`\nš Project ${projectName} created successfully!`));
|
|
214
|
+
console.log(chalk.blue('\nNext steps:'));
|
|
228
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;
|
|
219
|
+
|
|
229
220
|
if (options.skipInstall) {
|
|
230
|
-
console.log(chalk.gray(` ${
|
|
221
|
+
console.log(chalk.gray(` ${actualPackageManager} install`));
|
|
231
222
|
}
|
|
232
|
-
console.log(chalk.gray(` ${
|
|
223
|
+
console.log(chalk.gray(` ${actualPackageManager === 'npm' ? 'npm run dev' : actualPackageManager + ' dev'}`));
|
|
233
224
|
}
|
|
234
225
|
|
|
235
|
-
module.exports = { createProject };
|
|
226
|
+
module.exports = { createProject };
|
package/lib/setup.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const chalk = require(
|
|
4
|
-
const { runCommand } = require(
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { runCommand } = require('./install');
|
|
5
5
|
|
|
6
6
|
async function setupProject(config) {
|
|
7
7
|
const { projectPath, projectName } = config;
|
|
8
8
|
|
|
9
|
-
console.log(chalk.blue(
|
|
9
|
+
console.log(chalk.blue('\nšļø Setting up project structure...'));
|
|
10
10
|
|
|
11
11
|
// Create project directory
|
|
12
12
|
await fs.ensureDir(projectPath);
|
|
13
13
|
|
|
14
14
|
// Copy base template
|
|
15
|
-
const templatePath = path.join(__dirname,
|
|
15
|
+
const templatePath = path.join(__dirname, '../templates/base');
|
|
16
16
|
await fs.copy(templatePath, projectPath);
|
|
17
17
|
|
|
18
18
|
// Update package.json with project name and selected dependencies
|
|
@@ -26,11 +26,11 @@ async function setupProject(config) {
|
|
|
26
26
|
await initializeGit(config);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
console.log(chalk.green(
|
|
29
|
+
console.log(chalk.green('ā
Project structure created successfully!'));
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async function updatePackageJson(config) {
|
|
33
|
-
const packageJsonPath = path.join(config.projectPath,
|
|
33
|
+
const packageJsonPath = path.join(config.projectPath, 'package.json');
|
|
34
34
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
35
35
|
|
|
36
36
|
// Update name
|
|
@@ -41,89 +41,89 @@ async function updatePackageJson(config) {
|
|
|
41
41
|
const devDependencies = { ...packageJson.devDependencies };
|
|
42
42
|
|
|
43
43
|
// UI Library dependencies
|
|
44
|
-
if (config.uiLibrary ===
|
|
45
|
-
dependencies[
|
|
46
|
-
} else if (config.uiLibrary ===
|
|
47
|
-
dependencies[
|
|
48
|
-
dependencies[
|
|
49
|
-
dependencies[
|
|
50
|
-
} else if (config.uiLibrary ===
|
|
51
|
-
dependencies[
|
|
52
|
-
dependencies[
|
|
53
|
-
dependencies[
|
|
54
|
-
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
55
|
}
|
|
56
56
|
|
|
57
57
|
// CSS Framework dependencies
|
|
58
|
-
if (config.cssFramework ===
|
|
59
|
-
devDependencies[
|
|
60
|
-
devDependencies[
|
|
61
|
-
devDependencies[
|
|
62
|
-
} else if (config.cssFramework ===
|
|
63
|
-
dependencies[
|
|
64
|
-
devDependencies[
|
|
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
65
|
}
|
|
66
66
|
|
|
67
67
|
// State Management dependencies
|
|
68
|
-
if (config.stateManagement ===
|
|
69
|
-
dependencies[
|
|
70
|
-
dependencies[
|
|
71
|
-
dependencies[
|
|
72
|
-
} else if (config.stateManagement ===
|
|
73
|
-
dependencies[
|
|
74
|
-
} else if (config.stateManagement ===
|
|
75
|
-
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
76
|
}
|
|
77
77
|
|
|
78
78
|
// Data Fetching dependencies
|
|
79
|
-
if (config.dataFetching ===
|
|
80
|
-
dependencies[
|
|
81
|
-
dependencies[
|
|
82
|
-
} else if (config.dataFetching ===
|
|
83
|
-
dependencies[
|
|
84
|
-
} else if (config.dataFetching ===
|
|
85
|
-
dependencies[
|
|
86
|
-
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
87
|
}
|
|
88
88
|
|
|
89
89
|
// Routing dependencies
|
|
90
|
-
if (config.routing ===
|
|
91
|
-
dependencies[
|
|
92
|
-
} else if (config.routing ===
|
|
93
|
-
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
94
|
}
|
|
95
95
|
|
|
96
96
|
// Icon dependencies
|
|
97
|
-
if (config.icons ===
|
|
98
|
-
dependencies[
|
|
99
|
-
} else if (config.icons ===
|
|
100
|
-
dependencies[
|
|
101
|
-
} else if (config.icons ===
|
|
102
|
-
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
103
|
}
|
|
104
104
|
|
|
105
105
|
// Development Tools dependencies
|
|
106
|
-
if (config.devTools.includes(
|
|
107
|
-
devDependencies[
|
|
108
|
-
devDependencies[
|
|
109
|
-
devDependencies[
|
|
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
110
|
}
|
|
111
111
|
|
|
112
|
-
if (config.devTools.includes(
|
|
113
|
-
devDependencies[
|
|
114
|
-
devDependencies[
|
|
115
|
-
devDependencies[
|
|
116
|
-
devDependencies[
|
|
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
117
|
}
|
|
118
118
|
|
|
119
|
-
if (config.devTools.includes(
|
|
120
|
-
devDependencies[
|
|
121
|
-
devDependencies[
|
|
119
|
+
if (config.devTools.includes('husky')) {
|
|
120
|
+
devDependencies['husky'] = '^9.0.0';
|
|
121
|
+
devDependencies['lint-staged'] = '^15.0.0';
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// PWA dependencies
|
|
125
125
|
if (config.pwa) {
|
|
126
|
-
devDependencies[
|
|
126
|
+
devDependencies['vite-plugin-pwa'] = '^0.20.0';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
packageJson.dependencies = dependencies;
|
|
@@ -136,7 +136,7 @@ async function configureLibraries(config) {
|
|
|
136
136
|
const { projectPath } = config;
|
|
137
137
|
|
|
138
138
|
// Configure Tailwind if selected
|
|
139
|
-
if (config.cssFramework ===
|
|
139
|
+
if (config.cssFramework === 'tailwind') {
|
|
140
140
|
await configureTailwind(projectPath);
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -167,17 +167,11 @@ export default {
|
|
|
167
167
|
},
|
|
168
168
|
}`;
|
|
169
169
|
|
|
170
|
-
await fs.writeFile(
|
|
171
|
-
|
|
172
|
-
tailwindConfig,
|
|
173
|
-
);
|
|
174
|
-
await fs.writeFile(
|
|
175
|
-
path.join(projectPath, "postcss.config.js"),
|
|
176
|
-
postcssConfig,
|
|
177
|
-
);
|
|
170
|
+
await fs.writeFile(path.join(projectPath, 'tailwind.config.js'), tailwindConfig);
|
|
171
|
+
await fs.writeFile(path.join(projectPath, 'postcss.config.js'), postcssConfig);
|
|
178
172
|
|
|
179
173
|
// Add Tailwind directives to CSS
|
|
180
|
-
const cssPath = path.join(projectPath,
|
|
174
|
+
const cssPath = path.join(projectPath, 'src/index.css');
|
|
181
175
|
const tailwindDirectives = `@tailwind base;
|
|
182
176
|
@tailwind components;
|
|
183
177
|
@tailwind utilities;
|
|
@@ -185,7 +179,7 @@ export default {
|
|
|
185
179
|
`;
|
|
186
180
|
|
|
187
181
|
if (await fs.pathExists(cssPath)) {
|
|
188
|
-
const existingCss = await fs.readFile(cssPath,
|
|
182
|
+
const existingCss = await fs.readFile(cssPath, 'utf8');
|
|
189
183
|
await fs.writeFile(cssPath, tailwindDirectives + existingCss);
|
|
190
184
|
} else {
|
|
191
185
|
await fs.writeFile(cssPath, tailwindDirectives);
|
|
@@ -206,23 +200,16 @@ async function initializeGit(config) {
|
|
|
206
200
|
const { projectPath } = config;
|
|
207
201
|
|
|
208
202
|
try {
|
|
209
|
-
console.log(chalk.blue(
|
|
203
|
+
console.log(chalk.blue('š§ Initializing Git repository...'));
|
|
210
204
|
|
|
211
|
-
await runCommand(
|
|
212
|
-
await runCommand(
|
|
213
|
-
await runCommand(
|
|
214
|
-
"git",
|
|
215
|
-
["commit", "-m", "Initial commit from create-modern-react"],
|
|
216
|
-
projectPath,
|
|
217
|
-
);
|
|
205
|
+
await runCommand('git', ['init'], projectPath);
|
|
206
|
+
await runCommand('git', ['add', '.'], projectPath);
|
|
207
|
+
await runCommand('git', ['commit', '-m', 'Initial commit from create-modern-react'], projectPath);
|
|
218
208
|
|
|
219
|
-
console.log(chalk.green(
|
|
209
|
+
console.log(chalk.green('ā
Git repository initialized!'));
|
|
220
210
|
} catch (error) {
|
|
221
|
-
console.warn(
|
|
222
|
-
chalk.yellow("ā ļø Could not initialize Git repository:"),
|
|
223
|
-
error.message,
|
|
224
|
-
);
|
|
211
|
+
console.warn(chalk.yellow('ā ļø Could not initialize Git repository:'), error.message);
|
|
225
212
|
}
|
|
226
213
|
}
|
|
227
214
|
|
|
228
|
-
module.exports = { setupProject };
|
|
215
|
+
module.exports = { setupProject };
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useState } from
|
|
2
|
-
import
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import './App.css'
|
|
3
3
|
|
|
4
4
|
function App() {
|
|
5
|
-
const [count, setCount] = useState(0)
|
|
5
|
+
const [count, setCount] = useState(0)
|
|
6
6
|
|
|
7
7
|
return (
|
|
8
8
|
<div className="App">
|
|
@@ -16,10 +16,12 @@ function App() {
|
|
|
16
16
|
Edit <code>src/App.tsx</code> and save to test HMR
|
|
17
17
|
</p>
|
|
18
18
|
</div>
|
|
19
|
-
<p className="read-the-docs">
|
|
19
|
+
<p className="read-the-docs">
|
|
20
|
+
Click on the React logo to learn more
|
|
21
|
+
</p>
|
|
20
22
|
</div>
|
|
21
23
|
</div>
|
|
22
|
-
)
|
|
24
|
+
)
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
export default App
|
|
27
|
+
export default App
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import ReactDOM from
|
|
3
|
-
import App from
|
|
4
|
-
import
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom/client'
|
|
3
|
+
import App from './App.tsx'
|
|
4
|
+
import './index.css'
|
|
5
5
|
|
|
6
|
-
ReactDOM.createRoot(document.getElementById(
|
|
6
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
7
7
|
<React.StrictMode>
|
|
8
8
|
<App />
|
|
9
9
|
</React.StrictMode>,
|
|
10
|
-
)
|
|
10
|
+
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import react from
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
3
|
|
|
4
4
|
// https://vitejs.dev/config/
|
|
5
5
|
export default defineConfig({
|
|
6
6
|
plugins: [react()],
|
|
7
7
|
resolve: {
|
|
8
8
|
alias: {
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
})
|
|
9
|
+
'~': '/src'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
})
|