create-aziz-app 2.0.10 → 2.0.11

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.
@@ -217,6 +217,18 @@ function setupCSSFramework(targetDir, framework) {
217
217
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">`
218
218
  );
219
219
 
220
+ // Ensure live reload script is present
221
+ if (!html.includes('live-reload-client.js')) {
222
+ html = html.replace(
223
+ '<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
224
+ `<!-- Live Reload (only active in development) -->
225
+ <script src="scripts/live-reload-client.js"></script>
226
+
227
+ <!-- Babel compiles JSX in the browser -->
228
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
229
+ );
230
+ }
231
+
220
232
  // Add Bootstrap JS before closing body tag
221
233
  const bootstrapJS = `
222
234
  <!-- Bootstrap 5 JS -->
@@ -249,8 +261,32 @@ function setupCSSFramework(targetDir, framework) {
249
261
  </script>`
250
262
  );
251
263
 
264
+ // Ensure live reload script is present
265
+ if (!html.includes('live-reload-client.js')) {
266
+ html = html.replace(
267
+ '<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
268
+ `<!-- Live Reload (only active in development) -->
269
+ <script src="scripts/live-reload-client.js"></script>
270
+
271
+ <!-- Babel compiles JSX in the browser -->
272
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
273
+ );
274
+ }
275
+
252
276
  // Update components
253
277
  updateComponentsForTailwind(targetDir);
278
+ } else {
279
+ // For 'none', ensure live reload script is present
280
+ if (!html.includes('live-reload-client.js')) {
281
+ html = html.replace(
282
+ '<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
283
+ `<!-- Live Reload (only active in development) -->
284
+ <script src="scripts/live-reload-client.js"></script>
285
+
286
+ <!-- Babel compiles JSX in the browser -->
287
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
288
+ );
289
+ }
254
290
  }
255
291
  // If 'none', keep globals.css as is
256
292
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-aziz-app",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "description": "A React-like framework with state management, routing, and API routes - Create apps with auto-import, live reload, and Next.js-like features",
5
5
  "keywords": [
6
6
  "react",
package/template/app.html CHANGED
@@ -11,6 +11,9 @@
11
11
  <!-- Global Styles -->
12
12
  <link rel="stylesheet" href="styles/globals.css">
13
13
 
14
+ <!-- Live Reload (only active in development) -->
15
+ <script src="scripts/live-reload-client.js"></script>
16
+
14
17
  <!-- Babel compiles JSX in the browser -->
15
18
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
16
19
  </head>
@@ -2,15 +2,16 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The dev server provides a smooth development experience with live reload, automatic import generation, and file watching.
5
+ The dev server provides a smooth development experience with live reload using WebSocket, automatic import generation, and file watching.
6
6
 
7
7
  ## Features
8
8
 
9
- ### 🔄 Live Reload
9
+ ### 🔄 Live Reload (WebSocket-based)
10
10
  - **Automatic browser refresh** when you save files
11
11
  - **Visual indicator** in bottom-right corner shows connection status
12
- - **Smart caching** - forces cache clear on reload to always show latest changes
12
+ - **WebSocket connection** - reliable, fast, no caching issues
13
13
  - **CSS hot reload** - instant updates without regenerating imports
14
+ - **Always loaded** - script is part of your HTML, not injected
14
15
 
15
16
  ### 👀 File Watching
16
17
  The dev server watches these directories:
@@ -41,22 +42,36 @@ Then open the URL shown in console (usually `http://localhost:3000/`)
41
42
  ## Visual Indicators
42
43
 
43
44
  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
45
+ - 🔌 **Connecting...** - Initial connection (gray/green)
46
+ - ✅ **Live Reload** - Connected and watching (green, fades to 30%)
47
+ - 🔄 **Reloading...** - Changes detected, refreshing page (blue)
48
+ - ⚠️ **Disconnected** - Connection lost, attempting to reconnect (orange)
49
+ - ❌ **Offline** - Max reconnection attempts reached (red)
50
+
51
+ **Click the indicator** to manually reload the page.
48
52
 
49
53
  Hover over the indicator to make it fully visible.
50
54
 
51
55
  ## How It Works
52
56
 
53
57
  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
58
+ 2. **Browser loads** `live-reload-client.js` (always in HTML)
59
+ 3. **WebSocket connects** to dev server
60
+ 4. **File watcher** detects changes in watched directories
61
+ 5. **Debouncing** prevents multiple rapid reloads (300ms for CSS, 500ms for JS)
62
+ 6. **Import regeneration** (for JS/JSX changes only)
63
+ 7. **WebSocket message** sent to all connected browsers
64
+ 8. **Page reload** happens immediately
65
+
66
+ ## Why WebSocket?
67
+
68
+ Previous versions used Server-Sent Events (SSE) with runtime injection, which had caching issues. The new approach:
69
+
70
+ - ✅ **Always loaded** - Script is in HTML, not injected at runtime
71
+ - ✅ **No caching issues** - Browser always has the script
72
+ - ✅ **Bidirectional** - WebSocket allows two-way communication
73
+ - ✅ **Reliable** - Built-in reconnection logic
74
+ - ✅ **Fast** - Instant notifications, no polling
60
75
 
61
76
  ## Cache Control
62
77
 
@@ -67,28 +82,52 @@ Pragma: no-cache
67
82
  Expires: 0
68
83
  ```
69
84
 
70
- This ensures you always see the latest changes without needing Ctrl+Shift+R.
85
+ Plus, `app.html` has meta tags:
86
+ ```html
87
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
88
+ <meta http-equiv="Pragma" content="no-cache">
89
+ <meta http-equiv="Expires" content="0">
90
+ ```
71
91
 
72
92
  ## Troubleshooting
73
93
 
94
+ ### Indicator stuck on "Connecting..."?
95
+
96
+ 1. **Check dev server is running** - Terminal should show "✅ Dev server running"
97
+ 2. **Check browser console** (F12) - Look for WebSocket errors
98
+ 3. **Check port** - Browser URL should match server URL
99
+ 4. **Firewall** - Allow Node.js through firewall
100
+ 5. **Try different browser** - Rule out browser-specific issues
101
+
74
102
  ### 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)
103
+
104
+ 1. **Check indicator is green** - "✅ Live Reload"
105
+ 2. **Check terminal** - Should show "📝 Detected change: ..."
106
+ 3. **Check file was saved** - No unsaved indicator in editor
107
+ 4. **Check file location** - Must be in watched directories
108
+ 5. **Check for errors** - Browser console (F12) for JavaScript errors
109
+
110
+ ### WebSocket connection failed?
111
+
112
+ 1. **Port mismatch** - Check browser URL matches server port
113
+ 2. **Proxy/VPN** - May block WebSocket connections
114
+ 3. **Browser extension** - Ad blockers can interfere
115
+ 4. **Try incognito mode** - Rule out extensions
79
116
 
80
117
  ### Port already in use?
81
- The server will automatically try the next port (3001, 3002, etc.)
82
118
 
83
- ### Live reload disconnected?
84
- The page will automatically try to reconnect after 2 seconds.
119
+ The server automatically tries the next port. Check terminal for actual port:
120
+ ```
121
+ 🌐 Server running at http://localhost:3001/
122
+ ```
85
123
 
86
124
  ## Performance
87
125
 
88
126
  - **Debouncing** prevents reload spam when saving multiple files
89
127
  - **CSS-only changes** skip import regeneration for faster updates
90
- - **SSE connection** is lightweight and efficient
128
+ - **WebSocket** is lightweight and efficient
91
129
  - **Multiple browsers** can connect simultaneously
130
+ - **Automatic reconnection** if connection drops
92
131
 
93
132
  ## Development Tips
94
133
 
@@ -96,3 +135,50 @@ The page will automatically try to reconnect after 2 seconds.
96
135
  2. The visual indicator helps confirm live reload is working
97
136
  3. CSS changes are faster than JS changes (no import regeneration)
98
137
  4. Multiple browser tabs will all reload simultaneously
138
+ 5. Click the indicator to manually reload if needed
139
+
140
+ ## Production
141
+
142
+ The live reload script only runs in development (localhost). In production:
143
+ - Script detects non-localhost hostname
144
+ - Exits immediately without connecting
145
+ - No performance impact
146
+ - No visual indicator
147
+
148
+ ## Technical Details
149
+
150
+ ### WebSocket Protocol
151
+
152
+ The dev server implements a simple WebSocket server without external dependencies:
153
+ - Uses Node.js `http` module's `upgrade` event
154
+ - Performs WebSocket handshake manually
155
+ - Sends text frames to connected clients
156
+ - Handles connection cleanup automatically
157
+
158
+ ### Message Format
159
+
160
+ Server sends a single message type:
161
+ - `"reload"` - Triggers browser reload
162
+
163
+ ### Reconnection Logic
164
+
165
+ Client attempts to reconnect:
166
+ - Max 10 attempts
167
+ - 2-second delay between attempts
168
+ - Exponential backoff could be added if needed
169
+
170
+ ## Comparison with Other Tools
171
+
172
+ | Feature | Aziz App | Live Server | Vite |
173
+ |---------|----------|-------------|------|
174
+ | Setup | Zero config | Zero config | Config file |
175
+ | Speed | Fast | Fast | Very fast |
176
+ | WebSocket | ✅ | ✅ | ✅ |
177
+ | Auto-import | ✅ | ❌ | ✅ |
178
+ | Visual indicator | ✅ | ❌ | ❌ |
179
+ | Dependencies | None | None | Many |
180
+
181
+ ## Summary
182
+
183
+ The dev server provides a reliable, fast live reload experience using WebSocket. The script is always loaded (not injected), eliminating caching issues. File watching, debouncing, and automatic reconnection make development smooth and efficient.
184
+
@@ -0,0 +1,362 @@
1
+ # How It Works
2
+
3
+ ## System Architecture
4
+
5
+ Aziz App has three main systems working together:
6
+
7
+ 1. **Auto-Import System** - Automatically generates script tags
8
+ 2. **Live Reload System** - Automatically refreshes browser on changes
9
+ 3. **Routing System** - File-based routing with auto-registration
10
+
11
+ ## 1. Auto-Import System
12
+
13
+ ### Files Involved:
14
+ - `scripts/generate-imports.js` - Scans folders and generates script tags
15
+ - `app.html` - Contains the generated script tags
16
+ - `app.config.js` - Configuration (auto-generated)
17
+
18
+ ### How It Works:
19
+
20
+ ```
21
+ 1. Dev server starts
22
+ 2. Runs generate-imports.js
23
+ 3. Scans: api/, components/, pages/
24
+ 4. Generates <script> tags for each file
25
+ 5. Updates app.html (body section only)
26
+ 6. Browser loads all scripts
27
+ ```
28
+
29
+ ### What Gets Generated:
30
+
31
+ ```html
32
+ <!-- Load core modules -->
33
+ <script type="text/babel" src="modules/router.js"></script>
34
+ <script type="text/babel" src="modules/state.js"></script>
35
+ <script type="text/babel" src="modules/api.js"></script>
36
+
37
+ <!-- Load API routes -->
38
+ <script type="text/babel" src="api/hello.js"></script>
39
+
40
+ <!-- Load shared components -->
41
+ <script type="text/babel" src="components/Navbar.jsx"></script>
42
+
43
+ <!-- Load pages -->
44
+ <script type="text/babel" src="pages/Home.jsx"></script>
45
+
46
+ <!-- Start the app -->
47
+ <script type="text/babel">
48
+ startApp();
49
+ </script>
50
+ ```
51
+
52
+ ### When It Runs:
53
+
54
+ - **On server start** - Initial generation
55
+ - **On file change** - When .js/.jsx files are added/removed/renamed
56
+ - **Manual** - Run `npm run build`
57
+
58
+ ### What It Doesn't Touch:
59
+
60
+ - `<head>` section - Styles, meta tags, live-reload script
61
+ - Files outside watched folders
62
+ - Non-.js/.jsx files
63
+
64
+ ## 2. Live Reload System
65
+
66
+ ### Files Involved:
67
+ - `scripts/live-reload-client.js` - Browser-side WebSocket client
68
+ - `scripts/dev-server.js` - Server-side WebSocket server + file watcher
69
+ - `app.html` - Loads the client script
70
+
71
+ ### How It Works:
72
+
73
+ ```
74
+ 1. Browser loads app.html
75
+ 2. app.html includes live-reload-client.js
76
+ 3. Client creates WebSocket connection
77
+ 4. Server watches: api/, components/, pages/, modules/, styles/
78
+ 5. File changes detected
79
+ 6. Server sends "reload" message via WebSocket
80
+ 7. Client receives message
81
+ 8. Browser reloads page
82
+ ```
83
+
84
+ ### Architecture:
85
+
86
+ ```
87
+ ┌─────────────┐ ┌─────────────┐
88
+ │ Browser │ │ Dev Server │
89
+ │ │ │ │
90
+ │ live- │ WebSocket │ File │
91
+ │ reload- │◄──────────────────►│ Watcher │
92
+ │ client.js │ "reload" message │ │
93
+ │ │ │ │
94
+ └─────────────┘ └─────────────┘
95
+ │ │
96
+ │ Reload page │ Detects changes
97
+ ▼ ▼
98
+ app.html api/, pages/, etc.
99
+ ```
100
+
101
+ ### Why WebSocket?
102
+
103
+ - **Reliable** - Bidirectional, persistent connection
104
+ - **Fast** - Instant notifications
105
+ - **Simple** - No external dependencies
106
+ - **No caching** - Script is always loaded, not injected
107
+
108
+ ### Connection States:
109
+
110
+ 1. **Connecting** (gray) - Initial connection
111
+ 2. **Connected** (green) - Ready, watching for changes
112
+ 3. **Reloading** (blue) - Change detected, reloading
113
+ 4. **Disconnected** (orange) - Connection lost, reconnecting
114
+ 5. **Offline** (red) - Max reconnection attempts reached
115
+
116
+ ## 3. Routing System
117
+
118
+ ### Files Involved:
119
+ - `modules/router.js` - Core routing engine
120
+ - `pages/*.jsx` - Page components
121
+ - `app.html` - Root element (#root)
122
+
123
+ ### How It Works:
124
+
125
+ ```
126
+ 1. Browser loads all page files
127
+ 2. Each page defines a function: HomePage(), AboutPage(), etc.
128
+ 3. startApp() calls autoRegisterPages()
129
+ 4. Scans window for functions ending with "Page"
130
+ 5. Registers: HomePage → "home", AboutPage → "about"
131
+ 6. URL hash changes: #/about
132
+ 7. Router finds AboutPage() function
133
+ 8. Renders it into #root
134
+ ```
135
+
136
+ ### Auto-Registration:
137
+
138
+ ```javascript
139
+ // In pages/About.jsx
140
+ function AboutPage() {
141
+ return <div><h1>About</h1></div>;
142
+ }
143
+
144
+ // Router automatically does:
145
+ routes["about"] = AboutPage;
146
+ ```
147
+
148
+ ### Manual Registration (Optional):
149
+
150
+ ```javascript
151
+ // For custom route names
152
+ registerPage("user-profile", UserProfilePage);
153
+ ```
154
+
155
+ ## How They Work Together
156
+
157
+ ### Startup Sequence:
158
+
159
+ ```
160
+ 1. Run: npm run dev
161
+ 2. Dev server starts
162
+ 3. Runs generate-imports.js
163
+ 4. Scans folders, updates app.html
164
+ 5. Starts HTTP server on port 3000
165
+ 6. Starts WebSocket server
166
+ 7. Starts file watchers
167
+ 8. Ready! ✅
168
+ ```
169
+
170
+ ### Browser Load Sequence:
171
+
172
+ ```
173
+ 1. Browser requests http://localhost:3000/
174
+ 2. Server sends app.html
175
+ 3. Browser loads:
176
+ - styles/globals.css
177
+ - scripts/live-reload-client.js (connects to WebSocket)
178
+ - Babel standalone
179
+ - modules/router.js, state.js, api.js
180
+ - api/*.js
181
+ - components/*.jsx
182
+ - pages/*.jsx
183
+ 4. Calls startApp()
184
+ 5. Auto-registers pages
185
+ 6. Renders current route
186
+ 7. Done! ✅
187
+ ```
188
+
189
+ ### File Change Sequence:
190
+
191
+ ```
192
+ 1. Developer edits pages/Home.jsx
193
+ 2. File watcher detects change
194
+ 3. Debounce timer starts (500ms)
195
+ 4. Timer expires
196
+ 5. Runs generate-imports.js
197
+ 6. Updates app.html
198
+ 7. Sends "reload" via WebSocket
199
+ 8. Browser receives message
200
+ 9. Browser reloads
201
+ 10. New version loads
202
+ 11. Done! ✅
203
+ ```
204
+
205
+ ### CSS Change Sequence (Faster):
206
+
207
+ ```
208
+ 1. Developer edits styles/globals.css
209
+ 2. File watcher detects change
210
+ 3. Debounce timer starts (300ms)
211
+ 4. Timer expires
212
+ 5. Skips generate-imports (not needed for CSS)
213
+ 6. Sends "reload" via WebSocket
214
+ 7. Browser reloads
215
+ 8. Done! ✅
216
+ ```
217
+
218
+ ## File Organization
219
+
220
+ ### What Goes Where:
221
+
222
+ ```
223
+ template/
224
+ ├── app.html # Main HTML (auto-generated scripts)
225
+ ├── modules/ # Core system (don't modify)
226
+ │ ├── router.js # Routing + rendering
227
+ │ ├── state.js # State management
228
+ │ └── api.js # API system
229
+ ├── scripts/ # Build tools (don't modify)
230
+ │ ├── dev-server.js # Dev server + WebSocket
231
+ │ ├── generate-imports.js # Auto-import generator
232
+ │ └── live-reload-client.js # Browser WebSocket client
233
+ ├── api/ # Your API routes (auto-imported)
234
+ ├── components/ # Your components (auto-imported)
235
+ ├── pages/ # Your pages (auto-imported)
236
+ └── styles/ # Your styles (watched for changes)
237
+ ```
238
+
239
+ ### What You Modify:
240
+
241
+ - ✅ `api/` - Add your API routes
242
+ - ✅ `components/` - Add your components
243
+ - ✅ `pages/` - Add your pages
244
+ - ✅ `styles/` - Add your styles
245
+ - ❌ `modules/` - Core system
246
+ - ❌ `scripts/` - Build tools
247
+ - ⚠️ `app.html` - Only modify `<head>` section
248
+
249
+ ## Key Concepts
250
+
251
+ ### 1. Auto-Import is Automatic
252
+
253
+ You don't need to:
254
+ - Add `<script>` tags manually
255
+ - Run build commands after creating files
256
+ - Restart the dev server
257
+
258
+ Just create a file and save it!
259
+
260
+ ### 2. Live Reload is Always On
261
+
262
+ The `live-reload-client.js` script:
263
+ - Is always loaded (in `<head>`)
264
+ - Only runs on localhost
265
+ - Connects automatically
266
+ - Reconnects if disconnected
267
+ - Safe for production (does nothing)
268
+
269
+ ### 3. Pages Auto-Register
270
+
271
+ You don't need to:
272
+ - Call `registerPage()` (optional)
273
+ - Import pages manually
274
+ - Configure routes
275
+
276
+ Just name your function `YourNamePage()` and it works!
277
+
278
+ ### 4. Everything is Watched
279
+
280
+ The dev server watches:
281
+ - `api/` - API routes
282
+ - `components/` - Components
283
+ - `pages/` - Pages
284
+ - `modules/` - Core modules
285
+ - `styles/` - Stylesheets
286
+
287
+ Changes trigger:
288
+ - Import regeneration (for .js/.jsx)
289
+ - Browser reload (for everything)
290
+
291
+ ## Performance Optimizations
292
+
293
+ ### 1. Debouncing
294
+
295
+ Prevents multiple rapid reloads:
296
+ - CSS changes: 300ms delay
297
+ - JS/JSX changes: 500ms delay
298
+
299
+ ### 2. Selective Regeneration
300
+
301
+ - CSS changes: Skip import generation
302
+ - JS/JSX changes: Regenerate imports
303
+
304
+ ### 3. WebSocket Efficiency
305
+
306
+ - Single persistent connection
307
+ - Minimal message size ("reload")
308
+ - Automatic cleanup on disconnect
309
+
310
+ ### 4. Smart Watching
311
+
312
+ - Only watches specific folders
313
+ - Ignores non-code files
314
+ - Recursive: false (performance)
315
+
316
+ ## Troubleshooting
317
+
318
+ ### Import Generation Issues
319
+
320
+ **Problem:** New file not showing up
321
+
322
+ **Check:**
323
+ 1. File is in `api/`, `components/`, or `pages/`
324
+ 2. File ends with `.js` or `.jsx`
325
+ 3. Dev server is running
326
+ 4. Check terminal for errors
327
+
328
+ **Solution:** Run `npm run build` manually
329
+
330
+ ### Live Reload Issues
331
+
332
+ **Problem:** Changes not showing
333
+
334
+ **Check:**
335
+ 1. Indicator is green (✅ Live Reload)
336
+ 2. Dev server is running
337
+ 3. File was saved
338
+ 4. Browser console for errors
339
+
340
+ **Solution:** Click indicator to manually reload
341
+
342
+ ### Routing Issues
343
+
344
+ **Problem:** Page shows 404
345
+
346
+ **Check:**
347
+ 1. Function name ends with "Page"
348
+ 2. File is loaded (check app.html)
349
+ 3. Browser console shows auto-registration
350
+ 4. URL hash is correct (#/about)
351
+
352
+ **Solution:** Add manual registration
353
+
354
+ ## Summary
355
+
356
+ Aziz App combines three systems:
357
+
358
+ 1. **Auto-Import** - Scans folders, generates script tags
359
+ 2. **Live Reload** - WebSocket connection, file watching
360
+ 3. **Auto-Registration** - Scans functions, registers routes
361
+
362
+ Together, they provide a seamless development experience with zero configuration!
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "aziz-app",
3
+ "version": "1.0.2",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "aziz-app",
9
+ "version": "1.0.2",
10
+ "license": "ISC"
11
+ }
12
+ }
13
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aziz-app",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A simple, clean React-like framework with state management, routing, and API routes",
5
5
  "main": "app.html",
6
6
  "scripts": {
@@ -6,7 +6,7 @@ const http = require('http');
6
6
  const { exec } = require('child_process');
7
7
 
8
8
  const PORT = process.env.PORT || 3000;
9
- const clients = []; // Store SSE clients for live reload
9
+ const clients = new Set();
10
10
 
11
11
  console.log('🚀 Dev server starting...\n');
12
12
 
@@ -27,193 +27,117 @@ function generateImports() {
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
+ console.log(`🔄 [${timestamp}] Notifying ${clients.size} browser(s) to reload...`);
31
31
  clients.forEach(client => {
32
- client.write('data: reload\n\n');
33
- });
34
- }
35
-
36
- // Live reload script to inject
37
- function getLiveReloadScript() {
38
- return `
39
- <script>
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
- }
32
+ try {
33
+ client.send('reload');
34
+ } catch (e) {
35
+ clients.delete(client);
107
36
  }
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
- })();
121
- </script>
122
- `;
37
+ });
123
38
  }
124
39
 
125
40
  // Create HTTP server
126
- function createServer(port) {
127
- return http.createServer((req, res) => {
128
- // Handle live reload SSE endpoint
129
- if (req.url === '/live-reload' || req.url.startsWith('/live-reload?')) {
130
- res.writeHead(200, {
131
- 'Content-Type': 'text/event-stream',
41
+ const server = http.createServer((req, res) => {
42
+ // Strip query parameters for file serving (cache busting)
43
+ let filePath = '.' + req.url.split('?')[0];
44
+ if (filePath === './') {
45
+ filePath = './app.html';
46
+ }
47
+
48
+ const extname = String(path.extname(filePath)).toLowerCase();
49
+ const mimeTypes = {
50
+ '.html': 'text/html',
51
+ '.js': 'text/javascript',
52
+ '.jsx': 'text/javascript',
53
+ '.css': 'text/css',
54
+ '.json': 'application/json',
55
+ '.png': 'image/png',
56
+ '.jpg': 'image/jpg',
57
+ '.gif': 'image/gif',
58
+ '.svg': 'image/svg+xml',
59
+ '.wav': 'audio/wav',
60
+ '.mp4': 'video/mp4',
61
+ '.woff': 'application/font-woff',
62
+ '.ttf': 'application/font-ttf',
63
+ '.eot': 'application/vnd.ms-fontobject',
64
+ '.otf': 'application/font-otf',
65
+ '.wasm': 'application/wasm'
66
+ };
67
+
68
+ const contentType = mimeTypes[extname] || 'application/octet-stream';
69
+
70
+ fs.readFile(filePath, (error, content) => {
71
+ if (error) {
72
+ if (error.code === 'ENOENT') {
73
+ res.writeHead(404, { 'Content-Type': 'text/html' });
74
+ res.end('<h1>404 - File Not Found</h1>', 'utf-8');
75
+ } else {
76
+ res.writeHead(500);
77
+ res.end('Server Error: ' + error.code, 'utf-8');
78
+ }
79
+ } else {
80
+ // Add cache control headers to prevent caching during development
81
+ res.writeHead(200, {
82
+ 'Content-Type': contentType,
132
83
  'Cache-Control': 'no-cache, no-store, must-revalidate',
133
- 'Connection': 'keep-alive',
134
- 'Access-Control-Allow-Origin': '*',
135
- 'X-Accel-Buffering': 'no'
84
+ 'Pragma': 'no-cache',
85
+ 'Expires': '0'
136
86
  });
137
-
138
- // Send initial connection message
139
- res.write(':ok\n\n');
140
-
141
- clients.push(res);
142
- const timestamp = new Date().toLocaleTimeString();
143
- console.log(`🔌 [${timestamp}] Browser connected for live reload (${clients.length} total)`);
144
-
145
- req.on('close', () => {
146
- const index = clients.indexOf(res);
147
- if (index !== -1) {
148
- clients.splice(index, 1);
149
- }
150
- const closeTime = new Date().toLocaleTimeString();
151
- console.log(`🔌 [${closeTime}] Browser disconnected (${clients.length} remaining)`);
152
- });
153
-
154
- return;
87
+ res.end(content, 'utf-8');
155
88
  }
89
+ });
90
+ });
156
91
 
157
- // Strip query parameters for file serving (cache busting)
158
- let filePath = '.' + req.url.split('?')[0];
159
- if (filePath === './') {
160
- filePath = './app.html';
92
+ // WebSocket implementation (simple, no external dependencies)
93
+ server.on('upgrade', (req, socket, head) => {
94
+ // Simple WebSocket handshake
95
+ const key = req.headers['sec-websocket-key'];
96
+ const acceptKey = require('crypto')
97
+ .createHash('sha1')
98
+ .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
99
+ .digest('base64');
100
+
101
+ socket.write(
102
+ 'HTTP/1.1 101 Switching Protocols\r\n' +
103
+ 'Upgrade: websocket\r\n' +
104
+ 'Connection: Upgrade\r\n' +
105
+ `Sec-WebSocket-Accept: ${acceptKey}\r\n` +
106
+ '\r\n'
107
+ );
108
+
109
+ const timestamp = new Date().toLocaleTimeString();
110
+ console.log(`🔌 [${timestamp}] Browser connected (${clients.size + 1} total)`);
111
+
112
+ // Wrap socket with send method
113
+ const client = {
114
+ socket: socket,
115
+ send: function(message) {
116
+ const buffer = Buffer.from(message);
117
+ const frame = Buffer.alloc(buffer.length + 2);
118
+ frame[0] = 0x81; // Text frame
119
+ frame[1] = buffer.length;
120
+ buffer.copy(frame, 2);
121
+ socket.write(frame);
161
122
  }
123
+ };
162
124
 
163
- const extname = String(path.extname(filePath)).toLowerCase();
164
- const mimeTypes = {
165
- '.html': 'text/html',
166
- '.js': 'text/javascript',
167
- '.jsx': 'text/javascript',
168
- '.css': 'text/css',
169
- '.json': 'application/json',
170
- '.png': 'image/png',
171
- '.jpg': 'image/jpg',
172
- '.gif': 'image/gif',
173
- '.svg': 'image/svg+xml',
174
- '.wav': 'audio/wav',
175
- '.mp4': 'video/mp4',
176
- '.woff': 'application/font-woff',
177
- '.ttf': 'application/font-ttf',
178
- '.eot': 'application/vnd.ms-fontobject',
179
- '.otf': 'application/font-otf',
180
- '.wasm': 'application/wasm'
181
- };
125
+ clients.add(client);
182
126
 
183
- const contentType = mimeTypes[extname] || 'application/octet-stream';
127
+ socket.on('close', () => {
128
+ clients.delete(client);
129
+ const closeTime = new Date().toLocaleTimeString();
130
+ console.log(`🔌 [${closeTime}] Browser disconnected (${clients.size} remaining)`);
131
+ });
184
132
 
185
- fs.readFile(filePath, (error, content) => {
186
- if (error) {
187
- if (error.code === 'ENOENT') {
188
- res.writeHead(404, { 'Content-Type': 'text/html' });
189
- res.end('<h1>404 - File Not Found</h1>', 'utf-8');
190
- } else {
191
- res.writeHead(500);
192
- res.end('Server Error: ' + error.code, 'utf-8');
193
- }
194
- } else {
195
- // Inject live reload script into HTML files
196
- if (extname === '.html') {
197
- content = content.toString().replace('</body>', getLiveReloadScript() + '</body>');
198
- }
199
-
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
- });
207
- res.end(content, 'utf-8');
208
- }
209
- });
133
+ socket.on('error', (err) => {
134
+ clients.delete(client);
135
+ console.log('⚠️ Socket error:', err.message);
210
136
  });
211
- }
137
+ });
212
138
 
213
139
  // Function to start server on available port
214
140
  function startServer(port) {
215
- const server = createServer(port);
216
-
217
141
  server.listen(port, () => {
218
142
  console.log(`🌐 Server running at http://localhost:${port}/`);
219
143
  console.log(`📱 Open http://localhost:${port}/ in your browser\n`);
@@ -0,0 +1,105 @@
1
+ // Live Reload Client - Always loaded, connects to dev server
2
+ (function() {
3
+ // Only run in development (when dev server is running)
4
+ const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
5
+ if (!isDev) return;
6
+
7
+ // Create visual indicator
8
+ const indicator = document.createElement('div');
9
+ indicator.id = 'live-reload-indicator';
10
+ 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; cursor: pointer;';
11
+ indicator.textContent = '🔌 Connecting...';
12
+
13
+ // Wait for body to be ready
14
+ function addIndicator() {
15
+ if (document.body) {
16
+ document.body.appendChild(indicator);
17
+ } else {
18
+ setTimeout(addIndicator, 100);
19
+ }
20
+ }
21
+ addIndicator();
22
+
23
+ let ws;
24
+ let reconnectAttempts = 0;
25
+ const maxReconnectAttempts = 10;
26
+ const port = window.location.port || 3000;
27
+
28
+ function connect() {
29
+ try {
30
+ console.log('🔌 Connecting to live reload WebSocket...');
31
+ ws = new WebSocket(`ws://localhost:${port}`);
32
+
33
+ ws.onopen = function() {
34
+ console.log('✅ Live reload connected');
35
+ reconnectAttempts = 0;
36
+ indicator.style.background = '#4caf50';
37
+ indicator.textContent = '✅ Live Reload';
38
+ setTimeout(function() {
39
+ indicator.style.opacity = '0.3';
40
+ }, 2000);
41
+ };
42
+
43
+ ws.onmessage = function(event) {
44
+ if (event.data === 'reload') {
45
+ console.log('🔄 Changes detected, reloading...');
46
+ indicator.style.opacity = '1';
47
+ indicator.style.background = '#2196f3';
48
+ indicator.textContent = '🔄 Reloading...';
49
+
50
+ // Small delay to show the indicator
51
+ setTimeout(function() {
52
+ window.location.reload();
53
+ }, 100);
54
+ }
55
+ };
56
+
57
+ ws.onerror = function(err) {
58
+ console.log('⚠️ WebSocket error:', err);
59
+ };
60
+
61
+ ws.onclose = function() {
62
+ console.log('⚠️ Live reload disconnected');
63
+ indicator.style.background = '#ff9800';
64
+ indicator.style.opacity = '1';
65
+ indicator.textContent = '⚠️ Disconnected';
66
+
67
+ // Try to reconnect
68
+ if (reconnectAttempts < maxReconnectAttempts) {
69
+ reconnectAttempts++;
70
+ console.log(`Reconnecting... attempt ${reconnectAttempts}`);
71
+ setTimeout(connect, 2000);
72
+ } else {
73
+ indicator.textContent = '❌ Offline';
74
+ indicator.style.background = '#f44336';
75
+ }
76
+ };
77
+ } catch (e) {
78
+ console.error('Failed to create WebSocket:', e);
79
+ indicator.style.background = '#f44336';
80
+ indicator.textContent = '❌ Error';
81
+ }
82
+ }
83
+
84
+ // Start connection when page loads
85
+ if (document.readyState === 'loading') {
86
+ document.addEventListener('DOMContentLoaded', connect);
87
+ } else {
88
+ connect();
89
+ }
90
+
91
+ // Show indicator on hover
92
+ indicator.addEventListener('mouseenter', function() {
93
+ indicator.style.opacity = '1';
94
+ });
95
+ indicator.addEventListener('mouseleave', function() {
96
+ if (indicator.textContent.includes('Live Reload')) {
97
+ indicator.style.opacity = '0.3';
98
+ }
99
+ });
100
+
101
+ // Click to manually reload
102
+ indicator.addEventListener('click', function() {
103
+ window.location.reload();
104
+ });
105
+ })();