create-aziz-app 2.0.14 → 3.0.0

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.
Files changed (60) hide show
  1. package/README.md +227 -0
  2. package/bin/create-aziz-app.js +9 -9
  3. package/package.json +10 -11
  4. package/template/README.md +862 -138
  5. package/template/_framework/boot.js +36 -0
  6. package/template/_framework/hooks.js +207 -0
  7. package/template/{modules → _framework}/next-response.js +5 -13
  8. package/template/_framework/router.js +279 -0
  9. package/template/_framework/runtime.js +86 -0
  10. package/template/api/createUser.js +1 -1
  11. package/template/api/hello.js +8 -17
  12. package/template/api/loginUser.js +1 -1
  13. package/template/api/users.js +38 -134
  14. package/template/app/about/page.js +45 -0
  15. package/template/app/layout.js +23 -0
  16. package/template/app/page.js +54 -0
  17. package/template/app/test/page.js +55 -0
  18. package/template/components/Footer.js +10 -0
  19. package/template/components/Navbar.js +30 -0
  20. package/template/index.html +24 -0
  21. package/template/package.json +5 -14
  22. package/template/scripts/dev-server.js +330 -302
  23. package/template/scripts/live-reload-client.js +0 -1
  24. package/template/.vscode/settings.json +0 -15
  25. package/template/api/getUser.js +0 -60
  26. package/template/api/getUserWithDB.js +0 -65
  27. package/template/app.config.js +0 -41
  28. package/template/app.html +0 -55
  29. package/template/components/Counter.jsx +0 -36
  30. package/template/components/Footer.jsx +0 -9
  31. package/template/components/Navbar.jsx +0 -17
  32. package/template/docs/API-GUIDE.md +0 -196
  33. package/template/docs/API-ROUTES.md +0 -538
  34. package/template/docs/AUTO-IMPORT.md +0 -99
  35. package/template/docs/AUTO-REGISTRATION.md +0 -248
  36. package/template/docs/CSS-FRAMEWORKS.md +0 -221
  37. package/template/docs/DEV-SERVER.md +0 -184
  38. package/template/docs/GETTING-STARTED.md +0 -205
  39. package/template/docs/HOW-IT-WORKS.md +0 -362
  40. package/template/docs/NEXTJS-APP-ROUTER.md +0 -536
  41. package/template/docs/NEXTJS-COMPARISON.md +0 -473
  42. package/template/docs/QUICK-START.md +0 -111
  43. package/template/docs/STYLING.md +0 -269
  44. package/template/docs/TROUBLESHOOTING.md +0 -282
  45. package/template/modules/README.md +0 -129
  46. package/template/modules/api.js +0 -44
  47. package/template/modules/auto-import.js +0 -130
  48. package/template/modules/router.js +0 -218
  49. package/template/modules/state.js +0 -248
  50. package/template/package-lock.json +0 -13
  51. package/template/pages/About.jsx +0 -48
  52. package/template/pages/ApiDemo.jsx +0 -209
  53. package/template/pages/Contact.jsx +0 -25
  54. package/template/pages/Example.jsx +0 -59
  55. package/template/pages/Home.jsx +0 -39
  56. package/template/pages/HooksDemo.jsx +0 -194
  57. package/template/pages/test.jsx +0 -72
  58. package/template/scripts/build.js +0 -81
  59. package/template/scripts/generate-imports.js +0 -88
  60. package/template/tailwind.config.js +0 -17
@@ -1,188 +1,239 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
3
+ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const http = require('http');
6
- const { exec } = require('child_process');
6
+ const crypto = require('crypto');
7
7
 
8
8
  const PORT = process.env.PORT || 3000;
9
+ const ROOT = process.cwd();
9
10
  const clients = new Set();
10
11
  const apiHandlers = {};
11
12
 
12
- console.log('šŸš€ Dev server starting...\n');
13
+ console.log('šŸš€ Aziz.js dev server starting...\n');
14
+
15
+ // ─── Route Manifest ──────────────────────────────────────────
16
+ // Scans app/ folder and builds a list of pages and layouts
17
+
18
+ function buildRouteManifest() {
19
+ const manifest = [];
20
+ const appDir = path.join(ROOT, 'app');
21
+ if (!fs.existsSync(appDir)) return manifest;
22
+
23
+ function scan(dir, routeBase) {
24
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
25
+
26
+ for (const entry of entries) {
27
+ const fullPath = path.join(dir, entry.name);
28
+
29
+ if (entry.isDirectory()) {
30
+ // Dynamic route: [slug] → :slug
31
+ const segment = entry.name.startsWith('[') && entry.name.endsWith(']')
32
+ ? ':' + entry.name.slice(1, -1)
33
+ : entry.name;
34
+ scan(fullPath, routeBase === '/' ? '/' + segment : routeBase + '/' + segment);
35
+ } else if (entry.name === 'page.js' || entry.name === 'page.jsx') {
36
+ manifest.push({
37
+ type: 'page',
38
+ pathname: routeBase || '/',
39
+ filePath: '/app' + (routeBase === '/' ? '' : routeBase) + '/' + entry.name
40
+ });
41
+ } else if (entry.name === 'layout.js' || entry.name === 'layout.jsx') {
42
+ manifest.push({
43
+ type: 'layout',
44
+ pathname: routeBase || '/',
45
+ filePath: '/app' + (routeBase === '/' ? '' : routeBase) + '/' + entry.name
46
+ });
47
+ }
48
+ }
49
+ }
13
50
 
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;
51
+ scan(appDir, '/');
52
+ return manifest;
53
+ }
54
+
55
+ // ─── Bare Import Rewriter ─────────────────────────────────────
56
+ // Transforms: import X from 'mongoose' → import X from '/_modules/mongoose'
57
+ // Transforms: import X from '@/lib/db' → import X from '/lib/db.js'
58
+
59
+ function rewriteImports(code, filePath) {
60
+ // Rewrite @/ alias to absolute path
61
+ code = code.replace(
62
+ /from\s+['"]@\/([^'"]+)['"]/g,
63
+ (_, p) => `from '/${p}'`
64
+ );
65
+
66
+ // Rewrite bare specifiers (not starting with /, ./, ../)
67
+ code = code.replace(
68
+ /from\s+['"]([^'"./][^'"]*)['"]/g,
69
+ (match, specifier) => `from '/_modules/${specifier}'`
70
+ );
71
+
72
+ // Same for dynamic imports
73
+ code = code.replace(
74
+ /import\s*\(\s*['"]([^'"./][^'"]*)['"]\s*\)/g,
75
+ (_, specifier) => `import('/_modules/${specifier}')`
76
+ );
77
+
78
+ return code;
79
+ }
80
+
81
+ // ─── Module Resolver ─────────────────────────────────────────
82
+ // Serves node_modules files for bare imports
83
+
84
+ function resolveNodeModule(specifier) {
85
+ // Try to find the package in node_modules
86
+ const parts = specifier.split('/');
87
+ const pkgName = parts[0].startsWith('@') ? parts[0] + '/' + parts[1] : parts[0];
88
+ const subPath = parts[0].startsWith('@') ? parts.slice(2).join('/') : parts.slice(1).join('/');
89
+
90
+ const nodeModulesDir = path.join(ROOT, 'node_modules');
91
+
92
+ // Try package.json to find main/module entry
93
+ const pkgJsonPath = path.join(nodeModulesDir, pkgName, 'package.json');
94
+ if (!fs.existsSync(pkgJsonPath)) {
95
+ return null;
96
+ }
97
+
98
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
99
+
100
+ if (subPath) {
101
+ // Specific file requested
102
+ const candidates = [
103
+ path.join(nodeModulesDir, pkgName, subPath),
104
+ path.join(nodeModulesDir, pkgName, subPath + '.js'),
105
+ path.join(nodeModulesDir, pkgName, subPath + '/index.js'),
106
+ ];
107
+ for (const c of candidates) {
108
+ if (fs.existsSync(c)) return c;
109
+ }
110
+ return null;
20
111
  }
21
112
 
113
+ // Use module > browser > main priority
114
+ const entry = pkg.module || pkg.browser || pkg.main || 'index.js';
115
+ const entryPath = path.join(nodeModulesDir, pkgName, entry);
116
+ return fs.existsSync(entryPath) ? entryPath : null;
117
+ }
118
+
119
+ // ─── JSX Transform ───────────────────────────────────────────
120
+ // Adds /** @jsx h */ pragma if file uses JSX and doesn't have it
121
+
122
+ function ensureJsxPragma(code) {
123
+ if (code.includes('@jsx') || !code.includes('<')) return code;
124
+ // Simple heuristic: if it has JSX-like syntax, add pragma
125
+ if (/<[A-Z]|<[a-z]+[\s/>]/.test(code)) {
126
+ return '/** @jsx h */\n' + code;
127
+ }
128
+ return code;
129
+ }
130
+
131
+ // ─── API Route Loader ─────────────────────────────────────────
132
+
133
+ function loadApiRoutes() {
134
+ const apiDir = path.join(ROOT, 'api');
135
+ if (!fs.existsSync(apiDir)) return;
136
+
22
137
  const files = fs.readdirSync(apiDir).filter(f => f.endsWith('.js'));
23
- console.log(`šŸ“” Loading ${files.length} API routes...\n`);
138
+ console.log(`šŸ“” Loading ${files.length} API route(s):`);
24
139
 
25
140
  files.forEach(file => {
26
141
  const routeName = path.basename(file, '.js');
27
- const filePath = path.join(apiDir, file);
28
-
142
+ const filePath = path.join(apiDir, file);
143
+
29
144
  try {
30
- // Clear require cache to allow hot reload
31
145
  delete require.cache[require.resolve(filePath)];
32
-
33
- // Load the API module
34
- const module = require(filePath);
35
-
36
- // Support both styles:
37
- // 1. Default export function (old style)
38
- // 2. Named exports: GET, POST, PUT, DELETE (new Next.js App Router style)
39
-
40
- if (module.GET || module.POST || module.PUT || module.DELETE || module.PATCH) {
41
- // New style: Named exports
146
+ const mod = require(filePath);
147
+
148
+ if (mod.GET || mod.POST || mod.PUT || mod.DELETE || mod.PATCH) {
42
149
  const methods = {};
43
- if (module.GET) methods.GET = module.GET;
44
- if (module.POST) methods.POST = module.POST;
45
- if (module.PUT) methods.PUT = module.PUT;
46
- if (module.DELETE) methods.DELETE = module.DELETE;
47
- if (module.PATCH) methods.PATCH = module.PATCH;
48
-
150
+ ['GET','POST','PUT','DELETE','PATCH'].forEach(m => { if (mod[m]) methods[m] = mod[m]; });
49
151
  apiHandlers[routeName] = methods;
50
152
  console.log(` āœ… /api/${routeName} [${Object.keys(methods).join(', ')}]`);
51
153
  } else {
52
- // Old style: Default export
53
- const handler = module.default || module;
54
-
154
+ const handler = mod.default || mod;
55
155
  if (typeof handler === 'function') {
56
156
  apiHandlers[routeName] = handler;
57
157
  console.log(` āœ… /api/${routeName}`);
58
- } else {
59
- console.log(` āš ļø /api/${routeName} - No handler function exported`);
60
158
  }
61
159
  }
62
- } catch (error) {
63
- console.error(` āŒ /api/${routeName} - ${error.message}`);
160
+ } catch (err) {
161
+ console.error(` āŒ /api/${routeName} — ${err.message}`);
64
162
  }
65
163
  });
66
-
164
+
67
165
  console.log('');
68
166
  }
69
- }
70
167
 
71
- // Parse request body
168
+ // ─── Request Helpers ─────────────────────────────────────────
169
+
72
170
  function parseBody(req) {
73
171
  return new Promise((resolve, reject) => {
74
172
  let body = '';
75
- req.on('data', chunk => {
76
- body += chunk.toString();
77
- });
173
+ req.on('data', c => body += c);
78
174
  req.on('end', () => {
79
175
  try {
80
- if (body && req.headers['content-type']?.includes('application/json')) {
81
- resolve(JSON.parse(body));
82
- } else {
83
- resolve(body);
84
- }
85
- } catch (e) {
86
- resolve(body);
87
- }
176
+ resolve(body && req.headers['content-type']?.includes('application/json')
177
+ ? JSON.parse(body) : body);
178
+ } catch { resolve(body); }
88
179
  });
89
180
  req.on('error', reject);
90
181
  });
91
182
  }
92
183
 
93
- // Parse query string
94
184
  function parseQuery(url) {
95
- const query = {};
96
- const queryString = url.split('?')[1];
97
- if (queryString) {
98
- queryString.split('&').forEach(param => {
99
- const [key, value] = param.split('=');
100
- query[decodeURIComponent(key)] = decodeURIComponent(value || '');
101
- });
102
- }
103
- return query;
185
+ const q = {};
186
+ const qs = url.split('?')[1];
187
+ if (qs) qs.split('&').forEach(p => {
188
+ const [k, v] = p.split('=');
189
+ q[decodeURIComponent(k)] = decodeURIComponent(v || '');
190
+ });
191
+ return q;
104
192
  }
105
193
 
106
- // Handle API requests
194
+ // ─── API Request Handler ──────────────────────────────────────
195
+
107
196
  async function handleApiRequest(req, res) {
108
- const urlPath = req.url.split('?')[0];
197
+ const urlPath = req.url.split('?')[0];
109
198
  const routeName = urlPath.replace('/api/', '');
110
-
111
- const handler = apiHandlers[routeName];
112
-
199
+ const handler = apiHandlers[routeName];
200
+
113
201
  if (!handler) {
114
202
  res.writeHead(404, { 'Content-Type': 'application/json' });
115
- res.end(JSON.stringify({ error: 'API route not found' }));
203
+ res.end(JSON.stringify({ error: `API route /api/${routeName} not found` }));
116
204
  return;
117
205
  }
118
206
 
119
207
  try {
120
- // Parse request
121
- const body = await parseBody(req);
208
+ const body = await parseBody(req);
122
209
  const query = parseQuery(req.url);
123
-
124
- // Check if handler is a function (old style) or object with HTTP methods (new style)
125
- let result;
126
-
127
- if (typeof handler === 'function') {
128
- // Old style: default export function
129
- const apiReq = {
130
- method: req.method,
131
- body: body,
132
- query: query,
133
- headers: req.headers,
134
- url: req.url
135
- };
136
210
 
211
+ if (typeof handler === 'function') {
212
+ // Old style
213
+ const apiReq = { method: req.method, body, query, headers: req.headers, url: req.url };
137
214
  const apiRes = {
138
- statusCode: 200,
139
- headers: {},
140
- status(code) {
141
- this.statusCode = code;
142
- return this;
143
- },
144
- json(data) {
145
- res.writeHead(this.statusCode, {
146
- 'Content-Type': 'application/json',
147
- 'Access-Control-Allow-Origin': '*',
148
- ...this.headers
149
- });
150
- res.end(JSON.stringify(data));
151
- return this;
215
+ statusCode: 200, headers: {},
216
+ status(c) { this.statusCode = c; return this; },
217
+ json(d) {
218
+ res.writeHead(this.statusCode, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', ...this.headers });
219
+ res.end(JSON.stringify(d));
152
220
  },
153
- send(data) {
154
- res.writeHead(this.statusCode, {
155
- 'Content-Type': 'text/plain',
156
- 'Access-Control-Allow-Origin': '*',
157
- ...this.headers
158
- });
159
- res.end(data);
160
- return this;
221
+ send(d) {
222
+ res.writeHead(this.statusCode, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*', ...this.headers });
223
+ res.end(d);
161
224
  },
162
- setHeader(key, value) {
163
- this.headers[key] = value;
164
- return this;
165
- }
225
+ setHeader(k, v) { this.headers[k] = v; return this; }
166
226
  };
167
-
168
227
  await handler(apiReq, apiRes);
169
- } else if (typeof handler === 'object') {
170
- // New style: Named exports (GET, POST, PUT, DELETE)
171
- const methodHandler = handler[req.method];
172
-
173
- if (!methodHandler) {
174
- res.writeHead(405, {
175
- 'Content-Type': 'application/json',
176
- 'Allow': Object.keys(handler).join(', ')
177
- });
178
- res.end(JSON.stringify({
179
- error: 'Method not allowed',
180
- allowedMethods: Object.keys(handler)
181
- }));
228
+ } else {
229
+ // New style: named exports
230
+ const methodFn = handler[req.method];
231
+ if (!methodFn) {
232
+ res.writeHead(405, { 'Content-Type': 'application/json', 'Allow': Object.keys(handler).join(', ') });
233
+ res.end(JSON.stringify({ error: 'Method not allowed', allowed: Object.keys(handler) }));
182
234
  return;
183
235
  }
184
236
 
185
- // Create Next.js-style request object
186
237
  const request = {
187
238
  method: req.method,
188
239
  headers: req.headers,
@@ -191,240 +242,217 @@ async function handleApiRequest(req, res) {
191
242
  text: async () => typeof body === 'string' ? body : JSON.stringify(body)
192
243
  };
193
244
 
194
- // Call the handler
195
- result = await methodHandler(request);
245
+ const result = await methodFn(request);
196
246
 
197
- // Handle NextResponse
198
247
  if (result && result.body !== undefined) {
199
- const statusCode = result.status || 200;
200
- const headers = {
201
- 'Access-Control-Allow-Origin': '*',
202
- ...Object.fromEntries(result.headers || [])
203
- };
204
-
205
- res.writeHead(statusCode, headers);
248
+ const headers = { 'Access-Control-Allow-Origin': '*', ...Object.fromEntries(result.headers || []) };
249
+ res.writeHead(result.status || 200, headers);
206
250
  res.end(result.body);
207
251
  }
208
252
  }
209
-
210
- } catch (error) {
211
- console.error(`āŒ API Error on /api/${routeName}:`, error);
253
+ } catch (err) {
254
+ console.error(`āŒ API Error /api/${routeName}:`, err);
212
255
  res.writeHead(500, { 'Content-Type': 'application/json' });
213
- res.end(JSON.stringify({ error: error.message }));
256
+ res.end(JSON.stringify({ error: err.message }));
214
257
  }
215
258
  }
216
259
 
217
- // Function to generate imports
218
- function generateImports() {
219
- console.log('šŸ”„ Regenerating imports...');
220
- exec('node scripts/generate-imports.js', (error, stdout, stderr) => {
221
- if (error) {
222
- console.error('āŒ Error:', error);
223
- return;
224
- }
225
- console.log(stdout);
226
- // Notify all connected clients to reload
227
- notifyClients();
228
- });
229
- }
260
+ // ─── HTTP Server ──────────────────────────────────────────────
261
+
262
+ const MIME = {
263
+ '.html': 'text/html',
264
+ '.js': 'text/javascript',
265
+ '.jsx': 'text/javascript',
266
+ '.mjs': 'text/javascript',
267
+ '.css': 'text/css',
268
+ '.json': 'application/json',
269
+ '.png': 'image/png',
270
+ '.jpg': 'image/jpeg',
271
+ '.svg': 'image/svg+xml',
272
+ '.ico': 'image/x-icon',
273
+ '.woff': 'font/woff',
274
+ '.woff2':'font/woff2',
275
+ '.ttf': 'font/ttf',
276
+ };
277
+
278
+ const NO_CACHE = {
279
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
280
+ 'Pragma': 'no-cache',
281
+ 'Expires': '0'
282
+ };
230
283
 
231
- // Notify all clients to reload
232
- function notifyClients() {
233
- const timestamp = new Date().toLocaleTimeString();
234
- console.log(`šŸ”„ [${timestamp}] Notifying ${clients.size} browser(s) to reload...`);
235
- clients.forEach(client => {
236
- try {
237
- client.send('reload');
238
- } catch (e) {
239
- clients.delete(client);
240
- }
241
- });
242
- }
243
-
244
- // Create HTTP server
245
284
  const server = http.createServer(async (req, res) => {
246
- // Handle API routes
247
- if (req.url.startsWith('/api/')) {
285
+ const urlWithoutQuery = req.url.split('?')[0];
286
+
287
+ // ── CORS preflight
288
+ if (req.method === 'OPTIONS') {
289
+ res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' });
290
+ res.end();
291
+ return;
292
+ }
293
+
294
+ // ── API routes
295
+ if (urlWithoutQuery.startsWith('/api/')) {
248
296
  await handleApiRequest(req, res);
249
297
  return;
250
298
  }
251
299
 
252
- // Handle CORS preflight
253
- if (req.method === 'OPTIONS') {
254
- res.writeHead(200, {
255
- 'Access-Control-Allow-Origin': '*',
256
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
257
- 'Access-Control-Allow-Headers': 'Content-Type'
258
- });
259
- res.end();
300
+ // ── Route manifest (used by router.js to discover pages)
301
+ if (urlWithoutQuery === '/_framework/routes.json') {
302
+ const manifest = buildRouteManifest();
303
+ res.writeHead(200, { 'Content-Type': 'application/json', ...NO_CACHE });
304
+ res.end(JSON.stringify(manifest));
260
305
  return;
261
306
  }
262
307
 
263
- // Strip query parameters for file serving (cache busting)
264
- let filePath = '.' + req.url.split('?')[0];
265
- if (filePath === './') {
266
- filePath = './app.html';
308
+ // ── node_modules bare imports: /_modules/mongoose → node_modules/mongoose
309
+ if (urlWithoutQuery.startsWith('/_modules/')) {
310
+ const specifier = urlWithoutQuery.slice('/_modules/'.length);
311
+ const filePath = resolveNodeModule(specifier);
312
+
313
+ if (!filePath) {
314
+ res.writeHead(404, { 'Content-Type': 'application/json' });
315
+ res.end(JSON.stringify({ error: `Module not found: ${specifier}. Run: npm install ${specifier.split('/')[0]}` }));
316
+ return;
317
+ }
318
+
319
+ try {
320
+ let code = fs.readFileSync(filePath, 'utf8');
321
+ code = rewriteImports(code, filePath);
322
+ res.writeHead(200, { 'Content-Type': 'text/javascript', ...NO_CACHE });
323
+ res.end(code);
324
+ } catch (err) {
325
+ res.writeHead(500);
326
+ res.end('Error reading module: ' + err.message);
327
+ }
328
+ return;
267
329
  }
268
330
 
269
- const extname = String(path.extname(filePath)).toLowerCase();
270
- const mimeTypes = {
271
- '.html': 'text/html',
272
- '.js': 'text/javascript',
273
- '.jsx': 'text/javascript',
274
- '.css': 'text/css',
275
- '.json': 'application/json',
276
- '.png': 'image/png',
277
- '.jpg': 'image/jpg',
278
- '.gif': 'image/gif',
279
- '.svg': 'image/svg+xml',
280
- '.wav': 'audio/wav',
281
- '.mp4': 'video/mp4',
282
- '.woff': 'application/font-woff',
283
- '.ttf': 'application/font-ttf',
284
- '.eot': 'application/vnd.ms-fontobject',
285
- '.otf': 'application/font-otf',
286
- '.wasm': 'application/wasm'
287
- };
331
+ // ── Static files
332
+ let filePath = path.join(ROOT, urlWithoutQuery);
333
+
334
+ // Default to index.html
335
+ if (urlWithoutQuery === '/' || urlWithoutQuery === '') {
336
+ filePath = path.join(ROOT, 'index.html');
337
+ }
288
338
 
289
- const contentType = mimeTypes[extname] || 'application/octet-stream';
339
+ // SPA fallback: any path without extension → index.html
340
+ const ext = path.extname(urlWithoutQuery);
341
+ if (!ext && urlWithoutQuery !== '/') {
342
+ filePath = path.join(ROOT, 'index.html');
343
+ }
290
344
 
291
- fs.readFile(filePath, (error, content) => {
292
- if (error) {
293
- if (error.code === 'ENOENT') {
294
- res.writeHead(404, { 'Content-Type': 'text/html' });
295
- res.end('<h1>404 - File Not Found</h1>', 'utf-8');
296
- } else {
297
- res.writeHead(500);
298
- res.end('Server Error: ' + error.code, 'utf-8');
299
- }
345
+ if (!fs.existsSync(filePath)) {
346
+ // Try with .js extension
347
+ if (fs.existsSync(filePath + '.js')) {
348
+ filePath = filePath + '.js';
300
349
  } else {
301
- // Add cache control headers to prevent caching during development
302
- res.writeHead(200, {
303
- 'Content-Type': contentType,
304
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
305
- 'Pragma': 'no-cache',
306
- 'Expires': '0'
307
- });
308
- res.end(content, 'utf-8');
350
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
351
+ res.end('Not found: ' + urlWithoutQuery);
352
+ return;
309
353
  }
310
- });
354
+ }
355
+
356
+ try {
357
+ let content = fs.readFileSync(filePath);
358
+ const fileExt = path.extname(filePath).toLowerCase();
359
+ const contentType = MIME[fileExt] || 'application/octet-stream';
360
+
361
+ // Transform JS/JSX files: rewrite imports
362
+ if (fileExt === '.js' || fileExt === '.jsx' || fileExt === '.mjs') {
363
+ let code = content.toString();
364
+ code = rewriteImports(code, filePath);
365
+ content = Buffer.from(code);
366
+ }
367
+
368
+ res.writeHead(200, { 'Content-Type': contentType, ...NO_CACHE });
369
+ res.end(content);
370
+ } catch (err) {
371
+ res.writeHead(500);
372
+ res.end('Server error: ' + err.message);
373
+ }
311
374
  });
312
375
 
313
- // WebSocket implementation (simple, no external dependencies)
314
- server.on('upgrade', (req, socket, head) => {
315
- // Simple WebSocket handshake
376
+ // ─── WebSocket (live reload) ──────────────────────────────────
377
+
378
+ server.on('upgrade', (req, socket) => {
316
379
  const key = req.headers['sec-websocket-key'];
317
- const acceptKey = require('crypto')
318
- .createHash('sha1')
380
+ const accept = crypto.createHash('sha1')
319
381
  .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
320
382
  .digest('base64');
321
383
 
322
384
  socket.write(
323
385
  'HTTP/1.1 101 Switching Protocols\r\n' +
324
- 'Upgrade: websocket\r\n' +
325
- 'Connection: Upgrade\r\n' +
326
- `Sec-WebSocket-Accept: ${acceptKey}\r\n` +
327
- '\r\n'
386
+ 'Upgrade: websocket\r\nConnection: Upgrade\r\n' +
387
+ `Sec-WebSocket-Accept: ${accept}\r\n\r\n`
328
388
  );
329
389
 
330
- const timestamp = new Date().toLocaleTimeString();
331
- console.log(`šŸ”Œ [${timestamp}] Browser connected (${clients.size + 1} total)`);
332
-
333
- // Wrap socket with send method
334
390
  const client = {
335
- socket: socket,
336
- send: function(message) {
337
- const buffer = Buffer.from(message);
338
- const frame = Buffer.alloc(buffer.length + 2);
339
- frame[0] = 0x81; // Text frame
340
- frame[1] = buffer.length;
341
- buffer.copy(frame, 2);
391
+ send(msg) {
392
+ const buf = Buffer.from(msg);
393
+ const frame = Buffer.alloc(buf.length + 2);
394
+ frame[0] = 0x81; frame[1] = buf.length;
395
+ buf.copy(frame, 2);
342
396
  socket.write(frame);
343
397
  }
344
398
  };
345
399
 
346
400
  clients.add(client);
401
+ console.log(`šŸ”Œ Browser connected (${clients.size} total)`);
347
402
 
348
- socket.on('close', () => {
349
- clients.delete(client);
350
- const closeTime = new Date().toLocaleTimeString();
351
- console.log(`šŸ”Œ [${closeTime}] Browser disconnected (${clients.size} remaining)`);
352
- });
403
+ socket.on('close', () => { clients.delete(client); });
404
+ socket.on('error', () => { clients.delete(client); });
405
+ });
406
+
407
+ function notifyClients() {
408
+ console.log(`šŸ”„ Reloading ${clients.size} browser(s)...`);
409
+ clients.forEach(c => { try { c.send('reload'); } catch { clients.delete(c); } });
410
+ }
411
+
412
+ // ─── File Watcher ─────────────────────────────────────────────
353
413
 
354
- socket.on('error', (err) => {
355
- clients.delete(client);
356
- console.log('āš ļø Socket error:', err.message);
414
+ const watchDirs = ['app', 'api', 'components', 'lib', 'models', 'styles', '_framework'];
415
+ let reloadTimer = null;
416
+
417
+ console.log('šŸ‘€ Watching:');
418
+ watchDirs.forEach(dir => {
419
+ const full = path.join(ROOT, dir);
420
+ if (!fs.existsSync(full)) return;
421
+ console.log(` - ${dir}/`);
422
+
423
+ fs.watch(full, { recursive: true }, (_, filename) => {
424
+ if (!filename) return;
425
+ const ext = path.extname(filename);
426
+ if (!['.js', '.jsx', '.css', '.json'].includes(ext)) return;
427
+
428
+ console.log(`\nšŸ“ Changed: ${dir}/${filename}`);
429
+ clearTimeout(reloadTimer);
430
+
431
+ if (dir === 'api') {
432
+ loadApiRoutes();
433
+ }
434
+
435
+ reloadTimer = setTimeout(notifyClients, 300);
357
436
  });
358
437
  });
359
438
 
360
- // Function to start server on available port
439
+ // ─── Start ────────────────────────────────────────────────────
440
+
361
441
  function startServer(port) {
362
442
  server.listen(port, () => {
363
- console.log(`🌐 Server running at http://localhost:${port}/`);
364
- console.log(`šŸ“± Open http://localhost:${port}/ in your browser\n`);
365
- }).on('error', (err) => {
443
+ console.log(`\nāœ… Dev server running!`);
444
+ console.log(`🌐 http://localhost:${port}/\n`);
445
+ console.log('ā¹ļø Ctrl+C to stop\n');
446
+ }).on('error', err => {
366
447
  if (err.code === 'EADDRINUSE') {
367
- console.log(`āš ļø Port ${port} is busy, trying ${port + 1}...`);
448
+ console.log(`āš ļø Port ${port} busy, trying ${port + 1}...`);
368
449
  startServer(port + 1);
369
450
  } else {
370
- console.error('āŒ Server error:', err);
451
+ console.error('āŒ', err);
371
452
  }
372
453
  });
373
454
  }
374
455
 
375
- // Watch directories
376
- const watchDirs = ['api', 'components', 'pages', 'modules', 'styles'];
377
-
378
- // Debounce to prevent multiple rapid reloads
379
- let reloadTimeout = null;
380
-
381
- console.log('šŸ‘€ Watching for changes in:');
382
- watchDirs.forEach(dir => {
383
- if (fs.existsSync(dir)) {
384
- console.log(` - ${dir}/`);
385
- fs.watch(dir, { recursive: false }, (eventType, filename) => {
386
- if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx') || filename.endsWith('.css'))) {
387
- console.log(`\nšŸ“ Detected change: ${dir}/${filename}`);
388
-
389
- // Clear existing timeout
390
- if (reloadTimeout) {
391
- clearTimeout(reloadTimeout);
392
- }
393
-
394
- // Reload API routes if api/ folder changed
395
- if (dir === 'api' && filename.endsWith('.js')) {
396
- console.log('šŸ”„ Reloading API routes...');
397
- loadApiRoutes();
398
- }
399
-
400
- // For CSS changes, just reload without regenerating imports
401
- if (filename.endsWith('.css')) {
402
- reloadTimeout = setTimeout(() => {
403
- notifyClients();
404
- }, 300);
405
- } else {
406
- // For JS/JSX changes, regenerate imports
407
- reloadTimeout = setTimeout(() => {
408
- generateImports();
409
- }, 500);
410
- }
411
- }
412
- });
413
- }
414
- });
415
-
416
- console.log('\nāœ… Dev server running with live reload!');
417
- console.log('šŸ’” Create/modify files and browser will auto-refresh');
418
- console.log('ā¹ļø Press Ctrl+C to stop\n');
419
-
420
- // Load API routes
421
456
  loadApiRoutes();
422
-
423
- // Start server
424
457
  startServer(PORT);
425
-
426
- // Generate imports on startup
427
- generateImports();
428
-
429
- // Keep the process running
430
458
  process.stdin.resume();