2021-12-20 10:37:00 +00:00
|
|
|
import { computed, reactive, shallowRef } from 'vue'
|
2021-01-18 12:46:19 +00:00
|
|
|
import {
|
|
|
|
createRouter,
|
|
|
|
createWebHistory,
|
|
|
|
createMemoryHistory,
|
2022-09-04 08:11:28 +00:00
|
|
|
createWebHashHistory,
|
2022-04-05 15:36:18 +00:00
|
|
|
NavigationGuard,
|
|
|
|
RouteLocation
|
2021-01-18 12:46:19 +00:00
|
|
|
} from 'vue-router'
|
2022-02-14 14:18:07 +00:00
|
|
|
import { createError } from 'h3'
|
2022-05-06 16:02:50 +00:00
|
|
|
import { withoutBase, isEqual } from 'ufo'
|
2022-01-25 14:32:09 +00:00
|
|
|
import NuxtPage from './page'
|
2022-08-31 08:02:48 +00:00
|
|
|
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig, showError, clearError, navigateTo, useError, useState } from '#app'
|
2021-04-09 13:48:39 +00:00
|
|
|
// @ts-ignore
|
2022-09-04 07:42:47 +00:00
|
|
|
import _routes from '#build/routes'
|
2022-01-25 12:29:11 +00:00
|
|
|
// @ts-ignore
|
2022-03-15 16:57:41 +00:00
|
|
|
import routerOptions from '#build/router.options'
|
|
|
|
// @ts-ignore
|
2022-01-25 12:29:11 +00:00
|
|
|
import { globalMiddleware, namedMiddleware } from '#build/middleware'
|
2021-01-18 12:46:19 +00:00
|
|
|
|
2022-09-12 13:40:44 +00:00
|
|
|
declare module '@vue/runtime-core' {
|
2021-12-10 13:46:53 +00:00
|
|
|
export interface GlobalComponents {
|
|
|
|
NuxtPage: typeof NuxtPage
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-04 08:11:28 +00:00
|
|
|
// https://github.com/vuejs/router/blob/4a0cc8b9c1e642cdf47cc007fa5bbebde70afc66/packages/router/src/history/html5.ts#L37
|
2022-04-05 15:36:18 +00:00
|
|
|
function createCurrentLocation (
|
|
|
|
base: string,
|
|
|
|
location: Location
|
|
|
|
): string {
|
|
|
|
const { pathname, search, hash } = location
|
|
|
|
// allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
|
|
|
|
const hashPos = base.indexOf('#')
|
|
|
|
if (hashPos > -1) {
|
|
|
|
const slicePos = hash.includes(base.slice(hashPos))
|
|
|
|
? base.slice(hashPos).length
|
|
|
|
: 1
|
|
|
|
let pathFromHash = hash.slice(slicePos)
|
|
|
|
// prepend the starting slash to hash so the url starts with /#
|
|
|
|
if (pathFromHash[0] !== '/') { pathFromHash = '/' + pathFromHash }
|
|
|
|
return withoutBase(pathFromHash, '')
|
|
|
|
}
|
|
|
|
const path = withoutBase(pathname, base)
|
|
|
|
return path + search + hash
|
|
|
|
}
|
|
|
|
|
2022-05-02 10:00:08 +00:00
|
|
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
2022-09-04 08:11:28 +00:00
|
|
|
let routerBase = useRuntimeConfig().app.baseURL
|
|
|
|
if (routerOptions.hashMode && !routerBase.includes('#')) {
|
|
|
|
// allow the user to provide a `#` in the middle: `/base/#/app`
|
|
|
|
routerBase += '#'
|
|
|
|
}
|
2022-09-04 07:42:47 +00:00
|
|
|
|
2022-09-04 08:11:28 +00:00
|
|
|
const history = routerOptions.history?.(routerBase) ?? (process.client
|
|
|
|
? (routerOptions.hashMode ? createWebHashHistory(routerBase) : createWebHistory(routerBase))
|
|
|
|
: createMemoryHistory(routerBase)
|
|
|
|
)
|
2022-09-04 07:42:47 +00:00
|
|
|
|
|
|
|
const routes = routerOptions.routes?.(_routes) ?? _routes
|
2021-01-18 12:46:19 +00:00
|
|
|
|
2022-09-04 08:11:28 +00:00
|
|
|
const initialURL = process.server ? nuxtApp.ssrContext!.url : createCurrentLocation(routerBase, window.location)
|
2021-01-18 12:46:19 +00:00
|
|
|
const router = createRouter({
|
2022-03-15 16:57:41 +00:00
|
|
|
...routerOptions,
|
2022-09-04 07:42:47 +00:00
|
|
|
history,
|
2021-01-18 12:46:19 +00:00
|
|
|
routes
|
|
|
|
})
|
2021-10-18 18:31:37 +00:00
|
|
|
nuxtApp.vueApp.use(router)
|
2021-01-18 12:46:19 +00:00
|
|
|
|
|
|
|
const previousRoute = shallowRef(router.currentRoute.value)
|
|
|
|
router.afterEach((_to, from) => {
|
|
|
|
previousRoute.value = from
|
|
|
|
})
|
|
|
|
|
2021-10-18 18:31:37 +00:00
|
|
|
Object.defineProperty(nuxtApp.vueApp.config.globalProperties, 'previousRoute', {
|
2021-01-18 12:46:19 +00:00
|
|
|
get: () => previousRoute.value
|
|
|
|
})
|
|
|
|
|
2022-04-05 15:36:18 +00:00
|
|
|
// Allows suspending the route object until page navigation completes
|
2022-08-02 09:58:03 +00:00
|
|
|
const _route = shallowRef(router.resolve(initialURL) as RouteLocation)
|
|
|
|
const syncCurrentRoute = () => { _route.value = router.currentRoute.value }
|
2022-04-05 15:36:18 +00:00
|
|
|
nuxtApp.hook('page:finish', syncCurrentRoute)
|
|
|
|
router.afterEach((to, from) => {
|
|
|
|
// We won't trigger suspense if the component is reused between routes
|
|
|
|
// so we need to update the route manually
|
|
|
|
if (to.matched[0]?.components?.default === from.matched[0]?.components?.default) {
|
|
|
|
syncCurrentRoute()
|
|
|
|
}
|
|
|
|
})
|
2022-08-02 09:58:03 +00:00
|
|
|
|
|
|
|
// https://github.com/vuejs/router/blob/main/packages/router/src/router.ts#L1225-L1233
|
2022-08-12 17:47:58 +00:00
|
|
|
const route = {} as RouteLocation
|
2022-08-02 09:58:03 +00:00
|
|
|
for (const key in _route.value) {
|
2022-08-12 17:47:58 +00:00
|
|
|
(route as any)[key] = computed(() => _route.value[key as keyof RouteLocation])
|
2021-12-20 10:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nuxtApp._route = reactive(route)
|
|
|
|
|
2022-01-25 12:29:11 +00:00
|
|
|
nuxtApp._middleware = nuxtApp._middleware || {
|
|
|
|
global: [],
|
|
|
|
named: {}
|
|
|
|
}
|
|
|
|
|
2022-05-06 10:50:54 +00:00
|
|
|
const error = useError()
|
2022-05-02 10:00:08 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
if (process.server) {
|
|
|
|
await router.push(initialURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
await router.isReady()
|
2022-08-12 17:47:58 +00:00
|
|
|
} catch (error: any) {
|
2022-05-02 10:00:08 +00:00
|
|
|
// We'll catch 404s here
|
2022-07-21 14:29:03 +00:00
|
|
|
callWithNuxt(nuxtApp, showError, [error])
|
2022-05-02 10:00:08 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 08:02:48 +00:00
|
|
|
const initialLayout = useState('_layout')
|
2022-01-25 12:29:11 +00:00
|
|
|
router.beforeEach(async (to, from) => {
|
2022-01-26 11:56:24 +00:00
|
|
|
to.meta = reactive(to.meta)
|
2022-08-31 08:02:48 +00:00
|
|
|
if (nuxtApp.isHydrating) {
|
|
|
|
to.meta.layout = initialLayout.value ?? to.meta.layout
|
|
|
|
}
|
2022-01-25 12:29:11 +00:00
|
|
|
nuxtApp._processingMiddleware = true
|
|
|
|
|
|
|
|
type MiddlewareDef = string | NavigationGuard
|
|
|
|
const middlewareEntries = new Set<MiddlewareDef>([...globalMiddleware, ...nuxtApp._middleware.global])
|
|
|
|
for (const component of to.matched) {
|
|
|
|
const componentMiddleware = component.meta.middleware as MiddlewareDef | MiddlewareDef[]
|
|
|
|
if (!componentMiddleware) { continue }
|
|
|
|
if (Array.isArray(componentMiddleware)) {
|
|
|
|
for (const entry of componentMiddleware) {
|
|
|
|
middlewareEntries.add(entry)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
middlewareEntries.add(componentMiddleware)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const entry of middlewareEntries) {
|
2022-08-12 17:47:58 +00:00
|
|
|
const middleware = typeof entry === 'string' ? nuxtApp._middleware.named[entry] || await namedMiddleware[entry]?.().then((r: any) => r.default || r) : entry
|
2022-01-25 12:29:11 +00:00
|
|
|
|
2022-06-10 13:35:58 +00:00
|
|
|
if (!middleware) {
|
|
|
|
if (process.dev) {
|
|
|
|
throw new Error(`Unknown route middleware: '${entry}'. Valid middleware: ${Object.keys(namedMiddleware).map(mw => `'${mw}'`).join(', ')}.`)
|
|
|
|
}
|
|
|
|
throw new Error(`Unknown route middleware: '${entry}'.`)
|
2022-01-25 12:29:11 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:44:22 +00:00
|
|
|
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
2022-08-23 08:42:54 +00:00
|
|
|
if (process.server || (!nuxtApp.payload.serverRendered && nuxtApp.isHydrating)) {
|
2022-02-14 14:18:07 +00:00
|
|
|
if (result === false || result instanceof Error) {
|
2022-03-11 08:22:16 +00:00
|
|
|
const error = result || createError({
|
2022-10-10 10:18:20 +00:00
|
|
|
statusCode: 404,
|
|
|
|
statusMessage: `Page Not Found: ${initialURL}`
|
2022-02-14 14:18:07 +00:00
|
|
|
})
|
2022-11-09 09:08:37 +00:00
|
|
|
await callWithNuxt(nuxtApp, showError, [error])
|
|
|
|
return false
|
2022-02-14 14:18:07 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-25 12:29:11 +00:00
|
|
|
if (result || result === false) { return result }
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-05-02 10:00:08 +00:00
|
|
|
router.afterEach(async (to) => {
|
2022-01-25 12:29:11 +00:00
|
|
|
delete nuxtApp._processingMiddleware
|
2022-03-11 08:22:16 +00:00
|
|
|
|
2022-05-12 09:13:38 +00:00
|
|
|
if (process.client && !nuxtApp.isHydrating && error.value) {
|
|
|
|
// Clear any existing errors
|
|
|
|
await callWithNuxt(nuxtApp, clearError)
|
|
|
|
}
|
|
|
|
if (to.matched.length === 0) {
|
2022-07-21 14:29:03 +00:00
|
|
|
callWithNuxt(nuxtApp, showError, [createError({
|
2022-05-12 09:13:38 +00:00
|
|
|
statusCode: 404,
|
2022-08-08 12:11:50 +00:00
|
|
|
fatal: false,
|
2022-05-12 09:13:38 +00:00
|
|
|
statusMessage: `Page not found: ${to.fullPath}`
|
|
|
|
})])
|
|
|
|
} else if (process.server) {
|
2022-05-05 20:46:54 +00:00
|
|
|
const currentURL = to.fullPath || '/'
|
2022-05-06 16:02:50 +00:00
|
|
|
if (!isEqual(currentURL, initialURL)) {
|
2022-05-05 20:46:54 +00:00
|
|
|
await callWithNuxt(nuxtApp, navigateTo, [currentURL])
|
2022-05-02 10:00:08 +00:00
|
|
|
}
|
2021-01-18 12:46:19 +00:00
|
|
|
}
|
2022-05-02 10:00:08 +00:00
|
|
|
})
|
2021-07-15 09:47:15 +00:00
|
|
|
|
2022-05-02 10:00:08 +00:00
|
|
|
nuxtApp.hooks.hookOnce('app:created', async () => {
|
2022-02-14 14:18:07 +00:00
|
|
|
try {
|
2022-05-02 10:00:08 +00:00
|
|
|
await router.replace({
|
2022-05-03 09:31:58 +00:00
|
|
|
...router.resolve(initialURL),
|
2022-05-20 09:15:57 +00:00
|
|
|
name: undefined, // #4920, #$4982
|
2022-05-02 10:00:08 +00:00
|
|
|
force: true
|
|
|
|
})
|
2022-08-12 17:47:58 +00:00
|
|
|
} catch (error: any) {
|
2022-05-02 10:00:08 +00:00
|
|
|
// We'll catch middleware errors or deliberate exceptions here
|
2022-07-21 14:29:03 +00:00
|
|
|
callWithNuxt(nuxtApp, showError, [error])
|
2021-01-18 12:46:19 +00:00
|
|
|
}
|
|
|
|
})
|
2021-11-18 13:11:34 +00:00
|
|
|
|
|
|
|
return { provide: { router } }
|
2021-06-18 17:16:51 +00:00
|
|
|
})
|