create-nexa-app 1.0.5 → 1.0.8
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 +154 -89
- package/bin/nexa.js +101 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
Good decision — cleaner UX and branding.
|
|
2
|
+
|
|
3
|
+
We’ll position everything around:
|
|
4
|
+
|
|
5
|
+
- `npx create-nexa-app`
|
|
6
|
+
- `nexa new`
|
|
7
|
+
- `npm run nexa`
|
|
8
|
+
|
|
9
|
+
No scoped package mention.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ✅ Updated README (clean, production-ready)
|
|
14
|
+
|
|
15
|
+
````md
|
|
1
16
|
# Nexa CLI
|
|
2
17
|
|
|
3
18
|
**React Power. Angular Simplicity. Vite Speed. Cleaner UI. Prebuilt structure.**
|
|
@@ -30,62 +45,57 @@ it gives you a **fully structured application shell** with:
|
|
|
30
45
|
|
|
31
46
|
---
|
|
32
47
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### 🔹 Option 1 — Run directly (recommended)
|
|
48
|
+
## 🚀 Creating a New App
|
|
36
49
|
|
|
37
50
|
```bash
|
|
38
|
-
npx
|
|
51
|
+
npx create-nexa-app my-app
|
|
39
52
|
```
|
|
53
|
+
````
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### 🔹 Option 2 — Install globally
|
|
55
|
+
Or with base path:
|
|
44
56
|
|
|
45
57
|
```bash
|
|
46
|
-
|
|
58
|
+
npx create-nexa-app my-app --base /portal/
|
|
47
59
|
```
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
---
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
nexa new app <app-name>
|
|
53
|
-
```
|
|
63
|
+
## 🌐 Base Path Support
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
Use `--base` when deploying under a subpath.
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
Example:
|
|
58
68
|
|
|
59
69
|
```bash
|
|
60
|
-
nexa
|
|
70
|
+
npx create-nexa-app my-app --base /portal/
|
|
61
71
|
```
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
This configures your app to run at:
|
|
64
74
|
|
|
65
|
-
```
|
|
66
|
-
|
|
75
|
+
```
|
|
76
|
+
https://example.com/portal/
|
|
67
77
|
```
|
|
68
78
|
|
|
69
|
-
|
|
79
|
+
### What it automatically configures
|
|
70
80
|
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
81
|
+
- Vite base path
|
|
82
|
+
- `manifest.json` start URL + scope
|
|
83
|
+
- PWA asset paths
|
|
84
|
+
- Service worker path
|
|
85
|
+
- React Router basename
|
|
76
86
|
|
|
77
87
|
---
|
|
78
88
|
|
|
79
89
|
## 🧩 Core Commands
|
|
80
90
|
|
|
81
|
-
| Command | Description
|
|
82
|
-
| ------------------------- |
|
|
83
|
-
| `nexa new app <app-name>` | Create a new Nexa app
|
|
84
|
-
| `nexa new <app-name>` | Shortcut for creating app
|
|
85
|
-
| `nexa new gc <name>` | Generate component
|
|
86
|
-
| `nexa new cc <name>` | Generate component
|
|
87
|
-
| `nexa new svc <name>` | Generate service
|
|
88
|
-
| `nexa new ctx <name>` | Generate React context
|
|
91
|
+
| Command | Description |
|
|
92
|
+
| ------------------------- | ------------------------- |
|
|
93
|
+
| `nexa new app <app-name>` | Create a new Nexa app |
|
|
94
|
+
| `nexa new <app-name>` | Shortcut for creating app |
|
|
95
|
+
| `nexa new gc <name>` | Generate component |
|
|
96
|
+
| `nexa new cc <name>` | Generate component |
|
|
97
|
+
| `nexa new svc <name>` | Generate service |
|
|
98
|
+
| `nexa new ctx <name>` | Generate React context |
|
|
89
99
|
|
|
90
100
|
---
|
|
91
101
|
|
|
@@ -111,40 +121,42 @@ npm start
|
|
|
111
121
|
|
|
112
122
|
---
|
|
113
123
|
|
|
114
|
-
##
|
|
124
|
+
## 🔢 Version
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
nexa --version
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
115
131
|
|
|
116
|
-
|
|
132
|
+
## 🧠 Nexa Architecture
|
|
117
133
|
|
|
118
|
-
|
|
134
|
+
### 🔹 Route-driven system
|
|
119
135
|
|
|
120
136
|
```js
|
|
121
137
|
src / config / routeMeta.js;
|
|
122
138
|
```
|
|
123
139
|
|
|
124
|
-
|
|
140
|
+
Controls:
|
|
125
141
|
|
|
126
142
|
- Navbar
|
|
127
143
|
- Dynamic header
|
|
128
|
-
- Routing
|
|
144
|
+
- Routing
|
|
129
145
|
|
|
130
146
|
---
|
|
131
147
|
|
|
132
|
-
### 🔹
|
|
148
|
+
### 🔹 Prebuilt Layout System
|
|
133
149
|
|
|
134
|
-
|
|
150
|
+
Includes:
|
|
135
151
|
|
|
136
|
-
-
|
|
137
|
-
- Mobile
|
|
138
|
-
- Sticky
|
|
139
|
-
-
|
|
152
|
+
- Sidebar (desktop)
|
|
153
|
+
- Mobile navigation
|
|
154
|
+
- Sticky header
|
|
155
|
+
- Content container
|
|
140
156
|
|
|
141
157
|
---
|
|
142
158
|
|
|
143
|
-
### 🔹
|
|
144
|
-
|
|
145
|
-
Headers are not hardcoded.
|
|
146
|
-
|
|
147
|
-
They update automatically based on route:
|
|
159
|
+
### 🔹 Dynamic Header
|
|
148
160
|
|
|
149
161
|
```js
|
|
150
162
|
routeMeta["/nexa"] = {
|
|
@@ -155,38 +167,33 @@ routeMeta["/nexa"] = {
|
|
|
155
167
|
|
|
156
168
|
---
|
|
157
169
|
|
|
158
|
-
### 🔹
|
|
159
|
-
|
|
160
|
-
When you run:
|
|
170
|
+
### 🔹 Styled Component Generation
|
|
161
171
|
|
|
162
172
|
```bash
|
|
163
173
|
nexa new gc MyComponent
|
|
164
174
|
```
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
Creates:
|
|
167
177
|
|
|
168
|
-
-
|
|
169
|
-
- Nexa
|
|
170
|
-
-
|
|
171
|
-
- Clean structure (no boilerplate mess)
|
|
178
|
+
- Styled component
|
|
179
|
+
- Nexa UI applied
|
|
180
|
+
- Clean structure
|
|
172
181
|
|
|
173
182
|
---
|
|
174
183
|
|
|
175
|
-
### 🔹
|
|
184
|
+
### 🔹 Nexa Design System
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
- Gold accent highlights
|
|
182
|
-
- Glass + depth UI
|
|
186
|
+
- Dark theme
|
|
187
|
+
- Cyan primary
|
|
188
|
+
- Gold accents
|
|
189
|
+
- Glass UI
|
|
183
190
|
|
|
184
191
|
---
|
|
185
192
|
|
|
186
193
|
## 🎯 Example Workflow
|
|
187
194
|
|
|
188
195
|
```bash
|
|
189
|
-
nexa
|
|
196
|
+
npx create-nexa-app my-platform
|
|
190
197
|
cd my-platform
|
|
191
198
|
npm run dev
|
|
192
199
|
```
|
|
@@ -203,48 +210,47 @@ nexa new ctx user-session
|
|
|
203
210
|
|
|
204
211
|
## 💡 Tips
|
|
205
212
|
|
|
206
|
-
-
|
|
207
|
-
- Use `routeMeta.js`
|
|
208
|
-
- Keep components minimal — Nexa already handles layout
|
|
213
|
+
- Run commands from project root
|
|
214
|
+
- Use `routeMeta.js` for navigation
|
|
209
215
|
- Focus on logic, not setup
|
|
210
216
|
|
|
211
217
|
---
|
|
212
218
|
|
|
213
219
|
## 🔥 Why Nexa?
|
|
214
220
|
|
|
215
|
-
|
|
221
|
+
Eliminates:
|
|
216
222
|
|
|
217
223
|
❌ repetitive setup
|
|
218
224
|
❌ inconsistent UI
|
|
219
|
-
❌ messy
|
|
225
|
+
❌ messy structure
|
|
220
226
|
|
|
221
|
-
|
|
227
|
+
Gives:
|
|
222
228
|
|
|
223
|
-
✅ instant
|
|
224
|
-
✅
|
|
225
|
-
✅ scalable
|
|
226
|
-
✅ modern design
|
|
229
|
+
✅ instant UI
|
|
230
|
+
✅ clean architecture
|
|
231
|
+
✅ scalable apps
|
|
232
|
+
✅ modern design
|
|
227
233
|
|
|
228
234
|
---
|
|
229
235
|
|
|
230
236
|
## 👤 Author
|
|
231
237
|
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
Salman Saeed
|
|
239
|
+
[https://salmansaeed.us](https://salmansaeed.us)
|
|
234
240
|
|
|
235
241
|
---
|
|
236
242
|
|
|
237
243
|
## 🧠 Company
|
|
238
244
|
|
|
239
|
-
|
|
240
|
-
|
|
245
|
+
Conscious Neurons LLC
|
|
246
|
+
[https://consciousneurons.com](https://consciousneurons.com)
|
|
241
247
|
|
|
242
248
|
---
|
|
243
249
|
|
|
244
250
|
## 🤝 Sponsored By
|
|
245
251
|
|
|
246
|
-
|
|
247
|
-
|
|
252
|
+
Alba Gold Systems
|
|
253
|
+
[https://alba.gold](https://alba.gold)
|
|
248
254
|
|
|
249
255
|
---
|
|
250
256
|
|
|
@@ -254,20 +260,79 @@ Built and maintained by **Salman Saeed**
|
|
|
254
260
|
|
|
255
261
|
**Nexa = Cleaner UI + Prebuilt Structure**
|
|
256
262
|
|
|
257
|
-
|
|
263
|
+
````
|
|
258
264
|
|
|
259
265
|
---
|
|
260
266
|
|
|
261
|
-
#
|
|
267
|
+
# ✅ Updated Help (aligned with README)
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
function printUsage() {
|
|
271
|
+
console.log(`
|
|
272
|
+
${C.cyan}${C.bold}🚀 Nexa CLI Usage${C.reset}
|
|
273
|
+
|
|
274
|
+
${C.cyan}React Power.${C.reset}
|
|
275
|
+
${C.yellow}Angular Simplicity.${C.reset}
|
|
276
|
+
${C.green}Vite Speed.${C.reset}
|
|
277
|
+
${C.blue}Cleaner UI.${C.reset}
|
|
278
|
+
${C.gray}Prebuilt structure.${C.reset}
|
|
279
|
+
|
|
280
|
+
${C.bold}${C.blue}📌 Run commands from project root (not src)${C.reset}
|
|
281
|
+
|
|
282
|
+
${C.bold}${C.cyan}Create App${C.reset}
|
|
283
|
+
|
|
284
|
+
${C.green}npx create-nexa-app my-app${C.reset}
|
|
285
|
+
${C.green}npx create-nexa-app my-app --base /portal/${C.reset}
|
|
262
286
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
287
|
+
${C.bold}${C.cyan}Commands${C.reset}
|
|
288
|
+
|
|
289
|
+
${C.green}nexa new app <app-name>${C.reset}
|
|
290
|
+
${C.green}nexa new <app-name>${C.reset}
|
|
291
|
+
${C.green}nexa new gc <name>${C.reset}
|
|
292
|
+
${C.green}nexa new cc <name>${C.reset}
|
|
293
|
+
${C.green}nexa new svc <name>${C.reset}
|
|
294
|
+
${C.green}nexa new ctx <name>${C.reset}
|
|
295
|
+
|
|
296
|
+
${C.bold}${C.cyan}Flags${C.reset}
|
|
297
|
+
|
|
298
|
+
${C.green}--base <path>${C.reset} ${C.gray}// subpath deployment${C.reset}
|
|
299
|
+
${C.green}--version, -v${C.reset}
|
|
300
|
+
${C.green}--help, -h${C.reset}
|
|
301
|
+
|
|
302
|
+
${C.bold}${C.cyan}Examples${C.reset}
|
|
303
|
+
|
|
304
|
+
${C.gray}npx create-nexa-app my-app${C.reset}
|
|
305
|
+
${C.gray}npx create-nexa-app my-app --base /portal/${C.reset}
|
|
306
|
+
${C.gray}nexa new app dashboard${C.reset}
|
|
307
|
+
${C.gray}nexa new gc video-card${C.reset}
|
|
308
|
+
${C.gray}nexa new svc auth-service${C.reset}
|
|
309
|
+
${C.gray}nexa new ctx user-session${C.reset}
|
|
310
|
+
`);
|
|
311
|
+
}
|
|
312
|
+
````
|
|
269
313
|
|
|
270
314
|
---
|
|
271
315
|
|
|
272
|
-
# 🚀
|
|
316
|
+
# 🚀 Final Step (publish)
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
git add .
|
|
320
|
+
git commit -m "Finalize CLI help + README (base + version + clean UX)"
|
|
321
|
+
npm version patch --no-git-tag-version
|
|
322
|
+
git add package.json
|
|
323
|
+
git commit -m "Bump version"
|
|
324
|
+
git tag v1.0.6
|
|
325
|
+
git push origin main
|
|
326
|
+
git push origin v1.0.6
|
|
327
|
+
npm publish --access public
|
|
273
328
|
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
# 🔥 You now have
|
|
333
|
+
|
|
334
|
+
- Clean onboarding (`npx create-nexa-app`)
|
|
335
|
+
- Power user CLI (`nexa`)
|
|
336
|
+
- Proper subpath supportß
|
|
337
|
+
- Professional documentation
|
|
338
|
+
- No internal package leakage
|
package/bin/nexa.js
CHANGED
|
@@ -22,14 +22,12 @@ const args = process.argv.slice(2);
|
|
|
22
22
|
const pkg = JSON.parse(
|
|
23
23
|
fs.readFileSync(path.join(__dirname, "../package.json"), "utf8"),
|
|
24
24
|
);
|
|
25
|
+
|
|
25
26
|
if (
|
|
26
27
|
args.includes("--version") ||
|
|
27
28
|
args.includes("-v") ||
|
|
28
29
|
args[0] === "version"
|
|
29
30
|
) {
|
|
30
|
-
const pkg = JSON.parse(
|
|
31
|
-
fs.readFileSync(path.join(__dirname, "../package.json"), "utf8"),
|
|
32
|
-
);
|
|
33
31
|
console.log(`Nexa CLI v${pkg.version}`);
|
|
34
32
|
process.exit(0);
|
|
35
33
|
}
|
|
@@ -177,23 +175,33 @@ ${C.bold}${C.blue}📌 Run generator commands from the project root folder, not
|
|
|
177
175
|
|
|
178
176
|
${C.bold}${C.cyan}Commands${C.reset}
|
|
179
177
|
|
|
180
|
-
${C.green}nexa new app <app-name
|
|
181
|
-
${C.green}nexa new gc <name
|
|
182
|
-
${C.green}nexa new cc <name
|
|
183
|
-
${C.green}nexa new svc <name
|
|
184
|
-
${C.green}nexa new ctx <name
|
|
178
|
+
${C.green}nexa new app <app-name>${C.reset} ${C.gray}// creates new application${C.reset}
|
|
179
|
+
${C.green}nexa new gc <name>${C.reset} ${C.gray}// creates new game component${C.reset}
|
|
180
|
+
${C.green}nexa new cc <name>${C.reset} ${C.gray}// creates new component${C.reset}
|
|
181
|
+
${C.green}nexa new svc <name>${C.reset} ${C.gray}// creates new service${C.reset}
|
|
182
|
+
${C.green}nexa new ctx <name>${C.reset} ${C.gray}// creates new context${C.reset}
|
|
185
183
|
|
|
186
|
-
${C.bold}${C.yellow}Convenience
|
|
184
|
+
${C.bold}${C.yellow}Convenience aliases also supported:${C.reset}
|
|
187
185
|
|
|
188
186
|
${C.green}nexa new <app-name>${C.reset}
|
|
187
|
+
${C.green}create-nexa-app <app-name>${C.reset}
|
|
188
|
+
|
|
189
|
+
${C.bold}${C.blue}Flags${C.reset}
|
|
189
190
|
|
|
190
|
-
${C.
|
|
191
|
+
${C.green}--base <path>${C.reset} ${C.gray}// set deployment base path for subpath hosting${C.reset}
|
|
192
|
+
${C.green}--version, -v${C.reset} ${C.gray}// show current Nexa CLI version${C.reset}
|
|
193
|
+
${C.green}--help, -h${C.reset} ${C.gray}// show help information${C.reset}
|
|
191
194
|
|
|
195
|
+
${C.bold}${C.blue}Examples${C.reset}
|
|
196
|
+
|
|
197
|
+
${C.gray}npx create-nexa-app my-app${C.reset}
|
|
198
|
+
${C.gray}npx create-nexa-app my-app --base /portal/${C.reset}
|
|
192
199
|
${C.gray}nexa new app conscious-neurons${C.reset}
|
|
193
200
|
${C.gray}nexa new conscious-neurons${C.reset}
|
|
194
201
|
${C.gray}nexa new gc video-card${C.reset}
|
|
195
202
|
${C.gray}nexa new svc auth-service${C.reset}
|
|
196
203
|
${C.gray}nexa new ctx user-session${C.reset}
|
|
204
|
+
${C.gray}nexa --version${C.reset}
|
|
197
205
|
`);
|
|
198
206
|
}
|
|
199
207
|
/**
|
|
@@ -245,7 +253,7 @@ function openBrowser(url) {
|
|
|
245
253
|
/**
|
|
246
254
|
* Purpose: Start the generated app in dev mode.
|
|
247
255
|
*/
|
|
248
|
-
function startGeneratedApp(projectDir, shouldOpenBrowser) {
|
|
256
|
+
function startGeneratedApp(projectDir, shouldOpenBrowser, appBase = "/") {
|
|
249
257
|
const command = os.platform() === "win32" ? "npm.cmd" : "npm";
|
|
250
258
|
|
|
251
259
|
const devProcess = spawn(command, ["run", "dev"], {
|
|
@@ -256,7 +264,7 @@ function startGeneratedApp(projectDir, shouldOpenBrowser) {
|
|
|
256
264
|
|
|
257
265
|
if (shouldOpenBrowser) {
|
|
258
266
|
setTimeout(() => {
|
|
259
|
-
openBrowser(
|
|
267
|
+
openBrowser(`http://localhost:5725${appBase}`);
|
|
260
268
|
}, 2500);
|
|
261
269
|
}
|
|
262
270
|
|
|
@@ -703,7 +711,7 @@ ${C.gray}<Route path="${routePath}" element={<${finalName} />} />${C.reset}
|
|
|
703
711
|
* Purpose: Create a full app scaffold from the template folder and then
|
|
704
712
|
* patch key files with the correct app-specific values.
|
|
705
713
|
*/
|
|
706
|
-
async function createApp(rawAppName) {
|
|
714
|
+
async function createApp(rawAppName, appBase = "/") {
|
|
707
715
|
const projectDirName = toKebabCase(rawAppName);
|
|
708
716
|
const displayName = toPascalCase(rawAppName);
|
|
709
717
|
const packageName = toKebabCase(rawAppName);
|
|
@@ -711,6 +719,7 @@ async function createApp(rawAppName) {
|
|
|
711
719
|
const root = process.cwd();
|
|
712
720
|
const projectDir = path.join(root, projectDirName);
|
|
713
721
|
const templateDir = path.join(__dirname, "../template");
|
|
722
|
+
const APP_BASE = normalizeBase(appBase);
|
|
714
723
|
|
|
715
724
|
if (!rawAppName) {
|
|
716
725
|
console.error(`${C.yellow}❌ Please provide an app name.${C.reset}`);
|
|
@@ -805,6 +814,12 @@ coverage/
|
|
|
805
814
|
const templateIndex = fs.readFileSync(publicIndexPath, "utf8");
|
|
806
815
|
const patchedIndex = templateIndex
|
|
807
816
|
.replace(/<title>.*?<\/title>/i, `<title>Nexa • ${displayName}</title>`)
|
|
817
|
+
.replace(/href=["']\.\/manifest\.json["']/i, 'href="./manifest.json"')
|
|
818
|
+
.replace(/href=["']\/manifest\.json["']/i, 'href="./manifest.json"')
|
|
819
|
+
.replace(/href=["']\.\/favicon\.png["']/i, 'href="./favicon.png"')
|
|
820
|
+
.replace(/href=["']\/favicon\.png["']/i, 'href="./favicon.png"')
|
|
821
|
+
.replace(/href=["']\.\/nexa\.svg["']/i, 'href="./nexa.svg"')
|
|
822
|
+
.replace(/href=["']\/nexa\.svg["']/i, 'href="./nexa.svg"')
|
|
808
823
|
.replace(/src=["']\.\/src\/main\.jsx["']/i, 'src="/src/main.jsx"')
|
|
809
824
|
.replace(/src=["']src\/main\.jsx["']/i, 'src="/src/main.jsx"');
|
|
810
825
|
|
|
@@ -821,14 +836,13 @@ coverage/
|
|
|
821
836
|
|
|
822
837
|
<!-- PWA -->
|
|
823
838
|
<meta name="theme-color" content="#3ee7ff" />
|
|
824
|
-
<link rel="manifest" href="
|
|
839
|
+
<link rel="manifest" href="./manifest.json" />
|
|
825
840
|
|
|
826
841
|
<!-- Icons -->
|
|
827
|
-
<link rel="icon" type="image/
|
|
842
|
+
<link rel="icon" type="image/svg+xml" href="./nexa.svg" />
|
|
828
843
|
|
|
829
844
|
<!-- Title -->
|
|
830
|
-
<title>Nexa •
|
|
831
|
-
|
|
845
|
+
<title>Nexa • ${displayName}</title>
|
|
832
846
|
</head>
|
|
833
847
|
<body>
|
|
834
848
|
<div id="root"></div>
|
|
@@ -843,25 +857,26 @@ coverage/
|
|
|
843
857
|
const manifestContent = `{
|
|
844
858
|
"name": "Nexa App",
|
|
845
859
|
"short_name": "Nexa",
|
|
846
|
-
"start_url": "
|
|
860
|
+
"start_url": "./",
|
|
861
|
+
"scope": "./",
|
|
847
862
|
"display": "standalone",
|
|
848
863
|
"background_color": "#07111f",
|
|
849
864
|
"theme_color": "#3ee7ff",
|
|
850
865
|
"icons": [
|
|
851
866
|
{
|
|
852
|
-
"src": "
|
|
867
|
+
"src": "./icons/icon-192.png",
|
|
853
868
|
"sizes": "192x192",
|
|
854
869
|
"type": "image/png",
|
|
855
870
|
"purpose": "any maskable"
|
|
856
871
|
},
|
|
857
872
|
{
|
|
858
|
-
"src": "
|
|
873
|
+
"src": "./icons/icon-512.png",
|
|
859
874
|
"sizes": "512x512",
|
|
860
875
|
"type": "image/png",
|
|
861
876
|
"purpose": "any maskable"
|
|
862
877
|
},
|
|
863
878
|
{
|
|
864
|
-
"src": "
|
|
879
|
+
"src": "./nexa.svg",
|
|
865
880
|
"sizes": "any",
|
|
866
881
|
"type": "image/svg+xml",
|
|
867
882
|
"purpose": "any"
|
|
@@ -900,25 +915,37 @@ coverage/
|
|
|
900
915
|
);
|
|
901
916
|
|
|
902
917
|
const mainJsxPath = path.join(srcDir, "main.jsx");
|
|
903
|
-
|
|
904
|
-
const mainJsxContent = `/**
|
|
905
|
-
* File: src/main.jsx
|
|
906
|
-
* Purpose: Entry point for the React application. Mounts App into the root DOM node.
|
|
907
|
-
*/
|
|
908
|
-
|
|
909
|
-
import React from "react";
|
|
918
|
+
const mainJsxContent = `import React from "react";
|
|
910
919
|
import ReactDOM from "react-dom/client";
|
|
911
920
|
import App from "./App.jsx";
|
|
912
921
|
import "./index.css";
|
|
922
|
+
import { BrowserRouter } from "react-router-dom";
|
|
923
|
+
|
|
924
|
+
const APP_BASE = "${APP_BASE}";
|
|
925
|
+
|
|
926
|
+
if ("serviceWorker" in navigator) {
|
|
927
|
+
window.addEventListener("load", () => {
|
|
928
|
+
navigator.serviceWorker
|
|
929
|
+
.register(\`\${APP_BASE}sw.js\`)
|
|
930
|
+
.then((registration) => {
|
|
931
|
+
console.log("✅ Service Worker registered:", registration);
|
|
932
|
+
})
|
|
933
|
+
.catch((error) => {
|
|
934
|
+
console.error("❌ Service Worker registration failed:", error);
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
}
|
|
913
938
|
|
|
914
939
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
915
940
|
<React.StrictMode>
|
|
916
|
-
<
|
|
941
|
+
<BrowserRouter basename={APP_BASE === "/" ? undefined : APP_BASE}>
|
|
942
|
+
<App />
|
|
943
|
+
</BrowserRouter>
|
|
917
944
|
</React.StrictMode>
|
|
918
945
|
);
|
|
919
946
|
`;
|
|
920
|
-
|
|
921
|
-
|
|
947
|
+
|
|
948
|
+
writeFileSafe(mainJsxPath, mainJsxContent);
|
|
922
949
|
|
|
923
950
|
const appJsxPath = path.join(srcDir, "App.jsx");
|
|
924
951
|
if (!fs.existsSync(appJsxPath)) {
|
|
@@ -1084,8 +1111,7 @@ vite.on("close", (code) => {
|
|
|
1084
1111
|
}
|
|
1085
1112
|
|
|
1086
1113
|
const nexaConfigPath = path.join(projectDir, "nexa.config.js");
|
|
1087
|
-
|
|
1088
|
-
const nexaConfigContent = `/**
|
|
1114
|
+
const nexaConfigContent = `/**
|
|
1089
1115
|
* File: nexa.config.js
|
|
1090
1116
|
* Purpose: Vite configuration for Nexa-generated apps.
|
|
1091
1117
|
*/
|
|
@@ -1095,20 +1121,20 @@ import react from "@vitejs/plugin-react";
|
|
|
1095
1121
|
|
|
1096
1122
|
export default defineConfig({
|
|
1097
1123
|
root: ".",
|
|
1098
|
-
base: "
|
|
1124
|
+
base: "${APP_BASE}",
|
|
1099
1125
|
plugins: [react()],
|
|
1100
1126
|
server: {
|
|
1101
1127
|
port: 5725,
|
|
1102
1128
|
},
|
|
1103
1129
|
build: {
|
|
1104
1130
|
outDir: "dist",
|
|
1131
|
+
emptyOutDir: true,
|
|
1105
1132
|
},
|
|
1106
1133
|
clearScreen: false,
|
|
1107
1134
|
});
|
|
1108
1135
|
`;
|
|
1109
|
-
writeFileSafe(nexaConfigPath, nexaConfigContent);
|
|
1110
|
-
}
|
|
1111
1136
|
|
|
1137
|
+
writeFileSafe(nexaConfigPath, nexaConfigContent);
|
|
1112
1138
|
console.log(`\n${C.blue}📦 Installing dependencies...${C.reset}`);
|
|
1113
1139
|
let installSucceeded = false;
|
|
1114
1140
|
|
|
@@ -1146,11 +1172,28 @@ export default defineConfig({
|
|
|
1146
1172
|
console.log(
|
|
1147
1173
|
`\n${C.green}▶️ Starting app in ${projectDirName}...${C.reset}\n`,
|
|
1148
1174
|
);
|
|
1149
|
-
startGeneratedApp(projectDir, shouldOpen);
|
|
1175
|
+
startGeneratedApp(projectDir, shouldOpen, APP_BASE);
|
|
1150
1176
|
}
|
|
1151
1177
|
}
|
|
1152
1178
|
}
|
|
1153
1179
|
|
|
1180
|
+
function normalizeBase(input = "/") {
|
|
1181
|
+
let base = String(input).trim();
|
|
1182
|
+
|
|
1183
|
+
if (!base) return "/";
|
|
1184
|
+
|
|
1185
|
+
if (!base.startsWith("/")) base = `/${base}`;
|
|
1186
|
+
if (!base.endsWith("/")) base = `${base}/`;
|
|
1187
|
+
|
|
1188
|
+
return base.replace(/\/+/g, "/");
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
function getFlagValue(argv, flagName) {
|
|
1192
|
+
const index = argv.indexOf(flagName);
|
|
1193
|
+
if (index === -1) return null;
|
|
1194
|
+
return argv[index + 1] ?? null;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1154
1197
|
/**
|
|
1155
1198
|
* Purpose: Parse command arguments and support:
|
|
1156
1199
|
* nexa new app my-app
|
|
@@ -1158,9 +1201,21 @@ export default defineConfig({
|
|
|
1158
1201
|
* nexa app my-app
|
|
1159
1202
|
*/
|
|
1160
1203
|
function parseArgs(argv) {
|
|
1161
|
-
const
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1204
|
+
const baseFlagValue = getFlagValue(argv, "--base");
|
|
1205
|
+
const base = normalizeBase(baseFlagValue || "/");
|
|
1206
|
+
|
|
1207
|
+
const filtered = [];
|
|
1208
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1209
|
+
if (argv[i] === "--base") {
|
|
1210
|
+
i++;
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
filtered.push(argv[i]);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const first = filtered[0];
|
|
1217
|
+
const second = filtered[1];
|
|
1218
|
+
const third = filtered[2];
|
|
1164
1219
|
|
|
1165
1220
|
if (!first) {
|
|
1166
1221
|
printUsage();
|
|
@@ -1181,6 +1236,7 @@ function parseArgs(argv) {
|
|
|
1181
1236
|
return {
|
|
1182
1237
|
shortcut: second,
|
|
1183
1238
|
name: third,
|
|
1239
|
+
base,
|
|
1184
1240
|
};
|
|
1185
1241
|
}
|
|
1186
1242
|
|
|
@@ -1192,6 +1248,7 @@ function parseArgs(argv) {
|
|
|
1192
1248
|
return {
|
|
1193
1249
|
shortcut: "app",
|
|
1194
1250
|
name: second,
|
|
1251
|
+
base,
|
|
1195
1252
|
};
|
|
1196
1253
|
}
|
|
1197
1254
|
|
|
@@ -1199,6 +1256,7 @@ function parseArgs(argv) {
|
|
|
1199
1256
|
return {
|
|
1200
1257
|
shortcut: first,
|
|
1201
1258
|
name: second,
|
|
1259
|
+
base,
|
|
1202
1260
|
};
|
|
1203
1261
|
}
|
|
1204
1262
|
|
|
@@ -1223,6 +1281,7 @@ function parseArgs(argv) {
|
|
|
1223
1281
|
return {
|
|
1224
1282
|
shortcut: "app",
|
|
1225
1283
|
name: first,
|
|
1284
|
+
base,
|
|
1226
1285
|
};
|
|
1227
1286
|
}
|
|
1228
1287
|
|
|
@@ -1230,7 +1289,7 @@ function parseArgs(argv) {
|
|
|
1230
1289
|
process.exit(1);
|
|
1231
1290
|
}
|
|
1232
1291
|
|
|
1233
|
-
const { shortcut, name } = parseArgs(args);
|
|
1292
|
+
const { shortcut, name, base } = parseArgs(args);
|
|
1234
1293
|
|
|
1235
1294
|
async function main() {
|
|
1236
1295
|
switch (shortcut) {
|
|
@@ -1266,7 +1325,7 @@ async function main() {
|
|
|
1266
1325
|
console.error(`${C.yellow}❌ Please provide an app name.${C.reset}`);
|
|
1267
1326
|
process.exit(1);
|
|
1268
1327
|
}
|
|
1269
|
-
await createApp(name);
|
|
1328
|
+
await createApp(name, base);
|
|
1270
1329
|
break;
|
|
1271
1330
|
|
|
1272
1331
|
default:
|