Skip to content

Commit 2e03d68

Browse files
SanderVanRiessens.vanriessen
and
s.vanriessen
authored
feat: allow renaming types (ardeois#171)
Add `typeNamesMapping` option in order to update a type name --------- Co-authored-by: s.vanriessen <[email protected]>
1 parent 52b4a5d commit 2e03d68

File tree

7 files changed

+137
-7
lines changed

7 files changed

+137
-7
lines changed

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,26 @@ export const aUser = (overrides?: Partial<User>): User => {
155155
}
156156
```
157157
158+
### typeNamesMapping (`{ [typeName: string]: string }`, defaultValue: `{}`)
159+
160+
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:
161+
162+
```
163+
plugins:
164+
- typescript-mock-data:
165+
typesFile: '../generated-types.ts'
166+
typeNamesMapping:
167+
User: RenamedUser
168+
```
169+
170+
This will generate the following mock function:
171+
172+
```
173+
export const aUser = (overrides?: Partial<RenamedUser>): RenamedUser => {
174+
```
175+
176+
**Note:** It is not possible to rename your enums using this option.
177+
158178
### transformUnderscore (`boolean`, defaultValue: `true`)
159179
160180
When disabled, underscores will be retained for type names when the case is changed. It has no effect if `typeNames` is set to `keep`.

src/index.ts

+38-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Options<T = TypeNode> = {
3939
useImplementingTypes: boolean;
4040
defaultNullableToNull: boolean;
4141
nonNull: boolean;
42+
typeNamesMapping?: Record<string, string>;
4243
};
4344

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

68+
const renameImports = (list: string[], typeNamesMapping: Record<string, string>) => {
69+
return list.map((type) => {
70+
if (typeNamesMapping && typeNamesMapping[type]) {
71+
return `${type} as ${typeNamesMapping[type]}`;
72+
}
73+
return type;
74+
});
75+
};
76+
6777
const toMockName = (typedName: string, casedName: string, prefix?: string) => {
6878
if (prefix) {
6979
return `${prefix}${casedName}`;
@@ -380,14 +390,20 @@ const getNamedType = (opts: Options<NamedTypeNode | ObjectTypeDefinitionNode>):
380390
opts.typeNamesConvention,
381391
opts.transformUnderscore,
382392
);
383-
const casedNameWithPrefix = typeNameConverter(name, opts.typesPrefix);
393+
const renamedType = renameImports([name], opts.typeNamesMapping)[0];
394+
const casedNameWithPrefix = typeNameConverter(renamedType, opts.typesPrefix);
384395
return `relationshipsToOmit.has('${casedName}') ? {} as ${casedNameWithPrefix} : ${toMockName(
385396
name,
386397
casedName,
387398
opts.prefix,
388399
)}({}, relationshipsToOmit)`;
389400
} else {
390-
return `relationshipsToOmit.has('${casedName}') ? {} as ${casedName} : ${toMockName(
401+
const renamedType = renameImports([name], opts.typeNamesMapping)[0];
402+
const renamedCasedName = createNameConverter(
403+
opts.typeNamesConvention,
404+
opts.transformUnderscore,
405+
)(renamedType);
406+
return `relationshipsToOmit.has('${casedName}') ? {} as ${renamedCasedName} : ${toMockName(
391407
name,
392408
casedName,
393409
opts.prefix,
@@ -443,10 +459,12 @@ const getMockString = (
443459
prefix,
444460
typesPrefix = '',
445461
transformUnderscore: boolean,
462+
typeNamesMapping?: Record<string, string>,
446463
) => {
447464
const typeNameConverter = createNameConverter(typeNamesConvention, transformUnderscore);
465+
const NewTypeName = typeNamesMapping[typeName] || typeName;
448466
const casedName = typeNameConverter(typeName);
449-
const casedNameWithPrefix = typeNameConverter(typeName, typesPrefix);
467+
const casedNameWithPrefix = typeNameConverter(NewTypeName, typesPrefix);
450468
const typename = addTypename ? `\n __typename: '${typeName}',` : '';
451469
const typenameReturnType = addTypename ? `{ __typename: '${typeName}' } & ` : '';
452470

@@ -489,6 +507,7 @@ const getImportTypes = ({
489507
transformUnderscore,
490508
enumsAsTypes,
491509
useTypeImports,
510+
typeNamesMapping,
492511
}: {
493512
typeNamesConvention: NamingConvention;
494513
definitions: any;
@@ -499,19 +518,23 @@ const getImportTypes = ({
499518
transformUnderscore: boolean;
500519
enumsAsTypes: boolean;
501520
useTypeImports: boolean;
521+
typeNamesMapping?: Record<string, string>;
502522
}) => {
503523
const typenameConverter = createNameConverter(typeNamesConvention, transformUnderscore);
504524
const typeImports = typesPrefix?.endsWith('.')
505525
? [typesPrefix.slice(0, -1)]
506526
: definitions
507527
.filter(({ typeName }: { typeName: string }) => !!typeName)
508528
.map(({ typeName }: { typeName: string }) => typenameConverter(typeName, typesPrefix));
529+
509530
const enumTypes = enumsPrefix?.endsWith('.')
510531
? [enumsPrefix.slice(0, -1)]
511532
: types.filter(({ type }) => type === 'enum').map(({ name }) => typenameConverter(name, enumsPrefix));
512533

534+
const renamedTypeImports = renameImports(typeImports, typeNamesMapping);
535+
513536
if (!enumsAsTypes || useTypeImports) {
514-
typeImports.push(...enumTypes);
537+
renamedTypeImports.push(...enumTypes);
515538
}
516539

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

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

523-
return typesFile ? `${importPrefix}{ ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : '';
546+
return typesFile
547+
? `${importPrefix}{ ${renamedTypeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n`
548+
: '';
524549
};
525550

526551
type GeneratorName = keyof Casual.Casual | keyof Casual.functions | string;
@@ -564,6 +589,7 @@ export interface TypescriptMocksPluginConfig {
564589
useImplementingTypes?: boolean;
565590
defaultNullableToNull?: boolean;
566591
useTypeImports?: boolean;
592+
typeNamesMapping?: Record<string, string>;
567593
}
568594

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

618645
// List of types that are enums
619646
const types: TypeItem[] = [];
@@ -693,6 +720,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
693720
useImplementingTypes,
694721
defaultNullableToNull,
695722
nonNull: false,
723+
typeNamesMapping: config.typeNamesMapping,
696724
});
697725

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

736765
return ` ${field.name.value}: overrides && overrides.hasOwnProperty('${field.name.value}') ? overrides.${field.name.value}! : ${value},`;
@@ -747,6 +776,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
747776
config.prefix,
748777
config.typesPrefix,
749778
transformUnderscore,
779+
typeNamesMapping,
750780
);
751781
},
752782
};
@@ -770,6 +800,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
770800
config.prefix,
771801
config.typesPrefix,
772802
transformUnderscore,
803+
typeNamesMapping,
773804
);
774805
},
775806
};
@@ -791,6 +822,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
791822
config.prefix,
792823
config.typesPrefix,
793824
transformUnderscore,
825+
typeNamesMapping,
794826
);
795827
},
796828
};
@@ -813,6 +845,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
813845
transformUnderscore: transformUnderscore,
814846
useTypeImports: config.useTypeImports,
815847
enumsAsTypes,
848+
typeNamesMapping,
816849
});
817850
// Function that will generate the mocks.
818851
// We generate it after having visited because we need to distinct types from enums

tests/typeNamesMapping/schema.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { buildSchema } from 'graphql/index';
2+
3+
export default buildSchema(/* GraphQL */ `
4+
enum EnumExample {
5+
LOREM
6+
IPSUM
7+
}
8+
9+
type A {
10+
id: ID!
11+
str: String!
12+
email: String!
13+
}
14+
15+
type B {
16+
id: ID!
17+
str: String!
18+
email: String!
19+
}
20+
21+
type C {
22+
id: ID!
23+
str: String!
24+
enum: EnumExample!
25+
D: D!
26+
}
27+
28+
type D {
29+
nested: C!
30+
}
31+
`);

tests/typeNamesMapping/spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { plugin } from '../../src';
2+
import testSchema from './schema';
3+
4+
it('should support typeNamesMapping', async () => {
5+
const result = await plugin(testSchema, [], {
6+
typesFile: './types/graphql.ts',
7+
typeNamesMapping: { A: 'RenamedAType' },
8+
});
9+
10+
expect(result).toBeDefined();
11+
expect(result).toContain("import { A as RenamedAType, B, C, D, EnumExample } from './types/graphql';");
12+
});
13+
14+
it('should support typeNamesMapping with circular relationships', async () => {
15+
const result = await plugin(testSchema, [], {
16+
typesFile: './types/graphql.ts',
17+
typeNamesMapping: { D: 'RenamedDType' },
18+
terminateCircularRelationships: 'immediate',
19+
});
20+
21+
expect(result).toBeDefined();
22+
expect(result).toContain("import { A, B, C, D as RenamedDType, EnumExample } from './types/graphql';");
23+
expect(result).toContain(
24+
"D: overrides && overrides.hasOwnProperty('D') ? overrides.D! : relationshipsToOmit.has('D') ? {} as DAsRenamedDType : aD({}, relationshipsToOmit),",
25+
);
26+
});
27+
28+
it('should not support typeNamesMapping when enum type is given', async () => {
29+
const result = await plugin(testSchema, [], {
30+
typesFile: './types/graphql.ts',
31+
typeNamesMapping: { EnumExample: 'RenamedEnum' },
32+
});
33+
34+
expect(result).toBeDefined();
35+
expect(result).toContain("import { A, B, C, D, EnumExample } from './types/graphql';");
36+
});

tests/useTypeImports/__snapshots__/spec.ts.snap

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`should support useTypeImports 1`] = `
4-
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';
4+
"import type { Avatar, User, Partial, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';
55
66
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
77
return {
@@ -25,6 +25,12 @@ export const aUser = (overrides?: Partial<User>): User => {
2525
};
2626
};
2727
28+
export const aPartial = (overrides?: Partial<Partial>): Partial => {
29+
return {
30+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '262c8866-bf76-4ccf-b606-2a0b4742f81f',
31+
};
32+
};
33+
2834
export const aWithAvatar = (overrides?: Partial<WithAvatar>): WithAvatar => {
2935
return {
3036
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '99f515e7-21e0-461d-b823-0d4c7f4dafc5',

tests/useTypeImports/schema.ts

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export default buildSchema(/* GraphQL */ `
2222
prefixedEnum: Prefixed_Enum
2323
}
2424
25+
type Partial {
26+
id: ID!
27+
}
28+
2529
interface WithAvatar {
2630
id: ID!
2731
avatar: Avatar

tests/useTypeImports/spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ it('should support useTypeImports', async () => {
66

77
expect(result).toBeDefined();
88
expect(result).toContain(
9-
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';",
9+
"import type { Avatar, User, Partial, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';",
1010
);
1111
expect(result).toMatchSnapshot();
1212
});

0 commit comments

Comments
 (0)