@ressjs/vite-router 0.5.0 → 0.6.0-experimental.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 +41 -31
- 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 +23 -0
- package/dist/config/codegen.js +118 -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 +44 -0
- package/dist/config/load.d.ts +26 -0
- package/dist/config/load.js +73 -0
- package/dist/config/middleware.d.ts +18 -0
- package/dist/config/middleware.js +79 -0
- package/dist/config/rules.d.ts +48 -0
- package/dist/config/rules.js +149 -0
- package/dist/config/types.d.ts +164 -0
- package/dist/config/types.js +2 -0
- package/dist/config/validate.d.ts +19 -0
- package/dist/config/validate.js +105 -0
- package/dist/config/watch.d.ts +41 -0
- package/dist/config/watch.js +132 -0
- package/dist/fs/module-extensions.d.ts +82 -0
- package/dist/fs/module-extensions.js +196 -0
- package/dist/helpers/html-generator.d.ts +22 -5
- package/dist/helpers/html-generator.js +89 -120
- package/dist/helpers/middlewares.d.ts +7 -7
- package/dist/helpers/middlewares.js +51 -103
- package/dist/helpers/request-handler.d.ts +2 -2
- package/dist/helpers/request-handler.js +118 -25
- package/dist/index.d.ts +30 -3
- package/dist/index.js +70 -8
- 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 +66 -0
- package/dist/plugin/environments.js +72 -0
- package/dist/plugin/index.d.ts +54 -0
- package/dist/plugin/index.js +178 -0
- package/dist/plugin/virtual-entries.d.ts +26 -0
- package/dist/plugin/virtual-entries.js +80 -0
- package/dist/render.d.ts +23 -2
- package/dist/render.js +40 -53
- package/dist/router.d.ts +32 -13
- package/dist/router.js +147 -146
- package/dist/routes/dispatcher.d.ts +37 -0
- package/dist/routes/dispatcher.js +69 -0
- package/dist/routes/manifest.d.ts +17 -0
- package/dist/routes/manifest.js +56 -0
- package/dist/routes/match.d.ts +20 -0
- package/dist/routes/match.js +75 -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 +44 -0
- package/dist/routes/router.js +81 -0
- package/dist/routes/scan.d.ts +16 -0
- package/dist/routes/scan.js +82 -0
- package/dist/routes/types.d.ts +85 -0
- package/dist/routes/types.js +13 -0
- package/dist/runtime/dev-server.d.ts +62 -0
- package/dist/runtime/dev-server.js +94 -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 +42 -0
- package/dist/runtime/prod-server.js +142 -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 +50 -0
- package/dist/security/config.d.ts +130 -0
- package/dist/security/config.js +73 -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 +111 -0
- package/dist/security/headers.d.ts +62 -0
- package/dist/security/headers.js +187 -0
- package/dist/security/index.d.ts +24 -0
- package/dist/security/index.js +47 -0
- package/dist/security/serialize.d.ts +48 -0
- package/dist/security/serialize.js +146 -0
- package/dist/variants/assets.d.ts +23 -0
- package/dist/variants/assets.js +78 -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 +69 -0
- package/dist/variants/types.js +2 -0
- package/package.json +22 -10
- package/dist/helpers/vite-config.d.ts +0 -10
- package/dist/helpers/vite-config.js +0 -90
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin de ress.js para Vite.
|
|
3
|
+
*
|
|
4
|
+
* Único punto de entrada de configuración para un proyecto:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { defineConfig } from 'vite'
|
|
8
|
+
* import { ressPlugin } from '@ressjs/vite-router/plugin'
|
|
9
|
+
*
|
|
10
|
+
* export default defineConfig({ plugins: [ressPlugin()] })
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import { defineEnvironments, OUT_DIR } from './environments';
|
|
14
|
+
import type { VariantCatalog } from '../variants/types';
|
|
15
|
+
import { type ExtensionPolicy } from '../fs/module-extensions';
|
|
16
|
+
import { type SecurityOptions } from '../security';
|
|
17
|
+
/**
|
|
18
|
+
* El tipo `Plugin` de Vite se declara estructuralmente en vez de importarse,
|
|
19
|
+
* para que este paquete no dependa del tipo en tiempo de compilación. Vite es
|
|
20
|
+
* una dependencia entre pares: el consumidor la aporta.
|
|
21
|
+
*/
|
|
22
|
+
export interface RessPlugin {
|
|
23
|
+
name: string;
|
|
24
|
+
/** Opcional: el guard de `@ressjs/config` sólo aporta `resolveId`. */
|
|
25
|
+
config?: (userConfig: unknown, env: {
|
|
26
|
+
command: string;
|
|
27
|
+
mode: string;
|
|
28
|
+
}) => Promise<unknown> | unknown;
|
|
29
|
+
resolveId?: (id: string, importer?: string, options?: unknown) => string | undefined | null;
|
|
30
|
+
load?: (id: string) => string | undefined;
|
|
31
|
+
configureServer?: (server: any) => void;
|
|
32
|
+
writeBundle?: () => Promise<void> | void;
|
|
33
|
+
}
|
|
34
|
+
export declare function getVariantCatalog(): VariantCatalog | undefined;
|
|
35
|
+
export declare function refreshVariantCatalog(): Promise<VariantCatalog>;
|
|
36
|
+
export interface RessPluginOptions {
|
|
37
|
+
security?: SecurityOptions;
|
|
38
|
+
/**
|
|
39
|
+
* Qué extensiones acepta el proyecto. Se fusiona sobre los defaults una vez al
|
|
40
|
+
* arrancar. F-008 la absorbe cuando exista el archivo de configuración.
|
|
41
|
+
*/
|
|
42
|
+
extensions?: Partial<ExtensionPolicy>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* El plugin del framework.
|
|
46
|
+
*
|
|
47
|
+
* Devuelve dos: el propio y el guard que impide que `@ressjs/config` —valores de
|
|
48
|
+
* servidor, credenciales incluidas— termine en el paquete del navegador. El
|
|
49
|
+
* segundo no se agrega a mano justamente porque olvidarse es el modo de fallo que
|
|
50
|
+
* existe para evitar.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ressPlugin(options?: RessPluginOptions): RessPlugin[];
|
|
53
|
+
export declare function ressCorePlugin(options?: RessPluginOptions): RessPlugin;
|
|
54
|
+
export { defineEnvironments, OUT_DIR };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Plugin de ress.js para Vite.
|
|
4
|
+
*
|
|
5
|
+
* Único punto de entrada de configuración para un proyecto:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { defineConfig } from 'vite'
|
|
9
|
+
* import { ressPlugin } from '@ressjs/vite-router/plugin'
|
|
10
|
+
*
|
|
11
|
+
* export default defineConfig({ plugins: [ressPlugin()] })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.OUT_DIR = exports.defineEnvironments = void 0;
|
|
16
|
+
exports.getVariantCatalog = getVariantCatalog;
|
|
17
|
+
exports.refreshVariantCatalog = refreshVariantCatalog;
|
|
18
|
+
exports.ressPlugin = ressPlugin;
|
|
19
|
+
exports.ressCorePlugin = ressCorePlugin;
|
|
20
|
+
const environments_1 = require("./environments");
|
|
21
|
+
Object.defineProperty(exports, "defineEnvironments", { enumerable: true, get: function () { return environments_1.defineEnvironments; } });
|
|
22
|
+
Object.defineProperty(exports, "OUT_DIR", { enumerable: true, get: function () { return environments_1.OUT_DIR; } });
|
|
23
|
+
const pages_1 = require("../pages");
|
|
24
|
+
const catalog_1 = require("../variants/catalog");
|
|
25
|
+
const virtual_entries_1 = require("./virtual-entries");
|
|
26
|
+
const scan_1 = require("../routes/scan");
|
|
27
|
+
const module_extensions_1 = require("../fs/module-extensions");
|
|
28
|
+
const manifest_1 = require("../routes/manifest");
|
|
29
|
+
const security_1 = require("../security");
|
|
30
|
+
const config_1 = require("@ressjs/config");
|
|
31
|
+
const config_2 = require("../config");
|
|
32
|
+
/**
|
|
33
|
+
* Catálogo vigente del proceso.
|
|
34
|
+
*
|
|
35
|
+
* Se arma una vez y lo comparten el build y el servidor: es lo que garantiza que
|
|
36
|
+
* las entradas que se construyen y las que se resuelven por petición salgan de la
|
|
37
|
+
* misma fuente.
|
|
38
|
+
*/
|
|
39
|
+
let current;
|
|
40
|
+
function getVariantCatalog() {
|
|
41
|
+
return current;
|
|
42
|
+
}
|
|
43
|
+
async function refreshVariantCatalog() {
|
|
44
|
+
current = await (0, catalog_1.buildVariantCatalog)({ pages: (0, pages_1.getPages)() });
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
async function resolveInputs() {
|
|
48
|
+
const catalog = await refreshVariantCatalog();
|
|
49
|
+
return {
|
|
50
|
+
client: (0, virtual_entries_1.clientBuildInputs)(catalog),
|
|
51
|
+
ssr: (0, pages_1.getViteSSRInput)(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* El plugin del framework.
|
|
56
|
+
*
|
|
57
|
+
* Devuelve dos: el propio y el guard que impide que `@ressjs/config` —valores de
|
|
58
|
+
* servidor, credenciales incluidas— termine en el paquete del navegador. El
|
|
59
|
+
* segundo no se agrega a mano justamente porque olvidarse es el modo de fallo que
|
|
60
|
+
* existe para evitar.
|
|
61
|
+
*/
|
|
62
|
+
function ressPlugin(options = {}) {
|
|
63
|
+
return [ressCorePlugin(options), (0, config_1.configServerOnlyGuard)()];
|
|
64
|
+
}
|
|
65
|
+
function ressCorePlugin(options = {}) {
|
|
66
|
+
const security = (0, security_1.resolveSecurityOptions)(options.security);
|
|
67
|
+
/** Raíz del proyecto según Vite; puede no ser el directorio de trabajo. */
|
|
68
|
+
let root = process.cwd();
|
|
69
|
+
let manifestWritten = false;
|
|
70
|
+
let extensions = { ...module_extensions_1.DEFAULT_EXTENSION_POLICY, ...options.extensions };
|
|
71
|
+
/** La configuración del proyecto, vigente desde el hook `config`. */
|
|
72
|
+
let config;
|
|
73
|
+
return {
|
|
74
|
+
name: 'ress',
|
|
75
|
+
/**
|
|
76
|
+
* Se aporta desde `config` y no desde `configResolved` para que la
|
|
77
|
+
* configuración del usuario se fusione encima siguiendo las reglas normales
|
|
78
|
+
* de Vite, en vez de ser sobrescrita por el framework.
|
|
79
|
+
*/
|
|
80
|
+
async config(userConfig, env) {
|
|
81
|
+
// Se verifica acá, con la configuración del proyecto ya visible y antes de
|
|
82
|
+
// que el servidor escuche. Un aviso en la consola de un servidor de
|
|
83
|
+
// desarrollo no lo lee nadie; un error de arranque sí.
|
|
84
|
+
root = userConfig?.root ?? process.cwd();
|
|
85
|
+
// La configuración del proyecto se lee antes que nada: declara qué
|
|
86
|
+
// extensiones son código de usuario, bajo qué prefijo se sirve todo y qué
|
|
87
|
+
// variables llegan al navegador, y las tres decisiones condicionan lo que
|
|
88
|
+
// este hook devuelve.
|
|
89
|
+
config = await (0, config_2.loadConfig)(root, env?.mode ?? 'development');
|
|
90
|
+
extensions = { ...extensions, ...config.extensions };
|
|
91
|
+
await (0, config_2.writeGeneratedTypes)(root, {
|
|
92
|
+
'ress-platform.d.ts': (0, config_2.generatePlatformTypes)(config.platform),
|
|
93
|
+
'ress-config.d.ts': (0, config_2.generateProjectConfigTypes)((await import('@ressjs/config')).resolved()),
|
|
94
|
+
});
|
|
95
|
+
// Se fija acá y no al construir el plugin: `current` es estado global del
|
|
96
|
+
// proceso, así que crear un objeto de plugin —dos configuraciones en un
|
|
97
|
+
// mismo proceso, un test que lo importa— reescribiría la política que leen
|
|
98
|
+
// el descubrimiento y el escaneo de rutas.
|
|
99
|
+
(0, module_extensions_1.setExtensionPolicy)(extensions);
|
|
100
|
+
if (env?.command === 'serve')
|
|
101
|
+
(0, security_1.assertDevServerHardening)(userConfig);
|
|
102
|
+
return {
|
|
103
|
+
...(0, environments_1.defineEnvironments)(await resolveInputs()),
|
|
104
|
+
// Sólo al construir: en desarrollo el prefijo lo quita el pipeline de
|
|
105
|
+
// petición antes de que Vite vea la URL, así que su `base` sigue siendo
|
|
106
|
+
// `/`. Al construir sí tiene que quedar escrito en las referencias que
|
|
107
|
+
// los artefactos hacen entre sí.
|
|
108
|
+
...(config.basePath && env?.command === 'build'
|
|
109
|
+
? { base: config.basePath + '/' }
|
|
110
|
+
: {}),
|
|
111
|
+
// Sólo lo declarado en `publicEnv`. Lo demás no queda como `undefined`
|
|
112
|
+
// en el navegador: queda como código que nunca se generó.
|
|
113
|
+
...(config.publicEnv.length
|
|
114
|
+
? { define: (0, config_2.publicEnvDefines)(config.publicEnv, { strict: env?.command === 'build' }) }
|
|
115
|
+
: {}),
|
|
116
|
+
...(env?.command === 'serve'
|
|
117
|
+
? { server: (0, security_1.devServerDefaults)(security.dev.allowedOrigins) }
|
|
118
|
+
: {}),
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Emite el manifest de rutas junto al build.
|
|
123
|
+
*
|
|
124
|
+
* Cargarlo en producción es lo que evita volver a recorrer el sistema de
|
|
125
|
+
* archivos en cada arranque para deducir algo que ya no puede cambiar.
|
|
126
|
+
*
|
|
127
|
+
* El hook corre una vez por entorno —cliente y servidor— y el manifest es
|
|
128
|
+
* el mismo para los dos, así que se escribe una sola vez.
|
|
129
|
+
*/
|
|
130
|
+
async writeBundle() {
|
|
131
|
+
if (manifestWritten)
|
|
132
|
+
return;
|
|
133
|
+
manifestWritten = true;
|
|
134
|
+
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
135
|
+
const path = await import('node:path');
|
|
136
|
+
const data = await (0, scan_1.scanRoutes)({ strict: true, pagesDir: path.join(root, 'app/pages') });
|
|
137
|
+
const target = path.resolve(root, environments_1.OUT_DIR.base);
|
|
138
|
+
await mkdir(target, { recursive: true });
|
|
139
|
+
await writeFile(path.join(target, 'route-manifest.json'), (0, manifest_1.serializeRouteManifest)(data), 'utf-8');
|
|
140
|
+
},
|
|
141
|
+
resolveId(id) {
|
|
142
|
+
return (0, virtual_entries_1.resolveEntryId)(id);
|
|
143
|
+
},
|
|
144
|
+
load(id) {
|
|
145
|
+
const parsed = (0, virtual_entries_1.parseEntryId)(id);
|
|
146
|
+
if (!parsed || !current)
|
|
147
|
+
return undefined;
|
|
148
|
+
return (0, virtual_entries_1.generateEntryModule)(current, parsed.pageId, parsed.suffix);
|
|
149
|
+
},
|
|
150
|
+
/**
|
|
151
|
+
* Rearma el catálogo cuando cambia el **conjunto** de archivos, no su
|
|
152
|
+
* contenido: agregar o borrar una hoja de estilos cambia qué variantes
|
|
153
|
+
* declara una página, y eso tiene que reflejarse sin reiniciar. Los cambios
|
|
154
|
+
* de contenido los maneja la recarga en caliente de Vite.
|
|
155
|
+
*/
|
|
156
|
+
configureServer(server) {
|
|
157
|
+
const declaresVariants = (file) => file.includes('app/pages/') || file.includes('app\\pages\\');
|
|
158
|
+
const rebuild = (file) => {
|
|
159
|
+
if (declaresVariants(file))
|
|
160
|
+
void refreshVariantCatalog();
|
|
161
|
+
};
|
|
162
|
+
server.watcher?.on('add', rebuild);
|
|
163
|
+
server.watcher?.on('unlink', rebuild);
|
|
164
|
+
// Las dos superficies de configuración. Observar sólo `ress.config` dejaba
|
|
165
|
+
// mudo el cambio de una capa de `config/`.
|
|
166
|
+
if (config) {
|
|
167
|
+
(0, config_2.watchConfig)(server, {
|
|
168
|
+
root,
|
|
169
|
+
mode: server.config?.mode ?? 'development',
|
|
170
|
+
current: () => config,
|
|
171
|
+
onReload: (next) => {
|
|
172
|
+
config = next;
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VariantCatalog } from '../variants/types';
|
|
2
|
+
declare const PREFIX = "ress:entry";
|
|
3
|
+
declare const RESOLVED: string;
|
|
4
|
+
/** ¿Es el identificador de una entrada de build del catálogo? */
|
|
5
|
+
export declare function isEntryId(id: string): boolean;
|
|
6
|
+
export declare function resolveEntryId(id: string): string | undefined;
|
|
7
|
+
/** Extrae página y variante del identificador. */
|
|
8
|
+
export declare function parseEntryId(id: string): {
|
|
9
|
+
pageId: string;
|
|
10
|
+
suffix: string;
|
|
11
|
+
} | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Genera el módulo de hidratación de una página en una variante.
|
|
14
|
+
*
|
|
15
|
+
* Se escribe con `createElement` y no con JSX a propósito: así el módulo no
|
|
16
|
+
* depende de la transformación JSX ni de una extensión concreta, que es lo que
|
|
17
|
+
* permite que una página escrita en JavaScript se hidrate con el mismo código
|
|
18
|
+
* que una escrita en TypeScript.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateEntryModule(catalog: VariantCatalog, pageId: string, variantSuffix: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Qué le pasa el framework al empaquetador como entradas del entorno de cliente:
|
|
23
|
+
* una por página y variante, cada una apuntando a su módulo virtual.
|
|
24
|
+
*/
|
|
25
|
+
export declare function clientBuildInputs(catalog: VariantCatalog): Record<string, string>;
|
|
26
|
+
export { PREFIX as ENTRY_PREFIX, RESOLVED as ENTRY_RESOLVED_PREFIX };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ENTRY_RESOLVED_PREFIX = exports.ENTRY_PREFIX = void 0;
|
|
7
|
+
exports.isEntryId = isEntryId;
|
|
8
|
+
exports.resolveEntryId = resolveEntryId;
|
|
9
|
+
exports.parseEntryId = parseEntryId;
|
|
10
|
+
exports.generateEntryModule = generateEntryModule;
|
|
11
|
+
exports.clientBuildInputs = clientBuildInputs;
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const catalog_1 = require("../variants/catalog");
|
|
14
|
+
const PREFIX = 'ress:entry';
|
|
15
|
+
exports.ENTRY_PREFIX = PREFIX;
|
|
16
|
+
const RESOLVED = '\0' + PREFIX;
|
|
17
|
+
exports.ENTRY_RESOLVED_PREFIX = RESOLVED;
|
|
18
|
+
/** ¿Es el identificador de una entrada de build del catálogo? */
|
|
19
|
+
function isEntryId(id) {
|
|
20
|
+
return id.startsWith(PREFIX) || id.startsWith(RESOLVED);
|
|
21
|
+
}
|
|
22
|
+
function resolveEntryId(id) {
|
|
23
|
+
return id.startsWith(PREFIX) ? '\0' + id : undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Extrae página y variante del identificador. */
|
|
26
|
+
function parseEntryId(id) {
|
|
27
|
+
if (!id.startsWith(RESOLVED))
|
|
28
|
+
return undefined;
|
|
29
|
+
const query = id.slice(RESOLVED.length + 1); // salta el '?'
|
|
30
|
+
const params = new URLSearchParams(query);
|
|
31
|
+
const pageId = params.get('page');
|
|
32
|
+
if (!pageId)
|
|
33
|
+
return undefined;
|
|
34
|
+
return { pageId, suffix: params.get('variant') ?? '' };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Genera el módulo de hidratación de una página en una variante.
|
|
38
|
+
*
|
|
39
|
+
* Se escribe con `createElement` y no con JSX a propósito: así el módulo no
|
|
40
|
+
* depende de la transformación JSX ni de una extensión concreta, que es lo que
|
|
41
|
+
* permite que una página escrita en JavaScript se hidrate con el mismo código
|
|
42
|
+
* que una escrita en TypeScript.
|
|
43
|
+
*/
|
|
44
|
+
function generateEntryModule(catalog, pageId, variantSuffix) {
|
|
45
|
+
const page = catalog.pagesById.get(pageId);
|
|
46
|
+
if (!page) {
|
|
47
|
+
throw new Error(`[ress] No hay ninguna página con el identificador "${pageId}".`);
|
|
48
|
+
}
|
|
49
|
+
const variant = catalog.variantsByPage
|
|
50
|
+
.get(pageId)
|
|
51
|
+
?.find((v) => v.suffix === variantSuffix);
|
|
52
|
+
if (!variant) {
|
|
53
|
+
throw new Error(`[ress] La página "${pageId}" no declara la variante "${variantSuffix || '(base)'}".`);
|
|
54
|
+
}
|
|
55
|
+
const pageAbs = node_path_1.default.resolve(page.pageFile).replace(/\\/g, '/');
|
|
56
|
+
const styleImport = variant.styleFile
|
|
57
|
+
? `import ${JSON.stringify(node_path_1.default.resolve(variant.styleFile).replace(/\\/g, '/'))};\n`
|
|
58
|
+
: '';
|
|
59
|
+
return `${styleImport}import { hydrateRoot } from "react-dom/client";
|
|
60
|
+
import { StrictMode, createElement } from "react";
|
|
61
|
+
import Page from ${JSON.stringify(pageAbs)};
|
|
62
|
+
|
|
63
|
+
const props = window.__RESS_PROPS__ || window.__INITIAL_PROPS__ || {};
|
|
64
|
+
const root = document.getElementById("root");
|
|
65
|
+
|
|
66
|
+
if (root) {
|
|
67
|
+
hydrateRoot(root, createElement(StrictMode, null, createElement(Page, props)));
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Qué le pasa el framework al empaquetador como entradas del entorno de cliente:
|
|
73
|
+
* una por página y variante, cada una apuntando a su módulo virtual.
|
|
74
|
+
*/
|
|
75
|
+
function clientBuildInputs(catalog) {
|
|
76
|
+
return Object.fromEntries(catalog.buildEntries.map((entry) => [
|
|
77
|
+
(0, catalog_1.buildEntryNameFor)(entry.pageId, entry.variant.suffix),
|
|
78
|
+
entry.virtualId,
|
|
79
|
+
]));
|
|
80
|
+
}
|
package/dist/render.d.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
+
import type { ModuleLoader } from './runtime/module-loader';
|
|
3
|
+
import type { ClientPropsPolicy } from './security';
|
|
2
4
|
/**
|
|
3
5
|
* RENDER MIDDLEWARE PARA EL ROUTER
|
|
4
6
|
*/
|
|
5
7
|
export declare function createRenderMiddleware(page: any, options: any): RequestHandler;
|
|
6
8
|
export interface RenderOptions {
|
|
7
9
|
url: string;
|
|
10
|
+
/**
|
|
11
|
+
* La página que el despachador ya resolvió.
|
|
12
|
+
*
|
|
13
|
+
* Sin esto el render vuelve a buscarla por URL, con un criterio propio que no
|
|
14
|
+
* conoce grupos ni catch-all: dos resoluciones distintas para la misma
|
|
15
|
+
* pregunta, que es el defecto que esta spec elimina.
|
|
16
|
+
*/
|
|
17
|
+
page?: {
|
|
18
|
+
route: string;
|
|
19
|
+
file: string;
|
|
20
|
+
pageId?: string;
|
|
21
|
+
abs?: string;
|
|
22
|
+
};
|
|
8
23
|
props?: any;
|
|
24
|
+
/** Estado que dejaron los middlewares. Sólo viaja al cliente lo que la política declara. */
|
|
25
|
+
locals?: Record<string, unknown>;
|
|
9
26
|
}
|
|
10
27
|
export interface CreateRenderServerOptions {
|
|
11
|
-
|
|
28
|
+
/** Cargador de módulos. Ausente en producción: se importa el artefacto compilado. */
|
|
29
|
+
moduleLoader?: ModuleLoader;
|
|
30
|
+
/** Qué claves del estado del servidor viajan al cliente. */
|
|
31
|
+
clientProps?: ClientPropsPolicy;
|
|
32
|
+
isProduction?: boolean;
|
|
12
33
|
}
|
|
13
34
|
export declare function createRenderServer(options?: CreateRenderServerOptions): (renderOptions: RenderOptions) => Promise<{
|
|
14
35
|
html: string;
|
|
15
|
-
props:
|
|
36
|
+
props: Record<string, unknown>;
|
|
16
37
|
route: string;
|
|
17
38
|
page: string;
|
|
18
39
|
}>;
|
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
|
};
|
|
@@ -42,6 +9,8 @@ const react_1 = __importDefault(require("react"));
|
|
|
42
9
|
const server_1 = require("react-dom/server");
|
|
43
10
|
const pages_1 = require("./pages");
|
|
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");
|
|
45
14
|
/**
|
|
46
15
|
* RENDER MIDDLEWARE PARA EL ROUTER
|
|
47
16
|
*/
|
|
@@ -57,9 +26,11 @@ function createRenderMiddleware(page, options) {
|
|
|
57
26
|
}
|
|
58
27
|
function createRenderServer(options = {}) {
|
|
59
28
|
const pages = (0, pages_1.getPages)();
|
|
60
|
-
const {
|
|
29
|
+
const { moduleLoader } = options;
|
|
30
|
+
const policy = options.clientProps ?? (0, security_1.resolveSecurityOptions)().clientProps;
|
|
31
|
+
const isProduction = options.isProduction ?? process.env.NODE_ENV === 'production';
|
|
61
32
|
return async (renderOptions) => {
|
|
62
|
-
const { url, props = {} } = renderOptions;
|
|
33
|
+
const { url, props = {}, locals = {}, page } = renderOptions;
|
|
63
34
|
// Encuentra la página que coincide con la URL
|
|
64
35
|
// Función helper para hacer matching de rutas dinámicas
|
|
65
36
|
const matchRoute = (pageRoute, url) => {
|
|
@@ -77,28 +48,18 @@ function createRenderServer(options = {}) {
|
|
|
77
48
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
78
49
|
return regex.test(url);
|
|
79
50
|
};
|
|
80
|
-
const matchedPage = pages.find(
|
|
51
|
+
const matchedPage = page ?? pages.find(p => matchRoute(p.route, url));
|
|
81
52
|
if (!matchedPage) {
|
|
82
53
|
throw new Error(`Page not found for URL: ${url}`);
|
|
83
54
|
}
|
|
84
55
|
try {
|
|
85
|
-
// Import dinámico de la página usando vite.ssrLoadModule si está disponible
|
|
86
56
|
let pageModule;
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
const modulePath = `/${matchedPage.file}`;
|
|
90
|
-
pageModule = await vite.ssrLoadModule(modulePath);
|
|
57
|
+
if (moduleLoader) {
|
|
58
|
+
pageModule = await moduleLoader.load(`/${matchedPage.file}`);
|
|
91
59
|
}
|
|
92
60
|
else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
try {
|
|
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
|
-
}
|
|
61
|
+
const moduleUrl = (0, prod_server_1.resolveServerModulePath)(matchedPage.abs ?? matchedPage.file);
|
|
62
|
+
pageModule = await import(/* @vite-ignore */ moduleUrl);
|
|
102
63
|
}
|
|
103
64
|
const PageComponent = pageModule.default;
|
|
104
65
|
if (!PageComponent) {
|
|
@@ -113,9 +74,17 @@ function createRenderServer(options = {}) {
|
|
|
113
74
|
};
|
|
114
75
|
// Renderiza el componente con las props finales
|
|
115
76
|
const html = (0, server_1.renderToString)(react_1.default.createElement(PageComponent, finalProps));
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
77
|
+
// El componente ya recibió todo arriba. Lo que viaja al cliente es sólo
|
|
78
|
+
// lo que la política declara: enumerar lo que se excluye deja expuesta
|
|
79
|
+
// cada clave que un middleware agregue después.
|
|
80
|
+
//
|
|
81
|
+
// `serverSideProps` es lo que la página pidió explícitamente para el
|
|
82
|
+
// cliente; el resto de `res.locals` es estado del servidor y no viaja
|
|
83
|
+
// salvo que se declare.
|
|
84
|
+
const exposable = { ...locals, serverSideProps: props };
|
|
85
|
+
const clientProps = (0, security_1.pickClientProps)(exposable, policy);
|
|
86
|
+
if (!isProduction)
|
|
87
|
+
warnWithheld(matchedPage.route, exposable, clientProps);
|
|
119
88
|
return {
|
|
120
89
|
html,
|
|
121
90
|
props: clientProps,
|
|
@@ -129,3 +98,21 @@ function createRenderServer(options = {}) {
|
|
|
129
98
|
}
|
|
130
99
|
};
|
|
131
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Avisa, una vez por ruta, qué quedó fuera del cliente.
|
|
103
|
+
*
|
|
104
|
+
* Una página que renderiza en el servidor con datos que el cliente no tiene
|
|
105
|
+
* hidrata distinto, y ese desajuste no dice de dónde viene. El aviso lo nombra.
|
|
106
|
+
*/
|
|
107
|
+
const warnedRoutes = new Set();
|
|
108
|
+
function warnWithheld(route, serverProps, clientProps) {
|
|
109
|
+
if (warnedRoutes.has(route))
|
|
110
|
+
return;
|
|
111
|
+
const withheld = (0, security_1.withheldKeys)(serverProps, clientProps);
|
|
112
|
+
if (withheld.length === 0)
|
|
113
|
+
return;
|
|
114
|
+
warnedRoutes.add(route);
|
|
115
|
+
console.warn(`[ress] ${route}: estas claves existen en el servidor y no viajan al cliente: ` +
|
|
116
|
+
`${withheld.join(', ')}.\n` +
|
|
117
|
+
'Si la página las usa al hidratar, declaralas en `security.clientProps.expose`.');
|
|
118
|
+
}
|
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';
|