Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 28 additions & 29 deletions packages/nuxt/src/app/plugins/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ interface Router {
}

export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
const initialURL = process.client ? window.location.href : nuxtApp.ssrContext.url
const routes = []

const hooks: { [key in keyof RouterHooks]: RouterHooks[key][] } = {
Expand All @@ -100,7 +101,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
return () => hooks[hook].splice(hooks[hook].indexOf(guard), 1)
}

const route: Route = reactive(getRouteFromPath(process.client ? window.location.href : nuxtApp.ssrContext.url))
const route: Route = reactive(getRouteFromPath(initialURL))
async function handleNavigation (url: string, replace?: boolean): Promise<void> {
try {
// Resolve route
Expand Down Expand Up @@ -193,38 +194,36 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
named: {}
}

router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta || {})
nuxtApp._processingMiddleware = true

const middlewareEntries = new Set<RouteGuard>(nuxtApp._middleware.global)

for (const middleware of middlewareEntries) {
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
if (process.server) {
if (result === false || result instanceof Error) {
const error = result || createError({
statusMessage: `Route navigation aborted: ${nuxtApp.ssrContext.url}`
})
return callWithNuxt(nuxtApp, throwError, [error])
nuxtApp.hooks.hookOnce('app:created', async () => {
router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta || {})
nuxtApp._processingMiddleware = true

const middlewareEntries = new Set<RouteGuard>(nuxtApp._middleware.global)

for (const middleware of middlewareEntries) {
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
if (process.server) {
if (result === false || result instanceof Error) {
const error = result || createError({
statusMessage: `Route navigation aborted: ${initialURL}`
})
return callWithNuxt(nuxtApp, throwError, [error])
}
}
if (result || result === false) { return result }
}
if (result || result === false) { return result }
}
})

router.afterEach(() => {
delete nuxtApp._processingMiddleware
})
})

if (process.server) {
nuxtApp.hooks.hookOnce('app:created', async () => {
await router.push(nuxtApp.ssrContext.url)
if (route.fullPath !== nuxtApp.ssrContext.url) {
await navigateTo(route.fullPath)
}
router.afterEach(() => {
delete nuxtApp._processingMiddleware
})
}

await router.replace(initialURL)
if (route.fullPath !== initialURL) {
await callWithNuxt(nuxtApp, navigateTo, [route.fullPath])
}
})

return {
provide: {
Expand Down
65 changes: 37 additions & 28 deletions packages/nuxt/src/pages/runtime/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function createCurrentLocation (
return path + search + hash
}

export default defineNuxtPlugin((nuxtApp) => {
export default defineNuxtPlugin(async (nuxtApp) => {
nuxtApp.vueApp.component('NuxtPage', NuxtPage)
// TODO: remove before release - present for backwards compatibility & intentionally undocumented
nuxtApp.vueApp.component('NuxtNestedPage', NuxtPage)
Expand All @@ -59,6 +59,7 @@ export default defineNuxtPlugin((nuxtApp) => {
? createWebHistory(baseURL)
: createMemoryHistory(baseURL)

const initialURL = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location)
const router = createRouter({
...routerOptions,
history: routerHistory,
Expand All @@ -82,8 +83,7 @@ export default defineNuxtPlugin((nuxtApp) => {
}

// Allows suspending the route object until page navigation completes
const path = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location)
const _activeRoute = shallowRef(router.resolve(path) as RouteLocation)
const _activeRoute = shallowRef(router.resolve(initialURL) as RouteLocation)
const syncCurrentRoute = () => { _activeRoute.value = router.currentRoute.value }
nuxtApp.hook('page:finish', syncCurrentRoute)
router.afterEach((to, from) => {
Expand All @@ -107,6 +107,28 @@ export default defineNuxtPlugin((nuxtApp) => {
named: {}
}

router.afterEach((to) => {
if (to.matched.length === 0) {
callWithNuxt(nuxtApp, throwError, [createError({
statusCode: 404,
statusMessage: `Page not found: ${to.fullPath}`
})])
} else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) {
nuxtApp.ssrContext.res.statusCode = 404
}
})

try {
if (process.server) {
await router.push(initialURL)
}

await router.isReady()
} catch (error) {
// We'll catch 404s here
callWithNuxt(nuxtApp, throwError, [error])
}

router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta)
nuxtApp._processingMiddleware = true
Expand Down Expand Up @@ -141,7 +163,7 @@ export default defineNuxtPlugin((nuxtApp) => {
if (process.server) {
if (result === false || result instanceof Error) {
const error = result || createError({
statusMessage: `Route navigation aborted: ${nuxtApp.ssrContext.url}`
statusMessage: `Route navigation aborted: ${initialURL}`
})
return callWithNuxt(nuxtApp, throwError, [error])
}
Expand All @@ -150,37 +172,24 @@ export default defineNuxtPlugin((nuxtApp) => {
}
})

router.afterEach(() => {
router.afterEach(async (to) => {
delete nuxtApp._processingMiddleware
})

nuxtApp.hook('app:created', async () => {
router.afterEach((to) => {
if (to.matched.length === 0) {
callWithNuxt(nuxtApp, throwError, [createError({
statusCode: 404,
statusMessage: `Page not found: ${to.fullPath}`
})])
} else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) {
nuxtApp.ssrContext.res.statusCode = 404
}
})

if (process.server) {
router.afterEach(async (to) => {
if (to.fullPath !== nuxtApp.ssrContext.url) {
await navigateTo(to.fullPath)
}
})
if (to.fullPath !== initialURL) {
await callWithNuxt(nuxtApp, navigateTo, [to.fullPath])
}
}
})

nuxtApp.hooks.hookOnce('app:created', async () => {
try {
if (process.server) {
await router.push(nuxtApp.ssrContext.url)
}

await router.isReady()
await router.replace({
path: initialURL,
force: true
})
} catch (error) {
// We'll catch middleware errors or deliberate exceptions here
callWithNuxt(nuxtApp, throwError, [error])
}
})
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/basic/middleware/redirect.global.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export default defineNuxtRouteMiddleware(async (to) => {
const nuxtApp = useNuxtApp()
if (to.path.startsWith('/redirect/')) {
await new Promise(resolve => setTimeout(resolve, 100))
return navigateTo(to.path.slice('/redirect/'.length - 1))
}
const pluginPath = nuxtApp.$path()
if (process.server && !/redirect|navigate/.test(pluginPath) && to.path !== pluginPath) {
throw new Error('plugin did not run before middleware')
}
})
4 changes: 3 additions & 1 deletion test/fixtures/basic/plugins/my-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ export default defineNuxtPlugin(() => {
useHead({
titleTemplate: '%s - Fixture'
})
const path = useRoute().path
return {
provide: {
myPlugin: () => 'Injected by my-plugin'
myPlugin: () => 'Injected by my-plugin',
path: () => path
}
}
})