diff options
| author | DivisionByZero <[email protected]> | 2024-09-26 17:50:05 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-26 17:50:05 +0200 |
| commit | 9537dfddba882bd93d9a429697fd44bc72428426 (patch) | |
| tree | f60b5b694d5e4e8ddab228aeb20c7b7746685edc /src/internal | |
| parent | 424b120a4d94b15b6e77c04a0aaffd0016a9c870 (diff) | |
| download | faker-9537dfddba882bd93d9a429697fd44bc72428426.tar.xz faker-9537dfddba882bd93d9a429697fd44bc72428426.zip | |
infra: update file structure for util/internal (#3141)
Diffstat (limited to 'src/internal')
| -rw-r--r-- | src/internal/locale-proxy.ts | 115 | ||||
| -rw-r--r-- | src/internal/mersenne.ts | 48 | ||||
| -rw-r--r-- | src/internal/types.ts | 39 |
3 files changed, 154 insertions, 48 deletions
diff --git a/src/internal/locale-proxy.ts b/src/internal/locale-proxy.ts new file mode 100644 index 00000000..dbb9f277 --- /dev/null +++ b/src/internal/locale-proxy.ts @@ -0,0 +1,115 @@ +import type { LocaleDefinition } from '../definitions'; +import { FakerError } from '../errors/faker-error'; + +/** + * A proxy for LocaleDefinition 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]-?: LocaleProxyCategory<LocaleDefinition[key]>; +}>; + +type LocaleProxyCategory<T> = Readonly<{ + [key in keyof T]-?: LocaleProxyEntry<T[key]>; +}>; + +type LocaleProxyEntry<T> = unknown extends T ? T : Readonly<NonNullable<T>>; + +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 (typeof categoryName === 'symbol' || categoryName === 'nodeType') { + return target[categoryName]; + } + + if (categoryName in proxies) { + return proxies[categoryName]; + } + + return (proxies[categoryName] = createCategoryProxy( + categoryName, + target[categoryName] + )); + }, + + set: throwReadOnlyError, + deleteProperty: throwReadOnlyError, + }) as LocaleProxy; +} + +/** + * Checks that the value is not null or undefined and throws an error if it is. + * + * @param value The value to check. + * @param path The path to the locale data. + */ +export function assertLocaleData<T>( + value: T, + ...path: string[] +): asserts value is NonNullable<T> { + if (value === null) { + throw new FakerError( + `The locale data for '${path.join('.')}' 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 '${path.join('.')}' 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://fakerjs.dev/guide/localization.html` + ); + } +} + +/** + * 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< + TCategoryData extends Record<string | symbol, unknown>, +>( + categoryName: string, + categoryData: TCategoryData = {} as TCategoryData +): Required<TCategoryData> { + return new Proxy(categoryData, { + has(target: TCategoryData, entryName: keyof TCategoryData): boolean { + const value = target[entryName]; + return value != null; + }, + + get( + target: TCategoryData, + entryName: keyof TCategoryData + ): TCategoryData[keyof TCategoryData] { + const value = target[entryName]; + if (typeof entryName === 'symbol' || entryName === 'nodeType') { + return value; + } + + assertLocaleData(value, categoryName, entryName.toString()); + return value; + }, + + set: throwReadOnlyError, + deleteProperty: throwReadOnlyError, + }) as Required<TCategoryData>; +} diff --git a/src/internal/mersenne.ts b/src/internal/mersenne.ts index d01b5ee9..351faa4e 100644 --- a/src/internal/mersenne.ts +++ b/src/internal/mersenne.ts @@ -1,5 +1,3 @@ -import type { Randomizer } from '../randomizer'; - /** * Copyright (c) 2022-2023 Faker * @@ -325,49 +323,3 @@ export class MersenneTwister19937 { } // These real versions are due to Isaku Wada, 2002/01/09 } - -/** - * Generates a MersenneTwister19937 randomizer with 32 bits of precision. - * This is the default randomizer used by faker prior to v9.0. - */ -export function generateMersenne32Randomizer(): Randomizer { - const twister = new MersenneTwister19937(); - - twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)); - - return { - next(): number { - return twister.genrandReal2(); - }, - seed(seed: number | number[]): void { - if (typeof seed === 'number') { - twister.initGenrand(seed); - } else if (Array.isArray(seed)) { - twister.initByArray(seed, seed.length); - } - }, - }; -} - -/** - * Generates a MersenneTwister19937 randomizer with 53 bits of precision. - * This is the default randomizer used by faker starting with v9.0. - */ -export function generateMersenne53Randomizer(): Randomizer { - const twister = new MersenneTwister19937(); - - twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)); - - return { - next(): number { - return twister.genrandRes53(); - }, - seed(seed: number | number[]): void { - if (typeof seed === 'number') { - twister.initGenrand(seed); - } else if (Array.isArray(seed)) { - twister.initByArray(seed, seed.length); - } - }, - }; -} diff --git a/src/internal/types.ts b/src/internal/types.ts new file mode 100644 index 00000000..affdda7e --- /dev/null +++ b/src/internal/types.ts @@ -0,0 +1,39 @@ +/** + * Type that provides auto-suggestions but also any string. + * + * @see https://github.com/microsoft/TypeScript/issues/29729#issuecomment-471566609 + */ +export type LiteralUnion<TSuggested extends TBase, TBase = string> = + | TSuggested + | (TBase & { zz_IGNORE_ME?: never }); + +/** + * A function that returns a value. + * + * `Function` cannot be used instead because it doesn't accept class declarations. + * These would fail when invoked since they are invoked without the `new` keyword. + */ +export type Callable = ( + // TODO @Shinigami92 2023-02-14: This `any` type can be fixed by anyone if they want to. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...args: any[] +) => unknown; + +/** + * Type that represents a single method/function name of the given type. + */ +export type MethodOf<TObjectType, TSignature extends Callable = Callable> = { + [Key in keyof TObjectType]: TObjectType[Key] extends TSignature + ? Key extends string + ? Key + : never + : never; +}[keyof TObjectType]; + +/** + * Type that represents all method/function names of the given type. + */ +export type MethodsOf< + TObjectType, + TSignature extends Callable = Callable, +> = ReadonlyArray<MethodOf<TObjectType, TSignature>>; |
