Repository: nuxt/nuxt
File: packages/kit/src/loader/config.ts (TypeScript)

Code before:
import type { ConfigLayer, ConfigLayerMeta, LoadConfigOptions } from 'c12'
import { loadConfig } from 'c12'
import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
import { globby } from 'globby'
import defu, { createDefu } from 'defu'
import { basename, join, relative } from 'pathe'
import { resolveModuleURL } from 'exsolve'

import { directoryToURL } from '../internal/esm'

export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig>, 'overrides'> {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
  overrides?: Exclude<LoadConfigOptions<NuxtConfig>['overrides'], Promise<any> | Function>
}

const merger = createDefu((obj, key, value) => {
  if (Array.isArray(obj[key])) {
    if (Array.isArray(value)) {
      obj[key] = obj[key].concat(value)
      return true
    }

    if (typeof value === 'function') {
      obj[key] = value(obj[key])
      return true
    }
  }
})

export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
  // Automatically detect and import layers from `~~/layers/` directory
  const localLayers = await globby('layers/*', { onlyDirectories: true, cwd: opts.cwd || process.cwd() })
  opts.overrides = defu(opts.overrides, { _extends: localLayers })

  const globalSelf = globalThis as any
  globalSelf.defineNuxtConfig = (c: any) => c
  const { configFile, layers = [], cwd, config: nuxtConfig, meta } = await loadConfig<NuxtConfig>({
    name: 'nuxt',
    configFile: 'nuxt.config',
    rcFile: '.nuxtrc',
    extend: { extendKey: ['theme', 'extends', '_extends'] },
    dotenv: true,
    globalRc: true,
    merger: merger as any, // TODO: fix type in c12, it should accept createDefu directly
    ...opts,
  })
  delete globalSelf.defineNuxtConfig

  // Fill config
  nuxtConfig.rootDir ||= cwd
  nuxtConfig._nuxtConfigFile = configFile

Review comment (suggestion):
I am not sure this is always safe as some options actually take functions, for example in `vite.build`.

Code after:
import type { ConfigLayer, ConfigLayerMeta, LoadConfigOptions } from 'c12'
import { loadConfig } from 'c12'
import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
import { globby } from 'globby'
import defu, { createDefu } from 'defu'
import { basename, join, relative } from 'pathe'
import { resolveModuleURL } from 'exsolve'

import { directoryToURL } from '../internal/esm'

export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig>, 'overrides'> {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
  overrides?: Exclude<LoadConfigOptions<NuxtConfig>['overrides'], Promise<any> | Function>
}

const merger = createDefu((obj, key, value) => {
  if (Array.isArray(obj[key]) && Array.isArray(value)) {
    obj[key] = obj[key].concat(value)
    return true
  }
})

export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
  // Automatically detect and import layers from `~~/layers/` directory
  const localLayers = await globby('layers/*', { onlyDirectories: true, cwd: opts.cwd || process.cwd() })
  opts.overrides = defu(opts.overrides, { _extends: localLayers })

  const globalSelf = globalThis as any
  globalSelf.defineNuxtConfig = (c: any) => c
  const { configFile, layers = [], cwd, config: nuxtConfig, meta } = await loadConfig<NuxtConfig>({
    name: 'nuxt',
    configFile: 'nuxt.config',
    rcFile: '.nuxtrc',
    extend: { extendKey: ['theme', '_extends', 'extends'] },
    dotenv: true,
    globalRc: true,
    merger: merger as any, // TODO: fix type in c12, it should accept createDefu directly
    ...opts,
  })
  delete globalSelf.defineNuxtConfig

  // Fill config
  nuxtConfig.rootDir ||= cwd
  nuxtConfig._nuxtConfigFile = configFile
  nuxtConfig._nuxtConfigFiles = [configFile]
  nuxtConfig.alias ||= {}

  if (meta?.name) {
    const alias = `#layers/${meta.name}`
    nuxtConfig.alias[alias] ||= nuxtConfig.rootDir
  }