diff --git a/README.md b/README.md index c5936d8..93b99d4 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ Defines the file path containing all GraphQL types. This file can also be generated through graphql-codgen +### useTypeImports(boolean, defaultValue: false) + +Will use import type {} rather than import {} when importing only types. This gives compatibility with TypeScript's "importsNotUsedAsValues": "error" option + ### addTypename (`boolean`, defaultValue: `false`) Adds `__typename` property to mock data diff --git a/src/index.ts b/src/index.ts index 5b509bb..d25a190 100644 --- a/src/index.ts +++ b/src/index.ts @@ -465,6 +465,7 @@ const getImportTypes = ({ enumsPrefix, transformUnderscore, enumsAsTypes, + useTypeImports, }: { typeNamesConvention: NamingConvention; definitions: any; @@ -474,6 +475,7 @@ const getImportTypes = ({ enumsPrefix: string; transformUnderscore: boolean; enumsAsTypes: boolean; + useTypeImports: boolean; }) => { const typenameConverter = createNameConverter(typeNamesConvention, transformUnderscore); const typeImports = typesPrefix?.endsWith('.') @@ -493,7 +495,9 @@ const getImportTypes = ({ return self.indexOf(value) === index; } - return typesFile ? `import { ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : ''; + const importPrefix = `import ${useTypeImports ? 'type ' : ''}`; + + return typesFile ? `${importPrefix}{ ${typeImports.filter(onlyUnique).join(', ')} } from '${typesFile}';\n` : ''; }; type GeneratorName = keyof Casual.Casual | keyof Casual.functions | string; @@ -536,6 +540,7 @@ export interface TypescriptMocksPluginConfig { enumsAsTypes?: boolean; useImplementingTypes?: boolean; defaultNullableToNull?: boolean; + useTypeImports?: boolean; } interface TypeItem { @@ -781,6 +786,7 @@ export const plugin: PluginFunction = (schema, docu typesPrefix: config.typesPrefix, enumsPrefix: config.enumsPrefix, transformUnderscore: transformUnderscore, + useTypeImports: config.useTypeImports, enumsAsTypes, }); // Function that will generate the mocks. diff --git a/tests/useTypeImports/__snapshots__/spec.ts.snap b/tests/useTypeImports/__snapshots__/spec.ts.snap new file mode 100644 index 0000000..da46ca9 --- /dev/null +++ b/tests/useTypeImports/__snapshots__/spec.ts.snap @@ -0,0 +1,81 @@ +// 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'; + +export const anAvatar = (overrides?: Partial): Avatar => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38', + url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid', + }; +}; + +export const aUser = (overrides?: Partial): User => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750', + creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z', + login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online, + customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : AbcStatus.HasXyzStatus, + scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : 'neque', + camelCaseThing: overrides && overrides.hasOwnProperty('camelCaseThing') ? overrides.camelCaseThing! : aCamelCaseThing(), + unionThing: overrides && overrides.hasOwnProperty('unionThing') ? overrides.unionThing! : anAvatar(), + prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : PrefixedEnum.PrefixedValue, + }; +}; + +export const aWithAvatar = (overrides?: Partial): WithAvatar => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '89f515e7-31e0-461d-a230-c4c7f4dafc5c', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + }; +}; + +export const aCamelCaseThing = (overrides?: Partial): CamelCaseThing => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '345b9cf9-00fa-4974-800f-aeee5ee7fd42', + }; +}; + +export const aPrefixedResponse = (overrides?: Partial): PrefixedResponse => { + return { + ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'sunt', + }; +}; + +export const anAbcType = (overrides?: Partial): AbcType => { + return { + abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit', + }; +}; + +export const aListType = (overrides?: Partial): ListType => { + return { + stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['voluptatem'], + nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['eum'], + }; +}; + +export const anUpdateUserInput = (overrides?: Partial): UpdateUserInput => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc', + login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + }; +}; + +export const aMutation = (overrides?: Partial): Mutation => { + return { + updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(), + }; +}; + +export const aQuery = (overrides?: Partial): Query => { + return { + user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(), + prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixedResponse(), + }; +}; +" +`; diff --git a/tests/useTypeImports/schema.ts b/tests/useTypeImports/schema.ts new file mode 100644 index 0000000..8e1ac4e --- /dev/null +++ b/tests/useTypeImports/schema.ts @@ -0,0 +1,76 @@ +import { buildSchema } from 'graphql'; + +export default buildSchema(/* GraphQL */ ` + scalar Date + scalar AnyObject + + type Avatar { + id: ID! + url: String! + } + + type User implements WithAvatar { + id: ID! + creationDate: Date! + login: String! + avatar: Avatar + status: Status! + customStatus: ABCStatus + scalarValue: AnyObject! + camelCaseThing: camelCaseThing + unionThing: UnionThing + prefixedEnum: Prefixed_Enum + } + + interface WithAvatar { + id: ID! + avatar: Avatar + } + + type camelCaseThing { + id: ID! + } + + type Prefixed_Response { + ping: String! + } + + type ABCType { + abc: String! + } + + type ListType { + stringList: [String!]! + nullableStringList: [String!] + } + + input UpdateUserInput { + id: ID! + login: String + avatar: Avatar + } + + enum ABCStatus { + hasXYZStatus + } + + enum Status { + ONLINE + OFFLINE + } + + enum Prefixed_Enum { + PREFIXED_VALUE + } + + union UnionThing = Avatar | camelCaseThing + + type Mutation { + updateUser(user: UpdateUserInput): User + } + + type Query { + user: User! + prefixed_query: Prefixed_Response! + } +`); diff --git a/tests/useTypeImports/spec.ts b/tests/useTypeImports/spec.ts new file mode 100644 index 0000000..e98259c --- /dev/null +++ b/tests/useTypeImports/spec.ts @@ -0,0 +1,12 @@ +import { plugin } from '../../src'; +import testSchema from './schema'; + +it('should support useTypeImports', async () => { + const result = await plugin(testSchema, [], { typesFile: './types/graphql.ts', useTypeImports: true }); + + expect(result).toBeDefined(); + expect(result).toContain( + "import type { Avatar, User, WithAvatar, CamelCaseThing, PrefixedResponse, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, PrefixedEnum } from './types/graphql';", + ); + expect(result).toMatchSnapshot(); +});