@ressjs/vite-router 0.4.1 → 0.4.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/dist/helpers/html-generator.d.ts +1 -0
- package/dist/helpers/html-generator.js +31 -3
- 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 +8 -13
- package/dist/pages.js +43 -59
- package/dist/platform.d.ts +0 -8
- package/dist/platform.js +0 -37
- package/dist/render.d.ts +5 -0
- package/dist/render.js +34 -13
- package/dist/router.js +70 -27
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ export interface HTMLGenerationContext {
|
|
|
7
7
|
platform: string;
|
|
8
8
|
isProduction: boolean;
|
|
9
9
|
platformInfo: PlatformInfo | null;
|
|
10
|
+
serverSideResult?: any;
|
|
10
11
|
}
|
|
11
12
|
export declare function generateClientScript(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): Promise<string | null>;
|
|
12
13
|
export declare function generateClientCSS(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): string;
|
|
@@ -90,12 +90,40 @@ function generateClientCSS(pageFile, platformInfo, manifest, isProduction) {
|
|
|
90
90
|
return '';
|
|
91
91
|
}
|
|
92
92
|
async function generateCompleteHTML(context) {
|
|
93
|
-
const { template, rendered, page, manifest, isProduction, platformInfo } = context;
|
|
93
|
+
const { template, rendered, page, manifest, isProduction, platformInfo, serverSideResult } = context;
|
|
94
94
|
const scriptSrc = await generateClientScript(page.file, platformInfo, manifest, isProduction);
|
|
95
95
|
const scriptTag = scriptSrc ? `<script type="module" src="${scriptSrc}"></script>` : '';
|
|
96
96
|
const cssLinks = page?.file ? generateClientCSS(page.file, platformInfo, manifest, isProduction) : '';
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// Construir contenido HEAD extra y atributos desde serverSideResult
|
|
98
|
+
let headContent = cssLinks;
|
|
99
|
+
let htmlAttributes = '';
|
|
100
|
+
let bodyAttributes = '';
|
|
101
|
+
let pageTitle = '';
|
|
102
|
+
if (serverSideResult?.html) {
|
|
103
|
+
if (serverSideResult.html.head?.attributes) {
|
|
104
|
+
htmlAttributes = Object.entries(serverSideResult.html.head.attributes)
|
|
105
|
+
.map(([k, v]) => `${k}="${v}"`).join(' ');
|
|
106
|
+
}
|
|
107
|
+
if (serverSideResult.html.body?.attributes) {
|
|
108
|
+
bodyAttributes = Object.entries(serverSideResult.html.body.attributes)
|
|
109
|
+
.map(([k, v]) => `${k}="${v}"`).join(' ');
|
|
110
|
+
}
|
|
111
|
+
if (serverSideResult.html.head?.extraTags) {
|
|
112
|
+
headContent += '\n' + serverSideResult.html.head.extraTags.join('\n');
|
|
113
|
+
}
|
|
114
|
+
if (serverSideResult.title || serverSideResult.html.head?.title) {
|
|
115
|
+
pageTitle = `<title>${serverSideResult.title || serverSideResult.html.head?.title}</title>`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let html = template;
|
|
119
|
+
if (htmlAttributes)
|
|
120
|
+
html = html.replace('<html', `<html ${htmlAttributes}`);
|
|
121
|
+
if (bodyAttributes)
|
|
122
|
+
html = html.replace('<body', `<body ${bodyAttributes}`);
|
|
123
|
+
if (pageTitle)
|
|
124
|
+
html = html.replace(/<title>.*?<\/title>/i, pageTitle);
|
|
125
|
+
html = html
|
|
126
|
+
.replace('<!--app-head-->', headContent)
|
|
99
127
|
.replace('<!--app-html-->', rendered.html)
|
|
100
128
|
.replace('</body>', `<script>
|
|
101
129
|
window.__INITIAL_PROPS__=${JSON.stringify(rendered.props || {})};
|
|
@@ -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
|
@@ -6,21 +6,16 @@ export interface PageInfo {
|
|
|
6
6
|
}
|
|
7
7
|
export declare function getPages(): PageInfo[];
|
|
8
8
|
export declare function getViteInput(): Record<string, string>;
|
|
9
|
+
export declare function getViteSSRInput(): Record<string, string>;
|
|
9
10
|
export declare function loadManifest(distPath?: string): Promise<any>;
|
|
11
|
+
/**
|
|
12
|
+
* Convierte una ruta de página TypeScript a su equivalente compilado en producción
|
|
13
|
+
*
|
|
14
|
+
* @param pageFile - Ruta del archivo de página (relativa: "app/pages/pokedex/index.tsx" o absoluta: "/path/to/app/pages/pokedex/index.tsx")
|
|
15
|
+
* @returns Ruta absoluta del archivo compilado (ej: "/path/to/project/dist/server/pokedex/index.js")
|
|
16
|
+
*/
|
|
17
|
+
export declare function getCompiledProdPage(pageFile: string): string;
|
|
10
18
|
export declare function resolvePlatformCSS(basePath: string, platform: PlatformInfo): Promise<string>;
|
|
11
19
|
export declare function generateTempEntries(distPath?: string, platformInfo?: PlatformInfo): Promise<string[]>;
|
|
12
20
|
export declare function generateSingleEntry(page: PageInfo, entryPath: string, preferredScssFile: string): Promise<void>;
|
|
13
21
|
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
|
-
}>;
|
package/dist/pages.js
CHANGED
|
@@ -38,17 +38,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.getPages = getPages;
|
|
40
40
|
exports.getViteInput = getViteInput;
|
|
41
|
+
exports.getViteSSRInput = getViteSSRInput;
|
|
41
42
|
exports.loadManifest = loadManifest;
|
|
43
|
+
exports.getCompiledProdPage = getCompiledProdPage;
|
|
42
44
|
exports.resolvePlatformCSS = resolvePlatformCSS;
|
|
43
45
|
exports.generateTempEntries = generateTempEntries;
|
|
44
46
|
exports.generateSingleEntry = generateSingleEntry;
|
|
45
47
|
exports.getClientEntriesInput = getClientEntriesInput;
|
|
46
|
-
exports.createRenderServer = createRenderServer;
|
|
47
48
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
48
49
|
const node_path_1 = __importDefault(require("node:path"));
|
|
49
50
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
50
|
-
const react_1 = __importDefault(require("react"));
|
|
51
|
-
const server_1 = require("react-dom/server");
|
|
52
51
|
function getPages() {
|
|
53
52
|
// Busca tanto page.tsx como page/index.tsx y prioriza el más corto
|
|
54
53
|
const files = fast_glob_1.default.sync('app/pages/**/*.{tsx,jsx}');
|
|
@@ -74,6 +73,17 @@ function getViteInput() {
|
|
|
74
73
|
}
|
|
75
74
|
return input;
|
|
76
75
|
}
|
|
76
|
+
// Para Vite SSR: input incluyendo páginas + middlewares
|
|
77
|
+
function getViteSSRInput() {
|
|
78
|
+
const input = getViteInput(); // Empezar con las páginas
|
|
79
|
+
// Agregar TODOS los middlewares: .middlewares.ts y middlewares.ts
|
|
80
|
+
const middlewareFiles = fast_glob_1.default.sync('app/pages/**/{middlewares,*.middlewares}.{ts,js}');
|
|
81
|
+
for (const middlewareFile of middlewareFiles) {
|
|
82
|
+
const key = middlewareFile.replace(/^app\/pages\//, '').replace(/\.(ts|js)$/, '');
|
|
83
|
+
input[key] = node_path_1.default.resolve(middlewareFile);
|
|
84
|
+
}
|
|
85
|
+
return input;
|
|
86
|
+
}
|
|
77
87
|
async function loadManifest(distPath = 'dist') {
|
|
78
88
|
const assetsDir = node_path_1.default.join(distPath, 'server/.vite');
|
|
79
89
|
const files = await promises_1.default.readdir(assetsDir);
|
|
@@ -84,6 +94,36 @@ async function loadManifest(distPath = 'dist') {
|
|
|
84
94
|
const manifest = (await Promise.resolve(`${node_path_1.default.join(assetsDir, manifestFile)}`).then(s => __importStar(require(s)))).default;
|
|
85
95
|
return manifest;
|
|
86
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Convierte una ruta de página TypeScript a su equivalente compilado en producción
|
|
99
|
+
*
|
|
100
|
+
* @param pageFile - Ruta del archivo de página (relativa: "app/pages/pokedex/index.tsx" o absoluta: "/path/to/app/pages/pokedex/index.tsx")
|
|
101
|
+
* @returns Ruta absoluta del archivo compilado (ej: "/path/to/project/dist/server/pokedex/index.js")
|
|
102
|
+
*/
|
|
103
|
+
function getCompiledProdPage(pageFile) {
|
|
104
|
+
if (!pageFile.endsWith('.tsx')) {
|
|
105
|
+
throw new Error(`getCompiledProdPage expects a .tsx file, got: ${pageFile}`);
|
|
106
|
+
}
|
|
107
|
+
// Si es una ruta absoluta, extraer solo la parte relativa
|
|
108
|
+
let relativePath;
|
|
109
|
+
if (node_path_1.default.isAbsolute(pageFile)) {
|
|
110
|
+
// Buscar la parte después de "app/pages/" or "app\pages\" (Windows-compatible)
|
|
111
|
+
const normalizedPath = pageFile.replace(/\\/g, '/');
|
|
112
|
+
const appPagesIndex = normalizedPath.indexOf('app/pages/');
|
|
113
|
+
if (appPagesIndex === -1) {
|
|
114
|
+
throw new Error(`Absolute path must contain "app/pages/", got: ${pageFile}`);
|
|
115
|
+
}
|
|
116
|
+
relativePath = normalizedPath.substring(appPagesIndex);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
relativePath = pageFile;
|
|
120
|
+
}
|
|
121
|
+
// Convertir app/pages/pokedex/index.tsx a dist/server/pokedex/index.js
|
|
122
|
+
const compiledRelativePath = relativePath
|
|
123
|
+
.replace(/^app\/pages\//, '') // Quitar prefijo app/pages/
|
|
124
|
+
.replace(/\.tsx$/, '.js'); // Cambiar extensión .tsx a .js
|
|
125
|
+
return node_path_1.default.resolve(process.cwd(), 'dist', 'server', compiledRelativePath);
|
|
126
|
+
}
|
|
87
127
|
async function fileExists(file) {
|
|
88
128
|
try {
|
|
89
129
|
await promises_1.default.access(file);
|
|
@@ -238,59 +278,3 @@ async function getClientEntriesInput(distPath = 'dist') {
|
|
|
238
278
|
}
|
|
239
279
|
return input;
|
|
240
280
|
}
|
|
241
|
-
function createRenderServer(options = {}) {
|
|
242
|
-
const pages = getPages();
|
|
243
|
-
const { vite } = options;
|
|
244
|
-
return async (renderOptions) => {
|
|
245
|
-
const { url, props = {} } = renderOptions;
|
|
246
|
-
// Encuentra la página que coincide con la URL
|
|
247
|
-
const matchedPage = pages.find(page => {
|
|
248
|
-
if (page.route === '/')
|
|
249
|
-
return url === '/' || url === '';
|
|
250
|
-
return url === page.route || url.startsWith(page.route + '/');
|
|
251
|
-
});
|
|
252
|
-
if (!matchedPage) {
|
|
253
|
-
throw new Error(`Page not found for URL: ${url}`);
|
|
254
|
-
}
|
|
255
|
-
try {
|
|
256
|
-
// Import dinámico de la página usando vite.ssrLoadModule si está disponible
|
|
257
|
-
let pageModule;
|
|
258
|
-
if (vite) {
|
|
259
|
-
// En desarrollo: usar vite.ssrLoadModule para manejar .tsx
|
|
260
|
-
const modulePath = `/${matchedPage.file}`;
|
|
261
|
-
pageModule = await vite.ssrLoadModule(modulePath);
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
// En producción: usar import directo del archivo compilado
|
|
265
|
-
const moduleUrl = matchedPage.abs.replace(/\.tsx$/, '.js').replace('/app/pages/', '/dist/server/');
|
|
266
|
-
pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
267
|
-
}
|
|
268
|
-
const PageComponent = pageModule.default;
|
|
269
|
-
const getServerSideProps = pageModule.getServerSideProps;
|
|
270
|
-
if (!PageComponent) {
|
|
271
|
-
throw new Error(`No default export found in ${matchedPage.file}`);
|
|
272
|
-
}
|
|
273
|
-
// Si existe getServerSideProps, la ejecutamos para obtener props iniciales
|
|
274
|
-
let finalProps = { ...props };
|
|
275
|
-
if (getServerSideProps && typeof getServerSideProps === 'function') {
|
|
276
|
-
console.log(`Calling getServerSideProps for ${matchedPage.route}`);
|
|
277
|
-
const serverSideResult = await getServerSideProps();
|
|
278
|
-
if (serverSideResult && serverSideResult.props) {
|
|
279
|
-
finalProps = { ...finalProps, ...serverSideResult.props };
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Renderiza el componente con las props finales
|
|
283
|
-
const html = (0, server_1.renderToString)(react_1.default.createElement(PageComponent, finalProps));
|
|
284
|
-
return {
|
|
285
|
-
html,
|
|
286
|
-
props: finalProps,
|
|
287
|
-
route: matchedPage.route,
|
|
288
|
-
page: matchedPage.file
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
console.error(`Error rendering page ${matchedPage.route}:`, error);
|
|
293
|
-
throw error;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
package/dist/platform.d.ts
CHANGED
|
@@ -14,11 +14,3 @@ export interface PlatformInfo {
|
|
|
14
14
|
* Detecta plataforma usando headers custom + User-Agent fallback
|
|
15
15
|
*/
|
|
16
16
|
export declare function detectPlatform(userAgent: string, headers?: any): PlatformInfo;
|
|
17
|
-
/**
|
|
18
|
-
* Genera CSS class para HTML basado en platform info
|
|
19
|
-
*/
|
|
20
|
-
export declare function getPlatformCSSClass(platform: PlatformInfo): string;
|
|
21
|
-
/**
|
|
22
|
-
* Determina qué entry-client usar basado en platform
|
|
23
|
-
*/
|
|
24
|
-
export declare function resolveEntryClient(baseName: string, platform: PlatformInfo): string;
|
package/dist/platform.js
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.detectPlatform = detectPlatform;
|
|
8
|
-
exports.getPlatformCSSClass = getPlatformCSSClass;
|
|
9
|
-
exports.resolveEntryClient = resolveEntryClient;
|
|
10
8
|
/**
|
|
11
9
|
* Detecta plataforma usando headers custom + User-Agent fallback
|
|
12
10
|
*/
|
|
@@ -106,38 +104,3 @@ function detectFromUserAgent(userAgent) {
|
|
|
106
104
|
source: 'user-agent'
|
|
107
105
|
};
|
|
108
106
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Genera CSS class para HTML basado en platform info
|
|
111
|
-
*/
|
|
112
|
-
function getPlatformCSSClass(platform) {
|
|
113
|
-
const classes = [
|
|
114
|
-
platform.category, // 'web', 'webview', 'native'
|
|
115
|
-
platform.device, // 'phone', 'tablet', 'desktop'
|
|
116
|
-
platform.os // 'ios', 'android', etc.
|
|
117
|
-
];
|
|
118
|
-
return classes.join(' ');
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Determina qué entry-client usar basado en platform
|
|
122
|
-
*/
|
|
123
|
-
function resolveEntryClient(baseName, platform) {
|
|
124
|
-
const candidates = [
|
|
125
|
-
// Específico para mobile: device.os (phone.ios, tablet.android)
|
|
126
|
-
...(platform.device !== 'desktop'
|
|
127
|
-
? [`${baseName}__entry-client.${platform.device}.${platform.os}.tsx`]
|
|
128
|
-
: []),
|
|
129
|
-
// OS específico: ios, android
|
|
130
|
-
`${baseName}__entry-client.${platform.os}.tsx`,
|
|
131
|
-
// Device: mobile, desktop, tablet
|
|
132
|
-
`${baseName}__entry-client.${platform.device}.tsx`,
|
|
133
|
-
// Legacy compatibility
|
|
134
|
-
...(platform.device === 'phone' || platform.device === 'tablet'
|
|
135
|
-
? [`${baseName}__entry-client.mobile.tsx`]
|
|
136
|
-
: []),
|
|
137
|
-
// Fallback
|
|
138
|
-
`${baseName}__entry-client.tsx`
|
|
139
|
-
];
|
|
140
|
-
// En producción, deberías verificar que el archivo existe
|
|
141
|
-
// Por ahora retorna el primer candidato
|
|
142
|
-
return candidates[0];
|
|
143
|
-
}
|
package/dist/render.d.ts
CHANGED
package/dist/render.js
CHANGED
|
@@ -36,10 +36,25 @@ 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.createRenderMiddleware = createRenderMiddleware;
|
|
39
40
|
exports.createRenderServer = createRenderServer;
|
|
40
41
|
const react_1 = __importDefault(require("react"));
|
|
41
42
|
const server_1 = require("react-dom/server");
|
|
42
43
|
const pages_1 = require("./pages");
|
|
44
|
+
const request_handler_1 = require("./helpers/request-handler");
|
|
45
|
+
/**
|
|
46
|
+
* RENDER MIDDLEWARE PARA EL ROUTER
|
|
47
|
+
*/
|
|
48
|
+
function createRenderMiddleware(page, options) {
|
|
49
|
+
return async (req, res, next) => {
|
|
50
|
+
try {
|
|
51
|
+
await (0, request_handler_1.processRequest)({ req, res, page, next }, options);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
next(error);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
43
58
|
function createRenderServer(options = {}) {
|
|
44
59
|
const pages = (0, pages_1.getPages)();
|
|
45
60
|
const { vite } = options;
|
|
@@ -64,28 +79,34 @@ function createRenderServer(options = {}) {
|
|
|
64
79
|
}
|
|
65
80
|
else {
|
|
66
81
|
// En producción: usar import directo del archivo compilado
|
|
67
|
-
const moduleUrl =
|
|
68
|
-
|
|
82
|
+
const moduleUrl = (0, pages_1.getCompiledProdPage)(matchedPage.abs);
|
|
83
|
+
try {
|
|
84
|
+
pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
85
|
+
}
|
|
86
|
+
catch (importError) {
|
|
87
|
+
console.error('Error loading module:', importError);
|
|
88
|
+
throw importError;
|
|
89
|
+
}
|
|
69
90
|
}
|
|
70
91
|
const PageComponent = pageModule.default;
|
|
71
|
-
const getServerSideProps = pageModule.getServerSideProps;
|
|
72
92
|
if (!PageComponent) {
|
|
73
93
|
throw new Error(`No default export found in ${matchedPage.file}`);
|
|
74
94
|
}
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
}
|
|
95
|
+
// Los middlewares ya se ejecutaron antes de llegar aquí.
|
|
96
|
+
// `props` contiene todo lo que los middlewares pusieron en `res.locals`.
|
|
97
|
+
// Combinamos todo para SSR y luego filtramos lo sensible para el cliente.
|
|
98
|
+
const finalProps = {
|
|
99
|
+
...props,
|
|
100
|
+
...(props.serverSideProps || {})
|
|
101
|
+
};
|
|
84
102
|
// Renderiza el componente con las props finales
|
|
85
103
|
const html = (0, server_1.renderToString)(react_1.default.createElement(PageComponent, finalProps));
|
|
104
|
+
// Evitar mandar htmlConfig al cliente
|
|
105
|
+
const { htmlConfig, serverSideProps, ...rest } = finalProps;
|
|
106
|
+
const clientProps = serverSideProps || rest;
|
|
86
107
|
return {
|
|
87
108
|
html,
|
|
88
|
-
props:
|
|
109
|
+
props: clientProps,
|
|
89
110
|
route: matchedPage.route,
|
|
90
111
|
page: matchedPage.file
|
|
91
112
|
};
|
package/dist/router.js
CHANGED
|
@@ -11,7 +11,8 @@ const express_1 = require("express");
|
|
|
11
11
|
const pages_1 = require("./pages");
|
|
12
12
|
const express_2 = __importDefault(require("express"));
|
|
13
13
|
const vite_config_1 = require("./helpers/vite-config");
|
|
14
|
-
const
|
|
14
|
+
const middlewares_1 = require("./helpers/middlewares");
|
|
15
|
+
const render_1 = require("./render");
|
|
15
16
|
async function createAutoViteServer(options = {}) {
|
|
16
17
|
const { port = 5173, base = '/', enablePlatformDetection = true } = options;
|
|
17
18
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
@@ -36,12 +37,14 @@ async function createAutoViteServer(options = {}) {
|
|
|
36
37
|
app.use(express_2.default.static('public'));
|
|
37
38
|
const start = () => {
|
|
38
39
|
return app.listen(port, () => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
41
|
+
const pages = (0, pages_1.getPages)();
|
|
42
|
+
console.log(`🚀 Auto Vite Server started at http://localhost:${port}`);
|
|
43
|
+
console.log(`📄 Auto-detected routes:`);
|
|
44
|
+
pages.forEach(page => {
|
|
45
|
+
console.log(` ${page.route} -> ${page.file}`);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
45
48
|
});
|
|
46
49
|
};
|
|
47
50
|
return { app, vite: viteConfig.vite, start };
|
|
@@ -54,10 +57,9 @@ function createViteRouter(options = {}) {
|
|
|
54
57
|
return createManualRouter(options);
|
|
55
58
|
}
|
|
56
59
|
function createAutoRouter(options) {
|
|
57
|
-
const { enablePlatformDetection = true
|
|
60
|
+
const { renderFunction, enablePlatformDetection = true } = options;
|
|
58
61
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
59
62
|
let viteConfig = null;
|
|
60
|
-
let renderServer = null;
|
|
61
63
|
let initialized = false;
|
|
62
64
|
const mainRouter = (0, express_1.Router)();
|
|
63
65
|
const initializeIfNeeded = async () => {
|
|
@@ -77,38 +79,79 @@ function createAutoRouter(options) {
|
|
|
77
79
|
return viteConfig;
|
|
78
80
|
};
|
|
79
81
|
const pages = (0, pages_1.getPages)();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
// TU PSEUDOCÓDIGO IMPLEMENTADO CORRECTAMENTE:
|
|
83
|
+
// Initialize Vite ONCE before setting up any routes
|
|
84
|
+
const setupRoutes = async () => {
|
|
85
|
+
if (!viteConfig)
|
|
82
86
|
await initializeIfNeeded();
|
|
83
|
-
|
|
87
|
+
// Process pages sequentially to avoid race conditions
|
|
88
|
+
for (const page of pages) {
|
|
89
|
+
// Dynamic middleware loading for HMR support
|
|
90
|
+
const HMRMiddleware = await (0, middlewares_1.createDynamicPageMiddlewares)(page.route, page.file, viteConfig?.vite);
|
|
91
|
+
// Productive middlewares
|
|
92
|
+
const prevMiddlewares = await (0, middlewares_1.loadPageMiddlewares)(page.route, viteConfig?.vite);
|
|
93
|
+
const serverSidePropsMiddleware = await (0, middlewares_1.getServerSidePropsAsMiddleware)(page.file, viteConfig?.vite);
|
|
94
|
+
const renderMiddleware = (0, render_1.createRenderMiddleware)(page, {
|
|
84
95
|
enablePlatformDetection,
|
|
85
96
|
renderFunction,
|
|
86
|
-
viteConfig
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
viteConfig: () => viteConfig // pasar por función para evitar capturar null
|
|
98
|
+
});
|
|
99
|
+
const pageMiddlewares = isProduction ? [
|
|
100
|
+
...prevMiddlewares,
|
|
101
|
+
serverSidePropsMiddleware,
|
|
102
|
+
renderMiddleware
|
|
103
|
+
] : [
|
|
104
|
+
HMRMiddleware,
|
|
105
|
+
renderMiddleware
|
|
106
|
+
];
|
|
107
|
+
mainRouter.get(page.route, ...pageMiddlewares);
|
|
108
|
+
// Error middleware separado
|
|
109
|
+
mainRouter.use(page.route, (0, middlewares_1.createPageErrorHandler)(page.file, viteConfig?.vite));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// Setup routes asynchronously but don't block router creation
|
|
113
|
+
setupRoutes().catch(console.error);
|
|
90
114
|
return mainRouter;
|
|
91
115
|
}
|
|
92
116
|
function createManualRouter(options) {
|
|
93
|
-
const {
|
|
117
|
+
const { vite, renderFunction, enablePlatformDetection = true } = options;
|
|
94
118
|
const router = (0, express_1.Router)();
|
|
95
119
|
const pages = (0, pages_1.getPages)();
|
|
96
|
-
|
|
120
|
+
// Crear viteConfig para processRequest
|
|
97
121
|
const viteConfig = {
|
|
98
122
|
vite,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
123
|
+
templateHtml: options.templateHtml || '',
|
|
124
|
+
isProduction: options.isProduction || false,
|
|
125
|
+
manifest: null
|
|
102
126
|
};
|
|
103
|
-
pages
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
// Process pages sequentially to avoid race conditions
|
|
128
|
+
const setupRoutes = async () => {
|
|
129
|
+
for (const page of pages) {
|
|
130
|
+
// Dynamic middleware loading for HMR support(only in dev)
|
|
131
|
+
const HMRMiddleware = await (0, middlewares_1.createDynamicPageMiddlewares)(page.route, page.file, vite);
|
|
132
|
+
// Productive middlewares
|
|
133
|
+
const prevMiddlewares = await (0, middlewares_1.loadPageMiddlewares)(page.route, vite);
|
|
134
|
+
const serverSidePropsMiddleware = await (0, middlewares_1.getServerSidePropsAsMiddleware)(page.file, vite);
|
|
135
|
+
const renderMiddleware = (0, render_1.createRenderMiddleware)(page, {
|
|
106
136
|
enablePlatformDetection,
|
|
107
137
|
renderFunction,
|
|
108
138
|
viteConfig
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
});
|
|
140
|
+
const pageMiddlewares = options.isProduction ? [
|
|
141
|
+
...prevMiddlewares,
|
|
142
|
+
serverSidePropsMiddleware,
|
|
143
|
+
renderMiddleware
|
|
144
|
+
] : [
|
|
145
|
+
HMRMiddleware,
|
|
146
|
+
renderMiddleware
|
|
147
|
+
];
|
|
148
|
+
router.get(page.route, ...pageMiddlewares);
|
|
149
|
+
// Error middleware separado
|
|
150
|
+
router.use(page.route, (0, middlewares_1.createPageErrorHandler)(page.file, vite));
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// Setup routes asynchronously but don't block router creation
|
|
154
|
+
setupRoutes().catch(console.error);
|
|
112
155
|
return router;
|
|
113
156
|
}
|
|
114
157
|
function listDetectedPages() {
|