Skip to content

Commit 91471e9

Browse files
shaun-sweetShaun Sweetardeois
authored
feat(config): add import type support (#148)
Generate imports with `import type ...` when `useTypeImports` is `true` This helps for compatibility with `importsNotUsedAsValues` option --------- Co-authored-by: Shaun Sweet <[email protected]> Co-authored-by: Corentin Ardeois <[email protected]>
1 parent 87805a0 commit 91471e9

File tree

5 files changed

+180
-1
lines changed

5 files changed

+180
-1
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
Defines the file path containing all GraphQL types. This file can also be generated through graphql-codgen
1616

17+
### useTypeImports(boolean, defaultValue: false)
18+
19+
Will use import type {} rather than import {} when importing only types. This gives compatibility with TypeScript's "importsNotUsedAsValues": "error" option
20+
1721
### addTypename (`boolean`, defaultValue: `false`)
1822

1923
Adds `__typename` property to mock data

src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ const getImportTypes = ({
465465
enumsPrefix,
466466
transformUnderscore,
467467
enumsAsTypes,
468+
useTypeImports,
468469
}: {
469470
typeNamesConvention: NamingConvention;
470471
definitions: any;
@@ -474,6 +475,7 @@ const getImportTypes = ({
474475
enumsPrefix: string;
475476
transformUnderscore: boolean;
476477
enumsAsTypes: boolean;
478+
useTypeImports: boolean;
477479
}) => {
478480
const typenameConverter = createNameConverter(typeNamesConvention, transformUnderscore);
479481
const typeImports = typesPrefix?.endsWith('.')
@@ -493,7 +495,9 @@ const getImportTypes = ({
493495
return self.indexOf(value) === index;
494496
}
495497

496-
return typesFile ? `import { ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : '';
498+
const importPrefix = `import ${useTypeImports ? 'type ' : ''}`;
499+
500+
return typesFile ? `${importPrefix}{ ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : '';
497501
};
498502

499503
type GeneratorName = keyof Casual.Casual | keyof Casual.functions | string;
@@ -536,6 +540,7 @@ export interface TypescriptMocksPluginConfig {
536540
enumsAsTypes?: boolean;
537541
useImplementingTypes?: boolean;
538542
defaultNullableToNull?: boolean;
543+
useTypeImports?: boolean;
539544
}
540545

541546
interface TypeItem {
@@ -781,6 +786,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
781786
typesPrefix: config.typesPrefix,
782787
enumsPrefix: config.enumsPrefix,
783788
transformUnderscore: transformUnderscore,
789+
useTypeImports: config.useTypeImports,
784790
enumsAsTypes,
785791
});
786792
// Function that will generate the mocks.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`should support useTypeImports 1`] = `
4+
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';
5+
6+
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
7+
return {
8+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38',
9+
url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid',
10+
};
11+
};
12+
13+
export const aUser = (overrides?: Partial<User>): User => {
14+
return {
15+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
16+
creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z',
17+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
18+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
19+
status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online,
20+
customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : AbcStatus.HasXyzStatus,
21+
scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : 'neque',
22+
camelCaseThing: overrides && overrides.hasOwnProperty('camelCaseThing') ? overrides.camelCaseThing! : aCamelCaseThing(),
23+
unionThing: overrides && overrides.hasOwnProperty('unionThing') ? overrides.unionThing! : anAvatar(),
24+
prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : PrefixedEnum.PrefixedValue,
25+
};
26+
};
27+
28+
export const aWithAvatar = (overrides?: Partial<WithAvatar>): WithAvatar => {
29+
return {
30+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '89f515e7-31e0-461d-a230-c4c7f4dafc5c',
31+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
32+
};
33+
};
34+
35+
export const aCamelCaseThing = (overrides?: Partial<CamelCaseThing>): CamelCaseThing => {
36+
return {
37+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '345b9cf9-00fa-4974-800f-aeee5ee7fd42',
38+
};
39+
};
40+
41+
export const aPrefixedResponse = (overrides?: Partial<PrefixedResponse>): PrefixedResponse => {
42+
return {
43+
ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'sunt',
44+
};
45+
};
46+
47+
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {
48+
return {
49+
abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit',
50+
};
51+
};
52+
53+
export const aListType = (overrides?: Partial<ListType>): ListType => {
54+
return {
55+
stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['voluptatem'],
56+
nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['eum'],
57+
};
58+
};
59+
60+
export const anUpdateUserInput = (overrides?: Partial<UpdateUserInput>): UpdateUserInput => {
61+
return {
62+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc',
63+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui',
64+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
65+
};
66+
};
67+
68+
export const aMutation = (overrides?: Partial<Mutation>): Mutation => {
69+
return {
70+
updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(),
71+
};
72+
};
73+
74+
export const aQuery = (overrides?: Partial<Query>): Query => {
75+
return {
76+
user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(),
77+
prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixedResponse(),
78+
};
79+
};
80+
"
81+
`;

tests/useTypeImports/schema.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { buildSchema } from 'graphql';
2+
3+
export default buildSchema(/* GraphQL */ `
4+
scalar Date
5+
scalar AnyObject
6+
7+
type Avatar {
8+
id: ID!
9+
url: String!
10+
}
11+
12+
type User implements WithAvatar {
13+
id: ID!
14+
creationDate: Date!
15+
login: String!
16+
avatar: Avatar
17+
status: Status!
18+
customStatus: ABCStatus
19+
scalarValue: AnyObject!
20+
camelCaseThing: camelCaseThing
21+
unionThing: UnionThing
22+
prefixedEnum: Prefixed_Enum
23+
}
24+
25+
interface WithAvatar {
26+
id: ID!
27+
avatar: Avatar
28+
}
29+
30+
type camelCaseThing {
31+
id: ID!
32+
}
33+
34+
type Prefixed_Response {
35+
ping: String!
36+
}
37+
38+
type ABCType {
39+
abc: String!
40+
}
41+
42+
type ListType {
43+
stringList: [String!]!
44+
nullableStringList: [String!]
45+
}
46+
47+
input UpdateUserInput {
48+
id: ID!
49+
login: String
50+
avatar: Avatar
51+
}
52+
53+
enum ABCStatus {
54+
hasXYZStatus
55+
}
56+
57+
enum Status {
58+
ONLINE
59+
OFFLINE
60+
}
61+
62+
enum Prefixed_Enum {
63+
PREFIXED_VALUE
64+
}
65+
66+
union UnionThing = Avatar | camelCaseThing
67+
68+
type Mutation {
69+
updateUser(user: UpdateUserInput): User
70+
}
71+
72+
type Query {
73+
user: User!
74+
prefixed_query: Prefixed_Response!
75+
}
76+
`);

tests/useTypeImports/spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { plugin } from '../../src';
2+
import testSchema from './schema';
3+
4+
it('should support useTypeImports', async () => {
5+
const result = await plugin(testSchema, [], { typesFile: './types/graphql.ts', useTypeImports: true });
6+
7+
expect(result).toBeDefined();
8+
expect(result).toContain(
9+
"import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';",
10+
);
11+
expect(result).toMatchSnapshot();
12+
});

0 commit comments

Comments
 (0)