@ressjs/vite-router 0.5.2 → 0.6.0-rc.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/LICENSE +69 -0
- package/README.md +9 -515
- package/dist/client/entry.d.mts +2 -0
- package/dist/client/entry.mjs +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +8 -0
- package/dist/config/base-path.d.ts +44 -0
- package/dist/config/base-path.js +100 -0
- package/dist/config/codegen.d.ts +29 -0
- package/dist/config/codegen.js +131 -0
- package/dist/config/define.d.ts +8 -0
- package/dist/config/define.js +12 -0
- package/dist/config/env.d.ts +19 -0
- package/dist/config/env.js +40 -0
- package/dist/config/index.d.ts +18 -0
- package/dist/config/index.js +46 -0
- package/dist/config/load.d.ts +28 -0
- package/dist/config/load.js +82 -0
- package/dist/config/middleware.d.ts +18 -0
- package/dist/config/middleware.js +79 -0
- package/dist/config/rules.d.ts +59 -0
- package/dist/config/rules.js +162 -0
- package/dist/config/types.d.ts +189 -0
- package/dist/config/types.js +2 -0
- package/dist/config/validate.d.ts +19 -0
- package/dist/config/validate.js +143 -0
- package/dist/config/watch.d.ts +53 -0
- package/dist/config/watch.js +163 -0
- package/dist/fs/module-extensions.d.ts +105 -0
- package/dist/fs/module-extensions.js +213 -0
- package/dist/head/resolve.d.ts +5 -0
- package/dist/head/resolve.js +166 -0
- package/dist/head/types.d.ts +44 -0
- package/dist/head/types.js +2 -0
- package/dist/helpers/html-generator.d.ts +26 -6
- package/dist/helpers/html-generator.js +96 -138
- package/dist/helpers/middlewares.d.ts +32 -21
- package/dist/helpers/middlewares.js +181 -159
- package/dist/helpers/page-config-merge.d.ts +11 -0
- package/dist/helpers/page-config-merge.js +84 -0
- package/dist/helpers/request-handler.d.ts +15 -2
- package/dist/helpers/request-handler.js +164 -27
- package/dist/index.d.ts +35 -3
- package/dist/index.js +77 -8
- package/dist/isr/capture.d.ts +79 -0
- package/dist/isr/capture.js +222 -0
- package/dist/isr/handler.d.ts +49 -0
- package/dist/isr/handler.js +207 -0
- package/dist/isr/key.d.ts +26 -0
- package/dist/isr/key.js +59 -0
- package/dist/isr/page-config.d.ts +17 -0
- package/dist/isr/page-config.js +71 -0
- package/dist/isr/preview.d.ts +5 -0
- package/dist/isr/preview.js +38 -0
- package/dist/isr/public-request.d.ts +50 -0
- package/dist/isr/public-request.js +108 -0
- package/dist/isr/response.d.ts +41 -0
- package/dist/isr/response.js +128 -0
- package/dist/isr/store.d.ts +42 -0
- package/dist/isr/store.js +108 -0
- package/dist/isr/types.d.ts +62 -0
- package/dist/isr/types.js +2 -0
- package/dist/pages.d.ts +10 -9
- package/dist/pages.js +42 -261
- package/dist/platform.d.ts +11 -14
- package/dist/platform.js +18 -102
- package/dist/plugin/environments.d.ts +68 -0
- package/dist/plugin/environments.js +74 -0
- package/dist/plugin/index.d.ts +59 -0
- package/dist/plugin/index.js +195 -0
- package/dist/plugin/route-manifest.d.ts +19 -0
- package/dist/plugin/route-manifest.js +55 -0
- package/dist/plugin/virtual-entries.d.ts +26 -0
- package/dist/plugin/virtual-entries.js +80 -0
- package/dist/render.d.ts +39 -2
- package/dist/render.js +75 -77
- package/dist/router.d.ts +32 -13
- package/dist/router.js +180 -146
- package/dist/routes/dispatcher.d.ts +58 -0
- package/dist/routes/dispatcher.js +70 -0
- package/dist/routes/manifest.d.ts +17 -0
- package/dist/routes/manifest.js +62 -0
- package/dist/routes/match.d.ts +20 -0
- package/dist/routes/match.js +75 -0
- package/dist/routes/module.d.ts +14 -0
- package/dist/routes/module.js +30 -0
- package/dist/routes/parse.d.ts +31 -0
- package/dist/routes/parse.js +114 -0
- package/dist/routes/rank.d.ts +12 -0
- package/dist/routes/rank.js +49 -0
- package/dist/routes/router.d.ts +54 -0
- package/dist/routes/router.js +153 -0
- package/dist/routes/scan.d.ts +26 -0
- package/dist/routes/scan.js +98 -0
- package/dist/routes/types.d.ts +114 -0
- package/dist/routes/types.js +17 -0
- package/dist/runtime/dev-server.d.ts +64 -0
- package/dist/runtime/dev-server.js +94 -0
- package/dist/runtime/dev-styles.d.ts +13 -0
- package/dist/runtime/dev-styles.js +50 -0
- package/dist/runtime/module-loader.d.ts +55 -0
- package/dist/runtime/module-loader.js +122 -0
- package/dist/runtime/prod-server.d.ts +55 -0
- package/dist/runtime/prod-server.js +188 -0
- package/dist/runtime/template.d.ts +10 -0
- package/dist/runtime/template.js +33 -0
- package/dist/security/client-props.d.ts +25 -0
- package/dist/security/client-props.js +51 -0
- package/dist/security/config.d.ts +121 -0
- package/dist/security/config.js +77 -0
- package/dist/security/csp.d.ts +53 -0
- package/dist/security/csp.js +118 -0
- package/dist/security/dev-hardening.d.ts +46 -0
- package/dist/security/dev-hardening.js +65 -0
- package/dist/security/errors.d.ts +37 -0
- package/dist/security/errors.js +84 -0
- package/dist/security/escape.d.ts +40 -0
- package/dist/security/escape.js +90 -0
- package/dist/security/head-tags.d.ts +32 -0
- package/dist/security/head-tags.js +156 -0
- package/dist/security/headers.d.ts +76 -0
- package/dist/security/headers.js +278 -0
- package/dist/security/index.d.ts +24 -0
- package/dist/security/index.js +49 -0
- package/dist/security/serialize.d.ts +48 -0
- package/dist/security/serialize.js +146 -0
- package/dist/variants/assets.d.ts +33 -0
- package/dist/variants/assets.js +144 -0
- package/dist/variants/catalog.d.ts +33 -0
- package/dist/variants/catalog.js +79 -0
- package/dist/variants/platform-tokens.d.ts +9 -0
- package/dist/variants/platform-tokens.js +15 -0
- package/dist/variants/resolve.d.ts +40 -0
- package/dist/variants/resolve.js +90 -0
- package/dist/variants/suffix.d.ts +8 -0
- package/dist/variants/suffix.js +12 -0
- package/dist/variants/types.d.ts +72 -0
- package/dist/variants/types.js +2 -0
- package/package.json +23 -11
- package/dist/helpers/vite-config.d.ts +0 -10
- package/dist/helpers/vite-config.js +0 -90
package/dist/render.js
CHANGED
|
@@ -1,37 +1,4 @@
|
|
|
1
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
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
@@ -40,15 +7,36 @@ exports.createRenderMiddleware = createRenderMiddleware;
|
|
|
40
7
|
exports.createRenderServer = createRenderServer;
|
|
41
8
|
const react_1 = __importDefault(require("react"));
|
|
42
9
|
const server_1 = require("react-dom/server");
|
|
43
|
-
const
|
|
10
|
+
const node_url_1 = require("node:url");
|
|
44
11
|
const request_handler_1 = require("./helpers/request-handler");
|
|
12
|
+
const prod_server_1 = require("./runtime/prod-server");
|
|
13
|
+
const security_1 = require("./security");
|
|
14
|
+
const assets_1 = require("@ressjs/assets");
|
|
45
15
|
/**
|
|
46
16
|
* RENDER MIDDLEWARE PARA EL ROUTER
|
|
17
|
+
*
|
|
18
|
+
* El render server se arma una sola vez por ruta, en la primera petición, y
|
|
19
|
+
* se reusa en las siguientes. Antes se creaba en cada `processRequest` sin
|
|
20
|
+
* `renderServer`, y `createRenderServer` barría `app/pages` con `fast-glob`
|
|
21
|
+
* en cada request: cien peticiones, cien globs síncronos contra el
|
|
22
|
+
* filesystem en producción.
|
|
23
|
+
*
|
|
24
|
+
* La primera petición espera a que `viteConfig` (que puede llegar como thunk
|
|
25
|
+
* en modo automático) esté resuelto, así que armar el render server recién
|
|
26
|
+
* ahí, y no al declarar el middleware, es lo que garantiza que ya tenga el
|
|
27
|
+
* `moduleLoader` correcto.
|
|
47
28
|
*/
|
|
48
29
|
function createRenderMiddleware(page, options) {
|
|
30
|
+
let renderServer;
|
|
49
31
|
return async (req, res, next) => {
|
|
50
32
|
try {
|
|
51
|
-
|
|
33
|
+
// Con `renderFunction` el pipeline de render propio no corre: armar el
|
|
34
|
+
// render server igual sería trabajo tirado (y, en tests, un moduleLoader
|
|
35
|
+
// que no llegó a resolverse).
|
|
36
|
+
if (!renderServer && !options.renderFunction) {
|
|
37
|
+
renderServer = (0, request_handler_1.buildRenderServer)(options);
|
|
38
|
+
}
|
|
39
|
+
await (0, request_handler_1.processRequest)({ req, res, page, next }, options, renderServer);
|
|
52
40
|
}
|
|
53
41
|
catch (error) {
|
|
54
42
|
next(error);
|
|
@@ -56,49 +44,29 @@ function createRenderMiddleware(page, options) {
|
|
|
56
44
|
};
|
|
57
45
|
}
|
|
58
46
|
function createRenderServer(options = {}) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
47
|
+
const { moduleLoader } = options;
|
|
48
|
+
const policy = options.clientProps ?? (0, security_1.resolveSecurityOptions)().clientProps;
|
|
49
|
+
const isProduction = options.isProduction ?? process.env.NODE_ENV === 'production';
|
|
61
50
|
return async (renderOptions) => {
|
|
62
|
-
const { url, props = {} } = renderOptions;
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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));
|
|
81
|
-
if (!matchedPage) {
|
|
82
|
-
throw new Error(`Page not found for URL: ${url}`);
|
|
51
|
+
const { url, props = {}, locals = {}, page, hydrate = true } = renderOptions;
|
|
52
|
+
// La página la resuelve el despachador antes de llegar acá (route groups,
|
|
53
|
+
// catch-all, etc.); volver a buscarla por URL con un criterio propio sería
|
|
54
|
+
// una segunda resolución que no conoce esos casos y puede discrepar con
|
|
55
|
+
// la primera.
|
|
56
|
+
if (!page) {
|
|
57
|
+
throw new Error(`createRenderServer: falta \`page\` para la URL "${url}". El despachador tiene ` +
|
|
58
|
+
'que resolverla antes de invocar el render; no se busca más por glob.');
|
|
83
59
|
}
|
|
60
|
+
const matchedPage = page;
|
|
84
61
|
try {
|
|
85
|
-
// Import dinámico de la página usando vite.ssrLoadModule si está disponible
|
|
86
62
|
let pageModule;
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
const modulePath = `/${matchedPage.file}`;
|
|
90
|
-
pageModule = await vite.ssrLoadModule(modulePath);
|
|
63
|
+
if (moduleLoader) {
|
|
64
|
+
pageModule = await moduleLoader.load(`/${matchedPage.file}`);
|
|
91
65
|
}
|
|
92
66
|
else {
|
|
93
|
-
//
|
|
94
|
-
const moduleUrl = (0,
|
|
95
|
-
|
|
96
|
-
pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
|
|
97
|
-
}
|
|
98
|
-
catch (importError) {
|
|
99
|
-
console.error('Error loading module:', importError);
|
|
100
|
-
throw importError;
|
|
101
|
-
}
|
|
67
|
+
// Ruta absoluta: en Windows sólo se puede importar como URL `file://`.
|
|
68
|
+
const moduleUrl = (0, prod_server_1.resolveServerModulePath)(matchedPage.abs ?? matchedPage.file);
|
|
69
|
+
pageModule = await import(/* @vite-ignore */ (0, node_url_1.pathToFileURL)(moduleUrl).href);
|
|
102
70
|
}
|
|
103
71
|
const PageComponent = pageModule.default;
|
|
104
72
|
if (!PageComponent) {
|
|
@@ -111,14 +79,26 @@ function createRenderServer(options = {}) {
|
|
|
111
79
|
...props,
|
|
112
80
|
...(props.serverSideProps || {})
|
|
113
81
|
};
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
const
|
|
82
|
+
// El collector vive en este árbol, no en un singleton. Dos requests que
|
|
83
|
+
// renderizan a la vez nunca pueden mezclar sus títulos o metadatos.
|
|
84
|
+
const head = [];
|
|
85
|
+
const tree = react_1.default.createElement(assets_1.HeadCollectorProvider, { collector: head }, react_1.default.createElement(PageComponent, finalProps));
|
|
86
|
+
const html = (0, server_1.renderToString)(tree);
|
|
87
|
+
// El componente ya recibió todo arriba. Lo que viaja al cliente es sólo
|
|
88
|
+
// lo que la política declara: enumerar lo que se excluye deja expuesta
|
|
89
|
+
// cada clave que un middleware agregue después.
|
|
90
|
+
//
|
|
91
|
+
// `serverSideProps` es lo que la página pidió explícitamente para el
|
|
92
|
+
// cliente; el resto de `res.locals` es estado del servidor y no viaja
|
|
93
|
+
// salvo que se declare.
|
|
94
|
+
const exposable = { ...locals, serverSideProps: props };
|
|
95
|
+
const clientProps = hydrate ? (0, security_1.pickClientProps)(exposable, policy) : {};
|
|
96
|
+
if (hydrate && !isProduction)
|
|
97
|
+
warnWithheld(matchedPage.route, exposable, clientProps);
|
|
119
98
|
return {
|
|
120
99
|
html,
|
|
121
100
|
props: clientProps,
|
|
101
|
+
head,
|
|
122
102
|
route: matchedPage.route,
|
|
123
103
|
page: matchedPage.file
|
|
124
104
|
};
|
|
@@ -129,3 +109,21 @@ function createRenderServer(options = {}) {
|
|
|
129
109
|
}
|
|
130
110
|
};
|
|
131
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Avisa, una vez por ruta, qué quedó fuera del cliente.
|
|
114
|
+
*
|
|
115
|
+
* Una página que renderiza en el servidor con datos que el cliente no tiene
|
|
116
|
+
* hidrata distinto, y ese desajuste no dice de dónde viene. El aviso lo nombra.
|
|
117
|
+
*/
|
|
118
|
+
const warnedRoutes = new Set();
|
|
119
|
+
function warnWithheld(route, serverProps, clientProps) {
|
|
120
|
+
if (warnedRoutes.has(route))
|
|
121
|
+
return;
|
|
122
|
+
const withheld = (0, security_1.withheldKeys)(serverProps, clientProps);
|
|
123
|
+
if (withheld.length === 0)
|
|
124
|
+
return;
|
|
125
|
+
warnedRoutes.add(route);
|
|
126
|
+
console.warn(`[ress] ${route}: estas claves existen en el servidor y no viajan al cliente: ` +
|
|
127
|
+
`${withheld.join(', ')}.\n` +
|
|
128
|
+
'Si la página las usa al hidratar, declaralas en `security.clientProps.expose`.');
|
|
129
|
+
}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,27 +1,46 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
+
import type { RessRuntime } from './runtime/dev-server';
|
|
3
|
+
import type { ModuleLoader } from './runtime/module-loader';
|
|
4
|
+
import { type ResolvedSecurityOptions, type SecurityOptions } from './security';
|
|
5
|
+
import { type ResolvedConfig } from './config';
|
|
6
|
+
/** Espera a que el runtime, el manifest y las cadenas de middleware estén listos. */
|
|
7
|
+
export declare function waitForViteRouter(router: Router): Promise<void>;
|
|
2
8
|
export interface ViteRouterOptions {
|
|
3
9
|
enablePlatformDetection?: boolean;
|
|
4
10
|
isProduction?: boolean;
|
|
5
|
-
pagesDirectory?: string;
|
|
6
11
|
basePath?: string;
|
|
7
|
-
|
|
12
|
+
/** Runtime ya construido, para quien lo arma por su cuenta. */
|
|
13
|
+
runtime?: RessRuntime;
|
|
14
|
+
/** Cargador de módulos, para quien arma su propio servidor. */
|
|
15
|
+
moduleLoader?: ModuleLoader;
|
|
8
16
|
templateHtml?: string;
|
|
9
17
|
renderFunction?: (options: {
|
|
10
18
|
url: string;
|
|
11
19
|
platform: string;
|
|
12
20
|
req: any;
|
|
13
21
|
}) => Promise<string>;
|
|
22
|
+
/** Opciones de seguridad ya resueltas. F-008 las tomará de `ress.config.ts`. */
|
|
23
|
+
security?: ResolvedSecurityOptions;
|
|
24
|
+
/** Opciones de seguridad sin resolver; se completan con los defaults. */
|
|
25
|
+
securityOptions?: SecurityOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Configuración del proyecto ya resuelta. Si no se aporta, se carga de
|
|
28
|
+
* `ress.config` al arrancar: es el archivo que el proyecto ya declara, y
|
|
29
|
+
* exigir pasarla a mano deja al servidor y al build leyendo cosas distintas.
|
|
30
|
+
*/
|
|
31
|
+
config?: ResolvedConfig;
|
|
32
|
+
/** Raíz del proyecto, para leer la configuración. */
|
|
33
|
+
root?: string;
|
|
34
|
+
/** Modo con el que se resuelven las capas de configuración. */
|
|
35
|
+
mode?: string;
|
|
14
36
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
start: () => import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
|
|
24
|
-
}>;
|
|
37
|
+
/**
|
|
38
|
+
* El router de ress.js.
|
|
39
|
+
*
|
|
40
|
+
* Antes había dos constructores con el 90 % del cuerpo repetido, uno para quien
|
|
41
|
+
* deja que el framework arme el runtime y otro para quien lo aporta. La
|
|
42
|
+
* diferencia real entre los dos es de dónde sale el runtime, así que eso es lo
|
|
43
|
+
* único que se decide acá; el resto del camino es uno solo.
|
|
44
|
+
*/
|
|
25
45
|
export declare function createViteRouter(options?: ViteRouterOptions): Router;
|
|
26
|
-
export declare function listDetectedPages(): import("./pages").PageInfo[];
|
|
27
46
|
export { getPages } from './pages';
|
package/dist/router.js
CHANGED
|
@@ -1,161 +1,195 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.getPages = void 0;
|
|
7
|
-
exports.
|
|
4
|
+
exports.waitForViteRouter = waitForViteRouter;
|
|
8
5
|
exports.createViteRouter = createViteRouter;
|
|
9
|
-
exports.listDetectedPages = listDetectedPages;
|
|
10
6
|
const express_1 = require("express");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
app.use('/dist/.entries', express_2.default.static('dist/.entries'));
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
const prodFactory = (0, vite_config_1.createProdMiddleware)();
|
|
27
|
-
const prodMiddleware = await prodFactory();
|
|
28
|
-
app.use(prodMiddleware);
|
|
29
|
-
}
|
|
30
|
-
const viteRouter = createViteRouter({
|
|
31
|
-
enablePlatformDetection,
|
|
32
|
-
vite: viteConfig.vite,
|
|
33
|
-
templateHtml: viteConfig.templateHtml,
|
|
34
|
-
isProduction
|
|
35
|
-
});
|
|
36
|
-
app.use('/', viteRouter);
|
|
37
|
-
app.use(express_2.default.static('public'));
|
|
38
|
-
const start = () => {
|
|
39
|
-
return app.listen(port, () => {
|
|
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
|
-
const expressDisplay = page.route !== page.expressRoute ? ` (Express: ${page.expressRoute})` : '';
|
|
46
|
-
console.log(` ${page.route}${expressDisplay} -> ${page.file}`);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
return { app, vite: viteConfig.vite, start };
|
|
7
|
+
const prod_server_1 = require("./runtime/prod-server");
|
|
8
|
+
const security_1 = require("./security");
|
|
9
|
+
const router_1 = require("./routes/router");
|
|
10
|
+
const manifest_1 = require("./routes/manifest");
|
|
11
|
+
const scan_1 = require("./routes/scan");
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
const store_1 = require("./isr/store");
|
|
14
|
+
const request_handler_1 = require("./helpers/request-handler");
|
|
15
|
+
const routerReadiness = new WeakMap();
|
|
16
|
+
/** Espera a que el runtime, el manifest y las cadenas de middleware estén listos. */
|
|
17
|
+
function waitForViteRouter(router) {
|
|
18
|
+
return routerReadiness.get(router) ?? Promise.resolve();
|
|
52
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* El router de ress.js.
|
|
22
|
+
*
|
|
23
|
+
* Antes había dos constructores con el 90 % del cuerpo repetido, uno para quien
|
|
24
|
+
* deja que el framework arme el runtime y otro para quien lo aporta. La
|
|
25
|
+
* diferencia real entre los dos es de dónde sale el runtime, así que eso es lo
|
|
26
|
+
* único que se decide acá; el resto del camino es uno solo.
|
|
27
|
+
*/
|
|
53
28
|
function createViteRouter(options = {}) {
|
|
54
|
-
const needsAutoConfig = !options.vite && !options.templateHtml && options.isProduction !== true;
|
|
55
|
-
if (needsAutoConfig) {
|
|
56
|
-
return createAutoRouter(options);
|
|
57
|
-
}
|
|
58
|
-
return createManualRouter(options);
|
|
59
|
-
}
|
|
60
|
-
function createAutoRouter(options) {
|
|
61
29
|
const { renderFunction, enablePlatformDetection = true } = options;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
30
|
+
// Se resuelve recién dentro de `setup()`, después de cargar la
|
|
31
|
+
// configuración: `config.security` tiene que poder completar los defaults,
|
|
32
|
+
// y acá todavía no se leyó `ress.config`.
|
|
33
|
+
let security = options.security ?? (0, security_1.resolveSecurityOptions)(options.securityOptions);
|
|
34
|
+
const providesOwnRuntime = Boolean(options.moduleLoader || options.templateHtml);
|
|
35
|
+
const isProduction = options.isProduction ?? process.env.NODE_ENV === 'production';
|
|
65
36
|
const mainRouter = (0, express_1.Router)();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
37
|
+
let runtime = null;
|
|
38
|
+
/**
|
|
39
|
+
* El manifest vigente.
|
|
40
|
+
*
|
|
41
|
+
* Se guarda acá y no se lee del runtime en cada rearmado: en desarrollo hay
|
|
42
|
+
* que volver a escanear cuando aparece o desaparece un archivo, y el runtime
|
|
43
|
+
* lo construyó una sola vez al arrancar.
|
|
44
|
+
*/
|
|
45
|
+
let routes = null;
|
|
46
|
+
let state = null;
|
|
47
|
+
let pageRouter = null;
|
|
48
|
+
let assetMiddleware = null;
|
|
49
|
+
let configMiddleware = null;
|
|
50
|
+
let stale = false;
|
|
51
|
+
let rescan = false;
|
|
52
|
+
/** La caché incremental: se crea con la configuración, una por router. */
|
|
53
|
+
let isr;
|
|
54
|
+
const markRescan = () => {
|
|
55
|
+
stale = true;
|
|
56
|
+
rescan = true;
|
|
57
|
+
isr?.store.clear();
|
|
58
|
+
};
|
|
59
|
+
const chainContext = () => ({
|
|
60
|
+
moduleLoader: runtime?.moduleLoader,
|
|
61
|
+
renderOptions: {
|
|
62
|
+
enablePlatformDetection,
|
|
63
|
+
renderFunction,
|
|
64
|
+
security,
|
|
65
|
+
viteConfig: () => runtime,
|
|
66
|
+
},
|
|
67
|
+
isr,
|
|
68
|
+
});
|
|
69
|
+
const rebuild = async (rescan = false) => {
|
|
70
|
+
stale = false;
|
|
71
|
+
if (rescan || !routes)
|
|
72
|
+
routes = (0, manifest_1.createRouteManifest)(await (0, scan_1.scanRoutes)());
|
|
73
|
+
state = await (0, router_1.buildRouterState)(routes, chainContext());
|
|
74
|
+
};
|
|
75
|
+
const setup = async () => {
|
|
76
|
+
// La configuración primero: de ella salen el prefijo de ruta, el prefijo de
|
|
77
|
+
// artefactos y las reglas del pipeline, y las tres se necesitan antes de
|
|
78
|
+
// armar el runtime.
|
|
79
|
+
const config = options.config ??
|
|
80
|
+
(await (0, config_1.loadConfig)(options.root ?? process.cwd(), options.mode ?? (isProduction ? 'production' : 'development')));
|
|
81
|
+
// `config.security` es el que declara el proyecto en `ress.config`; sólo
|
|
82
|
+
// se pisa si quien arma el router aportó algo explícito.
|
|
83
|
+
security = options.security ?? (0, security_1.resolveSecurityOptions)(options.securityOptions ?? config.security);
|
|
84
|
+
configMiddleware = (0, config_1.createConfigMiddleware)(config);
|
|
85
|
+
const assetPrefix = (0, config_1.assetPrefixFor)({
|
|
86
|
+
basePath: config.basePath,
|
|
87
|
+
cdnPrefix: config.assets?.cdnPrefix,
|
|
88
|
+
});
|
|
89
|
+
if (options.runtime) {
|
|
90
|
+
runtime = options.runtime;
|
|
91
|
+
routes = runtime.routes;
|
|
92
|
+
assetMiddleware = isProduction
|
|
93
|
+
? await (0, prod_server_1.createProdAssetMiddleware)(undefined, { security, buildOutputs: runtime.buildOutputs })
|
|
94
|
+
: runtime.vite.middlewares;
|
|
73
95
|
}
|
|
74
|
-
else {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
else if (providesOwnRuntime) {
|
|
97
|
+
// Quien aporta su propio cargador de módulos no tiene runtime del
|
|
98
|
+
// framework, así que se arma uno mínimo con lo que declaró.
|
|
99
|
+
runtime = {
|
|
100
|
+
vite: null,
|
|
101
|
+
moduleLoader: options.moduleLoader,
|
|
102
|
+
templateHtml: options.templateHtml ?? '',
|
|
103
|
+
isProduction,
|
|
104
|
+
buildOutputs: new Set(),
|
|
105
|
+
close: async () => { },
|
|
106
|
+
};
|
|
78
107
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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, {
|
|
95
|
-
enablePlatformDetection,
|
|
96
|
-
renderFunction,
|
|
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.expressRoute, ...pageMiddlewares);
|
|
108
|
-
// Error middleware separado
|
|
109
|
-
mainRouter.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, viteConfig?.vite));
|
|
108
|
+
else {
|
|
109
|
+
// El `base` de Vite se queda en `/`: el prefijo de ruta lo quita el
|
|
110
|
+
// pipeline de petición, y lo que se escribe en el HTML lo lleva por el
|
|
111
|
+
// prefijo de artefactos.
|
|
112
|
+
runtime = await (0, prod_server_1.createRuntime)(isProduction, options.basePath ?? process.env.BASE ?? '/', assetPrefix);
|
|
113
|
+
routes = runtime.routes;
|
|
114
|
+
// Los estáticos se atienden antes que las páginas: un pedido de artefacto
|
|
115
|
+
// no tiene que recorrer el manifest de rutas, y una ruta que capture todo
|
|
116
|
+
// no debe interceptarlos.
|
|
117
|
+
assetMiddleware = isProduction
|
|
118
|
+
? await (0, prod_server_1.createProdAssetMiddleware)(undefined, { security, buildOutputs: runtime.buildOutputs })
|
|
119
|
+
: runtime.vite.middlewares;
|
|
110
120
|
}
|
|
121
|
+
const isrOptions = config.isr ?? (0, config_1.resolveIsrConfig)();
|
|
122
|
+
isr = {
|
|
123
|
+
store: new store_1.MemoryIsrStore({
|
|
124
|
+
maxEntries: isrOptions.maxEntries,
|
|
125
|
+
maxBytes: isrOptions.maxBytes,
|
|
126
|
+
timeoutMs: isrOptions.generationTimeoutMs,
|
|
127
|
+
}),
|
|
128
|
+
options: isrOptions,
|
|
129
|
+
// En desarrollo la página se genera en cada petición, bajo el mismo
|
|
130
|
+
// contrato público: los datos se ven frescos y lo que funciona acá
|
|
131
|
+
// funciona igual en producción.
|
|
132
|
+
cacheEnabled: isProduction || isrOptions.dev,
|
|
133
|
+
buildId: runtime.buildId ?? 'dev',
|
|
134
|
+
usesNonce: security.csp.enabled,
|
|
135
|
+
isProduction,
|
|
136
|
+
// Una entrada ISR que no puede compartirse vuelve a este valor: nunca
|
|
137
|
+
// puede ser público, aunque `security.cache.html` lo sea.
|
|
138
|
+
htmlCacheControl: (0, security_1.privateCacheControl)(security.cache.html),
|
|
139
|
+
resolvePlatform: (req) => (0, request_handler_1.resolveRequestPlatform)(req, enablePlatformDetection),
|
|
140
|
+
};
|
|
141
|
+
await rebuild();
|
|
142
|
+
pageRouter = (0, router_1.createPageRouter)(() => state, chainContext());
|
|
143
|
+
// Se marca y se rearma en la petición siguiente, no acá: el watcher avisa
|
|
144
|
+
// antes de que Vite termine de invalidar su grafo, y rearmar en ese instante
|
|
145
|
+
// volvería a leer la versión anterior del archivo.
|
|
146
|
+
runtime.onSourceChange?.(() => {
|
|
147
|
+
stale = true;
|
|
148
|
+
isr?.store.clear();
|
|
149
|
+
});
|
|
150
|
+
// Crear o borrar una página cambia el conjunto de rutas, no sólo el
|
|
151
|
+
// contenido de un módulo: hay que volver a escanear.
|
|
152
|
+
runtime.vite?.watcher?.on('add', markRescan);
|
|
153
|
+
runtime.vite?.watcher?.on('unlink', markRescan);
|
|
111
154
|
};
|
|
112
|
-
//
|
|
113
|
-
|
|
155
|
+
// El armado es asíncrono y una petición puede llegar antes de que termine. Se
|
|
156
|
+
// espera la promesa en vez de responder como si la ruta no existiera.
|
|
157
|
+
const ready = setup();
|
|
158
|
+
routerReadiness.set(mainRouter, ready);
|
|
159
|
+
mainRouter.use((req, res, next) => {
|
|
160
|
+
ready.then(() => {
|
|
161
|
+
const serve = () => {
|
|
162
|
+
// Se lee acá y no antes: la marca la pone el pipeline de configuración,
|
|
163
|
+
// que corre después de este callback.
|
|
164
|
+
const outside = req.ressOutsideBasePath === true;
|
|
165
|
+
// Después de la configuración, los artefactos: un pedido de artefacto no
|
|
166
|
+
// tiene que recorrer el manifest de rutas. Se registra acá y no con
|
|
167
|
+
// `use()` porque los dos middlewares se resuelven dentro del armado
|
|
168
|
+
// asíncrono, y registrarlos después dejaría al despachador por delante.
|
|
169
|
+
// Fuera del prefijo no hay páginas: declarar un prefijo y seguir
|
|
170
|
+
// sirviendo el sitio entero por la raíz es exactamente lo que un prefijo
|
|
171
|
+
// existe para evitar. Los artefactos sí se intentan igual, porque las
|
|
172
|
+
// rutas internas del servidor de desarrollo de Vite —`/@vite/client`,
|
|
173
|
+
// `/@fs/`— viven en la raíz y no bajo el prefijo de la aplicación.
|
|
174
|
+
const after = (err) => err ? next(err) : outside ? next() : dispatch(req, res, next);
|
|
175
|
+
if (!assetMiddleware)
|
|
176
|
+
return after();
|
|
177
|
+
assetMiddleware(req, res, after);
|
|
178
|
+
};
|
|
179
|
+
// Barra final, prefijo de ruta, redirecciones y reescrituras: todo lo que
|
|
180
|
+
// decide *qué* URL se está atendiendo va antes que quien la atiende.
|
|
181
|
+
if (!configMiddleware)
|
|
182
|
+
return serve();
|
|
183
|
+
configMiddleware(req, res, ((err) => (err ? next(err) : serve())));
|
|
184
|
+
}, next);
|
|
185
|
+
});
|
|
186
|
+
function dispatch(req, res, next) {
|
|
187
|
+
const run = () => (pageRouter ? pageRouter(req, res, next) : next());
|
|
188
|
+
if (!stale)
|
|
189
|
+
return run();
|
|
190
|
+
rebuild(rescan).then(() => { rescan = false; run(); }, next);
|
|
191
|
+
}
|
|
114
192
|
return mainRouter;
|
|
115
193
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const router = (0, express_1.Router)();
|
|
119
|
-
const pages = (0, pages_1.getPages)();
|
|
120
|
-
// Crear viteConfig para processRequest
|
|
121
|
-
const viteConfig = {
|
|
122
|
-
vite,
|
|
123
|
-
templateHtml: options.templateHtml || '',
|
|
124
|
-
isProduction: options.isProduction || false,
|
|
125
|
-
manifest: null
|
|
126
|
-
};
|
|
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, {
|
|
136
|
-
enablePlatformDetection,
|
|
137
|
-
renderFunction,
|
|
138
|
-
viteConfig
|
|
139
|
-
});
|
|
140
|
-
const pageMiddlewares = options.isProduction ? [
|
|
141
|
-
...prevMiddlewares,
|
|
142
|
-
serverSidePropsMiddleware,
|
|
143
|
-
renderMiddleware
|
|
144
|
-
] : [
|
|
145
|
-
HMRMiddleware,
|
|
146
|
-
renderMiddleware
|
|
147
|
-
];
|
|
148
|
-
router.get(page.expressRoute, ...pageMiddlewares);
|
|
149
|
-
// Error middleware separado
|
|
150
|
-
router.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, vite));
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
// Setup routes asynchronously but don't block router creation
|
|
154
|
-
setupRoutes().catch(console.error);
|
|
155
|
-
return router;
|
|
156
|
-
}
|
|
157
|
-
function listDetectedPages() {
|
|
158
|
-
return (0, pages_1.getPages)();
|
|
159
|
-
}
|
|
160
|
-
var pages_2 = require("./pages");
|
|
161
|
-
Object.defineProperty(exports, "getPages", { enumerable: true, get: function () { return pages_2.getPages; } });
|
|
194
|
+
var pages_1 = require("./pages");
|
|
195
|
+
Object.defineProperty(exports, "getPages", { enumerable: true, get: function () { return pages_1.getPages; } });
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
2
|
+
import type { RequestPlatform } from '../isr/key';
|
|
3
|
+
import type { ResolvedPageRendering } from '../isr/types';
|
|
4
|
+
import type { RouteEntry, RouteManifest } from './types';
|
|
5
|
+
declare global {
|
|
6
|
+
namespace Express {
|
|
7
|
+
interface Request {
|
|
8
|
+
/** La ruta que atendió esta petición. La usa el manejador de errores. */
|
|
9
|
+
ressRoute?: RouteEntry;
|
|
10
|
+
/**
|
|
11
|
+
* Los parámetros de la URL.
|
|
12
|
+
*
|
|
13
|
+
* Express reasigna `req.params` al entrar a cada router anidado, así que
|
|
14
|
+
* los valores que el despachador extrajo se perderían en el camino. Acá
|
|
15
|
+
* quedan en un campo propio, y `req.params` se mantiene por compatibilidad
|
|
16
|
+
* con quien ya lo lee.
|
|
17
|
+
*/
|
|
18
|
+
ressParams?: Record<string, string | string[]>;
|
|
19
|
+
/**
|
|
20
|
+
* La plataforma ya resuelta. La deja la caché incremental: la plataforma
|
|
21
|
+
* de la clave y la del documento tienen que ser la misma por construcción.
|
|
22
|
+
*/
|
|
23
|
+
ressPlatform?: RequestPlatform;
|
|
24
|
+
/**
|
|
25
|
+
* La configuración de render efectiva de esta petición: el export
|
|
26
|
+
* estático combinado con lo que devolvió `getServerSideProps`. Sólo
|
|
27
|
+
* existe si `getServerSideProps` devolvió campos de render.
|
|
28
|
+
*/
|
|
29
|
+
ressPageRendering?: ResolvedPageRendering;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Cómo se arma la cadena de middlewares de una entrada. */
|
|
34
|
+
export type ChainBuilder = (entry: RouteEntry) => Promise<RequestHandler[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Un único punto de entrada para todas las rutas.
|
|
37
|
+
*
|
|
38
|
+
* El modelo anterior registraba una ruta de Express por página, lo que obligaba
|
|
39
|
+
* a traducir cada patrón a la sintaxis de Express —que no puede expresar un
|
|
40
|
+
* catch-all opcional ni un grupo— y dejaba la precedencia en manos del orden de
|
|
41
|
+
* registro. Acá el manifest decide, y Express sólo ve un middleware.
|
|
42
|
+
*
|
|
43
|
+
* Las cadenas se construyen una vez, no por petición. `chains` se reemplaza
|
|
44
|
+
* entero cuando el manifest cambia en desarrollo, de modo que una petición nunca
|
|
45
|
+
* ve una mezcla de dos versiones.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createDispatcher(getState: () => {
|
|
48
|
+
manifest: RouteManifest;
|
|
49
|
+
chains: Map<string, RequestHandler[]>;
|
|
50
|
+
}): RequestHandler;
|
|
51
|
+
/**
|
|
52
|
+
* Ejecuta la cadena respetando el contrato de Express.
|
|
53
|
+
*
|
|
54
|
+
* Cada middleware decide si sigue: `next()` avanza, `next(error)` corta y
|
|
55
|
+
* propaga. Un throw síncrono se convierte en `next(error)` para que no quede una
|
|
56
|
+
* petición colgada.
|
|
57
|
+
*/
|
|
58
|
+
export declare function runMiddlewareChain(chain: readonly RequestHandler[], req: Request, res: Response, done: NextFunction): void;
|