@ubean/client 0.2.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/LICENSE +21 -0
- package/dist/app-Bc1d5Svw.js +433 -0
- package/dist/app-BknjDM9j.d.ts +101 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +2 -0
- package/dist/define-app.d.ts +138 -0
- package/dist/define-app.js +97 -0
- package/dist/index.d.ts +486 -0
- package/dist/index.js +1024 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +3 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Soybean
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { computed, createApp, createSSRApp, defineComponent, h, inject, markRaw, provide, reactive, ref, shallowRef, watchEffect } from "vue";
|
|
2
|
+
import { createMemoryHistory, createRouter, createWebHistory, useRoute, useRouter } from "vue-router";
|
|
3
|
+
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
|
+
import { useHead as useUnheadHead, useSeoMeta } from "@unhead/vue";
|
|
5
|
+
import { LOCALE_DATA_ID } from "@ubean/pages";
|
|
6
|
+
import { vClient } from "@ubean/islands/directive";
|
|
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
|
+
//#region src/use-page.ts
|
|
10
|
+
/**
|
|
11
|
+
* 框架层 `usePage()` — 路由感知的响应式页面上下文。
|
|
12
|
+
*
|
|
13
|
+
* 精简内核 `@ubean/vue` 的 `usePage()` 只返回 `PAGE_KEY` 注入的
|
|
14
|
+
* pageData(props/component/errors);框架页面协议(vue-node 风格的
|
|
15
|
+
* PageObject:url/params/query/meta)在这里补全 —— 字段路由驱动,
|
|
16
|
+
* `props`/`errors` 来自工厂提供的 `PAGE_KEY` 数据(SSR 初始数据)。
|
|
17
|
+
*
|
|
18
|
+
* 路由态之所以保留在框架层:框架页面的 SSR 首屏依赖 initialPage
|
|
19
|
+
* 数据,客户端导航后由 route 驱动刷新,合并语义属于框架协议。
|
|
20
|
+
*/
|
|
21
|
+
function usePage() {
|
|
22
|
+
const route = useRoute();
|
|
23
|
+
const pageData = inject(PAGE_KEY, null);
|
|
24
|
+
return reactive({
|
|
25
|
+
url: computed(() => route.fullPath),
|
|
26
|
+
params: computed(() => route.params),
|
|
27
|
+
query: computed(() => route.query),
|
|
28
|
+
meta: computed(() => route.meta),
|
|
29
|
+
props: computed(() => pageData?.props || {}),
|
|
30
|
+
component: computed(() => route.meta.pageName || pageData?.component || ""),
|
|
31
|
+
errors: computed(() => pageData?.errors || null)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/router.ts
|
|
36
|
+
/**
|
|
37
|
+
* Framework router factory — lives in `@ubean/client` (framework layer).
|
|
38
|
+
*
|
|
39
|
+
* The lean kernel `@ubean/vue` deliberately ships NO router factory: SPAs
|
|
40
|
+
* import their route table (hand-written or from `virtual:ubean-vue-routes`)
|
|
41
|
+
* and call vue-router's `createRouter` themselves. App factories
|
|
42
|
+
* (`createUbeanClientApp` / `createUbeanSSRApp`) are framework conveniences and
|
|
43
|
+
* own this wiring: history selection (web/memory), scroll behavior, and the
|
|
44
|
+
* `setup(router)` guard hook.
|
|
45
|
+
*/
|
|
46
|
+
function createUbeanRouter(options) {
|
|
47
|
+
const base = options.base || "/";
|
|
48
|
+
const history = options.ssr ? createMemoryHistory(base) : createWebHistory(base);
|
|
49
|
+
const router = createRouter({
|
|
50
|
+
history,
|
|
51
|
+
routes: options.routes,
|
|
52
|
+
scrollBehavior(_to, _from, savedPosition) {
|
|
53
|
+
if (savedPosition) return savedPosition;
|
|
54
|
+
if (_to.hash) return {
|
|
55
|
+
el: _to.hash,
|
|
56
|
+
behavior: "smooth"
|
|
57
|
+
};
|
|
58
|
+
return { top: 0 };
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
if (options.setup) options.setup(router);
|
|
62
|
+
if (options.ssr && options.initialUrl) router.push(options.initialUrl);
|
|
63
|
+
return router;
|
|
64
|
+
}
|
|
65
|
+
//#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
|
+
//#region src/page-macro.ts
|
|
270
|
+
function definePage(_meta) {}
|
|
271
|
+
function defineMeta(_meta) {}
|
|
272
|
+
function defineMiddleware(_handler) {}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/app.ts
|
|
275
|
+
/**
|
|
276
|
+
* Framework app factories — consumed by the ubean aggregator & `@ubean/ssr`.
|
|
277
|
+
*
|
|
278
|
+
* `createUbeanClientApp` / `createUbeanSSRApp` wire the lean kernel from
|
|
279
|
+
* `@ubean/vue` together with framework extras: unhead, i18n path
|
|
280
|
+
* localization, layout-chain resolution, islands directive, and
|
|
281
|
+
* SSR/hydration semantics.
|
|
282
|
+
*/
|
|
283
|
+
/**
|
|
284
|
+
* Normalize the `layout` route meta field into an array of layout names
|
|
285
|
+
* (outer → inner). Handles `string`, `string[]`, `false`, and `undefined`
|
|
286
|
+
* (falls back to `defaultLayout`). The `'default'` name is filtered out
|
|
287
|
+
* because the default layout is applied implicitly when no layout is specified.
|
|
288
|
+
*/
|
|
289
|
+
function normalizeLayoutNames(layout, defaultLayout) {
|
|
290
|
+
if (layout === false || layout == null) return layout === false ? [] : defaultLayout ? [defaultLayout] : [];
|
|
291
|
+
if (Array.isArray(layout)) return layout.filter((n) => typeof n === "string");
|
|
292
|
+
if (layout === "default") return defaultLayout ? [defaultLayout] : [];
|
|
293
|
+
return [layout];
|
|
294
|
+
}
|
|
295
|
+
function createLayoutWrapper(resolveLayoutComponent, defaultLayout) {
|
|
296
|
+
const layoutCache = /* @__PURE__ */ new Map();
|
|
297
|
+
const layoutComps = shallowRef([]);
|
|
298
|
+
async function loadLayout(name) {
|
|
299
|
+
if (layoutCache.has(name)) return layoutCache.get(name);
|
|
300
|
+
const comp = await resolveLayoutComponent(name);
|
|
301
|
+
const raw = comp ? markRaw(comp) : null;
|
|
302
|
+
layoutCache.set(name, raw);
|
|
303
|
+
return raw;
|
|
304
|
+
}
|
|
305
|
+
return defineComponent({
|
|
306
|
+
name: "UbeanLayoutView",
|
|
307
|
+
setup() {
|
|
308
|
+
const route = useRoute();
|
|
309
|
+
watchEffect(async () => {
|
|
310
|
+
const layoutField = route.meta.layout;
|
|
311
|
+
const names = normalizeLayoutNames(layoutField === void 0 ? defaultLayout ?? void 0 : layoutField, defaultLayout);
|
|
312
|
+
if (names.length === 0) {
|
|
313
|
+
layoutComps.value = [];
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const comps = await Promise.all(names.map((n) => loadLayout(n)));
|
|
317
|
+
layoutComps.value = comps;
|
|
318
|
+
});
|
|
319
|
+
return () => {
|
|
320
|
+
const comps = layoutComps.value;
|
|
321
|
+
if (comps.length === 0) return h(PageView);
|
|
322
|
+
return h(LayoutChainRenderer, {
|
|
323
|
+
components: comps,
|
|
324
|
+
depth: 0
|
|
325
|
+
});
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Resolve a component from either a direct `Component` or a
|
|
332
|
+
* function returning a `Component` (lazy resolver from auto-detection
|
|
333
|
+
* like `resolveLoadingComponent()` / `resolveErrorComponent()`).
|
|
334
|
+
* Returns `null` if no component is configured.
|
|
335
|
+
*/
|
|
336
|
+
function resolveComp(comp) {
|
|
337
|
+
if (!comp) return null;
|
|
338
|
+
if (typeof comp === "function" && !comp.render && !comp.setup) try {
|
|
339
|
+
const resolved = comp();
|
|
340
|
+
return resolved ? markRaw(resolved) : null;
|
|
341
|
+
} catch {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return markRaw(comp);
|
|
345
|
+
}
|
|
346
|
+
function createRootComponent(LayoutWrapper, page, transitionOpts, isSSR, loadingComponent, errorComponent) {
|
|
347
|
+
return defineComponent({
|
|
348
|
+
name: isSSR ? "UbeanSSRApp" : "UbeanAppRoot",
|
|
349
|
+
setup() {
|
|
350
|
+
provide(PAGE_KEY, page);
|
|
351
|
+
provide(TRANSITION_KEY, transitionOpts);
|
|
352
|
+
provide(SSR_KEY, isSSR);
|
|
353
|
+
provide(LOADING_KEY, loadingComponent ?? null);
|
|
354
|
+
provide(ERROR_KEY, errorComponent ?? null);
|
|
355
|
+
provide(LOCALIZE_PATH_KEY, (path) => localizePath$1(path));
|
|
356
|
+
return () => h(LayoutWrapper);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function createUbeanClientApp(options) {
|
|
361
|
+
if (!options.head) throw new Error("[ubean] createUbeanClientApp requires a head instance from @unhead/vue/client");
|
|
362
|
+
initClientI18n();
|
|
363
|
+
const initial = options.initialPage || {
|
|
364
|
+
component: "",
|
|
365
|
+
props: {},
|
|
366
|
+
params: {},
|
|
367
|
+
url: "/"
|
|
368
|
+
};
|
|
369
|
+
const head = options.head;
|
|
370
|
+
const transitionOpts = options.viewTransitions === false ? { enabled: false } : options.viewTransitions === true || options.viewTransitions == null ? { enabled: true } : options.viewTransitions;
|
|
371
|
+
const page = reactive({ ...initial });
|
|
372
|
+
const router = createUbeanRouter({
|
|
373
|
+
routes: options.routes,
|
|
374
|
+
ssr: false,
|
|
375
|
+
setup: options.routerSetup
|
|
376
|
+
});
|
|
377
|
+
const LayoutWrapper = createLayoutWrapper(options.resolveLayoutComponent, options.defaultLayout || null);
|
|
378
|
+
initCachedViewsFromRoutes(options.routes);
|
|
379
|
+
const RootComponent = createRootComponent(LayoutWrapper, page, transitionOpts, false, resolveComp(options.loadingComponent), resolveComp(options.errorComponent));
|
|
380
|
+
const app = options.hydrate ? createSSRApp(RootComponent) : createApp(RootComponent);
|
|
381
|
+
app.use(head);
|
|
382
|
+
app.use(router);
|
|
383
|
+
app.component("Link", Link);
|
|
384
|
+
app.component("PageView", PageView);
|
|
385
|
+
app.component("SlotView", SlotView);
|
|
386
|
+
app.directive("client", vClient);
|
|
387
|
+
app.config.globalProperties.$ubean = {
|
|
388
|
+
page,
|
|
389
|
+
head,
|
|
390
|
+
router
|
|
391
|
+
};
|
|
392
|
+
return {
|
|
393
|
+
app,
|
|
394
|
+
router,
|
|
395
|
+
head,
|
|
396
|
+
page
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function createUbeanSSRApp(initialPage, options) {
|
|
400
|
+
if (!options.head) throw new Error("[ubean] createUbeanSSRApp requires a head instance from @unhead/vue/server");
|
|
401
|
+
initClientI18n();
|
|
402
|
+
const head = options.head;
|
|
403
|
+
const page = reactive({ ...initialPage });
|
|
404
|
+
const router = createUbeanRouter({
|
|
405
|
+
routes: options.routes,
|
|
406
|
+
ssr: true,
|
|
407
|
+
initialUrl: initialPage.url,
|
|
408
|
+
setup: options.routerSetup
|
|
409
|
+
});
|
|
410
|
+
const LayoutWrapper = createLayoutWrapper(options.resolveLayoutComponent, options.defaultLayout || null);
|
|
411
|
+
initCachedViewsFromRoutes(options.routes);
|
|
412
|
+
const RootComponent = createRootComponent(LayoutWrapper, page, { enabled: false }, true, resolveComp(options.loadingComponent), resolveComp(options.errorComponent));
|
|
413
|
+
const app = createSSRApp(RootComponent);
|
|
414
|
+
app.use(head);
|
|
415
|
+
app.use(router);
|
|
416
|
+
app.component("Link", Link);
|
|
417
|
+
app.component("PageView", PageView);
|
|
418
|
+
app.component("SlotView", SlotView);
|
|
419
|
+
app.directive("client", vClient);
|
|
420
|
+
app.config.globalProperties.$ubean = {
|
|
421
|
+
page,
|
|
422
|
+
head,
|
|
423
|
+
router
|
|
424
|
+
};
|
|
425
|
+
return {
|
|
426
|
+
app,
|
|
427
|
+
router,
|
|
428
|
+
head
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
const Head$1 = Head;
|
|
432
|
+
//#endregion
|
|
433
|
+
export { getI18nConfig$1 as A, setLocale$1 as B, addLocale$1 as C, detectLocale$1 as D, detectBrowserLocale$1 as E, initClientI18n as F, useSwitchLocalePath as G, t$1 as H, localizePath$1 as I, createUbeanRouter as K, mergeLocale$1 as L, getLocaleDir$1 as M, getLocaleName$1 as N, extractLocaleFromPath$1 as O, getRegisteredLocales$1 as P, onLocaleChange$1 as R, definePage as S, defineLocale$1 as T, useI18n$1 as U, switchLocalePath$1 as V, useLocalePath as W, 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, getLocale$1 as j, getDefaultLocale$1 as k, PageView as l, createUbeanSSRApp as m, Head$1 as n, LayoutChainRenderer$1 as o, createUbeanClientApp as p, usePage as q, LAYOUT_CHAIN_KEY as r, Link as s, ERROR_KEY$1 as t, SSR_KEY$1 as u, useUnheadHead as v, clearLocales$1 as w, defineMiddleware as x, useViewTransition as y, setI18nConfig$1 as z };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { App, Component } from "vue";
|
|
2
|
+
import { RouteRecordRaw, Router, useRouter } from "vue-router";
|
|
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
|
+
import { VueHeadClient, useHead as useUnheadHead, useSeoMeta } from "@unhead/vue";
|
|
5
|
+
import { PageObject } from "@ubean/pages";
|
|
6
|
+
import { Input, RouteMeta } from "@ubean/shared";
|
|
7
|
+
//#region src/router.d.ts
|
|
8
|
+
interface CreateUbeanRouterOptions {
|
|
9
|
+
routes: RouteRecordRaw[];
|
|
10
|
+
ssr?: boolean;
|
|
11
|
+
initialUrl?: string;
|
|
12
|
+
base?: string;
|
|
13
|
+
/**
|
|
14
|
+
* 在 router 实例创建后、初始导航(SSR 的 `router.push(initialUrl)`)之前调用,
|
|
15
|
+
* 用于注册 `beforeEach` / `beforeResolve` / `afterEach` 等导航守卫。
|
|
16
|
+
*
|
|
17
|
+
* 守卫注册必须同步完成(守卫本身可返回 Promise)。
|
|
18
|
+
* 不要在此处执行异步操作(如发起 API 请求),因为这会延迟首次导航。
|
|
19
|
+
*
|
|
20
|
+
* 该回调在 Client 和 SSR 环境都会执行。
|
|
21
|
+
*/
|
|
22
|
+
setup?: (router: Router) => void;
|
|
23
|
+
}
|
|
24
|
+
declare function createUbeanRouter(options: CreateUbeanRouterOptions): Router;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/use-page.d.ts
|
|
27
|
+
/**
|
|
28
|
+
* 框架页面上下文(reactive,属性访问即取值)。
|
|
29
|
+
*
|
|
30
|
+
* 泛型 `TParams` 约束 params 形状:`usePage<{ id: string }>()` 后
|
|
31
|
+
* `page.params.id` 即为 `string`。
|
|
32
|
+
*/
|
|
33
|
+
interface UbeanVuePage<TParams extends Record<string, unknown> = Record<string, unknown>, TProps = Record<string, unknown>> {
|
|
34
|
+
url: string;
|
|
35
|
+
params: TParams;
|
|
36
|
+
query: Record<string, unknown>;
|
|
37
|
+
meta: Record<string, unknown>;
|
|
38
|
+
props: Record<string, TProps>;
|
|
39
|
+
component: string;
|
|
40
|
+
errors: Record<string, string> | null;
|
|
41
|
+
}
|
|
42
|
+
declare function usePage<TParams extends Record<string, unknown> = Record<string, unknown>, TProps = Record<string, unknown>>(): UbeanVuePage<TParams, TProps>;
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/page-macro.d.ts
|
|
45
|
+
/**
|
|
46
|
+
* 服务端 handler meta 提取结果(与 `@ubean/scan` 的 `DefineMetaResult`
|
|
47
|
+
* 结构一致)。`@ubean/client` 不依赖聚合层,此处内联保持类型独立。
|
|
48
|
+
*/
|
|
49
|
+
interface DefineMetaResult {
|
|
50
|
+
meta?: Record<string, unknown>;
|
|
51
|
+
requiresAuth?: boolean;
|
|
52
|
+
}
|
|
53
|
+
declare function definePage(_meta: PageMeta): void;
|
|
54
|
+
declare function defineMeta(_meta: Partial<RouteMeta>): DefineMetaResult | void;
|
|
55
|
+
declare function defineMiddleware<_I extends Input>(_handler: any): void;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/app.d.ts
|
|
58
|
+
interface UbeanAppOptions {
|
|
59
|
+
routes: RouteRecordRaw[];
|
|
60
|
+
resolveLayoutComponent: (name: string | false | null | undefined) => Promise<Component | null>;
|
|
61
|
+
defaultLayout?: string | null;
|
|
62
|
+
initialPage?: PageObject;
|
|
63
|
+
head?: VueHeadClient;
|
|
64
|
+
viewTransitions?: boolean | ViewTransitionOptions;
|
|
65
|
+
hydrate?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* 在 router 实例创建后、`app.use(router)` 之前调用,
|
|
68
|
+
* 用于注册 vue-router 的导航守卫(`beforeEach` / `beforeResolve` / `afterEach`)。
|
|
69
|
+
*
|
|
70
|
+
* Client 和 SSR 都会执行。守卫注册必须同步完成(守卫本身可返回 Promise)。
|
|
71
|
+
*/
|
|
72
|
+
routerSetup?: (router: ReturnType<typeof createUbeanRouter>) => void;
|
|
73
|
+
/**
|
|
74
|
+
* 页面加载时的 fallback 组件,在 SPA 导航懒加载页面组件期间显示。
|
|
75
|
+
* 来源:`pages/loading.vue` 自动检测 或 `defineApp({ loadingComponent })` 显式配置。
|
|
76
|
+
* 优先级:`defineApp({ loadingComponent })` > `pages/loading.vue`
|
|
77
|
+
*/
|
|
78
|
+
loadingComponent?: Component | (() => Component);
|
|
79
|
+
/**
|
|
80
|
+
* 渲染错误兜底组件,当页面组件渲染、异步解析或 setup 抛出错误时显示。
|
|
81
|
+
* 来源:`pages/error.vue` 自动检测 或 `defineApp({ errorComponent })` 显式配置。
|
|
82
|
+
* 优先级:`defineApp({ errorComponent })` > `pages/error.vue`
|
|
83
|
+
* 通过 Vue 的 `errorCaptured` 生命周期实现错误边界。
|
|
84
|
+
*/
|
|
85
|
+
errorComponent?: Component | (() => Component);
|
|
86
|
+
}
|
|
87
|
+
interface UbeanAppInstance {
|
|
88
|
+
app: App;
|
|
89
|
+
router: ReturnType<typeof createUbeanRouter>;
|
|
90
|
+
head: VueHeadClient;
|
|
91
|
+
page: PageObject;
|
|
92
|
+
}
|
|
93
|
+
declare function createUbeanClientApp(options: UbeanAppOptions): UbeanAppInstance;
|
|
94
|
+
declare function createUbeanSSRApp(initialPage: PageObject, options: Omit<UbeanAppOptions, 'initialPage'>): {
|
|
95
|
+
app: App<Element>;
|
|
96
|
+
router: import("vue-router")._RouterClassic;
|
|
97
|
+
head: VueHeadClient;
|
|
98
|
+
};
|
|
99
|
+
declare const Head: import("vue").DefineComponent;
|
|
100
|
+
//#endregion
|
|
101
|
+
export { CreateUbeanRouterOptions as A, useUnheadHead as C, definePage as D, defineMiddleware as E, UbeanVuePage as O, useSeoMeta as S, defineMeta as T, VueHeadClient as _, LOCALIZE_PATH_KEY$1 as a, ubeanVue as b, Link as c, SSR_KEY$1 as d, SlotView as f, UbeanVueOptions as g, UbeanAppOptions as h, LOADING_KEY$1 as i, createUbeanRouter as j, usePage as k, PAGE_KEY$1 as l, UbeanAppInstance as m, Head as n, LayoutChainContext as o, TRANSITION_KEY$1 as p, LAYOUT_CHAIN_KEY as r, LayoutChainRenderer$1 as s, ERROR_KEY$1 as t, PageView as u, createUbeanClientApp as v, useViewTransition as w, useRouter as x, createUbeanSSRApp as y };
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +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-BknjDM9j.js";
|
|
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
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { K 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, q as usePage, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useUnheadHead, x as defineMiddleware, y as useViewTransition } from "./app-Bc1d5Svw.js";
|
|
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 };
|