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.
Files changed (33) hide show
  1. package/README.md +658 -207
  2. package/bin/create-aziz-app.js +302 -302
  3. package/package.json +1 -1
  4. package/template/README.md +402 -402
  5. package/template/_framework/api.js +219 -219
  6. package/template/_framework/boot.js +33 -33
  7. package/template/_framework/hooks.js +367 -291
  8. package/template/_framework/next-response.js +128 -128
  9. package/template/_framework/route-match.js +145 -145
  10. package/template/_framework/router.js +465 -259
  11. package/template/_framework/runtime.js +264 -264
  12. package/template/app/layout.js +18 -18
  13. package/template/app/page.js +33 -33
  14. package/template/docs/01-getting-started.md +151 -151
  15. package/template/docs/02-project-structure.md +109 -109
  16. package/template/docs/03-pages-and-layouts.md +140 -140
  17. package/template/docs/04-routing.md +120 -120
  18. package/template/docs/05-hooks.md +169 -169
  19. package/template/docs/06-api-routes.md +210 -210
  20. package/template/docs/07-api-client.md +151 -151
  21. package/template/docs/08-styling.md +94 -94
  22. package/template/docs/09-imports-and-modules.md +97 -97
  23. package/template/docs/10-database-and-models.md +154 -154
  24. package/template/docs/11-authentication.md +171 -171
  25. package/template/docs/12-production-and-deployment.md +121 -121
  26. package/template/docs/13-internals.md +179 -179
  27. package/template/docs/README.md +67 -67
  28. package/template/index.html +20 -20
  29. package/template/package.json +16 -16
  30. package/template/scripts/dev-server.js +493 -381
  31. package/template/scripts/lib/api-router.js +525 -525
  32. package/template/scripts/lib/js-transform.js +213 -195
  33. package/template/scripts/lib/module-resolver.js +132 -132
@@ -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