Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce FakerCore #2838

Draft
wants to merge 14 commits into
base: next
Choose a base branch
from
20 changes: 20 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -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;
}
25 changes: 25 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
@@ -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;
}
61 changes: 40 additions & 21 deletions src/faker.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}

Expand All @@ -186,7 +205,7 @@ export class Faker extends SimpleFaker {
* @since 8.1.0
*/
getMetadata(): MetadataDefinition {
return this.rawDefinitions.metadata ?? {};
return this.fakerCore.locale.metadata ?? {};
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/modules/number/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
48 changes: 31 additions & 17 deletions src/simple-faker.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { FakerCore } from './core';
import { generateMersenne53Randomizer } from './internal/mersenne';
import { DatatypeModule } from './modules/datatype';
import { SimpleDateModule } from './modules/date';
Expand Down Expand Up @@ -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());
}

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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;
}
Expand Down
8 changes: 1 addition & 7 deletions test/all-functional.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 1 addition & 2 deletions test/modules/number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading