- theme runtimeConfig
+ appConfig:
+ {{ JSON.stringify(appConfig, null, 2) }}
+ runtimeConfig:
{{ JSON.stringify(themeConfig, null, 2) }}
Base Button
Fancy Button
diff --git a/packages/nuxi/src/commands/dev.ts b/packages/nuxi/src/commands/dev.ts
index b7dc1767fed..d63de0dbe06 100644
--- a/packages/nuxi/src/commands/dev.ts
+++ b/packages/nuxi/src/commands/dev.ts
@@ -132,7 +132,7 @@ export default defineNuxtCommand({
dLoad(true, `Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
}
} else if (isFileChange) {
- if (file.match(/(app|error)\.(js|ts|mjs|jsx|tsx|vue)$/)) {
+ if (file.match(/(app|error|app\.config)\.(js|ts|mjs|jsx|tsx|vue)$/)) {
dLoad(true, `\`${relativePath}\` ${event === 'add' ? 'created' : 'removed'}`)
}
}
diff --git a/packages/nuxt/src/app/config.ts b/packages/nuxt/src/app/config.ts
new file mode 100644
index 00000000000..d8f97df8174
--- /dev/null
+++ b/packages/nuxt/src/app/config.ts
@@ -0,0 +1,48 @@
+import type { AppConfig } from '@nuxt/schema'
+import { reactive } from 'vue'
+import { useNuxtApp } from './nuxt'
+// @ts-ignore
+import __appConfig from '#build/app.config.mjs'
+
+// Workaround for vite HMR with virtual modules
+export const _getAppConfig = () => __appConfig as AppConfig
+
+export function useAppConfig (): AppConfig {
+ const nuxtApp = useNuxtApp()
+ if (!nuxtApp._appConfig) {
+ nuxtApp._appConfig = reactive(__appConfig) as AppConfig
+ }
+ return nuxtApp._appConfig
+}
+
+// HMR Support
+if (process.dev) {
+ function applyHMR (newConfig: AppConfig) {
+ const appConfig = useAppConfig()
+ if (newConfig && appConfig) {
+ for (const key in newConfig) {
+ (appConfig as any)[key] = (newConfig as any)[key]
+ }
+ for (const key in appConfig) {
+ if (!(key in newConfig)) {
+ delete (appConfig as any)[key]
+ }
+ }
+ }
+ }
+
+ // Vite
+ if (import.meta.hot) {
+ import.meta.hot.accept((newModule) => {
+ const newConfig = newModule._getAppConfig()
+ applyHMR(newConfig)
+ })
+ }
+
+ // Webpack
+ if (import.meta.webpackHot) {
+ import.meta.webpackHot.accept('#build/app.config.mjs', () => {
+ applyHMR(__appConfig)
+ })
+ }
+}
diff --git a/packages/nuxt/src/app/index.ts b/packages/nuxt/src/app/index.ts
index b229b5e33e9..b32deafde28 100644
--- a/packages/nuxt/src/app/index.ts
+++ b/packages/nuxt/src/app/index.ts
@@ -3,6 +3,7 @@
export * from './nuxt'
export * from './composables'
export * from './components'
+export * from './config'
// eslint-disable-next-line import/no-restricted-paths
export type { PageMeta } from '../pages/runtime'
diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts
index b24bda3c8d5..458d92b732c 100644
--- a/packages/nuxt/src/app/nuxt.ts
+++ b/packages/nuxt/src/app/nuxt.ts
@@ -2,7 +2,7 @@
import { getCurrentInstance, reactive } from 'vue'
import type { App, onErrorCaptured, VNode } from 'vue'
import { createHooks, Hookable } from 'hookable'
-import type { RuntimeConfig } from '@nuxt/schema'
+import type { RuntimeConfig, AppConfigInput } from '@nuxt/schema'
import { getContext } from 'unctx'
import type { SSRContext } from 'vue-bundle-renderer/runtime'
import type { CompatibilityEvent } from 'h3'
@@ -281,3 +281,7 @@ export function useRuntimeConfig (): RuntimeConfig {
function defineGetter (obj: Record, key: K, val: V) {
Object.defineProperty(obj, key, { get: () => val })
}
+
+export function defineAppConfig (config: C): C {
+ return config
+}
diff --git a/packages/nuxt/src/auto-imports/presets.ts b/packages/nuxt/src/auto-imports/presets.ts
index 81d94856c4f..e9ca159bfda 100644
--- a/packages/nuxt/src/auto-imports/presets.ts
+++ b/packages/nuxt/src/auto-imports/presets.ts
@@ -1,6 +1,6 @@
import { defineUnimportPreset, Preset } from 'unimport'
-export const commonPresets: Preset[] = [
+const commonPresets: Preset[] = [
// #head
defineUnimportPreset({
from: '#head',
@@ -19,7 +19,7 @@ export const commonPresets: Preset[] = [
})
]
-export const appPreset = defineUnimportPreset({
+const appPreset = defineUnimportPreset({
from: '#app',
imports: [
'useAsyncData',
@@ -49,12 +49,14 @@ export const appPreset = defineUnimportPreset({
'isNuxtError',
'useError',
'createError',
- 'defineNuxtLink'
+ 'defineNuxtLink',
+ 'useAppConfig',
+ 'defineAppConfig'
]
})
// vue
-export const vuePreset = defineUnimportPreset({
+const vuePreset = defineUnimportPreset({
from: 'vue',
imports: [
//
diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts
index 85e5f89ee0d..618bc6310f7 100644
--- a/test/fixtures/basic/types.ts
+++ b/test/fixtures/basic/types.ts
@@ -1,6 +1,7 @@
import { expectTypeOf } from 'expect-type'
import { describe, it } from 'vitest'
import type { Ref } from 'vue'
+import type { AppConfig } from '@nuxt/schema'
import { NavigationFailure, RouteLocationNormalizedLoaded, RouteLocationRaw, useRouter as vueUseRouter } from 'vue-router'
import { defineNuxtConfig } from '~~/../../../packages/nuxt/src'
@@ -165,3 +166,17 @@ describe('composables', () => {
.toEqualTypeOf(useLazyAsyncData(() => Promise.resolve({ foo: Math.random() }), { transform: data => data.foo }))
})
})
+
+describe('app config', () => {
+ it('merges app config as expected', () => {
+ interface ExpectedMergedAppConfig {
+ fromLayer: boolean,
+ fromNuxtConfig: boolean,
+ nested: {
+ val: number
+ },
+ userConfig: number
+ }
+ expectTypeOf().toMatchTypeOf()
+ })
+})