UNPKG

nuxt

Version:

[![Nuxt banner](./.github/assets/banner.png)](https://nuxt.com)

178 lines (177 loc) 6.86 kB
import { computed, isReadonly, reactive, shallowRef } from "vue"; import { START_LOCATION, createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from "#vue-router"; import { createError } from "h3"; import { withoutBase } from "ufo"; 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"; import _routes from "#build/routes"; import routerOptions from "#build/router.options"; import { globalMiddleware, namedMiddleware } from "#build/middleware"; function createCurrentLocation(base, location, renderedPath) { const { pathname, search, hash } = location; const hashPos = base.indexOf("#"); if (hashPos > -1) { const slicePos = hash.includes(base.slice(hashPos)) ? base.slice(hashPos).length : 1; let pathFromHash = hash.slice(slicePos); if (pathFromHash[0] !== "/") { pathFromHash = "/" + pathFromHash; } return withoutBase(pathFromHash, ""); } const path = renderedPath || withoutBase(pathname, base); return path + (path.includes("?") ? "" : search) + hash; } const plugin = defineNuxtPlugin({ name: "nuxt:router", enforce: "pre", async setup(nuxtApp) { let routerBase = useRuntimeConfig().app.baseURL; if (routerOptions.hashMode && !routerBase.includes("#")) { routerBase += "#"; } const history = routerOptions.history?.(routerBase) ?? (process.client ? routerOptions.hashMode ? createWebHashHistory(routerBase) : createWebHistory(routerBase) : createMemoryHistory(routerBase)); const routes = routerOptions.routes?.(_routes) ?? _routes; let startPosition; const initialURL = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(routerBase, window.location, nuxtApp.payload.path); const router = createRouter({ ...routerOptions, scrollBehavior: (to, from, savedPosition) => { if (from === START_LOCATION) { startPosition = savedPosition; return; } router.options.scrollBehavior = routerOptions.scrollBehavior; return routerOptions.scrollBehavior?.(to, START_LOCATION, startPosition || savedPosition); }, history, routes }); nuxtApp.vueApp.use(router); const previousRoute = shallowRef(router.currentRoute.value); router.afterEach((_to, from) => { previousRoute.value = from; }); Object.defineProperty(nuxtApp.vueApp.config.globalProperties, "previousRoute", { get: () => previousRoute.value }); const _route = shallowRef(router.resolve(initialURL)); const syncCurrentRoute = () => { _route.value = router.currentRoute.value; }; nuxtApp.hook("page:finish", syncCurrentRoute); router.afterEach((to, from) => { if (to.matched[0]?.components?.default === from.matched[0]?.components?.default) { syncCurrentRoute(); } }); const route = {}; for (const key in _route.value) { route[key] = computed(() => _route.value[key]); } nuxtApp._route = reactive(route); nuxtApp._middleware = nuxtApp._middleware || { global: [], named: {} }; const error = useError(); try { if (process.server) { await router.push(initialURL); } await router.isReady(); } catch (error2) { await nuxtApp.runWithContext(() => showError(error2)); } const initialLayout = useState("_layout"); router.beforeEach(async (to, from) => { to.meta = reactive(to.meta); if (nuxtApp.isHydrating && initialLayout.value && !isReadonly(to.meta.layout)) { to.meta.layout = initialLayout.value; } nuxtApp._processingMiddleware = true; if (process.client || !nuxtApp.ssrContext?.islandContext) { const middlewareEntries = /* @__PURE__ */ new Set([...globalMiddleware, ...nuxtApp._middleware.global]); for (const component of to.matched) { const componentMiddleware = component.meta.middleware; if (!componentMiddleware) { continue; } if (Array.isArray(componentMiddleware)) { for (const entry of componentMiddleware) { middlewareEntries.add(entry); } } else { middlewareEntries.add(componentMiddleware); } } for (const entry of middlewareEntries) { const middleware = typeof entry === "string" ? nuxtApp._middleware.named[entry] || await namedMiddleware[entry]?.().then((r) => r.default || r) : entry; 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}'.`); } const result = await nuxtApp.runWithContext(() => middleware(to, from)); if (process.server || !nuxtApp.payload.serverRendered && nuxtApp.isHydrating) { if (result === false || result instanceof Error) { const error2 = result || createError({ statusCode: 404, statusMessage: `Page Not Found: ${initialURL}` }); await nuxtApp.runWithContext(() => showError(error2)); return false; } } if (result || result === false) { return result; } } } }); router.onError(() => { delete nuxtApp._processingMiddleware; }); router.afterEach(async (to, _from, failure) => { delete nuxtApp._processingMiddleware; if (process.client && !nuxtApp.isHydrating && error.value) { await nuxtApp.runWithContext(clearError); } if (process.server && failure?.type === 4) { return; } if (to.matched.length === 0 && (!process.server || !nuxtApp.ssrContext?.islandContext)) { await nuxtApp.runWithContext(() => showError(createError({ statusCode: 404, fatal: false, statusMessage: `Page not found: ${to.fullPath}` }))); } else if (process.server && to.redirectedFrom) { await nuxtApp.runWithContext(() => navigateTo(to.fullPath || "/")); } }); nuxtApp.hooks.hookOnce("app:created", async () => { try { await router.replace({ ...router.resolve(initialURL), name: void 0, // #4920, #4982 force: true }); router.options.scrollBehavior = routerOptions.scrollBehavior; } catch (error2) { await nuxtApp.runWithContext(() => showError(error2)); } }); return { provide: { router } }; } }); export default plugin;