diff --git a/dev-test/star-wars/types.avoidOptionals.ts b/dev-test/star-wars/types.avoidOptionals.ts index 96a96ea0b4d..d31bf22709a 100644 --- a/dev-test/star-wars/types.avoidOptionals.ts +++ b/dev-test/star-wars/types.avoidOptionals.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.excludeQueryAlpha.ts b/dev-test/star-wars/types.excludeQueryAlpha.ts index 7ccbc0a787c..142bda39b04 100644 --- a/dev-test/star-wars/types.excludeQueryAlpha.ts +++ b/dev-test/star-wars/types.excludeQueryAlpha.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.excludeQueryBeta.ts b/dev-test/star-wars/types.excludeQueryBeta.ts index 9f20261977e..ac64e697f8a 100644 --- a/dev-test/star-wars/types.excludeQueryBeta.ts +++ b/dev-test/star-wars/types.excludeQueryBeta.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.globallyAvailable.d.ts b/dev-test/star-wars/types.globallyAvailable.d.ts index 0e376af86bc..8045400d9c8 100644 --- a/dev-test/star-wars/types.globallyAvailable.d.ts +++ b/dev-test/star-wars/types.globallyAvailable.d.ts @@ -238,6 +238,13 @@ type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -247,6 +254,16 @@ type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.immutableTypes.ts b/dev-test/star-wars/types.immutableTypes.ts index bb485fff875..7d97df5dee9 100644 --- a/dev-test/star-wars/types.immutableTypes.ts +++ b/dev-test/star-wars/types.immutableTypes.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.preResolveTypes.onlyOperationTypes.ts b/dev-test/star-wars/types.preResolveTypes.onlyOperationTypes.ts index 9597c939d8f..c96287fddd9 100644 --- a/dev-test/star-wars/types.preResolveTypes.onlyOperationTypes.ts +++ b/dev-test/star-wars/types.preResolveTypes.onlyOperationTypes.ts @@ -49,6 +49,13 @@ export type ReviewInput = { stars: Scalars['Int']['input']; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -58,6 +65,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.preResolveTypes.ts b/dev-test/star-wars/types.preResolveTypes.ts index 99c11f7e757..11503496d1c 100644 --- a/dev-test/star-wars/types.preResolveTypes.ts +++ b/dev-test/star-wars/types.preResolveTypes.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.skipSchema.ts b/dev-test/star-wars/types.skipSchema.ts index 99c11f7e757..11503496d1c 100644 --- a/dev-test/star-wars/types.skipSchema.ts +++ b/dev-test/star-wars/types.skipSchema.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/dev-test/star-wars/types.ts b/dev-test/star-wars/types.ts index 99c11f7e757..11503496d1c 100644 --- a/dev-test/star-wars/types.ts +++ b/dev-test/star-wars/types.ts @@ -240,6 +240,13 @@ export type StarshipLengthArgs = { unit?: InputMaybe; }; +/** The input object sent when passing a color */ +export type ColorInput = { + blue: number; + green: number; + red: number; +}; + /** The episodes in the Star Wars trilogy */ export type Episode = /** Star Wars Episode V: The Empire Strikes Back, released in 1980. */ @@ -249,6 +256,16 @@ export type Episode = /** Star Wars Episode IV: A New Hope, released in 1977. */ | 'NEWHOPE'; +/** The input object sent when someone is creating a new review */ +export type ReviewInput = { + /** Comment about the movie, optional */ + commentary: string; + /** Favorite color, optional */ + favoriteColor: ColorInput; + /** 0-5 stars */ + stars: number; +}; + export type CreateReviewForEpisodeMutationVariables = Exact<{ episode: Episode; review: ReviewInput; diff --git a/packages/plugins/typescript/operations/src/index.ts b/packages/plugins/typescript/operations/src/index.ts index 107e330309c..ea44c79599c 100644 --- a/packages/plugins/typescript/operations/src/index.ts +++ b/packages/plugins/typescript/operations/src/index.ts @@ -25,25 +25,14 @@ export const plugin: PluginFunction typeof def === 'string').join('\n'); + const schemaTypesDefinitions = schemaTypes.definitions.filter(def => typeof def === 'string'); + + let content = [...schemaTypesDefinitions, ...operationsDefinitions].join('\n'); - const content: string[] = []; - if (schemaTypesContent) { - content.push(schemaTypesContent); + if (config.globalNamespace) { + content = ` + declare global { + ${content} + }`; } - content.push(operationsContent); return { prepend: [ @@ -67,7 +59,7 @@ export const plugin: PluginFunction`; + } + + NonNullType(node: NonNullTypeNode, _key: any, _parent: any, _path: any, ancestors: any): string | undefined { + if (!this.isValidVisit(ancestors)) { + return undefined; + } + + return node.type as any as string | undefined; + } + public getImports(): Array { return !this.config.globalNamespace && (this.config.inlineFragmentTypes === 'combine' || this.config.inlineFragmentTypes === 'mask') @@ -216,6 +332,25 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< return `${prefix}Exact<${variablesBlock === '{}' ? `{ [key: string]: never; }` : variablesBlock}>${extraType}`; } + private collectInnerTypesRecursively(type: GraphQLInputObjectType, usedInputTypes: UsedNamedInputTypes): void { + const fields = type.getFields(); + for (const field of Object.values(fields)) { + const fieldType = getNamedType(field.type); + if ( + fieldType && + (fieldType instanceof GraphQLEnumType || + fieldType instanceof GraphQLInputObjectType || + fieldType instanceof GraphQLScalarType) && + !usedInputTypes[fieldType.name] + ) { + usedInputTypes[fieldType.name] = fieldType; + if (fieldType instanceof GraphQLInputObjectType) { + this.collectInnerTypesRecursively(fieldType, usedInputTypes); + } + } + } + } + private collectUsedInputTypes({ schema, documentNode, @@ -227,6 +362,7 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< const usedInputTypes: UsedNamedInputTypes = {}; + // Collect input enums and input types visit(documentNode, { VariableDefinition: variableDefinitionNode => { visit(variableDefinitionNode, { @@ -236,15 +372,40 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< foundInputType && (foundInputType instanceof GraphQLInputObjectType || foundInputType instanceof GraphQLScalarType || - foundInputType instanceof GraphQLEnumType) + foundInputType instanceof GraphQLEnumType) && + !usedInputTypes[namedTypeNode.name.value] ) { usedInputTypes[namedTypeNode.name.value] = foundInputType; + if (foundInputType instanceof GraphQLInputObjectType) { + this.collectInnerTypesRecursively(foundInputType, usedInputTypes); + } } }, }); }, }); + // Collect output enums + const typeInfo = new TypeInfo(schema); + visit( + documentNode, + // AST doesn’t include field types (they are defined in schema) - only names. + // TypeInfo is a stateful helper that tracks typing context while walking the AST + // visitWithTypeInfo wires that context into a visitor. + visitWithTypeInfo(typeInfo, { + Field: () => { + const fieldType = typeInfo.getType(); + if (fieldType) { + const namedType = getNamedType(fieldType); + + if (namedType instanceof GraphQLEnumType) { + usedInputTypes[namedType.name] = namedType; + } + } + }, + }) + ); + return usedInputTypes; } diff --git a/packages/plugins/typescript/operations/tests/__snapshots__/ts-documents.spec.ts.snap b/packages/plugins/typescript/operations/tests/__snapshots__/ts-documents.spec.ts.snap index f364438cc65..3fc18750df5 100644 --- a/packages/plugins/typescript/operations/tests/__snapshots__/ts-documents.spec.ts.snap +++ b/packages/plugins/typescript/operations/tests/__snapshots__/ts-documents.spec.ts.snap @@ -57,7 +57,11 @@ export type ElementMetadataFragment = `; exports[`TypeScript Operations Plugin > Issues > #2916 - Missing import prefix with preResolveTypes: true and near-operation-file preset 1`] = ` -"export type UserQueryVariables = Types.Exact<{ [key: string]: never; }>; +"export type Department = + | 'Direction' + | 'Development'; + +export type UserQueryVariables = Types.Exact<{ [key: string]: never; }>; export type UserQuery = { user: { id: string, username: string, email: string, dep: Types.Department } }; diff --git a/packages/plugins/typescript/operations/tests/extract-all-types.spec.ts b/packages/plugins/typescript/operations/tests/extract-all-types.spec.ts index c64468cf85c..d24fe00a07c 100644 --- a/packages/plugins/typescript/operations/tests/extract-all-types.spec.ts +++ b/packages/plugins/typescript/operations/tests/extract-all-types.spec.ts @@ -401,7 +401,13 @@ describe('extractAllFieldsToTypes: true', () => { { outputFile: '' } ); expect(content).toMatchInlineSnapshot(` - "export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; + "export type CallType = + | 'OUTGOING' + | 'INCOMING' + | 'VOICEMAIL' + | 'UNKNOWN'; + + export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; export type ConversationBotSolutionFragment_BotSolution_originatedFrom_EmailInteraction = { __typename: 'EmailInteraction', originalEmailURLPath: string }; @@ -565,7 +571,13 @@ describe('extractAllFieldsToTypes: true', () => { { outputFile: '' } ); expect(content).toMatchInlineSnapshot(` - "export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = ( + "export type CallType = + | 'OUTGOING' + | 'INCOMING' + | 'VOICEMAIL' + | 'UNKNOWN'; + + export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = ( { id: string, htmlUrl: string, title: string, url: string } & { __typename: 'ArchivedArticle' } ); @@ -734,7 +746,13 @@ describe('extractAllFieldsToTypes: true', () => { { outputFile: '' } ); expect(content).toMatchInlineSnapshot(` - "export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; + "export type CallType = + | 'OUTGOING' + | 'INCOMING' + | 'VOICEMAIL' + | 'UNKNOWN'; + + export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; export type ConversationBotSolutionFragment_BotSolution_originatedFrom_EmailInteraction = ( { __typename: 'EmailInteraction' } @@ -972,7 +990,13 @@ describe('extractAllFieldsToTypes: true', () => { { outputFile: '' } ); expect(content).toMatchInlineSnapshot(` - "export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; + "export type CallType = + | 'OUTGOING' + | 'INCOMING' + | 'VOICEMAIL' + | 'UNKNOWN'; + + export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = { __typename: 'ArchivedArticle', id: string, htmlUrl: string, title: string, url: string }; export type ConversationBotSolutionFragment_BotSolution_originatedFrom_EmailInteraction = ( { __typename: 'EmailInteraction' } @@ -1207,7 +1231,13 @@ describe('extractAllFieldsToTypes: true', () => { { outputFile: '' } ); expect(content).toMatchInlineSnapshot(` - "export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = ( + "export type CallType = + | 'OUTGOING' + | 'INCOMING' + | 'VOICEMAIL' + | 'UNKNOWN'; + + export type ConversationBotSolutionFragment_BotSolution_article_ArchivedArticle = ( { __typename: 'ArchivedArticle' } & Pick< ArchivedArticle, diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts index 2f1cb9a5485..2c9cca7dce5 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts @@ -94,6 +94,12 @@ describe('TypeScript Operations Plugin - Standalone', () => { expect(result).toMatchInlineSnapshot(` "type Exact = { [K in keyof T]: T[K] }; + export type ResponseErrorType = + | 'NOT_FOUND' + | 'INPUT_VALIDATION_ERROR' + | 'FORBIDDEN_ERROR' + | 'UNEXPECTED_ERROR'; + /** UserRole Description */ export type UserRole = /** UserRole ADMIN */ @@ -101,6 +107,17 @@ describe('TypeScript Operations Plugin - Standalone', () => { /** UserRole CUSTOMER */ | 'CUSTOMER'; + /** UsersInput Description */ + export type UsersInput = { + /** UsersInput from */ + from: DateTime; + /** UsersInput to */ + to: DateTime; + role: UserRole; + }; + + export type DateTime = any; + export type UserQueryVariables = Exact<{ id: string; }>; @@ -136,6 +153,177 @@ describe('TypeScript Operations Plugin - Standalone', () => { // validateTs(content, undefined, undefined, undefined, undefined, true); }); + it('test generating input types enums in lists and inner field', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + users(input: UsersInput!): [User!]! + } + + type User { + id: ID! + } + + enum EnumRoot { + ENUM_A + ENUM_B + } + + enum EnumRootArray { + ENUM_C + ENUM_D + } + + enum EnumInnerArray { + ENUM_E + ENUM_F + } + + input EnumsInner { + enumsDeep: [EnumInnerArray!]! + } + + input UsersInput { + enum: EnumRoot! + enums: [EnumRootArray!]! + innerEnums: EnumsInner! + } + `); + const document = parse(/* GraphQL */ ` + query Users($input: UsersInput!) { + users(input: $input) { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], {})]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type EnumRoot = + | 'ENUM_A' + | 'ENUM_B'; + + export type EnumRootArray = + | 'ENUM_C' + | 'ENUM_D'; + + export type EnumInnerArray = + | 'ENUM_E' + | 'ENUM_F'; + + export type EnumsInner = { + enumsDeep: Array; + }; + + export type UsersInput = { + enum: EnumRoot; + enums: Array; + innerEnums: EnumsInner; + }; + + export type UsersQueryVariables = Exact<{ + input: UsersInput; + }>; + + + export type UsersQuery = { __typename?: 'Query', users: Array<{ __typename?: 'User', id: string }> }; + " + `); + }); + + it('test generating output enums in lists and inner field', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + user(id: ID!): User! + } + + enum EnumRoot { + ENUM_A + ENUM_B + } + + enum EnumRootArray { + ENUM_C + ENUM_D + } + + enum EnumInnerArray { + ENUM_E + ENUM_F + } + + type EnumsInner { + enumsDeep: [EnumInnerArray!]! + } + + type User { + enum: EnumRoot! + enums: [EnumRootArray!]! + innerEnums: EnumsInner! + } + `); + const document = parse(/* GraphQL */ ` + query User($id: ID!) { + user(id: $id) { + enum + enums + innerEnums { + enumsDeep + } + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + extractAllFieldsToTypes: true, // Extracts all fields to separate types (similar to apollo-codegen behavior) + printFieldsOnNewLines: true, // Prints each field on a new line (similar to apollo-codegen behavior) + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type EnumRoot = + | 'ENUM_A' + | 'ENUM_B'; + + export type EnumRootArray = + | 'ENUM_C' + | 'ENUM_D'; + + export type EnumInnerArray = + | 'ENUM_E' + | 'ENUM_F'; + + export type UserQuery_user_User_innerEnums_EnumsInner = { + __typename?: 'EnumsInner', + enumsDeep: Array + }; + + export type UserQuery_user_User = { + __typename?: 'User', + enum: EnumRoot, + enums: Array, + innerEnums: UserQuery_user_User_innerEnums_EnumsInner + }; + + export type UserQuery_Query = { + __typename?: 'Query', + user: UserQuery_user_User + }; + + + export type UserQueryVariables = Exact<{ + id: string; + }>; + + + export type UserQuery = UserQuery_Query; + " + `); + }); + it('test overrdiding config.scalars', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { @@ -172,6 +360,119 @@ describe('TypeScript Operations Plugin - Standalone', () => { `); }); + it('test render output enum from fragment in the same document', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum RoleType { + ROLE_A + ROLE_B + } + + type User { + id: ID! + name: String! + role: RoleType + pictureUrl: String + } + + type Query { + users: [User!]! + viewer: User! + } + `); + const document = parse(/* GraphQL */ ` + fragment UserBasic on User { + id + name + role + } + + query GetUsersAndViewer { + users { + ...UserBasic + } + viewer { + ...UserBasic + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], {})]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type RoleType = + | 'ROLE_A' + | 'ROLE_B'; + + export type UserBasicFragment = { __typename?: 'User', id: string, name: string, role?: RoleType | null }; + + export type GetUsersAndViewerQueryVariables = Exact<{ [key: string]: never; }>; + + + export type GetUsersAndViewerQuery = { __typename?: 'Query', users: Array<{ __typename?: 'User', id: string, name: string, role?: RoleType | null }>, viewer: { __typename?: 'User', id: string, name: string, role?: RoleType | null } }; + " + `); + }); + + it('test render output enum from fragment in a separate document', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum RoleType { + ROLE_A + ROLE_B + } + + type User { + id: ID! + name: String! + role: RoleType + pictureUrl: String + } + + type Query { + users: [User!]! + viewer: User! + } + `); + + const documentWithFragment = parse(/* GraphQL */ ` + fragment UserBasic on User { + id + name + role + } + `); + + const documentMain = parse(/* GraphQL */ ` + query GetUsersAndViewer { + users { + ...UserBasic + } + viewer { + ...UserBasic + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document: documentMain }, { document: documentWithFragment }], {}), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type RoleType = + | 'ROLE_A' + | 'ROLE_B'; + + export type GetUsersAndViewerQueryVariables = Exact<{ [key: string]: never; }>; + + + export type GetUsersAndViewerQuery = { __typename?: 'Query', users: Array<{ __typename?: 'User', id: string, name: string, role?: RoleType | null }>, viewer: { __typename?: 'User', id: string, name: string, role?: RoleType | null } }; + + export type UserBasicFragment = { __typename?: 'User', id: string, name: string, role?: RoleType | null }; + " + `); + }); + it('does not generate Variables, Result or Fragments when generatesOperationTypes is false', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { @@ -287,6 +588,12 @@ describe('TypeScript Operations Plugin - Standalone', () => { expect(result).toMatchInlineSnapshot(` " + export type ResponseErrorType = + | 'NOT_FOUND' + | 'INPUT_VALIDATION_ERROR' + | 'FORBIDDEN_ERROR' + | 'UNEXPECTED_ERROR'; + /** UserRole Description */ export type UserRole = /** UserRole ADMIN */ @@ -294,6 +601,16 @@ describe('TypeScript Operations Plugin - Standalone', () => { /** UserRole CUSTOMER */ | 'CUSTOMER'; + /** UsersInput Description */ + export type UsersInput = { + /** UsersInput from */ + from: DateTime; + /** UsersInput to */ + to: DateTime; + role: UserRole; + }; + + export type DateTime = any; " `);