vue-ssr-lite 0.2.15 → 0.2.18
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 +223 -970
- package/dist/SsrApplicationRuntime.d.ts +2 -2
- package/dist/SsrApplicationRuntime.d.ts.map +1 -1
- package/dist/SsrBrowserRuntime.d.ts +4 -4
- package/dist/SsrBrowserRuntime.d.ts.map +1 -1
- package/dist/SsrCanonicalOrigin.d.ts +21 -0
- package/dist/SsrCanonicalOrigin.d.ts.map +1 -0
- package/dist/SsrConfigCompileRuntime.d.ts +47 -24
- package/dist/SsrConfigCompileRuntime.d.ts.map +1 -1
- package/dist/SsrConfigTypes.d.ts +54 -22
- package/dist/SsrConfigTypes.d.ts.map +1 -1
- package/dist/SsrDomainRuntime.d.ts.map +1 -1
- package/dist/SsrEscape.d.ts +3 -0
- package/dist/SsrEscape.d.ts.map +1 -0
- package/dist/SsrManagedHead.d.ts +69 -0
- package/dist/SsrManagedHead.d.ts.map +1 -0
- package/dist/SsrPublicConfig.d.ts +5 -0
- package/dist/SsrPublicConfig.d.ts.map +1 -0
- package/dist/SsrRenderRuntime.d.ts +2 -2
- package/dist/SsrRenderRuntime.d.ts.map +1 -1
- package/dist/SsrRequestContext.d.ts +7 -2
- package/dist/SsrRequestContext.d.ts.map +1 -1
- package/dist/SsrResponseStatus.d.ts +19 -0
- package/dist/SsrResponseStatus.d.ts.map +1 -0
- package/dist/SsrRuntimeTypes.d.ts +49 -46
- package/dist/SsrRuntimeTypes.d.ts.map +1 -1
- package/dist/SsrSerialization.d.ts +2 -4
- package/dist/SsrSerialization.d.ts.map +1 -1
- package/dist/chunks/SsrApplicationRuntime-B9RwpVZI.mjs +398 -0
- package/dist/chunks/SsrConfigCompileRuntime-CdltAG8u.mjs +765 -0
- package/dist/chunks/SsrPublicConfig-DpPUzyp7.mjs +42 -0
- package/dist/chunks/SsrResponseStatus-BFKcIZXi.mjs +109 -0
- package/dist/chunks/SsrSerialization-DBzNdvSc.mjs +179 -0
- package/dist/chunks/SsrServerRuntime-wTuqOAdQ.mjs +760 -0
- package/dist/chunks/state-C1EVbCVi.mjs +177 -0
- package/dist/cli/SsrCliOptions.d.ts +3 -3
- package/dist/cli.mjs +2 -2
- package/dist/client.mjs +100 -71
- package/dist/core/extensions/ExtensionContext.d.ts +31 -0
- package/dist/core/extensions/ExtensionContext.d.ts.map +1 -0
- package/dist/core/extensions/ExtensionDefinition.d.ts +17 -0
- package/dist/core/extensions/ExtensionDefinition.d.ts.map +1 -0
- package/dist/core/extensions/ExtensionRuntime.d.ts +26 -0
- package/dist/core/extensions/ExtensionRuntime.d.ts.map +1 -0
- package/dist/core/extensions/defineExtension.d.ts +4 -0
- package/dist/core/extensions/defineExtension.d.ts.map +1 -0
- package/dist/core/extensions/index.d.ts +5 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/extensions/resolveBuiltInExtensions.d.ts +4 -0
- package/dist/extensions/resolveBuiltInExtensions.d.ts.map +1 -0
- package/dist/extensions/seo/SeoEndpoints.d.ts +15 -0
- package/dist/extensions/seo/SeoEndpoints.d.ts.map +1 -0
- package/dist/extensions/seo/client.d.ts +4 -0
- package/dist/extensions/seo/client.d.ts.map +1 -0
- package/dist/extensions/seo/index.d.ts +9 -0
- package/dist/extensions/seo/index.d.ts.map +1 -0
- package/dist/extensions/seo/normalize.d.ts +34 -0
- package/dist/extensions/seo/normalize.d.ts.map +1 -0
- package/dist/extensions/seo/robots.d.ts +3 -0
- package/dist/extensions/seo/robots.d.ts.map +1 -0
- package/dist/extensions/seo/server.d.ts +4 -0
- package/dist/extensions/seo/server.d.ts.map +1 -0
- package/dist/extensions/seo/sitemap.d.ts +14 -0
- package/dist/extensions/seo/sitemap.d.ts.map +1 -0
- package/dist/extensions/seo/state.d.ts +21 -0
- package/dist/extensions/seo/state.d.ts.map +1 -0
- package/dist/extensions/seo/types.d.ts +89 -0
- package/dist/extensions/seo/types.d.ts.map +1 -0
- package/dist/extensions/seo/useSeo.d.ts +3 -0
- package/dist/extensions/seo/useSeo.d.ts.map +1 -0
- package/dist/index.d.ts +11 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +54 -34
- package/dist/server/SsrHtmlRuntime.d.ts +3 -2
- package/dist/server/SsrHtmlRuntime.d.ts.map +1 -1
- package/dist/server/SsrServerRuntime.d.ts.map +1 -1
- package/dist/server/SsrSiteOriginRuntime.d.ts +19 -0
- package/dist/server/SsrSiteOriginRuntime.d.ts.map +1 -0
- package/dist/server/SsrSitemapConfig.d.ts +7 -0
- package/dist/server/SsrSitemapConfig.d.ts.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.mjs +95 -87
- package/dist/vite/SsrVitePlugin.d.ts.map +1 -1
- package/dist/vite.mjs +81 -64
- package/docs/refactorplan.md +2252 -0
- package/package.json +5 -8
- package/dist/chunks/SsrApplicationRuntime-CKjoPpvD.mjs +0 -272
- package/dist/chunks/SsrConfigRuntime-D0DkOrVQ.mjs +0 -4
- package/dist/chunks/SsrDiagnosticsRuntime-B5VbSgsA.mjs +0 -28
- package/dist/chunks/SsrHtmlRuntime-BJUxjM2F.mjs +0 -484
- package/dist/chunks/SsrReactivityRuntime-Cug-AFwk.mjs +0 -28
- package/dist/chunks/SsrSerialization-DeKozeIm.mjs +0 -80
- package/dist/chunks/SsrServerRuntime-BbWemkgs.mjs +0 -716
package/README.md
CHANGED
|
@@ -1,360 +1,103 @@
|
|
|
1
1
|
# vue-ssr-lite
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- SPA and SSR applications together
|
|
11
|
-
- Applications on different domains or subdomains
|
|
12
|
-
|
|
13
|
-
It includes routing, browser hydration, production builds, a managed Node server, health checks, custom endpoints, caching, timeouts, and Vue plugin support.
|
|
14
|
-
|
|
15
|
-
## Features
|
|
16
|
-
|
|
17
|
-
- Vue 3 server-side rendering
|
|
18
|
-
- Normal Vue SPA support
|
|
19
|
-
- SPA and SSR in one project
|
|
20
|
-
- Vue Router support
|
|
21
|
-
- Automatic browser hydration
|
|
22
|
-
- Multiple applications and HTML entries
|
|
23
|
-
- Domain and subdomain routing
|
|
24
|
-
- Runtime roles for one image / many process modes
|
|
25
|
-
- Generic `publicConfig` transport (Apollo, REST, Pinia, i18n stay in the app)
|
|
26
|
-
- SEO metadata and status codes
|
|
27
|
-
- Custom server endpoints
|
|
28
|
-
- Health and readiness checks
|
|
29
|
-
- Request timeouts
|
|
30
|
-
- Optional response caching
|
|
31
|
-
- Cookie allow / deny filtering (including deny-only)
|
|
32
|
-
- Proxy-aware host and protocol resolution
|
|
33
|
-
- Graceful shutdown
|
|
34
|
-
- TypeScript support
|
|
35
|
-
|
|
36
|
-
## Package Entry Points
|
|
3
|
+
Convention-first SSR for Vue 3 and Vite that scales from one normal Vue app to
|
|
4
|
+
many independent applications in one repository. A website, admin, dashboard,
|
|
5
|
+
shop, or customer portal can each choose SSR or SPA and be selected by domain
|
|
6
|
+
or subdomain—without separate repositories or manual server/browser bootstraps.
|
|
7
|
+
`vue-ssr-lite` owns the unified runtime, host routing, router history,
|
|
8
|
+
hydration, request isolation, and Node lifecycle while your applications remain
|
|
9
|
+
normal Vue applications.
|
|
37
10
|
|
|
38
|
-
|
|
39
|
-
| --- | --- |
|
|
40
|
-
| `vue-ssr-lite` | `defineSsrConfig`, `defineSsrApplication`, `useSsrDomain`, request context |
|
|
41
|
-
| `vue-ssr-lite/client` | Browser hydration and SPA mount |
|
|
42
|
-
| `vue-ssr-lite/server` | Managed Node server, config compile, host matching, cookies |
|
|
43
|
-
| `vue-ssr-lite/vite` | Vite plugin that wires HTML templates and virtual client entries |
|
|
44
|
-
|
|
45
|
-
CLI binary:
|
|
11
|
+
## Install
|
|
46
12
|
|
|
47
13
|
```bash
|
|
48
|
-
vue-ssr-lite
|
|
14
|
+
yarn add vue-ssr-lite
|
|
49
15
|
```
|
|
50
16
|
|
|
51
|
-
|
|
52
|
-
(or take `--config`).
|
|
53
|
-
- `start` loads the baked production runtime
|
|
54
|
-
(`dist/server/SsrRuntime.js` or `--server-output`) and does **not** require
|
|
55
|
-
source `ssr.config.*` in the working directory — slim Docker images only need
|
|
56
|
-
`package.json`, production `node_modules`, and `dist/`.
|
|
57
|
-
|
|
58
|
-
Requires Node.js 20 or newer.
|
|
59
|
-
|
|
60
|
-
Development servers use an operating-system-assigned HMR WebSocket port, so
|
|
61
|
-
multiple `vue-ssr-lite dev` processes can run concurrently. Use `--hmr-port`
|
|
62
|
-
or `VUE_SSR_LITE_HMR_PORT` when a proxy or container requires a fixed port.
|
|
63
|
-
|
|
64
|
-
## Installation
|
|
17
|
+
or:
|
|
65
18
|
|
|
66
19
|
```bash
|
|
67
|
-
npm install vue-ssr-lite
|
|
68
|
-
npm install --save-dev vite @vitejs/plugin-vue
|
|
20
|
+
npm install vue-ssr-lite
|
|
69
21
|
```
|
|
70
22
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Every project follows the same five pieces. `ssr.config` is the single source of
|
|
81
|
-
truth — there is no `spaEntries`, no `kind`, no manual `main.ts` mount, and no
|
|
82
|
-
`server.role`.
|
|
83
|
-
|
|
84
|
-
| Piece | Role |
|
|
85
|
-
| --- | --- |
|
|
86
|
-
| `ssr.config.ts` | Apps, domains, `runtime`, cookies, endpoints |
|
|
87
|
-
| `defineSsrApplication()` modules | One module per app (`application.module`) |
|
|
88
|
-
| HTML shells | Mount node only — **no** bootstrap `<script type="module" src>` |
|
|
89
|
-
| `vite.config.ts` | `vue()` + `vueSsrLite()` |
|
|
90
|
-
| CLI scripts | `vue-ssr-lite dev \| build \| start` |
|
|
91
|
-
|
|
92
|
-
Render modes (declared as `render` on each application):
|
|
93
|
-
|
|
94
|
-
| `render` | Behavior |
|
|
95
|
-
| --- | --- |
|
|
96
|
-
| `spa` | Serves the HTML shell; the plugin generates the browser mount entry |
|
|
97
|
-
| `ssr` | Server-renders Vue and generates the hydration client entry |
|
|
98
|
-
|
|
99
|
-
`vueSsrLite()` defaults the client Vite `build.outDir` to `dist/client` so
|
|
100
|
-
`vue-ssr-lite start` finds assets without a consumer override. Set
|
|
101
|
-
`build.outDir` or `server.clientOutDir` only when you need a different path.
|
|
102
|
-
|
|
103
|
-
Production compile requires top-level `runtime` (typically `APP_RUNTIME`) and
|
|
104
|
-
each app’s `domain.production`. `publicConfig` is opaque — validating API URLs
|
|
105
|
-
is the consumer’s job.
|
|
106
|
-
|
|
107
|
-
For async config, export a factory that *returns* `defineSsrConfig(...)`:
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
export default async () =>
|
|
111
|
-
defineSsrConfig({
|
|
112
|
-
name: 'my-platform',
|
|
113
|
-
runtime: process.env.APP_RUNTIME,
|
|
114
|
-
applications: { /* ... */ },
|
|
115
|
-
})
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`defineSsrConfig` itself is synchronous and accepts a plain object.
|
|
119
|
-
|
|
120
|
-
See `fixtures/clean-consumer` in this repo for a minimal SPA project that
|
|
121
|
-
matches this contract.
|
|
122
|
-
|
|
123
|
-
## Quick Start: SPA and SSR Together
|
|
124
|
-
|
|
125
|
-
The following example creates:
|
|
126
|
-
|
|
127
|
-
- A dashboard SPA on `app.example.com`
|
|
128
|
-
- A server-rendered website on all other hosts
|
|
129
|
-
|
|
130
|
-
## 1. Create the SPA Application
|
|
23
|
+
`vue-ssr-lite` is designed to be added to an existing Vue 3 + Vite
|
|
24
|
+
application. It uses the application's existing Vue and Vite installation; no
|
|
25
|
+
separate `@vue/server-renderer` installation is required. `vue-router` is
|
|
26
|
+
provided by `vue-ssr-lite` for the simple `routes` API because the runtime
|
|
27
|
+
creates that router. If application code directly imports Vue Router APIs for
|
|
28
|
+
the advanced router factory below, keep `vue-router` as a direct dependency of
|
|
29
|
+
the host application (existing routed applications should keep their current
|
|
30
|
+
dependency).
|
|
131
31
|
|
|
132
|
-
|
|
133
|
-
installation, public-config delivery, and mounting via the generated virtual
|
|
134
|
-
client — do **not** add a `main.ts` bootstrap.
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
// src/dashboard/DashboardBootstrap.ts
|
|
138
|
-
import { defineSsrApplication } from 'vue-ssr-lite'
|
|
139
|
-
import App from './App.vue'
|
|
140
|
-
import routes from './routes'
|
|
141
|
-
|
|
142
|
-
export const createDashboardApplication = defineSsrApplication({
|
|
143
|
-
id: 'dashboard',
|
|
144
|
-
rootComponent: App,
|
|
145
|
-
routes,
|
|
146
|
-
plugins: [/* your Vue plugins */],
|
|
147
|
-
})
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Create the SPA root component:
|
|
32
|
+
Requires Node.js 20 or newer.
|
|
151
33
|
|
|
152
|
-
|
|
153
|
-
<!-- src/dashboard/App.vue -->
|
|
154
|
-
<script setup lang="ts">
|
|
155
|
-
import { RouterView } from 'vue-router'
|
|
156
|
-
</script>
|
|
34
|
+
## One repo, one app or many
|
|
157
35
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
```
|
|
36
|
+
Start with the zero-config path when the repository contains one application.
|
|
37
|
+
When a product grows, add `applications` to keep multiple independently routed
|
|
38
|
+
Vue applications in the same codebase:
|
|
162
39
|
|
|
163
|
-
|
|
40
|
+
```text
|
|
41
|
+
my-platform/
|
|
42
|
+
├── src/
|
|
43
|
+
│ ├── website/main.ts → example.com → SSR
|
|
44
|
+
│ ├── admin/main.ts → admin.example.com → SPA
|
|
45
|
+
│ ├── shop/main.ts → shop.example.com → SSR
|
|
46
|
+
│ └── dashboard/main.ts → app.example.com → SPA
|
|
47
|
+
├── index.html
|
|
48
|
+
├── ssr.config.ts
|
|
49
|
+
├── vite.config.ts
|
|
50
|
+
└── package.json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
One unified `vue-ssr-lite` runtime/server selects the application from the
|
|
54
|
+
incoming host; different subdomains do not require separate repositories or
|
|
55
|
+
separate SSR implementations:
|
|
164
56
|
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<meta
|
|
172
|
-
name="viewport"
|
|
173
|
-
content="width=device-width, initial-scale=1" />
|
|
174
|
-
<title>Dashboard</title>
|
|
175
|
-
</head>
|
|
176
|
-
|
|
177
|
-
<body>
|
|
178
|
-
<div id="app"></div>
|
|
179
|
-
</body>
|
|
180
|
-
</html>
|
|
57
|
+
```text
|
|
58
|
+
one repository → vue-ssr-lite runtime
|
|
59
|
+
example.com → website → SSR
|
|
60
|
+
admin.example.com → admin → SPA
|
|
61
|
+
shop.example.com → shop → SSR
|
|
62
|
+
app.example.com → dashboard → SPA
|
|
181
63
|
```
|
|
182
64
|
|
|
183
|
-
##
|
|
184
|
-
|
|
185
|
-
Create the SSR root component:
|
|
65
|
+
## Zero-config single application
|
|
186
66
|
|
|
187
|
-
|
|
188
|
-
<!-- src/website/App.vue -->
|
|
189
|
-
<script setup lang="ts">
|
|
190
|
-
import { RouterView } from 'vue-router'
|
|
191
|
-
</script>
|
|
67
|
+
Start with the structure Vite already gives you:
|
|
192
68
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
import HomePage from './pages/HomePage.vue'
|
|
205
|
-
import AboutPage from './pages/AboutPage.vue'
|
|
206
|
-
import NotFoundPage from './pages/NotFoundPage.vue'
|
|
207
|
-
|
|
208
|
-
export const websiteRoutes: RouteRecordRaw[] = [
|
|
209
|
-
{
|
|
210
|
-
path: '/',
|
|
211
|
-
component: HomePage,
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
path: '/about',
|
|
215
|
-
component: AboutPage,
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
path: '/:pathMatch(.*)*',
|
|
219
|
-
component: NotFoundPage,
|
|
220
|
-
},
|
|
221
|
-
]
|
|
69
|
+
```text
|
|
70
|
+
my-vue-app/
|
|
71
|
+
├── src/
|
|
72
|
+
│ ├── main.ts
|
|
73
|
+
│ ├── App.vue
|
|
74
|
+
│ ├── router/
|
|
75
|
+
│ └── ...
|
|
76
|
+
├── index.html
|
|
77
|
+
├── vite.config.ts
|
|
78
|
+
└── package.json
|
|
222
79
|
```
|
|
223
80
|
|
|
224
|
-
|
|
81
|
+
Rewrite the existing browser bootstrap as a universal application definition:
|
|
225
82
|
|
|
226
83
|
```ts
|
|
227
|
-
// src/
|
|
228
|
-
import {
|
|
229
|
-
|
|
84
|
+
// src/main.ts
|
|
85
|
+
import { defineApplication } from 'vue-ssr-lite'
|
|
230
86
|
import App from './App.vue'
|
|
231
|
-
import
|
|
232
|
-
|
|
233
|
-
export const websiteApplication = defineSsrApplication({
|
|
234
|
-
id: 'website',
|
|
235
|
-
rootComponent: App,
|
|
236
|
-
routes: websiteRoutes,
|
|
237
|
-
})
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Create its HTML template:
|
|
241
|
-
|
|
242
|
-
```html
|
|
243
|
-
<!-- site.html -->
|
|
244
|
-
<!doctype html>
|
|
245
|
-
<html lang="en">
|
|
246
|
-
<head>
|
|
247
|
-
<meta charset="UTF-8" />
|
|
248
|
-
<meta
|
|
249
|
-
name="viewport"
|
|
250
|
-
content="width=device-width, initial-scale=1" />
|
|
251
|
-
</head>
|
|
252
|
-
|
|
253
|
-
<body>
|
|
254
|
-
<div id="app"></div>
|
|
255
|
-
</body>
|
|
256
|
-
</html>
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
The SSR browser entry and hydration setup are added automatically.
|
|
260
|
-
|
|
261
|
-
## 3. Create `ssr.config.ts`
|
|
87
|
+
import routes from './router/routes'
|
|
262
88
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
// ssr.config.ts — the only application / domain registration source
|
|
269
|
-
import { defineSsrConfig } from 'vue-ssr-lite'
|
|
270
|
-
|
|
271
|
-
export default defineSsrConfig({
|
|
272
|
-
name: 'my-platform',
|
|
273
|
-
runtime: process.env.APP_RUNTIME, // required in production
|
|
274
|
-
server: {
|
|
275
|
-
host: '0.0.0.0',
|
|
276
|
-
port: Number(process.env.PORT || 4173),
|
|
277
|
-
trustProxy: true,
|
|
278
|
-
},
|
|
279
|
-
applications: {
|
|
280
|
-
dashboard: {
|
|
281
|
-
render: 'spa',
|
|
282
|
-
application: {
|
|
283
|
-
module: './src/dashboard/DashboardBootstrap.ts',
|
|
284
|
-
exportName: 'createDashboardApplication',
|
|
285
|
-
},
|
|
286
|
-
template: 'index.html',
|
|
287
|
-
roles: ['unified', 'dashboard'],
|
|
288
|
-
domain: {
|
|
289
|
-
development: 'localhost',
|
|
290
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
291
|
-
mode: 'root-and-subdomains',
|
|
292
|
-
localAliases: true,
|
|
293
|
-
params: {
|
|
294
|
-
workspace: { source: 'last-subdomain-label' },
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
publicConfig: {
|
|
298
|
-
api: {
|
|
299
|
-
endpoint: process.env.VITE_GRAPHQL_ENDPOINT!,
|
|
300
|
-
timeout: 8_000,
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
website: {
|
|
305
|
-
render: 'ssr',
|
|
306
|
-
application: {
|
|
307
|
-
module: './src/website/SsrApplication.ts',
|
|
308
|
-
exportName: 'websiteApplication',
|
|
309
|
-
},
|
|
310
|
-
template: 'site.html',
|
|
311
|
-
roles: ['unified', 'website'],
|
|
312
|
-
domain: {
|
|
313
|
-
development: 'shop.localhost',
|
|
314
|
-
production: process.env.VITE_SHOP_BASE_DOMAIN!,
|
|
315
|
-
mode: 'root-and-subdomains',
|
|
316
|
-
customDomains: true,
|
|
317
|
-
params: {
|
|
318
|
-
storeDomain: { source: 'subdomain-or-hostname' },
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
publicConfig: {
|
|
322
|
-
api: {
|
|
323
|
-
endpoint: process.env.VITE_GRAPHQL_ENDPOINT!,
|
|
324
|
-
timeout: 8_000,
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
},
|
|
89
|
+
export default defineApplication({
|
|
90
|
+
root: App,
|
|
91
|
+
routes,
|
|
329
92
|
})
|
|
330
93
|
```
|
|
331
94
|
|
|
332
|
-
The
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
1. Exact hostname
|
|
338
|
-
2. Longest matching wildcard suffix (for example `*.shop.example.com` beats `*.example.com`)
|
|
339
|
-
3. Shorter matching wildcard suffix
|
|
340
|
-
4. Catch-all `*` (from `customDomains: true`)
|
|
341
|
-
5. `defaultApplicationId` when no host pattern matches
|
|
342
|
-
|
|
343
|
-
Overlapping wildcards are valid. Duplicate exact or identical wildcard patterns across applications are rejected at startup.
|
|
344
|
-
|
|
345
|
-
When the server starts, the console prints a ready message with a clickable local URL and the active role:
|
|
346
|
-
|
|
347
|
-
```text
|
|
348
|
-
✓ Server Ready
|
|
349
|
-
|
|
350
|
-
➜ Local: http://localhost:4173/
|
|
351
|
-
➜ Role: unified
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
## 4. Configure Vite
|
|
95
|
+
The application definition describes the Vue application. The runtime chooses
|
|
96
|
+
`createSSRApp`, request-safe memory history, web history, hydration, and plugin
|
|
97
|
+
installation for the active environment. Do not call `createApp().mount()` in
|
|
98
|
+
this file.
|
|
355
99
|
|
|
356
|
-
|
|
357
|
-
stays in `ssr.config.ts`:
|
|
100
|
+
The normal Vite integration is one plugin line:
|
|
358
101
|
|
|
359
102
|
```ts
|
|
360
103
|
// vite.config.ts
|
|
@@ -363,719 +106,229 @@ import vue from '@vitejs/plugin-vue'
|
|
|
363
106
|
import { vueSsrLite } from 'vue-ssr-lite/vite'
|
|
364
107
|
|
|
365
108
|
export default defineConfig({
|
|
366
|
-
|
|
367
|
-
vueSsrLite(),
|
|
368
|
-
vue(),
|
|
369
|
-
],
|
|
109
|
+
plugins: [vue(), vueSsrLite()],
|
|
370
110
|
})
|
|
371
111
|
```
|
|
372
112
|
|
|
373
|
-
`
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
- Client `build.outDir` defaulting to `dist/client` (aligned with the Node server)
|
|
377
|
-
- `virtual:vue-ssr-lite/client/<id>` SPA mount / SSR hydrate entries
|
|
378
|
-
- `virtual:vue-ssr-lite/runtime` for the Node server (SSR apps only; SPA modules are omitted)
|
|
379
|
-
|
|
380
|
-
HTML templates should not include a manual `<script type="module" src="...">`
|
|
381
|
-
bootstrap — the plugin strips those tags from matched templates and injects the
|
|
382
|
-
generated client entry. Editing `ssr.config.*` invalidates the cached config and
|
|
383
|
-
triggers a full reload in dev.
|
|
384
|
-
|
|
385
|
-
## 5. Add Commands
|
|
113
|
+
There is no `ssr.config.ts`, `app.ts`, `entry-client.ts`, `entry-server.ts`,
|
|
114
|
+
`site.html`, duplicate application ID, manual hydration, or explicit domain
|
|
115
|
+
configuration in this path. Add the normal lifecycle scripts:
|
|
386
116
|
|
|
387
117
|
```json
|
|
388
118
|
{
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
119
|
+
"scripts": {
|
|
120
|
+
"dev": "vue-ssr-lite dev",
|
|
121
|
+
"build": "vue-ssr-lite build",
|
|
122
|
+
"start": "vue-ssr-lite start"
|
|
123
|
+
}
|
|
394
124
|
}
|
|
395
125
|
```
|
|
396
126
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
```bash
|
|
400
|
-
npm run dev
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
Build and run production:
|
|
127
|
+
The defaults are:
|
|
404
128
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
129
|
+
| Concern | Convention |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| Application | `./src/main.ts` |
|
|
132
|
+
| HTML template | `./index.html` |
|
|
133
|
+
| Mount target | `#app` |
|
|
134
|
+
| Render mode | `ssr` |
|
|
135
|
+
| Single-app host | incoming `Host` header (`*`) |
|
|
136
|
+
| Server port | `PORT`, then the library default |
|
|
137
|
+
| Runtime role | `unified` |
|
|
409
138
|
|
|
410
|
-
##
|
|
139
|
+
## Existing `index.html` stays yours
|
|
411
140
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
name: 'my-website',
|
|
415
|
-
runtime: process.env.APP_RUNTIME,
|
|
416
|
-
applications: {
|
|
417
|
-
website: {
|
|
418
|
-
render: 'ssr',
|
|
419
|
-
application: {
|
|
420
|
-
module: './src/website/SsrApplication.ts',
|
|
421
|
-
exportName: 'websiteApplication',
|
|
422
|
-
},
|
|
423
|
-
template: 'site.html',
|
|
424
|
-
domain: {
|
|
425
|
-
development: 'localhost',
|
|
426
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
427
|
-
customDomains: true,
|
|
428
|
-
},
|
|
429
|
-
publicConfig: {
|
|
430
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
},
|
|
434
|
-
})
|
|
435
|
-
```
|
|
141
|
+
Keep the existing Vite HTML, including metadata, favicon links, styles,
|
|
142
|
+
analytics, verification tags, and unrelated module scripts:
|
|
436
143
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
applications: {
|
|
444
|
-
dashboard: {
|
|
445
|
-
render: 'spa',
|
|
446
|
-
application: {
|
|
447
|
-
module: './src/dashboard/DashboardBootstrap.ts',
|
|
448
|
-
exportName: 'createDashboardApplication',
|
|
449
|
-
},
|
|
450
|
-
template: 'index.html',
|
|
451
|
-
domain: {
|
|
452
|
-
development: 'localhost',
|
|
453
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
454
|
-
localAliases: true,
|
|
455
|
-
},
|
|
456
|
-
publicConfig: {
|
|
457
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
458
|
-
},
|
|
459
|
-
},
|
|
460
|
-
},
|
|
461
|
-
})
|
|
144
|
+
```html
|
|
145
|
+
<body>
|
|
146
|
+
<div id="app"></div>
|
|
147
|
+
<script type="module" src="/src/main.ts"></script>
|
|
148
|
+
<script type="module" src="/analytics.ts"></script>
|
|
149
|
+
</body>
|
|
462
150
|
```
|
|
463
151
|
|
|
464
|
-
The
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
## Using the Same Vue Plugins
|
|
152
|
+
The plugin replaces only the configured application bootstrap and preserves the
|
|
153
|
+
other module scripts. It adds the generated hydration entry and the internal
|
|
154
|
+
SSR markers without requiring a second HTML file.
|
|
468
155
|
|
|
469
|
-
|
|
156
|
+
## Optional `ssr.config.ts` overrides
|
|
470
157
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
import { defineApollo } from 'vue-apollo-client'
|
|
474
|
-
|
|
475
|
-
export default defineApollo(({ publicConfig }) => ({
|
|
476
|
-
endPoints: {
|
|
477
|
-
default: publicConfig?.api?.endpoint,
|
|
478
|
-
},
|
|
479
|
-
requestTimeoutMs: publicConfig?.api?.timeout,
|
|
480
|
-
}))
|
|
481
|
-
```
|
|
158
|
+
Configuration is an override layer. Unspecified values continue to use the
|
|
159
|
+
conventions above.
|
|
482
160
|
|
|
483
|
-
|
|
161
|
+
Custom application entry:
|
|
484
162
|
|
|
485
163
|
```ts
|
|
486
|
-
|
|
487
|
-
import { defineSsrApplication } from 'vue-ssr-lite'
|
|
488
|
-
|
|
489
|
-
import apollo from '../config/apollo'
|
|
490
|
-
import App from './App.vue'
|
|
491
|
-
import { websiteRoutes } from './routes'
|
|
164
|
+
import { defineSsrConfig } from 'vue-ssr-lite'
|
|
492
165
|
|
|
493
|
-
export
|
|
494
|
-
|
|
495
|
-
rootComponent: App,
|
|
496
|
-
routes: websiteRoutes,
|
|
497
|
-
plugins: [apollo],
|
|
166
|
+
export default defineSsrConfig({
|
|
167
|
+
app: './src/platform/main.ts',
|
|
498
168
|
})
|
|
499
169
|
```
|
|
500
170
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
You can use the same approach with:
|
|
504
|
-
|
|
505
|
-
- Apollo and GraphQL
|
|
506
|
-
- Pinia
|
|
507
|
-
- i18n
|
|
508
|
-
- Theme providers
|
|
509
|
-
- REST clients
|
|
510
|
-
- Authentication plugins
|
|
511
|
-
- Analytics plugins
|
|
512
|
-
- Custom Vue plugins
|
|
513
|
-
|
|
514
|
-
## Using GraphQL in SSR
|
|
515
|
-
|
|
516
|
-
Keep GraphQL operations in `.graphql` files:
|
|
517
|
-
|
|
518
|
-
```graphql
|
|
519
|
-
query GetPosts {
|
|
520
|
-
posts {
|
|
521
|
-
id
|
|
522
|
-
title
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
Use the generated composable in the Vue page:
|
|
528
|
-
|
|
529
|
-
```vue
|
|
530
|
-
<script setup lang="ts">
|
|
531
|
-
import { useGetPostsQuery } from '../graphql'
|
|
532
|
-
|
|
533
|
-
const { result, loading, error } = useGetPostsQuery(
|
|
534
|
-
{},
|
|
535
|
-
{
|
|
536
|
-
ssr: true,
|
|
537
|
-
fetchPolicy: 'cache-first',
|
|
538
|
-
},
|
|
539
|
-
)
|
|
540
|
-
</script>
|
|
541
|
-
|
|
542
|
-
<template>
|
|
543
|
-
<main>
|
|
544
|
-
<p v-if="loading">Loading...</p>
|
|
545
|
-
|
|
546
|
-
<p v-else-if="error">
|
|
547
|
-
{{ error.message }}
|
|
548
|
-
</p>
|
|
549
|
-
|
|
550
|
-
<article
|
|
551
|
-
v-for="post in result?.posts ?? []"
|
|
552
|
-
:key="post.id">
|
|
553
|
-
<h2>{{ post.title }}</h2>
|
|
554
|
-
</article>
|
|
555
|
-
</main>
|
|
556
|
-
</template>
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
The same generated composable can also be used inside the SPA.
|
|
560
|
-
|
|
561
|
-
Use `ssr: true` for public data that should be included in server-rendered HTML.
|
|
562
|
-
|
|
563
|
-
Use `ssr: false` for browser-only or private queries.
|
|
564
|
-
|
|
565
|
-
## Using REST APIs
|
|
566
|
-
|
|
567
|
-
REST APIs can be used through:
|
|
568
|
-
|
|
569
|
-
- Native `fetch`
|
|
570
|
-
- Axios
|
|
571
|
-
- Ky
|
|
572
|
-
- A custom Vue plugin
|
|
573
|
-
- Another SSR-compatible data package
|
|
574
|
-
|
|
575
|
-
Example:
|
|
576
|
-
|
|
577
|
-
```vue
|
|
578
|
-
<script setup lang="ts">
|
|
579
|
-
import { onServerPrefetch, ref } from 'vue'
|
|
580
|
-
|
|
581
|
-
interface Post {
|
|
582
|
-
id: string
|
|
583
|
-
title: string
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const posts = ref<Post[]>([])
|
|
587
|
-
|
|
588
|
-
const loadPosts = async () => {
|
|
589
|
-
const response = await fetch('https://api.example.com/posts')
|
|
590
|
-
|
|
591
|
-
posts.value = await response.json()
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
onServerPrefetch(loadPosts)
|
|
595
|
-
</script>
|
|
596
|
-
|
|
597
|
-
<template>
|
|
598
|
-
<article
|
|
599
|
-
v-for="post in posts"
|
|
600
|
-
:key="post.id">
|
|
601
|
-
<h2>{{ post.title }}</h2>
|
|
602
|
-
</article>
|
|
603
|
-
</template>
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
For reusable REST caching and hydration, register an SSR-compatible Vue plugin in the `plugins` array.
|
|
607
|
-
|
|
608
|
-
## Public Configuration
|
|
609
|
-
|
|
610
|
-
Pass opaque, browser-safe values per application in `ssr.config`:
|
|
171
|
+
Custom template or mount target:
|
|
611
172
|
|
|
612
173
|
```ts
|
|
613
|
-
applications: {
|
|
614
|
-
website: {
|
|
615
|
-
// ...
|
|
616
|
-
publicConfig: {
|
|
617
|
-
apiUrl: 'https://api.example.com',
|
|
618
|
-
graphqlEndpoint: 'https://api.example.com/graphql',
|
|
619
|
-
},
|
|
620
|
-
},
|
|
621
|
-
}
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
Access them in a component:
|
|
625
|
-
|
|
626
|
-
```ts
|
|
627
|
-
import { useSsrRequestContext } from 'vue-ssr-lite'
|
|
628
|
-
|
|
629
|
-
const context = useSsrRequestContext()
|
|
630
|
-
const apiUrl = context.publicConfig.apiUrl
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
The library does not interpret `publicConfig` (no GraphQL/REST assumptions).
|
|
634
|
-
Validate transport URLs in your own plugins or env checks.
|
|
635
|
-
|
|
636
|
-
## SEO
|
|
637
|
-
|
|
638
|
-
Set page metadata from a component:
|
|
639
|
-
|
|
640
|
-
```ts
|
|
641
|
-
import { useSsrRequestContext } from 'vue-ssr-lite'
|
|
642
|
-
|
|
643
|
-
const context = useSsrRequestContext()
|
|
644
|
-
|
|
645
|
-
context.head.value = {
|
|
646
|
-
title: 'Products',
|
|
647
|
-
description: 'Browse our latest products.',
|
|
648
|
-
robots: 'index, follow',
|
|
649
|
-
canonicalUrl: 'https://example.com/products',
|
|
650
|
-
ogTitle: 'Products',
|
|
651
|
-
ogDescription: 'Browse our latest products.',
|
|
652
|
-
ogImage: 'https://example.com/social.jpg',
|
|
653
|
-
twitterCard: 'summary_large_image',
|
|
654
|
-
}
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
## Status Codes
|
|
658
|
-
|
|
659
|
-
```ts
|
|
660
|
-
const context = useSsrRequestContext()
|
|
661
|
-
|
|
662
|
-
context.response.statusCode = 404
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
## Redirects
|
|
666
|
-
|
|
667
|
-
```ts
|
|
668
|
-
const context = useSsrRequestContext()
|
|
669
|
-
|
|
670
|
-
context.response.redirect = {
|
|
671
|
-
location: '/new-page',
|
|
672
|
-
statusCode: 307,
|
|
673
|
-
}
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
## Domain Routing
|
|
677
|
-
|
|
678
|
-
Domain configuration lives on each application. The library owns normalization
|
|
679
|
-
(ports, trailing dots, IPv4/IPv6 aliases), proxy header handling, environment
|
|
680
|
-
selection (`development` vs `production`), host specificity matching, custom
|
|
681
|
-
domains, context serialization, and hydration.
|
|
682
|
-
|
|
683
|
-
```ts
|
|
684
|
-
import { defineSsrConfig, useSsrDomain } from 'vue-ssr-lite'
|
|
685
|
-
|
|
686
174
|
export default defineSsrConfig({
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
server: { trustProxy: true },
|
|
690
|
-
applications: {
|
|
691
|
-
admin: {
|
|
692
|
-
render: 'spa',
|
|
693
|
-
application: {
|
|
694
|
-
module: './src/admin/AdminBootstrap.ts',
|
|
695
|
-
exportName: 'createAdminApplication',
|
|
696
|
-
},
|
|
697
|
-
template: 'index.html',
|
|
698
|
-
domain: {
|
|
699
|
-
development: 'localhost',
|
|
700
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
701
|
-
mode: 'root-and-subdomains',
|
|
702
|
-
localAliases: true,
|
|
703
|
-
params: {
|
|
704
|
-
workspace: { source: 'last-subdomain-label' },
|
|
705
|
-
},
|
|
706
|
-
},
|
|
707
|
-
publicConfig: {
|
|
708
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
website: {
|
|
712
|
-
render: 'ssr',
|
|
713
|
-
application: {
|
|
714
|
-
module: './src/website/SsrApplication.ts',
|
|
715
|
-
exportName: 'websiteApplication',
|
|
716
|
-
},
|
|
717
|
-
template: 'site.html',
|
|
718
|
-
domain: {
|
|
719
|
-
development: 'shop.localhost',
|
|
720
|
-
production: process.env.VITE_SHOP_BASE_DOMAIN!,
|
|
721
|
-
mode: 'root-and-subdomains',
|
|
722
|
-
customDomains: true,
|
|
723
|
-
params: {
|
|
724
|
-
storeDomain: { source: 'subdomain-or-hostname' },
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
publicConfig: {
|
|
728
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
729
|
-
},
|
|
730
|
-
},
|
|
731
|
-
},
|
|
175
|
+
template: './website.html',
|
|
176
|
+
mount: '#website',
|
|
732
177
|
})
|
|
733
178
|
```
|
|
734
179
|
|
|
735
|
-
|
|
180
|
+
Flat advanced single-app options stay flat:
|
|
736
181
|
|
|
737
182
|
```ts
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
183
|
+
export default defineSsrConfig({
|
|
184
|
+
server: {
|
|
185
|
+
trustProxy: true,
|
|
186
|
+
},
|
|
187
|
+
publicConfig: {
|
|
188
|
+
apiUrl: process.env.API_URL,
|
|
189
|
+
},
|
|
190
|
+
cookies: {
|
|
191
|
+
allow: ['session'],
|
|
192
|
+
},
|
|
193
|
+
})
|
|
748
194
|
```
|
|
749
195
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
| Priority | Pattern | Example winner |
|
|
753
|
-
| --- | --- | --- |
|
|
754
|
-
| 1 | Exact hostname | `shop.localhost` over `*.localhost` |
|
|
755
|
-
| 2 | Longer wildcard suffix | `*.shop.localhost` over `*.localhost` |
|
|
756
|
-
| 3 | Shorter wildcard suffix | `*.localhost` over `*` |
|
|
757
|
-
| 4 | Catch-all `*` | custom domains with no specific rule |
|
|
758
|
-
| 5 | `defaultApplicationId` | only when no pattern matches |
|
|
759
|
-
|
|
760
|
-
Overlapping roots such as `*.localhost` and `*.shop.localhost` are supported
|
|
761
|
-
because the longer suffix wins. Duplicate exact/wildcard ownership and multiple
|
|
762
|
-
catch-all (`*`) applications are rejected at startup.
|
|
763
|
-
|
|
764
|
-
Do not include protocols, paths, query strings, or ports in domain values.
|
|
765
|
-
|
|
766
|
-
`localAliases: true` adds bare loopback hosts (`localhost`, `127.0.0.1`, …) only
|
|
767
|
-
when the app’s development base is itself a loopback hostname. Subdomain bases
|
|
768
|
-
such as `shop.localhost` already expand to `*.shop.localhost` and do not claim
|
|
769
|
-
bare `localhost`, so multiple apps can enable `localAliases` safely.
|
|
196
|
+
Do not put single-application fields beside `applications`. Mixed configuration
|
|
197
|
+
is rejected early; move the field into the relevant application entry instead.
|
|
770
198
|
|
|
771
|
-
|
|
199
|
+
## Plugins and advanced routers
|
|
772
200
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
Roles let one codebase and one Docker image run as different process modes.
|
|
776
|
-
|
|
777
|
-
- Set top-level `runtime` to the active mode for this process.
|
|
778
|
-
- Set `roles` on each application to list which modes may serve it.
|
|
779
|
-
- Omit `roles` (or omit `runtime`) to keep an application available in every mode.
|
|
780
|
-
|
|
781
|
-
Role names are application-defined strings. Common patterns:
|
|
782
|
-
|
|
783
|
-
| Mode | Typical use |
|
|
784
|
-
| --- | --- |
|
|
785
|
-
| `unified` | Local development or a single process that serves every application |
|
|
786
|
-
| A private role | SPA / admin / back-office only |
|
|
787
|
-
| A public role | SSR website / marketing / storefront only |
|
|
788
|
-
|
|
789
|
-
Example:
|
|
201
|
+
Use a plugin factory for stateful integrations so each SSR request receives a
|
|
202
|
+
fresh Pinia, i18n, Apollo, or other request-sensitive instance:
|
|
790
203
|
|
|
791
204
|
```ts
|
|
792
|
-
export default
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
exportName: 'createAdminApplication',
|
|
801
|
-
},
|
|
802
|
-
template: 'index.html',
|
|
803
|
-
roles: ['unified', 'admin'],
|
|
804
|
-
domain: {
|
|
805
|
-
development: 'localhost',
|
|
806
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
807
|
-
mode: 'root-and-subdomains',
|
|
808
|
-
localAliases: true,
|
|
809
|
-
},
|
|
810
|
-
publicConfig: {
|
|
811
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
812
|
-
},
|
|
813
|
-
},
|
|
814
|
-
website: {
|
|
815
|
-
render: 'ssr',
|
|
816
|
-
application: {
|
|
817
|
-
module: './src/website/SsrApplication.ts',
|
|
818
|
-
exportName: 'websiteApplication',
|
|
819
|
-
},
|
|
820
|
-
template: 'site.html',
|
|
821
|
-
roles: ['unified', 'website'],
|
|
822
|
-
domain: {
|
|
823
|
-
development: 'shop.localhost',
|
|
824
|
-
production: process.env.VITE_SHOP_BASE_DOMAIN!,
|
|
825
|
-
mode: 'root-and-subdomains',
|
|
826
|
-
customDomains: true,
|
|
827
|
-
},
|
|
828
|
-
publicConfig: {
|
|
829
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
830
|
-
},
|
|
831
|
-
},
|
|
832
|
-
},
|
|
205
|
+
export default defineApplication({
|
|
206
|
+
root: App,
|
|
207
|
+
routes,
|
|
208
|
+
plugins: () => [
|
|
209
|
+
createPinia(),
|
|
210
|
+
createI18n(),
|
|
211
|
+
createApollo(),
|
|
212
|
+
],
|
|
833
213
|
})
|
|
834
214
|
```
|
|
835
215
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
-
|
|
841
|
-
|
|
842
|
-
If a host matches an application that the current role does not allow, the server responds with `421`.
|
|
843
|
-
|
|
844
|
-
Development may omit `runtime` (the compiler defaults the process role to
|
|
845
|
-
`unified`). Production compilation rejects a missing `runtime` or
|
|
846
|
-
`domain.production`. It does **not** validate `publicConfig` shape — pass
|
|
847
|
-
whatever your plugins need and validate API URLs in the consumer.
|
|
848
|
-
|
|
849
|
-
For async loading, export `async () => defineSsrConfig({ ... })`.
|
|
850
|
-
|
|
851
|
-
## Custom Endpoints
|
|
216
|
+
Stateless global-safe plugin arrays remain supported. For mature router setups,
|
|
217
|
+
the library supplies the environment-appropriate history while the application
|
|
218
|
+
keeps ownership of router options. If this factory directly imports
|
|
219
|
+
`createRouter` or other Vue Router APIs, the host application must declare
|
|
220
|
+
`vue-router` as a direct dependency; do not rely on another package's
|
|
221
|
+
transitive dependency under strict/non-hoisting package managers:
|
|
852
222
|
|
|
853
223
|
```ts
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
statusCode: 200,
|
|
865
|
-
body: 'User-agent: *\nAllow: /',
|
|
866
|
-
headers: {
|
|
867
|
-
'content-type': 'text/plain; charset=utf-8',
|
|
868
|
-
},
|
|
869
|
-
}
|
|
870
|
-
},
|
|
871
|
-
},
|
|
872
|
-
]
|
|
224
|
+
import { createRouter } from 'vue-router'
|
|
225
|
+
|
|
226
|
+
export default defineApplication({
|
|
227
|
+
root: App,
|
|
228
|
+
router: ({ history }) => createRouter({
|
|
229
|
+
history,
|
|
230
|
+
routes,
|
|
231
|
+
scrollBehavior,
|
|
232
|
+
}),
|
|
233
|
+
})
|
|
873
234
|
```
|
|
874
235
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
- `robots.txt`
|
|
878
|
-
- `sitemap.xml`
|
|
879
|
-
- Verification files
|
|
880
|
-
- Public JSON endpoints
|
|
236
|
+
`routes` and `router` are mutually exclusive. Router-less applications do not
|
|
237
|
+
create Vue Router history at all.
|
|
881
238
|
|
|
882
|
-
##
|
|
239
|
+
## Multi-application projects
|
|
883
240
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
Add readiness checks:
|
|
241
|
+
Multi-app mode is for products where several Vue applications live in the same
|
|
242
|
+
repository. They can share components, packages, types, services, and
|
|
243
|
+
infrastructure while keeping independent roots, routes, rendering modes,
|
|
244
|
+
templates, and host routing. Introduce `applications` only when there are
|
|
245
|
+
actually multiple applications; a single app does not need this configuration.
|
|
246
|
+
Each object key is the canonical application ID; do not repeat it in
|
|
247
|
+
`src/*/main.ts`.
|
|
892
248
|
|
|
893
249
|
```ts
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
250
|
+
import { defineSsrConfig } from 'vue-ssr-lite'
|
|
251
|
+
|
|
252
|
+
export default defineSsrConfig({
|
|
253
|
+
applications: {
|
|
254
|
+
website: {
|
|
255
|
+
app: './src/website/main.ts',
|
|
256
|
+
host: 'example.com',
|
|
257
|
+
},
|
|
258
|
+
admin: {
|
|
259
|
+
app: './src/admin/main.ts',
|
|
260
|
+
render: 'spa',
|
|
261
|
+
host: 'admin.example.com',
|
|
262
|
+
},
|
|
263
|
+
shop: {
|
|
264
|
+
app: './src/shop/main.ts',
|
|
265
|
+
host: 'shop.example.com',
|
|
266
|
+
},
|
|
267
|
+
dashboard: {
|
|
268
|
+
app: './src/dashboard/main.ts',
|
|
269
|
+
render: 'spa',
|
|
270
|
+
host: 'app.example.com',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
})
|
|
907
274
|
```
|
|
908
275
|
|
|
909
|
-
|
|
276
|
+
Omitted `render` means SSR, so `website` and `shop` are SSR applications while
|
|
277
|
+
`admin` and `dashboard` are SPAs. Rendering mode belongs to each application;
|
|
278
|
+
one SPA does not change the mode of the others. Each application module
|
|
279
|
+
default-exports `defineApplication(...)`:
|
|
910
280
|
|
|
911
281
|
```ts
|
|
912
|
-
|
|
282
|
+
// src/website/main.ts
|
|
283
|
+
import { defineApplication } from 'vue-ssr-lite'
|
|
284
|
+
import App from './App.vue'
|
|
913
285
|
|
|
914
|
-
|
|
915
|
-
|
|
286
|
+
export default defineApplication({
|
|
287
|
+
root: App,
|
|
916
288
|
})
|
|
917
289
|
```
|
|
918
290
|
|
|
919
|
-
|
|
291
|
+
For application host routing, use exact hosts such as `example.com`,
|
|
292
|
+
subdomains such as `admin.example.com`, wildcard hosts such as
|
|
293
|
+
`*.shop.example.com`, or the advanced `domain` options. Host specificity
|
|
294
|
+
determines the winner; duplicate ownership and ambiguous routing fail during
|
|
295
|
+
startup.
|
|
920
296
|
|
|
921
|
-
|
|
922
|
-
website: {
|
|
923
|
-
render: 'ssr',
|
|
924
|
-
application: {
|
|
925
|
-
module: './src/website/SsrApplication.ts',
|
|
926
|
-
exportName: 'websiteApplication',
|
|
927
|
-
},
|
|
928
|
-
template: 'site.html',
|
|
929
|
-
domain: {
|
|
930
|
-
development: 'localhost',
|
|
931
|
-
production: process.env.VITE_ROOT_DOMAIN!,
|
|
932
|
-
customDomains: true,
|
|
933
|
-
},
|
|
934
|
-
responseCache: {
|
|
935
|
-
store: responseCache,
|
|
936
|
-
ttlMs: 60_000,
|
|
937
|
-
},
|
|
938
|
-
publicConfig: {
|
|
939
|
-
api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
|
|
940
|
-
},
|
|
941
|
-
}
|
|
942
|
-
```
|
|
297
|
+
## Advanced capabilities
|
|
943
298
|
|
|
944
|
-
|
|
299
|
+
The normalized runtime still supports roles, domains and subdomains,
|
|
300
|
+
`publicConfig`, cookies, endpoints, response caching, readiness probes,
|
|
301
|
+
diagnostics, metrics, redirects, status codes, and proxy-aware host handling.
|
|
302
|
+
These belong in optional configuration and application code rather than in the
|
|
303
|
+
normal consumer bootstrap.
|
|
945
304
|
|
|
946
|
-
|
|
947
|
-
cookies per application:
|
|
305
|
+
Request context and domain context are available to advanced integrations:
|
|
948
306
|
|
|
949
307
|
```ts
|
|
950
|
-
|
|
951
|
-
allow: ['session'],
|
|
952
|
-
deny: ['admin_token', 'refresh_token'],
|
|
953
|
-
}
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
- Both empty → forward nothing (secure default when SSR needs no cookies).
|
|
957
|
-
- `allow` empty and `deny` non-empty → forward all cookies except `deny`.
|
|
958
|
-
- `allow` non-empty → forward `allow ∩ ¬deny`.
|
|
308
|
+
import { useSsrDomain, useSsrRequestContext } from 'vue-ssr-lite'
|
|
959
309
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
```ts
|
|
963
|
-
server: {
|
|
964
|
-
host: '0.0.0.0',
|
|
965
|
-
port: 4173,
|
|
966
|
-
trustProxy: false,
|
|
967
|
-
requestTimeoutMs: 15_000,
|
|
968
|
-
shutdownTimeoutMs: 10_000,
|
|
969
|
-
healthPath: '/healthz',
|
|
970
|
-
readinessPath: '/readyz',
|
|
971
|
-
logger: {
|
|
972
|
-
info: (event, details) => console.info(event, details ?? ''),
|
|
973
|
-
warn: (event, details) => console.warn(event, details ?? ''),
|
|
974
|
-
error: (event, details) => console.error(event, details ?? ''),
|
|
975
|
-
},
|
|
976
|
-
}
|
|
310
|
+
const domain = useSsrDomain()
|
|
311
|
+
const request = useSsrRequestContext()
|
|
977
312
|
```
|
|
978
313
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
## Server-side data resolution
|
|
983
|
-
|
|
984
|
-
`renderToString` awaits each component's native `onServerPrefetch`, which covers
|
|
985
|
-
data consumed reactively inside the component that declared it. For work started
|
|
986
|
-
OUTSIDE that lifecycle — a store action, an i18n loader, a lazy query — the
|
|
987
|
-
package exposes a generic, API-client-neutral **resolution contract**.
|
|
988
|
-
|
|
989
|
-
An installed plugin obtains it by injecting `SSR_REQUEST_RESOLUTION`
|
|
990
|
-
(`Symbol.for('vue-ssr:request-resolution')`, resolvable without importing this
|
|
991
|
-
package). It registers in-flight work with `track(promise)` and may call
|
|
992
|
-
`requestAdditionalPass()`. After each render pass the renderer awaits registered
|
|
993
|
-
work and re-renders when a plugin asked for another pass, up to
|
|
994
|
-
`server.maxResolutionPasses` (default 4) and bounded by
|
|
995
|
-
`server.resolutionDeadlineMs` and the request abort signal.
|
|
996
|
-
|
|
997
|
-
A fully resolvable page completes in **one pass** — extra passes occur only when
|
|
998
|
-
a plugin left work pending or requested one. `vue-ssr-lite` never inspects the
|
|
999
|
-
work; it only awaits it.
|
|
1000
|
-
|
|
1001
|
-
### Deferred parent → child dependencies
|
|
314
|
+
`publicConfig` is opaque and browser-safe; validate API URLs and integration
|
|
315
|
+
details in the consuming application or plugin.
|
|
1002
316
|
|
|
1003
|
-
|
|
1004
|
-
components mount, and each child runs its own async work. When a child's data is
|
|
1005
|
-
consumed DIRECTLY inside that child, a single pass suffices — Vue awaits the
|
|
1006
|
-
child's `onServerPrefetch` before rendering it.
|
|
317
|
+
## Failure messages
|
|
1007
318
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
319
|
+
Convention discovery fails early with actionable guidance when `src/main.ts`,
|
|
320
|
+
`index.html`, or the configured mount target is missing. The compiler also
|
|
321
|
+
reports invalid application exports, mixed single-/multi-app configuration, and
|
|
322
|
+
unresolved multi-app host routing before the server handles traffic.
|
|
1012
323
|
|
|
1013
|
-
|
|
1014
|
-
the store with `ssrWatch` exactly as you already would:
|
|
324
|
+
## Package entry points
|
|
1015
325
|
|
|
1016
|
-
|
|
1017
|
-
const { result } = useMyQuery(vars, { ssr: true })
|
|
1018
|
-
ssrWatch(() => result.value, (data) => {
|
|
1019
|
-
if (data) store.set(key, data)
|
|
1020
|
-
}, { immediate: true })
|
|
1021
|
-
```
|
|
1022
|
-
|
|
1023
|
-
`ssrWatch` automatically requests one more render pass when it fires from the
|
|
1024
|
-
awaited prefetch (after a sibling already rendered). On the resumed pass the
|
|
1025
|
-
data is warm in the API client's request cache — `vue-apollo-client` settles it
|
|
1026
|
-
synchronously at setup — so the same `ssrWatch` runs on its immediate tick, the
|
|
1027
|
-
sibling sees the store, and no further pass is requested. Bounded, and each
|
|
1028
|
-
operation runs exactly once. Nothing app-specific, and nothing to repeat per
|
|
1029
|
-
component or per project.
|
|
1030
|
-
|
|
1031
|
-
## SSR-safe reactivity
|
|
1032
|
-
|
|
1033
|
-
During SSR, Vue does not flush the scheduler, so `watch(src, cb)` and
|
|
1034
|
-
`watchEffect` run at most once and are then discarded — the most common cause of
|
|
1035
|
-
"the shell renders but the content is missing" bugs. Server-safe APIs:
|
|
1036
|
-
|
|
1037
|
-
| API | Server behaviour |
|
|
326
|
+
| Import | Purpose |
|
|
1038
327
|
| --- | --- |
|
|
1039
|
-
| `
|
|
1040
|
-
| `
|
|
1041
|
-
| `
|
|
1042
|
-
| `
|
|
1043
|
-
| `onMounted` / `onUpdated` | ❌ Browser only |
|
|
1044
|
-
|
|
1045
|
-
Use a `computed` to derive a value for the template; reach for `ssrWatch` /
|
|
1046
|
-
`ssrWatchEffect` (exported from `vue-ssr-lite` and `vue-ssr-lite/client`) when
|
|
1047
|
-
resolved data must drive an imperative side effect during the server render.
|
|
1048
|
-
|
|
1049
|
-
## Runtime configuration helpers
|
|
1050
|
-
|
|
1051
|
-
`vue-ssr-lite/server` provides declarative helpers so a runtime definition is
|
|
1052
|
-
configuration, not boilerplate: `ssrEnvBoolean`, `ssrEnvNumber`, `ssrEnvList`,
|
|
1053
|
-
`requireSsrEnv`, `requireSsrHostname`, `requireSsrEnum`, `createSsrConsoleLogger`
|
|
1054
|
-
and `createSsrSeoEndpoints` (`robots.txt` / `sitemap.xml` — `served`, `proxy` or
|
|
1055
|
-
`disallow` by option). Host helpers such as `normalizeSsrHost` remain exported;
|
|
1056
|
-
reuse them rather than re-implementing.
|
|
1057
|
-
|
|
1058
|
-
## Development diagnostics
|
|
1059
|
-
|
|
1060
|
-
When diagnostics are enabled (`server.diagnostics`, default on outside
|
|
1061
|
-
production) the renderer reports, with actionable messages: a loading
|
|
1062
|
-
placeholder or `aria-busy="true"` still present after resolution, a route that
|
|
1063
|
-
matched no component, and a body that resolved to nothing. These are dev-only
|
|
1064
|
-
warnings; production render paths are untouched.
|
|
1065
|
-
|
|
1066
|
-
## Summary
|
|
1067
|
-
|
|
1068
|
-
1. Add `ssr.config.ts` with `defineSsrConfig({ name, runtime, applications })`.
|
|
1069
|
-
2. For each app: `defineSsrApplication()` module, HTML shell (no bootstrap script),
|
|
1070
|
-
`render: 'spa' | 'ssr'`, and `application: { module, exportName }`.
|
|
1071
|
-
3. Wire Vite with `vue()` + `vueSsrLite()` (client assets land in `dist/client`).
|
|
1072
|
-
4. Run `vue-ssr-lite dev|build|start`.
|
|
1073
|
-
5. In production set `APP_RUNTIME` / `runtime` and every `domain.production`.
|
|
1074
|
-
|
|
1075
|
-
Optional: restrict apps with `roles`, filter cookies, add endpoints, or pass
|
|
1076
|
-
`server.onMetrics` / `server.renderError` through `ssr.config`.
|
|
1077
|
-
|
|
1078
|
-
SPA and SSR apps share one project, one build, and one production server.
|
|
328
|
+
| `vue-ssr-lite` | `defineApplication`, `defineSsrConfig`, request/domain context |
|
|
329
|
+
| `vue-ssr-lite/client` | Internal browser hydration and SPA mounting |
|
|
330
|
+
| `vue-ssr-lite/server` | Managed server, compilation, host matching, endpoints |
|
|
331
|
+
| `vue-ssr-lite/vite` | Vite HTML and generated client/server entry integration |
|
|
1079
332
|
|
|
1080
333
|
## License
|
|
1081
334
|
|