From 949d18445201c38601da718704edd08fb744915b Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Sun, 19 Jul 2020 14:58:03 +0300 Subject: [PATCH 1/2] New plugin for typed-document-node --- .../typed-document-node/jest.config.js | 1 + .../typed-document-node/package.json | 36 +++++++++++++++++++ .../typed-document-node/src/index.ts | 34 ++++++++++++++++++ tsconfig.json | 1 + 4 files changed, 72 insertions(+) create mode 100644 packages/plugins/typescript/typed-document-node/jest.config.js create mode 100644 packages/plugins/typescript/typed-document-node/package.json create mode 100644 packages/plugins/typescript/typed-document-node/src/index.ts diff --git a/packages/plugins/typescript/typed-document-node/jest.config.js b/packages/plugins/typescript/typed-document-node/jest.config.js new file mode 100644 index 00000000000..e35d633e27f --- /dev/null +++ b/packages/plugins/typescript/typed-document-node/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../../../jest.project')({ dirname: __dirname }); \ No newline at end of file diff --git a/packages/plugins/typescript/typed-document-node/package.json b/packages/plugins/typescript/typed-document-node/package.json new file mode 100644 index 00000000000..0bb1285fd4f --- /dev/null +++ b/packages/plugins/typescript/typed-document-node/package.json @@ -0,0 +1,36 @@ +{ + "name": "@graphql-codegen/typed-document-node", + "version": "1.17.0", + "description": "GraphQL Code Generator plugin for generating ready-to-use TypedDocumentNode based on GraphQL operations", + "repository": { + "type": "git", + "url": "https://github.com/dotansimha/graphql-code-generator.git", + "directory": "packages/plugins/typescript/apollo-angular" + }, + "license": "MIT", + "scripts": { + "lint": "eslint **/*.ts", + "test": "jest --no-watchman --config ../../../../jest.config.js", + "prepack": "bob prepack" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + }, + "dependencies": { + "change-case": "4.1.1", + "@graphql-codegen/plugin-helpers": "1.17.0", + "tslib": "~2.0.0" + }, + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "typings": "dist/index.d.ts", + "typescript": { + "definition": "dist/index.d.ts" + }, + "buildOptions": { + "input": "./src/index.ts" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/typescript/typed-document-node/src/index.ts b/packages/plugins/typescript/typed-document-node/src/index.ts new file mode 100644 index 00000000000..43c0f3d1f05 --- /dev/null +++ b/packages/plugins/typescript/typed-document-node/src/index.ts @@ -0,0 +1,34 @@ +import { Types, PluginValidateFn, PluginFunction } from '@graphql-codegen/plugin-helpers'; +import { parse, GraphQLSchema, OperationDefinitionNode } from 'graphql'; +import { pascalCase } from 'change-case'; +import { extname } from 'path'; + +export const plugin: PluginFunction<{}> = (schema: GraphQLSchema, documents: Types.DocumentFile[], config) => { + return { + prepend: [`import { TypedDocumentNode } from '@graphql-typed-document-node/core';`], + content: documents + .map(docFile => { + const operation = docFile.document.definitions[0] as OperationDefinitionNode; + const resultTypeName = pascalCase(operation.name.value) + pascalCase(operation.operation); + const variablesTypeName = pascalCase(operation.name.value) + pascalCase(operation.operation) + 'Variables'; + + return `export const ${operation.name.value}${pascalCase( + operation.operation + )}: TypedDocumentNode<${resultTypeName}, ${variablesTypeName}> = ${JSON.stringify( + parse(docFile.rawSDL, { noLocation: true }) + )};`; + }) + .join('\n'), + }; +}; + +export const validate: PluginValidateFn = async ( + schema: GraphQLSchema, + documents: Types.DocumentFile[], + config, + outputFile: string +) => { + if (extname(outputFile) !== '.ts') { + throw new Error(`Plugin "typed-document-node" requires extension to be ".ts"!`); + } +}; diff --git a/tsconfig.json b/tsconfig.json index f390f638e36..9bb95a4ceb0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -53,6 +53,7 @@ "@graphql-codegen/typescript-resolvers": ["packages/plugins/typescript/resolvers/src/index.ts"], "@graphql-codegen/typescript-stencil-apollo": ["packages/plugins/typescript/stencil-apollo/src/index.ts"], "@graphql-codegen/typescript-type-graphql": ["packages/plugins/typescript/type-graphql/src/index.ts"], + "@graphql-codegen/typed-document-node": ["packages/plugins/typescript/typed-document-node/src/index.ts"], "@graphql-codegen/typescript": ["packages/plugins/typescript/typescript/src/index.ts"], "@graphql-codegen/typescript-urql": ["packages/plugins/typescript/urql/src/index.ts"], "@graphql-codegen/typescript-vue-apollo": ["packages/plugins/typescript/vue-apollo/src/index.ts"], From 98bbc7b3c6d4d692fb4b38d54ea8a65a5346a0fd Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Sun, 19 Jul 2020 15:12:07 +0300 Subject: [PATCH 2/2] docs fixes websited fixes added docs --- .../generated-config/c-sharp-operations.md | 13 ++-- website/docs/generated-config/c-sharp.md | 18 +++++ .../docs/generated-config/flow-operations.md | 15 ++++ .../docs/generated-config/flow-resolvers.md | 25 +++++++ .../named-operations-object.md | 4 +- .../typescript-graphql-files-modules.md | 1 - .../generated-config/typescript-operations.md | 15 ++++ .../generated-config/typescript-resolvers.md | 53 +++++++++++++ website/docs/plugins/typed-document-node.md | 47 ++++++++++++ website/package.json | 1 + website/sidebars.js | 1 + website/src/components/live-demo/LiveDemo.js | 2 +- website/src/components/live-demo/examples.js | 13 ++++ website/static/config.schema.json | 74 +++++++++++++++++-- 14 files changed, 265 insertions(+), 17 deletions(-) create mode 100644 website/docs/plugins/typed-document-node.md diff --git a/website/docs/generated-config/c-sharp-operations.md b/website/docs/generated-config/c-sharp-operations.md index 4ed2b66424f..3178281227f 100644 --- a/website/docs/generated-config/c-sharp-operations.md +++ b/website/docs/generated-config/c-sharp-operations.md @@ -10,30 +10,31 @@ This plugin generates C# `class` based on your GraphQL operations. ## API Reference -### `namedClient` +### `namespaceName` type: `string` +default: `GraphQLCodeGen` -Defined the global value of `namedClient`. +Allow you to customize the namespace name. #### Usage Examples ```yml config: - namedClient: 'customName' + namespaceName: MyCompany.MyNamespace ``` -### `serviceName` +### `namedClient` type: `string` -Defined the global value of `serviceName`. +Defined the global value of `namedClient`. #### Usage Examples ```yml config: - serviceName: 'MySDK' + namedClient: 'customName' ``` ### `querySuffix` diff --git a/website/docs/generated-config/c-sharp.md b/website/docs/generated-config/c-sharp.md index af45555300f..e4043d93fdc 100644 --- a/website/docs/generated-config/c-sharp.md +++ b/website/docs/generated-config/c-sharp.md @@ -26,6 +26,24 @@ Overrides the default value of enum values declared in your GraphQL schema. A: 'foo' ``` +### `namespaceName` + +type: `string` +default: `GraphQLCodeGen` + +Allow you to customize the namespace name. + +#### Usage Examples + +```yml +generates: + src/main/c-sharp/my-org/my-app/MyTypes.cs: + plugins: + - c-sharp + config: + namespaceName: MyCompany.MyNamespace +``` + ### `className` type: `string` diff --git a/website/docs/generated-config/flow-operations.md b/website/docs/generated-config/flow-operations.md index 8c5e512d6fe..6c1fb22be05 100644 --- a/website/docs/generated-config/flow-operations.md +++ b/website/docs/generated-config/flow-operations.md @@ -84,6 +84,21 @@ plugins preResolveTypes: true ``` +### `skipTypeNameForRoot` + +type: `boolean` +default: `false` + +Avoid adding `__typename` for root types. This is ignored when a selection explictly specifies `__typename`. + +#### Usage Examples + +```yml +plugins + config: + skipTypeNameForRoot: true +``` + ### `globalNamespace` type: `boolean` diff --git a/website/docs/generated-config/flow-resolvers.md b/website/docs/generated-config/flow-resolvers.md index 0c5176f3308..7db5530f4ee 100644 --- a/website/docs/generated-config/flow-resolvers.md +++ b/website/docs/generated-config/flow-resolvers.md @@ -267,6 +267,31 @@ default: `false` Generates immutable types by adding `readonly` to properties and uses `ReadonlyArray`. +### `namespacedImportName` + +type: `string` +default: `''` + +Prefixes all GraphQL related generated types with that value, as namespaces import. +You can use this featuere to allow seperation of plugins to different files. + + +### `resolverTypeSuffix` + +type: `string` +default: `Resolvers` + +Suffix we add to each generated type resolver. + + +### `allResolversTypeName` + +type: `string` +default: `Resolvers` + +The type name to use when exporting all resolvers signature as unified type. + + ### `scalars` type: `ScalarsMap` diff --git a/website/docs/generated-config/named-operations-object.md b/website/docs/generated-config/named-operations-object.md index 22cf4cd1ba7..3fabab023f8 100644 --- a/website/docs/generated-config/named-operations-object.md +++ b/website/docs/generated-config/named-operations-object.md @@ -22,7 +22,7 @@ generates: path/to/file.ts: plugins: - typescript - - named-operations-object + - typescript-named-operations-object config: identifierName: ListAllOperations -``` +``` \ No newline at end of file diff --git a/website/docs/generated-config/typescript-graphql-files-modules.md b/website/docs/generated-config/typescript-graphql-files-modules.md index 3df4e66440c..15cfb3c7962 100644 --- a/website/docs/generated-config/typescript-graphql-files-modules.md +++ b/website/docs/generated-config/typescript-graphql-files-modules.md @@ -28,7 +28,6 @@ between generated types. generates: src/api/user-service/queries.d.ts documents: src/api/user-service/queries.graphql plugins: - - typescript - typescript-graphql-files-modules config: # resulting module definition path glob: "*\/api/user-service/queries.graphql" diff --git a/website/docs/generated-config/typescript-operations.md b/website/docs/generated-config/typescript-operations.md index f2ef07bb0d5..4068b6f1a34 100644 --- a/website/docs/generated-config/typescript-operations.md +++ b/website/docs/generated-config/typescript-operations.md @@ -126,6 +126,21 @@ plugins preResolveTypes: true ``` +### `skipTypeNameForRoot` + +type: `boolean` +default: `false` + +Avoid adding `__typename` for root types. This is ignored when a selection explictly specifies `__typename`. + +#### Usage Examples + +```yml +plugins + config: + skipTypeNameForRoot: true +``` + ### `operationResultSuffix` type: `string` diff --git a/website/docs/generated-config/typescript-resolvers.md b/website/docs/generated-config/typescript-resolvers.md index 4c12c1a79e3..559c238b740 100644 --- a/website/docs/generated-config/typescript-resolvers.md +++ b/website/docs/generated-config/typescript-resolvers.md @@ -120,6 +120,34 @@ path/to/file.ts: ) => Promise | TResult; ``` +### `allowParentTypeOverride` + +type: `boolean` + +Allow you to override the `ParentType` generic in each resolver, by avoid enforcing the base type of the generated generic type. + +This will generate `ParentType = Type` instead of `ParentType extends Type = Type` in each resolver. + +#### Usage Examples + +```yml + config: + allowParentTypeOverride: true +``` + +### `optionalInfoArgument` + +type: `boolean` + +Sets `info` argument of resolver function to be optional field. Useful for testing. + +#### Usage Examples + +```yml + config: + optionalInfoArgument: true +``` + ### `addUnderscoreToArgsType` type: `boolean` @@ -373,6 +401,31 @@ default: `false` Generates immutable types by adding `readonly` to properties and uses `ReadonlyArray`. +### `namespacedImportName` + +type: `string` +default: `''` + +Prefixes all GraphQL related generated types with that value, as namespaces import. +You can use this featuere to allow seperation of plugins to different files. + + +### `resolverTypeSuffix` + +type: `string` +default: `Resolvers` + +Suffix we add to each generated type resolver. + + +### `allResolversTypeName` + +type: `string` +default: `Resolvers` + +The type name to use when exporting all resolvers signature as unified type. + + ### `scalars` type: `ScalarsMap` diff --git a/website/docs/plugins/typed-document-node.md b/website/docs/plugins/typed-document-node.md new file mode 100644 index 00000000000..2496b1dc791 --- /dev/null +++ b/website/docs/plugins/typed-document-node.md @@ -0,0 +1,47 @@ +--- +id: typed-document-node +title: TypedDocumentNode +--- + +{@import ../plugins/client-note.md} + +This plugins generates a ready-to-use `TypedDocumentNode` (a combination of pre-compiled `DocumentNode` and the TypeScript signature it represents). + +For information about the setup and usage of `TypedDocumentNode`, [please refer to the library's documentation](https://github.com/dotansimha/graphql-typed-document-node). + +> This plugins requires `typescript` and `typescript-operations` as siblings. + +### Usage example + +```yaml +schema: SCHEMA_FILE_OR_ENDPOINT_HERE +documents: "./src/**/*.graphql" +generates: + ./src/graphql-operations.ts: + plugins: + - typescript + - typescript-operations + - typed-document-node +``` + +The example about will generate `TypedDocumentNode` with the needed types built-in, for example: + +```ts +// Represents the variables type of the operation - generated by `typescript` + `typescript-operations` plugins +export type RatesQueryVariables = Exact<{ + currency: Scalars['String']; +}>; + +// Represents the result type of the operation - generated by `typescript` + `typescript-operations` plugins +export type RatesQuery = ( + { __typename?: 'Query' } + & { rates?: Maybe + )>>> } +); + +// Generated by this plugin - creates a pre-compiled `DocumentNode` and passes result type and variables type as generics +export const ratesQuery: TypedDocumentNode = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{ ... ]}; +``` + diff --git a/website/package.json b/website/package.json index 5099288a293..df9a2e87189 100644 --- a/website/package.json +++ b/website/package.json @@ -48,6 +48,7 @@ "@graphql-codegen/schema-ast": "1.17.0", "@graphql-codegen/time": "1.17.0", "@graphql-codegen/typescript": "1.17.0", + "@graphql-codegen/typed-document-node": "1.17.0", "@graphql-codegen/typescript-apollo-angular": "1.17.0", "@graphql-codegen/typescript-compatibility": "1.17.0", "@graphql-codegen/typescript-generic-sdk": "1.17.0", diff --git a/website/sidebars.js b/website/sidebars.js index c150b6be5cd..5b39730139e 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -28,6 +28,7 @@ module.exports = { 'plugins/typescript', 'plugins/typescript-operations', 'plugins/typescript-resolvers', + 'plugins/typed-document-node', 'plugins/typescript-graphql-request', 'plugins/typescript-react-apollo', 'plugins/typescript-vue-apollo', diff --git a/website/src/components/live-demo/LiveDemo.js b/website/src/components/live-demo/LiveDemo.js index 34a67388b4f..28c1963c7b6 100644 --- a/website/src/components/live-demo/LiveDemo.js +++ b/website/src/components/live-demo/LiveDemo.js @@ -91,7 +91,7 @@ export const LiveDemo = () => { menu: styles => ({ ...styles, ...(isDarkTheme ? { backgroundColor: 'black' } : {}) }), control: styles => ({ ...styles, ...(isDarkTheme ? { backgroundColor: 'black' } : {}) }), container: styles => ({ ...styles, display: 'inline-block', width: '100%', textAlign: 'left' }), - option: styles => ({ ...styles, fontSize: 13 }), + option: (styles, { isFocused }) => ({ ...styles, fontSize: 13, ...(isDarkTheme && isFocused ? { backgroundColor: 'gray' } : {}) }), singleValue: styles => ({ ...styles, width: '100%', ...(isDarkTheme ? { color: 'white' } : {}) }), }} isMulti={false} diff --git a/website/src/components/live-demo/examples.js b/website/src/components/live-demo/examples.js index 3c65e644051..b141f9219bb 100644 --- a/website/src/components/live-demo/examples.js +++ b/website/src/components/live-demo/examples.js @@ -134,6 +134,19 @@ export const EXAMPLES = { schema: TS_SCHEMA, documents: TS_QUERY, }, + { + name: 'TypedDocumentNode', + description: `This plugin generates a per-compiled version of \`DocumentNode\`, with the result and variables types bundled into the object, using this library: https://github.com/dotansimha/graphql-typed-document-node`, + tags: ['typescript', 'frontend'], + config: `generates: + operations-types.ts: + plugins: + - typescript + - typescript-operations + - typed-document-node`, + schema: TS_SCHEMA, + documents: TS_QUERY, + }, { name: 'React-Apollo Hooks', description: `This example uses types generated by \`typescript\` and \`typescript-operations\`, and creates a fully type-safe React Hooks, based on your GraphQL operations.`, diff --git a/website/static/config.schema.json b/website/static/config.schema.json index 6d2636e1598..4e2ff7d4414 100644 --- a/website/static/config.schema.json +++ b/website/static/config.schema.json @@ -254,6 +254,11 @@ "description": "Avoid using `Pick` and resolve the actual primitive type of all selection set.", "type": "boolean" }, + "skipTypeNameForRoot": { + "default": false, + "description": "Avoid adding `__typename` for root types. This is ignored when a selection explictly specifies `__typename`.", + "type": "boolean" + }, "operationResultSuffix": { "default": "", "description": "Adds a suffix to generated operation result type names", @@ -353,6 +358,11 @@ "$ref": "#/definitions/EnumValuesMap", "description": "Overrides the default value of enum values declared in your GraphQL schema." }, + "namespaceName": { + "default": "GraphQLCodeGen", + "description": "Allow you to customize the namespace name.", + "type": "string" + }, "className": { "default": "Types", "description": "Allow you to customize the parent class name.", @@ -393,12 +403,13 @@ "description": "This plugin generates C# `class` based on your GraphQL operations.", "type": "object", "properties": { - "namedClient": { - "description": "Defined the global value of `namedClient`.", + "namespaceName": { + "default": "GraphQLCodeGen", + "description": "Allow you to customize the namespace name.", "type": "string" }, - "serviceName": { - "description": "Defined the global value of `serviceName`.", + "namedClient": { + "description": "Defined the global value of `namedClient`.", "type": "string" }, "querySuffix": { @@ -919,6 +930,14 @@ "default": "(parent: TParent, args: TArgs, context: TContext, info: GraphQLResolveInfo) => Promise | TResult", "type": "string" }, + "allowParentTypeOverride": { + "description": "Allow you to override the `ParentType` generic in each resolver, by avoid enforcing the base type of the generated generic type.\n\nThis will generate `ParentType = Type` instead of `ParentType extends Type = Type` in each resolver.", + "type": "boolean" + }, + "optionalInfoArgument": { + "description": "Sets `info` argument of resolver function to be optional field. Useful for testing.", + "type": "boolean" + }, "addUnderscoreToArgsType": { "description": "Adds `_` to generated `Args` types in order to avoid duplicate identifiers.", "type": "boolean" @@ -989,6 +1008,21 @@ "description": "Generates immutable types by adding `readonly` to properties and uses `ReadonlyArray`.", "type": "boolean" }, + "namespacedImportName": { + "default": "''", + "description": "Prefixes all GraphQL related generated types with that value, as namespaces import.\nYou can use this featuere to allow seperation of plugins to different files.", + "type": "string" + }, + "resolverTypeSuffix": { + "default": "Resolvers", + "description": "Suffix we add to each generated type resolver.", + "type": "string" + }, + "allResolversTypeName": { + "default": "Resolvers", + "description": "The type name to use when exporting all resolvers signature as unified type.", + "type": "string" + }, "scalars": { "$ref": "#/definitions/ScalarsMap", "description": "Extends or overrides the built-in scalars and custom GraphQL scalars to a custom type." @@ -2060,6 +2094,21 @@ "description": "Generates immutable types by adding `readonly` to properties and uses `ReadonlyArray`.", "type": "boolean" }, + "namespacedImportName": { + "default": "''", + "description": "Prefixes all GraphQL related generated types with that value, as namespaces import.\nYou can use this featuere to allow seperation of plugins to different files.", + "type": "string" + }, + "resolverTypeSuffix": { + "default": "Resolvers", + "description": "Suffix we add to each generated type resolver.", + "type": "string" + }, + "allResolversTypeName": { + "default": "Resolvers", + "description": "The type name to use when exporting all resolvers signature as unified type.", + "type": "string" + }, "scalars": { "$ref": "#/definitions/ScalarsMap", "description": "Extends or overrides the built-in scalars and custom GraphQL scalars to a custom type." @@ -2110,6 +2159,11 @@ "description": "Avoid using `Pick` and resolve the actual primitive type of all selection set.", "type": "boolean" }, + "skipTypeNameForRoot": { + "default": false, + "description": "Avoid adding `__typename` for root types. This is ignored when a selection explictly specifies `__typename`.", + "type": "boolean" + }, "globalNamespace": { "default": false, "description": "Puts all generated code under `global` namespace. Useful for Stencil integration.", @@ -3032,11 +3086,17 @@ { "type": "array", "items": { - "type": "string" + "type": [ + "string", + "object" + ] } }, { - "type": "string" + "type": [ + "string", + "object" + ] } ] }, @@ -3314,7 +3374,7 @@ "type": "string" }, "Source": { - "description": "A representation of source input to GraphQL.\n`name` and `locationOffset` are optional. They are useful for clients who\nstore GraphQL documents in source files; for example, if the GraphQL input\nstarts at line 40 in a file named Foo.graphql, it might be useful for name to\nbe \"Foo.graphql\" and location to be `{ line: 40, column: 0 }`.\nline and column in locationOffset are 1-indexed", + "description": "A representation of source input to GraphQL. The `name` and `locationOffset` parameters are\noptional, but they are useful for clients who store GraphQL documents in source files.\nFor example, if the GraphQL input starts at line 40 in a file named `Foo.graphql`, it might\nbe useful for `name` to be `\"Foo.graphql\"` and location to be `{ line: 40, column: 1 }`.\nThe `line` and `column` properties in `locationOffset` are 1-indexed.", "type": "object", "properties": { "body": {