@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,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDevAssetIndex = createDevAssetIndex;
|
|
4
|
+
exports.createProdAssetIndex = createProdAssetIndex;
|
|
5
|
+
const catalog_1 = require("./catalog");
|
|
6
|
+
/**
|
|
7
|
+
* Artefactos en desarrollo.
|
|
8
|
+
*
|
|
9
|
+
* Vite sirve el módulo virtual directamente, así que el guion es su propia URL.
|
|
10
|
+
* Los estilos no se listan porque el módulo los importa y Vite los inyecta.
|
|
11
|
+
*/
|
|
12
|
+
function createDevAssetIndex(catalog, base = '/') {
|
|
13
|
+
return {
|
|
14
|
+
find(pageId, variantSuffix) {
|
|
15
|
+
const variants = catalog.variantsByPage.get(pageId);
|
|
16
|
+
if (!variants?.some((v) => v.suffix === variantSuffix))
|
|
17
|
+
return undefined;
|
|
18
|
+
// El prefijo lo agrega el framework, no Vite: el pipeline de petición lo
|
|
19
|
+
// quita antes de que Vite vea la URL, así que su propio `base` es `/`.
|
|
20
|
+
return { js: joinBase(base, '@id/' + (0, catalog_1.virtualModuleIdFor)(pageId, variantSuffix)), css: [] };
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Artefactos en producción, tomados del manifest del cliente.
|
|
26
|
+
*
|
|
27
|
+
* El manifest se lee una vez al arrancar y se pasa a un índice por página y
|
|
28
|
+
* variante, de modo que servir una petición sea una consulta en memoria y no una
|
|
29
|
+
* búsqueda.
|
|
30
|
+
*/
|
|
31
|
+
function createProdAssetIndex(catalog, manifest, base = '/') {
|
|
32
|
+
const byPageAndVariant = new Map();
|
|
33
|
+
// El manifest indexa por el identificador de la entrada, que para un módulo
|
|
34
|
+
// virtual conserva el prefijo con el que se declaró.
|
|
35
|
+
const byEntryName = new Map();
|
|
36
|
+
for (const chunk of Object.values(manifest)) {
|
|
37
|
+
if (chunk.name)
|
|
38
|
+
byEntryName.set(chunk.name, chunk);
|
|
39
|
+
}
|
|
40
|
+
for (const entry of catalog.buildEntries) {
|
|
41
|
+
const entryName = (0, catalog_1.buildEntryNameFor)(entry.pageId, entry.variant.suffix);
|
|
42
|
+
const chunk = manifest[entry.virtualId] ??
|
|
43
|
+
manifest[NULL_BYTE + entry.virtualId] ??
|
|
44
|
+
byEntryName.get(entryName) ??
|
|
45
|
+
findByFilename(manifest, entryName);
|
|
46
|
+
if (!chunk)
|
|
47
|
+
continue;
|
|
48
|
+
byPageAndVariant.set(key(entry.pageId, entry.variant.suffix), {
|
|
49
|
+
js: joinBase(base, chunk.file),
|
|
50
|
+
css: (chunk.css ?? []).map((f) => joinBase(base, f)),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return { find: (pageId, variantSuffix) => byPageAndVariant.get(key(pageId, variantSuffix)) };
|
|
54
|
+
}
|
|
55
|
+
/** Prefijo con el que los empaquetadores marcan un módulo virtual resuelto. */
|
|
56
|
+
const NULL_BYTE = String.fromCharCode(0);
|
|
57
|
+
/**
|
|
58
|
+
* Respaldo por nombre de archivo.
|
|
59
|
+
*
|
|
60
|
+
* Los identificadores virtuales pueden aparecer saneados en el manifest según
|
|
61
|
+
* cómo el empaquetador normalice los caracteres del query, así que se busca
|
|
62
|
+
* además el artefacto cuyo nombre empiece por el de la entrada.
|
|
63
|
+
*/
|
|
64
|
+
function findByFilename(manifest, entryName) {
|
|
65
|
+
const target = entryName.replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
66
|
+
return Object.values(manifest).find((c) => {
|
|
67
|
+
const file = c.file.split('/').pop() ?? '';
|
|
68
|
+
return file.startsWith(target + '-') || file.startsWith(target + '.');
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const key = (pageId, variantSuffix) => `${pageId} ${variantSuffix}`;
|
|
72
|
+
function joinBase(base, file) {
|
|
73
|
+
// `/` ya es el prefijo absoluto; concatenarlo como `b + file` producía
|
|
74
|
+
// `//assets/...`, que el navegador interpreta como un host distinto. El
|
|
75
|
+
// mismo error rompía en desarrollo la URL `//@id/ress:entry...`.
|
|
76
|
+
const prefix = base === '/' ? '' : base.endsWith('/') ? base.slice(0, -1) : base;
|
|
77
|
+
return `${prefix}/${file.replace(/^\/+/, '')}`;
|
|
78
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PageInfo } from '../pages';
|
|
2
|
+
import type { VariantCatalog, PlatformRegistry } from './types';
|
|
3
|
+
export interface CatalogOptions {
|
|
4
|
+
pages: PageInfo[];
|
|
5
|
+
registry?: PlatformRegistry;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Identificador estable de una página, compartido con el manifest de rutas.
|
|
9
|
+
*
|
|
10
|
+
* Los separadores y los corchetes de parámetro se sanean porque el identificador
|
|
11
|
+
* termina siendo el nombre de una entrada de build.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pageIdFromFile(pageFile: string): string;
|
|
14
|
+
/** Identificador del módulo virtual que hidrata esta página en esta variante. */
|
|
15
|
+
export declare function virtualModuleIdFor(pageId: string, suffix: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Nombre de la entrada de build de una página en una variante.
|
|
18
|
+
*
|
|
19
|
+
* El sufijo se une con `--` y sus puntos internos se vuelven guiones porque el
|
|
20
|
+
* empaquetador interpreta el ultimo punto del nombre de una entrada como su
|
|
21
|
+
* extension: con `page1__index.webview.android`, el artefacto de estilos sale
|
|
22
|
+
* como `page1__index.webview.css` y pierde el eje que lo distingue.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildEntryNameFor(pageId: string, suffix: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Arma el catálogo: recorre las páginas y averigua qué variantes declara cada una.
|
|
27
|
+
*
|
|
28
|
+
* Corre **una sola vez** —al arrancar el servidor de desarrollo y al construir— y
|
|
29
|
+
* nunca durante una petición. Qué variantes existen lo deciden los archivos del
|
|
30
|
+
* proyecto, así que dos ejecuciones sobre el mismo árbol producen el mismo
|
|
31
|
+
* catálogo, y el build no depende de qué clientes visitaron el sitio antes.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildVariantCatalog(opts: CatalogOptions): Promise<VariantCatalog>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pageIdFromFile = pageIdFromFile;
|
|
4
|
+
exports.virtualModuleIdFor = virtualModuleIdFor;
|
|
5
|
+
exports.buildEntryNameFor = buildEntryNameFor;
|
|
6
|
+
exports.buildVariantCatalog = buildVariantCatalog;
|
|
7
|
+
const module_extensions_1 = require("../fs/module-extensions");
|
|
8
|
+
const platform_tokens_1 = require("./platform-tokens");
|
|
9
|
+
const assets_1 = require("@ressjs/assets");
|
|
10
|
+
/**
|
|
11
|
+
* Identificador estable de una página, compartido con el manifest de rutas.
|
|
12
|
+
*
|
|
13
|
+
* Los separadores y los corchetes de parámetro se sanean porque el identificador
|
|
14
|
+
* termina siendo el nombre de una entrada de build.
|
|
15
|
+
*/
|
|
16
|
+
function pageIdFromFile(pageFile) {
|
|
17
|
+
return pageFile
|
|
18
|
+
.replace(/^app\/pages\//, '')
|
|
19
|
+
.replace(/\.[^./]+$/, '')
|
|
20
|
+
.replace(/\//g, '__')
|
|
21
|
+
.replace(/\[([^\]]+)\]/g, '_$1_');
|
|
22
|
+
}
|
|
23
|
+
/** Identificador del módulo virtual que hidrata esta página en esta variante. */
|
|
24
|
+
function virtualModuleIdFor(pageId, suffix) {
|
|
25
|
+
return `ress:entry?page=${pageId}&variant=${encodeURIComponent(suffix)}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Nombre de la entrada de build de una página en una variante.
|
|
29
|
+
*
|
|
30
|
+
* El sufijo se une con `--` y sus puntos internos se vuelven guiones porque el
|
|
31
|
+
* empaquetador interpreta el ultimo punto del nombre de una entrada como su
|
|
32
|
+
* extension: con `page1__index.webview.android`, el artefacto de estilos sale
|
|
33
|
+
* como `page1__index.webview.css` y pierde el eje que lo distingue.
|
|
34
|
+
*/
|
|
35
|
+
function buildEntryNameFor(pageId, suffix) {
|
|
36
|
+
return suffix ? `${pageId}--${suffix.replace(/\./g, '-')}` : pageId;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Arma el catálogo: recorre las páginas y averigua qué variantes declara cada una.
|
|
40
|
+
*
|
|
41
|
+
* Corre **una sola vez** —al arrancar el servidor de desarrollo y al construir— y
|
|
42
|
+
* nunca durante una petición. Qué variantes existen lo deciden los archivos del
|
|
43
|
+
* proyecto, así que dos ejecuciones sobre el mismo árbol producen el mismo
|
|
44
|
+
* catálogo, y el build no depende de qué clientes visitaron el sitio antes.
|
|
45
|
+
*/
|
|
46
|
+
async function buildVariantCatalog(opts) {
|
|
47
|
+
const registry = opts.registry ?? platform_tokens_1.defaultPlatformRegistry;
|
|
48
|
+
const buildEntries = [];
|
|
49
|
+
const variantsByPage = new Map();
|
|
50
|
+
const pagesById = new Map();
|
|
51
|
+
for (const page of opts.pages) {
|
|
52
|
+
const pageId = pageIdFromFile(page.file);
|
|
53
|
+
// La extensión la recorta el módulo canónico: los archivos de estilos de una
|
|
54
|
+
// página `.jsx` se descubren igual que los de una `.tsx`.
|
|
55
|
+
const base = (0, module_extensions_1.stripScriptExtension)(page.file);
|
|
56
|
+
const variants = (await (0, assets_1.discoverStyleVariants)(base, registry)).map((variant) => ({
|
|
57
|
+
suffix: variant.suffix,
|
|
58
|
+
tags: variant.tags,
|
|
59
|
+
styleFile: variant.sourceFile,
|
|
60
|
+
}));
|
|
61
|
+
variantsByPage.set(pageId, variants);
|
|
62
|
+
pagesById.set(pageId, { route: page.route, pageFile: page.file });
|
|
63
|
+
for (const variant of variants) {
|
|
64
|
+
buildEntries.push({
|
|
65
|
+
pageId,
|
|
66
|
+
route: page.route,
|
|
67
|
+
pageFile: page.file,
|
|
68
|
+
variant,
|
|
69
|
+
virtualId: virtualModuleIdFor(pageId, variant.suffix),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Orden estable: el resultado no debe depender de cómo el sistema de archivos
|
|
74
|
+
// devuelva los nombres.
|
|
75
|
+
buildEntries.sort((a, b) => a.pageId === b.pageId
|
|
76
|
+
? a.variant.suffix.localeCompare(b.variant.suffix)
|
|
77
|
+
: a.pageId.localeCompare(b.pageId));
|
|
78
|
+
return { buildEntries, variantsByPage, pagesById };
|
|
79
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qué tokens de plataforma son válidos y a qué eje pertenece cada uno.
|
|
3
|
+
*
|
|
4
|
+
* El vocabulario vive en `@ressjs/platform`, que no depende del router: es la
|
|
5
|
+
* misma autoridad que consultan el puente del WebView, el cliente y la
|
|
6
|
+
* detección por petición. Tener una lista propia acá significaría que el router
|
|
7
|
+
* puede discrepar con el resto del framework sobre qué es una plataforma.
|
|
8
|
+
*/
|
|
9
|
+
export { createPlatformRegistry, defaultPlatformRegistry, DEFAULT_AXIS_PRECEDENCE as AXIS_PRECEDENCE, } from '@ressjs/platform';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AXIS_PRECEDENCE = exports.defaultPlatformRegistry = exports.createPlatformRegistry = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Qué tokens de plataforma son válidos y a qué eje pertenece cada uno.
|
|
6
|
+
*
|
|
7
|
+
* El vocabulario vive en `@ressjs/platform`, que no depende del router: es la
|
|
8
|
+
* misma autoridad que consultan el puente del WebView, el cliente y la
|
|
9
|
+
* detección por petición. Tener una lista propia acá significaría que el router
|
|
10
|
+
* puede discrepar con el resto del framework sobre qué es una plataforma.
|
|
11
|
+
*/
|
|
12
|
+
var platform_1 = require("@ressjs/platform");
|
|
13
|
+
Object.defineProperty(exports, "createPlatformRegistry", { enumerable: true, get: function () { return platform_1.createPlatformRegistry; } });
|
|
14
|
+
Object.defineProperty(exports, "defaultPlatformRegistry", { enumerable: true, get: function () { return platform_1.defaultPlatformRegistry; } });
|
|
15
|
+
Object.defineProperty(exports, "AXIS_PRECEDENCE", { enumerable: true, get: function () { return platform_1.DEFAULT_AXIS_PRECEDENCE; } });
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PlatformInfo } from '../platform';
|
|
2
|
+
import type { Axis, PlatformRegistry, Variant } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* De todas las variantes que una página declara, cuál le toca a esta plataforma.
|
|
5
|
+
*
|
|
6
|
+
* Gana la que describe la situación con más precisión, entre las que le aplican.
|
|
7
|
+
* Para un teléfono Android dentro de un WebView, con las variantes `base`,
|
|
8
|
+
* `mobile` y `webview.android` declaradas, gana `webview.android`: las tres
|
|
9
|
+
* aplican, y esa es la que dice más sobre el cliente.
|
|
10
|
+
*
|
|
11
|
+
* Es la **única** implementación de esta decisión en el framework: la usan tanto
|
|
12
|
+
* el servidor por petición como el build. Que sea una sola es lo que impide que
|
|
13
|
+
* se construya una variante con un nombre y se busque con otro.
|
|
14
|
+
*
|
|
15
|
+
* El criterio es de especificidad y no una lista de prioridades fija, porque una
|
|
16
|
+
* lista no escala: agregar televisores o escritorio nativo obligaría a
|
|
17
|
+
* reescribirla entera y a decidir a mano dónde entra cada combinación nueva.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveVariant(platform: PlatformInfo, available: Variant[], registry: PlatformRegistry): Variant;
|
|
20
|
+
/**
|
|
21
|
+
* Devuelve la resolución con su explicación, para el diagnóstico de F-034 y el
|
|
22
|
+
* explorador de F-035. Un resultado inesperado sin el porqué deja en el mismo
|
|
23
|
+
* lugar que no tener nada.
|
|
24
|
+
*/
|
|
25
|
+
export declare function explainResolution(platform: PlatformInfo, available: Variant[], registry: PlatformRegistry): {
|
|
26
|
+
selected: Variant;
|
|
27
|
+
candidates: Array<{
|
|
28
|
+
suffix: string;
|
|
29
|
+
applies: boolean;
|
|
30
|
+
specificity: number;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Por qué ejes varía una página.
|
|
35
|
+
*
|
|
36
|
+
* Es lo que su respuesta declara en `Vary`: si todas sus variantes son la base,
|
|
37
|
+
* el documento es el mismo para cualquier cliente y no varía por nada. Si sólo
|
|
38
|
+
* distingue televisores, varía por el dispositivo y por nada más.
|
|
39
|
+
*/
|
|
40
|
+
export declare function varyingAxes(available: readonly Variant[]): Axis[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveVariant = resolveVariant;
|
|
4
|
+
exports.explainResolution = explainResolution;
|
|
5
|
+
exports.varyingAxes = varyingAxes;
|
|
6
|
+
/**
|
|
7
|
+
* De todas las variantes que una página declara, cuál le toca a esta plataforma.
|
|
8
|
+
*
|
|
9
|
+
* Gana la que describe la situación con más precisión, entre las que le aplican.
|
|
10
|
+
* Para un teléfono Android dentro de un WebView, con las variantes `base`,
|
|
11
|
+
* `mobile` y `webview.android` declaradas, gana `webview.android`: las tres
|
|
12
|
+
* aplican, y esa es la que dice más sobre el cliente.
|
|
13
|
+
*
|
|
14
|
+
* Es la **única** implementación de esta decisión en el framework: la usan tanto
|
|
15
|
+
* el servidor por petición como el build. Que sea una sola es lo que impide que
|
|
16
|
+
* se construya una variante con un nombre y se busque con otro.
|
|
17
|
+
*
|
|
18
|
+
* El criterio es de especificidad y no una lista de prioridades fija, porque una
|
|
19
|
+
* lista no escala: agregar televisores o escritorio nativo obligaría a
|
|
20
|
+
* reescribirla entera y a decidir a mano dónde entra cada combinación nueva.
|
|
21
|
+
*/
|
|
22
|
+
function resolveVariant(platform, available, registry) {
|
|
23
|
+
const base = available.find((v) => v.suffix === '');
|
|
24
|
+
// 1. Aplican las variantes cuyos tags matchean todos. La base no tiene tags,
|
|
25
|
+
// así que aplica siempre.
|
|
26
|
+
const applicable = available.filter((v) => v.tags.every((tag) => registry.matches(tag, platform)));
|
|
27
|
+
if (applicable.length === 0) {
|
|
28
|
+
if (!base) {
|
|
29
|
+
throw new Error('[ress] Ninguna variante aplica y no hay variante base. ' +
|
|
30
|
+
'Toda página necesita una variante sin sufijo como respaldo.');
|
|
31
|
+
}
|
|
32
|
+
return base;
|
|
33
|
+
}
|
|
34
|
+
// 2. Gana la que describe la situación más precisa: más tags, más específica.
|
|
35
|
+
// 3. A igual cantidad, decide el eje más significativo que cada una declara.
|
|
36
|
+
return applicable.reduce((best, candidate) => compare(candidate, best, registry) > 0 ? candidate : best);
|
|
37
|
+
}
|
|
38
|
+
/** Positivo si `a` es más específica que `b`. */
|
|
39
|
+
function compare(a, b, registry) {
|
|
40
|
+
if (a.tags.length !== b.tags.length)
|
|
41
|
+
return a.tags.length - b.tags.length;
|
|
42
|
+
// Empate de especificidad: gana la que declara el eje más significativo.
|
|
43
|
+
// `category` cambia la página más profundamente que `device`, porque un
|
|
44
|
+
// WebView embebido difiere de un navegador más de lo que un teléfono difiere
|
|
45
|
+
// de una tablet.
|
|
46
|
+
const rankA = topAxisRank(a, registry);
|
|
47
|
+
const rankB = topAxisRank(b, registry);
|
|
48
|
+
if (rankA !== rankB)
|
|
49
|
+
return rankB - rankA;
|
|
50
|
+
// Sin criterio que las distinga, el orden alfabético del sufijo mantiene el
|
|
51
|
+
// resultado estable entre ejecuciones.
|
|
52
|
+
return b.suffix.localeCompare(a.suffix);
|
|
53
|
+
}
|
|
54
|
+
/** Posición del eje más significativo que declara la variante. Menor es mejor. */
|
|
55
|
+
function topAxisRank(v, registry) {
|
|
56
|
+
if (v.tags.length === 0)
|
|
57
|
+
return registry.axisPrecedence.length;
|
|
58
|
+
return Math.min(...v.tags.map((t) => registry.axisPrecedence.indexOf(t.axis)));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Devuelve la resolución con su explicación, para el diagnóstico de F-034 y el
|
|
62
|
+
* explorador de F-035. Un resultado inesperado sin el porqué deja en el mismo
|
|
63
|
+
* lugar que no tener nada.
|
|
64
|
+
*/
|
|
65
|
+
function explainResolution(platform, available, registry) {
|
|
66
|
+
const selected = resolveVariant(platform, available, registry);
|
|
67
|
+
return {
|
|
68
|
+
selected,
|
|
69
|
+
candidates: available.map((v) => ({
|
|
70
|
+
suffix: v.suffix,
|
|
71
|
+
applies: v.tags.every((tag) => registry.matches(tag, platform)),
|
|
72
|
+
specificity: v.tags.length,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Por qué ejes varía una página.
|
|
78
|
+
*
|
|
79
|
+
* Es lo que su respuesta declara en `Vary`: si todas sus variantes son la base,
|
|
80
|
+
* el documento es el mismo para cualquier cliente y no varía por nada. Si sólo
|
|
81
|
+
* distingue televisores, varía por el dispositivo y por nada más.
|
|
82
|
+
*/
|
|
83
|
+
function varyingAxes(available) {
|
|
84
|
+
const axes = new Set();
|
|
85
|
+
for (const variant of available) {
|
|
86
|
+
for (const tag of variant.tags)
|
|
87
|
+
axes.add(tag.axis);
|
|
88
|
+
}
|
|
89
|
+
return [...axes];
|
|
90
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convierte el sufijo de un archivo de variante en tags clasificados.
|
|
3
|
+
*
|
|
4
|
+
* La implementación vive en `@ressjs/platform` junto al registro que la
|
|
5
|
+
* alimenta: separarlas dejaría que el parseo aceptara tokens que el registro no
|
|
6
|
+
* reconoce, o al revés.
|
|
7
|
+
*/
|
|
8
|
+
export { parseVariantSuffix } from '@ressjs/platform';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseVariantSuffix = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Convierte el sufijo de un archivo de variante en tags clasificados.
|
|
6
|
+
*
|
|
7
|
+
* La implementación vive en `@ressjs/platform` junto al registro que la
|
|
8
|
+
* alimenta: separarlas dejaría que el parseo aceptara tokens que el registro no
|
|
9
|
+
* reconoce, o al revés.
|
|
10
|
+
*/
|
|
11
|
+
var platform_1 = require("@ressjs/platform");
|
|
12
|
+
Object.defineProperty(exports, "parseVariantSuffix", { enumerable: true, get: function () { return platform_1.parseVariantSuffix; } });
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PlatformInfo } from '../platform';
|
|
2
|
+
/**
|
|
3
|
+
* Vocabulario de este módulo, de arriba hacia abajo:
|
|
4
|
+
*
|
|
5
|
+
* - Una **página** puede existir en varias **variantes**: versiones de la misma
|
|
6
|
+
* página dirigidas a plataformas distintas. El proyecto las declara creando
|
|
7
|
+
* archivos con un sufijo (`index.tv.scss` declara la variante `tv`).
|
|
8
|
+
* - El **catálogo** es la lista completa de páginas con sus variantes. Se arma
|
|
9
|
+
* una sola vez, al arrancar y al construir.
|
|
10
|
+
* - Cada par página + variante es una **entrada de build**, que produce su
|
|
11
|
+
* propio guion y su propia hoja de estilos.
|
|
12
|
+
* - Servir una petición es elegir una variante para la plataforma que pide, y
|
|
13
|
+
* buscar en el **índice de artefactos** qué archivos le corresponden.
|
|
14
|
+
*
|
|
15
|
+
* Qué tokens existen y cómo se decide si uno aplica a un cliente no se define
|
|
16
|
+
* acá: es el modelo de plataforma, y vive en `@ressjs/platform`.
|
|
17
|
+
*/
|
|
18
|
+
export type { Axis, PlatformRegistry, VariantTag } from '@ressjs/platform';
|
|
19
|
+
import type { VariantTag } from '@ressjs/platform';
|
|
20
|
+
/** Una versión de una página dirigida a un subconjunto de plataformas. */
|
|
21
|
+
export interface Variant {
|
|
22
|
+
/** Sufijo canónico. Cadena vacía para la base. */
|
|
23
|
+
suffix: string;
|
|
24
|
+
/** Vacío en la base, que por eso aplica siempre. */
|
|
25
|
+
tags: VariantTag[];
|
|
26
|
+
/** Archivo de estilos que la declara. Ausente si la página no tiene estilos. */
|
|
27
|
+
styleFile?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Una página en una de sus variantes: lo que el empaquetador construye. */
|
|
30
|
+
export interface BuildEntry {
|
|
31
|
+
pageId: string;
|
|
32
|
+
route: string;
|
|
33
|
+
pageFile: string;
|
|
34
|
+
variant: Variant;
|
|
35
|
+
/** Módulo virtual que el empaquetador usa como entrada de build. */
|
|
36
|
+
virtualId: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Qué variantes tiene cada página del proyecto.
|
|
40
|
+
*
|
|
41
|
+
* Es la fuente única de la que salen tanto las entradas que se construyen como
|
|
42
|
+
* las que se resuelven por petición. Que sean la misma es lo que garantiza que
|
|
43
|
+
* el servidor nunca pida un artefacto que el build no produjo.
|
|
44
|
+
*/
|
|
45
|
+
export interface VariantCatalog {
|
|
46
|
+
/** Todas las combinaciones página + variante, en orden estable. */
|
|
47
|
+
buildEntries: BuildEntry[];
|
|
48
|
+
/** Variantes de cada página, para resolver sin recorrer todo el catálogo. */
|
|
49
|
+
variantsByPage: Map<string, Variant[]>;
|
|
50
|
+
/** Datos de la página, indexados por su identificador. */
|
|
51
|
+
pagesById: Map<string, {
|
|
52
|
+
route: string;
|
|
53
|
+
pageFile: string;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Qué archivos servir para una página y una variante.
|
|
58
|
+
*
|
|
59
|
+
* Tiene la misma forma en desarrollo y en producción, de modo que quien lo
|
|
60
|
+
* consulta no necesite saber en cuál corre. Devuelve `undefined` cuando esa
|
|
61
|
+
* combinación no se construyó.
|
|
62
|
+
*/
|
|
63
|
+
export interface AssetIndex {
|
|
64
|
+
find(pageId: string, variantSuffix: string): {
|
|
65
|
+
js: string;
|
|
66
|
+
css: string[];
|
|
67
|
+
} | undefined;
|
|
68
|
+
}
|
|
69
|
+
export type { PlatformInfo };
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ressjs/vite-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-experimental.0",
|
|
4
4
|
"description": "Express router factory for ress.js with automatic page detection, SSR and dynamic routes support",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
5
|
"types": "dist/index.d.ts",
|
|
7
6
|
"publishConfig": {
|
|
8
7
|
"access": "public"
|
|
@@ -12,6 +11,16 @@
|
|
|
12
11
|
"types": "./dist/index.d.ts",
|
|
13
12
|
"require": "./dist/index.js",
|
|
14
13
|
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./plugin": {
|
|
16
|
+
"types": "./dist/plugin/index.d.ts",
|
|
17
|
+
"require": "./dist/plugin/index.js",
|
|
18
|
+
"import": "./dist/plugin/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./client": {
|
|
21
|
+
"types": "./dist/client/entry.d.mts",
|
|
22
|
+
"require": "./dist/client/index.js",
|
|
23
|
+
"import": "./dist/client/entry.mjs"
|
|
15
24
|
}
|
|
16
25
|
},
|
|
17
26
|
"files": [
|
|
@@ -36,26 +45,29 @@
|
|
|
36
45
|
"author": "ressjs team",
|
|
37
46
|
"license": "MIT",
|
|
38
47
|
"devDependencies": {
|
|
39
|
-
"@jest/globals": "^29.7.0",
|
|
40
48
|
"@types/compression": "^1.7.5",
|
|
41
49
|
"@types/express": "^5.0.3",
|
|
42
|
-
"@types/jest": "^30.0.0",
|
|
43
50
|
"@types/node": "^22.17.2",
|
|
44
51
|
"axios": "^1.11.0",
|
|
45
52
|
"express": "^5.1.0",
|
|
46
|
-
"
|
|
47
|
-
"ts-jest": "^29.4.1",
|
|
48
|
-
"typescript": "~5.7.3",
|
|
53
|
+
"typescript": "~5.9.0",
|
|
49
54
|
"vitest": "^3.2.4"
|
|
50
55
|
},
|
|
51
56
|
"dependencies": {
|
|
57
|
+
"@ressjs/assets": "0.6.0-experimental.0",
|
|
58
|
+
"@ressjs/config": "0.6.0-experimental.0",
|
|
59
|
+
"@ressjs/platform": "0.6.0-experimental.0",
|
|
52
60
|
"compression": "^1.8.0",
|
|
53
61
|
"fast-glob": "^3.3.3",
|
|
54
62
|
"sirv": "^3.0.1"
|
|
55
63
|
},
|
|
56
64
|
"peerDependencies": {
|
|
57
|
-
"vite": "^6.0.0",
|
|
58
65
|
"react": "^19.0.0",
|
|
59
|
-
"react-dom": "^19.0.0"
|
|
60
|
-
|
|
66
|
+
"react-dom": "^19.0.0",
|
|
67
|
+
"vite": "^8.0.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=20.19.0"
|
|
71
|
+
},
|
|
72
|
+
"main": "dist/index.js"
|
|
61
73
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface ViteConfig {
|
|
2
|
-
vite?: any;
|
|
3
|
-
manifest: any;
|
|
4
|
-
templateHtml: string;
|
|
5
|
-
isProduction: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function setupDev(base?: string): Promise<ViteConfig>;
|
|
8
|
-
export declare function setupProd(): Promise<ViteConfig>;
|
|
9
|
-
export declare function createProdMiddleware(): () => Promise<import("express-serve-static-core").Router>;
|
|
10
|
-
export declare function setupVite(isProduction: boolean, base?: string): Promise<ViteConfig>;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.setupDev = setupDev;
|
|
40
|
-
exports.setupProd = setupProd;
|
|
41
|
-
exports.createProdMiddleware = createProdMiddleware;
|
|
42
|
-
exports.setupVite = setupVite;
|
|
43
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
44
|
-
const express_1 = __importDefault(require("express"));
|
|
45
|
-
const pages_1 = require("../pages");
|
|
46
|
-
async function setupDev(base = '/') {
|
|
47
|
-
const vite = await Promise.resolve().then(() => __importStar(require('vite'))).then(m => m.createServer({
|
|
48
|
-
server: { middlewareMode: true },
|
|
49
|
-
appType: 'custom',
|
|
50
|
-
base
|
|
51
|
-
}));
|
|
52
|
-
let manifest = {};
|
|
53
|
-
try {
|
|
54
|
-
manifest = JSON.parse(await promises_1.default.readFile('./dist/client/.vite/manifest.json', 'utf-8'));
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// Expected in dev if no build yet
|
|
58
|
-
}
|
|
59
|
-
await (0, pages_1.generateTempEntries)();
|
|
60
|
-
const templateHtml = await promises_1.default.readFile('./index.html', 'utf-8');
|
|
61
|
-
return { vite, manifest, templateHtml, isProduction: false };
|
|
62
|
-
}
|
|
63
|
-
async function setupProd() {
|
|
64
|
-
const manifest = JSON.parse(await promises_1.default.readFile('./dist/client/.vite/manifest.json', 'utf-8'));
|
|
65
|
-
const templateHtml = await promises_1.default.readFile('./index.html', 'utf-8');
|
|
66
|
-
return {
|
|
67
|
-
vite: null,
|
|
68
|
-
manifest,
|
|
69
|
-
templateHtml,
|
|
70
|
-
isProduction: true
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function createProdMiddleware() {
|
|
74
|
-
const middlewares = express_1.default.Router();
|
|
75
|
-
return async () => {
|
|
76
|
-
const compression = (await Promise.resolve().then(() => __importStar(require('compression')))).default;
|
|
77
|
-
const sirv = (await Promise.resolve().then(() => __importStar(require('sirv')))).default;
|
|
78
|
-
middlewares.use(compression());
|
|
79
|
-
middlewares.use(sirv('./dist/client', { extensions: [] }));
|
|
80
|
-
return middlewares;
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
async function setupVite(isProduction, base = '/') {
|
|
84
|
-
if (isProduction) {
|
|
85
|
-
return await setupProd();
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
return await setupDev(base);
|
|
89
|
-
}
|
|
90
|
-
}
|