From 3c4949acfbafe2f9f6e70eb60a0451308abfc1a3 Mon Sep 17 00:00:00 2001 From: Gianluca Di Francesco Date: Mon, 2 Sep 2024 13:12:58 +0200 Subject: [PATCH] fix(nuxt): allow updating `appConfig` with non-iterable objects (#28773) --- packages/nuxt/src/app/config.ts | 13 +++++++++-- test/nuxt/composables.test.ts | 38 +++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/packages/nuxt/src/app/config.ts b/packages/nuxt/src/app/config.ts index 4be4349fc4..476f828abf 100644 --- a/packages/nuxt/src/app/config.ts +++ b/packages/nuxt/src/app/config.ts @@ -11,6 +11,15 @@ type DeepPartial = T extends Function ? T : T extends Record ? { // Workaround for vite HMR with virtual modules export const _getAppConfig = () => __appConfig as AppConfig +function isPojoOrArray (val: unknown): val is object { + return ( + Array.isArray(val) || + (!!val && + typeof val === 'object' && + val.constructor?.name === 'Object') + ) +} + function deepDelete (obj: any, newObj: any) { for (const key in obj) { const val = newObj[key] @@ -18,7 +27,7 @@ function deepDelete (obj: any, newObj: any) { delete (obj as any)[key] } - if (val !== null && typeof val === 'object') { + if (isPojoOrArray(val)) { deepDelete(obj[key], newObj[key]) } } @@ -27,7 +36,7 @@ function deepDelete (obj: any, newObj: any) { function deepAssign (obj: any, newObj: any) { for (const key in newObj) { const val = newObj[key] - if (val !== null && typeof val === 'object') { + if (isPojoOrArray(val)) { const defaultVal = Array.isArray(val) ? [] : {} obj[key] = obj[key] || defaultVal deepAssign(obj[key], val) diff --git a/test/nuxt/composables.test.ts b/test/nuxt/composables.test.ts index bd8865d028..364279f7eb 100644 --- a/test/nuxt/composables.test.ts +++ b/test/nuxt/composables.test.ts @@ -33,23 +33,33 @@ registerEndpoint('/api/test', defineEventHandler(event => ({ describe('app config', () => { it('can be updated', () => { const appConfig = useAppConfig() - expect(appConfig).toMatchInlineSnapshot(` - { - "nuxt": {}, - } - `) - updateAppConfig({ + expect(appConfig).toStrictEqual({ nuxt: {} }) + + type UpdateAppConfig = Parameters[0] + + const initConfig: UpdateAppConfig = { new: 'value', nuxt: { nested: 42 }, + regExp: /foo/g, + date: new Date(1111, 11, 11), + arr: [1, 2, 3], + } + updateAppConfig(initConfig) + expect(appConfig).toStrictEqual(initConfig) + + const newConfig: UpdateAppConfig = { + nuxt: { anotherNested: 24 }, + regExp: /bar/g, + date: new Date(2222, 12, 12), + arr: [4, 5], + } + updateAppConfig(newConfig) + expect(appConfig).toStrictEqual({ + ...initConfig, + ...newConfig, + nuxt: { ...initConfig.nuxt, ...newConfig.nuxt }, + arr: [4, 5, 3], }) - expect(appConfig).toMatchInlineSnapshot(` - { - "new": "value", - "nuxt": { - "nested": 42, - }, - } - `) }) })