@ressjs/vite-router 0.4.2 → 0.5.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ressjs/vite-router
2
2
 
3
- Advanced Express router with platform detection and Server-Side Rendering (SSR) for RESS.js applications.
3
+ Advanced Express router with **dynamic routes**, platform detection, and Server-Side Rendering (SSR) for RESS.js applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,16 +10,50 @@ npm install @ressjs/vite-router
10
10
 
11
11
  ## Features
12
12
 
13
- - **Advanced Routing**: Auto-discovers pages with platform-specific entry points
14
- - **Platform Detection**: Automatic device, OS, and WebView detection
15
- - **SSR Support**: Complete server-side rendering with proper hydration
16
- - **Asset Management**: Smart CSS/JS bundle resolution based on platform
17
- - **Development Mode**: Hot reload with fast refresh and dev server
18
- - **Production Ready**: Optimized builds with manifest-based asset loading
19
- - **TypeScript**: Full TypeScript support with comprehensive type definitions
13
+ - **🎯 Dynamic Routes**: Next.js-style routing with `[param]` syntax
14
+ - **🎨 Platform Detection**: Automatic device, OS, and WebView detection
15
+ - **⚡ SSR Support**: Complete server-side rendering with proper hydration
16
+ - **📱 Smart Assets**: Platform-specific CSS/JS bundle resolution
17
+ - **🔧 Middleware System**: File-based middleware with execution order control
18
+ - **🔄 Development Mode**: Hot reload with fast refresh and dev server
19
+ - **🚀 Production Ready**: Optimized builds with manifest-based asset loading
20
+ - **📝 TypeScript**: Full TypeScript support with comprehensive type definitions
20
21
 
21
22
  ## Quick Start
22
23
 
24
+ ### Vite Configuration
25
+
26
+ Create a `vite.config.ts` file in your project root:
27
+
28
+ ```typescript
29
+ import { defineConfig } from 'vite'
30
+ import react from '@vitejs/plugin-react'
31
+ import { getClientEntriesInput, getViteSSRInput } from '@ressjs/vite-router'
32
+
33
+ const isSSR = !!process.env.VITE_SSR_BUILD
34
+
35
+ export default defineConfig(async () => ({
36
+ plugins: [react()],
37
+ server: {
38
+ watch: {
39
+ ignored: ['**/dist/.entries/**'] // Ignore generated entries
40
+ }
41
+ },
42
+ build: {
43
+ manifest: true,
44
+ outDir: isSSR ? 'dist/server' : 'dist/client',
45
+ rollupOptions: {
46
+ input: isSSR
47
+ ? getViteSSRInput() // SSR entry point
48
+ : await getClientEntriesInput(), // Auto-generated client entries
49
+ },
50
+ ssr: isSSR,
51
+ },
52
+ }))
53
+ ```
54
+
55
+ **Important**: The router automatically generates entry files for each page/platform combination in `dist/.entries/`. These are used by Vite for building client bundles.
56
+
23
57
  ### Basic Setup
24
58
 
25
59
  ```typescript
@@ -36,55 +70,368 @@ app.use('/', router)
36
70
  app.listen(3000, () => console.log('Server running on port 3000'))
37
71
  ```
38
72
 
39
- ### Development Server
73
+ ### Development Server Setup
74
+
75
+ Create a `server.js` file for development:
76
+
77
+ ```typescript
78
+ import { createServer } from 'vite'
79
+ import { createViteRouter } from '@ressjs/vite-router'
80
+ import express from 'express'
81
+
82
+ async function createDevServer() {
83
+ const app = express()
84
+
85
+ // Create Vite server in middleware mode
86
+ const vite = await createServer({
87
+ server: { middlewareMode: true },
88
+ appType: 'custom'
89
+ })
90
+
91
+ // Use vite's connect instance as middleware
92
+ app.use(vite.ssrLoadModule)
93
+
94
+ // Create router with Vite instance
95
+ const router = createViteRouter({
96
+ vite,
97
+ enablePlatformDetection: true,
98
+ isProduction: false
99
+ })
100
+
101
+ app.use('/', router)
102
+ app.listen(3000, () => console.log('Dev server running on port 3000'))
103
+ }
104
+
105
+ createDevServer()
106
+ ```
107
+
108
+ ### Package.json Scripts
109
+
110
+ Add these scripts to your `package.json`:
111
+
112
+ ```json
113
+ {
114
+ "scripts": {
115
+ "dev": "node server.js",
116
+ "build": "npm run build:client && npm run build:server",
117
+ "build:client": "vite build",
118
+ "build:server": "VITE_SSR_BUILD=true vite build",
119
+ "preview": "NODE_ENV=production node server.js",
120
+ "type-check": "tsc --noEmit"
121
+ },
122
+ "dependencies": {
123
+ "@ressjs/vite-router": "^0.5.0",
124
+ "express": "^4.18.0",
125
+ "react": "^18.0.0",
126
+ "react-dom": "^18.0.0",
127
+ "vite": "^5.0.0"
128
+ },
129
+ "devDependencies": {
130
+ "@types/express": "^4.17.0",
131
+ "@types/react": "^18.0.0",
132
+ "@types/react-dom": "^18.0.0",
133
+ "@vitejs/plugin-react": "^4.0.0",
134
+ "typescript": "^5.0.0"
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### TypeScript Backend Integration
140
+
141
+ For full-stack TypeScript projects with backend APIs, use this enhanced configuration:
142
+
143
+ ```json
144
+ {
145
+ "name": "my-fullstack-app",
146
+ "type": "module",
147
+ "scripts": {
148
+ "dev": "PORT=3000 vite-node server.ts",
149
+ "build": "npm run build:client && npm run build:server",
150
+ "build:client": "vite build --outDir dist/client",
151
+ "build:server": "cross-env VITE_SSR_BUILD=1 vite build --ssr --outDir dist/server",
152
+ "preview": "cross-env NODE_ENV=production PORT=3000 tsx server.ts",
153
+ "start": "npm run build && npm run preview"
154
+ },
155
+ "dependencies": {
156
+ "@ressjs/vite-router": "^0.5.0",
157
+ "cross-env": "^7.0.3",
158
+ "express": "^4.18.0",
159
+ "react": "^18.0.0",
160
+ "react-dom": "^18.0.0",
161
+ "tsx": "^4.7.1",
162
+ "typescript": "^5.0.0",
163
+ "vite": "^5.0.0"
164
+ },
165
+ "devDependencies": {
166
+ "@types/express": "^4.17.0",
167
+ "@types/node": "^22.0.0",
168
+ "@types/react": "^18.0.0",
169
+ "@types/react-dom": "^18.0.0",
170
+ "@vitejs/plugin-react": "^4.0.0",
171
+ "vite-node": "^3.2.0"
172
+ }
173
+ }
174
+ ```
175
+
176
+ **Key differences for TypeScript backends**:
177
+ - **`vite-node server.ts`**: Direct TypeScript execution in development
178
+ - **`tsx server.ts`**: Fast TypeScript runner for production
179
+ - **`type: "module"`**: Enable ES modules in Node.js
180
+ - **`cross-env`**: Cross-platform environment variables
181
+
182
+ **When to use this setup**:
183
+ - Building APIs alongside your frontend
184
+ - Need database connections, authentication, file uploads
185
+ - Want full TypeScript integration across the stack
186
+ - Require additional Express middleware and routes
187
+
188
+ **Key Points**:
189
+ - **`VITE_SSR_BUILD=true`**: Environment variable that tells Vite config to build for SSR
190
+ - **Separate builds**: Client and server are built separately with different entry points
191
+ - **Auto-generated entries**: `getClientEntriesInput()` discovers all page/platform combinations
192
+
193
+ ### Auto Server (WIP)
40
194
 
41
195
  ```typescript
42
196
  import { createAutoViteServer } from '@ressjs/vite-router'
43
197
 
44
- // Automatically configures Vite dev server with SSR
45
- const server = await createAutoViteServer({
198
+ // Automatically configures Vite dev server with SSR (Work In Progress)
199
+ const { start } = await createAutoViteServer({
46
200
  port: 3000,
47
201
  enablePlatformDetection: true
48
202
  })
203
+
204
+ start()
205
+ ```
206
+
207
+ ## 🎯 Dynamic Routes
208
+
209
+ Create pages with parameters using Next.js-style bracket syntax:
210
+
211
+ ### File Structure
212
+ ```
213
+ app/pages/
214
+ ├── index.tsx # / route
215
+ ├── about.tsx # /about route
216
+ ├── users/
217
+ │ ├── index.tsx # /users route
218
+ │ └── [id].tsx # /users/:id route
219
+ ├── products/
220
+ │ ├── [category].tsx # /products/:category route
221
+ │ └── [category]/
222
+ │ └── [id].tsx # /products/:category/:id route
223
+ └── blog/
224
+ └── [...slug].tsx # /blog/* route (coming soon)
49
225
  ```
50
226
 
51
- ## Platform Detection
227
+ ### Route Examples
52
228
 
53
- The router automatically detects:
229
+ | File | Route | Express Route | URL Example |
230
+ |------|-------|---------------|-------------|
231
+ | `users/[id].tsx` | `/users/[id]` | `/users/:id` | `/users/123` |
232
+ | `products/[category]/[id].tsx` | `/products/[category]/[id]` | `/products/:category/:id` | `/products/phones/iphone` |
233
+ | `photocard/[photoId].tsx` | `/photocard/[photoId]` | `/photocard/:photoId` | `/photocard/abc123xyz` |
234
+
235
+ ### Accessing Parameters
236
+
237
+ ```typescript
238
+ // app/pages/users/[id].tsx
239
+ export default function UserPage({ id, user }) {
240
+ return (
241
+ <div>
242
+ <h1>User {id}</h1>
243
+ <p>Name: {user.name}</p>
244
+ </div>
245
+ )
246
+ }
54
247
 
55
- - **Device Type**: mobile, tablet, desktop
56
- - **Operating System**: iOS, Android, Windows, macOS, Linux
57
- - **Environment**: webview, browser
58
- - **Category**: webview.android, webview.ios, mobile, etc.
248
+ export async function getServerSideProps(req, res) {
249
+ const { id } = req.params // Access route parameters
250
+
251
+ // Fetch user data
252
+ const user = await fetchUser(id)
253
+
254
+ return {
255
+ props: { id, user }
256
+ }
257
+ }
258
+ ```
59
259
 
60
- ### Platform-Specific Assets
260
+ ### Dynamic Route Styles
61
261
 
62
- Based on detection, the router serves appropriate assets:
262
+ Dynamic routes support platform-specific styles:
63
263
 
64
264
  ```
65
- app/pages/home/index.tsx # Base page
66
- app/pages/home/index.scss # Base styles
67
- app/pages/home/index.mobile.scss # Mobile-specific styles
68
- app/pages/home/index.webview.android.scss # Android WebView styles
265
+ app/pages/products/[id].tsx # Product page component
266
+ app/pages/products/[id].scss # Desktop styles
267
+ app/pages/products/[id].mobile.scss # Mobile styles
268
+ app/pages/products/[id].webview.scss # WebView styles
69
269
  ```
70
270
 
71
- Generated entry-clients:
271
+ ## 🔧 Middleware System
272
+
273
+ **Important**: Dynamic routes `[param].tsx` do **NOT** support dedicated middleware files. Use page-level or directory-level middleware instead.
274
+
275
+ ### Middleware Execution Order
276
+
72
277
  ```
73
- dist/.entries/home__index__entry-client.tsx # Base
74
- dist/.entries/home__index__entry-client.mobile.tsx # Mobile
75
- dist/.entries/home__index__entry-client.webview.android.tsx # Android WebView
278
+ app/pages/
279
+ ├── middlewares.ts # --> Executes 1st (global)
280
+ ├── products/
281
+ │ ├── middlewares.ts # --> Executes 2nd (directory-level)
282
+ │ ├── index.tsx
283
+ │ ├── index.middlewares.ts # --> Executes 3rd (page-specific)
284
+ │ └── [id].tsx # --> No middleware support for dynamic routes
285
+ ├── users/
286
+ │ ├── [id].tsx # --> Uses directory and global middleware only
287
+ │ └── middlewares.ts # --> Executes for all /users/* routes
288
+ └── checkout/
289
+ ├── middlewares.ts # --> Executes 2nd for /checkout/*
290
+ ├── step1.tsx
291
+ ├── step1.middlewares.ts # --> Executes 3rd for /checkout/step1
292
+ ├── step2.tsx
293
+ └── step2.middlewares.ts # --> Executes 3rd for /checkout/step2
76
294
  ```
77
295
 
78
- ## API Reference
296
+ ### Middleware Types
79
297
 
80
- ### createViteRouter(options)
298
+ #### 1. Global Middleware
299
+ ```typescript
300
+ // app/pages/middlewares.ts --> Executes 1st for ALL routes
301
+ export default function globalMiddleware(req, res, next) {
302
+ console.log('Global middleware for:', req.url)
303
+ req.startTime = Date.now()
304
+ next()
305
+ }
306
+ ```
81
307
 
82
- Creates an Express router with SSR capabilities.
308
+ #### 2. Directory Middleware
309
+ ```typescript
310
+ // app/pages/products/middlewares.ts --> Executes 2nd for /products/*
311
+ export default function productsMiddleware(req, res, next) {
312
+ console.log('Products middleware for:', req.url)
313
+ req.section = 'products'
314
+ next()
315
+ }
316
+ ```
317
+
318
+ #### 3. Page-Specific Middleware
319
+ ```typescript
320
+ // app/pages/products/index.middlewares.ts --> Executes 3rd for /products only
321
+ export default function productListMiddleware(req, res, next) {
322
+ console.log('Product list middleware')
323
+ req.page = 'product-list'
324
+ next()
325
+ }
326
+ ```
327
+
328
+ #### ❌ Not Supported for Dynamic Routes
329
+ ```typescript
330
+ // ❌ app/pages/products/[id].middlewares.ts - NOT supported
331
+ // Dynamic routes cannot have dedicated middleware files
332
+ ```
333
+
334
+ ### Complete Execution Flow Example
335
+
336
+ For URL `/products/123`:
337
+
338
+ 1. **Global**: `app/pages/middlewares.ts` (if exists)
339
+ 2. **Directory**: `app/pages/products/middlewares.ts` (if exists)
340
+ 3. **Page**: Built-in `getServerSideProps` from `[id].tsx`
341
+ 4. **Render**: Component render and HTML generation
342
+
343
+ ## 📱 Platform Detection
344
+
345
+ The router automatically detects and serves platform-specific assets:
346
+
347
+ ### Detection Capabilities
348
+
349
+ - **Device Type**: `phone`, `tablet`, `desktop`
350
+ - **Operating System**: `ios`, `android`, `windows`, `macos`, `linux`
351
+ - **Environment**: `webview`, `web`, `native`
352
+ - **Category**: `webview.android`, `webview.ios`, `mobile`, etc.
353
+
354
+ ### Platform Headers (React Native)
355
+
356
+ React Native clients should send these headers:
357
+
358
+ ```javascript
359
+ {
360
+ 'x-ressjs-platform': 'webview', // Required
361
+ 'x-ressjs-os': 'ios', // Required
362
+ 'x-ressjs-device': 'phone', // Required
363
+ 'x-ressjs-version': Platform.Version, // Optional
364
+ 'x-ressjs-app-version': '1.0.0' // Optional
365
+ }
366
+ ```
367
+
368
+ ### Asset Resolution Priority
369
+
370
+ For a page like `app/pages/home/index.tsx`:
371
+
372
+ 1. **Device + OS Specific**: `index.phone.ios.scss`
373
+ 2. **Device Specific**: `index.phone.scss`
374
+ 3. **Category Specific**: `index.mobile.scss`
375
+ 4. **WebView Specific**: `index.webview.scss`
376
+ 5. **Base Fallback**: `index.scss`
377
+
378
+ ### Platform CSS Examples
379
+
380
+ ```
381
+ app/pages/home/
382
+ ├── index.tsx # React component
383
+ ├── index.scss # Desktop/base styles
384
+ ├── index.mobile.scss # Phone + tablet
385
+ ├── index.phone.scss # Phone only
386
+ ├── index.tablet.scss # Tablet only
387
+ ├── index.webview.scss # All WebView
388
+ ├── index.webview.ios.scss # iOS WebView
389
+ ├── index.webview.android.scss # Android WebView
390
+ └── index.desktop.linux.scss # Linux desktop
391
+ ```
392
+
393
+ ## 🎨 getServerSideProps
394
+
395
+ Every page can export a `getServerSideProps` function for server-side data fetching:
396
+
397
+ ```typescript
398
+ export async function getServerSideProps(req, res) {
399
+ // Access route parameters (for dynamic routes)
400
+ const { id, category } = req.params
401
+
402
+ // Access query parameters
403
+ const { search, filter } = req.query
404
+
405
+ // Access request headers
406
+ const userAgent = req.headers['user-agent']
407
+
408
+ // Return props for the component
409
+ return {
410
+ props: {
411
+ id,
412
+ data: await fetchData(id),
413
+ timestamp: new Date().toISOString()
414
+ },
415
+ // Optional: HTML modifications
416
+ title: `Product ${id}`,
417
+ html: {
418
+ head: {
419
+ title: `Custom Title`,
420
+ extraTags: ['<meta name="description" content="...">']
421
+ }
422
+ }
423
+ }
424
+ }
425
+ ```
426
+
427
+ ## 🚀 API Reference
428
+
429
+ ### createViteRouter(options)
83
430
 
84
431
  ```typescript
85
432
  interface ViteRouterOptions {
86
433
  enablePlatformDetection?: boolean // Enable platform detection (default: true)
87
- vite?: any // Vite dev server instance
434
+ vite?: any // Vite dev server instance
88
435
  manifest?: any // Production manifest
89
436
  templateHtml?: string // HTML template
90
437
  isProduction?: boolean // Production mode
@@ -96,61 +443,89 @@ interface ViteRouterOptions {
96
443
  }
97
444
  ```
98
445
 
99
- ### generateHTML(template, rendered, page, manifest, platform, isProduction, platformInfo)
100
-
101
- Generates complete HTML with proper asset injection and hydration.
446
+ ### createAutoViteServer(options)
102
447
 
103
448
  ```typescript
104
- export async function generateHTML(
105
- template: string,
106
- rendered: any,
107
- page: any,
108
- manifest: any,
109
- platform: string,
110
- isProduction: boolean,
111
- platformInfo: any = null
112
- ): Promise<string>
449
+ interface AutoViteServerOptions {
450
+ port?: number // Server port (default: 5173)
451
+ base?: string // Base path (default: '/')
452
+ enablePlatformDetection?: boolean // Enable platform detection (default: true)
453
+ }
113
454
  ```
114
455
 
115
456
  ### Utility Functions
116
457
 
117
458
  ```typescript
118
- // Get all discovered pages
119
- import { getPages, listDetectedPages } from '@ressjs/vite-router'
120
-
121
- const pages = await getPages()
122
- const pageList = listDetectedPages()
459
+ import {
460
+ getPages,
461
+ listDetectedPages,
462
+ convertRouteToExpress
463
+ } from '@ressjs/vite-router'
464
+
465
+ // Get all discovered pages with route info
466
+ const pages = getPages()
467
+ // [{ route: '/users/[id]', expressRoute: '/users/:id', file: '...', abs: '...' }]
468
+
469
+ // Convert Next.js route to Express route
470
+ const expressRoute = convertRouteToExpress('/users/[id]') // '/users/:id'
123
471
  ```
124
472
 
125
- ## File Structure
473
+ ## 🔧 Production Build
126
474
 
127
- The router expects the following structure:
475
+ ### Build Process
128
476
 
129
- ```
130
- app/
131
- pages/
132
- index.tsx # Home page
133
- about/
134
- index.tsx # About page
135
- index.mobile.scss # Mobile styles
136
- products/
137
- [id].tsx # Dynamic route
138
- [id].webview.ios.scss # iOS WebView styles
477
+ ```bash
478
+ # 1. Build client assets
479
+ vite build --outDir dist/client
480
+
481
+ # 2. Build server bundle
482
+ vite build --ssr --outDir dist/server
483
+
484
+ # 3. Start production server
485
+ NODE_ENV=production node server.js
139
486
  ```
140
487
 
141
- ## Development
488
+ ### Example Production Server
142
489
 
143
- ```bash
144
- # Build the package
145
- npm run build
490
+ ```typescript
491
+ import express from 'express'
492
+ import { createViteRouter } from '@ressjs/vite-router'
493
+
494
+ const app = express()
146
495
 
147
- # Run tests
148
- npm test
496
+ // Serve static assets
497
+ app.use('/assets', express.static('dist/client/assets'))
149
498
 
150
- # Watch mode
151
- npm run dev
499
+ // Create router with production settings
500
+ const router = createViteRouter({
501
+ isProduction: true,
502
+ enablePlatformDetection: true
503
+ })
504
+
505
+ app.use('/', router)
506
+ app.listen(3000)
152
507
  ```
153
508
 
154
- ## License
509
+ ## 🐛 Troubleshooting
510
+
511
+ ### Dynamic Route Not Working
512
+
513
+ 1. **Check file naming**: Use `[param].tsx`, not `{param}.tsx`
514
+ 2. **Verify parameters**: Access via `req.params.param` in `getServerSideProps`
515
+ 3. **Build order**: Run `npm run build` after adding new dynamic routes
516
+
517
+ ### Middleware Not Executing
518
+
519
+ 1. **File naming**: Use `middlewares.ts` (plural) or `page.middlewares.ts`
520
+ 2. **Dynamic routes**: Don't create `[param].middlewares.ts` - not supported
521
+ 3. **Export**: Use `export default function` format
522
+
523
+ ### Platform Detection Issues
524
+
525
+ 1. **Headers**: Ensure React Native sends required `x-ressjs-*` headers
526
+ 2. **CSS files**: Create platform-specific CSS files if needed
527
+ 3. **Case sensitivity**: Use lowercase for OS names (`ios`, `android`)
528
+
529
+ ## 📄 License
155
530
 
156
- MIT
531
+ MIT
@@ -8,7 +8,12 @@ exports.generateClientCSS = generateClientCSS;
8
8
  exports.generateCompleteHTML = generateCompleteHTML;
9
9
  const promises_1 = __importDefault(require("node:fs/promises"));
10
10
  async function generateClientScript(pageFile, platformInfo, manifest, isProduction) {
11
- const rel = pageFile.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
11
+ // Sanitizar nombre: convertir [id] a _id_ para nombres de archivo (igual que en pages.ts)
12
+ const rel = pageFile
13
+ .replace(/^app\/pages\//, '')
14
+ .replace(/\.tsx$|\.jsx$/, '')
15
+ .replace(/\//g, '__')
16
+ .replace(/\[([^\]]+)\]/g, '_$1_');
12
17
  let entryKey = `${rel}__entry-client.tsx`;
13
18
  if (platformInfo && platformInfo.device !== 'desktop') {
14
19
  // Priority order (from highest to lowest):
@@ -59,7 +64,12 @@ function generateClientCSS(pageFile, platformInfo, manifest, isProduction) {
59
64
  if (!isProduction || !pageFile) {
60
65
  return '';
61
66
  }
62
- const rel = pageFile.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
67
+ // Sanitizar nombre: convertir [id] a _id_ para nombres de archivo (igual que en pages.ts)
68
+ const rel = pageFile
69
+ .replace(/^app\/pages\//, '')
70
+ .replace(/\.tsx$|\.jsx$/, '')
71
+ .replace(/\//g, '__')
72
+ .replace(/\[([^\]]+)\]/g, '_$1_');
63
73
  let entryKey = `dist/.entries/${rel}__entry-client.tsx`;
64
74
  if (platformInfo && platformInfo.device !== 'desktop') {
65
75
  // Priority order (from highest to lowest):
@@ -367,9 +367,11 @@ async function loadMiddlewareModule(middlewareInfo, vite) {
367
367
  }
368
368
  else {
369
369
  // En producción, usar archivos compilados
370
+ // Vite transforms [param] to _param_ in compiled filenames
370
371
  const compiledPath = middlewareInfo.file
371
372
  .replace(/^app\/pages\//, 'dist/server/')
372
- .replace(/\.ts$/, '.js');
373
+ .replace(/\.ts$/, '.js')
374
+ .replace(/\[(\w+)\]/g, '_$1_'); // [photocardId] -> _photocardId_
373
375
  const moduleUrl = node_path_1.default.resolve(compiledPath);
374
376
  moduleToLoad = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
375
377
  }
package/dist/pages.d.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import type { PlatformInfo } from './platform';
2
2
  export interface PageInfo {
3
3
  route: string;
4
+ expressRoute: string;
4
5
  file: string;
5
6
  abs: string;
6
7
  }
8
+ /**
9
+ * Convierte rutas estilo Next.js con parámetros dinámicos a formato Express
10
+ * Ejemplos:
11
+ * '/photocard/[idPhotocard]' → '/photocard/:idPhotocard'
12
+ * '/products/[category]/[id]' → '/products/:category/:id'
13
+ * '/home' → '/home' (sin cambios)
14
+ */
15
+ export declare function convertRouteToExpress(route: string): string;
7
16
  export declare function getPages(): PageInfo[];
8
17
  export declare function getViteInput(): Record<string, string>;
9
18
  export declare function getViteSSRInput(): Record<string, string>;
package/dist/pages.js CHANGED
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.convertRouteToExpress = convertRouteToExpress;
39
40
  exports.getPages = getPages;
40
41
  exports.getViteInput = getViteInput;
41
42
  exports.getViteSSRInput = getViteSSRInput;
@@ -48,6 +49,16 @@ exports.getClientEntriesInput = getClientEntriesInput;
48
49
  const promises_1 = __importDefault(require("node:fs/promises"));
49
50
  const node_path_1 = __importDefault(require("node:path"));
50
51
  const fast_glob_1 = __importDefault(require("fast-glob"));
52
+ /**
53
+ * Convierte rutas estilo Next.js con parámetros dinámicos a formato Express
54
+ * Ejemplos:
55
+ * '/photocard/[idPhotocard]' → '/photocard/:idPhotocard'
56
+ * '/products/[category]/[id]' → '/products/:category/:id'
57
+ * '/home' → '/home' (sin cambios)
58
+ */
59
+ function convertRouteToExpress(route) {
60
+ return route.replace(/\[([^\]]+)\]/g, ':$1');
61
+ }
51
62
  function getPages() {
52
63
  // Busca tanto page.tsx como page/index.tsx y prioriza el más corto
53
64
  const files = fast_glob_1.default.sync('app/pages/**/*.{tsx,jsx}');
@@ -59,7 +70,8 @@ function getPages() {
59
70
  // Prioriza page.tsx sobre page/index.tsx
60
71
  if (!seen.has(route)) {
61
72
  seen.add(route);
62
- pages.push({ route, file, abs: node_path_1.default.resolve(file) });
73
+ const expressRoute = convertRouteToExpress(route);
74
+ pages.push({ route, expressRoute, file, abs: node_path_1.default.resolve(file) });
63
75
  }
64
76
  }
65
77
  return pages;
@@ -119,9 +131,11 @@ function getCompiledProdPage(pageFile) {
119
131
  relativePath = pageFile;
120
132
  }
121
133
  // Convertir app/pages/pokedex/index.tsx a dist/server/pokedex/index.js
134
+ // También convierte [id] a _id_ para rutas dinámicas
122
135
  const compiledRelativePath = relativePath
123
136
  .replace(/^app\/pages\//, '') // Quitar prefijo app/pages/
124
- .replace(/\.tsx$/, '.js'); // Cambiar extensión .tsx a .js
137
+ .replace(/\.tsx$/, '.js') // Cambiar extensión .tsx a .js
138
+ .replace(/\[([^\]]+)\]/g, '_$1_'); // Convertir [id] a _id_
125
139
  return node_path_1.default.resolve(process.cwd(), 'dist', 'server', compiledRelativePath);
126
140
  }
127
141
  async function fileExists(file) {
@@ -185,7 +199,12 @@ async function generateTempEntries(distPath = 'dist', platformInfo) {
185
199
  // Solo crea el directorio si no existe, no lo borra
186
200
  await promises_1.default.mkdir(entriesDir, { recursive: true });
187
201
  const promises = pages.map(async (page) => {
188
- const rel = page.file.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
202
+ // Sanitizar nombre: convertir [id] a _id_ para nombres de archivo
203
+ const rel = page.file
204
+ .replace(/^app\/pages\//, '')
205
+ .replace(/\.tsx$|\.jsx$/, '')
206
+ .replace(/\//g, '__')
207
+ .replace(/\[([^\]]+)\]/g, '_$1_');
189
208
  if (platformInfo && platformInfo.device !== 'desktop') {
190
209
  // Solo genera entry específico para mobile devices (phone, tablet)
191
210
  const basePath = page.file.replace(/\.(tsx|jsx)$/, '');
package/dist/render.js CHANGED
@@ -61,11 +61,23 @@ function createRenderServer(options = {}) {
61
61
  return async (renderOptions) => {
62
62
  const { url, props = {} } = renderOptions;
63
63
  // Encuentra la página que coincide con la URL
64
- const matchedPage = pages.find(page => {
65
- if (page.route === '/')
64
+ // Función helper para hacer matching de rutas dinámicas
65
+ const matchRoute = (pageRoute, url) => {
66
+ if (pageRoute === '/')
66
67
  return url === '/' || url === '';
67
- return url === page.route || url.startsWith(page.route + '/');
68
- });
68
+ // Si no tiene parámetros dinámicos, hacer matching exacto
69
+ if (!pageRoute.includes('[')) {
70
+ return url === pageRoute || url.startsWith(pageRoute + '/');
71
+ }
72
+ // Convertir ruta de página a regex para matching dinámico
73
+ // Ejemplo: /photocard/[id] → /^\/photocard\/([^\/]+)$/
74
+ const regexPattern = pageRoute
75
+ .replace(/\[([^\]]+)\]/g, '([^/]+)') // [id] → ([^/]+)
76
+ .replace(/\//g, '\\/'); // escapar slashes
77
+ const regex = new RegExp(`^${regexPattern}$`);
78
+ return regex.test(url);
79
+ };
80
+ const matchedPage = pages.find(page => matchRoute(page.route, url));
69
81
  if (!matchedPage) {
70
82
  throw new Error(`Page not found for URL: ${url}`);
71
83
  }
package/dist/router.js CHANGED
@@ -42,7 +42,8 @@ async function createAutoViteServer(options = {}) {
42
42
  console.log(`🚀 Auto Vite Server started at http://localhost:${port}`);
43
43
  console.log(`📄 Auto-detected routes:`);
44
44
  pages.forEach(page => {
45
- console.log(` ${page.route} -> ${page.file}`);
45
+ const expressDisplay = page.route !== page.expressRoute ? ` (Express: ${page.expressRoute})` : '';
46
+ console.log(` ${page.route}${expressDisplay} -> ${page.file}`);
46
47
  });
47
48
  }
48
49
  });
@@ -79,7 +80,6 @@ function createAutoRouter(options) {
79
80
  return viteConfig;
80
81
  };
81
82
  const pages = (0, pages_1.getPages)();
82
- // TU PSEUDOCÓDIGO IMPLEMENTADO CORRECTAMENTE:
83
83
  // Initialize Vite ONCE before setting up any routes
84
84
  const setupRoutes = async () => {
85
85
  if (!viteConfig)
@@ -104,9 +104,9 @@ function createAutoRouter(options) {
104
104
  HMRMiddleware,
105
105
  renderMiddleware
106
106
  ];
107
- mainRouter.get(page.route, ...pageMiddlewares);
107
+ mainRouter.get(page.expressRoute, ...pageMiddlewares);
108
108
  // Error middleware separado
109
- mainRouter.use(page.route, (0, middlewares_1.createPageErrorHandler)(page.file, viteConfig?.vite));
109
+ mainRouter.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, viteConfig?.vite));
110
110
  }
111
111
  };
112
112
  // Setup routes asynchronously but don't block router creation
@@ -145,9 +145,9 @@ function createManualRouter(options) {
145
145
  HMRMiddleware,
146
146
  renderMiddleware
147
147
  ];
148
- router.get(page.route, ...pageMiddlewares);
148
+ router.get(page.expressRoute, ...pageMiddlewares);
149
149
  // Error middleware separado
150
- router.use(page.route, (0, middlewares_1.createPageErrorHandler)(page.file, vite));
150
+ router.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, vite));
151
151
  }
152
152
  };
153
153
  // Setup routes asynchronously but don't block router creation
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ressjs/vite-router",
3
- "version": "0.4.2",
4
- "description": "Express router factory for ress.js with automatic page detection and SSR",
3
+ "version": "0.5.2",
4
+ "description": "Express router factory for ress.js with automatic page detection, SSR and dynamic routes support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "publishConfig": {