@ressjs/vite-router 0.4.1 → 0.5.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.
- package/README.md +444 -69
- package/dist/helpers/html-generator.d.ts +1 -0
- package/dist/helpers/html-generator.js +43 -5
- package/dist/helpers/middlewares.d.ts +72 -0
- package/dist/helpers/middlewares.js +381 -0
- package/dist/helpers/request-handler.js +21 -4
- package/dist/helpers/vite-config.d.ts +1 -1
- package/dist/helpers/vite-config.js +8 -15
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5 -4
- package/dist/pages.d.ts +17 -13
- package/dist/pages.js +64 -61
- package/dist/platform.d.ts +0 -8
- package/dist/platform.js +0 -37
- package/dist/render.d.ts +5 -0
- package/dist/render.js +50 -17
- package/dist/router.js +70 -27
- package/package.json +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Request, RequestHandler, Response } from 'express';
|
|
2
|
+
export interface MiddlewareInfo {
|
|
3
|
+
route: string;
|
|
4
|
+
file: string;
|
|
5
|
+
abs: string;
|
|
6
|
+
type: 'page-specific' | 'folder-global';
|
|
7
|
+
depth: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ServerSideResult {
|
|
10
|
+
props?: Record<string, any>;
|
|
11
|
+
title?: string;
|
|
12
|
+
html?: {
|
|
13
|
+
head?: {
|
|
14
|
+
title?: string;
|
|
15
|
+
attributes?: Record<string, string>;
|
|
16
|
+
extraTags?: string[];
|
|
17
|
+
};
|
|
18
|
+
body?: {
|
|
19
|
+
attributes?: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface PageModule {
|
|
24
|
+
default?: React.ComponentType<any>;
|
|
25
|
+
getServerSideProps?: (req: Request, res: Response) => Promise<ServerSideResult> | ServerSideResult;
|
|
26
|
+
errorMiddleware?: (err: any, req: Request, res: Response, next: any) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Busca archivos de middlewares tanto específicos por página como globales por carpeta
|
|
30
|
+
*/
|
|
31
|
+
export declare function getMiddlewares(): MiddlewareInfo[];
|
|
32
|
+
/**
|
|
33
|
+
* Carga middlewares para una página específica y los convierte en middlewares de Express
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadPageMiddlewares(pageRoute: string, vite?: any): Promise<RequestHandler[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Carga el módulo completo de una página
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadPageModule(pageFile: string, vite?: any): Promise<PageModule | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Convierte getServerSideProps en un middleware de Express
|
|
42
|
+
*/
|
|
43
|
+
export declare function createGetServerSidePropsMiddleware(pageModule: PageModule): RequestHandler;
|
|
44
|
+
/**
|
|
45
|
+
* Creates a unified error handler for a specific page route
|
|
46
|
+
* This replaces the old createPageErrorMiddleware with a more useful approach
|
|
47
|
+
*/
|
|
48
|
+
export declare function createPageErrorHandler(pageFile: string, vite?: any): (err: any, req: any, res: any, next: any) => Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Creates a dynamic middleware handler that reloads middlewares on each request in development
|
|
51
|
+
* This enables HMR for middlewares
|
|
52
|
+
* I FUCKING HATE THIS FUNCTION BUT IT SEEMS TO WORK
|
|
53
|
+
*/
|
|
54
|
+
export declare function createDynamicPageMiddlewares(pageRoute: string, pageFile: string, vite?: any): RequestHandler;
|
|
55
|
+
/**
|
|
56
|
+
* 1. Resuelve middlewares previos al getServerSideProps
|
|
57
|
+
*/
|
|
58
|
+
export declare function getPagePrevMiddlewares(pageRoute: string, vite?: any): Promise<RequestHandler[]>;
|
|
59
|
+
/**
|
|
60
|
+
* 2. Convierte getServerSideProps a middleware
|
|
61
|
+
*/
|
|
62
|
+
export declare function getServerSidePropsAsMiddleware(pageFile: string, vite?: any): Promise<RequestHandler>;
|
|
63
|
+
/**
|
|
64
|
+
* 3. Error middleware con fallback automático y jerarquía
|
|
65
|
+
* Orden de prioridad (el más específico gana):
|
|
66
|
+
* 1. errorMiddleware en page.tsx (más específico)
|
|
67
|
+
* 2. errorMiddleware en [page].middlewares.ts
|
|
68
|
+
* 3. errorMiddleware en carpeta/middlewares.ts
|
|
69
|
+
* 4. errorMiddleware en root middlewares.ts (más general)
|
|
70
|
+
* 5. defaultErrorMiddleware (fallback)
|
|
71
|
+
*/
|
|
72
|
+
export declare function getErrorMiddleware(pageFile: string, vite?: any): Promise<any>;
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getMiddlewares = getMiddlewares;
|
|
40
|
+
exports.loadPageMiddlewares = loadPageMiddlewares;
|
|
41
|
+
exports.loadPageModule = loadPageModule;
|
|
42
|
+
exports.createGetServerSidePropsMiddleware = createGetServerSidePropsMiddleware;
|
|
43
|
+
exports.createPageErrorHandler = createPageErrorHandler;
|
|
44
|
+
exports.createDynamicPageMiddlewares = createDynamicPageMiddlewares;
|
|
45
|
+
exports.getPagePrevMiddlewares = getPagePrevMiddlewares;
|
|
46
|
+
exports.getServerSidePropsAsMiddleware = getServerSidePropsAsMiddleware;
|
|
47
|
+
exports.getErrorMiddleware = getErrorMiddleware;
|
|
48
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
49
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
50
|
+
const pages_1 = require("../pages");
|
|
51
|
+
/**
|
|
52
|
+
* Busca archivos de middlewares tanto específicos por página como globales por carpeta
|
|
53
|
+
*/
|
|
54
|
+
function getMiddlewares() {
|
|
55
|
+
// Busca archivos .middlewares.ts y middlewares.ts
|
|
56
|
+
const files = fast_glob_1.default.sync('app/pages/**/*.middlewares.{ts,js}');
|
|
57
|
+
const globalFiles = fast_glob_1.default.sync('app/pages/**/middlewares.{ts,js}');
|
|
58
|
+
const middlewares = [];
|
|
59
|
+
// Procesar archivos específicos de página: page.middlewares.ts
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
let route = file.replace(/^app\/pages/, '').replace(/\.middlewares\.(ts|js)$/, '');
|
|
62
|
+
route = route.replace(/\/index$/, '') || '/';
|
|
63
|
+
// Calcular depth: contar slashes en la ruta del archivo
|
|
64
|
+
const depth = (file.match(/\//g) || []).length;
|
|
65
|
+
middlewares.push({
|
|
66
|
+
route,
|
|
67
|
+
file,
|
|
68
|
+
abs: node_path_1.default.resolve(file),
|
|
69
|
+
type: 'page-specific',
|
|
70
|
+
depth
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Procesar archivos globales: middlewares.ts
|
|
74
|
+
for (const file of globalFiles) {
|
|
75
|
+
let route = file.replace(/^app\/pages/, '').replace(/\/middlewares\.(ts|js)$/, '');
|
|
76
|
+
route = route || '/';
|
|
77
|
+
// Calcular depth: contar slashes en la ruta del archivo
|
|
78
|
+
const depth = (file.match(/\//g) || []).length;
|
|
79
|
+
middlewares.push({
|
|
80
|
+
route,
|
|
81
|
+
file,
|
|
82
|
+
abs: node_path_1.default.resolve(file),
|
|
83
|
+
type: 'folder-global',
|
|
84
|
+
depth
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return middlewares;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Carga middlewares para una página específica y los convierte en middlewares de Express
|
|
91
|
+
*/
|
|
92
|
+
async function loadPageMiddlewares(pageRoute, vite) {
|
|
93
|
+
const allMiddlewares = getMiddlewares();
|
|
94
|
+
// Filtrar middlewares relevantes para esta página usando jerarquía
|
|
95
|
+
const relevantMiddlewares = allMiddlewares.filter(middleware => {
|
|
96
|
+
if (middleware.type === 'folder-global') {
|
|
97
|
+
// Un middleware global aplica si la página está dentro de su carpeta
|
|
98
|
+
// ej: middleware en "/pokedex" aplica a "/pokedex" y "/pokedex/detail"
|
|
99
|
+
return pageRoute.startsWith(middleware.route) || middleware.route === '/';
|
|
100
|
+
}
|
|
101
|
+
if (middleware.type === 'page-specific') {
|
|
102
|
+
// Un middleware específico aplica solo a esa página exacta por ruta
|
|
103
|
+
return middleware.route === pageRoute;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
});
|
|
107
|
+
// Ordenar por jerarquía: depth menor primero, luego por tipo
|
|
108
|
+
relevantMiddlewares.sort((a, b) => {
|
|
109
|
+
// Primero por depth (carpetas más arriba en la jerarquía van primero)
|
|
110
|
+
if (a.depth !== b.depth) {
|
|
111
|
+
return a.depth - b.depth;
|
|
112
|
+
}
|
|
113
|
+
// Si están al mismo nivel, folder-global va antes que page-specific
|
|
114
|
+
if (a.type === 'folder-global' && b.type === 'page-specific')
|
|
115
|
+
return -1;
|
|
116
|
+
if (a.type === 'page-specific' && b.type === 'folder-global')
|
|
117
|
+
return 1;
|
|
118
|
+
return 0;
|
|
119
|
+
});
|
|
120
|
+
const expressMiddlewares = [];
|
|
121
|
+
// Cargar middlewares de archivos .middlewares.ts
|
|
122
|
+
for (const middlewareFile of relevantMiddlewares) {
|
|
123
|
+
try {
|
|
124
|
+
const moduleToLoad = await loadMiddlewareModule(middlewareFile, vite);
|
|
125
|
+
const handlers = moduleToLoad.default || [];
|
|
126
|
+
for (const handler of handlers) {
|
|
127
|
+
if (typeof handler === 'function') {
|
|
128
|
+
expressMiddlewares.push(handler);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(`Error loading middleware ${middlewareFile.file}:`, error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return expressMiddlewares;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Carga el módulo completo de una página
|
|
140
|
+
*/
|
|
141
|
+
async function loadPageModule(pageFile, vite) {
|
|
142
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
143
|
+
const cacheKey = `${pageFile}-${process.env.NODE_ENV || 'development'}`;
|
|
144
|
+
// Only use cache in production mode to allow HMR in development
|
|
145
|
+
if (isProduction && pageModuleCache.has(cacheKey)) {
|
|
146
|
+
return pageModuleCache.get(cacheKey) || null;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
let pageModule;
|
|
150
|
+
if (vite) {
|
|
151
|
+
const modulePath = `/${pageFile}`;
|
|
152
|
+
pageModule = await vite.ssrLoadModule(modulePath);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
if (pageFile.endsWith('.tsx')) {
|
|
156
|
+
const moduleUrl = (0, pages_1.getCompiledProdPage)(pageFile);
|
|
157
|
+
pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const moduleUrl = node_path_1.default.resolve(pageFile);
|
|
161
|
+
pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const result = pageModule;
|
|
165
|
+
// Only cache in production mode
|
|
166
|
+
if (isProduction) {
|
|
167
|
+
pageModuleCache.set(cacheKey, result);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error(`Error loading page module ${pageFile}:`, error);
|
|
173
|
+
// Only cache null result in production mode
|
|
174
|
+
if (isProduction) {
|
|
175
|
+
pageModuleCache.set(cacheKey, null);
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Convierte getServerSideProps en un middleware de Express
|
|
182
|
+
*/
|
|
183
|
+
function createGetServerSidePropsMiddleware(pageModule) {
|
|
184
|
+
return async (req, res, next) => {
|
|
185
|
+
if (!pageModule.getServerSideProps) {
|
|
186
|
+
next();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const result = await pageModule.getServerSideProps(req, res);
|
|
191
|
+
// Manejar ServerSideResult
|
|
192
|
+
if (result && typeof result === 'object') {
|
|
193
|
+
// SEPARAR: Configuración HTML se queda en servidor, props van al cliente
|
|
194
|
+
// Configuración HTML → solo para el servidor (NO va al cliente)
|
|
195
|
+
if (result.title || result.html) {
|
|
196
|
+
res.locals.htmlConfig = {
|
|
197
|
+
title: result.title,
|
|
198
|
+
html: result.html
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Props → solo en un bucket claro (serverSideProps)
|
|
202
|
+
if (result.props) {
|
|
203
|
+
res.locals.serverSideProps = {
|
|
204
|
+
...(res.locals.serverSideProps || {}),
|
|
205
|
+
...result.props
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
next();
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
next(error);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Creates a unified error handler for a specific page route
|
|
218
|
+
* This replaces the old createPageErrorMiddleware with a more useful approach
|
|
219
|
+
*/
|
|
220
|
+
function createPageErrorHandler(pageFile, vite) {
|
|
221
|
+
return async (err, req, res, next) => {
|
|
222
|
+
const errorMiddleware = await getErrorMiddleware(pageFile, vite);
|
|
223
|
+
errorMiddleware(err, req, res, next);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Error middleware por defecto
|
|
228
|
+
*/
|
|
229
|
+
const defaultErrorMiddleware = (err, _req, res, _next) => {
|
|
230
|
+
console.error(err);
|
|
231
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
232
|
+
console.warn('No custom error handler configured. Add errorMiddleware export to your page or middleware file.');
|
|
233
|
+
}
|
|
234
|
+
res.status(500).send(err.message || 'Internal Server Error');
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Creates a dynamic middleware handler that reloads middlewares on each request in development
|
|
238
|
+
* This enables HMR for middlewares
|
|
239
|
+
* I FUCKING HATE THIS FUNCTION BUT IT SEEMS TO WORK
|
|
240
|
+
*/
|
|
241
|
+
function createDynamicPageMiddlewares(pageRoute, pageFile, vite) {
|
|
242
|
+
return async (req, res, next) => {
|
|
243
|
+
try {
|
|
244
|
+
const middlewares = [
|
|
245
|
+
...(await loadPageMiddlewares(pageRoute, vite)),
|
|
246
|
+
await getServerSidePropsAsMiddleware(pageFile, vite)
|
|
247
|
+
];
|
|
248
|
+
// Execute middlewares sequentially
|
|
249
|
+
let currentIndex = 0;
|
|
250
|
+
const executeNext = (err) => {
|
|
251
|
+
if (err)
|
|
252
|
+
return next(err);
|
|
253
|
+
const currentMiddleware = middlewares[currentIndex++];
|
|
254
|
+
if (!currentMiddleware)
|
|
255
|
+
return next();
|
|
256
|
+
currentMiddleware(req, res, executeNext);
|
|
257
|
+
};
|
|
258
|
+
executeNext();
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
next(error);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 1. Resuelve middlewares previos al getServerSideProps
|
|
267
|
+
*/
|
|
268
|
+
async function getPagePrevMiddlewares(pageRoute, vite) {
|
|
269
|
+
return await loadPageMiddlewares(pageRoute, vite);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 2. Convierte getServerSideProps a middleware
|
|
273
|
+
*/
|
|
274
|
+
async function getServerSidePropsAsMiddleware(pageFile, vite) {
|
|
275
|
+
const pageModule = await loadPageModule(pageFile, vite);
|
|
276
|
+
return pageModule ? createGetServerSidePropsMiddleware(pageModule) : (_req, _res, next) => next();
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* 3. Error middleware con fallback automático y jerarquía
|
|
280
|
+
* Orden de prioridad (el más específico gana):
|
|
281
|
+
* 1. errorMiddleware en page.tsx (más específico)
|
|
282
|
+
* 2. errorMiddleware en [page].middlewares.ts
|
|
283
|
+
* 3. errorMiddleware en carpeta/middlewares.ts
|
|
284
|
+
* 4. errorMiddleware en root middlewares.ts (más general)
|
|
285
|
+
* 5. defaultErrorMiddleware (fallback)
|
|
286
|
+
*/
|
|
287
|
+
async function getErrorMiddleware(pageFile, vite) {
|
|
288
|
+
const pageModule = await loadPageModule(pageFile, vite);
|
|
289
|
+
// 1. Prioridad máxima: errorMiddleware en la página
|
|
290
|
+
if (pageModule?.errorMiddleware) {
|
|
291
|
+
return pageModule.errorMiddleware;
|
|
292
|
+
}
|
|
293
|
+
// 2-4. Buscar errorMiddleware en middlewares siguiendo jerarquía
|
|
294
|
+
const errorMiddleware = await findErrorMiddlewareInHierarchy(pageFile, vite);
|
|
295
|
+
if (errorMiddleware) {
|
|
296
|
+
return errorMiddleware;
|
|
297
|
+
}
|
|
298
|
+
// 5. Fallback al default
|
|
299
|
+
return defaultErrorMiddleware;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Busca errorMiddleware en la jerarquía de middlewares
|
|
303
|
+
* Desde el más específico (página) hasta el más general (root)
|
|
304
|
+
*/
|
|
305
|
+
async function findErrorMiddlewareInHierarchy(pageFile, vite) {
|
|
306
|
+
// Obtener todos los middlewares que podrían aplicar a esta página
|
|
307
|
+
const allMiddlewares = getMiddlewares();
|
|
308
|
+
// Convertir pageFile a route para matching
|
|
309
|
+
let pageRoute = pageFile.replace(/^app\/pages/, '').replace(/\.(tsx|ts|jsx|js)$/, '');
|
|
310
|
+
pageRoute = pageRoute.replace(/\/index$/, '') || '/';
|
|
311
|
+
// Filtrar middlewares relevantes para esta página
|
|
312
|
+
const relevantMiddlewares = allMiddlewares
|
|
313
|
+
.filter(mw => {
|
|
314
|
+
// Para page-specific middlewares: debe coincidir exactamente con la ruta
|
|
315
|
+
if (mw.type === 'page-specific') {
|
|
316
|
+
return mw.route === pageRoute;
|
|
317
|
+
}
|
|
318
|
+
// Para folder-global middlewares: la página debe estar en esa carpeta o subcarpeta
|
|
319
|
+
if (mw.route === '/')
|
|
320
|
+
return true; // root middleware applies to all
|
|
321
|
+
// Si el middleware está en una carpeta, debe aplicar a todas las páginas de esa carpeta
|
|
322
|
+
// Ejemplo: /error-test/middlewares.ts aplica a /error-test/index, /error-test/sub, etc.
|
|
323
|
+
return pageRoute.startsWith(mw.route + '/') || pageRoute === mw.route;
|
|
324
|
+
})
|
|
325
|
+
.sort((a, b) => {
|
|
326
|
+
// Ordenar por especificidad: mayor depth = más específico
|
|
327
|
+
if (a.depth !== b.depth)
|
|
328
|
+
return b.depth - a.depth;
|
|
329
|
+
// Si mismo depth, page-specific > folder-global
|
|
330
|
+
if (a.type === 'page-specific' && b.type === 'folder-global')
|
|
331
|
+
return -1;
|
|
332
|
+
if (a.type === 'folder-global' && b.type === 'page-specific')
|
|
333
|
+
return 1;
|
|
334
|
+
return 0;
|
|
335
|
+
});
|
|
336
|
+
// Buscar errorMiddleware en orden de especificidad
|
|
337
|
+
for (const middlewareInfo of relevantMiddlewares) {
|
|
338
|
+
try {
|
|
339
|
+
const middlewareModule = await loadMiddlewareModule(middlewareInfo, vite);
|
|
340
|
+
if (middlewareModule?.errorMiddleware) {
|
|
341
|
+
return middlewareModule.errorMiddleware;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.error(`Error loading middleware ${middlewareInfo.file}:`, error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
// Cache for loaded middleware modules to avoid multiple ssrLoadModule calls
|
|
351
|
+
const middlewareModuleCache = new Map();
|
|
352
|
+
// Cache for loaded page modules to avoid multiple ssrLoadModule calls
|
|
353
|
+
const pageModuleCache = new Map();
|
|
354
|
+
/**
|
|
355
|
+
* Cargar un módulo de middleware específico
|
|
356
|
+
*/
|
|
357
|
+
async function loadMiddlewareModule(middlewareInfo, vite) {
|
|
358
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
359
|
+
const cacheKey = `${middlewareInfo.file}-${process.env.NODE_ENV || 'development'}`;
|
|
360
|
+
// Only use cache in production mode to allow HMR in development
|
|
361
|
+
if (isProduction && middlewareModuleCache.has(cacheKey)) {
|
|
362
|
+
return middlewareModuleCache.get(cacheKey);
|
|
363
|
+
}
|
|
364
|
+
let moduleToLoad;
|
|
365
|
+
if (!isProduction && vite) {
|
|
366
|
+
moduleToLoad = await vite.ssrLoadModule(middlewareInfo.file);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
// En producción, usar archivos compilados
|
|
370
|
+
const compiledPath = middlewareInfo.file
|
|
371
|
+
.replace(/^app\/pages\//, 'dist/server/')
|
|
372
|
+
.replace(/\.ts$/, '.js');
|
|
373
|
+
const moduleUrl = node_path_1.default.resolve(compiledPath);
|
|
374
|
+
moduleToLoad = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
375
|
+
}
|
|
376
|
+
// Only cache in production mode
|
|
377
|
+
if (isProduction) {
|
|
378
|
+
middlewareModuleCache.set(cacheKey, moduleToLoad);
|
|
379
|
+
}
|
|
380
|
+
return moduleToLoad;
|
|
381
|
+
}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.processRequest = processRequest;
|
|
7
7
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
8
|
const pages_1 = require("../pages");
|
|
9
|
+
const render_1 = require("../render");
|
|
9
10
|
const platform_1 = require("../platform");
|
|
10
11
|
const html_generator_1 = require("./html-generator");
|
|
11
12
|
async function preparePlatform(req, enablePlatformDetection = true, isProduction = false) {
|
|
@@ -25,7 +26,12 @@ async function preparePlatform(req, enablePlatformDetection = true, isProduction
|
|
|
25
26
|
}
|
|
26
27
|
async function processRequest(context, config, renderServer) {
|
|
27
28
|
const { req, res, page, next } = context;
|
|
28
|
-
|
|
29
|
+
// config.viteConfig can be a ViteConfig or a thunk returning it (auto mode)
|
|
30
|
+
const resolvedViteConfig = typeof config.viteConfig === 'function'
|
|
31
|
+
? config.viteConfig()
|
|
32
|
+
: config.viteConfig;
|
|
33
|
+
const { enablePlatformDetection, renderFunction } = config;
|
|
34
|
+
const viteConfig = resolvedViteConfig;
|
|
29
35
|
try {
|
|
30
36
|
const platformContext = await preparePlatform(req, enablePlatformDetection, viteConfig.isProduction);
|
|
31
37
|
if (renderFunction) {
|
|
@@ -40,13 +46,22 @@ async function processRequest(context, config, renderServer) {
|
|
|
40
46
|
}
|
|
41
47
|
if (!renderServer) {
|
|
42
48
|
const renderOptions = viteConfig.vite ? { vite: viteConfig.vite } : {};
|
|
43
|
-
renderServer = (0,
|
|
49
|
+
renderServer = (0, render_1.createRenderServer)(renderOptions);
|
|
44
50
|
}
|
|
45
51
|
const protocol = req.headers['x-forwarded-proto'] || (req.connection?.encrypted ? 'https' : 'http');
|
|
46
52
|
const host = req.headers.host || 'localhost';
|
|
47
53
|
const urlObj = new URL(req.url, `${protocol}://${host}`);
|
|
48
54
|
const pathname = urlObj.pathname;
|
|
49
|
-
|
|
55
|
+
// Inicializar res.locals si no existe (para middlewares)
|
|
56
|
+
if (!res.locals) {
|
|
57
|
+
res.locals = {};
|
|
58
|
+
}
|
|
59
|
+
const rendered = await renderServer({
|
|
60
|
+
url: pathname,
|
|
61
|
+
req,
|
|
62
|
+
res,
|
|
63
|
+
props: res.locals.serverSideProps
|
|
64
|
+
});
|
|
50
65
|
const template = await getTemplate(req, viteConfig);
|
|
51
66
|
const html = await (0, html_generator_1.generateCompleteHTML)({
|
|
52
67
|
template,
|
|
@@ -55,7 +70,9 @@ async function processRequest(context, config, renderServer) {
|
|
|
55
70
|
manifest: viteConfig.manifest,
|
|
56
71
|
platform: platformContext.platform,
|
|
57
72
|
isProduction: viteConfig.isProduction,
|
|
58
|
-
platformInfo: platformContext.platformInfo
|
|
73
|
+
platformInfo: platformContext.platformInfo,
|
|
74
|
+
// pasar solo la config segura
|
|
75
|
+
...(res.locals?.htmlConfig ? { serverSideResult: res.locals.htmlConfig } : {})
|
|
59
76
|
});
|
|
60
77
|
res.setHeader('Content-Type', 'text/html');
|
|
61
78
|
res.send(html);
|
|
@@ -5,6 +5,6 @@ export interface ViteConfig {
|
|
|
5
5
|
isProduction: boolean;
|
|
6
6
|
}
|
|
7
7
|
export declare function setupDev(base?: string): Promise<ViteConfig>;
|
|
8
|
-
export declare function setupProd(): Promise<
|
|
8
|
+
export declare function setupProd(): Promise<ViteConfig>;
|
|
9
9
|
export declare function createProdMiddleware(): () => Promise<import("express-serve-static-core").Router>;
|
|
10
10
|
export declare function setupVite(isProduction: boolean, base?: string): Promise<ViteConfig>;
|
|
@@ -63,7 +63,12 @@ async function setupDev(base = '/') {
|
|
|
63
63
|
async function setupProd() {
|
|
64
64
|
const manifest = JSON.parse(await promises_1.default.readFile('./dist/client/.vite/manifest.json', 'utf-8'));
|
|
65
65
|
const templateHtml = await promises_1.default.readFile('./index.html', 'utf-8');
|
|
66
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
vite: null,
|
|
68
|
+
manifest,
|
|
69
|
+
templateHtml,
|
|
70
|
+
isProduction: true
|
|
71
|
+
};
|
|
67
72
|
}
|
|
68
73
|
function createProdMiddleware() {
|
|
69
74
|
const middlewares = express_1.default.Router();
|
|
@@ -77,21 +82,9 @@ function createProdMiddleware() {
|
|
|
77
82
|
}
|
|
78
83
|
async function setupVite(isProduction, base = '/') {
|
|
79
84
|
if (isProduction) {
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
vite: undefined,
|
|
83
|
-
manifest: prodConfig.manifest,
|
|
84
|
-
templateHtml: prodConfig.templateHtml,
|
|
85
|
-
isProduction: true
|
|
86
|
-
};
|
|
85
|
+
return await setupProd();
|
|
87
86
|
}
|
|
88
87
|
else {
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
vite: devConfig.vite,
|
|
92
|
-
manifest: devConfig.manifest,
|
|
93
|
-
templateHtml: '',
|
|
94
|
-
isProduction: false
|
|
95
|
-
};
|
|
88
|
+
return await setupDev(base);
|
|
96
89
|
}
|
|
97
90
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createViteRouter, createAutoViteServer, listDetectedPages, getPages } from './router';
|
|
2
|
-
export { getViteInput, getClientEntriesInput, generateTempEntries,
|
|
3
|
-
export {
|
|
2
|
+
export { getViteInput, getViteSSRInput, getClientEntriesInput, generateTempEntries, resolvePlatformCSS } from './pages';
|
|
3
|
+
export { createRenderServer } from './render';
|
|
4
|
+
export { detectPlatform } from './platform';
|
|
4
5
|
export type { PlatformInfo } from './platform';
|
|
5
6
|
export type { ViteRouterOptions, AutoViteServerOptions } from './router';
|
|
6
7
|
export type { PageInfo } from './pages';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.detectPlatform = exports.createRenderServer = exports.resolvePlatformCSS = exports.generateTempEntries = exports.getClientEntriesInput = exports.getViteSSRInput = exports.getViteInput = exports.getPages = exports.listDetectedPages = exports.createAutoViteServer = exports.createViteRouter = void 0;
|
|
4
4
|
// Main API
|
|
5
5
|
var router_1 = require("./router");
|
|
6
6
|
Object.defineProperty(exports, "createViteRouter", { enumerable: true, get: function () { return router_1.createViteRouter; } });
|
|
@@ -10,12 +10,13 @@ Object.defineProperty(exports, "getPages", { enumerable: true, get: function ()
|
|
|
10
10
|
// Core functions for Vite config
|
|
11
11
|
var pages_1 = require("./pages");
|
|
12
12
|
Object.defineProperty(exports, "getViteInput", { enumerable: true, get: function () { return pages_1.getViteInput; } });
|
|
13
|
+
Object.defineProperty(exports, "getViteSSRInput", { enumerable: true, get: function () { return pages_1.getViteSSRInput; } });
|
|
13
14
|
Object.defineProperty(exports, "getClientEntriesInput", { enumerable: true, get: function () { return pages_1.getClientEntriesInput; } });
|
|
14
15
|
Object.defineProperty(exports, "generateTempEntries", { enumerable: true, get: function () { return pages_1.generateTempEntries; } });
|
|
15
|
-
Object.defineProperty(exports, "createRenderServer", { enumerable: true, get: function () { return pages_1.createRenderServer; } });
|
|
16
16
|
Object.defineProperty(exports, "resolvePlatformCSS", { enumerable: true, get: function () { return pages_1.resolvePlatformCSS; } });
|
|
17
|
+
// Render functions
|
|
18
|
+
var render_1 = require("./render");
|
|
19
|
+
Object.defineProperty(exports, "createRenderServer", { enumerable: true, get: function () { return render_1.createRenderServer; } });
|
|
17
20
|
// Platform detection
|
|
18
21
|
var platform_1 = require("./platform");
|
|
19
22
|
Object.defineProperty(exports, "detectPlatform", { enumerable: true, get: function () { return platform_1.detectPlatform; } });
|
|
20
|
-
Object.defineProperty(exports, "getPlatformCSSClass", { enumerable: true, get: function () { return platform_1.getPlatformCSSClass; } });
|
|
21
|
-
Object.defineProperty(exports, "resolveEntryClient", { enumerable: true, get: function () { return platform_1.resolveEntryClient; } });
|
package/dist/pages.d.ts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
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>;
|
|
18
|
+
export declare function getViteSSRInput(): Record<string, string>;
|
|
9
19
|
export declare function loadManifest(distPath?: string): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* Convierte una ruta de página TypeScript a su equivalente compilado en producción
|
|
22
|
+
*
|
|
23
|
+
* @param pageFile - Ruta del archivo de página (relativa: "app/pages/pokedex/index.tsx" o absoluta: "/path/to/app/pages/pokedex/index.tsx")
|
|
24
|
+
* @returns Ruta absoluta del archivo compilado (ej: "/path/to/project/dist/server/pokedex/index.js")
|
|
25
|
+
*/
|
|
26
|
+
export declare function getCompiledProdPage(pageFile: string): string;
|
|
10
27
|
export declare function resolvePlatformCSS(basePath: string, platform: PlatformInfo): Promise<string>;
|
|
11
28
|
export declare function generateTempEntries(distPath?: string, platformInfo?: PlatformInfo): Promise<string[]>;
|
|
12
29
|
export declare function generateSingleEntry(page: PageInfo, entryPath: string, preferredScssFile: string): Promise<void>;
|
|
13
30
|
export declare function getClientEntriesInput(distPath?: string): Promise<Record<string, string>>;
|
|
14
|
-
export interface RenderOptions {
|
|
15
|
-
url: string;
|
|
16
|
-
props?: any;
|
|
17
|
-
}
|
|
18
|
-
export interface CreateRenderServerOptions {
|
|
19
|
-
vite?: any;
|
|
20
|
-
}
|
|
21
|
-
export declare function createRenderServer(options?: CreateRenderServerOptions): (renderOptions: RenderOptions) => Promise<{
|
|
22
|
-
html: string;
|
|
23
|
-
props: any;
|
|
24
|
-
route: string;
|
|
25
|
-
page: string;
|
|
26
|
-
}>;
|