@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
|
@@ -0,0 +1,59 @@
|
|
|
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 BuildOutputBundle } from './route-manifest';
|
|
17
|
+
import { type SecurityOptions } from '../security';
|
|
18
|
+
/**
|
|
19
|
+
* El tipo `Plugin` de Vite se declara estructuralmente en vez de importarse,
|
|
20
|
+
* para que este paquete no dependa del tipo en tiempo de compilación. Vite es
|
|
21
|
+
* una dependencia entre pares: el consumidor la aporta.
|
|
22
|
+
*/
|
|
23
|
+
export interface RessPlugin {
|
|
24
|
+
name: string;
|
|
25
|
+
/** Opcional: el guard de `@ressjs/config` sólo aporta `resolveId`. */
|
|
26
|
+
config?: (userConfig: unknown, env: {
|
|
27
|
+
command: string;
|
|
28
|
+
mode: string;
|
|
29
|
+
}) => Promise<unknown> | unknown;
|
|
30
|
+
resolveId?: (id: string, importer?: string, options?: unknown) => string | undefined | null;
|
|
31
|
+
load?: (id: string) => string | undefined;
|
|
32
|
+
configureServer?: (server: any) => void;
|
|
33
|
+
writeBundle?: (this: {
|
|
34
|
+
environment?: {
|
|
35
|
+
name?: string;
|
|
36
|
+
};
|
|
37
|
+
}, options: unknown, bundle: BuildOutputBundle) => Promise<void> | void;
|
|
38
|
+
}
|
|
39
|
+
export declare function getVariantCatalog(): VariantCatalog | undefined;
|
|
40
|
+
export declare function refreshVariantCatalog(): Promise<VariantCatalog>;
|
|
41
|
+
export interface RessPluginOptions {
|
|
42
|
+
security?: SecurityOptions;
|
|
43
|
+
/**
|
|
44
|
+
* Qué extensiones acepta el proyecto. Se fusiona sobre los defaults una vez al
|
|
45
|
+
* arrancar. F-008 la absorbe cuando exista el archivo de configuración.
|
|
46
|
+
*/
|
|
47
|
+
extensions?: Partial<ExtensionPolicy>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* El plugin del framework.
|
|
51
|
+
*
|
|
52
|
+
* Devuelve dos: el propio y el guard que impide que `@ressjs/config` —valores de
|
|
53
|
+
* servidor, credenciales incluidas— termine en el paquete del navegador. El
|
|
54
|
+
* segundo no se agrega a mano justamente porque olvidarse es el modo de fallo que
|
|
55
|
+
* existe para evitar.
|
|
56
|
+
*/
|
|
57
|
+
export declare function ressPlugin(options?: RessPluginOptions): RessPlugin[];
|
|
58
|
+
export declare function ressCorePlugin(options?: RessPluginOptions): RessPlugin;
|
|
59
|
+
export { defineEnvironments, OUT_DIR };
|
|
@@ -0,0 +1,195 @@
|
|
|
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 middlewares_1 = require("../helpers/middlewares");
|
|
28
|
+
const module_extensions_1 = require("../fs/module-extensions");
|
|
29
|
+
const manifest_1 = require("../routes/manifest");
|
|
30
|
+
const route_manifest_1 = require("./route-manifest");
|
|
31
|
+
const security_1 = require("../security");
|
|
32
|
+
const config_1 = require("@ressjs/config");
|
|
33
|
+
const config_2 = require("../config");
|
|
34
|
+
/**
|
|
35
|
+
* Catálogo vigente del proceso.
|
|
36
|
+
*
|
|
37
|
+
* Se arma una vez y lo comparten el build y el servidor: es lo que garantiza que
|
|
38
|
+
* las entradas que se construyen y las que se resuelven por petición salgan de la
|
|
39
|
+
* misma fuente.
|
|
40
|
+
*/
|
|
41
|
+
let current;
|
|
42
|
+
function getVariantCatalog() {
|
|
43
|
+
return current;
|
|
44
|
+
}
|
|
45
|
+
async function refreshVariantCatalog() {
|
|
46
|
+
current = await (0, catalog_1.buildVariantCatalog)({ pages: (0, pages_1.getPages)() });
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
async function resolveInputs() {
|
|
50
|
+
const catalog = await refreshVariantCatalog();
|
|
51
|
+
return {
|
|
52
|
+
client: (0, virtual_entries_1.clientBuildInputs)(catalog),
|
|
53
|
+
ssr: (0, pages_1.getViteSSRInput)(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* El plugin del framework.
|
|
58
|
+
*
|
|
59
|
+
* Devuelve dos: el propio y el guard que impide que `@ressjs/config` —valores de
|
|
60
|
+
* servidor, credenciales incluidas— termine en el paquete del navegador. El
|
|
61
|
+
* segundo no se agrega a mano justamente porque olvidarse es el modo de fallo que
|
|
62
|
+
* existe para evitar.
|
|
63
|
+
*/
|
|
64
|
+
function ressPlugin(options = {}) {
|
|
65
|
+
return [ressCorePlugin(options), (0, config_1.configServerOnlyGuard)()];
|
|
66
|
+
}
|
|
67
|
+
function ressCorePlugin(options = {}) {
|
|
68
|
+
// Se recalcula en el hook `config`, después de `loadConfig`: hasta ese
|
|
69
|
+
// momento no se leyó `config.security`, así que acá sólo hay defaults.
|
|
70
|
+
let security = (0, security_1.resolveSecurityOptions)(options.security);
|
|
71
|
+
/** Raíz del proyecto según Vite; puede no ser el directorio de trabajo. */
|
|
72
|
+
let root = process.cwd();
|
|
73
|
+
let manifestWritten = false;
|
|
74
|
+
let extensions = { ...module_extensions_1.DEFAULT_EXTENSION_POLICY, ...options.extensions };
|
|
75
|
+
/** La configuración del proyecto, vigente desde el hook `config`. */
|
|
76
|
+
let config;
|
|
77
|
+
return {
|
|
78
|
+
name: 'ress',
|
|
79
|
+
/**
|
|
80
|
+
* Se aporta desde `config` y no desde `configResolved` para que la
|
|
81
|
+
* configuración del usuario se fusione encima siguiendo las reglas normales
|
|
82
|
+
* de Vite, en vez de ser sobrescrita por el framework.
|
|
83
|
+
*/
|
|
84
|
+
async config(userConfig, env) {
|
|
85
|
+
// Se verifica acá, con la configuración del proyecto ya visible y antes de
|
|
86
|
+
// que el servidor escuche. Un aviso en la consola de un servidor de
|
|
87
|
+
// desarrollo no lo lee nadie; un error de arranque sí.
|
|
88
|
+
root = userConfig?.root ?? process.cwd();
|
|
89
|
+
// La configuración del proyecto se lee antes que nada: declara qué
|
|
90
|
+
// extensiones son código de usuario, bajo qué prefijo se sirve todo y qué
|
|
91
|
+
// variables llegan al navegador, y las tres decisiones condicionan lo que
|
|
92
|
+
// este hook devuelve.
|
|
93
|
+
config = await (0, config_2.loadConfig)(root, env?.mode ?? 'development');
|
|
94
|
+
extensions = { ...extensions, ...config.extensions };
|
|
95
|
+
// `config.security` es lo que declara el proyecto; sólo se pisa si quien
|
|
96
|
+
// arma el plugin aportó algo explícito.
|
|
97
|
+
security = (0, security_1.resolveSecurityOptions)(options.security ?? config.security);
|
|
98
|
+
await (0, config_2.writeProjectTypes)(root, config.platform);
|
|
99
|
+
// Se fija acá y no al construir el plugin: `current` es estado global del
|
|
100
|
+
// proceso, así que crear un objeto de plugin —dos configuraciones en un
|
|
101
|
+
// mismo proceso, un test que lo importa— reescribiría la política que leen
|
|
102
|
+
// el descubrimiento y el escaneo de rutas.
|
|
103
|
+
(0, module_extensions_1.setExtensionPolicy)(extensions);
|
|
104
|
+
if (env?.command === 'serve')
|
|
105
|
+
(0, security_1.assertDevServerHardening)(userConfig);
|
|
106
|
+
return {
|
|
107
|
+
...(0, environments_1.defineEnvironments)(await resolveInputs()),
|
|
108
|
+
// Sólo al construir: en desarrollo el prefijo lo quita el pipeline de
|
|
109
|
+
// petición antes de que Vite vea la URL, así que su `base` sigue siendo
|
|
110
|
+
// `/`. Al construir sí tiene que quedar escrito en las referencias que
|
|
111
|
+
// los artefactos hacen entre sí.
|
|
112
|
+
...(config.basePath && env?.command === 'build'
|
|
113
|
+
? { base: config.basePath + '/' }
|
|
114
|
+
: {}),
|
|
115
|
+
// Sólo lo declarado en `publicEnv`. Lo demás no queda como `undefined`
|
|
116
|
+
// en el navegador: queda como código que nunca se generó.
|
|
117
|
+
...(config.publicEnv.length
|
|
118
|
+
? { define: (0, config_2.publicEnvDefines)(config.publicEnv, { strict: env?.command === 'build' }) }
|
|
119
|
+
: {}),
|
|
120
|
+
...(env?.command === 'serve'
|
|
121
|
+
? { server: (0, security_1.devServerDefaults)(security.dev.allowedOrigins) }
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Emite el manifest de rutas junto al build.
|
|
127
|
+
*
|
|
128
|
+
* Cargarlo en producción es lo que evita volver a recorrer el sistema de
|
|
129
|
+
* archivos en cada arranque para deducir algo que ya no puede cambiar.
|
|
130
|
+
*
|
|
131
|
+
* El hook corre una vez por entorno —cliente y servidor— y el manifest es
|
|
132
|
+
* el mismo para los dos, así que se escribe una sola vez.
|
|
133
|
+
*/
|
|
134
|
+
async writeBundle(_options, bundle) {
|
|
135
|
+
// El cliente se construye primero, pero sólo el entorno SSR conoce los
|
|
136
|
+
// chunks que hay que registrar. Escribir durante el cliente dejaba un
|
|
137
|
+
// manifest válido en apariencia pero imposible de usar para cargar prod.
|
|
138
|
+
if (this.environment?.name !== 'ssr' || manifestWritten)
|
|
139
|
+
return;
|
|
140
|
+
manifestWritten = true;
|
|
141
|
+
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
142
|
+
const path = await import('node:path');
|
|
143
|
+
const scanned = await (0, scan_1.scanRoutes)({
|
|
144
|
+
strict: true,
|
|
145
|
+
root,
|
|
146
|
+
pagesDir: path.join(root, 'app/pages'),
|
|
147
|
+
});
|
|
148
|
+
const data = (0, route_manifest_1.attachServerBuildChunks)({ ...scanned, middlewares: (0, middlewares_1.getMiddlewares)({ cwd: root }).map(middlewares_1.toMiddlewareEntry) }, bundle, root);
|
|
149
|
+
const target = path.resolve(root, environments_1.OUT_DIR.base);
|
|
150
|
+
await mkdir(target, { recursive: true });
|
|
151
|
+
await writeFile(path.join(target, 'route-manifest.json'), (0, manifest_1.serializeRouteManifest)(data), 'utf-8');
|
|
152
|
+
// Identifica este build. La caché incremental lo usa en la clave: un
|
|
153
|
+
// documento generado por otro build puede apuntar a artefactos que ya no
|
|
154
|
+
// existen, y la hidratación rota que produce no dice de dónde viene.
|
|
155
|
+
const { randomUUID } = await import('node:crypto');
|
|
156
|
+
await writeFile(path.join(target, environments_1.BUILD_ID_FILE), randomUUID(), 'utf-8');
|
|
157
|
+
},
|
|
158
|
+
resolveId(id) {
|
|
159
|
+
return (0, virtual_entries_1.resolveEntryId)(id);
|
|
160
|
+
},
|
|
161
|
+
load(id) {
|
|
162
|
+
const parsed = (0, virtual_entries_1.parseEntryId)(id);
|
|
163
|
+
if (!parsed || !current)
|
|
164
|
+
return undefined;
|
|
165
|
+
return (0, virtual_entries_1.generateEntryModule)(current, parsed.pageId, parsed.suffix);
|
|
166
|
+
},
|
|
167
|
+
/**
|
|
168
|
+
* Rearma el catálogo cuando cambia el **conjunto** de archivos, no su
|
|
169
|
+
* contenido: agregar o borrar una hoja de estilos cambia qué variantes
|
|
170
|
+
* declara una página, y eso tiene que reflejarse sin reiniciar. Los cambios
|
|
171
|
+
* de contenido los maneja la recarga en caliente de Vite.
|
|
172
|
+
*/
|
|
173
|
+
configureServer(server) {
|
|
174
|
+
const declaresVariants = (file) => file.includes('app/pages/') || file.includes('app\\pages\\');
|
|
175
|
+
const rebuild = (file) => {
|
|
176
|
+
if (declaresVariants(file))
|
|
177
|
+
void refreshVariantCatalog();
|
|
178
|
+
};
|
|
179
|
+
server.watcher?.on('add', rebuild);
|
|
180
|
+
server.watcher?.on('unlink', rebuild);
|
|
181
|
+
// Las dos superficies de configuración. Observar sólo `ress.config` dejaba
|
|
182
|
+
// mudo el cambio de una capa de `config/`.
|
|
183
|
+
if (config) {
|
|
184
|
+
(0, config_2.watchConfig)(server, {
|
|
185
|
+
root,
|
|
186
|
+
mode: server.config?.mode ?? 'development',
|
|
187
|
+
current: () => config,
|
|
188
|
+
onReload: (next) => {
|
|
189
|
+
config = next;
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RouteManifestData } from '../routes/types';
|
|
2
|
+
export interface BuildOutputChunk {
|
|
3
|
+
type: 'chunk';
|
|
4
|
+
fileName: string;
|
|
5
|
+
facadeModuleId?: string | null;
|
|
6
|
+
isEntry?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface BuildOutputAsset {
|
|
9
|
+
type: 'asset';
|
|
10
|
+
fileName: string;
|
|
11
|
+
}
|
|
12
|
+
export type BuildOutput = BuildOutputChunk | BuildOutputAsset;
|
|
13
|
+
export type BuildOutputBundle = Record<string, BuildOutput>;
|
|
14
|
+
/**
|
|
15
|
+
* Completa el manifest con los nombres que el bundler emitió realmente.
|
|
16
|
+
* `facadeModuleId` une cada chunk de entrada con su fuente sin adivinar reglas
|
|
17
|
+
* de nombres ni depender de cómo Rolldown sanee segmentos dinámicos.
|
|
18
|
+
*/
|
|
19
|
+
export declare function attachServerBuildChunks(data: RouteManifestData, bundle: BuildOutputBundle, root: string): RouteManifestData;
|
|
@@ -0,0 +1,55 @@
|
|
|
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.attachServerBuildChunks = attachServerBuildChunks;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
/**
|
|
9
|
+
* Completa el manifest con los nombres que el bundler emitió realmente.
|
|
10
|
+
* `facadeModuleId` une cada chunk de entrada con su fuente sin adivinar reglas
|
|
11
|
+
* de nombres ni depender de cómo Rolldown sanee segmentos dinámicos.
|
|
12
|
+
*/
|
|
13
|
+
function attachServerBuildChunks(data, bundle, root) {
|
|
14
|
+
const chunksBySource = new Map();
|
|
15
|
+
for (const output of Object.values(bundle)) {
|
|
16
|
+
if (output.type !== 'chunk' || !output.isEntry || !output.facadeModuleId)
|
|
17
|
+
continue;
|
|
18
|
+
const source = normalizeAbsolute(output.facadeModuleId, root);
|
|
19
|
+
chunksBySource.set(source, portableRelativeChunk(output.fileName));
|
|
20
|
+
}
|
|
21
|
+
const entries = data.entries.map((entry) => {
|
|
22
|
+
const source = normalizeAbsolute(entry.sourceFile, root);
|
|
23
|
+
const buildChunk = chunksBySource.get(source);
|
|
24
|
+
if (!buildChunk) {
|
|
25
|
+
throw new Error(`[ress] El build SSR no emitió un chunk de entrada para "${entry.sourceFile}" ` +
|
|
26
|
+
`(pageId "${entry.pageId}").`);
|
|
27
|
+
}
|
|
28
|
+
return { ...entry, server: { ...entry.server, buildChunk } };
|
|
29
|
+
});
|
|
30
|
+
// Los middlewares, igual que las páginas: producción los carga por el chunk
|
|
31
|
+
// registrado, sin volver a mirar las fuentes. Si el build no emitió uno, el
|
|
32
|
+
// manifest no se publica: un middleware sin chunk dejaría su ruta abierta.
|
|
33
|
+
const middlewares = data.middlewares?.map((middleware) => {
|
|
34
|
+
const buildChunk = chunksBySource.get(normalizeAbsolute(middleware.file, root));
|
|
35
|
+
if (!buildChunk) {
|
|
36
|
+
throw new Error(`[ress] El build SSR no emitió un chunk de entrada para el middleware "${middleware.file}".`);
|
|
37
|
+
}
|
|
38
|
+
return { ...middleware, server: { ...middleware.server, buildChunk } };
|
|
39
|
+
});
|
|
40
|
+
return { ...data, entries, ...(middlewares ? { middlewares } : {}) };
|
|
41
|
+
}
|
|
42
|
+
function normalizeAbsolute(file, root) {
|
|
43
|
+
const clean = file.replace(/[?#].*$/, '');
|
|
44
|
+
return node_path_1.default.resolve(root, clean).replace(/\\/g, '/');
|
|
45
|
+
}
|
|
46
|
+
function portableRelativeChunk(fileName) {
|
|
47
|
+
const normalized = fileName.replace(/\\/g, '/');
|
|
48
|
+
if (normalized.startsWith('/') ||
|
|
49
|
+
/^[A-Za-z]:\//.test(normalized) ||
|
|
50
|
+
normalized === '..' ||
|
|
51
|
+
normalized.startsWith('../')) {
|
|
52
|
+
throw new Error(`[ress] El build SSR produjo un nombre de chunk no portable: "${fileName}".`);
|
|
53
|
+
}
|
|
54
|
+
return normalized;
|
|
55
|
+
}
|
|
@@ -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__ || {};
|
|
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,55 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
+
import type { ModuleLoader } from './runtime/module-loader';
|
|
3
|
+
import type { ClientPropsPolicy } from './security';
|
|
4
|
+
import { type CollectedHeadTag } from '@ressjs/assets';
|
|
2
5
|
/**
|
|
3
6
|
* RENDER MIDDLEWARE PARA EL ROUTER
|
|
7
|
+
*
|
|
8
|
+
* El render server se arma una sola vez por ruta, en la primera petición, y
|
|
9
|
+
* se reusa en las siguientes. Antes se creaba en cada `processRequest` sin
|
|
10
|
+
* `renderServer`, y `createRenderServer` barría `app/pages` con `fast-glob`
|
|
11
|
+
* en cada request: cien peticiones, cien globs síncronos contra el
|
|
12
|
+
* filesystem en producción.
|
|
13
|
+
*
|
|
14
|
+
* La primera petición espera a que `viteConfig` (que puede llegar como thunk
|
|
15
|
+
* en modo automático) esté resuelto, así que armar el render server recién
|
|
16
|
+
* ahí, y no al declarar el middleware, es lo que garantiza que ya tenga el
|
|
17
|
+
* `moduleLoader` correcto.
|
|
4
18
|
*/
|
|
5
19
|
export declare function createRenderMiddleware(page: any, options: any): RequestHandler;
|
|
6
20
|
export interface RenderOptions {
|
|
7
21
|
url: string;
|
|
22
|
+
/**
|
|
23
|
+
* La página que el despachador ya resolvió. Obligatoria.
|
|
24
|
+
*
|
|
25
|
+
* Antes era opcional y, sin ella, el render la buscaba por URL con un
|
|
26
|
+
* criterio propio que no conoce grupos ni catch-all, escaneando `app/pages`
|
|
27
|
+
* en cada petición. Quien llamaba a `createRenderServer()` sólo con `url`
|
|
28
|
+
* tiene que pasar la página resuelta: `{ url, page: { route, file } }`.
|
|
29
|
+
*/
|
|
30
|
+
page: {
|
|
31
|
+
route: string;
|
|
32
|
+
file: string;
|
|
33
|
+
pageId?: string;
|
|
34
|
+
abs?: string;
|
|
35
|
+
};
|
|
8
36
|
props?: any;
|
|
37
|
+
/** Estado que dejaron los middlewares. Sólo viaja al cliente lo que la política declara. */
|
|
38
|
+
locals?: Record<string, unknown>;
|
|
39
|
+
/** `false`: la página no hidrata, así que no hay estado que calcular para el cliente. */
|
|
40
|
+
hydrate?: boolean;
|
|
9
41
|
}
|
|
10
42
|
export interface CreateRenderServerOptions {
|
|
11
|
-
|
|
43
|
+
/** Cargador de módulos. Ausente en producción: se importa el artefacto compilado. */
|
|
44
|
+
moduleLoader?: ModuleLoader;
|
|
45
|
+
/** Qué claves del estado del servidor viajan al cliente. */
|
|
46
|
+
clientProps?: ClientPropsPolicy;
|
|
47
|
+
isProduction?: boolean;
|
|
12
48
|
}
|
|
13
49
|
export declare function createRenderServer(options?: CreateRenderServerOptions): (renderOptions: RenderOptions) => Promise<{
|
|
14
50
|
html: string;
|
|
15
|
-
props:
|
|
51
|
+
props: Record<string, unknown>;
|
|
52
|
+
head: CollectedHeadTag[];
|
|
16
53
|
route: string;
|
|
17
54
|
page: string;
|
|
18
55
|
}>;
|