Skip to content
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
6 changes: 6 additions & 0 deletions .changeset/proud-jobs-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphql-codegen/visitor-plugin-common': minor
'@graphql-codegen/typescript-operations': minor
---

Add generatesOperationTypes to typescript-operations to allow omitting operation types such as Variables, Query/Mutation/Subscription selection set, and Fragment types
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ParsedDocumentsConfig extends ParsedTypesConfig {
experimentalFragmentVariables: boolean;
mergeFragmentTypes: boolean;
customDirectives: CustomDirectivesConfig;
generatesOperationTypes: boolean;
}

export interface RawDocumentsConfig extends RawTypesConfig {
Expand Down Expand Up @@ -175,6 +176,29 @@ export interface RawDocumentsConfig extends RawTypesConfig {
* ```
*/
customDirectives?: CustomDirectivesConfig;

/**
* @description Whether to generate operation types such as Variables, Query/Mutation/Subscription selection set, and Fragment types
* @default true
* @exampleMarkdown
* ```ts filename="codegen.ts"
* import type { CodegenConfig } from '@graphql-codegen/cli';
*
* const config: CodegenConfig = {
* // ...
* generates: {
* 'path/to/file.ts': {
* plugins: ['typescript-operations'],
* config: {
* generatesOperationTypes: false,
* },
* },
* },
* };
* export default config;
* ```
*/
generatesOperationTypes?: boolean;
}

export class BaseDocumentsVisitor<
Expand Down Expand Up @@ -207,6 +231,7 @@ export class BaseDocumentsVisitor<
operationResultSuffix: getConfigValue(rawConfig.operationResultSuffix, ''),
scalars: buildScalarsFromConfig(_schema, rawConfig, defaultScalars),
customDirectives: getConfigValue(rawConfig.customDirectives, { apolloUnmask: false }),
generatesOperationTypes: getConfigValue(rawConfig.generatesOperationTypes, true),
...((additionalConfig || {}) as any),
});

Expand Down Expand Up @@ -261,6 +286,10 @@ export class BaseDocumentsVisitor<
}

FragmentDefinition(node: FragmentDefinitionNode): string {
if (!this.config.generatesOperationTypes) {
return null;
}

const fragmentRootType = this._schema.getType(node.typeCondition.name.value);
const selectionSet = this._selectionSetToObject.createNext(fragmentRootType, node.selectionSet);
const fragmentSuffix = this.getFragmentSuffix(node);
Expand Down Expand Up @@ -289,7 +318,11 @@ export class BaseDocumentsVisitor<
return variablesBlock;
}

OperationDefinition(node: OperationDefinitionNode): string {
OperationDefinition(node: OperationDefinitionNode): string | null {
if (!this.config.generatesOperationTypes) {
return null;
}

const name = this.handleAnonymousOperation(node);
const operationRootType = getRootType(node.operation, this._schema);

Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/typescript/operations/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const plugin: PluginFunction<TypeScriptDocumentsPluginConfig, Types.Compl
...visitor.getImports(),
...visitor.getEnumsImports(),
...visitor.getGlobalDeclarations(visitor.config.noExport),
'type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };',
visitor.getExactUtilityType(),
],
content: content.join('\n'),
};
Expand Down
8 changes: 8 additions & 0 deletions packages/plugins/typescript/operations/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,12 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor<
useTypeImports: this.config.useTypeImports,
});
}

getExactUtilityType(): string | null {
if (!this.config.generatesOperationTypes) {
return null;
}

return 'type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };';
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mergeOutputs } from '@graphql-codegen/plugin-helpers';
// import { validateTs } from '@graphql-codegen/testing';
import { validateTs } from '@graphql-codegen/testing';
import { buildSchema, parse } from 'graphql';
import { plugin } from '../src/index.js';

Expand Down Expand Up @@ -171,4 +171,132 @@ describe('TypeScript Operations Plugin - Standalone', () => {
"
`);
});

it('does not generate Variables, Result or Fragments when generatesOperationTypes is false', async () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
user(id: ID!): User
users(input: UsersInput!): UsersResponse!
}

type Mutation {
makeUserAdmin(id: ID!): User!
}

type Subscription {
userChanges(id: ID!): User!
}

type ResponseError {
error: ResponseErrorType!
}

enum ResponseErrorType {
NOT_FOUND
INPUT_VALIDATION_ERROR
FORBIDDEN_ERROR
UNEXPECTED_ERROR
}

type User {
id: ID!
name: String!
role: UserRole!
createdAt: DateTime!
}

"UserRole Description"
enum UserRole {
"UserRole ADMIN"
ADMIN
"UserRole CUSTOMER"
CUSTOMER
}

"UsersInput Description"
input UsersInput {
"UsersInput from"
from: DateTime
"UsersInput to"
to: DateTime
role: UserRole
}

type UsersResponseOk {
result: [User!]!
}
union UsersResponse = UsersResponseOk | ResponseError

scalar DateTime
`);
const document = parse(/* GraphQL */ `
query User($id: ID!) {
user(id: $id) {
id
name
role
createdAt
}
}

query Users($input: UsersInput!) {
users(input: $input) {
... on UsersResponseOk {
result {
...UserFragment
}
}
... on ResponseError {
error
}
}
}

query UsersWithScalarInput($from: DateTime!, $to: DateTime, $role: UserRole) {
users(input: { from: $from, to: $to, role: $role }) {
... on UsersResponseOk {
result {
__typename
}
}
... on ResponseError {
__typename
}
}
}

mutation MakeAdmin {
makeUserAdmin(id: "100") {
...UserFragment
}
}

subscription UserChanges {
makeUserAdmin(id: "100") {
...UserFragment
}
}

fragment UserFragment on User {
id
role
}
`);

const result = mergeOutputs([await plugin(schema, [{ document }], { generatesOperationTypes: false })]);

expect(result).toMatchInlineSnapshot(`
"
/** UserRole Description */
export type UserRole =
/** UserRole ADMIN */
| 'ADMIN'
/** UserRole CUSTOMER */
| 'CUSTOMER';

"
`);

validateTs(result, undefined, undefined, undefined, undefined, true);
});
});