Merge branch 'patch-26' of https://github.com/GalacticHypernova/nuxt into patch-26

This commit is contained in:
tbitw2549 2024-10-23 21:10:40 +03:00
commit c6c4f4efc8
30 changed files with 598 additions and 404 deletions

View File

@ -75,7 +75,7 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- name: Initialize CodeQL
uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
config: |
paths:
@ -91,7 +91,7 @@ jobs:
queries: +security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
category: "/language:javascript-typescript"

View File

@ -19,4 +19,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5

View File

@ -19,7 +19,7 @@ jobs:
steps:
# Cache lychee results (e.g. to avoid hitting rate limits)
- name: Restore lychee cache
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}

View File

@ -68,7 +68,7 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
if: github.repository == 'nuxt/nuxt' && success()
with:
sarif_file: results.sarif

View File

@ -67,6 +67,7 @@ export default defineNuxtConfig({
// app: 'app'
// },
// experimental: {
// scanPageMeta: 'after-resolve',
// sharedPrerenderData: false,
// compileTemplate: true,
// resetAsyncDataToUndefined: true,
@ -236,6 +237,45 @@ export default defineNuxtConfig({
})
```
#### Scan Page Meta After Resolution
🚦 **Impact Level**: Minimal
##### What Changed
We now scan page metadata (defined in `definePageMeta`) _after_ calling the `pages:extend` hook rather than before.
##### Reasons for Change
This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook.
##### Migration Steps
If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`.
```diff
export default defineNuxtConfig({
hooks: {
- 'pages:extend'(pages) {
+ 'pages:resolved'(pages) {
const myPage = pages.find(page => page.path === '/')
myPage.meta ||= {}
myPage.meta.layout = 'overridden-layout'
}
}
})
```
Alternatively, you can revert to the previous behaviour with:
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
scanPageMeta: true
}
})
```
#### Shared Prerender Data
🚦 **Impact Level**: Medium

View File

@ -334,6 +334,8 @@ This option allows exposing some route metadata defined in `definePageMeta` at b
This only works with static or strings/arrays rather than variables or conditional assignment. See [original issue](https://github.com/nuxt/nuxt/issues/24770) for more information and context.
It is also possible to scan page metadata only after all routes have been registered in `pages:extend`. Then another hook, `pages:resolved` will be called. To enable this behavior, set `scanPageMeta: 'after-resolve'`.
You can disable this feature if it causes issues in your project.
```ts twoslash [nuxt.config.ts]

View File

@ -61,6 +61,7 @@ export default defineNuxtConfig({
app: 'app'
},
experimental: {
scanPageMeta: 'after-resolve',
sharedPrerenderData: false,
compileTemplate: true,
resetAsyncDataToUndefined: true,

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

@ -40,7 +40,7 @@
"@nuxt/ui-templates": "workspace:*",
"@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*",
"@types/node": "20.16.13",
"@types/node": "20.16.15",
"@vue/compiler-core": "3.5.12",
"@vue/compiler-dom": "3.5.12",
"@vue/shared": "3.5.12",
@ -57,7 +57,7 @@
"typescript": "5.6.3",
"ufo": "1.5.4",
"unbuild": "3.0.0-rc.11",
"vite": "5.4.9",
"vite": "5.4.10",
"vue": "3.5.12"
},
"devDependencies": {
@ -69,7 +69,7 @@
"@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.1.0",
"@types/eslint__js": "8.42.3",
"@types/node": "20.16.13",
"@types/node": "20.16.15",
"@types/semver": "7.5.8",
"@unhead/schema": "1.11.10",
"@unhead/vue": "1.11.10",
@ -92,7 +92,7 @@
"jiti": "2.3.3",
"markdownlint-cli": "0.42.0",
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"nuxi": "3.14.0",
"nuxi": "3.15.0",
"nuxt": "workspace:*",
"nuxt-content-twoslash": "0.1.1",
"ofetch": "1.4.1",

View File

@ -48,12 +48,12 @@
"untyped": "^1.5.1"
},
"devDependencies": {
"@rspack/core": "1.0.13",
"@rspack/core": "1.0.14",
"@types/hash-sum": "1.0.2",
"@types/semver": "7.5.8",
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"unbuild": "3.0.0-rc.11",
"vite": "5.4.9",
"vite": "5.4.10",
"vitest": "2.1.3",
"webpack": "5.95.0"
},

View File

@ -95,7 +95,7 @@
"mlly": "^1.7.2",
"nanotar": "^0.1.1",
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"nuxi": "^3.14.0",
"nuxi": "^3.15.0",
"nypm": "^0.3.12",
"ofetch": "^1.4.1",
"ohash": "^1.1.4",
@ -132,7 +132,7 @@
"@vitejs/plugin-vue": "5.1.4",
"@vue/compiler-sfc": "3.5.12",
"unbuild": "3.0.0-rc.11",
"vite": "5.4.9",
"vite": "5.4.10",
"vitest": "2.1.3"
},
"peerDependencies": {

View File

@ -505,7 +505,7 @@ export default defineNuxtModule({
const { routes, imports } = normalizeRoutes(app.pages, new Set(), {
serverComponentRuntime,
clientComponentRuntime,
overrideMeta: nuxt.options.experimental.scanPageMeta,
overrideMeta: !!nuxt.options.experimental.scanPageMeta,
})
return [...imports, `export default ${routes}`].join('\n')
},

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

@ -64,18 +64,25 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
})
const pages = uniqueBy(allRoutes, 'path')
const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages
if (shouldAugment) {
if (shouldAugment === false) {
await nuxt.callHook('pages:extend', pages)
return pages
}
if (shouldAugment === 'after-resolve') {
await nuxt.callHook('pages:extend', pages)
await augmentPages(pages, nuxt.vfs)
} else {
const augmentedPages = await augmentPages(pages, nuxt.vfs)
await nuxt.callHook('pages:extend', pages)
await augmentPages(pages, nuxt.vfs, augmentedPages)
augmentedPages.clear()
} else {
await nuxt.callHook('pages:extend', pages)
}
await nuxt.callHook('pages:resolved', pages)
return pages
}
@ -184,7 +191,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> = {}
@ -266,7 +273,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
@ -535,6 +542,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`,
@ -578,7 +586,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

@ -31,7 +31,7 @@
"dependencies": {
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
"@nuxt/kit": "workspace:*",
"@rspack/core": "^1.0.13",
"@rspack/core": "^1.0.14",
"autoprefixer": "^10.4.20",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",

View File

@ -53,7 +53,7 @@
"unbuild": "3.0.0-rc.11",
"unctx": "2.3.1",
"unenv": "1.10.0",
"vite": "5.4.9",
"vite": "5.4.10",
"vue": "3.5.12",
"vue-bundle-renderer": "2.1.1",
"vue-loader": "17.4.2",

View File

@ -297,8 +297,13 @@ export default defineUntypedSchema({
* This only works with static or strings/arrays rather than variables or conditional assignment.
*
* @see [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/24770)
* @type {boolean | 'after-resolve'}
*/
scanPageMeta: true,
scanPageMeta: {
async $resolve (val, get) {
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? 'after-resolve' : true)
},
},
/**
* Automatically share payload _data_ between pages that are prerendered. This can result in a significant

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
@ -183,12 +184,19 @@ export interface NuxtHooks {
'builder:watch': (event: WatchEvent, path: string) => HookResult
/**
* Called after pages routes are resolved.
* @param pages Array containing resolved pages
* Called after page routes are scanned from the file system.
* @param pages Array containing scanned pages
* @returns Promise
*/
'pages:extend': (pages: NuxtPage[]) => HookResult
/**
* Called after page routes have been augmented with scanned metadata.
* @param pages Array containing resolved pages
* @returns Promise
*/
'pages:resolved': (pages: NuxtPage[]) => HookResult
/**
* Called when resolving `app/router.options` files. It allows modifying the detected router options files
* and adding new ones.

View File

@ -18,7 +18,7 @@
"test": "pnpm lint && pnpm build"
},
"devDependencies": {
"@unocss/reset": "0.63.4",
"@unocss/reset": "0.63.6",
"critters": "0.0.25",
"html-validate": "8.24.2",
"htmlnano": "2.1.1",
@ -29,7 +29,7 @@
"scule": "1.3.0",
"tinyexec": "0.3.1",
"tinyglobby": "0.2.9",
"unocss": "0.63.4",
"vite": "5.4.9"
"unocss": "0.63.6",
"vite": "5.4.10"
}
}

View File

@ -62,7 +62,7 @@
"ufo": "^1.5.4",
"unenv": "^1.10.0",
"unplugin": "^1.14.1",
"vite": "^5.4.9",
"vite": "^5.4.10",
"vite-node": "^2.1.3",
"vite-plugin-checker": "^0.8.0",
"vue-bundle-renderer": "^2.1.1"

View File

@ -9,13 +9,12 @@ function sortPlugins ({ plugins, order }: NuxtOptions['postcss']): string[] {
}
export async function resolveCSSOptions (nuxt: Nuxt): Promise<ViteConfig['css']> {
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> } = {
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> & { plugins: Plugin[] } } = {
postcss: {
plugins: [],
},
}
css.postcss.plugins = []
const postcssOptions = nuxt.options.postcss
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })

View File

@ -76,7 +76,7 @@
},
"devDependencies": {
"@nuxt/schema": "workspace:*",
"@rspack/core": "1.0.13",
"@rspack/core": "1.0.14",
"@types/hash-sum": "1.0.2",
"@types/lodash-es": "4.17.12",
"@types/pify": "5.0.4",

View File

@ -34,24 +34,26 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
}))
/** Inject rollup plugin for Nitro to handle dynamic imports from webpack chunks */
const nitro = useNitro()
const dynamicRequirePlugin = dynamicRequire({
dir: resolve(nuxt.options.buildDir, 'dist/server'),
inline:
if (!nuxt.options.dev) {
const nitro = useNitro()
const dynamicRequirePlugin = dynamicRequire({
dir: resolve(nuxt.options.buildDir, 'dist/server'),
inline:
nitro.options.node === false || nitro.options.inlineDynamicImports,
ignore: [
'client.manifest.mjs',
'server.js',
'server.cjs',
'server.mjs',
'server.manifest.mjs',
],
})
const prerenderRollupPlugins = nitro.options._config.rollupConfig!.plugins as InputPluginOption[]
const rollupPlugins = nitro.options.rollupConfig!.plugins as InputPluginOption[]
ignore: [
'client.manifest.mjs',
'server.js',
'server.cjs',
'server.mjs',
'server.manifest.mjs',
],
})
const prerenderRollupPlugins = nitro.options._config.rollupConfig!.plugins as InputPluginOption[]
const rollupPlugins = nitro.options.rollupConfig!.plugins as InputPluginOption[]
prerenderRollupPlugins.push(dynamicRequirePlugin)
rollupPlugins.push(dynamicRequirePlugin)
prerenderRollupPlugins.push(dynamicRequirePlugin)
rollupPlugins.push(dynamicRequirePlugin)
}
await nuxt.callHook(`${builder}:config`, webpackConfigs)

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,18 @@ export default defineNuxtModule({
name: 'page-extend',
path: '/page-extend',
file: resolver.resolve('../runtime/page.vue'),
}, {
})
})
nuxt.hook('pages:resolved', (pages) => {
pages.push({
path: '/big-page-1',
file: resolver.resolve('./pages/big-page.vue'),
meta: {
layout: false,
},
}, {
},
{
path: '/big-page-2',
file: resolver.resolve('./pages/big-page.vue'),
meta: {

View File

@ -75,7 +75,7 @@ export default defineNuxtConfig({
_layout: page.meta?.layout,
},
})
nuxt.hook('pages:extend', (pages) => {
nuxt.hook('pages:resolved', (pages) => {
const newPages = []
for (const page of pages) {
if (routesToDuplicate.includes(page.path)) {
@ -88,7 +88,7 @@ export default defineNuxtConfig({
},
function (_options, nuxt) {
// to check that page metadata is preserved
nuxt.hook('pages:extend', (pages) => {
nuxt.hook('pages:resolved', (pages) => {
const customName = pages.find(page => page.name === 'some-custom-name')
if (!customName) { throw new Error('Page with custom name not found') }
if (customName.path !== '/some-custom-path') { throw new Error('Page path not extracted') }