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