@ubean/client 0.2.2 → 0.3.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/dist/{app-Bc1d5Svw.js → app-B6vVB7hp.js} +13 -209
- package/dist/{app-BknjDM9j.d.ts → app-BP3eySMH.d.ts} +6 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +1 -1
- package/dist/i18n-t0G3VyBs.js +206 -0
- package/dist/index.d.ts +73 -47
- package/dist/index.js +5 -4
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/ssr.d.ts +32 -0
- package/dist/ssr.js +267 -0
- package/package.json +17 -8
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as getI18nRuntimeConfig, l as localizePath, r as createUbeanI18n, s as initClientI18n, t as bindI18nRuntime } from "./i18n-t0G3VyBs.js";
|
|
2
|
+
import { computed, createApp, createSSRApp, defineComponent, h, inject, markRaw, provide, reactive, shallowRef, watchEffect } from "vue";
|
|
2
3
|
import { createMemoryHistory, createRouter, createWebHistory, useRoute, useRouter } from "vue-router";
|
|
3
4
|
import { ERROR_KEY, ERROR_KEY as ERROR_KEY$1, LAYOUT_CHAIN_KEY, LOADING_KEY, LOADING_KEY as LOADING_KEY$1, LOCALIZE_PATH_KEY, LOCALIZE_PATH_KEY as LOCALIZE_PATH_KEY$1, LayoutChainRenderer, LayoutChainRenderer as LayoutChainRenderer$1, Link, PAGE_KEY, PAGE_KEY as PAGE_KEY$1, PageView, SSR_KEY, SSR_KEY as SSR_KEY$1, SlotView, TRANSITION_KEY, TRANSITION_KEY as TRANSITION_KEY$1, initCachedViewsFromRoutes, ubeanVue, useViewTransition } from "@ubean/vue";
|
|
4
5
|
import { useHead as useUnheadHead, useSeoMeta } from "@unhead/vue";
|
|
5
|
-
import { LOCALE_DATA_ID } from "@ubean/pages";
|
|
6
6
|
import { vClient } from "@ubean/islands/directive";
|
|
7
7
|
import { Head } from "@unhead/vue/components";
|
|
8
|
-
import { addLocale, clearLocales, defineLocale, detectBrowserLocale, detectLocale, extractLocaleFromPath, getDefaultLocale, getI18nConfig, getLocale, getLocaleDir, getLocaleName, getRegisteredLocales, localizePath, mergeLocale, onLocaleChange, setI18nConfig, setLocale, switchLocalePath, t, useI18n } from "@ubean/i18n";
|
|
9
8
|
//#region src/use-page.ts
|
|
10
9
|
/**
|
|
11
10
|
* 框架层 `usePage()` — 路由感知的响应式页面上下文。
|
|
@@ -63,209 +62,6 @@ function createUbeanRouter(options) {
|
|
|
63
62
|
return router;
|
|
64
63
|
}
|
|
65
64
|
//#endregion
|
|
66
|
-
//#region src/i18n.ts
|
|
67
|
-
const _global = globalThis;
|
|
68
|
-
function hydrateLocale() {
|
|
69
|
-
if (typeof _global.document === "undefined") return {
|
|
70
|
-
locale: null,
|
|
71
|
-
dir: "ltr"
|
|
72
|
-
};
|
|
73
|
-
const el = _global.document.getElementById(LOCALE_DATA_ID);
|
|
74
|
-
if (!el) return {
|
|
75
|
-
locale: null,
|
|
76
|
-
dir: "ltr"
|
|
77
|
-
};
|
|
78
|
-
try {
|
|
79
|
-
const data = JSON.parse(el.textContent || "null");
|
|
80
|
-
if (data && typeof data.locale === "string") return {
|
|
81
|
-
locale: data.locale,
|
|
82
|
-
dir: data.dir === "rtl" ? "rtl" : "ltr",
|
|
83
|
-
messages: data.messages,
|
|
84
|
-
availableLocales: Array.isArray(data.availableLocales) ? data.availableLocales : void 0
|
|
85
|
-
};
|
|
86
|
-
} catch {
|
|
87
|
-
return {
|
|
88
|
-
locale: null,
|
|
89
|
-
dir: "ltr"
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
locale: null,
|
|
94
|
-
dir: "ltr"
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function syncHtmlLang(locale, dir) {
|
|
98
|
-
if (typeof _global.document === "undefined") return;
|
|
99
|
-
const html = _global.document.documentElement;
|
|
100
|
-
if (html) {
|
|
101
|
-
html.setAttribute("lang", locale);
|
|
102
|
-
html.setAttribute("dir", dir);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Lazily hydrate SSR-injected locale data (idempotent).
|
|
107
|
-
*
|
|
108
|
-
* Previously this ran as a module-load side effect (reading the DOM and
|
|
109
|
-
* mutating `<html lang/dir>` on import). It is now invoked explicitly by
|
|
110
|
-
* `createUbeanClientApp` / `createUbeanSSRApp` and by the read APIs below,
|
|
111
|
-
* keeping module import side-effect free.
|
|
112
|
-
*/
|
|
113
|
-
let _i18nHydrated = false;
|
|
114
|
-
function initClientI18n() {
|
|
115
|
-
if (_i18nHydrated) return;
|
|
116
|
-
_i18nHydrated = true;
|
|
117
|
-
const { locale: hydratedLocale, dir: hydratedDir, messages: hydratedMessages, availableLocales: hydratedAvailable } = hydrateLocale();
|
|
118
|
-
if (!hydratedLocale) return;
|
|
119
|
-
if (hydratedAvailable && hydratedAvailable.length > 0) {
|
|
120
|
-
const sorted = [...hydratedAvailable].sort((a, b) => (b.isDefault ? 1 : 0) - (a.isDefault ? 1 : 0));
|
|
121
|
-
for (const loc of sorted) {
|
|
122
|
-
const isCurrent = loc.code === hydratedLocale;
|
|
123
|
-
defineLocale({
|
|
124
|
-
code: loc.code,
|
|
125
|
-
messages: isCurrent && hydratedMessages ? hydratedMessages : {},
|
|
126
|
-
name: loc.name,
|
|
127
|
-
dir: loc.dir,
|
|
128
|
-
isDefault: loc.isDefault
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
} else if (hydratedMessages && typeof hydratedMessages === "object") defineLocale({
|
|
132
|
-
code: hydratedLocale,
|
|
133
|
-
messages: hydratedMessages,
|
|
134
|
-
dir: hydratedDir
|
|
135
|
-
});
|
|
136
|
-
setLocale(hydratedLocale);
|
|
137
|
-
localeRef.value = hydratedLocale;
|
|
138
|
-
syncHtmlLang(hydratedLocale, hydratedDir);
|
|
139
|
-
}
|
|
140
|
-
const localeRef = ref(getLocale());
|
|
141
|
-
onLocaleChange((newLocale) => {
|
|
142
|
-
localeRef.value = newLocale;
|
|
143
|
-
syncHtmlLang(newLocale, getLocaleDir(newLocale));
|
|
144
|
-
});
|
|
145
|
-
function useI18n$1() {
|
|
146
|
-
initClientI18n();
|
|
147
|
-
const core = useI18n();
|
|
148
|
-
const localeDir = computed(() => getLocaleDir(localeRef.value));
|
|
149
|
-
const localeName = computed(() => getLocaleName(localeRef.value));
|
|
150
|
-
function translate(key, params) {
|
|
151
|
-
localeRef.value;
|
|
152
|
-
return core.t(key, params);
|
|
153
|
-
}
|
|
154
|
-
return {
|
|
155
|
-
locale: localeRef,
|
|
156
|
-
get fallbackLocale() {
|
|
157
|
-
return core.fallbackLocale;
|
|
158
|
-
},
|
|
159
|
-
get availableLocales() {
|
|
160
|
-
return core.availableLocales;
|
|
161
|
-
},
|
|
162
|
-
t: translate,
|
|
163
|
-
setLocale(locale) {
|
|
164
|
-
core.setLocale(locale);
|
|
165
|
-
},
|
|
166
|
-
getLocale() {
|
|
167
|
-
return localeRef.value;
|
|
168
|
-
},
|
|
169
|
-
onLocaleChange(callback) {
|
|
170
|
-
return core.onLocaleChange(callback);
|
|
171
|
-
},
|
|
172
|
-
getLocaleDir(locale) {
|
|
173
|
-
return core.getLocaleDir(locale);
|
|
174
|
-
},
|
|
175
|
-
getLocaleName(locale) {
|
|
176
|
-
return core.getLocaleName(locale);
|
|
177
|
-
},
|
|
178
|
-
localeDir,
|
|
179
|
-
localeName
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function defineLocale$1(definition) {
|
|
183
|
-
initClientI18n();
|
|
184
|
-
const result = defineLocale(definition);
|
|
185
|
-
localeRef.value = getLocale();
|
|
186
|
-
return result;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* 建立对当前 locale 的响应式依赖(模板中的 `t()` / `localizePath()` 经此驱动重渲染)。
|
|
190
|
-
* i18n core 的状态存于 globalThis(非响应式),Vue 侧的 `localeRef` 是唯一响应源。
|
|
191
|
-
*/
|
|
192
|
-
function trackLocale() {
|
|
193
|
-
initClientI18n();
|
|
194
|
-
localeRef.value;
|
|
195
|
-
}
|
|
196
|
-
function t$1(key, params) {
|
|
197
|
-
trackLocale();
|
|
198
|
-
return t(key, params);
|
|
199
|
-
}
|
|
200
|
-
function setLocale$1(locale) {
|
|
201
|
-
setLocale(locale);
|
|
202
|
-
}
|
|
203
|
-
function getLocale$1() {
|
|
204
|
-
return localeRef.value;
|
|
205
|
-
}
|
|
206
|
-
function onLocaleChange$1(callback) {
|
|
207
|
-
return onLocaleChange(callback);
|
|
208
|
-
}
|
|
209
|
-
function getLocaleDir$1(locale) {
|
|
210
|
-
return getLocaleDir(locale);
|
|
211
|
-
}
|
|
212
|
-
function getLocaleName$1(locale) {
|
|
213
|
-
return getLocaleName(locale);
|
|
214
|
-
}
|
|
215
|
-
function getRegisteredLocales$1() {
|
|
216
|
-
initClientI18n();
|
|
217
|
-
return getRegisteredLocales();
|
|
218
|
-
}
|
|
219
|
-
function detectLocale$1(acceptLanguage) {
|
|
220
|
-
return detectLocale(acceptLanguage);
|
|
221
|
-
}
|
|
222
|
-
function detectBrowserLocale$1() {
|
|
223
|
-
return detectBrowserLocale();
|
|
224
|
-
}
|
|
225
|
-
function addLocale$1(code, messages, options) {
|
|
226
|
-
addLocale(code, messages, options);
|
|
227
|
-
}
|
|
228
|
-
function mergeLocale$1(code, messages) {
|
|
229
|
-
mergeLocale(code, messages);
|
|
230
|
-
}
|
|
231
|
-
function clearLocales$1() {
|
|
232
|
-
clearLocales();
|
|
233
|
-
localeRef.value = getLocale();
|
|
234
|
-
}
|
|
235
|
-
function getI18nConfig$1() {
|
|
236
|
-
return getI18nConfig();
|
|
237
|
-
}
|
|
238
|
-
function setI18nConfig$1(config) {
|
|
239
|
-
setI18nConfig(config);
|
|
240
|
-
}
|
|
241
|
-
function localizePath$1(path, locale) {
|
|
242
|
-
trackLocale();
|
|
243
|
-
return localizePath(path, locale);
|
|
244
|
-
}
|
|
245
|
-
function switchLocalePath$1(newLocale, currentPath) {
|
|
246
|
-
trackLocale();
|
|
247
|
-
return switchLocalePath(newLocale, currentPath);
|
|
248
|
-
}
|
|
249
|
-
function getDefaultLocale$1() {
|
|
250
|
-
return getDefaultLocale();
|
|
251
|
-
}
|
|
252
|
-
function extractLocaleFromPath$1(path) {
|
|
253
|
-
return extractLocaleFromPath(path);
|
|
254
|
-
}
|
|
255
|
-
function useSwitchLocalePath() {
|
|
256
|
-
initClientI18n();
|
|
257
|
-
return computed(() => (newLocale) => {
|
|
258
|
-
const path = typeof _global.window !== "undefined" ? _global.window.location.pathname : "/";
|
|
259
|
-
return switchLocalePath(newLocale, path);
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
function useLocalePath() {
|
|
263
|
-
initClientI18n();
|
|
264
|
-
return computed(() => (path, locale) => {
|
|
265
|
-
return localizePath(path, locale || localeRef.value);
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
//#endregion
|
|
269
65
|
//#region src/page-macro.ts
|
|
270
66
|
function definePage(_meta) {}
|
|
271
67
|
function defineMeta(_meta) {}
|
|
@@ -273,7 +69,7 @@ function defineMiddleware(_handler) {}
|
|
|
273
69
|
//#endregion
|
|
274
70
|
//#region src/app.ts
|
|
275
71
|
/**
|
|
276
|
-
* Framework app factories — consumed by the ubean aggregator & `@ubean/ssr`.
|
|
72
|
+
* Framework app factories — consumed by the ubean aggregator & `@ubean/client/ssr`.
|
|
277
73
|
*
|
|
278
74
|
* `createUbeanClientApp` / `createUbeanSSRApp` wire the lean kernel from
|
|
279
75
|
* `@ubean/vue` together with framework extras: unhead, i18n path
|
|
@@ -352,7 +148,7 @@ function createRootComponent(LayoutWrapper, page, transitionOpts, isSSR, loading
|
|
|
352
148
|
provide(SSR_KEY, isSSR);
|
|
353
149
|
provide(LOADING_KEY, loadingComponent ?? null);
|
|
354
150
|
provide(ERROR_KEY, errorComponent ?? null);
|
|
355
|
-
provide(LOCALIZE_PATH_KEY, (path) => localizePath
|
|
151
|
+
provide(LOCALIZE_PATH_KEY, (path, locale) => localizePath(path, locale));
|
|
356
152
|
return () => h(LayoutWrapper);
|
|
357
153
|
}
|
|
358
154
|
});
|
|
@@ -378,8 +174,12 @@ function createUbeanClientApp(options) {
|
|
|
378
174
|
initCachedViewsFromRoutes(options.routes);
|
|
379
175
|
const RootComponent = createRootComponent(LayoutWrapper, page, transitionOpts, false, resolveComp(options.loadingComponent), resolveComp(options.errorComponent));
|
|
380
176
|
const app = options.hydrate ? createSSRApp(RootComponent) : createApp(RootComponent);
|
|
177
|
+
const i18n = options.i18n ?? createUbeanI18n();
|
|
178
|
+
app.use(i18n);
|
|
381
179
|
app.use(head);
|
|
382
180
|
app.use(router);
|
|
181
|
+
bindI18nRuntime(i18n, router);
|
|
182
|
+
app.provide("ubean:i18n-runtime-config", getI18nRuntimeConfig());
|
|
383
183
|
app.component("Link", Link);
|
|
384
184
|
app.component("PageView", PageView);
|
|
385
185
|
app.component("SlotView", SlotView);
|
|
@@ -411,8 +211,12 @@ function createUbeanSSRApp(initialPage, options) {
|
|
|
411
211
|
initCachedViewsFromRoutes(options.routes);
|
|
412
212
|
const RootComponent = createRootComponent(LayoutWrapper, page, { enabled: false }, true, resolveComp(options.loadingComponent), resolveComp(options.errorComponent));
|
|
413
213
|
const app = createSSRApp(RootComponent);
|
|
214
|
+
const i18n = options.i18n ?? createUbeanI18n();
|
|
215
|
+
app.use(i18n);
|
|
414
216
|
app.use(head);
|
|
415
217
|
app.use(router);
|
|
218
|
+
bindI18nRuntime(i18n, router);
|
|
219
|
+
app.provide("ubean:i18n-runtime-config", getI18nRuntimeConfig());
|
|
416
220
|
app.component("Link", Link);
|
|
417
221
|
app.component("PageView", PageView);
|
|
418
222
|
app.component("SlotView", SlotView);
|
|
@@ -430,4 +234,4 @@ function createUbeanSSRApp(initialPage, options) {
|
|
|
430
234
|
}
|
|
431
235
|
const Head$1 = Head;
|
|
432
236
|
//#endregion
|
|
433
|
-
export {
|
|
237
|
+
export { createUbeanRouter as C, definePage as S, useSeoMeta as _, LOCALIZE_PATH_KEY$1 as a, defineMeta as b, PAGE_KEY$1 as c, SlotView as d, TRANSITION_KEY$1 as f, useRouter as g, ubeanVue as h, LOADING_KEY$1 as i, PageView as l, createUbeanSSRApp as m, Head$1 as n, LayoutChainRenderer$1 as o, createUbeanClientApp as p, LAYOUT_CHAIN_KEY as r, Link as s, ERROR_KEY$1 as t, SSR_KEY$1 as u, useUnheadHead as v, usePage as w, defineMiddleware as x, useViewTransition as y };
|
|
@@ -3,6 +3,7 @@ import { RouteRecordRaw, Router, useRouter } from "vue-router";
|
|
|
3
3
|
import { ERROR_KEY as ERROR_KEY$1, LAYOUT_CHAIN_KEY, LOADING_KEY as LOADING_KEY$1, LOCALIZE_PATH_KEY as LOCALIZE_PATH_KEY$1, LayoutChainContext, LayoutChainRenderer as LayoutChainRenderer$1, Link, PAGE_KEY as PAGE_KEY$1, PageMeta, PageView, SSR_KEY as SSR_KEY$1, SlotView, TRANSITION_KEY as TRANSITION_KEY$1, UbeanVueOptions, ViewTransitionOptions, ubeanVue, useViewTransition } from "@ubean/vue";
|
|
4
4
|
import { VueHeadClient, useHead as useUnheadHead, useSeoMeta } from "@unhead/vue";
|
|
5
5
|
import { PageObject } from "@ubean/pages";
|
|
6
|
+
import { I18n } from "vue-i18n";
|
|
6
7
|
import { Input, RouteMeta } from "@ubean/shared";
|
|
7
8
|
//#region src/router.d.ts
|
|
8
9
|
interface CreateUbeanRouterOptions {
|
|
@@ -83,6 +84,11 @@ interface UbeanAppOptions {
|
|
|
83
84
|
* 通过 Vue 的 `errorCaptured` 生命周期实现错误边界。
|
|
84
85
|
*/
|
|
85
86
|
errorComponent?: Component | (() => Component);
|
|
87
|
+
/**
|
|
88
|
+
* vue-i18n instance (`legacy: false`). Created by `createUbeanI18n`.
|
|
89
|
+
* Installed with `app.use(i18n)` before the router.
|
|
90
|
+
*/
|
|
91
|
+
i18n?: I18n;
|
|
86
92
|
}
|
|
87
93
|
interface UbeanAppInstance {
|
|
88
94
|
app: App;
|
package/dist/app.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient, a as LOCALIZE_PATH_KEY, b as ubeanVue, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, j as createUbeanRouter, k as usePage, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanClientApp, w as useViewTransition, x as useRouter, y as createUbeanSSRApp } from "./app-
|
|
1
|
+
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient, a as LOCALIZE_PATH_KEY, b as ubeanVue, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, j as createUbeanRouter, k as usePage, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanClientApp, w as useViewTransition, x as useRouter, y as createUbeanSSRApp } from "./app-BP3eySMH.js";
|
|
2
2
|
export { type CreateUbeanRouterOptions, ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, UbeanAppInstance, UbeanAppOptions, type UbeanVueOptions, type UbeanVuePage, type VueHeadClient, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useRouter, useSeoMeta, useViewTransition };
|
package/dist/app.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as createUbeanRouter, S as definePage, _ as useSeoMeta, a as LOCALIZE_PATH_KEY, b as defineMeta, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useRouter, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useUnheadHead, w as usePage, x as defineMiddleware, y as useViewTransition } from "./app-B6vVB7hp.js";
|
|
2
2
|
export { ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useRouter, useSeoMeta, useViewTransition };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useRoute } from "vue-router";
|
|
3
|
+
import { LOCALE_DATA_ID } from "@ubean/pages";
|
|
4
|
+
import { createI18n, useI18n as useVueI18n } from "vue-i18n";
|
|
5
|
+
import { buildLocaleHead, extractLocaleFromPath, localizePath, switchLocalePath } from "@ubean/i18n/browser";
|
|
6
|
+
//#region src/i18n.ts
|
|
7
|
+
const RUNTIME_KEY = "__UBEAN_I18N_RUNTIME__";
|
|
8
|
+
function getRuntimeState() {
|
|
9
|
+
const g = globalThis;
|
|
10
|
+
if (!g[RUNTIME_KEY]) g[RUNTIME_KEY] = {
|
|
11
|
+
config: null,
|
|
12
|
+
loader: null,
|
|
13
|
+
metas: []
|
|
14
|
+
};
|
|
15
|
+
return g[RUNTIME_KEY];
|
|
16
|
+
}
|
|
17
|
+
function getRuntimeConfigState() {
|
|
18
|
+
return getRuntimeState().config;
|
|
19
|
+
}
|
|
20
|
+
function setRuntimeConfigState(config) {
|
|
21
|
+
getRuntimeState().config = config;
|
|
22
|
+
}
|
|
23
|
+
function bindI18nRuntime(i18n, router) {
|
|
24
|
+
const state = getRuntimeState();
|
|
25
|
+
state.i18n = i18n;
|
|
26
|
+
if (router) state.router = router;
|
|
27
|
+
}
|
|
28
|
+
function configureI18nRuntime(options) {
|
|
29
|
+
const state = getRuntimeState();
|
|
30
|
+
state.config = options.config;
|
|
31
|
+
if (options.loadLocale) state.loader = options.loadLocale;
|
|
32
|
+
if (options.locales) state.metas = options.locales;
|
|
33
|
+
}
|
|
34
|
+
function getI18nRuntimeConfig() {
|
|
35
|
+
return getRuntimeConfigState();
|
|
36
|
+
}
|
|
37
|
+
function localeCodesFrom(input) {
|
|
38
|
+
if (!Array.isArray(input)) return [];
|
|
39
|
+
const codes = [];
|
|
40
|
+
for (const item of input) if (typeof item === "string") codes.push(item);
|
|
41
|
+
else if (item && typeof item === "object" && "code" in item) codes.push(String(item.code));
|
|
42
|
+
return codes;
|
|
43
|
+
}
|
|
44
|
+
function applyHydratedPayload(hydrated) {
|
|
45
|
+
const state = getRuntimeState();
|
|
46
|
+
if (hydrated.routing && !state.config) setRuntimeConfigState({
|
|
47
|
+
defaultLocale: hydrated.routing.defaultLocale,
|
|
48
|
+
locales: localeCodesFrom(hydrated.routing.locales),
|
|
49
|
+
strategy: hydrated.routing.strategy,
|
|
50
|
+
fallbackLocale: hydrated.fallbackLocale || hydrated.routing.defaultLocale,
|
|
51
|
+
cookieName: hydrated.cookieName || "ubean_locale",
|
|
52
|
+
baseUrl: hydrated.baseUrl || ""
|
|
53
|
+
});
|
|
54
|
+
else if (hydrated.routing && state.config && !state.config.locales.length) state.config.locales = localeCodesFrom(hydrated.routing.locales);
|
|
55
|
+
const metas = hydrated.locales || hydrated.availableLocales;
|
|
56
|
+
if (metas) state.metas = metas;
|
|
57
|
+
}
|
|
58
|
+
function routingFrom(config) {
|
|
59
|
+
return {
|
|
60
|
+
defaultLocale: config.defaultLocale,
|
|
61
|
+
locales: config.locales,
|
|
62
|
+
strategy: config.strategy
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readHydratedPayload() {
|
|
66
|
+
if (typeof document === "undefined") return null;
|
|
67
|
+
const el = document.getElementById(LOCALE_DATA_ID);
|
|
68
|
+
if (!el?.textContent) return null;
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(el.textContent);
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function createUbeanI18n(options) {
|
|
76
|
+
const hydrated = typeof document !== "undefined" ? readHydratedPayload() : null;
|
|
77
|
+
const cfg = getRuntimeConfigState();
|
|
78
|
+
const locale = options?.locale || hydrated?.locale || cfg?.defaultLocale || "en";
|
|
79
|
+
const fallbackLocale = options?.fallbackLocale || hydrated?.fallbackLocale || cfg?.fallbackLocale || cfg?.defaultLocale || locale;
|
|
80
|
+
const messages = { ...options?.messages };
|
|
81
|
+
if (hydrated?.messages && hydrated.locale) messages[hydrated.locale] = hydrated.messages;
|
|
82
|
+
if (hydrated?.fallbackMessages && hydrated.fallbackLocale && hydrated.fallbackLocale !== hydrated.locale) messages[hydrated.fallbackLocale] = hydrated.fallbackMessages;
|
|
83
|
+
if (!messages[locale]) messages[locale] = {};
|
|
84
|
+
if (fallbackLocale !== locale && !messages[fallbackLocale]) messages[fallbackLocale] = {};
|
|
85
|
+
if (hydrated) applyHydratedPayload(hydrated);
|
|
86
|
+
const hydratedConfig = getRuntimeState().config;
|
|
87
|
+
if (hydratedConfig && options?.vueI18n) hydratedConfig.vueI18n = options.vueI18n;
|
|
88
|
+
return createI18n({
|
|
89
|
+
legacy: false,
|
|
90
|
+
locale,
|
|
91
|
+
fallbackLocale,
|
|
92
|
+
messages,
|
|
93
|
+
missingWarn: false,
|
|
94
|
+
fallbackWarn: false,
|
|
95
|
+
...cfg?.vueI18n,
|
|
96
|
+
...options?.vueI18n
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function installUbeanI18n(app, i18n) {
|
|
100
|
+
app.use(i18n);
|
|
101
|
+
}
|
|
102
|
+
function writeLocaleCookie(code) {
|
|
103
|
+
if (typeof document === "undefined") return;
|
|
104
|
+
const name = getRuntimeConfigState()?.cookieName || "ubean_locale";
|
|
105
|
+
document.cookie = `${name}=${encodeURIComponent(code)}; Path=/; SameSite=Lax`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Framework setLocale: load messages, switch composer locale, write cookie,
|
|
109
|
+
* navigate to the localized path (`no_prefix` skips navigation).
|
|
110
|
+
*
|
|
111
|
+
* Uses the runtime bound by `bindI18nRuntime` (not Vue composables) so it is
|
|
112
|
+
* safe to call from click handlers.
|
|
113
|
+
*/
|
|
114
|
+
async function setLocale(code) {
|
|
115
|
+
const state = getRuntimeState();
|
|
116
|
+
const composer = state.i18n?.global;
|
|
117
|
+
if (!composer) return;
|
|
118
|
+
composer.locale.value = code;
|
|
119
|
+
writeLocaleCookie(code);
|
|
120
|
+
const loader = state.loader;
|
|
121
|
+
const loadPromise = loader ? loader(code).then((msgs) => {
|
|
122
|
+
composer.setLocaleMessage(code, msgs);
|
|
123
|
+
}) : Promise.resolve();
|
|
124
|
+
const cfg = state.config;
|
|
125
|
+
let navPromise = Promise.resolve();
|
|
126
|
+
if (cfg && cfg.strategy !== "no_prefix") {
|
|
127
|
+
const router = state.router;
|
|
128
|
+
const currentPath = (router?.currentRoute.value.path || router?.currentRoute.value.fullPath?.split("?")[0] || (typeof window !== "undefined" ? window.location.pathname : "/")).split("?")[0];
|
|
129
|
+
const target = switchLocalePath(code, currentPath, routingFrom(cfg));
|
|
130
|
+
if (target !== currentPath && router) navPromise = router.replace(target).then(() => void 0, () => void 0);
|
|
131
|
+
}
|
|
132
|
+
await Promise.all([loadPromise, navPromise]);
|
|
133
|
+
}
|
|
134
|
+
function getLocale() {
|
|
135
|
+
try {
|
|
136
|
+
return String(useVueI18n().locale.value);
|
|
137
|
+
} catch {
|
|
138
|
+
return getRuntimeConfigState()?.defaultLocale || "en";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function localizePath$1(path, locale) {
|
|
142
|
+
const cfg = getRuntimeConfigState();
|
|
143
|
+
if (!cfg) return path;
|
|
144
|
+
return localizePath(path, locale || getLocale(), routingFrom(cfg));
|
|
145
|
+
}
|
|
146
|
+
function switchLocalePath$1(locale, path) {
|
|
147
|
+
const cfg = getRuntimeConfigState();
|
|
148
|
+
if (!cfg) return path || "/";
|
|
149
|
+
const current = path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
150
|
+
return switchLocalePath(locale, current, routingFrom(cfg));
|
|
151
|
+
}
|
|
152
|
+
function extractLocaleFromPath$1(path) {
|
|
153
|
+
const codes = getRuntimeConfigState()?.locales || [];
|
|
154
|
+
return extractLocaleFromPath(path, codes);
|
|
155
|
+
}
|
|
156
|
+
function useI18n() {
|
|
157
|
+
return useVueI18n();
|
|
158
|
+
}
|
|
159
|
+
function t(key, ...args) {
|
|
160
|
+
const translate = useVueI18n().t;
|
|
161
|
+
return String(translate(key, ...args));
|
|
162
|
+
}
|
|
163
|
+
function useLocalePath() {
|
|
164
|
+
const i18n = useVueI18n();
|
|
165
|
+
return (path, locale) => localizePath$1(path, locale ?? String(i18n.locale.value));
|
|
166
|
+
}
|
|
167
|
+
function useSwitchLocalePath() {
|
|
168
|
+
const route = useRoute();
|
|
169
|
+
return (locale) => switchLocalePath$1(locale, route.path);
|
|
170
|
+
}
|
|
171
|
+
function useLocaleRoute() {
|
|
172
|
+
return useLocalePath();
|
|
173
|
+
}
|
|
174
|
+
function useLocaleHead() {
|
|
175
|
+
const i18n = useVueI18n();
|
|
176
|
+
const route = useRoute();
|
|
177
|
+
return computed(() => {
|
|
178
|
+
const cfg = getRuntimeConfigState();
|
|
179
|
+
const routing = cfg ? routingFrom(cfg) : {
|
|
180
|
+
defaultLocale: "en",
|
|
181
|
+
locales: ["en"],
|
|
182
|
+
strategy: "prefix_except_default"
|
|
183
|
+
};
|
|
184
|
+
return buildLocaleHead({
|
|
185
|
+
path: route.path,
|
|
186
|
+
locale: String(i18n.locale.value),
|
|
187
|
+
locales: (getRuntimeState().metas || []).map((l) => ({
|
|
188
|
+
code: l.code,
|
|
189
|
+
language: l.language,
|
|
190
|
+
name: l.name,
|
|
191
|
+
dir: l.dir,
|
|
192
|
+
isDefault: l.isDefault
|
|
193
|
+
})),
|
|
194
|
+
routing,
|
|
195
|
+
baseUrl: cfg?.baseUrl
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** Hydrate routing config from SSR payload before the first render. */
|
|
200
|
+
function initClientI18n() {
|
|
201
|
+
const hydrated = readHydratedPayload();
|
|
202
|
+
if (!hydrated) return;
|
|
203
|
+
applyHydratedPayload(hydrated);
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { useSwitchLocalePath as _, getI18nRuntimeConfig as a, installUbeanI18n as c, switchLocalePath$1 as d, t as f, useLocaleRoute as g, useLocalePath as h, extractLocaleFromPath$1 as i, localizePath$1 as l, useLocaleHead as m, configureI18nRuntime as n, getLocale as o, useI18n as p, createUbeanI18n as r, initClientI18n as s, bindI18nRuntime as t, setLocale as u };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient$1, h as UbeanAppOptions, j as createUbeanRouter, k as usePage, m as UbeanAppInstance, n as Head, v as createUbeanClientApp, y as createUbeanSSRApp } from "./app-
|
|
1
|
+
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient$1, h as UbeanAppOptions, j as createUbeanRouter, k as usePage, m as UbeanAppInstance, n as Head, v as createUbeanClientApp, y as createUbeanSSRApp } from "./app-BP3eySMH.js";
|
|
2
2
|
import { AppPluginConfig, DefineAppOptions, ResolvedAppConfig, applyAppConfig, createDefaultAppConfig, defineApp, mergeAppConfig } from "./define-app.js";
|
|
3
|
-
import { ComputedRef, Ref, ShallowRef } from "vue";
|
|
3
|
+
import { App, ComputedRef, Ref, ShallowRef } from "vue";
|
|
4
4
|
import { useRouter } from "vue-router";
|
|
5
5
|
import { ERROR_KEY, ErrorBoundary, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, RouteLocation, RouteLocationRaw, SSR_KEY, SlotView, TRANSITION_KEY, TypedLinkProps, UbeanVueOptions, UbeanVuePageData, UseCacheViewsReturn, UsePageTransitionReturn, UseReloadSignalReturn, ViewTransitionOptions, clearPageTransition, disablePageCache, enablePageCache, excludePageCache, getCacheEnabled, getCachedViewNames, getExcludedViewNames, getNamedPageWrapper, getNavigationType, getPageTransitionName, getReloadCounter, includePageCache, initCachedViewsFromRoutes, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isReloading, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveRoute, setPageTransition, supportsViewTransitions, ubeanVue, useCacheViews, usePageTransition, useReloadSignal, useViewTransition, useViewTransitionState, withViewTransition } from "@ubean/vue";
|
|
6
6
|
import { VueHeadClient, injectHead } from "@unhead/vue";
|
|
7
7
|
import { createHead as createClientHead } from "@unhead/vue/client";
|
|
8
|
-
import { PageObject } from "@ubean/pages";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { DataResult, PageObject, UseAsyncDataOptions, UseFetchOptions, invalidateData, setDefaultFetch, useAsyncData, useData, useFetch } from "@ubean/pages";
|
|
9
|
+
import { Composer, I18n, I18n as VueI18nInstance } from "vue-i18n";
|
|
10
|
+
import { LocaleRoutingConfig, buildLocaleHead } from "@ubean/i18n/browser";
|
|
11
|
+
import { HydrateIslandsOptions, IslandHydrateOptions, IslandHydrationScheduler, IslandRecord, collectIslands, hasPendingIslands, hydrateIsland, hydrateIslands, scheduleIslandHydration } from "@ubean/islands/runtime";
|
|
11
12
|
import { LinkTag, MetaTag, SeoMetadata } from "@ubean/seo";
|
|
12
13
|
//#region src/client.d.ts
|
|
13
14
|
declare function getInitialPageData<T = Record<string, unknown>>(): PageObject<T> | null;
|
|
@@ -46,7 +47,7 @@ interface DataCacheStore {
|
|
|
46
47
|
getTimestamp: (key: string | symbol) => number | undefined;
|
|
47
48
|
}
|
|
48
49
|
declare function createDataCacheStore(): DataCacheStore;
|
|
49
|
-
interface UseAsyncDataOptions<T> {
|
|
50
|
+
interface UseAsyncDataOptions$1<T> {
|
|
50
51
|
key?: string;
|
|
51
52
|
tags?: string[];
|
|
52
53
|
lazy?: boolean;
|
|
@@ -67,7 +68,7 @@ interface UseAsyncDataReturn<T> {
|
|
|
67
68
|
refresh: () => Promise<void>;
|
|
68
69
|
invalidate: () => void;
|
|
69
70
|
}
|
|
70
|
-
declare function createUseAsyncData(store: DataCacheStore): <T = unknown>(keyOrFetcher: string | (() => Promise<T>), fetcherOrOptions?: (() => Promise<T>) | UseAsyncDataOptions<T>, options?: UseAsyncDataOptions<T>) => UseAsyncDataReturn<T>;
|
|
71
|
+
declare function createUseAsyncData(store: DataCacheStore): <T = unknown>(keyOrFetcher: string | (() => Promise<T>), fetcherOrOptions?: (() => Promise<T>) | UseAsyncDataOptions$1<T>, options?: UseAsyncDataOptions$1<T>) => UseAsyncDataReturn<T>;
|
|
71
72
|
declare function invalidateCache(store: DataCacheStore, keyOrTag: string): number;
|
|
72
73
|
declare function clearCache(store: DataCacheStore): void;
|
|
73
74
|
declare function defineDataKey(key: string): symbol;
|
|
@@ -83,54 +84,79 @@ declare function useServerData<T>(fetcher: () => Promise<T>): Promise<T>;
|
|
|
83
84
|
declare function getInvalidatedKeysForAction(actionName: string, invalidationMap: Record<string, Array<string | symbol>>, store: DataCacheStore): number;
|
|
84
85
|
//#endregion
|
|
85
86
|
//#region src/i18n.d.ts
|
|
86
|
-
|
|
87
|
-
interface
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
type LocaleMessages = Record<string, unknown>;
|
|
88
|
+
interface I18nRuntimeConfig {
|
|
89
|
+
defaultLocale: string;
|
|
90
|
+
locales: string[];
|
|
91
|
+
strategy: LocaleRoutingConfig['strategy'];
|
|
91
92
|
fallbackLocale: string;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
getLocale: () => string;
|
|
96
|
-
onLocaleChange: (callback: LocaleChangeCallback) => () => void;
|
|
97
|
-
getLocaleDir: (locale?: string) => 'ltr' | 'rtl';
|
|
98
|
-
getLocaleName: (locale?: string) => string | undefined;
|
|
99
|
-
localeDir: {
|
|
100
|
-
value: 'ltr' | 'rtl';
|
|
101
|
-
};
|
|
102
|
-
localeName: {
|
|
103
|
-
value: string | undefined;
|
|
104
|
-
};
|
|
93
|
+
cookieName: string;
|
|
94
|
+
baseUrl: string;
|
|
95
|
+
vueI18n?: Record<string, unknown>;
|
|
105
96
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
declare function onLocaleChange(callback: LocaleChangeCallback): () => void;
|
|
112
|
-
declare function getLocaleDir(locale?: string): 'ltr' | 'rtl';
|
|
113
|
-
declare function getLocaleName(locale?: string): string | undefined;
|
|
114
|
-
declare function getRegisteredLocales(): string[];
|
|
115
|
-
declare function detectLocale(acceptLanguage?: string): string;
|
|
116
|
-
declare function detectBrowserLocale(): string;
|
|
117
|
-
declare function addLocale(code: string, messages: LocaleMessages, options?: {
|
|
118
|
-
name?: string;
|
|
97
|
+
interface LocaleLoader {
|
|
98
|
+
(code: string): Promise<LocaleMessages>;
|
|
99
|
+
}
|
|
100
|
+
interface HydratedLocalePayload {
|
|
101
|
+
locale?: string;
|
|
119
102
|
dir?: 'ltr' | 'rtl';
|
|
103
|
+
messages?: LocaleMessages;
|
|
104
|
+
fallbackLocale?: string;
|
|
105
|
+
fallbackMessages?: LocaleMessages;
|
|
106
|
+
routing?: LocaleRoutingConfig;
|
|
107
|
+
cookieName?: string;
|
|
108
|
+
baseUrl?: string;
|
|
109
|
+
locales?: Array<{
|
|
110
|
+
code: string;
|
|
111
|
+
language?: string;
|
|
112
|
+
name?: string;
|
|
113
|
+
dir?: 'ltr' | 'rtl';
|
|
114
|
+
isDefault?: boolean;
|
|
115
|
+
}>;
|
|
116
|
+
availableLocales?: Array<{
|
|
117
|
+
code: string;
|
|
118
|
+
language?: string;
|
|
119
|
+
name?: string;
|
|
120
|
+
dir?: 'ltr' | 'rtl';
|
|
121
|
+
isDefault?: boolean;
|
|
122
|
+
}>;
|
|
123
|
+
}
|
|
124
|
+
declare function configureI18nRuntime(options: {
|
|
125
|
+
config: I18nRuntimeConfig;
|
|
126
|
+
loadLocale?: LocaleLoader;
|
|
127
|
+
locales?: HydratedLocalePayload['locales'];
|
|
120
128
|
}): void;
|
|
121
|
-
declare function
|
|
122
|
-
declare function
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
declare function getI18nRuntimeConfig(): I18nRuntimeConfig | null;
|
|
130
|
+
declare function createUbeanI18n(options?: {
|
|
131
|
+
locale?: string;
|
|
132
|
+
fallbackLocale?: string;
|
|
133
|
+
messages?: Record<string, LocaleMessages>;
|
|
134
|
+
vueI18n?: Record<string, unknown>;
|
|
135
|
+
}): I18n;
|
|
136
|
+
declare function installUbeanI18n(app: App, i18n: I18n): void;
|
|
137
|
+
/**
|
|
138
|
+
* Framework setLocale: load messages, switch composer locale, write cookie,
|
|
139
|
+
* navigate to the localized path (`no_prefix` skips navigation).
|
|
140
|
+
*
|
|
141
|
+
* Uses the runtime bound by `bindI18nRuntime` (not Vue composables) so it is
|
|
142
|
+
* safe to call from click handlers.
|
|
143
|
+
*/
|
|
144
|
+
declare function setLocale(code: string): Promise<void>;
|
|
145
|
+
declare function getLocale(): string;
|
|
125
146
|
declare function localizePath(path: string, locale?: string): string;
|
|
126
|
-
declare function switchLocalePath(
|
|
127
|
-
declare function getDefaultLocale(): string;
|
|
147
|
+
declare function switchLocalePath(locale: string, path?: string): string;
|
|
128
148
|
declare function extractLocaleFromPath(path: string): {
|
|
129
149
|
locale: string | null;
|
|
130
150
|
pathWithoutLocale: string;
|
|
131
151
|
};
|
|
132
|
-
declare function
|
|
133
|
-
declare function
|
|
152
|
+
declare function useI18n(): Composer;
|
|
153
|
+
declare function t(key: string, ...args: unknown[]): string;
|
|
154
|
+
declare function useLocalePath(): (path: string, locale?: string) => string;
|
|
155
|
+
declare function useSwitchLocalePath(): (locale: string) => string;
|
|
156
|
+
declare function useLocaleRoute(): (path: string, locale?: string) => string;
|
|
157
|
+
declare function useLocaleHead(): ComputedRef<ReturnType<typeof buildLocaleHead>>;
|
|
158
|
+
/** Hydrate routing config from SSR payload before the first render. */
|
|
159
|
+
declare function initClientI18n(): void;
|
|
134
160
|
//#endregion
|
|
135
161
|
//#region src/color-mode.d.ts
|
|
136
162
|
interface ColorModeConfig {
|
|
@@ -483,4 +509,4 @@ declare function _resetSearch(): void;
|
|
|
483
509
|
*/
|
|
484
510
|
declare function _setPagefindMock(mock: any): void;
|
|
485
511
|
//#endregion
|
|
486
|
-
export { type AppPluginConfig, type ColorMode, type ColorModeConfig, type CreateUbeanRouterOptions, type DataCacheStore, type DefineAppOptions, ERROR_KEY, ErrorBoundary, Head, type VueHeadClient as HeadClient, type HydrateIslandsOptions, type IslandHydrateOptions, type IslandRecord, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, type LinkProps, type LinkTag, type MetaTag, PAGE_KEY, PageView, type PartyTownConfig, type ResolvedAppConfig, type RouteLocation, type RouteLocationRaw, SSR_KEY, type ScriptTrigger, type SearchFilters, type SearchResult, type SearchRuntimeConfig, type SeoMetadata, SlotView, TRANSITION_KEY, type TypedLinkProps, type UbeanAppInstance, type UbeanAppOptions, type UbeanVueContext, type UbeanVueOptions, type UbeanVuePage, type UbeanVuePageData, type UseAsyncDataOptions, type UseAsyncDataReturn, type UseCacheViewsReturn, type UsePageTransitionReturn, type UseReloadSignalReturn, type UseScriptOptions, type UseScriptReturn, type UseSearchOptions, type UseSearchReturn, type ViewTransitionOptions, type VueHeadClient$1 as VueHeadClient, type VueI18nInstance, _resetColorMode, _resetPartyTown, _resetSearch, _setPagefindMock,
|
|
512
|
+
export { type AppPluginConfig, type ColorMode, type ColorModeConfig, type CreateUbeanRouterOptions, type DataCacheStore, type DataResult, type DefineAppOptions, ERROR_KEY, ErrorBoundary, Head, type VueHeadClient as HeadClient, type HydrateIslandsOptions, type I18nRuntimeConfig, type IslandHydrateOptions, type IslandHydrationScheduler, type IslandRecord, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, type LinkProps, type LinkTag, type LocaleLoader, type LocaleMessages, type MetaTag, PAGE_KEY, PageView, type PartyTownConfig, type ResolvedAppConfig, type RouteLocation, type RouteLocationRaw, SSR_KEY, type ScriptTrigger, type SearchFilters, type SearchResult, type SearchRuntimeConfig, type SeoMetadata, SlotView, TRANSITION_KEY, type TypedLinkProps, type UbeanAppInstance, type UbeanAppOptions, type UbeanVueContext, type UbeanVueOptions, type UbeanVuePage, type UbeanVuePageData, type UseAsyncDataOptions, type UseAsyncDataReturn, type UseCacheViewsReturn, type UseFetchOptions, type UsePageTransitionReturn, type UseReloadSignalReturn, type UseScriptOptions, type UseScriptReturn, type UseSearchOptions, type UseSearchReturn, type ViewTransitionOptions, type VueHeadClient$1 as VueHeadClient, type VueI18nInstance, _resetColorMode, _resetPartyTown, _resetSearch, _setPagefindMock, applyAppConfig, clearCache, clearPageTransition, collectIslands, configureColorMode, configureI18nRuntime, configurePartyTown, configureSearch, createClientHead, createDataCacheStore, createDefaultAppConfig, createLinkHandler, createUbeanClientApp, createUbeanI18n, createUbeanRouter, createUbeanSSRApp, createUseAsyncData, defineApp, defineDataKey, defineMeta, defineMiddleware, definePage, disablePageCache, enablePageCache, excludePageCache, executeSearch, extractLocaleFromPath, extractPageData, forceColorMode, getCacheEnabled, getCachedViewNames, getColorModeConfig, getColorModeScript, getExcludedViewNames, getI18nRuntimeConfig, getInitialPageData, getInitialState, getInvalidatedKeysForAction, getLocale, getNamedPageWrapper, getNavigationType, getPageTransitionName, getPartyTownConfig, getPartyTownHeadContent, getPartyTownScript, getReloadCounter, getSearchConfig, hasPendingIslands, hydrateIsland, hydrateIslands, includePageCache, initCachedViewsFromRoutes, initClientI18n, initPagefind, injectHead, installUbeanI18n, invalidateCache, invalidateData, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isPagefindLoaded, isPartyTownEnabled, isReloading, localizePath, mergeAppConfig, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveColorModeConfig, resolvePartyTownConfig, resolveRoute, resolveSearchConfig, scheduleIslandHydration, setDefaultFetch, setLocale, setPageTransition, supportsViewTransitions, switchLocalePath, t, ubeanVue, unforceColorMode, useAsyncData, useCacheViews, useColorMode, useData, useFetch, useUnheadHead as useHead, useHeadInstance, useI18n, useLocaleHead, useLocalePath, useLocaleRoute, usePage, usePageTransition, useReloadSignal, useRouter, useScript, useSearch, useSeoMeta, useServerData, useSwitchLocalePath, useViewTransition, useViewTransitionState, withViewTransition };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as createUbeanRouter, S as definePage, _ as useSeoMeta, b as defineMeta, m as createUbeanSSRApp, n as Head, p as createUbeanClientApp, v as useUnheadHead, w as usePage, x as defineMiddleware } from "./app-B6vVB7hp.js";
|
|
2
|
+
import { _ as useSwitchLocalePath, a as getI18nRuntimeConfig, c as installUbeanI18n, d as switchLocalePath, f as t, g as useLocaleRoute, h as useLocalePath, i as extractLocaleFromPath, l as localizePath, m as useLocaleHead, n as configureI18nRuntime, o as getLocale, p as useI18n, r as createUbeanI18n, s as initClientI18n, u as setLocale } from "./i18n-t0G3VyBs.js";
|
|
2
3
|
import { applyAppConfig, createDefaultAppConfig, defineApp, mergeAppConfig } from "./define-app.js";
|
|
3
4
|
import { computed, getCurrentInstance, markRaw, onMounted, onScopeDispose, onUnmounted, ref, shallowRef, watch } from "vue";
|
|
4
5
|
import { useRouter } from "vue-router";
|
|
5
6
|
import { ERROR_KEY, ErrorBoundary, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, clearPageTransition, disablePageCache, enablePageCache, excludePageCache, getCacheEnabled, getCachedViewNames, getExcludedViewNames, getNamedPageWrapper, getNavigationType, getPageTransitionName, getReloadCounter, includePageCache, initCachedViewsFromRoutes, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isReloading, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveRoute, setPageTransition, supportsViewTransitions, ubeanVue, useCacheViews, usePageTransition, useReloadSignal, useViewTransition, useViewTransitionState, withViewTransition } from "@ubean/vue";
|
|
6
7
|
import { injectHead, injectHead as injectHead$1 } from "@unhead/vue";
|
|
7
8
|
import { createHead as createClientHead } from "@unhead/vue/client";
|
|
8
|
-
import { defineDataKey as defineDataKey$1 } from "@ubean/pages";
|
|
9
|
-
import { collectIslands, hydrateIsland, hydrateIslands } from "@ubean/islands/runtime";
|
|
9
|
+
import { defineDataKey as defineDataKey$1, invalidateData, setDefaultFetch, useAsyncData, useData, useFetch } from "@ubean/pages";
|
|
10
|
+
import { collectIslands, hasPendingIslands, hydrateIsland, hydrateIslands, scheduleIslandHydration } from "@ubean/islands/runtime";
|
|
10
11
|
//#region src/client.ts
|
|
11
12
|
const _global$1 = globalThis;
|
|
12
13
|
function getInitialPageData() {
|
|
@@ -1021,4 +1022,4 @@ function _setPagefindMock(mock) {
|
|
|
1021
1022
|
pagefindPromise = Promise.resolve(mock);
|
|
1022
1023
|
}
|
|
1023
1024
|
//#endregion
|
|
1024
|
-
export { ERROR_KEY, ErrorBoundary, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, _resetColorMode, _resetPartyTown, _resetSearch, _setPagefindMock,
|
|
1025
|
+
export { ERROR_KEY, ErrorBoundary, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, _resetColorMode, _resetPartyTown, _resetSearch, _setPagefindMock, applyAppConfig, clearCache, clearPageTransition, collectIslands, configureColorMode, configureI18nRuntime, configurePartyTown, configureSearch, createClientHead, createDataCacheStore, createDefaultAppConfig, createLinkHandler, createUbeanClientApp, createUbeanI18n, createUbeanRouter, createUbeanSSRApp, createUseAsyncData, defineApp, defineDataKey, defineMeta, defineMiddleware, definePage, disablePageCache, enablePageCache, excludePageCache, executeSearch, extractLocaleFromPath, extractPageData, forceColorMode, getCacheEnabled, getCachedViewNames, getColorModeConfig, getColorModeScript, getExcludedViewNames, getI18nRuntimeConfig, getInitialPageData, getInitialState, getInvalidatedKeysForAction, getLocale, getNamedPageWrapper, getNavigationType, getPageTransitionName, getPartyTownConfig, getPartyTownHeadContent, getPartyTownScript, getReloadCounter, getSearchConfig, hasPendingIslands, hydrateIsland, hydrateIslands, includePageCache, initCachedViewsFromRoutes, initClientI18n, initPagefind, injectHead, installUbeanI18n, invalidateCache, invalidateData, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isPagefindLoaded, isPartyTownEnabled, isReloading, localizePath, mergeAppConfig, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveColorModeConfig, resolvePartyTownConfig, resolveRoute, resolveSearchConfig, scheduleIslandHydration, setDefaultFetch, setLocale, setPageTransition, supportsViewTransitions, switchLocalePath, t, ubeanVue, unforceColorMode, useAsyncData, useCacheViews, useColorMode, useData, useFetch, useUnheadHead as useHead, useHeadInstance, useI18n, useLocaleHead, useLocalePath, useLocaleRoute, usePage, usePageTransition, useReloadSignal, useRouter, useScript, useSearch, useSeoMeta, useServerData, useSwitchLocalePath, useViewTransition, useViewTransitionState, withViewTransition };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient, a as LOCALIZE_PATH_KEY, b as ubeanVue, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, j as createUbeanRouter, k as usePage, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanClientApp, w as useViewTransition, x as useRouter, y as createUbeanSSRApp } from "./app-
|
|
1
|
+
import { A as CreateUbeanRouterOptions, C as useUnheadHead, D as definePage, E as defineMiddleware, O as UbeanVuePage, S as useSeoMeta, T as defineMeta, _ as VueHeadClient, a as LOCALIZE_PATH_KEY, b as ubeanVue, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, j as createUbeanRouter, k as usePage, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanClientApp, w as useViewTransition, x as useRouter, y as createUbeanSSRApp } from "./app-BP3eySMH.js";
|
|
2
2
|
import { createHead as createServerHead } from "@unhead/vue/server";
|
|
3
3
|
export { type CreateUbeanRouterOptions, ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, UbeanAppInstance, UbeanAppOptions, type UbeanVueOptions, type UbeanVuePage, type VueHeadClient, createServerHead, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useRouter, useSeoMeta, useViewTransition };
|
package/dist/server.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as createUbeanRouter, S as definePage, _ as useSeoMeta, a as LOCALIZE_PATH_KEY, b as defineMeta, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useRouter, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useUnheadHead, w as usePage, x as defineMiddleware, y as useViewTransition } from "./app-B6vVB7hp.js";
|
|
2
2
|
import { createHead as createServerHead } from "@unhead/vue/server";
|
|
3
3
|
export { ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, createServerHead, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useRouter, useSeoMeta, useViewTransition };
|
package/dist/ssr.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ResolvedAppConfig } from "./define-app.js";
|
|
2
|
+
import { Component } from "vue";
|
|
3
|
+
import { RouteRecordRaw } from "vue-router";
|
|
4
|
+
import { PageRenderer } from "@ubean/pages";
|
|
5
|
+
import { renderToString } from "@vue/server-renderer";
|
|
6
|
+
//#region src/ssr.d.ts
|
|
7
|
+
interface VueRendererSimpleOptions {
|
|
8
|
+
resolvePageComponent: (path: string) => Promise<Component | null>;
|
|
9
|
+
resolveLayoutComponent: (name: string | false | null | undefined) => Promise<Component | null>;
|
|
10
|
+
defaultLayout?: string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Resolves the user's `defineApp` config for the server side.
|
|
13
|
+
* When provided, plugins / globalComponents / provides / head / onAppCreated
|
|
14
|
+
* will be applied to each SSR app instance.
|
|
15
|
+
*/
|
|
16
|
+
resolveAppConfig?: () => ResolvedAppConfig | Promise<ResolvedAppConfig>;
|
|
17
|
+
}
|
|
18
|
+
interface VueRendererRouterOptions {
|
|
19
|
+
routes: RouteRecordRaw[];
|
|
20
|
+
resolveLayoutComponent: (name: string | false | null | undefined) => Promise<Component | null>;
|
|
21
|
+
defaultLayout?: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the user's `defineApp` config for the server side.
|
|
24
|
+
* When provided, plugins / globalComponents / provides / head / onAppCreated
|
|
25
|
+
* will be applied to each SSR app instance.
|
|
26
|
+
*/
|
|
27
|
+
resolveAppConfig?: () => ResolvedAppConfig | Promise<ResolvedAppConfig>;
|
|
28
|
+
}
|
|
29
|
+
type VueRendererOptions = VueRendererSimpleOptions | VueRendererRouterOptions;
|
|
30
|
+
declare function createVueRenderer(options: VueRendererOptions): PageRenderer;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { VueRendererOptions, VueRendererRouterOptions, VueRendererSimpleOptions, createVueRenderer, renderToString };
|
package/dist/ssr.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { r as createUbeanI18n } from "./i18n-t0G3VyBs.js";
|
|
2
|
+
import { applyAppConfig } from "./define-app.js";
|
|
3
|
+
import { createSSRApp, defineComponent, h, provide, reactive } from "vue";
|
|
4
|
+
import { SSR_CONTENT_MARKER, STATE_DATA_ID, STATE_MARKER, __clearDataPayload, __clearDeferred, __resolveDataPayload, __resolveDeferred, __serializeDataPayload, __serializeDeferred, safeJsonStringify } from "@ubean/pages";
|
|
5
|
+
import { buildLocaleHead } from "@ubean/i18n/browser";
|
|
6
|
+
import { createHead, renderSSRHead, transformHtmlTemplate } from "@unhead/vue/server";
|
|
7
|
+
import { renderToNodeStream, renderToString, renderToString as renderToString$1 } from "@vue/server-renderer";
|
|
8
|
+
import { getIslandsBootstrapScript } from "@ubean/islands";
|
|
9
|
+
//#region src/ssr.ts
|
|
10
|
+
/**
|
|
11
|
+
* Push a `PageHead` (from `defineApp` or page-level `head`) into an @unhead head instance.
|
|
12
|
+
* Extracted so both the global app head and per-page head use the same mapping.
|
|
13
|
+
*/
|
|
14
|
+
function pushPageHead(head, pageHead) {
|
|
15
|
+
const headInput = {};
|
|
16
|
+
if (pageHead.title) headInput.title = pageHead.title;
|
|
17
|
+
if (pageHead.htmlAttrs) headInput.htmlAttrs = pageHead.htmlAttrs;
|
|
18
|
+
if (pageHead.bodyAttrs) headInput.bodyAttrs = pageHead.bodyAttrs;
|
|
19
|
+
if (pageHead.meta) headInput.meta = pageHead.meta;
|
|
20
|
+
if (pageHead.link) headInput.link = pageHead.link;
|
|
21
|
+
if (pageHead.script) headInput.script = pageHead.script;
|
|
22
|
+
head.push(headInput);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Apply user's `defineApp` config (plugins, globalComponents, provides) and
|
|
26
|
+
* invoke `onAppCreated` on a freshly created SSR app instance.
|
|
27
|
+
*/
|
|
28
|
+
async function applyServerAppConfig(app, appConfig) {
|
|
29
|
+
applyAppConfig(app, appConfig, "server");
|
|
30
|
+
if (appConfig.onAppCreated) await appConfig.onAppCreated(app);
|
|
31
|
+
}
|
|
32
|
+
function isSimpleOptions(opts) {
|
|
33
|
+
return "resolvePageComponent" in opts;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve a single layout component by name.
|
|
37
|
+
* Returns `null` when layout is `false`, `null`, or not found.
|
|
38
|
+
* ubean uses flat single-layer layout (no nesting), matching the
|
|
39
|
+
* `layout: string | false` field on each page route.
|
|
40
|
+
*/
|
|
41
|
+
async function resolveSingleLayout(layoutName, defaultLayout, resolveLayoutComponent) {
|
|
42
|
+
const resolved = layoutName === void 0 ? defaultLayout : layoutName;
|
|
43
|
+
if (resolved === false || resolved == null) return null;
|
|
44
|
+
return resolveLayoutComponent(resolved);
|
|
45
|
+
}
|
|
46
|
+
function createSimpleApp(pageObj, PageComp, layout, head) {
|
|
47
|
+
const pageData = reactive({ ...pageObj });
|
|
48
|
+
const PAGE_KEY = Symbol("ubean-page");
|
|
49
|
+
const RootComponent = defineComponent({
|
|
50
|
+
name: "UbeanSimpleApp",
|
|
51
|
+
setup() {
|
|
52
|
+
provide(PAGE_KEY, pageData);
|
|
53
|
+
return () => {
|
|
54
|
+
const inner = h(PageComp, pageObj.props || {});
|
|
55
|
+
if (!layout) return inner;
|
|
56
|
+
return h(layout, {}, { default: () => inner });
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const app = createSSRApp(RootComponent);
|
|
61
|
+
app.use(head);
|
|
62
|
+
return app;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Shared setup for both buffered and streaming renders: creates the head
|
|
66
|
+
* instance, pushes static head (defineApp + locale + page), builds the Vue
|
|
67
|
+
* SSR app, and returns everything the render methods need.
|
|
68
|
+
*/
|
|
69
|
+
async function prepareRender(options, pageObj, renderContext) {
|
|
70
|
+
const head = createHead();
|
|
71
|
+
const appConfig = options.resolveAppConfig ? await options.resolveAppConfig() : null;
|
|
72
|
+
if (appConfig?.head) pushPageHead(head, appConfig.head);
|
|
73
|
+
if (renderContext?.locale) {
|
|
74
|
+
const routing = renderContext.routing;
|
|
75
|
+
if (routing && renderContext.availableLocales?.length) {
|
|
76
|
+
const tags = buildLocaleHead({
|
|
77
|
+
path: pageObj.url.split("?")[0] || "/",
|
|
78
|
+
locale: renderContext.locale,
|
|
79
|
+
locales: renderContext.availableLocales,
|
|
80
|
+
routing,
|
|
81
|
+
baseUrl: renderContext.baseUrl
|
|
82
|
+
});
|
|
83
|
+
head.push({
|
|
84
|
+
htmlAttrs: tags.htmlAttrs,
|
|
85
|
+
link: tags.link,
|
|
86
|
+
meta: tags.meta
|
|
87
|
+
});
|
|
88
|
+
} else head.push({ htmlAttrs: {
|
|
89
|
+
lang: renderContext.locale,
|
|
90
|
+
dir: renderContext.localeDir || "ltr"
|
|
91
|
+
} });
|
|
92
|
+
}
|
|
93
|
+
if (pageObj.head) pushPageHead(head, pageObj.head);
|
|
94
|
+
const i18n = renderContext?.locale ? createUbeanI18n({
|
|
95
|
+
locale: renderContext.locale,
|
|
96
|
+
fallbackLocale: renderContext.fallbackLocale || renderContext.locale,
|
|
97
|
+
messages: {
|
|
98
|
+
[renderContext.locale]: renderContext.messages || {},
|
|
99
|
+
...renderContext.fallbackLocale && renderContext.fallbackMessages && renderContext.fallbackLocale !== renderContext.locale ? { [renderContext.fallbackLocale]: renderContext.fallbackMessages } : {}
|
|
100
|
+
}
|
|
101
|
+
}) : void 0;
|
|
102
|
+
let renderApp;
|
|
103
|
+
if (isSimpleOptions(options)) {
|
|
104
|
+
const pageComponent = await options.resolvePageComponent(pageObj.component);
|
|
105
|
+
if (!pageComponent) throw new Error(`[ubean] Could not resolve page component: ${pageObj.component}`);
|
|
106
|
+
const app = createSimpleApp(pageObj, pageComponent, pageObj.layout === false ? null : await resolveSingleLayout(pageObj.layout, options.defaultLayout || null, options.resolveLayoutComponent), head);
|
|
107
|
+
if (i18n) app.use(i18n);
|
|
108
|
+
if (appConfig) await applyServerAppConfig(app, appConfig);
|
|
109
|
+
renderApp = app;
|
|
110
|
+
} else {
|
|
111
|
+
const { createUbeanSSRApp } = await import("@ubean/client/app");
|
|
112
|
+
const { app: createdApp, router } = createUbeanSSRApp(pageObj, {
|
|
113
|
+
routes: options.routes,
|
|
114
|
+
resolveLayoutComponent: options.resolveLayoutComponent,
|
|
115
|
+
defaultLayout: options.defaultLayout,
|
|
116
|
+
head,
|
|
117
|
+
i18n
|
|
118
|
+
});
|
|
119
|
+
if (appConfig) await applyServerAppConfig(createdApp, appConfig);
|
|
120
|
+
await router.isReady();
|
|
121
|
+
renderApp = createdApp;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
app: renderApp,
|
|
125
|
+
head,
|
|
126
|
+
appConfig
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Serialize SSR state (e.g. Pinia state) from the app instance after render.
|
|
131
|
+
* Returns `undefined` when there is no state or serialization fails.
|
|
132
|
+
*/
|
|
133
|
+
async function resolveState(app, appConfig) {
|
|
134
|
+
if (!appConfig?.serializeState) return void 0;
|
|
135
|
+
try {
|
|
136
|
+
const serialized = await appConfig.serializeState(app);
|
|
137
|
+
if (serialized && Object.keys(serialized).length > 0) return serialized;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error("[ubean-ssr] serializeState failed:", err);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* P9-24: Collect dynamic head tags that were added during streaming.
|
|
144
|
+
*
|
|
145
|
+
* During streaming SSR, the initial `<head>` is sent before the Vue app
|
|
146
|
+
* renders. Any `useHead()` / `useSeoMeta()` calls inside component setup
|
|
147
|
+
* push entries to the head instance *after* the initial head was sent.
|
|
148
|
+
*
|
|
149
|
+
* This function compares the head state before and after streaming to
|
|
150
|
+
* find tags that were added dynamically, and returns them as an HTML string
|
|
151
|
+
* that can be injected before the stream closes.
|
|
152
|
+
*
|
|
153
|
+
* This ensures SEO crawlers and social media bots see the complete metadata
|
|
154
|
+
* (title, og:tags, etc.) without waiting for client hydration.
|
|
155
|
+
*/
|
|
156
|
+
function collectDynamicHeadTags(head, staticHeadTags) {
|
|
157
|
+
const fullTags = renderSSRHead(head).headTags || "";
|
|
158
|
+
if (!fullTags || fullTags === staticHeadTags) return "";
|
|
159
|
+
const staticLines = new Set(staticHeadTags.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
160
|
+
const dynamicLines = fullTags.split("\n").map((l) => l.trim()).filter((l) => Boolean(l) && !staticLines.has(l));
|
|
161
|
+
if (dynamicLines.length === 0) return "";
|
|
162
|
+
return dynamicLines.join("\n");
|
|
163
|
+
}
|
|
164
|
+
function createVueRenderer(options) {
|
|
165
|
+
const render = async (pageObj, shellHtml, _assetTags, renderContext) => {
|
|
166
|
+
__clearDeferred();
|
|
167
|
+
__clearDataPayload();
|
|
168
|
+
const { app, head, appConfig } = await prepareRender(options, pageObj, renderContext);
|
|
169
|
+
const appHtml = await renderToString$1(app);
|
|
170
|
+
const state = await resolveState(app, appConfig);
|
|
171
|
+
const deferredData = await __resolveDeferred();
|
|
172
|
+
const deferredScript = __serializeDeferred(deferredData);
|
|
173
|
+
const dataPayload = __resolveDataPayload();
|
|
174
|
+
const dataScript = __serializeDataPayload(dataPayload);
|
|
175
|
+
if (!shellHtml) {
|
|
176
|
+
const tail = (dataScript ? dataScript : "") + (deferredScript ? deferredScript : "");
|
|
177
|
+
return tail ? appHtml + tail : appHtml;
|
|
178
|
+
}
|
|
179
|
+
const htmlWithApp = shellHtml.replace(SSR_CONTENT_MARKER, appHtml);
|
|
180
|
+
let html = transformHtmlTemplate(head, htmlWithApp);
|
|
181
|
+
const preStateScripts = (dataScript ? `${dataScript}\n ` : "") + (deferredScript ? `${deferredScript}\n ` : "");
|
|
182
|
+
if (preStateScripts) html = html.replace(new RegExp(`(<script id="${STATE_DATA_ID}")`), `${preStateScripts}$1`);
|
|
183
|
+
if (state) return {
|
|
184
|
+
html,
|
|
185
|
+
state
|
|
186
|
+
};
|
|
187
|
+
return html;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Streaming SSR: 返回一个 ReadableStream<Uint8Array>,将 HTML 文档分块流式输出。
|
|
191
|
+
*
|
|
192
|
+
* 与 `render` 的关键差异:
|
|
193
|
+
* - 使用 Vue 的 `renderToStream` 替代 `renderToString`,app HTML 边渲染边输出
|
|
194
|
+
* - 静态 head(defineApp + definePage)在流式开始前注入 `<head>`
|
|
195
|
+
* - SSR state(Pinia 等)在 app 流结束后、tail 之前注入为 `<script>`(移到 app div 之后,
|
|
196
|
+
* 因为 state 只有渲染完成后才可用;客户端通过 getElementById 读取,位置无关)
|
|
197
|
+
* - P9-24: 动态 `useHead()`(组件 setup 内)在 app 流结束后收集,作为 late head tags
|
|
198
|
+
* 注入到 tail 之前。浏览器会将 `<meta>`/`<title>`/`<link>` 标签自动移入 `<head>`,
|
|
199
|
+
* 确保 SEO 爬虫和社交机器人看到完整 metadata,无需等待客户端水合。
|
|
200
|
+
*/
|
|
201
|
+
const renderToStreamFn = (pageObj, shellHtml, _assetTags, renderContext) => {
|
|
202
|
+
const encoder = new TextEncoder();
|
|
203
|
+
async function pumpVueStream(vueStream, controller) {
|
|
204
|
+
for await (const chunk of vueStream) {
|
|
205
|
+
const text = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
206
|
+
controller.enqueue(encoder.encode(text));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return new ReadableStream({ async start(controller) {
|
|
210
|
+
try {
|
|
211
|
+
__clearDeferred();
|
|
212
|
+
__clearDataPayload();
|
|
213
|
+
const { app, head, appConfig } = await prepareRender(options, pageObj, renderContext);
|
|
214
|
+
if (!shellHtml) {
|
|
215
|
+
await pumpVueStream(renderToNodeStream(app), controller);
|
|
216
|
+
const dataPayload = __resolveDataPayload();
|
|
217
|
+
const dataScript = __serializeDataPayload(dataPayload);
|
|
218
|
+
if (dataScript) controller.enqueue(encoder.encode(dataScript));
|
|
219
|
+
const deferredData = await __resolveDeferred();
|
|
220
|
+
const deferredScript = __serializeDeferred(deferredData);
|
|
221
|
+
if (deferredScript) controller.enqueue(encoder.encode(deferredScript));
|
|
222
|
+
controller.close();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const markerIdx = shellHtml.indexOf(SSR_CONTENT_MARKER);
|
|
226
|
+
if (markerIdx === -1) {
|
|
227
|
+
await pumpVueStream(renderToNodeStream(app), controller);
|
|
228
|
+
const dataPayload = __resolveDataPayload();
|
|
229
|
+
const dataScript = __serializeDataPayload(dataPayload);
|
|
230
|
+
if (dataScript) controller.enqueue(encoder.encode(dataScript));
|
|
231
|
+
const deferredData = await __resolveDeferred();
|
|
232
|
+
const deferredScript = __serializeDeferred(deferredData);
|
|
233
|
+
if (deferredScript) controller.enqueue(encoder.encode(deferredScript));
|
|
234
|
+
controller.close();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
let headPart = shellHtml.slice(0, markerIdx);
|
|
238
|
+
const tailPart = shellHtml.slice(markerIdx + SSR_CONTENT_MARKER.length);
|
|
239
|
+
const stateScriptRegex = new RegExp(`<script id="${STATE_DATA_ID}" type="application/json">${STATE_MARKER}<\/script>`);
|
|
240
|
+
headPart = headPart.replace(stateScriptRegex, "");
|
|
241
|
+
headPart = transformHtmlTemplate(head, headPart);
|
|
242
|
+
const staticHeadTags = renderSSRHead(head).headTags || "";
|
|
243
|
+
controller.enqueue(encoder.encode(headPart));
|
|
244
|
+
await pumpVueStream(renderToNodeStream(app), controller);
|
|
245
|
+
const dynamicHeadTags = collectDynamicHeadTags(head, staticHeadTags);
|
|
246
|
+
const dataPayload = __resolveDataPayload();
|
|
247
|
+
const dataScript = __serializeDataPayload(dataPayload);
|
|
248
|
+
const deferredData = await __resolveDeferred();
|
|
249
|
+
const deferredScript = __serializeDeferred(deferredData);
|
|
250
|
+
const state = await resolveState(app, appConfig);
|
|
251
|
+
const stateScript = `<script id="${STATE_DATA_ID}" type="application/json">${state ? safeJsonStringify(state) : ""}<\/script>`;
|
|
252
|
+
const tailContent = (dynamicHeadTags ? `${dynamicHeadTags}\n` : "") + (dataScript ? `${dataScript}\n ` : "") + (deferredScript ? `${deferredScript}\n ` : "") + stateScript + tailPart;
|
|
253
|
+
controller.enqueue(encoder.encode(tailContent));
|
|
254
|
+
controller.close();
|
|
255
|
+
} catch (err) {
|
|
256
|
+
controller.error(err);
|
|
257
|
+
}
|
|
258
|
+
} });
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
render,
|
|
262
|
+
renderToStream: renderToStreamFn,
|
|
263
|
+
preambleScript: getIslandsBootstrapScript()
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
export { createVueRenderer, renderToString };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Framework client runtime for ubean — layered over the lean @ubean/vue kernel, adding app factories, unhead, i18n, data layer, islands hydration and SSR state helpers",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -25,17 +25,23 @@
|
|
|
25
25
|
"./server": {
|
|
26
26
|
"types": "./dist/server.d.ts",
|
|
27
27
|
"import": "./dist/server.js"
|
|
28
|
+
},
|
|
29
|
+
"./ssr": {
|
|
30
|
+
"types": "./dist/ssr.d.ts",
|
|
31
|
+
"import": "./dist/ssr.js"
|
|
28
32
|
}
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
|
-
"@unhead/vue": "^3.
|
|
35
|
+
"@unhead/vue": "^3.4.0",
|
|
36
|
+
"@vue/server-renderer": "^3.5.41",
|
|
32
37
|
"vue": "^3.5.41",
|
|
38
|
+
"vue-i18n": "11.4.8",
|
|
33
39
|
"vue-router": "^5.2.0",
|
|
34
|
-
"@ubean/
|
|
35
|
-
"@ubean/i18n": "0.
|
|
36
|
-
"@ubean/
|
|
37
|
-
"@ubean/
|
|
38
|
-
"@ubean/vue": "0.
|
|
40
|
+
"@ubean/pages": "0.3.0",
|
|
41
|
+
"@ubean/i18n": "0.3.0",
|
|
42
|
+
"@ubean/shared": "0.3.0",
|
|
43
|
+
"@ubean/islands": "0.3.0",
|
|
44
|
+
"@ubean/vue": "0.3.0"
|
|
39
45
|
},
|
|
40
46
|
"devDependencies": {
|
|
41
47
|
"@types/node": "^26.2.0",
|
|
@@ -43,13 +49,16 @@
|
|
|
43
49
|
"vite-plus": "0.2.9"
|
|
44
50
|
},
|
|
45
51
|
"peerDependencies": {
|
|
46
|
-
"@ubean/seo": "0.
|
|
52
|
+
"@ubean/seo": "0.3.0"
|
|
47
53
|
},
|
|
48
54
|
"peerDependenciesMeta": {
|
|
49
55
|
"@ubean/seo": {
|
|
50
56
|
"optional": true
|
|
51
57
|
}
|
|
52
58
|
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=22"
|
|
61
|
+
},
|
|
53
62
|
"scripts": {
|
|
54
63
|
"build": "vp pack",
|
|
55
64
|
"dev": "vp pack --watch",
|