From efae660d1724886d00e906a72d4635b624b40757 Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Tue, 19 Oct 2021 18:49:29 +0300 Subject: [PATCH] Allow to use graphql-modules-preset plugin without graphql-modules library (#6865) --- .changeset/nasty-mails-end.md | 5 ++ .../presets/graphql-modules/src/builder.ts | 10 +++- .../presets/graphql-modules/src/config.ts | 33 ++++--------- packages/presets/graphql-modules/src/index.ts | 4 +- .../graphql-modules/tests/builder.spec.ts | 48 +++++++++++++++++++ .../graphql-modules/tests/integration.spec.ts | 24 ++++++++++ .../typescript-vue-apollo-smart-ops.md | 2 +- website/docs/presets/graphql-modules.md | 25 +++++++++- website/static/config.schema.json | 11 ++++- 9 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 .changeset/nasty-mails-end.md diff --git a/.changeset/nasty-mails-end.md b/.changeset/nasty-mails-end.md new file mode 100644 index 00000000000..61f824a1f8e --- /dev/null +++ b/.changeset/nasty-mails-end.md @@ -0,0 +1,5 @@ +--- +'@graphql-codegen/graphql-modules-preset': minor +--- + +Added an option to allow to skip generating code related to graphql-modules library diff --git a/packages/presets/graphql-modules/src/builder.ts b/packages/presets/graphql-modules/src/builder.ts index a23a503bb36..2a0a529cc81 100644 --- a/packages/presets/graphql-modules/src/builder.ts +++ b/packages/presets/graphql-modules/src/builder.ts @@ -46,6 +46,7 @@ export function buildModule( rootTypes, schema, baseVisitor, + useGraphQLModules, }: { importNamespace: string; importPath: string; @@ -54,6 +55,7 @@ export function buildModule( rootTypes: string[]; baseVisitor: BaseVisitor; schema?: GraphQLSchema; + useGraphQLModules: boolean; } ): string { const picks: Record> = createObject(registryKeys, () => ({})); @@ -114,7 +116,11 @@ export function buildModule( // // An actual output - const imports = [`import * as ${importNamespace} from "${importPath}";`, `import * as gm from "graphql-modules";`]; + const imports = [`import * as ${importNamespace} from "${importPath}";`]; + + if (useGraphQLModules) { + imports.push(`import * as gm from "graphql-modules";`); + } let content = [ printDefinedFields(), @@ -124,7 +130,7 @@ export function buildModule( printScalars(visited), printResolveSignaturesPerType(visited), printResolversType(visited), - printResolveMiddlewareMap(), + useGraphQLModules ? printResolveMiddlewareMap() : undefined, ] .filter(Boolean) .join('\n\n'); diff --git a/packages/presets/graphql-modules/src/config.ts b/packages/presets/graphql-modules/src/config.ts index d94c21f459d..05a4a6599e3 100644 --- a/packages/presets/graphql-modules/src/config.ts +++ b/packages/presets/graphql-modules/src/config.ts @@ -68,18 +68,6 @@ export type ModulesConfig = { * @type string * @description Required, sets the file name for the generated files. * - * @example - * ```yml - * generates: - * src/: - * preset: modules - * presetConfig: - * baseTypesPath: types.ts - * filename: types.ts - * plugins: - * - typescript-operations - * - typescript-react-apollo - * ``` */ filename: string; /** @@ -92,18 +80,15 @@ export type ModulesConfig = { * `prefix`: will prefix all types from a specific module with the module name. * `none`: will skip encapsulation, and generate type as-is. * - * @example - * ```yml - * generates: - * src/: - * preset: modules - * presetConfig: - * baseTypesPath: types.ts - * filename: types.ts - * plugins: - * - typescript-operations - * - typescript-react-apollo - * ``` */ encapsulateModuleTypes: 'prefix' | 'namespace' | 'none'; + /** + * @name useGraphQLModules + * @type boolean + * @default true + * @description By default, the generated types will be generate some code specific to `graphql-modules` library. + * + * If you are not using GraphQL-Modules, you can disable this feature by setting this to `false`. + */ + useGraphQLModules?: boolean; }; diff --git a/packages/presets/graphql-modules/src/index.ts b/packages/presets/graphql-modules/src/index.ts index 53fc7bd9748..b2b27a4db6f 100644 --- a/packages/presets/graphql-modules/src/index.ts +++ b/packages/presets/graphql-modules/src/index.ts @@ -4,10 +4,11 @@ import { resolve, relative, join } from 'path'; import { groupSourcesByModule, stripFilename, normalize, isGraphQLPrimitive } from './utils'; import { buildModule } from './builder'; import { ModulesConfig } from './config'; -import { BaseVisitor } from '@graphql-codegen/visitor-plugin-common'; +import { BaseVisitor, getConfigValue } from '@graphql-codegen/visitor-plugin-common'; export const preset: Types.OutputPreset = { buildGeneratesSection: options => { + const useGraphQLModules = getConfigValue(options?.presetConfig.useGraphQLModules, true); const { baseOutputDir } = options; const { baseTypesPath, encapsulateModuleTypes } = options.presetConfig; @@ -103,6 +104,7 @@ export const preset: Types.OutputPreset = { shouldDeclare, schema, baseVisitor, + useGraphQLModules, rootTypes: [ schema.getQueryType()?.name, schema.getMutationType()?.name, diff --git a/packages/presets/graphql-modules/tests/builder.spec.ts b/packages/presets/graphql-modules/tests/builder.spec.ts index 3d003f59cf5..c3e62c90dfe 100644 --- a/packages/presets/graphql-modules/tests/builder.spec.ts +++ b/packages/presets/graphql-modules/tests/builder.spec.ts @@ -73,6 +73,7 @@ test('should generate interface field resolvers', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, } ); @@ -81,6 +82,39 @@ test('should generate interface field resolvers', () => { expect(output).toContain(`export type BaseUserResolvers = Pick;`); }); +test('should not generate graphql-modules code when useGraphQLModules=false', () => { + const output = buildModule( + 'test', + parse(/* GraphQL */ ` + interface BaseUser { + id: ID! + email: String! + } + + type User implements BaseUser { + id: ID! + email: String! + } + + type Query { + me: BaseUser! + } + `), + { + importPath: '../types', + importNamespace: 'core', + encapsulate: 'none', + shouldDeclare: false, + rootTypes: ROOT_TYPES, + baseVisitor, + useGraphQLModules: false, + } + ); + + expect(output).not.toContain(`graphql-modules`); + expect(output).not.toContain(`gm.`); +}); + test('should generate interface extensions field resolvers ', () => { const output = buildModule( 'test', @@ -100,6 +134,7 @@ test('should generate interface extensions field resolvers ', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, } ); @@ -116,6 +151,7 @@ test('should include import statement', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -131,6 +167,7 @@ test('should work with naming conventions', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toContain(`Pick { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(`export namespace TestModule {`); @@ -159,6 +197,7 @@ test('encapsulate: should wrap correctly with a declared namespace', () => { shouldDeclare: true, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(`declare namespace TestModule {`); @@ -172,6 +211,7 @@ test('encapsulate: should wrap correctly with prefix', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toMatchSnapshot(); @@ -195,6 +235,7 @@ test('should pick fields from defined and extended types', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -227,6 +268,7 @@ test('should reexport used types but not defined in module', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -245,6 +287,7 @@ test('should export partial types, only those defined in module or root types', shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -275,6 +318,7 @@ test('should export partial types of scalars, only those defined in module or ro shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -295,6 +339,7 @@ test('should use and export resolver signatures of types defined or extended in shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -332,6 +377,7 @@ test('should not generate resolver signatures of types that are not defined or e shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).not.toContain('CommentResolvers'); @@ -345,6 +391,7 @@ test('should generate an aggregation of individual resolver signatures', () => { shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toBeSimilarStringTo(` @@ -366,6 +413,7 @@ test('should generate a signature for ResolveMiddleware (with widlcards)', () => shouldDeclare: false, rootTypes: ROOT_TYPES, baseVisitor, + useGraphQLModules: true, }); expect(output).toContain(`import * as gm from "graphql-modules";`); diff --git a/packages/presets/graphql-modules/tests/integration.spec.ts b/packages/presets/graphql-modules/tests/integration.spec.ts index ae1b8a9b676..17010604218 100644 --- a/packages/presets/graphql-modules/tests/integration.spec.ts +++ b/packages/presets/graphql-modules/tests/integration.spec.ts @@ -87,6 +87,30 @@ describe('Integration', () => { expect(output[4].content).toMatch(importStatement); }); + test('should allow to disable graphql-modules', async () => { + const output = await executeCodegen({ + generates: { + './tests/test-files/modules': { + schema: './tests/test-files/modules/*/types/*.graphql', + plugins: ['typescript', 'typescript-resolvers'], + preset: 'graphql-modules', + presetConfig: { + importBaseTypesFrom: '@types', + baseTypesPath: 'global-types.ts', + filename: 'module-types.ts', + encapsulateModuleTypes: 'none', + useGraphQLModules: false, + }, + }, + }, + }); + + for (const record of output) { + expect(record).not.toContain(`graphql-modules`); + expect(record).not.toContain(`gm.`); + } + }); + test('each module-types should include a relative import to glob-types module', async () => { const output = await executeCodegen(options); const importStatement = `import * as Types from "../global-types";`; diff --git a/website/docs/generated-config/typescript-vue-apollo-smart-ops.md b/website/docs/generated-config/typescript-vue-apollo-smart-ops.md index e222dfd9937..000fd6193fc 100644 --- a/website/docs/generated-config/typescript-vue-apollo-smart-ops.md +++ b/website/docs/generated-config/typescript-vue-apollo-smart-ops.md @@ -542,4 +542,4 @@ default: `inline` Whether fragment types should be inlined into other operations. "inline" is the default behavior and will perform deep inlining fragment types within operation type definitions. -"combine" is the previous behavior that uses fragment type references without inlining the types (and might cause issues with deeply nested fragment that uses list types). +"combine" is the previous behavior that uses fragment type references without inlining the types (and might cauuse issues with deeply nested fragment that uses list types). diff --git a/website/docs/presets/graphql-modules.md b/website/docs/presets/graphql-modules.md index a91b403e42a..04755a056bb 100644 --- a/website/docs/presets/graphql-modules.md +++ b/website/docs/presets/graphql-modules.md @@ -3,13 +3,15 @@ id: graphql-modules title: graphql-modules --- -The `@graphql-codegen/graphql-modules-preset` generates `.ts` file with TypeScript types, per each [GraphQL-Modules](http://graphql-modules.com/) module definition. +The `@graphql-codegen/graphql-modules-preset` generates `.ts` file with TypeScript types, per each directory that contains GraphQL SDL definitions. The generates files will be generated based on each module definition, and based on the GraphQL schema defined in that specific module, allowing you to write type-safe resolvers, while keeping modules types boundaries. :::caution Usage Requirements -This preset generates code for `graphql-modules` @ `v1`. Previous versions are not supported. +This preset generates code for `graphql-modules` @ `v1` (previous versions are not supported) by default. + +If you are not using `graphql-modules`, you can set `useGraphQLModules: false` to disable this behaviour. ::: @@ -19,6 +21,21 @@ This preset generates code for `graphql-modules` @ `v1`. Previous versions are n ## Usage Example +Given a folder structure with the following files: + +``` +- src/ + - modules/ + - user/ + - resolvers.ts + - typedefs/ + - user.graphql + - product/ + - resolvers.ts + - typedefs/ + - product.graphql +``` + Here's a short example for generating types and resolvers for 2 modules: ```yaml @@ -50,3 +67,7 @@ export const resolvers: MyModule.Resolvers = { ``` > You can find [an example project here](https://github.com/dotansimha/graphql-code-generator/tree/master/dev-test/modules). + +## Using without GraphQL-Modules + +By defualt, this preset it generating code for `graphql-modules`, but if you are not using it, you can set `useGraphQLModules: false` in your preset configuration to generate fully agnostic types that are based on folder strucutre only. diff --git a/website/static/config.schema.json b/website/static/config.schema.json index f15140ccf2a..180757581e9 100644 --- a/website/static/config.schema.json +++ b/website/static/config.schema.json @@ -3450,6 +3450,10 @@ "encapsulateModuleTypes": { "type": "string", "description": "Configure how to encapsulate the module types, to avoid confusion.\n\n`namespace` (default): will wrap all types in a TypeScript namespace, using the module name.\n`prefix`: will prefix all types from a specific module with the module name.\n`none`: will skip encapsulation, and generate type as-is.\nDefault value: \"namespace\"" + }, + "useGraphQLModules": { + "type": "boolean", + "description": "By default, the generated types will be generate some code specific to `graphql-modules` library.\n\nIf you are not using GraphQL-Modules, you can disable this feature by setting this to `false`.\nDefault value: \"true\"" } } }, @@ -3497,7 +3501,12 @@ }, "GqlTagConfig": { "type": "object", - "properties": {} + "properties": { + "augmentedModuleName": { + "description": "Instead of generating a `gql` function, this preset can also generate a d.ts that will enhance the `gql` function of your framework.\n\nE.g. `graphql-tag` or `@urql/core`.", + "type": "string" + } + } }, "Types.InstanceOrArray": { "anyOf": [