From 8a0bbf5faa03c294d308a13fe210ba6aaeef6968 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sun, 23 Apr 2023 18:51:27 +0200 Subject: feat: introduce locale proxy (#2004) --- src/locale-proxy.ts | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/locale-proxy.ts (limited to 'src/locale-proxy.ts') diff --git a/src/locale-proxy.ts b/src/locale-proxy.ts new file mode 100644 index 00000000..c5c6aa1b --- /dev/null +++ b/src/locale-proxy.ts @@ -0,0 +1,91 @@ +import type { LocaleDefinition } from './definitions'; +import { FakerError } from './errors/faker-error'; + +/** + * A proxy for LocaleDefinitions that marks all properties as required and throws an error when an entry is accessed that is not defined. + */ +export type LocaleProxy = Readonly<{ + [key in keyof LocaleDefinition]-?: Readonly< + Required> + >; +}>; + +const throwReadOnlyError: () => never = () => { + throw new FakerError('You cannot edit the locale data on the faker instance'); +}; + +/** + * Creates a proxy for LocaleDefinition that throws an error if an undefined property is accessed. + * + * @param locale The locale definition to create the proxy for. + */ +export function createLocaleProxy(locale: LocaleDefinition): LocaleProxy { + const proxies = {} as LocaleDefinition; + return new Proxy(locale, { + has(): true { + // Categories are always present (proxied), that's why we return true. + return true; + }, + + get( + target: LocaleDefinition, + categoryName: keyof LocaleDefinition + ): LocaleDefinition[keyof LocaleDefinition] { + if (categoryName in proxies) { + return proxies[categoryName]; + } + + return (proxies[categoryName] = createCategoryProxy( + categoryName, + target[categoryName] + )); + }, + + set: throwReadOnlyError, + deleteProperty: throwReadOnlyError, + }) as LocaleProxy; +} + +/** + * Creates a proxy for a category that throws an error when accessing an undefined property. + * + * @param categoryName The name of the category. + * @param categoryData The module to create the proxy for. + */ +function createCategoryProxy< + CategoryData extends Record +>( + categoryName: string, + categoryData: CategoryData = {} as CategoryData +): Required { + return new Proxy(categoryData, { + has(target: CategoryData, entryName: keyof CategoryData): boolean { + const value = target[entryName]; + return value != null; + }, + + get( + target: CategoryData, + entryName: keyof CategoryData + ): CategoryData[keyof CategoryData] { + const value = target[entryName]; + if (value === null) { + throw new FakerError( + `The locale data for '${categoryName}.${entryName.toString()}' aren't applicable to this locale. + If you think this is a bug, please report it at: https://github.com/faker-js/faker` + ); + } else if (value === undefined) { + throw new FakerError( + `The locale data for '${categoryName}.${entryName.toString()}' are missing in this locale. + Please contribute the missing data to the project or use a locale/Faker instance that has these data. + For more information see https://next.fakerjs.dev/guide/localization.html` + ); + } else { + return value; + } + }, + + set: throwReadOnlyError, + deleteProperty: throwReadOnlyError, + }) as Required; +} -- cgit v1.2.3