@nitronjs/framework 0.2.27 → 0.3.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 (58) hide show
  1. package/README.md +260 -170
  2. package/lib/Auth/Auth.js +2 -2
  3. package/lib/Build/CssBuilder.js +5 -7
  4. package/lib/Build/EffectivePropUsage.js +174 -0
  5. package/lib/Build/FactoryTransform.js +1 -21
  6. package/lib/Build/FileAnalyzer.js +1 -32
  7. package/lib/Build/Manager.js +354 -58
  8. package/lib/Build/PropUsageAnalyzer.js +1189 -0
  9. package/lib/Build/jsxRuntime.js +25 -155
  10. package/lib/Build/plugins.js +212 -146
  11. package/lib/Build/propUtils.js +70 -0
  12. package/lib/Console/Commands/DevCommand.js +30 -10
  13. package/lib/Console/Commands/MakeCommand.js +8 -1
  14. package/lib/Console/Output.js +0 -2
  15. package/lib/Console/Stubs/rsc-consumer.tsx +74 -0
  16. package/lib/Console/Stubs/vendor-dev.tsx +30 -41
  17. package/lib/Console/Stubs/vendor.tsx +25 -1
  18. package/lib/Core/Config.js +0 -6
  19. package/lib/Core/Paths.js +0 -19
  20. package/lib/Database/Migration/Checksum.js +0 -3
  21. package/lib/Database/Migration/MigrationRepository.js +0 -8
  22. package/lib/Database/Migration/MigrationRunner.js +1 -2
  23. package/lib/Database/Model.js +19 -11
  24. package/lib/Database/QueryBuilder.js +25 -4
  25. package/lib/Database/Schema/Blueprint.js +10 -0
  26. package/lib/Database/Schema/Manager.js +2 -0
  27. package/lib/Date/DateTime.js +1 -1
  28. package/lib/Dev/DevContext.js +44 -0
  29. package/lib/Dev/DevErrorPage.js +990 -0
  30. package/lib/Dev/DevIndicator.js +836 -0
  31. package/lib/HMR/Server.js +16 -37
  32. package/lib/Http/Server.js +171 -23
  33. package/lib/Logging/Log.js +34 -2
  34. package/lib/Mail/Mail.js +41 -10
  35. package/lib/Route/Router.js +43 -19
  36. package/lib/Runtime/Entry.js +10 -6
  37. package/lib/Session/Manager.js +103 -1
  38. package/lib/Session/Session.js +0 -4
  39. package/lib/Support/Str.js +6 -4
  40. package/lib/Translation/Lang.js +376 -32
  41. package/lib/Translation/pluralize.js +81 -0
  42. package/lib/Validation/MagicBytes.js +120 -0
  43. package/lib/Validation/Validator.js +46 -29
  44. package/lib/View/Client/hmr-client.js +100 -90
  45. package/lib/View/Client/spa.js +121 -50
  46. package/lib/View/ClientManifest.js +60 -0
  47. package/lib/View/FlightRenderer.js +100 -0
  48. package/lib/View/Layout.js +0 -3
  49. package/lib/View/PropFilter.js +81 -0
  50. package/lib/View/View.js +230 -495
  51. package/lib/index.d.ts +22 -1
  52. package/package.json +2 -2
  53. package/skeleton/config/app.js +1 -0
  54. package/skeleton/config/server.js +13 -0
  55. package/skeleton/config/session.js +3 -0
  56. package/lib/Build/HydrationBuilder.js +0 -190
  57. package/lib/Console/Stubs/page-hydration-dev.tsx +0 -72
  58. package/lib/Console/Stubs/page-hydration.tsx +0 -53
package/README.md CHANGED
@@ -1,39 +1,49 @@
1
- # NitronJS
1
+ <p align="center">
2
+ <h1 align="center">NitronJS</h1>
3
+ </p>
2
4
 
3
- A modern full-stack Node.js framework with React Server Components, Islands Architecture, and built-in tooling. Built on React and Fastify for high performance and developer productivity.
5
+ <p align="center">
6
+ A full-stack MVC framework built on Fastify and React Server Components.<br/>
7
+ Build production-ready Node.js applications with a built-in ORM, authentication, mail, validation, and instant HMR.
8
+ </p>
4
9
 
5
- ## Why NitronJS?
10
+ <p align="center">
11
+ <a href="https://www.npmjs.com/package/@nitronjs/framework"><img src="https://img.shields.io/npm/v/@nitronjs/framework.svg" alt="npm version"></a>
12
+ <a href="https://www.npmjs.com/package/@nitronjs/framework"><img src="https://img.shields.io/npm/l/@nitronjs/framework.svg" alt="license"></a>
13
+ <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/@nitronjs/framework.svg" alt="node version"></a>
14
+ </p>
6
15
 
7
- - **Zero Config** - Start building immediately. No webpack, no babel config, no boilerplate
8
- - **Server Components by Default** - All components render on the server. Add `"use client"` only where needed
9
- - **Islands Architecture** - Client components hydrate independently, keeping your pages fast
10
- - **Instant HMR** - Changes reflect immediately without losing state
11
- - **Full-Stack** - Database ORM, authentication, sessions, validation all built-in
16
+ <p align="center">
17
+ <b>NitronJS is currently under active development. APIs may change before the stable release.</b>
18
+ </p>
12
19
 
13
- ## Installation
20
+ ---
21
+
22
+ ## Quick Start
14
23
 
15
24
  ```bash
16
25
  npx -y @nitronjs/framework my-app
17
26
  cd my-app
27
+ npm run storage:link
18
28
  npm run dev
19
29
  ```
20
30
 
21
- > **Note:** The `-y` flag skips npm's confirmation prompt for a smoother experience.
22
-
23
31
  Your app will be running at `http://localhost:3000`
24
32
 
33
+ ---
34
+
25
35
  ## Core Concepts
26
36
 
27
37
  ### Server Components (Default)
28
38
 
29
- Every `.tsx` file in `resources/views/` is a Server Component by default. They run on the server, have full access to your database, file system, and never ship JavaScript to the browser.
39
+ Every `.tsx` file in `resources/views/` is a Server Component by default. They run on the server and have full access to your database and file system.
30
40
 
31
41
  ```tsx
32
42
  // resources/views/Site/Home.tsx
33
- import User from '@/app/Models/User.js';
43
+ import User from '@models/User';
34
44
 
35
45
  export default async function Home() {
36
- const users = await User.get(); // Direct database access
46
+ const users = await User.get();
37
47
 
38
48
  return (
39
49
  <div>
@@ -53,7 +63,6 @@ export default async function Home() {
53
63
  Add `"use client"` at the top of a file to make it interactive. These components hydrate on the browser and can use React hooks.
54
64
 
55
65
  ```tsx
56
- // resources/views/Components/Counter.tsx
57
66
  "use client";
58
67
  import { useState } from 'react';
59
68
 
@@ -68,25 +77,9 @@ export default function Counter() {
68
77
  }
69
78
  ```
70
79
 
71
- Use client components inside server components:
72
-
73
- ```tsx
74
- // resources/views/Site/Home.tsx (Server Component)
75
- import Counter from '../Components/Counter';
76
-
77
- export default function Home() {
78
- return (
79
- <div>
80
- <h1>Welcome</h1>
81
- <Counter /> {/* Hydrates as an island */}
82
- </div>
83
- );
84
- }
85
- ```
86
-
87
80
  ### Layouts
88
81
 
89
- Create `Layout.tsx` files to wrap pages. Layouts are discovered automatically by walking up the directory tree.
82
+ Create `Layout.tsx` files to wrap pages. Layouts are discovered automatically by walking up the directory tree. During SPA navigation, layouts persist and only page content is updated — no full page reloads.
90
83
 
91
84
  ```
92
85
  resources/views/
@@ -98,53 +91,14 @@ resources/views/
98
91
  └── Dashboard.tsx # Uses both layouts
99
92
  ```
100
93
 
101
- ```tsx
102
- // resources/views/Layout.tsx
103
- export default function RootLayout({ children }) {
104
- return (
105
- <html lang="en">
106
- <head>
107
- <meta charSet="UTF-8" />
108
- <title>My App</title>
109
- </head>
110
- <body>
111
- {children}
112
- </body>
113
- </html>
114
- );
115
- }
116
- ```
117
-
118
- ```tsx
119
- // resources/views/Admin/Layout.tsx
120
- export default function AdminLayout({ children }) {
121
- return (
122
- <div className="admin-wrapper">
123
- <nav>Admin Navigation</nav>
124
- <main>{children}</main>
125
- </div>
126
- );
127
- }
128
- ```
129
-
130
- Disable layout for a specific page:
131
-
132
- ```tsx
133
- export const layout = false;
134
-
135
- export default function FullscreenPage() {
136
- return <div>No layout wrapper</div>;
137
- }
138
- ```
139
-
140
- ## Routing
94
+ ### Routing
141
95
 
142
96
  Define routes in `routes/web.js`:
143
97
 
144
98
  ```javascript
145
99
  import { Route } from '@nitronjs/framework';
146
- import HomeController from '#app/Controllers/HomeController.js';
147
- import UserController from '#app/Controllers/UserController.js';
100
+ import HomeController from '../app/Controllers/HomeController.js';
101
+ import UserController from '../app/Controllers/UserController.js';
148
102
 
149
103
  // Basic routes
150
104
  Route.get('/', HomeController.index).name('home');
@@ -153,33 +107,60 @@ Route.get('/about', HomeController.about).name('about');
153
107
  // Route parameters
154
108
  Route.get('/users/:id', UserController.show).name('user.show');
155
109
 
156
- // Route groups with middleware
157
- Route.prefix('/admin').middleware('auth').group(() => {
158
- Route.get('/dashboard', AdminController.dashboard).name('admin.dashboard');
159
- Route.get('/users', AdminController.users).name('admin.users');
110
+ // RESTful routes
111
+ Route.post('/users', UserController.store).name('user.store');
112
+ Route.put('/users/:id', UserController.update).name('user.update');
113
+ Route.delete('/users/:id', UserController.destroy).name('user.destroy');
114
+
115
+ // Route groups with prefix, name prefix, and middleware
116
+ Route.prefix('/admin').name('admin.').middleware('auth').group(() => {
117
+ Route.get('/', DashboardController.index).name('dashboard');
118
+ Route.get('/users', AdminUserController.index).name('users');
119
+
120
+ // Nested groups
121
+ Route.prefix('/pages').name('pages.').group(() => {
122
+ Route.get('/:id/edit', PagesController.edit).name('edit');
123
+ });
160
124
  });
161
125
 
162
- // RESTful routes
163
- Route.post('/users', UserController.store);
164
- Route.put('/users/:id', UserController.update);
165
- Route.delete('/users/:id', UserController.destroy);
126
+ // Middleware-only groups
127
+ Route.middleware('guest').group(() => {
128
+ Route.get('/login', AuthController.getLogin).name('login');
129
+ Route.post('/login', AuthController.postLogin);
130
+ });
166
131
  ```
167
132
 
168
- Generate URLs using route names:
133
+ #### URL Generation
169
134
 
170
- ```tsx
171
- <a href={route('user.show', { id: 1 })}>View User</a>
135
+ The global `route()` function is available everywhere — controllers, views, and client-side code:
136
+
137
+ ```javascript
138
+ // Basic
139
+ route('home') // => "/"
140
+
141
+ // With parameters
142
+ route('user.show', { id: 1 }) // => "/users/1"
143
+
144
+ // With query string
145
+ route('admin.users', {}, { page: 2, q: 'search' }) // => "/admin/users?page=2&q=search"
146
+
147
+ // Parameters + query string
148
+ route('admin.pages.edit', { id: 5 }, { tab: 'seo' }) // => "/admin/pages/5/edit?tab=seo"
172
149
  ```
173
150
 
174
- ## Controllers
151
+ ### Controllers
175
152
 
176
153
  ```javascript
177
154
  // app/Controllers/UserController.js
155
+ import User from '../Models/User.js';
156
+
178
157
  class UserController {
179
158
  static async index(req, res) {
180
159
  const users = await User.get();
181
160
 
182
- return res.view('User/Index', { users });
161
+ return res.view('User/Index', {
162
+ users
163
+ });
183
164
  }
184
165
 
185
166
  static async show(req, res) {
@@ -189,7 +170,9 @@ class UserController {
189
170
  return res.status(404).view('Errors/NotFound');
190
171
  }
191
172
 
192
- return res.view('User/Show', { user });
173
+ return res.view('User/Show', {
174
+ user
175
+ });
193
176
  }
194
177
 
195
178
  static async store(req, res) {
@@ -207,12 +190,28 @@ class UserController {
207
190
  export default UserController;
208
191
  ```
209
192
 
210
- ## Database
193
+ ### Path Aliases
194
+
195
+ NitronJS provides built-in path aliases for clean imports in `.tsx` view files:
196
+
197
+ | Alias | Path |
198
+ |---|---|
199
+ | `@models/*` | `app/Models/*` |
200
+ | `@controllers/*` | `app/Controllers/*` |
201
+ | `@middlewares/*` | `app/Middlewares/*` |
202
+ | `@views/*` | `resources/views/*` |
203
+ | `@css/*` | `resources/css/*` |
204
+ | `@/*` | Project root |
205
+
206
+ ---
211
207
 
212
- ### Models
208
+ ## Features
209
+
210
+ ### Database
211
+
212
+ #### Models
213
213
 
214
214
  ```javascript
215
- // app/Models/User.js
216
215
  import { Model } from '@nitronjs/framework';
217
216
 
218
217
  export default class User extends Model {
@@ -220,30 +219,25 @@ export default class User extends Model {
220
219
  }
221
220
  ```
222
221
 
223
- ### Queries
222
+ #### Queries
224
223
 
225
224
  ```javascript
226
- // Find all
227
225
  const users = await User.get();
228
-
229
- // Find by ID
230
226
  const user = await User.find(1);
231
-
232
- // Where clauses
233
227
  const admins = await User.where('role', '=', 'admin').get();
234
- const active = await User.where('active', true).get();
235
228
 
236
- // Chaining
237
229
  const results = await User
238
- .where('role', '=', 'admin')
239
230
  .where('active', true)
240
231
  .orderBy('created_at', 'desc')
241
232
  .limit(10)
242
233
  .get();
243
234
 
244
- // First result
245
235
  const user = await User.where('email', '=', 'john@example.com').first();
246
236
 
237
+ // Aggregates
238
+ const total = await User.count();
239
+ const maxAge = await User.max('age');
240
+
247
241
  // Create
248
242
  const user = new User();
249
243
  user.name = 'John';
@@ -257,59 +251,58 @@ await User.where('id', '=', 1).update({ name: 'Jane' });
257
251
  await User.where('id', '=', 1).delete();
258
252
  ```
259
253
 
260
- ### Migrations
254
+ #### Migrations
261
255
 
262
256
  ```bash
263
- npm run make:migration create_users_table
257
+ npm run make:migration create_posts_table
264
258
  ```
265
259
 
266
260
  ```javascript
267
- // database/migrations/2024_01_01_000000_create_users_table.js
268
261
  import { Schema } from '@nitronjs/framework';
269
262
 
270
- class CreateUsersTable {
263
+ class CreatePostsTable {
271
264
  static async up() {
272
- await Schema.create('users', (table) => {
265
+ await Schema.create('posts', (table) => {
273
266
  table.id();
274
- table.string('name');
275
- table.string('email').unique();
276
- table.string('password');
267
+ table.string('title');
268
+ table.text('body');
269
+ table.string('slug').unique();
270
+ table.boolean('published').default(false);
271
+ table.json('metadata').nullable();
277
272
  table.timestamp('created_at');
278
273
  table.timestamp('updated_at').nullable();
279
274
  });
280
275
  }
281
276
 
282
277
  static async down() {
283
- await Schema.dropIfExists('users');
278
+ await Schema.dropIfExists('posts');
284
279
  }
285
280
  }
286
281
 
287
- export default CreateUsersTable;
282
+ export default CreatePostsTable;
288
283
  ```
289
284
 
290
- Run migrations:
291
-
292
285
  ```bash
293
286
  npm run migrate # Run pending migrations
294
287
  npm run migrate:fresh # Drop all tables and re-run
295
- npm run migrate:fresh --seed # Drop, migrate, and seed
288
+ npm run migrate:fresh:seed # Drop, migrate, and seed
289
+ npm run migrate:rollback # Rollback last batch
290
+ npm run migrate:status # Show migration status
296
291
  ```
297
292
 
298
- ## Authentication
293
+ ### Authentication
299
294
 
300
295
  ```javascript
301
- // In a controller or middleware
302
296
  class AuthController {
303
297
  static async login(req, res) {
304
298
  const { email, password } = req.body;
305
-
306
299
  const success = await req.auth.attempt({ email, password });
307
300
 
308
301
  if (success) {
309
- return req.redirect('/dashboard');
302
+ return res.redirect(route('dashboard'));
310
303
  }
311
304
 
312
- return req.view('Auth/Login', {
305
+ return res.view('Auth/Login', {
313
306
  error: 'Invalid credentials'
314
307
  });
315
308
  }
@@ -317,7 +310,7 @@ class AuthController {
317
310
  static async logout(req, res) {
318
311
  await req.auth.logout();
319
312
 
320
- return res.redirect('/');
313
+ return res.redirect(route('home'));
321
314
  }
322
315
  }
323
316
 
@@ -325,32 +318,26 @@ class AuthController {
325
318
  if (req.auth.check()) {
326
319
  const user = await req.auth.user();
327
320
  }
321
+
322
+ // Named guards
323
+ req.auth.guard('admin').check();
328
324
  ```
329
325
 
330
- ## Session
326
+ ### Sessions
331
327
 
332
328
  ```javascript
333
- // Set value
334
329
  req.session.set('key', 'value');
335
-
336
- // Get value
337
330
  const value = req.session.get('key');
338
331
 
339
- // Flash messages (one-time)
332
+ // Flash messages
340
333
  req.session.flash('success', 'Profile updated!');
341
334
  const message = req.session.getFlash('success');
342
335
 
343
- // All data
344
- const data = req.session.all();
345
-
346
- // Regenerate session ID (after login)
336
+ // Regenerate session ID
347
337
  req.session.regenerate();
348
-
349
- // CSRF token
350
- const token = req.session.getCsrfToken();
351
338
  ```
352
339
 
353
- ## Validation
340
+ ### Validation
354
341
 
355
342
  ```javascript
356
343
  import { Validator } from '@nitronjs/framework';
@@ -358,8 +345,9 @@ import { Validator } from '@nitronjs/framework';
358
345
  const validation = Validator.make(req.body, {
359
346
  name: 'required|string|min:2|max:100',
360
347
  email: 'required|email',
361
- password: 'required|string|min:8',
362
- age: 'numeric|min:18'
348
+ password: 'required|string|min:8|confirmed',
349
+ age: 'numeric|min:18',
350
+ avatar: 'file|mimes:png,jpg|max:2097152'
363
351
  });
364
352
 
365
353
  if (validation.fails()) {
@@ -369,9 +357,7 @@ if (validation.fails()) {
369
357
  }
370
358
  ```
371
359
 
372
- ## Middleware
373
-
374
- Create custom middleware:
360
+ ### Middleware
375
361
 
376
362
  ```javascript
377
363
  // app/Middlewares/CheckAge.js
@@ -380,7 +366,6 @@ class CheckAge {
380
366
  if (req.query.age < 18) {
381
367
  return res.status(403).send('Access denied');
382
368
  }
383
- // No return = continue to next handler
384
369
  }
385
370
  }
386
371
 
@@ -393,34 +378,114 @@ Register in `app/Kernel.js`:
393
378
  export default {
394
379
  routeMiddlewares: {
395
380
  'check-age': CheckAge,
396
- 'auth': AuthMiddleware,
381
+ 'auth': AuthMiddleware
397
382
  }
398
383
  };
399
384
  ```
400
385
 
401
- Apply to routes:
386
+ ### Mail
402
387
 
403
388
  ```javascript
404
- Route.middleware('check-age').get('/restricted', Controller.method);
405
- Route.middleware('auth', 'check-age').get('/admin', AdminController.index);
389
+ import { Mail } from '@nitronjs/framework';
390
+
391
+ await Mail.from('noreply@app.com')
392
+ .to('user@email.com')
393
+ .subject('Welcome!')
394
+ .html('<h1>Hello</h1>')
395
+ .attachment({ filename: 'file.pdf', path: '/path/to/file.pdf' })
396
+ .send();
397
+
398
+ // Using a template
399
+ await Mail.to('user@email.com')
400
+ .view('emails/welcome', { name: 'Alice' })
401
+ .send();
406
402
  ```
407
403
 
408
- ## CSS & Tailwind
404
+ ### Storage
409
405
 
410
- Put your CSS files in `resources/css/`. Tailwind CSS is automatically detected and processed.
406
+ ```javascript
407
+ import { Storage } from '@nitronjs/framework';
408
+
409
+ await Storage.put(file, 'upload_files', 'image.jpg');
410
+ const buffer = await Storage.get('upload_files/image.jpg');
411
+ await Storage.delete('upload_files/old.jpg');
412
+ await Storage.move('upload_files/a.jpg', 'upload_files/b.jpg');
413
+ Storage.exists('upload_files/image.jpg');
414
+ Storage.url('upload_files/image.jpg'); // => "/storage/upload_files/image.jpg"
415
+ ```
411
416
 
412
- ```css
413
- /* resources/css/app.css */
414
- @import "tailwindcss";
417
+ ### Encryption
418
+
419
+ ```javascript
420
+ import { AES } from '@nitronjs/framework';
421
+
422
+ const token = AES.encrypt({ userId: 1, expires: '2025-12-31' });
423
+ const data = AES.decrypt(token); // Returns false on tamper
415
424
  ```
416
425
 
417
- CSS files are automatically linked in your views:
426
+ ### Hashing
418
427
 
419
- ```tsx
420
- // CSS from resources/css/ is available at /storage/css/
421
- <link rel="stylesheet" href="/storage/css/app.css" />
428
+ ```javascript
429
+ import { Hash } from '@nitronjs/framework';
430
+
431
+ const hashed = await Hash.make('password123');
432
+ const valid = await Hash.check('password123', hashed);
433
+ ```
434
+
435
+ ### Logging
436
+
437
+ ```javascript
438
+ import { Log } from '@nitronjs/framework';
439
+
440
+ Log.info('User registered', { userId: 1 });
441
+ Log.error('Payment failed', { orderId: 123 });
442
+ Log.debug('Query executed', { sql: '...' });
443
+ ```
444
+
445
+ ### DateTime
446
+
447
+ ```javascript
448
+ import { DateTime } from '@nitronjs/framework';
449
+
450
+ DateTime.toSQL(); // "2025-01-15 10:30:00"
451
+ DateTime.getDate(timestamp, 'Y-m-d H:i:s');
452
+ DateTime.addDays(7);
453
+ DateTime.subHours(2);
422
454
  ```
423
455
 
456
+ ### Faker
457
+
458
+ Built-in fake data generator for seeders and testing:
459
+
460
+ ```javascript
461
+ import { Faker } from '@nitronjs/framework';
462
+
463
+ Faker.fullName(); // "John Smith"
464
+ Faker.email(); // "john@example.com"
465
+ Faker.sentence(); // "Lorem ipsum dolor sit amet."
466
+ Faker.int(1, 100); // 42
467
+ Faker.boolean(); // true
468
+ Faker.uuid(); // "550e8400-e29b-41d4-a716-446655440000"
469
+ Faker.creditCard(); // Luhn-valid card number
470
+ Faker.hexColor(); // "#a3f29c"
471
+ Faker.oneOf(['a', 'b', 'c']);
472
+ ```
473
+
474
+ ### String Utilities
475
+
476
+ ```javascript
477
+ import { Str } from '@nitronjs/framework';
478
+
479
+ Str.slug('Hello World'); // "hello-world"
480
+ Str.camel('user_name'); // "userName"
481
+ Str.pascal('user_name'); // "UserName"
482
+ Str.snake('userName'); // "user_name"
483
+ Str.random(32); // Random string
484
+ Str.limit('Long text...', 10); // "Long text..."
485
+ ```
486
+
487
+ ---
488
+
424
489
  ## CLI Commands
425
490
 
426
491
  ```bash
@@ -432,6 +497,9 @@ npm run start # Start production server
432
497
  # Database
433
498
  npm run migrate # Run migrations
434
499
  npm run migrate:fresh # Fresh migration
500
+ npm run migrate:fresh:seed # Fresh migration + seed
501
+ npm run migrate:rollback # Rollback last batch
502
+ npm run migrate:status # Show migration status
435
503
  npm run seed # Run seeders
436
504
 
437
505
  # Code Generation
@@ -445,31 +513,55 @@ npm run make:seeder <name>
445
513
  npm run storage:link # Create storage symlink
446
514
  ```
447
515
 
516
+ ---
517
+
448
518
  ## Project Structure
449
519
 
450
520
  ```
451
521
  my-app/
452
522
  ├── app/
453
- │ ├── Controllers/ # Request handlers
454
- │ ├── Middlewares/ # Custom middleware
455
- │ └── Models/ # Database models
456
- ├── config/ # Configuration files
523
+ │ ├── Controllers/ # Request handlers
524
+ │ ├── Middlewares/ # Custom middleware
525
+ │ └── Models/ # Database models
526
+ ├── config/ # Configuration files
457
527
  │ ├── app.js
528
+ │ ├── auth.js
458
529
  │ ├── database.js
459
- └── server.js
530
+ ├── hash.js
531
+ │ ├── server.js
532
+ │ └── session.js
460
533
  ├── database/
461
- │ ├── migrations/ # Database migrations
462
- │ └── seeders/ # Database seeders
463
- ├── public/ # Static assets
534
+ │ ├── migrations/ # Database migrations
535
+ │ └── seeders/ # Database seeders
536
+ ├── public/ # Static assets
464
537
  ├── resources/
465
- │ ├── css/ # Stylesheets
466
- │ └── views/ # React components (TSX)
538
+ │ ├── css/ # Stylesheets
539
+ │ └── views/ # React components (TSX)
467
540
  ├── routes/
468
- │ └── web.js # Route definitions
469
- ├── storage/ # File storage
470
- └── .env # Environment variables
541
+ │ └── web.js # Route definitions
542
+ ├── storage/ # File storage
543
+ └── .env # Environment variables
471
544
  ```
472
545
 
546
+ ---
547
+
548
+ ## CSS & Tailwind
549
+
550
+ Put your CSS files in `resources/css/`. Tailwind CSS v4 is automatically detected and processed.
551
+
552
+ ```css
553
+ /* resources/css/global.css */
554
+ @import "tailwindcss";
555
+ ```
556
+
557
+ Import in your `.tsx` files using the `@css` alias:
558
+
559
+ ```tsx
560
+ import "@css/global.css";
561
+ ```
562
+
563
+ ---
564
+
473
565
  ## Configuration
474
566
 
475
567
  Access configuration values:
@@ -478,7 +570,7 @@ Access configuration values:
478
570
  import { Config } from '@nitronjs/framework';
479
571
 
480
572
  const appName = Config.get('app.name');
481
- const dbHost = Config.get('database.host', 'localhost'); // with default
573
+ const dbHost = Config.get('database.host', 'localhost');
482
574
  ```
483
575
 
484
576
  Environment variables in `.env`:
@@ -495,15 +587,13 @@ DATABASE_USERNAME=root
495
587
  DATABASE_PASSWORD=
496
588
  ```
497
589
 
590
+ ---
591
+
498
592
  ## Requirements
499
593
 
500
594
  - Node.js 18+
501
595
  - MySQL 5.7+ or MariaDB 10.3+
502
596
 
503
- ## Documentation
504
-
505
- Full documentation available at [nitronjs.dev](https://nitronjs.dev/docs)
506
-
507
597
  ## License
508
598
 
509
599
  ISC