From e29d1e5e65e14860d127dbf270dde4f7b1584053 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Mon, 22 Jun 2026 17:13:44 +0300 Subject: [PATCH 1/3] fix: clone settings locale data --- src/settings/settings.test.ts | 9 +++++++++ src/settings/settings.ts | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/settings/settings.test.ts b/src/settings/settings.test.ts index 4cfca9b..4aaff3b 100644 --- a/src/settings/settings.test.ts +++ b/src/settings/settings.test.ts @@ -23,4 +23,13 @@ describe('settings', () => { settings.setLocale(LOCALE); expect(settings.getLocale()).toEqual(LOCALE); }); + + it('should clone locale data with function values', () => { + 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..ca199c2 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -5,6 +5,20 @@ 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; + } + + 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 +64,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) { From 589af2ae33bdff1306ee3564ada67df2adb9ed7a Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 24 Jun 2026 10:46:53 +0300 Subject: [PATCH 2/3] fix: address locale data review feedback --- src/settings/settings.test.ts | 2 ++ src/settings/settings.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/settings/settings.test.ts b/src/settings/settings.test.ts index 4aaff3b..6d94c2b 100644 --- a/src/settings/settings.test.ts +++ b/src/settings/settings.test.ts @@ -25,6 +25,8 @@ describe('settings', () => { }); it('should clone locale data with function values', () => { + settings.setLocale('en'); + const localeData = settings.getLocaleData(); const nextLocaleData = settings.getLocaleData(); diff --git a/src/settings/settings.ts b/src/settings/settings.ts index ca199c2..0d281fe 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -14,6 +14,7 @@ function cloneLocaleData(value: T): T { return value.map((item) => cloneLocaleData(item)) as T; } + // Dayjs locale data is JSON-like with function fields; this is not a general structuredClone replacement. return Object.fromEntries( Object.entries(value).map(([key, item]) => [key, cloneLocaleData(item)]), ) as T; From 27b66696686db82910d9e4e804029a322e2b36ca Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 24 Jun 2026 11:01:26 +0300 Subject: [PATCH 3/3] docs: clarify locale data clone comment --- src/settings/settings.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 0d281fe..3659b90 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -14,7 +14,9 @@ function cloneLocaleData(value: T): T { return value.map((item) => cloneLocaleData(item)) as T; } - // Dayjs locale data is JSON-like with function fields; this is not a general structuredClone replacement. + // 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;