UNPKG

@nuxt/schema

Version:

Nuxt types and default configuration

1,232 lines (1,226 loc) 102 kB
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