forked from nuxt/framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
composables.ts
123 lines (103 loc) · 3.84 KB
/
composables.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { getCurrentInstance, onBeforeUnmount, isRef, watch, reactive, toRef, isReactive, Ref, set } from '@vue/composition-api'
import type { CombinedVueInstance } from 'vue/types/vue'
import type { MetaInfo } from 'vue-meta'
import type VueRouter from 'vue-router'
import type { Route } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/schema'
import { useNuxtApp } from './app'
export { useLazyAsyncData } from './asyncData'
export { useLazyFetch } from './fetch'
export { useCookie } from './cookie'
export * from '@vue/composition-api'
const mock = () => () => { throw new Error('not implemented') }
export const useAsyncData = mock()
export const useFetch = mock()
export const useHydration = mock()
// Runtime config helper
export const useRuntimeConfig = () => {
const nuxtApp = useNuxtApp()
if (!nuxtApp.$config) {
nuxtApp.$config = reactive(nuxtApp.nuxt2Context.app.$config)
}
return nuxtApp.$config as RuntimeConfig
}
// Auto-import equivalents for `vue-router`
export const useRouter = () => {
return useNuxtApp()?.nuxt2Context.app.router as VueRouter
}
// This provides an equivalent interface to `vue-router` (unlike legacy implementation)
export const useRoute = () => {
const nuxtApp = useNuxtApp()
if (!nuxtApp._route) {
Object.defineProperty(nuxtApp, '__route', {
get: () => nuxtApp.nuxt2Context.app.context.route
})
nuxtApp._route = reactive(nuxtApp.__route)
const router = useRouter()
router.afterEach(route => Object.assign(nuxtApp._route, route))
}
return nuxtApp._route as Route
}
// payload.state is used for vuex by nuxt 2
export const useState = <T>(key: string, init?: (() => T)): Ref<T> => {
const nuxtApp = useNuxtApp()
if (!nuxtApp.payload.useState) {
nuxtApp.payload.useState = {}
}
if (!isReactive(nuxtApp.payload.useState)) {
nuxtApp.payload.useState = reactive(nuxtApp.payload.useState)
}
// see @vuejs/composition-api reactivity tracking on a reactive object with set
if (!(key in nuxtApp.payload.useState)) {
set(nuxtApp.payload.useState, key, undefined)
}
const state = toRef(nuxtApp.payload.useState, key)
if (state.value === undefined && init) {
state.value = init()
}
return state
}
type Reffed<T extends Record<string, any>> = {
[P in keyof T]: T[P] extends Array<infer A> ? Ref<Array<Reffed<A>>> | Array<Reffed<A>> : T[P] extends Record<string, any> ? Reffed<T[P]> | Ref<Reffed<T[P]>> : T[P] | Ref<T[P]>
}
function unwrap (value: any): Record<string, any> {
if (!value || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number') { return value }
if (Array.isArray(value)) { return value.map(i => unwrap(i)) }
if (isRef(value)) { return unwrap(value.value) }
if (typeof value === 'object') {
return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, unwrap(value)]))
}
return value
}
type AugmentedComponent = CombinedVueInstance<Vue, object, object, object, Record<never, any>> & {
_vueMeta?: boolean
$metaInfo?: MetaInfo
}
export const useNuxt2Meta = (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaInfo>)) => {
const vm = getCurrentInstance()!.proxy as AugmentedComponent
const meta = vm.$meta()
const $root = vm.$root
if (!vm._vueMeta) {
vm._vueMeta = true
let parent = vm.$parent as AugmentedComponent
while (parent && parent !== $root) {
if (parent._vueMeta === undefined) {
parent._vueMeta = false
}
parent = parent.$parent
}
}
// @ts-ignore
vm.$options.head = vm.$options.head || {}
const metaSource = metaOptions instanceof Function ? metaOptions : () => metaOptions
const unwatch = watch(metaSource, (metaInfo: MetaInfo) => {
vm.$metaInfo = {
...vm.$metaInfo || {},
...unwrap(metaInfo)
}
if (process.client) {
meta.refresh()
}
}, { immediate: true, deep: true })
onBeforeUnmount(unwatch)
}