Skip to content

feat: update and use faker as default generator #169

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

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
109 changes: 55 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,60 +207,16 @@ In the above example all resolvers with the name `email` will use the `internet.

For detailed configuration options, see [GeneratorOptions](#generatoroptions-type) documentation.

### generateLibrary (`'casual' | 'faker'`, defaultValue: `'casual'`)
### generateLibrary (`'casual' | 'faker'`, defaultValue: `'faker'`)

Select a library to generate mock values. The default is [casual](https://github.com/boo1ean/casual), Other options include [faker](https://github.com/faker-js/faker).
casual dependents on Node API and cannot be executed in a browser. faker is useful when you want to use a mock function with the dynamicValues option enabled in the browser.
Select a library to generate mock values. The default is [faker](https://github.com/faker-js/faker), Other options include [casual](https://github.com/boo1ean/casual)
casual is not maintained and will be remove in future major versions.
faker is useful when you want to use a mock function with the dynamicValues option enabled in the browser.

### `GeneratorOptions` type

This type is used in `scalars` and `fieldGeneration` options.

Examples using **casual**

**Shorthand if you don't have arguments**

```yaml
fieldName: date # gets translated to casual.date()
```

**With arguments**

```yaml
fieldName: # gets translated to casual.date('YYYY-MM-DD')
generator: date
arguments: 'YYYY-MM-DD'
```

**With multiple arguments**

```yaml
fieldName: # gets translated to casual.integer(-100, 100)
generator: integer
arguments:
- -100
- 100
```

**With extra function call**

```yaml
fieldName: # gets translated to casual.integer.toFixed()
generator: integer
extra:
function: toFixed
```

**With extra function call arguments**

```yaml
fieldName: # gets translated to casual.integer.toFixed(3)
generator: integer
extra:
function: toFixed
arguments: 3
```

Examples using **faker**

**With arguments**
Expand Down Expand Up @@ -299,17 +255,17 @@ plugins:
**With extra function call**

```yaml
fieldName: # gets translated to casual.date().toLocaleDateString()
generator: date
fieldName: # gets translated to faker.date.past().toLocaleDateString()
generator: date.past
extra:
function: toLocaleDateString
```

**With extra function call arguments**

```yaml
fieldName: # gets translated to casual.date().toLocaleDateString('en_GB)
generator: date
fieldName: # gets translated to faker.date.past().toLocaleDateString('en_GB)
generator: date.past
extra:
function: toLocaleDateString
arguments: 'en_GB'
Expand All @@ -322,6 +278,51 @@ fieldName: # gets translated to casual.date().toLocaleDateString('en_GB)
fieldName: arrayBufferGenerator()
```

Examples using **casual** (deprecated)

**Shorthand if you don't have arguments**

```yaml
fieldName: date # gets translated to casual.date()
```

**With arguments**

```yaml
fieldName: # gets translated to casual.date('YYYY-MM-DD')
generator: date
arguments: 'YYYY-MM-DD'
```

**With multiple arguments**

```yaml
fieldName: # gets translated to casual.integer(-100, 100)
generator: integer
arguments:
- -100
- 100
```

**With extra function call**

```yaml
fieldName: # gets translated to casual.integer.toFixed()
generator: integer
extra:
function: toFixed
```

**With extra function call arguments**

```yaml
fieldName: # gets translated to casual.integer.toFixed(3)
generator: integer
extra:
function: toFixed
arguments: 3
```

## Examples of usage

**codegen.yml**
Expand All @@ -340,7 +341,7 @@ generates:
enumValues: upper-case#upperCase
typeNames: keep
scalars:
AWSTimestamp: unix_time # gets translated to casual.unix_time
AWSTimestamp: number.int # gets translated to faker.number.int()
```

### With `eslint-disable` rule
Expand All @@ -363,7 +364,7 @@ generates:
enumValues: upper-case#upperCase
typeNames: keep
scalars:
AWSTimestamp: unix_time # gets translated to casual.unix_time
AWSTimestamp: number.int # gets translated to faker.number.int()
```

## Example of generated code
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fakes"
],
"dependencies": {
"@faker-js/faker": "^7.5.0",
"@faker-js/faker": "^8.4.1",
"@graphql-codegen/plugin-helpers": "^5.0.4",
"casual": "^1.6.2",
"change-case-all": "^1.0.15",
Expand Down
24 changes: 14 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
NamedTypeNode,
ObjectTypeDefinitionNode,
} from 'graphql';
import { faker } from '@faker-js/faker';
import * as allFakerLocales from '@faker-js/faker';
import casual from 'casual';
import { oldVisit, PluginFunction, resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
import { sentenceCase } from 'sentence-case';
Expand All @@ -32,6 +32,7 @@ type Options<T = TypeNode> = {
listElementCount: number;
dynamicValues: boolean;
generateLibrary: 'casual' | 'faker';
generatorLocale: string;
fieldGeneration?: TypeFieldMap;
enumsAsTypes?: boolean;
useTypeImports?: boolean;
Expand Down Expand Up @@ -146,8 +147,8 @@ const getCasualCustomValue = (
return value;
};

const getFakerGenerators = (generatorName: GeneratorName) => {
let embeddedGenerator: unknown = faker;
const getFakerGenerators = (generatorName: GeneratorName, locale: string) => {
let embeddedGenerator: unknown = allFakerLocales[`faker${locale.toUpperCase()}`];
let dynamicGenerator = 'faker';

if (typeof generatorName === 'string') {
Expand All @@ -173,7 +174,10 @@ const getFakerCustomValue = (
opts: Options<NamedTypeNode | ObjectTypeDefinitionNode>,
) => {
// If there is a mapping to a `faker` type, then use it
const { embeddedGenerator, dynamicGenerator } = getFakerGenerators(generatorDefinition.generator);
const { embeddedGenerator, dynamicGenerator } = getFakerGenerators(
generatorDefinition.generator,
opts.generatorLocale,
);
if (!embeddedGenerator && generatorDefinition.generator) {
return generatorDefinition.generator;
}
Expand Down Expand Up @@ -278,6 +282,7 @@ const getNamedType = (opts: Options<NamedTypeNode | ObjectTypeDefinitionNode>):
const mockValueGenerator = setupMockValueGenerator({
generateLibrary: opts.generateLibrary,
dynamicValues: opts.dynamicValues,
generatorLocale: opts.generatorLocale,
});
if (!opts.dynamicValues) mockValueGenerator.seed(hashedString(opts.typeName + opts.fieldName));
const name = opts.currentType.name.value;
Expand Down Expand Up @@ -603,15 +608,12 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
const transformUnderscore = config.transformUnderscore ?? true;
const listElementCount = Math.max(0, config.listElementCount ?? 1);
const dynamicValues = !!config.dynamicValues;
const generateLibrary = config.generateLibrary || 'casual';
const generateLibrary = config.generateLibrary || 'faker';
const enumsAsTypes = config.enumsAsTypes ?? false;
const useTypeImports = config.useTypeImports ?? false;
const useImplementingTypes = config.useImplementingTypes ?? false;
const defaultNullableToNull = config.defaultNullableToNull ?? false;

if (generateLibrary === 'faker' && config.locale) {
faker.setLocale(config.locale);
}
const generatorLocale = config.locale || 'en';

// List of types that are enums
const types: TypeItem[] = [];
Expand Down Expand Up @@ -684,6 +686,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
listElementCount,
dynamicValues,
generateLibrary,
generatorLocale,
fieldGeneration: config.fieldGeneration,
enumsAsTypes,
useTypeImports,
Expand Down Expand Up @@ -721,6 +724,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
listElementCount,
dynamicValues,
generateLibrary,
generatorLocale,
fieldGeneration: config.fieldGeneration,
enumsAsTypes,
useTypeImports,
Expand Down Expand Up @@ -817,7 +821,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
.filter((mockFn: () => string) => !!mockFn)
.map((mockFn: () => string) => mockFn())
.join('\n');
const functionTokens = setupFunctionTokens(generateLibrary);
const functionTokens = setupFunctionTokens(generateLibrary, generatorLocale);

let mockFile = '';
if (dynamicValues) mockFile += `${functionTokens.import}\n`;
Expand Down
52 changes: 32 additions & 20 deletions src/mockValueGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { faker } from '@faker-js/faker';
import { Faker } from '@faker-js/faker';
import * as allFakerLocales from '@faker-js/faker';
import casual from 'casual';

interface MockValueGenerator {
Expand All @@ -14,12 +15,14 @@ interface MockValueGenerator {

type MockValueGeneratorOptions = {
dynamicValues: boolean;
generatorLocale: string;
};

type FunctionTokens = Record<'import' | 'seed' | 'seedFunction', string>;

type SetupMockValueGeneratorOptions = {
generateLibrary: 'casual' | 'faker';
generatorLocale: string;
dynamicValues: boolean;
};

Expand Down Expand Up @@ -53,52 +56,61 @@ const casualFunctionTokens: FunctionTokens = {

class FakerMockValueGenerator implements MockValueGenerator {
dynamicValues: boolean;
private fakerInstance: Faker;

constructor(opts: MockValueGeneratorOptions) {
this.dynamicValues = opts.dynamicValues;
const fakerImport = `faker${opts.generatorLocale.toUpperCase()}`;
if (!(fakerImport in allFakerLocales)) {
throw new Error(`Cannot find faker version for locale ${opts.generatorLocale.toUpperCase()}`);
}
this.fakerInstance = allFakerLocales[`faker${opts.generatorLocale.toUpperCase()}`];
}

word = () => (this.dynamicValues ? `faker.lorem.word()` : `'${faker.lorem.word()}'`);
uuid = () => (this.dynamicValues ? `faker.datatype.uuid()` : `'${faker.datatype.uuid()}'`);
boolean = () => (this.dynamicValues ? `faker.datatype.boolean()` : faker.datatype.boolean());
word = () => (this.dynamicValues ? `faker.lorem.word()` : `'${this.fakerInstance.lorem.word()}'`);
uuid = () => (this.dynamicValues ? `faker.string.uuid()` : `'${this.fakerInstance.string.uuid()}'`);
boolean = () => (this.dynamicValues ? `faker.datatype.boolean()` : this.fakerInstance.datatype.boolean());
integer = () =>
this.dynamicValues
? `faker.datatype.number({ min: 0, max: 9999 })`
: faker.datatype.number({ min: 0, max: 9999 });
? `faker.number.int({ min: 0, max: 9999 })`
: this.fakerInstance.number.int({ min: 0, max: 9999 });
float = () =>
this.dynamicValues
? `faker.datatype.float({ min: 0, max: 10, precision: 0.1 })`
: faker.datatype.float({ min: 0, max: 10, precision: 0.1 });
? `faker.number.float({ min: 0, max: 10, fractionDigits: 1 })`
: this.fakerInstance.number.float({ min: 0, max: 10, fractionDigits: 1 });
date = () =>
this.dynamicValues
? `faker.date.past(1, new Date(2022, 0)).toISOString()`
: `'${faker.date.past(1, new Date(2022, 0)).toISOString()}'`;
seed = (seed: number) => faker.seed(seed);
? `faker.date.past({ years: 1, refDate: new Date(2022, 0) }).toISOString()`
: `'${this.fakerInstance.date.past({ years: 1, refDate: new Date(2022, 0) }).toISOString()}'`;
seed = (seed: number) => this.fakerInstance.seed(seed);
}

const fakerFunctionTokens: FunctionTokens = {
import: `import { faker } from '@faker-js/faker';`,
seed: 'faker.seed(0);',
seedFunction: 'export const seedMocks = (seed: number) => faker.seed(seed);',
};
function getFakerFunctionTokens(locale = 'en'): FunctionTokens {
return {
import: `import { faker${locale.toUpperCase()} as faker } from '@faker-js/faker';`,
seed: 'faker.seed(0);',
seedFunction: 'export const seedMocks = (seed: number) => faker.seed(seed);',
};
}

export const setupMockValueGenerator = ({
generateLibrary,
dynamicValues,
generatorLocale,
}: SetupMockValueGeneratorOptions): MockValueGenerator => {
switch (generateLibrary) {
case 'casual':
return new CasualMockValueGenerator({ dynamicValues });
return new CasualMockValueGenerator({ dynamicValues, generatorLocale });
case 'faker':
return new FakerMockValueGenerator({ dynamicValues });
return new FakerMockValueGenerator({ dynamicValues, generatorLocale });
}
};

export const setupFunctionTokens = (generateLibrary: 'casual' | 'faker'): FunctionTokens => {
export const setupFunctionTokens = (generateLibrary: 'casual' | 'faker', locale?: string): FunctionTokens => {
switch (generateLibrary) {
case 'casual':
return casualFunctionTokens;
case 'faker':
return fakerFunctionTokens;
return getFakerFunctionTokens(locale);
}
};
Loading
Loading