2021-10-06 12:37:45 +00:00
/* eslint-disable no-use-before-define */
2022-12-11 21:44:52 +00:00
import { getCurrentInstance , reactive } from 'vue'
import type { App , onErrorCaptured , VNode , Ref } from 'vue'
import type { Hookable } from 'hookable'
import { createHooks } from 'hookable'
2022-04-01 09:55:23 +00:00
import { getContext } from 'unctx'
2022-08-07 09:52:34 +00:00
import type { SSRContext } from 'vue-bundle-renderer/runtime'
2022-10-15 18:42:57 +00:00
import type { H3Event } from 'h3'
2022-11-24 12:24:14 +00:00
// eslint-disable-next-line import/no-restricted-paths
import type { NuxtIslandContext } from '../core/runtime/nitro/renderer'
2023-02-13 22:42:04 +00:00
import type { RuntimeConfig , AppConfigInput } from 'nuxt/schema'
2021-01-18 12:46:19 +00:00
2023-03-07 22:51:08 +00:00
const nuxtAppCtx = /* #__PURE__ */ getContext < NuxtApp > ( 'nuxt-app' )
2022-04-01 09:55:23 +00:00
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
2022-08-11 16:34:39 +00:00
type AppRenderedContext = { ssrContext : NuxtApp [ 'ssrContext' ] }
2021-08-26 18:57:36 +00:00
export interface RuntimeNuxtHooks {
'app:created' : ( app : App < Element > ) = > HookResult
'app:beforeMount' : ( app : App < Element > ) = > HookResult
'app:mounted' : ( app : App < Element > ) = > HookResult
2022-08-11 16:34:39 +00:00
'app:rendered' : ( ctx : AppRenderedContext ) = > 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
2023-02-16 12:43:58 +00:00
'app:chunkError' : ( options : { error : any } ) = > HookResult
2022-03-28 17:12:41 +00:00
'app:data:refresh' : ( keys? : string [ ] ) = > HookResult
2022-09-13 20:20:23 +00:00
'link:prefetch' : ( link : string ) = > HookResult
2021-08-26 18:57:36 +00:00
'page:start' : ( Component? : VNode ) = > HookResult
'page:finish' : ( Component? : VNode ) = > HookResult
2022-10-19 12:43:03 +00:00
'page:transition:finish' : ( Component? : VNode ) = > 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
}
2022-08-12 17:47:58 +00:00
export interface NuxtSSRContext extends SSRContext {
url : string
2022-10-15 18:42:57 +00:00
event : H3Event
2022-08-12 17:47:58 +00:00
runtimeConfig : RuntimeConfig
noSSR : boolean
/** whether we are rendering an SSR error */
error? : boolean
nuxt : _NuxtApp
payload : _NuxtApp [ 'payload' ]
teleports? : Record < string , string >
renderMeta ? : ( ) = > Promise < NuxtMeta > | NuxtMeta
2022-11-24 12:24:14 +00:00
islandContext? : NuxtIslandContext
2022-08-12 17:47:58 +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
2023-03-01 15:08:23 +00:00
versions : Record < string , string >
2021-01-18 12:46:19 +00:00
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
2022-08-12 17:47:58 +00:00
_asyncDataPromises : Record < string , Promise < any > | undefined >
2022-08-30 10:34:09 +00:00
_asyncData : Record < string , {
data : Ref < any >
pending : Ref < boolean >
error : Ref < any >
2022-10-08 14:18:57 +00:00
} | undefined >
isHydrating? : boolean
deferHydration : ( ) = > ( ) = > void | Promise < void >
2022-08-12 17:47:58 +00:00
ssrContext? : NuxtSSRContext
2021-01-18 12:46:19 +00:00
payload : {
2022-06-08 19:37:50 +00:00
serverRendered? : boolean
2022-09-10 13:57:16 +00:00
prerenderedAt? : number
2022-08-12 17:47:58 +00:00
data : Record < string , any >
state : Record < string , any >
2021-01-18 12:46:19 +00:00
rendered? : Function
2022-08-08 14:33:31 +00:00
error? : Error | {
url : string
2023-02-13 22:55:29 +00:00
statusCode : number
2022-08-08 14:33:31 +00:00
statusMessage : string
message : string
description : string
data? : any
2022-08-22 10:12:02 +00:00
} | null
2021-01-18 12:46:19 +00:00
[ key : string ] : any
}
2022-11-10 13:27:59 +00:00
static : {
data : Record < string , any >
}
2021-01-18 12:46:19 +00:00
provide : ( name : string , value : any ) = > void
}
2022-09-01 09:08:56 +00:00
export interface NuxtApp extends _NuxtApp { }
2021-11-18 13:11:34 +00:00
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 ) {
2022-10-08 14:18:57 +00:00
let hydratingCount = 0
2021-10-18 18:31:37 +00:00
const nuxtApp : NuxtApp = {
2021-01-18 12:46:19 +00:00
provide : undefined ,
globalName : 'nuxt' ,
2023-03-01 15:08:23 +00:00
versions : {
get nuxt ( ) { return __NUXT_VERSION__ } ,
get vue ( ) { return nuxtApp . vueApp . version }
} ,
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 } )
} ) ,
2022-11-10 13:27:59 +00:00
static : {
data : { }
} ,
2021-01-18 12:46:19 +00:00
isHydrating : process.client ,
2022-10-08 14:18:57 +00:00
deferHydration ( ) {
if ( ! nuxtApp . isHydrating ) { return ( ) = > { } }
hydratingCount ++
let called = false
return ( ) = > {
if ( called ) { return }
called = true
hydratingCount --
if ( hydratingCount === 0 ) {
nuxtApp . isHydrating = false
return nuxtApp . callHook ( 'app:suspense:resolve' )
}
}
} ,
2021-10-08 14:21:55 +00:00
_asyncDataPromises : { } ,
2022-08-30 10:34:09 +00:00
_asyncData : { } ,
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 )
2022-11-10 09:43:24 +00:00
// @ts-expect-error
2021-10-18 18:31:37 +00:00
defineGetter ( nuxtApp . vueApp . config . globalProperties , '$nuxt' , nuxtApp )
2021-01-18 12:46:19 +00:00
if ( process . server ) {
2022-08-08 14:33:31 +00:00
// Expose nuxt to the renderContext
if ( nuxtApp . ssrContext ) {
nuxtApp . ssrContext . nuxt = nuxtApp
}
2021-01-18 12:46:19 +00:00
// Expose to server renderer to create window.__NUXT__
2022-06-08 19:37:50 +00:00
nuxtApp . ssrContext = nuxtApp . ssrContext || { } as any
2022-08-12 17:47:58 +00:00
if ( nuxtApp . ssrContext ! . payload ) {
Object . assign ( nuxtApp . payload , nuxtApp . ssrContext ! . payload )
2022-08-08 14:33:31 +00:00
}
2022-08-12 17:47:58 +00:00
nuxtApp . ssrContext ! . payload = nuxtApp . payload
2021-01-18 12:46:19 +00:00
2022-08-08 14:33:31 +00:00
// Expose client runtime-config to the payload
2022-04-11 19:55:43 +00:00
nuxtApp . payload . config = {
2022-08-12 17:47:58 +00:00
public : options . ssrContext ! . runtimeConfig . public ,
app : options.ssrContext ! . runtimeConfig . app
2022-04-11 19:55:43 +00:00
}
2021-10-02 20:30:20 +00:00
}
2023-02-16 12:43:58 +00:00
// Listen to chunk load errors
if ( process . client ) {
window . addEventListener ( 'nuxt.preloadError' , ( event ) = > {
nuxtApp . callHook ( 'app:chunkError' , { error : ( event as Event & { payload : Error } ) . payload } )
} )
2023-03-09 14:37:39 +00:00
// Log errors captured when running plugins, in the `app:created` and `app:beforeMount` hooks
// as well as when mounting the app and in the `app:mounted` hook
nuxtApp . hook ( 'app:error' , ( . . . args ) = > { console . error ( '[nuxt] error caught during app initialization' , . . . args ) } )
}
2023-02-21 15:06:10 +00:00
2022-04-12 14:59:48 +00:00
// Expose runtime config
const runtimeConfig = process . server
2022-08-12 17:47:58 +00:00
? options . ssrContext ! . runtimeConfig
2022-04-12 14:59:48 +00:00
: reactive ( nuxtApp . payload . config )
2022-08-12 17:47:58 +00:00
// Backward compatibility following #4254
2022-04-12 14:59:48 +00:00
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-08-12 17:47:58 +00:00
const unwrappedPlugins : Plugin [ ] = [ ]
const legacyInjectPlugins : Plugin [ ] = [ ]
const invalidPlugins : Plugin [ ] = [ ]
2022-07-12 17:06:55 +00:00
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 [ ]
}
2022-08-26 15:47:29 +00:00
export function defineNuxtPlugin < T extends Record < string , any > > ( 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 > ) {
2023-01-23 11:13:21 +00:00
const fn : ( ) = > ReturnType < T > = ( ) = > args ? setup ( . . . args as Parameters < T > ) : setup ( )
2021-11-23 17:50:20 +00:00
if ( process . server ) {
2023-01-23 11:13:21 +00:00
return nuxtAppCtx . callAsync ( nuxt , fn )
2022-04-01 09:55:23 +00:00
} 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 } )
}
2022-08-17 15:23:13 +00:00
export function defineAppConfig < C extends AppConfigInput > ( config : C ) : C {
return config
}