diff options
| author | Shinigami <[email protected]> | 2023-09-18 05:52:42 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-18 05:52:42 +0200 |
| commit | d6a4f8c2ddf9e957e875bc3fab77e496104d1320 (patch) | |
| tree | 8c87a5d2baf4dfad8d96da7a4823adaf8bd005ee /src | |
| parent | f40e217c6275ab555ecbe3ca0046526e44b31263 (diff) | |
| download | faker-d6a4f8c2ddf9e957e875bc3fab77e496104d1320.tar.xz faker-d6a4f8c2ddf9e957e875bc3fab77e496104d1320.zip | |
feat: split SimpleFaker class from Faker (#2369)
Diffstat (limited to 'src')
| -rw-r--r-- | src/faker.ts | 192 | ||||
| -rw-r--r-- | src/index.ts | 5 | ||||
| -rw-r--r-- | src/internal/bind-this-to-member-functions.ts | 15 | ||||
| -rw-r--r-- | src/modules/datatype/index.ts | 4 | ||||
| -rw-r--r-- | src/modules/date/index.ts | 216 | ||||
| -rw-r--r-- | src/modules/helpers/index.ts | 419 | ||||
| -rw-r--r-- | src/modules/number/index.ts | 4 | ||||
| -rw-r--r-- | src/modules/string/index.ts | 4 | ||||
| -rw-r--r-- | src/simple-faker.ts | 220 |
9 files changed, 569 insertions, 510 deletions
diff --git a/src/faker.ts b/src/faker.ts index 9161497e..43811f05 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -1,8 +1,6 @@ import type { LocaleDefinition, MetadataDefinition } from './definitions'; import { FakerError } from './errors/faker-error'; import { deprecated } from './internal/deprecated'; -import type { Mersenne } from './internal/mersenne/mersenne'; -import mersenne from './internal/mersenne/mersenne'; import type { LocaleProxy } from './locale-proxy'; import { createLocaleProxy } from './locale-proxy'; import { AirlineModule } from './modules/airline'; @@ -11,7 +9,6 @@ import { ColorModule } from './modules/color'; import { CommerceModule } from './modules/commerce'; import { CompanyModule } from './modules/company'; import { DatabaseModule } from './modules/database'; -import { DatatypeModule } from './modules/datatype'; import { DateModule } from './modules/date'; import { FinanceModule } from './modules/finance'; import { GitModule } from './modules/git'; @@ -23,16 +20,15 @@ import type { LocationModule as AddressModule } from './modules/location'; import { LocationModule } from './modules/location'; import { LoremModule } from './modules/lorem'; import { MusicModule } from './modules/music'; -import { NumberModule } from './modules/number'; import type { PersonModule as NameModule } from './modules/person'; import { PersonModule } from './modules/person'; import { PhoneModule } from './modules/phone'; import { RandomModule } from './modules/random'; import { ScienceModule } from './modules/science'; -import { StringModule } from './modules/string'; import { SystemModule } from './modules/system'; import { VehicleModule } from './modules/vehicle'; import { WordModule } from './modules/word'; +import { SimpleFaker } from './simple-faker'; import { mergeLocales } from './utils/merge-locales'; /** @@ -60,61 +56,9 @@ import { mergeLocales } from './utils/merge-locales'; * * customFaker.music.genre(); // throws Error as this data is not available in `es` */ -export class Faker { +export class Faker extends SimpleFaker { readonly rawDefinitions: LocaleDefinition; readonly definitions: LocaleProxy; - private _defaultRefDate: () => Date = () => new Date(); - - /** - * Gets a new reference date used to generate relative dates. - */ - get defaultRefDate(): () => Date { - return this._defaultRefDate; - } - - /** - * Sets the `refDate` source to use if no `refDate` date is passed to the date methods. - * - * @param dateOrSource The function or the static value used to generate the `refDate` date instance. - * The function must return a new valid `Date` instance for every call. - * Defaults to `() => new Date()`. - * - * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) - * @see faker.seed() for reproducible results. - * - * @example - * faker.seed(1234); - * - * // Default behavior - * // faker.setDefaultRefDate(); - * faker.date.past(); // Changes based on the current date/time - * - * // Use a static ref date - * faker.setDefaultRefDate(new Date('2020-01-01')); - * faker.date.past(); // Reproducible '2019-07-03T08:27:58.118Z' - * - * // Use a ref date that changes every time it is used - * let clock = new Date("2020-01-01").getTime(); - * faker.setDefaultRefDate(() => { - * clock += 1000; // +1s - * return new Date(clock); - * }); - * - * faker.defaultRefDate() // 2020-01-01T00:00:01Z - * faker.defaultRefDate() // 2020-01-01T00:00:02Z - */ - setDefaultRefDate( - dateOrSource: string | Date | number | (() => Date) = () => new Date() - ): void { - if (typeof dateOrSource === 'function') { - this._defaultRefDate = dateOrSource; - } else { - this._defaultRefDate = () => new Date(dateOrSource); - } - } - - /** @internal */ - private readonly _mersenne: Mersenne = mersenne(); /** * @deprecated Use the modules specific to the type of data you want to generate instead. @@ -122,10 +66,6 @@ export class Faker { // eslint-disable-next-line deprecation/deprecation readonly random: RandomModule = new RandomModule(this); - readonly helpers: HelpersModule = new HelpersModule(this); - - readonly datatype: DatatypeModule = new DatatypeModule(this); - readonly airline: AirlineModule = new AirlineModule(this); readonly animal: AnimalModule = new AnimalModule(this); readonly color: ColorModule = new ColorModule(this); @@ -136,16 +76,15 @@ export class Faker { readonly finance = new FinanceModule(this); readonly git: GitModule = new GitModule(this); readonly hacker: HackerModule = new HackerModule(this); + readonly helpers: HelpersModule = new HelpersModule(this); readonly image: ImageModule = new ImageModule(this); readonly internet: InternetModule = new InternetModule(this); readonly location: LocationModule = new LocationModule(this); readonly lorem: LoremModule = new LoremModule(this); readonly music: MusicModule = new MusicModule(this); readonly person: PersonModule = new PersonModule(this); - readonly number: NumberModule = new NumberModule(this); readonly phone: PhoneModule = new PhoneModule(this); readonly science: ScienceModule = new ScienceModule(this); - readonly string: StringModule = new StringModule(this); readonly system: SystemModule = new SystemModule(this); readonly vehicle: VehicleModule = new VehicleModule(this); readonly word: WordModule = new WordModule(this); @@ -299,9 +238,12 @@ export class Faker { localeFallback?: string; } ) { + super(); + const { locales } = options as { locales: Record<string, LocaleDefinition>; }; + if (locales != null) { deprecated({ deprecated: @@ -337,128 +279,6 @@ export class Faker { } /** - * Sets the seed or generates a new one. - * - * Please note that generated values are dependent on both the seed and the - * number of calls that have been made since it was set. - * - * This method is intended to allow for consistent values in tests, so you - * might want to use hardcoded values as the seed. - * - * In addition to that it can be used for creating truly random tests - * (by passing no arguments), that still can be reproduced if needed, - * by logging the result and explicitly setting it if needed. - * - * @param seed The seed to use. Defaults to a random number. - * - * @returns The seed that was set. - * - * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) - * @see faker.setDefaultRefDate() when generating relative dates. - * - * @example - * // Consistent values for tests: - * faker.seed(42) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * faker.seed(42) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * // Random but reproducible tests: - * // Simply log the seed, and if you need to reproduce it, insert the seed here - * console.log('Running test with seed:', faker.seed()); - */ - seed(seed?: number): number; - /** - * Sets the seed array. - * - * Please note that generated values are dependent on both the seed and the - * number of calls that have been made since it was set. - * - * This method is intended to allow for consistent values in a tests, so you - * might want to use hardcoded values as the seed. - * - * In addition to that it can be used for creating truly random tests - * (by passing no arguments), that still can be reproduced if needed, - * by logging the result and explicitly setting it if needed. - * - * @param seedArray The seed array to use. - * - * @returns The seed array that was set. - * - * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) - * @see faker.setDefaultRefDate() when generating relative dates. - * - * @example - * // Consistent values for tests: - * faker.seed([42, 13, 17]) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * faker.seed([42, 13, 17]) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * // Random but reproducible tests: - * // Simply log the seed, and if you need to reproduce it, insert the seed here - * console.log('Running test with seed:', faker.seed()); - */ - seed(seedArray: number[]): number[]; - /** - * Sets the seed or generates a new one. - * - * Please note that generated values are dependent on both the seed and the - * number of calls that have been made since it was set. - * - * This method is intended to allow for consistent values in a tests, so you - * might want to use hardcoded values as the seed. - * - * In addition to that it can be used for creating truly random tests - * (by passing no arguments), that still can be reproduced if needed, - * by logging the result and explicitly setting it if needed. - * - * @param seed The seed or seed array to use. - * - * @returns The seed that was set. - * - * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) - * @see faker.setDefaultRefDate() when generating relative dates. - * - * @example - * // Consistent values for tests (using a number): - * faker.seed(42) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * faker.seed(42) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * // Consistent values for tests (using an array): - * faker.seed([42, 13, 17]) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * faker.seed([42, 13, 17]) - * faker.number.int(10); // 4 - * faker.number.int(10); // 8 - * - * // Random but reproducible tests: - * // Simply log the seed, and if you need to reproduce it, insert the seed here - * console.log('Running test with seed:', faker.seed()); - */ - seed(seed?: number | number[]): number | number[]; - seed( - seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) - ): number | number[] { - this._mersenne.seed(seed); - - return seed; - } - - /** * Returns an object with metadata about the current locale. * * @example diff --git a/src/index.ts b/src/index.ts index a1523bc2..000541b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -98,11 +98,11 @@ export type { CommerceModule } from './modules/commerce'; export type { CompanyModule } from './modules/company'; export type { DatabaseModule } from './modules/database'; export type { DatatypeModule } from './modules/datatype'; -export type { DateModule } from './modules/date'; +export type { DateModule, SimpleDateModule } from './modules/date'; export type { Currency, FinanceModule } from './modules/finance'; export type { GitModule } from './modules/git'; export type { HackerModule } from './modules/hacker'; -export type { HelpersModule } from './modules/helpers'; +export type { HelpersModule, SimpleHelpersModule } from './modules/helpers'; export type { ImageModule } from './modules/image'; export type { InternetModule } from './modules/internet'; export type { @@ -128,4 +128,5 @@ export type { StringModule } from './modules/string'; export type { SystemModule } from './modules/system'; export type { VehicleModule } from './modules/vehicle'; export type { WordModule } from './modules/word'; +export { SimpleFaker, simpleFaker } from './simple-faker'; export { mergeLocales } from './utils/merge-locales'; diff --git a/src/internal/bind-this-to-member-functions.ts b/src/internal/bind-this-to-member-functions.ts index f2a9d3d5..26b7cfe4 100644 --- a/src/internal/bind-this-to-member-functions.ts +++ b/src/internal/bind-this-to-member-functions.ts @@ -15,11 +15,14 @@ export function bindThisToMemberFunctions<TClass extends { new (): any }>( instance: InstanceType<TClass> ): void { - for (const name of Object.getOwnPropertyNames( - Object.getPrototypeOf(instance) - )) { - if (typeof instance[name] === 'function' && name !== 'constructor') { - instance[name] = instance[name].bind(instance); + let p = Object.getPrototypeOf(instance); + do { + for (const name of Object.getOwnPropertyNames(p)) { + if (typeof instance[name] === 'function' && name !== 'constructor') { + instance[name] = instance[name].bind(instance); + } } - } + + p = Object.getPrototypeOf(p); + } while (p !== Object.prototype); } diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts index ab6998ae..6f735e0e 100644 --- a/src/modules/datatype/index.ts +++ b/src/modules/datatype/index.ts @@ -1,4 +1,4 @@ -import type { Faker } from '../..'; +import type { SimpleFaker } from '../..'; import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions'; import { deprecated } from '../../internal/deprecated'; @@ -12,7 +12,7 @@ import { deprecated } from '../../internal/deprecated'; * For a simple random true or false value, use [`boolean()`](https://fakerjs.dev/api/datatype.html#boolean). */ export class DatatypeModule { - constructor(private readonly faker: Faker) { + constructor(private readonly faker: SimpleFaker) { bindThisToMemberFunctions(this); } diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts index 88680ebf..f63ed452 100644 --- a/src/modules/date/index.ts +++ b/src/modules/date/index.ts @@ -1,4 +1,4 @@ -import type { Faker } from '../..'; +import type { Faker, SimpleFaker } from '../..'; import type { DateEntryDefinition } from '../../definitions'; import { FakerError } from '../../errors/faker-error'; import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions'; @@ -23,23 +23,8 @@ function toDate( return date; } -/** - * Module to generate dates. - * - * ### Overview - * - * To quickly generate a date in the past, use [`recent()`](https://fakerjs.dev/api/date.html#recent) (last day) or [`past()`](https://fakerjs.dev/api/date.html#past) (last year). - * To quickly generate a date in the future, use [`soon()`](https://fakerjs.dev/api/date.html#soon) (next day) or [`future()`](https://fakerjs.dev/api/date.html#future) (next year). - * For a realistic birthdate for an adult, use [`birthdate()`](https://fakerjs.dev/api/date.html#birthdate). - * - * For more control, any of these methods can be customized with further options, or use [`between()`](https://fakerjs.dev/api/date.html#between) to generate a single date between two dates, or [`betweens()`](https://fakerjs.dev/api/date.html#betweens) for multiple dates. - * - * You can generate random localized month and weekday names using [`month()`](https://fakerjs.dev/api/date.html#month) and [`weekday()`](https://fakerjs.dev/api/date.html#weekday). - * - * These methods have additional concerns about reproducibility, see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results). - */ -export class DateModule { - constructor(private readonly faker: Faker) { +export class SimpleDateModule { + constructor(protected readonly faker: SimpleFaker) { bindThisToMemberFunctions(this); } @@ -827,6 +812,114 @@ export class DateModule { } /** + * Returns a random birthdate. + * + * @param options The options to use to generate the birthdate. If no options are set, an age between 18 and 80 (inclusive) is generated. + * @param options.min The minimum age or year to generate a birthdate. + * @param options.max The maximum age or year to generate a birthdate. + * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `now`. + * @param options.mode The mode to generate the birthdate. Supported modes are `'age'` and `'year'` . + * + * There are two modes available `'age'` and `'year'`: + * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`). + * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`). + * + * Defaults to `year`. + * + * @example + * faker.date.birthdate() // 1977-07-10T01:37:30.719Z + * faker.date.birthdate({ min: 18, max: 65, mode: 'age' }) // 2003-11-02T20:03:20.116Z + * faker.date.birthdate({ min: 1900, max: 2000, mode: 'year' }) // 1940-08-20T08:53:07.538Z + * + * @since 7.0.0 + */ + birthdate( + options: { + /** + * The minimum age or year to generate a birthdate. + * + * @default 18 + */ + min?: number; + /** + * The maximum age or year to generate a birthdate. + * + * @default 80 + */ + max?: number; + /** + * The mode to generate the birthdate. Supported modes are `'age'` and `'year'` . + * + * There are two modes available `'age'` and `'year'`: + * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`). + * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`). + * + * @default 'year' + */ + mode?: 'age' | 'year'; + /** + * The date to use as reference point for the newly generated date. + * + * @default faker.defaultRefDate() + */ + refDate?: string | Date | number; + } = {} + ): Date { + if (options.max < options.min) { + throw new FakerError( + `Max ${options.max} should be larger than or equal to min ${options.min}.` + ); + } + + const mode = options.mode === 'age' ? 'age' : 'year'; + const refDate = toDate(options.refDate, this.faker.defaultRefDate); + const refYear = refDate.getUTCFullYear(); + + // If no min or max is specified, generate a random date between (now - 80) years and (now - 18) years respectively + // So that people can still be considered as adults in most cases + + // Convert to epoch timestamps + let min: number; + let max: number; + if (mode === 'age') { + min = new Date(refDate).setUTCFullYear(refYear - (options.max ?? 80) - 1); + max = new Date(refDate).setUTCFullYear(refYear - (options.min ?? 18)); + } else { + // Avoid generating dates the first and last date of the year + // to avoid running into other years depending on the timezone. + min = new Date(Date.UTC(0, 0, 2)).setUTCFullYear( + options.min ?? refYear - 80 + ); + max = new Date(Date.UTC(0, 11, 30)).setUTCFullYear( + options.max ?? refYear - 18 + ); + } + + return new Date(this.faker.number.int({ min, max })); + } +} + +/** + * Module to generate dates. + * + * ### Overview + * + * To quickly generate a date in the past, use [`recent()`](https://fakerjs.dev/api/date.html#recent) (last day) or [`past()`](https://fakerjs.dev/api/date.html#past) (last year). + * To quickly generate a date in the future, use [`soon()`](https://fakerjs.dev/api/date.html#soon) (next day) or [`future()`](https://fakerjs.dev/api/date.html#future) (next year). + * For a realistic birthdate for an adult, use [`birthdate()`](https://fakerjs.dev/api/date.html#birthdate). + * + * For more control, any of these methods can be customized with further options, or use [`between()`](https://fakerjs.dev/api/date.html#between) to generate a single date between two dates, or [`betweens()`](https://fakerjs.dev/api/date.html#betweens) for multiple dates. + * + * You can generate random localized month and weekday names using [`month()`](https://fakerjs.dev/api/date.html#month) and [`weekday()`](https://fakerjs.dev/api/date.html#weekday). + * + * These methods have additional concerns about reproducibility, see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results). + */ +export class DateModule extends SimpleDateModule { + constructor(protected readonly faker: Faker) { + super(faker); + } + + /** * Returns a random name of a month. * * @param options The optional options to use. @@ -1204,91 +1297,4 @@ export class DateModule { return this.faker.helpers.arrayElement(source[type]); } - - /** - * Returns a random birthdate. - * - * @param options The options to use to generate the birthdate. If no options are set, an age between 18 and 80 (inclusive) is generated. - * @param options.min The minimum age or year to generate a birthdate. - * @param options.max The maximum age or year to generate a birthdate. - * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `now`. - * @param options.mode The mode to generate the birthdate. Supported modes are `'age'` and `'year'` . - * - * There are two modes available `'age'` and `'year'`: - * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`). - * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`). - * - * Defaults to `year`. - * - * @example - * faker.date.birthdate() // 1977-07-10T01:37:30.719Z - * faker.date.birthdate({ min: 18, max: 65, mode: 'age' }) // 2003-11-02T20:03:20.116Z - * faker.date.birthdate({ min: 1900, max: 2000, mode: 'year' }) // 1940-08-20T08:53:07.538Z - * - * @since 7.0.0 - */ - birthdate( - options: { - /** - * The minimum age or year to generate a birthdate. - * - * @default 18 - */ - min?: number; - /** - * The maximum age or year to generate a birthdate. - * - * @default 80 - */ - max?: number; - /** - * The mode to generate the birthdate. Supported modes are `'age'` and `'year'` . - * - * There are two modes available `'age'` and `'year'`: - * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`). - * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`). - * - * @default 'year' - */ - mode?: 'age' | 'year'; - /** - * The date to use as reference point for the newly generated date. - * - * @default faker.defaultRefDate() - */ - refDate?: string | Date | number; - } = {} - ): Date { - if (options.max < options.min) { - throw new FakerError( - `Max ${options.max} should be larger than or equal to min ${options.min}.` - ); - } - - const mode = options.mode === 'age' ? 'age' : 'year'; - const refDate = toDate(options.refDate, this.faker.defaultRefDate); - const refYear = refDate.getUTCFullYear(); - - // If no min or max is specified, generate a random date between (now - 80) years and (now - 18) years respectively - // So that people can still be considered as adults in most cases - - // Convert to epoch timestamps - let min: number; - let max: number; - if (mode === 'age') { - min = new Date(refDate).setUTCFullYear(refYear - (options.max ?? 80) - 1); - max = new Date(refDate).setUTCFullYear(refYear - (options.min ?? 18)); - } else { - // Avoid generating dates the first and last date of the year - // to avoid running into other years depending on the timezone. - min = new Date(Date.UTC(0, 0, 2)).setUTCFullYear( - options.min ?? refYear - 80 - ); - max = new Date(Date.UTC(0, 11, 30)).setUTCFullYear( - options.max ?? refYear - 18 - ); - } - - return new Date(this.faker.number.int({ min, max })); - } } diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index 39b42253..3889c572 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -1,4 +1,4 @@ -import type { Faker } from '../..'; +import type { Faker, SimpleFaker } from '../..'; import { FakerError } from '../../errors/faker-error'; import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions'; import { deprecated } from '../../internal/deprecated'; @@ -24,7 +24,7 @@ import * as uniqueExec from './unique'; * @since 8.0.0 */ function getRepetitionsBasedOnQuantifierParameters( - faker: Faker, + faker: SimpleFaker, quantifierSymbol: string, quantifierMin: string, quantifierMax: string @@ -94,7 +94,10 @@ function getRepetitionsBasedOnQuantifierParameters( * * @since 5.0.0 */ -function legacyRegexpStringParse(faker: Faker, string: string = ''): string { +function legacyRegexpStringParse( + faker: SimpleFaker, + string: string = '' +): string { // Deal with range repeat `{min,max}` const RANGE_REP_REG = /(.)\{(\d+)\,(\d+)\}/; const REP_REG = /(.)\{(\d+)\}/; @@ -155,18 +158,7 @@ function legacyRegexpStringParse(faker: Faker, string: string = ''): string { return string; } -/** - * Module with various helper methods providing basic (seed-dependent) operations useful for implementing faker methods. - * - * ### Overview - * - * A particularly helpful method is [`arrayElement()`](https://fakerjs.dev/api/helpers.html#arrayelement) which returns a random element from an array. This is useful when adding custom data that Faker doesn't contain. - * - * There are alternatives of this method for objects ([`objectKey()`](https://fakerjs.dev/api/helpers.html#objectkey) and [`objectValue()`](https://fakerjs.dev/api/helpers.html#objectvalue)) and enums ([`enumValue()`](https://fakerjs.dev/api/helpers.html#enumvalue)). You can also return multiple elements ([`arrayElements()`](https://fakerjs.dev/api/helpers.html#arrayelements)) or elements according to a weighting ([`weightedArrayElement()`](https://fakerjs.dev/api/helpers.html#weightedarrayelement)). - * - * A number of methods can generate strings according to various patterns: [`replaceSymbols()`](https://fakerjs.dev/api/helpers.html#replacesymbols), [`replaceSymbolWithNumber()`](https://fakerjs.dev/api/helpers.html#replacesymbolwithnumber), and [`fromRegExp()`](https://fakerjs.dev/api/helpers.html#fromregexp). - */ -export class HelpersModule { +export class SimpleHelpersModule { /** * Global store of unique values. * This means that faker should *never* return duplicate values across all API methods when using `faker.helpers.unique` without passing `options.store`. @@ -175,7 +167,7 @@ export class HelpersModule { */ private readonly uniqueStore: Record<RecordKey, RecordKey> = {}; - constructor(private readonly faker: Faker) { + constructor(protected readonly faker: SimpleFaker) { bindThisToMemberFunctions(this); } @@ -1054,6 +1046,212 @@ export class HelpersModule { } /** + * Helper method that converts the given number or range to a number. + * + * @param numberOrRange The number or range to convert. + * @param numberOrRange.min The minimum value for the range. + * @param numberOrRange.max The maximum value for the range. + * + * @example + * faker.helpers.rangeToNumber(1) // 1 + * faker.helpers.rangeToNumber({ min: 1, max: 10 }) // 5 + * + * @since 8.0.0 + */ + rangeToNumber( + numberOrRange: + | number + | { + /** + * The minimum value for the range. + */ + min: number; + /** + * The maximum value for the range. + */ + max: number; + } + ): number { + if (typeof numberOrRange === 'number') { + return numberOrRange; + } + + return this.faker.number.int(numberOrRange); + } + + /** + * Generates a unique result using the results of the given method. + * Used unique entries will be stored internally and filtered from subsequent calls. + * + * @template TMethod The type of the method to execute. + * + * @param method The method used to generate the values. + * @param args The arguments used to call the method. + * @param options The optional options used to configure this method. + * @param options.startTime This parameter does nothing. + * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`. + * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`. + * @param options.currentIterations This parameter does nothing. + * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. + * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. + * @param options.store The store of unique entries. Defaults to a global store. + * + * @see https://github.com/faker-js/faker/issues/1785#issuecomment-1407773744 + * + * @example + * faker.helpers.unique(faker.person.firstName) // 'Corbin' + * + * @since 7.5.0 + * + * @deprecated Please find a dedicated npm package instead, or even create one on your own if you want to. + * More info can be found in issue [faker-js/faker #1785](https://github.com/faker-js/faker/issues/1785). + */ + unique< + TMethod extends ( + // 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 + ...parameters: any[] + ) => RecordKey, + >( + method: TMethod, + args: Parameters<TMethod> = [] as Parameters<TMethod>, + options: { + /** + * This parameter does nothing. + * + * @default new Date().getTime() + */ + startTime?: number; + /** + * The time in milliseconds this method may take before throwing an error. + * + * @default 50 + */ + maxTime?: number; + /** + * The total number of attempts to try before throwing an error. + * + * @default 50 + */ + maxRetries?: number; + /** + * This parameter does nothing. + * + * @default 0 + */ + currentIterations?: number; + /** + * The value or values that should be excluded/skipped. + * + * @default [] + */ + exclude?: RecordKey | RecordKey[]; + /** + * The function used to determine whether a value was already returned. + * + * Defaults to check the existence of the key. + * + * @default (obj, key) => (obj[key] === undefined ? -1 : 0) + */ + compare?: (obj: Record<RecordKey, RecordKey>, key: RecordKey) => 0 | -1; + /** + * The store of unique entries. + * + * Defaults to a global store. + */ + store?: Record<RecordKey, RecordKey>; + } = {} + ): ReturnType<TMethod> { + deprecated({ + deprecated: 'faker.helpers.unique', + proposed: + 'https://github.com/faker-js/faker/issues/1785#issuecomment-1407773744', + since: '8.0', + until: '9.0', + }); + + const { + maxTime = 50, + maxRetries = 50, + exclude = [], + store = this.uniqueStore, + } = options; + return uniqueExec.exec(method, args, { + ...options, + startTime: new Date().getTime(), + maxTime, + maxRetries, + currentIterations: 0, + exclude, + store, + }); + } + + /** + * Generates an array containing values returned by the given method. + * + * @template TResult The type of elements. + * + * @param method The method used to generate the values. + * @param options The optional options object. + * @param options.count The number or range of elements to generate. Defaults to `3`. + * + * @example + * faker.helpers.multiple(faker.person.firstName) // [ 'Aniya', 'Norval', 'Dallin' ] + * faker.helpers.multiple(faker.person.firstName, { count: 3 }) // [ 'Santos', 'Lavinia', 'Lavinia' ] + * + * @since 8.0.0 + */ + multiple<TResult>( + method: () => TResult, + options: { + /** + * The number or range of elements to generate. + * + * @default 3 + */ + count?: + | number + | { + /** + * The minimum value for the range. + */ + min: number; + /** + * The maximum value for the range. + */ + max: number; + }; + } = {} + ): TResult[] { + const count = this.rangeToNumber(options.count ?? 3); + if (count <= 0) { + return []; + } + + // TODO @ST-DDT 2022-11-21: Add support for unique option + + return Array.from({ length: count }, method); + } +} + +/** + * Module with various helper methods providing basic (seed-dependent) operations useful for implementing faker methods. + * + * ### Overview + * + * A particularly helpful method is [`arrayElement()`](https://fakerjs.dev/api/helpers.html#arrayelement) which returns a random element from an array. This is useful when adding custom data that Faker doesn't contain. + * + * There are alternatives of this method for objects ([`objectKey()`](https://fakerjs.dev/api/helpers.html#objectkey) and [`objectValue()`](https://fakerjs.dev/api/helpers.html#objectvalue)) and enums ([`enumValue()`](https://fakerjs.dev/api/helpers.html#enumvalue)). You can also return multiple elements ([`arrayElements()`](https://fakerjs.dev/api/helpers.html#arrayelements)) or elements according to a weighting ([`weightedArrayElement()`](https://fakerjs.dev/api/helpers.html#weightedarrayelement)). + * + * A number of methods can generate strings according to various patterns: [`replaceSymbols()`](https://fakerjs.dev/api/helpers.html#replacesymbols), [`replaceSymbolWithNumber()`](https://fakerjs.dev/api/helpers.html#replacesymbolwithnumber), and [`fromRegExp()`](https://fakerjs.dev/api/helpers.html#fromregexp). + */ +export class HelpersModule extends SimpleHelpersModule { + constructor(protected readonly faker: Faker) { + super(faker); + } + + /** * Generator for combining faker methods based on a static string input. * * Note: We recommend using string template literals instead of `fake()`, @@ -1277,193 +1475,4 @@ export class HelpersModule { // return the response recursively until we are done finding all tags return this.fake(res); } - - /** - * Helper method that converts the given number or range to a number. - * - * @param numberOrRange The number or range to convert. - * @param numberOrRange.min The minimum value for the range. - * @param numberOrRange.max The maximum value for the range. - * - * @example - * faker.helpers.rangeToNumber(1) // 1 - * faker.helpers.rangeToNumber({ min: 1, max: 10 }) // 5 - * - * @since 8.0.0 - */ - rangeToNumber( - numberOrRange: - | number - | { - /** - * The minimum value for the range. - */ - min: number; - /** - * The maximum value for the range. - */ - max: number; - } - ): number { - if (typeof numberOrRange === 'number') { - return numberOrRange; - } - - return this.faker.number.int(numberOrRange); - } - - /** - * Generates a unique result using the results of the given method. - * Used unique entries will be stored internally and filtered from subsequent calls. - * - * @template TMethod The type of the method to execute. - * - * @param method The method used to generate the values. - * @param args The arguments used to call the method. - * @param options The optional options used to configure this method. - * @param options.startTime This parameter does nothing. - * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`. - * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`. - * @param options.currentIterations This parameter does nothing. - * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. - * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. - * @param options.store The store of unique entries. Defaults to a global store. - * - * @see https://github.com/faker-js/faker/issues/1785#issuecomment-1407773744 - * - * @example - * faker.helpers.unique(faker.person.firstName) // 'Corbin' - * - * @since 7.5.0 - * - * @deprecated Please find a dedicated npm package instead, or even create one on your own if you want to. - * More info can be found in issue [faker-js/faker #1785](https://github.com/faker-js/faker/issues/1785). - */ - unique< - TMethod extends ( - // 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 - ...parameters: any[] - ) => RecordKey, - >( - method: TMethod, - args: Parameters<TMethod> = [] as Parameters<TMethod>, - options: { - /** - * This parameter does nothing. - * - * @default new Date().getTime() - */ - startTime?: number; - /** - * The time in milliseconds this method may take before throwing an error. - * - * @default 50 - */ - maxTime?: number; - /** - * The total number of attempts to try before throwing an error. - * - * @default 50 - */ - maxRetries?: number; - /** - * This parameter does nothing. - * - * @default 0 - */ - currentIterations?: number; - /** - * The value or values that should be excluded/skipped. - * - * @default [] - */ - exclude?: RecordKey | RecordKey[]; - /** - * The function used to determine whether a value was already returned. - * - * Defaults to check the existence of the key. - * - * @default (obj, key) => (obj[key] === undefined ? -1 : 0) - */ - compare?: (obj: Record<RecordKey, RecordKey>, key: RecordKey) => 0 | -1; - /** - * The store of unique entries. - * - * Defaults to a global store. - */ - store?: Record<RecordKey, RecordKey>; - } = {} - ): ReturnType<TMethod> { - deprecated({ - deprecated: 'faker.helpers.unique', - proposed: - 'https://github.com/faker-js/faker/issues/1785#issuecomment-1407773744', - since: '8.0', - until: '9.0', - }); - - const { - maxTime = 50, - maxRetries = 50, - exclude = [], - store = this.uniqueStore, - } = options; - return uniqueExec.exec(method, args, { - ...options, - startTime: new Date().getTime(), - maxTime, - maxRetries, - currentIterations: 0, - exclude, - store, - }); - } - - /** - * Generates an array containing values returned by the given method. - * - * @template TResult The type of elements. - * - * @param method The method used to generate the values. - * @param options The optional options object. - * @param options.count The number or range of elements to generate. Defaults to `3`. - * - * @example - * faker.helpers.multiple(faker.person.firstName) // [ 'Aniya', 'Norval', 'Dallin' ] - * faker.helpers.multiple(faker.person.firstName, { count: 3 }) // [ 'Santos', 'Lavinia', 'Lavinia' ] - * - * @since 8.0.0 - */ - multiple<TResult>( - method: () => TResult, - options: { - /** - * The number or range of elements to generate. - * - * @default 3 - */ - count?: - | number - | { - /** - * The minimum value for the range. - */ - min: number; - /** - * The maximum value for the range. - */ - max: number; - }; - } = {} - ): TResult[] { - const count = this.rangeToNumber(options.count ?? 3); - if (count <= 0) { - return []; - } - - // TODO @ST-DDT 2022-11-21: Add support for unique option - - return Array.from({ length: count }, method); - } } diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 6e0774e1..a2e64724 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -1,4 +1,4 @@ -import type { Faker } from '../..'; +import type { SimpleFaker } from '../..'; import { FakerError } from '../../errors/faker-error'; import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions'; import type { Mersenne } from '../../internal/mersenne/mersenne'; @@ -18,7 +18,7 @@ import type { Mersenne } from '../../internal/mersenne/mersenne'; * - For credit card numbers, use [`faker.finance.creditCardNumber()`](https://fakerjs.dev/api/finance.html#creditcardnumber). */ export class NumberModule { - constructor(private readonly faker: Faker) { + constructor(private readonly faker: SimpleFaker) { bindThisToMemberFunctions(this); } diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 536a1c6a..8167ebea 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -1,4 +1,4 @@ -import type { Faker } from '../..'; +import type { SimpleFaker } from '../..'; import { FakerError } from '../../errors/faker-error'; import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions'; import type { LiteralUnion } from '../../utils/types'; @@ -103,7 +103,7 @@ const SAMPLE_MAX_LENGTH = 2 ** 20; * - The [`faker.helpers`](https://fakerjs.dev/api/helpers.html) module includes a number of string related methods. */ export class StringModule { - constructor(private readonly faker: Faker) { + constructor(private readonly faker: SimpleFaker) { bindThisToMemberFunctions(this); } diff --git a/src/simple-faker.ts b/src/simple-faker.ts new file mode 100644 index 00000000..c4ecb8df --- /dev/null +++ b/src/simple-faker.ts @@ -0,0 +1,220 @@ +import type { Mersenne } from './internal/mersenne/mersenne'; +import mersenne from './internal/mersenne/mersenne'; +import { DatatypeModule } from './modules/datatype'; +import { SimpleDateModule } from './modules/date'; +import { SimpleHelpersModule } from './modules/helpers'; +import { NumberModule } from './modules/number'; +import { StringModule } from './modules/string'; + +/** + * This is a simplified Faker class that doesn't need any localized data to generate its output. + * + * It only includes methods from: + * - `datatype` + * - `date` (without `month` and `weekday`) + * - `helpers` (without `fake`) + * - `number` + * - `string` + * + * @example + * import { simpleFaker } from '@faker-js/faker'; + * // const { simpleFaker } = require('@faker-js/faker'); + * + * // simpleFaker.seed(1234); + * + * simpleFaker.number.int(10); // 4 + * simpleFaker.string.uuid(); // 'c50e1f5c-86e8-4aa9-888e-168e0a182519' + */ +export class SimpleFaker { + protected _defaultRefDate: () => Date = () => new Date(); + + /** + * Gets a new reference date used to generate relative dates. + */ + get defaultRefDate(): () => Date { + return this._defaultRefDate; + } + + /** + * Sets the `refDate` source to use if no `refDate` date is passed to the date methods. + * + * @param dateOrSource The function or the static value used to generate the `refDate` date instance. + * The function must return a new valid `Date` instance for every call. + * Defaults to `() => new Date()`. + * + * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) + * @see faker.seed() for reproducible results. + * + * @example + * faker.seed(1234); + * + * // Default behavior + * // faker.setDefaultRefDate(); + * faker.date.past(); // Changes based on the current date/time + * + * // Use a static ref date + * faker.setDefaultRefDate(new Date('2020-01-01')); + * faker.date.past(); // Reproducible '2019-07-03T08:27:58.118Z' + * + * // Use a ref date that changes every time it is used + * let clock = new Date("2020-01-01").getTime(); + * faker.setDefaultRefDate(() => { + * clock += 1000; // +1s + * return new Date(clock); + * }); + * + * faker.defaultRefDate() // 2020-01-01T00:00:01Z + * faker.defaultRefDate() // 2020-01-01T00:00:02Z + */ + setDefaultRefDate( + dateOrSource: string | Date | number | (() => Date) = () => new Date() + ): void { + if (typeof dateOrSource === 'function') { + this._defaultRefDate = dateOrSource; + } else { + this._defaultRefDate = () => new Date(dateOrSource); + } + } + + /** @internal */ + private readonly _mersenne: Mersenne = mersenne(); + + readonly datatype: DatatypeModule = new DatatypeModule(this); + readonly date: SimpleDateModule = new SimpleDateModule(this); + readonly helpers: SimpleHelpersModule = new SimpleHelpersModule(this); + readonly number: NumberModule = new NumberModule(this); + readonly string: StringModule = new StringModule(this); + + /** + * Creates a new instance of SimpleFaker. + * + * In nearly any case you should use the prebuilt `simpleFaker` instances instead of the constructor. + */ + constructor() { + // This empty constructor just exists for VitePress docs + } + + /** + * Sets the seed or generates a new one. + * + * Please note that generated values are dependent on both the seed and the + * number of calls that have been made since it was set. + * + * This method is intended to allow for consistent values in tests, so you + * might want to use hardcoded values as the seed. + * + * In addition to that it can be used for creating truly random tests + * (by passing no arguments), that still can be reproduced if needed, + * by logging the result and explicitly setting it if needed. + * + * @param seed The seed to use. Defaults to a random number. + * + * @returns The seed that was set. + * + * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) + * @see faker.setDefaultRefDate() when generating relative dates. + * + * @example + * // Consistent values for tests: + * faker.seed(42) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * faker.seed(42) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * // Random but reproducible tests: + * // Simply log the seed, and if you need to reproduce it, insert the seed here + * console.log('Running test with seed:', faker.seed()); + */ + seed(seed?: number): number; + /** + * Sets the seed array. + * + * Please note that generated values are dependent on both the seed and the + * number of calls that have been made since it was set. + * + * This method is intended to allow for consistent values in a tests, so you + * might want to use hardcoded values as the seed. + * + * In addition to that it can be used for creating truly random tests + * (by passing no arguments), that still can be reproduced if needed, + * by logging the result and explicitly setting it if needed. + * + * @param seedArray The seed array to use. + * + * @returns The seed array that was set. + * + * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) + * @see faker.setDefaultRefDate() when generating relative dates. + * + * @example + * // Consistent values for tests: + * faker.seed([42, 13, 17]) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * faker.seed([42, 13, 17]) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * // Random but reproducible tests: + * // Simply log the seed, and if you need to reproduce it, insert the seed here + * console.log('Running test with seed:', faker.seed()); + */ + seed(seedArray: number[]): number[]; + /** + * Sets the seed or generates a new one. + * + * Please note that generated values are dependent on both the seed and the + * number of calls that have been made since it was set. + * + * This method is intended to allow for consistent values in a tests, so you + * might want to use hardcoded values as the seed. + * + * In addition to that it can be used for creating truly random tests + * (by passing no arguments), that still can be reproduced if needed, + * by logging the result and explicitly setting it if needed. + * + * @param seed The seed or seed array to use. + * + * @returns The seed that was set. + * + * @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results) + * @see faker.setDefaultRefDate() when generating relative dates. + * + * @example + * // Consistent values for tests (using a number): + * faker.seed(42) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * faker.seed(42) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * // Consistent values for tests (using an array): + * faker.seed([42, 13, 17]) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * faker.seed([42, 13, 17]) + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 + * + * // Random but reproducible tests: + * // Simply log the seed, and if you need to reproduce it, insert the seed here + * console.log('Running test with seed:', faker.seed()); + */ + seed(seed?: number | number[]): number | number[]; + seed( + seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) + ): number | number[] { + this._mersenne.seed(seed); + + return seed; + } +} + +export const simpleFaker = new SimpleFaker(); |
