valaxy 0.26.13 → 0.28.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/app/data.ts +2 -2
- package/client/components/AppLink.vue +4 -0
- package/client/components/ClientOnly.ts +12 -0
- package/client/components/ValaxyDynamicComponent.vue +22 -14
- package/client/composables/common.ts +6 -5
- package/client/composables/dark.ts +18 -11
- package/client/composables/features/copy-markdown.ts +85 -0
- package/client/composables/features/index.ts +1 -0
- package/client/composables/post/index.ts +35 -22
- package/client/config.ts +4 -2
- package/client/entry-ssr.ts +25 -0
- package/client/index.d.ts +2 -8
- package/client/locales/en.yml +8 -0
- package/client/locales/zh-CN.yml +8 -0
- package/client/main.ts +82 -22
- package/client/modules/components.ts +2 -2
- package/client/modules/floating-vue.ts +2 -3
- package/client/modules/valaxy.ts +2 -3
- package/client/setup/main.ts +2 -3
- package/client/setups.ts +24 -3
- package/client/stores/site.ts +11 -6
- package/client/styles/common/view-transition.css +4 -9
- package/client/styles/css-vars.scss +7 -6
- package/client/tsconfig.json +0 -1
- package/client/types/index.ts +2 -2
- package/dist/node/cli/index.mjs +15 -10
- package/dist/node/index.d.mts +155 -10
- package/dist/node/index.mjs +15 -10
- package/dist/shared/{valaxy.CEmGx65r.mjs → valaxy.CKsfoRMA.mjs} +3265 -1908
- package/dist/shared/{valaxy.gqLhCu14.d.mts → valaxy.Dgja0_Y0.d.mts} +165 -58
- package/dist/types/index.d.mts +4 -7
- package/package.json +27 -28
- package/shared/node/i18n.ts +15 -1
- package/types/config.ts +74 -0
- package/types/frontmatter/page.ts +6 -1
- package/types/index.ts +3 -0
- package/types/vue-router.d.ts +29 -0
- package/client/templates/loader.vue +0 -10
package/client/app/data.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import type { Router } from 'vue-router'
|
|
3
|
-
import type { PageData } from '../../types'
|
|
3
|
+
import type { PageData, Post } from '../../types'
|
|
4
4
|
import { computed } from 'vue'
|
|
5
5
|
|
|
6
6
|
export interface ValaxyData<FM = PageData['frontmatter']> {
|
|
@@ -16,6 +16,6 @@ export function initData(router: Router): ValaxyData {
|
|
|
16
16
|
page: computed(() => (router.currentRoute.value as unknown as {
|
|
17
17
|
data: PageData
|
|
18
18
|
}).data),
|
|
19
|
-
frontmatter: computed(() => router.currentRoute.value.meta.frontmatter)
|
|
19
|
+
frontmatter: computed(() => router.currentRoute.value.meta.frontmatter!) as Ref<Post>,
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineComponent, onMounted, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export default defineComponent({
|
|
4
|
+
name: 'ClientOnly',
|
|
5
|
+
setup(_, { slots }) {
|
|
6
|
+
const show = ref(false)
|
|
7
|
+
onMounted(() => {
|
|
8
|
+
show.value = true
|
|
9
|
+
})
|
|
10
|
+
return () => show.value ? slots.default?.() : null
|
|
11
|
+
},
|
|
12
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Component } from 'vue'
|
|
3
|
-
import { compile,
|
|
3
|
+
import { compile, computed, defineComponent } from 'vue'
|
|
4
4
|
|
|
5
5
|
const props = withDefaults(
|
|
6
6
|
defineProps<{
|
|
@@ -12,22 +12,30 @@ const props = withDefaults(
|
|
|
12
12
|
},
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Separate compile from component creation — compile() is expensive and
|
|
16
|
+
// should only re-run when the template string itself changes.
|
|
17
|
+
const compiledRender = computed(() => {
|
|
18
|
+
if (!props.templateStr)
|
|
19
|
+
return null
|
|
20
|
+
return compile(props.templateStr)
|
|
21
|
+
})
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
// defineComponent creates a proper component instance so that compile()'s
|
|
24
|
+
// render function can access data properties through the instance context.
|
|
25
|
+
// Avoids defineAsyncComponent which causes "missing template" warnings in SSG.
|
|
26
|
+
const dynamicComponent = computed<Component | null>(() => {
|
|
27
|
+
const render = compiledRender.value
|
|
28
|
+
if (!render)
|
|
29
|
+
return null
|
|
30
|
+
return defineComponent({
|
|
31
|
+
data: () => ({ ...props.data }),
|
|
20
32
|
render,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
dynamicComponent.value = defineAsyncComponent(() => Promise.resolve(componentDefinition))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
watch(() => [props.templateStr, props.data], () => {
|
|
27
|
-
createComponent()
|
|
28
|
-
}, { immediate: true })
|
|
33
|
+
})
|
|
34
|
+
})
|
|
29
35
|
</script>
|
|
30
36
|
|
|
31
37
|
<template>
|
|
32
|
-
<
|
|
38
|
+
<div v-if="dynamicComponent">
|
|
39
|
+
<component :is="dynamicComponent" />
|
|
40
|
+
</div>
|
|
33
41
|
</template>
|
|
@@ -2,7 +2,7 @@ import type { PostFrontMatter } from '../../types'
|
|
|
2
2
|
import type { ValaxyData } from '../app/data'
|
|
3
3
|
|
|
4
4
|
import { isClient } from '@vueuse/core'
|
|
5
|
-
import { computed, inject } from 'vue'
|
|
5
|
+
import { computed, hasInjectionContext, inject } from 'vue'
|
|
6
6
|
import { useRoute } from 'vue-router'
|
|
7
7
|
import { dataSymbol, useSiteConfig } from '../config'
|
|
8
8
|
|
|
@@ -73,10 +73,11 @@ export function useEncryptedPhotos() {
|
|
|
73
73
|
* inject pageData
|
|
74
74
|
*/
|
|
75
75
|
export function useData<FM = Record<string, any>>(): ValaxyData<FM> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
if (!hasInjectionContext())
|
|
77
|
+
throw new Error('[Valaxy] useData() must be called inside setup() or a component lifecycle')
|
|
78
|
+
const data = inject<ValaxyData<FM>>(dataSymbol)
|
|
79
|
+
if (!data)
|
|
80
|
+
throw new Error('[Valaxy] data not properly injected in app')
|
|
80
81
|
return data
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { UseDarkOptions } from '@vueuse/core'
|
|
2
2
|
import { useDark, useToggle } from '@vueuse/core'
|
|
3
|
-
import { computed } from 'vue'
|
|
3
|
+
import { computed, nextTick } from 'vue'
|
|
4
4
|
|
|
5
5
|
export function useValaxyDark(options: {
|
|
6
6
|
/**
|
|
@@ -31,8 +31,12 @@ export function useValaxyDark(options: {
|
|
|
31
31
|
if (options.circleTransition)
|
|
32
32
|
import('valaxy/client/styles/common/view-transition.css')
|
|
33
33
|
|
|
34
|
+
const enableTransitions = () =>
|
|
35
|
+
'startViewTransition' in document
|
|
36
|
+
&& window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
|
37
|
+
|
|
34
38
|
function toggleDarkWithTransition(event: MouseEvent, options: { duration?: number, easing?: EffectTiming['easing'] } = {}) {
|
|
35
|
-
if (!
|
|
39
|
+
if (!enableTransitions()) {
|
|
36
40
|
toggleDark()
|
|
37
41
|
return
|
|
38
42
|
}
|
|
@@ -44,23 +48,26 @@ export function useValaxyDark(options: {
|
|
|
44
48
|
Math.max(y, innerHeight - y),
|
|
45
49
|
)
|
|
46
50
|
|
|
47
|
-
const
|
|
51
|
+
const clipPath = [
|
|
52
|
+
`circle(0px at ${x}px ${y}px)`,
|
|
53
|
+
`circle(${endRadius}px at ${x}px ${y}px)`,
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const transition = document.startViewTransition(async () => {
|
|
48
57
|
toggleDark()
|
|
58
|
+
await nextTick()
|
|
49
59
|
})
|
|
50
60
|
|
|
51
61
|
transition.ready.then(() => {
|
|
52
|
-
const clipPath = [
|
|
53
|
-
`circle(0px at ${x}px ${y}px)`,
|
|
54
|
-
`circle(${endRadius}px at ${x}px ${y}px)`,
|
|
55
|
-
]
|
|
56
62
|
document.documentElement.animate(
|
|
57
63
|
{
|
|
58
|
-
clipPath: isDark.value ? clipPath.reverse() : clipPath,
|
|
64
|
+
clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
|
-
duration: options.duration
|
|
62
|
-
easing: options.easing
|
|
63
|
-
|
|
67
|
+
duration: options.duration ?? 200,
|
|
68
|
+
easing: options.easing ?? 'ease-in',
|
|
69
|
+
fill: 'forwards',
|
|
70
|
+
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`,
|
|
64
71
|
},
|
|
65
72
|
)
|
|
66
73
|
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isClient, useClipboard } from '@vueuse/core'
|
|
2
|
+
import { computed, ref, watchEffect } from 'vue'
|
|
3
|
+
import { useRoute } from 'vue-router'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Composable for copying raw Markdown content of the current post.
|
|
7
|
+
* Requires `siteConfig.llms.files: true` to have .md files available at build output.
|
|
8
|
+
*
|
|
9
|
+
* The `available` ref is initially `false` and becomes `true` after a HEAD request
|
|
10
|
+
* confirms the `.md` file exists. This allows themes to conditionally render
|
|
11
|
+
* the copy button only when the llms feature is enabled.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```vue
|
|
15
|
+
* <script setup>
|
|
16
|
+
* import { useCopyMarkdown } from 'valaxy'
|
|
17
|
+
* const { copy, copied, loading, available, error } = useCopyMarkdown()
|
|
18
|
+
* </script>
|
|
19
|
+
* <template>
|
|
20
|
+
* <button v-if="available" @click="copy" :disabled="loading">
|
|
21
|
+
* {{ copied ? 'Copied!' : 'Copy Markdown' }}
|
|
22
|
+
* </button>
|
|
23
|
+
* <span v-if="error" class="text-red">{{ error }}</span>
|
|
24
|
+
* </template>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useCopyMarkdown() {
|
|
28
|
+
const route = useRoute()
|
|
29
|
+
const copied = ref(false)
|
|
30
|
+
const loading = ref(false)
|
|
31
|
+
const available = ref(false)
|
|
32
|
+
const error = ref<string | null>(null)
|
|
33
|
+
const { copy: copyToClipboard } = useClipboard({ legacy: true })
|
|
34
|
+
|
|
35
|
+
const mdUrl = computed(() => {
|
|
36
|
+
const p = route.path !== '/' && route.path.endsWith('/')
|
|
37
|
+
? route.path.slice(0, -1)
|
|
38
|
+
: route.path
|
|
39
|
+
return `${p}.md`
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Probe the .md file to detect availability (siteConfig.llms.files enabled at build time)
|
|
43
|
+
if (isClient) {
|
|
44
|
+
watchEffect(() => {
|
|
45
|
+
available.value = false
|
|
46
|
+
error.value = null
|
|
47
|
+
fetch(mdUrl.value, { method: 'HEAD' })
|
|
48
|
+
.then((res) => { available.value = res.ok })
|
|
49
|
+
.catch(() => { available.value = false })
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function copy() {
|
|
54
|
+
if (loading.value)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
error.value = null
|
|
58
|
+
loading.value = true
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(mdUrl.value)
|
|
61
|
+
if (!res.ok)
|
|
62
|
+
throw new Error(`Failed to fetch ${mdUrl.value}: ${res.status}`)
|
|
63
|
+
|
|
64
|
+
const text = await res.text()
|
|
65
|
+
await copyToClipboard(text)
|
|
66
|
+
copied.value = true
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
copied.value = false
|
|
69
|
+
}, 2000)
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : 'Unknown error'
|
|
73
|
+
error.value = msg
|
|
74
|
+
console.error('[valaxy] Failed to copy markdown:', err)
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
error.value = null
|
|
77
|
+
}, 3000)
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
loading.value = false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { copy, copied, loading, mdUrl, available, error }
|
|
85
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Post } from 'valaxy'
|
|
1
|
+
import type { Post, SiteConfig } from 'valaxy'
|
|
2
2
|
import type { ComputedRef } from 'vue'
|
|
3
3
|
import { orderByMeta, useSiteConfig } from 'valaxy'
|
|
4
4
|
import { computed } from 'vue'
|
|
@@ -37,6 +37,39 @@ export function usePageList() {
|
|
|
37
37
|
})
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Pure function to filter and sort posts from page list
|
|
42
|
+
* Can be used in both composables and stores without inject() issues
|
|
43
|
+
*/
|
|
44
|
+
export function filterAndSortPosts(
|
|
45
|
+
pages: Post[],
|
|
46
|
+
siteConfig: SiteConfig,
|
|
47
|
+
params: { type?: string } = {},
|
|
48
|
+
): Post[] {
|
|
49
|
+
// Filter posts
|
|
50
|
+
const routes = pages
|
|
51
|
+
.filter(i =>
|
|
52
|
+
i.path?.startsWith('/posts')
|
|
53
|
+
&& !i.path?.endsWith('.html')
|
|
54
|
+
&& i.date
|
|
55
|
+
&& (!params.type || i.type === params.type)
|
|
56
|
+
&& (!i.hide || i.hide === 'index'), // hide `hide: all` posts
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
function sortBySiteConfigOrderBy(posts: Post[]) {
|
|
60
|
+
const orderBy = siteConfig.orderBy
|
|
61
|
+
return orderByMeta(posts, orderBy)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 置顶
|
|
66
|
+
*/
|
|
67
|
+
const topPosts = sortBySiteConfigOrderBy(routes.filter(i => i.top)).sort((a, b) => b.top! - a.top!)
|
|
68
|
+
const otherPosts = sortBySiteConfigOrderBy(routes.filter(i => !i.top))
|
|
69
|
+
|
|
70
|
+
return topPosts.concat(otherPosts)
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
/**
|
|
41
74
|
* get post list in 'pages/posts' folder
|
|
42
75
|
* todo: use vue provide/inject to global
|
|
@@ -47,26 +80,6 @@ export function usePostList(params: {
|
|
|
47
80
|
const siteConfig = useSiteConfig()
|
|
48
81
|
const pageList = usePageList()
|
|
49
82
|
return computed(() => {
|
|
50
|
-
|
|
51
|
-
.filter(i =>
|
|
52
|
-
i.path?.startsWith('/posts')
|
|
53
|
-
&& !i.path?.endsWith('.html')
|
|
54
|
-
&& i.date
|
|
55
|
-
&& (!params.type || i.type === params.type)
|
|
56
|
-
&& (!i.hide || i.hide === 'index'), // hide `hide: all` posts
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
function sortBySiteConfigOrderBy(posts: Post[]) {
|
|
60
|
-
const orderBy = siteConfig.value.orderBy
|
|
61
|
-
return orderByMeta(posts, orderBy)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 置顶
|
|
66
|
-
*/
|
|
67
|
-
const topPosts = sortBySiteConfigOrderBy(routes.filter(i => i.top)).sort((a, b) => b.top! - a.top!)
|
|
68
|
-
const otherPosts = sortBySiteConfigOrderBy(routes.filter(i => !i.top))
|
|
69
|
-
|
|
70
|
-
return topPosts.concat(otherPosts)
|
|
83
|
+
return filterAndSortPosts(pageList.value, siteConfig.value, params)
|
|
71
84
|
})
|
|
72
85
|
}
|
package/client/config.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { ComputedRef, InjectionKey } from 'vue'
|
|
|
6
6
|
// https://github.com/microsoft/TypeScript/issues/42873
|
|
7
7
|
import type { DefaultTheme, ValaxyConfig } from '../types'
|
|
8
8
|
import type { ValaxyData } from './app/data'
|
|
9
|
-
import { computed, inject, readonly, shallowRef } from 'vue'
|
|
9
|
+
import { computed, hasInjectionContext, inject, readonly, shallowRef } from 'vue'
|
|
10
10
|
|
|
11
11
|
// @ts-expect-error virtual module @valaxyjs/config
|
|
12
12
|
import valaxyConfig from '/@valaxyjs/config'
|
|
@@ -62,10 +62,12 @@ export function initContext() {
|
|
|
62
62
|
* @public
|
|
63
63
|
*/
|
|
64
64
|
export function useValaxyConfig<ThemeConfig = DefaultTheme.Config>() {
|
|
65
|
+
if (!hasInjectionContext())
|
|
66
|
+
throw new Error('[Valaxy] useValaxyConfig() must be called inside setup() or a component lifecycle')
|
|
65
67
|
const config = inject<ComputedRef<ValaxyConfig<ThemeConfig>>>(valaxyConfigSymbol)
|
|
66
68
|
if (!config)
|
|
67
69
|
throw new Error('[Valaxy] site config not properly injected in app')
|
|
68
|
-
return config
|
|
70
|
+
return config
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHead } from '@unhead/vue/server'
|
|
2
|
+
import { renderToString } from 'vue/server-renderer'
|
|
3
|
+
import { createValaxyApp, routesWithLayout } from './main'
|
|
4
|
+
|
|
5
|
+
export async function render(routePath: string) {
|
|
6
|
+
const ctx = createValaxyApp({ routePath, createHead })
|
|
7
|
+
const { app, router, head } = ctx
|
|
8
|
+
|
|
9
|
+
await router.push(routePath)
|
|
10
|
+
await router.isReady()
|
|
11
|
+
|
|
12
|
+
const ssrCtx: Record<string, any> = {}
|
|
13
|
+
const html = await renderToString(app, ssrCtx)
|
|
14
|
+
await ctx.triggerOnSSRAppRendered()
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
html,
|
|
18
|
+
head,
|
|
19
|
+
modules: ssrCtx.modules as Set<string> | undefined,
|
|
20
|
+
teleports: ssrCtx.teleports as Record<string, string> | undefined,
|
|
21
|
+
initialState: ctx.initialState,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { routesWithLayout as routes }
|
package/client/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Header } from '@valaxyjs/utils'
|
|
2
1
|
import type { Ref } from 'vue'
|
|
3
2
|
import type { Post } from '../types'
|
|
4
3
|
|
|
5
4
|
import './shims.d'
|
|
5
|
+
// Import vue-router RouteMeta augmentation
|
|
6
|
+
import '../types/vue-router.d'
|
|
6
7
|
|
|
7
8
|
export * from '../dist/types/index.mjs'
|
|
8
9
|
export * from './index'
|
|
@@ -12,13 +13,6 @@ declare module '@docsearch/js' {
|
|
|
12
13
|
export default docsearch
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
declare module 'vue-router' {
|
|
16
|
-
interface RouteMeta {
|
|
17
|
-
headers: Header[]
|
|
18
|
-
frontmatter: Post
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
declare interface Window {
|
|
23
17
|
// for devtools
|
|
24
18
|
__VUE_DEVTOOLS_ROUTER__: import('vue-router').Router
|
package/client/locales/en.yml
CHANGED
|
@@ -50,6 +50,14 @@ post:
|
|
|
50
50
|
related_posts: Related posts
|
|
51
51
|
view_link: View link
|
|
52
52
|
read_more: READ MORE
|
|
53
|
+
copy_markdown: Copy Markdown
|
|
54
|
+
copy_markdown_link: Copy Markdown Link
|
|
55
|
+
copy_page: Copy page
|
|
56
|
+
copied_markdown: Copied!
|
|
57
|
+
view_as_markdown: View as Markdown
|
|
58
|
+
open_in_github: Open in GitHub
|
|
59
|
+
open_in_chatgpt: Open in ChatGPT
|
|
60
|
+
open_in_claude: Open in Claude
|
|
53
61
|
cover: Cover
|
|
54
62
|
time_warning: This article was last updated {ago}. The information described in this article may have changed.
|
|
55
63
|
copyright:
|
package/client/locales/zh-CN.yml
CHANGED
|
@@ -50,6 +50,14 @@ post:
|
|
|
50
50
|
related_posts: 相关文章
|
|
51
51
|
view_link: 查看链接
|
|
52
52
|
read_more: 阅读更多
|
|
53
|
+
copy_markdown: 复制 Markdown
|
|
54
|
+
copy_markdown_link: 复制 Markdown 链接
|
|
55
|
+
copy_page: 复制页面
|
|
56
|
+
copied_markdown: 已复制!
|
|
57
|
+
view_as_markdown: 查看 Markdown
|
|
58
|
+
open_in_github: 在 GitHub 中打开
|
|
59
|
+
open_in_chatgpt: 在 ChatGPT 中打开
|
|
60
|
+
open_in_claude: 在 Claude 中打开
|
|
53
61
|
cover: 封面
|
|
54
62
|
time_warning: '本文最后更新于 {ago},文中所描述的信息可能已发生改变。'
|
|
55
63
|
copyright:
|
package/client/main.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ValaxySSGContext } from './setups'
|
|
2
|
+
|
|
2
3
|
import { dataSymbol, initValaxyConfig, valaxyConfigSymbol } from 'valaxy'
|
|
3
4
|
import { setupLayouts } from 'virtual:generated-layouts'
|
|
4
|
-
import {
|
|
5
|
-
|
|
5
|
+
import { createSSRApp, createApp as vueCreateApp } from 'vue'
|
|
6
|
+
import { createMemoryHistory, createRouter, createWebHistory } from 'vue-router'
|
|
6
7
|
import { routes as autoRoutes } from 'vue-router/auto-routes'
|
|
8
|
+
|
|
7
9
|
// import App from '/@valaxyjs/App.vue'
|
|
8
10
|
import App from './App.vue'
|
|
9
|
-
|
|
10
11
|
import { initData } from './app/data'
|
|
12
|
+
import ClientOnly from './components/ClientOnly'
|
|
11
13
|
|
|
12
14
|
import setupMain from './setup/main'
|
|
13
15
|
import { setupValaxyDevTools } from './utils/dev'
|
|
@@ -44,7 +46,7 @@ function filterDraft(routes: any[]) {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// not filter hide for ssg
|
|
47
|
-
const routesWithLayout = setupLayouts(import.meta.env.DEV
|
|
49
|
+
export const routesWithLayout = setupLayouts(import.meta.env.DEV
|
|
48
50
|
? routes
|
|
49
51
|
: filterDraft(routes),
|
|
50
52
|
)
|
|
@@ -52,29 +54,87 @@ const routesWithLayout = setupLayouts(import.meta.env.DEV
|
|
|
52
54
|
if (import.meta.env.DEV)
|
|
53
55
|
setupValaxyDevTools()
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
interface CreateValaxyAppOptions {
|
|
58
|
+
routePath?: string
|
|
59
|
+
createHead?: () => any
|
|
60
|
+
hydrate?: boolean
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createValaxyApp(options: CreateValaxyAppOptions = {}): ValaxySSGContext {
|
|
64
|
+
const { routePath, createHead, hydrate } = options
|
|
65
|
+
const isSSR = import.meta.env.SSR
|
|
66
|
+
|
|
67
|
+
// Use createSSRApp for server-side rendering and for client-side hydration
|
|
68
|
+
// of SSG pre-rendered pages. vueCreateApp is only for pure SPA mode.
|
|
69
|
+
const app = (isSSR || hydrate) ? createSSRApp(App) : vueCreateApp(App)
|
|
70
|
+
|
|
71
|
+
const history = isSSR
|
|
72
|
+
? createMemoryHistory(import.meta.env.BASE_URL)
|
|
73
|
+
: createWebHistory(import.meta.env.BASE_URL)
|
|
74
|
+
|
|
75
|
+
const router = createRouter({
|
|
76
|
+
history,
|
|
59
77
|
routes: routesWithLayout,
|
|
60
|
-
base: import.meta.env.BASE_URL,
|
|
61
78
|
scrollBehavior(to, from) {
|
|
62
79
|
if (to.path !== from.path)
|
|
63
80
|
return { top: 0 }
|
|
64
81
|
},
|
|
65
|
-
}
|
|
66
|
-
(ctx) => {
|
|
67
|
-
// app-level provide
|
|
68
|
-
const { app, router } = ctx
|
|
82
|
+
})
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
app.provide(dataSymbol, data)
|
|
84
|
+
const head = createHead ? createHead() : undefined
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
app.use(router)
|
|
87
|
+
if (head)
|
|
88
|
+
app.use(head)
|
|
89
|
+
app.component('ClientOnly', ClientOnly)
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
// SSR render callback management
|
|
92
|
+
const appRenderCallbacks: (() => void)[] = []
|
|
77
93
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
const ctx: ValaxySSGContext = {
|
|
95
|
+
app,
|
|
96
|
+
router,
|
|
97
|
+
head,
|
|
98
|
+
routes: routesWithLayout,
|
|
99
|
+
isClient: !isSSR,
|
|
100
|
+
initialState: {},
|
|
101
|
+
onSSRAppRendered: isSSR ? (cb: () => void) => appRenderCallbacks.push(cb) : () => {},
|
|
102
|
+
triggerOnSSRAppRendered: () => Promise.all(appRenderCallbacks.map(cb => cb())),
|
|
103
|
+
routePath,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Restore serialised state on client
|
|
107
|
+
if (!isSSR && typeof window !== 'undefined' && (window as any).__INITIAL_STATE__) {
|
|
108
|
+
ctx.initialState = JSON.parse((window as any).__INITIAL_STATE__)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// app-level provide
|
|
112
|
+
const data = initData(router)
|
|
113
|
+
app.provide(dataSymbol, data)
|
|
114
|
+
app.provide(valaxyConfigSymbol, valaxyConfig)
|
|
115
|
+
|
|
116
|
+
setupMain(ctx, valaxyConfig)
|
|
117
|
+
|
|
118
|
+
return ctx
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Client-side auto-mount
|
|
122
|
+
if (!import.meta.env.SSR) {
|
|
123
|
+
;(async () => {
|
|
124
|
+
const { createHead } = await import('@unhead/vue/client')
|
|
125
|
+
// Detect SSG pre-rendered content for proper hydration
|
|
126
|
+
const appEl = document.getElementById('app')
|
|
127
|
+
const hydrate = !!(appEl && appEl.innerHTML.trim())
|
|
128
|
+
const { app, router } = createValaxyApp({ createHead, hydrate })
|
|
129
|
+
await router.isReady()
|
|
130
|
+
app.mount('#app', hydrate)
|
|
131
|
+
})()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Legacy compatibility export for vite-ssg.
|
|
136
|
+
* vite-ssg expects `createApp(routePath)` returning `{ app, router, ... }`.
|
|
137
|
+
* This wraps `createValaxyApp` to match that signature so
|
|
138
|
+
* `--ssg-engine vite-ssg` continues to work.
|
|
139
|
+
*/
|
|
140
|
+
export const createApp = (routePath?: string) => createValaxyApp({ routePath })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ValaxySSGContext } from '../setups'
|
|
2
2
|
|
|
3
3
|
import AppLink from '../components/AppLink.vue'
|
|
4
4
|
import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
|
|
@@ -7,7 +7,7 @@ import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
|
|
|
7
7
|
* register global components
|
|
8
8
|
* @param ctx
|
|
9
9
|
*/
|
|
10
|
-
export function registerGlobalComponents(ctx:
|
|
10
|
+
export function registerGlobalComponents(ctx: ValaxySSGContext) {
|
|
11
11
|
ctx.app.component('AppLink', AppLink)
|
|
12
12
|
ctx.app.component('VT', ValaxyTranslate)
|
|
13
13
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
|
|
2
|
-
import type { ViteSSGContext } from 'vite-ssg'
|
|
3
|
-
|
|
4
2
|
import type { ComputedRef } from 'vue'
|
|
3
|
+
import type { ValaxySSGContext } from '../setups'
|
|
5
4
|
import FloatingVue from 'floating-vue'
|
|
6
5
|
import 'floating-vue/dist/style.css'
|
|
7
6
|
|
|
8
|
-
export async function install({ app }:
|
|
7
|
+
export async function install({ app }: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
|
|
9
8
|
// @see https://floating-vue.starpad.dev/guide/config#default-values
|
|
10
9
|
const defaultFloatingVueConfig = {}
|
|
11
10
|
app.use(FloatingVue, Object.assign(defaultFloatingVueConfig, config.value.siteConfig.floatingVue || {}))
|
package/client/modules/valaxy.ts
CHANGED
|
@@ -8,11 +8,10 @@ import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
|
|
|
8
8
|
*/
|
|
9
9
|
// import messages from '@intlify/unplugin-vue-i18n/messages'
|
|
10
10
|
|
|
11
|
-
import type { ViteSSGContext } from 'vite-ssg'
|
|
12
|
-
|
|
13
11
|
import type { ComputedRef } from 'vue'
|
|
14
12
|
import type { Router } from 'vue-router'
|
|
15
13
|
import type { PageDataPayload } from '../../types'
|
|
14
|
+
import type { ValaxySSGContext } from '../setups'
|
|
16
15
|
import { ensureSuffix } from '@antfu/utils'
|
|
17
16
|
import { useStorage } from '@vueuse/core'
|
|
18
17
|
import { createI18n } from 'vue-i18n'
|
|
@@ -52,7 +51,7 @@ export const i18n = createI18n({
|
|
|
52
51
|
missingWarn: false,
|
|
53
52
|
})
|
|
54
53
|
|
|
55
|
-
export async function install({ app, router }:
|
|
54
|
+
export async function install({ app, router }: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
|
|
56
55
|
const locale = useStorage('valaxy-locale', config?.value.siteConfig.lang || 'en')
|
|
57
56
|
i18n.global.locale.value = locale.value
|
|
58
57
|
|
package/client/setup/main.ts
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
// https://github.com/microsoft/TypeScript/issues/42873
|
|
5
5
|
import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
|
|
6
6
|
/* __imports__ */
|
|
7
|
-
import type { ViteSSGContext } from 'vite-ssg'
|
|
8
|
-
|
|
9
7
|
import type { ComputedRef } from 'vue'
|
|
8
|
+
import type { ValaxySSGContext } from '../setups'
|
|
10
9
|
|
|
11
10
|
import { consola } from 'consola'
|
|
12
11
|
import { registerGlobalComponents } from '../modules/components'
|
|
@@ -16,7 +15,7 @@ import { install as installPinia } from '../modules/pinia'
|
|
|
16
15
|
import { install as installUnhead } from '../modules/unhead'
|
|
17
16
|
import { install as installValaxy } from '../modules/valaxy'
|
|
18
17
|
|
|
19
|
-
export default function setupMain(ctx:
|
|
18
|
+
export default function setupMain(ctx: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
|
|
20
19
|
// @ts-expect-error inject in runtime
|
|
21
20
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
22
21
|
const injection_arg = ctx
|