mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-12 19:58:10 +00:00
feat(schema): add runtime + internal type validation (#30844)
This commit is contained in:
parent
5a71ef8ace
commit
6a4b7232fc
@ -7,7 +7,7 @@ import escapeRE from 'escape-string-regexp'
|
||||
import { hash } from 'ohash'
|
||||
import { camelCase } from 'scule'
|
||||
import { filename } from 'pathe/utils'
|
||||
import type { NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema'
|
||||
import type { NuxtOptions, NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema'
|
||||
import type { Nitro } from 'nitropack'
|
||||
|
||||
import { annotatePlugins, checkForCircularDependencies } from './app'
|
||||
@ -199,9 +199,18 @@ export const schemaTemplate: NuxtTemplate = {
|
||||
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
||||
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
||||
|
||||
const modules = nuxt.options._installedModules
|
||||
.filter(m => m.meta && m.meta.configKey && m.meta.name && !m.meta.name.startsWith('nuxt:') && m.meta.name !== 'nuxt-config-schema')
|
||||
.map(m => [genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m] as const)
|
||||
const modules: [string, string, NuxtOptions['_installedModules'][number]][] = []
|
||||
for (const m of nuxt.options._installedModules) {
|
||||
// modules without sufficient metadata
|
||||
if (!m.meta || !m.meta.configKey || !m.meta.name) {
|
||||
continue
|
||||
}
|
||||
// core nuxt modules
|
||||
if (m.meta.name.startsWith('nuxt:') || m.meta.name === 'nuxt-config-schema') {
|
||||
continue
|
||||
}
|
||||
modules.push([genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m])
|
||||
}
|
||||
|
||||
const privateRuntimeConfig = Object.create(null)
|
||||
for (const key in nuxt.options.runtimeConfig) {
|
||||
@ -224,7 +233,7 @@ export const schemaTemplate: NuxtTemplate = {
|
||||
} else if (mod.meta?.repository) {
|
||||
if (typeof mod.meta.repository === 'string') {
|
||||
link = mod.meta.repository
|
||||
} else if (typeof mod.meta.repository.url === 'string') {
|
||||
} else if (typeof mod.meta.repository === 'object' && 'url' in mod.meta.repository && typeof mod.meta.repository.url === 'string') {
|
||||
link = mod.meta.repository.url
|
||||
}
|
||||
if (link) {
|
||||
|
@ -37,6 +37,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pug": "2.0.10",
|
||||
"@types/rollup-plugin-visualizer": "4.2.4",
|
||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||
"@types/webpack-hot-middleware": "2.25.9",
|
||||
"@unhead/schema": "1.11.18",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
||||
@ -46,14 +49,17 @@
|
||||
"c12": "2.0.1",
|
||||
"chokidar": "4.0.3",
|
||||
"compatx": "0.1.8",
|
||||
"css-minimizer-webpack-plugin": "7.0.0",
|
||||
"esbuild-loader": "4.2.2",
|
||||
"file-loader": "6.2.0",
|
||||
"h3": "1.15.0",
|
||||
"hookable": "5.5.3",
|
||||
"ignore": "7.0.3",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"nitropack": "2.10.4",
|
||||
"ofetch": "1.4.1",
|
||||
"pkg-types": "1.3.1",
|
||||
"postcss": "8.5.1",
|
||||
"sass-loader": "16.0.4",
|
||||
"scule": "1.3.0",
|
||||
"unbuild": "3.3.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Configure Nuxt component auto-registration.
|
||||
*
|
||||
@ -14,10 +14,13 @@ export default defineUntypedSchema({
|
||||
if (Array.isArray(val)) {
|
||||
return { dirs: val }
|
||||
}
|
||||
if (val === undefined || val === true) {
|
||||
return { dirs: [{ path: '~/components/global', global: true }, '~/components'] }
|
||||
if (val === false) {
|
||||
return { dirs: [] }
|
||||
}
|
||||
return {
|
||||
dirs: [{ path: '~/components/global', global: true }, '~/components'],
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
return val
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defu } from 'defu'
|
||||
import { resolve } from 'pathe'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
import type { AppHeadMetaObject } from '../types/head'
|
||||
import type { NuxtAppConfig } from '../types/config'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Vue.js config
|
||||
*/
|
||||
@ -27,7 +28,18 @@ export default defineUntypedSchema({
|
||||
* Include Vue compiler in runtime bundle.
|
||||
*/
|
||||
runtimeCompiler: {
|
||||
$resolve: async (val, get) => val ?? await get('experimental.runtimeVueCompiler') ?? false,
|
||||
$resolve: async (val, get) => {
|
||||
if (typeof val === 'boolean') {
|
||||
return val
|
||||
}
|
||||
// @ts-expect-error TODO: formally deprecate in v4
|
||||
const legacyProperty = await get('experimental.runtimeVueCompiler') as unknown
|
||||
if (typeof legacyProperty === 'boolean') {
|
||||
return legacyProperty
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -41,7 +53,7 @@ export default defineUntypedSchema({
|
||||
* 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,
|
||||
config: {},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -68,12 +80,22 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
*/
|
||||
baseURL: {
|
||||
$resolve: val => val || process.env.NUXT_APP_BASE_URL || '/',
|
||||
$resolve: (val) => {
|
||||
if (typeof val === 'string') {
|
||||
return val
|
||||
}
|
||||
return 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/',
|
||||
$resolve: (val) => {
|
||||
if (typeof val === 'string') {
|
||||
return val
|
||||
}
|
||||
return process.env.NUXT_APP_BUILD_ASSETS_DIR || '/_nuxt/'
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -96,7 +118,12 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
*/
|
||||
cdnURL: {
|
||||
$resolve: async (val, get) => (await get('dev')) ? '' : (process.env.NUXT_APP_CDN_URL ?? val) || '',
|
||||
$resolve: async (val, get) => {
|
||||
if (await get('dev')) {
|
||||
return ''
|
||||
}
|
||||
return process.env.NUXT_APP_CDN_URL || (typeof val === 'string' ? val : '')
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -132,14 +159,20 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('../src/types/config').NuxtAppConfig['head']}
|
||||
*/
|
||||
head: {
|
||||
$resolve: async (val: Partial<AppHeadMetaObject> | undefined, get) => {
|
||||
const resolved = defu(val, await get('meta') as Partial<AppHeadMetaObject>, {
|
||||
$resolve: async (_val, get) => {
|
||||
// @ts-expect-error TODO: remove in Nuxt v4
|
||||
const legacyMetaValues = await get('meta') as Record<string, unknown>
|
||||
const val: Partial<NuxtAppConfig['head']> = _val && typeof _val === 'object' ? _val : {}
|
||||
|
||||
type NormalizedMetaObject = Required<Pick<AppHeadMetaObject, 'meta' | 'link' | 'style' | 'script' | 'noscript'>>
|
||||
|
||||
const resolved: NuxtAppConfig['head'] & NormalizedMetaObject = defu(val, legacyMetaValues, {
|
||||
meta: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
} as Required<Pick<AppHeadMetaObject, 'meta' | 'link' | 'style' | 'script' | 'noscript'>>)
|
||||
} satisfies NormalizedMetaObject)
|
||||
|
||||
// provides default charset and viewport if not set
|
||||
if (!resolved.meta.find(m => m.charset)?.charset) {
|
||||
@ -190,9 +223,13 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('../src/types/config').NuxtAppConfig['viewTransition']}
|
||||
*/
|
||||
viewTransition: {
|
||||
$resolve: async (val, get) => val ?? await (get('experimental') as Promise<Record<string, any>>).then(
|
||||
e => e?.viewTransition,
|
||||
) ?? false,
|
||||
$resolve: async (val, get) => {
|
||||
if (val === 'always' || typeof val === 'boolean') {
|
||||
return val
|
||||
}
|
||||
|
||||
return await get('experimental').then(e => e.viewTransition) ?? false
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -211,14 +248,14 @@ export default defineUntypedSchema({
|
||||
* @deprecated Prefer `rootAttrs.id` instead
|
||||
*/
|
||||
rootId: {
|
||||
$resolve: val => val === false ? false : (val || '__nuxt'),
|
||||
$resolve: val => val === false ? false : (val && typeof val === 'string' ? val : '__nuxt'),
|
||||
},
|
||||
|
||||
/**
|
||||
* Customize Nuxt root element tag.
|
||||
*/
|
||||
rootTag: {
|
||||
$resolve: val => val || 'div',
|
||||
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||
},
|
||||
|
||||
/**
|
||||
@ -226,11 +263,12 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||
*/
|
||||
rootAttrs: {
|
||||
$resolve: async (val: undefined | null | Record<string, unknown>, get) => {
|
||||
$resolve: async (val, get) => {
|
||||
const rootId = await get('app.rootId')
|
||||
return defu(val, {
|
||||
return {
|
||||
id: rootId === false ? undefined : (rootId || '__nuxt'),
|
||||
})
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -238,7 +276,7 @@ export default defineUntypedSchema({
|
||||
* Customize Nuxt Teleport element tag.
|
||||
*/
|
||||
teleportTag: {
|
||||
$resolve: val => val || 'div',
|
||||
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||
},
|
||||
|
||||
/**
|
||||
@ -247,7 +285,7 @@ export default defineUntypedSchema({
|
||||
* @deprecated Prefer `teleportAttrs.id` instead
|
||||
*/
|
||||
teleportId: {
|
||||
$resolve: val => val === false ? false : (val || 'teleports'),
|
||||
$resolve: val => val === false ? false : (val && typeof val === 'string' ? val : 'teleports'),
|
||||
},
|
||||
|
||||
/**
|
||||
@ -255,11 +293,12 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||
*/
|
||||
teleportAttrs: {
|
||||
$resolve: async (val: undefined | null | Record<string, unknown>, get) => {
|
||||
$resolve: async (val, get) => {
|
||||
const teleportId = await get('app.teleportId')
|
||||
return defu(val, {
|
||||
return {
|
||||
id: teleportId === false ? undefined : (teleportId || 'teleports'),
|
||||
})
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -267,12 +306,12 @@ export default defineUntypedSchema({
|
||||
* Customize Nuxt SpaLoader element tag.
|
||||
*/
|
||||
spaLoaderTag: {
|
||||
$resolve: val => val || 'div',
|
||||
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||
},
|
||||
|
||||
/**
|
||||
* Customize Nuxt Nuxt SpaLoader element attributes.
|
||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||
* @type {Partial<typeof import('@unhead/schema').HtmlAttributes>}
|
||||
*/
|
||||
spaLoaderAttrs: {
|
||||
id: '__nuxt-loader',
|
||||
@ -332,10 +371,18 @@ export default defineUntypedSchema({
|
||||
* }
|
||||
* </style>
|
||||
* ```
|
||||
* @type {string | boolean}
|
||||
* @type {string | boolean | undefined}
|
||||
*/
|
||||
spaLoadingTemplate: {
|
||||
$resolve: async (val: string | boolean | undefined, get) => typeof val === 'string' ? resolve(await get('srcDir') as string, val) : val ?? null,
|
||||
$resolve: async (val, get) => {
|
||||
if (typeof val === 'string') {
|
||||
return resolve(await get('srcDir'), val)
|
||||
}
|
||||
if (typeof val === 'boolean') {
|
||||
return val
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -386,7 +433,21 @@ export default defineUntypedSchema({
|
||||
* @type {string[]}
|
||||
*/
|
||||
css: {
|
||||
$resolve: (val: string[] | undefined) => (val ?? []).map((c: any) => c.src || c),
|
||||
$resolve: (val) => {
|
||||
if (!Array.isArray(val)) {
|
||||
return []
|
||||
}
|
||||
const css: string[] = []
|
||||
for (const item of val) {
|
||||
if (typeof item === 'string') {
|
||||
css.push(item)
|
||||
} else if (item && 'src' in item) {
|
||||
// TODO: remove in Nuxt v4
|
||||
css.push(item.src)
|
||||
}
|
||||
}
|
||||
return css
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -410,12 +471,13 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('@unhead/schema').RenderSSRHeadOptions}
|
||||
*/
|
||||
renderSSRHeadOptions: {
|
||||
$resolve: async (val: Record<string, unknown> | undefined, get) => {
|
||||
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
||||
$resolve: async (val, get) => {
|
||||
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||
|
||||
return defu(val, {
|
||||
return {
|
||||
...typeof val === 'object' ? val : {},
|
||||
omitLineBreaks: isV4,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,25 +1,35 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defu } from 'defu'
|
||||
import { join } from 'pathe'
|
||||
import { isTest } from 'std-env'
|
||||
import { consola } from 'consola'
|
||||
import type { Nuxt } from 'nuxt/schema'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* 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' | 'webpack' | 'rspack' | { bundle: (nuxt: unknown) => Promise<void> } | undefined = 'vite', get) => {
|
||||
if (typeof val === 'object') {
|
||||
return val
|
||||
$resolve: async (val, get) => {
|
||||
if (val && typeof val === 'object' && 'bundle' in val) {
|
||||
return val as { bundle: (nuxt: Nuxt) => Promise<void> }
|
||||
}
|
||||
const map: Record<string, string> = {
|
||||
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)
|
||||
type Builder = 'vite' | 'webpack' | 'rspack'
|
||||
if (typeof val === 'string' && val in map) {
|
||||
// TODO: improve normalisation inference
|
||||
return map[val as keyof typeof map] as Builder
|
||||
}
|
||||
// @ts-expect-error TODO: remove old, unsupported config in v4
|
||||
if (await get('vite') === false) {
|
||||
return map.webpack as Builder
|
||||
}
|
||||
return map.vite as Builder
|
||||
},
|
||||
},
|
||||
|
||||
@ -37,14 +47,15 @@ export default defineUntypedSchema({
|
||||
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
||||
*/
|
||||
sourcemap: {
|
||||
$resolve: async (val: boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' } | undefined, get) => {
|
||||
$resolve: async (val, get) => {
|
||||
if (typeof val === 'boolean') {
|
||||
return { server: val, client: val }
|
||||
}
|
||||
return defu(val, {
|
||||
return {
|
||||
server: true,
|
||||
client: await get('dev'),
|
||||
})
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -56,11 +67,11 @@ export default defineUntypedSchema({
|
||||
* @type {'silent' | 'info' | 'verbose'}
|
||||
*/
|
||||
logLevel: {
|
||||
$resolve: (val: string | undefined) => {
|
||||
if (val && !['silent', 'info', 'verbose'].includes(val)) {
|
||||
$resolve: (val) => {
|
||||
if (val && typeof val === 'string' && !['silent', 'info', 'verbose'].includes(val)) {
|
||||
consola.warn(`Invalid \`logLevel\` option: \`${val}\`. Must be one of: \`silent\`, \`info\`, \`verbose\`.`)
|
||||
}
|
||||
return val ?? (isTest ? 'silent' : 'info')
|
||||
return val && typeof val === 'string' ? val as 'silent' | 'info' | 'verbose' : (isTest ? 'silent' : 'info')
|
||||
},
|
||||
},
|
||||
|
||||
@ -81,7 +92,20 @@ export default defineUntypedSchema({
|
||||
* @type {Array<string | RegExp | ((ctx: { isClient?: boolean; isServer?: boolean; isDev: boolean }) => string | RegExp | false)>}
|
||||
*/
|
||||
transpile: {
|
||||
$resolve: (val: Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)> | undefined) => (val || []).filter(Boolean),
|
||||
$resolve: (val) => {
|
||||
const transpile: Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)> = []
|
||||
if (Array.isArray(val)) {
|
||||
for (const pattern of val) {
|
||||
if (!pattern) {
|
||||
continue
|
||||
}
|
||||
if (typeof pattern === 'string' || typeof pattern === 'function' || pattern instanceof RegExp) {
|
||||
transpile.push(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
return transpile
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -110,16 +134,17 @@ export default defineUntypedSchema({
|
||||
* 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)}
|
||||
* @type {boolean | { enabled?: boolean } & ((0 extends 1 & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options ? Record<string, unknown> : typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options) | typeof import('rollup-plugin-visualizer').PluginVisualizerOptions)}
|
||||
*/
|
||||
analyze: {
|
||||
$resolve: async (val: boolean | { enabled?: boolean } | Record<string, unknown>, get) => {
|
||||
const [rootDir, analyzeDir] = await Promise.all([get('rootDir'), get('analyzeDir')]) as [string, string]
|
||||
return defu(typeof val === 'boolean' ? { enabled: val } : val, {
|
||||
$resolve: async (val, get) => {
|
||||
const [rootDir, analyzeDir] = await Promise.all([get('rootDir'), get('analyzeDir')])
|
||||
return {
|
||||
template: 'treemap',
|
||||
projectRoot: rootDir,
|
||||
filename: join(analyzeDir, '{name}.html'),
|
||||
})
|
||||
...typeof val === 'boolean' ? { enabled: val } : typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -139,7 +164,7 @@ export default defineUntypedSchema({
|
||||
* @type {Array<{ name: string, source?: string | RegExp, argumentLength: number }>}
|
||||
*/
|
||||
keyedComposables: {
|
||||
$resolve: (val: Array<{ name: string, argumentLength: string }> | undefined) => [
|
||||
$resolve: val => [
|
||||
{ name: 'callOnce', argumentLength: 3 },
|
||||
{ name: 'defineNuxtComponent', argumentLength: 2 },
|
||||
{ name: 'useState', argumentLength: 2 },
|
||||
@ -147,7 +172,7 @@ export default defineUntypedSchema({
|
||||
{ name: 'useAsyncData', argumentLength: 3 },
|
||||
{ name: 'useLazyAsyncData', argumentLength: 3 },
|
||||
{ name: 'useLazyFetch', argumentLength: 3 },
|
||||
...val || [],
|
||||
...Array.isArray(val) ? val : [],
|
||||
].filter(Boolean),
|
||||
},
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { readdir } from 'node:fs/promises'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { basename, join, relative, resolve } from 'pathe'
|
||||
import { isDebug, isDevelopment, isTest } from 'std-env'
|
||||
import { defu } from 'defu'
|
||||
import { findWorkspaceDir } from 'pkg-types'
|
||||
|
||||
import type { RuntimeConfig } from '../types/config'
|
||||
import type { NuxtDebugOptions } from '../types/debug'
|
||||
import type { NuxtModule } from '../types/module'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Extend project from multiple local or remote sources.
|
||||
*
|
||||
@ -21,7 +21,7 @@ export default defineUntypedSchema({
|
||||
* @see [`giget` documentation](https://github.com/unjs/giget)
|
||||
* @type {string | [string, typeof import('c12').SourceOptions?] | (string | [string, typeof import('c12').SourceOptions?])[]}
|
||||
*/
|
||||
extends: null,
|
||||
extends: undefined,
|
||||
|
||||
/**
|
||||
* Specify a compatibility date for your app.
|
||||
@ -43,7 +43,7 @@ export default defineUntypedSchema({
|
||||
* You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository.
|
||||
* @type {string}
|
||||
*/
|
||||
theme: null,
|
||||
theme: undefined,
|
||||
|
||||
/**
|
||||
* Define the root directory of your application.
|
||||
@ -67,9 +67,9 @@ export default defineUntypedSchema({
|
||||
* It is normally not needed to configure this option.
|
||||
*/
|
||||
workspaceDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
||||
const rootDir = await get('rootDir') as string
|
||||
return val ? resolve(rootDir, val) : await findWorkspaceDir(rootDir).catch(() => rootDir)
|
||||
$resolve: async (val, get) => {
|
||||
const rootDir = await get('rootDir')
|
||||
return val && typeof val === 'string' ? resolve(rootDir, val) : await findWorkspaceDir(rootDir).catch(() => rootDir)
|
||||
},
|
||||
},
|
||||
|
||||
@ -105,14 +105,14 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
*/
|
||||
srcDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
||||
if (val) {
|
||||
return resolve(await get('rootDir') as string, val)
|
||||
$resolve: async (val, get) => {
|
||||
if (val && typeof val === 'string') {
|
||||
return resolve(await get('rootDir'), val)
|
||||
}
|
||||
|
||||
const [rootDir, isV4] = await Promise.all([
|
||||
get('rootDir') as Promise<string>,
|
||||
(get('future') as Promise<Record<string, unknown>>).then(r => r.compatibilityVersion === 4),
|
||||
get('rootDir'),
|
||||
get('future').then(r => r.compatibilityVersion === 4),
|
||||
])
|
||||
|
||||
if (!isV4) {
|
||||
@ -138,7 +138,7 @@ export default defineUntypedSchema({
|
||||
}
|
||||
}
|
||||
const keys = ['assets', 'layouts', 'middleware', 'pages', 'plugins'] as const
|
||||
const dirs = await Promise.all(keys.map(key => get(`dir.${key}`) as Promise<string>))
|
||||
const dirs = await Promise.all(keys.map(key => get(`dir.${key}`)))
|
||||
for (const dir of dirs) {
|
||||
if (existsSync(resolve(rootDir, dir))) {
|
||||
return rootDir
|
||||
@ -157,13 +157,13 @@ export default defineUntypedSchema({
|
||||
*
|
||||
*/
|
||||
serverDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
||||
if (val) {
|
||||
const rootDir = await get('rootDir') as string
|
||||
$resolve: async (val, get) => {
|
||||
if (val && typeof val === 'string') {
|
||||
const rootDir = await get('rootDir')
|
||||
return resolve(rootDir, val)
|
||||
}
|
||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
||||
return join(isV4 ? await get('rootDir') as string : await get('srcDir') as string, 'server')
|
||||
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||
return join(isV4 ? await get('rootDir') : await get('srcDir'), 'server')
|
||||
},
|
||||
},
|
||||
|
||||
@ -180,7 +180,10 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
*/
|
||||
buildDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => resolve(await get('rootDir') as string, val || '.nuxt'),
|
||||
$resolve: async (val, get) => {
|
||||
const rootDir = await get('rootDir')
|
||||
return resolve(rootDir, val && typeof val === 'string' ? val : '.nuxt')
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -189,14 +192,14 @@ export default defineUntypedSchema({
|
||||
* Defaults to `nuxt-app`.
|
||||
*/
|
||||
appId: {
|
||||
$resolve: (val: string) => val ?? 'nuxt-app',
|
||||
$resolve: val => val && typeof val === 'string' ? 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: string | undefined, get): Promise<string> => {
|
||||
$resolve: async (val, get): Promise<string> => {
|
||||
if (typeof val === 'string') { return val }
|
||||
|
||||
const [isDev, isTest] = await Promise.all([get('dev') as Promise<boolean>, get('test') as Promise<boolean>])
|
||||
@ -220,12 +223,17 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
modulesDir: {
|
||||
$default: ['node_modules'],
|
||||
$resolve: async (val: string[] | undefined, get): Promise<string[]> => {
|
||||
const rootDir = await get('rootDir') as string
|
||||
return [...new Set([
|
||||
...(val || []).map((dir: string) => resolve(rootDir, dir)),
|
||||
resolve(rootDir, 'node_modules'),
|
||||
])]
|
||||
$resolve: async (val, get) => {
|
||||
const rootDir = await get('rootDir')
|
||||
const modulesDir = new Set<string>([resolve(rootDir, 'node_modules')])
|
||||
if (Array.isArray(val)) {
|
||||
for (const dir of val) {
|
||||
if (dir && typeof dir === 'string') {
|
||||
modulesDir.add(resolve(rootDir, dir))
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...modulesDir]
|
||||
},
|
||||
},
|
||||
|
||||
@ -235,9 +243,9 @@ export default defineUntypedSchema({
|
||||
* If a relative path is specified, it will be relative to your `rootDir`.
|
||||
*/
|
||||
analyzeDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => val
|
||||
? resolve(await get('rootDir') as string, val)
|
||||
: resolve(await get('buildDir') as string, 'analyze'),
|
||||
$resolve: async (val, get) => val && typeof val === 'string'
|
||||
? resolve(await get('rootDir'), val)
|
||||
: resolve(await get('buildDir'), 'analyze'),
|
||||
},
|
||||
|
||||
/**
|
||||
@ -246,14 +254,14 @@ export default defineUntypedSchema({
|
||||
* Normally, you should not need to set this.
|
||||
*/
|
||||
dev: {
|
||||
$resolve: val => val ?? Boolean(isDevelopment),
|
||||
$resolve: val => typeof val === 'boolean' ? val : Boolean(isDevelopment),
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether your app is being unit tested.
|
||||
*/
|
||||
test: {
|
||||
$resolve: val => val ?? Boolean(isTest),
|
||||
$resolve: val => typeof val === 'boolean' ? val : Boolean(isTest),
|
||||
},
|
||||
|
||||
/**
|
||||
@ -267,11 +275,8 @@ export default defineUntypedSchema({
|
||||
* @type {boolean | (typeof import('../src/types/debug').NuxtDebugOptions) | undefined}
|
||||
*/
|
||||
debug: {
|
||||
$resolve: (val: boolean | NuxtDebugOptions | undefined) => {
|
||||
$resolve: (val) => {
|
||||
val ??= isDebug
|
||||
if (val === false) {
|
||||
return val
|
||||
}
|
||||
if (val === true) {
|
||||
return {
|
||||
templates: true,
|
||||
@ -286,7 +291,10 @@ export default defineUntypedSchema({
|
||||
hydration: true,
|
||||
} satisfies Required<NuxtDebugOptions>
|
||||
}
|
||||
return val
|
||||
if (val && typeof val === 'object') {
|
||||
return val
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
|
||||
@ -295,7 +303,7 @@ export default defineUntypedSchema({
|
||||
* If set to `false` generated pages will have no content.
|
||||
*/
|
||||
ssr: {
|
||||
$resolve: val => val ?? true,
|
||||
$resolve: val => typeof val === 'boolean' ? val : true,
|
||||
},
|
||||
|
||||
/**
|
||||
@ -324,7 +332,20 @@ export default defineUntypedSchema({
|
||||
* @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: string[] | undefined): string[] => (val || []).filter(Boolean),
|
||||
$resolve: (val) => {
|
||||
const modules: Array<string | NuxtModule | [NuxtModule | string, Record<string, any>]> = []
|
||||
if (Array.isArray(val)) {
|
||||
for (const mod of val) {
|
||||
if (!mod) {
|
||||
continue
|
||||
}
|
||||
if (typeof mod === 'string' || typeof mod === 'function' || (Array.isArray(mod) && mod[0])) {
|
||||
modules.push(mod)
|
||||
}
|
||||
}
|
||||
}
|
||||
return modules
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -334,13 +355,13 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
dir: {
|
||||
app: {
|
||||
$resolve: async (val: string | undefined, get) => {
|
||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
||||
$resolve: async (val, get) => {
|
||||
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||
if (isV4) {
|
||||
const [srcDir, rootDir] = await Promise.all([get('srcDir') as Promise<string>, get('rootDir') as Promise<string>])
|
||||
return resolve(await get('srcDir') as string, val || (srcDir === rootDir ? 'app' : '.'))
|
||||
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')])
|
||||
return resolve(await get('srcDir'), val && typeof val === 'string' ? val : (srcDir === rootDir ? 'app' : '.'))
|
||||
}
|
||||
return val || 'app'
|
||||
return val && typeof val === 'string' ? val : 'app'
|
||||
},
|
||||
},
|
||||
/**
|
||||
@ -362,12 +383,12 @@ export default defineUntypedSchema({
|
||||
* The modules directory, each file in which will be auto-registered as a Nuxt module.
|
||||
*/
|
||||
modules: {
|
||||
$resolve: async (val: string | undefined, get) => {
|
||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
||||
$resolve: async (val, get) => {
|
||||
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||
if (isV4) {
|
||||
return resolve(await get('rootDir') as string, val || 'modules')
|
||||
return resolve(await get('rootDir'), val && typeof val === 'string' ? val : 'modules')
|
||||
}
|
||||
return val || 'modules'
|
||||
return val && typeof val === 'string' ? val : 'modules'
|
||||
},
|
||||
},
|
||||
|
||||
@ -391,18 +412,25 @@ export default defineUntypedSchema({
|
||||
* and copied across into your `dist` folder when your app is generated.
|
||||
*/
|
||||
public: {
|
||||
$resolve: async (val: string | undefined, get) => {
|
||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
||||
$resolve: async (val, get) => {
|
||||
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||
if (isV4) {
|
||||
return resolve(await get('rootDir') as string, val || await get('dir.static') as string || 'public')
|
||||
return resolve(await get('rootDir'), val && typeof val === 'string' ? val : (await get('dir.static') || 'public'))
|
||||
}
|
||||
return val || await get('dir.static') as string || 'public'
|
||||
return val && typeof val === 'string' ? val : (await get('dir.static') || 'public')
|
||||
},
|
||||
},
|
||||
|
||||
// TODO: remove in v4
|
||||
static: {
|
||||
// @ts-expect-error schema has invalid types
|
||||
$schema: { deprecated: 'use `dir.public` option instead' },
|
||||
$resolve: async (val, get) => val || await get('dir.public') || 'public',
|
||||
$resolve: async (val, get) => {
|
||||
if (val && typeof val === 'string') {
|
||||
return val
|
||||
}
|
||||
return await get('dir.public') || 'public'
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -410,7 +438,17 @@ export default defineUntypedSchema({
|
||||
* The extensions that should be resolved by the Nuxt resolver.
|
||||
*/
|
||||
extensions: {
|
||||
$resolve: (val: string[] | undefined): string[] => ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue', ...val || []].filter(Boolean),
|
||||
$resolve: (val): string[] => {
|
||||
const extensions = ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue']
|
||||
if (Array.isArray(val)) {
|
||||
for (const item of val) {
|
||||
if (item && typeof item === 'string') {
|
||||
extensions.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return extensions
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -454,8 +492,8 @@ export default defineUntypedSchema({
|
||||
* @type {Record<string, string>}
|
||||
*/
|
||||
alias: {
|
||||
$resolve: async (val: Record<string, string>, get): Promise<Record<string, string>> => {
|
||||
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')]) as [string, string, string, string, string, string]
|
||||
$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,
|
||||
@ -466,7 +504,7 @@ export default defineUntypedSchema({
|
||||
[basename(publicDir)]: resolve(srcDir, publicDir),
|
||||
'#build': buildDir,
|
||||
'#internal/nuxt/paths': resolve(buildDir, 'paths.mjs'),
|
||||
...val,
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -491,7 +529,7 @@ export default defineUntypedSchema({
|
||||
* By default, the `ignorePrefix` is set to '-', ignoring any files starting with '-'.
|
||||
*/
|
||||
ignorePrefix: {
|
||||
$resolve: val => val ?? '-',
|
||||
$resolve: val => val && typeof val === 'string' ? val : '-',
|
||||
},
|
||||
|
||||
/**
|
||||
@ -499,18 +537,27 @@ export default defineUntypedSchema({
|
||||
* inside the `ignore` array will be ignored in building.
|
||||
*/
|
||||
ignore: {
|
||||
$resolve: async (val: string[] | undefined, get): Promise<string[]> => {
|
||||
const [rootDir, ignorePrefix, analyzeDir, buildDir] = await Promise.all([get('rootDir'), get('ignorePrefix'), get('analyzeDir'), get('buildDir')]) as [string, string, string, string]
|
||||
return [
|
||||
$resolve: async (val, get): Promise<string[]> => {
|
||||
const [rootDir, ignorePrefix, analyzeDir, buildDir] = await Promise.all([get('rootDir'), get('ignorePrefix'), get('analyzeDir'), get('buildDir')])
|
||||
const ignore = new Set<string>([
|
||||
'**/*.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)
|
||||
])
|
||||
if (ignorePrefix) {
|
||||
ignore.add(`**/${ignorePrefix}*.*`)
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
for (const pattern in val) {
|
||||
if (pattern) {
|
||||
ignore.add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...ignore]
|
||||
},
|
||||
},
|
||||
|
||||
@ -523,8 +570,11 @@ export default defineUntypedSchema({
|
||||
* @type {Array<string | RegExp>}
|
||||
*/
|
||||
watch: {
|
||||
$resolve: (val: Array<unknown> | undefined) => {
|
||||
return (val || []).filter((b: unknown) => typeof b === 'string' || b instanceof RegExp)
|
||||
$resolve: (val) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.filter((b: unknown) => typeof b === 'string' || b instanceof RegExp)
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
|
||||
@ -580,7 +630,7 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
* @type {typeof import('../src/types/hooks').NuxtHooks}
|
||||
*/
|
||||
hooks: null,
|
||||
hooks: undefined,
|
||||
|
||||
/**
|
||||
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
|
||||
@ -608,8 +658,9 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('../src/types/config').RuntimeConfig}
|
||||
*/
|
||||
runtimeConfig: {
|
||||
$resolve: async (val: RuntimeConfig, get): Promise<Record<string, unknown>> => {
|
||||
const [app, buildId] = await Promise.all([get('app') as Promise<Record<string, string>>, get('buildId') as Promise<string>])
|
||||
$resolve: async (_val, get) => {
|
||||
const val = _val && typeof _val === 'object' ? _val : {}
|
||||
const [app, buildId] = await Promise.all([get('app'), get('buildId')])
|
||||
provideFallbackValues(val)
|
||||
return defu(val, {
|
||||
public: {},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
import { template as loadingTemplate } from '../../../ui-templates/dist/templates/loading'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
devServer: {
|
||||
/**
|
||||
* Whether to enable HTTPS.
|
||||
@ -21,9 +21,12 @@ export default defineUntypedSchema({
|
||||
https: false,
|
||||
|
||||
/** Dev server listening port */
|
||||
port: process.env.NUXT_PORT || process.env.NITRO_PORT || process.env.PORT || 3000,
|
||||
port: Number(process.env.NUXT_PORT || process.env.NITRO_PORT || process.env.PORT || 3000),
|
||||
|
||||
/** Dev server listening host */
|
||||
/**
|
||||
* Dev server listening host
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
host: process.env.NUXT_HOST || process.env.NITRO_HOST || process.env.HOST || undefined,
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* `future` is for early opting-in to new features that will become default in a future
|
||||
* (possibly major) version of the framework.
|
||||
@ -63,10 +63,10 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
typescriptBundlerResolution: {
|
||||
async $resolve (val, get) {
|
||||
// TODO: remove in v3.10
|
||||
val = val ?? await (get('experimental') as Promise<Record<string, any>>).then(e => e?.typescriptBundlerResolution)
|
||||
// @ts-expect-error TODO: remove in v3.10
|
||||
val = typeof val === 'boolean' ? val : await (get('experimental')).then(e => e?.typescriptBundlerResolution as string | undefined)
|
||||
if (typeof val === 'boolean') { return val }
|
||||
const setting = await get('typescript.tsConfig.compilerOptions.moduleResolution') as string | undefined
|
||||
const setting = await get('typescript.tsConfig').then(r => r?.compilerOptions?.moduleResolution)
|
||||
if (setting) {
|
||||
return setting.toLowerCase() === 'bundler'
|
||||
}
|
||||
@ -86,14 +86,22 @@ export default defineUntypedSchema({
|
||||
* @type {boolean | ((id?: string) => boolean)}
|
||||
*/
|
||||
inlineStyles: {
|
||||
async $resolve (val, get) {
|
||||
// TODO: remove in v3.10
|
||||
val = val ?? await (get('experimental') as Promise<Record<string, any>>).then((e: Record<string, any>) => e?.inlineSSRStyles)
|
||||
if (val === false || (await get('dev')) || (await get('ssr')) === false || (await get('builder')) === '@nuxt/webpack-builder') {
|
||||
async $resolve (_val, get) {
|
||||
const val = typeof _val === 'boolean' || typeof _val === 'function'
|
||||
? _val
|
||||
// @ts-expect-error TODO: legacy property - remove in v3.10
|
||||
: await (get('experimental')).then(e => e?.inlineSSRStyles) as undefined | boolean
|
||||
if (
|
||||
val === false ||
|
||||
(await get('dev')) ||
|
||||
(await get('ssr')) === false ||
|
||||
// @ts-expect-error TODO: handled normalised types
|
||||
(await get('builder')) === '@nuxt/webpack-builder'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
// Enabled by default for vite prod with ssr (for vue components)
|
||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? (id: string) => id && id.includes('.vue') : true)
|
||||
return val ?? ((await get('future')).compatibilityVersion === 4 ? (id?: string) => !!id && id.includes('.vue') : true)
|
||||
},
|
||||
},
|
||||
|
||||
@ -106,7 +114,9 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
devLogs: {
|
||||
async $resolve (val, get) {
|
||||
if (val !== undefined) { return val }
|
||||
if (typeof val === 'boolean' || val === 'silent') {
|
||||
return val
|
||||
}
|
||||
const [isDev, isTest] = await Promise.all([get('dev'), get('test')])
|
||||
return isDev && !isTest
|
||||
},
|
||||
@ -118,8 +128,10 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
noScripts: {
|
||||
async $resolve (val, get) {
|
||||
// TODO: remove in v3.10
|
||||
return val ?? await (get('experimental') as Promise<Record<string, any>>).then((e: Record<string, any>) => e?.noScripts) ?? false
|
||||
return typeof val === 'boolean'
|
||||
? val
|
||||
// @ts-expect-error TODO: legacy property - remove in v3.10
|
||||
: (await (get('experimental')).then(e => e?.noScripts as boolean | undefined) ?? false)
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -128,7 +140,7 @@ export default defineUntypedSchema({
|
||||
* Set to true to generate an async entry point for the Vue bundle (for module federation support).
|
||||
*/
|
||||
asyncEntry: {
|
||||
$resolve: val => val ?? false,
|
||||
$resolve: val => typeof val === 'boolean' ? val : false,
|
||||
},
|
||||
|
||||
// TODO: Remove when nitro has support for mocking traced dependencies
|
||||
@ -178,7 +190,17 @@ export default defineUntypedSchema({
|
||||
if (val === 'reload') {
|
||||
return 'automatic'
|
||||
}
|
||||
return val ?? 'automatic'
|
||||
if (val === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
const validOptions = ['manual', 'automatic', 'automatic-immediate'] as const
|
||||
type EmitRouteChunkError = typeof validOptions[number]
|
||||
if (typeof val === 'string' && validOptions.includes(val as EmitRouteChunkError)) {
|
||||
return val as EmitRouteChunkError
|
||||
}
|
||||
|
||||
return 'automatic'
|
||||
},
|
||||
},
|
||||
|
||||
@ -347,14 +369,16 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
watcher: {
|
||||
$resolve: async (val, get) => {
|
||||
if (val) {
|
||||
return val
|
||||
const validOptions = ['chokidar', 'parcel', 'chokidar-granular'] as const
|
||||
type WatcherOption = typeof validOptions[number]
|
||||
if (typeof val === 'string' && validOptions.includes(val as WatcherOption)) {
|
||||
return val as WatcherOption
|
||||
}
|
||||
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')]) as [string, string]
|
||||
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')])
|
||||
if (srcDir === rootDir) {
|
||||
return 'chokidar-granular'
|
||||
return 'chokidar-granular' as const
|
||||
}
|
||||
return 'chokidar'
|
||||
return 'chokidar' as const
|
||||
},
|
||||
},
|
||||
|
||||
@ -396,7 +420,7 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
scanPageMeta: {
|
||||
async $resolve (val, get) {
|
||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? 'after-resolve' : true)
|
||||
return typeof val === 'boolean' || val === 'after-resolve' ? val : ((await get('future')).compatibilityVersion === 4 ? 'after-resolve' : true)
|
||||
},
|
||||
},
|
||||
|
||||
@ -435,7 +459,7 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
sharedPrerenderData: {
|
||||
async $resolve (val, get) {
|
||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
||||
return typeof val === 'boolean' ? val : ((await get('future')).compatibilityVersion === 4)
|
||||
},
|
||||
},
|
||||
|
||||
@ -569,7 +593,7 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
normalizeComponentNames: {
|
||||
$resolve: async (val, get) => {
|
||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
||||
return typeof val === 'boolean' ? val : ((await get('future')).compatibilityVersion === 4)
|
||||
},
|
||||
},
|
||||
|
||||
@ -580,7 +604,9 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
spaLoadingTemplateLocation: {
|
||||
$resolve: async (val, get) => {
|
||||
return val ?? (((await get('future') as Record<string, unknown>).compatibilityVersion === 4) ? 'body' : 'within')
|
||||
const validOptions = ['body', 'within'] as const
|
||||
type SpaLoadingTemplateLocation = typeof validOptions[number]
|
||||
return typeof val === 'string' && validOptions.includes(val as SpaLoadingTemplateLocation) ? val as SpaLoadingTemplateLocation : (((await get('future')).compatibilityVersion === 4) ? 'body' : 'within')
|
||||
},
|
||||
},
|
||||
|
||||
@ -590,7 +616,7 @@ export default defineUntypedSchema({
|
||||
* @see [the Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension#tracks)
|
||||
*/
|
||||
browserDevtoolsTiming: {
|
||||
$resolve: async (val, get) => val ?? await get('dev'),
|
||||
$resolve: async (val, get) => typeof val === 'boolean' ? val : await get('dev'),
|
||||
},
|
||||
|
||||
/**
|
||||
@ -598,7 +624,7 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
debugModuleMutation: {
|
||||
$resolve: async (val, get) => {
|
||||
return val ?? Boolean(await get('debug'))
|
||||
return typeof val === 'boolean' ? val : Boolean(await get('debug'))
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
generate: {
|
||||
/**
|
||||
* The routes to generate.
|
||||
|
@ -1,3 +1,5 @@
|
||||
import type { ResolvableConfigSchema } from '../utils/definition'
|
||||
|
||||
import adhoc from './adhoc'
|
||||
import app from './app'
|
||||
import build from './build'
|
||||
@ -28,4 +30,4 @@ export default {
|
||||
...typescript,
|
||||
...vite,
|
||||
...webpack,
|
||||
}
|
||||
} satisfies ResolvableConfigSchema
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/** @private */
|
||||
_majorVersion: 3,
|
||||
/** @private */
|
||||
@ -25,7 +25,7 @@ export default defineUntypedSchema({
|
||||
appDir: '',
|
||||
/**
|
||||
* @private
|
||||
* @type {Array<{ meta: ModuleMeta; module: NuxtModule, timings?: Record<string, number | undefined>; entryPath?: string }>}
|
||||
* @type {Array<{ meta: typeof import('../src/types/module').ModuleMeta; module: typeof import('../src/types/module').NuxtModule, timings?: Record<string, number | undefined>; entryPath?: string }>}
|
||||
*/
|
||||
_installedModules: [],
|
||||
/** @private */
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import type { RuntimeConfig } from '../types/config'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Configuration for Nitro.
|
||||
* @see [Nitro configuration docs](https://nitro.unjs.io/config/)
|
||||
@ -9,8 +8,8 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
nitro: {
|
||||
runtimeConfig: {
|
||||
$resolve: async (val: Record<string, any> | undefined, get) => {
|
||||
const runtimeConfig = await get('runtimeConfig') as RuntimeConfig
|
||||
$resolve: async (val, get) => {
|
||||
const runtimeConfig = await get('runtimeConfig')
|
||||
return {
|
||||
...runtimeConfig,
|
||||
app: {
|
||||
@ -27,10 +26,12 @@ export default defineUntypedSchema({
|
||||
},
|
||||
},
|
||||
routeRules: {
|
||||
$resolve: async (val: Record<string, any> | undefined, get) => ({
|
||||
...await get('routeRules') as Record<string, any>,
|
||||
...val,
|
||||
}),
|
||||
$resolve: async (val, get) => {
|
||||
return {
|
||||
...await get('routeRules'),
|
||||
...(val && typeof val === 'object' ? val : {}),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
const ensureItemIsLast = (item: string) => (arr: string[]) => {
|
||||
const index = arr.indexOf(item)
|
||||
@ -17,7 +17,7 @@ const orderPresets = {
|
||||
},
|
||||
}
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
postcss: {
|
||||
/**
|
||||
* A strategy for ordering PostCSS plugins.
|
||||
@ -25,14 +25,20 @@ export default defineUntypedSchema({
|
||||
* @type {'cssnanoLast' | 'autoprefixerLast' | 'autoprefixerAndCssnanoLast' | string[] | ((names: string[]) => string[])}
|
||||
*/
|
||||
order: {
|
||||
$resolve: (val: string | string[] | ((plugins: string[]) => string[])): string[] | ((plugins: string[]) => string[]) => {
|
||||
$resolve: (val) => {
|
||||
if (typeof val === 'string') {
|
||||
if (!(val in orderPresets)) {
|
||||
throw new Error(`[nuxt] Unknown PostCSS order preset: ${val}`)
|
||||
}
|
||||
return orderPresets[val as keyof typeof orderPresets]
|
||||
}
|
||||
return val ?? orderPresets.autoprefixerAndCssnanoLast
|
||||
if (typeof val === 'function') {
|
||||
return val as (names: string[]) => string[]
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return val
|
||||
}
|
||||
return orderPresets.autoprefixerAndCssnanoLast
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
router: {
|
||||
/**
|
||||
* Additional router options passed to `vue-router`. On top of the options for `vue-router`,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Configuration for Nuxt's TypeScript integration.
|
||||
*
|
||||
@ -23,7 +23,17 @@ export default defineUntypedSchema({
|
||||
* @type {'vite' | 'webpack' | 'rspack' | 'shared' | false | undefined}
|
||||
*/
|
||||
builder: {
|
||||
$resolve: val => val ?? null,
|
||||
$resolve: (val) => {
|
||||
const validBuilderTypes = ['vite', 'webpack', 'rspack', 'shared'] as const
|
||||
type ValidBuilderType = typeof validBuilderTypes[number]
|
||||
if (typeof val === 'string' && validBuilderTypes.includes(val as ValidBuilderType)) {
|
||||
return val as ValidBuilderType
|
||||
}
|
||||
if (val === false) {
|
||||
return false
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { consola } from 'consola'
|
||||
import { resolve } from 'pathe'
|
||||
import { isTest } from 'std-env'
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import type { NuxtDebugOptions } from '../types/debug'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
/**
|
||||
* Configuration that will be passed directly to Vite.
|
||||
*
|
||||
@ -14,22 +13,21 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
vite: {
|
||||
root: {
|
||||
$resolve: async (val, get) => val ?? (await get('srcDir')),
|
||||
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('srcDir')),
|
||||
},
|
||||
mode: {
|
||||
$resolve: async (val, get) => val ?? (await get('dev') ? 'development' : 'production'),
|
||||
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('dev') ? 'development' : 'production'),
|
||||
},
|
||||
define: {
|
||||
$resolve: async (val: Record<string, any> | undefined, get) => {
|
||||
const [isDev, debug] = await Promise.all([get('dev'), get('debug')]) as [boolean, boolean | NuxtDebugOptions]
|
||||
|
||||
$resolve: async (_val, get) => {
|
||||
const [isDev, isDebug] = await Promise.all([get('dev'), get('debug')])
|
||||
return {
|
||||
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': Boolean(debug && (debug === true || debug.hydration)),
|
||||
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': Boolean(isDebug && (isDebug === true || isDebug.hydration)),
|
||||
'process.dev': isDev,
|
||||
'import.meta.dev': isDev,
|
||||
'process.test': isTest,
|
||||
'import.meta.test': isTest,
|
||||
...val,
|
||||
..._val && typeof _val === 'object' ? _val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -37,6 +35,7 @@ export default defineUntypedSchema({
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
publicDir: {
|
||||
// @ts-expect-error this is missing from our `vite` types deliberately, so users do not configure it
|
||||
$resolve: (val) => {
|
||||
if (val) {
|
||||
consola.warn('Directly configuring the `vite.publicDir` option is not supported. Instead, set `dir.public`. You can read more in `https://nuxt.com/docs/api/nuxt-config#public`.')
|
||||
@ -46,46 +45,51 @@ export default defineUntypedSchema({
|
||||
},
|
||||
vue: {
|
||||
isProduction: {
|
||||
$resolve: async (val, get) => val ?? !(await get('dev')),
|
||||
$resolve: async (val, get) => typeof val === 'boolean' ? val : !(await get('dev')),
|
||||
},
|
||||
template: {
|
||||
compilerOptions: {
|
||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions,
|
||||
$resolve: async (val, get) => val ?? (await get('vue')).compilerOptions,
|
||||
},
|
||||
transformAssetUrls: {
|
||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).transformAssetUrls,
|
||||
$resolve: async (val, get) => val ?? (await get('vue')).transformAssetUrls,
|
||||
},
|
||||
},
|
||||
script: {
|
||||
hoistStatic: {
|
||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions?.hoistStatic,
|
||||
$resolve: async (val, get) => typeof val === 'boolean' ? val : (await get('vue')).compilerOptions?.hoistStatic,
|
||||
},
|
||||
},
|
||||
features: {
|
||||
propsDestructure: {
|
||||
$resolve: async (val, get) => {
|
||||
if (val !== undefined && val !== null) {
|
||||
if (typeof val === 'boolean') {
|
||||
return val
|
||||
}
|
||||
const vueOptions = await get('vue') as Record<string, any> || {}
|
||||
return Boolean(vueOptions.script?.propsDestructure ?? vueOptions.propsDestructure)
|
||||
const vueOptions = await get('vue') || {}
|
||||
return Boolean(
|
||||
// @ts-expect-error TODO: remove in future: supporting a legacy schema
|
||||
vueOptions.script?.propsDestructure
|
||||
?? vueOptions.propsDestructure,
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
vueJsx: {
|
||||
$resolve: async (val: Record<string, any>, get) => {
|
||||
$resolve: async (val, get) => {
|
||||
return {
|
||||
isCustomElement: (await get('vue') as Record<string, any>).compilerOptions?.isCustomElement,
|
||||
...val,
|
||||
// TODO: investigate type divergence between types for @vue/compiler-core and @vue/babel-plugin-jsx
|
||||
isCustomElement: (await get('vue')).compilerOptions?.isCustomElement as undefined | ((tag: string) => boolean),
|
||||
...typeof val === 'object' ? val : {},
|
||||
}
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: {
|
||||
$resolve: async (val: string[] | undefined, get) => [
|
||||
...val || [],
|
||||
...(await get('build.transpile') as Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)>).filter(i => typeof i === 'string'),
|
||||
$resolve: async (val, get) => [
|
||||
...Array.isArray(val) ? val : [],
|
||||
...(await get('build.transpile')).filter(i => typeof i === 'string'),
|
||||
'vue-demi',
|
||||
],
|
||||
},
|
||||
@ -98,29 +102,29 @@ export default defineUntypedSchema({
|
||||
clearScreen: true,
|
||||
build: {
|
||||
assetsDir: {
|
||||
$resolve: async (val, get) => val ?? (await get('app') as Record<string, string>).buildAssetsDir?.replace(/^\/+/, ''),
|
||||
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('app')).buildAssetsDir?.replace(/^\/+/, ''),
|
||||
},
|
||||
emptyOutDir: false,
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
allow: {
|
||||
$resolve: async (val: string[] | undefined, get) => {
|
||||
const [buildDir, srcDir, rootDir, workspaceDir, modulesDir] = await Promise.all([get('buildDir'), get('srcDir'), get('rootDir'), get('workspaceDir'), get('modulesDir')]) as [string, string, string, string, string]
|
||||
$resolve: async (val, get) => {
|
||||
const [buildDir, srcDir, rootDir, workspaceDir, modulesDir] = await Promise.all([get('buildDir'), get('srcDir'), get('rootDir'), get('workspaceDir'), get('modulesDir')])
|
||||
return [...new Set([
|
||||
buildDir,
|
||||
srcDir,
|
||||
rootDir,
|
||||
workspaceDir,
|
||||
...(modulesDir),
|
||||
...val ?? [],
|
||||
...Array.isArray(val) ? val : [],
|
||||
])]
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cacheDir: {
|
||||
$resolve: async (val, get) => val ?? resolve(await get('rootDir') as string, 'node_modules/.cache/vite'),
|
||||
$resolve: async (val, get) => typeof val === 'string' ? val : resolve(await get('rootDir'), 'node_modules/.cache/vite'),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { defu } from 'defu'
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
import type { VueLoaderOptions } from 'vue-loader'
|
||||
import { defineResolvers } from '../utils/definition'
|
||||
|
||||
export default defineUntypedSchema({
|
||||
export default defineResolvers({
|
||||
webpack: {
|
||||
/**
|
||||
* Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them.
|
||||
@ -17,8 +16,8 @@ export default defineUntypedSchema({
|
||||
* @type {boolean | { enabled?: boolean } & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options}
|
||||
*/
|
||||
analyze: {
|
||||
$resolve: async (val: boolean | { enabled?: boolean } | Record<string, unknown>, get) => {
|
||||
const value = typeof val === 'boolean' ? { enabled: val } : val
|
||||
$resolve: async (val, get) => {
|
||||
const value = typeof val === 'boolean' ? { enabled: val } : (val && typeof val === 'object' ? val : {})
|
||||
return defu(value, await get('build.analyze') as { enabled?: boolean } | Record<string, unknown>)
|
||||
},
|
||||
},
|
||||
@ -83,7 +82,7 @@ export default defineUntypedSchema({
|
||||
* Enables CSS source map support (defaults to `true` in development).
|
||||
*/
|
||||
cssSourceMap: {
|
||||
$resolve: async (val, get) => val ?? await get('dev'),
|
||||
$resolve: async (val, get) => typeof val === 'boolean' ? val : await get('dev'),
|
||||
},
|
||||
|
||||
/**
|
||||
@ -147,7 +146,10 @@ export default defineUntypedSchema({
|
||||
for (const name of styleLoaders) {
|
||||
const loader = loaders[name]
|
||||
if (loader && loader.sourceMap === undefined) {
|
||||
loader.sourceMap = Boolean(await get('build.cssSourceMap'))
|
||||
loader.sourceMap = Boolean(
|
||||
// @ts-expect-error TODO: remove legacay configuration
|
||||
await get('build.cssSourceMap'),
|
||||
)
|
||||
}
|
||||
}
|
||||
return loaders
|
||||
@ -165,30 +167,27 @@ export default defineUntypedSchema({
|
||||
|
||||
/**
|
||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
||||
* @default
|
||||
* ```ts
|
||||
* { esModule: false }
|
||||
* ```
|
||||
*/
|
||||
file: { esModule: false },
|
||||
file: { esModule: false, limit: 1000 },
|
||||
|
||||
/**
|
||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
||||
* @default
|
||||
* ```ts
|
||||
* { esModule: false, limit: 1000 }
|
||||
* { esModule: false }
|
||||
* ```
|
||||
*/
|
||||
fontUrl: { esModule: false, limit: 1000 },
|
||||
|
||||
/**
|
||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
||||
* @default
|
||||
* ```ts
|
||||
* { esModule: false, limit: 1000 }
|
||||
* { esModule: false }
|
||||
* ```
|
||||
*/
|
||||
imgUrl: { esModule: false, limit: 1000 },
|
||||
@ -205,26 +204,38 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
vue: {
|
||||
transformAssetUrls: {
|
||||
$resolve: async (val, get) => (val ?? (await get('vue.transformAssetUrls'))) as VueLoaderOptions['transformAssetUrls'],
|
||||
$resolve: async (val, get) => (val ?? (await get('vue.transformAssetUrls'))),
|
||||
},
|
||||
compilerOptions: {
|
||||
$resolve: async (val, get) => (val ?? (await get('vue.compilerOptions'))) as VueLoaderOptions['compilerOptions'],
|
||||
$resolve: async (val, get) => (val ?? (await get('vue.compilerOptions'))),
|
||||
},
|
||||
propsDestructure: {
|
||||
$resolve: async (val, get) => Boolean(val ?? await get('vue.propsDestructure')),
|
||||
},
|
||||
} satisfies { [K in keyof VueLoaderOptions]: { $resolve: (val: unknown, get: (id: string) => Promise<unknown>) => Promise<VueLoaderOptions[K]> } },
|
||||
},
|
||||
|
||||
/**
|
||||
* See [css-loader](https://github.com/webpack-contrib/css-loader) for available options.
|
||||
*/
|
||||
css: {
|
||||
importLoaders: 0,
|
||||
/**
|
||||
* @type {boolean | { filter: (url: string, resourcePath: string) => boolean }}
|
||||
*/
|
||||
url: {
|
||||
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
||||
},
|
||||
esModule: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* See [css-loader](https://github.com/webpack-contrib/css-loader) for available options.
|
||||
*/
|
||||
cssModules: {
|
||||
importLoaders: 0,
|
||||
/**
|
||||
* @type {boolean | { filter: (url: string, resourcePath: string) => boolean }}
|
||||
*/
|
||||
url: {
|
||||
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
||||
},
|
||||
@ -241,7 +252,6 @@ export default defineUntypedSchema({
|
||||
|
||||
/**
|
||||
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
||||
* @type {typeof import('sass-loader')['Options']}
|
||||
* @default
|
||||
* ```ts
|
||||
* {
|
||||
@ -259,7 +269,6 @@ export default defineUntypedSchema({
|
||||
|
||||
/**
|
||||
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
||||
* @type {typeof import('sass-loader')['Options']}
|
||||
*/
|
||||
scss: {},
|
||||
|
||||
@ -300,7 +309,14 @@ export default defineUntypedSchema({
|
||||
* @type {false | typeof import('css-minimizer-webpack-plugin').BasePluginOptions & typeof import('css-minimizer-webpack-plugin').DefinedDefaultMinimizerAndOptions<any>}
|
||||
*/
|
||||
optimizeCSS: {
|
||||
$resolve: async (val, get) => val ?? (await get('build.extractCSS') ? {} : false),
|
||||
$resolve: async (val, get) => {
|
||||
if (val === false || (val && typeof val === 'object')) {
|
||||
return val
|
||||
}
|
||||
// @ts-expect-error TODO: remove legacy configuration
|
||||
const extractCSS = await get('build.extractCSS')
|
||||
return extractCSS ? {} : false
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -310,7 +326,9 @@ export default defineUntypedSchema({
|
||||
optimization: {
|
||||
runtimeChunk: 'single',
|
||||
/** Set minimize to `false` to disable all minimizers. (It is disabled in development by default). */
|
||||
minimize: { $resolve: async (val, get) => val ?? !(await get('dev')) },
|
||||
minimize: {
|
||||
$resolve: async (val, get) => typeof val === 'boolean' ? val : !(await get('dev')),
|
||||
},
|
||||
/** You can set minimizer to a customized array of plugins. */
|
||||
minimizer: undefined,
|
||||
splitChunks: {
|
||||
@ -323,15 +341,12 @@ export default defineUntypedSchema({
|
||||
/**
|
||||
* Customize PostCSS Loader.
|
||||
* same options as [`postcss-loader` options](https://github.com/webpack-contrib/postcss-loader#options)
|
||||
* @type {{ execute?: boolean, postcssOptions: typeof import('postcss').ProcessOptions, sourceMap?: boolean, implementation?: any }}
|
||||
* @type {{ execute?: boolean, postcssOptions: typeof import('postcss').ProcessOptions & { plugins: Record<string, unknown> & { autoprefixer?: typeof import('autoprefixer').Options; cssnano?: typeof import('cssnano').Options } }, sourceMap?: boolean, implementation?: any }}
|
||||
*/
|
||||
postcss: {
|
||||
postcssOptions: {
|
||||
config: {
|
||||
$resolve: async (val, get) => val ?? (await get('postcss.config')),
|
||||
},
|
||||
plugins: {
|
||||
$resolve: async (val, get) => val ?? (await get('postcss.plugins')),
|
||||
$resolve: async (val, get) => val && typeof val === 'object' ? val : (await get('postcss.plugins')),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -21,6 +21,12 @@ export interface ModuleMeta {
|
||||
*/
|
||||
compatibility?: NuxtCompatibility
|
||||
|
||||
/**
|
||||
* Fully resolved path used internally by Nuxt. Do not depend on this value.
|
||||
* @internal
|
||||
*/
|
||||
rawPath?: string
|
||||
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
|
56
packages/schema/src/utils/definition.ts
Normal file
56
packages/schema/src/utils/definition.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import type { InputObject } from 'untyped'
|
||||
|
||||
import { defineUntypedSchema } from 'untyped'
|
||||
|
||||
import type { ConfigSchema } from '../../schema/config'
|
||||
|
||||
type KeysOf<T, Prefix extends string | unknown = unknown> = keyof T extends string
|
||||
?
|
||||
{
|
||||
[K in keyof T]: K extends string
|
||||
? string extends K
|
||||
? never // exclude generic 'string' type
|
||||
: unknown extends Prefix
|
||||
? `${K | KeysOf<T[K], K>}`
|
||||
: Prefix extends string
|
||||
? `${Prefix}.${K | KeysOf<T[K], `hey.${Prefix}.${K}`>}`
|
||||
: never
|
||||
: never
|
||||
}[keyof T]
|
||||
: never
|
||||
|
||||
type ReturnFromKey<T, K extends string> = keyof T extends string
|
||||
? K extends keyof T
|
||||
? T[K]
|
||||
: K extends `${keyof T}.${string}`
|
||||
? K extends `${infer Prefix}.${string}`
|
||||
? Prefix extends keyof T
|
||||
? K extends `${Prefix}.${infer Suffix}`
|
||||
? ReturnFromKey<T[Prefix], Suffix>
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
type Awaitable<T> = T | Promise<T>
|
||||
|
||||
interface Resolvers<ReturnValue> {
|
||||
$resolve: (val: unknown, get: <K extends KeysOf<ConfigSchema>>(key: K) => Promise<ReturnFromKey<ConfigSchema, K>>) => Awaitable<ReturnValue>
|
||||
$schema?: InputObject['$schema']
|
||||
$default?: ReturnValue
|
||||
}
|
||||
|
||||
type Resolvable<Namespace> = keyof Exclude<NonNullable<Namespace>, boolean | string | (() => any)> extends string
|
||||
? {
|
||||
[K in keyof Namespace]: Partial<Resolvable<Namespace[K]>> | Resolvers<Namespace[K]>
|
||||
} | Namespace
|
||||
: Namespace | Resolvers<Namespace>
|
||||
|
||||
export function defineResolvers<C extends Partial<Resolvable<ConfigSchema>>> (config: C) {
|
||||
return defineUntypedSchema(config) /* as C */
|
||||
}
|
||||
|
||||
export type ResolvableConfigSchema = Partial<Resolvable<ConfigSchema>>
|
||||
|
||||
export { defineUntypedSchema } from 'untyped'
|
@ -708,6 +708,15 @@ importers:
|
||||
'@types/pug':
|
||||
specifier: 2.0.10
|
||||
version: 2.0.10
|
||||
'@types/rollup-plugin-visualizer':
|
||||
specifier: 4.2.4
|
||||
version: 4.2.4
|
||||
'@types/webpack-bundle-analyzer':
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0
|
||||
'@types/webpack-hot-middleware':
|
||||
specifier: 2.25.9
|
||||
version: 2.25.9
|
||||
'@unhead/schema':
|
||||
specifier: 1.11.18
|
||||
version: 1.11.18
|
||||
@ -735,6 +744,9 @@ importers:
|
||||
compatx:
|
||||
specifier: 0.1.8
|
||||
version: 0.1.8
|
||||
css-minimizer-webpack-plugin:
|
||||
specifier: 7.0.0
|
||||
version: 7.0.0(webpack@5.96.1)
|
||||
esbuild-loader:
|
||||
specifier: 4.2.2
|
||||
version: 4.2.2(webpack@5.96.1)
|
||||
@ -750,6 +762,9 @@ importers:
|
||||
ignore:
|
||||
specifier: 7.0.3
|
||||
version: 7.0.3
|
||||
mini-css-extract-plugin:
|
||||
specifier: 2.9.2
|
||||
version: 2.9.2(webpack@5.96.1)
|
||||
nitropack:
|
||||
specifier: 2.10.4
|
||||
version: 2.10.4(typescript@5.7.3)
|
||||
@ -759,6 +774,9 @@ importers:
|
||||
pkg-types:
|
||||
specifier: 1.3.1
|
||||
version: 1.3.1
|
||||
postcss:
|
||||
specifier: 8.5.1
|
||||
version: 8.5.1
|
||||
sass-loader:
|
||||
specifier: 16.0.4
|
||||
version: 16.0.4(@rspack/core@1.2.2)(webpack@5.96.1)
|
||||
@ -2966,6 +2984,9 @@ packages:
|
||||
'@types/resolve@1.20.2':
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
|
||||
'@types/rollup-plugin-visualizer@4.2.4':
|
||||
resolution: {integrity: sha512-BW4Q6D1Qy5gno5qHWrnMDC2dOe/TAKXvqCpckOggCCu+XpS+ZZJJ1lq1+K3bvYccoO3Y7f5kglbFAgYGqCgULg==}
|
||||
|
||||
'@types/semver@7.5.8':
|
||||
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
|
||||
|
||||
@ -9892,6 +9913,10 @@ snapshots:
|
||||
|
||||
'@types/resolve@1.20.2': {}
|
||||
|
||||
'@types/rollup-plugin-visualizer@4.2.4':
|
||||
dependencies:
|
||||
rollup: 4.34.6
|
||||
|
||||
'@types/semver@7.5.8': {}
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
Loading…
Reference in New Issue
Block a user