create-modern-react 1.0.0 ā 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -72
- package/bin/index.js +13 -13
- package/lib/install.js +103 -32
- package/lib/prompts.js +152 -179
- package/lib/setup.js +267 -159
- 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 +2 -1
- package/templates/base/package.json +33 -14
- package/templates/base/postcss.config.js +6 -0
- package/templates/base/src/App.tsx +5 -18
- 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 +73 -60
- 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 +4 -3
- package/templates/base/tsconfig.node.json +22 -0
- package/templates/base/vite.config.ts +65 -4
- 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
|
@@ -1,228 +1,336 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const chalk = require(
|
|
4
|
-
const { runCommand } = require(
|
|
5
|
-
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { runCommand } = require('./install');
|
|
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(
|
|
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
|
|
15
|
-
|
|
17
|
+
// Step 2: Copy base template
|
|
18
|
+
console.log(chalk.gray(' Copying base template...'));
|
|
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
|
-
const packageJsonPath = path.join(config.projectPath,
|
|
64
|
+
const packageJsonPath = path.join(config.projectPath, 'package.json');
|
|
34
65
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
35
66
|
|
|
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[
|
|
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";
|
|
74
|
+
// Redux dependencies
|
|
75
|
+
if (config.useRedux) {
|
|
76
|
+
dependencies['@reduxjs/toolkit'] = '^2.2.0';
|
|
77
|
+
dependencies['react-redux'] = '^9.1.0';
|
|
78
|
+
dependencies['redux-persist'] = '^6.0.0';
|
|
110
79
|
}
|
|
111
80
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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'];
|
|
117
87
|
}
|
|
118
88
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
devDependencies[
|
|
122
|
-
|
|
89
|
+
// Husky dependencies
|
|
90
|
+
if (config.useHusky) {
|
|
91
|
+
devDependencies['husky'] = '^9.1.0';
|
|
92
|
+
devDependencies['lint-staged'] = '^15.2.0';
|
|
123
93
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
// Add prepare script for husky
|
|
95
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
96
|
+
packageJson.scripts.prepare = 'husky';
|
|
127
97
|
}
|
|
128
98
|
|
|
129
|
-
packageJson.dependencies = dependencies;
|
|
130
|
-
packageJson.devDependencies = devDependencies;
|
|
99
|
+
packageJson.dependencies = sortObjectKeys(dependencies);
|
|
100
|
+
packageJson.devDependencies = sortObjectKeys(devDependencies);
|
|
131
101
|
|
|
132
102
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
133
103
|
}
|
|
134
104
|
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
);
|
|
137
114
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
}
|
|
141
141
|
}
|
|
142
|
+
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
}
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
|
|
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
|
+
);
|
|
148
173
|
}
|
|
149
174
|
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
}`;
|
|
175
|
+
export { ThemeProvider, useTheme } from './theme-provider';
|
|
176
|
+
`;
|
|
169
177
|
|
|
170
178
|
await fs.writeFile(
|
|
171
|
-
path.join(projectPath,
|
|
172
|
-
|
|
173
|
-
);
|
|
174
|
-
await fs.writeFile(
|
|
175
|
-
path.join(projectPath, "postcss.config.js"),
|
|
176
|
-
postcssConfig,
|
|
179
|
+
path.join(projectPath, 'src/providers/index.tsx'),
|
|
180
|
+
providersContent
|
|
177
181
|
);
|
|
178
182
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
@tailwind components;
|
|
183
|
-
@tailwind utilities;
|
|
183
|
+
// Create styles directory if it doesn't exist
|
|
184
|
+
await fs.ensureDir(path.join(projectPath, 'src/styles'));
|
|
185
|
+
}
|
|
184
186
|
|
|
185
|
-
|
|
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;
|
|
204
|
+
}
|
|
186
205
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
+
}
|
|
225
|
+
|
|
226
|
+
export { ThemeProvider, useTheme } from './theme-provider';
|
|
227
|
+
`;
|
|
190
228
|
} else {
|
|
191
|
-
|
|
192
|
-
|
|
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;
|
|
193
237
|
}
|
|
194
238
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
+
);
|
|
198
255
|
}
|
|
199
256
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
+
);
|
|
203
265
|
}
|
|
204
266
|
|
|
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
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Initialize git repository
|
|
278
|
+
*/
|
|
205
279
|
async function initializeGit(config) {
|
|
206
280
|
const { projectPath } = config;
|
|
207
281
|
|
|
208
282
|
try {
|
|
209
|
-
console.log(chalk.
|
|
283
|
+
console.log(chalk.gray(' Initializing Git repository...'));
|
|
210
284
|
|
|
211
|
-
await runCommand(
|
|
212
|
-
await runCommand(
|
|
285
|
+
await runCommand('git', ['init'], projectPath);
|
|
286
|
+
await runCommand('git', ['add', '.'], projectPath);
|
|
213
287
|
await runCommand(
|
|
214
|
-
|
|
215
|
-
[
|
|
216
|
-
projectPath
|
|
288
|
+
'git',
|
|
289
|
+
['commit', '-m', 'Initial commit from create-modern-react'],
|
|
290
|
+
projectPath
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
console.log(chalk.green(' ā
Git repository initialized!'));
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.warn(
|
|
296
|
+
chalk.yellow(' ā ļø Could not initialize Git repository:'),
|
|
297
|
+
error.message
|
|
217
298
|
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
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...'));
|
|
218
308
|
|
|
219
|
-
|
|
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!'));
|
|
220
316
|
} catch (error) {
|
|
221
317
|
console.warn(
|
|
222
|
-
chalk.yellow(
|
|
223
|
-
error.message
|
|
318
|
+
chalk.yellow(' ā ļø Could not setup Husky:'),
|
|
319
|
+
error.message
|
|
224
320
|
);
|
|
225
321
|
}
|
|
226
322
|
}
|
|
227
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
|
+
|
|
228
336
|
module.exports = { setupProject };
|
package/package.json
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-modern-react",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Create
|
|
3
|
+
"version": "2.0.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,9 +1,10 @@
|
|
|
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>
|