@ressjs/vite-router 0.3.0 → 0.4.1

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/dist/index.js CHANGED
@@ -1,6 +1,21 @@
1
- // Main API - Versión simplificada que funciona con el servidor existente
2
- export { createViteRouter, createAutoViteServer, detectPlatform, listDetectedPages, getPages } from './simple-router.js';
3
- // Legacy exports para compatibilidad (deprecated)
4
- export { RouterFactory, createInitializedViteRouter } from './router-factory.js';
5
- export { PageDetector } from './page-detector.js';
6
- export { EntryGenerator } from './entry-generator.js';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveEntryClient = exports.getPlatformCSSClass = exports.detectPlatform = exports.resolvePlatformCSS = exports.createRenderServer = exports.generateTempEntries = exports.getClientEntriesInput = exports.getViteInput = exports.getPages = exports.listDetectedPages = exports.createAutoViteServer = exports.createViteRouter = void 0;
4
+ // Main API
5
+ var router_1 = require("./router");
6
+ Object.defineProperty(exports, "createViteRouter", { enumerable: true, get: function () { return router_1.createViteRouter; } });
7
+ Object.defineProperty(exports, "createAutoViteServer", { enumerable: true, get: function () { return router_1.createAutoViteServer; } });
8
+ Object.defineProperty(exports, "listDetectedPages", { enumerable: true, get: function () { return router_1.listDetectedPages; } });
9
+ Object.defineProperty(exports, "getPages", { enumerable: true, get: function () { return router_1.getPages; } });
10
+ // Core functions for Vite config
11
+ var pages_1 = require("./pages");
12
+ Object.defineProperty(exports, "getViteInput", { enumerable: true, get: function () { return pages_1.getViteInput; } });
13
+ Object.defineProperty(exports, "getClientEntriesInput", { enumerable: true, get: function () { return pages_1.getClientEntriesInput; } });
14
+ Object.defineProperty(exports, "generateTempEntries", { enumerable: true, get: function () { return pages_1.generateTempEntries; } });
15
+ Object.defineProperty(exports, "createRenderServer", { enumerable: true, get: function () { return pages_1.createRenderServer; } });
16
+ Object.defineProperty(exports, "resolvePlatformCSS", { enumerable: true, get: function () { return pages_1.resolvePlatformCSS; } });
17
+ // Platform detection
18
+ var platform_1 = require("./platform");
19
+ Object.defineProperty(exports, "detectPlatform", { enumerable: true, get: function () { return platform_1.detectPlatform; } });
20
+ Object.defineProperty(exports, "getPlatformCSSClass", { enumerable: true, get: function () { return platform_1.getPlatformCSSClass; } });
21
+ Object.defineProperty(exports, "resolveEntryClient", { enumerable: true, get: function () { return platform_1.resolveEntryClient; } });
@@ -0,0 +1,26 @@
1
+ import type { PlatformInfo } from './platform';
2
+ export interface PageInfo {
3
+ route: string;
4
+ file: string;
5
+ abs: string;
6
+ }
7
+ export declare function getPages(): PageInfo[];
8
+ export declare function getViteInput(): Record<string, string>;
9
+ export declare function loadManifest(distPath?: string): Promise<any>;
10
+ export declare function resolvePlatformCSS(basePath: string, platform: PlatformInfo): Promise<string>;
11
+ export declare function generateTempEntries(distPath?: string, platformInfo?: PlatformInfo): Promise<string[]>;
12
+ export declare function generateSingleEntry(page: PageInfo, entryPath: string, preferredScssFile: string): Promise<void>;
13
+ export declare function getClientEntriesInput(distPath?: string): Promise<Record<string, string>>;
14
+ export interface RenderOptions {
15
+ url: string;
16
+ props?: any;
17
+ }
18
+ export interface CreateRenderServerOptions {
19
+ vite?: any;
20
+ }
21
+ export declare function createRenderServer(options?: CreateRenderServerOptions): (renderOptions: RenderOptions) => Promise<{
22
+ html: string;
23
+ props: any;
24
+ route: string;
25
+ page: string;
26
+ }>;
package/dist/pages.js ADDED
@@ -0,0 +1,296 @@
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.getPages = getPages;
40
+ exports.getViteInput = getViteInput;
41
+ exports.loadManifest = loadManifest;
42
+ exports.resolvePlatformCSS = resolvePlatformCSS;
43
+ exports.generateTempEntries = generateTempEntries;
44
+ exports.generateSingleEntry = generateSingleEntry;
45
+ exports.getClientEntriesInput = getClientEntriesInput;
46
+ exports.createRenderServer = createRenderServer;
47
+ const promises_1 = __importDefault(require("node:fs/promises"));
48
+ const node_path_1 = __importDefault(require("node:path"));
49
+ const fast_glob_1 = __importDefault(require("fast-glob"));
50
+ const react_1 = __importDefault(require("react"));
51
+ const server_1 = require("react-dom/server");
52
+ function getPages() {
53
+ // Busca tanto page.tsx como page/index.tsx y prioriza el más corto
54
+ const files = fast_glob_1.default.sync('app/pages/**/*.{tsx,jsx}');
55
+ const seen = new Set();
56
+ const pages = [];
57
+ for (const file of files) {
58
+ let route = file.replace(/^app\/pages/, '').replace(/\.tsx$|\.jsx$/, '');
59
+ route = route.replace(/\/index$/, '') || '/';
60
+ // Prioriza page.tsx sobre page/index.tsx
61
+ if (!seen.has(route)) {
62
+ seen.add(route);
63
+ pages.push({ route, file, abs: node_path_1.default.resolve(file) });
64
+ }
65
+ }
66
+ return pages;
67
+ }
68
+ // Para Vite: input { [route]: absPath }
69
+ function getViteInput() {
70
+ const pages = getPages();
71
+ const input = {};
72
+ for (const page of pages) {
73
+ input[page.file.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '')] = page.abs;
74
+ }
75
+ return input;
76
+ }
77
+ async function loadManifest(distPath = 'dist') {
78
+ const assetsDir = node_path_1.default.join(distPath, 'server/.vite');
79
+ const files = await promises_1.default.readdir(assetsDir);
80
+ const manifestFile = files.find(f => /^manifest-.*\.js$/.test(f));
81
+ if (!manifestFile)
82
+ throw new Error('No manifest file found in dist/server/assets');
83
+ // Import dinámico del manifest
84
+ const manifest = (await Promise.resolve(`${node_path_1.default.join(assetsDir, manifestFile)}`).then(s => __importStar(require(s)))).default;
85
+ return manifest;
86
+ }
87
+ async function fileExists(file) {
88
+ try {
89
+ await promises_1.default.access(file);
90
+ return true;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ /**
97
+ * Platform-aware CSS resolution
98
+ * Busca CSS files con prioridades basadas en platform detection
99
+ */
100
+ // Función helper para determinar si es probablemente un dispositivo móvil
101
+ function isProbablyMobile(platform) {
102
+ // Mobile si es phone/tablet, o webview (que suele ser mobile), o category native (React Native)
103
+ return platform.device === 'phone' ||
104
+ platform.device === 'tablet' ||
105
+ platform.category === 'webview' ||
106
+ platform.category === 'native';
107
+ }
108
+ async function resolvePlatformCSS(basePath, platform) {
109
+ // Orden de prioridad corregido: OS first → device+OS → device → mobile condicional
110
+ const priorities = [];
111
+ // 1. OS específico primero: android.scss, ios.scss
112
+ priorities.push(`${basePath}.${platform.os}.scss`);
113
+ // Para webview, agregar combinaciones específicas después del OS
114
+ if (platform.category === 'webview') {
115
+ // 2. webview.os.scss (ej: webview.android.scss, webview.ios.scss)
116
+ priorities.push(`${basePath}.webview.${platform.os}.scss`);
117
+ // 3. webview.scss general
118
+ priorities.push(`${basePath}.webview.scss`);
119
+ }
120
+ // Para native o web, agregar combinaciones device.os
121
+ if (platform.category === 'native' || platform.category === 'web') {
122
+ // 2. device.os.scss (ej: phone.android.scss, tablet.ios.scss)
123
+ priorities.push(`${basePath}.${platform.device}.${platform.os}.scss`);
124
+ }
125
+ // 3. Device específico: phone.scss, tablet.scss, desktop.scss
126
+ priorities.push(`${basePath}.${platform.device}.scss`);
127
+ // 4. Mobile fallback SOLO si es probablemente móvil
128
+ if (isProbablyMobile(platform)) {
129
+ priorities.push(`${basePath}.mobile.scss`);
130
+ }
131
+ // 5. Fallback final
132
+ priorities.push(`${basePath}.scss`);
133
+ // Retorna el primer archivo que exista
134
+ for (const file of priorities) {
135
+ if (await fileExists(file)) {
136
+ return file;
137
+ }
138
+ }
139
+ // Si ninguno existe, retorna el CSS base
140
+ return `${basePath}.scss`;
141
+ }
142
+ async function generateTempEntries(distPath = 'dist', platformInfo) {
143
+ const pages = getPages();
144
+ const entriesDir = node_path_1.default.resolve(distPath, '.entries');
145
+ // Solo crea el directorio si no existe, no lo borra
146
+ await promises_1.default.mkdir(entriesDir, { recursive: true });
147
+ const promises = pages.map(async (page) => {
148
+ const rel = page.file.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
149
+ if (platformInfo && platformInfo.device !== 'desktop') {
150
+ // Solo genera entry específico para mobile devices (phone, tablet)
151
+ const basePath = page.file.replace(/\.(tsx|jsx)$/, '');
152
+ const platformCSS = await resolvePlatformCSS(basePath, platformInfo);
153
+ // Generar entry específico con el CSS resuelto (puede ser base o específico)
154
+ const platformSuffix = `${platformInfo.device}.${platformInfo.os}`;
155
+ const entryPath = node_path_1.default.join(entriesDir, rel + `__entry-client.${platformSuffix}.tsx`);
156
+ await generateSingleEntry(page, entryPath, platformCSS);
157
+ return [entryPath];
158
+ }
159
+ else {
160
+ // Lógica mejorada: busca dinámicamente todos los archivos CSS específicos
161
+ const basePath = page.file.replace(/\.(tsx|jsx)$/, '');
162
+ const baseScss = basePath + '.scss';
163
+ const results = [];
164
+ // Genera entry-client base (desktop) - solo CSS base
165
+ const entryPath = node_path_1.default.join(entriesDir, rel + '__entry-client.tsx');
166
+ await generateSingleEntry(page, entryPath, baseScss);
167
+ results.push(entryPath);
168
+ // Buscar dinámicamente todos los archivos CSS específicos de plataforma
169
+ const glob = await Promise.resolve().then(() => __importStar(require('fast-glob')));
170
+ const cssFiles = await glob.default(`${basePath}.*.scss`);
171
+ for (const cssFile of cssFiles) {
172
+ // Extraer el sufijo del archivo CSS (ej: mobile, webview.android, desktop.linux)
173
+ const suffix = cssFile
174
+ .replace(basePath + '.', '')
175
+ .replace('.scss', '');
176
+ // Generar entry-client específico
177
+ const specificEntryPath = node_path_1.default.join(entriesDir, rel + `__entry-client.${suffix}.tsx`);
178
+ await generateSingleEntry(page, specificEntryPath, cssFile);
179
+ results.push(specificEntryPath);
180
+ }
181
+ return results;
182
+ }
183
+ });
184
+ const entryFiles = await Promise.all(promises);
185
+ return entryFiles.flat().filter(Boolean);
186
+ }
187
+ async function generateSingleEntry(page, entryPath, preferredScssFile) {
188
+ // Verifica si el archivo ya existe y es más reciente que el archivo fuente
189
+ try {
190
+ const entryStat = await promises_1.default.stat(entryPath);
191
+ const pageStat = await promises_1.default.stat(page.abs);
192
+ if (entryStat.mtime > pageStat.mtime) {
193
+ return; // El entry ya existe y está actualizado
194
+ }
195
+ }
196
+ catch {
197
+ // El archivo no existe, continúa con la generación
198
+ }
199
+ const importPath = node_path_1.default.relative(node_path_1.default.dirname(entryPath), page.abs).replace(/\\/g, '/');
200
+ // Lógica de fallback: intenta cargar el CSS preferido, si no existe carga el base
201
+ let scssToImport = '';
202
+ let scssImport = '';
203
+ if (await fileExists(preferredScssFile)) {
204
+ // Existe el CSS específico, usarlo
205
+ scssToImport = preferredScssFile;
206
+ }
207
+ else {
208
+ // No existe el CSS específico, usar el CSS base como fallback
209
+ const baseScss = page.file.replace(/\.(tsx|jsx)$/, '.scss');
210
+ if (await fileExists(baseScss)) {
211
+ scssToImport = baseScss;
212
+ }
213
+ }
214
+ if (scssToImport) {
215
+ const scssImportPath = node_path_1.default.relative(node_path_1.default.dirname(entryPath), scssToImport).replace(/\\/g, '/');
216
+ scssImport = `import "${scssImportPath.startsWith('.') ? scssImportPath : './' + scssImportPath}";`;
217
+ }
218
+ const template = `
219
+ ${scssImport}
220
+ import { StrictMode } from "react";
221
+ import { hydrateRoot } from "react-dom/client";
222
+ import Page from "${importPath.startsWith('.') ? importPath : './' + importPath}";
223
+ const props = window.__INITIAL_PROPS__ || {};
224
+ hydrateRoot(document.getElementById("root"), <StrictMode><Page {...props} /></StrictMode>);
225
+ `.trim();
226
+ await promises_1.default.mkdir(node_path_1.default.dirname(entryPath), { recursive: true });
227
+ await promises_1.default.writeFile(entryPath, template);
228
+ }
229
+ async function getClientEntriesInput(distPath = 'dist') {
230
+ await generateTempEntries(distPath);
231
+ // Incluir todos los entry-clients: base, mobile, desktop.linux
232
+ const entryFiles = fast_glob_1.default.sync(`${distPath}/.entries/**/*__entry-client*.tsx`);
233
+ const input = {};
234
+ for (const file of entryFiles) {
235
+ // Mantener el nombre completo del archivo como key para diferenciar las plataformas
236
+ const key = file.replace(new RegExp(`^${distPath}/\\.entries/`), '');
237
+ input[key] = node_path_1.default.resolve(file);
238
+ }
239
+ return input;
240
+ }
241
+ function createRenderServer(options = {}) {
242
+ const pages = getPages();
243
+ const { vite } = options;
244
+ return async (renderOptions) => {
245
+ const { url, props = {} } = renderOptions;
246
+ // Encuentra la página que coincide con la URL
247
+ const matchedPage = pages.find(page => {
248
+ if (page.route === '/')
249
+ return url === '/' || url === '';
250
+ return url === page.route || url.startsWith(page.route + '/');
251
+ });
252
+ if (!matchedPage) {
253
+ throw new Error(`Page not found for URL: ${url}`);
254
+ }
255
+ try {
256
+ // Import dinámico de la página usando vite.ssrLoadModule si está disponible
257
+ let pageModule;
258
+ if (vite) {
259
+ // En desarrollo: usar vite.ssrLoadModule para manejar .tsx
260
+ const modulePath = `/${matchedPage.file}`;
261
+ pageModule = await vite.ssrLoadModule(modulePath);
262
+ }
263
+ else {
264
+ // En producción: usar import directo del archivo compilado
265
+ const moduleUrl = matchedPage.abs.replace(/\.tsx$/, '.js').replace('/app/pages/', '/dist/server/');
266
+ pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
267
+ }
268
+ const PageComponent = pageModule.default;
269
+ const getServerSideProps = pageModule.getServerSideProps;
270
+ if (!PageComponent) {
271
+ throw new Error(`No default export found in ${matchedPage.file}`);
272
+ }
273
+ // Si existe getServerSideProps, la ejecutamos para obtener props iniciales
274
+ let finalProps = { ...props };
275
+ if (getServerSideProps && typeof getServerSideProps === 'function') {
276
+ console.log(`Calling getServerSideProps for ${matchedPage.route}`);
277
+ const serverSideResult = await getServerSideProps();
278
+ if (serverSideResult && serverSideResult.props) {
279
+ finalProps = { ...finalProps, ...serverSideResult.props };
280
+ }
281
+ }
282
+ // Renderiza el componente con las props finales
283
+ const html = (0, server_1.renderToString)(react_1.default.createElement(PageComponent, finalProps));
284
+ return {
285
+ html,
286
+ props: finalProps,
287
+ route: matchedPage.route,
288
+ page: matchedPage.file
289
+ };
290
+ }
291
+ catch (error) {
292
+ console.error(`Error rendering page ${matchedPage.route}:`, error);
293
+ throw error;
294
+ }
295
+ };
296
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Platform detection for ress.js
3
+ * Combina headers custom de React Native con User-Agent parsing
4
+ */
5
+ export interface PlatformInfo {
6
+ category: 'web' | 'webview' | 'native';
7
+ os: 'ios' | 'android' | 'windows' | 'macos' | 'linux' | 'unknown';
8
+ device: 'phone' | 'tablet' | 'desktop';
9
+ version?: string;
10
+ appVersion?: string;
11
+ source: 'headers' | 'user-agent';
12
+ }
13
+ /**
14
+ * Detecta plataforma usando headers custom + User-Agent fallback
15
+ */
16
+ export declare function detectPlatform(userAgent: string, headers?: any): PlatformInfo;
17
+ /**
18
+ * Genera CSS class para HTML basado en platform info
19
+ */
20
+ export declare function getPlatformCSSClass(platform: PlatformInfo): string;
21
+ /**
22
+ * Determina qué entry-client usar basado en platform
23
+ */
24
+ export declare function resolveEntryClient(baseName: string, platform: PlatformInfo): string;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Platform detection for ress.js
4
+ * Combina headers custom de React Native con User-Agent parsing
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.detectPlatform = detectPlatform;
8
+ exports.getPlatformCSSClass = getPlatformCSSClass;
9
+ exports.resolveEntryClient = resolveEntryClient;
10
+ /**
11
+ * Detecta plataforma usando headers custom + User-Agent fallback
12
+ */
13
+ function detectPlatform(userAgent, headers = {}) {
14
+ const ressHeaders = headers;
15
+ // 1. React Native headers detection
16
+ if (ressHeaders['x-rn-platform']) {
17
+ return {
18
+ category: 'native',
19
+ os: ressHeaders['x-rn-platform'] || 'android',
20
+ device: 'phone', // React Native es típicamente mobile
21
+ version: ressHeaders['x-rn-version'],
22
+ source: 'headers'
23
+ };
24
+ }
25
+ // 2. WebView headers detection
26
+ if (ressHeaders['x-webview'] === 'true' || ressHeaders['x-ressjs-platform'] === 'webview') {
27
+ const os = ressHeaders['x-platform'] || ressHeaders['x-ressjs-os'] || 'android';
28
+ return {
29
+ category: 'webview',
30
+ os: os,
31
+ device: ressHeaders['x-ressjs-device'] || 'phone',
32
+ version: ressHeaders['x-ressjs-version'],
33
+ appVersion: ressHeaders['x-ressjs-app-version'],
34
+ source: 'headers'
35
+ };
36
+ }
37
+ // 3. Ress.js specific headers (original logic)
38
+ if (ressHeaders['x-ressjs-platform'] === 'webview') {
39
+ return {
40
+ category: 'webview',
41
+ os: ressHeaders['x-ressjs-os'] || 'android',
42
+ device: ressHeaders['x-ressjs-device'] || 'phone',
43
+ version: ressHeaders['x-ressjs-version'],
44
+ appVersion: ressHeaders['x-ressjs-app-version'],
45
+ source: 'headers'
46
+ };
47
+ }
48
+ // 4. User-Agent fallback detection
49
+ return detectFromUserAgent(userAgent);
50
+ }
51
+ /**
52
+ * Detección por User-Agent (navegadores estándar)
53
+ */
54
+ function detectFromUserAgent(userAgent) {
55
+ const ua = userAgent.toLowerCase();
56
+ // SPECIAL CASE: React Native
57
+ if (ua.includes('reactnative')) {
58
+ let os = 'android';
59
+ if (ua.includes('ios')) {
60
+ os = 'ios';
61
+ }
62
+ else if (ua.includes('android')) {
63
+ os = 'android';
64
+ }
65
+ return {
66
+ category: 'native',
67
+ os,
68
+ device: 'phone', // React Native siempre mobile
69
+ source: 'user-agent'
70
+ };
71
+ }
72
+ // Detectar OS
73
+ let os = 'unknown';
74
+ if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ios')) {
75
+ os = 'ios';
76
+ }
77
+ else if (ua.includes('android')) {
78
+ os = 'android';
79
+ }
80
+ else if (ua.includes('windows')) {
81
+ os = 'windows';
82
+ }
83
+ else if (ua.includes('macintosh') || ua.includes('mac os x')) {
84
+ os = 'macos';
85
+ }
86
+ else if (ua.includes('linux')) {
87
+ os = 'linux';
88
+ }
89
+ // Detectar device type
90
+ let device = 'desktop';
91
+ if (ua.includes('ipad')) {
92
+ device = 'tablet';
93
+ }
94
+ else if (ua.includes('mobile') || ua.includes('phone')) {
95
+ device = 'phone';
96
+ }
97
+ else if (ua.includes('tablet')) {
98
+ device = 'tablet';
99
+ }
100
+ // Detectar category (web vs webview)
101
+ let category = 'web';
102
+ return {
103
+ category,
104
+ os,
105
+ device,
106
+ source: 'user-agent'
107
+ };
108
+ }
109
+ /**
110
+ * Genera CSS class para HTML basado en platform info
111
+ */
112
+ function getPlatformCSSClass(platform) {
113
+ const classes = [
114
+ platform.category, // 'web', 'webview', 'native'
115
+ platform.device, // 'phone', 'tablet', 'desktop'
116
+ platform.os // 'ios', 'android', etc.
117
+ ];
118
+ return classes.join(' ');
119
+ }
120
+ /**
121
+ * Determina qué entry-client usar basado en platform
122
+ */
123
+ function resolveEntryClient(baseName, platform) {
124
+ const candidates = [
125
+ // Específico para mobile: device.os (phone.ios, tablet.android)
126
+ ...(platform.device !== 'desktop'
127
+ ? [`${baseName}__entry-client.${platform.device}.${platform.os}.tsx`]
128
+ : []),
129
+ // OS específico: ios, android
130
+ `${baseName}__entry-client.${platform.os}.tsx`,
131
+ // Device: mobile, desktop, tablet
132
+ `${baseName}__entry-client.${platform.device}.tsx`,
133
+ // Legacy compatibility
134
+ ...(platform.device === 'phone' || platform.device === 'tablet'
135
+ ? [`${baseName}__entry-client.mobile.tsx`]
136
+ : []),
137
+ // Fallback
138
+ `${baseName}__entry-client.tsx`
139
+ ];
140
+ // En producción, deberías verificar que el archivo existe
141
+ // Por ahora retorna el primer candidato
142
+ return candidates[0];
143
+ }
@@ -0,0 +1,13 @@
1
+ export interface RenderOptions {
2
+ url: string;
3
+ props?: any;
4
+ }
5
+ export interface CreateRenderServerOptions {
6
+ vite?: any;
7
+ }
8
+ export declare function createRenderServer(options?: CreateRenderServerOptions): (renderOptions: RenderOptions) => Promise<{
9
+ html: string;
10
+ props: any;
11
+ route: string;
12
+ page: string;
13
+ }>;
package/dist/render.js ADDED
@@ -0,0 +1,98 @@
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.createRenderServer = createRenderServer;
40
+ const react_1 = __importDefault(require("react"));
41
+ const server_1 = require("react-dom/server");
42
+ const pages_1 = require("./pages");
43
+ function createRenderServer(options = {}) {
44
+ const pages = (0, pages_1.getPages)();
45
+ const { vite } = options;
46
+ return async (renderOptions) => {
47
+ const { url, props = {} } = renderOptions;
48
+ // Encuentra la página que coincide con la URL
49
+ const matchedPage = pages.find(page => {
50
+ if (page.route === '/')
51
+ return url === '/' || url === '';
52
+ return url === page.route || url.startsWith(page.route + '/');
53
+ });
54
+ if (!matchedPage) {
55
+ throw new Error(`Page not found for URL: ${url}`);
56
+ }
57
+ try {
58
+ // Import dinámico de la página usando vite.ssrLoadModule si está disponible
59
+ let pageModule;
60
+ if (vite) {
61
+ // En desarrollo: usar vite.ssrLoadModule para manejar .tsx
62
+ const modulePath = `/${matchedPage.file}`;
63
+ pageModule = await vite.ssrLoadModule(modulePath);
64
+ }
65
+ else {
66
+ // En producción: usar import directo del archivo compilado
67
+ const moduleUrl = matchedPage.abs.replace(/\.tsx$/, '.js').replace('/app/pages/', '/dist/server/');
68
+ pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
69
+ }
70
+ const PageComponent = pageModule.default;
71
+ const getServerSideProps = pageModule.getServerSideProps;
72
+ if (!PageComponent) {
73
+ throw new Error(`No default export found in ${matchedPage.file}`);
74
+ }
75
+ // Si existe getServerSideProps, la ejecutamos para obtener props iniciales
76
+ let finalProps = { ...props };
77
+ if (getServerSideProps && typeof getServerSideProps === 'function') {
78
+ console.log(`Calling getServerSideProps for ${matchedPage.route}`);
79
+ const serverSideResult = await getServerSideProps();
80
+ if (serverSideResult && serverSideResult.props) {
81
+ finalProps = { ...finalProps, ...serverSideResult.props };
82
+ }
83
+ }
84
+ // Renderiza el componente con las props finales
85
+ const html = (0, server_1.renderToString)(react_1.default.createElement(PageComponent, finalProps));
86
+ return {
87
+ html,
88
+ props: finalProps,
89
+ route: matchedPage.route,
90
+ page: matchedPage.file
91
+ };
92
+ }
93
+ catch (error) {
94
+ console.error(`Error rendering page ${matchedPage.route}:`, error);
95
+ throw error;
96
+ }
97
+ };
98
+ }
@@ -1,10 +1,11 @@
1
1
  import { Router } from 'express';
2
2
  export interface ViteRouterOptions {
3
3
  enablePlatformDetection?: boolean;
4
+ isProduction?: boolean;
5
+ pagesDirectory?: string;
6
+ basePath?: string;
4
7
  vite?: any;
5
- manifest?: any;
6
8
  templateHtml?: string;
7
- isProduction?: boolean;
8
9
  renderFunction?: (options: {
9
10
  url: string;
10
11
  platform: string;
@@ -16,28 +17,11 @@ export interface AutoViteServerOptions {
16
17
  base?: string;
17
18
  enablePlatformDetection?: boolean;
18
19
  }
19
- /**
20
- * Crea un servidor Express completo con vite-router automático
21
- * Esta es la función de alto nivel para casos simples
22
- */
23
20
  export declare function createAutoViteServer(options?: AutoViteServerOptions): Promise<{
24
21
  app: import("express-serve-static-core").Express;
25
22
  vite: any;
26
23
  start: () => import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
27
24
  }>;
28
- /**
29
- * Crea un Express router que automáticamente detecta páginas, las rutea,
30
- * y maneja todo el SSR + hidratación del cliente.
31
- *
32
- * Si no se pasan opciones, auto-configura Vite, manifest, templates, etc.
33
- */
34
25
  export declare function createViteRouter(options?: ViteRouterOptions): Router;
35
- /**
36
- * Detecta platform desde el User-Agent
37
- */
38
- export declare function detectPlatform(userAgent: string): 'desktop' | 'mobile';
39
- /**
40
- * Helper para listar páginas detectadas
41
- */
42
- export declare function listDetectedPages(): import("@ressjs/core").PageInfo[];
43
- export { getPages } from '@ressjs/core';
26
+ export declare function listDetectedPages(): import("./pages").PageInfo[];
27
+ export { getPages } from './pages';