2021-10-06 12:37:45 +00:00
/* eslint-disable no-use-before-define */
2021-11-02 11:27:25 +00:00
import { getCurrentInstance , reactive } from 'vue'
2022-03-11 08:22:16 +00:00
import type { App , onErrorCaptured , VNode } from 'vue'
2021-08-27 12:51:40 +00:00
import { createHooks , Hookable } from 'hookable'
2021-11-21 16:14:46 +00:00
import type { RuntimeConfig } from '@nuxt/schema'
2022-04-01 09:55:23 +00:00
import { getContext } from 'unctx'
2022-06-08 19:37:50 +00:00
import type { SSRContext } from 'vue-bundle-renderer'
import type { CompatibilityEvent } from 'h3'
2022-07-27 09:53:53 +00:00
// eslint-disable-next-line import/no-restricted-paths
import type { NuxtRenderContext } from '../core/runtime/nitro/renderer'
2021-01-18 12:46:19 +00:00
2022-04-01 09:55:23 +00:00
const nuxtAppCtx = getContext < NuxtApp > ( 'nuxt-app' )
2021-07-15 11:28:04 +00:00
type NuxtMeta = {
htmlAttrs? : string
headAttrs? : string
bodyAttrs? : string
headTags? : string
2022-02-16 17:56:30 +00:00
bodyScriptsPrepend? : string
2021-07-15 11:28:04 +00:00
bodyScripts? : string
}
2021-08-26 18:57:36 +00:00
type HookResult = Promise < void > | void
export interface RuntimeNuxtHooks {
'app:created' : ( app : App < Element > ) = > HookResult
'app:beforeMount' : ( app : App < Element > ) = > HookResult
'app:mounted' : ( app : App < Element > ) = > HookResult
2022-07-27 09:53:53 +00:00
'app:rendered' : ( ctx : NuxtRenderContext ) = > HookResult
2022-04-19 19:13:11 +00:00
'app:redirected' : ( ) = > HookResult
2022-01-26 17:24:54 +00:00
'app:suspense:resolve' : ( Component? : VNode ) = > HookResult
2022-03-11 08:22:16 +00:00
'app:error' : ( err : any ) = > HookResult
'app:error:cleared' : ( options : { redirect? : string } ) = > HookResult
2022-03-28 17:12:41 +00:00
'app:data:refresh' : ( keys? : string [ ] ) = > HookResult
2021-08-26 18:57:36 +00:00
'page:start' : ( Component? : VNode ) = > HookResult
'page:finish' : ( Component? : VNode ) = > HookResult
2021-10-06 12:37:45 +00:00
'meta:register' : ( metaRenderers : Array < ( nuxt : NuxtApp ) = > NuxtMeta | Promise < NuxtMeta > > ) = > HookResult
2021-12-21 14:44:35 +00:00
'vue:setup' : ( ) = > void
2022-03-11 08:22:16 +00:00
'vue:error' : ( . . . args : Parameters < Parameters < typeof onErrorCaptured > [ 0 ] > ) = > HookResult
2021-08-26 18:57:36 +00:00
}
2021-11-18 13:11:34 +00:00
interface _NuxtApp {
2021-10-18 18:31:37 +00:00
vueApp : App < Element >
2021-01-18 12:46:19 +00:00
globalName : string
2021-08-27 12:51:40 +00:00
hooks : Hookable < RuntimeNuxtHooks >
2021-11-18 13:11:34 +00:00
hook : _NuxtApp [ 'hooks' ] [ 'hook' ]
callHook : _NuxtApp [ 'hooks' ] [ 'callHook' ]
2021-01-18 12:46:19 +00:00
[ key : string ] : any
2021-04-03 10:03:20 +00:00
_asyncDataPromises? : Record < string , Promise < any > >
2022-06-08 19:37:50 +00:00
ssrContext? : SSRContext & {
url : string
event : CompatibilityEvent
/** @deprecated Use `event` instead. */
req? : CompatibilityEvent [ 'req' ]
/** @deprecated Use `event` instead. */
res? : CompatibilityEvent [ 'res' ]
runtimeConfig : RuntimeConfig
noSSR : boolean
error? : any
nuxt : _NuxtApp
payload : _NuxtApp [ 'payload' ]
teleports? : Record < string , string >
2021-07-19 13:27:20 +00:00
renderMeta ? : ( ) = > Promise < NuxtMeta > | NuxtMeta
2021-07-15 11:28:04 +00:00
}
2021-01-18 12:46:19 +00:00
payload : {
2022-06-08 19:37:50 +00:00
serverRendered? : boolean
2021-03-17 09:17:18 +00:00
data? : Record < string , any >
2021-10-11 17:48:03 +00:00
state? : Record < string , any >
2021-01-18 12:46:19 +00:00
rendered? : Function
[ key : string ] : any
}
provide : ( name : string , value : any ) = > void
}
2021-11-18 13:11:34 +00:00
export interface NuxtApp extends _NuxtApp { }
2021-06-18 17:16:51 +00:00
export const NuxtPluginIndicator = '__nuxt_plugin'
2021-11-18 13:11:34 +00:00
export interface Plugin < Injections extends Record < string , any > = Record < string , any > > {
( nuxt : _NuxtApp ) : Promise < void > | Promise < { provide? : Injections } > | void | { provide? : Injections }
2021-06-18 17:16:51 +00:00
[ NuxtPluginIndicator ] ? : true
}
2021-01-18 12:46:19 +00:00
export interface CreateOptions {
2021-10-18 18:31:37 +00:00
vueApp : NuxtApp [ 'vueApp' ]
2021-08-27 13:30:53 +00:00
ssrContext? : NuxtApp [ 'ssrContext' ]
globalName? : NuxtApp [ 'globalName' ]
2021-01-18 12:46:19 +00:00
}
2021-10-02 18:40:10 +00:00
export function createNuxtApp ( options : CreateOptions ) {
2021-10-18 18:31:37 +00:00
const nuxtApp : NuxtApp = {
2021-01-18 12:46:19 +00:00
provide : undefined ,
globalName : 'nuxt' ,
2021-10-11 17:48:03 +00:00
payload : reactive ( {
data : { } ,
state : { } ,
2021-11-24 19:59:04 +00:00
_errors : { } ,
2021-10-11 17:48:03 +00:00
. . . ( process . client ? window . __NUXT__ : { serverRendered : true } )
} ) ,
2021-01-18 12:46:19 +00:00
isHydrating : process.client ,
2021-10-08 14:21:55 +00:00
_asyncDataPromises : { } ,
2021-01-18 12:46:19 +00:00
. . . options
2021-08-27 13:30:53 +00:00
} as any as NuxtApp
2021-01-18 12:46:19 +00:00
2021-10-18 18:31:37 +00:00
nuxtApp . hooks = createHooks < RuntimeNuxtHooks > ( )
nuxtApp . hook = nuxtApp . hooks . hook
nuxtApp . callHook = nuxtApp . hooks . callHook
2021-01-18 12:46:19 +00:00
2021-10-18 18:31:37 +00:00
nuxtApp . provide = ( name : string , value : any ) = > {
2021-01-18 12:46:19 +00:00
const $name = '$' + name
2021-10-18 18:31:37 +00:00
defineGetter ( nuxtApp , $name , value )
defineGetter ( nuxtApp . vueApp . config . globalProperties , $name , value )
2021-01-18 12:46:19 +00:00
}
2021-02-03 18:14:30 +00:00
// Inject $nuxt
2021-10-18 18:31:37 +00:00
defineGetter ( nuxtApp . vueApp , '$nuxt' , nuxtApp )
defineGetter ( nuxtApp . vueApp . config . globalProperties , '$nuxt' , nuxtApp )
2021-01-18 12:46:19 +00:00
// Expose nuxt to the renderContext
2021-10-18 18:31:37 +00:00
if ( nuxtApp . ssrContext ) {
nuxtApp . ssrContext . nuxt = nuxtApp
2021-01-18 12:46:19 +00:00
}
if ( process . server ) {
// Expose to server renderer to create window.__NUXT__
2022-06-08 19:37:50 +00:00
nuxtApp . ssrContext = nuxtApp . ssrContext || { } as any
2021-10-18 18:31:37 +00:00
nuxtApp . ssrContext . payload = nuxtApp . payload
2021-01-18 12:46:19 +00:00
}
2022-04-12 14:59:48 +00:00
// Expose client runtime-config to the payload
2021-10-02 20:30:20 +00:00
if ( process . server ) {
2022-04-11 19:55:43 +00:00
nuxtApp . payload . config = {
public : options . ssrContext . runtimeConfig . public ,
app : options.ssrContext.runtimeConfig.app
}
2021-10-02 20:30:20 +00:00
}
2022-04-12 14:59:48 +00:00
// Expose runtime config
const runtimeConfig = process . server
? options . ssrContext . runtimeConfig
: reactive ( nuxtApp . payload . config )
// Backward compatibilty following #4254
const compatibilityConfig = new Proxy ( runtimeConfig , {
get ( target , prop ) {
if ( prop === 'public' ) {
return target . public
}
return target [ prop ] ? ? target . public [ prop ]
} ,
set ( target , prop , value ) {
if ( process . server || prop === 'public' || prop === 'app' ) {
return false // Throws TypeError
}
target [ prop ] = value
target . public [ prop ] = value
return true
}
} )
nuxtApp . provide ( 'config' , compatibilityConfig )
2021-10-18 18:31:37 +00:00
return nuxtApp
2021-01-18 12:46:19 +00:00
}
2021-11-18 13:11:34 +00:00
export async function applyPlugin ( nuxtApp : NuxtApp , plugin : Plugin ) {
2021-02-19 01:08:45 +00:00
if ( typeof plugin !== 'function' ) { return }
2022-01-25 12:29:11 +00:00
const { provide } = await callWithNuxt ( nuxtApp , plugin , [ nuxtApp ] ) || { }
2021-11-18 13:11:34 +00:00
if ( provide && typeof provide === 'object' ) {
for ( const key in provide ) {
nuxtApp . provide ( key , provide [ key ] )
}
}
2021-01-18 12:46:19 +00:00
}
2021-10-18 18:31:37 +00:00
export async function applyPlugins ( nuxtApp : NuxtApp , plugins : Plugin [ ] ) {
2021-01-18 12:46:19 +00:00
for ( const plugin of plugins ) {
2021-10-18 18:31:37 +00:00
await applyPlugin ( nuxtApp , plugin )
2021-01-18 12:46:19 +00:00
}
}
2021-04-09 13:48:39 +00:00
2022-07-06 19:15:00 +00:00
export function normalizePlugins ( _plugins : Plugin [ ] ) {
2022-07-12 17:06:55 +00:00
const unwrappedPlugins = [ ]
const legacyInjectPlugins = [ ]
const invalidPlugins = [ ]
2021-06-18 17:16:51 +00:00
const plugins = _plugins . map ( ( plugin ) = > {
2021-11-02 09:49:45 +00:00
if ( typeof plugin !== 'function' ) {
2022-07-12 17:06:55 +00:00
invalidPlugins . push ( plugin )
return null
}
if ( plugin . length > 1 ) {
legacyInjectPlugins . push ( plugin )
// Allow usage without wrapper but warn
// TODO: Skip invalid in next releases
// @ts-ignore
return ( nuxtApp : NuxtApp ) = > plugin ( nuxtApp , nuxtApp . provide )
// return null
}
if ( ! isNuxtPlugin ( plugin ) ) {
unwrappedPlugins . push ( plugin )
// Allow usage without wrapper but warn
2021-11-02 09:49:45 +00:00
}
2021-06-18 17:16:51 +00:00
return plugin
2022-07-12 17:06:55 +00:00
} ) . filter ( Boolean )
if ( process . dev && legacyInjectPlugins . length ) {
console . warn ( '[warn] [nuxt] You are using a plugin with legacy Nuxt 2 format (context, inject) which is likely to be broken. In the future they will be ignored:' , legacyInjectPlugins . map ( p = > p . name || p ) . join ( ',' ) )
}
if ( process . dev && invalidPlugins . length ) {
console . warn ( '[warn] [nuxt] Some plugins are not exposing a function and skipped:' , invalidPlugins )
}
if ( process . dev && unwrappedPlugins . length ) {
console . warn ( '[warn] [nuxt] You are using a plugin that has not been wrapped in `defineNuxtPlugin`. It is advised to wrap your plugins as in the future this may enable enhancements:' , unwrappedPlugins . map ( p = > p . name || p ) . join ( ',' ) )
}
2021-06-18 17:16:51 +00:00
return plugins as Plugin [ ]
}
2021-11-18 13:11:34 +00:00
export function defineNuxtPlugin < T > ( plugin : Plugin < T > ) {
2021-06-18 17:16:51 +00:00
plugin [ NuxtPluginIndicator ] = true
return plugin
}
2022-07-12 17:06:55 +00:00
export function isNuxtPlugin ( plugin : unknown ) {
return typeof plugin === 'function' && NuxtPluginIndicator in plugin
}
2021-04-09 13:48:39 +00:00
/ * *
* Ensures that the setup function passed in has access to the Nuxt instance via ` useNuxt ` .
2021-04-15 18:49:29 +00:00
*
2021-04-09 13:48:39 +00:00
* @param nuxt A Nuxt instance
* @param setup The function to call
* /
2022-03-16 22:44:22 +00:00
export function callWithNuxt < T extends ( ...args : any [ ] ) = > any > ( nuxt : NuxtApp | _NuxtApp , setup : T , args? : Parameters < T > ) {
2022-04-01 09:55:23 +00:00
const fn = ( ) = > args ? setup ( . . . args as Parameters < T > ) : setup ( )
2021-11-23 17:50:20 +00:00
if ( process . server ) {
2022-04-01 09:55:23 +00:00
return nuxtAppCtx . callAsync < ReturnType < T > > ( nuxt , fn )
} else {
// In client side we could assume nuxt app is singleton
nuxtAppCtx . set ( nuxt )
return fn ( )
2021-11-23 17:50:20 +00:00
}
2021-04-09 13:48:39 +00:00
}
/ * *
* Returns the current Nuxt instance .
* /
2022-02-15 09:50:11 +00:00
export function useNuxtApp ( ) {
2022-08-04 11:00:01 +00:00
const nuxtAppInstance = nuxtAppCtx . tryUse ( )
2021-04-09 13:48:39 +00:00
2022-06-15 12:00:34 +00:00
if ( ! nuxtAppInstance ) {
const vm = getCurrentInstance ( )
if ( ! vm ) {
2021-04-09 13:48:39 +00:00
throw new Error ( 'nuxt instance unavailable' )
}
2022-06-15 12:00:34 +00:00
return vm . appContext . app . $nuxt as NuxtApp
2021-04-09 13:48:39 +00:00
}
2022-06-15 12:00:34 +00:00
return nuxtAppInstance
2021-04-09 13:48:39 +00:00
}
2021-10-02 20:30:20 +00:00
2021-11-10 12:40:02 +00:00
export function useRuntimeConfig ( ) : RuntimeConfig {
2021-10-02 20:30:20 +00:00
return useNuxtApp ( ) . $config
}
2021-10-18 18:31:37 +00:00
function defineGetter < K extends string | number | symbol , V > ( obj : Record < K , V > , key : K , val : V ) {
Object . defineProperty ( obj , key , { get : ( ) = > val } )
}