Skip to content

Commit

Permalink
feat: split SimpleFaker class from Faker (#2369)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 committed Sep 18, 2023
1 parent f40e217 commit d6a4f8c
Show file tree
Hide file tree
Showing 14 changed files with 692 additions and 524 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/api-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
export const apiPages = [
{ text: 'Overview', link: '/api/' },
{ text: 'Faker', link: '/api/faker.html' },
{ text: 'SimpleFaker', link: '/api/simpleFaker.html' },
{ text: 'Airline', link: '/api/airline.html' },
{ text: 'Animal', link: '/api/animal.html' },
{ text: 'Color', link: '/api/color.html' },
Expand Down
15 changes: 15 additions & 0 deletions docs/guide/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ or alternatively you can set a default reference date for all these methods:
faker.setDefaultRefDate('2023-01-01T00:00:00.000Z');
```

## Simple data generation

Faker provides a `simpleFaker` that can be used to generate data that are not based on any locales like numbers and strings.
Also **helpers** like `arrayElement` or `multiple` are available.

This is useful if you just want to generate e.g. `uuid`s for your test environment, but don't want/need to initiate/load a full Faker instance, which would include at least 500KB of locale data.

```ts
import { simpleFaker } from '@faker-js/faker';

const uuid = simpleFaker.string.uuid();
```

See more about `SimpleFaker` in the [API docs](/api/simpleFaker).

## Create complex objects

Faker mostly generates values for primitives.
Expand Down
40 changes: 28 additions & 12 deletions scripts/apidoc/fakerClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,48 @@ import type { Method } from '../../docs/.vitepress/components/api-docs/method';
import { writeApiDocsModule } from './apiDocsWriter';
import { analyzeModule, processModuleMethods } from './moduleMethods';
import { analyzeSignature } from './signature';
import { selectApiSignature } from './typedoc';
import { extractModuleFieldName, selectApiSignature } from './typedoc';
import type { ModuleSummary } from './utils';

export async function processFakerClass(
export async function processFakerClasses(
project: ProjectReflection
): Promise<ModuleSummary> {
const fakerClass = project
): Promise<ModuleSummary[]> {
const fakerClasses = project
.getChildrenByKind(ReflectionKind.Class)
.filter((clazz) => clazz.name === 'Faker')[0];
.filter((clazz) => clazz.name === 'Faker' || clazz.name === 'SimpleFaker');

if (!fakerClass) {
throw new Error('Faker class not found');
if (fakerClasses.length !== 2) {
throw new Error('Faker classes not found');
}

return processClass(fakerClass);
return Promise.all(fakerClasses.map(processClass));
}

async function processClass(
fakerClass: DeclarationReflection
): Promise<ModuleSummary> {
console.log(`Processing Faker class`);
const { name } = fakerClass;
const moduleFieldName = extractModuleFieldName(fakerClass);

console.log(`Processing ${name} class`);

const { comment, deprecated } = analyzeModule(fakerClass);
const methods: Method[] = [];

console.debug(`- constructor`);
methods.push(await processConstructor(fakerClass));

methods.push(...(await processModuleMethods(fakerClass, 'faker.')));
methods.push(
...(await processModuleMethods(fakerClass, `${moduleFieldName}.`))
);

return writeApiDocsModule('Faker', 'faker', comment, deprecated, methods);
return writeApiDocsModule(
name,
moduleFieldName,
comment,
deprecated,
methods
);
}

async function processConstructor(
Expand All @@ -45,7 +57,11 @@ async function processConstructor(

const signature = selectApiSignature(constructor);

const method = await analyzeSignature(signature, '', 'new Faker');
const method = await analyzeSignature(
signature,
'',
`new ${fakerClass.name}`
);

return {
...method,
Expand Down
4 changes: 2 additions & 2 deletions scripts/apidoc/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
writeApiSearchIndex,
writeSourceBaseUrl,
} from './apiDocsWriter';
import { processFakerClass } from './fakerClass';
import { processFakerClasses } from './fakerClass';
import { processFakerUtilities } from './fakerUtilities';
import { processModules } from './moduleMethods';
import { loadProject } from './typedoc';
Expand All @@ -23,7 +23,7 @@ export async function generate(): Promise<void> {
await app.generateJson(project, pathOutputJson);

const pages = await Promise.all([
processFakerClass(project),
...(await processFakerClasses(project)),
...(await processModules(project)).sort((a, b) =>
a.text.localeCompare(b.text)
),
Expand Down
192 changes: 6 additions & 186 deletions src/faker.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand All @@ -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';

/**
Expand Down Expand Up @@ -60,72 +56,16 @@ 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.
*/
// 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);
Expand All @@ -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);
Expand Down Expand Up @@ -299,9 +238,12 @@ export class Faker {
localeFallback?: string;
}
) {
super();

const { locales } = options as {
locales: Record<string, LocaleDefinition>;
};

if (locales != null) {
deprecated({
deprecated:
Expand Down Expand Up @@ -336,128 +278,6 @@ export class Faker {
this.definitions = createLocaleProxy(this.rawDefinitions);
}

/**
* 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.
*
Expand Down
Loading

0 comments on commit d6a4f8c

Please sign in to comment.