Skip to content

Commit

Permalink
refactor: relocate group of functions for clarity (#965)
Browse files Browse the repository at this point in the history
* refactor: relocate group of functions to nuxt.ts for clarity

* refactor: relocate group of functions to composabels dir for clarity

* refactor: relocate group of functions to composabels dir for clarity

* refactor: relocate group of functions to composabels dir for clarity

* refactor: relocate group of functions to nuxt.ts for clarity

* refactor: relocate group of functions to composabels dir for clarity

* refactor: relocate group of functions to composabels dir for clarity

* refactor: relocate group of functions to composabels dir for clarity
  • Loading branch information
wattanx authored Nov 13, 2023
1 parent d4f4f48 commit 83e0df6
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 230 deletions.
30 changes: 21 additions & 9 deletions packages/bridge/src/imports/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,44 @@ export const commonPresets: InlinePreset[] = [

const granularAppPresets: InlinePreset[] = [
{
imports: ['defineNuxtComponent', 'setNuxtAppInstance', 'useNuxtApp', 'defineNuxtPlugin'],
from: '#app/app'
imports: ['setNuxtAppInstance', 'useNuxtApp', 'defineNuxtPlugin', 'useRuntimeConfig', 'useNuxt2Meta'],
from: '#app/nuxt'
},
{
imports: ['useRuntimeConfig', 'useNuxt2Meta', 'useRoute', 'useRouter', 'useState', 'abortNavigation', 'addRouteMiddleware', 'defineNuxtRouteMiddleware', 'navigateTo'],
from: '#app/composables'
imports: ['defineNuxtComponent'],
from: '#app/composables/component'
},
{
imports: ['useRoute', 'useRouter', 'abortNavigation', 'addRouteMiddleware', 'defineNuxtRouteMiddleware', 'navigateTo'],
from: '#app/composables/router'
},
{
imports: ['useState'],
from: '#app/composables/state'
},
{
imports: ['useLazyAsyncData', 'refreshNuxtData'],
from: '#app/asyncData'
from: '#app/composables/asyncData'
},
{
imports: ['clearError', 'createError', 'isNuxtError', 'showError', 'useError', 'throwError'],
from: '#app/error'
from: '#app/composables/error'
},
{
imports: ['useLazyFetch'],
from: '#app/fetch'
from: '#app/composables/fetch'
},
{
imports: ['useCookie'],
from: '#app/cookie'
from: '#app/composables/cookie'
},
{
imports: ['useRequestHeaders', 'useRequestEvent'],
from: '#app/ssr'
from: '#app/composables/ssr'
},
{
imports: ['useAsyncData', 'useFetch', 'useHydration'],
from: '#app/mocks'
}
]

Expand Down
2 changes: 1 addition & 1 deletion packages/bridge/src/runtime/app.plugin.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue, { version } from 'vue'
import { createHooks } from 'hookable'
import { setNuxtAppInstance } from '#app/app'
import { setNuxtAppInstance } from '#app/nuxt'
import { globalMiddleware } from '#build/global-middleware'

// Reshape payload to match key `useLazyAsyncData` expects
Expand Down
88 changes: 0 additions & 88 deletions packages/bridge/src/runtime/app.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,2 @@
import type { DefineComponent } from 'vue'
import { useHead } from '@unhead/vue'
import type { NuxtAppCompat } from '@nuxt/bridge-schema'
import { defineComponent, getCurrentInstance } from './composables'

export const isVue2 = true
export const isVue3 = false

export const defineNuxtComponent: typeof defineComponent =
function defineNuxtComponent (...args: any[]): any {
const [options, key] = args
const { setup, head, ...opts } = options

// Avoid wrapping if no options api is used
if (!setup && !options.asyncData && !options.head) {
return {
...options
}
}

return {
_fetchKeyBase: key,
...opts,
setup (props, ctx) {
const nuxtApp = useNuxtApp()
const res = setup ? callWithNuxt(nuxtApp, setup, [props, ctx]) : {}

if (options.head) {
const nuxtApp = useNuxtApp()
useHead(typeof options.head === 'function' ? () => options.head(nuxtApp) : options.head)
}

return res
}
} as DefineComponent
}

export interface Context {
$_nuxtApp: NuxtAppCompat
}

let currentNuxtAppInstance: NuxtAppCompat | null

export const setNuxtAppInstance = (nuxt: NuxtAppCompat | null) => {
currentNuxtAppInstance = nuxt
}

/**
* Ensures that the setup function passed in has access to the Nuxt instance via `useNuxt`.
* @param nuxt A Nuxt instance
* @param setup The function to call
*/
export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtAppCompat, setup: T, args?: Parameters<T>) {
setNuxtAppInstance(nuxt)
const p: ReturnType<T> = args ? setup(...args as Parameters<T>) : setup()
if (process.server) {
// Unset nuxt instance to prevent context-sharing in server-side
setNuxtAppInstance(null)
}
return p
}

interface Plugin {
(nuxt: NuxtAppCompat): Promise<void> | Promise<{ provide?: Record<string, any> }> | void | { provide?: Record<string, any> }
}

export function defineNuxtPlugin (plugin: Plugin): (ctx: Context, inject: (id: string, value: any) => void) => void {
return async (ctx, inject) => {
const result = await callWithNuxt(ctx.$_nuxtApp, plugin, [ctx.$_nuxtApp])
if (result && result.provide) {
for (const key in result.provide) {
inject(key, result.provide[key])
}
}
return result
}
}

export const useNuxtApp = (): NuxtAppCompat => {
const vm = getCurrentInstance()

if (!vm) {
if (!currentNuxtAppInstance) {
throw new Error('nuxt app instance unavailable')
}
return currentNuxtAppInstance
}

return vm.proxy.$_nuxtApp
}
5 changes: 3 additions & 2 deletions packages/bridge/src/runtime/capi.legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { defu } from 'defu'
import { ComputedRef, computed, getCurrentInstance as getVM, isReactive, isRef, onBeforeMount, onServerPrefetch, reactive, ref, set, shallowRef, toRaw, toRefs, watch } from 'vue'
import type { Route } from 'vue-router'
import type { Nuxt2Context } from '@nuxt/bridge-schema'
import { useNuxtApp } from './app'
import { useRouter as _useRouter, useRoute as _useRoute, useState } from './composables'
import { useNuxtApp } from './nuxt'
import { useRouter as _useRouter, useRoute as _useRoute } from './composables/router'
import { useState } from './composables/state'
// @ts-expect-error virtual file
import { isFullStatic } from '#build/composition-globals.mjs'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { onBeforeMount, onServerPrefetch, onUnmounted, ref, getCurrentInstance, watch } from 'vue'
import type { Ref, WatchSource } from 'vue'
import type { NuxtAppCompat } from '@nuxt/bridge-schema'
import { useNuxtApp } from './app'
import { useNuxtApp } from '../nuxt'

export type _Transform<Input = any, Output = any> = (input: Input) => Output

Expand Down
34 changes: 34 additions & 0 deletions packages/bridge/src/runtime/composables/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

import { defineComponent } from 'vue'
import type { DefineComponent } from 'vue'
import { useHead } from '@unhead/vue'
import { useNuxtApp, callWithNuxt } from '../nuxt'

export const defineNuxtComponent: typeof defineComponent =
function defineNuxtComponent (...args: any[]): any {
const [options, key] = args
const { setup, head, ...opts } = options

// Avoid wrapping if no options api is used
if (!setup && !options.asyncData && !options.head) {
return {
...options
}
}

return {
_fetchKeyBase: key,
...opts,
setup (props, ctx) {
const nuxtApp = useNuxtApp()
const res = setup ? callWithNuxt(nuxtApp, setup, [props, ctx]) : {}

if (options.head) {
const nuxtApp = useNuxtApp()
useHead(typeof options.head === 'function' ? () => options.head(nuxtApp) : options.head)
}

return res
}
} as DefineComponent
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'co
import { appendHeader } from 'h3'
import type { H3Event } from 'h3'
import destr from 'destr'
import { useNuxtApp } from './app'
import { useNuxtApp } from '../nuxt'
import { useRequestEvent } from './ssr'

type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createError as _createError, H3Error } from 'h3'
import { toRef } from 'vue'
import { useNuxtApp } from './app'
import { useNuxtApp } from '../nuxt'

export const useError = () => toRef(useNuxtApp().payload, 'error')

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { FetchOptions, FetchRequest } from 'ofetch'
import type { TypedInternalResponse } from 'nitropack'
import { hash } from 'ohash'
import { computed, isRef, Ref } from 'vue'
import { computed, isRef } from 'vue'
import type { Ref } from 'vue'
import type { AsyncDataOptions, _Transform, KeyOfRes } from './asyncData'
import { useAsyncData } from './asyncData'

Expand Down
8 changes: 8 additions & 0 deletions packages/bridge/src/runtime/composables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { useLazyAsyncData, refreshNuxtData } from './asyncData'
export * from './component'
export { useCookie } from './cookie'
export { clearError, createError, isNuxtError, throwError, showError, useError } from './error'
export { useLazyFetch } from './fetch'
export * from './router'
export { useRequestHeaders, useRequestEvent } from './ssr'
export * from './state'
Original file line number Diff line number Diff line change
@@ -1,42 +1,13 @@
import { getCurrentInstance, onBeforeUnmount, isRef, watch, reactive, toRef, isReactive, Ref, set } from 'vue'
import type { CombinedVueInstance } from 'vue/types/vue'
import type { MetaInfo } from 'vue-meta'
import { getCurrentInstance, reactive } from 'vue'
import type VueRouter from 'vue-router'
import type { Location, RawLocation, Route, NavigationFailure } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/schema'
import { sendRedirect } from 'h3'
import { defu } from 'defu'
import { useRouter as useVueRouter, useRoute as useVueRoute } from 'vue-router/composables'
import { hasProtocol, joinURL, parseURL } from 'ufo'
import { useNuxtApp, callWithNuxt } from './app'
import { useNuxtApp, callWithNuxt, useRuntimeConfig } from '../nuxt'
import { createError, showError } from './error'
import type { NuxtError } from './error'

export { useLazyAsyncData, refreshNuxtData } from './asyncData'
export { useLazyFetch } from './fetch'
export { useCookie } from './cookie'
export { clearError, createError, isNuxtError, throwError, showError, useError } from './error'
export { useRequestHeaders, useRequestEvent } from './ssr'

export * from 'vue'

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) {
return nuxtApp._config as RuntimeConfig
}

nuxtApp._config = reactive(nuxtApp.$config)
return nuxtApp._config as RuntimeConfig
}

// Auto-import equivalents for `vue-router`
export const useRouter = () => {
if (getCurrentInstance()) {
Expand Down Expand Up @@ -66,98 +37,6 @@ export const useRoute = () => {
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
}

/** internal */
function metaInfoFromOptions (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaInfo>)) {
return metaOptions instanceof Function ? metaOptions : () => metaOptions
}

export const useNuxt2Meta = (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaInfo>)) => {
let vm: AugmentedComponent | null = null
try {
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 unwatch = watch(metaInfoFromOptions(metaOptions), (metaInfo: MetaInfo) => {
vm.$metaInfo = {
...vm.$metaInfo || {},
...unwrap(metaInfo)
}
if (process.client) {
meta.refresh()
}
}, { immediate: true, deep: true })

onBeforeUnmount(unwatch)
} catch {
const app = (useNuxtApp().nuxt2Context as any).app
if (typeof app.head === 'function') {
const originalHead = app.head
app.head = function () {
const head = originalHead.call(this) || {}
return defu(unwrap(metaInfoFromOptions(metaOptions)()), head)
}
} else {
app.head = defu(unwrap(metaInfoFromOptions(metaOptions)()), app.head)
}
}
}

export interface AddRouteMiddlewareOptions {
global?: boolean
}
Expand Down Expand Up @@ -218,6 +97,8 @@ export const navigateTo = (to: RawLocation | undefined | null, options?: Navigat
const nuxtApp = useNuxtApp()
if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) {
const redirectLocation = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).resolved.fullPath || '/')

// @ts-expect-error
return nuxtApp.callHook('app:redirected').then(() => sendRedirect(nuxtApp.ssrContext!.event, redirectLocation, options?.redirectCode || 302))
}
}
Expand Down
Loading

0 comments on commit 83e0df6

Please sign in to comment.