create-aziz-app 2.0.10 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,10 +6,153 @@ 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
+ const apiHandlers = {};
10
11
 
11
12
  console.log('šŸš€ Dev server starting...\n');
12
13
 
14
+ // Load API routes from api/ folder
15
+ function loadApiRoutes() {
16
+ const apiDir = path.join(process.cwd(), 'api');
17
+ if (!fs.existsSync(apiDir)) {
18
+ console.log('āš ļø No api/ folder found\n');
19
+ return;
20
+ }
21
+
22
+ const files = fs.readdirSync(apiDir).filter(f => f.endsWith('.js'));
23
+ console.log(`šŸ“” Loading ${files.length} API routes...\n`);
24
+
25
+ files.forEach(file => {
26
+ const routeName = path.basename(file, '.js');
27
+ const filePath = path.join(apiDir, file);
28
+
29
+ try {
30
+ // Clear require cache to allow hot reload
31
+ delete require.cache[require.resolve(filePath)];
32
+
33
+ // Load the API handler
34
+ const handler = require(filePath);
35
+
36
+ if (typeof handler === 'function') {
37
+ apiHandlers[routeName] = handler;
38
+ console.log(` āœ… /api/${routeName}`);
39
+ } else if (handler.default && typeof handler.default === 'function') {
40
+ apiHandlers[routeName] = handler.default;
41
+ console.log(` āœ… /api/${routeName}`);
42
+ } else {
43
+ console.log(` āš ļø /api/${routeName} - No default export`);
44
+ }
45
+ } catch (error) {
46
+ console.error(` āŒ /api/${routeName} - ${error.message}`);
47
+ }
48
+ });
49
+
50
+ console.log('');
51
+ }
52
+
53
+ // Parse request body
54
+ function parseBody(req) {
55
+ return new Promise((resolve, reject) => {
56
+ let body = '';
57
+ req.on('data', chunk => {
58
+ body += chunk.toString();
59
+ });
60
+ req.on('end', () => {
61
+ try {
62
+ if (body && req.headers['content-type']?.includes('application/json')) {
63
+ resolve(JSON.parse(body));
64
+ } else {
65
+ resolve(body);
66
+ }
67
+ } catch (e) {
68
+ resolve(body);
69
+ }
70
+ });
71
+ req.on('error', reject);
72
+ });
73
+ }
74
+
75
+ // Parse query string
76
+ function parseQuery(url) {
77
+ const query = {};
78
+ const queryString = url.split('?')[1];
79
+ if (queryString) {
80
+ queryString.split('&').forEach(param => {
81
+ const [key, value] = param.split('=');
82
+ query[decodeURIComponent(key)] = decodeURIComponent(value || '');
83
+ });
84
+ }
85
+ return query;
86
+ }
87
+
88
+ // Handle API requests
89
+ async function handleApiRequest(req, res) {
90
+ const urlPath = req.url.split('?')[0];
91
+ const routeName = urlPath.replace('/api/', '');
92
+
93
+ const handler = apiHandlers[routeName];
94
+
95
+ if (!handler) {
96
+ res.writeHead(404, { 'Content-Type': 'application/json' });
97
+ res.end(JSON.stringify({ error: 'API route not found' }));
98
+ return;
99
+ }
100
+
101
+ try {
102
+ // Parse request
103
+ const body = await parseBody(req);
104
+ const query = parseQuery(req.url);
105
+
106
+ // Create Next.js-like req/res objects
107
+ const apiReq = {
108
+ method: req.method,
109
+ body: body,
110
+ query: query,
111
+ headers: req.headers,
112
+ url: req.url
113
+ };
114
+
115
+ const apiRes = {
116
+ statusCode: 200,
117
+ headers: {},
118
+ status(code) {
119
+ this.statusCode = code;
120
+ return this;
121
+ },
122
+ json(data) {
123
+ res.writeHead(this.statusCode, {
124
+ 'Content-Type': 'application/json',
125
+ 'Access-Control-Allow-Origin': '*',
126
+ ...this.headers
127
+ });
128
+ res.end(JSON.stringify(data));
129
+ return this;
130
+ },
131
+ send(data) {
132
+ res.writeHead(this.statusCode, {
133
+ 'Content-Type': 'text/plain',
134
+ 'Access-Control-Allow-Origin': '*',
135
+ ...this.headers
136
+ });
137
+ res.end(data);
138
+ return this;
139
+ },
140
+ setHeader(key, value) {
141
+ this.headers[key] = value;
142
+ return this;
143
+ }
144
+ };
145
+
146
+ // Call the handler
147
+ await handler(apiReq, apiRes);
148
+
149
+ } catch (error) {
150
+ console.error(`āŒ API Error on /api/${routeName}:`, error);
151
+ res.writeHead(500, { 'Content-Type': 'application/json' });
152
+ res.end(JSON.stringify({ error: error.message }));
153
+ }
154
+ }
155
+
13
156
  // Function to generate imports
14
157
  function generateImports() {
15
158
  console.log('šŸ”„ Regenerating imports...');
@@ -27,193 +170,134 @@ function generateImports() {
27
170
  // Notify all clients to reload
28
171
  function notifyClients() {
29
172
  const timestamp = new Date().toLocaleTimeString();
30
- console.log(`šŸ”„ [${timestamp}] Notifying ${clients.length} browser(s) to reload...`);
173
+ console.log(`šŸ”„ [${timestamp}] Notifying ${clients.size} browser(s) to reload...`);
31
174
  clients.forEach(client => {
32
- client.write('data: reload\n\n');
175
+ try {
176
+ client.send('reload');
177
+ } catch (e) {
178
+ clients.delete(client);
179
+ }
33
180
  });
34
181
  }
35
182
 
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
- }
107
- }
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
- }
183
+ // Create HTTP server
184
+ const server = http.createServer(async (req, res) => {
185
+ // Handle API routes
186
+ if (req.url.startsWith('/api/')) {
187
+ await handleApiRequest(req, res);
188
+ return;
189
+ }
190
+
191
+ // Handle CORS preflight
192
+ if (req.method === 'OPTIONS') {
193
+ res.writeHead(200, {
194
+ 'Access-Control-Allow-Origin': '*',
195
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
196
+ 'Access-Control-Allow-Headers': 'Content-Type'
119
197
  });
120
- })();
121
- </script>
122
- `;
123
- }
198
+ res.end();
199
+ return;
200
+ }
124
201
 
125
- // 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',
202
+ // Strip query parameters for file serving (cache busting)
203
+ let filePath = '.' + req.url.split('?')[0];
204
+ if (filePath === './') {
205
+ filePath = './app.html';
206
+ }
207
+
208
+ const extname = String(path.extname(filePath)).toLowerCase();
209
+ const mimeTypes = {
210
+ '.html': 'text/html',
211
+ '.js': 'text/javascript',
212
+ '.jsx': 'text/javascript',
213
+ '.css': 'text/css',
214
+ '.json': 'application/json',
215
+ '.png': 'image/png',
216
+ '.jpg': 'image/jpg',
217
+ '.gif': 'image/gif',
218
+ '.svg': 'image/svg+xml',
219
+ '.wav': 'audio/wav',
220
+ '.mp4': 'video/mp4',
221
+ '.woff': 'application/font-woff',
222
+ '.ttf': 'application/font-ttf',
223
+ '.eot': 'application/vnd.ms-fontobject',
224
+ '.otf': 'application/font-otf',
225
+ '.wasm': 'application/wasm'
226
+ };
227
+
228
+ const contentType = mimeTypes[extname] || 'application/octet-stream';
229
+
230
+ fs.readFile(filePath, (error, content) => {
231
+ if (error) {
232
+ if (error.code === 'ENOENT') {
233
+ res.writeHead(404, { 'Content-Type': 'text/html' });
234
+ res.end('<h1>404 - File Not Found</h1>', 'utf-8');
235
+ } else {
236
+ res.writeHead(500);
237
+ res.end('Server Error: ' + error.code, 'utf-8');
238
+ }
239
+ } else {
240
+ // Add cache control headers to prevent caching during development
241
+ res.writeHead(200, {
242
+ 'Content-Type': contentType,
132
243
  'Cache-Control': 'no-cache, no-store, must-revalidate',
133
- 'Connection': 'keep-alive',
134
- 'Access-Control-Allow-Origin': '*',
135
- 'X-Accel-Buffering': 'no'
136
- });
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)`);
244
+ 'Pragma': 'no-cache',
245
+ 'Expires': '0'
152
246
  });
153
-
154
- return;
247
+ res.end(content, 'utf-8');
155
248
  }
249
+ });
250
+ });
251
+
252
+ // WebSocket implementation (simple, no external dependencies)
253
+ server.on('upgrade', (req, socket, head) => {
254
+ // Simple WebSocket handshake
255
+ const key = req.headers['sec-websocket-key'];
256
+ const acceptKey = require('crypto')
257
+ .createHash('sha1')
258
+ .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
259
+ .digest('base64');
260
+
261
+ socket.write(
262
+ 'HTTP/1.1 101 Switching Protocols\r\n' +
263
+ 'Upgrade: websocket\r\n' +
264
+ 'Connection: Upgrade\r\n' +
265
+ `Sec-WebSocket-Accept: ${acceptKey}\r\n` +
266
+ '\r\n'
267
+ );
156
268
 
157
- // Strip query parameters for file serving (cache busting)
158
- let filePath = '.' + req.url.split('?')[0];
159
- if (filePath === './') {
160
- filePath = './app.html';
269
+ const timestamp = new Date().toLocaleTimeString();
270
+ console.log(`šŸ”Œ [${timestamp}] Browser connected (${clients.size + 1} total)`);
271
+
272
+ // Wrap socket with send method
273
+ const client = {
274
+ socket: socket,
275
+ send: function(message) {
276
+ const buffer = Buffer.from(message);
277
+ const frame = Buffer.alloc(buffer.length + 2);
278
+ frame[0] = 0x81; // Text frame
279
+ frame[1] = buffer.length;
280
+ buffer.copy(frame, 2);
281
+ socket.write(frame);
161
282
  }
283
+ };
162
284
 
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
- };
285
+ clients.add(client);
182
286
 
183
- const contentType = mimeTypes[extname] || 'application/octet-stream';
287
+ socket.on('close', () => {
288
+ clients.delete(client);
289
+ const closeTime = new Date().toLocaleTimeString();
290
+ console.log(`šŸ”Œ [${closeTime}] Browser disconnected (${clients.size} remaining)`);
291
+ });
184
292
 
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
- });
293
+ socket.on('error', (err) => {
294
+ clients.delete(client);
295
+ console.log('āš ļø Socket error:', err.message);
210
296
  });
211
- }
297
+ });
212
298
 
213
299
  // Function to start server on available port
214
300
  function startServer(port) {
215
- const server = createServer(port);
216
-
217
301
  server.listen(port, () => {
218
302
  console.log(`🌐 Server running at http://localhost:${port}/`);
219
303
  console.log(`šŸ“± Open http://localhost:${port}/ in your browser\n`);
@@ -246,6 +330,12 @@ watchDirs.forEach(dir => {
246
330
  clearTimeout(reloadTimeout);
247
331
  }
248
332
 
333
+ // Reload API routes if api/ folder changed
334
+ if (dir === 'api' && filename.endsWith('.js')) {
335
+ console.log('šŸ”„ Reloading API routes...');
336
+ loadApiRoutes();
337
+ }
338
+
249
339
  // For CSS changes, just reload without regenerating imports
250
340
  if (filename.endsWith('.css')) {
251
341
  reloadTimeout = setTimeout(() => {
@@ -266,6 +356,9 @@ console.log('\nāœ… Dev server running with live reload!');
266
356
  console.log('šŸ’” Create/modify files and browser will auto-refresh');
267
357
  console.log('ā¹ļø Press Ctrl+C to stop\n');
268
358
 
359
+ // Load API routes
360
+ loadApiRoutes();
361
+
269
362
  // Start server
270
363
  startServer(PORT);
271
364
 
@@ -31,11 +31,9 @@ function scanDirectory(dir, extensions = []) {
31
31
  }
32
32
 
33
33
  // Scan for files
34
- const apiRoutes = scanDirectory('api', ['.js']);
35
34
  const components = scanDirectory('components', ['.jsx', '.js']);
36
35
  const pages = scanDirectory('pages', ['.jsx', '.js']);
37
36
 
38
- console.log(`šŸ“” Found ${apiRoutes.length} API routes`);
39
37
  console.log(`🧩 Found ${components.length} components`);
40
38
  console.log(`šŸ“„ Found ${pages.length} pages\n`);
41
39
 
@@ -48,14 +46,6 @@ scriptTags += ' <script type="text/babel" src="modules/router.js"></script>\n
48
46
  scriptTags += ' <script type="text/babel" src="modules/state.js"></script>\n';
49
47
  scriptTags += ' <script type="text/babel" src="modules/api.js"></script>\n\n';
50
48
 
51
- if (apiRoutes.length > 0) {
52
- scriptTags += ' <!-- Load API routes -->\n';
53
- apiRoutes.forEach(route => {
54
- scriptTags += ` <script type="text/babel" src="${route}"></script>\n`;
55
- });
56
- scriptTags += '\n';
57
- }
58
-
59
49
  if (components.length > 0) {
60
50
  scriptTags += ' <!-- Load shared components -->\n';
61
51
  components.forEach(component => {
@@ -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
+ })();