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/setup.js
CHANGED
|
@@ -3,32 +3,63 @@ const path = require('path');
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { runCommand } = require('./install');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Main setup function - orchestrates the entire project generation
|
|
8
|
+
*/
|
|
6
9
|
async function setupProject(config) {
|
|
7
10
|
const { projectPath, projectName } = config;
|
|
8
11
|
|
|
9
|
-
console.log(chalk.blue('\nšļø Setting up project structure
|
|
12
|
+
console.log(chalk.blue('\nšļø Setting up project structure...\n'));
|
|
10
13
|
|
|
11
|
-
// Create project directory
|
|
14
|
+
// Step 1: Create project directory
|
|
12
15
|
await fs.ensureDir(projectPath);
|
|
13
16
|
|
|
14
|
-
// Copy base template
|
|
17
|
+
// Step 2: Copy base template
|
|
18
|
+
console.log(chalk.gray(' Copying base template...'));
|
|
15
19
|
const templatePath = path.join(__dirname, '../templates/base');
|
|
16
20
|
await fs.copy(templatePath, projectPath);
|
|
17
21
|
|
|
18
|
-
//
|
|
19
|
-
|
|
22
|
+
// Step 3: Handle Antd vs Shadcn/ui
|
|
23
|
+
if (config.useAntd) {
|
|
24
|
+
console.log(chalk.gray(' Configuring Ant Design (removing Shadcn/ui)...'));
|
|
25
|
+
await removeDirectory(path.join(projectPath, 'src/components/ui'));
|
|
26
|
+
await fs.remove(path.join(projectPath, 'components.json'));
|
|
27
|
+
await copyOptionalTemplate('antd', projectPath);
|
|
28
|
+
await updateProvidersForAntd(projectPath);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Step 4: Copy optional feature templates
|
|
32
|
+
if (config.useRedux) {
|
|
33
|
+
console.log(chalk.gray(' Adding Redux Toolkit + Redux Persist...'));
|
|
34
|
+
await copyOptionalTemplate('redux', projectPath);
|
|
35
|
+
await updateProvidersForRedux(projectPath, config.useAntd);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (config.useHusky) {
|
|
39
|
+
console.log(chalk.gray(' Adding Husky + lint-staged...'));
|
|
40
|
+
await copyOptionalTemplate('husky', projectPath);
|
|
41
|
+
}
|
|
20
42
|
|
|
21
|
-
//
|
|
22
|
-
|
|
43
|
+
// Step 5: Update package.json
|
|
44
|
+
console.log(chalk.gray(' Configuring package.json...'));
|
|
45
|
+
await updatePackageJson(config);
|
|
23
46
|
|
|
24
|
-
// Initialize git if requested
|
|
25
|
-
if (config.
|
|
47
|
+
// Step 6: Initialize git if requested
|
|
48
|
+
if (config.initGit) {
|
|
26
49
|
await initializeGit(config);
|
|
27
50
|
}
|
|
28
51
|
|
|
29
|
-
|
|
52
|
+
// Step 7: Setup Husky if selected
|
|
53
|
+
if (config.useHusky && config.initGit) {
|
|
54
|
+
await setupHusky(projectPath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(chalk.green('\nā
Project structure created successfully!\n'));
|
|
30
58
|
}
|
|
31
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Update package.json with project name and selected dependencies
|
|
62
|
+
*/
|
|
32
63
|
async function updatePackageJson(config) {
|
|
33
64
|
const packageJsonPath = path.join(config.projectPath, 'package.json');
|
|
34
65
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
@@ -36,180 +67,270 @@ async function updatePackageJson(config) {
|
|
|
36
67
|
// Update name
|
|
37
68
|
packageJson.name = config.projectName;
|
|
38
69
|
|
|
39
|
-
// Add dependencies
|
|
70
|
+
// Add optional dependencies
|
|
40
71
|
const dependencies = { ...packageJson.dependencies };
|
|
41
72
|
const devDependencies = { ...packageJson.devDependencies };
|
|
42
73
|
|
|
43
|
-
//
|
|
44
|
-
if (config.
|
|
45
|
-
dependencies['
|
|
46
|
-
|
|
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';
|
|
74
|
+
// Redux dependencies
|
|
75
|
+
if (config.useRedux) {
|
|
76
|
+
dependencies['@reduxjs/toolkit'] = '^2.2.0';
|
|
77
|
+
dependencies['react-redux'] = '^9.1.0';
|
|
71
78
|
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
79
|
}
|
|
88
80
|
|
|
89
|
-
//
|
|
90
|
-
if (config.
|
|
91
|
-
dependencies['
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
// Ant Design dependencies (replaces Shadcn)
|
|
82
|
+
if (config.useAntd) {
|
|
83
|
+
dependencies['antd'] = '^5.20.0';
|
|
84
|
+
dependencies['@ant-design/icons'] = '^5.4.0';
|
|
85
|
+
// Remove Radix dependency when using Antd
|
|
86
|
+
delete dependencies['@radix-ui/react-slot'];
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
//
|
|
97
|
-
if (config.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
dependencies['react-icons'] = '^5.0.0';
|
|
101
|
-
} else if (config.icons === 'heroicons') {
|
|
102
|
-
dependencies['@heroicons/react'] = '^2.0.0';
|
|
103
|
-
}
|
|
89
|
+
// Husky dependencies
|
|
90
|
+
if (config.useHusky) {
|
|
91
|
+
devDependencies['husky'] = '^9.1.0';
|
|
92
|
+
devDependencies['lint-staged'] = '^15.2.0';
|
|
104
93
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
devDependencies['@storybook/addon-essentials'] = '^8.0.0';
|
|
109
|
-
devDependencies['storybook'] = '^8.0.0';
|
|
94
|
+
// Add prepare script for husky
|
|
95
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
96
|
+
packageJson.scripts.prepare = 'husky';
|
|
110
97
|
}
|
|
111
98
|
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
}
|
|
99
|
+
packageJson.dependencies = sortObjectKeys(dependencies);
|
|
100
|
+
packageJson.devDependencies = sortObjectKeys(devDependencies);
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
devDependencies['lint-staged'] = '^15.0.0';
|
|
122
|
-
}
|
|
102
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
103
|
+
}
|
|
123
104
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Copy an optional template to the project
|
|
107
|
+
*/
|
|
108
|
+
async function copyOptionalTemplate(templateName, projectPath) {
|
|
109
|
+
const optionalTemplatePath = path.join(
|
|
110
|
+
__dirname,
|
|
111
|
+
'../templates/optional',
|
|
112
|
+
templateName
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (await fs.pathExists(optionalTemplatePath)) {
|
|
116
|
+
// For redux and antd, copy to src directory
|
|
117
|
+
if (templateName === 'redux') {
|
|
118
|
+
await fs.copy(optionalTemplatePath, path.join(projectPath, 'src/redux'));
|
|
119
|
+
} else if (templateName === 'antd') {
|
|
120
|
+
await fs.copy(optionalTemplatePath, path.join(projectPath, 'src/antd'));
|
|
121
|
+
// Also copy the styles file to src/styles
|
|
122
|
+
const stylesFile = path.join(optionalTemplatePath, 'styles/antd-overrides.css');
|
|
123
|
+
if (await fs.pathExists(stylesFile)) {
|
|
124
|
+
await fs.ensureDir(path.join(projectPath, 'src/styles'));
|
|
125
|
+
await fs.copy(
|
|
126
|
+
stylesFile,
|
|
127
|
+
path.join(projectPath, 'src/styles/antd-overrides.css')
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
} else if (templateName === 'husky') {
|
|
131
|
+
// Copy husky files to root
|
|
132
|
+
await fs.copy(
|
|
133
|
+
path.join(optionalTemplatePath, '.husky'),
|
|
134
|
+
path.join(projectPath, '.husky')
|
|
135
|
+
);
|
|
136
|
+
await fs.copy(
|
|
137
|
+
path.join(optionalTemplatePath, '.lintstagedrc.json'),
|
|
138
|
+
path.join(projectPath, '.lintstagedrc.json')
|
|
139
|
+
);
|
|
140
|
+
}
|
|
127
141
|
}
|
|
142
|
+
}
|
|
128
143
|
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Update providers/index.tsx to include Antd ConfigProvider
|
|
146
|
+
*/
|
|
147
|
+
async function updateProvidersForAntd(projectPath) {
|
|
148
|
+
const providersContent = `import { type ReactNode } from 'react';
|
|
149
|
+
import { ThemeProvider } from './theme-provider';
|
|
150
|
+
import { RootLayout } from '~/components/layout';
|
|
151
|
+
import { ErrorBoundary } from '~/components/layout';
|
|
152
|
+
import { AntdConfigProvider } from '~/antd';
|
|
153
|
+
import '~/styles/antd-overrides.css';
|
|
154
|
+
|
|
155
|
+
interface ProvidersProps {
|
|
156
|
+
children: ReactNode;
|
|
157
|
+
}
|
|
131
158
|
|
|
132
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Application providers composition
|
|
161
|
+
* Wraps the app with all necessary context providers
|
|
162
|
+
*/
|
|
163
|
+
export function Providers({ children }: ProvidersProps) {
|
|
164
|
+
return (
|
|
165
|
+
<ErrorBoundary>
|
|
166
|
+
<ThemeProvider defaultTheme="system" storageKey="app-theme">
|
|
167
|
+
<AntdConfigProvider>
|
|
168
|
+
<RootLayout>{children}</RootLayout>
|
|
169
|
+
</AntdConfigProvider>
|
|
170
|
+
</ThemeProvider>
|
|
171
|
+
</ErrorBoundary>
|
|
172
|
+
);
|
|
133
173
|
}
|
|
134
174
|
|
|
135
|
-
|
|
136
|
-
|
|
175
|
+
export { ThemeProvider, useTheme } from './theme-provider';
|
|
176
|
+
`;
|
|
137
177
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
178
|
+
await fs.writeFile(
|
|
179
|
+
path.join(projectPath, 'src/providers/index.tsx'),
|
|
180
|
+
providersContent
|
|
181
|
+
);
|
|
142
182
|
|
|
143
|
-
//
|
|
144
|
-
await
|
|
183
|
+
// Create styles directory if it doesn't exist
|
|
184
|
+
await fs.ensureDir(path.join(projectPath, 'src/styles'));
|
|
185
|
+
}
|
|
145
186
|
|
|
146
|
-
|
|
147
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Update providers/index.tsx to include Redux Provider
|
|
189
|
+
*/
|
|
190
|
+
async function updateProvidersForRedux(projectPath, hasAntd) {
|
|
191
|
+
let providersContent;
|
|
192
|
+
|
|
193
|
+
if (hasAntd) {
|
|
194
|
+
providersContent = `import { type ReactNode } from 'react';
|
|
195
|
+
import { ThemeProvider } from './theme-provider';
|
|
196
|
+
import { RootLayout } from '~/components/layout';
|
|
197
|
+
import { ErrorBoundary } from '~/components/layout';
|
|
198
|
+
import { AntdConfigProvider } from '~/antd';
|
|
199
|
+
import { ReduxProvider } from '~/redux';
|
|
200
|
+
import '~/styles/antd-overrides.css';
|
|
201
|
+
|
|
202
|
+
interface ProvidersProps {
|
|
203
|
+
children: ReactNode;
|
|
148
204
|
}
|
|
149
205
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
await fs.writeFile(path.join(projectPath, 'tailwind.config.js'), tailwindConfig);
|
|
171
|
-
await fs.writeFile(path.join(projectPath, 'postcss.config.js'), postcssConfig);
|
|
172
|
-
|
|
173
|
-
// Add Tailwind directives to CSS
|
|
174
|
-
const cssPath = path.join(projectPath, 'src/index.css');
|
|
175
|
-
const tailwindDirectives = `@tailwind base;
|
|
176
|
-
@tailwind components;
|
|
177
|
-
@tailwind utilities;
|
|
206
|
+
/**
|
|
207
|
+
* Application providers composition
|
|
208
|
+
* Wraps the app with all necessary context providers
|
|
209
|
+
*
|
|
210
|
+
* Order: ErrorBoundary > Redux > Theme > Antd > Layout
|
|
211
|
+
*/
|
|
212
|
+
export function Providers({ children }: ProvidersProps) {
|
|
213
|
+
return (
|
|
214
|
+
<ErrorBoundary>
|
|
215
|
+
<ReduxProvider>
|
|
216
|
+
<ThemeProvider defaultTheme="system" storageKey="app-theme">
|
|
217
|
+
<AntdConfigProvider>
|
|
218
|
+
<RootLayout>{children}</RootLayout>
|
|
219
|
+
</AntdConfigProvider>
|
|
220
|
+
</ThemeProvider>
|
|
221
|
+
</ReduxProvider>
|
|
222
|
+
</ErrorBoundary>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
178
225
|
|
|
226
|
+
export { ThemeProvider, useTheme } from './theme-provider';
|
|
179
227
|
`;
|
|
180
|
-
|
|
181
|
-
if (await fs.pathExists(cssPath)) {
|
|
182
|
-
const existingCss = await fs.readFile(cssPath, 'utf8');
|
|
183
|
-
await fs.writeFile(cssPath, tailwindDirectives + existingCss);
|
|
184
228
|
} else {
|
|
185
|
-
|
|
186
|
-
|
|
229
|
+
providersContent = `import { type ReactNode } from 'react';
|
|
230
|
+
import { ThemeProvider } from './theme-provider';
|
|
231
|
+
import { RootLayout } from '~/components/layout';
|
|
232
|
+
import { ErrorBoundary } from '~/components/layout';
|
|
233
|
+
import { ReduxProvider } from '~/redux';
|
|
234
|
+
|
|
235
|
+
interface ProvidersProps {
|
|
236
|
+
children: ReactNode;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Application providers composition
|
|
241
|
+
* Wraps the app with all necessary context providers
|
|
242
|
+
*
|
|
243
|
+
* Order: ErrorBoundary > Redux > Theme > Layout
|
|
244
|
+
*/
|
|
245
|
+
export function Providers({ children }: ProvidersProps) {
|
|
246
|
+
return (
|
|
247
|
+
<ErrorBoundary>
|
|
248
|
+
<ReduxProvider>
|
|
249
|
+
<ThemeProvider defaultTheme="system" storageKey="app-theme">
|
|
250
|
+
<RootLayout>{children}</RootLayout>
|
|
251
|
+
</ThemeProvider>
|
|
252
|
+
</ReduxProvider>
|
|
253
|
+
</ErrorBoundary>
|
|
254
|
+
);
|
|
187
255
|
}
|
|
188
256
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
257
|
+
export { ThemeProvider, useTheme } from './theme-provider';
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await fs.writeFile(
|
|
262
|
+
path.join(projectPath, 'src/providers/index.tsx'),
|
|
263
|
+
providersContent
|
|
264
|
+
);
|
|
192
265
|
}
|
|
193
266
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
267
|
+
/**
|
|
268
|
+
* Remove a directory and all its contents
|
|
269
|
+
*/
|
|
270
|
+
async function removeDirectory(dirPath) {
|
|
271
|
+
if (await fs.pathExists(dirPath)) {
|
|
272
|
+
await fs.remove(dirPath);
|
|
273
|
+
}
|
|
197
274
|
}
|
|
198
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Initialize git repository
|
|
278
|
+
*/
|
|
199
279
|
async function initializeGit(config) {
|
|
200
280
|
const { projectPath } = config;
|
|
201
281
|
|
|
202
282
|
try {
|
|
203
|
-
console.log(chalk.
|
|
283
|
+
console.log(chalk.gray(' Initializing Git repository...'));
|
|
204
284
|
|
|
205
285
|
await runCommand('git', ['init'], projectPath);
|
|
206
286
|
await runCommand('git', ['add', '.'], projectPath);
|
|
207
|
-
await runCommand(
|
|
287
|
+
await runCommand(
|
|
288
|
+
'git',
|
|
289
|
+
['commit', '-m', 'Initial commit from create-modern-react'],
|
|
290
|
+
projectPath
|
|
291
|
+
);
|
|
208
292
|
|
|
209
|
-
console.log(chalk.green('ā
Git repository initialized!'));
|
|
293
|
+
console.log(chalk.green(' ā
Git repository initialized!'));
|
|
210
294
|
} catch (error) {
|
|
211
|
-
console.warn(
|
|
295
|
+
console.warn(
|
|
296
|
+
chalk.yellow(' ā ļø Could not initialize Git repository:'),
|
|
297
|
+
error.message
|
|
298
|
+
);
|
|
212
299
|
}
|
|
213
300
|
}
|
|
214
301
|
|
|
215
|
-
|
|
302
|
+
/**
|
|
303
|
+
* Setup Husky hooks after git init
|
|
304
|
+
*/
|
|
305
|
+
async function setupHusky(projectPath) {
|
|
306
|
+
try {
|
|
307
|
+
console.log(chalk.gray(' Setting up Husky git hooks...'));
|
|
308
|
+
|
|
309
|
+
// Make pre-commit hook executable
|
|
310
|
+
const preCommitPath = path.join(projectPath, '.husky/pre-commit');
|
|
311
|
+
if (await fs.pathExists(preCommitPath)) {
|
|
312
|
+
await fs.chmod(preCommitPath, '755');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
console.log(chalk.green(' ā
Husky configured!'));
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.warn(
|
|
318
|
+
chalk.yellow(' ā ļø Could not setup Husky:'),
|
|
319
|
+
error.message
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Sort object keys alphabetically
|
|
326
|
+
*/
|
|
327
|
+
function sortObjectKeys(obj) {
|
|
328
|
+
return Object.keys(obj)
|
|
329
|
+
.sort()
|
|
330
|
+
.reduce((sorted, key) => {
|
|
331
|
+
sorted[key] = obj[key];
|
|
332
|
+
return sorted;
|
|
333
|
+
}, {});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
module.exports = { setupProject };
|
package/package.json
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-modern-react",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Create
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Create production-ready React + TypeScript + Tailwind applications in seconds",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-modern-react": "./bin/index.js"
|
|
8
8
|
},
|
|
9
9
|
"engines": {
|
|
10
|
-
"node": ">=
|
|
10
|
+
"node": ">=18.0.0"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"react",
|
|
14
|
-
"vite",
|
|
15
14
|
"typescript",
|
|
15
|
+
"vite",
|
|
16
|
+
"tailwindcss",
|
|
16
17
|
"cli",
|
|
18
|
+
"scaffold",
|
|
17
19
|
"boilerplate",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
20
|
+
"create-react-app",
|
|
21
|
+
"starter-kit",
|
|
22
|
+
"shadcn-ui",
|
|
23
|
+
"react-starter",
|
|
24
|
+
"vite-template",
|
|
25
|
+
"react-boilerplate",
|
|
26
|
+
"npx",
|
|
27
|
+
"frontend",
|
|
28
|
+
"antd",
|
|
29
|
+
"redux"
|
|
21
30
|
],
|
|
22
31
|
"author": "Abhay Rana",
|
|
23
32
|
"license": "MIT",
|
|
24
33
|
"repository": {
|
|
25
34
|
"type": "git",
|
|
26
|
-
"url": "https://github.com/abhay-rana/create-modern-react.git"
|
|
35
|
+
"url": "git+https://github.com/abhay-rana/create-modern-react.git"
|
|
27
36
|
},
|
|
28
37
|
"bugs": {
|
|
29
38
|
"url": "https://github.com/abhay-rana/create-modern-react/issues"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
env: { browser: true, es2020: true },
|
|
4
|
+
extends: [
|
|
5
|
+
'eslint:recommended',
|
|
6
|
+
'plugin:@typescript-eslint/recommended',
|
|
7
|
+
'plugin:react-hooks/recommended',
|
|
8
|
+
],
|
|
9
|
+
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
|
10
|
+
parser: '@typescript-eslint/parser',
|
|
11
|
+
plugins: ['react-refresh', 'unused-imports'],
|
|
12
|
+
rules: {
|
|
13
|
+
'react-refresh/only-export-components': [
|
|
14
|
+
'warn',
|
|
15
|
+
{ allowConstantExport: true },
|
|
16
|
+
],
|
|
17
|
+
// Unused imports
|
|
18
|
+
'unused-imports/no-unused-imports': 'error',
|
|
19
|
+
'unused-imports/no-unused-vars': [
|
|
20
|
+
'warn',
|
|
21
|
+
{
|
|
22
|
+
vars: 'all',
|
|
23
|
+
varsIgnorePattern: '^_',
|
|
24
|
+
args: 'after-used',
|
|
25
|
+
argsIgnorePattern: '^_',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
// TypeScript
|
|
29
|
+
'@typescript-eslint/no-unused-vars': 'off', // Handled by unused-imports
|
|
30
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
31
|
+
'@typescript-eslint/prefer-as-const': 'error',
|
|
32
|
+
// General
|
|
33
|
+
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
|
34
|
+
'prefer-const': 'error',
|
|
35
|
+
'no-var': 'error',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "default",
|
|
4
|
+
"rsc": false,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "tailwind.config.js",
|
|
8
|
+
"css": "src/index.css",
|
|
9
|
+
"baseColor": "slate",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "~/components",
|
|
15
|
+
"utils": "~/lib/utils"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
2
|
+
<html lang="en" class="light">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta name="description" content="Modern React application built with create-modern-react" />
|
|
7
8
|
<title>Modern React App</title>
|
|
8
9
|
</head>
|
|
9
10
|
<body>
|
|
10
11
|
<div id="root"></div>
|
|
11
12
|
<script type="module" src="/src/main.tsx"></script>
|
|
12
13
|
</body>
|
|
13
|
-
</html>
|
|
14
|
+
</html>
|