create-aziz-app 2.0.7 → 2.0.9
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/package.json +1 -1
- package/template/README.md +23 -6
- package/template/app.html +28 -24
- package/template/docs/AUTO-REGISTRATION.md +248 -0
- package/template/docs/DEV-SERVER.md +98 -0
- package/template/docs/TROUBLESHOOTING.md +282 -0
- package/template/modules/router.js +28 -5
- package/template/pages/About.jsx +17 -2
- package/template/pages/ApiDemo.jsx +1 -1
- package/template/pages/Contact.jsx +1 -3
- package/template/pages/Example.jsx +59 -0
- package/template/pages/Home.jsx +1 -2
- package/template/pages/test.jsx +1 -2
- package/template/scripts/dev-server.js +128 -24
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -72,8 +72,7 @@ Just create files in the appropriate folders - they're automatically imported!
|
|
|
72
72
|
// pages/Profile.jsx
|
|
73
73
|
/** @jsx h */
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// Auto-registers as "#/profile" - no registerPage() needed!
|
|
77
76
|
function ProfilePage() {
|
|
78
77
|
const [name, setName] = useState('');
|
|
79
78
|
|
|
@@ -93,8 +92,19 @@ function ProfilePage() {
|
|
|
93
92
|
|
|
94
93
|
**Navigate to:** `http://localhost:3000/#/profile`
|
|
95
94
|
|
|
95
|
+
✨ **That's it!** No `registerPage()` call needed. Just name your function `YourNamePage()` and it auto-registers!
|
|
96
|
+
|
|
96
97
|
## ✨ Features
|
|
97
98
|
|
|
99
|
+
### Auto-Registration ⚡
|
|
100
|
+
```javascript
|
|
101
|
+
// Just name your function with "Page" suffix
|
|
102
|
+
function AboutPage() {
|
|
103
|
+
return <div><h1>About</h1></div>;
|
|
104
|
+
}
|
|
105
|
+
// Automatically becomes route "#/about"
|
|
106
|
+
```
|
|
107
|
+
|
|
98
108
|
### State Management
|
|
99
109
|
```javascript
|
|
100
110
|
const [count, setCount] = useState(0);
|
|
@@ -103,8 +113,11 @@ const [name, setName] = useState('');
|
|
|
103
113
|
|
|
104
114
|
### Routing
|
|
105
115
|
```javascript
|
|
106
|
-
//
|
|
107
|
-
|
|
116
|
+
// Auto-registers based on function name
|
|
117
|
+
function HomePage() { } // → #/home
|
|
118
|
+
|
|
119
|
+
// Or manually register for custom names
|
|
120
|
+
registerPage("user-profile", UserProfilePage);
|
|
108
121
|
|
|
109
122
|
// Navigate
|
|
110
123
|
navigate("about");
|
|
@@ -138,8 +151,11 @@ The CLI will automatically set up your chosen framework!
|
|
|
138
151
|
## 📚 Documentation
|
|
139
152
|
|
|
140
153
|
- **[Getting Started](docs/GETTING-STARTED.md)** - Complete setup guide
|
|
154
|
+
- **[Auto-Registration](docs/AUTO-REGISTRATION.md)** - How pages auto-register
|
|
141
155
|
- **[API Guide](docs/API-GUIDE.md)** - How to create API routes
|
|
142
156
|
- **[Auto-Import](docs/AUTO-IMPORT.md)** - Auto-import system details
|
|
157
|
+
- **[Dev Server](docs/DEV-SERVER.md)** - Live reload and development
|
|
158
|
+
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Fix common issues
|
|
143
159
|
- **[Quick Start](docs/QUICK-START.md)** - Quick examples
|
|
144
160
|
|
|
145
161
|
## 🛠️ Commands
|
|
@@ -153,8 +169,9 @@ npm run build # Generate imports manually
|
|
|
153
169
|
|
|
154
170
|
### Pages
|
|
155
171
|
- Files in `pages/` become routes
|
|
156
|
-
-
|
|
157
|
-
-
|
|
172
|
+
- Name functions with "Page" suffix (e.g., `AboutPage`)
|
|
173
|
+
- Auto-registers as route (e.g., `#/about`)
|
|
174
|
+
- Optional: Use `registerPage("custom-name", Component)` for custom routes
|
|
158
175
|
|
|
159
176
|
### Components
|
|
160
177
|
- Reusable UI components
|
package/template/app.html
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
7
|
+
<meta http-equiv="Pragma" content="no-cache">
|
|
8
|
+
<meta http-equiv="Expires" content="0">
|
|
6
9
|
<title>My Aziz App</title>
|
|
7
10
|
|
|
8
11
|
<!-- Global Styles -->
|
|
@@ -22,30 +25,31 @@
|
|
|
22
25
|
<!-- Footer is always visible -->
|
|
23
26
|
<div id="footer"></div>
|
|
24
27
|
|
|
25
|
-
<!-- Load core modules -->
|
|
26
|
-
<script type="text/babel" src="modules/router.js"></script>
|
|
27
|
-
<script type="text/babel" src="modules/state.js"></script>
|
|
28
|
-
<script type="text/babel" src="modules/api.js"></script>
|
|
29
|
-
|
|
30
|
-
<!-- Load API routes -->
|
|
31
|
-
<script type="text/babel" src="api/hello.js"></script>
|
|
32
|
-
<script type="text/babel" src="api/users.js"></script>
|
|
33
|
-
|
|
34
|
-
<!-- Load shared components -->
|
|
35
|
-
<script type="text/babel" src="components/Counter.jsx"></script>
|
|
36
|
-
<script type="text/babel" src="components/Footer.jsx"></script>
|
|
37
|
-
<script type="text/babel" src="components/Navbar.jsx"></script>
|
|
38
|
-
|
|
39
|
-
<!-- Load pages -->
|
|
40
|
-
<script type="text/babel" src="pages/About.jsx"></script>
|
|
41
|
-
<script type="text/babel" src="pages/ApiDemo.jsx"></script>
|
|
42
|
-
<script type="text/babel" src="pages/Contact.jsx"></script>
|
|
43
|
-
<script type="text/babel" src="pages/
|
|
44
|
-
<script type="text/babel" src="pages/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
<!-- Load core modules -->
|
|
29
|
+
<script type="text/babel" src="modules/router.js"></script>
|
|
30
|
+
<script type="text/babel" src="modules/state.js"></script>
|
|
31
|
+
<script type="text/babel" src="modules/api.js"></script>
|
|
32
|
+
|
|
33
|
+
<!-- Load API routes -->
|
|
34
|
+
<script type="text/babel" src="api/hello.js"></script>
|
|
35
|
+
<script type="text/babel" src="api/users.js"></script>
|
|
36
|
+
|
|
37
|
+
<!-- Load shared components -->
|
|
38
|
+
<script type="text/babel" src="components/Counter.jsx"></script>
|
|
39
|
+
<script type="text/babel" src="components/Footer.jsx"></script>
|
|
40
|
+
<script type="text/babel" src="components/Navbar.jsx"></script>
|
|
41
|
+
|
|
42
|
+
<!-- Load pages -->
|
|
43
|
+
<script type="text/babel" src="pages/About.jsx"></script>
|
|
44
|
+
<script type="text/babel" src="pages/ApiDemo.jsx"></script>
|
|
45
|
+
<script type="text/babel" src="pages/Contact.jsx"></script>
|
|
46
|
+
<script type="text/babel" src="pages/Example.jsx"></script>
|
|
47
|
+
<script type="text/babel" src="pages/Home.jsx"></script>
|
|
48
|
+
<script type="text/babel" src="pages/test.jsx"></script>
|
|
49
|
+
|
|
50
|
+
<!-- Start the app -->
|
|
51
|
+
<script type="text/babel">
|
|
52
|
+
startApp();
|
|
49
53
|
</script>
|
|
50
54
|
|
|
51
55
|
</body>
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Auto-Registration Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Pages automatically register themselves based on their function name. No need to call `registerPage()` manually!
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
### ✨ Automatic Registration
|
|
10
|
+
|
|
11
|
+
Just name your page function with "Page" at the end:
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
/** @jsx h */
|
|
15
|
+
|
|
16
|
+
// That's it! Automatically becomes route "#/example"
|
|
17
|
+
function ExamplePage() {
|
|
18
|
+
return (
|
|
19
|
+
<div>
|
|
20
|
+
<h1>Example Page</h1>
|
|
21
|
+
<p>This page auto-registered!</p>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Function Name → Route Mapping
|
|
28
|
+
|
|
29
|
+
The system converts your function name to a route:
|
|
30
|
+
|
|
31
|
+
| Function Name | Auto Route | URL |
|
|
32
|
+
|--------------|------------|-----|
|
|
33
|
+
| `HomePage()` | `home` | `#/home` |
|
|
34
|
+
| `AboutPage()` | `about` | `#/about` |
|
|
35
|
+
| `ContactPage()` | `contact` | `#/contact` |
|
|
36
|
+
| `UserProfilePage()` | `userprofile` | `#/userprofile` |
|
|
37
|
+
| `MyAwesomePage()` | `myawesome` | `#/myawesome` |
|
|
38
|
+
|
|
39
|
+
**Rule:** Remove "Page" suffix and convert to lowercase.
|
|
40
|
+
|
|
41
|
+
## Manual Registration (Optional)
|
|
42
|
+
|
|
43
|
+
You can still manually register for custom route names:
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
/** @jsx h */
|
|
47
|
+
|
|
48
|
+
// Custom route name with dashes or different naming
|
|
49
|
+
registerPage("user-profile", UserProfilePage);
|
|
50
|
+
|
|
51
|
+
function UserProfilePage() {
|
|
52
|
+
return <div><h1>User Profile</h1></div>;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### When to Use Manual Registration
|
|
57
|
+
|
|
58
|
+
- **Custom route names** with dashes: `"api-demo"`, `"user-settings"`
|
|
59
|
+
- **Multiple routes** pointing to same component
|
|
60
|
+
- **Override auto-registration** with different name
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
### Example 1: Simple Auto Page
|
|
65
|
+
|
|
66
|
+
```jsx
|
|
67
|
+
/** @jsx h */
|
|
68
|
+
|
|
69
|
+
function ProductsPage() {
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<h1>Products</h1>
|
|
73
|
+
<p>Browse our products</p>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Access at: `#/products`
|
|
80
|
+
|
|
81
|
+
### Example 2: With State
|
|
82
|
+
|
|
83
|
+
```jsx
|
|
84
|
+
/** @jsx h */
|
|
85
|
+
|
|
86
|
+
function DashboardPage() {
|
|
87
|
+
const [count, setCount] = useState(0);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<h1>Dashboard</h1>
|
|
92
|
+
<p>Count: {count}</p>
|
|
93
|
+
<button onClick={() => setCount(count + 1)}>
|
|
94
|
+
Increment
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Access at: `#/dashboard`
|
|
102
|
+
|
|
103
|
+
### Example 3: Manual Registration
|
|
104
|
+
|
|
105
|
+
```jsx
|
|
106
|
+
/** @jsx h */
|
|
107
|
+
|
|
108
|
+
// Want route "#/user-settings" instead of "#/usersettings"
|
|
109
|
+
registerPage("user-settings", UserSettingsPage);
|
|
110
|
+
|
|
111
|
+
function UserSettingsPage() {
|
|
112
|
+
return (
|
|
113
|
+
<div>
|
|
114
|
+
<h1>User Settings</h1>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Access at: `#/user-settings`
|
|
121
|
+
|
|
122
|
+
## Creating a New Page
|
|
123
|
+
|
|
124
|
+
### Step 1: Create the file
|
|
125
|
+
|
|
126
|
+
Create `pages/MyNewPage.jsx`:
|
|
127
|
+
|
|
128
|
+
```jsx
|
|
129
|
+
/** @jsx h */
|
|
130
|
+
|
|
131
|
+
function MyNewPage() {
|
|
132
|
+
return (
|
|
133
|
+
<div>
|
|
134
|
+
<h1>My New Page</h1>
|
|
135
|
+
<div class="card">
|
|
136
|
+
<p>This is my new page!</p>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Step 2: That's it!
|
|
144
|
+
|
|
145
|
+
The dev server will:
|
|
146
|
+
1. Detect the new file
|
|
147
|
+
2. Auto-generate the import in `app.html`
|
|
148
|
+
3. Auto-register the route as `#/mynew`
|
|
149
|
+
4. Reload the browser
|
|
150
|
+
|
|
151
|
+
Navigate to `#/mynew` and your page appears!
|
|
152
|
+
|
|
153
|
+
## Debugging
|
|
154
|
+
|
|
155
|
+
### Check Auto-Registration
|
|
156
|
+
|
|
157
|
+
Open browser console when the app starts. You'll see:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
📄 Auto-registered: home → HomePage()
|
|
161
|
+
📄 Auto-registered: about → AboutPage()
|
|
162
|
+
📄 Auto-registered: contact → ContactPage()
|
|
163
|
+
📄 Auto-registered: mynew → MyNewPage()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Page Not Found?
|
|
167
|
+
|
|
168
|
+
1. **Check function name** - Must end with "Page"
|
|
169
|
+
2. **Check file is loaded** - Look in `app.html` for the script tag
|
|
170
|
+
3. **Check console** - Look for auto-registration message
|
|
171
|
+
4. **Try manual registration** - Use `registerPage("route", ComponentPage)`
|
|
172
|
+
|
|
173
|
+
## Best Practices
|
|
174
|
+
|
|
175
|
+
### ✅ Do This
|
|
176
|
+
|
|
177
|
+
```jsx
|
|
178
|
+
// Clear, descriptive names
|
|
179
|
+
function HomePage() { }
|
|
180
|
+
function AboutPage() { }
|
|
181
|
+
function ContactPage() { }
|
|
182
|
+
function UserProfilePage() { }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### ❌ Avoid This
|
|
186
|
+
|
|
187
|
+
```jsx
|
|
188
|
+
// Won't auto-register (missing "Page" suffix)
|
|
189
|
+
function Home() { }
|
|
190
|
+
function About() { }
|
|
191
|
+
|
|
192
|
+
// Will create weird routes
|
|
193
|
+
function Page() { } // route: "" (empty)
|
|
194
|
+
function MyPage() { } // route: "my" (too generic)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Migration Guide
|
|
198
|
+
|
|
199
|
+
### Old Way (Still Works)
|
|
200
|
+
|
|
201
|
+
```jsx
|
|
202
|
+
/** @jsx h */
|
|
203
|
+
|
|
204
|
+
registerPage("about", AboutPage);
|
|
205
|
+
|
|
206
|
+
function AboutPage() {
|
|
207
|
+
return <div><h1>About</h1></div>;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### New Way (Simpler)
|
|
212
|
+
|
|
213
|
+
```jsx
|
|
214
|
+
/** @jsx h */
|
|
215
|
+
|
|
216
|
+
function AboutPage() {
|
|
217
|
+
return <div><h1>About</h1></div>;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Both work! The new way is just less code.
|
|
222
|
+
|
|
223
|
+
## Advanced: Multiple Routes
|
|
224
|
+
|
|
225
|
+
Point multiple routes to the same component:
|
|
226
|
+
|
|
227
|
+
```jsx
|
|
228
|
+
/** @jsx h */
|
|
229
|
+
|
|
230
|
+
// Auto-registers as "dashboard"
|
|
231
|
+
function DashboardPage() {
|
|
232
|
+
return <div><h1>Dashboard</h1></div>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Also register as "home" and "index"
|
|
236
|
+
registerPage("home", DashboardPage);
|
|
237
|
+
registerPage("index", DashboardPage);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Now `#/dashboard`, `#/home`, and `#/index` all show the same page!
|
|
241
|
+
|
|
242
|
+
## Summary
|
|
243
|
+
|
|
244
|
+
- **Function name ends with "Page"** → Auto-registers
|
|
245
|
+
- **Route name** = function name without "Page", lowercase
|
|
246
|
+
- **Manual registration** still available for custom names
|
|
247
|
+
- **Dev server** handles everything automatically
|
|
248
|
+
- **Zero configuration** needed for basic pages
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Dev Server Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The dev server provides a smooth development experience with live reload, automatic import generation, and file watching.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### 🔄 Live Reload
|
|
10
|
+
- **Automatic browser refresh** when you save files
|
|
11
|
+
- **Visual indicator** in bottom-right corner shows connection status
|
|
12
|
+
- **Smart caching** - forces cache clear on reload to always show latest changes
|
|
13
|
+
- **CSS hot reload** - instant updates without regenerating imports
|
|
14
|
+
|
|
15
|
+
### 👀 File Watching
|
|
16
|
+
The dev server watches these directories:
|
|
17
|
+
- `api/` - API route files
|
|
18
|
+
- `components/` - React-like components
|
|
19
|
+
- `pages/` - Page components
|
|
20
|
+
- `modules/` - Core system modules
|
|
21
|
+
- `styles/` - CSS files
|
|
22
|
+
|
|
23
|
+
### 🚀 Auto Import Generation
|
|
24
|
+
- Automatically generates script tags in `app.html`
|
|
25
|
+
- Runs on startup and whenever JS/JSX files change
|
|
26
|
+
- No need to manually add imports
|
|
27
|
+
|
|
28
|
+
### 🎯 Smart Port Selection
|
|
29
|
+
- Defaults to port 3000
|
|
30
|
+
- Automatically finds next available port if 3000 is busy
|
|
31
|
+
- Shows the actual URL in console
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then open the URL shown in console (usually `http://localhost:3000/`)
|
|
40
|
+
|
|
41
|
+
## Visual Indicators
|
|
42
|
+
|
|
43
|
+
The live reload indicator in the bottom-right corner shows:
|
|
44
|
+
- 🔌 **Connecting...** - Initial connection
|
|
45
|
+
- ✅ **Live Reload Active** - Connected and watching (fades after 2s)
|
|
46
|
+
- 🔄 **Reloading...** - Changes detected, refreshing page
|
|
47
|
+
- ⚠️ **Reconnecting...** - Connection lost, attempting to reconnect
|
|
48
|
+
|
|
49
|
+
Hover over the indicator to make it fully visible.
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
1. **Server starts** and generates initial imports
|
|
54
|
+
2. **Browser connects** via Server-Sent Events (SSE)
|
|
55
|
+
3. **File watcher** detects changes in watched directories
|
|
56
|
+
4. **Debouncing** prevents multiple rapid reloads (300ms for CSS, 500ms for JS)
|
|
57
|
+
5. **Import regeneration** (for JS/JSX changes only)
|
|
58
|
+
6. **Browser notification** via SSE
|
|
59
|
+
7. **Page reload** with cache busting
|
|
60
|
+
|
|
61
|
+
## Cache Control
|
|
62
|
+
|
|
63
|
+
The dev server sends these headers to prevent caching:
|
|
64
|
+
```
|
|
65
|
+
Cache-Control: no-cache, no-store, must-revalidate
|
|
66
|
+
Pragma: no-cache
|
|
67
|
+
Expires: 0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This ensures you always see the latest changes without needing Ctrl+Shift+R.
|
|
71
|
+
|
|
72
|
+
## Troubleshooting
|
|
73
|
+
|
|
74
|
+
### Changes not showing?
|
|
75
|
+
1. Check the visual indicator - is it green?
|
|
76
|
+
2. Open browser console - look for "✅ Live reload connected"
|
|
77
|
+
3. Check terminal - does it show "🔄 Notifying X browser(s) to reload..."?
|
|
78
|
+
4. Try hard refresh once: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
|
|
79
|
+
|
|
80
|
+
### Port already in use?
|
|
81
|
+
The server will automatically try the next port (3001, 3002, etc.)
|
|
82
|
+
|
|
83
|
+
### Live reload disconnected?
|
|
84
|
+
The page will automatically try to reconnect after 2 seconds.
|
|
85
|
+
|
|
86
|
+
## Performance
|
|
87
|
+
|
|
88
|
+
- **Debouncing** prevents reload spam when saving multiple files
|
|
89
|
+
- **CSS-only changes** skip import regeneration for faster updates
|
|
90
|
+
- **SSE connection** is lightweight and efficient
|
|
91
|
+
- **Multiple browsers** can connect simultaneously
|
|
92
|
+
|
|
93
|
+
## Development Tips
|
|
94
|
+
|
|
95
|
+
1. Keep the terminal visible to see file change notifications
|
|
96
|
+
2. The visual indicator helps confirm live reload is working
|
|
97
|
+
3. CSS changes are faster than JS changes (no import regeneration)
|
|
98
|
+
4. Multiple browser tabs will all reload simultaneously
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
## Live Reload Issues
|
|
4
|
+
|
|
5
|
+
### Indicator Stuck on "Connecting..."
|
|
6
|
+
|
|
7
|
+
**Symptoms:**
|
|
8
|
+
- Green indicator shows "🔌 Connecting..." and never changes
|
|
9
|
+
- Changes don't show without hard refresh (Ctrl+Shift+R)
|
|
10
|
+
|
|
11
|
+
**Causes:**
|
|
12
|
+
- Browser cached old version of `app.html` without live reload script
|
|
13
|
+
- SSE connection blocked by browser extension or firewall
|
|
14
|
+
|
|
15
|
+
**Solutions:**
|
|
16
|
+
|
|
17
|
+
1. **Hard refresh once** to clear cache:
|
|
18
|
+
- Windows/Linux: `Ctrl + Shift + R`
|
|
19
|
+
- Mac: `Cmd + Shift + R`
|
|
20
|
+
|
|
21
|
+
2. **Check browser console** (F12):
|
|
22
|
+
- Look for: `"🔌 Connecting to live reload at http://localhost:3000/live-reload"`
|
|
23
|
+
- Look for: `"✅ Live reload connected"`
|
|
24
|
+
- If you see errors, note them
|
|
25
|
+
|
|
26
|
+
3. **Disable browser extensions** temporarily:
|
|
27
|
+
- Ad blockers can block SSE connections
|
|
28
|
+
- Try in incognito/private mode
|
|
29
|
+
|
|
30
|
+
4. **Check terminal output**:
|
|
31
|
+
- Should show: `"🔌 [time] Browser connected for live reload (1 total)"`
|
|
32
|
+
- If not showing, SSE connection failed
|
|
33
|
+
|
|
34
|
+
5. **Restart dev server**:
|
|
35
|
+
```bash
|
|
36
|
+
# Stop with Ctrl+C
|
|
37
|
+
npm run dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
6. **Clear browser cache completely**:
|
|
41
|
+
- Chrome: Settings → Privacy → Clear browsing data
|
|
42
|
+
- Firefox: Settings → Privacy → Clear Data
|
|
43
|
+
- Edge: Settings → Privacy → Clear browsing data
|
|
44
|
+
|
|
45
|
+
### Changes Not Showing
|
|
46
|
+
|
|
47
|
+
**Symptoms:**
|
|
48
|
+
- Indicator shows "✅ Live Reload Active"
|
|
49
|
+
- Page reloads but changes don't appear
|
|
50
|
+
|
|
51
|
+
**Solutions:**
|
|
52
|
+
|
|
53
|
+
1. **Check file was saved**:
|
|
54
|
+
- Verify file has no unsaved indicator in editor
|
|
55
|
+
- Check terminal shows: `"📝 Detected change: pages/Home.jsx"`
|
|
56
|
+
|
|
57
|
+
2. **Check imports were regenerated**:
|
|
58
|
+
- Terminal should show: `"🔄 Regenerating imports..."`
|
|
59
|
+
- Check `app.html` has your file listed
|
|
60
|
+
|
|
61
|
+
3. **Verify file location**:
|
|
62
|
+
- Files must be in: `api/`, `components/`, `pages/`, `modules/`, or `styles/`
|
|
63
|
+
- Files outside these folders won't trigger reload
|
|
64
|
+
|
|
65
|
+
4. **Check for JavaScript errors**:
|
|
66
|
+
- Open browser console (F12)
|
|
67
|
+
- Look for red error messages
|
|
68
|
+
- Fix any syntax errors in your code
|
|
69
|
+
|
|
70
|
+
5. **Manual import regeneration**:
|
|
71
|
+
```bash
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Indicator Shows "❌ Connection Error"
|
|
76
|
+
|
|
77
|
+
**Symptoms:**
|
|
78
|
+
- Red indicator with error message
|
|
79
|
+
- Console shows EventSource errors
|
|
80
|
+
|
|
81
|
+
**Solutions:**
|
|
82
|
+
|
|
83
|
+
1. **Check dev server is running**:
|
|
84
|
+
- Terminal should show: `"✅ Dev server running with live reload!"`
|
|
85
|
+
- If not, run: `npm run dev`
|
|
86
|
+
|
|
87
|
+
2. **Check port is correct**:
|
|
88
|
+
- Browser URL should match terminal output
|
|
89
|
+
- Example: `http://localhost:3000/`
|
|
90
|
+
|
|
91
|
+
3. **Firewall blocking connection**:
|
|
92
|
+
- Allow Node.js through firewall
|
|
93
|
+
- Try different port: `PORT=3001 npm run dev`
|
|
94
|
+
|
|
95
|
+
4. **Browser doesn't support EventSource**:
|
|
96
|
+
- Update to latest browser version
|
|
97
|
+
- Try different browser (Chrome, Firefox, Edge)
|
|
98
|
+
|
|
99
|
+
## Page Not Found (404)
|
|
100
|
+
|
|
101
|
+
**Symptoms:**
|
|
102
|
+
- Clicking link shows 404 page
|
|
103
|
+
- URL is correct but page doesn't load
|
|
104
|
+
|
|
105
|
+
**Solutions:**
|
|
106
|
+
|
|
107
|
+
1. **Check function name**:
|
|
108
|
+
```jsx
|
|
109
|
+
// ✅ Correct - ends with "Page"
|
|
110
|
+
function AboutPage() { }
|
|
111
|
+
|
|
112
|
+
// ❌ Wrong - missing "Page"
|
|
113
|
+
function About() { }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
2. **Check file is loaded**:
|
|
117
|
+
- Open `app.html`
|
|
118
|
+
- Look for: `<script type="text/babel" src="pages/YourPage.jsx"></script>`
|
|
119
|
+
- If missing, run: `npm run build`
|
|
120
|
+
|
|
121
|
+
3. **Check browser console**:
|
|
122
|
+
- Look for: `"📄 Auto-registered: about → AboutPage()"`
|
|
123
|
+
- If missing, function name is wrong
|
|
124
|
+
|
|
125
|
+
4. **Manual registration**:
|
|
126
|
+
```jsx
|
|
127
|
+
// Add this if auto-registration fails
|
|
128
|
+
registerPage("about", AboutPage);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## State Not Working
|
|
132
|
+
|
|
133
|
+
**Symptoms:**
|
|
134
|
+
- `useState` is undefined
|
|
135
|
+
- State doesn't update
|
|
136
|
+
|
|
137
|
+
**Solutions:**
|
|
138
|
+
|
|
139
|
+
1. **Check `/** @jsx h */` at top of file**:
|
|
140
|
+
```jsx
|
|
141
|
+
/** @jsx h */
|
|
142
|
+
|
|
143
|
+
function MyPage() {
|
|
144
|
+
const [count, setCount] = useState(0);
|
|
145
|
+
// ...
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
2. **Check state.js is loaded**:
|
|
150
|
+
- Open `app.html`
|
|
151
|
+
- Look for: `<script type="text/babel" src="modules/state.js"></script>`
|
|
152
|
+
|
|
153
|
+
3. **Check browser console for errors**:
|
|
154
|
+
- Look for: `"useState is not defined"`
|
|
155
|
+
- If found, state.js didn't load
|
|
156
|
+
|
|
157
|
+
## Styling Not Applied
|
|
158
|
+
|
|
159
|
+
**Symptoms:**
|
|
160
|
+
- Page has no styling
|
|
161
|
+
- Looks like plain HTML
|
|
162
|
+
|
|
163
|
+
**Solutions:**
|
|
164
|
+
|
|
165
|
+
1. **Check CSS file is linked**:
|
|
166
|
+
- Open `app.html`
|
|
167
|
+
- Look for: `<link rel="stylesheet" href="styles/globals.css">`
|
|
168
|
+
|
|
169
|
+
2. **Check CSS file exists**:
|
|
170
|
+
- Verify `styles/globals.css` exists
|
|
171
|
+
- Check file has content
|
|
172
|
+
|
|
173
|
+
3. **Check class names**:
|
|
174
|
+
```jsx
|
|
175
|
+
// ✅ Correct
|
|
176
|
+
<div class="card">
|
|
177
|
+
|
|
178
|
+
// ❌ Wrong (React syntax)
|
|
179
|
+
<div className="card">
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
4. **Hard refresh**:
|
|
183
|
+
- CSS might be cached
|
|
184
|
+
- Press: `Ctrl + Shift + R` (Windows) or `Cmd + Shift + R` (Mac)
|
|
185
|
+
|
|
186
|
+
## API Routes Not Working
|
|
187
|
+
|
|
188
|
+
**Symptoms:**
|
|
189
|
+
- `fetchApi()` returns 404
|
|
190
|
+
- API endpoint not found
|
|
191
|
+
|
|
192
|
+
**Solutions:**
|
|
193
|
+
|
|
194
|
+
1. **Check API file is loaded**:
|
|
195
|
+
- Open `app.html`
|
|
196
|
+
- Look for: `<script type="text/babel" src="api/yourfile.js"></script>`
|
|
197
|
+
|
|
198
|
+
2. **Check route registration**:
|
|
199
|
+
```jsx
|
|
200
|
+
// In api/users.js
|
|
201
|
+
registerApiRoute('users', async (req, res) => {
|
|
202
|
+
// handler
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
3. **Check URL format**:
|
|
207
|
+
```jsx
|
|
208
|
+
// ✅ Correct
|
|
209
|
+
await fetchApi('/api/users');
|
|
210
|
+
|
|
211
|
+
// ❌ Wrong
|
|
212
|
+
await fetchApi('api/users'); // missing leading /
|
|
213
|
+
await fetchApi('/users'); // missing /api/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
4. **Check browser console**:
|
|
217
|
+
- Look for network errors
|
|
218
|
+
- Check request URL is correct
|
|
219
|
+
|
|
220
|
+
## Port Already in Use
|
|
221
|
+
|
|
222
|
+
**Symptoms:**
|
|
223
|
+
- Error: `EADDRINUSE: address already in use :::3000`
|
|
224
|
+
|
|
225
|
+
**Solutions:**
|
|
226
|
+
|
|
227
|
+
1. **Server auto-finds next port**:
|
|
228
|
+
- Check terminal for actual port
|
|
229
|
+
- Example: `"Server running at http://localhost:3001/"`
|
|
230
|
+
|
|
231
|
+
2. **Kill process on port 3000**:
|
|
232
|
+
```bash
|
|
233
|
+
# Windows
|
|
234
|
+
netstat -ano | findstr :3000
|
|
235
|
+
taskkill /PID <PID> /F
|
|
236
|
+
|
|
237
|
+
# Mac/Linux
|
|
238
|
+
lsof -ti:3000 | xargs kill -9
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
3. **Use different port**:
|
|
242
|
+
```bash
|
|
243
|
+
PORT=3001 npm run dev
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## General Tips
|
|
247
|
+
|
|
248
|
+
### Before Asking for Help
|
|
249
|
+
|
|
250
|
+
1. **Check browser console** (F12) for errors
|
|
251
|
+
2. **Check terminal output** for server errors
|
|
252
|
+
3. **Try hard refresh** (Ctrl+Shift+R)
|
|
253
|
+
4. **Try incognito mode** to rule out extensions
|
|
254
|
+
5. **Restart dev server** (Ctrl+C, then `npm run dev`)
|
|
255
|
+
|
|
256
|
+
### Debug Checklist
|
|
257
|
+
|
|
258
|
+
- [ ] Dev server is running
|
|
259
|
+
- [ ] Browser URL matches server URL
|
|
260
|
+
- [ ] File is saved
|
|
261
|
+
- [ ] File is in correct folder
|
|
262
|
+
- [ ] `/** @jsx h */` at top of JSX files
|
|
263
|
+
- [ ] Function name ends with "Page"
|
|
264
|
+
- [ ] No JavaScript errors in console
|
|
265
|
+
- [ ] Live reload indicator is green
|
|
266
|
+
|
|
267
|
+
### Still Having Issues?
|
|
268
|
+
|
|
269
|
+
1. **Check documentation**:
|
|
270
|
+
- [Getting Started](GETTING-STARTED.md)
|
|
271
|
+
- [Auto-Registration](AUTO-REGISTRATION.md)
|
|
272
|
+
- [Dev Server](DEV-SERVER.md)
|
|
273
|
+
|
|
274
|
+
2. **Create minimal reproduction**:
|
|
275
|
+
- Create new app: `npx create-aziz-app test-app`
|
|
276
|
+
- Test if issue persists in fresh app
|
|
277
|
+
|
|
278
|
+
3. **Report issue** with:
|
|
279
|
+
- Browser console output
|
|
280
|
+
- Terminal output
|
|
281
|
+
- Code that's not working
|
|
282
|
+
- Steps to reproduce
|
|
@@ -56,17 +56,35 @@ function h(tag, props, ...children) {
|
|
|
56
56
|
// "#/contact" → Contact page
|
|
57
57
|
//
|
|
58
58
|
// To add a new page:
|
|
59
|
-
// 1. Create pages/YourPage.jsx
|
|
60
|
-
// 2.
|
|
61
|
-
// 3.
|
|
59
|
+
// 1. Create pages/YourPage.jsx with a function like AboutPage()
|
|
60
|
+
// 2. The page will automatically be registered!
|
|
61
|
+
// 3. Optional: Call registerPage("about", AboutPage) for custom route names
|
|
62
62
|
// ============================================================
|
|
63
63
|
const routes = {};
|
|
64
64
|
|
|
65
65
|
// registerPage("about", AboutPage) — called by each page file
|
|
66
|
+
// Now optional! Pages auto-register based on function name
|
|
66
67
|
function registerPage(path, component) {
|
|
67
68
|
routes[path] = component;
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
// Auto-register pages from global scope
|
|
72
|
+
// Looks for functions ending with "Page" (e.g., AboutPage, HomePage)
|
|
73
|
+
function autoRegisterPages() {
|
|
74
|
+
for (let key in window) {
|
|
75
|
+
if (key.endsWith('Page') && typeof window[key] === 'function') {
|
|
76
|
+
// Convert "AboutPage" → "about", "HomePage" → "home"
|
|
77
|
+
const routeName = key.replace('Page', '').toLowerCase();
|
|
78
|
+
|
|
79
|
+
// Only register if not already registered
|
|
80
|
+
if (!routes[routeName]) {
|
|
81
|
+
routes[routeName] = window[key];
|
|
82
|
+
console.log(`📄 Auto-registered: ${routeName} → ${key}()`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
function getCurrentPath() {
|
|
71
89
|
// Remove the "#/" prefix → "about", "contact", or "" for home
|
|
72
90
|
return window.location.hash.replace("#/", "") || "home";
|
|
@@ -162,8 +180,10 @@ function NotFoundPage() {
|
|
|
162
180
|
return (
|
|
163
181
|
<div class="not-found">
|
|
164
182
|
<h1>404</h1>
|
|
165
|
-
<p>
|
|
166
|
-
<button class="btn btn-primary" onClick={() => navigate("home")}>
|
|
183
|
+
<p>Oops! The page you're looking for doesn't exist.</p>
|
|
184
|
+
<button class="btn btn-primary" onClick={() => navigate("home")}>
|
|
185
|
+
Go Home
|
|
186
|
+
</button>
|
|
167
187
|
</div>
|
|
168
188
|
);
|
|
169
189
|
}
|
|
@@ -174,6 +194,9 @@ function NotFoundPage() {
|
|
|
174
194
|
// Called once after all scripts are loaded
|
|
175
195
|
// ============================================================
|
|
176
196
|
function startApp() {
|
|
197
|
+
// Auto-register any pages that weren't manually registered
|
|
198
|
+
autoRegisterPages();
|
|
199
|
+
|
|
177
200
|
renderShell();
|
|
178
201
|
renderPage();
|
|
179
202
|
|
package/template/pages/About.jsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** @jsx h */
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
registerPage("about", AboutPage);
|
|
3
|
+
// Optional: You can still manually register if you want a custom route name
|
|
4
|
+
// registerPage("about", AboutPage);
|
|
5
5
|
|
|
6
|
+
// Or just define the function and it auto-registers as "about"
|
|
6
7
|
function AboutPage() {
|
|
7
8
|
return (
|
|
8
9
|
<div>
|
|
@@ -28,6 +29,20 @@ function AboutPage() {
|
|
|
28
29
|
Go to Contact →
|
|
29
30
|
</button>
|
|
30
31
|
</div>
|
|
32
|
+
|
|
33
|
+
<div class="card">
|
|
34
|
+
<h2>✨ Auto-Registration</h2>
|
|
35
|
+
<p>
|
|
36
|
+
This page works without calling <code>registerPage()</code>!
|
|
37
|
+
Just name your function <code>YourNamePage()</code> and it automatically
|
|
38
|
+
becomes a route.
|
|
39
|
+
</p>
|
|
40
|
+
<ul style={{ lineHeight: '1.8', marginLeft: '20px' }}>
|
|
41
|
+
<li><code>HomePage()</code> → <code>#/home</code></li>
|
|
42
|
+
<li><code>AboutPage()</code> → <code>#/about</code></li>
|
|
43
|
+
<li><code>ContactPage()</code> → <code>#/contact</code></li>
|
|
44
|
+
</ul>
|
|
45
|
+
</div>
|
|
31
46
|
</div>
|
|
32
47
|
);
|
|
33
48
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** @jsx h */
|
|
2
|
+
|
|
3
|
+
// That's it! No registerPage() needed.
|
|
4
|
+
// This automatically becomes route "#/example"
|
|
5
|
+
function ExamplePage() {
|
|
6
|
+
const [message, setMessage] = useState('Hello from Example Page! 🎉');
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div>
|
|
10
|
+
<h1>Example Page</h1>
|
|
11
|
+
|
|
12
|
+
<div class="card">
|
|
13
|
+
<h2>✨ Auto-Registration Demo</h2>
|
|
14
|
+
<p>
|
|
15
|
+
This page was created without calling <code>registerPage()</code>.
|
|
16
|
+
Just define a function ending with "Page" and it works!
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<div style={{
|
|
20
|
+
background: '#f0f0f0',
|
|
21
|
+
padding: '16px',
|
|
22
|
+
borderRadius: '8px',
|
|
23
|
+
fontFamily: 'monospace',
|
|
24
|
+
fontSize: '14px'
|
|
25
|
+
}}>
|
|
26
|
+
<strong>Function name:</strong> ExamplePage()<br/>
|
|
27
|
+
<strong>Auto route:</strong> #/example<br/>
|
|
28
|
+
<strong>URL:</strong> {window.location.href}
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="card">
|
|
33
|
+
<h2>State Management Works Too</h2>
|
|
34
|
+
<p>{message}</p>
|
|
35
|
+
<button
|
|
36
|
+
class="btn btn-primary"
|
|
37
|
+
onClick={() => setMessage('State updated! ✅')}
|
|
38
|
+
>
|
|
39
|
+
Update Message
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="card">
|
|
44
|
+
<h2>Navigation</h2>
|
|
45
|
+
<div style={{ display: 'flex', gap: '12px' }}>
|
|
46
|
+
<button class="btn btn-primary" onClick={() => navigate("home")}>
|
|
47
|
+
Home
|
|
48
|
+
</button>
|
|
49
|
+
<button class="btn btn-primary" onClick={() => navigate("about")}>
|
|
50
|
+
About
|
|
51
|
+
</button>
|
|
52
|
+
<button class="btn btn-primary" onClick={() => navigate("test")}>
|
|
53
|
+
Test
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
package/template/pages/Home.jsx
CHANGED
package/template/pages/test.jsx
CHANGED
|
@@ -26,57 +26,136 @@ function generateImports() {
|
|
|
26
26
|
|
|
27
27
|
// Notify all clients to reload
|
|
28
28
|
function notifyClients() {
|
|
29
|
-
|
|
29
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
30
|
+
console.log(`🔄 [${timestamp}] Notifying ${clients.length} browser(s) to reload...`);
|
|
30
31
|
clients.forEach(client => {
|
|
31
32
|
client.write('data: reload\n\n');
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
// Live reload script to inject
|
|
36
|
-
|
|
37
|
+
function getLiveReloadScript() {
|
|
38
|
+
return `
|
|
37
39
|
<script>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
(function() {
|
|
41
|
+
// Create visual indicator
|
|
42
|
+
const indicator = document.createElement('div');
|
|
43
|
+
indicator.id = 'live-reload-indicator';
|
|
44
|
+
indicator.style.cssText = 'position: fixed; bottom: 20px; right: 20px; background: #4caf50; color: white; padding: 8px 16px; border-radius: 20px; font-size: 12px; font-family: monospace; z-index: 9999; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.3s;';
|
|
45
|
+
indicator.textContent = '🔌 Connecting...';
|
|
46
|
+
document.body.appendChild(indicator);
|
|
47
|
+
|
|
48
|
+
// Live reload client
|
|
49
|
+
console.log('🔌 Connecting to live reload at ' + window.location.origin + '/live-reload');
|
|
50
|
+
|
|
51
|
+
let evtSource;
|
|
52
|
+
let reconnectAttempts = 0;
|
|
53
|
+
const maxReconnectAttempts = 5;
|
|
54
|
+
|
|
55
|
+
function connect() {
|
|
56
|
+
try {
|
|
57
|
+
evtSource = new EventSource('/live-reload');
|
|
58
|
+
|
|
59
|
+
evtSource.onopen = function() {
|
|
60
|
+
console.log('✅ Live reload connected');
|
|
61
|
+
reconnectAttempts = 0;
|
|
62
|
+
indicator.style.background = '#4caf50';
|
|
63
|
+
indicator.textContent = '✅ Live Reload Active';
|
|
64
|
+
setTimeout(function() {
|
|
65
|
+
indicator.style.opacity = '0.3';
|
|
66
|
+
}, 2000);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
evtSource.onmessage = function(event) {
|
|
70
|
+
if (event.data === 'reload') {
|
|
71
|
+
console.log('🔄 Changes detected, reloading...');
|
|
72
|
+
indicator.style.opacity = '1';
|
|
73
|
+
indicator.style.background = '#2196f3';
|
|
74
|
+
indicator.textContent = '🔄 Reloading...';
|
|
75
|
+
setTimeout(function() {
|
|
76
|
+
// Add timestamp to force cache bust
|
|
77
|
+
var url = window.location.href.split('?')[0];
|
|
78
|
+
window.location.href = url + '?t=' + Date.now();
|
|
79
|
+
}, 100);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
evtSource.onerror = function(err) {
|
|
84
|
+
console.log('⚠️ Live reload connection error');
|
|
85
|
+
indicator.style.background = '#ff9800';
|
|
86
|
+
indicator.style.opacity = '1';
|
|
87
|
+
indicator.textContent = '⚠️ Connection Lost';
|
|
88
|
+
|
|
89
|
+
if (evtSource) {
|
|
90
|
+
evtSource.close();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
94
|
+
reconnectAttempts++;
|
|
95
|
+
console.log('Reconnecting... attempt ' + reconnectAttempts);
|
|
96
|
+
setTimeout(connect, 2000);
|
|
97
|
+
} else {
|
|
98
|
+
indicator.textContent = '❌ Reload Failed';
|
|
99
|
+
console.log('Max reconnection attempts reached. Please refresh manually.');
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.error('Failed to create EventSource:', e);
|
|
104
|
+
indicator.style.background = '#f44336';
|
|
105
|
+
indicator.textContent = '❌ Connection Error';
|
|
106
|
+
}
|
|
44
107
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
108
|
+
|
|
109
|
+
connect();
|
|
110
|
+
|
|
111
|
+
// Show indicator on hover
|
|
112
|
+
indicator.addEventListener('mouseenter', function() {
|
|
113
|
+
indicator.style.opacity = '1';
|
|
114
|
+
});
|
|
115
|
+
indicator.addEventListener('mouseleave', function() {
|
|
116
|
+
if (indicator.textContent.includes('Active')) {
|
|
117
|
+
indicator.style.opacity = '0.3';
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
})();
|
|
50
121
|
</script>
|
|
51
122
|
`;
|
|
123
|
+
}
|
|
52
124
|
|
|
53
125
|
// Create HTTP server
|
|
54
126
|
function createServer(port) {
|
|
55
127
|
return http.createServer((req, res) => {
|
|
56
128
|
// Handle live reload SSE endpoint
|
|
57
|
-
if (req.url === '/live-reload') {
|
|
129
|
+
if (req.url === '/live-reload' || req.url.startsWith('/live-reload?')) {
|
|
58
130
|
res.writeHead(200, {
|
|
59
131
|
'Content-Type': 'text/event-stream',
|
|
60
|
-
'Cache-Control': 'no-cache',
|
|
132
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
61
133
|
'Connection': 'keep-alive',
|
|
62
|
-
'Access-Control-Allow-Origin': '*'
|
|
134
|
+
'Access-Control-Allow-Origin': '*',
|
|
135
|
+
'X-Accel-Buffering': 'no'
|
|
63
136
|
});
|
|
64
137
|
|
|
138
|
+
// Send initial connection message
|
|
139
|
+
res.write(':ok\n\n');
|
|
140
|
+
|
|
65
141
|
clients.push(res);
|
|
66
|
-
|
|
142
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
143
|
+
console.log(`🔌 [${timestamp}] Browser connected for live reload (${clients.length} total)`);
|
|
67
144
|
|
|
68
145
|
req.on('close', () => {
|
|
69
146
|
const index = clients.indexOf(res);
|
|
70
147
|
if (index !== -1) {
|
|
71
148
|
clients.splice(index, 1);
|
|
72
149
|
}
|
|
73
|
-
|
|
150
|
+
const closeTime = new Date().toLocaleTimeString();
|
|
151
|
+
console.log(`🔌 [${closeTime}] Browser disconnected (${clients.length} remaining)`);
|
|
74
152
|
});
|
|
75
153
|
|
|
76
154
|
return;
|
|
77
155
|
}
|
|
78
156
|
|
|
79
|
-
|
|
157
|
+
// Strip query parameters for file serving (cache busting)
|
|
158
|
+
let filePath = '.' + req.url.split('?')[0];
|
|
80
159
|
if (filePath === './') {
|
|
81
160
|
filePath = './app.html';
|
|
82
161
|
}
|
|
@@ -115,10 +194,16 @@ function createServer(port) {
|
|
|
115
194
|
} else {
|
|
116
195
|
// Inject live reload script into HTML files
|
|
117
196
|
if (extname === '.html') {
|
|
118
|
-
content = content.toString().replace('</body>',
|
|
197
|
+
content = content.toString().replace('</body>', getLiveReloadScript() + '</body>');
|
|
119
198
|
}
|
|
120
199
|
|
|
121
|
-
|
|
200
|
+
// Add cache control headers to prevent caching during development
|
|
201
|
+
res.writeHead(200, {
|
|
202
|
+
'Content-Type': contentType,
|
|
203
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
204
|
+
'Pragma': 'no-cache',
|
|
205
|
+
'Expires': '0'
|
|
206
|
+
});
|
|
122
207
|
res.end(content, 'utf-8');
|
|
123
208
|
}
|
|
124
209
|
});
|
|
@@ -143,16 +228,35 @@ function startServer(port) {
|
|
|
143
228
|
}
|
|
144
229
|
|
|
145
230
|
// Watch directories
|
|
146
|
-
const watchDirs = ['api', 'components', 'pages', 'modules'];
|
|
231
|
+
const watchDirs = ['api', 'components', 'pages', 'modules', 'styles'];
|
|
232
|
+
|
|
233
|
+
// Debounce to prevent multiple rapid reloads
|
|
234
|
+
let reloadTimeout = null;
|
|
147
235
|
|
|
148
236
|
console.log('👀 Watching for changes in:');
|
|
149
237
|
watchDirs.forEach(dir => {
|
|
150
238
|
if (fs.existsSync(dir)) {
|
|
151
239
|
console.log(` - ${dir}/`);
|
|
152
240
|
fs.watch(dir, { recursive: false }, (eventType, filename) => {
|
|
153
|
-
if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx'))) {
|
|
241
|
+
if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx') || filename.endsWith('.css'))) {
|
|
154
242
|
console.log(`\n📝 Detected change: ${dir}/${filename}`);
|
|
155
|
-
|
|
243
|
+
|
|
244
|
+
// Clear existing timeout
|
|
245
|
+
if (reloadTimeout) {
|
|
246
|
+
clearTimeout(reloadTimeout);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// For CSS changes, just reload without regenerating imports
|
|
250
|
+
if (filename.endsWith('.css')) {
|
|
251
|
+
reloadTimeout = setTimeout(() => {
|
|
252
|
+
notifyClients();
|
|
253
|
+
}, 300);
|
|
254
|
+
} else {
|
|
255
|
+
// For JS/JSX changes, regenerate imports
|
|
256
|
+
reloadTimeout = setTimeout(() => {
|
|
257
|
+
generateImports();
|
|
258
|
+
}, 500);
|
|
259
|
+
}
|
|
156
260
|
}
|
|
157
261
|
});
|
|
158
262
|
}
|