@ressjs/vite-router 0.2.1 → 0.4.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 +94 -166
- package/dist/helpers/html-generator.d.ts +13 -0
- package/dist/helpers/html-generator.js +108 -0
- package/dist/helpers/request-handler.d.ts +17 -0
- package/dist/helpers/request-handler.js +79 -0
- package/dist/helpers/vite-config.d.ts +10 -0
- package/dist/helpers/vite-config.js +97 -0
- package/dist/index.d.ts +6 -10
- package/dist/index.js +21 -7
- package/dist/pages.d.ts +26 -0
- package/dist/pages.js +296 -0
- package/dist/platform.d.ts +24 -0
- package/dist/platform.js +143 -0
- package/dist/render.d.ts +13 -0
- package/dist/render.js +98 -0
- package/dist/{simple-router.d.ts → router.d.ts} +5 -22
- package/dist/router.js +118 -0
- package/package.json +19 -8
- package/dist/entry-generator.d.ts +0 -34
- package/dist/entry-generator.d.ts.map +0 -1
- package/dist/entry-generator.js +0 -113
- package/dist/entry-generator.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/page-detector.d.ts +0 -44
- package/dist/page-detector.d.ts.map +0 -1
- package/dist/page-detector.js +0 -92
- package/dist/page-detector.js.map +0 -1
- package/dist/router-factory.d.ts +0 -58
- package/dist/router-factory.d.ts.map +0 -1
- package/dist/router-factory.js +0 -153
- package/dist/router-factory.js.map +0 -1
- package/dist/simple-router.d.ts.map +0 -1
- package/dist/simple-router.js +0 -341
- package/dist/simple-router.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ressjs/vite-router
|
|
2
2
|
|
|
3
|
-
Express router
|
|
3
|
+
Advanced Express router with platform detection and Server-Side Rendering (SSR) for RESS.js applications.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,219 +10,147 @@ npm install @ressjs/vite-router
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
-
|
|
13
|
+
- **Advanced Routing**: Auto-discovers pages with platform-specific entry points
|
|
14
|
+
- **Platform Detection**: Automatic device, OS, and WebView detection
|
|
15
|
+
- **SSR Support**: Complete server-side rendering with proper hydration
|
|
16
|
+
- **Asset Management**: Smart CSS/JS bundle resolution based on platform
|
|
17
|
+
- **Development Mode**: Hot reload with fast refresh and dev server
|
|
18
|
+
- **Production Ready**: Optimized builds with manifest-based asset loading
|
|
19
|
+
- **TypeScript**: Full TypeScript support with comprehensive type definitions
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
+
## Quick Start
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
npm install @ressjs/vite-router
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Uso Básico
|
|
23
|
+
### Basic Setup
|
|
27
24
|
|
|
28
|
-
```
|
|
25
|
+
```typescript
|
|
29
26
|
import express from 'express'
|
|
30
27
|
import { createViteRouter } from '@ressjs/vite-router'
|
|
31
28
|
|
|
32
29
|
const app = express()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
pagesDir: 'app/pages',
|
|
37
|
-
outputDir: 'dist/.entries',
|
|
38
|
-
enableSSR: true,
|
|
39
|
-
enablePlatformDetection: true
|
|
30
|
+
const router = createViteRouter({
|
|
31
|
+
enablePlatformDetection: true,
|
|
32
|
+
isProduction: process.env.NODE_ENV === 'production'
|
|
40
33
|
})
|
|
41
34
|
|
|
42
|
-
app.use('/',
|
|
43
|
-
app.listen(3000)
|
|
35
|
+
app.use('/', router)
|
|
36
|
+
app.listen(3000, () => console.log('Server running on port 3000'))
|
|
44
37
|
```
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
import { RouterFactory } from '@ressjs/vite-router'
|
|
50
|
-
|
|
51
|
-
const factory = new RouterFactory({
|
|
52
|
-
pagesDir: 'app/pages',
|
|
53
|
-
outputDir: 'temp',
|
|
54
|
-
enableSSR: true,
|
|
55
|
-
enablePlatformDetection: true,
|
|
56
|
-
staticDir: 'public'
|
|
57
|
-
})
|
|
39
|
+
### Development Server
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const router = factory.createRouter()
|
|
63
|
-
app.use('/', router)
|
|
41
|
+
```typescript
|
|
42
|
+
import { createAutoViteServer } from '@ressjs/vite-router'
|
|
64
43
|
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
44
|
+
// Automatically configures Vite dev server with SSR
|
|
45
|
+
const server = await createAutoViteServer({
|
|
46
|
+
port: 3000,
|
|
47
|
+
enablePlatformDetection: true
|
|
68
48
|
})
|
|
69
49
|
```
|
|
70
50
|
|
|
71
|
-
##
|
|
51
|
+
## Platform Detection
|
|
72
52
|
|
|
73
|
-
|
|
53
|
+
The router automatically detects:
|
|
74
54
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
├── products/
|
|
80
|
-
│ ├── index.tsx -> ruta: /products
|
|
81
|
-
│ └── [id].tsx -> ruta: /products/:id
|
|
82
|
-
└── blog/
|
|
83
|
-
└── post.tsx -> ruta: /blog/post
|
|
84
|
-
```
|
|
55
|
+
- **Device Type**: mobile, tablet, desktop
|
|
56
|
+
- **Operating System**: iOS, Android, Windows, macOS, Linux
|
|
57
|
+
- **Environment**: webview, browser
|
|
58
|
+
- **Category**: webview.android, webview.ios, mobile, etc.
|
|
85
59
|
|
|
86
|
-
|
|
60
|
+
### Platform-Specific Assets
|
|
87
61
|
|
|
88
|
-
|
|
62
|
+
Based on detection, the router serves appropriate assets:
|
|
89
63
|
|
|
90
64
|
```
|
|
91
|
-
app/pages/home/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
└── index.desktop.scss -> solo desktop
|
|
65
|
+
app/pages/home/index.tsx # Base page
|
|
66
|
+
app/pages/home/index.scss # Base styles
|
|
67
|
+
app/pages/home/index.mobile.scss # Mobile-specific styles
|
|
68
|
+
app/pages/home/index.webview.android.scss # Android WebView styles
|
|
96
69
|
```
|
|
97
70
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// En el cliente se añade automáticamente:
|
|
104
|
-
document.documentElement.classList.add('mobile') // o 'desktop'
|
|
71
|
+
Generated entry-clients:
|
|
72
|
+
```
|
|
73
|
+
dist/.entries/home__index__entry-client.tsx # Base
|
|
74
|
+
dist/.entries/home__index__entry-client.mobile.tsx # Mobile
|
|
75
|
+
dist/.entries/home__index__entry-client.webview.android.tsx # Android WebView
|
|
105
76
|
```
|
|
106
77
|
|
|
107
|
-
## API
|
|
78
|
+
## API Reference
|
|
108
79
|
|
|
109
|
-
###
|
|
80
|
+
### createViteRouter(options)
|
|
110
81
|
|
|
111
|
-
|
|
82
|
+
Creates an Express router with SSR capabilities.
|
|
112
83
|
|
|
113
84
|
```typescript
|
|
114
|
-
interface
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
85
|
+
interface ViteRouterOptions {
|
|
86
|
+
enablePlatformDetection?: boolean // Enable platform detection (default: true)
|
|
87
|
+
vite?: any // Vite dev server instance
|
|
88
|
+
manifest?: any // Production manifest
|
|
89
|
+
templateHtml?: string // HTML template
|
|
90
|
+
isProduction?: boolean // Production mode
|
|
91
|
+
renderFunction?: (options: { // Custom render function
|
|
92
|
+
url: string
|
|
93
|
+
platform: string
|
|
94
|
+
req: any
|
|
95
|
+
}) => Promise<string>
|
|
120
96
|
}
|
|
121
97
|
```
|
|
122
98
|
|
|
123
|
-
###
|
|
124
|
-
|
|
125
|
-
Versión async que inicializa automáticamente el router.
|
|
126
|
-
|
|
127
|
-
### `RouterFactory`
|
|
128
|
-
|
|
129
|
-
Clase para control completo del router.
|
|
99
|
+
### generateHTML(template, rendered, page, manifest, platform, isProduction, platformInfo)
|
|
130
100
|
|
|
131
|
-
|
|
101
|
+
Generates complete HTML with proper asset injection and hydration.
|
|
132
102
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const detector = new PageDetector({
|
|
145
|
-
pagesDir: 'app/pages'
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
const pages = await detector.getDetectedPages()
|
|
149
|
-
const match = detector.matchRoute('/products/123', pages)
|
|
103
|
+
```typescript
|
|
104
|
+
export async function generateHTML(
|
|
105
|
+
template: string,
|
|
106
|
+
rendered: any,
|
|
107
|
+
page: any,
|
|
108
|
+
manifest: any,
|
|
109
|
+
platform: string,
|
|
110
|
+
isProduction: boolean,
|
|
111
|
+
platformInfo: any = null
|
|
112
|
+
): Promise<string>
|
|
150
113
|
```
|
|
151
114
|
|
|
152
|
-
###
|
|
153
|
-
|
|
154
|
-
Generador de entry clients dinámicos.
|
|
155
|
-
|
|
156
|
-
```javascript
|
|
157
|
-
import { EntryGenerator } from '@ressjs/vite-router'
|
|
115
|
+
### Utility Functions
|
|
158
116
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
})
|
|
117
|
+
```typescript
|
|
118
|
+
// Get all discovered pages
|
|
119
|
+
import { getPages, listDetectedPages } from '@ressjs/vite-router'
|
|
163
120
|
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
'/products',
|
|
167
|
-
platform,
|
|
168
|
-
pages
|
|
169
|
-
)
|
|
121
|
+
const pages = await getPages()
|
|
122
|
+
const pageList = listDetectedPages()
|
|
170
123
|
```
|
|
171
124
|
|
|
172
|
-
##
|
|
173
|
-
|
|
174
|
-
Ejemplo completo con Vite development server:
|
|
125
|
+
## File Structure
|
|
175
126
|
|
|
176
|
-
|
|
177
|
-
import fs from 'node:fs/promises'
|
|
178
|
-
import express from 'express'
|
|
179
|
-
import { createInitializedViteRouter } from '@ressjs/vite-router'
|
|
180
|
-
|
|
181
|
-
const isProduction = process.env.NODE_ENV === 'production'
|
|
182
|
-
const app = express()
|
|
183
|
-
|
|
184
|
-
let vite
|
|
185
|
-
if (!isProduction) {
|
|
186
|
-
const { createServer } = await import('vite')
|
|
187
|
-
vite = await createServer({
|
|
188
|
-
server: { middlewareMode: true },
|
|
189
|
-
appType: 'custom'
|
|
190
|
-
})
|
|
191
|
-
app.use(vite.middlewares)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Router automático con SSR
|
|
195
|
-
const viteRouter = await createInitializedViteRouter({
|
|
196
|
-
pagesDir: 'app/pages',
|
|
197
|
-
enableSSR: true,
|
|
198
|
-
enablePlatformDetection: true
|
|
199
|
-
})
|
|
127
|
+
The router expects the following structure:
|
|
200
128
|
|
|
201
|
-
|
|
202
|
-
app
|
|
129
|
+
```
|
|
130
|
+
app/
|
|
131
|
+
pages/
|
|
132
|
+
index.tsx # Home page
|
|
133
|
+
about/
|
|
134
|
+
index.tsx # About page
|
|
135
|
+
index.mobile.scss # Mobile styles
|
|
136
|
+
products/
|
|
137
|
+
[id].tsx # Dynamic route
|
|
138
|
+
[id].webview.ios.scss # iOS WebView styles
|
|
203
139
|
```
|
|
204
140
|
|
|
205
|
-
##
|
|
206
|
-
|
|
207
|
-
El router crea automáticamente el siguiente stack de middleware:
|
|
141
|
+
## Development
|
|
208
142
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
4. **SSR Rendering** - Renderiza servidor-side con React
|
|
213
|
-
5. **Static Files** - Fallback para archivos estáticos
|
|
143
|
+
```bash
|
|
144
|
+
# Build the package
|
|
145
|
+
npm run build
|
|
214
146
|
|
|
215
|
-
|
|
147
|
+
# Run tests
|
|
148
|
+
npm test
|
|
216
149
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
RouterFactoryOptions,
|
|
220
|
-
RouteContext,
|
|
221
|
-
PageRouterInfo,
|
|
222
|
-
EntryGeneratorOptions
|
|
223
|
-
} from '@ressjs/vite-router'
|
|
150
|
+
# Watch mode
|
|
151
|
+
npm run dev
|
|
224
152
|
```
|
|
225
153
|
|
|
226
|
-
##
|
|
154
|
+
## License
|
|
227
155
|
|
|
228
|
-
|
|
156
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PlatformInfo } from '../platform';
|
|
2
|
+
export interface HTMLGenerationContext {
|
|
3
|
+
template: string;
|
|
4
|
+
rendered: any;
|
|
5
|
+
page: any;
|
|
6
|
+
manifest: any;
|
|
7
|
+
platform: string;
|
|
8
|
+
isProduction: boolean;
|
|
9
|
+
platformInfo: PlatformInfo | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function generateClientScript(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): Promise<string | null>;
|
|
12
|
+
export declare function generateClientCSS(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): string;
|
|
13
|
+
export declare function generateCompleteHTML(context: HTMLGenerationContext): Promise<string>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateClientScript = generateClientScript;
|
|
7
|
+
exports.generateClientCSS = generateClientCSS;
|
|
8
|
+
exports.generateCompleteHTML = generateCompleteHTML;
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
async function generateClientScript(pageFile, platformInfo, manifest, isProduction) {
|
|
11
|
+
const rel = pageFile.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
|
|
12
|
+
let entryKey = `${rel}__entry-client.tsx`;
|
|
13
|
+
if (platformInfo && platformInfo.device !== 'desktop') {
|
|
14
|
+
// Priority order (from highest to lowest):
|
|
15
|
+
const priorities = [
|
|
16
|
+
// 1. Specific: device.os.tsx
|
|
17
|
+
`${rel}__entry-client.${platformInfo.device}.${platformInfo.os}.tsx`,
|
|
18
|
+
// 2. DeviceType: device.tsx
|
|
19
|
+
`${rel}__entry-client.${platformInfo.device}.tsx`,
|
|
20
|
+
// 3. Mobile Fallback: mobile.tsx (only for phone/tablet)
|
|
21
|
+
...(platformInfo.device === 'phone' || platformInfo.device === 'tablet'
|
|
22
|
+
? [`${rel}__entry-client.mobile.tsx`]
|
|
23
|
+
: []),
|
|
24
|
+
// 4. OS General: os.tsx
|
|
25
|
+
`${rel}__entry-client.${platformInfo.os}.tsx`
|
|
26
|
+
];
|
|
27
|
+
// Find first existing entry following priority order
|
|
28
|
+
for (const candidate of priorities) {
|
|
29
|
+
if (isProduction) {
|
|
30
|
+
// In production, check manifest
|
|
31
|
+
const manifestKey = `dist/.entries/${candidate}`;
|
|
32
|
+
if (manifest[manifestKey]) {
|
|
33
|
+
entryKey = candidate;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// In development, check if physical file exists
|
|
39
|
+
try {
|
|
40
|
+
await promises_1.default.access(`dist/.entries/${candidate}`);
|
|
41
|
+
entryKey = candidate;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// File doesn't exist, continue to next priority
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!isProduction) {
|
|
51
|
+
return `/dist/.entries/${entryKey}`;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const chunk = manifest[`dist/.entries/${entryKey}`];
|
|
55
|
+
return chunk ? `/${chunk.file}` : null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function generateClientCSS(pageFile, platformInfo, manifest, isProduction) {
|
|
59
|
+
if (!isProduction || !pageFile) {
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
const rel = pageFile.replace(/^app\/pages\//, '').replace(/\.tsx$|\.jsx$/, '').replace(/\//g, '__');
|
|
63
|
+
let entryKey = `dist/.entries/${rel}__entry-client.tsx`;
|
|
64
|
+
if (platformInfo && platformInfo.device !== 'desktop') {
|
|
65
|
+
// Priority order (from highest to lowest):
|
|
66
|
+
const priorities = [
|
|
67
|
+
// 1. Specific: device.os.tsx
|
|
68
|
+
`dist/.entries/${rel}__entry-client.${platformInfo.device}.${platformInfo.os}.tsx`,
|
|
69
|
+
// 2. DeviceType: device.tsx
|
|
70
|
+
`dist/.entries/${rel}__entry-client.${platformInfo.device}.tsx`,
|
|
71
|
+
// 3. Mobile Fallback: mobile.tsx (only for phone/tablet)
|
|
72
|
+
...(platformInfo.device === 'phone' || platformInfo.device === 'tablet'
|
|
73
|
+
? [`dist/.entries/${rel}__entry-client.mobile.tsx`]
|
|
74
|
+
: []),
|
|
75
|
+
// 4. OS General: os.tsx
|
|
76
|
+
`dist/.entries/${rel}__entry-client.${platformInfo.os}.tsx`
|
|
77
|
+
];
|
|
78
|
+
// Find first existing entry following priority order
|
|
79
|
+
for (const candidate of priorities) {
|
|
80
|
+
if (manifest[candidate]) {
|
|
81
|
+
entryKey = candidate;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const entryChunk = manifest[entryKey];
|
|
87
|
+
if (entryChunk && entryChunk.css) {
|
|
88
|
+
return entryChunk.css.map((cssFile) => `<link rel="stylesheet" href="/${cssFile}">`).join('');
|
|
89
|
+
}
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
92
|
+
async function generateCompleteHTML(context) {
|
|
93
|
+
const { template, rendered, page, manifest, isProduction, platformInfo } = context;
|
|
94
|
+
const scriptSrc = await generateClientScript(page.file, platformInfo, manifest, isProduction);
|
|
95
|
+
const scriptTag = scriptSrc ? `<script type="module" src="${scriptSrc}"></script>` : '';
|
|
96
|
+
const cssLinks = page?.file ? generateClientCSS(page.file, platformInfo, manifest, isProduction) : '';
|
|
97
|
+
const html = template
|
|
98
|
+
.replace('<!--app-head-->', cssLinks)
|
|
99
|
+
.replace('<!--app-html-->', rendered.html)
|
|
100
|
+
.replace('</body>', `<script>
|
|
101
|
+
window.__INITIAL_PROPS__=${JSON.stringify(rendered.props || {})};
|
|
102
|
+
window.__PAGE_ROUTE__=${JSON.stringify(page.route)};
|
|
103
|
+
window.__PAGE_FILE__=${JSON.stringify(page.file)};
|
|
104
|
+
</script>
|
|
105
|
+
${scriptTag}
|
|
106
|
+
</body>`);
|
|
107
|
+
return html;
|
|
108
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ViteConfig } from './vite-config';
|
|
2
|
+
export interface RequestContext {
|
|
3
|
+
req: any;
|
|
4
|
+
res: any;
|
|
5
|
+
page: any;
|
|
6
|
+
next: any;
|
|
7
|
+
}
|
|
8
|
+
export interface RouterConfig {
|
|
9
|
+
enablePlatformDetection: boolean;
|
|
10
|
+
renderFunction?: (options: {
|
|
11
|
+
url: string;
|
|
12
|
+
platform: string;
|
|
13
|
+
req: any;
|
|
14
|
+
}) => Promise<string>;
|
|
15
|
+
viteConfig: ViteConfig;
|
|
16
|
+
}
|
|
17
|
+
export declare function processRequest(context: RequestContext, config: RouterConfig, renderServer?: any): Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.processRequest = processRequest;
|
|
7
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
|
+
const pages_1 = require("../pages");
|
|
9
|
+
const platform_1 = require("../platform");
|
|
10
|
+
const html_generator_1 = require("./html-generator");
|
|
11
|
+
async function preparePlatform(req, enablePlatformDetection = true, isProduction = false) {
|
|
12
|
+
if (!enablePlatformDetection) {
|
|
13
|
+
return {
|
|
14
|
+
platform: 'desktop',
|
|
15
|
+
platformInfo: null
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const userAgent = req.headers['user-agent'] || '';
|
|
19
|
+
const platformInfo = (0, platform_1.detectPlatform)(userAgent, req.headers);
|
|
20
|
+
const platform = (platformInfo.device === 'phone' || platformInfo.device === 'tablet') ? 'mobile' : 'desktop';
|
|
21
|
+
if (!isProduction && platformInfo) {
|
|
22
|
+
await (0, pages_1.generateTempEntries)('dist', platformInfo);
|
|
23
|
+
}
|
|
24
|
+
return { platform, platformInfo };
|
|
25
|
+
}
|
|
26
|
+
async function processRequest(context, config, renderServer) {
|
|
27
|
+
const { req, res, page, next } = context;
|
|
28
|
+
const { enablePlatformDetection, renderFunction, viteConfig } = config;
|
|
29
|
+
try {
|
|
30
|
+
const platformContext = await preparePlatform(req, enablePlatformDetection, viteConfig.isProduction);
|
|
31
|
+
if (renderFunction) {
|
|
32
|
+
const html = await renderFunction({
|
|
33
|
+
url: req.url,
|
|
34
|
+
platform: platformContext.platform,
|
|
35
|
+
req
|
|
36
|
+
});
|
|
37
|
+
res.setHeader('Content-Type', 'text/html');
|
|
38
|
+
res.send(html);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!renderServer) {
|
|
42
|
+
const renderOptions = viteConfig.vite ? { vite: viteConfig.vite } : {};
|
|
43
|
+
renderServer = (0, pages_1.createRenderServer)(renderOptions);
|
|
44
|
+
}
|
|
45
|
+
const protocol = req.headers['x-forwarded-proto'] || (req.connection?.encrypted ? 'https' : 'http');
|
|
46
|
+
const host = req.headers.host || 'localhost';
|
|
47
|
+
const urlObj = new URL(req.url, `${protocol}://${host}`);
|
|
48
|
+
const pathname = urlObj.pathname;
|
|
49
|
+
const rendered = await renderServer({ url: pathname });
|
|
50
|
+
const template = await getTemplate(req, viteConfig);
|
|
51
|
+
const html = await (0, html_generator_1.generateCompleteHTML)({
|
|
52
|
+
template,
|
|
53
|
+
rendered,
|
|
54
|
+
page,
|
|
55
|
+
manifest: viteConfig.manifest,
|
|
56
|
+
platform: platformContext.platform,
|
|
57
|
+
isProduction: viteConfig.isProduction,
|
|
58
|
+
platformInfo: platformContext.platformInfo
|
|
59
|
+
});
|
|
60
|
+
res.setHeader('Content-Type', 'text/html');
|
|
61
|
+
res.send(html);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(`Error handling ${req.url}:`, error);
|
|
65
|
+
if (viteConfig.vite?.ssrFixStacktrace) {
|
|
66
|
+
viteConfig.vite.ssrFixStacktrace(error);
|
|
67
|
+
}
|
|
68
|
+
next(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function getTemplate(req, viteConfig) {
|
|
72
|
+
if (!viteConfig.isProduction) {
|
|
73
|
+
const template = await promises_1.default.readFile('./index.html', 'utf-8');
|
|
74
|
+
return viteConfig.vite ? await viteConfig.vite.transformIndexHtml(req.url, template) : template;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return viteConfig.templateHtml;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
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<Pick<ViteConfig, 'manifest' | 'templateHtml'>>;
|
|
9
|
+
export declare function createProdMiddleware(): () => Promise<import("express-serve-static-core").Router>;
|
|
10
|
+
export declare function setupVite(isProduction: boolean, base?: string): Promise<ViteConfig>;
|
|
@@ -0,0 +1,97 @@
|
|
|
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 { manifest, templateHtml };
|
|
67
|
+
}
|
|
68
|
+
function createProdMiddleware() {
|
|
69
|
+
const middlewares = express_1.default.Router();
|
|
70
|
+
return async () => {
|
|
71
|
+
const compression = (await Promise.resolve().then(() => __importStar(require('compression')))).default;
|
|
72
|
+
const sirv = (await Promise.resolve().then(() => __importStar(require('sirv')))).default;
|
|
73
|
+
middlewares.use(compression());
|
|
74
|
+
middlewares.use(sirv('./dist/client', { extensions: [] }));
|
|
75
|
+
return middlewares;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function setupVite(isProduction, base = '/') {
|
|
79
|
+
if (isProduction) {
|
|
80
|
+
const prodConfig = await setupProd();
|
|
81
|
+
return {
|
|
82
|
+
vite: undefined,
|
|
83
|
+
manifest: prodConfig.manifest,
|
|
84
|
+
templateHtml: prodConfig.templateHtml,
|
|
85
|
+
isProduction: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const devConfig = await setupDev(base);
|
|
90
|
+
return {
|
|
91
|
+
vite: devConfig.vite,
|
|
92
|
+
manifest: devConfig.manifest,
|
|
93
|
+
templateHtml: '',
|
|
94
|
+
isProduction: false
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
export { createViteRouter, createAutoViteServer,
|
|
2
|
-
export
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
7
|
-
export type { RouterOptions, PageRouterInfo } from './page-detector.js';
|
|
8
|
-
export type { EntryGeneratorOptions } from './entry-generator.js';
|
|
9
|
-
export type { PageInfo } from '@ressjs/core';
|
|
10
|
-
//# sourceMappingURL=index.d.ts.map
|
|
1
|
+
export { createViteRouter, createAutoViteServer, listDetectedPages, getPages } from './router';
|
|
2
|
+
export { getViteInput, getClientEntriesInput, generateTempEntries, createRenderServer, resolvePlatformCSS } from './pages';
|
|
3
|
+
export { detectPlatform, getPlatformCSSClass, resolveEntryClient } from './platform';
|
|
4
|
+
export type { PlatformInfo } from './platform';
|
|
5
|
+
export type { ViteRouterOptions, AutoViteServerOptions } from './router';
|
|
6
|
+
export type { PageInfo } from './pages';
|