mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
feat(nuxt): add nuxtApp.runWithContext
(#20608)
This commit is contained in:
parent
a81f9e4c82
commit
da3357449f
@ -9,7 +9,7 @@
|
||||
|
||||
<script setup>
|
||||
import { defineAsyncComponent, onErrorCaptured, onServerPrefetch, provide } from 'vue'
|
||||
import { callWithNuxt, useNuxtApp } from '#app/nuxt'
|
||||
import { useNuxtApp } from '#app/nuxt'
|
||||
import { isNuxtError, showError, useError } from '#app/composables/error'
|
||||
import { useRoute } from '#app/composables/router'
|
||||
import AppComponent from '#build/app-component.mjs'
|
||||
@ -40,7 +40,7 @@ const error = useError()
|
||||
onErrorCaptured((err, target, info) => {
|
||||
nuxtApp.hooks.callHook('vue:error', err, target, info).catch(hookError => console.error('[nuxt] Error in `vue:error` hook', hookError))
|
||||
if (process.server || (isNuxtError(err) && (err.fatal || err.unhandled))) {
|
||||
const p = callWithNuxt(nuxtApp, showError, [err])
|
||||
const p = nuxtApp.runWithContext(() => showError(err))
|
||||
onServerPrefetch(() => p)
|
||||
return false // suppress error from breaking render
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { getCurrentInstance, reactive, toRefs } from 'vue'
|
||||
import type { DefineComponent, defineComponent } from 'vue'
|
||||
import { useHead } from '@unhead/vue'
|
||||
import type { NuxtApp } from '../nuxt'
|
||||
import { callWithNuxt, useNuxtApp } from '../nuxt'
|
||||
import { useNuxtApp } from '../nuxt'
|
||||
import { useAsyncData } from './asyncData'
|
||||
import { useRoute } from './router'
|
||||
import { createError } from './error'
|
||||
@ -10,12 +10,12 @@ import { createError } from './error'
|
||||
export const NuxtComponentIndicator = '__nuxt_component'
|
||||
|
||||
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) {
|
||||
const nuxt = useNuxtApp()
|
||||
const nuxtApp = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const vm = getCurrentInstance()!
|
||||
const { fetchKey } = vm.proxy!.$options
|
||||
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
|
||||
const { data, error } = await useAsyncData(`options:asyncdata:${key}`, () => callWithNuxt(nuxt, fn, [nuxt]))
|
||||
const { data, error } = await useAsyncData(`options:asyncdata:${key}`, () => nuxtApp.runWithContext(() => fn(nuxtApp)))
|
||||
if (error.value) {
|
||||
throw createError(error.value)
|
||||
}
|
||||
@ -43,7 +43,7 @@ export const defineNuxtComponent: typeof defineComponent =
|
||||
...options,
|
||||
setup (props, ctx) {
|
||||
const nuxtApp = useNuxtApp()
|
||||
const res = setup ? Promise.resolve(callWithNuxt(nuxtApp, setup, [props, ctx])).then(r => r || {}) : {}
|
||||
const res = setup ? Promise.resolve(nuxtApp.runWithContext(() => setup(props, ctx))).then(r => r || {}) : {}
|
||||
|
||||
const promises: Promise<any>[] = []
|
||||
if (options.asyncData) {
|
||||
|
@ -76,6 +76,8 @@ interface _NuxtApp {
|
||||
hook: _NuxtApp['hooks']['hook']
|
||||
callHook: _NuxtApp['hooks']['callHook']
|
||||
|
||||
runWithContext: <T extends () => any>(fn: T) => ReturnType<T> | Promise<Awaited<ReturnType<T>>>
|
||||
|
||||
[key: string]: unknown
|
||||
|
||||
/** @internal */
|
||||
@ -193,6 +195,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
static: {
|
||||
data: {}
|
||||
},
|
||||
runWithContext: (fn: any) => callWithNuxt(nuxtApp, fn),
|
||||
isHydrating: process.client,
|
||||
deferHydration () {
|
||||
if (!nuxtApp.isHydrating) { return () => {} }
|
||||
@ -224,7 +227,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
if (process.server) {
|
||||
async function contextCaller (hooks: HookCallback[], args: any[]) {
|
||||
for (const hook of hooks) {
|
||||
await nuxtAppCtx.callAsync(nuxtApp, () => hook(...args))
|
||||
await nuxtApp.runWithContext(() => hook(...args))
|
||||
}
|
||||
}
|
||||
// Patch callHook to preserve NuxtApp context on server
|
||||
@ -288,7 +291,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
|
||||
export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
if (typeof plugin !== 'function') { return }
|
||||
const { provide } = await callWithNuxt(nuxtApp, plugin, [nuxtApp]) || {}
|
||||
const { provide } = await nuxtApp.runWithContext(() => plugin(nuxtApp)) || {}
|
||||
if (provide && typeof provide === 'object') {
|
||||
for (const key in provide) {
|
||||
nuxtApp.provide(key, provide[key])
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { reactive, ref, shallowReactive, shallowRef } from 'vue'
|
||||
import { definePayloadReviver, getNuxtClientPayload } from '#app/composables/payload'
|
||||
import { createError } from '#app/composables/error'
|
||||
import { callWithNuxt, defineNuxtPlugin } from '#app/nuxt'
|
||||
import { defineNuxtPlugin } from '#app/nuxt'
|
||||
|
||||
const revivers = {
|
||||
NuxtError: (data: any) => createError(data),
|
||||
@ -20,7 +20,7 @@ export default defineNuxtPlugin({
|
||||
for (const reviver in revivers) {
|
||||
definePayloadReviver(reviver, revivers[reviver as keyof typeof revivers])
|
||||
}
|
||||
Object.assign(nuxtApp.payload, await callWithNuxt(nuxtApp, getNuxtClientPayload, []))
|
||||
Object.assign(nuxtApp.payload, await nuxtApp.runWithContext(getNuxtClientPayload))
|
||||
// For backwards compatibility - TODO: remove later
|
||||
window.__NUXT__ = nuxtApp.payload
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { h, isReadonly, reactive } from 'vue'
|
||||
import { isEqual, joinURL, parseQuery, parseURL, stringifyParsedURL, stringifyQuery, withoutBase } from 'ufo'
|
||||
import { createError } from 'h3'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig } from '../nuxt'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '../nuxt'
|
||||
import { clearError, showError } from '../composables/error'
|
||||
import { navigateTo } from '../composables/router'
|
||||
import { useState } from '../composables/state'
|
||||
@ -142,7 +142,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
window.history[replace ? 'replaceState' : 'pushState']({}, '', joinURL(baseURL, to.fullPath))
|
||||
if (!nuxtApp.isHydrating) {
|
||||
// Clear any existing errors
|
||||
await callWithNuxt(nuxtApp, clearError)
|
||||
await nuxtApp.runWithContext(clearError)
|
||||
}
|
||||
}
|
||||
// Run afterEach hooks
|
||||
@ -238,7 +238,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
const middlewareEntries = new Set<RouteGuard>([...globalMiddleware, ...nuxtApp._middleware.global])
|
||||
|
||||
for (const middleware of middlewareEntries) {
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
const result = await nuxtApp.runWithContext(() => middleware(to, from))
|
||||
if (process.server) {
|
||||
if (result === false || result instanceof Error) {
|
||||
const error = result || createError({
|
||||
@ -246,7 +246,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
statusMessage: `Page Not Found: ${initialURL}`
|
||||
})
|
||||
delete nuxtApp._processingMiddleware
|
||||
return callWithNuxt(nuxtApp, showError, [error])
|
||||
return nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
}
|
||||
if (result || result === false) { return result }
|
||||
@ -257,7 +257,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
|
||||
await router.replace(initialURL)
|
||||
if (!isEqual(route.fullPath, initialURL)) {
|
||||
await callWithNuxt(nuxtApp, navigateTo, [route.fullPath])
|
||||
await nuxtApp.runWithContext(() => navigateTo(route.fullPath))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { createError } from 'h3'
|
||||
import { withoutBase } from 'ufo'
|
||||
|
||||
import type { PageMeta, Plugin, RouteMiddleware } from '../../../app/index'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||
import { clearError, showError, useError } from '#app/composables/error'
|
||||
import { useState } from '#app/composables/state'
|
||||
import { navigateTo } from '#app/composables/router'
|
||||
@ -113,7 +113,7 @@ export default defineNuxtPlugin({
|
||||
await router.isReady()
|
||||
} catch (error: any) {
|
||||
// We'll catch 404s here
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
|
||||
const initialLayout = useState('_layout')
|
||||
@ -148,14 +148,14 @@ export default defineNuxtPlugin({
|
||||
throw new Error(`Unknown route middleware: '${entry}'.`)
|
||||
}
|
||||
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
const result = await nuxtApp.runWithContext(() => middleware(to, from))
|
||||
if (process.server || (!nuxtApp.payload.serverRendered && nuxtApp.isHydrating)) {
|
||||
if (result === false || result instanceof Error) {
|
||||
const error = result || createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `Page Not Found: ${initialURL}`
|
||||
})
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -170,19 +170,19 @@ export default defineNuxtPlugin({
|
||||
|
||||
if (process.client && !nuxtApp.isHydrating && error.value) {
|
||||
// Clear any existing errors
|
||||
await callWithNuxt(nuxtApp, clearError)
|
||||
await nuxtApp.runWithContext(clearError)
|
||||
}
|
||||
if (process.server && failure?.type === 4 /* ErrorTypes.NAVIGATION_ABORTED */) {
|
||||
return
|
||||
}
|
||||
if (to.matched.length === 0) {
|
||||
await callWithNuxt(nuxtApp, showError, [createError({
|
||||
await nuxtApp.runWithContext(() => showError(createError({
|
||||
statusCode: 404,
|
||||
fatal: false,
|
||||
statusMessage: `Page not found: ${to.fullPath}`
|
||||
})])
|
||||
})))
|
||||
} else if (process.server && to.redirectedFrom) {
|
||||
await callWithNuxt(nuxtApp, navigateTo, [to.fullPath || '/'])
|
||||
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/'))
|
||||
}
|
||||
})
|
||||
|
||||
@ -195,7 +195,7 @@ export default defineNuxtPlugin({
|
||||
})
|
||||
} catch (error: any) {
|
||||
// We'll catch middleware errors or deliberate exceptions here
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createError, showError } from '#app/composables/error'
|
||||
import { callWithNuxt, useNuxtApp } from '#app/nuxt'
|
||||
import { useNuxtApp } from '#app/nuxt'
|
||||
import { defineNuxtRouteMiddleware, useRouter } from '#app/composables/router'
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
@ -24,7 +24,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
if (final === to) {
|
||||
const unsub = router.afterEach(async () => {
|
||||
unsub()
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
// We pretend to have navigated to the invalid route so
|
||||
// that the user can return to the previous page with
|
||||
// the back button.
|
||||
|
@ -34,7 +34,7 @@ describe.skipIf(isWindows || process.env.TEST_BUILDER === 'webpack' || process.e
|
||||
|
||||
it('default client bundle size', async () => {
|
||||
stats.client = await analyzeSizes('**/*.js', publicDir)
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"94.0k"')
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"94.1k"')
|
||||
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
||||
[
|
||||
"_nuxt/entry.js",
|
||||
@ -45,7 +45,7 @@ describe.skipIf(isWindows || process.env.TEST_BUILDER === 'webpack' || process.e
|
||||
|
||||
it('default server bundle size', async () => {
|
||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"66.6k"')
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"66.7k"')
|
||||
|
||||
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||
expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2654k"')
|
||||
|
6
test/fixtures/basic/types.ts
vendored
6
test/fixtures/basic/types.ts
vendored
@ -96,7 +96,7 @@ describe('middleware', () => {
|
||||
addRouteMiddleware('example', (to, from) => {
|
||||
expectTypeOf(to).toEqualTypeOf<RouteLocationNormalizedLoaded>()
|
||||
expectTypeOf(from).toEqualTypeOf<RouteLocationNormalizedLoaded>()
|
||||
expectTypeOf(navigateTo).toEqualTypeOf <(to: RouteLocationRaw | null | undefined, options ?: NavigateToOptions) => RouteLocationRaw | void | false | Promise<void | NavigationFailure | false>>()
|
||||
expectTypeOf(navigateTo).toEqualTypeOf<(to: RouteLocationRaw | null | undefined, options?: NavigateToOptions) => RouteLocationRaw | void | false | Promise<void | NavigationFailure | false>>()
|
||||
navigateTo('/')
|
||||
abortNavigation()
|
||||
abortNavigation('error string')
|
||||
@ -315,4 +315,8 @@ describe('composables inference', () => {
|
||||
const bob = callWithNuxt({} as any, () => true)
|
||||
expectTypeOf<typeof bob>().toEqualTypeOf<boolean | Promise<boolean>>()
|
||||
})
|
||||
it('runWithContext', () => {
|
||||
const bob = useNuxtApp().runWithContext(() => true)
|
||||
expectTypeOf<typeof bob>().toEqualTypeOf<boolean | Promise<boolean>>()
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user