create-aziz-app 3.1.2 → 3.1.3
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.
- package/README.md +658 -207
- package/bin/create-aziz-app.js +302 -302
- package/package.json +1 -1
- package/template/README.md +402 -402
- package/template/_framework/api.js +219 -219
- package/template/_framework/boot.js +33 -33
- package/template/_framework/hooks.js +367 -291
- package/template/_framework/next-response.js +128 -128
- package/template/_framework/route-match.js +145 -145
- package/template/_framework/router.js +465 -259
- package/template/_framework/runtime.js +264 -264
- package/template/app/layout.js +18 -18
- package/template/app/page.js +33 -33
- package/template/docs/01-getting-started.md +151 -151
- package/template/docs/02-project-structure.md +109 -109
- package/template/docs/03-pages-and-layouts.md +140 -140
- package/template/docs/04-routing.md +120 -120
- package/template/docs/05-hooks.md +169 -169
- package/template/docs/06-api-routes.md +210 -210
- package/template/docs/07-api-client.md +151 -151
- package/template/docs/08-styling.md +94 -94
- package/template/docs/09-imports-and-modules.md +97 -97
- package/template/docs/10-database-and-models.md +154 -154
- package/template/docs/11-authentication.md +171 -171
- package/template/docs/12-production-and-deployment.md +121 -121
- package/template/docs/13-internals.md +179 -179
- package/template/docs/README.md +67 -67
- package/template/index.html +20 -20
- package/template/package.json +16 -16
- package/template/scripts/dev-server.js +493 -381
- package/template/scripts/lib/api-router.js +525 -525
- package/template/scripts/lib/js-transform.js +213 -195
- package/template/scripts/lib/module-resolver.js +132 -132
package/template/README.md
CHANGED
|
@@ -1,402 +1,402 @@
|
|
|
1
|
-
# ⚡ Aziz.js
|
|
2
|
-
|
|
3
|
-
A lightweight Next.js clone with file-based routing, React hooks, and ES module imports — **zero dependencies, instant startup**.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
Next.js: ~400KB + 20 dependencies + 10s startup
|
|
7
|
-
Aziz.js: ~50KB + 0 dependencies + <1s startup
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Quick Start
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install
|
|
16
|
-
npm run dev
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Open [http://localhost:3000](http://localhost:3000) — that's it.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Project Structure
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
my-app/
|
|
27
|
-
├── index.html ← entry point (don't touch)
|
|
28
|
-
│
|
|
29
|
-
├── app/ ← your pages and layouts
|
|
30
|
-
│ ├── layout.js ← root layout (wraps every page)
|
|
31
|
-
│ └── page.js ← route: /
|
|
32
|
-
│
|
|
33
|
-
├── styles/
|
|
34
|
-
│ └── globals.css ← global styles
|
|
35
|
-
│
|
|
36
|
-
├── _framework/ ← runtime, hooks, router (don't touch)
|
|
37
|
-
├── scripts/ ← dev server + live reload (don't touch)
|
|
38
|
-
└── docs/ ← full documentation
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Pages
|
|
44
|
-
|
|
45
|
-
Every `page.js` inside `app/` becomes a route. Export a default function — no imports needed.
|
|
46
|
-
|
|
47
|
-
```js
|
|
48
|
-
// app/about/page.js → route: /about
|
|
49
|
-
export default function Page() {
|
|
50
|
-
return (
|
|
51
|
-
<div>
|
|
52
|
-
<h1>About</h1>
|
|
53
|
-
<p>Hello from the about page!</p>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Route mapping
|
|
60
|
-
|
|
61
|
-
| File | Route |
|
|
62
|
-
|------|-------|
|
|
63
|
-
| `app/page.js` | `/` |
|
|
64
|
-
| `app/about/page.js` | `/about` |
|
|
65
|
-
| `app/blog/page.js` | `/blog` |
|
|
66
|
-
| `app/blog/[slug]/page.js` | `/blog/:slug` |
|
|
67
|
-
| `app/blog/[...slug]/page.js` | `/blog/*` (catch-all) |
|
|
68
|
-
|
|
69
|
-
### Creating a new page
|
|
70
|
-
|
|
71
|
-
Just create the file — no registration needed:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
mkdir app/dashboard
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
```js
|
|
78
|
-
// app/dashboard/page.js → route: /dashboard
|
|
79
|
-
export default function Page() {
|
|
80
|
-
return <h1>Dashboard</h1>;
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Layouts
|
|
87
|
-
|
|
88
|
-
`layout.js` wraps all pages at the same level and below. The root `app/layout.js` wraps every page.
|
|
89
|
-
|
|
90
|
-
```js
|
|
91
|
-
// app/layout.js
|
|
92
|
-
export default function Layout({ children }) {
|
|
93
|
-
return (
|
|
94
|
-
<div>
|
|
95
|
-
<main>{children}</main>
|
|
96
|
-
</div>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Nested layouts
|
|
102
|
-
|
|
103
|
-
```js
|
|
104
|
-
// app/dashboard/layout.js — wraps all /dashboard/* pages
|
|
105
|
-
export default function Layout({ children }) {
|
|
106
|
-
return (
|
|
107
|
-
<div style={{ display: 'flex' }}>
|
|
108
|
-
<div style={{ flex: 1 }}>{children}</div>
|
|
109
|
-
</div>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Navigation
|
|
117
|
-
|
|
118
|
-
### navigate()
|
|
119
|
-
|
|
120
|
-
```js
|
|
121
|
-
navigate('/about');
|
|
122
|
-
navigate('/blog/my-post');
|
|
123
|
-
navigate('/');
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Link component
|
|
127
|
-
|
|
128
|
-
```js
|
|
129
|
-
<Link href="/about">About</Link>
|
|
130
|
-
<Link href="/blog/hello" className="nav-link">Blog Post</Link>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### useRouter hook
|
|
134
|
-
|
|
135
|
-
```js
|
|
136
|
-
export default function Page() {
|
|
137
|
-
const router = useRouter();
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<div>
|
|
141
|
-
<p>Current path: {router.pathname}</p>
|
|
142
|
-
<button onClick={() => router.push('/about')}>Go to About</button>
|
|
143
|
-
<button onClick={() => router.back()}>Back</button>
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### useParams — dynamic routes
|
|
150
|
-
|
|
151
|
-
```js
|
|
152
|
-
// app/blog/[slug]/page.js
|
|
153
|
-
export default function Page() {
|
|
154
|
-
const { slug } = useParams();
|
|
155
|
-
return <h1>Post: {slug}</h1>;
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### useSearchParams — query strings
|
|
160
|
-
|
|
161
|
-
```js
|
|
162
|
-
// URL: /search?q=hello&page=2
|
|
163
|
-
export default function Page() {
|
|
164
|
-
const searchParams = useSearchParams();
|
|
165
|
-
const query = searchParams.get('q'); // "hello"
|
|
166
|
-
const page = searchParams.get('page'); // "2"
|
|
167
|
-
|
|
168
|
-
return <p>Searching for: {query}</p>;
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## React Hooks
|
|
175
|
-
|
|
176
|
-
All hooks work exactly like React — **no imports needed**.
|
|
177
|
-
|
|
178
|
-
### useState
|
|
179
|
-
|
|
180
|
-
```js
|
|
181
|
-
const [count, setCount] = useState(0);
|
|
182
|
-
|
|
183
|
-
setCount(prev => prev + 1);
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### useEffect
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
document.title = `Count: ${count}`;
|
|
191
|
-
}, [count]);
|
|
192
|
-
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
const timer = setInterval(() => tick(), 1000);
|
|
195
|
-
return () => clearInterval(timer);
|
|
196
|
-
}, []);
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### useMemo
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
const total = useMemo(() => {
|
|
203
|
-
return items.filter(i => i.active).length;
|
|
204
|
-
}, [items]);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### useCallback
|
|
208
|
-
|
|
209
|
-
```js
|
|
210
|
-
const handleClick = useCallback(() => {
|
|
211
|
-
doSomething(id);
|
|
212
|
-
}, [id]);
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### useRef
|
|
216
|
-
|
|
217
|
-
```js
|
|
218
|
-
const inputRef = useRef(null);
|
|
219
|
-
|
|
220
|
-
<input ref={inputRef} type="text" />
|
|
221
|
-
|
|
222
|
-
inputRef.current.focus();
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### useReducer
|
|
226
|
-
|
|
227
|
-
```js
|
|
228
|
-
const reducer = (state, action) => {
|
|
229
|
-
switch (action.type) {
|
|
230
|
-
case 'increment': return { count: state.count + 1 };
|
|
231
|
-
case 'decrement': return { count: state.count - 1 };
|
|
232
|
-
default: return state;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const [state, dispatch] = useReducer(reducer, { count: 0 });
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### useContext
|
|
240
|
-
|
|
241
|
-
```js
|
|
242
|
-
const ThemeContext = createContext('light');
|
|
243
|
-
|
|
244
|
-
<ThemeContext.Provider value="dark">
|
|
245
|
-
{children}
|
|
246
|
-
</ThemeContext.Provider>
|
|
247
|
-
|
|
248
|
-
const theme = useContext(ThemeContext);
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### useLocalStorage
|
|
252
|
-
|
|
253
|
-
```js
|
|
254
|
-
const [name, setName] = useLocalStorage('userName', '');
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Imports
|
|
260
|
-
|
|
261
|
-
### Importing other files
|
|
262
|
-
|
|
263
|
-
```js
|
|
264
|
-
// Absolute path
|
|
265
|
-
import Button from '/components/Button.js';
|
|
266
|
-
|
|
267
|
-
// @/ alias (same as absolute)
|
|
268
|
-
import Button from '@/components/Button';
|
|
269
|
-
|
|
270
|
-
// npm packages (install first: npm install lodash)
|
|
271
|
-
import _ from 'lodash';
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
The dev server resolves bare imports from `node_modules` automatically — no bundler config needed.
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## Components
|
|
279
|
-
|
|
280
|
-
Components are just functions that return JSX — no pragma or imports needed.
|
|
281
|
-
|
|
282
|
-
```js
|
|
283
|
-
// components/Button.js
|
|
284
|
-
export default function Button({ onClick, children, variant = 'primary' }) {
|
|
285
|
-
return (
|
|
286
|
-
<button class={`btn btn-${variant}`} onClick={onClick}>
|
|
287
|
-
{children}
|
|
288
|
-
</button>
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
```js
|
|
294
|
-
// Using the component
|
|
295
|
-
import Button from '/components/Button.js';
|
|
296
|
-
|
|
297
|
-
<Button onClick={() => alert('hi')} variant="danger">Delete</Button>
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### JSX rules
|
|
301
|
-
|
|
302
|
-
```js
|
|
303
|
-
// Use class instead of className
|
|
304
|
-
<div class="container">
|
|
305
|
-
|
|
306
|
-
// Use onInput for text inputs (not onChange)
|
|
307
|
-
<input onInput={(e) => setValue(e.target.value)} />
|
|
308
|
-
|
|
309
|
-
// Self-closing tags need /
|
|
310
|
-
<input type="text" />
|
|
311
|
-
|
|
312
|
-
// Expressions in {}
|
|
313
|
-
<p>{count > 0 ? 'positive' : 'zero'}</p>
|
|
314
|
-
|
|
315
|
-
// Lists
|
|
316
|
-
{items.map(item => <li key={item.id}>{item.name}</li>)}
|
|
317
|
-
|
|
318
|
-
// Conditional rendering
|
|
319
|
-
{isLoggedIn && <Dashboard />}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## Styling
|
|
325
|
-
|
|
326
|
-
### globals.css
|
|
327
|
-
|
|
328
|
-
Edit `styles/globals.css` for global styles. Built-in utility classes:
|
|
329
|
-
|
|
330
|
-
```css
|
|
331
|
-
.card { background: white; border-radius: 10px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin-bottom: 20px; }
|
|
332
|
-
.btn { display: inline-block; padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer; }
|
|
333
|
-
.btn-primary { background: #61dafb; color: #282c34; font-weight: bold; }
|
|
334
|
-
.btn-danger { background: #ff6b6b; color: white; }
|
|
335
|
-
.btn-grey { background: #ccc; color: #333; }
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Inline styles
|
|
339
|
-
|
|
340
|
-
```js
|
|
341
|
-
<div style={{ padding: '20px', color: 'red', fontSize: '16px' }}>
|
|
342
|
-
Hello
|
|
343
|
-
</div>
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### Tailwind CSS
|
|
347
|
-
|
|
348
|
-
If you scaffolded with Tailwind, it is loaded via CDN with `primary` and `dark` colors pre-configured:
|
|
349
|
-
|
|
350
|
-
```js
|
|
351
|
-
// Use Tailwind classes directly in any file
|
|
352
|
-
<div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow">
|
|
353
|
-
<h1 class="text-2xl font-bold text-dark">Hello</h1>
|
|
354
|
-
</div>
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
To extend the config, edit the `tailwind.config` block in `index.html`.
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## Dev Server
|
|
362
|
-
|
|
363
|
-
```bash
|
|
364
|
-
npm run dev
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
- Serves your app at `http://localhost:3000`
|
|
368
|
-
- Watches all source folders and live-reloads on save
|
|
369
|
-
- Resolves bare imports from `node_modules`
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
# Custom port
|
|
373
|
-
PORT=4000 npm run dev
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
---
|
|
377
|
-
|
|
378
|
-
## Comparison with Next.js
|
|
379
|
-
|
|
380
|
-
| Feature | Aziz.js | Next.js |
|
|
381
|
-
|---------|---------|---------|
|
|
382
|
-
| File-based routing | ✅ | ✅ |
|
|
383
|
-
| `page.js` convention | ✅ | ✅ |
|
|
384
|
-
| `layout.js` convention | ✅ | ✅ |
|
|
385
|
-
| Dynamic routes `[slug]` | ✅ | ✅ |
|
|
386
|
-
| Catch-all `[...slug]` | ✅ | ✅ |
|
|
387
|
-
| React hooks | ✅ | ✅ |
|
|
388
|
-
| No imports needed in pages | ✅ | ❌ |
|
|
389
|
-
| ES module imports | ✅ | ✅ |
|
|
390
|
-
| `@/` path alias | ✅ | ✅ |
|
|
391
|
-
| Live reload | ✅ | ✅ |
|
|
392
|
-
| SSR / SSG | ❌ | ✅ |
|
|
393
|
-
| TypeScript | ❌ | ✅ |
|
|
394
|
-
| Bundle size | **~50KB** | ~400KB+ |
|
|
395
|
-
| Dependencies | **0** | 20+ |
|
|
396
|
-
| Startup time | **< 1s** | 10–30s |
|
|
397
|
-
|
|
398
|
-
---
|
|
399
|
-
|
|
400
|
-
## License
|
|
401
|
-
|
|
402
|
-
MIT
|
|
1
|
+
# ⚡ Aziz.js
|
|
2
|
+
|
|
3
|
+
A lightweight Next.js clone with file-based routing, React hooks, and ES module imports — **zero dependencies, instant startup**.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Next.js: ~400KB + 20 dependencies + 10s startup
|
|
7
|
+
Aziz.js: ~50KB + 0 dependencies + <1s startup
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Open [http://localhost:3000](http://localhost:3000) — that's it.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Project Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
my-app/
|
|
27
|
+
├── index.html ← entry point (don't touch)
|
|
28
|
+
│
|
|
29
|
+
├── app/ ← your pages and layouts
|
|
30
|
+
│ ├── layout.js ← root layout (wraps every page)
|
|
31
|
+
│ └── page.js ← route: /
|
|
32
|
+
│
|
|
33
|
+
├── styles/
|
|
34
|
+
│ └── globals.css ← global styles
|
|
35
|
+
│
|
|
36
|
+
├── _framework/ ← runtime, hooks, router (don't touch)
|
|
37
|
+
├── scripts/ ← dev server + live reload (don't touch)
|
|
38
|
+
└── docs/ ← full documentation
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Pages
|
|
44
|
+
|
|
45
|
+
Every `page.js` inside `app/` becomes a route. Export a default function — no imports needed.
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// app/about/page.js → route: /about
|
|
49
|
+
export default function Page() {
|
|
50
|
+
return (
|
|
51
|
+
<div>
|
|
52
|
+
<h1>About</h1>
|
|
53
|
+
<p>Hello from the about page!</p>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Route mapping
|
|
60
|
+
|
|
61
|
+
| File | Route |
|
|
62
|
+
|------|-------|
|
|
63
|
+
| `app/page.js` | `/` |
|
|
64
|
+
| `app/about/page.js` | `/about` |
|
|
65
|
+
| `app/blog/page.js` | `/blog` |
|
|
66
|
+
| `app/blog/[slug]/page.js` | `/blog/:slug` |
|
|
67
|
+
| `app/blog/[...slug]/page.js` | `/blog/*` (catch-all) |
|
|
68
|
+
|
|
69
|
+
### Creating a new page
|
|
70
|
+
|
|
71
|
+
Just create the file — no registration needed:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
mkdir app/dashboard
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// app/dashboard/page.js → route: /dashboard
|
|
79
|
+
export default function Page() {
|
|
80
|
+
return <h1>Dashboard</h1>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Layouts
|
|
87
|
+
|
|
88
|
+
`layout.js` wraps all pages at the same level and below. The root `app/layout.js` wraps every page.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// app/layout.js
|
|
92
|
+
export default function Layout({ children }) {
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
<main>{children}</main>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Nested layouts
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
// app/dashboard/layout.js — wraps all /dashboard/* pages
|
|
105
|
+
export default function Layout({ children }) {
|
|
106
|
+
return (
|
|
107
|
+
<div style={{ display: 'flex' }}>
|
|
108
|
+
<div style={{ flex: 1 }}>{children}</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Navigation
|
|
117
|
+
|
|
118
|
+
### navigate()
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
navigate('/about');
|
|
122
|
+
navigate('/blog/my-post');
|
|
123
|
+
navigate('/');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Link component
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
<Link href="/about">About</Link>
|
|
130
|
+
<Link href="/blog/hello" className="nav-link">Blog Post</Link>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### useRouter hook
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
export default function Page() {
|
|
137
|
+
const router = useRouter();
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div>
|
|
141
|
+
<p>Current path: {router.pathname}</p>
|
|
142
|
+
<button onClick={() => router.push('/about')}>Go to About</button>
|
|
143
|
+
<button onClick={() => router.back()}>Back</button>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### useParams — dynamic routes
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
// app/blog/[slug]/page.js
|
|
153
|
+
export default function Page() {
|
|
154
|
+
const { slug } = useParams();
|
|
155
|
+
return <h1>Post: {slug}</h1>;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### useSearchParams — query strings
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// URL: /search?q=hello&page=2
|
|
163
|
+
export default function Page() {
|
|
164
|
+
const searchParams = useSearchParams();
|
|
165
|
+
const query = searchParams.get('q'); // "hello"
|
|
166
|
+
const page = searchParams.get('page'); // "2"
|
|
167
|
+
|
|
168
|
+
return <p>Searching for: {query}</p>;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## React Hooks
|
|
175
|
+
|
|
176
|
+
All hooks work exactly like React — **no imports needed**.
|
|
177
|
+
|
|
178
|
+
### useState
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
const [count, setCount] = useState(0);
|
|
182
|
+
|
|
183
|
+
setCount(prev => prev + 1);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### useEffect
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
document.title = `Count: ${count}`;
|
|
191
|
+
}, [count]);
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
const timer = setInterval(() => tick(), 1000);
|
|
195
|
+
return () => clearInterval(timer);
|
|
196
|
+
}, []);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### useMemo
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
const total = useMemo(() => {
|
|
203
|
+
return items.filter(i => i.active).length;
|
|
204
|
+
}, [items]);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### useCallback
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
const handleClick = useCallback(() => {
|
|
211
|
+
doSomething(id);
|
|
212
|
+
}, [id]);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### useRef
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
const inputRef = useRef(null);
|
|
219
|
+
|
|
220
|
+
<input ref={inputRef} type="text" />
|
|
221
|
+
|
|
222
|
+
inputRef.current.focus();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### useReducer
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
const reducer = (state, action) => {
|
|
229
|
+
switch (action.type) {
|
|
230
|
+
case 'increment': return { count: state.count + 1 };
|
|
231
|
+
case 'decrement': return { count: state.count - 1 };
|
|
232
|
+
default: return state;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const [state, dispatch] = useReducer(reducer, { count: 0 });
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### useContext
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
const ThemeContext = createContext('light');
|
|
243
|
+
|
|
244
|
+
<ThemeContext.Provider value="dark">
|
|
245
|
+
{children}
|
|
246
|
+
</ThemeContext.Provider>
|
|
247
|
+
|
|
248
|
+
const theme = useContext(ThemeContext);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### useLocalStorage
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
const [name, setName] = useLocalStorage('userName', '');
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Imports
|
|
260
|
+
|
|
261
|
+
### Importing other files
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
// Absolute path
|
|
265
|
+
import Button from '/components/Button.js';
|
|
266
|
+
|
|
267
|
+
// @/ alias (same as absolute)
|
|
268
|
+
import Button from '@/components/Button';
|
|
269
|
+
|
|
270
|
+
// npm packages (install first: npm install lodash)
|
|
271
|
+
import _ from 'lodash';
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The dev server resolves bare imports from `node_modules` automatically — no bundler config needed.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Components
|
|
279
|
+
|
|
280
|
+
Components are just functions that return JSX — no pragma or imports needed.
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
// components/Button.js
|
|
284
|
+
export default function Button({ onClick, children, variant = 'primary' }) {
|
|
285
|
+
return (
|
|
286
|
+
<button class={`btn btn-${variant}`} onClick={onClick}>
|
|
287
|
+
{children}
|
|
288
|
+
</button>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
// Using the component
|
|
295
|
+
import Button from '/components/Button.js';
|
|
296
|
+
|
|
297
|
+
<Button onClick={() => alert('hi')} variant="danger">Delete</Button>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### JSX rules
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// Use class instead of className
|
|
304
|
+
<div class="container">
|
|
305
|
+
|
|
306
|
+
// Use onInput for text inputs (not onChange)
|
|
307
|
+
<input onInput={(e) => setValue(e.target.value)} />
|
|
308
|
+
|
|
309
|
+
// Self-closing tags need /
|
|
310
|
+
<input type="text" />
|
|
311
|
+
|
|
312
|
+
// Expressions in {}
|
|
313
|
+
<p>{count > 0 ? 'positive' : 'zero'}</p>
|
|
314
|
+
|
|
315
|
+
// Lists
|
|
316
|
+
{items.map(item => <li key={item.id}>{item.name}</li>)}
|
|
317
|
+
|
|
318
|
+
// Conditional rendering
|
|
319
|
+
{isLoggedIn && <Dashboard />}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Styling
|
|
325
|
+
|
|
326
|
+
### globals.css
|
|
327
|
+
|
|
328
|
+
Edit `styles/globals.css` for global styles. Built-in utility classes:
|
|
329
|
+
|
|
330
|
+
```css
|
|
331
|
+
.card { background: white; border-radius: 10px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin-bottom: 20px; }
|
|
332
|
+
.btn { display: inline-block; padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer; }
|
|
333
|
+
.btn-primary { background: #61dafb; color: #282c34; font-weight: bold; }
|
|
334
|
+
.btn-danger { background: #ff6b6b; color: white; }
|
|
335
|
+
.btn-grey { background: #ccc; color: #333; }
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Inline styles
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
<div style={{ padding: '20px', color: 'red', fontSize: '16px' }}>
|
|
342
|
+
Hello
|
|
343
|
+
</div>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Tailwind CSS
|
|
347
|
+
|
|
348
|
+
If you scaffolded with Tailwind, it is loaded via CDN with `primary` and `dark` colors pre-configured:
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
// Use Tailwind classes directly in any file
|
|
352
|
+
<div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow">
|
|
353
|
+
<h1 class="text-2xl font-bold text-dark">Hello</h1>
|
|
354
|
+
</div>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
To extend the config, edit the `tailwind.config` block in `index.html`.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Dev Server
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
npm run dev
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
- Serves your app at `http://localhost:3000`
|
|
368
|
+
- Watches all source folders and live-reloads on save
|
|
369
|
+
- Resolves bare imports from `node_modules`
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Custom port
|
|
373
|
+
PORT=4000 npm run dev
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Comparison with Next.js
|
|
379
|
+
|
|
380
|
+
| Feature | Aziz.js | Next.js |
|
|
381
|
+
|---------|---------|---------|
|
|
382
|
+
| File-based routing | ✅ | ✅ |
|
|
383
|
+
| `page.js` convention | ✅ | ✅ |
|
|
384
|
+
| `layout.js` convention | ✅ | ✅ |
|
|
385
|
+
| Dynamic routes `[slug]` | ✅ | ✅ |
|
|
386
|
+
| Catch-all `[...slug]` | ✅ | ✅ |
|
|
387
|
+
| React hooks | ✅ | ✅ |
|
|
388
|
+
| No imports needed in pages | ✅ | ❌ |
|
|
389
|
+
| ES module imports | ✅ | ✅ |
|
|
390
|
+
| `@/` path alias | ✅ | ✅ |
|
|
391
|
+
| Live reload | ✅ | ✅ |
|
|
392
|
+
| SSR / SSG | ❌ | ✅ |
|
|
393
|
+
| TypeScript | ❌ | ✅ |
|
|
394
|
+
| Bundle size | **~50KB** | ~400KB+ |
|
|
395
|
+
| Dependencies | **0** | 20+ |
|
|
396
|
+
| Startup time | **< 1s** | 10–30s |
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT
|