Skip to content

feat: rename types in config #171

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 2 commits into from
Sep 20, 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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,26 @@ export const aUser = (overrides?: Partial<User>): User => {
}
```
### typeNamesMapping (`{ [typeName: string]: string }`, defaultValue: `{}`)
Allows you to define mappings to rename the types. This is useful when you want to override the generated type name. For example, if you have a type called `User` and you want to rename it to `RenamedUser` you can do the following:
```
plugins:
- typescript-mock-data:
typesFile: '../generated-types.ts'
typeNamesMapping:
User: RenamedUser
```
This will generate the following mock function:
```
export const aUser = (overrides?: Partial<RenamedUser>): RenamedUser => {
```
**Note:** It is not possible to rename your enums using this option.
### transformUnderscore (`boolean`, defaultValue: `true`)
When disabled, underscores will be retained for type names when the case is changed. It has no effect if `typeNames` is set to `keep`.
Expand Down
43 changes: 38 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Options<T = TypeNode> = {
useImplementingTypes: boolean;
defaultNullableToNull: boolean;
nonNull: boolean;
typeNamesMapping?: Record<string, string>;
};

const getTerminateCircularRelationshipsConfig = ({ terminateCircularRelationships }: TypescriptMocksPluginConfig) =>
Expand All @@ -64,6 +65,15 @@ const createNameConverter =
return `${prefix}${convertName(value, resolveExternalModuleAndFn(convention), transformUnderscore)}`;
};

const renameImports = (list: string[], typeNamesMapping: Record<string, string>) => {
return list.map((type) => {
if (typeNamesMapping && typeNamesMapping[type]) {
return `${type} as ${typeNamesMapping[type]}`;
}
return type;
});
};

const toMockName = (typedName: string, casedName: string, prefix?: string) => {
if (prefix) {
return `${prefix}${casedName}`;
Expand Down Expand Up @@ -380,14 +390,20 @@ const getNamedType = (opts: Options<NamedTypeNode | ObjectTypeDefinitionNode>):
opts.typeNamesConvention,
opts.transformUnderscore,
);
const casedNameWithPrefix = typeNameConverter(name, opts.typesPrefix);
const renamedType = renameImports([name], opts.typeNamesMapping)[0];
const casedNameWithPrefix = typeNameConverter(renamedType, opts.typesPrefix);
return `relationshipsToOmit.has('${casedName}') ? {} as ${casedNameWithPrefix} : ${toMockName(
name,
casedName,
opts.prefix,
)}({}, relationshipsToOmit)`;
} else {
return `relationshipsToOmit.has('${casedName}') ? {} as ${casedName} : ${toMockName(
const renamedType = renameImports([name], opts.typeNamesMapping)[0];
const renamedCasedName = createNameConverter(
opts.typeNamesConvention,
opts.transformUnderscore,
)(renamedType);
return `relationshipsToOmit.has('${casedName}') ? {} as ${renamedCasedName} : ${toMockName(
name,
casedName,
opts.prefix,
Expand Down Expand Up @@ -443,10 +459,12 @@ const getMockString = (
prefix,
typesPrefix = '',
transformUnderscore: boolean,
typeNamesMapping?: Record<string, string>,
) => {
const typeNameConverter = createNameConverter(typeNamesConvention, transformUnderscore);
const NewTypeName = typeNamesMapping[typeName] || typeName;
const casedName = typeNameConverter(typeName);
const casedNameWithPrefix = typeNameConverter(typeName, typesPrefix);
const casedNameWithPrefix = typeNameConverter(NewTypeName, typesPrefix);
const typename = addTypename ? `\n __typename: '${typeName}',` : '';
const typenameReturnType = addTypename ? `{ __typename: '${typeName}' } & ` : '';

Expand Down Expand Up @@ -489,6 +507,7 @@ const getImportTypes = ({
transformUnderscore,
enumsAsTypes,
useTypeImports,
typeNamesMapping,
}: {
typeNamesConvention: NamingConvention;
definitions: any;
Expand All @@ -499,19 +518,23 @@ const getImportTypes = ({
transformUnderscore: boolean;
enumsAsTypes: boolean;
useTypeImports: boolean;
typeNamesMapping?: Record<string, string>;
}) => {
const typenameConverter = createNameConverter(typeNamesConvention, transformUnderscore);
const typeImports = typesPrefix?.endsWith('.')
? [typesPrefix.slice(0, -1)]
: definitions
.filter(({ typeName }: { typeName: string }) => !!typeName)
.map(({ typeName }: { typeName: string }) => typenameConverter(typeName, typesPrefix));

const enumTypes = enumsPrefix?.endsWith('.')
? [enumsPrefix.slice(0, -1)]
: types.filter(({ type }) => type === 'enum').map(({ name }) => typenameConverter(name, enumsPrefix));

const renamedTypeImports = renameImports(typeImports, typeNamesMapping);

if (!enumsAsTypes || useTypeImports) {
typeImports.push(...enumTypes);
renamedTypeImports.push(...enumTypes);
}

function onlyUnique(value, index, self) {
Expand All @@ -520,7 +543,9 @@ const getImportTypes = ({

const importPrefix = `import ${useTypeImports ? 'type ' : ''}`;

return typesFile ? `${importPrefix}{ ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : '';
return typesFile
? `${importPrefix}{ ${renamedTypeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n`
: '';
};

type GeneratorName = keyof Casual.Casual | keyof Casual.functions | string;
Expand Down Expand Up @@ -564,6 +589,7 @@ export interface TypescriptMocksPluginConfig {
useImplementingTypes?: boolean;
defaultNullableToNull?: boolean;
useTypeImports?: boolean;
typeNamesMapping?: Record<string, string>;
}

interface TypeItem {
Expand Down Expand Up @@ -614,6 +640,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
const useImplementingTypes = config.useImplementingTypes ?? false;
const defaultNullableToNull = config.defaultNullableToNull ?? false;
const generatorLocale = config.locale || 'en';
const typeNamesMapping = config.typeNamesMapping || {};

// List of types that are enums
const types: TypeItem[] = [];
Expand Down Expand Up @@ -693,6 +720,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
useImplementingTypes,
defaultNullableToNull,
nonNull: false,
typeNamesMapping: config.typeNamesMapping,
});

return ` ${fieldName}: overrides && overrides.hasOwnProperty('${fieldName}') ? overrides.${fieldName}! : ${value},`;
Expand Down Expand Up @@ -731,6 +759,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
useImplementingTypes,
defaultNullableToNull,
nonNull: false,
typeNamesMapping: config.typeNamesMapping,
});

return ` ${field.name.value}: overrides && overrides.hasOwnProperty('${field.name.value}') ? overrides.${field.name.value}! : ${value},`;
Expand All @@ -747,6 +776,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
config.prefix,
config.typesPrefix,
transformUnderscore,
typeNamesMapping,
);
},
};
Expand All @@ -770,6 +800,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
config.prefix,
config.typesPrefix,
transformUnderscore,
typeNamesMapping,
);
},
};
Expand All @@ -791,6 +822,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
config.prefix,
config.typesPrefix,
transformUnderscore,
typeNamesMapping,
);
},
};
Expand All @@ -813,6 +845,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
transformUnderscore: transformUnderscore,
useTypeImports: config.useTypeImports,
enumsAsTypes,
typeNamesMapping,
});
// Function that will generate the mocks.
// We generate it after having visited because we need to distinct types from enums
Expand Down
31 changes: 31 additions & 0 deletions tests/typeNamesMapping/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { buildSchema } from 'graphql/index';

export default buildSchema(/* GraphQL */ `
enum EnumExample {
LOREM
IPSUM
}

type A {
id: ID!
str: String!
email: String!
}

type B {
id: ID!
str: String!
email: String!
}

type C {
id: ID!
str: String!
enum: EnumExample!
D: D!
}

type D {
nested: C!
}
`);
36 changes: 36 additions & 0 deletions tests/typeNamesMapping/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { plugin } from '../../src';
import testSchema from './schema';

it('should support typeNamesMapping', async () => {
const result = await plugin(testSchema, [], {
typesFile: './types/graphql.ts',
typeNamesMapping: { A: 'RenamedAType' },
});

expect(result).toBeDefined();
expect(result).toContain("import { A as RenamedAType, B, C, D, EnumExample } from './types/graphql';");
});

it('should support typeNamesMapping with circular relationships', async () => {
const result = await plugin(testSchema, [], {
typesFile: './types/graphql.ts',
typeNamesMapping: { D: 'RenamedDType' },
terminateCircularRelationships: 'immediate',
});

expect(result).toBeDefined();
expect(result).toContain("import { A, B, C, D as RenamedDType, EnumExample } from './types/graphql';");
expect(result).toContain(
"D: overrides && overrides.hasOwnProperty('D') ? overrides.D! : relationshipsToOmit.has('D') ? {} as DAsRenamedDType : aD({}, relationshipsToOmit),",
);
});

it('should not support typeNamesMapping when enum type is given', async () => {
const result = await plugin(testSchema, [], {
typesFile: './types/graphql.ts',
typeNamesMapping: { EnumExample: 'RenamedEnum' },
});

expect(result).toBeDefined();
expect(result).toContain("import { A, B, C, D, EnumExample } from './types/graphql';");
});
8 changes: 7 additions & 1 deletion tests/useTypeImports/__snapshots__/spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should support useTypeImports 1`] = `
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';
"import type { Avatar, User, Partial, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';

export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
return {
Expand All @@ -25,6 +25,12 @@ export const aUser = (overrides?: Partial<User>): User => {
};
};

export const aPartial = (overrides?: Partial<Partial>): Partial => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '262c8866-bf76-4ccf-b606-2a0b4742f81f',
};
};

export const aWithAvatar = (overrides?: Partial<WithAvatar>): WithAvatar => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '99f515e7-21e0-461d-b823-0d4c7f4dafc5',
Expand Down
4 changes: 4 additions & 0 deletions tests/useTypeImports/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export default buildSchema(/* GraphQL */ `
prefixedEnum: Prefixed_Enum
}

type Partial {
id: ID!
}

interface WithAvatar {
id: ID!
avatar: Avatar
Expand Down
2 changes: 1 addition & 1 deletion tests/useTypeImports/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ it('should support useTypeImports', async () => {

expect(result).toBeDefined();
expect(result).toContain(
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';",
"import type { Avatar, User, Partial, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';",
);
expect(result).toMatchSnapshot();
});
Loading