From 24a25bace8374de4f2255b8a248ad0d53c0726c8 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sat, 20 Apr 2024 15:50:50 +0200 Subject: [PATCH 1/5] feat: introduce FakerCore --- package.json | 5 --- src/config.ts | 20 ++++++++++++ src/core.ts | 25 +++++++++++++++ src/faker.ts | 61 ++++++++++++++++++++++++------------- src/modules/number/index.ts | 6 ++-- src/simple-faker.ts | 48 ++++++++++++++++++----------- test/all-functional.spec.ts | 8 +---- test/modules/number.spec.ts | 3 +- 8 files changed, 120 insertions(+), 56 deletions(-) create mode 100644 src/config.ts create mode 100644 src/core.ts diff --git a/package.json b/package.json index 782f5f8f8f4..0f1b1cedb51 100644 --- a/package.json +++ b/package.json @@ -134,10 +134,5 @@ "vitepress": "1.1.0", "vitest": "1.5.0", "vue": "3.4.21" - }, - "packageManager": "pnpm@9.0.1", - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0" } } diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000000..991dda70e6b --- /dev/null +++ b/src/config.ts @@ -0,0 +1,20 @@ +/** + * The possible configuration options, that can be set. + * This type exists to be extended for plugins via type augmentation. + * + * The `@default` tag is used to indicate the default value, that should be used if, the config is absent. + */ +export interface FakerConfig { + /** + * The function used to generate the `refDate` date instance, if not provided as method param. + * The function must return a new valid `Date` instance for every call. + * + * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) + * @see faker.seed(): For generating reproducible values. + * + * @since 9.0.0 + * + * @default () => new Date() + */ + refDate?: () => Date; +} diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 00000000000..3609fd65f0a --- /dev/null +++ b/src/core.ts @@ -0,0 +1,25 @@ +import type { FakerConfig } from './config'; +import type { LocaleDefinition } from './definitions'; +import type { Randomizer } from './randomizer'; + +/** + * The core grants access to the locale data, the randomizer and config settings. + */ +export interface FakerCore { + /** + * The locale data associated with this instance. + * + * Always present, but it might be empty if the locale data is not available. + */ + readonly locale: LocaleDefinition; + + /** + * The randomizer used to generate random values. + */ + readonly randomizer: Randomizer; + + /** + * The configuration settings used by this instance. + */ + readonly config: FakerConfig; +} diff --git a/src/faker.ts b/src/faker.ts index 5f5e14ce392..b5987d41fe8 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -1,6 +1,8 @@ +import type { FakerCore } from './core'; import type { LocaleDefinition, MetadataDefinition } from './definitions'; import { FakerError } from './errors/faker-error'; import { deprecated } from './internal/deprecated'; +import { generateMersenne53Randomizer } from './internal/mersenne'; import type { LocaleProxy } from './locale-proxy'; import { createLocaleProxy } from './locale-proxy'; import { AirlineModule } from './modules/airline'; @@ -58,7 +60,6 @@ import { mergeLocales } from './utils/merge-locales'; * customFaker.music.genre(); // throws Error as this data is not available in `es` */ export class Faker extends SimpleFaker { - readonly rawDefinitions: LocaleDefinition; readonly definitions: LocaleProxy; readonly airline: AirlineModule = new AirlineModule(this); @@ -85,6 +86,10 @@ export class Faker extends SimpleFaker { readonly vehicle: VehicleModule = new VehicleModule(this); readonly word: WordModule = new WordModule(this); + get rawDefinitions(): LocaleDefinition { + return this.fakerCore.locale; + } + // Aliases /** @deprecated Use {@link Faker#location} instead */ get address(): AddressModule { @@ -138,27 +143,40 @@ export class Faker extends SimpleFaker { * * @since 8.0.0 */ - constructor(options: { - /** - * The locale data to use for this instance. - * If an array is provided, the first locale that has a definition for a given property will be used. - * - * @see mergeLocales(): For more information about how the locales are merged. - */ - locale: LocaleDefinition | LocaleDefinition[]; + constructor( + options: + | { + /** + * The locale data to use for this instance. + * If an array is provided, the first locale that has a definition for a given property will be used. + * + * @see mergeLocales(): For more information about how the locales are merged. + */ + locale: LocaleDefinition | LocaleDefinition[]; - /** - * The Randomizer to use. - * Specify this only if you want to use it to achieve a specific goal, - * such as sharing the same random generator with other instances/tools. - * - * @default generateMersenne53Randomizer() - */ + /** + * The Randomizer to use. + * Specify this only if you want to use it to achieve a specific goal, + * such as sharing the same random generator with other instances/tools. + * + * @default generateMersenne53Randomizer() + */ + randomizer?: Randomizer; + } + | { + /** + * The faker core with the randomizer, locale data and config to use. + */ + fakerCore: FakerCore; + } + ); + constructor(options: { + locale?: LocaleDefinition | LocaleDefinition[]; randomizer?: Randomizer; + fakerCore?: FakerCore; }) { - super({ randomizer: options.randomizer }); - - let { locale } = options; + const { randomizer = generateMersenne53Randomizer(), fakerCore } = options; + let { locale = {} } = options; if (Array.isArray(locale)) { if (locale.length === 0) { @@ -170,7 +188,8 @@ export class Faker extends SimpleFaker { locale = mergeLocales(locale); } - this.rawDefinitions = locale; + super({ fakerCore: fakerCore ?? { locale, randomizer, config: {} } }); + this.definitions = createLocaleProxy(this.rawDefinitions); } @@ -186,7 +205,7 @@ export class Faker extends SimpleFaker { * @since 8.1.0 */ getMetadata(): MetadataDefinition { - return this.rawDefinitions.metadata ?? {}; + return this.fakerCore.locale.metadata ?? {}; } } diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index be5e213f272..621b0adc78a 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -96,8 +96,7 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - // @ts-expect-error: access private member field - const randomizer = this.faker._randomizer; + const { randomizer } = this.faker.fakerCore; const real = randomizer.next(); const delta = effectiveMax - effectiveMin + 1; // +1 for inclusive max bounds and even distribution return Math.floor(real * delta + effectiveMin) * multipleOf; @@ -214,8 +213,7 @@ export class NumberModule extends SimpleModuleBase { return int / factor; } - // @ts-expect-error: access private member field - const randomizer = this.faker._randomizer; + const { randomizer } = this.faker.fakerCore; const real = randomizer.next(); return real * (max - min) + min; } diff --git a/src/simple-faker.ts b/src/simple-faker.ts index e775cbc7b5d..11bce1bd9d0 100644 --- a/src/simple-faker.ts +++ b/src/simple-faker.ts @@ -1,3 +1,4 @@ +import type { FakerCore } from './core'; import { generateMersenne53Randomizer } from './internal/mersenne'; import { DatatypeModule } from './modules/datatype'; import { SimpleDateModule } from './modules/date'; @@ -26,13 +27,13 @@ import type { Randomizer } from './randomizer'; * simpleFaker.string.uuid(); // 'c50e1f5c-86e8-4aa9-888e-168e0a182519' */ export class SimpleFaker { - protected _defaultRefDate: () => Date = () => new Date(); + readonly fakerCore: FakerCore; /** * Gets a new reference date used to generate relative dates. */ get defaultRefDate(): () => Date { - return this._defaultRefDate; + return this.fakerCore.config.refDate ?? (() => new Date()); } /** @@ -72,15 +73,12 @@ export class SimpleFaker { dateOrSource: string | Date | number | (() => Date) = () => new Date() ): void { if (typeof dateOrSource === 'function') { - this._defaultRefDate = dateOrSource; + this.fakerCore.config.refDate = dateOrSource; } else { - this._defaultRefDate = () => new Date(dateOrSource); + this.fakerCore.config.refDate = () => new Date(dateOrSource); } } - /** @internal */ - private readonly _randomizer: Randomizer; - readonly datatype: DatatypeModule = new DatatypeModule(this); readonly date: SimpleDateModule = new SimpleDateModule(this); readonly helpers: SimpleHelpersModule = new SimpleHelpersModule(this); @@ -110,21 +108,37 @@ export class SimpleFaker { * * @since 8.1.0 */ + constructor( + options?: + | { + /** + * The Randomizer to use. + * Specify this only if you want to use it to achieve a specific goal, + * such as sharing the same random generator with other instances/tools. + * + * @default generateMersenne53Randomizer() + */ + randomizer?: Randomizer; + } + | { + /** + * The faker core with the randomizer and config to use. + */ + fakerCore: FakerCore; + } + ); constructor( options: { - /** - * The Randomizer to use. - * Specify this only if you want to use it to achieve a specific goal, - * such as sharing the same random generator with other instances/tools. - * - * @default generateMersenne53Randomizer() - */ randomizer?: Randomizer; + fakerCore?: FakerCore; } = {} ) { - const { randomizer = generateMersenne53Randomizer() } = options; + const { + randomizer = generateMersenne53Randomizer(), + fakerCore = { locale: {}, randomizer, config: {} }, + } = options; - this._randomizer = randomizer; + this.fakerCore = fakerCore; } /** @@ -250,7 +264,7 @@ export class SimpleFaker { seed( seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) ): number | number[] { - this._randomizer.seed(seed); + this.fakerCore.randomizer.seed(seed); return seed; } diff --git a/test/all-functional.spec.ts b/test/all-functional.spec.ts index 12b69ba1fb7..384519cbd5f 100644 --- a/test/all-functional.spec.ts +++ b/test/all-functional.spec.ts @@ -3,13 +3,7 @@ import type { Faker, allLocales } from '../src'; import { allFakers, fakerEN } from '../src'; import { keys } from '../src/internal/keys'; -const IGNORED_MODULES = new Set([ - 'rawDefinitions', - 'definitions', - 'helpers', - '_randomizer', - '_defaultRefDate', -]); +const IGNORED_MODULES = new Set(['rawDefinitions', 'helpers', 'fakerCore']); function getMethodNamesByModules(faker: Faker): { [module: string]: string[] } { return Object.fromEntries( diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index f318fc5be09..4c45efd6f82 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -629,8 +629,7 @@ describe('number', () => { describe('value range tests', () => { const customFaker = new SimpleFaker(); - // @ts-expect-error: access private member field - const randomizer = customFaker._randomizer; + const { randomizer } = customFaker.fakerCore; describe('int', () => { it('should be able to return 0', () => { randomizer.next = () => 0; From 1688003780f2e1794eca503c41ce3b13ce94842d Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sat, 20 Apr 2024 16:13:31 +0200 Subject: [PATCH 2/5] chore: restore --- package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package.json b/package.json index 0f1b1cedb51..782f5f8f8f4 100644 --- a/package.json +++ b/package.json @@ -134,5 +134,10 @@ "vitepress": "1.1.0", "vitest": "1.5.0", "vue": "3.4.21" + }, + "packageManager": "pnpm@9.0.1", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" } } From 790e64574e74de3b0a216db445cee1e069e1d61c Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Mon, 22 Apr 2024 23:28:26 +0200 Subject: [PATCH 3/5] chore: apply suggestions --- src/config.ts | 2 +- src/faker.ts | 8 ++++++-- src/simple-faker.ts | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index 991dda70e6b..5b3084290b7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -16,5 +16,5 @@ export interface FakerConfig { * * @default () => new Date() */ - refDate?: () => Date; + defaultRefDate?: () => Date; } diff --git a/src/faker.ts b/src/faker.ts index b5987d41fe8..22804100f45 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -175,7 +175,6 @@ export class Faker extends SimpleFaker { randomizer?: Randomizer; fakerCore?: FakerCore; }) { - const { randomizer = generateMersenne53Randomizer(), fakerCore } = options; let { locale = {} } = options; if (Array.isArray(locale)) { @@ -188,7 +187,12 @@ export class Faker extends SimpleFaker { locale = mergeLocales(locale); } - super({ fakerCore: fakerCore ?? { locale, randomizer, config: {} } }); + const { + randomizer = generateMersenne53Randomizer(), + fakerCore = { locale, randomizer, config: {} }, + } = options; + + super({ fakerCore }); this.definitions = createLocaleProxy(this.rawDefinitions); } diff --git a/src/simple-faker.ts b/src/simple-faker.ts index 11bce1bd9d0..80ef3a53076 100644 --- a/src/simple-faker.ts +++ b/src/simple-faker.ts @@ -33,7 +33,7 @@ export class SimpleFaker { * Gets a new reference date used to generate relative dates. */ get defaultRefDate(): () => Date { - return this.fakerCore.config.refDate ?? (() => new Date()); + return this.fakerCore.config.defaultRefDate ?? (() => new Date()); } /** @@ -73,9 +73,9 @@ export class SimpleFaker { dateOrSource: string | Date | number | (() => Date) = () => new Date() ): void { if (typeof dateOrSource === 'function') { - this.fakerCore.config.refDate = dateOrSource; + this.fakerCore.config.defaultRefDate = dateOrSource; } else { - this.fakerCore.config.refDate = () => new Date(dateOrSource); + this.fakerCore.config.defaultRefDate = () => new Date(dateOrSource); } } From 6ef3999308d31990c1aced4658291a2d590dcac1 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Tue, 14 May 2024 22:36:33 +0200 Subject: [PATCH 4/5] chore: todos --- src/faker.ts | 1 + test/all-functional.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/faker.ts b/src/faker.ts index 22804100f45..fab9d8dfef0 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -87,6 +87,7 @@ export class Faker extends SimpleFaker { readonly word: WordModule = new WordModule(this); get rawDefinitions(): LocaleDefinition { + // TODO @ST-DDT 2024-05-14: Should we deprecate this? return this.fakerCore.locale; } diff --git a/test/all-functional.spec.ts b/test/all-functional.spec.ts index 384519cbd5f..201cf63010d 100644 --- a/test/all-functional.spec.ts +++ b/test/all-functional.spec.ts @@ -3,7 +3,7 @@ import type { Faker, allLocales } from '../src'; import { allFakers, fakerEN } from '../src'; import { keys } from '../src/internal/keys'; -const IGNORED_MODULES = new Set(['rawDefinitions', 'helpers', 'fakerCore']); +const IGNORED_MODULES = new Set(['definitions', 'helpers', 'fakerCore']); function getMethodNamesByModules(faker: Faker): { [module: string]: string[] } { return Object.fromEntries( From 66f46968569d68e101a949cc3639cbfe6e1018f1 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Tue, 14 May 2024 22:37:59 +0200 Subject: [PATCH 5/5] chore: workaround --- src/faker.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/faker.ts b/src/faker.ts index fab9d8dfef0..abb925f53cb 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -176,6 +176,8 @@ export class Faker extends SimpleFaker { randomizer?: Randomizer; fakerCore?: FakerCore; }) { + super(options); + let { locale = {} } = options; if (Array.isArray(locale)) { @@ -193,7 +195,10 @@ export class Faker extends SimpleFaker { fakerCore = { locale, randomizer, config: {} }, } = options; - super({ fakerCore }); + // TODO @ST-DDT 2024-05-14: Workaround for https://github.com/egoist/tsup/issues/1124 + // @ts-expect-error: fakerCore is not writable + this.fakerCore = fakerCore; + // super({ fakerCore }); this.definitions = createLocaleProxy(this.rawDefinitions); }