Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
24a25ba
feat: introduce FakerCore
ST-DDT Apr 20, 2024
1688003
chore: restore
ST-DDT Apr 20, 2024
2ef0270
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Apr 22, 2024
790e645
chore: apply suggestions
ST-DDT Apr 22, 2024
e4da2f5
Merge branch 'next' into feat/samfn/faker-core
ST-DDT May 14, 2024
6ef3999
chore: todos
ST-DDT May 14, 2024
66f4696
chore: workaround
ST-DDT May 14, 2024
7353a69
Merge branch 'next' into feat/samfn/faker-core
ST-DDT May 18, 2024
f96a407
Merge branch 'next' into feat/samfn/faker-core
ST-DDT May 30, 2024
d99d3b4
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Jun 21, 2024
aa8d8d6
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Aug 1, 2024
a54120a
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Sep 14, 2024
e807a8e
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Sep 16, 2024
e659674
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Oct 16, 2024
df2f308
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Mar 22, 2026
6dada60
chore: cleanup imports
ST-DDT Mar 22, 2026
4f9907a
chore: simplify FakerOptions
ST-DDT Mar 22, 2026
24bb1bf
chore: export new functions and types
ST-DDT Mar 22, 2026
b2bf1e2
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Mar 28, 2026
54add10
chore: edit after review
ST-DDT Mar 29, 2026
49ee0fb
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Apr 5, 2026
9a12eee
Merge branch 'next' into feat/samfn/faker-core
ST-DDT Apr 8, 2026
1f3f42c
chore: remove comment
ST-DDT Apr 8, 2026
0c2e3de
chore: bump since version
ST-DDT Apr 8, 2026
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
20 changes: 20 additions & 0 deletions src/config.ts
Comment thread
ST-DDT marked this conversation as resolved.
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()
*/
defaultRefDate?: () => Date;
}
102 changes: 102 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import type { FakerConfig } from './config';
import type { LocaleDefinition } from './definitions';
import type { Randomizer } from './randomizer';
import { mergeLocales } from './utils/merge-locales';
import { generateMersenne53Randomizer } from './utils/mersenne';

/**
* 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;
}

export interface FakerOptions {
/**
* The locale definitions to use. If not provided, this core will not have any locale data and thus all methods that rely on locale data will throw an error when called.
*
* @default {}
*/
locale?: LocaleDefinition | LocaleDefinition[];
/**
* The randomizer used to generate random values.
*
* @default generateMersenne53Randomizer()
*/
randomizer?: Randomizer;
/**
* The configuration options for all methods.
*
* @default {}
*/
config?: FakerConfig;
/**
* The initial seed to use.
* The seed can be used to generate reproducible values.
*
* Refer to the `seed()` method for more information.
*
* Defaults to a random seed.
*/
seed?: number;
}

/**
* Helper function to create a FakerCore instance.
*
* @param options The options to create the FakerCore instance with.
* @param options.locale The locale definitions to use.
* If not provided, this core will not have any locale data and thus all methods that rely on locale data will throw an error when called.
* This can be useful if you want to use least amount of memory possible and only use methods that do not rely on locale data.
* @param options.randomizer The randomizer used to generate random values.
* Defaults to `generateMersenne53Randomizer()`.
* @param options.config The configuration options for all methods.
* Defaults to an empty config.
* @param options.seed The initial seed to use.
* The seed can be used to generate reproducible values.
* Refer to the `seed()` method for more information.
* Defaults to a random seed.
*
* @returns The newly created FakerCore instance.
*
* @example
* import { createFakerCore, en } from '@faker-js/faker';
*
* createFakerCore() // no locale data, default randomizer and empty config
* createFakerCore({ locale: en }) // custom locale data, default randomizer and empty config
Comment thread
ST-DDT marked this conversation as resolved.
*
* @since 10.5.0
*/
export function createFakerCore(options: FakerOptions = {}): FakerCore {
const {
locale = {},
randomizer = generateMersenne53Randomizer(),
config = {},
seed,
} = options;

if (randomizer != null && seed != null) {
randomizer.seed(seed);
}

return {
locale: Array.isArray(locale) ? mergeLocales(locale) : locale,
randomizer,
config,
};
}
63 changes: 16 additions & 47 deletions src/faker.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { FakerOptions } from './core';
import type { LocaleDefinition, MetadataDefinition } from './definitions';
import { FakerError } from './errors/faker-error';
import type { LocaleProxy } from './internal/locale-proxy';
Expand Down Expand Up @@ -26,9 +27,7 @@ import { ScienceModule } from './modules/science';
import { SystemModule } from './modules/system';
import { VehicleModule } from './modules/vehicle';
import { WordModule } from './modules/word';
import type { Randomizer } from './randomizer';
import { SimpleFaker } from './simple-faker';
import { mergeLocales } from './utils/merge-locales';

/**
* This is Faker's main class containing all modules that can be used to generate data.
Expand Down Expand Up @@ -56,7 +55,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 Down Expand Up @@ -84,6 +82,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;
}
Comment thread
ST-DDT marked this conversation as resolved.

/**
* Creates a new instance of Faker.
*
Expand Down Expand Up @@ -120,51 +122,20 @@ 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.
* Please make sure that all required locales and their parent locales are present, e.g. `[de_AT, de, en, base]`.
*
* @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()
*/
randomizer?: Randomizer;
constructor(options: FakerOptions) {
super(options);

/**
* The initial seed to use.
* The seed can be used to generate reproducible values.
*
* Refer to the `seed()` method for more information.
*
* Defaults to a random seed.
*/
seed?: number;
}) {
super({ randomizer: options.randomizer, seed: options.seed });
const { locale } = options;

let { locale } = options;

if (Array.isArray(locale)) {
if (locale.length === 0) {
throw new FakerError(
'The locale option must contain at least one locale definition.'
);
}

locale = mergeLocales(locale);
// TODO @ST-DDT 2026-03-08: We should either not throw or throw consistently when locale data are empty.
// And likely refer to simpleFaker as alternative
if (Array.isArray(locale) && locale.length === 0) {
throw new FakerError(
'The locale option must contain at least one locale definition.'
);
}

this.rawDefinitions = locale;
this.definitions = createLocaleProxy(this.rawDefinitions);
this.definitions = createLocaleProxy(this.fakerCore.locale);
}
Comment thread
ST-DDT marked this conversation as resolved.

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

export type FakerOptions = ConstructorParameters<typeof Faker>[0];
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export type { FakerConfig } from './config';
export { createFakerCore } from './core';
export type { FakerCore, FakerOptions } from './core';
export type {
AirlineDefinition,
AnimalDefinition,
Expand Down Expand Up @@ -30,7 +33,6 @@ export type {
} from './definitions';
export { FakerError } from './errors/faker-error';
export { Faker } from './faker';
export type { FakerOptions } from './faker';
export * from './locale';
export { fakerEN as faker } from './locale';
export * from './locales';
Expand Down
2 changes: 1 addition & 1 deletion src/modules/helpers/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const REGEX_DOT_OR_BRACKET = /\.|\(/;
export function fakeEval(
expression: string,
faker: Faker,
entrypoints: ReadonlyArray<unknown> = [faker, faker.rawDefinitions]
entrypoints: ReadonlyArray<unknown> = [faker, faker.fakerCore.locale]
): unknown {
if (expression.length === 0) {
throw new FakerError('Eval expression cannot be empty.');
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 @@ -210,8 +209,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
9 changes: 3 additions & 6 deletions src/modules/person/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,10 @@ export class PersonModule extends ModuleBase {
* @since 8.0.0
*/
lastName(sex?: SexType): string {
if (this.faker.rawDefinitions.person?.last_name_pattern != null) {
const patterns = this.faker.fakerCore.locale.person?.last_name_pattern;
if (patterns != null) {
const pattern = this.faker.helpers.weightedArrayElement(
selectDefinition(
this.faker,
sex,
this.faker.rawDefinitions.person.last_name_pattern
)
selectDefinition(this.faker, sex, patterns)
);
return this.faker.helpers.fake(pattern);
}
Expand Down
55 changes: 16 additions & 39 deletions src/simple-faker.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { FakerCore, FakerOptions } from './core';
import { createFakerCore } from './core';
import { randomSeed } from './internal/seed';
import { DatatypeModule } from './modules/datatype';
import { SimpleDateModule } from './modules/date';
import { SimpleHelpersModule } from './modules/helpers';
import { SimpleLocationModule } from './modules/location';
import { NumberModule } from './modules/number';
import { StringModule } from './modules/string';
import type { Randomizer } from './randomizer';
import { generateMersenne53Randomizer } from './utils/mersenne';

export const DEFAULT_REF_DATE_SOURCE: () => Date = () => new Date();

/**
* This is a simplified Faker class that doesn't need any localized data to generate its output.
Expand All @@ -29,13 +31,18 @@ import { generateMersenne53Randomizer } from './utils/mersenne';
* simpleFaker.string.uuid(); // 'c50e1f5c-86e8-4aa9-888e-168e0a182519'
*/
export class SimpleFaker {
protected _defaultRefDate: () => Date = () => new Date();
/**
* The faker core containing the randomizer and config to use.
*
* @internal
*/
readonly fakerCore: FakerCore;

/**
* Gets a new reference date used to generate relative dates.
*/
get defaultRefDate(): () => Date {
return this._defaultRefDate;
return this.fakerCore.config.defaultRefDate ?? DEFAULT_REF_DATE_SOURCE;
}
Comment thread
ST-DDT marked this conversation as resolved.

/**
Expand Down Expand Up @@ -75,15 +82,12 @@ export class SimpleFaker {
dateOrSource: string | Date | number | (() => Date) = () => new Date()
): void {
if (typeof dateOrSource === 'function') {
this._defaultRefDate = dateOrSource;
this.fakerCore.config.defaultRefDate = dateOrSource;
} else {
this._defaultRefDate = () => new Date(dateOrSource);
this.fakerCore.config.defaultRefDate = () => 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 @@ -118,35 +122,8 @@ 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 initial seed to use.
* The seed can be used to generate reproducible values.
*
* Refer to the `seed()` method for more information.
*
* Defaults to a random seed.
*/
seed?: number;
} = {}
) {
const { randomizer, seed } = options;

if (randomizer != null && seed != null) {
randomizer.seed(seed);
}

this._randomizer = randomizer ?? generateMersenne53Randomizer(seed);
constructor(options?: FakerOptions) {
this.fakerCore = createFakerCore(options);
}

/**
Expand Down Expand Up @@ -271,7 +248,7 @@ export class SimpleFaker {
*/
seed(seed?: number | number[]): number | number[];
seed(seed: number | number[] = randomSeed()): number | number[] {
this._randomizer.seed(seed);
this.fakerCore.randomizer.seed(seed);

return seed;
}
Expand Down
Loading
Loading