mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-24 22:55:13 +00:00
feat(nuxt3): add middleware via route meta (#2858)
This commit is contained in:
parent
2d1b772827
commit
dccc0c9c6f
@ -1,4 +1,4 @@
|
||||
**/node_modules
|
||||
|
||||
docs/content/index.md
|
||||
docs/content/**/15.nuxt.config.md
|
||||
docs/content/**/*.nuxt.config.md
|
||||
|
2
docs/.gitignore
vendored
2
docs/.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
schema
|
||||
15.nuxt.config.md
|
||||
*.nuxt.config.md
|
||||
|
@ -32,6 +32,7 @@ Vue Version | 2 | 3 | Yes
|
||||
Assets | ✅ | ✅ | No
|
||||
Components | ✅ | ✅ | No
|
||||
Layouts | ✅ | ✅ | Yes
|
||||
Middleware | ✅ | ✅ | Yes
|
||||
Error Pages | ✅ | 🚧 | Yes
|
||||
Pages | ✅ | ✅ | Yes
|
||||
Pages: Dynamic Params | ✅ | ✅ | Yes
|
||||
|
@ -206,6 +206,10 @@ Of course, you are welcome to define metadata for your own use throughout your a
|
||||
|
||||
You can define the layout used to render the route. This can be either false (to disable any layout), a string or a ref/computed, if you want to make it reactive in some way. [More about layouts](/docs/directory-structure/layouts).
|
||||
|
||||
#### `middleware`
|
||||
|
||||
You can define middleware to apply before loading this page. It will be merged with all the other middleware used in any matching parent/child routes. It can be a string, a function (an anonymous/inlined middleware function following [the global before guard pattern](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards)), or an array of strings/functions. [More about named middleware](/docs/directory-structure/middleware).
|
||||
|
||||
#### `layoutTransition` and `pageTransition`
|
||||
|
||||
You can define transition properties for the `<transition>` components that wraps your pages and layouts, or pass `false` to disable the `<transition>` wrapper for that route. [More about transitions](https://v3.vuejs.org/guide/transitions-overview.html).
|
86
docs/content/3.docs/2.directory-structure/7.middleware.md
Normal file
86
docs/content/3.docs/2.directory-structure/7.middleware.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
icon: IconDirectory
|
||||
title: 'middleware'
|
||||
head.title: Middleware directory
|
||||
---
|
||||
|
||||
# Middleware directory
|
||||
|
||||
Nuxt provides a customizable **route middleware** framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route.
|
||||
|
||||
::alert{type=info}
|
||||
Route middleware run within the Vue part of your Nuxt app. Despite the similar name, they are completely different from server middleware, which are run in the Nitro server part of your app.
|
||||
::
|
||||
|
||||
There are three kinds of route middleware:
|
||||
|
||||
1. Anonymous (or inline) route middleware, which are defined directly in the pages where they are used.
|
||||
2. Named route middleware, which are placed in the `middleware/` directory and will be automatically loaded via asynchronous import when used on a page.
|
||||
3. Global route middleware, which are placed in the `middleware/` directory (with a `.global` suffix) and will be automatically run on every route change.
|
||||
|
||||
The first two kinds of route middleware can be [defined in `definePageMeta`](/docs/directory-structure/pages).
|
||||
|
||||
## Format
|
||||
|
||||
Route middleware are navigation guards that receive the current route and the next route as arguments.
|
||||
|
||||
```js
|
||||
export default defineNuxtRouteMiddleware((to, from) => {
|
||||
if (to.params.id === '1') {
|
||||
return abortNavigation()
|
||||
}
|
||||
return navigateTo('/')
|
||||
})
|
||||
```
|
||||
|
||||
Nuxt provides two globally available helpers that can be returned directly from the middleware:
|
||||
|
||||
1. `navigateTo (route: string | Route)` - Redirects to the given route, within plugins or middleware. It can also be called directly on client side to perform a page navigation.
|
||||
2. `abortNavigation (err?: string | Error)` - Aborts the navigation, with an optional error message.
|
||||
|
||||
Unlike, navigation guards in [the vue-router docs](https://next.router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards), a third `next()` argument is not passed, and redirects or route cancellation is handled by returning a value from the middleware. Possible return values are:
|
||||
|
||||
* nothing - does not block navigation and will move to the next middleware function, if any, or complete the route navigation
|
||||
* `navigateTo('/')` or `navigateTo({ path: '/' })` - redirects to the given path
|
||||
* `abortNavigation()` - stops the current navigation
|
||||
* `abortNavigation(error)` - rejects the current navigation with an error
|
||||
|
||||
::alert{type=warning}
|
||||
It is advised to use the helper functions above for performing redirects or stopping navigation. Other possible return values described in [the vue-router docs](https://next.router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards) may work but there may be breaking changes in future.
|
||||
::
|
||||
|
||||
## Adding middleware dynamically
|
||||
|
||||
It is possible to add global or named route middleware manually using the `addRouteMiddleware()` helper function, such as from within a plugin.
|
||||
|
||||
```ts
|
||||
export default defineNuxtPlugin(() => {
|
||||
addRouteMiddleware('global-test', () => {
|
||||
console.log('this global middleware was added in a plugin and will be run on every route change')
|
||||
}, { global: true })
|
||||
|
||||
addRouteMiddleware('named-test', () => {
|
||||
console.log('this named middleware was added in a plugin and would override any existing middleware of the same name')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Example: a named route middleware
|
||||
|
||||
```bash
|
||||
-| middleware/
|
||||
---| auth.ts
|
||||
```
|
||||
|
||||
In your page file, you can reference this route middleware
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
// or middleware: 'auth'
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
Now, before navigation to that page can complete, the `auth` route middleware will be run.
|
@ -6,7 +6,7 @@ import { upperFirst } from 'scule'
|
||||
export async function main () {
|
||||
const rootDir = resolve(__dirname, '..')
|
||||
const configTemplate = resolve(__dirname, 'nuxt.config.md')
|
||||
const configFile = resolve(rootDir, 'content/3.docs/2.directory-structure/15.nuxt.config.md')
|
||||
const configFile = resolve(rootDir, 'content/3.docs/2.directory-structure/16.nuxt.config.md')
|
||||
await generateDocs({ configFile, configTemplate })
|
||||
}
|
||||
|
||||
|
29
examples/with-middleware/app.vue
Normal file
29
examples/with-middleware/app.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtExampleLayout example="with-middleware">
|
||||
<NuxtPage />
|
||||
|
||||
<template #nav>
|
||||
<nav class="flex align-center gap-4 p-4">
|
||||
<NuxtLink to="/" class="n-link-base">
|
||||
Home
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/forbidden" class="n-link-base">
|
||||
Forbidden
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/redirect" class="n-link-base">
|
||||
Redirect
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="text-center p-4 op-50">
|
||||
Current route: <code>{{ route.path }}</code>
|
||||
</div>
|
||||
</template>
|
||||
</NuxtExampleLayout>
|
||||
</template>
|
3
examples/with-middleware/middleware/always-run.global.ts
Normal file
3
examples/with-middleware/middleware/always-run.global.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default defineNuxtRouteMiddleware(() => {
|
||||
console.log('running global middleware')
|
||||
})
|
8
examples/with-middleware/middleware/redirect-me.ts
Normal file
8
examples/with-middleware/middleware/redirect-me.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
const { $config } = useNuxtApp()
|
||||
if ($config) {
|
||||
console.log('Accessed runtime config within middleware.')
|
||||
}
|
||||
console.log('Heading to', to.path, 'but I think we should go somewhere else...')
|
||||
return '/secret'
|
||||
})
|
7
examples/with-middleware/nuxt.config.ts
Normal file
7
examples/with-middleware/nuxt.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineNuxtConfig } from 'nuxt3'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
]
|
||||
})
|
13
examples/with-middleware/package.json
Normal file
13
examples/with-middleware/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "example-with-middleware",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxi build",
|
||||
"dev": "nuxi dev",
|
||||
"start": "nuxi preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest",
|
||||
"nuxt3": "latest"
|
||||
}
|
||||
}
|
15
examples/with-middleware/pages/forbidden.vue
Normal file
15
examples/with-middleware/pages/forbidden.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
Forbidden
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
// This is an example of inline middleware
|
||||
middleware: () => {
|
||||
console.log('Strictly forbidden.')
|
||||
return false
|
||||
}
|
||||
})
|
||||
</script>
|
5
examples/with-middleware/pages/index.vue
Normal file
5
examples/with-middleware/pages/index.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
Home
|
||||
</div>
|
||||
</template>
|
12
examples/with-middleware/pages/redirect.vue
Normal file
12
examples/with-middleware/pages/redirect.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
You should never see this page
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'redirect-me'
|
||||
})
|
||||
</script>
|
11
examples/with-middleware/pages/secret.vue
Normal file
11
examples/with-middleware/pages/secret.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
You've landed on a page that wasn't in the menu!
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: 'named-test'
|
||||
})
|
||||
</script>
|
9
examples/with-middleware/plugins/add.ts
Normal file
9
examples/with-middleware/plugins/add.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export default defineNuxtPlugin(() => {
|
||||
addRouteMiddleware('global-test', () => {
|
||||
console.log('this global middleware was added in a plugin')
|
||||
}, { global: true })
|
||||
|
||||
addRouteMiddleware('named-test', () => {
|
||||
console.log('this named middleware was added in a plugin')
|
||||
})
|
||||
})
|
3
examples/with-middleware/tsconfig.json
Normal file
3
examples/with-middleware/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
@ -123,7 +123,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 callWithNuxt(nuxtApp, plugin, [nuxtApp]) || {}
|
||||
if (provide && typeof provide === 'object') {
|
||||
for (const key in provide) {
|
||||
nuxtApp.provide(key, provide[key])
|
||||
@ -179,9 +179,9 @@ export const setNuxtAppInstance = (nuxt: NuxtApp | null) => {
|
||||
* @param nuxt A Nuxt instance
|
||||
* @param setup The function to call
|
||||
*/
|
||||
export function callWithNuxt<T extends () => any> (nuxt: NuxtApp, setup: T) {
|
||||
export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtApp, setup: T, args?: Parameters<T>) {
|
||||
setNuxtAppInstance(nuxt)
|
||||
const p: ReturnType<T> = setup()
|
||||
const p: ReturnType<T> = args ? setup(...args as Parameters<T>) : setup()
|
||||
if (process.server) {
|
||||
// Unset nuxt instance to prevent context-sharing in server-side
|
||||
setNuxtAppInstance(null)
|
||||
|
@ -2,7 +2,7 @@ import { existsSync } from 'fs'
|
||||
import { defineNuxtModule, addTemplate, addPlugin, templateUtils, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
|
||||
import { resolve } from 'pathe'
|
||||
import { distDir } from '../dirs'
|
||||
import { resolveLayouts, resolvePagesRoutes, normalizeRoutes } from './utils'
|
||||
import { resolveLayouts, resolvePagesRoutes, normalizeRoutes, resolveMiddleware, getImportName } from './utils'
|
||||
import { TransformMacroPlugin, TransformMacroPluginOptions } from './macros'
|
||||
|
||||
export default defineNuxtModule({
|
||||
@ -40,9 +40,18 @@ export default defineNuxtModule({
|
||||
|
||||
nuxt.hook('autoImports:extend', (autoImports) => {
|
||||
const composablesFile = resolve(runtimeDir, 'composables')
|
||||
autoImports.push({ name: 'useRouter', as: 'useRouter', from: composablesFile })
|
||||
autoImports.push({ name: 'useRoute', as: 'useRoute', from: composablesFile })
|
||||
autoImports.push({ name: 'definePageMeta', as: 'definePageMeta', from: composablesFile })
|
||||
const composables = [
|
||||
'useRouter',
|
||||
'useRoute',
|
||||
'defineNuxtRouteMiddleware',
|
||||
'definePageMeta',
|
||||
'navigateTo',
|
||||
'abortNavigation',
|
||||
'addRouteMiddleware'
|
||||
]
|
||||
for (const composable of composables) {
|
||||
autoImports.push({ name: composable, as: composable, from: composablesFile })
|
||||
}
|
||||
})
|
||||
|
||||
// Extract macros from pages
|
||||
@ -69,6 +78,44 @@ export default defineNuxtModule({
|
||||
}
|
||||
})
|
||||
|
||||
// Add middleware template
|
||||
addTemplate({
|
||||
filename: 'middleware.mjs',
|
||||
async getContents () {
|
||||
const middleware = await resolveMiddleware()
|
||||
await nuxt.callHook('pages:middleware:extend', middleware)
|
||||
const middlewareObject = Object.fromEntries(middleware.map(mw => [mw.name, `{() => import('${mw.path}')}`]))
|
||||
const globalMiddleware = middleware.filter(mw => mw.global)
|
||||
return [
|
||||
...globalMiddleware.map(mw => `import ${getImportName(mw.name)} from '${mw.path}'`),
|
||||
`export const globalMiddleware = [${globalMiddleware.map(mw => getImportName(mw.name)).join(', ')}]`,
|
||||
`export const namedMiddleware = ${templateUtils.serialize(middlewareObject)}`
|
||||
].join('\n')
|
||||
}
|
||||
})
|
||||
|
||||
addTemplate({
|
||||
filename: 'middleware.d.ts',
|
||||
write: true,
|
||||
getContents: async () => {
|
||||
const composablesFile = resolve(runtimeDir, 'composables')
|
||||
const middleware = await resolveMiddleware()
|
||||
return [
|
||||
'import type { NavigationGuard } from \'vue-router\'',
|
||||
`export type MiddlewareKey = ${middleware.map(mw => `"${mw.name}"`).join(' | ') || 'string'}`,
|
||||
`declare module '${composablesFile}' {`,
|
||||
' interface PageMeta {',
|
||||
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>',
|
||||
' }',
|
||||
'}'
|
||||
].join('\n')
|
||||
}
|
||||
})
|
||||
|
||||
nuxt.hook('prepare:types', ({ references }) => {
|
||||
references.push({ path: resolve(nuxt.options.buildDir, 'middleware.d.ts') })
|
||||
})
|
||||
|
||||
// Add layouts template
|
||||
addTemplate({
|
||||
filename: 'layouts.mjs',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ComputedRef, /* KeepAliveProps, */ Ref, TransitionProps } from 'vue'
|
||||
import type { Router, RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
|
||||
import { useNuxtApp } from '#app'
|
||||
|
||||
export const useRouter = () => {
|
||||
@ -37,3 +37,50 @@ export const definePageMeta = (meta: PageMeta): void => {
|
||||
warnRuntimeUsage('definePageMeta')
|
||||
}
|
||||
}
|
||||
|
||||
export interface RouteMiddleware {
|
||||
(to: RouteLocationNormalized, from: RouteLocationNormalized): ReturnType<NavigationGuard>
|
||||
}
|
||||
|
||||
export const defineNuxtRouteMiddleware = (middleware: RouteMiddleware) => middleware
|
||||
|
||||
export interface AddRouteMiddlewareOptions {
|
||||
global?: boolean
|
||||
}
|
||||
|
||||
export const addRouteMiddleware = (name: string, middleware: RouteMiddleware, options: AddRouteMiddlewareOptions = {}) => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (options.global) {
|
||||
nuxtApp._middleware.global.push(middleware)
|
||||
} else {
|
||||
nuxtApp._middleware.named[name] = middleware
|
||||
}
|
||||
}
|
||||
|
||||
const isProcessingMiddleware = () => {
|
||||
try {
|
||||
if (useNuxtApp()._processingMiddleware) {
|
||||
return true
|
||||
}
|
||||
} catch {}
|
||||
return false
|
||||
}
|
||||
|
||||
export const navigateTo = (to: RouteLocationRaw) => {
|
||||
if (isProcessingMiddleware()) {
|
||||
return to
|
||||
}
|
||||
const router: Router = process.server ? useRouter() : (window as any).$nuxt.router
|
||||
return router.push(to)
|
||||
}
|
||||
|
||||
/** This will abort navigation within a Nuxt route middleware handler. */
|
||||
export const abortNavigation = (err?: Error | string) => {
|
||||
if (process.dev && !isProcessingMiddleware()) {
|
||||
throw new Error('abortNavigation() is only usable inside a route middleware handler.')
|
||||
}
|
||||
if (err) {
|
||||
throw err instanceof Error ? err : new Error(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -3,14 +3,17 @@ import {
|
||||
createRouter,
|
||||
createWebHistory,
|
||||
createMemoryHistory,
|
||||
RouterLink
|
||||
RouterLink,
|
||||
NavigationGuard
|
||||
} from 'vue-router'
|
||||
import NuxtNestedPage from './nested-page.vue'
|
||||
import NuxtPage from './page.vue'
|
||||
import NuxtLayout from './layout'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '#app'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig } from '#app'
|
||||
// @ts-ignore
|
||||
import routes from '#build/routes'
|
||||
// @ts-ignore
|
||||
import { globalMiddleware, namedMiddleware } from '#build/middleware'
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
@ -57,9 +60,53 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
|
||||
nuxtApp._route = reactive(route)
|
||||
|
||||
nuxtApp._middleware = nuxtApp._middleware || {
|
||||
global: [],
|
||||
named: {}
|
||||
}
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
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) {
|
||||
const middleware = typeof entry === 'string' ? nuxtApp._middleware.named[entry] || await namedMiddleware[entry]?.().then(r => r.default || r) : entry
|
||||
|
||||
if (process.dev && !middleware) {
|
||||
console.warn(`Unknown middleware: ${entry}. Valid options are ${Object.keys(namedMiddleware).join(', ')}.`)
|
||||
}
|
||||
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
if (result || result === false) { return result }
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(() => {
|
||||
delete nuxtApp._processingMiddleware
|
||||
})
|
||||
|
||||
nuxtApp.hook('app:created', async () => {
|
||||
if (process.server) {
|
||||
router.push(nuxtApp.ssrContext.url)
|
||||
|
||||
router.afterEach((to) => {
|
||||
if (to.fullPath !== nuxtApp.ssrContext.url) {
|
||||
nuxtApp.ssrContext.res.setHeader('Location', to.fullPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
await router.isReady()
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { basename, extname, normalize, relative, resolve } from 'pathe'
|
||||
import { encodePath } from 'ufo'
|
||||
import type { Nuxt, NuxtPage } from '@nuxt/schema'
|
||||
import { resolveFiles } from '@nuxt/kit'
|
||||
import type { Nuxt, NuxtMiddleware, NuxtPage } from '@nuxt/schema'
|
||||
import { resolveFiles, useNuxt } from '@nuxt/kit'
|
||||
import { kebabCase, pascalCase } from 'scule'
|
||||
|
||||
enum SegmentParserState {
|
||||
@ -219,10 +219,7 @@ export async function resolveLayouts (nuxt: Nuxt) {
|
||||
const layoutDir = resolve(nuxt.options.srcDir, nuxt.options.dir.layouts)
|
||||
const files = await resolveFiles(layoutDir, `*{${nuxt.options.extensions.join(',')}}`)
|
||||
|
||||
return files.map((file) => {
|
||||
const name = kebabCase(basename(file).replace(extname(file), '')).replace(/["']/g, '')
|
||||
return { name, file }
|
||||
})
|
||||
return files.map(file => ({ name: getNameFromPath(file), file }))
|
||||
}
|
||||
|
||||
export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> = new Set()): { imports: Set<string>, routes: NuxtPage[]} {
|
||||
@ -230,7 +227,7 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
||||
imports: metaImports,
|
||||
routes: routes.map((route) => {
|
||||
const file = normalize(route.file)
|
||||
const metaImportName = `${pascalCase(file.replace(/[^\w]/g, ''))}Meta`
|
||||
const metaImportName = getImportName(file) + 'Meta'
|
||||
metaImports.add(`import { meta as ${metaImportName} } from '${file}?macro=true'`)
|
||||
return {
|
||||
...route,
|
||||
@ -241,3 +238,22 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveMiddleware (): Promise<NuxtMiddleware[]> {
|
||||
const nuxt = useNuxt()
|
||||
const middlewareDir = resolve(nuxt.options.srcDir, nuxt.options.dir.middleware)
|
||||
const files = await resolveFiles(middlewareDir, `*{${nuxt.options.extensions.join(',')}}`)
|
||||
return files.map(path => ({ name: getNameFromPath(path), path, global: hasSuffix(path, '.global') }))
|
||||
}
|
||||
|
||||
function getNameFromPath (path: string) {
|
||||
return kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '').replace('.global', '')
|
||||
}
|
||||
|
||||
function hasSuffix (path: string, suffix: string) {
|
||||
return basename(path).replace(extname(path), '').endsWith(suffix)
|
||||
}
|
||||
|
||||
export function getImportName (name: string) {
|
||||
return pascalCase(name).replace(/[^\w]/g, '')
|
||||
}
|
||||
|
@ -41,6 +41,12 @@ export type NuxtPage = {
|
||||
children?: NuxtPage[]
|
||||
}
|
||||
|
||||
export type NuxtMiddleware = {
|
||||
name: string
|
||||
path: string
|
||||
global?: boolean
|
||||
}
|
||||
|
||||
export interface NuxtHooks {
|
||||
// Kit
|
||||
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
|
||||
@ -51,6 +57,7 @@ export interface NuxtHooks {
|
||||
'app:templatesGenerated': (app: NuxtApp) => HookResult
|
||||
'builder:generateApp': () => HookResult
|
||||
'pages:extend': (pages: NuxtPage[]) => HookResult
|
||||
'pages:middleware:extend': (middleware: NuxtMiddleware[]) => HookResult
|
||||
|
||||
// Auto imports
|
||||
'autoImports:sources': (autoImportSources: AutoImportSource[]) => HookResult
|
||||
|
@ -11048,6 +11048,15 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-with-middleware@workspace:examples/with-middleware":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-with-middleware@workspace:examples/with-middleware"
|
||||
dependencies:
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||
nuxt3: latest
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-with-pages@workspace:examples/with-pages":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-with-pages@workspace:examples/with-pages"
|
||||
|
Loading…
Reference in New Issue
Block a user