mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
feat(nuxt3): automatically inject returns from plugins (#2001)
Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
dd4dd9e30e
commit
4e9a27257b
@ -36,6 +36,10 @@ console.log(nuxtApp.$hello('name')) // Prints "Hello name!"
|
||||
|
||||
In Nuxt 2 plugins, this was referred to as [inject function](https://nuxtjs.org/docs/directory-structure/plugins#inject-in-root--context)
|
||||
|
||||
::alert{icon=👉}
|
||||
It is possible to inject helpers by returning an object with a `provide` key. See the [plugins documentation](/docs/directory-structure/plugins) for more information.
|
||||
::
|
||||
|
||||
## NuxtApp interface (advanced)
|
||||
|
||||
`nuxtApp` has the following properties: (note: this is an internal interface and some properties might change until stable release)
|
||||
|
@ -24,22 +24,63 @@ export default defineNuxtPlugin(nuxtApp => {
|
||||
})
|
||||
```
|
||||
|
||||
## Typing plugins
|
||||
## Automatically providing helpers
|
||||
|
||||
If you provide a global property on the nuxt app instance, you can declare the type of this property like this:
|
||||
If you would like to provide a helper on the `NuxtApp` instance, just return it from the plugin under a `provide` key. For example:
|
||||
|
||||
```ts
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
export default defineNuxtPlugin(nuxtApp => {
|
||||
nuxtApp.provide('hello', msg => `Hello ${msg}!`);
|
||||
export default defineNuxtPlugin(() => {
|
||||
return {
|
||||
provide: {
|
||||
hello: () => 'world'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In another file you can use this:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
{{ $hello() }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// alternatively, you can also use it here
|
||||
const { $hello } = useNuxtApp()
|
||||
</script>
|
||||
```
|
||||
|
||||
## Typing plugins
|
||||
|
||||
If you return your helpers from the plugin, they will be typed automatically; you'll find them typed for the return of `useNuxtApp()` and within your templates.
|
||||
|
||||
::alert
|
||||
If you need to use a provided helper _within_ another plugin, you can call `useNuxtApp()` to get the typed version. But in general this should be avoided unless you are certain of the plugins' order.
|
||||
::
|
||||
|
||||
### Advanced
|
||||
|
||||
For advanced use-cases, you can declare the type of injected properties like this:
|
||||
|
||||
```ts [index.d.ts]
|
||||
declare module '#app' {
|
||||
interface NuxtApp {
|
||||
$hello (msg: string): string
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$hello (msg: string): string
|
||||
}
|
||||
}
|
||||
|
||||
export { }
|
||||
```
|
||||
|
||||
## Vue plugins
|
||||
|
@ -25,13 +25,13 @@ export interface RuntimeNuxtHooks {
|
||||
'meta:register': (metaRenderers: Array<(nuxt: NuxtApp) => NuxtMeta | Promise<NuxtMeta>>) => HookResult
|
||||
}
|
||||
|
||||
export interface NuxtApp {
|
||||
interface _NuxtApp {
|
||||
vueApp: App<Element>
|
||||
globalName: string
|
||||
|
||||
hooks: Hookable<RuntimeNuxtHooks>
|
||||
hook: NuxtApp['hooks']['hook']
|
||||
callHook: NuxtApp['hooks']['callHook']
|
||||
hook: _NuxtApp['hooks']['hook']
|
||||
callHook: _NuxtApp['hooks']['callHook']
|
||||
|
||||
[key: string]: any
|
||||
|
||||
@ -52,9 +52,11 @@ export interface NuxtApp {
|
||||
provide: (name: string, value: any) => void
|
||||
}
|
||||
|
||||
export interface NuxtApp extends _NuxtApp { }
|
||||
|
||||
export const NuxtPluginIndicator = '__nuxt_plugin'
|
||||
export interface Plugin {
|
||||
(nuxt: NuxtApp): Promise<void> | void
|
||||
export interface Plugin<Injections extends Record<string, any> = Record<string, any>> {
|
||||
(nuxt: _NuxtApp): Promise<void> | Promise<{ provide?: Injections }> | void | { provide?: Injections }
|
||||
[NuxtPluginIndicator]?: true
|
||||
}
|
||||
export interface LegacyPlugin {
|
||||
@ -117,9 +119,14 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
return nuxtApp
|
||||
}
|
||||
|
||||
export function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
if (typeof plugin !== 'function') { return }
|
||||
return callWithNuxt(nuxtApp, () => plugin(nuxtApp))
|
||||
const { provide } = await callWithNuxt(nuxtApp, () => plugin(nuxtApp)) || {}
|
||||
if (provide && typeof provide === 'object') {
|
||||
for (const key in provide) {
|
||||
nuxtApp.provide(key, provide[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) {
|
||||
@ -149,7 +156,7 @@ export function normalizePlugins (_plugins: Array<Plugin | LegacyPlugin>) {
|
||||
return plugins as Plugin[]
|
||||
}
|
||||
|
||||
export function defineNuxtPlugin (plugin: Plugin) {
|
||||
export function defineNuxtPlugin<T> (plugin: Plugin<T>) {
|
||||
plugin[NuxtPluginIndicator] = true
|
||||
return plugin
|
||||
}
|
||||
@ -170,11 +177,11 @@ export const setNuxtAppInstance = (nuxt: NuxtApp | null) => {
|
||||
* @param nuxt A Nuxt instance
|
||||
* @param setup The function to call
|
||||
*/
|
||||
export async function callWithNuxt (nuxt: NuxtApp, setup: () => any) {
|
||||
export function callWithNuxt<T extends () => any> (nuxt: NuxtApp, setup: T) {
|
||||
setNuxtAppInstance(nuxt)
|
||||
const p = setup()
|
||||
const p: ReturnType<T> = setup()
|
||||
setNuxtAppInstance(null)
|
||||
await p
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,6 +42,7 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
// Add nuxt3 types
|
||||
nuxt.hook('prepare:types', (opts) => {
|
||||
opts.references.push({ types: 'nuxt3' })
|
||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'plugins.d.ts') })
|
||||
})
|
||||
|
||||
// Init user modules
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { templateUtils } from '@nuxt/kit'
|
||||
import type { Nuxt, NuxtApp } from '@nuxt/kit'
|
||||
|
||||
import { relative } from 'pathe'
|
||||
|
||||
type TemplateContext = {
|
||||
nuxt: Nuxt;
|
||||
app: NuxtApp;
|
||||
@ -74,3 +76,32 @@ export const appViewTemplate = {
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export const pluginsDeclaration = {
|
||||
filename: 'plugins.d.ts',
|
||||
write: true,
|
||||
getContents: (ctx: TemplateContext) => {
|
||||
const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => `\\${e}`).join('|')})$`, 'g')
|
||||
const tsImports = ctx.app.plugins.map(p => relative(ctx.nuxt.options.buildDir, p.src).replace(EXTENSION_RE, ''))
|
||||
|
||||
return `// Generated by Nuxt3'
|
||||
import type { Plugin } from '#app'
|
||||
|
||||
type Decorate<T extends Record<string, any>> = { [K in keyof T as K extends string ? \`$\${K}\` : never]: T[K] }
|
||||
|
||||
type InjectionType<A extends Plugin> = A extends Plugin<infer T> ? Decorate<T> : unknown
|
||||
|
||||
type NuxtAppInjections = \n ${tsImports.map(p => `InjectionType<typeof import('${p}').default>`).join(' &\n ')}
|
||||
|
||||
declare module '#app' {
|
||||
interface NuxtApp extends NuxtAppInjections { }
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties extends NuxtAppInjections { }
|
||||
}
|
||||
|
||||
export { }
|
||||
`
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
routes
|
||||
})
|
||||
nuxtApp.vueApp.use(router)
|
||||
nuxtApp.provide('router', router)
|
||||
|
||||
const previousRoute = shallowRef(router.currentRoute.value)
|
||||
router.afterEach((_to, from) => {
|
||||
@ -53,4 +52,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.ssrContext.error = error
|
||||
}
|
||||
})
|
||||
|
||||
return { provide: { router } }
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user