create-aziz-app 2.0.11 → 2.0.13

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.
@@ -1,54 +1,248 @@
1
1
  /** @jsx h */
2
2
 
3
3
  // ============================================================
4
- // STATE MANAGEMENT SYSTEM
5
- // A simple, reactive state management system
4
+ // STATE MANAGEMENT
5
+ // React-like hooks system (useState, useEffect, etc.)
6
+ // Exactly like React/Next.js!
6
7
  // ============================================================
7
8
 
8
- // Component state storage - keyed by component instance
9
- const componentStates = new Map();
10
9
  let currentComponent = null;
11
- let hookIndex = 0;
10
+ let currentHookIndex = 0;
11
+ const componentStates = new Map();
12
+
13
+ // Get or create state for current component
14
+ function getComponentState() {
15
+ if (!currentComponent) {
16
+ currentComponent = { hooks: [] };
17
+ }
18
+ return currentComponent;
19
+ }
12
20
 
13
- // Create a hook-like state for components
21
+ // useState - Exactly like React!
14
22
  function useState(initialValue) {
15
- // Create a unique key for this component and hook position
16
- const componentKey = currentComponent || 'global';
17
- const currentHookIndex = hookIndex++;
18
- const stateKey = `${componentKey}_${currentHookIndex}`;
19
-
20
- // Initialize if not exists
21
- if (!componentStates.has(stateKey)) {
22
- componentStates.set(stateKey, initialValue);
23
+ const component = getComponentState();
24
+ const hookIndex = currentHookIndex++;
25
+
26
+ // Initialize state if first render
27
+ if (component.hooks[hookIndex] === undefined) {
28
+ component.hooks[hookIndex] = {
29
+ value: typeof initialValue === 'function' ? initialValue() : initialValue
30
+ };
23
31
  }
32
+
33
+ const hook = component.hooks[hookIndex];
34
+
35
+ const setState = (newValue) => {
36
+ const nextValue = typeof newValue === 'function'
37
+ ? newValue(hook.value)
38
+ : newValue;
39
+
40
+ if (hook.value !== nextValue) {
41
+ hook.value = nextValue;
42
+ // Trigger re-render
43
+ if (typeof renderPage === 'function') {
44
+ renderPage();
45
+ }
46
+ }
47
+ };
48
+
49
+ return [hook.value, setState];
50
+ }
51
+
52
+ // useEffect - Exactly like React!
53
+ function useEffect(callback, dependencies) {
54
+ const component = getComponentState();
55
+ const hookIndex = currentHookIndex++;
56
+
57
+ const oldDeps = component.hooks[hookIndex]?.deps;
24
58
 
25
- // Get current value
26
- const currentValue = componentStates.get(stateKey);
27
-
28
- // Return getter and setter
29
- const setValue = (newValue) => {
30
- componentStates.set(stateKey, newValue);
31
- // Reset hook index before re-render
32
- hookIndex = 0;
33
- // Trigger re-render
34
- if (typeof renderPage === 'function') {
35
- renderPage();
59
+ let hasChanged = true;
60
+ if (oldDeps) {
61
+ hasChanged = dependencies.some((dep, i) => dep !== oldDeps[i]);
62
+ }
63
+
64
+ if (hasChanged) {
65
+ // Clean up previous effect
66
+ if (component.hooks[hookIndex]?.cleanup) {
67
+ component.hooks[hookIndex].cleanup();
36
68
  }
69
+
70
+ // Run effect after render
71
+ setTimeout(() => {
72
+ const cleanup = callback();
73
+ if (component.hooks[hookIndex]) {
74
+ component.hooks[hookIndex].cleanup = cleanup;
75
+ }
76
+ }, 0);
77
+ }
78
+
79
+ component.hooks[hookIndex] = {
80
+ deps: dependencies,
81
+ cleanup: component.hooks[hookIndex]?.cleanup
37
82
  };
83
+ }
84
+
85
+ // useMemo - Exactly like React!
86
+ function useMemo(factory, dependencies) {
87
+ const component = getComponentState();
88
+ const hookIndex = currentHookIndex++;
89
+
90
+ const oldDeps = component.hooks[hookIndex]?.deps;
38
91
 
39
- return [currentValue, setValue];
92
+ let hasChanged = true;
93
+ if (oldDeps) {
94
+ hasChanged = dependencies.some((dep, i) => dep !== oldDeps[i]);
95
+ }
96
+
97
+ if (hasChanged || !component.hooks[hookIndex]) {
98
+ const value = factory();
99
+ component.hooks[hookIndex] = {
100
+ value,
101
+ deps: dependencies
102
+ };
103
+ }
104
+
105
+ return component.hooks[hookIndex].value;
106
+ }
107
+
108
+ // useCallback - Exactly like React!
109
+ function useCallback(callback, dependencies) {
110
+ return useMemo(() => callback, dependencies);
40
111
  }
41
112
 
42
- // Reset hook index before each render
113
+ // useRef - Exactly like React!
114
+ function useRef(initialValue) {
115
+ const component = getComponentState();
116
+ const hookIndex = currentHookIndex++;
117
+
118
+ if (!component.hooks[hookIndex]) {
119
+ component.hooks[hookIndex] = {
120
+ current: initialValue
121
+ };
122
+ }
123
+
124
+ return component.hooks[hookIndex];
125
+ }
126
+
127
+ // useReducer - Exactly like React!
128
+ function useReducer(reducer, initialState, init) {
129
+ const [state, setState] = useState(
130
+ init ? init(initialState) : initialState
131
+ );
132
+
133
+ const dispatch = (action) => {
134
+ setState(prevState => reducer(prevState, action));
135
+ };
136
+
137
+ return [state, dispatch];
138
+ }
139
+
140
+ // Custom hook: useLocalStorage
141
+ function useLocalStorage(key, initialValue) {
142
+ const [storedValue, setStoredValue] = useState(() => {
143
+ try {
144
+ const item = window.localStorage.getItem(key);
145
+ return item ? JSON.parse(item) : initialValue;
146
+ } catch (error) {
147
+ console.error(error);
148
+ return initialValue;
149
+ }
150
+ });
151
+
152
+ const setValue = (value) => {
153
+ try {
154
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
155
+ setStoredValue(valueToStore);
156
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
157
+ } catch (error) {
158
+ console.error(error);
159
+ }
160
+ };
161
+
162
+ return [storedValue, setValue];
163
+ }
164
+
165
+ // Custom hook: useFetch
166
+ function useFetch(url, options = {}) {
167
+ const [data, setData] = useState(null);
168
+ const [loading, setLoading] = useState(true);
169
+ const [error, setError] = useState(null);
170
+
171
+ useEffect(() => {
172
+ let cancelled = false;
173
+
174
+ const fetchData = async () => {
175
+ try {
176
+ setLoading(true);
177
+ const response = await fetch(url, options);
178
+
179
+ if (!response.ok) {
180
+ throw new Error(`HTTP error! status: ${response.status}`);
181
+ }
182
+
183
+ const json = await response.json();
184
+
185
+ if (!cancelled) {
186
+ setData(json);
187
+ setError(null);
188
+ }
189
+ } catch (e) {
190
+ if (!cancelled) {
191
+ setError(e.message);
192
+ setData(null);
193
+ }
194
+ } finally {
195
+ if (!cancelled) {
196
+ setLoading(false);
197
+ }
198
+ }
199
+ };
200
+
201
+ fetchData();
202
+
203
+ return () => {
204
+ cancelled = true;
205
+ };
206
+ }, [url, JSON.stringify(options)]);
207
+
208
+ return { data, loading, error };
209
+ }
210
+
211
+ // Reset hooks before each render
43
212
  function resetHooks() {
44
- hookIndex = 0;
213
+ currentHookIndex = 0;
214
+ }
215
+
216
+ // Initialize component state
217
+ function initComponent() {
218
+ currentComponent = { hooks: [] };
219
+ currentHookIndex = 0;
45
220
  }
46
221
 
47
- // Export to global scope - make it available like React
222
+ // Export to global scope (like React)
48
223
  window.useState = useState;
224
+ window.useEffect = useEffect;
225
+ window.useMemo = useMemo;
226
+ window.useCallback = useCallback;
227
+ window.useRef = useRef;
228
+ window.useReducer = useReducer;
229
+ window.useLocalStorage = useLocalStorage;
230
+ window.useFetch = useFetch;
49
231
  window.resetHooks = resetHooks;
50
- // Also make it available without window prefix
232
+ window.initComponent = initComponent;
233
+
234
+ // Also make available without window prefix
51
235
  if (typeof globalThis !== 'undefined') {
52
236
  globalThis.useState = useState;
237
+ globalThis.useEffect = useEffect;
238
+ globalThis.useMemo = useMemo;
239
+ globalThis.useCallback = useCallback;
240
+ globalThis.useRef = useRef;
241
+ globalThis.useReducer = useReducer;
242
+ globalThis.useLocalStorage = useLocalStorage;
243
+ globalThis.useFetch = useFetch;
53
244
  globalThis.resetHooks = resetHooks;
54
- }
245
+ globalThis.initComponent = initComponent;
246
+ }
247
+
248
+ console.log('āœ… React-like hooks loaded: useState, useEffect, useMemo, useCallback, useRef, useReducer');
@@ -0,0 +1,194 @@
1
+ /** @jsx h */
2
+
3
+ // Demo page showing all React-like hooks
4
+ function HooksDemoPage() {
5
+ // useState - Basic state
6
+ const [count, setCount] = useState(0);
7
+ const [name, setName] = useState('');
8
+
9
+ // useEffect - Side effects
10
+ useEffect(() => {
11
+ console.log('Component mounted!');
12
+ document.title = `Count: ${count}`;
13
+
14
+ return () => {
15
+ console.log('Cleanup!');
16
+ };
17
+ }, [count]);
18
+
19
+ // useMemo - Memoized value
20
+ const expensiveValue = useMemo(() => {
21
+ console.log('Computing expensive value...');
22
+ return count * 2;
23
+ }, [count]);
24
+
25
+ // useCallback - Memoized function
26
+ const handleIncrement = useCallback(() => {
27
+ setCount(c => c + 1);
28
+ }, []);
29
+
30
+ // useRef - Mutable ref
31
+ const inputRef = useRef(null);
32
+
33
+ const focusInput = () => {
34
+ if (inputRef.current) {
35
+ inputRef.current.focus();
36
+ }
37
+ };
38
+
39
+ // useReducer - Complex state
40
+ const [state, dispatch] = useReducer((state, action) => {
41
+ switch (action.type) {
42
+ case 'increment':
43
+ return { ...state, value: state.value + 1 };
44
+ case 'decrement':
45
+ return { ...state, value: state.value - 1 };
46
+ case 'reset':
47
+ return { ...state, value: 0 };
48
+ default:
49
+ return state;
50
+ }
51
+ }, { value: 0 });
52
+
53
+ // useLocalStorage - Custom hook
54
+ const [savedName, setSavedName] = useLocalStorage('userName', '');
55
+
56
+ // useFetch - Custom hook
57
+ const { data, loading, error } = useFetch('/api/hello?name=Hooks');
58
+
59
+ return (
60
+ <div>
61
+ <h1>React Hooks Demo šŸŖ</h1>
62
+
63
+ <div class="card">
64
+ <h2>useState</h2>
65
+ <p>Basic state management</p>
66
+ <div class="count-display">{count}</div>
67
+ <div class="counter-actions">
68
+ <button class="btn btn-primary" onClick={() => setCount(count + 1)}>
69
+ Increment
70
+ </button>
71
+ <button class="btn btn-danger" onClick={() => setCount(count - 1)}>
72
+ Decrement
73
+ </button>
74
+ <button class="btn btn-grey" onClick={() => setCount(0)}>
75
+ Reset
76
+ </button>
77
+ </div>
78
+ </div>
79
+
80
+ <div class="card">
81
+ <h2>useEffect</h2>
82
+ <p>Check the browser console and page title!</p>
83
+ <p>Current count: <strong>{count}</strong></p>
84
+ <p style={{ color: '#666', fontSize: '14px' }}>
85
+ useEffect runs after render and updates the document title
86
+ </p>
87
+ </div>
88
+
89
+ <div class="card">
90
+ <h2>useMemo</h2>
91
+ <p>Memoized expensive computation</p>
92
+ <p>Count Ɨ 2 = <strong>{expensiveValue}</strong></p>
93
+ <p style={{ color: '#666', fontSize: '14px' }}>
94
+ Check console - computation only runs when count changes
95
+ </p>
96
+ </div>
97
+
98
+ <div class="card">
99
+ <h2>useCallback</h2>
100
+ <p>Memoized callback function</p>
101
+ <button class="btn btn-primary" onClick={handleIncrement}>
102
+ Increment (Memoized)
103
+ </button>
104
+ <p style={{ color: '#666', fontSize: '14px', marginTop: '8px' }}>
105
+ Function reference stays the same between renders
106
+ </p>
107
+ </div>
108
+
109
+ <div class="card">
110
+ <h2>useRef</h2>
111
+ <p>Mutable reference to DOM element</p>
112
+ <input
113
+ ref={inputRef}
114
+ type="text"
115
+ placeholder="Click button to focus me"
116
+ value={name}
117
+ onInput={(e) => setName(e.target.value)}
118
+ />
119
+ <button class="btn btn-primary" onClick={focusInput} style={{ marginTop: '8px' }}>
120
+ Focus Input
121
+ </button>
122
+ </div>
123
+
124
+ <div class="card">
125
+ <h2>useReducer</h2>
126
+ <p>Complex state management with actions</p>
127
+ <div class="count-display">{state.value}</div>
128
+ <div class="counter-actions">
129
+ <button class="btn btn-primary" onClick={() => dispatch({ type: 'increment' })}>
130
+ +
131
+ </button>
132
+ <button class="btn btn-danger" onClick={() => dispatch({ type: 'decrement' })}>
133
+ āˆ’
134
+ </button>
135
+ <button class="btn btn-grey" onClick={() => dispatch({ type: 'reset' })}>
136
+ Reset
137
+ </button>
138
+ </div>
139
+ </div>
140
+
141
+ <div class="card">
142
+ <h2>useLocalStorage</h2>
143
+ <p>Persistent state in localStorage</p>
144
+ <input
145
+ type="text"
146
+ placeholder="Enter your name"
147
+ value={savedName}
148
+ onInput={(e) => setSavedName(e.target.value)}
149
+ />
150
+ <p style={{ marginTop: '8px' }}>
151
+ Saved name: <strong>{savedName || 'Not set'}</strong>
152
+ </p>
153
+ <p style={{ color: '#666', fontSize: '14px' }}>
154
+ Refresh the page - your name persists!
155
+ </p>
156
+ </div>
157
+
158
+ <div class="card">
159
+ <h2>useFetch</h2>
160
+ <p>Data fetching with loading and error states</p>
161
+ {loading && <p>Loading...</p>}
162
+ {error && <p style={{ color: 'red' }}>Error: {error}</p>}
163
+ {data && (
164
+ <div style={{
165
+ background: '#f0f0f0',
166
+ padding: '12px',
167
+ borderRadius: '8px',
168
+ fontFamily: 'monospace',
169
+ fontSize: '14px'
170
+ }}>
171
+ <pre>{JSON.stringify(data, null, 2)}</pre>
172
+ </div>
173
+ )}
174
+ </div>
175
+
176
+ <div class="card">
177
+ <h2>All Hooks Available</h2>
178
+ <ul style={{ lineHeight: '1.8', marginLeft: '20px' }}>
179
+ <li><code>useState</code> - State management</li>
180
+ <li><code>useEffect</code> - Side effects</li>
181
+ <li><code>useMemo</code> - Memoized values</li>
182
+ <li><code>useCallback</code> - Memoized functions</li>
183
+ <li><code>useRef</code> - Mutable refs</li>
184
+ <li><code>useReducer</code> - Complex state</li>
185
+ <li><code>useLocalStorage</code> - Persistent state</li>
186
+ <li><code>useFetch</code> - Data fetching</li>
187
+ </ul>
188
+ <p style={{ marginTop: '12px', color: '#666' }}>
189
+ Just like React/Next.js! šŸŽ‰
190
+ </p>
191
+ </div>
192
+ </div>
193
+ );
194
+ }
@@ -7,9 +7,152 @@ const { exec } = require('child_process');
7
7
 
8
8
  const PORT = process.env.PORT || 3000;
9
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 module = require(filePath);
35
+
36
+ // Support both default export and module.exports
37
+ const handler = module.default || module;
38
+
39
+ if (typeof handler === 'function') {
40
+ apiHandlers[routeName] = handler;
41
+ console.log(` āœ… /api/${routeName}`);
42
+ } else {
43
+ console.log(` āš ļø /api/${routeName} - No handler function exported`);
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...');
@@ -38,7 +181,24 @@ function notifyClients() {
38
181
  }
39
182
 
40
183
  // Create HTTP server
41
- const server = http.createServer((req, res) => {
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'
197
+ });
198
+ res.end();
199
+ return;
200
+ }
201
+
42
202
  // Strip query parameters for file serving (cache busting)
43
203
  let filePath = '.' + req.url.split('?')[0];
44
204
  if (filePath === './') {
@@ -170,6 +330,12 @@ watchDirs.forEach(dir => {
170
330
  clearTimeout(reloadTimeout);
171
331
  }
172
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
+
173
339
  // For CSS changes, just reload without regenerating imports
174
340
  if (filename.endsWith('.css')) {
175
341
  reloadTimeout = setTimeout(() => {
@@ -190,6 +356,9 @@ console.log('\nāœ… Dev server running with live reload!');
190
356
  console.log('šŸ’” Create/modify files and browser will auto-refresh');
191
357
  console.log('ā¹ļø Press Ctrl+C to stop\n');
192
358
 
359
+ // Load API routes
360
+ loadApiRoutes();
361
+
193
362
  // Start server
194
363
  startServer(PORT);
195
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 => {