@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.
Files changed (110) hide show
  1. package/README.md +41 -31
  2. package/dist/client/entry.d.mts +2 -0
  3. package/dist/client/entry.mjs +1 -0
  4. package/dist/client/index.d.ts +2 -0
  5. package/dist/client/index.js +8 -0
  6. package/dist/config/base-path.d.ts +44 -0
  7. package/dist/config/base-path.js +100 -0
  8. package/dist/config/codegen.d.ts +23 -0
  9. package/dist/config/codegen.js +118 -0
  10. package/dist/config/define.d.ts +8 -0
  11. package/dist/config/define.js +12 -0
  12. package/dist/config/env.d.ts +19 -0
  13. package/dist/config/env.js +40 -0
  14. package/dist/config/index.d.ts +18 -0
  15. package/dist/config/index.js +44 -0
  16. package/dist/config/load.d.ts +26 -0
  17. package/dist/config/load.js +73 -0
  18. package/dist/config/middleware.d.ts +18 -0
  19. package/dist/config/middleware.js +79 -0
  20. package/dist/config/rules.d.ts +48 -0
  21. package/dist/config/rules.js +149 -0
  22. package/dist/config/types.d.ts +164 -0
  23. package/dist/config/types.js +2 -0
  24. package/dist/config/validate.d.ts +19 -0
  25. package/dist/config/validate.js +105 -0
  26. package/dist/config/watch.d.ts +41 -0
  27. package/dist/config/watch.js +132 -0
  28. package/dist/fs/module-extensions.d.ts +82 -0
  29. package/dist/fs/module-extensions.js +196 -0
  30. package/dist/helpers/html-generator.d.ts +22 -5
  31. package/dist/helpers/html-generator.js +89 -120
  32. package/dist/helpers/middlewares.d.ts +7 -7
  33. package/dist/helpers/middlewares.js +51 -103
  34. package/dist/helpers/request-handler.d.ts +2 -2
  35. package/dist/helpers/request-handler.js +118 -25
  36. package/dist/index.d.ts +30 -3
  37. package/dist/index.js +70 -8
  38. package/dist/pages.d.ts +10 -9
  39. package/dist/pages.js +42 -261
  40. package/dist/platform.d.ts +11 -14
  41. package/dist/platform.js +18 -102
  42. package/dist/plugin/environments.d.ts +66 -0
  43. package/dist/plugin/environments.js +72 -0
  44. package/dist/plugin/index.d.ts +54 -0
  45. package/dist/plugin/index.js +178 -0
  46. package/dist/plugin/virtual-entries.d.ts +26 -0
  47. package/dist/plugin/virtual-entries.js +80 -0
  48. package/dist/render.d.ts +23 -2
  49. package/dist/render.js +40 -53
  50. package/dist/router.d.ts +32 -13
  51. package/dist/router.js +147 -146
  52. package/dist/routes/dispatcher.d.ts +37 -0
  53. package/dist/routes/dispatcher.js +69 -0
  54. package/dist/routes/manifest.d.ts +17 -0
  55. package/dist/routes/manifest.js +56 -0
  56. package/dist/routes/match.d.ts +20 -0
  57. package/dist/routes/match.js +75 -0
  58. package/dist/routes/parse.d.ts +31 -0
  59. package/dist/routes/parse.js +114 -0
  60. package/dist/routes/rank.d.ts +12 -0
  61. package/dist/routes/rank.js +49 -0
  62. package/dist/routes/router.d.ts +44 -0
  63. package/dist/routes/router.js +81 -0
  64. package/dist/routes/scan.d.ts +16 -0
  65. package/dist/routes/scan.js +82 -0
  66. package/dist/routes/types.d.ts +85 -0
  67. package/dist/routes/types.js +13 -0
  68. package/dist/runtime/dev-server.d.ts +62 -0
  69. package/dist/runtime/dev-server.js +94 -0
  70. package/dist/runtime/module-loader.d.ts +55 -0
  71. package/dist/runtime/module-loader.js +122 -0
  72. package/dist/runtime/prod-server.d.ts +42 -0
  73. package/dist/runtime/prod-server.js +142 -0
  74. package/dist/runtime/template.d.ts +10 -0
  75. package/dist/runtime/template.js +33 -0
  76. package/dist/security/client-props.d.ts +25 -0
  77. package/dist/security/client-props.js +50 -0
  78. package/dist/security/config.d.ts +130 -0
  79. package/dist/security/config.js +73 -0
  80. package/dist/security/csp.d.ts +53 -0
  81. package/dist/security/csp.js +118 -0
  82. package/dist/security/dev-hardening.d.ts +46 -0
  83. package/dist/security/dev-hardening.js +65 -0
  84. package/dist/security/errors.d.ts +37 -0
  85. package/dist/security/errors.js +84 -0
  86. package/dist/security/escape.d.ts +40 -0
  87. package/dist/security/escape.js +90 -0
  88. package/dist/security/head-tags.d.ts +32 -0
  89. package/dist/security/head-tags.js +111 -0
  90. package/dist/security/headers.d.ts +62 -0
  91. package/dist/security/headers.js +187 -0
  92. package/dist/security/index.d.ts +24 -0
  93. package/dist/security/index.js +47 -0
  94. package/dist/security/serialize.d.ts +48 -0
  95. package/dist/security/serialize.js +146 -0
  96. package/dist/variants/assets.d.ts +23 -0
  97. package/dist/variants/assets.js +78 -0
  98. package/dist/variants/catalog.d.ts +33 -0
  99. package/dist/variants/catalog.js +79 -0
  100. package/dist/variants/platform-tokens.d.ts +9 -0
  101. package/dist/variants/platform-tokens.js +15 -0
  102. package/dist/variants/resolve.d.ts +40 -0
  103. package/dist/variants/resolve.js +90 -0
  104. package/dist/variants/suffix.d.ts +8 -0
  105. package/dist/variants/suffix.js +12 -0
  106. package/dist/variants/types.d.ts +69 -0
  107. package/dist/variants/types.js +2 -0
  108. package/package.json +22 -10
  109. package/dist/helpers/vite-config.d.ts +0 -10
  110. package/dist/helpers/vite-config.js +0 -90
@@ -0,0 +1,41 @@
1
+ import type { ResolvedConfig } from './types';
2
+ /**
3
+ * Recarga de la configuración en desarrollo.
4
+ *
5
+ * Se observa la carpeta `config/`: cualquiera de sus capas, no sólo la del modo
6
+ * activo, porque `default` y `local` también participan de la fusión.
7
+ */
8
+ /**
9
+ * Claves que se pueden aplicar sin reiniciar.
10
+ *
11
+ * Todas se consultan al atender una petición, así que la siguiente ya ve el
12
+ * valor nuevo.
13
+ */
14
+ export declare const HOT_KEYS: readonly ["redirects", "rewrites", "headers", "trailingSlash"];
15
+ /**
16
+ * Claves que exigen reiniciar, y por qué.
17
+ *
18
+ * El framework lo dice nombrando la clave en vez de aplicar la mitad de la
19
+ * configuración: media configuración aplicada es un estado que nadie declaró y
20
+ * que produce errores que no se corresponden con ningún archivo.
21
+ */
22
+ export declare const RESTART_REASONS: Record<string, string>;
23
+ /** Qué claves de primer nivel cambiaron entre dos configuraciones resueltas. */
24
+ export declare function changedKeys(previous: ResolvedConfig, next: ResolvedConfig): string[];
25
+ /** El motivo del reinicio, o nada si todo lo que cambió se aplica en caliente. */
26
+ export declare function restartReason(changed: readonly string[]): string | undefined;
27
+ /** ¿Este archivo es una capa de configuración del proyecto? */
28
+ export declare function isConfigLayer(file: string, root: string): boolean;
29
+ export interface WatchConfigOptions {
30
+ root: string;
31
+ mode: string;
32
+ /** La configuración vigente, para comparar contra la nueva. */
33
+ current: () => ResolvedConfig;
34
+ /** Qué hacer con la configuración nueva cuando se puede aplicar en caliente. */
35
+ onReload: (config: ResolvedConfig) => void;
36
+ /** Qué hacer cuando hace falta reiniciar. Por defecto, pedírselo a Vite. */
37
+ onRestart?: (reason: string) => void;
38
+ log?: (message: string) => void;
39
+ }
40
+ /** Conecta el observador de Vite con la carpeta de configuración. */
41
+ export declare function watchConfig(server: any, options: WatchConfigOptions): void;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESTART_REASONS = exports.HOT_KEYS = void 0;
4
+ exports.changedKeys = changedKeys;
5
+ exports.restartReason = restartReason;
6
+ exports.isConfigLayer = isConfigLayer;
7
+ exports.watchConfig = watchConfig;
8
+ const config_1 = require("@ressjs/config");
9
+ const load_1 = require("./load");
10
+ /**
11
+ * Recarga de la configuración en desarrollo.
12
+ *
13
+ * Se observa la carpeta `config/`: cualquiera de sus capas, no sólo la del modo
14
+ * activo, porque `default` y `local` también participan de la fusión.
15
+ */
16
+ /**
17
+ * Claves que se pueden aplicar sin reiniciar.
18
+ *
19
+ * Todas se consultan al atender una petición, así que la siguiente ya ve el
20
+ * valor nuevo.
21
+ */
22
+ // `security` no está acá: se carga y se valida, pero el pipeline de petición
23
+ // todavía no la lee (ver el comentario en `config/types.ts`), así que no hay
24
+ // nada que reaplicar en caliente cuando cambia.
25
+ exports.HOT_KEYS = ['redirects', 'rewrites', 'headers', 'trailingSlash'];
26
+ /**
27
+ * Claves que exigen reiniciar, y por qué.
28
+ *
29
+ * El framework lo dice nombrando la clave en vez de aplicar la mitad de la
30
+ * configuración: media configuración aplicada es un estado que nadie declaró y
31
+ * que produce errores que no se corresponden con ningún archivo.
32
+ */
33
+ exports.RESTART_REASONS = {
34
+ basePath: 'cambia la URL bajo la que se sirve todo, incluidos los artefactos ya emitidos',
35
+ extensions: 'cambia qué archivos son páginas, y eso redefine el catálogo de entradas',
36
+ platform: 'redefine los ejes de plataforma, y con ellos las variantes que se construyen',
37
+ publicEnv: 'las variables se reemplazan en tiempo de build, no al atender una petición',
38
+ assets: 'el índice de artefactos se arma una vez, con el prefijo vigente al arrancar',
39
+ dev: 'son opciones del servidor de desarrollo, que ya está escuchando',
40
+ // F-033: `beforeRouter`/`afterRouter` se montan una vez, al construir la
41
+ // aplicación de servidor. Tratar su cambio como aplicable en caliente
42
+ // dejaría un "recargado" en el log sin ningún efecto real.
43
+ server: 'los middlewares de la vía de escape se montan una sola vez, al construir el servidor',
44
+ };
45
+ /** Qué claves de primer nivel cambiaron entre dos configuraciones resueltas. */
46
+ function changedKeys(previous, next) {
47
+ const keys = new Set([...Object.keys(previous), ...Object.keys(next)]);
48
+ const changed = [];
49
+ for (const key of keys) {
50
+ const a = stable(previous[key]);
51
+ const b = stable(next[key]);
52
+ if (a !== b)
53
+ changed.push(key);
54
+ }
55
+ return changed.sort();
56
+ }
57
+ /**
58
+ * Serialización estable para comparar.
59
+ *
60
+ * Las claves de un objeto se ordenan: dos configuraciones equivalentes escritas
61
+ * en distinto orden no son un cambio, y tratarlas como tal reiniciaría el
62
+ * servidor por nada.
63
+ */
64
+ function stable(value) {
65
+ if (typeof value === 'function')
66
+ return `fn:${String(value)}`;
67
+ if (value === undefined)
68
+ return 'undefined';
69
+ return JSON.stringify(value, (_key, inner) => {
70
+ if (typeof inner === 'function')
71
+ return `fn:${String(inner)}`;
72
+ if (inner && typeof inner === 'object' && !Array.isArray(inner)) {
73
+ return Object.fromEntries(Object.entries(inner).sort(([a], [b]) => a.localeCompare(b)));
74
+ }
75
+ return inner;
76
+ });
77
+ }
78
+ /** El motivo del reinicio, o nada si todo lo que cambió se aplica en caliente. */
79
+ function restartReason(changed) {
80
+ for (const key of changed) {
81
+ const reason = exports.RESTART_REASONS[key];
82
+ if (reason)
83
+ return `\`${key}\`: ${reason}`;
84
+ }
85
+ return undefined;
86
+ }
87
+ /** ¿Este archivo es una capa de configuración del proyecto? */
88
+ function isConfigLayer(file, root) {
89
+ const normalized = file.replace(/\\/g, '/');
90
+ const base = root.replace(/\\/g, '/').replace(/\/+$/, '');
91
+ if (!normalized.startsWith(base + '/'))
92
+ return false;
93
+ const relative = normalized.slice(base.length + 1);
94
+ return /^config\/[^/]+$/.test(relative) || /^config\.[^/]+$/.test(relative);
95
+ }
96
+ /** Conecta el observador de Vite con la carpeta de configuración. */
97
+ function watchConfig(server, options) {
98
+ const log = options.log ?? ((message) => console.log(message));
99
+ const restart = options.onRestart ??
100
+ ((reason) => {
101
+ log(`[ress] Reiniciando: ${reason}`);
102
+ void server?.restart?.();
103
+ });
104
+ const onChange = async (file) => {
105
+ if (!isConfigLayer(file, options.root))
106
+ return;
107
+ // Lo resuelto se descarta entero: la fusión depende de todas las capas, así
108
+ // que un cambio en cualquiera de ellas invalida el resultado.
109
+ (0, config_1.invalidateConfig)();
110
+ let next;
111
+ try {
112
+ next = await (0, load_1.loadConfig)(options.root, options.mode);
113
+ }
114
+ catch (error) {
115
+ // Un archivo a medio escribir no tiene que voltear el servidor: se informa
116
+ // y se sigue con la configuración anterior hasta que vuelva a guardarse.
117
+ log(`[ress] La configuración no se pudo cargar: ${error.message}`);
118
+ return;
119
+ }
120
+ const changed = changedKeys(options.current(), next);
121
+ if (changed.length === 0)
122
+ return;
123
+ const reason = restartReason(changed);
124
+ if (reason)
125
+ return restart(reason);
126
+ options.onReload(next);
127
+ log(`[ress] Configuración recargada: ${changed.join(', ')}`);
128
+ };
129
+ for (const event of ['add', 'change', 'unlink']) {
130
+ server?.watcher?.on(event, (file) => void onChange(file));
131
+ }
132
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Qué extensiones acepta el framework, declarado una sola vez.
3
+ *
4
+ * Antes cada punto del pipeline tenía su propia idea: el descubrimiento globeaba
5
+ * `.tsx` y `.jsx`, el render exigía `.tsx` y lanzaba con cualquier otra cosa, y
6
+ * la carga en producción ramificaba por extensión. El resultado era que un
7
+ * proyecto en JavaScript descubría sus páginas y reventaba al renderizarlas.
8
+ *
9
+ * Ninguna función de este módulo toca el sistema de archivos: son puras sobre
10
+ * cadenas, y por eso se pueden probar sin montar un proyecto.
11
+ */
12
+ export declare const SCRIPT_EXTENSIONS: readonly [".tsx", ".ts", ".jsx", ".js"];
13
+ export type ScriptExtension = (typeof SCRIPT_EXTENSIONS)[number];
14
+ export interface ExtensionPolicy {
15
+ /** Extensiones válidas para un archivo de página. */
16
+ page: readonly ScriptExtension[];
17
+ /** Extensiones válidas para un archivo de middleware. */
18
+ middleware: readonly ScriptExtension[];
19
+ /** Extensión que produce el build. Siempre una sola. */
20
+ compiled: '.js';
21
+ /** Sufijos que se ignoran aunque la extensión coincida. */
22
+ ignoredSuffixes: readonly string[];
23
+ }
24
+ export declare const DEFAULT_EXTENSION_POLICY: ExtensionPolicy;
25
+ /** `['.tsx','.ts']` → `{tsx,ts}`, la forma que entiende un glob. */
26
+ export declare function globBrace(extensions: readonly ScriptExtension[]): string;
27
+ export declare function pageGlob(policy: ExtensionPolicy, pagesDir?: string): string;
28
+ export declare function middlewareGlobs(policy: ExtensionPolicy, pagesDir?: string): string[];
29
+ /**
30
+ * La extensión de un archivo, si es una de las que el framework reconoce.
31
+ *
32
+ * Se compara en minúsculas: en macOS y Windows un `Index.TSX` es el mismo
33
+ * archivo, y rechazarlo por la capitalización sería un fallo incomprensible.
34
+ */
35
+ export declare function scriptExtensionOf(file: string): ScriptExtension | undefined;
36
+ export declare function isIgnoredModule(file: string, policy?: ExtensionPolicy): boolean;
37
+ /**
38
+ * El archivo sin su extensión.
39
+ *
40
+ * Lanza en vez de devolver el path intacto: un archivo que llegó hasta acá sin
41
+ * una extensión reconocida es un error de configuración, y devolverlo tal cual
42
+ * lo convertiría en un fallo más adelante y más difícil de ubicar.
43
+ */
44
+ export declare function stripScriptExtension(file: string): string;
45
+ export declare function isPageFile(file: string, policy?: ExtensionPolicy): boolean;
46
+ /**
47
+ * La ruta que un archivo de middleware protege.
48
+ *
49
+ * `pokedex/middlewares.tsx` protege `/pokedex`; `home/index.middlewares.js`
50
+ * protege `/home/index`. Antes esto se derivaba con una expresión que sólo
51
+ * recortaba `.ts` y `.js`, así que un middleware `.tsx` producía una ruta con la
52
+ * extensión adentro y no coincidía con ninguna página: no corría, y sin aviso.
53
+ */
54
+ export declare function middlewareRoute(file: string, pagesDir?: string): string;
55
+ export declare function isMiddlewareFile(file: string, policy?: ExtensionPolicy): boolean;
56
+ /**
57
+ * Identificador de una página, sin su extensión ni el prefijo del directorio.
58
+ *
59
+ * Es la clave que comparten el catálogo de variantes y el manifest de rutas, así
60
+ * que dos archivos que sólo difieren en la extensión producen el mismo: es lo
61
+ * que permite detectar que ambos existen.
62
+ */
63
+ export declare function pageIdOf(file: string, pagesDir?: string): string;
64
+ /**
65
+ * Dónde queda un módulo después de compilarse.
66
+ *
67
+ * El build emite siempre `.js`, cualquiera sea la extensión de origen, y sanea
68
+ * los corchetes de los parámetros porque no pueden formar parte de un nombre de
69
+ * archivo en todos los sistemas.
70
+ */
71
+ export declare function toCompiledPath(file: string, policy?: ExtensionPolicy, pagesDir?: string): string;
72
+ /**
73
+ * Dos archivos que producen la misma página son ambiguos.
74
+ *
75
+ * Cuál gana dependería del orden en que el sistema de archivos los devolvió, y
76
+ * ese orden cambia entre sistemas. Mejor fallar al arrancar nombrando los dos.
77
+ */
78
+ export declare function assertNoModuleCollision(files: readonly string[], pagesDir?: string, options?: {
79
+ strict?: boolean;
80
+ }): void;
81
+ export declare function setExtensionPolicy(policy: ExtensionPolicy): void;
82
+ export declare function getExtensionPolicy(): ExtensionPolicy;
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * Qué extensiones acepta el framework, declarado una sola vez.
4
+ *
5
+ * Antes cada punto del pipeline tenía su propia idea: el descubrimiento globeaba
6
+ * `.tsx` y `.jsx`, el render exigía `.tsx` y lanzaba con cualquier otra cosa, y
7
+ * la carga en producción ramificaba por extensión. El resultado era que un
8
+ * proyecto en JavaScript descubría sus páginas y reventaba al renderizarlas.
9
+ *
10
+ * Ninguna función de este módulo toca el sistema de archivos: son puras sobre
11
+ * cadenas, y por eso se pueden probar sin montar un proyecto.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.DEFAULT_EXTENSION_POLICY = exports.SCRIPT_EXTENSIONS = void 0;
15
+ exports.globBrace = globBrace;
16
+ exports.pageGlob = pageGlob;
17
+ exports.middlewareGlobs = middlewareGlobs;
18
+ exports.scriptExtensionOf = scriptExtensionOf;
19
+ exports.isIgnoredModule = isIgnoredModule;
20
+ exports.stripScriptExtension = stripScriptExtension;
21
+ exports.isPageFile = isPageFile;
22
+ exports.middlewareRoute = middlewareRoute;
23
+ exports.isMiddlewareFile = isMiddlewareFile;
24
+ exports.pageIdOf = pageIdOf;
25
+ exports.toCompiledPath = toCompiledPath;
26
+ exports.assertNoModuleCollision = assertNoModuleCollision;
27
+ exports.setExtensionPolicy = setExtensionPolicy;
28
+ exports.getExtensionPolicy = getExtensionPolicy;
29
+ exports.SCRIPT_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];
30
+ exports.DEFAULT_EXTENSION_POLICY = {
31
+ page: exports.SCRIPT_EXTENSIONS,
32
+ middleware: exports.SCRIPT_EXTENSIONS,
33
+ compiled: '.js',
34
+ // Un `.d.ts` declara tipos: nunca es un módulo que se pueda ejecutar.
35
+ ignoredSuffixes: ['.d.ts'],
36
+ };
37
+ /** `['.tsx','.ts']` → `{tsx,ts}`, la forma que entiende un glob. */
38
+ function globBrace(extensions) {
39
+ if (extensions.length === 0) {
40
+ throw new Error('[ress] La política de extensiones quedó vacía. ' +
41
+ `Declará al menos una de: ${exports.SCRIPT_EXTENSIONS.join(', ')}.`);
42
+ }
43
+ const bare = extensions.map((e) => e.slice(1));
44
+ return bare.length === 1 ? bare[0] : `{${bare.join(',')}}`;
45
+ }
46
+ function pageGlob(policy, pagesDir = 'app/pages') {
47
+ return `${pagesDir.replace(/\\/g, '/')}/**/*.${globBrace(policy.page)}`;
48
+ }
49
+ function middlewareGlobs(policy, pagesDir = 'app/pages') {
50
+ const ext = globBrace(policy.middleware);
51
+ const normalizedPagesDir = pagesDir.replace(/\\/g, '/');
52
+ return [`${normalizedPagesDir}/**/middlewares.${ext}`, `${normalizedPagesDir}/**/*.middlewares.${ext}`];
53
+ }
54
+ /**
55
+ * La extensión de un archivo, si es una de las que el framework reconoce.
56
+ *
57
+ * Se compara en minúsculas: en macOS y Windows un `Index.TSX` es el mismo
58
+ * archivo, y rechazarlo por la capitalización sería un fallo incomprensible.
59
+ */
60
+ function scriptExtensionOf(file) {
61
+ const lower = file.toLowerCase();
62
+ // De más larga a más corta: `.tsx` termina en `x`, no en `.ts`, pero el orden
63
+ // deja explícito que la comparación no es ambigua.
64
+ return exports.SCRIPT_EXTENSIONS.find((ext) => lower.endsWith(ext));
65
+ }
66
+ function isIgnoredModule(file, policy = exports.DEFAULT_EXTENSION_POLICY) {
67
+ const lower = file.toLowerCase();
68
+ return policy.ignoredSuffixes.some((suffix) => lower.endsWith(suffix.toLowerCase()));
69
+ }
70
+ /**
71
+ * El archivo sin su extensión.
72
+ *
73
+ * Lanza en vez de devolver el path intacto: un archivo que llegó hasta acá sin
74
+ * una extensión reconocida es un error de configuración, y devolverlo tal cual
75
+ * lo convertiría en un fallo más adelante y más difícil de ubicar.
76
+ */
77
+ function stripScriptExtension(file) {
78
+ const ext = scriptExtensionOf(file);
79
+ if (!ext) {
80
+ throw new Error(`[ress] "${file}" no tiene una extensión que el framework reconozca. ` +
81
+ `Válidas: ${exports.SCRIPT_EXTENSIONS.join(', ')}.`);
82
+ }
83
+ return file.slice(0, -ext.length);
84
+ }
85
+ /**
86
+ * Archivos que viven junto a las páginas sin ser una.
87
+ *
88
+ * Hace falta desde que la política acepta `.ts` y `.js`: un archivo suelto ahí
89
+ * es casi siempre un ayudante —un cliente de API, un tipo compartido, un test— y
90
+ * tomarlo por una ruta produce una URL que existe y falla al renderizar, porque
91
+ * no exporta ningún componente.
92
+ */
93
+ const NOT_A_PAGE = /(^|\/)middlewares\.[^.]+$|\.(middlewares|test|spec)\.[^.]+$|(^|\/)_/;
94
+ function isPageFile(file, policy = exports.DEFAULT_EXTENSION_POLICY) {
95
+ if (isIgnoredModule(file, policy))
96
+ return false;
97
+ if (NOT_A_PAGE.test('/' + normalize(file)))
98
+ return false;
99
+ const ext = scriptExtensionOf(file);
100
+ return ext !== undefined && policy.page.includes(ext);
101
+ }
102
+ /**
103
+ * La ruta que un archivo de middleware protege.
104
+ *
105
+ * `pokedex/middlewares.tsx` protege `/pokedex`; `home/index.middlewares.js`
106
+ * protege `/home/index`. Antes esto se derivaba con una expresión que sólo
107
+ * recortaba `.ts` y `.js`, así que un middleware `.tsx` producía una ruta con la
108
+ * extensión adentro y no coincidía con ninguna página: no corría, y sin aviso.
109
+ */
110
+ function middlewareRoute(file, pagesDir = 'app/pages') {
111
+ const relative = stripScriptExtension(relativeToPages(file, pagesDir));
112
+ return '/' + relative.replace(/\.middlewares$/, '').replace(/\/?middlewares$/, '');
113
+ }
114
+ function isMiddlewareFile(file, policy = exports.DEFAULT_EXTENSION_POLICY) {
115
+ if (isIgnoredModule(file, policy))
116
+ return false;
117
+ const ext = scriptExtensionOf(file);
118
+ if (!ext || !policy.middleware.includes(ext))
119
+ return false;
120
+ const name = normalize(file).split('/').pop() ?? '';
121
+ return name.startsWith('middlewares.') || name.includes('.middlewares.');
122
+ }
123
+ /**
124
+ * Identificador de una página, sin su extensión ni el prefijo del directorio.
125
+ *
126
+ * Es la clave que comparten el catálogo de variantes y el manifest de rutas, así
127
+ * que dos archivos que sólo difieren en la extensión producen el mismo: es lo
128
+ * que permite detectar que ambos existen.
129
+ */
130
+ function pageIdOf(file, pagesDir = 'app/pages') {
131
+ const relative = relativeToPages(file, pagesDir);
132
+ return stripScriptExtension(relative);
133
+ }
134
+ /**
135
+ * Dónde queda un módulo después de compilarse.
136
+ *
137
+ * El build emite siempre `.js`, cualquiera sea la extensión de origen, y sanea
138
+ * los corchetes de los parámetros porque no pueden formar parte de un nombre de
139
+ * archivo en todos los sistemas.
140
+ */
141
+ function toCompiledPath(file, policy = exports.DEFAULT_EXTENSION_POLICY, pagesDir = 'app/pages') {
142
+ // `stripScriptExtension` lanza si no reconoce la extensión, que es lo correcto:
143
+ // devolver el nombre intacto producía `foo.mjs` → `foo.mjs.js`, un artefacto
144
+ // que no existe y cuyo fallo aparece recién al importarlo en producción.
145
+ const relative = stripScriptExtension(relativeToPages(file, pagesDir));
146
+ return relative.replace(/\[([^\]]+)\]/g, '_$1_') + policy.compiled;
147
+ }
148
+ /**
149
+ * Dos archivos que producen la misma página son ambiguos.
150
+ *
151
+ * Cuál gana dependería del orden en que el sistema de archivos los devolvió, y
152
+ * ese orden cambia entre sistemas. Mejor fallar al arrancar nombrando los dos.
153
+ */
154
+ function assertNoModuleCollision(files, pagesDir = 'app/pages', options = {}) {
155
+ const byId = new Map();
156
+ for (const file of files) {
157
+ const id = pageIdOf(file, pagesDir);
158
+ const previous = byId.get(id);
159
+ if (previous) {
160
+ const message = `[ress] "${previous}" y "${file}" definen la misma página. ` +
161
+ 'Cuál gana dependería del orden del sistema de archivos, que cambia entre ' +
162
+ 'sistemas operativos. Dejá uno solo.';
163
+ // Al construir es un error; en desarrollo se avisa y se sigue con el
164
+ // primero, para no dejar el servidor caído mientras alguien renombra.
165
+ if (options.strict)
166
+ throw new Error(message);
167
+ console.warn(`${message}\nPor ahora responde "${previous}".`);
168
+ continue;
169
+ }
170
+ byId.set(id, file);
171
+ }
172
+ }
173
+ /** Los separadores de Windows se normalizan: el resto del framework usa `/`. */
174
+ function normalize(file) {
175
+ return file.replace(/\\/g, '/');
176
+ }
177
+ function relativeToPages(file, pagesDir) {
178
+ const normalized = normalize(file);
179
+ const marker = normalize(pagesDir).replace(/\/$/, '') + '/';
180
+ const index = normalized.indexOf(marker);
181
+ return index === -1 ? normalized.replace(/^\//, '') : normalized.slice(index + marker.length);
182
+ }
183
+ /**
184
+ * Política vigente del proceso.
185
+ *
186
+ * El descubrimiento corre en varios lugares que no reciben opciones —el
187
+ * catálogo, el escaneo de rutas, la carga de middlewares—, así que el plugin la
188
+ * fija una vez al resolver la configuración y todos leen la misma.
189
+ */
190
+ let current = exports.DEFAULT_EXTENSION_POLICY;
191
+ function setExtensionPolicy(policy) {
192
+ current = policy;
193
+ }
194
+ function getExtensionPolicy() {
195
+ return current;
196
+ }
@@ -1,14 +1,31 @@
1
- import { PlatformInfo } from '../platform';
1
+ import { type PlatformInfo } from '../platform';
2
+ import { type SecurityContext } from '../security';
3
+ import type { AssetIndex, VariantCatalog } from '../variants/types';
4
+ export { resolvePageAssets } from '@ressjs/assets';
5
+ /**
6
+ * Qué archivos servirle a esta plataforma: elige la variante y busca sus artefactos.
7
+ *
8
+ * Son dos consultas en memoria. El catálogo ya está armado y los artefactos ya
9
+ * están indexados desde que arrancó el servidor, así que servir una página no
10
+ * toca el sistema de archivos ni escribe nada.
11
+ *
12
+ * Si la variante elegida no tiene artefactos construidos, responde los de la
13
+ * base antes que no responder nada.
14
+ */
2
15
  export interface HTMLGenerationContext {
3
16
  template: string;
4
17
  rendered: any;
5
18
  page: any;
6
- manifest: any;
7
19
  platform: string;
8
20
  isProduction: boolean;
9
21
  platformInfo: PlatformInfo | null;
10
22
  serverSideResult?: any;
23
+ catalog?: VariantCatalog;
24
+ assets?: AssetIndex;
25
+ /** Request-scoped links prepared before rendering (and overridable by middleware). */
26
+ styles?: {
27
+ js: string;
28
+ css: string[];
29
+ };
11
30
  }
12
- export declare function generateClientScript(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): Promise<string | null>;
13
- export declare function generateClientCSS(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): string;
14
- export declare function generateCompleteHTML(context: HTMLGenerationContext): Promise<string>;
31
+ export declare function generateCompleteHTML(context: HTMLGenerationContext, security?: SecurityContext): Promise<string>;