opnform/resources/js/router/index.js

244 lines
5.6 KiB
JavaScript
Raw Normal View History

2022-09-20 19:59:52 +00:00
import routes from './routes'
2023-10-14 15:31:30 +00:00
import { createWebHistory, createRouter } from 'vue-router'
2023-10-08 12:35:15 +00:00
import * as Sentry from '@sentry/vue'
import { useAppStore } from '../stores/app'
2023-10-19 13:44:40 +00:00
import { defineComponent, nextTick } from 'vue'
2022-09-20 19:59:52 +00:00
// The middleware for every page of the application.
2023-12-07 11:41:57 +00:00
const globalMiddleware = ['check-auth', 'custom-domains']
2022-09-20 19:59:52 +00:00
// Load middleware modules dynamically.
const requireContext = import.meta.glob('../middleware/**/*.js', { eager: true })
2022-09-20 19:59:52 +00:00
const routeMiddleware = resolveMiddleware(
requireContext
2022-09-20 19:59:52 +00:00
)
2023-10-14 15:31:30 +00:00
const router = createCustomRouter()
2022-09-20 19:59:52 +00:00
export default router
2023-10-14 15:31:30 +00:00
function createCustomRouter () {
const router = createRouter({
2022-09-20 19:59:52 +00:00
scrollBehavior,
2023-10-14 15:31:30 +00:00
history: createWebHistory(),
2022-09-20 19:59:52 +00:00
routes
})
router.beforeEach(beforeEach)
router.afterEach(afterEach)
return router
}
2023-10-14 16:24:44 +00:00
async function getMatchedComponents (to) {
return resolveComponents(to.matched.map((record) => {
const component = record.components.default
return typeof component === 'function' ? defineComponent(component) : component
}))
}
2022-09-20 19:59:52 +00:00
/**
* Global router guard.
*
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
async function beforeEach (to, from, next) {
const appStore = useAppStore()
2023-10-08 12:35:15 +00:00
// Sentry tracking
2023-10-19 08:46:04 +00:00
if (window.config.sentry_dsn) {
2023-10-08 12:35:15 +00:00
Sentry.configureScope((scope) => scope.setTransactionName(to?.name || 'Unknown route name'))
}
2022-09-20 19:59:52 +00:00
let components = []
// External redirect
if (to.matched.some((record) => record.meta.externalUrl)) {
const url = to.meta.externalUrl
window.location.replace(url)
return
}
try {
// Get the matched components and resolve them.
2023-10-14 16:24:44 +00:00
components = await getMatchedComponents(to)
2022-09-20 19:59:52 +00:00
} catch (error) {
if (/^Loading( CSS)? chunk (\d)+ failed\./.test(error.message)) {
window.location.reload(true)
return
}
}
if (components.length === 0) {
return next()
}
// Start the loading bar.
if (components[components.length - 1].loading !== false) {
nextTick(() => appStore.loaderStart())
2022-09-20 19:59:52 +00:00
}
// Get the middleware for all the matched components.
const middleware = getMiddleware(components)
// Call each middleware.
callMiddleware(middleware, to, from, (...args) => {
// Set the application layout only if "next()" was called with no args.
if (args.length === 0) {
2023-01-26 09:52:48 +00:00
if (components[0].layout) {
appStore.setLayout(components[0].layout)
2023-01-26 09:52:48 +00:00
} else if (components[0].default && components[0].default.layout) {
appStore.setLayout(components[0].default.layout)
2023-01-28 11:50:30 +00:00
} else {
appStore.setLayout(null)
2023-01-26 09:52:48 +00:00
}
2022-09-20 19:59:52 +00:00
}
next(...args)
})
}
/**
* Global after hook.
*
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
async function afterEach (to, from, next) {
const appStore = useAppStore()
nextTick(() => appStore.loaderFinish())
2022-09-20 19:59:52 +00:00
}
/**
* Call each middleware.
*
* @param {Array} middleware
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
function callMiddleware (middleware, to, from, next) {
const appStore = useAppStore()
2022-09-20 19:59:52 +00:00
const stack = middleware.reverse()
const _next = (...args) => {
// Stop if "_next" was called with an argument or the stack is empty.
if (args.length > 0 || stack.length === 0) {
2023-10-19 13:44:40 +00:00
if (args.length > 0) {
appStore.loaderFinish()
2023-10-19 13:44:40 +00:00
}
2022-09-20 19:59:52 +00:00
return next(...args)
}
2023-01-27 17:55:02 +00:00
const {
middleware,
params
} = parseMiddleware(stack.pop())
2022-09-20 19:59:52 +00:00
if (typeof middleware === 'function') {
middleware(to, from, _next, params)
} else if (routeMiddleware[middleware]) {
routeMiddleware[middleware](to, from, _next, params)
} else {
throw Error(`Undefined middleware [${middleware}]`)
}
}
_next()
}
/**
* @param {String|Function} middleware
* @return {Object}
*/
function parseMiddleware (middleware) {
if (typeof middleware === 'function') {
return { middleware }
}
const [name, params] = middleware.split(':')
return { middleware: name, params }
}
/**
* Merge the the global middleware with the components middleware.
*
* @param {Array} components
* @return {Array}
*/
function getMiddleware (components) {
const middleware = [...globalMiddleware]
2023-01-26 11:03:29 +00:00
components.forEach(component => {
let compMiddleware
if (component.middleware) {
compMiddleware = component.middleware
} else if (component.default && component.default.middleware) {
compMiddleware = component.default.middleware
}
2023-01-27 17:55:02 +00:00
if (compMiddleware) {
if (Array.isArray(compMiddleware)) {
middleware.push(...compMiddleware)
} else {
middleware.push(compMiddleware)
}
2022-09-20 19:59:52 +00:00
}
})
return middleware
}
/**
* Scroll Behavior
*
* @link https://router.vuejs.org/en/advanced/scroll-behavior.html
*
* @param {Route} to
* @param {Route} from
* @param {Object|undefined} savedPosition
* @return {Object}
*/
function scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
2023-10-16 10:54:51 +00:00
return {}
2022-09-20 19:59:52 +00:00
}
2023-01-27 17:55:02 +00:00
/**
* @param {Object} requireContext
* @return {Object}
*/
function resolveMiddleware (requireContext) {
const middlewares = {}
Object.keys(requireContext)
2022-09-20 19:59:52 +00:00
.map(file =>
[file.match(/[^/]*(?=\.[^.]*$)/)[0], requireContext[file]]
).forEach(([name, middleware]) => {
2023-10-14 15:31:30 +00:00
middlewares[name] = middleware.default || middleware
})
return middlewares
2022-09-20 19:59:52 +00:00
}
2023-10-16 10:54:51 +00:00
/**
* Resolve async components.
*
* @param {Array} components
* @return {Array}
*/
function resolveComponents (components) {
return Promise.all(components.map(component => {
return typeof component === 'function' ? component() : component
}))
}