Skip to content

Commit

Permalink
feat(nuxt): add nuxtApp.runWithContext (nuxt#20608)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored May 3, 2023
1 parent a81f9e4 commit da33574
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 29 deletions.
4 changes: 2 additions & 2 deletions packages/nuxt/src/app/components/nuxt-root.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
}
Expand Down
8 changes: 4 additions & 4 deletions packages/nuxt/src/app/composables/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ 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'

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)
}
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions packages/nuxt/src/app/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 () => {} }
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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])
Expand Down
4 changes: 2 additions & 2 deletions packages/nuxt/src/app/plugins/revive-payload.client.ts
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -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
}
Expand Down
10 changes: 5 additions & 5 deletions packages/nuxt/src/app/plugins/router.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -238,15 +238,15 @@ 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({
statusCode: 404,
statusMessage: `Page Not Found: ${initialURL}`
})
delete nuxtApp._processingMiddleware
return callWithNuxt(nuxtApp, showError, [error])
return nuxtApp.runWithContext(() => showError(error))
}
}
if (result || result === false) { return result }
Expand All @@ -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))
}
})

Expand Down
18 changes: 9 additions & 9 deletions packages/nuxt/src/pages/runtime/plugins/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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 || '/'))
}
})

Expand All @@ -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))
}
})

Expand Down
4 changes: 2 additions & 2 deletions packages/nuxt/src/pages/runtime/validate.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions test/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"')
Expand Down
6 changes: 5 additions & 1 deletion test/fixtures/basic/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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>>()
})
})

0 comments on commit da33574

Please sign in to comment.