diff --git a/src/settings/settings.test.ts b/src/settings/settings.test.ts index 4cfca9b..6d94c2b 100644 --- a/src/settings/settings.test.ts +++ b/src/settings/settings.test.ts @@ -23,4 +23,15 @@ describe('settings', () => { settings.setLocale(LOCALE); expect(settings.getLocale()).toEqual(LOCALE); }); + + it('should clone locale data with function values', () => { + settings.setLocale('en'); + + const localeData = settings.getLocaleData(); + const nextLocaleData = settings.getLocaleData(); + + expect(typeof localeData.ordinal).toBe('function'); + expect(localeData).not.toBe(nextLocaleData); + expect(localeData.formats).not.toBe(nextLocaleData.formats); + }); }); diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 0b532be..3659b90 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -5,6 +5,23 @@ import {normalizeTimeZone} from '../timeZone'; import {localeLoaders} from './locales'; import type {Locale, Parser, PublicSettings, UpdateLocaleConfig} from './types'; +function cloneLocaleData(value: T): T { + if (!value || typeof value !== 'object') { + return value; + } + + if (Array.isArray(value)) { + return value.map((item) => cloneLocaleData(item)) as T; + } + + // Dayjs locale data contains function fields such as ordinal. + // Clone array/plain-object containers so callers can safely mutate the result, + // while keeping locale functions callable by reference. + return Object.fromEntries( + Object.entries(value).map(([key, item]) => [key, cloneLocaleData(item)]), + ) as T; +} + class Settings implements PublicSettings { // 'en' - preloaded locale in dayjs private loadedLocales = new Set(['en']); @@ -50,7 +67,7 @@ class Settings implements PublicSettings { throw new Error('There is something really wrong happening. Locale data is absent.'); } - return structuredClone(localeObject) as Locale; + return cloneLocaleData(localeObject) as Locale; } setLocale(locale: string) {