Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/settings/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,15 @@ describe('settings', () => {
settings.setLocale(LOCALE);
expect(settings.getLocale()).toEqual(LOCALE);
});

it('should clone locale data with function values', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test runs against whatever locale the previous test left set (de) rather than a locale it sets itself. It passes regardless since en also has ordinal + formats, but calling settings.setLocale('en') at the start would make it self-contained and order-independent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 589af2a: added settings.setLocale('en') at the start of this test, so it is self-contained and order-independent.

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);
});
});
19 changes: 18 additions & 1 deletion src/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ import {normalizeTimeZone} from '../timeZone';
import {localeLoaders} from './locales';
import type {Locale, Parser, PublicSettings, UpdateLocaleConfig} from './types';

function cloneLocaleData<T>(value: T): T {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a one-line comment that this helper intentionally assumes locale data is function-or-JSON-shaped. Unlike structuredClone, it would stack-overflow on a circular reference and would flatten Date/RegExp/Map/Set to {}. None of those exist in dayjs locale objects today (only strings/numbers/arrays/plain objects/functions), so nothing to fix here — just a note so a future locale field doesn't silently get mangled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 589af2a: added a one-line note that this helper is specific to Dayjs JSON-like locale data with function fields and is not a general structuredClone replacement.

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']);
Expand Down Expand Up @@ -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) {
Expand Down
Loading