feat(nuxt): allow enabling route props in definePageMeta (#29586)

This commit is contained in:
xjccc 2024-10-22 20:57:16 +08:00 committed by GitHub
parent eaeda4ee1e
commit d275b382ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 116 additions and 5 deletions

View File

@ -30,6 +30,7 @@ interface PageMeta {
redirect?: RouteRecordRedirectOption
name?: string
path?: string
props?: RouteRecordRaw['props']
alias?: string | string[]
pageTransition?: boolean | TransitionProps
layoutTransition?: boolean | TransitionProps
@ -63,6 +64,12 @@ interface PageMeta {
You may define a [custom regular expression](#using-a-custom-regular-expression) if you have a more complex pattern than can be expressed with the file name.
**`props`**
- **Type**: [`RouteRecordRaw['props']`](https://router.vuejs.org/guide/essentials/passing-props)
Allows accessing the route `params` as props passed to the page component.
**`alias`**
- **Type**: `string | string[]`

View File

@ -1,6 +1,6 @@
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
import { getCurrentInstance } from 'vue'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRaw, RouteRecordRedirectOption } from 'vue-router'
import { useRoute } from 'vue-router'
import type { NitroRouteConfig } from 'nitro/types'
import { useNuxtApp } from '#app/nuxt'
@ -37,6 +37,11 @@ export interface PageMeta {
name?: string
/** You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. */
path?: string
/**
* Allows accessing the route `params` as props passed to the page component.
* @see https://router.vuejs.org/guide/essentials/passing-props
*/
props?: RouteRecordRaw['props']
/** Set to `false` to avoid scrolling to top on page navigations */
scrollToTop?: boolean | ((to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded) => boolean)
}

View File

@ -190,7 +190,7 @@ export function extractScriptContent (html: string) {
}
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
const extractionKeys = ['name', 'path', 'alias', 'redirect'] as const
const extractionKeys = ['name', 'path', 'props', 'alias', 'redirect'] as const
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
const pageContentsCache: Record<string, string> = {}
@ -272,7 +272,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
continue
}
if (property.value.type !== 'Literal' || typeof property.value.value !== 'string') {
if (property.value.type !== 'Literal' || (typeof property.value.value !== 'string' && typeof property.value.value !== 'boolean')) {
console.debug(`[nuxt] Skipping extraction of \`${key}\` metadata as it is not a string literal or array of string literals (reading \`${absolutePath}\`).`)
dynamicProperties.add(key)
continue
@ -539,6 +539,7 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
const metaRoute: NormalizedRoute = {
name: `${metaImportName}?.name ?? ${route.name}`,
path: `${metaImportName}?.path ?? ${route.path}`,
props: `${metaImportName}?.props ?? false`,
meta: `${metaImportName} || {}`,
alias: `${metaImportName}?.alias || []`,
redirect: `${metaImportName}?.redirect`,
@ -582,7 +583,7 @@ async function createClientPage(loader) {
}
// set to extracted value or delete if none extracted
for (const key of ['meta', 'alias', 'redirect'] satisfies NormalizedRouteKeys) {
for (const key of ['meta', 'alias', 'redirect', 'props'] satisfies NormalizedRouteKeys) {
if (markedDynamic.has(key)) { continue }
if (route[key] == null) {

View File

@ -6,6 +6,7 @@
"meta": "{ ...(mockMeta || {}), ...{"someMetaData":true} }",
"name": "mockMeta?.name ?? "pushed-route"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -24,6 +25,18 @@
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
"name": "mockMeta?.name ?? "page-with-meta"",
"path": "mockMeta?.path ?? "/page-with-meta"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
"route.meta props generate by file": [
{
"alias": "mockMeta?.alias || []",
"component": "() => import("pages/page-with-props.vue")",
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "page-with-props"",
"path": "mockMeta?.path ?? "/page-with-props"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -34,6 +47,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "test:name"",
"path": "mockMeta?.path ?? "/test\\:name"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -50,6 +64,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "param-index"",
"path": "mockMeta?.path ?? """,
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -58,6 +73,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "param-index-sibling"",
"path": "mockMeta?.path ?? "sibling"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -65,6 +81,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? undefined",
"path": "mockMeta?.path ?? """,
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -73,6 +90,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "param-sibling"",
"path": "mockMeta?.path ?? "sibling"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -80,6 +98,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? undefined",
"path": "mockMeta?.path ?? "/param"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -91,6 +110,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "wrapper-expose-other"",
"path": "mockMeta?.path ?? """,
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -99,6 +119,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "wrapper-expose-other-sibling"",
"path": "mockMeta?.path ?? "sibling"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -106,6 +127,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? undefined",
"path": "mockMeta?.path ?? "/wrapper-expose/other"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -116,6 +138,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "home"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": ""/"",
},
],
@ -126,6 +149,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "slug"",
"path": "mockMeta?.path ?? "/:slug(.*)*"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -134,6 +158,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -144,6 +169,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -152,6 +178,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "slug"",
"path": "mockMeta?.path ?? "/:slug()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -163,6 +190,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "foo"",
"path": "mockMeta?.path ?? """,
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -170,6 +198,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? undefined",
"path": "mockMeta?.path ?? "/:foo?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -178,6 +207,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "optional-opt"",
"path": "mockMeta?.path ?? "/optional/:opt?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -186,6 +216,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "optional-prefix-opt"",
"path": "mockMeta?.path ?? "/optional/prefix-:opt?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -194,6 +225,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "optional-opt-postfix"",
"path": "mockMeta?.path ?? "/optional/:opt?-postfix"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -202,6 +234,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "optional-prefix-opt-postfix"",
"path": "mockMeta?.path ?? "/optional/prefix-:opt?-postfix"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -210,6 +243,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "bar"",
"path": "mockMeta?.path ?? "/:bar()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -218,6 +252,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "nonopt-slug"",
"path": "mockMeta?.path ?? "/nonopt/:slug()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -226,6 +261,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "opt-slug"",
"path": "mockMeta?.path ?? "/opt/:slug?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -234,6 +270,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "sub-route-slug"",
"path": "mockMeta?.path ?? "/:sub?/route-:slug()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -244,6 +281,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "stories"",
"path": "mockMeta?.path ?? "/:stories(.*)*"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -252,6 +290,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "stories-id"",
"path": "mockMeta?.path ?? "/stories/:id()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -262,6 +301,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "stories-id"",
"path": "mockMeta?.path ?? "/stories/:id()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -270,6 +310,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "stories"",
"path": "mockMeta?.path ?? "/:stories(.*)*"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -280,6 +321,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "kebab-case"",
"path": "mockMeta?.path ?? "/kebab-case"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -290,6 +332,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "snake_case"",
"path": "mockMeta?.path ?? "/snake_case"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -300,6 +343,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -308,6 +352,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent"",
"path": "mockMeta?.path ?? "/parent"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -316,6 +361,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent-child"",
"path": "mockMeta?.path ?? "/parent/child"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -329,6 +375,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent-child"",
"path": "mockMeta?.path ?? "child"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -336,6 +383,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent"",
"path": "mockMeta?.path ?? "/parent"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -346,6 +394,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -357,6 +406,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "about"",
"path": "mockMeta?.path ?? """,
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -364,6 +414,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? undefined",
"path": "mockMeta?.path ?? "/about"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -377,6 +428,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index-index-all"",
"path": "mockMeta?.path ?? "all"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -384,6 +436,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -394,6 +447,7 @@
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
"name": "mockMeta?.name ?? "page-with-meta"",
"path": "mockMeta?.path ?? "/page-with-meta"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -404,6 +458,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent-child"",
"path": "mockMeta?.path ?? "/parent/:child()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -412,6 +467,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "parent-child"",
"path": "mockMeta?.path ?? "/parent-:child()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -422,6 +478,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "foo"",
"path": "mockMeta?.path ?? "/:foo?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -430,6 +487,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "foo"",
"path": "mockMeta?.path ?? "/:foo()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -440,6 +498,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "a1_1a"",
"path": "mockMeta?.path ?? "/:a1_1a()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -448,6 +507,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "b2.2b"",
"path": "mockMeta?.path ?? "/:b2.2b()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -456,6 +516,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "b2_2b"",
"path": "mockMeta?.path ?? "/:b2()_:2b()"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -464,6 +525,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "c33c"",
"path": "mockMeta?.path ?? "/:c33c?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
{
@ -472,6 +534,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "d44d"",
"path": "mockMeta?.path ?? "/:d44d?"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -482,6 +545,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "home"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
@ -492,6 +556,7 @@
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "index"",
"path": "mockMeta?.path ?? "/"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],

View File

@ -24,6 +24,13 @@
"path": ""/page-with-meta"",
},
],
"route.meta props generate by file": [
{
"component": "() => import("pages/page-with-props.vue")",
"name": ""page-with-props"",
"path": ""/page-with-props"",
},
],
"should allow pages with `:` in their path": [
{
"component": "() => import("pages/test:name.vue")",

View File

@ -211,6 +211,7 @@ describe('normalizeRoutes', () => {
{
name: indexN6pT4Un8hYMeta?.name ?? undefined,
path: indexN6pT4Un8hYMeta?.path ?? "/",
props: indexN6pT4Un8hYMeta?.props ?? false,
meta: { ...(indexN6pT4Un8hYMeta || {}), ...{"layout":"test","foo":"bar"} },
alias: indexN6pT4Un8hYMeta?.alias || [],
redirect: indexN6pT4Un8hYMeta?.redirect,

View File

@ -601,6 +601,30 @@ describe('pages:generateRoutesFromFiles', () => {
},
],
},
{
description: 'route.meta props generate by file',
files: [
{
path: `${pagesDir}/page-with-props.vue`,
template: `
<script setup lang="ts">
definePageMeta({
props: true
})
</script>
`,
},
],
output: [
{
name: 'page-with-props',
path: '/page-with-props',
file: `${pagesDir}/page-with-props.vue`,
children: [],
props: true,
},
],
},
{
description: 'should handle route groups',
files: [

View File

@ -8,7 +8,7 @@ import type { Import, InlinePreset, Unimport } from 'unimport'
import type { Compiler, Configuration, Stats } from 'webpack'
import type { Nitro, NitroConfig } from 'nitro/types'
import type { Schema, SchemaDefinition } from 'untyped'
import type { RouteLocationRaw } from 'vue-router'
import type { RouteLocationRaw, RouteRecordRaw } from 'vue-router'
import type { VueCompilerOptions } from '@vue/language-core'
import type { NuxtCompatibility, NuxtCompatibilityIssues, ViteConfig } from '..'
import type { Component, ComponentsOptions } from './components'
@ -28,6 +28,7 @@ export type VueTSConfig = 0 extends 1 & VueCompilerOptions ? TSConfig : TSConfig
export type NuxtPage = {
name?: string
path: string
props?: RouteRecordRaw['props']
file?: string
meta?: Record<string, any>
alias?: string[] | string