create-aziz-app 2.0.12 → 2.0.14

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
+ }
@@ -30,17 +30,34 @@ function loadApiRoutes() {
30
30
  // Clear require cache to allow hot reload
31
31
  delete require.cache[require.resolve(filePath)];
32
32
 
33
- // Load the API handler
34
- const handler = require(filePath);
33
+ // Load the API module
34
+ const module = require(filePath);
35
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}`);
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
42
+ 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
+
49
+ apiHandlers[routeName] = methods;
50
+ console.log(` ✅ /api/${routeName} [${Object.keys(methods).join(', ')}]`);
42
51
  } else {
43
- console.log(` ⚠️ /api/${routeName} - No default export`);
52
+ // Old style: Default export
53
+ const handler = module.default || module;
54
+
55
+ if (typeof handler === 'function') {
56
+ apiHandlers[routeName] = handler;
57
+ console.log(` ✅ /api/${routeName}`);
58
+ } else {
59
+ console.log(` ⚠️ /api/${routeName} - No handler function exported`);
60
+ }
44
61
  }
45
62
  } catch (error) {
46
63
  console.error(` ❌ /api/${routeName} - ${error.message}`);
@@ -49,6 +66,7 @@ function loadApiRoutes() {
49
66
 
50
67
  console.log('');
51
68
  }
69
+ }
52
70
 
53
71
  // Parse request body
54
72
  function parseBody(req) {
@@ -103,48 +121,91 @@ async function handleApiRequest(req, res) {
103
121
  const body = await parseBody(req);
104
122
  const query = parseQuery(req.url);
105
123
 
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
+ // 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
+
137
+ 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;
152
+ },
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;
161
+ },
162
+ setHeader(key, value) {
163
+ this.headers[key] = value;
164
+ return this;
165
+ }
166
+ };
167
+
168
+ 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, {
124
175
  '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
176
+ 'Allow': Object.keys(handler).join(', ')
136
177
  });
137
- res.end(data);
138
- return this;
139
- },
140
- setHeader(key, value) {
141
- this.headers[key] = value;
142
- return this;
178
+ res.end(JSON.stringify({
179
+ error: 'Method not allowed',
180
+ allowedMethods: Object.keys(handler)
181
+ }));
182
+ return;
143
183
  }
144
- };
145
184
 
146
- // Call the handler
147
- await handler(apiReq, apiRes);
185
+ // Create Next.js-style request object
186
+ const request = {
187
+ method: req.method,
188
+ headers: req.headers,
189
+ url: `http://localhost:${PORT}${req.url}`,
190
+ json: async () => body,
191
+ text: async () => typeof body === 'string' ? body : JSON.stringify(body)
192
+ };
193
+
194
+ // Call the handler
195
+ result = await methodHandler(request);
196
+
197
+ // Handle NextResponse
198
+ 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);
206
+ res.end(result.body);
207
+ }
208
+ }
148
209
 
149
210
  } catch (error) {
150
211
  console.error(`❌ API Error on /api/${routeName}:`, error);