create-nexa-app 1.0.1 → 1.0.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/README.md +154 -89
- package/bin/nexa.js +110 -41
- 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}
|
|
190
|
+
|
|
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}
|
|
189
194
|
|
|
190
|
-
${C.bold}${C.
|
|
195
|
+
${C.bold}${C.blue}Examples${C.reset}
|
|
191
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,21 @@ 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(
|
|
818
|
+
/href=["']\.\/manifest\.json["']/i,
|
|
819
|
+
`href="${APP_BASE}manifest.json"`,
|
|
820
|
+
)
|
|
821
|
+
.replace(
|
|
822
|
+
/href=["']\/manifest\.json["']/i,
|
|
823
|
+
`href="${APP_BASE}manifest.json"`,
|
|
824
|
+
)
|
|
825
|
+
.replace(
|
|
826
|
+
/href=["']\.\/favicon\.png["']/i,
|
|
827
|
+
`href="${APP_BASE}favicon.png"`,
|
|
828
|
+
)
|
|
829
|
+
.replace(/href=["']\/favicon\.png["']/i, `href="${APP_BASE}favicon.png"`)
|
|
830
|
+
.replace(/href=["']\.\/nexa\.svg["']/i, `href="${APP_BASE}nexa.svg"`)
|
|
831
|
+
.replace(/href=["']\/nexa\.svg["']/i, `href="${APP_BASE}nexa.svg"`)
|
|
808
832
|
.replace(/src=["']\.\/src\/main\.jsx["']/i, 'src="/src/main.jsx"')
|
|
809
833
|
.replace(/src=["']src\/main\.jsx["']/i, 'src="/src/main.jsx"');
|
|
810
834
|
|
|
@@ -821,13 +845,13 @@ coverage/
|
|
|
821
845
|
|
|
822
846
|
<!-- PWA -->
|
|
823
847
|
<meta name="theme-color" content="#3ee7ff" />
|
|
824
|
-
<link rel="manifest" href="
|
|
848
|
+
<link rel="manifest" href="${APP_BASE}manifest.json" />
|
|
825
849
|
|
|
826
850
|
<!-- Icons -->
|
|
827
|
-
<link rel="icon" type="image/png" href="
|
|
851
|
+
<link rel="icon" type="image/png" href="${APP_BASE}favicon.png" />
|
|
828
852
|
|
|
829
853
|
<!-- Title -->
|
|
830
|
-
<title>Nexa •
|
|
854
|
+
<title>Nexa • by Conscious Neurons</title>
|
|
831
855
|
|
|
832
856
|
</head>
|
|
833
857
|
<body>
|
|
@@ -843,25 +867,26 @@ coverage/
|
|
|
843
867
|
const manifestContent = `{
|
|
844
868
|
"name": "Nexa App",
|
|
845
869
|
"short_name": "Nexa",
|
|
846
|
-
"start_url": "
|
|
870
|
+
"start_url": "${APP_BASE}",
|
|
871
|
+
"scope": "${APP_BASE}",
|
|
847
872
|
"display": "standalone",
|
|
848
873
|
"background_color": "#07111f",
|
|
849
874
|
"theme_color": "#3ee7ff",
|
|
850
875
|
"icons": [
|
|
851
876
|
{
|
|
852
|
-
"src": "
|
|
877
|
+
"src": "${APP_BASE}icons/icon-192.png",
|
|
853
878
|
"sizes": "192x192",
|
|
854
879
|
"type": "image/png",
|
|
855
880
|
"purpose": "any maskable"
|
|
856
881
|
},
|
|
857
882
|
{
|
|
858
|
-
"src": "
|
|
883
|
+
"src": "${APP_BASE}icons/icon-512.png",
|
|
859
884
|
"sizes": "512x512",
|
|
860
885
|
"type": "image/png",
|
|
861
886
|
"purpose": "any maskable"
|
|
862
887
|
},
|
|
863
888
|
{
|
|
864
|
-
"src": "
|
|
889
|
+
"src": "${APP_BASE}nexa.svg",
|
|
865
890
|
"sizes": "any",
|
|
866
891
|
"type": "image/svg+xml",
|
|
867
892
|
"purpose": "any"
|
|
@@ -900,25 +925,37 @@ coverage/
|
|
|
900
925
|
);
|
|
901
926
|
|
|
902
927
|
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";
|
|
928
|
+
const mainJsxContent = `import React from "react";
|
|
910
929
|
import ReactDOM from "react-dom/client";
|
|
911
930
|
import App from "./App.jsx";
|
|
912
931
|
import "./index.css";
|
|
932
|
+
import { BrowserRouter } from "react-router-dom";
|
|
933
|
+
|
|
934
|
+
const APP_BASE = "${APP_BASE}";
|
|
935
|
+
|
|
936
|
+
if ("serviceWorker" in navigator) {
|
|
937
|
+
window.addEventListener("load", () => {
|
|
938
|
+
navigator.serviceWorker
|
|
939
|
+
.register(\`\${APP_BASE}sw.js\`)
|
|
940
|
+
.then((registration) => {
|
|
941
|
+
console.log("✅ Service Worker registered:", registration);
|
|
942
|
+
})
|
|
943
|
+
.catch((error) => {
|
|
944
|
+
console.error("❌ Service Worker registration failed:", error);
|
|
945
|
+
});
|
|
946
|
+
});
|
|
947
|
+
}
|
|
913
948
|
|
|
914
949
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
915
950
|
<React.StrictMode>
|
|
916
|
-
<
|
|
951
|
+
<BrowserRouter basename={APP_BASE === "/" ? undefined : APP_BASE}>
|
|
952
|
+
<App />
|
|
953
|
+
</BrowserRouter>
|
|
917
954
|
</React.StrictMode>
|
|
918
955
|
);
|
|
919
956
|
`;
|
|
920
|
-
|
|
921
|
-
|
|
957
|
+
|
|
958
|
+
writeFileSafe(mainJsxPath, mainJsxContent);
|
|
922
959
|
|
|
923
960
|
const appJsxPath = path.join(srcDir, "App.jsx");
|
|
924
961
|
if (!fs.existsSync(appJsxPath)) {
|
|
@@ -1084,8 +1121,7 @@ vite.on("close", (code) => {
|
|
|
1084
1121
|
}
|
|
1085
1122
|
|
|
1086
1123
|
const nexaConfigPath = path.join(projectDir, "nexa.config.js");
|
|
1087
|
-
|
|
1088
|
-
const nexaConfigContent = `/**
|
|
1124
|
+
const nexaConfigContent = `/**
|
|
1089
1125
|
* File: nexa.config.js
|
|
1090
1126
|
* Purpose: Vite configuration for Nexa-generated apps.
|
|
1091
1127
|
*/
|
|
@@ -1095,20 +1131,20 @@ import react from "@vitejs/plugin-react";
|
|
|
1095
1131
|
|
|
1096
1132
|
export default defineConfig({
|
|
1097
1133
|
root: ".",
|
|
1098
|
-
base: "
|
|
1134
|
+
base: "${APP_BASE}",
|
|
1099
1135
|
plugins: [react()],
|
|
1100
1136
|
server: {
|
|
1101
1137
|
port: 5725,
|
|
1102
1138
|
},
|
|
1103
1139
|
build: {
|
|
1104
1140
|
outDir: "dist",
|
|
1141
|
+
emptyOutDir: true,
|
|
1105
1142
|
},
|
|
1106
1143
|
clearScreen: false,
|
|
1107
1144
|
});
|
|
1108
1145
|
`;
|
|
1109
|
-
writeFileSafe(nexaConfigPath, nexaConfigContent);
|
|
1110
|
-
}
|
|
1111
1146
|
|
|
1147
|
+
writeFileSafe(nexaConfigPath, nexaConfigContent);
|
|
1112
1148
|
console.log(`\n${C.blue}📦 Installing dependencies...${C.reset}`);
|
|
1113
1149
|
let installSucceeded = false;
|
|
1114
1150
|
|
|
@@ -1146,11 +1182,28 @@ export default defineConfig({
|
|
|
1146
1182
|
console.log(
|
|
1147
1183
|
`\n${C.green}▶️ Starting app in ${projectDirName}...${C.reset}\n`,
|
|
1148
1184
|
);
|
|
1149
|
-
startGeneratedApp(projectDir, shouldOpen);
|
|
1185
|
+
startGeneratedApp(projectDir, shouldOpen, APP_BASE);
|
|
1150
1186
|
}
|
|
1151
1187
|
}
|
|
1152
1188
|
}
|
|
1153
1189
|
|
|
1190
|
+
function normalizeBase(input = "/") {
|
|
1191
|
+
let base = String(input).trim();
|
|
1192
|
+
|
|
1193
|
+
if (!base) return "/";
|
|
1194
|
+
|
|
1195
|
+
if (!base.startsWith("/")) base = `/${base}`;
|
|
1196
|
+
if (!base.endsWith("/")) base = `${base}/`;
|
|
1197
|
+
|
|
1198
|
+
return base.replace(/\/+/g, "/");
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function getFlagValue(argv, flagName) {
|
|
1202
|
+
const index = argv.indexOf(flagName);
|
|
1203
|
+
if (index === -1) return null;
|
|
1204
|
+
return argv[index + 1] ?? null;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1154
1207
|
/**
|
|
1155
1208
|
* Purpose: Parse command arguments and support:
|
|
1156
1209
|
* nexa new app my-app
|
|
@@ -1158,9 +1211,21 @@ export default defineConfig({
|
|
|
1158
1211
|
* nexa app my-app
|
|
1159
1212
|
*/
|
|
1160
1213
|
function parseArgs(argv) {
|
|
1161
|
-
const
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1214
|
+
const baseFlagValue = getFlagValue(argv, "--base");
|
|
1215
|
+
const base = normalizeBase(baseFlagValue || "/");
|
|
1216
|
+
|
|
1217
|
+
const filtered = [];
|
|
1218
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1219
|
+
if (argv[i] === "--base") {
|
|
1220
|
+
i++;
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
filtered.push(argv[i]);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const first = filtered[0];
|
|
1227
|
+
const second = filtered[1];
|
|
1228
|
+
const third = filtered[2];
|
|
1164
1229
|
|
|
1165
1230
|
if (!first) {
|
|
1166
1231
|
printUsage();
|
|
@@ -1181,6 +1246,7 @@ function parseArgs(argv) {
|
|
|
1181
1246
|
return {
|
|
1182
1247
|
shortcut: second,
|
|
1183
1248
|
name: third,
|
|
1249
|
+
base,
|
|
1184
1250
|
};
|
|
1185
1251
|
}
|
|
1186
1252
|
|
|
@@ -1192,6 +1258,7 @@ function parseArgs(argv) {
|
|
|
1192
1258
|
return {
|
|
1193
1259
|
shortcut: "app",
|
|
1194
1260
|
name: second,
|
|
1261
|
+
base,
|
|
1195
1262
|
};
|
|
1196
1263
|
}
|
|
1197
1264
|
|
|
@@ -1199,6 +1266,7 @@ function parseArgs(argv) {
|
|
|
1199
1266
|
return {
|
|
1200
1267
|
shortcut: first,
|
|
1201
1268
|
name: second,
|
|
1269
|
+
base,
|
|
1202
1270
|
};
|
|
1203
1271
|
}
|
|
1204
1272
|
|
|
@@ -1223,6 +1291,7 @@ function parseArgs(argv) {
|
|
|
1223
1291
|
return {
|
|
1224
1292
|
shortcut: "app",
|
|
1225
1293
|
name: first,
|
|
1294
|
+
base,
|
|
1226
1295
|
};
|
|
1227
1296
|
}
|
|
1228
1297
|
|
|
@@ -1230,7 +1299,7 @@ function parseArgs(argv) {
|
|
|
1230
1299
|
process.exit(1);
|
|
1231
1300
|
}
|
|
1232
1301
|
|
|
1233
|
-
const { shortcut, name } = parseArgs(args);
|
|
1302
|
+
const { shortcut, name, base } = parseArgs(args);
|
|
1234
1303
|
|
|
1235
1304
|
async function main() {
|
|
1236
1305
|
switch (shortcut) {
|
|
@@ -1266,7 +1335,7 @@ async function main() {
|
|
|
1266
1335
|
console.error(`${C.yellow}❌ Please provide an app name.${C.reset}`);
|
|
1267
1336
|
process.exit(1);
|
|
1268
1337
|
}
|
|
1269
|
-
await createApp(name);
|
|
1338
|
+
await createApp(name, base);
|
|
1270
1339
|
break;
|
|
1271
1340
|
|
|
1272
1341
|
default:
|