create-modern-react 2.3.6 → 2.3.7

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/lib/setup.js CHANGED
@@ -38,6 +38,8 @@ async function setupProject(config) {
38
38
  await fs.remove(path.join(projectPath, 'components.json'));
39
39
  await copyOptionalTemplate('antd', projectPath);
40
40
  await updateProvidersForAntd(projectPath);
41
+ // Replace components that use ~/components/ui with antd-compatible versions
42
+ await replaceComponentsForAntd(projectPath);
41
43
  }
42
44
 
43
45
  // Step 4: Copy optional feature templates
@@ -45,6 +47,10 @@ async function setupProject(config) {
45
47
  console.log(chalk.gray(' Adding Redux Toolkit + Redux Persist...'));
46
48
  await copyOptionalTemplate('redux', projectPath);
47
49
  await updateProvidersForRedux(projectPath, config.useAntd);
50
+ // If antd is selected, replace redux provider with antd-compatible version
51
+ if (config.useAntd) {
52
+ await replaceReduxProviderForAntd(projectPath);
53
+ }
48
54
  }
49
55
 
50
56
  if (config.useForms) {
@@ -210,6 +216,50 @@ export { ThemeProvider, useTheme } from './theme-provider';
210
216
  await fs.ensureDir(path.join(projectPath, 'src/styles'));
211
217
  }
212
218
 
219
+ /**
220
+ * Replace components that import from ~/components/ui with antd-compatible versions
221
+ * These files use shadcn Button, Card, Skeleton which are removed when antd is selected
222
+ */
223
+ async function replaceComponentsForAntd(projectPath) {
224
+ const replacementsPath = path.join(
225
+ __dirname,
226
+ '../templates/optional/antd-replacements'
227
+ );
228
+
229
+ // Files to replace: [source in antd-replacements, target in project]
230
+ const filesToReplace = [
231
+ ['components/layout/error-boundary.tsx', 'src/components/layout/error-boundary.tsx'],
232
+ ['routes/index.tsx', 'src/routes/index.tsx'],
233
+ ['screens/home/index.tsx', 'src/screens/home/index.tsx'],
234
+ ['screens/not-found/index.tsx', 'src/screens/not-found/index.tsx'],
235
+ ];
236
+
237
+ for (const [source, target] of filesToReplace) {
238
+ const sourcePath = path.join(replacementsPath, source);
239
+ const targetPath = path.join(projectPath, target);
240
+
241
+ if (await fs.pathExists(sourcePath)) {
242
+ await fs.copy(sourcePath, targetPath);
243
+ }
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Replace Redux provider with antd-compatible version
249
+ * The base provider imports Skeleton from ~/components/ui which is removed for antd
250
+ */
251
+ async function replaceReduxProviderForAntd(projectPath) {
252
+ const antdProviderPath = path.join(
253
+ __dirname,
254
+ '../templates/optional/antd-replacements/redux/provider.tsx'
255
+ );
256
+ const targetPath = path.join(projectPath, 'src/redux/provider.tsx');
257
+
258
+ if (await fs.pathExists(antdProviderPath)) {
259
+ await fs.copy(antdProviderPath, targetPath);
260
+ }
261
+ }
262
+
213
263
  /**
214
264
  * Update providers/index.tsx to include Redux Provider
215
265
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-modern-react",
3
- "version": "2.3.6",
3
+ "version": "2.3.7",
4
4
  "description": "Create production-ready React + TypeScript + Tailwind applications in seconds",
5
5
  "main": "bin/index.js",
6
6
  "bin": {
@@ -0,0 +1,63 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ import { AlertTriangle, RefreshCw } from 'lucide-react';
3
+ import { Button } from 'antd';
4
+
5
+ interface Props {
6
+ children: ReactNode;
7
+ fallback?: ReactNode;
8
+ }
9
+
10
+ interface State {
11
+ hasError: boolean;
12
+ error: Error | null;
13
+ }
14
+
15
+ export class ErrorBoundary extends Component<Props, State> {
16
+ public state: State = {
17
+ hasError: false,
18
+ error: null,
19
+ };
20
+
21
+ public static getDerivedStateFromError(error: Error): State {
22
+ return { hasError: true, error };
23
+ }
24
+
25
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
26
+ console.error('ErrorBoundary caught an error:', error, errorInfo);
27
+ }
28
+
29
+ private handleRetry = () => {
30
+ this.setState({ hasError: false, error: null });
31
+ };
32
+
33
+ public render() {
34
+ if (this.state.hasError) {
35
+ if (this.props.fallback) {
36
+ return this.props.fallback;
37
+ }
38
+
39
+ return (
40
+ <div className="flex min-h-[400px] flex-col items-center justify-center gap-4 p-8">
41
+ <div className="flex h-16 w-16 items-center justify-center rounded-full bg-red-50 dark:bg-red-950">
42
+ <AlertTriangle className="h-8 w-8 text-red-500" />
43
+ </div>
44
+ <div className="text-center">
45
+ <h2 className="text-xl font-semibold">Something went wrong</h2>
46
+ <p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
47
+ {this.state.error?.message || 'An unexpected error occurred'}
48
+ </p>
49
+ </div>
50
+ <Button
51
+ onClick={this.handleRetry}
52
+ icon={<RefreshCw className="h-4 w-4" />}
53
+ className="mt-4"
54
+ >
55
+ Try again
56
+ </Button>
57
+ </div>
58
+ );
59
+ }
60
+
61
+ return this.props.children;
62
+ }
63
+ }
@@ -0,0 +1,33 @@
1
+ import { Provider } from 'react-redux';
2
+ import { PersistGate } from 'redux-persist/integration/react';
3
+ import { store, persistor } from './store';
4
+ import { Skeleton } from 'antd';
5
+
6
+ interface ReduxProviderProps {
7
+ children: React.ReactNode;
8
+ }
9
+
10
+ function LoadingFallback() {
11
+ return (
12
+ <div className="flex min-h-screen items-center justify-center">
13
+ <div className="flex flex-col items-center gap-4">
14
+ <Skeleton.Avatar active size={48} shape="circle" />
15
+ <Skeleton.Input active size="small" style={{ width: 128 }} />
16
+ </div>
17
+ </div>
18
+ );
19
+ }
20
+
21
+ /**
22
+ * Redux Provider with persistence
23
+ * Wraps the application with Redux store and persist gate
24
+ */
25
+ export function ReduxProvider({ children }: ReduxProviderProps) {
26
+ return (
27
+ <Provider store={store}>
28
+ <PersistGate loading={<LoadingFallback />} persistor={persistor}>
29
+ {children}
30
+ </PersistGate>
31
+ </Provider>
32
+ );
33
+ }
@@ -0,0 +1,40 @@
1
+ import { Suspense } from 'react';
2
+ import { Route, Switch } from 'wouter';
3
+ import { routes } from './routes';
4
+ import { Skeleton } from 'antd';
5
+
6
+ /**
7
+ * Loading fallback component
8
+ */
9
+ function RouteLoading() {
10
+ return (
11
+ <div className="flex min-h-screen items-center justify-center">
12
+ <div className="flex flex-col items-center gap-4">
13
+ <Skeleton.Avatar active size={48} shape="circle" />
14
+ <Skeleton.Input active size="small" style={{ width: 128 }} />
15
+ </div>
16
+ </div>
17
+ );
18
+ }
19
+
20
+ /**
21
+ * Application router using Wouter
22
+ * - Lightweight (2KB) alternative to React Router
23
+ * - Supports lazy loading with React.Suspense
24
+ * - Simple API with <Route> and <Switch>
25
+ */
26
+ export function AppRouter() {
27
+ return (
28
+ <Suspense fallback={<RouteLoading />}>
29
+ <Switch>
30
+ {routes.map(({ path, component: Component }) => (
31
+ <Route key={path} path={path}>
32
+ <Component />
33
+ </Route>
34
+ ))}
35
+ </Switch>
36
+ </Suspense>
37
+ );
38
+ }
39
+
40
+ export { routes } from './routes';
@@ -0,0 +1,227 @@
1
+ import { useState } from 'react';
2
+ import { Moon, Sun, Github, Zap, Bot, ExternalLink } from 'lucide-react';
3
+ import { Button, Card } from 'antd';
4
+ import { useTheme } from '~/providers';
5
+
6
+ export default function Home() {
7
+ const { theme, setTheme, resolvedTheme } = useTheme();
8
+ const [count, setCount] = useState(0);
9
+
10
+ const toggleTheme = () => {
11
+ setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
12
+ };
13
+
14
+ return (
15
+ <div className="flex min-h-screen flex-col items-center justify-center p-8">
16
+ <div className="w-full max-w-2xl space-y-8">
17
+ {/* Header */}
18
+ <div className="text-center">
19
+ <div className="mb-4 flex items-center justify-between">
20
+ <div className="w-40" />
21
+ <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-primary text-primary-foreground">
22
+ <Zap className="h-8 w-8" />
23
+ </div>
24
+ <Button
25
+ type="default"
26
+ href="https://github.com/abhay-rana/create-modern-react"
27
+ target="_blank"
28
+ rel="noopener noreferrer"
29
+ icon={<Github className="h-4 w-4" />}
30
+ >
31
+ View CLI on GitHub
32
+ </Button>
33
+ </div>
34
+ <h1 className="text-4xl font-bold tracking-tight">
35
+ create-modern-react
36
+ </h1>
37
+ <p className="mt-2 text-muted-foreground">
38
+ Production-ready React + TypeScript + Tailwind in seconds
39
+ </p>
40
+ <div className="mt-4">
41
+ <code className="rounded-md bg-muted px-3 py-1.5 font-mono text-sm">
42
+ npx create-modern-react my-app
43
+ </code>
44
+ </div>
45
+ </div>
46
+
47
+ {/* Counter Card */}
48
+ <Card
49
+ title={
50
+ <div className="flex items-center justify-between">
51
+ <span>Interactive Counter</span>
52
+ <Button
53
+ type="text"
54
+ shape="circle"
55
+ onClick={toggleTheme}
56
+ aria-label="Toggle theme"
57
+ icon={
58
+ resolvedTheme === 'dark' ? (
59
+ <Sun className="h-5 w-5" />
60
+ ) : (
61
+ <Moon className="h-5 w-5" />
62
+ )
63
+ }
64
+ />
65
+ </div>
66
+ }
67
+ >
68
+ <div className="space-y-4">
69
+ <div className="flex items-center justify-center gap-4">
70
+ <Button size="large" onClick={() => setCount((c) => c - 1)}>
71
+ -
72
+ </Button>
73
+ <span className="min-w-[4rem] text-center text-4xl font-bold tabular-nums">
74
+ {count}
75
+ </span>
76
+ <Button size="large" onClick={() => setCount((c) => c + 1)}>
77
+ +
78
+ </Button>
79
+ </div>
80
+ <p className="text-center text-sm text-muted-foreground">
81
+ Click the buttons to update the count
82
+ </p>
83
+ </div>
84
+ </Card>
85
+
86
+ {/* Features */}
87
+ <div className="grid gap-4 sm:grid-cols-2">
88
+ <FeatureCard
89
+ title="Vite + SWC"
90
+ description="Lightning fast builds with Hot Module Replacement"
91
+ />
92
+ <FeatureCard
93
+ title="TypeScript"
94
+ description="Full type safety with strict mode enabled"
95
+ />
96
+ <FeatureCard
97
+ title="Tailwind CSS"
98
+ description="Utility-first CSS with dark mode support"
99
+ />
100
+ <FeatureCard
101
+ title="Ant Design"
102
+ description="Enterprise-class UI components"
103
+ />
104
+ </div>
105
+
106
+ {/* AI Skills Highlight */}
107
+ <div className="rounded-lg border bg-gradient-to-r from-primary/5 to-primary/10 p-4">
108
+ <div className="flex items-start gap-3">
109
+ <Bot className="h-5 w-5 text-primary mt-0.5" />
110
+ <div>
111
+ <h3 className="font-semibold">Claude Code AI Skills</h3>
112
+ <p className="mt-1 text-sm text-muted-foreground">
113
+ 8 pre-configured skills for React best practices, UI/UX design, browser testing, and spec refinement
114
+ </p>
115
+ </div>
116
+ </div>
117
+ </div>
118
+
119
+ {/* Projects Showcase */}
120
+ <div className="space-y-4">
121
+ <h2 className="text-center text-2xl font-bold">
122
+ Production Projects created using this boilerplate
123
+ </h2>
124
+ <div className="grid gap-6 sm:grid-cols-2">
125
+ <ProjectCard
126
+ category="AI/Career"
127
+ title="ResumeFreePro"
128
+ description="AI-Powered Resume Builder"
129
+ designStyle="Modern + Glassmorphism"
130
+ url="https://resumefreepro.com?utm_source=starter-template&utm_medium=landing-page&utm_campaign=create-modern-react-demo"
131
+ previewUrl="https://storage.resumefreepro.com/media-files/resumefreepro.webp"
132
+ />
133
+ <ProjectCard
134
+ category="E-Pharmacy"
135
+ title="HealthMug"
136
+ description="Online Pharmacy Platform"
137
+ designStyle="Clean + Professional"
138
+ url="https://healthmug.com?utm_source=starter-template&utm_medium=landing-page&utm_campaign=create-modern-react-demo"
139
+ previewUrl="https://storage.resumefreepro.com/media-files/healthmug.webp"
140
+ />
141
+ </div>
142
+ </div>
143
+
144
+ <p className="text-center text-xs text-muted-foreground">
145
+ Current theme: {theme} (resolved: {resolvedTheme})
146
+ </p>
147
+ </div>
148
+ </div>
149
+ );
150
+ }
151
+
152
+ function FeatureCard({
153
+ title,
154
+ description,
155
+ }: {
156
+ title: string;
157
+ description: string;
158
+ }) {
159
+ return (
160
+ <div className="rounded-lg border bg-card p-4 text-card-foreground">
161
+ <h3 className="font-semibold">{title}</h3>
162
+ <p className="mt-1 text-sm text-muted-foreground">{description}</p>
163
+ </div>
164
+ );
165
+ }
166
+
167
+ function ProjectCard({
168
+ category,
169
+ title,
170
+ description,
171
+ designStyle,
172
+ url,
173
+ previewUrl,
174
+ }: {
175
+ category: string;
176
+ title: string;
177
+ description: string;
178
+ designStyle: string;
179
+ url: string;
180
+ previewUrl: string;
181
+ }) {
182
+ return (
183
+ <a
184
+ href={url}
185
+ target="_blank"
186
+ rel="noopener noreferrer"
187
+ className="group block"
188
+ >
189
+ <Card
190
+ hoverable
191
+ className="overflow-hidden"
192
+ cover={
193
+ <div className="relative h-48 overflow-hidden bg-muted">
194
+ <img
195
+ src={previewUrl}
196
+ alt={`${title} preview`}
197
+ className="h-full w-full object-cover object-top transition-transform duration-300 group-hover:scale-105"
198
+ />
199
+ {/* Overlay on hover */}
200
+ <div className="absolute inset-0 flex items-center justify-center bg-black/60 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
201
+ <div className="text-center text-white">
202
+ <ExternalLink className="mx-auto h-8 w-8" />
203
+ <p className="mt-2 text-sm font-medium">View Demo</p>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ }
208
+ >
209
+ <div className="space-y-3">
210
+ {/* Category Badge */}
211
+ <span className="inline-block rounded-md bg-primary/10 px-2 py-1 text-xs font-medium text-primary">
212
+ {category}
213
+ </span>
214
+
215
+ {/* Title & Description */}
216
+ <div>
217
+ <h3 className="font-bold">{title}</h3>
218
+ <p className="mt-1 text-sm text-muted-foreground">{description}</p>
219
+ </div>
220
+
221
+ {/* Design Style */}
222
+ <p className="text-xs text-muted-foreground">{designStyle}</p>
223
+ </div>
224
+ </Card>
225
+ </a>
226
+ );
227
+ }
@@ -0,0 +1,30 @@
1
+ import { Link } from 'wouter';
2
+ import { Home, ArrowLeft } from 'lucide-react';
3
+ import { Button } from 'antd';
4
+
5
+ export default function NotFound() {
6
+ return (
7
+ <div className="flex min-h-screen flex-col items-center justify-center p-8">
8
+ <div className="text-center">
9
+ <h1 className="text-9xl font-bold text-muted-foreground/20">404</h1>
10
+ <h2 className="mt-4 text-2xl font-semibold">Page not found</h2>
11
+ <p className="mt-2 text-muted-foreground">
12
+ Sorry, we couldn't find the page you're looking for.
13
+ </p>
14
+ <div className="mt-8 flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
15
+ <Link href="/">
16
+ <Button type="primary" icon={<Home className="h-4 w-4" />}>
17
+ Go home
18
+ </Button>
19
+ </Link>
20
+ <Button
21
+ icon={<ArrowLeft className="h-4 w-4" />}
22
+ onClick={() => window.history.back()}
23
+ >
24
+ Go back
25
+ </Button>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ );
30
+ }