From 3bf856830ba28ef48f7f28df7949562324fb191c Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 25 Sep 2021 09:24:37 +0100 Subject: [PATCH] feat(bridge): add `useNuxtApp` and `defineNuxtPlugin` composables (#576) --- packages/bridge/src/capi.ts | 7 ++- packages/bridge/src/runtime/app.mjs | 5 -- packages/bridge/src/runtime/app.plugin.mjs | 27 +++++++++ packages/bridge/src/runtime/app.ts | 59 +++++++++++++++++++ .../{composables.mjs => composables.ts} | 0 packages/nuxt3/src/app/nuxt.ts | 1 - 6 files changed, 92 insertions(+), 7 deletions(-) delete mode 100644 packages/bridge/src/runtime/app.mjs create mode 100644 packages/bridge/src/runtime/app.plugin.mjs create mode 100644 packages/bridge/src/runtime/app.ts rename packages/bridge/src/runtime/{composables.mjs => composables.ts} (100%) diff --git a/packages/bridge/src/capi.ts b/packages/bridge/src/capi.ts index 623148d6a8..38056c42ce 100644 --- a/packages/bridge/src/capi.ts +++ b/packages/bridge/src/capi.ts @@ -1,4 +1,4 @@ -import { useNuxt, addPluginTemplate } from '@nuxt/kit' +import { useNuxt, addPlugin, addPluginTemplate } from '@nuxt/kit' import { resolve } from 'upath' import { distDir } from './dirs' @@ -14,6 +14,11 @@ export function setupCAPIBridge (_options: any) { nuxt.options.alias['@vue/composition-api'] = require.resolve('@vue/composition-api/dist/vue-composition-api.mjs') const capiPluginPath = resolve(distDir, 'runtime/capi.plugin.mjs') addPluginTemplate({ filename: 'capi.plugin.mjs', src: capiPluginPath }) + + // Add support for useNuxtApp + addPlugin(resolve(distDir, 'runtime/app.plugin.mjs')) + + // Register Composition API before loading the rest of app nuxt.hook('webpack:config', (configs) => { // @ts-ignore configs.forEach(config => config.entry.app.unshift(capiPluginPath)) diff --git a/packages/bridge/src/runtime/app.mjs b/packages/bridge/src/runtime/app.mjs deleted file mode 100644 index 5db30f7f4e..0000000000 --- a/packages/bridge/src/runtime/app.mjs +++ /dev/null @@ -1,5 +0,0 @@ -const mock = () => () => { throw new Error('not implemented') } - -export const defineNuxtPlugin = mock() -export const defineNuxtComponent = mock() -export const useNuxtApp = mock() diff --git a/packages/bridge/src/runtime/app.plugin.mjs b/packages/bridge/src/runtime/app.plugin.mjs new file mode 100644 index 0000000000..82c18b0ef4 --- /dev/null +++ b/packages/bridge/src/runtime/app.plugin.mjs @@ -0,0 +1,27 @@ +import { createHooks } from 'hookable/dist/index.mjs' +import { setNuxtInstance } from '#app' + +export default (ctx, inject) => { + const nuxt = { + provide: inject, + globalName: 'nuxt', + payload: process.client ? ctx.nuxtState : ctx.ssrContext.nuxt, + isHydrating: ctx.isHMR, + legacyNuxt: ctx.app + } + nuxt.hooks = createHooks() + nuxt.hook = nuxt.hooks.hook + nuxt.callHook = nuxt.hooks.callHook + + if (!Array.isArray(ctx.app.created)) { + ctx.app.created = [ctx.app.created] + } + + ctx.app.created.push(function () { + nuxt.legacyApp = this + }) + + setNuxtInstance(nuxt) + + inject('_nuxtApp', nuxt) +} diff --git a/packages/bridge/src/runtime/app.ts b/packages/bridge/src/runtime/app.ts new file mode 100644 index 0000000000..9d183f3be7 --- /dev/null +++ b/packages/bridge/src/runtime/app.ts @@ -0,0 +1,59 @@ +import type { Hookable } from 'hookable' +// @ts-ignore +import type { Vue } from 'vue/types/vue' +import type { ComponentOptions } from 'vue' +import { defineComponent, getCurrentInstance } from './composables' + +export const defineNuxtComponent = defineComponent + +export interface RuntimeNuxtHooks { } + +export interface NuxtAppCompat { + legacyNuxt: Vue + legacyApp: ComponentOptions + + globalName: string + + hooks: Hookable + hook: NuxtAppCompat['hooks']['hook'] + callHook: NuxtAppCompat['hooks']['callHook'] + + [key: string]: any + + ssrContext?: Record + payload: { + [key: string]: any + } + + provide: (name: string, value: any) => void +} + +export interface Context { + // eslint-disable-next-line + $_nuxtApp: NuxtAppCompat +} + +let currentNuxtInstance: NuxtAppCompat | null + +export const setNuxtInstance = (nuxt: NuxtAppCompat | null) => { + currentNuxtInstance = nuxt +} + +export const defineNuxtPlugin = plugin => (ctx: Context) => { + setNuxtInstance(ctx.$_nuxtApp) + plugin(ctx.$_nuxtApp) + setNuxtInstance(null) +} + +export const useNuxtApp = () => { + const vm = getCurrentInstance() + + if (!vm) { + if (!currentNuxtInstance) { + throw new Error('nuxt instance unavailable') + } + return currentNuxtInstance + } + + return vm?.proxy.$_nuxtApp +} diff --git a/packages/bridge/src/runtime/composables.mjs b/packages/bridge/src/runtime/composables.ts similarity index 100% rename from packages/bridge/src/runtime/composables.mjs rename to packages/bridge/src/runtime/composables.ts diff --git a/packages/nuxt3/src/app/nuxt.ts b/packages/nuxt3/src/app/nuxt.ts index 983ebdcd0f..00e62c5db0 100644 --- a/packages/nuxt3/src/app/nuxt.ts +++ b/packages/nuxt3/src/app/nuxt.ts @@ -68,7 +68,6 @@ export function createNuxt (options: CreateOptions) { const nuxt: NuxtApp = { provide: undefined, globalName: 'nuxt', - state: {}, payload: {}, isHydrating: process.client, ...options