build-app-with 2.0.11 → 2.0.12
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 +182 -69
- package/dist/build-app-with.mjs +464 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,109 +1,222 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Build App With
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Interactive CLI for scaffolding production-ready web applications**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/build-app-with)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](package.json)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Choose a framework, pick your features, and get a fully configured project in seconds — no manual setup required.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
- **⚛️ React with Next.js** - Full-stack React framework with server-side rendering
|
|
11
|
-
- **⚡ React with Vite** - Lightning-fast React development with modern tooling
|
|
11
|
+
## Quick Start
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
```bash
|
|
14
|
+
# Run directly (no install needed)
|
|
15
|
+
npx build-app-with
|
|
16
|
+
|
|
17
|
+
# Or provide a project name upfront
|
|
18
|
+
npx build-app-with my-app
|
|
19
|
+
```
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
**Prerequisites:** Node.js 18+ and npm 8+
|
|
18
22
|
|
|
19
|
-
### ✨ Recommended: Use directly (no installation needed)
|
|
20
23
|
```bash
|
|
21
|
-
npx build-app-with
|
|
24
|
+
npx build-app-with --help # Show usage info
|
|
25
|
+
npx build-app-with --version # Show version
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
##
|
|
28
|
+
## Supported Frameworks
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
| Framework | Type | Description |
|
|
31
|
+
|-----------|------|-------------|
|
|
32
|
+
| **Next.js** | Full-stack | SSR, API routes, App Router. TypeScript enforced. |
|
|
33
|
+
| **Vite + React** | Frontend | Lightning-fast HMR with optional TypeScript. |
|
|
34
|
+
| **Rsbuild + React** | Frontend | Rust-powered bundler (Rspack). TypeScript enforced. |
|
|
35
|
+
| **Express.js** | Backend | Traditional Node.js web server with extensive middleware ecosystem. |
|
|
36
|
+
| **Fastify** | Backend | High-performance Node.js API framework with plugin architecture. |
|
|
29
37
|
|
|
30
|
-
##
|
|
38
|
+
## Preset System
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
- **CSS Frameworks**: Tailwind CSS, Bootstrap, Material-UI, Chakra UI, shadcn/ui
|
|
34
|
-
- **Components**: Pre-built component libraries and design systems
|
|
40
|
+
When you choose **Vite + React** or **Rsbuild + React**, you pick a preset that determines default features:
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
| Preset | What's Included |
|
|
43
|
+
|--------|----------------|
|
|
44
|
+
| **Starter** | React + CSS only. Minimal starting point. |
|
|
45
|
+
| **Standard** (default) | React Router DOM, Zustand, React Hook Form, React Icons, React Toastify |
|
|
46
|
+
| **Full** | Everything in Standard + Framer Motion, React Table, React Helmet, react-i18next |
|
|
47
|
+
| **Custom** | Choose every feature manually |
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
- **SQL**: PostgreSQL, MySQL, SQLite
|
|
42
|
-
- **NoSQL**: MongoDB
|
|
43
|
-
- **ORMs**: Prisma, Mongoose, Sequelize
|
|
49
|
+
Standard and Full presets let you add more features on top of the defaults. Next.js and backend frameworks use a Default / Customize toggle instead.
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
- **TypeScript**: Full type safety and IntelliSense
|
|
47
|
-
- **Testing**: Jest test framework with example tests
|
|
48
|
-
- **Code Quality**: ESLint, Prettier, pre-commit hooks
|
|
49
|
-
- **Docker**: Production-ready containerization
|
|
51
|
+
## Features Reference
|
|
50
52
|
|
|
51
|
-
###
|
|
52
|
-
- **Security**: CORS, Helmet, rate limiting, input validation
|
|
53
|
-
- **Logging**: Structured logging with Winston
|
|
54
|
-
- **API Docs**: Automatic Swagger/OpenAPI documentation
|
|
55
|
-
- **Performance**: Optimized builds and caching
|
|
53
|
+
### CSS Frameworks (Frontend)
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
Tailwind CSS, Bootstrap, Material-UI (MUI), Chakra UI, shadcn/ui, Mantine, Styled Components, or Vanilla CSS.
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
Available for Vite, Rsbuild (all non-Starter presets), and Next.js (Customize mode).
|
|
58
|
+
|
|
59
|
+
### Authentication
|
|
60
|
+
|
|
61
|
+
**Frontend (Next.js, Vite, Rsbuild):**
|
|
62
|
+
Clerk, NextAuth.js (Next.js only), Auth0, Firebase Auth
|
|
63
|
+
|
|
64
|
+
**Backend (Express, Fastify):**
|
|
65
|
+
JWT, Session-based, OAuth (Passport.js)
|
|
66
|
+
|
|
67
|
+
### Project Structure
|
|
68
|
+
|
|
69
|
+
**React frontends (Vite, Rsbuild):**
|
|
70
|
+
|
|
71
|
+
| Structure | Description |
|
|
72
|
+
|-----------|-------------|
|
|
73
|
+
| Simple | Flat `src/` with basic components — good for prototypes |
|
|
74
|
+
| Feature-based | Organized by feature (`features/auth/`, `features/dashboard/`) with shared hooks, services, and utils |
|
|
75
|
+
| Domain-driven | Enterprise pattern with `domains/`, `shared/`, `app/`, and `infrastructure/` layers |
|
|
76
|
+
|
|
77
|
+
**Express:**
|
|
78
|
+
Simple, Modular (by feature), or Layered (Controller-Service-Repository)
|
|
79
|
+
|
|
80
|
+
**Fastify:**
|
|
81
|
+
Simple, Modular (by feature), or Plugin-based (Fastify plugins)
|
|
82
|
+
|
|
83
|
+
### React Feature Categories
|
|
84
|
+
|
|
85
|
+
These 7 categories are available for Vite + React and Rsbuild + React projects (Custom preset, or as additions to Standard/Full):
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
| Category | Options |
|
|
88
|
+
|----------|---------|
|
|
89
|
+
| **Routing** | React Router DOM |
|
|
90
|
+
| **State Management** | Redux Toolkit, Zustand, MobX, Recoil |
|
|
91
|
+
| **Forms** | React Hook Form, Formik |
|
|
92
|
+
| **Animations** | Framer Motion, React Spring, React Transition Group |
|
|
93
|
+
| **Drag & Drop** | React DnD, React Beautiful DnD |
|
|
94
|
+
| **UI Libraries** | Ant Design, Bootstrap, MUI, Chakra UI, shadcn/ui, Mantine |
|
|
95
|
+
| **Utilities** | React Icons, React Toastify, React Table, React Select, React Helmet, react-i18next, Storybook |
|
|
96
|
+
|
|
97
|
+
### Backend Features (Express / Fastify)
|
|
98
|
+
|
|
99
|
+
| Category | Options |
|
|
100
|
+
|----------|---------|
|
|
101
|
+
| **Databases** | MongoDB, PostgreSQL, MySQL, SQLite |
|
|
102
|
+
| **ORMs** | Mongoose (MongoDB), Prisma (PostgreSQL/SQLite), Sequelize (MySQL) |
|
|
103
|
+
| **Security** | CORS, Helmet (Express), Rate limiting (Fastify) |
|
|
104
|
+
| **Logging** | Morgan (request logging), Winston (structured logging) |
|
|
105
|
+
| **Validation** | Express Validator / Fastify validation |
|
|
106
|
+
| **API Docs** | Swagger / OpenAPI |
|
|
107
|
+
| **Environment** | dotenv (.env support) |
|
|
108
|
+
| **Testing** | Jest test setup with example tests |
|
|
109
|
+
| **Docker** | Dockerfile and docker-compose configuration |
|
|
110
|
+
|
|
111
|
+
Fastify also supports **WebSocket** and **GraphQL** as additional feature choices.
|
|
112
|
+
|
|
113
|
+
## Interactive Flow
|
|
114
|
+
|
|
115
|
+
Running `npx build-app-with` walks you through these steps:
|
|
65
116
|
|
|
66
|
-
# Create a Vite React app with all the bells and whistles
|
|
67
|
-
npx build-app-with my-react-app
|
|
68
117
|
```
|
|
118
|
+
? Choose your app framework:
|
|
119
|
+
> Next.js (React Full-stack)
|
|
120
|
+
Vite + React (Frontend)
|
|
121
|
+
Rsbuild + React (Frontend)
|
|
122
|
+
Express.js (Node.js Backend)
|
|
123
|
+
Fastify (Node.js Backend)
|
|
124
|
+
|
|
125
|
+
? Choose your project setup: # React frontends only
|
|
126
|
+
> Starter / Standard / Full / Custom
|
|
127
|
+
|
|
128
|
+
? What is your project name? # Skipped if provided via CLI
|
|
129
|
+
> my-app
|
|
69
130
|
|
|
70
|
-
|
|
131
|
+
? Use TypeScript? # Vite Custom preset only
|
|
132
|
+
> Yes / No
|
|
71
133
|
|
|
72
|
-
|
|
134
|
+
? Choose a CSS framework: # Frontend frameworks
|
|
135
|
+
> Tailwind CSS / Bootstrap / MUI / ...
|
|
136
|
+
|
|
137
|
+
? Choose authentication strategy: # Customize mode
|
|
138
|
+
> Clerk / NextAuth.js / Auth0 / ...
|
|
139
|
+
|
|
140
|
+
? Select Routing features: # React feature categories
|
|
141
|
+
? Select State Management features:
|
|
142
|
+
? Select Forms features:
|
|
143
|
+
...
|
|
144
|
+
|
|
145
|
+
? After setup, do you want to:
|
|
146
|
+
[ ] Initialize a Git repository
|
|
147
|
+
[ ] Install dependencies
|
|
148
|
+
[ ] Run the development server
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## After Project Creation
|
|
73
152
|
|
|
74
153
|
```bash
|
|
75
|
-
cd
|
|
76
|
-
npm install
|
|
77
|
-
npm run dev
|
|
154
|
+
cd my-app
|
|
155
|
+
npm install # If not done during setup
|
|
156
|
+
npm run dev # Start development server
|
|
78
157
|
```
|
|
79
158
|
|
|
80
|
-
**
|
|
81
|
-
**For Express/Fastify**: API runs on http://localhost:3000
|
|
159
|
+
**Default ports:**
|
|
82
160
|
|
|
83
|
-
|
|
161
|
+
| Framework | Port | URL |
|
|
162
|
+
|-----------|------|-----|
|
|
163
|
+
| Next.js | 3000 | http://localhost:3000 |
|
|
164
|
+
| Vite + React | 5173 | http://localhost:5173 |
|
|
165
|
+
| Rsbuild + React | 3000 | http://localhost:3000 |
|
|
166
|
+
| Express.js | 3000 | http://localhost:3000 |
|
|
167
|
+
| Fastify | 3000 | http://localhost:3000 |
|
|
84
168
|
|
|
85
|
-
|
|
169
|
+
## Project Structure Example
|
|
86
170
|
|
|
87
|
-
|
|
171
|
+
A typical **Vite + React** project with the **feature-based** structure:
|
|
88
172
|
|
|
89
|
-
|
|
173
|
+
```
|
|
174
|
+
my-app/
|
|
175
|
+
├── public/
|
|
176
|
+
├── src/
|
|
177
|
+
│ ├── main.tsx
|
|
178
|
+
│ ├── App.tsx
|
|
179
|
+
│ ├── index.css
|
|
180
|
+
│ ├── components/
|
|
181
|
+
│ │ └── common/
|
|
182
|
+
│ │ └── Header.tsx
|
|
183
|
+
│ ├── features/
|
|
184
|
+
│ │ ├── auth/
|
|
185
|
+
│ │ │ └── Login.tsx
|
|
186
|
+
│ │ └── dashboard/
|
|
187
|
+
│ │ └── Dashboard.tsx
|
|
188
|
+
│ ├── hooks/
|
|
189
|
+
│ │ └── useLocalStorage.ts
|
|
190
|
+
│ ├── services/
|
|
191
|
+
│ │ └── api.ts
|
|
192
|
+
│ ├── utils/
|
|
193
|
+
│ │ └── helpers.ts
|
|
194
|
+
│ └── types/
|
|
195
|
+
│ └── index.ts
|
|
196
|
+
├── .env
|
|
197
|
+
├── .gitignore
|
|
198
|
+
├── package.json
|
|
199
|
+
├── vite.config.ts
|
|
200
|
+
└── tsconfig.json
|
|
201
|
+
```
|
|
90
202
|
|
|
91
|
-
|
|
203
|
+
## Why Build App With?
|
|
92
204
|
|
|
93
|
-
**
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
- 🔄 **Up-to-date** - Always uses the latest stable versions
|
|
205
|
+
- **Fast** — Go from zero to a running project in seconds
|
|
206
|
+
- **Production-ready** — Security best practices, proper project structure, and sensible defaults built in
|
|
207
|
+
- **Flexible** — 5 frameworks, 4 presets, 30+ configurable features — pick exactly what you need
|
|
208
|
+
- **Secure** — Path traversal prevention, command injection guards, and secret generation for backend projects
|
|
209
|
+
- **Current** — Uses latest stable versions of all dependencies
|
|
99
210
|
|
|
211
|
+
## Contributing
|
|
100
212
|
|
|
101
|
-
|
|
213
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
102
214
|
|
|
103
|
-
|
|
104
|
-
- 💬 Discussions: [GitHub Discussions](https://github.com/imnayakshubham/build-app-with/discussions)
|
|
215
|
+
## License
|
|
105
216
|
|
|
106
|
-
|
|
217
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
107
218
|
|
|
219
|
+
## Support
|
|
108
220
|
|
|
109
|
-
|
|
221
|
+
- Issues: [GitHub Issues](https://github.com/imnayakshubham/build-app-with/issues)
|
|
222
|
+
- Discussions: [GitHub Discussions](https://github.com/imnayakshubham/build-app-with/discussions)
|
package/dist/build-app-with.mjs
CHANGED
|
@@ -205,7 +205,8 @@ const vite_features_DEPENDENCY_VERSIONS = {
|
|
|
205
205
|
'react-i18next': '^14.0.0',
|
|
206
206
|
'i18next': '^23.7.11',
|
|
207
207
|
'@storybook/react': '^7.6.3',
|
|
208
|
-
'@storybook/react-vite': '^7.6.3'
|
|
208
|
+
'@storybook/react-vite': '^7.6.3',
|
|
209
|
+
'storybook-builder-rsbuild': '^0.1.1'
|
|
209
210
|
};
|
|
210
211
|
|
|
211
212
|
// Feature to Package Mapping
|
|
@@ -330,7 +331,7 @@ function applyPresetFeatures(answers, preset) {
|
|
|
330
331
|
* @returns {boolean} True if feature prompts should be shown
|
|
331
332
|
*/
|
|
332
333
|
function shouldShowFeaturePrompts(answers) {
|
|
333
|
-
return answers.framework === 'vite-react' &&
|
|
334
|
+
return (answers.framework === 'vite-react' || answers.framework === 'rsbuild-react') &&
|
|
334
335
|
answers.vitePreset &&
|
|
335
336
|
answers.vitePreset !== VITE_PRESETS.STARTER;
|
|
336
337
|
}
|
|
@@ -369,6 +370,8 @@ function getProperty(answers, property, defaultValue = '') {
|
|
|
369
370
|
|
|
370
371
|
|
|
371
372
|
|
|
373
|
+
const isReactFrontend = (fw) => fw === 'vite-react' || fw === 'rsbuild-react';
|
|
374
|
+
|
|
372
375
|
function getFeaturePrompts(currentAnswers) {
|
|
373
376
|
return [
|
|
374
377
|
{
|
|
@@ -381,7 +384,7 @@ function getFeaturePrompts(currentAnswers) {
|
|
|
381
384
|
when: (default_answers) => {
|
|
382
385
|
const answers = default_answers ?? currentAnswers
|
|
383
386
|
return (answers.setupType === 'customize' || answers.allowCustomFeatures) &&
|
|
384
|
-
answers.framework
|
|
387
|
+
isReactFrontend(answers.framework)
|
|
385
388
|
}
|
|
386
389
|
},
|
|
387
390
|
{
|
|
@@ -497,6 +500,7 @@ function getInitialPrompts(cliProjectName = null) {
|
|
|
497
500
|
choices: [
|
|
498
501
|
{ name: 'Next.js (React Full-stack)', value: 'nextjs' },
|
|
499
502
|
{ name: 'Vite + React (Frontend)', value: 'vite-react' },
|
|
503
|
+
{ name: 'Rsbuild + React (Frontend)', value: 'rsbuild-react' },
|
|
500
504
|
{ name: 'Express.js (Node.js Backend)', value: 'express' },
|
|
501
505
|
{ name: 'Fastify (Node.js Backend)', value: 'fastify' }
|
|
502
506
|
]
|
|
@@ -513,7 +517,7 @@ function getInitialPrompts(cliProjectName = null) {
|
|
|
513
517
|
{ name: '⚙️ Custom - Advanced (choose features manually)', value: 'custom' }
|
|
514
518
|
],
|
|
515
519
|
default: 1, // Standard is default
|
|
516
|
-
when: (answers) => answers.framework
|
|
520
|
+
when: (answers) => isReactFrontend(answers.framework)
|
|
517
521
|
},
|
|
518
522
|
// 2b. Setup type for other frameworks
|
|
519
523
|
{
|
|
@@ -524,7 +528,7 @@ function getInitialPrompts(cliProjectName = null) {
|
|
|
524
528
|
{ name: 'Default (Quick start with recommended settings)', value: 'default' },
|
|
525
529
|
{ name: 'Customize (Advanced: choose your own settings)', value: 'customize' }
|
|
526
530
|
],
|
|
527
|
-
when: (answers) => answers.framework
|
|
531
|
+
when: (answers) => !isReactFrontend(answers.framework)
|
|
528
532
|
}
|
|
529
533
|
];
|
|
530
534
|
|
|
@@ -584,8 +588,8 @@ function getCustomizationPrompts(initialAnswers) {
|
|
|
584
588
|
],
|
|
585
589
|
when: (answers) => {
|
|
586
590
|
const merged = { ...initialAnswers, ...answers };
|
|
587
|
-
// For Vite: show for non-Starter presets
|
|
588
|
-
if (merged.framework
|
|
591
|
+
// For React frontends (Vite/Rsbuild): show for non-Starter presets
|
|
592
|
+
if (isReactFrontend(merged.framework)) {
|
|
589
593
|
return merged.vitePreset !== 'starter';
|
|
590
594
|
}
|
|
591
595
|
// Only show CSS framework for frontend frameworks
|
|
@@ -610,7 +614,7 @@ function getCustomizationPrompts(initialAnswers) {
|
|
|
610
614
|
default: 'clerk',
|
|
611
615
|
when: (answers) => {
|
|
612
616
|
const merged = { ...initialAnswers, ...answers };
|
|
613
|
-
return merged.setupType === 'customize' && ['nextjs', 'vite-react'].includes(merged.framework);
|
|
617
|
+
return merged.setupType === 'customize' && ['nextjs', 'vite-react', 'rsbuild-react'].includes(merged.framework);
|
|
614
618
|
}
|
|
615
619
|
},
|
|
616
620
|
{
|
|
@@ -624,7 +628,7 @@ function getCustomizationPrompts(initialAnswers) {
|
|
|
624
628
|
],
|
|
625
629
|
when: (answers) => {
|
|
626
630
|
const merged = { ...initialAnswers, ...answers };
|
|
627
|
-
return merged.setupType === 'customize' && merged.framework
|
|
631
|
+
return merged.setupType === 'customize' && isReactFrontend(merged.framework);
|
|
628
632
|
}
|
|
629
633
|
},
|
|
630
634
|
// ESLint and Prettier prompts hidden per user request
|
|
@@ -652,14 +656,14 @@ function getCustomizationPrompts(initialAnswers) {
|
|
|
652
656
|
}
|
|
653
657
|
];
|
|
654
658
|
|
|
655
|
-
// Add feature prompts for Vite +
|
|
659
|
+
// Add feature prompts for React frontend users (Vite + Rsbuild)
|
|
656
660
|
const featurePrompts = getFeaturePrompts();
|
|
657
661
|
featurePrompts.forEach(prompt => {
|
|
658
662
|
const originalWhen = prompt.when;
|
|
659
663
|
prompt.when = (answers) => {
|
|
660
664
|
const merged = { ...initialAnswers, ...answers };
|
|
661
|
-
// Show for Vite
|
|
662
|
-
if (merged.framework
|
|
665
|
+
// Show for React frontend frameworks (Vite / Rsbuild)
|
|
666
|
+
if (isReactFrontend(merged.framework)) {
|
|
663
667
|
// For Custom preset: show all feature prompts
|
|
664
668
|
if (merged.vitePreset === 'custom') {
|
|
665
669
|
return originalWhen ? originalWhen(merged) : true;
|
|
@@ -701,8 +705,8 @@ function getPrompts(cliProjectName = null) {
|
|
|
701
705
|
{ name: 'Vanilla CSS', value: 'vanilla' }
|
|
702
706
|
],
|
|
703
707
|
when: (answers) => {
|
|
704
|
-
// For Vite: show for non-Starter presets
|
|
705
|
-
if (answers.framework
|
|
708
|
+
// For React frontends (Vite/Rsbuild): show for non-Starter presets
|
|
709
|
+
if (isReactFrontend(answers.framework)) {
|
|
706
710
|
return answers.vitePreset !== 'starter';
|
|
707
711
|
}
|
|
708
712
|
// Only show CSS framework for frontend frameworks
|
|
@@ -725,7 +729,7 @@ function getPrompts(cliProjectName = null) {
|
|
|
725
729
|
{ name: 'None', value: 'none' }
|
|
726
730
|
],
|
|
727
731
|
default: 'clerk',
|
|
728
|
-
when: (answers) => answers.setupType === 'customize' && ['nextjs', 'vite-react'].includes(answers.framework)
|
|
732
|
+
when: (answers) => answers.setupType === 'customize' && ['nextjs', 'vite-react', 'rsbuild-react'].includes(answers.framework)
|
|
729
733
|
},
|
|
730
734
|
{
|
|
731
735
|
type: 'list',
|
|
@@ -736,7 +740,7 @@ function getPrompts(cliProjectName = null) {
|
|
|
736
740
|
{ name: 'Simple (Basic structure)', value: 'simple' },
|
|
737
741
|
{ name: 'Domain-driven (Advanced architecture)', value: 'domain-driven' }
|
|
738
742
|
],
|
|
739
|
-
when: (answers) => answers.setupType === 'customize' && answers.framework
|
|
743
|
+
when: (answers) => answers.setupType === 'customize' && isReactFrontend(answers.framework)
|
|
740
744
|
},
|
|
741
745
|
// ESLint and Prettier prompts hidden per user request
|
|
742
746
|
{
|
|
@@ -768,7 +772,7 @@ function getPrompts(cliProjectName = null) {
|
|
|
768
772
|
featurePrompts.forEach(prompt => {
|
|
769
773
|
const originalWhen = prompt.when;
|
|
770
774
|
prompt.when = (answers) => {
|
|
771
|
-
if (answers.framework
|
|
775
|
+
if (isReactFrontend(answers.framework)) {
|
|
772
776
|
if (answers.vitePreset === 'custom') {
|
|
773
777
|
return originalWhen ? originalWhen(answers) : true;
|
|
774
778
|
}
|
|
@@ -793,9 +797,13 @@ function finalizeAnswers(answers) {
|
|
|
793
797
|
if (answers.framework === 'vite-react' && answers.typescript === undefined) {
|
|
794
798
|
answers.typescript = true;
|
|
795
799
|
}
|
|
800
|
+
// Rsbuild always uses TypeScript
|
|
801
|
+
if (answers.framework === 'rsbuild-react') {
|
|
802
|
+
answers.typescript = true;
|
|
803
|
+
}
|
|
796
804
|
|
|
797
|
-
// Apply Vite
|
|
798
|
-
if (answers.framework
|
|
805
|
+
// Apply preset features for React frontends (Vite / Rsbuild)
|
|
806
|
+
if (isReactFrontend(answers.framework) && answers.vitePreset) {
|
|
799
807
|
// Set setupType for compatibility with existing code
|
|
800
808
|
answers.setupType = presetToSetupType(answers.vitePreset);
|
|
801
809
|
|
|
@@ -975,7 +983,7 @@ function generatePackageJson(answers) {
|
|
|
975
983
|
}
|
|
976
984
|
|
|
977
985
|
// Automatically add TanStack Query and Axios for React-based apps
|
|
978
|
-
if (answers.framework === 'vite-react' || answers.framework === 'nextjs') {
|
|
986
|
+
if (answers.framework === 'vite-react' || answers.framework === 'rsbuild-react' || answers.framework === 'nextjs') {
|
|
979
987
|
packageJson.dependencies['@tanstack/react-query'] = '^5.14.2';
|
|
980
988
|
packageJson.dependencies['axios'] = '^1.6.2';
|
|
981
989
|
packageJson.devDependencies['@tanstack/react-query-devtools'] = '^5.14.2';
|
|
@@ -1170,14 +1178,14 @@ function getOverlayDependencies(answers) {
|
|
|
1170
1178
|
'react-select': ['react-select'],
|
|
1171
1179
|
'react-helmet': ['react-helmet-async'],
|
|
1172
1180
|
'react-i18next': ['react-i18next', 'i18next'],
|
|
1173
|
-
'storybook': ['@storybook/react', '@storybook/react-vite']
|
|
1181
|
+
'storybook': ['@storybook/react', answers.framework === 'rsbuild-react' ? 'storybook-builder-rsbuild' : '@storybook/react-vite']
|
|
1174
1182
|
};
|
|
1175
1183
|
answers.utilities.forEach(util => {
|
|
1176
1184
|
const pkgs = utilPkgMap[util] || [];
|
|
1177
1185
|
pkgs.forEach(pkg => {
|
|
1178
1186
|
const version = vite_features_DEPENDENCY_VERSIONS[pkg] || 'latest';
|
|
1179
1187
|
// Storybook packages are dev dependencies
|
|
1180
|
-
if (pkg.startsWith('@storybook/')) {
|
|
1188
|
+
if (pkg.startsWith('@storybook/') || pkg === 'storybook-builder-rsbuild') {
|
|
1181
1189
|
devDependencies[pkg] = version;
|
|
1182
1190
|
} else {
|
|
1183
1191
|
dependencies[pkg] = version;
|
|
@@ -4057,7 +4065,9 @@ const ALLOWED_COMMANDS = {
|
|
|
4057
4065
|
'create-next-app@latest',
|
|
4058
4066
|
'create-next-app',
|
|
4059
4067
|
'create-vite@latest',
|
|
4060
|
-
'create-vite'
|
|
4068
|
+
'create-vite',
|
|
4069
|
+
'create-rsbuild@latest',
|
|
4070
|
+
'create-rsbuild'
|
|
4061
4071
|
],
|
|
4062
4072
|
allowedFlags: [
|
|
4063
4073
|
'--version', '--help',
|
|
@@ -4151,6 +4161,11 @@ function validateCommandArgs(command, args) {
|
|
|
4151
4161
|
if (npxCommand.startsWith('create-vite')) {
|
|
4152
4162
|
return validateCreateViteArgs(args.slice(1), config);
|
|
4153
4163
|
}
|
|
4164
|
+
|
|
4165
|
+
// For create-rsbuild, validate remaining arguments
|
|
4166
|
+
if (npxCommand.startsWith('create-rsbuild')) {
|
|
4167
|
+
return validateCreateRsbuildArgs(args.slice(1), config);
|
|
4168
|
+
}
|
|
4154
4169
|
}
|
|
4155
4170
|
|
|
4156
4171
|
for (const arg of args) {
|
|
@@ -4272,6 +4287,66 @@ function validateCreateViteArgs(args, config) {
|
|
|
4272
4287
|
return true;
|
|
4273
4288
|
}
|
|
4274
4289
|
|
|
4290
|
+
/**
|
|
4291
|
+
* Validate create-rsbuild specific arguments
|
|
4292
|
+
* @param {string[]} args - Arguments after create-rsbuild command
|
|
4293
|
+
* @param {Object} config - NPX command configuration
|
|
4294
|
+
* @returns {boolean} True if arguments are valid
|
|
4295
|
+
*/
|
|
4296
|
+
function validateCreateRsbuildArgs(args, config) {
|
|
4297
|
+
const VALID_TEMPLATES = ['react', 'react-ts'];
|
|
4298
|
+
|
|
4299
|
+
for (let i = 0; i < args.length; i++) {
|
|
4300
|
+
const arg = args[i];
|
|
4301
|
+
|
|
4302
|
+
// Check for shell metacharacters
|
|
4303
|
+
if (/[;&|`$(){}[\]<>\\]/.test(arg)) {
|
|
4304
|
+
logger.error(`Argument contains shell metacharacters: ${arg}`);
|
|
4305
|
+
return false;
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
if (arg.startsWith('-')) {
|
|
4309
|
+
// It's a flag
|
|
4310
|
+
if (!config.allowedFlags.includes(arg)) {
|
|
4311
|
+
logger.error(`Flag not in allowlist: ${arg}`);
|
|
4312
|
+
return false;
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
// Handle --template flag with value
|
|
4316
|
+
if (arg === '--template' && i + 1 < args.length) {
|
|
4317
|
+
const template = args[i + 1];
|
|
4318
|
+
if (!VALID_TEMPLATES.includes(template)) {
|
|
4319
|
+
logger.error(`Invalid Rsbuild template: ${template}`);
|
|
4320
|
+
return false;
|
|
4321
|
+
}
|
|
4322
|
+
i++; // Skip the next argument as it's the value for this flag
|
|
4323
|
+
}
|
|
4324
|
+
} else {
|
|
4325
|
+
// It's likely a project name - validate it
|
|
4326
|
+
if (!validatePackageName(arg) && !/^[a-zA-Z0-9-_]+$/.test(arg)) {
|
|
4327
|
+
logger.error(`Invalid project name: ${arg}`);
|
|
4328
|
+
return false;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
return true;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
/**
|
|
4337
|
+
* Validate and sanitize Rsbuild project creation arguments
|
|
4338
|
+
* @param {string} projectName - Project name
|
|
4339
|
+
* @param {Object} options - Rsbuild options
|
|
4340
|
+
* @returns {string[]} Sanitized arguments array
|
|
4341
|
+
*/
|
|
4342
|
+
function sanitizeRsbuildArgs(projectName, options = {}) {
|
|
4343
|
+
const sanitizedName = sanitizeProjectName(projectName);
|
|
4344
|
+
const args = ['create-rsbuild@latest', sanitizedName];
|
|
4345
|
+
const template = options.typescript ? 'react-ts' : 'react';
|
|
4346
|
+
args.push('--template', template);
|
|
4347
|
+
return args;
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4275
4350
|
/**
|
|
4276
4351
|
* Validate and sanitize Vite project creation arguments
|
|
4277
4352
|
* @param {string} projectName - Project name
|
|
@@ -4734,6 +4809,347 @@ async function generateCreditsComponent(projectPath, answers) {
|
|
|
4734
4809
|
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'Credits.jsx'), creditsComponent);
|
|
4735
4810
|
}
|
|
4736
4811
|
|
|
4812
|
+
;// ./src/generators/rsbuild/rsbuild-project-generator.js
|
|
4813
|
+
|
|
4814
|
+
|
|
4815
|
+
|
|
4816
|
+
|
|
4817
|
+
|
|
4818
|
+
|
|
4819
|
+
|
|
4820
|
+
|
|
4821
|
+
|
|
4822
|
+
|
|
4823
|
+
|
|
4824
|
+
/**
|
|
4825
|
+
* Create the base Rsbuild project using create-rsbuild CLI
|
|
4826
|
+
*/
|
|
4827
|
+
async function createRsbuildBase(projectPath, answers) {
|
|
4828
|
+
logger.debug('Creating base Rsbuild project...');
|
|
4829
|
+
|
|
4830
|
+
try {
|
|
4831
|
+
const sanitizedArgs = sanitizeRsbuildArgs(answers.projectName, {
|
|
4832
|
+
typescript: true // Rsbuild always uses TypeScript
|
|
4833
|
+
});
|
|
4834
|
+
|
|
4835
|
+
await secureExec('npx', sanitizedArgs, {
|
|
4836
|
+
stdio: 'pipe',
|
|
4837
|
+
cwd: process.cwd(),
|
|
4838
|
+
timeout: 300000 // 5 minutes
|
|
4839
|
+
});
|
|
4840
|
+
|
|
4841
|
+
logger.debug('Base Rsbuild project created');
|
|
4842
|
+
} catch (error) {
|
|
4843
|
+
throw new Error(`Failed to create Rsbuild project: ${error.message}`);
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
/**
|
|
4848
|
+
* Overlay customizations onto a scaffolded Rsbuild project.
|
|
4849
|
+
* Assumes create-rsbuild has already run and created the base project.
|
|
4850
|
+
* Reuses Vite file generators since the React code is build-tool agnostic.
|
|
4851
|
+
*/
|
|
4852
|
+
async function generateRsbuildProject(projectPath, answers) {
|
|
4853
|
+
// Merge overlay dependencies into the scaffold's package.json
|
|
4854
|
+
await rsbuild_project_generator_mergeOverlayDependencies(projectPath, answers);
|
|
4855
|
+
|
|
4856
|
+
// Generate rsbuild.config.ts (always TypeScript)
|
|
4857
|
+
await generateRsbuildConfig(projectPath, answers);
|
|
4858
|
+
|
|
4859
|
+
// Generate Tailwind config if needed
|
|
4860
|
+
if (answers.cssFramework === 'tailwind') {
|
|
4861
|
+
const tailwindConfig = generateTailwindConfig(answers);
|
|
4862
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(projectPath, 'tailwind.config.js'), tailwindConfig);
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
// Generate ESLint config if requested
|
|
4866
|
+
if (answers.eslint) {
|
|
4867
|
+
const eslintConfig = generateESLintConfig(answers);
|
|
4868
|
+
await external_fs_extra_["default"].writeJSON(external_path_namespaceObject.join(projectPath, '.eslintrc.json'), eslintConfig, { spaces: 2 });
|
|
4869
|
+
}
|
|
4870
|
+
|
|
4871
|
+
// Generate Prettier config if requested
|
|
4872
|
+
if (answers.prettier) {
|
|
4873
|
+
const prettierConfig = generatePrettierConfig();
|
|
4874
|
+
await external_fs_extra_["default"].writeJSON(external_path_namespaceObject.join(projectPath, '.prettierrc'), prettierConfig, { spaces: 2 });
|
|
4875
|
+
}
|
|
4876
|
+
|
|
4877
|
+
// Remove default create-rsbuild assets we'll replace
|
|
4878
|
+
await rsbuild_project_generator_removeDefaultAssets(projectPath);
|
|
4879
|
+
|
|
4880
|
+
// Generate project files (overlay mode - reuses Vite file generators)
|
|
4881
|
+
await generateProjectFiles(projectPath, answers, { overlay: true });
|
|
4882
|
+
|
|
4883
|
+
// Generate Clerk authentication if selected
|
|
4884
|
+
if (answers.authStrategy === 'clerk') {
|
|
4885
|
+
await rsbuild_project_generator_generateClerkAuth(projectPath, answers);
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
// Generate .env.example (Rsbuild uses PUBLIC_ prefix)
|
|
4889
|
+
await rsbuild_project_generator_generateEnvExample(projectPath, answers);
|
|
4890
|
+
|
|
4891
|
+
// Generate README (Rsbuild-specific)
|
|
4892
|
+
await rsbuild_project_generator_generateReadme(projectPath, answers);
|
|
4893
|
+
|
|
4894
|
+
// Generate credits component
|
|
4895
|
+
await rsbuild_project_generator_generateCreditsComponent(projectPath, answers);
|
|
4896
|
+
}
|
|
4897
|
+
|
|
4898
|
+
/**
|
|
4899
|
+
* Read the scaffold's package.json and merge in overlay dependencies
|
|
4900
|
+
*/
|
|
4901
|
+
async function rsbuild_project_generator_mergeOverlayDependencies(projectPath, answers) {
|
|
4902
|
+
const pkgPath = external_path_namespaceObject.join(projectPath, 'package.json');
|
|
4903
|
+
const existingPkg = await external_fs_extra_["default"].readJSON(pkgPath);
|
|
4904
|
+
const overlay = getOverlayDependencies(answers);
|
|
4905
|
+
|
|
4906
|
+
existingPkg.dependencies = {
|
|
4907
|
+
...(existingPkg.dependencies || {}),
|
|
4908
|
+
...overlay.dependencies
|
|
4909
|
+
};
|
|
4910
|
+
|
|
4911
|
+
existingPkg.devDependencies = {
|
|
4912
|
+
...(existingPkg.devDependencies || {}),
|
|
4913
|
+
...overlay.devDependencies
|
|
4914
|
+
};
|
|
4915
|
+
|
|
4916
|
+
await external_fs_extra_["default"].writeJSON(pkgPath, existingPkg, { spaces: 2 });
|
|
4917
|
+
}
|
|
4918
|
+
|
|
4919
|
+
/**
|
|
4920
|
+
* Generate rsbuild.config.ts with project settings
|
|
4921
|
+
*/
|
|
4922
|
+
async function generateRsbuildConfig(projectPath, answers) {
|
|
4923
|
+
const config = `import { defineConfig } from '@rsbuild/core';
|
|
4924
|
+
import { pluginReact } from '@rsbuild/plugin-react';
|
|
4925
|
+
|
|
4926
|
+
export default defineConfig({
|
|
4927
|
+
plugins: [pluginReact()],
|
|
4928
|
+
source: {
|
|
4929
|
+
entry: {
|
|
4930
|
+
index: './src/main.tsx'
|
|
4931
|
+
}
|
|
4932
|
+
},
|
|
4933
|
+
server: {
|
|
4934
|
+
port: 3000,
|
|
4935
|
+
open: true
|
|
4936
|
+
},
|
|
4937
|
+
output: {
|
|
4938
|
+
sourceMap: true
|
|
4939
|
+
}
|
|
4940
|
+
});
|
|
4941
|
+
`;
|
|
4942
|
+
|
|
4943
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(projectPath, 'rsbuild.config.ts'), config);
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
/**
|
|
4947
|
+
* Remove default create-rsbuild assets that we'll replace with our own
|
|
4948
|
+
*/
|
|
4949
|
+
async function rsbuild_project_generator_removeDefaultAssets(projectPath) {
|
|
4950
|
+
const filesToRemove = [
|
|
4951
|
+
external_path_namespaceObject.join(projectPath, 'src', 'index.tsx'),
|
|
4952
|
+
external_path_namespaceObject.join(projectPath, 'src', 'App.css'),
|
|
4953
|
+
external_path_namespaceObject.join(projectPath, 'src', 'App.tsx')
|
|
4954
|
+
];
|
|
4955
|
+
|
|
4956
|
+
for (const filePath of filesToRemove) {
|
|
4957
|
+
if (await external_fs_extra_["default"].pathExists(filePath)) {
|
|
4958
|
+
await external_fs_extra_["default"].remove(filePath);
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4963
|
+
async function rsbuild_project_generator_generateReadme(projectPath, answers) {
|
|
4964
|
+
const selectedFeatures = Array.isArray(answers.features) ? answers.features : [];
|
|
4965
|
+
const allFeatures = [...selectedFeatures];
|
|
4966
|
+
if (answers.authStrategy && answers.authStrategy !== 'none') {
|
|
4967
|
+
allFeatures.push(answers.authStrategy);
|
|
4968
|
+
}
|
|
4969
|
+
if (answers.typescript) {
|
|
4970
|
+
allFeatures.push('typescript');
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4973
|
+
const credits = generateCreditsSection('rsbuild-react', allFeatures);
|
|
4974
|
+
|
|
4975
|
+
const readme = `# ${answers.projectName}
|
|
4976
|
+
|
|
4977
|
+
A React application built with Rsbuild and ${answers.cssFramework === 'vanilla' ? 'vanilla CSS' : answers.cssFramework}.
|
|
4978
|
+
|
|
4979
|
+
## Features
|
|
4980
|
+
|
|
4981
|
+
- \u26A1\uFE0F Rsbuild (Rspack) for fast development and building
|
|
4982
|
+
- \u269B\uFE0F React + TypeScript
|
|
4983
|
+
- \uD83C\uDFA8 ${answers.cssFramework === 'vanilla' ? 'Vanilla CSS' : answers.cssFramework}
|
|
4984
|
+
${answers.authStrategy && answers.authStrategy !== 'none' ? `- \uD83D\uDD10 ${answers.authStrategy === 'clerk' ? 'Clerk Authentication' : answers.authStrategy}` : ''}
|
|
4985
|
+
${selectedFeatures.length > 0 ? `- \uD83D\uDCE6 Additional packages: ${selectedFeatures.join(', ')}` : ''}
|
|
4986
|
+
|
|
4987
|
+
## Getting Started
|
|
4988
|
+
|
|
4989
|
+
1. Install dependencies:
|
|
4990
|
+
\`\`\`bash
|
|
4991
|
+
npm install
|
|
4992
|
+
\`\`\`
|
|
4993
|
+
|
|
4994
|
+
2. Start the development server:
|
|
4995
|
+
\`\`\`bash
|
|
4996
|
+
npm run dev
|
|
4997
|
+
\`\`\`
|
|
4998
|
+
|
|
4999
|
+
3. Build for production:
|
|
5000
|
+
\`\`\`bash
|
|
5001
|
+
npm run build
|
|
5002
|
+
\`\`\`
|
|
5003
|
+
|
|
5004
|
+
## Project Structure
|
|
5005
|
+
|
|
5006
|
+
This project uses a ${answers.projectStructure || 'simple'} structure for better organization and maintainability.
|
|
5007
|
+
|
|
5008
|
+
## Available Scripts
|
|
5009
|
+
|
|
5010
|
+
- \`npm run dev\` - Start development server
|
|
5011
|
+
- \`npm run build\` - Build for production
|
|
5012
|
+
- \`npm run preview\` - Preview production build
|
|
5013
|
+
|
|
5014
|
+
${answers.authStrategy === 'clerk' ? `
|
|
5015
|
+
## \uD83D\uDD10 Clerk Authentication Setup
|
|
5016
|
+
|
|
5017
|
+
1. Sign up at [clerk.com](https://clerk.com)
|
|
5018
|
+
2. Create a new application
|
|
5019
|
+
3. Copy your API keys to \`.env.local\`:
|
|
5020
|
+
\`\`\`bash
|
|
5021
|
+
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
5022
|
+
\`\`\`
|
|
5023
|
+
|
|
5024
|
+
\u26A0\uFE0F **Security Warning**: Never commit real API keys to version control!
|
|
5025
|
+
4. Configure your authentication settings in the Clerk dashboard
|
|
5026
|
+
` : ''}
|
|
5027
|
+
|
|
5028
|
+
## License
|
|
5029
|
+
|
|
5030
|
+
MIT
|
|
5031
|
+
|
|
5032
|
+
${credits}
|
|
5033
|
+
`;
|
|
5034
|
+
|
|
5035
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(projectPath, 'README.md'), readme);
|
|
5036
|
+
}
|
|
5037
|
+
|
|
5038
|
+
async function rsbuild_project_generator_generateClerkAuth(projectPath, answers) {
|
|
5039
|
+
const srcDir = external_path_namespaceObject.join(projectPath, 'src');
|
|
5040
|
+
await external_fs_extra_["default"].ensureDir(srcDir);
|
|
5041
|
+
|
|
5042
|
+
// Generate Clerk provider (uses PUBLIC_ prefix for Rsbuild)
|
|
5043
|
+
const clerkProvider = `import React from 'react';
|
|
5044
|
+
import { ClerkProvider } from '@clerk/clerk-react';
|
|
5045
|
+
|
|
5046
|
+
const CLERK_PUBLISHABLE_KEY = import.meta.env.PUBLIC_CLERK_PUBLISHABLE_KEY;
|
|
5047
|
+
|
|
5048
|
+
if (!CLERK_PUBLISHABLE_KEY) {
|
|
5049
|
+
throw new Error('Missing Publishable Key');
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
export function ClerkProviderWrapper({ children }) {
|
|
5053
|
+
return (
|
|
5054
|
+
<ClerkProvider publishableKey={CLERK_PUBLISHABLE_KEY}>
|
|
5055
|
+
{children}
|
|
5056
|
+
</ClerkProvider>
|
|
5057
|
+
);
|
|
5058
|
+
}
|
|
5059
|
+
`;
|
|
5060
|
+
|
|
5061
|
+
await external_fs_extra_["default"].ensureDir(external_path_namespaceObject.join(srcDir, 'components'));
|
|
5062
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'ClerkProvider.jsx'), clerkProvider);
|
|
5063
|
+
|
|
5064
|
+
// Generate auth components
|
|
5065
|
+
const signInComponent = `import React from 'react';
|
|
5066
|
+
import { SignIn } from '@clerk/clerk-react';
|
|
5067
|
+
|
|
5068
|
+
export function SignInForm() {
|
|
5069
|
+
return (
|
|
5070
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
5071
|
+
<SignIn />
|
|
5072
|
+
</div>
|
|
5073
|
+
);
|
|
5074
|
+
}
|
|
5075
|
+
`;
|
|
5076
|
+
|
|
5077
|
+
const signUpComponent = `import React from 'react';
|
|
5078
|
+
import { SignUp } from '@clerk/clerk-react';
|
|
5079
|
+
|
|
5080
|
+
export function SignUpForm() {
|
|
5081
|
+
return (
|
|
5082
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
5083
|
+
<SignUp />
|
|
5084
|
+
</div>
|
|
5085
|
+
);
|
|
5086
|
+
}
|
|
5087
|
+
`;
|
|
5088
|
+
|
|
5089
|
+
const userButtonComponent = `import React from 'react';
|
|
5090
|
+
import { UserButton } from '@clerk/clerk-react';
|
|
5091
|
+
|
|
5092
|
+
export function UserProfile() {
|
|
5093
|
+
return (
|
|
5094
|
+
<div className="flex items-center gap-4">
|
|
5095
|
+
<UserButton afterSignOutUrl="/" />
|
|
5096
|
+
</div>
|
|
5097
|
+
);
|
|
5098
|
+
}
|
|
5099
|
+
`;
|
|
5100
|
+
|
|
5101
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'SignInForm.jsx'), signInComponent);
|
|
5102
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'SignUpForm.jsx'), signUpComponent);
|
|
5103
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'UserProfile.jsx'), userButtonComponent);
|
|
5104
|
+
}
|
|
5105
|
+
|
|
5106
|
+
async function rsbuild_project_generator_generateEnvExample(projectPath, answers) {
|
|
5107
|
+
let envContent = `# API Configuration
|
|
5108
|
+
PUBLIC_API_URL=http://localhost:3000/api
|
|
5109
|
+
`;
|
|
5110
|
+
|
|
5111
|
+
if (answers.authStrategy === 'clerk') {
|
|
5112
|
+
envContent += `
|
|
5113
|
+
# Clerk Authentication
|
|
5114
|
+
# WARNING: Replace with your actual Clerk API keys from https://clerk.com
|
|
5115
|
+
# Never commit real API keys to version control!
|
|
5116
|
+
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
5117
|
+
`;
|
|
5118
|
+
} else if (answers.authStrategy === 'auth0') {
|
|
5119
|
+
envContent += `
|
|
5120
|
+
# Auth0 Configuration
|
|
5121
|
+
PUBLIC_AUTH0_DOMAIN=your-tenant.auth0.com
|
|
5122
|
+
PUBLIC_AUTH0_CLIENT_ID=your-client-id
|
|
5123
|
+
`;
|
|
5124
|
+
} else if (answers.authStrategy === 'firebase-auth') {
|
|
5125
|
+
envContent += `
|
|
5126
|
+
# Firebase Configuration
|
|
5127
|
+
PUBLIC_FIREBASE_API_KEY=your-api-key
|
|
5128
|
+
PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
|
|
5129
|
+
PUBLIC_FIREBASE_PROJECT_ID=your-project-id
|
|
5130
|
+
`;
|
|
5131
|
+
}
|
|
5132
|
+
|
|
5133
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(projectPath, '.env.example'), envContent);
|
|
5134
|
+
}
|
|
5135
|
+
|
|
5136
|
+
async function rsbuild_project_generator_generateCreditsComponent(projectPath, answers) {
|
|
5137
|
+
const srcDir = external_path_namespaceObject.join(projectPath, 'src');
|
|
5138
|
+
await external_fs_extra_["default"].ensureDir(external_path_namespaceObject.join(srcDir, 'components'));
|
|
5139
|
+
|
|
5140
|
+
const selectedFeatures = Array.isArray(answers.features) ? answers.features : [];
|
|
5141
|
+
const allFeatures = [...selectedFeatures];
|
|
5142
|
+
if (answers.authStrategy && answers.authStrategy !== 'none') {
|
|
5143
|
+
allFeatures.push(answers.authStrategy);
|
|
5144
|
+
}
|
|
5145
|
+
if (answers.typescript) {
|
|
5146
|
+
allFeatures.push('typescript');
|
|
5147
|
+
}
|
|
5148
|
+
|
|
5149
|
+
const creditsComponent = generateReactCreditsComponent('rsbuild-react', allFeatures);
|
|
5150
|
+
await external_fs_extra_["default"].writeFile(external_path_namespaceObject.join(srcDir, 'components', 'Credits.jsx'), creditsComponent);
|
|
5151
|
+
}
|
|
5152
|
+
|
|
4737
5153
|
;// ./src/utils/path-security.js
|
|
4738
5154
|
/**
|
|
4739
5155
|
* Path security utilities to prevent directory traversal and path injection attacks
|
|
@@ -9470,6 +9886,7 @@ function successMessage(projectName) {
|
|
|
9470
9886
|
|
|
9471
9887
|
|
|
9472
9888
|
|
|
9889
|
+
|
|
9473
9890
|
async function createApp(cliProjectName = null) {
|
|
9474
9891
|
try {
|
|
9475
9892
|
// === Stage 1: Initial prompts (framework, preset, project name, TypeScript) ===
|
|
@@ -9487,6 +9904,7 @@ async function createApp(cliProjectName = null) {
|
|
|
9487
9904
|
|
|
9488
9905
|
const isNext = initialAnswers.framework === 'nextjs';
|
|
9489
9906
|
const isVite = initialAnswers.framework === 'vite-react';
|
|
9907
|
+
const isRsbuild = initialAnswers.framework === 'rsbuild-react';
|
|
9490
9908
|
|
|
9491
9909
|
// === Stage 2: Run official scaffold for frameworks that use CLI tools ===
|
|
9492
9910
|
if (isVite) {
|
|
@@ -9503,6 +9921,19 @@ async function createApp(cliProjectName = null) {
|
|
|
9503
9921
|
logger.stopSpinner(false, 'Failed to create Vite project');
|
|
9504
9922
|
throw error;
|
|
9505
9923
|
}
|
|
9924
|
+
} else if (isRsbuild) {
|
|
9925
|
+
if (initialAnswers.typescript === undefined) {
|
|
9926
|
+
initialAnswers.typescript = true;
|
|
9927
|
+
}
|
|
9928
|
+
|
|
9929
|
+
const spinner = logger.startSpinner('Creating Rsbuild project...');
|
|
9930
|
+
try {
|
|
9931
|
+
await createRsbuildBase(projectPath, initialAnswers);
|
|
9932
|
+
logger.stopSpinner(true, 'Rsbuild project scaffold created!');
|
|
9933
|
+
} catch (error) {
|
|
9934
|
+
logger.stopSpinner(false, 'Failed to create Rsbuild project');
|
|
9935
|
+
throw error;
|
|
9936
|
+
}
|
|
9506
9937
|
} else if (!isNext) {
|
|
9507
9938
|
// For Express/Fastify, create directory ourselves
|
|
9508
9939
|
logger.debug(`Creating project directory: ${projectPath}`);
|
|
@@ -9512,7 +9943,7 @@ async function createApp(cliProjectName = null) {
|
|
|
9512
9943
|
// Next.js: directory creation is handled by create-next-app inside generateNextJSProject
|
|
9513
9944
|
|
|
9514
9945
|
// Derive setupType from vitePreset so customization prompts can gate on it
|
|
9515
|
-
if (isVite && initialAnswers.vitePreset) {
|
|
9946
|
+
if ((isVite || isRsbuild) && initialAnswers.vitePreset) {
|
|
9516
9947
|
initialAnswers.setupType = presetToSetupType(initialAnswers.vitePreset);
|
|
9517
9948
|
}
|
|
9518
9949
|
|
|
@@ -9534,6 +9965,15 @@ async function createApp(cliProjectName = null) {
|
|
|
9534
9965
|
logger.stopSpinner(false, 'Failed to customize project');
|
|
9535
9966
|
throw error;
|
|
9536
9967
|
}
|
|
9968
|
+
} else if (isRsbuild) {
|
|
9969
|
+
const spinner = logger.startSpinner('Customizing project...');
|
|
9970
|
+
try {
|
|
9971
|
+
await generateRsbuildProject(projectPath, answers);
|
|
9972
|
+
logger.stopSpinner(true, 'Project customized successfully!');
|
|
9973
|
+
} catch (error) {
|
|
9974
|
+
logger.stopSpinner(false, 'Failed to customize project');
|
|
9975
|
+
throw error;
|
|
9976
|
+
}
|
|
9537
9977
|
} else if (isNext) {
|
|
9538
9978
|
await generateNextJSProject(projectPath, answers);
|
|
9539
9979
|
} else if (answers.framework === 'express') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "build-app-with",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "🚀 Interactive CLI tool to quickly create modern web applications. Use with 'npx build-app-with my-app' or install globally. Choose from React (Next.js/Vite), Node.js backends (Express/Fastify), with built-in TypeScript, authentication, databases, and more.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|