diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 6d4d62524e..20d59295dc 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -4,7 +4,7 @@ import ignore from 'ignore' import type { LoadNuxtOptions } from '@nuxt/kit' import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' import { resolvePath as _resolvePath } from 'mlly' -import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions, RuntimeConfig } from 'nuxt/schema' +import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema' import type { PackageJson } from 'pkg-types' import { readPackageJSON, resolvePackageJSON } from 'pkg-types' import { hash } from 'ohash' @@ -645,8 +645,22 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise { options._modules.push('@nuxt/telemetry') } - // Ensure we share runtime config between Nuxt and Nitro - options.runtimeConfig = options.nitro.runtimeConfig as RuntimeConfig + // Ensure we share key config between Nuxt and Nitro + createPortalProperties(options.nitro.runtimeConfig, options, ['nitro.runtimeConfig', 'runtimeConfig']) + createPortalProperties(options.nitro.routeRules, options, ['nitro.routeRules', 'routeRules']) + + // prevent replacement of options.nitro + const nitroOptions = options.nitro + Object.defineProperties(options, { + nitro: { + configurable: false, + enumerable: true, + get: () => nitroOptions, + set (value) { + Object.assign(nitroOptions, value) + }, + }, + }) const nuxt = createNuxt(options) @@ -695,3 +709,31 @@ function deduplicateArray (maybeArray: T): T { } return fresh as T } + +function createPortalProperties (sourceValue: any, options: NuxtOptions, paths: string[]) { + let sharedValue = sourceValue + + for (const path of paths) { + const segments = path.split('.') + const key = segments.pop()! + let parent: Record = options + + while (segments.length) { + const key = segments.shift()! + parent = parent[key] || (parent[key] = {}) + } + + delete parent[key] + + Object.defineProperties(parent, { + [key]: { + configurable: false, + enumerable: true, + get: () => sharedValue, + set (value) { + sharedValue = value + }, + }, + }) + } +} diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index 08f427b2a6..320f957777 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -1,5 +1,6 @@ import { addBuildPlugin, addComponent } from 'nuxt/kit' import type { NuxtPage } from 'nuxt/schema' +import { defu } from 'defu' import { createUnplugin } from 'unplugin' import { withoutLeadingSlash } from 'ufo' @@ -88,10 +89,17 @@ export default defineNuxtConfig({ runtimeConfig: { public: { needsFallback: undefined, - testConfig: 123, }, }, modules: [ + function (_options, nuxt) { + // ensure setting `runtimeConfig` also sets `nitro.runtimeConfig` + nuxt.options.runtimeConfig = defu(nuxt.options.runtimeConfig, { + public: { + testConfig: 123, + }, + }) + }, function (_options, nuxt) { nuxt.hook('modules:done', () => { // @ts-expect-error not valid nuxt option