@nuxt/schema
Version:
1,232 lines (1,226 loc) • 102 kB
JavaScript
import 'scule';
import { defu } from 'defu';
import { resolve, join, basename, relative } from 'pathe';
import { isTest, isDevelopment, isDebug } from 'std-env';
import { consola } from 'consola';
import { existsSync } from 'node:fs';
import { readdir } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import { findWorkspaceDir } from 'pkg-types';
function defineUntypedSchema(options) {
return options;
}
const adhoc = defineUntypedSchema({
/**
* Configure Nuxt component auto-registration.
*
* Any components in the directories configured here can be used throughout your
* pages, layouts (and other components) without needing to explicitly import them.
* @see [`components/` directory documentation](https://nuxt.com/docs/guide/directory-structure/components)
* @type {boolean | typeof import('../src/types/components').ComponentsOptions | typeof import('../src/types/components').ComponentsOptions['dirs']}
*/
components: {
$resolve: (val) => {
if (Array.isArray(val)) {
return { dirs: val };
}
if (val === undefined || val === true) {
return { dirs: [{ path: "~/components/global", global: true }, "~/components"] };
}
return val;
}
},
/**
* Configure how Nuxt auto-imports composables into your application.
* @see [Nuxt documentation](https://nuxt.com/docs/guide/directory-structure/composables)
* @type {typeof import('../src/types/imports').ImportsOptions}
*/
imports: {
global: false,
/**
* Whether to scan your `composables/` and `utils/` directories for composables to auto-import.
* Auto-imports registered by Nuxt or other modules, such as imports from `vue` or `nuxt`, will still be enabled.
*/
scan: true,
/**
* An array of custom directories that will be auto-imported.
* Note that this option will not override the default directories (~/composables, ~/utils).
* @example
* ```js
* imports: {
* // Auto-import pinia stores defined in `~/stores`
* dirs: ['stores']
* }
* ```
*/
dirs: []
},
/**
* Whether to use the vue-router integration in Nuxt 3. If you do not provide a value it will be
* enabled if you have a `pages/` directory in your source folder.
* @type {boolean}
*/
pages: undefined,
/**
* Manually disable nuxt telemetry.
* @see [Nuxt Telemetry](https://github.com/nuxt/telemetry) for more information.
* @type {boolean | Record<string, any>}
*/
telemetry: undefined,
/**
* Enable Nuxt DevTools for development.
*
* Breaking changes for devtools might not reflect on the version of Nuxt.
* @see [Nuxt DevTools](https://devtools.nuxt.com/) for more information.
* @type { { enabled: boolean, [key: string]: any } }
*/
devtools: {}
});
const app = defineUntypedSchema({
/**
* Vue.js config
*/
vue: {
/** @type {typeof import('@vue/compiler-sfc').AssetURLTagConfig} */
transformAssetUrls: {
video: ["src", "poster"],
source: ["src"],
img: ["src"],
image: ["xlink:href", "href"],
use: ["xlink:href", "href"]
},
/**
* Options for the Vue compiler that will be passed at build time.
* @see [Vue documentation](https://vuejs.org/api/application.html#app-config-compileroptions)
* @type {typeof import('@vue/compiler-core').CompilerOptions}
*/
compilerOptions: {},
/**
* Include Vue compiler in runtime bundle.
*/
runtimeCompiler: {
$resolve: async (val, get) => val ?? await get("experimental.runtimeVueCompiler") ?? false
},
/**
* Enable reactive destructure for `defineProps`
* @type {boolean}
*/
propsDestructure: true,
/**
* It is possible to pass configure the Vue app globally. Only serializable options
* may be set in your `nuxt.config`. All other options should be set at runtime in a Nuxt plugin..
* @see [Vue app config documentation](https://vuejs.org/api/application.html#app-config)
*/
config: undefined
},
/**
* Nuxt App configuration.
*/
app: {
/**
* The base path of your Nuxt application.
*
* For example:
* @example
* ```ts
* export default defineNuxtConfig({
* app: {
* baseURL: '/prefix/'
* }
* })
* ```
*
* This can also be set at runtime by setting the NUXT_APP_BASE_URL environment variable.
* @example
* ```bash
* NUXT_APP_BASE_URL=/prefix/ node .output/server/index.mjs
* ```
*/
baseURL: {
$resolve: (val) => val || process.env.NUXT_APP_BASE_URL || "/"
},
/** The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set). This is set at build time and should not be customized at runtime. */
buildAssetsDir: {
$resolve: (val) => val || process.env.NUXT_APP_BUILD_ASSETS_DIR || "/_nuxt/"
},
/**
* An absolute URL to serve the public folder from (production-only).
*
* For example:
* @example
* ```ts
* export default defineNuxtConfig({
* app: {
* cdnURL: 'https://mycdn.org/'
* }
* })
* ```
*
* This can be set to a different value at runtime by setting the `NUXT_APP_CDN_URL` environment variable.
* @example
* ```bash
* NUXT_APP_CDN_URL=https://mycdn.org/ node .output/server/index.mjs
* ```
*/
cdnURL: {
$resolve: async (val, get) => await get("dev") ? "" : (process.env.NUXT_APP_CDN_URL ?? val) || ""
},
/**
* Set default configuration for `<head>` on every page.
* @example
* ```js
* app: {
* head: {
* meta: [
* // <meta name="viewport" content="width=device-width, initial-scale=1">
* { name: 'viewport', content: 'width=device-width, initial-scale=1' }
* ],
* script: [
* // <script src="https://myawesome-lib.js"><\/script>
* { src: 'https://awesome-lib.js' }
* ],
* link: [
* // <link rel="stylesheet" href="https://myawesome-lib.css">
* { rel: 'stylesheet', href: 'https://awesome-lib.css' }
* ],
* // please note that this is an area that is likely to change
* style: [
* // <style type="text/css">:root { color: red }</style>
* { children: ':root { color: red }', type: 'text/css' }
* ],
* noscript: [
* // <noscript>JavaScript is required</noscript>
* { children: 'JavaScript is required' }
* ]
* }
* }
* ```
* @type {typeof import('../src/types/config').NuxtAppConfig['head']}
*/
head: {
$resolve: async (val, get) => {
const resolved = defu(val, await get("meta"), {
meta: [],
link: [],
style: [],
script: [],
noscript: []
});
if (!resolved.meta.find((m) => m.charset)?.charset) {
resolved.meta.unshift({ charset: resolved.charset || "utf-8" });
}
if (!resolved.meta.find((m) => m.name === "viewport")?.content) {
resolved.meta.unshift({ name: "viewport", content: resolved.viewport || "width=device-width, initial-scale=1" });
}
resolved.meta = resolved.meta.filter(Boolean);
resolved.link = resolved.link.filter(Boolean);
resolved.style = resolved.style.filter(Boolean);
resolved.script = resolved.script.filter(Boolean);
resolved.noscript = resolved.noscript.filter(Boolean);
return resolved;
}
},
/**
* Default values for layout transitions.
*
* This can be overridden with `definePageMeta` on an individual page.
* Only JSON-serializable values are allowed.
* @see [Vue Transition docs](https://vuejs.org/api/built-in-components.html#transition)
* @type {typeof import('../src/types/config').NuxtAppConfig['layoutTransition']}
*/
layoutTransition: false,
/**
* Default values for page transitions.
*
* This can be overridden with `definePageMeta` on an individual page.
* Only JSON-serializable values are allowed.
* @see [Vue Transition docs](https://vuejs.org/api/built-in-components.html#transition)
* @type {typeof import('../src/types/config').NuxtAppConfig['pageTransition']}
*/
pageTransition: false,
/**
* Default values for view transitions.
*
* This only has an effect when **experimental** support for View Transitions is
* [enabled in your nuxt.config file](/docs/getting-started/transitions#view-transitions-api-experimental).
*
* This can be overridden with `definePageMeta` on an individual page.
* @see [Nuxt View Transition API docs](https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental)
* @type {typeof import('../src/types/config').NuxtAppConfig['viewTransition']}
*/
viewTransition: {
$resolve: async (val, get) => val ?? await get("experimental").then(
(e) => e?.viewTransition
) ?? false
},
/**
* Default values for KeepAlive configuration between pages.
*
* This can be overridden with `definePageMeta` on an individual page.
* Only JSON-serializable values are allowed.
* @see [Vue KeepAlive](https://vuejs.org/api/built-in-components.html#keepalive)
* @type {typeof import('../src/types/config').NuxtAppConfig['keepalive']}
*/
keepalive: false,
/**
* Customize Nuxt root element id.
* @type {string | false}
* @deprecated Prefer `rootAttrs.id` instead
*/
rootId: {
$resolve: (val) => val === false ? false : val || "__nuxt"
},
/**
* Customize Nuxt root element tag.
*/
rootTag: {
$resolve: (val) => val || "div"
},
/**
* Customize Nuxt root element id.
* @type {typeof import('@unhead/schema').HtmlAttributes}
*/
rootAttrs: {
$resolve: async (val, get) => {
const rootId = await get("app.rootId");
return defu(val, {
id: rootId === false ? undefined : rootId || "__nuxt"
});
}
},
/**
* Customize Nuxt Teleport element tag.
*/
teleportTag: {
$resolve: (val) => val || "div"
},
/**
* Customize Nuxt Teleport element id.
* @type {string | false}
* @deprecated Prefer `teleportAttrs.id` instead
*/
teleportId: {
$resolve: (val) => val === false ? false : val || "teleports"
},
/**
* Customize Nuxt Teleport element attributes.
* @type {typeof import('@unhead/schema').HtmlAttributes}
*/
teleportAttrs: {
$resolve: async (val, get) => {
const teleportId = await get("app.teleportId");
return defu(val, {
id: teleportId === false ? undefined : teleportId || "teleports"
});
}
},
/**
* Customize Nuxt SpaLoader element tag.
*/
spaLoaderTag: {
$resolve: (val) => val || "div"
},
/**
* Customize Nuxt Nuxt SpaLoader element attributes.
* @type {typeof import('@unhead/schema').HtmlAttributes}
*/
spaLoaderAttrs: {
id: "__nuxt-loader"
}
},
/**
* Boolean or a path to an HTML file with the contents of which will be inserted into any HTML page
* rendered with `ssr: false`.
*
* - If it is unset, it will use `~/app/spa-loading-template.html` file in one of your layers, if it exists.
* - If it is false, no SPA loading indicator will be loaded.
* - If true, Nuxt will look for `~/app/spa-loading-template.html` file in one of your layers, or a
* default Nuxt image will be used.
*
* Some good sources for spinners are [SpinKit](https://github.com/tobiasahlin/SpinKit) or [SVG Spinners](https://icones.js.org/collection/svg-spinners).
* @example ~/app/spa-loading-template.html
* ```html
* <!-- https://github.com/barelyhuman/snips/blob/dev/pages/css-loader.md -->
* <div class="loader"></div>
* <style>
* .loader {
* display: block;
* position: fixed;
* z-index: 1031;
* top: 50%;
* left: 50%;
* transform: translate(-50%, -50%);
* width: 18px;
* height: 18px;
* box-sizing: border-box;
* border: solid 2px transparent;
* border-top-color: #000;
* border-left-color: #000;
* border-bottom-color: #efefef;
* border-right-color: #efefef;
* border-radius: 50%;
* -webkit-animation: loader 400ms linear infinite;
* animation: loader 400ms linear infinite;
* }
*
* \@-webkit-keyframes loader {
* 0% {
* -webkit-transform: translate(-50%, -50%) rotate(0deg);
* }
* 100% {
* -webkit-transform: translate(-50%, -50%) rotate(360deg);
* }
* }
* \@keyframes loader {
* 0% {
* transform: translate(-50%, -50%) rotate(0deg);
* }
* 100% {
* transform: translate(-50%, -50%) rotate(360deg);
* }
* }
* </style>
* ```
* @type {string | boolean}
*/
spaLoadingTemplate: {
$resolve: async (val, get) => typeof val === "string" ? resolve(await get("srcDir"), val) : val ?? null
},
/**
* An array of nuxt app plugins.
*
* Each plugin can be a string (which can be an absolute or relative path to a file).
* If it ends with `.client` or `.server` then it will be automatically loaded only
* in the appropriate context.
*
* It can also be an object with `src` and `mode` keys.
* @note Plugins are also auto-registered from the `~/plugins` directory
* and these plugins do not need to be listed in `nuxt.config` unless you
* need to customize their order. All plugins are deduplicated by their src path.
* @see [`plugins/` directory documentation](https://nuxt.com/docs/guide/directory-structure/plugins)
* @example
* ```js
* plugins: [
* '~/plugins/foo.client.js', // only in client side
* '~/plugins/bar.server.js', // only in server side
* '~/plugins/baz.js', // both client & server
* { src: '~/plugins/both-sides.js' },
* { src: '~/plugins/client-only.js', mode: 'client' }, // only on client side
* { src: '~/plugins/server-only.js', mode: 'server' } // only on server side
* ]
* ```
* @type {(typeof import('../src/types/nuxt').NuxtPlugin | string)[]}
*/
plugins: [],
/**
* You can define the CSS files/modules/libraries you want to set globally
* (included in every page).
*
* Nuxt will automatically guess the file type by its extension and use the
* appropriate pre-processor. You will still need to install the required
* loader if you need to use them.
* @example
* ```js
* css: [
* // Load a Node.js module directly (here it's a Sass file).
* 'bulma',
* // CSS file in the project
* '~/assets/css/main.css',
* // SCSS file in the project
* '~/assets/css/main.scss'
* ]
* ```
* @type {string[]}
*/
css: {
$resolve: (val) => (val ?? []).map((c) => c.src || c)
},
/**
* An object that allows us to configure the `unhead` nuxt module.
*/
unhead: {
/**
* An object that will be passed to `renderSSRHead` to customize the output.
*
* @see [`unhead` options documentation](https://unhead.unjs.io/setup/ssr/installation#options)
*
* @example
* ```ts
* export default defineNuxtConfig({
* unhead: {
* renderSSRHeadOptions: {
* omitLineBreaks: true
* }
* })
* ```
* @type {typeof import('@unhead/schema').RenderSSRHeadOptions}
*/
renderSSRHeadOptions: {
$resolve: async (val, get) => {
const isV4 = (await get("future")).compatibilityVersion === 4;
return defu(val, {
omitLineBreaks: isV4
});
}
}
}
});
const build = defineUntypedSchema({
/**
* The builder to use for bundling the Vue part of your application.
* @type {'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: typeof import('../src/types/nuxt').Nuxt) => Promise<void> }}
*/
builder: {
$resolve: async (val = "vite", get) => {
if (typeof val === "object") {
return val;
}
const map = {
rspack: "@nuxt/rspack-builder",
vite: "@nuxt/vite-builder",
webpack: "@nuxt/webpack-builder"
};
return map[val] || val || (await get("vite") === false ? map.webpack : map.vite);
}
},
/**
* Configures whether and how sourcemaps are generated for server and/or client bundles.
*
* If set to a single boolean, that value applies to both server and client.
* Additionally, the `'hidden'` option is also available for both server and client.
*
* Available options for both client and server:
* - `true`: Generates sourcemaps and includes source references in the final bundle.
* - `false`: Does not generate any sourcemaps.
* - `'hidden'`: Generates sourcemaps but does not include references in the final bundle.
*
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
*/
sourcemap: {
$resolve: async (val, get) => {
if (typeof val === "boolean") {
return { server: val, client: val };
}
return defu(val, {
server: true,
client: await get("dev")
});
}
},
/**
* Log level when building logs.
*
* Defaults to 'silent' when running in CI or when a TTY is not available.
* This option is then used as 'silent' in Vite and 'none' in Webpack
* @type {'silent' | 'info' | 'verbose'}
*/
logLevel: {
$resolve: (val) => {
if (val && !["silent", "info", "verbose"].includes(val)) {
consola.warn(`Invalid \`logLevel\` option: \`${val}\`. Must be one of: \`silent\`, \`info\`, \`verbose\`.`);
}
return val ?? (isTest ? "silent" : "info");
}
},
/**
* Shared build configuration.
*/
build: {
/**
* If you want to transpile specific dependencies with Babel, you can add them here.
* Each item in transpile can be a package name, a function, a string or regex object matching the
* dependency's file name.
*
* You can also use a function to conditionally transpile. The function will receive an object ({ isDev, isServer, isClient, isModern, isLegacy }).
* @example
* ```js
* transpile: [({ isLegacy }) => isLegacy && 'ky']
* ```
* @type {Array<string | RegExp | ((ctx: { isClient?: boolean; isServer?: boolean; isDev: boolean }) => string | RegExp | false)>}
*/
transpile: {
$resolve: (val) => (val || []).filter(Boolean)
},
/**
* It is recommended to use `addTemplate` from `@nuxt/kit` instead of this option.
*
* @example
* ```js
* templates: [
* {
* src: '~/modules/support/plugin.js', // `src` can be absolute or relative
* dst: 'support.js', // `dst` is relative to project `.nuxt` dir
* }
* ]
* ```
* @type {typeof import('../src/types/nuxt').NuxtTemplate<any>[]}
*/
templates: [],
/**
* Nuxt allows visualizing your bundles and how to optimize them.
*
* Set to `true` to enable bundle analysis, or pass an object with options: [for webpack](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin) or [for vite](https://github.com/btd/rollup-plugin-visualizer#options).
* @example
* ```js
* analyze: {
* analyzerMode: 'static'
* }
* ```
* @type {boolean | { enabled?: boolean } & ((0 extends 1 & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options ? {} : typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options) | typeof import('rollup-plugin-visualizer').PluginVisualizerOptions)}
*/
analyze: {
$resolve: async (val, get) => {
const [rootDir, analyzeDir] = await Promise.all([get("rootDir"), get("analyzeDir")]);
return defu(typeof val === "boolean" ? { enabled: val } : val, {
template: "treemap",
projectRoot: rootDir,
filename: join(analyzeDir, "{name}.html")
});
}
}
},
/**
* Build time optimization configuration.
*/
optimization: {
/**
* Functions to inject a key for.
*
* As long as the number of arguments passed to the function is less than `argumentLength`, an
* additional magic string will be injected that can be used to deduplicate requests between server
* and client. You will need to take steps to handle this additional key.
*
* The key will be unique based on the location of the function being invoked within the file.
* @type {Array<{ name: string, source?: string | RegExp, argumentLength: number }>}
*/
keyedComposables: {
$resolve: (val) => [
{ name: "callOnce", argumentLength: 3 },
{ name: "defineNuxtComponent", argumentLength: 2 },
{ name: "useState", argumentLength: 2 },
{ name: "useFetch", argumentLength: 3 },
{ name: "useAsyncData", argumentLength: 3 },
{ name: "useLazyAsyncData", argumentLength: 3 },
{ name: "useLazyFetch", argumentLength: 3 },
...val || []
].filter(Boolean)
},
/**
* Tree shake code from specific builds.
*/
treeShake: {
/**
* Tree shake composables from the server or client builds.
* @example
* ```js
* treeShake: { client: { myPackage: ['useServerOnlyComposable'] } }
* ```
*/
composables: {
server: {
$resolve: async (val, get) => defu(
val || {},
await get("dev") ? {} : {
"vue": ["onMounted", "onUpdated", "onUnmounted", "onBeforeMount", "onBeforeUpdate", "onBeforeUnmount", "onRenderTracked", "onRenderTriggered", "onActivated", "onDeactivated"],
"#app": ["definePayloadReviver", "definePageMeta"]
}
)
},
client: {
$resolve: async (val, get) => defu(
val || {},
await get("dev") ? {} : {
"vue": ["onRenderTracked", "onRenderTriggered", "onServerPrefetch"],
"#app": ["definePayloadReducer", "definePageMeta", "onPrehydrate"]
}
)
}
}
},
/**
* Options passed directly to the transformer from `unctx` that preserves async context
* after `await`.
* @type {typeof import('unctx/transform').TransformerOptions}
*/
asyncTransforms: {
asyncFunctions: ["defineNuxtPlugin", "defineNuxtRouteMiddleware"],
objectDefinitions: {
defineNuxtComponent: ["asyncData", "setup"],
defineNuxtPlugin: ["setup"],
definePageMeta: ["middleware", "validate"]
}
}
}
});
const common = defineUntypedSchema({
/**
* Extend project from multiple local or remote sources.
*
* Value should be either a string or array of strings pointing to source directories or config path relative to current config.
*
* You can use `github:`, `gh:` `gitlab:` or `bitbucket:`
* @see [`c12` docs on extending config layers](https://github.com/unjs/c12#extending-config-layer-from-remote-sources)
* @see [`giget` documentation](https://github.com/unjs/giget)
* @type {string | [string, typeof import('c12').SourceOptions?] | (string | [string, typeof import('c12').SourceOptions?])[]}
*/
extends: null,
/**
* Specify a compatibility date for your app.
*
* This is used to control the behavior of presets in Nitro, Nuxt Image
* and other modules that may change behavior without a major version bump.
*
* We plan to improve the tooling around this feature in the future.
*
* @type {typeof import('compatx').CompatibilityDateSpec}
*/
compatibilityDate: undefined,
/**
* Extend project from a local or remote source.
*
* Value should be a string pointing to source directory or config path relative to current config.
*
* You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository.
* @type {string}
*/
theme: null,
/**
* Define the root directory of your application.
*
* This property can be overwritten (for example, running `nuxt ./my-app/`
* will set the `rootDir` to the absolute path of `./my-app/` from the
* current/working directory.
*
* It is normally not needed to configure this option.
*/
rootDir: {
$resolve: (val) => typeof val === "string" ? resolve(val) : process.cwd()
},
/**
* Define the workspace directory of your application.
*
* Often this is used when in a monorepo setup. Nuxt will attempt to detect
* your workspace directory automatically, but you can override it here.
*
* It is normally not needed to configure this option.
*/
workspaceDir: {
$resolve: async (val, get) => {
const rootDir = await get("rootDir");
return val ? resolve(rootDir, val) : await findWorkspaceDir(rootDir).catch(() => rootDir);
}
},
/**
* Define the source directory of your Nuxt application.
*
* If a relative path is specified, it will be relative to the `rootDir`.
* @example
* ```js
* export default {
* srcDir: 'src/'
* }
* ```
* This would work with the following folder structure:
* ```bash
* -| app/
* ---| node_modules/
* ---| nuxt.config.js
* ---| package.json
* ---| src/
* ------| assets/
* ------| components/
* ------| layouts/
* ------| middleware/
* ------| pages/
* ------| plugins/
* ------| public/
* ------| store/
* ------| server/
* ------| app.config.ts
* ------| app.vue
* ------| error.vue
* ```
*/
srcDir: {
$resolve: async (val, get) => {
if (val) {
return resolve(await get("rootDir"), val);
}
const [rootDir, isV4] = await Promise.all([
get("rootDir"),
get("future").then((r) => r.compatibilityVersion === 4)
]);
if (!isV4) {
return rootDir;
}
const srcDir = resolve(rootDir, "app");
if (!existsSync(srcDir)) {
return rootDir;
}
const srcDirFiles = /* @__PURE__ */ new Set();
const files = await readdir(srcDir).catch(() => []);
for (const file of files) {
if (file !== "spa-loading-template.html" && !file.startsWith("router.options")) {
srcDirFiles.add(file);
}
}
if (srcDirFiles.size === 0) {
for (const file of ["app.vue", "App.vue"]) {
if (existsSync(resolve(rootDir, file))) {
return rootDir;
}
}
const keys = ["assets", "layouts", "middleware", "pages", "plugins"];
const dirs = await Promise.all(keys.map((key) => get(`dir.${key}`)));
for (const dir of dirs) {
if (existsSync(resolve(rootDir, dir))) {
return rootDir;
}
}
}
return srcDir;
}
},
/**
* Define the server directory of your Nuxt application, where Nitro
* routes, middleware and plugins are kept.
*
* If a relative path is specified, it will be relative to your `rootDir`.
*
*/
serverDir: {
$resolve: async (val, get) => {
if (val) {
const rootDir = await get("rootDir");
return resolve(rootDir, val);
}
const isV4 = (await get("future")).compatibilityVersion === 4;
return join(isV4 ? await get("rootDir") : await get("srcDir"), "server");
}
},
/**
* Define the directory where your built Nuxt files will be placed.
*
* Many tools assume that `.nuxt` is a hidden directory (because it starts
* with a `.`). If that is a problem, you can use this option to prevent that.
* @example
* ```js
* export default {
* buildDir: 'nuxt-build'
* }
* ```
*/
buildDir: {
$resolve: async (val, get) => resolve(await get("rootDir"), val || ".nuxt")
},
/**
* For multi-app projects, the unique id of the Nuxt application.
*
* Defaults to `nuxt-app`.
*/
appId: {
$resolve: (val) => val ?? "nuxt-app"
},
/**
* A unique identifier matching the build. This may contain the hash of the current state of the project.
*/
buildId: {
$resolve: async (val, get) => {
if (typeof val === "string") {
return val;
}
const [isDev, isTest2] = await Promise.all([get("dev"), get("test")]);
return isDev ? "dev" : isTest2 ? "test" : randomUUID();
}
},
/**
* Used to set the modules directories for path resolving (for example, webpack's
* `resolveLoading`, `nodeExternals` and `postcss`).
*
* The configuration path is relative to `options.rootDir` (default is current working directory).
*
* Setting this field may be necessary if your project is organized as a yarn workspace-styled mono-repository.
* @example
* ```js
* export default {
* modulesDir: ['../../node_modules']
* }
* ```
*/
modulesDir: {
$default: ["node_modules"],
$resolve: async (val, get) => {
const rootDir = await get("rootDir");
return [.../* @__PURE__ */ new Set([
...(val || []).map((dir) => resolve(rootDir, dir)),
resolve(rootDir, "node_modules")
])];
}
},
/**
* The directory where Nuxt will store the generated files when running `nuxt analyze`.
*
* If a relative path is specified, it will be relative to your `rootDir`.
*/
analyzeDir: {
$resolve: async (val, get) => val ? resolve(await get("rootDir"), val) : resolve(await get("buildDir"), "analyze")
},
/**
* Whether Nuxt is running in development mode.
*
* Normally, you should not need to set this.
*/
dev: Boolean(isDevelopment),
/**
* Whether your app is being unit tested.
*/
test: Boolean(isTest),
/**
* Set to `true` to enable debug mode.
*
* At the moment, it prints out hook names and timings on the server, and
* logs hook arguments as well in the browser.
*
*/
debug: {
$resolve: (val) => val ?? isDebug
},
/**
* Whether to enable rendering of HTML - either dynamically (in server mode) or at generate time.
* If set to `false` generated pages will have no content.
*/
ssr: {
$resolve: (val) => val ?? true
},
/**
* Modules are Nuxt extensions which can extend its core functionality and add endless integrations.
*
* Each module is either a string (which can refer to a package, or be a path to a file), a
* tuple with the module as first string and the options as a second object, or an inline module function.
*
* Nuxt tries to resolve each item in the modules array using node require path
* (in `node_modules`) and then will be resolved from project `srcDir` if `~` alias is used.
* @note Modules are executed sequentially so the order is important. First, the modules defined in `nuxt.config.ts` are loaded. Then, modules found in the `modules/`
* directory are executed, and they load in alphabetical order.
* @example
* ```js
* modules: [
* // Using package name
* '@nuxtjs/axios',
* // Relative to your project srcDir
* '~/modules/awesome.js',
* // Providing options
* ['@nuxtjs/google-analytics', { ua: 'X1234567' }],
* // Inline definition
* function () {}
* ]
* ```
* @type {(typeof import('../src/types/module').NuxtModule<any> | string | [typeof import('../src/types/module').NuxtModule | string, Record<string, any>] | undefined | null | false)[]}
*/
modules: {
$resolve: (val) => (val || []).filter(Boolean)
},
/**
* Customize default directory structure used by Nuxt.
*
* It is better to stick with defaults unless needed.
*/
dir: {
app: {
$resolve: async (val, get) => {
const isV4 = (await get("future")).compatibilityVersion === 4;
if (isV4) {
const [srcDir, rootDir] = await Promise.all([get("srcDir"), get("rootDir")]);
return resolve(await get("srcDir"), val || (srcDir === rootDir ? "app" : "."));
}
return val || "app";
}
},
/**
* The assets directory (aliased as `~assets` in your build).
*/
assets: "assets",
/**
* The layouts directory, each file of which will be auto-registered as a Nuxt layout.
*/
layouts: "layouts",
/**
* The middleware directory, each file of which will be auto-registered as a Nuxt middleware.
*/
middleware: "middleware",
/**
* The modules directory, each file in which will be auto-registered as a Nuxt module.
*/
modules: {
$resolve: async (val, get) => {
const isV4 = (await get("future")).compatibilityVersion === 4;
if (isV4) {
return resolve(await get("rootDir"), val || "modules");
}
return val || "modules";
}
},
/**
* The directory which will be processed to auto-generate your application page routes.
*/
pages: "pages",
/**
* The plugins directory, each file of which will be auto-registered as a Nuxt plugin.
*/
plugins: "plugins",
/**
* The shared directory. This directory is shared between the app and the server.
*/
shared: "shared",
/**
* The directory containing your static files, which will be directly accessible via the Nuxt server
* and copied across into your `dist` folder when your app is generated.
*/
public: {
$resolve: async (val, get) => {
const isV4 = (await get("future")).compatibilityVersion === 4;
if (isV4) {
return resolve(await get("rootDir"), val || await get("dir.static") || "public");
}
return val || await get("dir.static") || "public";
}
},
static: {
$schema: { deprecated: "use `dir.public` option instead" },
$resolve: async (val, get) => val || await get("dir.public") || "public"
}
},
/**
* The extensions that should be resolved by the Nuxt resolver.
*/
extensions: {
$resolve: (val) => [".js", ".jsx", ".mjs", ".ts", ".tsx", ".vue", ...val || []].filter(Boolean)
},
/**
* You can improve your DX by defining additional aliases to access custom directories
* within your JavaScript and CSS.
* @note Within a webpack context (image sources, CSS - but not JavaScript) you _must_ access
* your alias by prefixing it with `~`.
* @note These aliases will be automatically added to the generated `.nuxt/tsconfig.json` so you can get full
* type support and path auto-complete. In case you need to extend options provided by `./.nuxt/tsconfig.json`
* further, make sure to add them here or within the `typescript.tsConfig` property in `nuxt.config`.
* @example
* ```js
* export default {
* alias: {
* 'images': fileURLToPath(new URL('./assets/images', import.meta.url)),
* 'style': fileURLToPath(new URL('./assets/style', import.meta.url)),
* 'data': fileURLToPath(new URL('./assets/other/data', import.meta.url))
* }
* }
* ```
*
* ```html
* <template>
* <img src="~images/main-bg.jpg">
* </template>
*
* <script>
* import data from 'data/test.json'
* <\/script>
*
* <style>
* // Uncomment the below
* //@import '~style/variables.scss';
* //@import '~style/utils.scss';
* //@import '~style/base.scss';
* body {
* background-image: url('~images/main-bg.jpg');
* }
* </style>
* ```
* @type {Record<string, string>}
*/
alias: {
$resolve: async (val, get) => {
const [srcDir, rootDir, assetsDir, publicDir, buildDir, sharedDir] = await Promise.all([get("srcDir"), get("rootDir"), get("dir.assets"), get("dir.public"), get("buildDir"), get("dir.shared")]);
return {
"~": srcDir,
"@": srcDir,
"~~": rootDir,
"@@": rootDir,
"#shared": resolve(rootDir, sharedDir),
[basename(assetsDir)]: resolve(srcDir, assetsDir),
[basename(publicDir)]: resolve(srcDir, publicDir),
"#build": buildDir,
"#internal/nuxt/paths": resolve(buildDir, "paths.mjs"),
...val
};
}
},
/**
* Pass options directly to `node-ignore` (which is used by Nuxt to ignore files).
* @see [node-ignore](https://github.com/kaelzhang/node-ignore)
* @example
* ```js
* ignoreOptions: {
* ignorecase: false
* }
* ```
* @type {typeof import('ignore').Options}
*/
ignoreOptions: undefined,
/**
* Any file in `pages/`, `layouts/`, `middleware/`, and `public/` directories will be ignored during
* the build process if its filename starts with the prefix specified by `ignorePrefix`. This is intended to prevent
* certain files from being processed or served in the built application.
* By default, the `ignorePrefix` is set to '-', ignoring any files starting with '-'.
*/
ignorePrefix: {
$resolve: (val) => val ?? "-"
},
/**
* More customizable than `ignorePrefix`: all files matching glob patterns specified
* inside the `ignore` array will be ignored in building.
*/
ignore: {
$resolve: async (val, get) => {
const [rootDir, ignorePrefix, analyzeDir, buildDir] = await Promise.all([get("rootDir"), get("ignorePrefix"), get("analyzeDir"), get("buildDir")]);
return [
"**/*.stories.{js,cts,mts,ts,jsx,tsx}",
// ignore storybook files
"**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}",
// ignore tests
"**/*.d.{cts,mts,ts}",
// ignore type declarations
"**/.{pnpm-store,vercel,netlify,output,git,cache,data}",
relative(rootDir, analyzeDir),
relative(rootDir, buildDir),
ignorePrefix && `**/${ignorePrefix}*.*`,
...val || []
].filter(Boolean);
}
},
/**
* The watch property lets you define patterns that will restart the Nuxt dev server when changed.
*
* It is an array of strings or regular expressions. Strings should be either absolute paths or
* relative to the `srcDir` (and the `srcDir` of any layers). Regular expressions will be matched
* against the path relative to the project `srcDir` (and the `srcDir` of any layers).
* @type {Array<string | RegExp>}
*/
watch: {
$resolve: (val) => {
return (val || []).filter((b) => typeof b === "string" || b instanceof RegExp);
}
},
/**
* The watchers property lets you overwrite watchers configuration in your `nuxt.config`.
*/
watchers: {
/** An array of event types, which, when received, will cause the watcher to restart. */
rewatchOnRawEvents: undefined,
/**
* `watchOptions` to pass directly to webpack.
* @see [webpack@4 watch options](https://v4.webpack.js.org/configuration/watch/#watchoptions).
*/
webpack: {
aggregateTimeout: 1e3
},
/**
* Options to pass directly to `chokidar`.
* @see [chokidar](https://github.com/paulmillr/chokidar#api)
*/
chokidar: {
ignoreInitial: true
}
},
/**
* Hooks are listeners to Nuxt events that are typically used in modules,
* but are also available in `nuxt.config`.
*
* Internally, hooks follow a naming pattern using colons (e.g., build:done).
*
* For ease of configuration, you can also structure them as an hierarchical
* object in `nuxt.config` (as below).
* @example
* ```js
* import fs from 'node:fs'
* import path from 'node:path'
* export default {
* hooks: {
* build: {
* done(builder) {
* const extraFilePath = path.join(
* builder.nuxt.options.buildDir,
* 'extra-file'
* )
* fs.writeFileSync(extraFilePath, 'Something extra')
* }
* }
* }
* }
* ```
* @type {typeof import('../src/types/hooks').NuxtHooks}
*/
hooks: null,
/**
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
*
* The value of this object is accessible from server only using `useRuntimeConfig`.
*
* It mainly should hold _private_ configuration which is not exposed on the frontend.
* This could include a reference to your API secret tokens.
*
* Anything under `public` and `app` will be exposed to the frontend as well.
*
* Values are automatically replaced by matching env variables at runtime, e.g. setting an environment
* variable `NUXT_API_KEY=my-api-key NUXT_PUBLIC_BASE_URL=/foo/` would overwrite the two values in the example below.
* @example
* ```js
* export default {
* runtimeConfig: {
* apiKey: '', // Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY
* public: {
* baseURL: '' // Exposed to the frontend as well.
* }
* }
* }
* ```
* @type {typeof import('../src/types/config').RuntimeConfig}
*/
runtimeConfig: {
$resolve: async (val, get) => {
const [app, buildId] = await Promise.all([get("app"), get("buildId")]);
provideFallbackValues(val);
return defu(val, {
public: {},
app: {
buildId,
baseURL: app.baseURL,
buildAssetsDir: app.buildAssetsDir,
cdnURL: app.cdnURL
}
});
}
},
/**
* Additional app configuration
*
* For programmatic usage and type support, you can directly provide app config with this option.
* It will be merged with `app.config` file as default value.
* @type {typeof import('../src/types/config').AppConfig}
*/
appConfig: {
nuxt: {}
},
$schema: {}
});
function provideFallbackValues(obj) {
for (const key in obj) {
if (typeof obj[key] === "undefined" || obj[key] === null) {
obj[key] = "";
} else if (typeof obj[key] === "object") {
provideFallbackValues(obj[key]);
}
}
}
const _messages = { "appName": "Nuxt", "version": "", "loading": "Loading" };
const template = (messages) => {
messages = { ..._messages, ...messages };
return '<!DOCTYPE html><html lang="en"><head><title>' + messages.loading + " | " + messages.appName + '</title><meta charset="utf-8"><meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport"><style>.nuxt-loader-bar{animation:gradient 2s infinite;animation-fill-mode:forwards;animation-timing-function:linear;background:repeating-linear-gradient(90deg,#36e4da 0,#1de0b1 25%,#00dc82 50%,#1de0b1 75%,#36e4da);background-position:0 0;background-size:200% auto;bottom:0;height:100px;height:5px;left:0;position:fixed;right:0}.visual-effects .nuxt-loader-bar{bottom:-50px;filter:blur(100px);height:100px;left:-50px;right:-50px}.visual-effects .mouse-gradient{background:repeating-linear-gradient(90deg,#00dc82 0,#1de0b1 50%,#36e4da);filter:blur(100px);opacity:.5}#animation-toggle{opacity:0;padding:10px;position:fixed;right:0;top:0;transition:opacity .4s ease-in}#animation-toggle:hover{opacity:.8}@keyframes gradient{0%{background-position:0 0}to{background-position:-200% 0}}@media (prefers-color-scheme:dark){body,html{color:#fff;color-scheme:dark}.nuxt-loader-bar{opacity:.5}}*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}a{text-decoration:inherit}a,button{color:inherit}button{font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0;text-transform:none}[type=button],button{-webkit-appearance:button;background-color:transparent;background-image:none}button{cursor:pointer}svg{display:block;vertical-align:middle}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.z-20{z-index:20}.h-\\[200px\\]{height:200px}.min-h-screen{min-height:100vh}.w-\\[200px\\]{width:200px}.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.overflow-hidden{overflow:hidden}.rounded-full{border-radius:9999px}.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.text-center{text-align:center}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (prefers-color-scheme:dark){.dark\\:bg-black{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}}</style><script>!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll(\'link[rel="modulepreload"]\'))r(e);new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)})).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();<\/script></head><body class="bg-white dark:bg-black flex flex-col items-center justify-center min-h-screen overflow-hidden relative text-center visual-effects"><div id="mouseLight" class="absolute h-[200px] mouse-gradient rounded-full top-0 transition-opacity w-[200px]"></div><a href="https://nuxt.com" target="_blank" rel="noopener" class="nuxt-logo z-20" aria-label="Nuxt"> <svg xmlns="http://www.w3.org/2000/svg" width="214" height="53" fill="none" class="nuxt-img" viewBox="0 0 800 200"><path fill="#00DC82" d="M168.303 200h111.522c3.543 0 7.022-.924 10.09-2.679A20.1 20.1 0 0 0 297.3 190a19.86 19.86 0 0 0 2.7-10.001 19.86 19.86 0 0 0-2.709-9.998L222.396 41.429a20.1 20.1 0 0 0-7.384-7.32 20.3 20.3 0 0 0-10.088-2.679c-3.541 0-7.02.925-10.087 2.68a20.1 20.1 0 0 0-7.384 7.32l-19.15 32.896L130.86 9.998a20.1 20.1 0 0 0-7.387-7.32A20.3 20.3 0 0 0 113.384 0c-3.542 0-7.022.924-10.09 2.679a20.1 20.1 0 0 0-7.387 7.319L2.709 170A19.85 19.85 0 0 0 0 179.999c-.002 3.511.93 6.96 2.7 10.001a20.1 20.1 0 0 0 7.385 7.321A20.3 20.3 0 0 0 20.175 200h70.004c27.737 0 48.192-12.075 62.266-35.633l34.171-58.652 18.303-31.389 54.93 94.285h-73.233zm-79.265-31.421-48.854-.011 73.232-125.706 36.541 62.853-24.466 42.01c-9.347 15.285-19.965 20.854-36.453 20.854"/><path fill="currentColor" d="M377 200a4 4 0 0 0 4-4v-93s5.244 8.286 15 25l38.707 66.961c1.789 3.119 5.084 5.039 8.649 5.039H470V50h-27a4 4 0 0 0-4 4v94l-17-30-36.588-62.98c-1.792-3.108-5.081-5.02-8.639-5.02H350v150zm299.203-56.143L710.551 92h-25.73a9.97 9.97 0 0 0-8.333 4.522L660.757 120.5l-15.731-23.978A9.97 9.97 0 0 0 636.693 92h-25.527l34.348 51.643L608.524 200h24.966a9.97 9.97 0 0 0 8.29-4.458l19.18-28.756 18.981 28.72a9.97 9.97 0 0 0 8.313 4.494h24.736zM724.598 92h19.714V60.071h28.251V92H800v24.857h-27.437V159.5c0 10.5 5.284 15.429 14.43 15.429H800V200h-16.869c-23.576 0-38.819-14.143-38.819-39.214v-43.929h-19.714zM590 92h-15c-3.489 0-6.218.145-8.5 2.523-2.282 2.246-2.5 3.63-2.5 7.066v52.486c0 8.058-.376 12.962-4 16.925-3.624 3.831-8.619 5-16 5-7.247 0-12.376-1.169-16-5-3.624-3.963-4-8.867-4-16.925v-52.486c0-3.435-.218-4.82-2.5-7.066C519.218 92.145 516.489 92 513 92h-15v62.422q0 21.006 11.676 33.292C517.594 195.905 529.103 200 544 200s26.204-4.095 34.123-12.286Q590 175.428 590 154.422z"/></svg> </a><button id="animation-toggle" type="button">Animation Enabled</button><div class="nuxt-loader-bar"></div><script>const ANIMATION_KEY="nuxt-loading-enable-animation",isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);let isLowPerformance=checkIsLowPerformance(),enableAnimation="false"!==localStorage.getItem(ANIMATION_KEY)&&("true"===localStorage.getItem(ANIMATION_KEY)||!isLowPerformance);const mouseLight=window.document.getElementById("mouseLight"),nuxtImg=window.document.querySelector(".nuxt-img"),animationToggle=window.document.getElementById("animation-toggle"),body=window.document.body;let bodyRect;function checkIsLowPerformance(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches||navigator.hardwareConcurrency<2||navigator.deviceMemory<1||isSafari}function calculateDistance(e,t,n){return Math.floor(Math.sqrt(Math.pow(t-(e.x+e.width/2),2)+Math.pow(n-(e.top+e.height/2),2)))}function onFocusOut(){enableAnimation&&(mouseLight.style.opacity=0,nuxtImg.style.opacity=.7)}function onMouseMove(e){if(!enableAnimation)return;const t=nuxtImg.getBoundingClientRect();bodyRect||(bodyRect=body.getBoundingClientRect());const n=calculateDistance(t,e.pageX,e.pageY),o=Math.ma