diff --git a/.changeset/lazy-laws-brake.md b/.changeset/lazy-laws-brake.md new file mode 100644 index 00000000000..5c8e4410087 --- /dev/null +++ b/.changeset/lazy-laws-brake.md @@ -0,0 +1,6 @@ +--- +'@graphql-codegen/visitor-plugin-common': minor +'@graphql-codegen/typescript-operations': minor +--- + +Added allowUndefinedQueryVariables as config option diff --git a/packages/plugins/other/visitor-plugin-common/src/base-documents-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/base-documents-visitor.ts index e9c6410437e..1cc7d3b9dc4 100644 --- a/packages/plugins/other/visitor-plugin-common/src/base-documents-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/base-documents-visitor.ts @@ -258,7 +258,7 @@ export class BaseDocumentsVisitor< .join('\n\n'); } - protected applyVariablesWrapper(variablesBlock: string): string { + protected applyVariablesWrapper(variablesBlock: string, _operationType?: string): string { return variablesBlock; } @@ -294,7 +294,7 @@ export class BaseDocumentsVisitor< const operationVariables = new DeclarationBlock({ ...this._declarationBlockConfig, - blockTransformer: t => this.applyVariablesWrapper(t), + blockTransformer: t => this.applyVariablesWrapper(t, operationType), }) .export() .asKind('type') diff --git a/packages/plugins/typescript/operations/src/config.ts b/packages/plugins/typescript/operations/src/config.ts index 7fb48cdadf6..9e768b01946 100644 --- a/packages/plugins/typescript/operations/src/config.ts +++ b/packages/plugins/typescript/operations/src/config.ts @@ -267,4 +267,29 @@ export interface TypeScriptDocumentsPluginConfig extends RawDocumentsConfig { * ``` */ maybeValue?: string; + + /** + * @description Adds undefined as a possible type for query variables + * @default false + * + * @exampleMarkdown + * ```ts filename="codegen.ts" + * import type { CodegenConfig } from '@graphql-codegen/cli'; + * + * const config: CodegenConfig = { + * // ... + * generates: { + * 'path/to/file.ts': { + * plugins: ['typescript'], + * config: { + * allowUndefinedQueryVariables: true + * }, + * }, + * }, + * }; + * export default config; + * ``` + */ + + allowUndefinedQueryVariables?: boolean; } diff --git a/packages/plugins/typescript/operations/src/visitor.ts b/packages/plugins/typescript/operations/src/visitor.ts index 24c9a71d474..f604a2308d7 100644 --- a/packages/plugins/typescript/operations/src/visitor.ts +++ b/packages/plugins/typescript/operations/src/visitor.ts @@ -24,6 +24,7 @@ export interface TypeScriptDocumentsParsedConfig extends ParsedDocumentsConfig { immutableTypes: boolean; noExport: boolean; maybeValue: string; + allowUndefinedQueryVariables: boolean; } export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< @@ -41,6 +42,7 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< nonOptionalTypename: getConfigValue(config.nonOptionalTypename, false), preResolveTypes: getConfigValue(config.preResolveTypes, true), mergeFragmentTypes: getConfigValue(config.mergeFragmentTypes, false), + allowUndefinedQueryVariables: getConfigValue(config.allowUndefinedQueryVariables, false), } as TypeScriptDocumentsParsedConfig, schema ); @@ -134,9 +136,10 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< return ';'; } - protected applyVariablesWrapper(variablesBlock: string): string { + protected applyVariablesWrapper(variablesBlock: string, operationType: string): string { const prefix = this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : ''; + const extraType = this.config.allowUndefinedQueryVariables && operationType === 'Query' ? ' | undefined' : ''; - return `${prefix}Exact<${variablesBlock === '{}' ? `{ [key: string]: never; }` : variablesBlock}>`; + return `${prefix}Exact<${variablesBlock === '{}' ? `{ [key: string]: never; }` : variablesBlock}>${extraType}`; } } diff --git a/packages/plugins/typescript/operations/tests/ts-documents.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.spec.ts index 65ca1a8a0fe..f37405c3b77 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.spec.ts @@ -382,6 +382,52 @@ describe('TypeScript Operations Plugin', () => { export type UserQuery = { __typename?: 'Query', user: { __typename?: 'User', name: string, age?: number | 'specialType', address?: string, nicknames?: Array | 'specialType', parents?: Array } }; `); }); + + it('should add undefined as possible value according to allowUndefinedQueryVariables', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + user: User! + } + + type User { + name: String! + age: Int + address: String! + nicknames: [String!] + parents: [User!]! + } + `); + + const fragment = parse(/* GraphQL */ ` + query user($showProperty: Boolean!) { + user { + name + age + address @include(if: $showProperty) + nicknames @include(if: $showProperty) + parents @include(if: $showProperty) + } + } + `); + + const { content } = await plugin( + schema, + [{ location: '', document: fragment }], + { + preResolveTypes: true, + allowUndefinedQueryVariables: true, + }, + { + outputFile: 'graphql.ts', + } + ); + + expect(content).toBeSimilarStringTo(` + export type UserQueryVariables = Exact<{ + showProperty: Scalars['Boolean']['input']; + }> | undefined; + `); + }); }); describe('Scalars', () => {