diff --git a/.changeset/dirty-tigers-chew.md b/.changeset/dirty-tigers-chew.md new file mode 100644 index 00000000000..e7e6a363dc2 --- /dev/null +++ b/.changeset/dirty-tigers-chew.md @@ -0,0 +1,5 @@ +--- +'@graphql-codegen/gql-tag-operations-preset': minor +--- + +add support for importing base schema types diff --git a/packages/presets/gql-tag-operations/src/index.ts b/packages/presets/gql-tag-operations/src/index.ts index 6a76f433a3f..cb1195feced 100644 --- a/packages/presets/gql-tag-operations/src/index.ts +++ b/packages/presets/gql-tag-operations/src/index.ts @@ -61,6 +61,35 @@ export type GqlTagConfig = { * ``` */ fragmentMasking?: FragmentMaskingConfig | boolean; + /** + * @description If base schema types are in another file, + * you can specify this as the relative path to it. + * + * @exampleMarkdown + * ```yaml {5} + * generates: + * path/to/file.ts: + * preset: gql-tag-operations-preset + * presetConfig: + * importTypesPath: types.ts + * ``` + */ + importTypesPath: string; + /** + * @description Optional, override the name of the import namespace used to import from the `baseTypesPath` file. + * @default Types + * + * @exampleMarkdown + * ```yaml {6} + * generates: + * src/: + * preset: gql-tag-operations-preset + * presetConfig: + * importTypesPath: types.ts + * importTypesNamespace: SchemaTypes + * ``` + */ + importTypesNamespace?: string; /** * @description Specify the name of the "graphql tag" function to use * @default "gql" @@ -115,13 +144,11 @@ export const preset: Types.OutputPreset = { [`gen-dts`]: gqlTagPlugin, }; - const plugins: Array = [ - { [`add`]: { content: `/* eslint-disable */` } }, - { [`typescript`]: {} }, - { [`typescript-operations`]: {} }, - { [`typed-document-node`]: {} }, - ...options.plugins, - ]; + const plugins: Array = [{ [`add`]: { content: `/* eslint-disable */` } }]; + if (!options.presetConfig.importTypesPath) { + plugins.push({ [`typescript`]: {} }); + } + plugins.push({ [`typescript-operations`]: {} }, { [`typed-document-node`]: {} }, ...options.plugins); const genDtsPlugins: Array = [ { [`add`]: { content: `/* eslint-disable */` } }, @@ -134,11 +161,18 @@ export const preset: Types.OutputPreset = { reexports.push('gql'); } - const config = { + const config: Record = { ...options.config, inlineFragmentTypes: isMaskingFragments ? 'mask' : options.config['inlineFragmentTypes'], }; + if (options.presetConfig.importTypesPath) { + const importType = options.config.useTypeImports ? 'import type' : 'import'; + const importTypesNamespace = options.presetConfig.importTypesNamespace || 'Types'; + plugins[0].add.content += `\n${importType} * as ${importTypesNamespace} from '${options.presetConfig.importTypesPath}';`; + config.namespacedImportName = importTypesNamespace; + } + let fragmentMaskingFileGenerateConfig: Types.GenerateOptions | null = null; if (isMaskingFragments === true) { diff --git a/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts b/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts index 01db9223dff..90a517fed61 100644 --- a/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts +++ b/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts @@ -752,4 +752,74 @@ describe('gql-tag-operations-preset', () => { const graphqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(graphqlFile).toBeDefined(); }); + + it('supports importing base schema types', async () => { + const result = await executeCodegen({ + schema: [ + /* GraphQL */ ` + type Query { + a: String + b: String + c: String + } + `, + ], + documents: path.join(__dirname, 'fixtures/simple-uppercase-operation-name.ts'), + generates: { + out1: { + preset, + presetConfig: { + importTypesPath: './base-types.ts', + }, + plugins: [], + }, + }, + }); + + expect(result.length).toBe(3); + const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); + expect(gqlFile.content).toMatchInlineSnapshot(` + "/* eslint-disable */ + import * as graphql from './graphql'; + import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + + const documents = { + "\\n query A {\\n a\\n }\\n": graphql.ADocument, + "\\n query B {\\n b\\n }\\n": graphql.BDocument, + "\\n fragment C on Query {\\n c\\n }\\n": graphql.CFragmentDoc, + }; + + export function gql(source: "\\n query A {\\n a\\n }\\n"): (typeof documents)["\\n query A {\\n a\\n }\\n"]; + export function gql(source: "\\n query B {\\n b\\n }\\n"): (typeof documents)["\\n query B {\\n b\\n }\\n"]; + export function gql(source: "\\n fragment C on Query {\\n c\\n }\\n"): (typeof documents)["\\n fragment C on Query {\\n c\\n }\\n"]; + + export function gql(source: string): unknown; + export function gql(source: string) { + return (documents as any)[source] ?? {}; + } + + export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;" + `); + const graphqlFile = result.find(file => file.filename === 'out1/graphql.ts'); + expect(graphqlFile.content).toMatchInlineSnapshot(` + "/* eslint-disable */ + import * as Types from './base-types.ts'; + import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + export type AQueryVariables = Types.Exact<{ [key: string]: never; }>; + + + export type AQuery = { __typename?: 'Query', a?: string | null }; + + export type BQueryVariables = Types.Exact<{ [key: string]: never; }>; + + + export type BQuery = { __typename?: 'Query', b?: string | null }; + + export type CFragment = { __typename?: 'Query', c?: string | null }; + + export const CFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"C"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"c"}}]}}]} as unknown as DocumentNode; + export const ADocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"A"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"a"}}]}}]} as unknown as DocumentNode; + export const BDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"B"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"b"}}]}}]} as unknown as DocumentNode;" + `); + }); });