diff --git a/docs/content/2.guide/3.directory-structure/10.pages.md b/docs/content/2.guide/3.directory-structure/10.pages.md index 9bf36c07f3..59a7a72a00 100644 --- a/docs/content/2.guide/3.directory-structure/10.pages.md +++ b/docs/content/2.guide/3.directory-structure/10.pages.md @@ -267,6 +267,8 @@ Of course, you are welcome to define metadata for your own use throughout your a Nuxt will automatically wrap your page in [the Vue `` component](https://vuejs.org/guide/built-ins/keep-alive.html#keepalive) if you set `keepalive: true` in your `definePageMeta`. This might be useful to do, for example, in a parent route that has dynamic child routes, if you want to preserve page state across route changes. You can also set props to be passed to `` (see a full list [here](https://vuejs.org/api/built-in-components.html#keepalive)). +You can set a default value for this property [in your `nuxt.config`](/api/configuration/nuxt.config#keepalive). + #### `key` [See above](#child-route-keys). @@ -283,6 +285,8 @@ You can define middleware to apply before loading this page. It will be merged w You can define transition properties for the `` component that wraps your pages and layouts, or pass `false` to disable the `` wrapper for that route. You can see a list of options that can be passed [here](https://vuejs.org/api/built-in-components.html#transition) or read [more about how transitions work](https://vuejs.org/guide/built-ins/transition.html#transition). +You can set default values for these properties [in your `nuxt.config`](/api/configuration/nuxt.config#layouttransition). + #### `alias` You can define page aliases. They allow you to access the same page from different paths. It can be either a string or an array of strings as defined [here](https://router.vuejs.org/guide/essentials/redirect-and-alias.html#alias) on vue-router documentation. diff --git a/packages/nuxt/src/app/components/layout.ts b/packages/nuxt/src/app/components/layout.ts index c578a6e9a2..c1ccc6a607 100644 --- a/packages/nuxt/src/app/components/layout.ts +++ b/packages/nuxt/src/app/components/layout.ts @@ -3,8 +3,8 @@ import { _wrapIf } from './utils' import { useRoute } from '#app' // @ts-ignore import layouts from '#build/layouts' - -const defaultLayoutTransition = { name: 'layout', mode: 'out-in' } +// @ts-ignore +import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs' export default defineComponent({ props: { diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 3fa41de712..cc5dc9055d 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -5,6 +5,7 @@ import { isAbsolute, join, relative } from 'pathe' import { resolveSchema, generateTypes } from 'untyped' import escapeRE from 'escape-string-regexp' import { hash } from 'ohash' +import { camelCase } from 'scule' export interface TemplateContext { nuxt: Nuxt @@ -242,3 +243,11 @@ export const publicPathTemplate: NuxtTemplate = { ].filter(Boolean).join('\n') } } + +// Allow direct access to specific exposed nuxt.config +export const nuxtConfigTemplate = { + filename: 'nuxt.config.mjs', + getContents: (ctx: TemplateContext) => { + return Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`).join('\n\n') + } +} diff --git a/packages/nuxt/src/head/module.ts b/packages/nuxt/src/head/module.ts index 44a58b588c..68c5cfec26 100644 --- a/packages/nuxt/src/head/module.ts +++ b/packages/nuxt/src/head/module.ts @@ -1,5 +1,5 @@ import { resolve } from 'pathe' -import { addPlugin, addTemplate, defineNuxtModule } from '@nuxt/kit' +import { addPlugin, defineNuxtModule } from '@nuxt/kit' import { distDir } from '../dirs' export default defineNuxtModule({ @@ -15,12 +15,6 @@ export default defineNuxtModule({ // Add #head alias nuxt.options.alias['#head'] = runtimeDir - // Add global meta configuration - addTemplate({ - filename: 'meta.config.mjs', - getContents: () => 'export default ' + JSON.stringify({ globalMeta: nuxt.options.app.head }) - }) - // Add generic plugin addPlugin({ src: resolve(runtimeDir, 'plugin') }) diff --git a/packages/nuxt/src/head/runtime/plugin.ts b/packages/nuxt/src/head/runtime/plugin.ts index f9713dad13..7d945fc735 100644 --- a/packages/nuxt/src/head/runtime/plugin.ts +++ b/packages/nuxt/src/head/runtime/plugin.ts @@ -3,11 +3,11 @@ import * as Components from './components' import { useHead } from './composables' import { defineNuxtPlugin, useNuxtApp } from '#app' // @ts-ignore -import metaConfig from '#build/meta.config.mjs' +import { appHead } from '#build/nuxt.config.mjs' type MetaComponents = typeof Components declare module 'vue' { - export interface GlobalComponents extends MetaComponents {} + export interface GlobalComponents extends MetaComponents { } } const metaMixin = { @@ -28,7 +28,7 @@ const metaMixin = { } export default defineNuxtPlugin((nuxtApp) => { - useHead(markRaw({ title: '', ...metaConfig.globalMeta })) + useHead(markRaw({ title: '', ...appHead })) nuxtApp.vueApp.mixin(metaMixin) diff --git a/packages/nuxt/src/pages/runtime/page.ts b/packages/nuxt/src/pages/runtime/page.ts index 9d394a2531..e13d9c6e61 100644 --- a/packages/nuxt/src/pages/runtime/page.ts +++ b/packages/nuxt/src/pages/runtime/page.ts @@ -6,6 +6,8 @@ import type { RouteLocation } from 'vue-router' import { generateRouteKey, RouterViewSlotProps, wrapInKeepAlive } from './utils' import { useNuxtApp } from '#app' import { _wrapIf } from '#app/components/utils' +// @ts-ignore +import { appPageTransition as defaultPageTransition, appKeepalive as defaultKeepaliveConfig } from '#build/nuxt.config.mjs' const isNestedKey = Symbol('isNested') @@ -39,8 +41,8 @@ export default defineComponent({ const transitionProps = routeProps.route.meta.pageTransition ?? defaultPageTransition return _wrapIf(Transition, transitionProps, - wrapInKeepAlive(routeProps.route.meta.keepalive, isNested && nuxtApp.isHydrating - // Include route children in parent suspense + wrapInKeepAlive(routeProps.route.meta.keepalive ?? defaultKeepaliveConfig, isNested && nuxtApp.isHydrating + // Include route children in parent suspense ? h(Component, { key, routeProps, pageKey: key, hasTransition: !!transitionProps } as {}) : h(Suspense, { onPending: () => nuxtApp.callHook('page:start', routeProps.Component), @@ -58,8 +60,6 @@ export default defineComponent({ [key: string]: any }> -const defaultPageTransition = { name: 'page', mode: 'out-in' } - const Component = defineComponent({ // TODO: Type props // eslint-disable-next-line vue/require-prop-types diff --git a/packages/schema/src/config/_app.ts b/packages/schema/src/config/_app.ts index 86de94b4fd..045bd6ee37 100644 --- a/packages/schema/src/config/_app.ts +++ b/packages/schema/src/config/_app.ts @@ -100,7 +100,7 @@ export default { * } * } * ``` - * @type {typeof import('../src/types/meta').MetaObject} + * @type {typeof import('../src/types/config').NuxtAppConfig['head']} * @version 3 */ head: { @@ -123,7 +123,37 @@ export default { return resolved } - } + }, + /** + * Default values for layout transitions. + * + * This can be overridden with `definePageMeta` on an individual page. + * Only JSON-serializable values are allowed. + * + * @see https://vuejs.org/api/built-in-components.html#transition + * @type {typeof import('../src/types/config').NuxtAppConfig['layoutTransition']} + */ + layoutTransition: { name: 'layout', mode: 'out-in' }, + /** + * Default values for page transitions. + * + * This can be overridden with `definePageMeta` on an individual page. + * Only JSON-serializable values are allowed. + * + * @see https://vuejs.org/api/built-in-components.html#transition + * @type {typeof import('../src/types/config').NuxtAppConfig['pageTransition']} + */ + pageTransition: { name: 'page', mode: 'out-in' }, + /** + * Default values for KeepAlive configuration between pages. + * + * This can be overridden with `definePageMeta` on an individual page. + * Only JSON-serializable values are allowed. + * + * @see https://vuejs.org/api/built-in-components.html#keepalive + * @type {typeof import('../src/types/config').NuxtAppConfig['keepalive']} + */ + keepalive: false, }, /** * The path to an HTML template file for rendering Nuxt responses. diff --git a/packages/schema/src/types/config.ts b/packages/schema/src/types/config.ts index 66b902a4f9..3fda92c80c 100644 --- a/packages/schema/src/types/config.ts +++ b/packages/schema/src/types/config.ts @@ -1,6 +1,8 @@ +import type { KeepAliveProps, TransitionProps } from 'vue' import { ConfigSchema } from '../../schema/config' import type { UserConfig as ViteUserConfig } from 'vite' import type { Options as VuePluginOptions } from '@vitejs/plugin-vue' +import type { MetaObject } from './meta' type DeepPartial = T extends Function ? T : T extends Record ? { [P in keyof T]?: DeepPartial } : T @@ -13,8 +15,8 @@ export interface NuxtConfig extends DeepPartial> { // TODO: Expose ConfigLayer from c12 interface ConfigLayer { - config: T; - cwd: string; + config: T + cwd: string configFile: string } export type NuxtConfigLayer = ConfigLayer { nitro?: never } +export interface NuxtAppConfig { + head: MetaObject + layoutTransition: boolean | TransitionProps + pageTransition: boolean | TransitionProps + keepalive: boolean | KeepAliveProps +} + export interface AppConfig { }