diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b50cd4..1423e99b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Changes in this section are not yet released. If you need access to these change - **Features** - Custom error messages when types or interfaces are missing fields which suggests adding a `@gqlField` docblock tag. - Custom error message when your project has no types defined. Intended to help guide new users. +- **Improvements** + - Better import deduplication in generated TypeScript code - ## **Bug Fixes** ## 0.0.29 diff --git a/examples/production-app/schema.ts b/examples/production-app/schema.ts index b0bcd0b6..32dd8631 100644 --- a/examples/production-app/schema.ts +++ b/examples/production-app/schema.ts @@ -2,22 +2,14 @@ * Executable schema generated by Grats (https://grats.capt.dev) * Do not manually edit. Regenerate by running `npx grats`. */ -import { id as likeIdResolver } from "./graphql/Node"; -import { id as userIdResolver } from "./graphql/Node"; -import { id as postIdResolver } from "./graphql/Node"; -import { nodes as postConnectionNodesResolver } from "./models/PostConnection"; -import { nodes as likeConnectionNodesResolver } from "./models/LikeConnection"; -import { likes as queryLikesResolver } from "./models/LikeConnection"; -import { node as queryNodeResolver } from "./graphql/Node"; -import { nodes as queryNodesResolver } from "./graphql/Node"; -import { posts as queryPostsResolver } from "./models/PostConnection"; -import { nodes as userConnectionNodesResolver } from "./models/UserConnection"; -import { users as queryUsersResolver } from "./models/UserConnection"; +import { id as likeIdResolver, id as userIdResolver, id as postIdResolver, node as queryNodeResolver, nodes as queryNodesResolver } from "./graphql/Node"; +import { nodes as postConnectionNodesResolver, posts as queryPostsResolver } from "./models/PostConnection"; +import { nodes as likeConnectionNodesResolver, likes as queryLikesResolver, postLikes as subscriptionPostLikesResolver } from "./models/LikeConnection"; +import { nodes as userConnectionNodesResolver, users as queryUsersResolver } from "./models/UserConnection"; import { Viewer as queryViewerResolver } from "./models/Viewer"; import { createLike as mutationCreateLikeResolver } from "./models/Like"; import { createPost as mutationCreatePostResolver } from "./models/Post"; import { createUser as mutationCreateUserResolver } from "./models/User"; -import { postLikes as subscriptionPostLikesResolver } from "./models/LikeConnection"; import { GraphQLSchema, GraphQLObjectType, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLString, GraphQLScalarType, GraphQLID, GraphQLInterfaceType, GraphQLBoolean, GraphQLInputObjectType } from "graphql"; export function getSchema(): GraphQLSchema { const DateType: GraphQLScalarType = new GraphQLScalarType({ diff --git a/package.json b/package.json index b526849c..1d9a58b8 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "semver": "^7.5.4" }, "devDependencies": { - "@graphql-tools/utils": "^9.2.1", "@types/node": "^18.14.6", "@types/semver": "^7.5.6", "@typescript-eslint/eslint-plugin": "^5.55.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c9730dfa..b0af8687 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,9 +21,6 @@ importers: specifier: 5.5.4 version: 5.5.4 devDependencies: - '@graphql-tools/utils': - specifier: ^9.2.1 - version: 9.2.1(graphql@16.9.0) '@types/node': specifier: ^18.14.6 version: 18.15.0 @@ -1843,12 +1840,24 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/merge@9.0.12': + resolution: {integrity: sha512-ECkUdgWkizhzQ6JJg16MCYnIN2r2+q/vP5smzi3YeeJkZ/3f9ynFDkaqoMg0Ddg9MugR03hMiQQrssk5f0389Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/merge@9.0.4': resolution: {integrity: sha512-MivbDLUQ+4Q8G/Hp/9V72hbn810IJDEZQ57F01sHnlrrijyadibfVhaQfW/pNH+9T/l8ySZpaR/DpL5i+ruZ+g==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@10.0.11': + resolution: {integrity: sha512-cYr/7SJSKtdwPByTKHlBr0tYGf7/sYNyzKlPhPMHWoYyGxtn8ytbfF6wEUcxuaOoqksIFxOGr+WOJh1WvShb6A==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@10.0.3': resolution: {integrity: sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==} engines: {node: '>=16.0.0'} @@ -1872,6 +1881,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@10.6.2': + resolution: {integrity: sha512-ABZHTpwiVR8oE2//NI/nnU3nNhbBpqMlMYyCF5cnqjLfhlyOdFfoRuhYEATEsmMfDg0ijGreULywK/SmepVGfw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@9.2.1': resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} peerDependencies: @@ -1928,6 +1943,7 @@ packages: '@humanwhocodes/config-array@0.11.8': resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -1939,6 +1955,7 @@ packages: '@humanwhocodes/object-schema@1.2.1': resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead '@import-maps/resolve@1.0.1': resolution: {integrity: sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA==} @@ -4124,6 +4141,10 @@ packages: resolution: {integrity: sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==} engines: {node: '>=16.0.0'} + cross-inspect@1.0.1: + resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} + engines: {node: '>=16.0.0'} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -5302,10 +5323,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported glob@9.3.4: resolution: {integrity: sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==} @@ -5675,6 +5698,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.3: resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} @@ -5733,10 +5757,12 @@ packages: is-accessor-descriptor@0.1.6: resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v0.1.7 is-accessor-descriptor@1.0.0: resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==} engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v1.0.1 is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -5771,10 +5797,12 @@ packages: is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v0.1.5 is-data-descriptor@1.0.0: resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==} engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v1.0.1 is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -8048,6 +8076,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true robust-predicates@3.0.2: @@ -11465,34 +11494,48 @@ snapshots: graphql: 16.8.1 tslib: 2.6.2 - '@graphql-tools/merge@9.0.4(graphql@16.8.1)': + '@graphql-tools/merge@9.0.12(graphql@16.8.1)': dependencies: - '@graphql-tools/utils': 10.2.0(graphql@16.8.1) + '@graphql-tools/utils': 10.6.2(graphql@16.8.1) graphql: 16.8.1 tslib: 2.6.2 - '@graphql-tools/merge@9.0.4(graphql@16.9.0)': + '@graphql-tools/merge@9.0.12(graphql@16.9.0)': dependencies: - '@graphql-tools/utils': 10.2.0(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) graphql: 16.9.0 tslib: 2.6.2 - '@graphql-tools/schema@10.0.3(graphql@16.8.1)': + '@graphql-tools/merge@9.0.4(graphql@16.8.1)': dependencies: - '@graphql-tools/merge': 9.0.4(graphql@16.8.1) '@graphql-tools/utils': 10.2.0(graphql@16.8.1) graphql: 16.8.1 tslib: 2.6.2 + + '@graphql-tools/schema@10.0.11(graphql@16.8.1)': + dependencies: + '@graphql-tools/merge': 9.0.12(graphql@16.8.1) + '@graphql-tools/utils': 10.6.2(graphql@16.8.1) + graphql: 16.8.1 + tslib: 2.6.2 value-or-promise: 1.0.12 - '@graphql-tools/schema@10.0.3(graphql@16.9.0)': + '@graphql-tools/schema@10.0.11(graphql@16.9.0)': dependencies: - '@graphql-tools/merge': 9.0.4(graphql@16.9.0) - '@graphql-tools/utils': 10.2.0(graphql@16.9.0) + '@graphql-tools/merge': 9.0.12(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) graphql: 16.9.0 tslib: 2.6.2 value-or-promise: 1.0.12 + '@graphql-tools/schema@10.0.3(graphql@16.8.1)': + dependencies: + '@graphql-tools/merge': 9.0.4(graphql@16.8.1) + '@graphql-tools/utils': 10.2.0(graphql@16.8.1) + graphql: 16.8.1 + tslib: 2.6.2 + value-or-promise: 1.0.12 + '@graphql-tools/schema@9.0.19(graphql@16.8.1)': dependencies: '@graphql-tools/merge': 8.4.2(graphql@16.8.1) @@ -11525,6 +11568,22 @@ snapshots: graphql: 16.9.0 tslib: 2.6.2 + '@graphql-tools/utils@10.6.2(graphql@16.8.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + cross-inspect: 1.0.1 + dset: 3.1.3 + graphql: 16.8.1 + tslib: 2.6.2 + + '@graphql-tools/utils@10.6.2(graphql@16.9.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + cross-inspect: 1.0.1 + dset: 3.1.3 + graphql: 16.9.0 + tslib: 2.6.2 + '@graphql-tools/utils@9.2.1(graphql@16.6.0)': dependencies: '@graphql-typed-document-node/core': 3.1.2(graphql@16.6.0) @@ -11537,12 +11596,6 @@ snapshots: graphql: 16.8.1 tslib: 2.5.0 - '@graphql-tools/utils@9.2.1(graphql@16.9.0)': - dependencies: - '@graphql-typed-document-node/core': 3.1.2(graphql@16.9.0) - graphql: 16.9.0 - tslib: 2.5.0 - '@graphql-typed-document-node/core@3.1.2(graphql@16.6.0)': dependencies: graphql: 16.6.0 @@ -11551,10 +11604,6 @@ snapshots: dependencies: graphql: 16.8.1 - '@graphql-typed-document-node/core@3.1.2(graphql@16.9.0)': - dependencies: - graphql: 16.9.0 - '@graphql-typed-document-node/core@3.2.0(graphql@16.8.1)': dependencies: graphql: 16.8.1 @@ -14377,6 +14426,10 @@ snapshots: dependencies: tslib: 2.6.2 + cross-inspect@1.0.1: + dependencies: + tslib: 2.6.2 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -15889,7 +15942,7 @@ snapshots: dependencies: '@envelop/core': 5.0.0 '@graphql-tools/executor': 1.2.0(graphql@16.8.1) - '@graphql-tools/schema': 10.0.3(graphql@16.8.1) + '@graphql-tools/schema': 10.0.11(graphql@16.8.1) '@graphql-tools/utils': 10.2.0(graphql@16.8.1) '@graphql-yoga/logger': 2.0.0 '@graphql-yoga/subscription': 5.0.0 @@ -15904,7 +15957,7 @@ snapshots: dependencies: '@envelop/core': 5.0.0 '@graphql-tools/executor': 1.2.0(graphql@16.9.0) - '@graphql-tools/schema': 10.0.3(graphql@16.9.0) + '@graphql-tools/schema': 10.0.11(graphql@16.9.0) '@graphql-tools/utils': 10.2.0(graphql@16.9.0) '@graphql-yoga/logger': 2.0.0 '@graphql-yoga/subscription': 5.0.0 diff --git a/src/Extractor.ts b/src/Extractor.ts index 15f640c4..3b443cfd 100644 --- a/src/Extractor.ts +++ b/src/Extractor.ts @@ -38,8 +38,8 @@ import { extend, invariant } from "./utils/helpers"; import * as Act from "./CodeActions"; import { InputValueDefinitionNodeOrResolverArg, - UnresolvedResolverParam, -} from "./metadataDirectives.js"; + ResolverArgument, +} from "./resolverSignature"; export const LIBRARY_IMPORT_NAME = "grats"; export const LIBRARY_NAME = "Grats"; @@ -452,19 +452,10 @@ class Extractor { return this.report(node, E.fieldVariableNotTopLevelExported()); } - const tsModulePath = relativePath(node.getSourceFile().fileName); - - const metadataDirective = this.gql.fieldMetadataDirective(node, { - tsModulePath, - name: null, - exportName: funcName == null ? null : funcName.text, - }); - - if (declaration.initializer == null) { - return this.report(node, E.fieldVariableIsNotArrowFunction()); - } - - if (!ts.isArrowFunction(declaration.initializer)) { + if ( + declaration.initializer == null || + !ts.isArrowFunction(declaration.initializer) + ) { return this.report(node, E.fieldVariableIsNotArrowFunction()); } @@ -486,7 +477,7 @@ class Extractor { ); } - this.collectAbstractField(declaration.initializer, name, metadataDirective); + this.collectAbstractField(declaration.initializer, funcName, null, name); } functionDeclarationExtendType( @@ -501,15 +492,7 @@ class Extractor { return this.report(node, E.functionFieldNotTopLevel()); } - const tsModulePath = relativePath(node.getSourceFile().fileName); - - const metadataDirective = this.gql.fieldMetadataDirective(node, { - tsModulePath, - name: null, - exportName: funcName == null ? null : funcName.text, - }); - - this.collectAbstractField(node, name, metadataDirective); + this.collectAbstractField(node, funcName, null, name); } staticMethodExtendType(node: ts.MethodDeclaration, tag: ts.JSDocTag) { @@ -528,7 +511,7 @@ class Extractor { return this.report(classNode, E.staticMethodClassNotTopLevel()); } - let exportName: string | null = null; + let exportName: ts.Identifier | null = null; const isExported = classNode.modifiers?.some((modifier) => { return modifier.kind === ts.SyntaxKind.ExportKeyword; @@ -555,24 +538,17 @@ class Extractor { const className = this.expectNameIdentifier(classNode.name); if (className == null) return null; - exportName = className.text; + exportName = className; } - const tsModulePath = relativePath(node.getSourceFile().fileName); - - const metadataDirective = this.gql.fieldMetadataDirective(methodName, { - tsModulePath, - name: methodName.text, - exportName, - }); - - this.collectAbstractField(node, name, metadataDirective); + this.collectAbstractField(node, exportName, methodName, name); } collectAbstractField( node: ts.FunctionDeclaration | ts.MethodDeclaration | ts.ArrowFunction, + exportName: ts.Identifier | null, + methodName: ts.Identifier | null, name: NameNode, - metadataDirective: ConstDirectiveNode, ) { const [typeParam, ...restParams] = node.parameters; if (typeParam == null) { @@ -597,7 +573,9 @@ class Extractor { const type = this.collectType(node.type, { kind: "OUTPUT" }); if (type == null) return null; - const directives = [metadataDirective]; + const tsModulePath = relativePath(node.getSourceFile().fileName); + + const directives: ConstDirectiveNode[] = []; const description = this.collectDescription(node); const deprecated = this.collectDeprecated(node); @@ -605,12 +583,7 @@ class Extractor { directives.push(deprecated); } - const killsParentOnExceptionDirective = - this.killsParentOnExceptionDirective(node); - - if (killsParentOnExceptionDirective != null) { - directives.push(killsParentOnExceptionDirective); - } + const killsParentOnException = this.killsParentOnException(node); const field = this.gql.fieldDefinition( node, @@ -619,10 +592,23 @@ class Extractor { args, directives, description, - [ - { kind: "named", name: "source", sourceNode: typeParam }, - ...resolverParams, - ], + killsParentOnException, + methodName == null + ? { + kind: "function", + path: tsModulePath, + exportName: exportName == null ? null : exportName.text, + arguments: [{ kind: "source", node: typeParam }, ...resolverParams], + node, + } + : { + kind: "staticMethod", + path: tsModulePath, + exportName: exportName == null ? null : exportName.text, + arguments: [{ kind: "source", node: typeParam }, ...resolverParams], + name: methodName.text, + node, + }, ); this.definitions.push( this.gql.abstractFieldDefinition(node, typeName, field), @@ -1395,13 +1381,7 @@ class Extractor { // https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ1rHgOwgFwCcBXYPeAgCgAciAjEAS2BQDNEBfAShXdXaA return null; } - const directives = [ - this.gql.fieldMetadataDirective(node.name, { - name: id.text == name.value ? null : id.text, - tsModulePath: null, - exportName: null, - }), - ]; + const directives: ConstDirectiveNode[] = []; const type = this.collectType(node.type, { kind: "OUTPUT" }); if (type == null) return null; @@ -1412,12 +1392,7 @@ class Extractor { } const description = this.collectDescription(node); - const killsParentOnExceptionDirective = - this.killsParentOnExceptionDirective(node); - - if (killsParentOnExceptionDirective != null) { - directives.push(killsParentOnExceptionDirective); - } + const killsParentOnException = this.killsParentOnException(node); return this.gql.fieldDefinition( node, @@ -1426,7 +1401,12 @@ class Extractor { null, directives, description, - null, + killsParentOnException, + { + kind: "property", + name: id.text, + node, + }, ); } @@ -1851,25 +1831,14 @@ class Extractor { const id = this.expectNameIdentifier(node.name); if (id == null) return null; - const directives = [ - this.gql.fieldMetadataDirective(node.name, { - name: id.text === name.value ? null : id.text, - tsModulePath: null, - exportName: null, - }), - ]; + const directives: ConstDirectiveNode[] = []; const deprecated = this.collectDeprecated(node); if (deprecated != null) { directives.push(deprecated); } - const killsParentOnExceptionDirective = - this.killsParentOnExceptionDirective(node); - - if (killsParentOnExceptionDirective != null) { - directives.push(killsParentOnExceptionDirective); - } + const killsParentOnException = this.killsParentOnException(node); return this.gql.fieldDefinition( node, @@ -1878,15 +1847,27 @@ class Extractor { args, directives, description, - isCallable(node) ? resolverParams : null, + killsParentOnException, + isCallable(node) + ? { + kind: "method", + name: id.text === name.value ? null : id.text, + arguments: resolverParams, + node, + } + : { + kind: "property", + name: id.text === name.value ? null : id.text, + node, + }, ); } resolverParams(parameters: ReadonlyArray): { - resolverParams: UnresolvedResolverParam[]; + resolverParams: ResolverArgument[]; args: readonly InputValueDefinitionNode[] | null; } | null { - const resolverParams: UnresolvedResolverParam[] = []; + const resolverParams: ResolverArgument[] = []; let args: { param: ts.ParameterDeclaration; @@ -1909,7 +1890,7 @@ class Extractor { tsRelated(args.param, "Previous type literal"), ]); } - resolverParams.push({ kind: "named", name: "args", sourceNode: param }); + resolverParams.push({ kind: "argumentsObject", node: param }); args = { param, inputs: [] }; let defaults: ArgDefaults | null = null; @@ -1928,7 +1909,7 @@ class Extractor { const inputDefinition = this.collectParamArg(param); if (inputDefinition == null) return null; - resolverParams.push({ kind: "unresolved", inputDefinition }); + resolverParams.push({ kind: "unresolved", inputDefinition, node: param }); } return { resolverParams, args: args ? args.inputs : null }; } @@ -2102,20 +2083,7 @@ class Extractor { directives.push(deprecated); } - directives.push( - this.gql.fieldMetadataDirective(node.name, { - name: id.text === name.value ? null : id.text, - exportName: null, - tsModulePath: null, - }), - ); - - const killsParentOnExceptionDirective = - this.killsParentOnExceptionDirective(node); - - if (killsParentOnExceptionDirective != null) { - directives.push(killsParentOnExceptionDirective); - } + const killsParentOnException = this.killsParentOnException(node); return this.gql.fieldDefinition( node, @@ -2124,7 +2092,12 @@ class Extractor { null, directives, description, - null, + killsParentOnException, + { + kind: "property", + name: id.text === name.value ? null : id.text, + node, + }, ); } // TODO: Support separate modes for input and output types @@ -2289,16 +2262,15 @@ class Extractor { // the server to handle field level executions by simply returning null for // that field. // https://graphql.org/learn/best-practices/#nullability - killsParentOnExceptionDirective( - parentNode: ts.Node, - ): ConstDirectiveNode | null { + killsParentOnException(parentNode: ts.Node): NameNode | null { const tags = ts.getJSDocTags(parentNode); const killsParentOnExceptions = tags.find( (tag) => tag.tagName.text === KILLS_PARENT_ON_EXCEPTION_TAG, ); if (killsParentOnExceptions) { - return this.gql.killsParentOnExceptionDirective( + return this.gql.name( killsParentOnExceptions.tagName, + KILLS_PARENT_ON_EXCEPTION_TAG, ); } return null; diff --git a/src/GraphQLAstExtensions.ts b/src/GraphQLAstExtensions.ts new file mode 100644 index 00000000..f9c45074 --- /dev/null +++ b/src/GraphQLAstExtensions.ts @@ -0,0 +1,73 @@ +import { ResolverSignature } from "./resolverSignature"; + +/** + * In most cases we can use directives to annotate constructs + * however, it't not possible to annotate an individual TypeNode. + * Additionally, we can't use sets or maps to "tag" nodes because + * there are places where we immutably update the AST to make changes. + * + * Instead, we cheat and add properties to some nodes. These types use + * interface merging to add our own properties to the AST. + * + * We try to use this approach sparingly. + */ +declare module "graphql" { + export interface ListTypeNode { + /** + * Grats metadata: Whether the list type was defined as an AsyncIterable. + * Used to ensure that all fields on `Subscription` return an AsyncIterable. + */ + isAsyncIterable?: boolean; + } + export interface NameNode { + /** + * Grats metadata: A unique identifier for the node. Used to track + * data about nodes in lookup data structures. + */ + tsIdentifier: number; + } + export interface ObjectTypeDefinitionNode { + /** + * Grats metadata: Indicates that the type was materialized as part of + * generic type resolution. + */ + wasSynthesized?: boolean; + hasTypeNameField: boolean; + exported?: { + tsModulePath: string; + exportName: string | null; + }; + } + export interface UnionTypeDefinitionNode { + /** + * Grats metadata: Indicates that the type was materialized as part of + * generic type resolution. + */ + wasSynthesized?: boolean; + } + export interface InterfaceTypeDefinitionNode { + /** + * Grats metadata: Indicates that the type was materialized as part of + * generic type resolution. + */ + wasSynthesized?: boolean; + } + export interface ObjectTypeExtensionNode { + /** + * Grats metadata: Indicates that we don't know yet if this is extending an interface + * or a type. + */ + mayBeInterface?: boolean; + } + + export interface FieldDefinitionNode { + /** + * Grats metadata: Describes the backing resolver for a field. Eventually + * this gets transformed into a @resolver directive. However, we delay doing + * that to avoid repeated parsing, and to allow for unresolved types early + * on during compilation. + */ + resolver?: ResolverSignature; + killsParentOnException?: NameNode; + } +} diff --git a/src/GraphQLConstructor.ts b/src/GraphQLConstructor.ts index e72239f7..0d9ceea0 100644 --- a/src/GraphQLConstructor.ts +++ b/src/GraphQLConstructor.ts @@ -33,67 +33,14 @@ import { ObjectTypeExtensionNode, } from "graphql"; import * as ts from "typescript"; -import { - makeKillsParentOnExceptionDirective, - TS_MODULE_PATH_ARG, - FIELD_NAME_ARG, - FIELD_METADATA_DIRECTIVE, - EXPORT_NAME_ARG, - UnresolvedResolverParam, - InputValueDefinitionNodeOrResolverArg, -} from "./metadataDirectives"; import { uniqueId } from "./utils/helpers"; import { DiagnosticResult, TsLocatableNode } from "./utils/DiagnosticError"; +import { + InputValueDefinitionNodeOrResolverArg, + ResolverSignature, +} from "./resolverSignature"; export class GraphQLConstructor { - fieldMetadataDirective( - node: ts.Node, - metadata: { - tsModulePath: string | null; - name: string | null; - exportName: string | null; - }, - ): ConstDirectiveNode { - const args: ConstArgumentNode[] = []; - if (metadata.tsModulePath != null) { - args.push( - this.constArgument( - node, - this.name(node, TS_MODULE_PATH_ARG), - this.string(node, metadata.tsModulePath), - ), - ); - } - if (metadata.name != null) { - args.push( - this.constArgument( - node, - this.name(node, FIELD_NAME_ARG), - this.string(node, metadata.name), - ), - ); - } - if (metadata.exportName != null) { - args.push( - this.constArgument( - node, - this.name(node, EXPORT_NAME_ARG), - this.string(node, metadata.exportName), - ), - ); - } - - return this.constDirective( - node, - this.name(node, FIELD_METADATA_DIRECTIVE), - args, - ); - } - - killsParentOnExceptionDirective(node: ts.Node): ConstDirectiveNode { - return makeKillsParentOnExceptionDirective(loc(node)); - } - /* Top Level Types */ unionTypeDefinition( node: ts.Node, @@ -184,7 +131,6 @@ export class GraphQLConstructor { } /* Field Definitions */ - fieldDefinition( node: ts.Node, name: NameNode, @@ -192,7 +138,8 @@ export class GraphQLConstructor { args: readonly InputValueDefinitionNode[] | null, directives: readonly ConstDirectiveNode[], description: StringValueNode | null, - resolverParams: UnresolvedResolverParam[] | null, + killsParentOnException: NameNode | null, + resolver: ResolverSignature, ): FieldDefinitionNode { return { kind: Kind.FIELD_DEFINITION, @@ -202,7 +149,8 @@ export class GraphQLConstructor { type, arguments: args ?? undefined, directives: this._optionalList(directives), - resolverParams: resolverParams ?? undefined, + resolver, + killsParentOnException: killsParentOnException ?? undefined, }; } diff --git a/src/TypeContext.ts b/src/TypeContext.ts index 29f5d454..548d8473 100644 --- a/src/TypeContext.ts +++ b/src/TypeContext.ts @@ -236,6 +236,7 @@ export class TypeContext { const name = definition.name; const declaration = this._idToDeclaration.get(name.tsIdentifier); if (!declaration) { + console.log(definition); throw new Error(`Could not find declaration for ${name.value}`); } return declaration; diff --git a/src/cli.ts b/src/cli.ts index eb53e622..1cbfd849 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -134,12 +134,12 @@ function writeSchemaFilesAndReport( config: ParsedCommandLineGrats, configPath: string, ) { - const { schema, doc } = schemaAndDoc; + const { schema, doc, resolvers } = schemaAndDoc; const gratsConfig: GratsConfig = config.raw.grats; const dest = resolve(dirname(configPath), gratsConfig.tsSchema); - const code = printExecutableSchema(schema, gratsConfig, dest); + const code = printExecutableSchema(schema, resolvers, gratsConfig, dest); writeFileSync(dest, code); console.error(`Grats: Wrote TypeScript schema to \`${dest}\`.`); @@ -148,6 +148,15 @@ function writeSchemaFilesAndReport( const absOutput = resolve(dirname(configPath), gratsConfig.graphqlSchema); writeFileSync(absOutput, schemaStr); console.error(`Grats: Wrote schema to \`${absOutput}\`.`); + + if (config.raw.grats.EXPERIMENTAL__emitMetadata) { + const absOutput = resolve( + dirname(configPath), + gratsConfig.graphqlSchema.replace(/\.graphql$/, ".json"), + ); + writeFileSync(absOutput, JSON.stringify(resolvers, null, 2)); + console.error(`Grats: Wrote resolver signatures to \`${absOutput}\`.`); + } } /** diff --git a/src/codegen/TSAstBuilder.ts b/src/codegen/TSAstBuilder.ts new file mode 100644 index 00000000..c82561b3 --- /dev/null +++ b/src/codegen/TSAstBuilder.ts @@ -0,0 +1,221 @@ +import * as ts from "typescript"; +import { isNonNull } from "../utils/helpers"; +import * as path from "path"; +import { resolveRelativePath } from "../gratsRoot"; + +const F = ts.factory; + +/** + * A helper class to build up a TypeScript document AST. + */ +export default class TSAstBuilder { + _imports: ts.Statement[] = []; + imports: Map = new Map(); + _helpers: ts.Statement[] = []; + _statements: ts.Statement[] = []; + + constructor( + private _destination: string, + private importModuleSpecifierEnding: string, + ) {} + addHelper(statement: ts.Statement) { + this._helpers.push(statement); + } + addStatement(statement: ts.Statement) { + this._statements.push(statement); + } + + createBlockWithScope(closure: () => void): ts.Block { + const initialStatements = this._statements; + this._statements = []; + closure(); + const block = F.createBlock(this._statements, true); + this._statements = initialStatements; + return block; + } + + // Helper for the common case. + method( + name: string, + params: ts.ParameterDeclaration[], + statements: ts.Statement[], + ): ts.MethodDeclaration { + return F.createMethodDeclaration( + undefined, + undefined, + name, + undefined, + undefined, + params, + undefined, + F.createBlock(statements, true), + ); + } + + // Helper for the common case of a single string argument. + param(name: string, type?: ts.TypeNode): ts.ParameterDeclaration { + return F.createParameterDeclaration( + undefined, + undefined, + name, + undefined, + type, + undefined, + ); + } + + functionDeclaration( + name: string, + modifiers: ts.Modifier[] | undefined, + type: ts.TypeNode | undefined, + body: ts.Block, + ): void { + this.addStatement( + F.createFunctionDeclaration( + modifiers, + undefined, + name, + undefined, + [], + type, + body, + ), + ); + } + + // Helper to allow for nullable elements. + objectLiteral( + properties: Array, + ): ts.ObjectLiteralExpression { + return F.createObjectLiteralExpression(properties.filter(isNonNull), true); + } + + constDeclaration( + name: string, + initializer: ts.Expression, + type?: ts.TypeNode, + ): void { + this.addStatement( + F.createVariableStatement( + undefined, + F.createVariableDeclarationList( + [ + F.createVariableDeclaration( + F.createIdentifier(name), + undefined, + type, + initializer, + ), + ], + ts.NodeFlags.Const, + ), + ), + ); + } + + import(from: string, names: { name: string; as?: string }[]) { + let moduleImports = this.imports.get(from); + if (moduleImports == null) { + moduleImports = []; + this.imports.set(from, moduleImports); + } + for (const { name, as } of names) { + let seen = false; + for (const imp of moduleImports) { + if (imp.name === name && imp.as === as) { + seen = true; + } + } + if (!seen) { + moduleImports.push({ name, as }); + } + } + } + + importDefault(from: string, as: string) { + this._imports.push( + F.createImportDeclaration( + undefined, + F.createImportClause(false, F.createIdentifier(as), undefined), + F.createStringLiteral(from), + ), + ); + } + + importUserConstruct( + tsModulePath: string, + exportName: string | null, + localName: string, + ): void { + const abs = resolveRelativePath(tsModulePath); + const relative = replaceExt( + path.relative(path.dirname(this._destination), abs), + this.importModuleSpecifierEnding, + ); + const modulePath = `./${normalizeRelativePathToPosix(relative)}`; + if (exportName == null) { + this.importDefault(modulePath, localName); + } else { + this.import(modulePath, [{ name: exportName, as: localName }]); + } + } + + print(): string { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const sourceFile = ts.createSourceFile( + "tempFile.ts", + "", + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS, + ); + + for (const [from, names] of this.imports) { + const namedImports = names.map((name) => { + if (name.as) { + return F.createImportSpecifier( + false, + F.createIdentifier(name.name), + F.createIdentifier(name.as), + ); + } else { + return F.createImportSpecifier( + false, + undefined, + F.createIdentifier(name.name), + ); + } + }); + this._imports.push( + F.createImportDeclaration( + undefined, + F.createImportClause( + false, + undefined, + F.createNamedImports(namedImports), + ), + F.createStringLiteral(from), + ), + ); + } + + return printer.printList( + ts.ListFormat.MultiLine, + F.createNodeArray([ + ...this._imports, + ...this._helpers, + ...this._statements, + ]), + sourceFile, + ); + } +} +function replaceExt(filePath: string, newSuffix: string): string { + const ext = path.extname(filePath); + return filePath.slice(0, -ext.length) + newSuffix; +} + +// https://github.com/sindresorhus/slash/blob/98b618f5a3bfcb5dd374b204868818845b87bb2f/index.js#L8C9-L8C33 +function normalizeRelativePathToPosix(unknownPath: string): string { + return unknownPath.replace(/\\/g, "/"); +} diff --git a/src/codegen/resolverCodegen.ts b/src/codegen/resolverCodegen.ts new file mode 100644 index 00000000..2ab8aec7 --- /dev/null +++ b/src/codegen/resolverCodegen.ts @@ -0,0 +1,299 @@ +import { ConstDirectiveNode, GraphQLField } from "graphql"; +import * as ts from "typescript"; +import { SEMANTIC_NON_NULL_DIRECTIVE } from "../publicDirectives"; +import { + ASSERT_NON_NULL_HELPER, + createAssertNonNullHelper, +} from "../codegenHelpers"; +import { nullThrows } from "../utils/helpers"; +import { ResolverArgument, ResolverDefinition, Metadata } from "../metadata"; +import TSAstBuilder from "./TSAstBuilder"; + +const RESOLVER_ARGS: string[] = ["source", "args", "context", "info"]; + +const F = ts.factory; + +/** + * Codegen specifically for generating resolver methods for a given field. + * Having this separate from the other codegen classes allows it to be used + * for any codegen that needs to generate resolver methods. + */ +export default class ResolverCodegen { + _helpers: Set = new Set(); + constructor(public ts: TSAstBuilder, public _resolvers: Metadata) {} + resolveMethod( + fieldName: string, + methodName: string, + parentTypeName: string, + ): ts.MethodDeclaration | null { + const { resolver } = this._resolvers.types[parentTypeName][fieldName]; + if (this.isDefaultResolverSignature(fieldName, resolver)) { + return null; + } + switch (resolver.kind) { + case "property": + return this.ts.method( + methodName, + [this.ts.param("source")], + [ + F.createReturnStatement( + F.createPropertyAccessExpression( + F.createIdentifier("source"), + F.createIdentifier(resolver.name ?? fieldName), + ), + ), + ], + ); + case "method": { + return this.ts.method( + methodName, + extractUsedParams(resolver.arguments ?? [], true).map((name) => + this.ts.param(name), + ), + [ + F.createReturnStatement( + F.createCallExpression( + F.createPropertyAccessExpression( + F.createIdentifier("source"), + F.createIdentifier(resolver.name ?? fieldName), + ), + [], + (resolver.arguments ?? []).map((arg) => { + return this.resolverParam(arg); + }), + ), + ), + ], + ); + } + case "function": { + const resolverName = formatResolverFunctionVarName( + parentTypeName, + fieldName, + ); + this.ts.importUserConstruct( + resolver.path, + resolver.exportName, + resolverName, + ); + return this.ts.method( + methodName, + extractUsedParams(resolver.arguments ?? [], true).map((name) => + this.ts.param(name), + ), + [ + F.createReturnStatement( + F.createCallExpression( + F.createIdentifier(resolverName), + undefined, + (resolver.arguments ?? []).map((arg) => { + return this.resolverParam(arg); + }), + ), + ), + ], + ); + } + case "staticMethod": { + // Note: This name is guaranteed to be unique, but for static methods, it + // means we import the same class multiple times with multiple names. + const resolverName = formatResolverFunctionVarName( + parentTypeName, + fieldName, + ); + this.ts.importUserConstruct( + resolver.path, + resolver.exportName, + resolverName, + ); + return this.ts.method( + methodName, + extractUsedParams(resolver.arguments ?? [], true).map((name) => + this.ts.param(name), + ), + [ + F.createReturnStatement( + F.createCallExpression( + F.createPropertyAccessExpression( + F.createIdentifier(resolverName), + F.createIdentifier(resolver.name), + ), + undefined, + (resolver.arguments ?? []).map((arg) => { + return this.resolverParam(arg); + }), + ), + ), + ], + ); + } + default: + // @ts-expect-error + throw new Error(`Unexpected resolver kind ${fieldDefinition.kind}`); + } + } + isDefaultResolverSignature( + fieldName: string, + signature: ResolverDefinition, + ): boolean { + switch (signature.kind) { + case "property": + return signature.name == null || fieldName === signature.name; + case "method": + if (signature.name != null && fieldName !== signature.name) { + return false; + } + if (signature.arguments == null || signature.arguments.length === 0) { + return true; + } + return signature.arguments.every((arg, i) => { + switch (i) { + case 0: + return arg.kind === "argumentsObject"; + // TODO: More? + default: + return false; + } + }); + case "function": + return false; + case "staticMethod": + return false; + } + } + // Either `args`, `context`, `info`, or a positional argument like + // `args.someArg`. + resolverParam(arg: ResolverArgument): ts.Expression { + switch (arg.kind) { + case "argumentsObject": + return F.createIdentifier("args"); + case "context": + return F.createIdentifier("context"); + case "information": + return F.createIdentifier("info"); + case "source": + return F.createIdentifier("source"); + case "named": + return F.createPropertyAccessExpression( + F.createIdentifier("args"), + F.createIdentifier(arg.name), + ); + default: + // @ts-expect-error + throw new Error(`Unexpected resolver kind ${arg.kind}`); + } + } + // If a field is smantically non-null, we need to wrap the resolver in a + // runtime check to ensure that the resolver does not return null. + maybeApplySemanticNullRuntimeCheck( + field: GraphQLField, + method_: ts.MethodDeclaration | null, + methodName: string, + ): ts.MethodDeclaration | null { + const semanticNonNull = fieldDirective(field, SEMANTIC_NON_NULL_DIRECTIVE); + if (semanticNonNull == null) { + return method_; + } + + if (!this._helpers.has(ASSERT_NON_NULL_HELPER)) { + this._helpers.add(ASSERT_NON_NULL_HELPER); + this.ts.addHelper(createAssertNonNullHelper()); + } + + const method = method_ ?? this.defaultResolverMethod(methodName); + + const bodyStatements = method.body?.statements; + if (bodyStatements == null || bodyStatements.length === 0) { + throw new Error(`Expected method to have a body`); + } + let foundReturn = false; + const newBodyStatements = bodyStatements.map((statement) => { + if (ts.isReturnStatement(statement)) { + foundReturn = true; + // We need to wrap the return statement in a call to the runtime check + return F.createReturnStatement( + F.createCallExpression( + F.createIdentifier(ASSERT_NON_NULL_HELPER), + [], + [nullThrows(statement.expression)], + ), + ); + } + return statement; + }); + if (!foundReturn) { + throw new Error(`Expected method to have a return statement`); + } + return { ...method, body: F.createBlock(newBodyStatements, true) }; + } + + defaultResolverMethod(methodName: string): ts.MethodDeclaration { + this.ts.import("graphql", [{ name: "defaultFieldResolver" }]); + return this.ts.method( + methodName, + RESOLVER_ARGS.map((name) => this.ts.param(name)), + [ + F.createReturnStatement( + F.createCallExpression( + F.createIdentifier("defaultFieldResolver"), + undefined, + RESOLVER_ARGS.map((name) => F.createIdentifier(name)), + ), + ), + ], + ); + } +} + +// Here we try to avoid including unused args. +// +// Unused trailing args are trimmed, unused intermediate args are prefixed with +// an underscore. +function extractUsedParams( + resolverParams: ResolverArgument[], + includeSource: boolean = false, +): string[] { + const wrapperArgs: string[] = []; + + let adding = false; + for (let i = RESOLVER_ARGS.length - 1; i >= 0; i--) { + const name = RESOLVER_ARGS[i]; + const used = + resolverParams.some((param) => { + return ( + (param.kind === "named" && name === "args") || + (param.kind === "argumentsObject" && name === "args") || + (param.kind === "context" && name === "context") || + (param.kind === "information" && name === "info") || + (param.kind === "source" && name === "source") + ); + }) || + (name === "source" && includeSource); + + if (used) { + adding = true; + } + if (!adding) { + continue; + } + + wrapperArgs.unshift(used ? name : `_${name}`); + } + return wrapperArgs; +} + +function fieldDirective( + field: GraphQLField, + name: string, +): ConstDirectiveNode | null { + return field.astNode?.directives?.find((d) => d.name.value === name) ?? null; +} + +function formatResolverFunctionVarName( + parentTypeName: string, + fieldName: string, +): string { + const parent = parentTypeName[0].toLowerCase() + parentTypeName.slice(1); + const field = fieldName[0].toUpperCase() + fieldName.slice(1); + return `${parent}${field}Resolver`; +} diff --git a/src/codegen/resolverMapCodegen.ts b/src/codegen/resolverMapCodegen.ts new file mode 100644 index 00000000..055874cf --- /dev/null +++ b/src/codegen/resolverMapCodegen.ts @@ -0,0 +1,110 @@ +import * as ts from "typescript"; +import { GratsConfig } from "../gratsConfig"; +import TSAstBuilder from "./TSAstBuilder"; +import ResolverCodegen from "./resolverCodegen"; +import { Metadata, FieldDefinition } from "../metadata"; +import { GraphQLObjectType, GraphQLSchema } from "graphql"; + +const F = ts.factory; + +/** + * EXPERIMENTAL! + * + * Codegen for a GraphQL Tools style resolver map. This is an alternative to + * generating a GraphQLSchema directly. This is mostly provided as an example + * and the goal is that eventually it should be possible to generate this output + * in userland. + * + * https://the-guild.dev/graphql/tools/docs/resolvers#resolver-map + */ +export function resolverMapCodegen( + schema: GraphQLSchema, + resolvers: Metadata, + config: GratsConfig, + destination: string, +): string { + const codegen = new Codegen(schema, resolvers, config, destination); + + codegen.resolverMapExport(); + + return codegen.ts.print(); +} + +class Codegen { + ts: TSAstBuilder; + resolvers: ResolverCodegen; + + constructor( + public _schema: GraphQLSchema, + public _resolvers: Metadata, + config: GratsConfig, + destination: string, + ) { + this.ts = new TSAstBuilder(destination, config.importModuleSpecifierEnding); + this.resolvers = new ResolverCodegen(this.ts, _resolvers); + } + + resolverMapExport(): void { + // I'm not crazy about this. One of Grats' design goals is to be as tightly + // coupled to just TypeScript and GraphQL JS. Ideally we would not do + // _anything_ coupled to other libraries but instead provide a way for users + // to do this themselves. + this.ts.import("@graphql-tools/utils", [{ name: "IResolvers" }]); + this.ts.functionDeclaration( + "getResolverMap", + [F.createModifier(ts.SyntaxKind.ExportKeyword)], + F.createTypeReferenceNode("IResolvers"), + this.ts.createBlockWithScope(() => { + this.ts.addStatement(F.createReturnStatement(this.resolverMap())); + }), + ); + } + + resolverMap(): ts.ObjectLiteralExpression { + return this.ts.objectLiteral(this.types()); + } + + types(): ts.ObjectLiteralElementLike[] { + const types: ts.ObjectLiteralElementLike[] = []; + for (const [typeName, fields] of Object.entries(this._resolvers.types)) { + const resolverMethods = this.resolversMethods(typeName, fields); + if (resolverMethods.length > 0) { + types.push( + F.createPropertyAssignment( + typeName, + this.ts.objectLiteral(resolverMethods), + ), + ); + } + } + return types; + } + + resolversMethods( + typeName: string, + fieldDefinitions: Record, + ): ts.ObjectLiteralElementLike[] { + const graphQLType = this._schema.getType(typeName); + if (!(graphQLType instanceof GraphQLObjectType)) { + throw new Error(`Type ${typeName} is not an object type`); + } + const fields: ts.ObjectLiteralElementLike[] = []; + for (const fieldName of Object.keys(fieldDefinitions)) { + const method = this.resolvers.resolveMethod( + fieldName, + fieldName, + typeName, + ); + const wrapped = this.resolvers.maybeApplySemanticNullRuntimeCheck( + graphQLType.getFields()[fieldName], + method, + fieldName, + ); + if (wrapped != null) { + fields.push(wrapped); + } + } + + return fields; + } +} diff --git a/src/codegen.ts b/src/codegen/schemaCodegen.ts similarity index 60% rename from src/codegen.ts rename to src/codegen/schemaCodegen.ts index 9b64f163..a67502b7 100644 --- a/src/codegen.ts +++ b/src/codegen/schemaCodegen.ts @@ -1,5 +1,4 @@ import { - ConstDirectiveNode, GraphQLAbstractType, GraphQLArgument, GraphQLEnumType, @@ -26,24 +25,12 @@ import { isUnionType, } from "graphql"; import * as ts from "typescript"; -import * as path from "path"; -import { - FIELD_METADATA_DIRECTIVE, - FieldParam, - parseFieldMetadataDirective, - ResolvedResolverParam, -} from "./metadataDirectives"; -import { resolveRelativePath } from "./gratsRoot"; -import { SEMANTIC_NON_NULL_DIRECTIVE } from "./publicDirectives"; -import { - ASSERT_NON_NULL_HELPER, - createAssertNonNullHelper, -} from "./codegenHelpers"; -import { extend, nullThrows } from "./utils/helpers"; -import { GratsConfig } from "./gratsConfig.js"; -import { naturalCompare } from "./utils/naturalCompare"; - -const RESOLVER_ARGS: FieldParam[] = ["source", "args", "context", "info"]; +import { extend, nullThrows } from "../utils/helpers"; +import { GratsConfig } from "../gratsConfig.js"; +import { naturalCompare } from "../utils/naturalCompare"; +import TSAstBuilder from "./TSAstBuilder"; +import ResolverCodegen from "./resolverCodegen"; +import { Metadata } from "../metadata"; const F = ts.factory; @@ -51,10 +38,11 @@ const F = ts.factory; // GraphQLSchema implementing that schema. export function codegen( schema: GraphQLSchema, + resolvers: Metadata, config: GratsConfig, destination: string, ): string { - const codegen = new Codegen(schema, config, destination); + const codegen = new Codegen(schema, resolvers, config, destination); codegen.schemaDeclarationExport(); @@ -62,26 +50,20 @@ export function codegen( } class Codegen { - _imports: ts.Statement[] = []; + ts: TSAstBuilder; + resolvers: ResolverCodegen; _typeNameMappings: Map = new Map(); - _helpers: Map = new Map(); _typeDefinitions: Set = new Set(); _graphQLImports: Set = new Set(); - _statements: ts.Statement[] = []; constructor( public _schema: GraphQLSchema, - public _config: GratsConfig, - public _destination: string, - ) {} - - createBlockWithScope(closure: () => void): ts.Block { - const initialStatements = this._statements; - this._statements = []; - closure(); - const block = F.createBlock(this._statements, true); - this._statements = initialStatements; - return block; + _resolvers: Metadata, + config: GratsConfig, + destination: string, + ) { + this.ts = new TSAstBuilder(destination, config.importModuleSpecifierEnding); + this.resolvers = new ResolverCodegen(this.ts, _resolvers); } graphQLImport(name: string): ts.Identifier { @@ -95,12 +77,12 @@ class Codegen { } schemaDeclarationExport(): void { - this.functionDeclaration( + this.ts.functionDeclaration( "getSchema", [F.createModifier(ts.SyntaxKind.ExportKeyword)], this.graphQLTypeImport("GraphQLSchema"), - this.createBlockWithScope(() => { - this._statements.push( + this.ts.createBlockWithScope(() => { + this.ts.addStatement( F.createReturnStatement( F.createNewExpression( this.graphQLImport("GraphQLSchema"), @@ -114,7 +96,7 @@ class Codegen { } schemaConfig(): ts.ObjectLiteralExpression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(this._schema.description), this.query(), this.mutation(), @@ -195,7 +177,7 @@ class Codegen { if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLObjectType"), @@ -211,7 +193,7 @@ class Codegen { } objectTypeConfig(obj: GraphQLObjectType): ts.ObjectLiteralExpression { - return this.objectLiteral([ + return this.ts.objectLiteral([ F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), this.description(obj.description), this.fields(obj, false), @@ -219,213 +201,6 @@ class Codegen { ]); } - importUserConstruct( - tsModulePath: string, - exportName: string | null, - localName: string, - ): void { - const abs = resolveRelativePath(tsModulePath); - const relative = replaceExt( - path.relative(path.dirname(this._destination), abs), - this._config.importModuleSpecifierEnding, - ); - const modulePath = `./${normalizeRelativePathToPosix(relative)}`; - if (exportName == null) { - this.importDefault(modulePath, localName); - } else { - this.import(modulePath, [{ name: exportName, as: localName }]); - } - } - - resolveMethod( - field: GraphQLField, - methodName: string, - parentTypeName: string, - ): ts.MethodDeclaration | null { - const metadataDirective = nullThrows( - fieldDirective(field, FIELD_METADATA_DIRECTIVE), - ); - const metadata = parseFieldMetadataDirective(metadataDirective); - const fieldAst = nullThrows(field.astNode); - const resolverParams: ResolvedResolverParam[] | undefined = - fieldAst.resolverParams?.map((param) => { - switch (param.kind) { - case "named": - case "positionalArg": - return param; - case "unresolved": - throw new Error("Expected resolver param to have been resolved."); - default: { - const _exhaustive: never = param; - throw new Error("Unexpected param kind"); - } - } - }); - - if (metadata.tsModulePath != null) { - // Note: This name is guaranteed to be unique, but for static methods, it - // means we import the same class multiple times with multiple names. - const resolverName = formatResolverFunctionVarName( - parentTypeName, - field.name, - ); - - this.importUserConstruct( - metadata.tsModulePath, - metadata.exportName, - resolverName, - ); - - let resolverAccess: ts.Expression = F.createIdentifier(resolverName); - - if (metadata.name != null) { - resolverAccess = F.createPropertyAccessExpression( - resolverAccess, - F.createIdentifier(metadata.name), - ); - } - - // Params expected by the user-defined resolver function. - const usedResolverParams = resolverParams ?? []; - - // Params passed to the default resolver function. - const wrapperParams: string[] = extractUsedParams(usedResolverParams); - - return this.method( - methodName, - wrapperParams.map((name) => { - return this.param(name); - }), - [ - F.createReturnStatement( - F.createCallExpression( - resolverAccess, - undefined, - usedResolverParams.map((name) => { - return this.resolverParam(name); - }), - ), - ), - ], - ); - } - const defaultParams = - resolverParams == null || paramsAreInDefaultOrder(resolverParams); - if (metadata.name != null || !defaultParams) { - const prop = F.createPropertyAccessExpression( - F.createIdentifier("source"), - F.createIdentifier(metadata.name ?? field.name), - ); - - let valueExpression: ts.Expression = prop; - - if (resolverParams != null) { - valueExpression = F.createCallExpression( - prop, - undefined, - resolverParams.map((name) => { - return this.resolverParam(name); - }), - ); - } - - const usedWrapperParams: ResolvedResolverParam[] = [ - { kind: "named", name: "source" }, - ]; - if (resolverParams != null) { - // Push with ... is safe because resolverParams is known to be - // a small array. - usedWrapperParams.push(...resolverParams); - } - - return this.method( - methodName, - extractUsedParams(usedWrapperParams).map((name) => this.param(name)), - [F.createReturnStatement(valueExpression)], - ); - } - - // If the resolver name matches the field name, and the field is not backed by a function, - // we can just use the default resolver. - return null; - } - - // Either `args`, `context`, `info`, or a positional argument like - // `args.someArg`. - resolverParam(param: ResolvedResolverParam): ts.Expression { - switch (param.kind) { - case "named": - return F.createIdentifier(param.name); - case "positionalArg": - return F.createPropertyAccessExpression( - F.createIdentifier("args"), - F.createIdentifier(param.inputDefinition.name.value), - ); - default: { - const _exhaustive: never = param; - throw new Error("Unexpected param kind"); - } - } - } - - // If a field is smantically non-null, we need to wrap the resolver in a - // runtime check to ensure that the resolver does not return null. - maybeApplySemanticNullRuntimeCheck( - field: GraphQLField, - method_: ts.MethodDeclaration | null, - ): ts.MethodDeclaration | null { - const semanticNonNull = fieldDirective(field, SEMANTIC_NON_NULL_DIRECTIVE); - if (semanticNonNull == null) { - return method_; - } - - if (!this._helpers.has(ASSERT_NON_NULL_HELPER)) { - this._helpers.set(ASSERT_NON_NULL_HELPER, createAssertNonNullHelper()); - } - - const method = method_ ?? this.defaultResolverMethod(); - - const bodyStatements = method.body?.statements; - if (bodyStatements == null || bodyStatements.length === 0) { - throw new Error(`Expected method to have a body`); - } - let foundReturn = false; - const newBodyStatements = bodyStatements.map((statement) => { - if (ts.isReturnStatement(statement)) { - foundReturn = true; - // We need to wrap the return statement in a call to the runtime check - return F.createReturnStatement( - F.createCallExpression( - F.createIdentifier(ASSERT_NON_NULL_HELPER), - [], - [nullThrows(statement.expression)], - ), - ); - } - return statement; - }); - if (!foundReturn) { - throw new Error(`Expected method to have a return statement`); - } - return { ...method, body: F.createBlock(newBodyStatements, true) }; - } - - defaultResolverMethod(): ts.MethodDeclaration { - return this.method( - "resolve", - RESOLVER_ARGS.map((name) => this.param(name)), - [ - F.createReturnStatement( - F.createCallExpression( - this.graphQLImport("defaultFieldResolver"), - undefined, - RESOLVER_ARGS.map((name) => F.createIdentifier(name)), - ), - ), - ], - ); - } - fields( obj: GraphQLObjectType | GraphQLInterfaceType, isInterface: boolean, @@ -437,10 +212,10 @@ class Codegen { ); }); - return this.method( + return this.ts.method( "fields", [], - [F.createReturnStatement(this.objectLiteral(fields))], + [F.createReturnStatement(this.ts.objectLiteral(fields))], ); } @@ -449,7 +224,7 @@ class Codegen { ): ts.MethodDeclaration | null { const interfaces = obj.getInterfaces(); if (!interfaces.length) return null; - return this.method( + return this.ts.method( "interfaces", [], [ @@ -466,7 +241,7 @@ class Codegen { const varName = `${obj.name}Type`; if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLInterfaceType"), @@ -483,7 +258,7 @@ class Codegen { interfaceTypeConfig(obj: GraphQLInterfaceType): ts.ObjectLiteralExpression { this._schema.getPossibleTypes(obj); - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(obj.description), F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), this.fields(obj, true), @@ -496,7 +271,7 @@ class Codegen { const varName = `${obj.name}Type`; if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLUnionType"), @@ -523,7 +298,7 @@ class Codegen { if (exportedMetadata != null) { if (!this._typeNameMappings.has(t.name)) { const localName = `${t.name}Class`; - this.importUserConstruct( + this.ts.importUserConstruct( exportedMetadata.tsModulePath, exportedMetadata.exportName, localName, @@ -542,10 +317,10 @@ class Codegen { } unionTypeConfig(obj: GraphQLUnionType): ts.ObjectLiteralExpression { - return this.objectLiteral([ + return this.ts.objectLiteral([ F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), this.description(obj.description), - this.method( + this.ts.method( "types", [], [ @@ -564,7 +339,7 @@ class Codegen { const varName = `${obj.name}Type`; if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLScalarType"), @@ -580,7 +355,7 @@ class Codegen { } customScalarTypeConfig(obj: GraphQLScalarType): ts.ObjectLiteralExpression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(obj.description), F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), ]); @@ -590,7 +365,7 @@ class Codegen { const varName = `${obj.name}Type`; if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLInputObjectType"), @@ -614,7 +389,7 @@ class Codegen { if (obj.isOneOf) { properties.push(F.createPropertyAssignment("isOneOf", F.createTrue())); } - return this.objectLiteral(properties); + return this.ts.objectLiteral(properties); } inputFields(obj: GraphQLInputObjectType): ts.MethodDeclaration { @@ -622,15 +397,15 @@ class Codegen { return F.createPropertyAssignment(name, this.inputFieldConfig(field)); }); - return this.method( + return this.ts.method( "fields", [], - [F.createReturnStatement(this.objectLiteral(fields))], + [F.createReturnStatement(this.ts.objectLiteral(fields))], ); } inputFieldConfig(field: GraphQLInputField): ts.Expression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(field.description), this.deprecated(field), F.createPropertyAssignment("name", F.createStringLiteral(field.name)), @@ -657,7 +432,7 @@ class Codegen { extend(props, this.fieldMethods(field, parentTypeName)); } - return this.objectLiteral(props); + return this.ts.objectLiteral(props); } fieldMethods( @@ -667,26 +442,37 @@ class Codegen { // Note: We assume the default name is used here. When custom operation types are supported // we'll need to update this. if (parentTypeName !== "Subscription") { - const resolve = this.resolveMethod(field, "resolve", parentTypeName); - return [this.maybeApplySemanticNullRuntimeCheck(field, resolve)]; + const resolve = this.resolvers.resolveMethod( + field.name, + "resolve", + parentTypeName, + ); + return [ + this.resolvers.maybeApplySemanticNullRuntimeCheck( + field, + resolve, + "resolve", + ), + ]; } return [ // TODO: Maybe avoid adding `assertNonNull` for subscription resolvers? - this.resolveMethod(field, "subscribe", parentTypeName), + this.resolvers.resolveMethod(field.name, "subscribe", parentTypeName), // Identity function (method?) - this.maybeApplySemanticNullRuntimeCheck( + this.resolvers.maybeApplySemanticNullRuntimeCheck( field, - this.method( + this.ts.method( "resolve", - [this.param("payload")], + [this.ts.param("payload")], [F.createReturnStatement(F.createIdentifier("payload"))], ), + "resolve", ), ]; } argMap(args: ReadonlyArray): ts.ObjectLiteralExpression { - return this.objectLiteral( + return this.ts.objectLiteral( args.map((arg) => F.createPropertyAssignment(arg.name, this.argConfig(arg)), ), @@ -694,7 +480,7 @@ class Codegen { } argConfig(arg: GraphQLArgument): ts.Expression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(arg.description), this.deprecated(arg), F.createPropertyAssignment("name", F.createStringLiteral(arg.name)), @@ -715,7 +501,7 @@ class Codegen { const varName = `${obj.name}Type`; if (!this._typeDefinitions.has(varName)) { this._typeDefinitions.add(varName); - this.constDeclaration( + this.ts.constDeclaration( varName, F.createNewExpression( this.graphQLImport("GraphQLEnumType"), @@ -731,7 +517,7 @@ class Codegen { } enumTypeConfig(obj: GraphQLEnumType): ts.ObjectLiteralExpression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(obj.description), F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), this.enumValues(obj), @@ -743,11 +529,11 @@ class Codegen { return F.createPropertyAssignment(value.name, this.enumValue(value)); }); - return F.createPropertyAssignment("values", this.objectLiteral(values)); + return F.createPropertyAssignment("values", this.ts.objectLiteral(values)); } enumValue(obj: GraphQLEnumValue): ts.Expression { - return this.objectLiteral([ + return this.ts.objectLiteral([ this.description(obj.description), this.deprecated(obj), F.createPropertyAssignment("value", F.createStringLiteral(obj.name)), @@ -770,7 +556,7 @@ class Codegen { value.map((v) => this.defaultValue(v)), ); } else { - return this.objectLiteral( + return this.ts.objectLiteral( Object.entries(value).map(([k, v]) => F.createPropertyAssignment(k, this.defaultValue(v)), ), @@ -828,124 +614,6 @@ class Codegen { } } - constDeclaration( - name: string, - initializer: ts.Expression, - type?: ts.TypeNode, - ): void { - this._statements.push( - F.createVariableStatement( - undefined, - F.createVariableDeclarationList( - [ - F.createVariableDeclaration( - F.createIdentifier(name), - undefined, - type, - initializer, - ), - ], - ts.NodeFlags.Const, - ), - ), - ); - } - - functionDeclaration( - name: string, - modifiers: ts.Modifier[] | undefined, - type: ts.TypeNode | undefined, - body: ts.Block, - ): void { - this._statements.push( - F.createFunctionDeclaration( - modifiers, - undefined, - name, - undefined, - [], - type, - body, - ), - ); - } - - // Helper to allow for nullable elements. - objectLiteral( - properties: Array, - ): ts.ObjectLiteralExpression { - return F.createObjectLiteralExpression(properties.filter(isNonNull), true); - } - - // Helper for the common case. - method( - name: string, - params: ts.ParameterDeclaration[], - statements: ts.Statement[], - ): ts.MethodDeclaration { - return F.createMethodDeclaration( - undefined, - undefined, - name, - undefined, - undefined, - params, - undefined, - F.createBlock(statements, true), - ); - } - - // Helper for the common case of a single string argument. - param(name: string, type?: ts.TypeNode): ts.ParameterDeclaration { - return F.createParameterDeclaration( - undefined, - undefined, - name, - undefined, - type, - undefined, - ); - } - - import(from: string, names: { name: string; as?: string }[]) { - const namedImports = names.map((name) => { - if (name.as) { - return F.createImportSpecifier( - false, - F.createIdentifier(name.name), - F.createIdentifier(name.as), - ); - } else { - return F.createImportSpecifier( - false, - undefined, - F.createIdentifier(name.name), - ); - } - }); - this._imports.push( - F.createImportDeclaration( - undefined, - F.createImportClause( - false, - undefined, - F.createNamedImports(namedImports), - ), - F.createStringLiteral(from), - ), - ); - } - - importDefault(from: string, as: string) { - this._imports.push( - F.createImportDeclaration( - undefined, - F.createImportClause(false, F.createIdentifier(as), undefined), - F.createStringLiteral(from), - ), - ); - } - resolveTypeFunctionDeclaration(): ts.FunctionDeclaration { return F.createFunctionDeclaration( undefined, @@ -1078,22 +746,13 @@ class Codegen { } print(): string { - const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); - const sourceFile = ts.createSourceFile( - "tempFile.ts", - "", - ts.ScriptTarget.Latest, - false, - ts.ScriptKind.TS, - ); - - this.import( + this.ts.import( "graphql", [...this._graphQLImports].map((name) => ({ name })), ); if (this._typeNameMappings.size > 0) { - this._statements.push( + this.ts.addStatement( F.createVariableStatement( undefined, F.createVariableDeclarationList( @@ -1116,7 +775,7 @@ class Codegen { ); for (const [typeName, className] of typeNameEntries) { - this._statements.push( + this.ts.addStatement( F.createExpressionStatement( F.createCallExpression( F.createPropertyAccessExpression( @@ -1129,83 +788,9 @@ class Codegen { ), ); } - this._statements.push(this.resolveTypeFunctionDeclaration()); + this.ts.addStatement(this.resolveTypeFunctionDeclaration()); } - return printer.printList( - ts.ListFormat.MultiLine, - F.createNodeArray([ - ...this._imports, - ...this._helpers.values(), - ...this._statements, - ]), - sourceFile, - ); + return this.ts.print(); } } - -// Here we try to avoid including unused args. -// -// Unused trailing args are trimmed, unused intermediate args are prefixed with -// an underscore. -function extractUsedParams(resolverParams: ResolvedResolverParam[]): string[] { - const wrapperArgs: string[] = []; - - let adding = false; - for (let i = RESOLVER_ARGS.length - 1; i >= 0; i--) { - const name = RESOLVER_ARGS[i]; - const used = resolverParams.some((param) => { - return ( - (param.kind === "named" && param.name === name) || - (param.kind === "positionalArg" && name) === "args" - ); - }); - if (used) { - adding = true; - } - if (!adding) { - continue; - } - - wrapperArgs.unshift(used ? name : `_${name}`); - } - return wrapperArgs; -} - -function paramsAreInDefaultOrder(params: ResolvedResolverParam[]) { - return params.every((param, i) => { - return param.kind === "named" && param.name === RESOLVER_ARGS[i + 1]; - }); -} - -function fieldDirective( - field: GraphQLField, - name: string, -): ConstDirectiveNode | null { - return field.astNode?.directives?.find((d) => d.name.value === name) ?? null; -} - -function replaceExt(filePath: string, newSuffix: string): string { - const ext = path.extname(filePath); - return filePath.slice(0, -ext.length) + newSuffix; -} - -// Predicate function for filtering out null values -// Includes TypeScript refinement for narrowing the type -function isNonNull(value: T | null | undefined): value is T { - return value != null; -} - -function formatResolverFunctionVarName( - parentTypeName: string, - fieldName: string, -): string { - const parent = parentTypeName[0].toLowerCase() + parentTypeName.slice(1); - const field = fieldName[0].toUpperCase() + fieldName.slice(1); - return `${parent}${field}Resolver`; -} - -// https://github.com/sindresorhus/slash/blob/98b618f5a3bfcb5dd374b204868818845b87bb2f/index.js#L8C9-L8C33 -function normalizeRelativePathToPosix(unknownPath: string): string { - return unknownPath.replace(/\\/g, "/"); -} diff --git a/src/gratsConfig.ts b/src/gratsConfig.ts index 1a8e4297..c5019e2f 100644 --- a/src/gratsConfig.ts +++ b/src/gratsConfig.ts @@ -52,6 +52,17 @@ export type GratsConfig = { // file extension. In TypeScript code this generally means import paths must end // with `.js`. If set to null, no ending will be appended. importModuleSpecifierEnding: string; // Defaults to no ending, or "" + + // EXPERIMENTAL: THIS OPTION WILL BE RENAMED OR REMOVED IN A FUTURE RELEASE + // Emit a JSON file alongside the generated schema file which contains the + // metadata containing information about the resolvers. + EXPERIMENTAL__emitMetadata: boolean; // Default: false + + // EXPERIMENTAL: THIS OPTION WILL BE RENAMED OR REMOVED IN A FUTURE RELEASE + // Instead of emitting a TypeScript file which creates a GraphQLSchema, emit + // a TypeScript file which creates a GraphQL Tools style Resolver Map. + // https://the-guild.dev/graphql/tools/docs/resolvers#resolver-map + EXPERIMENTAL__emitResolverMap: boolean; // Default: false }; export type ParsedCommandLineGrats = Omit & { @@ -77,6 +88,8 @@ const VALID_CONFIG_KEYS = new Set([ "schemaHeader", "tsSchemaHeader", "importModuleSpecifierEnding", + "EXPERIMENTAL__emitMetadata", + "EXPERIMENTAL__emitResolverMap", ]); // TODO: Make this return diagnostics @@ -191,6 +204,30 @@ export function validateGratsOptions( ); } + if (gratsOptions.EXPERIMENTAL__emitMetadata === undefined) { + gratsOptions.EXPERIMENTAL__emitMetadata = false; + } else if (typeof gratsOptions.EXPERIMENTAL__emitMetadata !== "boolean") { + throw new Error( + "Grats: The Grats config option `EXPERIMENTAL__emitMetadata` must be a boolean if provided.", + ); + } else { + console.warn( + "Grats: The `EXPERIMENTAL__emitMetadata` option is experimental and will be renamed or removed in a future release.", + ); + } + + if (gratsOptions.EXPERIMENTAL__emitResolverMap === undefined) { + gratsOptions.EXPERIMENTAL__emitResolverMap = false; + } else if (typeof gratsOptions.EXPERIMENTAL__emitResolverMap !== "boolean") { + throw new Error( + "Grats: The Grats config option `EXPERIMENTAL__emitResolverMap` must be a boolean if provided.", + ); + } else { + console.warn( + "Grats: The `EXPERIMENTAL__emitResolverMap` option is experimental and will be renamed or removed in a future release.", + ); + } + return { ...options, raw: { ...options.raw, grats: gratsOptions }, diff --git a/src/index.ts b/src/index.ts index 8d833c20..1c0605a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ export * from "./Types"; export * from "./lib"; // Used by the experimental TypeScript plugin export { extract } from "./Extractor"; -export { codegen } from "./codegen"; +export { codegen } from "./codegen/schemaCodegen"; // #FIXME: Report diagnostics instead of throwing! export function getParsedTsConfig( diff --git a/src/lib.ts b/src/lib.ts index cfb3f5ce..11a3f60c 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -21,7 +21,6 @@ import { ParsedCommandLineGrats } from "./gratsConfig"; import { validateTypenames } from "./validations/validateTypenames"; import { extractSnapshotsFromProgram } from "./transforms/snapshotsFromProgram"; import { validateMergedInterfaces } from "./validations/validateMergedInterfaces"; -import { addMetadataDirectives } from "./metadataDirectives"; import { addInterfaceFields } from "./transforms/addInterfaceFields"; import { filterNonGqlInterfaces } from "./transforms/filterNonGqlInterfaces"; import { validateAsyncIterable } from "./validations/validateAsyncIterable"; @@ -33,6 +32,8 @@ import { validateSemanticNullability } from "./validations/validateSemanticNulla import { resolveTypes } from "./transforms/resolveTypes"; import { resolveResolverParams } from "./transforms/resolveResolverParams"; import { customSpecValidations } from "./validations/customSpecValidations"; +import { makeResolverSignature } from "./transforms/makeResolverSignature"; +import { Metadata } from "./metadata"; // Export the TypeScript plugin implementation used by // grats-ts-plugin @@ -43,6 +44,7 @@ export { GratsConfig } from "./gratsConfig"; export type SchemaAndDoc = { schema: GraphQLSchema; doc: DocumentNode; + resolvers: Metadata; }; // Construct a schema, using GraphQL schema language @@ -96,11 +98,8 @@ export function extractSchemaAndDoc( ); const docResult = new ResultPipe(validationResult) - // Add the metadata directive definitions to definitions - // found in the snapshot. - .map(() => addMetadataDirectives(snapshot.definitions)) // Filter out any `implements` clauses that are not GraphQL interfaces. - .map((definitions) => filterNonGqlInterfaces(ctx, definitions)) + .map(() => filterNonGqlInterfaces(ctx, snapshot.definitions)) .andThen((definitions) => resolveResolverParams(ctx, definitions)) .andThen((definitions) => resolveTypes(ctx, definitions)) // If you define a field on an interface using the functional style, we need to add @@ -127,6 +126,7 @@ export function extractSchemaAndDoc( return docResult; } const doc = docResult.value; + const resolvers = makeResolverSignature(doc); // Build and validate the schema with regards to the GraphQL spec. return ( @@ -136,7 +136,7 @@ export function extractSchemaAndDoc( .andThen((schema) => validateTypenames(schema, typesWithTypename)) .andThen((schema) => validateSemanticNullability(schema, config)) // Combine the schema and document into a single result. - .map((schema) => ({ schema, doc })) + .map((schema) => ({ schema, doc, resolvers })) .result() ); }) diff --git a/src/metadata.ts b/src/metadata.ts new file mode 100644 index 00000000..3969a610 --- /dev/null +++ b/src/metadata.ts @@ -0,0 +1,117 @@ +/** + * Grats extracts a GraphQL schema from your TypeScript source code, but it also + * infers additional non-Schema information, such as the signature of your + * resolves. + * + * In order to allow external tools to make use of Grats' analysis, we are + * are EXPERIMENTING with exposing the result of Grats' analysis as JSON. This + * file contains the TypeScript types describing that shape. + * + * In the future we may formalize this more with things like JSON schema, but + * for now we are just using TypeScript types. + */ + +// Note: An example of replacing codegen with a dynamic default resolve powered +// by this JSON schema: +// https://gist.github.com/captbaritone/f66d0355645a32494da368d0448b9d7a + +/** Metadata for the full schema */ +export type Metadata = { + /** Types in the schema */ + types: Record>; +}; + +/** A GraphQL object type definition */ +export type ObjectTypeDefinition = { + kind: "object"; + fields: Record; +}; + +/** A GraphQL field */ +export type FieldDefinition = { + resolver: ResolverDefinition; +}; + +/** + * Information about the resolver for this field. Should be sufficient to either + * dynamically invoke the resolver at runtime (inefficiently) or codegen JavaScript + * to define the resolver function. + */ +export type ResolverDefinition = + | PropertyResolver + | FunctionResolver + | MethodResolver + | StaticMethodResolver; + +/** + * A field which is simply backed by a property (or getter) on the source object + */ +export type PropertyResolver = { + kind: "property"; + name: string | null; // If omitted the field name is the same as the property name +}; + +/** + * A field which is backed by a function exported from a module. This is the + * most flexible kind of resolver. + */ +export type FunctionResolver = { + kind: "function"; + path: string; // Path to the module + exportName: string | null; // Name of the export. If omitted the function is the default export. + arguments: ResolverArgument[] | null; +}; + +/** + * A field which is backed by a method on the source object + */ +export type MethodResolver = { + kind: "method"; + name: string | null; // Method name. If omitted, the method name is the same as the field name + arguments: ResolverArgument[] | null; +}; + +/** + * A field which is backed by a static method on a class exported from a module + */ +export type StaticMethodResolver = { + kind: "staticMethod"; + path: string; // Path to the module + exportName: string | null; // Export name. If omitted, the class is the default export + name: string; // Method name + arguments: ResolverArgument[] | null; +}; + +/** An argument expected by a resolver function or method */ +export type ResolverArgument = + | SourceArgument + | ArgumentsObjectArgument + | ContextArgument + | InformationArgument + | NamedArgument; + +/** The source or parent object */ +export type SourceArgument = { + kind: "source"; +}; + +/** An arguments object containing all the field arguments */ +export type ArgumentsObjectArgument = { + kind: "argumentsObject"; +}; + +/** The GraphQL context */ +export type ContextArgument = { + kind: "context"; +}; + +/** The GraphQL info object */ +export type InformationArgument = { + kind: "information"; +}; + +/** A single named GraphQL argument */ +export type NamedArgument = { + kind: "named"; + name: string; // Name of the GraphQL field argument +}; diff --git a/src/metadataDirectives.ts b/src/metadataDirectives.ts deleted file mode 100644 index 679e3da0..00000000 --- a/src/metadataDirectives.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as ts from "typescript"; -import { - ConstDirectiveNode, - ConstValueNode, - DefinitionNode, - DocumentNode, - InputValueDefinitionNode, - Kind, - Location, - NameNode, - parse, - StringValueNode, - TypeNode, -} from "graphql"; -import { uniqueId } from "./utils/helpers"; -import { DiagnosticResult } from "./utils/DiagnosticError.js"; - -/** - * In most cases we can use directives to annotate constructs - * however, it't not possible to annotate an individual TypeNode. - * Additionally, we can't use sets or maps to "tag" nodes because - * there are places where we immutably update the AST to make changes. - * - * Instead, we cheat and add properties to some nodes. These types use - * interface merging to add our own properties to the AST. - * - * We try to use this approach sparingly. - */ -declare module "graphql" { - export interface ListTypeNode { - /** - * Grats metadata: Whether the list type was defined as an AsyncIterable. - * Used to ensure that all fields on `Subscription` return an AsyncIterable. - */ - isAsyncIterable?: boolean; - } - export interface NameNode { - /** - * Grats metadata: A unique identifier for the node. Used to track - * data about nodes in lookup data structures. - */ - tsIdentifier: number; - } - export interface ObjectTypeDefinitionNode { - /** - * Grats metadata: Indicates that the type was materialized as part of - * generic type resolution. - */ - wasSynthesized?: boolean; - hasTypeNameField: boolean; - exported?: { - tsModulePath: string; - exportName: string | null; - }; - } - export interface UnionTypeDefinitionNode { - /** - * Grats metadata: Indicates that the type was materialized as part of - * generic type resolution. - */ - wasSynthesized?: boolean; - } - export interface InterfaceTypeDefinitionNode { - /** - * Grats metadata: Indicates that the type was materialized as part of - * generic type resolution. - */ - wasSynthesized?: boolean; - } - export interface ObjectTypeExtensionNode { - /** - * Grats metadata: Indicates that we don't know yet if this is extending an interface - * or a type. - */ - mayBeInterface?: boolean; - } - export interface FieldDefinitionNode { - /** - * Grats metadata: Indicates the params expected by the field resolver and their order - */ - resolverParams?: UnresolvedResolverParam[]; - } -} - -/** - * At extraction time we don't know if a resolver arg is context, info, or a - * positional GraphQL argument. If it's a positional argument, we need to ensure - * it has a valid name. If it's just info or context, it's fine if it doesn't - * have a name e.g. (destructured). - */ -export interface InputValueDefinitionNodeOrResolverArg { - readonly kind: Kind.INPUT_VALUE_DEFINITION; - readonly loc: Location; - readonly description?: StringValueNode; - // This is the only property that is different. - readonly name: DiagnosticResult; - readonly type: TypeNode; - readonly defaultValue?: ConstValueNode; - readonly directives?: ReadonlyArray; -} - -export type UnresolvedResolverParam = - | NamedFieldParam - | PositionalFieldParam - | Unresolved; - -export type ResolvedResolverParam = NamedFieldParam | PositionalFieldParam; - -export type NamedFieldParam = { - kind: "named"; - // Used by diagnostics to point to the source of the param - sourceNode?: ts.Node; - name: FieldParam; -}; -export type PositionalFieldParam = { - kind: "positionalArg"; - inputDefinition: InputValueDefinitionNode; -}; -export type Unresolved = { - kind: "unresolved"; - inputDefinition: InputValueDefinitionNodeOrResolverArg; -}; - -export type FieldParam = "source" | "args" | "context" | "info"; - -export const FIELD_METADATA_DIRECTIVE = "metadata"; -export const EXPORT_NAME_ARG = "exportName"; -export const FIELD_NAME_ARG = "name"; -export const TS_MODULE_PATH_ARG = "tsModulePath"; -export const ASYNC_ITERABLE_ARG = "asyncIterable"; - -export const KILLS_PARENT_ON_EXCEPTION_DIRECTIVE = "killsParentOnException"; - -export const METADATA_DIRECTIVE_NAMES = new Set([ - FIELD_METADATA_DIRECTIVE, - KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, -]); - -export const DIRECTIVES_AST: DocumentNode = parse(` - directive @${FIELD_METADATA_DIRECTIVE}( - """ - Name of property/method/function. Defaults to field name. - """ - ${FIELD_NAME_ARG}: String - """ - Path of the TypeScript module to import if the field is a function. - """ - ${TS_MODULE_PATH_ARG}: String - """ - Export name of the field. For function fields this is the exported function name, - for static method fields, this is the exported class name. - """ - ${EXPORT_NAME_ARG}: String - ) on FIELD_DEFINITION - directive @${KILLS_PARENT_ON_EXCEPTION_DIRECTIVE} on FIELD_DEFINITION -`); - -export function addMetadataDirectives( - definitions: Array, -): Array { - return [...DIRECTIVES_AST.definitions, ...definitions]; -} - -export type FieldMetadata = { - tsModulePath: string | null; - name: string | null; - exportName: string | null; -}; - -export function makeKillsParentOnExceptionDirective( - loc: Location, -): ConstDirectiveNode { - return { - kind: Kind.DIRECTIVE, - loc, - name: { - kind: Kind.NAME, - loc, - value: KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, - tsIdentifier: uniqueId(), - }, - arguments: [], - }; -} - -export function parseFieldMetadataDirective( - directive: ConstDirectiveNode, -): FieldMetadata { - if (directive.name.value !== FIELD_METADATA_DIRECTIVE) { - throw new Error(`Expected directive to be ${FIELD_METADATA_DIRECTIVE}`); - } - - return { - name: getStringArg(directive, FIELD_NAME_ARG), - tsModulePath: getStringArg(directive, TS_MODULE_PATH_ARG), - exportName: getStringArg(directive, EXPORT_NAME_ARG), - }; -} - -function getStringArg( - directive: ConstDirectiveNode, - argName: string, -): string | null { - const arg = directive.arguments?.find((arg) => arg.name.value === argName); - if (!arg) { - return null; - } - - if (arg.value.kind !== Kind.STRING) { - throw new Error(`Expected argument ${argName} to be a string`); - } - - return arg.value.value; -} diff --git a/src/printSchema.ts b/src/printSchema.ts index c3aea7cc..186576dc 100644 --- a/src/printSchema.ts +++ b/src/printSchema.ts @@ -6,8 +6,9 @@ import { specifiedScalarTypes, } from "graphql"; import { GratsConfig } from "./gratsConfig"; -import { codegen } from "./codegen"; -import { METADATA_DIRECTIVE_NAMES } from "./metadataDirectives"; +import { codegen } from "./codegen/schemaCodegen"; +import { Metadata } from "./metadata"; +import { resolverMapCodegen } from "./codegen/resolverMapCodegen"; /** * Prints code for a TypeScript module that exports a GraphQLSchema. @@ -15,10 +16,13 @@ import { METADATA_DIRECTIVE_NAMES } from "./metadataDirectives"; */ export function printExecutableSchema( schema: GraphQLSchema, + resolvers: Metadata, config: GratsConfig, destination: string, ): string { - const code = codegen(schema, config, destination); + const code = config.EXPERIMENTAL__emitResolverMap + ? resolverMapCodegen(schema, resolvers, config, destination) + : codegen(schema, resolvers, config, destination); return applyTypeScriptHeader(config, code); } @@ -44,12 +48,6 @@ export function applySDLHeader(config: GratsConfig, sdl: string): string { export function printSDLWithoutMetadata(doc: DocumentNode): string { const trimmed = visit(doc, { - DirectiveDefinition(t) { - return METADATA_DIRECTIVE_NAMES.has(t.name.value) ? null : t; - }, - Directive(t) { - return METADATA_DIRECTIVE_NAMES.has(t.name.value) ? null : t; - }, ScalarTypeDefinition(t) { return specifiedScalarTypes.some((scalar) => scalar.name === t.name.value) ? null diff --git a/src/resolverSignature.ts b/src/resolverSignature.ts new file mode 100644 index 00000000..34c52a13 --- /dev/null +++ b/src/resolverSignature.ts @@ -0,0 +1,104 @@ +import * as ts from "typescript"; +import { + ConstDirectiveNode, + ConstValueNode, + InputValueDefinitionNode, + Kind, + Location, + NameNode, + StringValueNode, + TypeNode, +} from "graphql"; +import { DiagnosticResult } from "./utils/DiagnosticError"; + +/** + * Describes the backing resolver for a field. This broadly matches the metadata + * shape that is part of the public API of Grats, but also includes location + * information as well as information about resolver with types which have not + * yet been resolved. + */ +export type ResolverSignature = + | { + kind: "property"; + name: string | null; + node: ts.Node; + } + | { + kind: "method"; + name: string | null; + arguments: ResolverArgument[] | null; + node: ts.Node; + } + | { + kind: "function"; + path: string; + exportName: string | null; + arguments: ResolverArgument[] | null; + node: ts.Node; + } + | { + kind: "staticMethod"; + path: string; + exportName: string | null; + name: string; + arguments: ResolverArgument[] | null; + node: ts.Node; + }; + +export type SourceResolverArgument = { + kind: "source"; + node: ts.Node; +}; + +export type ArgumentsObjectResolverArgument = { + kind: "argumentsObject"; + node: ts.Node; +}; + +export type ContextResolverArgument = { + kind: "context"; + node: ts.Node; +}; + +export type InformationResolverArgument = { + kind: "information"; + node: ts.Node; +}; + +export type NamedResolverArgument = { + kind: "named"; + name: string; + node: ts.Node; + inputDefinition: InputValueDefinitionNode; +}; + +export type UnresolvedResolverArgument = { + kind: "unresolved"; + inputDefinition: InputValueDefinitionNodeOrResolverArg; + node: ts.Node; +}; + +export type ResolverArgument = + | SourceResolverArgument + | ArgumentsObjectResolverArgument + | ContextResolverArgument + | InformationResolverArgument + | NamedResolverArgument + | UnresolvedResolverArgument; + +/** + * At extraction time we don't know if a resolver arg is context, info, or a + * positional GraphQL argument. If it's a positional argument, we need to ensure + * it has a valid name. If it's just info or context, it's fine if it doesn't + * have a name e.g. (destructured). + */ +export interface InputValueDefinitionNodeOrResolverArg { + readonly kind: Kind.INPUT_VALUE_DEFINITION; + readonly loc: Location; + readonly description?: StringValueNode; + // This is the only property that is different. + readonly name: DiagnosticResult; + readonly type: TypeNode; + readonly defaultValue?: ConstValueNode; + readonly directives?: ReadonlyArray; +} diff --git a/src/tests/fixtures/arguments/ArgumentWithDescription.ts.expected b/src/tests/fixtures/arguments/ArgumentWithDescription.ts.expected index 278a2694..e6fbec22 100644 --- a/src/tests/fixtures/arguments/ArgumentWithDescription.ts.expected +++ b/src/tests/fixtures/arguments/ArgumentWithDescription.ts.expected @@ -20,7 +20,7 @@ type SomeType { hello( """The greeting to use.""" greeting: String! - ): String @metadata + ): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/arguments/CustomScalarArgument.ts.expected b/src/tests/fixtures/arguments/CustomScalarArgument.ts.expected index ff71ee6f..c3fac90f 100644 --- a/src/tests/fixtures/arguments/CustomScalarArgument.ts.expected +++ b/src/tests/fixtures/arguments/CustomScalarArgument.ts.expected @@ -19,7 +19,7 @@ OUTPUT scalar MyString type SomeType { - hello(greeting: MyString!): String @metadata + hello(greeting: MyString!): String } -- TypeScript -- import { GraphQLSchema, GraphQLScalarType, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/arguments/DeprecatedArgument.ts.expected b/src/tests/fixtures/arguments/DeprecatedArgument.ts.expected index 7cce9370..7ad112cf 100644 --- a/src/tests/fixtures/arguments/DeprecatedArgument.ts.expected +++ b/src/tests/fixtures/arguments/DeprecatedArgument.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String @deprecated(reason: "Not used anymore")): String @metadata + hello(greeting: String @deprecated(reason: "Not used anymore")): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/arguments/GqlTypeUsedAsPositionalArg.ts.expected b/src/tests/fixtures/arguments/GqlTypeUsedAsPositionalArg.ts.expected index f34c2aac..4550479b 100644 --- a/src/tests/fixtures/arguments/GqlTypeUsedAsPositionalArg.ts.expected +++ b/src/tests/fixtures/arguments/GqlTypeUsedAsPositionalArg.ts.expected @@ -25,7 +25,7 @@ input Greeting { } type SomeType { - hello(greeting: Greeting!): String @metadata + hello(greeting: Greeting!): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/arguments/NullableArguments.ts.expected b/src/tests/fixtures/arguments/NullableArguments.ts.expected index bd6c017f..099492f7 100644 --- a/src/tests/fixtures/arguments/NullableArguments.ts.expected +++ b/src/tests/fixtures/arguments/NullableArguments.ts.expected @@ -22,8 +22,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello1(greeting: String): String @metadata - hello2(greeting: String): String @metadata + hello1(greeting: String): String + hello2(greeting: String): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/arguments/OptionalArgument.ts.expected b/src/tests/fixtures/arguments/OptionalArgument.ts.expected index 0d8f07aa..71b4085c 100644 --- a/src/tests/fixtures/arguments/OptionalArgument.ts.expected +++ b/src/tests/fixtures/arguments/OptionalArgument.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String): String @metadata + hello(greeting: String): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/arguments/OptionalNonNullableArgumentWithDefault.ts.expected b/src/tests/fixtures/arguments/OptionalNonNullableArgumentWithDefault.ts.expected index c7ec6760..f6989a74 100644 --- a/src/tests/fixtures/arguments/OptionalNonNullableArgumentWithDefault.ts.expected +++ b/src/tests/fixtures/arguments/OptionalNonNullableArgumentWithDefault.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String! = "Hello"): String @metadata + hello(greeting: String! = "Hello"): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/arguments/PositionalArgDeprecatedOptional.ts.expected b/src/tests/fixtures/arguments/PositionalArgDeprecatedOptional.ts.expected index c3458b69..10a6615a 100644 --- a/src/tests/fixtures/arguments/PositionalArgDeprecatedOptional.ts.expected +++ b/src/tests/fixtures/arguments/PositionalArgDeprecatedOptional.ts.expected @@ -28,7 +28,7 @@ input Greeting { } type SomeType { - hello(greeting: Greeting @deprecated(reason: "Unused!")): String @metadata + hello(greeting: Greeting @deprecated(reason: "Unused!")): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/arguments/PositionalArgDeprecatedWithDefault.ts.expected b/src/tests/fixtures/arguments/PositionalArgDeprecatedWithDefault.ts.expected index 15b8e73c..8b1fa4dd 100644 --- a/src/tests/fixtures/arguments/PositionalArgDeprecatedWithDefault.ts.expected +++ b/src/tests/fixtures/arguments/PositionalArgDeprecatedWithDefault.ts.expected @@ -28,7 +28,7 @@ input Greeting { } type SomeType { - hello(greeting: Greeting! = {name: "Alice", salutation: "Hullo"} @deprecated(reason: "Unused!")): String @metadata + hello(greeting: Greeting! = {name: "Alice", salutation: "Hullo"} @deprecated(reason: "Unused!")): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/arguments/PositionalArgOptional.ts.expected b/src/tests/fixtures/arguments/PositionalArgOptional.ts.expected index d780b8d6..b6d774da 100644 --- a/src/tests/fixtures/arguments/PositionalArgOptional.ts.expected +++ b/src/tests/fixtures/arguments/PositionalArgOptional.ts.expected @@ -25,7 +25,7 @@ input Greeting { } type SomeType { - hello(greeting: String): String @metadata + hello(greeting: String): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/arguments/PositionalArgWithDefault.ts.expected b/src/tests/fixtures/arguments/PositionalArgWithDefault.ts.expected index 54f1b9d2..40e668b8 100644 --- a/src/tests/fixtures/arguments/PositionalArgWithDefault.ts.expected +++ b/src/tests/fixtures/arguments/PositionalArgWithDefault.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String! = "Hello"): String @metadata + hello(greeting: String! = "Hello"): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/arguments/PositionalArgWithDescription.ts.expected b/src/tests/fixtures/arguments/PositionalArgWithDescription.ts.expected index a68f1ce1..2fd60175 100644 --- a/src/tests/fixtures/arguments/PositionalArgWithDescription.ts.expected +++ b/src/tests/fixtures/arguments/PositionalArgWithDescription.ts.expected @@ -31,7 +31,7 @@ type SomeType { hello( """How to greet the user""" greeting: Greeting! - ): String @metadata + ): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/arguments/StringArgument.ts.expected b/src/tests/fixtures/arguments/StringArgument.ts.expected index 26efaadd..87cdb9aa 100644 --- a/src/tests/fixtures/arguments/StringArgument.ts.expected +++ b/src/tests/fixtures/arguments/StringArgument.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String!): String @metadata + hello(greeting: String!): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/built_in_scalars/FloatField.ts.expected b/src/tests/fixtures/built_in_scalars/FloatField.ts.expected index 72e27c57..dc6e8769 100644 --- a/src/tests/fixtures/built_in_scalars/FloatField.ts.expected +++ b/src/tests/fixtures/built_in_scalars/FloatField.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - ratio: Float @metadata + ratio: Float } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLFloat } from "graphql"; diff --git a/src/tests/fixtures/built_in_scalars/FloatFieldAliasedImport.ts.expected b/src/tests/fixtures/built_in_scalars/FloatFieldAliasedImport.ts.expected index 26a07d18..de2d5463 100644 --- a/src/tests/fixtures/built_in_scalars/FloatFieldAliasedImport.ts.expected +++ b/src/tests/fixtures/built_in_scalars/FloatFieldAliasedImport.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - ratio: Float @metadata + ratio: Float } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLFloat } from "graphql"; diff --git a/src/tests/fixtures/built_in_scalars/IdField.ts.expected b/src/tests/fixtures/built_in_scalars/IdField.ts.expected index 21d3a71d..7ea3ec8c 100644 --- a/src/tests/fixtures/built_in_scalars/IdField.ts.expected +++ b/src/tests/fixtures/built_in_scalars/IdField.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - id: ID @metadata + id: ID } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLID } from "graphql"; diff --git a/src/tests/fixtures/built_in_scalars/IntField.ts.expected b/src/tests/fixtures/built_in_scalars/IntField.ts.expected index 1bd189e1..aa597960 100644 --- a/src/tests/fixtures/built_in_scalars/IntField.ts.expected +++ b/src/tests/fixtures/built_in_scalars/IntField.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - age: Int @metadata + age: Int } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLInt } from "graphql"; diff --git a/src/tests/fixtures/configOptions/importModuleSpecifierEnding.ts.expected b/src/tests/fixtures/configOptions/importModuleSpecifierEnding.ts.expected index d1dcff07..f65aec19 100644 --- a/src/tests/fixtures/configOptions/importModuleSpecifierEnding.ts.expected +++ b/src/tests/fixtures/configOptions/importModuleSpecifierEnding.ts.expected @@ -19,8 +19,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/configOptions/importModuleSpecifierEnding.ts") - hello: String @metadata + greeting: String + hello: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./importModuleSpecifierEnding.js"; diff --git a/src/tests/fixtures/configOptions/nonNullableIsNull.invalid.ts.expected b/src/tests/fixtures/configOptions/nonNullableIsNull.invalid.ts.expected index fef421e5..fe83299b 100644 --- a/src/tests/fixtures/configOptions/nonNullableIsNull.invalid.ts.expected +++ b/src/tests/fixtures/configOptions/nonNullableIsNull.invalid.ts.expected @@ -13,7 +13,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/custom_scalars/DefineCustomScalar.ts.expected b/src/tests/fixtures/custom_scalars/DefineCustomScalar.ts.expected index 209fbcab..9700a121 100644 --- a/src/tests/fixtures/custom_scalars/DefineCustomScalar.ts.expected +++ b/src/tests/fixtures/custom_scalars/DefineCustomScalar.ts.expected @@ -17,7 +17,7 @@ OUTPUT scalar MyUrl type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLScalarType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/custom_scalars/DefineCustomScalarWithDescription.ts.expected b/src/tests/fixtures/custom_scalars/DefineCustomScalarWithDescription.ts.expected index e242bda8..6db585cb 100644 --- a/src/tests/fixtures/custom_scalars/DefineCustomScalarWithDescription.ts.expected +++ b/src/tests/fixtures/custom_scalars/DefineCustomScalarWithDescription.ts.expected @@ -21,7 +21,7 @@ OUTPUT scalar MyUrl type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLScalarType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/custom_scalars/DefineRenamedCustomScalar.ts.expected b/src/tests/fixtures/custom_scalars/DefineRenamedCustomScalar.ts.expected index 7f0b6213..e5ffef5c 100644 --- a/src/tests/fixtures/custom_scalars/DefineRenamedCustomScalar.ts.expected +++ b/src/tests/fixtures/custom_scalars/DefineRenamedCustomScalar.ts.expected @@ -17,7 +17,7 @@ OUTPUT scalar CustomName type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLScalarType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentArray.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentArray.ts.expected index 4bb44e36..dcb2bbb2 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentArray.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentArray.ts.expected @@ -21,7 +21,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - someField1(inputs: [String!] = ["hello", "there"]): String @metadata + someField1(inputs: [String!] = ["hello", "there"]): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentBooleanLiteral.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentBooleanLiteral.ts.expected index ce0cd03c..b61161b9 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentBooleanLiteral.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentBooleanLiteral.ts.expected @@ -21,8 +21,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - someField1(greet: Boolean = false): String @metadata - someField2(greet: Boolean = true): String @metadata + someField1(greet: Boolean = false): String + someField2(greet: Boolean = true): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLBoolean } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentNullLiteral.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentNullLiteral.ts.expected index 0c323ddd..8513e7a1 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentNullLiteral.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentNullLiteral.ts.expected @@ -20,8 +20,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - someField1(hello: String = null): String @metadata - someField2(hello: String = null): String @metadata + someField1(hello: String = null): String + someField2(hello: String = null): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentNumberLiteral.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentNumberLiteral.ts.expected index cbdca822..a75d2b6f 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentNumberLiteral.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentNumberLiteral.ts.expected @@ -20,8 +20,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - floatField(scale: Float! = 10): String @metadata - intField(count: Int! = 10): String @metadata + floatField(scale: Float! = 10): String + intField(count: Int! = 10): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull, GraphQLFloat, GraphQLInt } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentObjectLiteral.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentObjectLiteral.ts.expected index bbc0f057..2d37f69a 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentObjectLiteral.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentObjectLiteral.ts.expected @@ -31,7 +31,7 @@ input ConnectionInput { } type SomeType { - someField1(input: ConnectionInput = {first: 10, offset: 100}): String @metadata + someField1(input: ConnectionInput = {first: 10, offset: 100}): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLInt, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentStringLiteral.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentStringLiteral.ts.expected index b7f464f1..ca2b0ad8 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentStringLiteral.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentStringLiteral.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String! = "hello"): String @metadata + hello(greeting: String! = "hello"): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/default_values/DefaultArgumentStringLiteralBackticks.ts.expected b/src/tests/fixtures/default_values/DefaultArgumentStringLiteralBackticks.ts.expected index b44b8344..ec07c28b 100644 --- a/src/tests/fixtures/default_values/DefaultArgumentStringLiteralBackticks.ts.expected +++ b/src/tests/fixtures/default_values/DefaultArgumentStringLiteralBackticks.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello(greeting: String! = "hello"): String @metadata + hello(greeting: String! = "hello"): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/descriptions/BlankLinesAroundDescription.ts.expected b/src/tests/fixtures/descriptions/BlankLinesAroundDescription.ts.expected index 311caacb..87c2568d 100644 --- a/src/tests/fixtures/descriptions/BlankLinesAroundDescription.ts.expected +++ b/src/tests/fixtures/descriptions/BlankLinesAroundDescription.ts.expected @@ -33,7 +33,7 @@ OUTPUT -- SDL -- """Sup""" type SomeType { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/descriptions/BlankLinesFollowTypeTag.ts.expected b/src/tests/fixtures/descriptions/BlankLinesFollowTypeTag.ts.expected index 182f6685..d6743ec7 100644 --- a/src/tests/fixtures/descriptions/BlankLinesFollowTypeTag.ts.expected +++ b/src/tests/fixtures/descriptions/BlankLinesFollowTypeTag.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/descriptions/MultilineDescription.ts.expected b/src/tests/fixtures/descriptions/MultilineDescription.ts.expected index 1fa7240d..f1146a2a 100644 --- a/src/tests/fixtures/descriptions/MultilineDescription.ts.expected +++ b/src/tests/fixtures/descriptions/MultilineDescription.ts.expected @@ -25,7 +25,7 @@ All mimsy were the borogoves, And the mome raths outgrabe. """ type SomeType { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/enums/DeprecatedEnumVariant.ts.expected b/src/tests/fixtures/enums/DeprecatedEnumVariant.ts.expected index 3bdb433b..785bdcd5 100644 --- a/src/tests/fixtures/enums/DeprecatedEnumVariant.ts.expected +++ b/src/tests/fixtures/enums/DeprecatedEnumVariant.ts.expected @@ -30,7 +30,7 @@ enum Enum { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/enums/Enum.ts.expected b/src/tests/fixtures/enums/Enum.ts.expected index 6658f717..c3d38093 100644 --- a/src/tests/fixtures/enums/Enum.ts.expected +++ b/src/tests/fixtures/enums/Enum.ts.expected @@ -23,7 +23,7 @@ enum Enum { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumFromUnionType.ts.expected b/src/tests/fixtures/enums/EnumFromUnionType.ts.expected index 6ff62482..7542ed7d 100644 --- a/src/tests/fixtures/enums/EnumFromUnionType.ts.expected +++ b/src/tests/fixtures/enums/EnumFromUnionType.ts.expected @@ -20,7 +20,7 @@ enum MyEnum { } type SomeType { - hello: MyEnum @metadata + hello: MyEnum } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumFromUnionTypeOfStringLiteral.ts.expected b/src/tests/fixtures/enums/EnumFromUnionTypeOfStringLiteral.ts.expected index c033a351..1d08f1fb 100644 --- a/src/tests/fixtures/enums/EnumFromUnionTypeOfStringLiteral.ts.expected +++ b/src/tests/fixtures/enums/EnumFromUnionTypeOfStringLiteral.ts.expected @@ -19,7 +19,7 @@ enum MyEnum { } type SomeType { - hello: MyEnum @metadata + hello: MyEnum } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumFromUnionTypeWithDescription.ts.expected b/src/tests/fixtures/enums/EnumFromUnionTypeWithDescription.ts.expected index e4e2152a..3c0a9a66 100644 --- a/src/tests/fixtures/enums/EnumFromUnionTypeWithDescription.ts.expected +++ b/src/tests/fixtures/enums/EnumFromUnionTypeWithDescription.ts.expected @@ -24,7 +24,7 @@ enum MyEnum { } type SomeType { - hello: MyEnum @metadata + hello: MyEnum } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumValuesDifferentThanNames.ts.expected b/src/tests/fixtures/enums/EnumValuesDifferentThanNames.ts.expected index d19d87be..84adb34b 100644 --- a/src/tests/fixtures/enums/EnumValuesDifferentThanNames.ts.expected +++ b/src/tests/fixtures/enums/EnumValuesDifferentThanNames.ts.expected @@ -23,7 +23,7 @@ enum Enum { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumWithDescription.ts.expected b/src/tests/fixtures/enums/EnumWithDescription.ts.expected index 4b52a834..9df6f389 100644 --- a/src/tests/fixtures/enums/EnumWithDescription.ts.expected +++ b/src/tests/fixtures/enums/EnumWithDescription.ts.expected @@ -28,7 +28,7 @@ enum Enum { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/enums/EnumWithVariantWithDescription.ts.expected b/src/tests/fixtures/enums/EnumWithVariantWithDescription.ts.expected index 32c67a8f..644eee6d 100644 --- a/src/tests/fixtures/enums/EnumWithVariantWithDescription.ts.expected +++ b/src/tests/fixtures/enums/EnumWithVariantWithDescription.ts.expected @@ -27,7 +27,7 @@ enum Enum { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/examples/playground.ts.expected b/src/tests/fixtures/examples/playground.ts.expected index 1567aa26..ca80f296 100644 --- a/src/tests/fixtures/examples/playground.ts.expected +++ b/src/tests/fixtures/examples/playground.ts.expected @@ -40,15 +40,15 @@ OUTPUT ----------------- -- SDL -- type SomeType { - getUser: User @metadata(exportName: "getUser", tsModulePath: "grats/src/tests/fixtures/examples/playground.ts") - me: User @metadata - viewer: User @deprecated(reason: "Please use `me` instead.") @metadata + getUser: User + me: User + viewer: User @deprecated(reason: "Please use `me` instead.") } """A user in our kick-ass system!""" type User { - greeting(salutation: String!): String @metadata - name: String @metadata + greeting(salutation: String!): String + name: String } -- TypeScript -- import { getUser as someTypeGetUserResolver } from "./playground"; diff --git a/src/tests/fixtures/examples/readme.ts.expected b/src/tests/fixtures/examples/readme.ts.expected index f76177e3..5c499dc9 100644 --- a/src/tests/fixtures/examples/readme.ts.expected +++ b/src/tests/fixtures/examples/readme.ts.expected @@ -35,18 +35,17 @@ OUTPUT ----------------- -- SDL -- type Query { - me: User @metadata(exportName: "me", tsModulePath: "grats/src/tests/fixtures/examples/readme.ts") - viewer: User @deprecated(reason: "Please use `me` instead.") @metadata(exportName: "viewer", tsModulePath: "grats/src/tests/fixtures/examples/readme.ts") + me: User + viewer: User @deprecated(reason: "Please use `me` instead.") } """A user in our kick-ass system!""" type User { - greeting(salutation: String!): String @metadata - name: String @metadata + greeting(salutation: String!): String + name: String } -- TypeScript -- -import { me as queryMeResolver } from "./readme"; -import { viewer as queryViewerResolver } from "./readme"; +import { me as queryMeResolver, viewer as queryViewerResolver } from "./readme"; import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; export function getSchema(): GraphQLSchema { const UserType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts.expected b/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts.expected index f264e712..d23579ad 100644 --- a/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts.expected +++ b/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts.expected @@ -35,21 +35,20 @@ OUTPUT -- SDL -- interface IPerson { greeting: String - hello: String @metadata + hello: String } type Admin implements IPerson { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts") - hello: String @metadata + greeting: String + hello: String } type User implements IPerson { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_interface/addStringFieldToInterface.ts") - hello: String @metadata + greeting: String + hello: String } -- TypeScript -- -import { greeting as adminGreetingResolver } from "./addStringFieldToInterface"; -import { greeting as userGreetingResolver } from "./addStringFieldToInterface"; +import { greeting as adminGreetingResolver, greeting as userGreetingResolver } from "./addStringFieldToInterface"; import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; export function getSchema(): GraphQLSchema { const IPersonType: GraphQLInterfaceType = new GraphQLInterfaceType({ diff --git a/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts.expected b/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts.expected index 0665b11e..e425c920 100644 --- a/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts.expected +++ b/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts.expected @@ -47,15 +47,14 @@ interface IThing { } type Admin implements IPerson & IThing { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts") + greeting: String } type User implements IPerson & IThing { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_interface/addStringFieldToInterfaceImplementedByInterface.ts") + greeting: String } -- TypeScript -- -import { greeting as adminGreetingResolver } from "./addStringFieldToInterfaceImplementedByInterface"; -import { greeting as userGreetingResolver } from "./addStringFieldToInterfaceImplementedByInterface"; +import { greeting as adminGreetingResolver, greeting as userGreetingResolver } from "./addStringFieldToInterfaceImplementedByInterface"; import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; export function getSchema(): GraphQLSchema { const IThingType: GraphQLInterfaceType = new GraphQLInterfaceType({ diff --git a/src/tests/fixtures/extend_type/addDeprecatedField.ts.expected b/src/tests/fixtures/extend_type/addDeprecatedField.ts.expected index 8be35ec5..7c0e6135 100644 --- a/src/tests/fixtures/extend_type/addDeprecatedField.ts.expected +++ b/src/tests/fixtures/extend_type/addDeprecatedField.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @deprecated(reason: "Because reasons") @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/addDeprecatedField.ts") + greeting: String @deprecated(reason: "Because reasons") } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./addDeprecatedField"; diff --git a/src/tests/fixtures/extend_type/addFieldWithArguments.ts.expected b/src/tests/fixtures/extend_type/addFieldWithArguments.ts.expected index 5b93b964..21a1574c 100644 --- a/src/tests/fixtures/extend_type/addFieldWithArguments.ts.expected +++ b/src/tests/fixtures/extend_type/addFieldWithArguments.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting(name: String!): String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/addFieldWithArguments.ts") + greeting(name: String!): String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./addFieldWithArguments"; diff --git a/src/tests/fixtures/extend_type/addFieldWithDescription.ts.expected b/src/tests/fixtures/extend_type/addFieldWithDescription.ts.expected index cea46a48..467fd3ea 100644 --- a/src/tests/fixtures/extend_type/addFieldWithDescription.ts.expected +++ b/src/tests/fixtures/extend_type/addFieldWithDescription.ts.expected @@ -20,7 +20,7 @@ OUTPUT -- SDL -- type SomeType { """Best field ever!""" - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/addFieldWithDescription.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./addFieldWithDescription"; diff --git a/src/tests/fixtures/extend_type/addRenamedFieldToSomeType.ts.expected b/src/tests/fixtures/extend_type/addRenamedFieldToSomeType.ts.expected index 0225075f..5dda3004 100644 --- a/src/tests/fixtures/extend_type/addRenamedFieldToSomeType.ts.expected +++ b/src/tests/fixtures/extend_type/addRenamedFieldToSomeType.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/addRenamedFieldToSomeType.ts") + hello: String } -- TypeScript -- import { greeting as someTypeHelloResolver } from "./addRenamedFieldToSomeType"; diff --git a/src/tests/fixtures/extend_type/addStringFieldToSomeType.ts.expected b/src/tests/fixtures/extend_type/addStringFieldToSomeType.ts.expected index 0872e1e0..6330c39f 100644 --- a/src/tests/fixtures/extend_type/addStringFieldToSomeType.ts.expected +++ b/src/tests/fixtures/extend_type/addStringFieldToSomeType.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/addStringFieldToSomeType.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./addStringFieldToSomeType"; diff --git a/src/tests/fixtures/extend_type/defaultExport.ts.expected b/src/tests/fixtures/extend_type/defaultExport.ts.expected index a4186299..f9b8f1f1 100644 --- a/src/tests/fixtures/extend_type/defaultExport.ts.expected +++ b/src/tests/fixtures/extend_type/defaultExport.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(tsModulePath: "grats/src/tests/fixtures/extend_type/defaultExport.ts") + greeting: String } -- TypeScript -- import someTypeGreetingResolver from "./defaultExport"; diff --git a/src/tests/fixtures/extend_type/fieldAsExportedArrowFunction.ts.expected b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunction.ts.expected index e7731762..a739d933 100644 --- a/src/tests/fixtures/extend_type/fieldAsExportedArrowFunction.ts.expected +++ b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunction.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/fieldAsExportedArrowFunction.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./fieldAsExportedArrowFunction"; diff --git a/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts new file mode 100644 index 00000000..fa46bf1a --- /dev/null +++ b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts @@ -0,0 +1,9 @@ +/** @gqlType */ +class SomeType { + // No fields +} + +/** @gqlField */ +export const greeting = (_: SomeType, name: string): string => { + return `Hello ${name}`; +}; diff --git a/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts.expected b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts.expected new file mode 100644 index 00000000..59dc8f31 --- /dev/null +++ b/src/tests/fixtures/extend_type/fieldAsExportedArrowFunctionPositionalArgs.ts.expected @@ -0,0 +1,48 @@ +----------------- +INPUT +----------------- +/** @gqlType */ +class SomeType { + // No fields +} + +/** @gqlField */ +export const greeting = (_: SomeType, name: string): string => { + return `Hello ${name}`; +}; + +----------------- +OUTPUT +----------------- +-- SDL -- +type SomeType { + greeting(name: String!): String +} +-- TypeScript -- +import { greeting as someTypeGreetingResolver } from "./fieldAsExportedArrowFunctionPositionalArgs"; +import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; +export function getSchema(): GraphQLSchema { + const SomeTypeType: GraphQLObjectType = new GraphQLObjectType({ + name: "SomeType", + fields() { + return { + greeting: { + name: "greeting", + type: GraphQLString, + args: { + name: { + name: "name", + type: new GraphQLNonNull(GraphQLString) + } + }, + resolve(source, args) { + return someTypeGreetingResolver(source, args.name); + } + } + }; + } + }); + return new GraphQLSchema({ + types: [SomeTypeType] + }); +} diff --git a/src/tests/fixtures/extend_type/fieldAsExportedAsyncArrowFunction.ts.expected b/src/tests/fixtures/extend_type/fieldAsExportedAsyncArrowFunction.ts.expected index 9f5a85cc..ec54ab59 100644 --- a/src/tests/fixtures/extend_type/fieldAsExportedAsyncArrowFunction.ts.expected +++ b/src/tests/fixtures/extend_type/fieldAsExportedAsyncArrowFunction.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/fieldAsExportedAsyncArrowFunction.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./fieldAsExportedAsyncArrowFunction"; diff --git a/src/tests/fixtures/extend_type/functionFieldOnTypeDefinedWithInterface.ts.expected b/src/tests/fixtures/extend_type/functionFieldOnTypeDefinedWithInterface.ts.expected index 8c05f8d2..9799b95d 100644 --- a/src/tests/fixtures/extend_type/functionFieldOnTypeDefinedWithInterface.ts.expected +++ b/src/tests/fixtures/extend_type/functionFieldOnTypeDefinedWithInterface.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type Cat { - catSound: String @metadata(exportName: "catSound", tsModulePath: "grats/src/tests/fixtures/extend_type/functionFieldOnTypeDefinedWithInterface.ts") + catSound: String } -- TypeScript -- import { catSound as catCatSoundResolver } from "./functionFieldOnTypeDefinedWithInterface"; diff --git a/src/tests/fixtures/extend_type/interfaceFirstArgumentType.ts.expected b/src/tests/fixtures/extend_type/interfaceFirstArgumentType.ts.expected index 4a357890..859324eb 100644 --- a/src/tests/fixtures/extend_type/interfaceFirstArgumentType.ts.expected +++ b/src/tests/fixtures/extend_type/interfaceFirstArgumentType.ts.expected @@ -23,12 +23,12 @@ OUTPUT ----------------- -- SDL -- interface IFoo { - bar: String @metadata + bar: String greeting: String } type SomeType { - foo: String @metadata + foo: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/extend_type/optionalModelType.ts.expected b/src/tests/fixtures/extend_type/optionalModelType.ts.expected index fdc00e3d..a30d8916 100644 --- a/src/tests/fixtures/extend_type/optionalModelType.ts.expected +++ b/src/tests/fixtures/extend_type/optionalModelType.ts.expected @@ -23,7 +23,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/extend_type/optionalModelType.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./optionalModelType"; diff --git a/src/tests/fixtures/field_definitions/DeprecatedMethodField.ts.expected b/src/tests/fixtures/field_definitions/DeprecatedMethodField.ts.expected index 7b115ccc..71c4c7bc 100644 --- a/src/tests/fixtures/field_definitions/DeprecatedMethodField.ts.expected +++ b/src/tests/fixtures/field_definitions/DeprecatedMethodField.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @deprecated(reason: "Use something else.") @metadata + hello: String @deprecated(reason: "Use something else.") } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/DeprecatedPropertyField.ts.expected b/src/tests/fixtures/field_definitions/DeprecatedPropertyField.ts.expected index 206fe997..944c38f4 100644 --- a/src/tests/fixtures/field_definitions/DeprecatedPropertyField.ts.expected +++ b/src/tests/fixtures/field_definitions/DeprecatedPropertyField.ts.expected @@ -15,7 +15,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @deprecated(reason: "Use something else.") @metadata + hello: String @deprecated(reason: "Use something else.") } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethod.ts.expected b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethod.ts.expected index f27c92f5..236c0e4f 100644 --- a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethod.ts.expected +++ b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethod.ts.expected @@ -20,11 +20,11 @@ OUTPUT ----------------- -- SDL -- type Query { - getUser: User @metadata(exportName: "User", name: "getUser", tsModulePath: "grats/src/tests/fixtures/field_definitions/FieldAsStaticClassMethod.ts") + getUser: User } type User { - name: String @metadata + name: String } -- TypeScript -- import { User as queryGetUserResolver } from "./FieldAsStaticClassMethod"; diff --git a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnNonGqlClass.ts.expected b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnNonGqlClass.ts.expected index 0d3cfc28..e9ffb17b 100644 --- a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnNonGqlClass.ts.expected +++ b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnNonGqlClass.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type Query { - greet: String @metadata(exportName: "SomeClass", name: "greet", tsModulePath: "grats/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnNonGqlClass.ts") + greet: String } -- TypeScript -- import { SomeClass as queryGreetResolver } from "./FieldAsStaticClassMethodOnNonGqlClass"; diff --git a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnUnnamedNonGqlClass.ts.expected b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnUnnamedNonGqlClass.ts.expected index 8100c240..2017754c 100644 --- a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnUnnamedNonGqlClass.ts.expected +++ b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnUnnamedNonGqlClass.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type Query { - greet: String @metadata(name: "greet", tsModulePath: "grats/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodOnUnnamedNonGqlClass.ts") + greet: String } -- TypeScript -- import queryGreetResolver from "./FieldAsStaticClassMethodOnUnnamedNonGqlClass"; diff --git a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodWithClassAsDefaultExport.ts.expected b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodWithClassAsDefaultExport.ts.expected index c7211a00..16364980 100644 --- a/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodWithClassAsDefaultExport.ts.expected +++ b/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodWithClassAsDefaultExport.ts.expected @@ -20,11 +20,11 @@ OUTPUT ----------------- -- SDL -- type Query { - getUser: User @metadata(name: "getUser", tsModulePath: "grats/src/tests/fixtures/field_definitions/FieldAsStaticClassMethodWithClassAsDefaultExport.ts") + getUser: User } type User { - name: String @metadata + name: String } -- TypeScript -- import queryGetUserResolver from "./FieldAsStaticClassMethodWithClassAsDefaultExport"; diff --git a/src/tests/fixtures/field_definitions/GetAcessorField.ts.expected b/src/tests/fixtures/field_definitions/GetAcessorField.ts.expected index f89c9196..d7377ce5 100644 --- a/src/tests/fixtures/field_definitions/GetAcessorField.ts.expected +++ b/src/tests/fixtures/field_definitions/GetAcessorField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/MethodSignatureOnInterface.ts.expected b/src/tests/fixtures/field_definitions/MethodSignatureOnInterface.ts.expected index d2ad4502..2137e0aa 100644 --- a/src/tests/fixtures/field_definitions/MethodSignatureOnInterface.ts.expected +++ b/src/tests/fixtures/field_definitions/MethodSignatureOnInterface.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- interface ICarly { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts.expected b/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts.expected index 2cde1fa4..2e12dfa1 100644 --- a/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts.expected +++ b/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts.expected @@ -25,16 +25,15 @@ OUTPUT ----------------- -- SDL -- type Query { - getUser: User @metadata(exportName: "User", name: "getUser", tsModulePath: "grats/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts") - getUsers: [User!] @metadata(exportName: "User", name: "getUsers", tsModulePath: "grats/src/tests/fixtures/field_definitions/MultipleFieldsAsStaticClassMethods.ts") + getUser: User + getUsers: [User!] } type User { - name: String @metadata + name: String } -- TypeScript -- -import { User as queryGetUserResolver } from "./MultipleFieldsAsStaticClassMethods"; -import { User as queryGetUsersResolver } from "./MultipleFieldsAsStaticClassMethods"; +import { User as queryGetUserResolver, User as queryGetUsersResolver } from "./MultipleFieldsAsStaticClassMethods"; import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLNonNull } from "graphql"; export function getSchema(): GraphQLSchema { const UserType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/fixtures/field_definitions/ParameterPropertyField.ts.expected b/src/tests/fixtures/field_definitions/ParameterPropertyField.ts.expected index 011b1ba2..13ddc8d2 100644 --- a/src/tests/fixtures/field_definitions/ParameterPropertyField.ts.expected +++ b/src/tests/fixtures/field_definitions/ParameterPropertyField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/ParameterPropertyFieldDeprecated.ts.expected b/src/tests/fixtures/field_definitions/ParameterPropertyFieldDeprecated.ts.expected index f4c49a11..3ef64623 100644 --- a/src/tests/fixtures/field_definitions/ParameterPropertyFieldDeprecated.ts.expected +++ b/src/tests/fixtures/field_definitions/ParameterPropertyFieldDeprecated.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @deprecated(reason: "Don't use this") @metadata + hello: String @deprecated(reason: "Don't use this") } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/ParameterPropertyFieldReadOnly.ts.expected b/src/tests/fixtures/field_definitions/ParameterPropertyFieldReadOnly.ts.expected index 4ee845ee..8ad67d2a 100644 --- a/src/tests/fixtures/field_definitions/ParameterPropertyFieldReadOnly.ts.expected +++ b/src/tests/fixtures/field_definitions/ParameterPropertyFieldReadOnly.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/ParameterPropertyFieldRenamed.ts.expected b/src/tests/fixtures/field_definitions/ParameterPropertyFieldRenamed.ts.expected index 8df142ef..4d7af756 100644 --- a/src/tests/fixtures/field_definitions/ParameterPropertyFieldRenamed.ts.expected +++ b/src/tests/fixtures/field_definitions/ParameterPropertyFieldRenamed.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata(name: "foo") + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/ParameterPropertyFieldWithDescription.ts.expected b/src/tests/fixtures/field_definitions/ParameterPropertyFieldWithDescription.ts.expected index db0e620c..af7c8620 100644 --- a/src/tests/fixtures/field_definitions/ParameterPropertyFieldWithDescription.ts.expected +++ b/src/tests/fixtures/field_definitions/ParameterPropertyFieldWithDescription.ts.expected @@ -18,7 +18,7 @@ OUTPUT -- SDL -- type SomeType { """Greet the world!""" - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/PublicFieldMethod.ts.expected b/src/tests/fixtures/field_definitions/PublicFieldMethod.ts.expected index dc3f53fc..a1bc6d96 100644 --- a/src/tests/fixtures/field_definitions/PublicFieldMethod.ts.expected +++ b/src/tests/fixtures/field_definitions/PublicFieldMethod.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type User { - greet: String @metadata + greet: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/RenamedField.ts.expected b/src/tests/fixtures/field_definitions/RenamedField.ts.expected index eb4a28e5..14986629 100644 --- a/src/tests/fixtures/field_definitions/RenamedField.ts.expected +++ b/src/tests/fixtures/field_definitions/RenamedField.ts.expected @@ -17,8 +17,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(name: "somePropertyField") - salutaion: String @metadata(name: "someMethodField") + greeting: String + salutaion: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/RenamedFieldWithArgs.ts.expected b/src/tests/fixtures/field_definitions/RenamedFieldWithArgs.ts.expected index d0bda130..24782311 100644 --- a/src/tests/fixtures/field_definitions/RenamedFieldWithArgs.ts.expected +++ b/src/tests/fixtures/field_definitions/RenamedFieldWithArgs.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greetz(greeting: String!): String @metadata(name: "hello") + greetz(greeting: String!): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/RenamedFieldWithDescription.ts.expected b/src/tests/fixtures/field_definitions/RenamedFieldWithDescription.ts.expected index 52b242b3..8f92f183 100644 --- a/src/tests/fixtures/field_definitions/RenamedFieldWithDescription.ts.expected +++ b/src/tests/fixtures/field_definitions/RenamedFieldWithDescription.ts.expected @@ -26,9 +26,9 @@ OUTPUT -- SDL -- type SomeType { """Number 1 greeting.""" - greeting: String @metadata(name: "somePropertyField") + greeting: String """Number 1 salutation.""" - salutaion: String @metadata(name: "someMethodField") + salutaion: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_definitions/StringFieldWithDescription.ts.expected b/src/tests/fixtures/field_definitions/StringFieldWithDescription.ts.expected index 4d8db285..956c1052 100644 --- a/src/tests/fixtures/field_definitions/StringFieldWithDescription.ts.expected +++ b/src/tests/fixtures/field_definitions/StringFieldWithDescription.ts.expected @@ -18,7 +18,7 @@ OUTPUT -- SDL -- type SomeType { """Greet the world!""" - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ArrayField.ts.expected b/src/tests/fixtures/field_values/ArrayField.ts.expected index 959da8c6..3ba4d47d 100644 --- a/src/tests/fixtures/field_values/ArrayField.ts.expected +++ b/src/tests/fixtures/field_values/ArrayField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: [String!] @metadata + hello: [String!] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ArrayOfPromises.ts.expected b/src/tests/fixtures/field_values/ArrayOfPromises.ts.expected index f7614df1..b930798b 100644 --- a/src/tests/fixtures/field_values/ArrayOfPromises.ts.expected +++ b/src/tests/fixtures/field_values/ArrayOfPromises.ts.expected @@ -14,8 +14,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - b: [String!] @metadata - c: [String!] @metadata + b: [String!] + c: [String!] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ArrayShorthandField.ts.expected b/src/tests/fixtures/field_values/ArrayShorthandField.ts.expected index b9a774be..6b611ee5 100644 --- a/src/tests/fixtures/field_values/ArrayShorthandField.ts.expected +++ b/src/tests/fixtures/field_values/ArrayShorthandField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: [String!] @metadata + hello: [String!] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ArrayWithNullableItems.ts.expected b/src/tests/fixtures/field_values/ArrayWithNullableItems.ts.expected index facfcb0a..015a63d5 100644 --- a/src/tests/fixtures/field_values/ArrayWithNullableItems.ts.expected +++ b/src/tests/fixtures/field_values/ArrayWithNullableItems.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: [String] @metadata + hello: [String] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/AsyncPromiseField.ts.expected b/src/tests/fixtures/field_values/AsyncPromiseField.ts.expected index 9689e790..e20460f1 100644 --- a/src/tests/fixtures/field_values/AsyncPromiseField.ts.expected +++ b/src/tests/fixtures/field_values/AsyncPromiseField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/BooleanField.ts.expected b/src/tests/fixtures/field_values/BooleanField.ts.expected index 877feccd..95d13592 100644 --- a/src/tests/fixtures/field_values/BooleanField.ts.expected +++ b/src/tests/fixtures/field_values/BooleanField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - haveBeenGreeted: Boolean @metadata + haveBeenGreeted: Boolean } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLBoolean } from "graphql"; diff --git a/src/tests/fixtures/field_values/CustomScalar.ts.expected b/src/tests/fixtures/field_values/CustomScalar.ts.expected index 1af2eafc..ffa23dab 100644 --- a/src/tests/fixtures/field_values/CustomScalar.ts.expected +++ b/src/tests/fixtures/field_values/CustomScalar.ts.expected @@ -19,7 +19,7 @@ OUTPUT scalar MyString type SomeType { - hello: MyString @metadata + hello: MyString } -- TypeScript -- import { GraphQLSchema, GraphQLScalarType, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/field_values/KitchenSink.ts.expected b/src/tests/fixtures/field_values/KitchenSink.ts.expected index 1f0b1f6b..1e1646ae 100644 --- a/src/tests/fixtures/field_values/KitchenSink.ts.expected +++ b/src/tests/fixtures/field_values/KitchenSink.ts.expected @@ -63,22 +63,22 @@ OUTPUT ----------------- -- SDL -- type Group { - description: String @metadata - members: [User!] @metadata - name: String @metadata + description: String + members: [User!] + name: String } type SomeType { - greetings(greeting: String!): [String!] @metadata - greetings1(greeting: String!): [String!] @metadata - greetings2(greeting: String!): [String!] @metadata - hello(greeting: String!): String @metadata - me: User @metadata + greetings(greeting: String!): [String!] + greetings1(greeting: String!): [String!] + greetings2(greeting: String!): [String!] + hello(greeting: String!): String + me: User } type User { - groups: [Group!] @metadata - name: String @metadata + groups: [Group!] + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/field_values/LinkedField.ts.expected b/src/tests/fixtures/field_values/LinkedField.ts.expected index 0e5ca641..d9b4da56 100644 --- a/src/tests/fixtures/field_values/LinkedField.ts.expected +++ b/src/tests/fixtures/field_values/LinkedField.ts.expected @@ -26,12 +26,12 @@ OUTPUT ----------------- -- SDL -- type SomeType { - me: User @metadata + me: User } type User { - friends: [User!] @metadata - name: String @metadata + friends: [User!] + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/LinkedFieldWithTypeArg.ts.expected b/src/tests/fixtures/field_values/LinkedFieldWithTypeArg.ts.expected index d694ace3..45c9e351 100644 --- a/src/tests/fixtures/field_values/LinkedFieldWithTypeArg.ts.expected +++ b/src/tests/fixtures/field_values/LinkedFieldWithTypeArg.ts.expected @@ -28,12 +28,12 @@ OUTPUT ----------------- -- SDL -- type SomeType { - me: User @metadata + me: User } type User { - friends: [User!] @metadata - name: String @metadata + friends: [User!] + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/MaybePromise.ts.expected b/src/tests/fixtures/field_values/MaybePromise.ts.expected index f6de143a..4e01479f 100644 --- a/src/tests/fixtures/field_values/MaybePromise.ts.expected +++ b/src/tests/fixtures/field_values/MaybePromise.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - b: String @metadata + b: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/OptionalFields.ts.expected b/src/tests/fixtures/field_values/OptionalFields.ts.expected index 1ccd58eb..c1439640 100644 --- a/src/tests/fixtures/field_values/OptionalFields.ts.expected +++ b/src/tests/fixtures/field_values/OptionalFields.ts.expected @@ -26,10 +26,10 @@ OUTPUT ----------------- -- SDL -- type SomeType { - adieu: String @metadata - farewell: String @metadata - goodbye: String @metadata - hello: String @metadata + adieu: String + farewell: String + goodbye: String + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/OptionalProperty.ts.expected b/src/tests/fixtures/field_values/OptionalProperty.ts.expected index ef6c149e..1ec2c7dd 100644 --- a/src/tests/fixtures/field_values/OptionalProperty.ts.expected +++ b/src/tests/fixtures/field_values/OptionalProperty.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ParenthesizedType.ts.expected b/src/tests/fixtures/field_values/ParenthesizedType.ts.expected index 09334f45..acf67fac 100644 --- a/src/tests/fixtures/field_values/ParenthesizedType.ts.expected +++ b/src/tests/fixtures/field_values/ParenthesizedType.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: [String] @metadata + hello: [String] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/PromiseOfPromise.ts.expected b/src/tests/fixtures/field_values/PromiseOfPromise.ts.expected index bc6676ab..e84ff807 100644 --- a/src/tests/fixtures/field_values/PromiseOfPromise.ts.expected +++ b/src/tests/fixtures/field_values/PromiseOfPromise.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - b: String @metadata + b: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/ReadonlyArrayField.ts.expected b/src/tests/fixtures/field_values/ReadonlyArrayField.ts.expected index bc984257..f07eadc5 100644 --- a/src/tests/fixtures/field_values/ReadonlyArrayField.ts.expected +++ b/src/tests/fixtures/field_values/ReadonlyArrayField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: [String!] @metadata + hello: [String!] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/RenamedType.ts.expected b/src/tests/fixtures/field_values/RenamedType.ts.expected index 1a3c7255..e6ed6017 100644 --- a/src/tests/fixtures/field_values/RenamedType.ts.expected +++ b/src/tests/fixtures/field_values/RenamedType.ts.expected @@ -18,11 +18,11 @@ OUTPUT ----------------- -- SDL -- type SomeType { - me: User @metadata + me: User } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/RenamedTypeOutOfOrder.ts.expected b/src/tests/fixtures/field_values/RenamedTypeOutOfOrder.ts.expected index 89da8f6e..0292e546 100644 --- a/src/tests/fixtures/field_values/RenamedTypeOutOfOrder.ts.expected +++ b/src/tests/fixtures/field_values/RenamedTypeOutOfOrder.ts.expected @@ -18,11 +18,11 @@ OUTPUT ----------------- -- SDL -- type SomeType { - me: User @metadata + me: User } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/StringField.ts.expected b/src/tests/fixtures/field_values/StringField.ts.expected index 271c5bd8..c4a78eab 100644 --- a/src/tests/fixtures/field_values/StringField.ts.expected +++ b/src/tests/fixtures/field_values/StringField.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/StringFieldKillsParentOnException.ts.expected b/src/tests/fixtures/field_values/StringFieldKillsParentOnException.ts.expected index 4f306aa1..69b3feb7 100644 --- a/src/tests/fixtures/field_values/StringFieldKillsParentOnException.ts.expected +++ b/src/tests/fixtures/field_values/StringFieldKillsParentOnException.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String! @killsParentOnException @metadata + hello: String! } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/UnionField.ts.expected b/src/tests/fixtures/field_values/UnionField.ts.expected index 0ef88bcb..5e6dd3c9 100644 --- a/src/tests/fixtures/field_values/UnionField.ts.expected +++ b/src/tests/fixtures/field_values/UnionField.ts.expected @@ -33,15 +33,15 @@ OUTPUT union Actor = Entity | User type Entity { - description: String @metadata + description: String } type SomeType { - me: Actor @metadata + me: Actor } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/non_default_nullable/NonNullablePromise.ts.expected b/src/tests/fixtures/field_values/non_default_nullable/NonNullablePromise.ts.expected index 66fead21..67a6e38b 100644 --- a/src/tests/fixtures/field_values/non_default_nullable/NonNullablePromise.ts.expected +++ b/src/tests/fixtures/field_values/non_default_nullable/NonNullablePromise.ts.expected @@ -15,7 +15,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String! @metadata + hello: String! } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/field_values/non_default_nullable/NullablePromise.ts.expected b/src/tests/fixtures/field_values/non_default_nullable/NullablePromise.ts.expected index 84fc92dc..05c16424 100644 --- a/src/tests/fixtures/field_values/non_default_nullable/NullablePromise.ts.expected +++ b/src/tests/fixtures/field_values/non_default_nullable/NullablePromise.ts.expected @@ -15,7 +15,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/generics/complexMutualReferences.ts.expected b/src/tests/fixtures/generics/complexMutualReferences.ts.expected index d35e4c93..af0b52a7 100644 --- a/src/tests/fixtures/generics/complexMutualReferences.ts.expected +++ b/src/tests/fixtures/generics/complexMutualReferences.ts.expected @@ -26,16 +26,16 @@ OUTPUT ----------------- -- SDL -- type Baz { - bazField: BazBar @metadata + bazField: BazBar } type BazBar { - anotherField: BazFoo @metadata + anotherField: BazFoo } type BazFoo { - baz: Baz @metadata - someField: BazBar @metadata + baz: Baz + someField: BazBar } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/generics/connection.ts.expected b/src/tests/fixtures/generics/connection.ts.expected index c63b1dfe..1d3bc501 100644 --- a/src/tests/fixtures/generics/connection.ts.expected +++ b/src/tests/fixtures/generics/connection.ts.expected @@ -46,28 +46,28 @@ OUTPUT ----------------- -- SDL -- type Page { - name: String @metadata + name: String } type PageConnection { - edges: PageEdge @metadata - pageInfo: PageInfo @metadata + edges: PageEdge + pageInfo: PageInfo } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page } type PageInfo { - endCursor: String @metadata - hasNextPage: Boolean @metadata - hasPreviousPage: Boolean @metadata - startCursor: String @metadata + endCursor: String + hasNextPage: Boolean + hasPreviousPage: Boolean + startCursor: String } type User { - pages: PageConnection @metadata + pages: PageConnection } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLBoolean } from "graphql"; diff --git a/src/tests/fixtures/generics/connectionEdgeAsGeneric.ts.expected b/src/tests/fixtures/generics/connectionEdgeAsGeneric.ts.expected index 7ab8d401..b4cb4133 100644 --- a/src/tests/fixtures/generics/connectionEdgeAsGeneric.ts.expected +++ b/src/tests/fixtures/generics/connectionEdgeAsGeneric.ts.expected @@ -46,28 +46,28 @@ OUTPUT ----------------- -- SDL -- type Page { - name: String @metadata + name: String } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page } type PageEdgeConnection { - edges: PageEdge @metadata - pageInfo: PageInfo @metadata + edges: PageEdge + pageInfo: PageInfo } type PageInfo { - endCursor: String @metadata - hasNextPage: Boolean @metadata - hasPreviousPage: Boolean @metadata - startCursor: String @metadata + endCursor: String + hasNextPage: Boolean + hasPreviousPage: Boolean + startCursor: String } type User { - pages: PageEdgeConnection @metadata + pages: PageEdgeConnection } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLBoolean } from "graphql"; diff --git a/src/tests/fixtures/generics/defineFieldOnGeneric.ts.expected b/src/tests/fixtures/generics/defineFieldOnGeneric.ts.expected index f98ebc13..310e4d33 100644 --- a/src/tests/fixtures/generics/defineFieldOnGeneric.ts.expected +++ b/src/tests/fixtures/generics/defineFieldOnGeneric.ts.expected @@ -27,14 +27,14 @@ OUTPUT ----------------- -- SDL -- type Page { - title: String @metadata + title: String } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page """Re-expose title directly on the edge""" - title: String @metadata(exportName: "title", tsModulePath: "grats/src/tests/fixtures/generics/defineFieldOnGeneric.ts") + title: String } -- TypeScript -- import { title as pageEdgeTitleResolver } from "./defineFieldOnGeneric"; diff --git a/src/tests/fixtures/generics/edge.ts.expected b/src/tests/fixtures/generics/edge.ts.expected index ceb6ac8c..b4ab4a72 100644 --- a/src/tests/fixtures/generics/edge.ts.expected +++ b/src/tests/fixtures/generics/edge.ts.expected @@ -40,24 +40,24 @@ OUTPUT ----------------- -- SDL -- type Page { - name: String @metadata + name: String } type PageConnection { - edges: [PageEdge!] @metadata - pageInfo: PageInfo @metadata + edges: [PageEdge!] + pageInfo: PageInfo } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page } type PageInfo { - endCursor: String @metadata - hasNextPage: Boolean @metadata - hasPreviousPage: Boolean @metadata - startCursor: String @metadata + endCursor: String + hasNextPage: Boolean + hasPreviousPage: Boolean + startCursor: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLNonNull, GraphQLBoolean } from "graphql"; diff --git a/src/tests/fixtures/generics/genericInputType.ts.expected b/src/tests/fixtures/generics/genericInputType.ts.expected index d395fc48..0e4aafc8 100644 --- a/src/tests/fixtures/generics/genericInputType.ts.expected +++ b/src/tests/fixtures/generics/genericInputType.ts.expected @@ -32,7 +32,7 @@ input AnotherInputSomeInput { } type SomeClass { - someField(someArg: AnotherInputSomeInput!): String @metadata + someField(someArg: AnotherInputSomeInput!): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/generics/genericInterface.ts.expected b/src/tests/fixtures/generics/genericInterface.ts.expected index f9f7ce40..596266c0 100644 --- a/src/tests/fixtures/generics/genericInterface.ts.expected +++ b/src/tests/fixtures/generics/genericInterface.ts.expected @@ -29,17 +29,17 @@ OUTPUT ----------------- -- SDL -- interface DogFriendly { - to: Dog @metadata + to: Dog } type Dog { - bestFriend: DogFriendly @metadata - name: String @metadata + bestFriend: DogFriendly + name: String } type User implements DogFriendly { - name: String @metadata - to: Dog @metadata + name: String + to: Dog } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/generics/genericOverArg.ts.expected b/src/tests/fixtures/generics/genericOverArg.ts.expected index 60b0bc00..1ac837ad 100644 --- a/src/tests/fixtures/generics/genericOverArg.ts.expected +++ b/src/tests/fixtures/generics/genericOverArg.ts.expected @@ -31,11 +31,11 @@ input SomeInput { } type Query { - someField: SomeInputSomeClass @metadata(exportName: "someField", tsModulePath: "grats/src/tests/fixtures/generics/genericOverArg.ts") + someField: SomeInputSomeClass } type SomeInputSomeClass { - someField(someArg: SomeInput): String @metadata + someField(someArg: SomeInput): String } -- TypeScript -- import { someField as querySomeFieldResolver } from "./genericOverArg"; diff --git a/src/tests/fixtures/generics/genericReferencedMoreThanOnce.ts.expected b/src/tests/fixtures/generics/genericReferencedMoreThanOnce.ts.expected index e1022ab7..6981ac97 100644 --- a/src/tests/fixtures/generics/genericReferencedMoreThanOnce.ts.expected +++ b/src/tests/fixtures/generics/genericReferencedMoreThanOnce.ts.expected @@ -33,16 +33,16 @@ OUTPUT union PageResult = Err | Page type Err { - message: String @metadata + message: String } type Page { - name: String @metadata + name: String } type SomeType { - alsoPageResult: PageResult @metadata - pageResult: PageResult @metadata + alsoPageResult: PageResult + pageResult: PageResult } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/generics/genericTypeUsingClass.ts.expected b/src/tests/fixtures/generics/genericTypeUsingClass.ts.expected index 9db7cbdb..2bb6fb34 100644 --- a/src/tests/fixtures/generics/genericTypeUsingClass.ts.expected +++ b/src/tests/fixtures/generics/genericTypeUsingClass.ts.expected @@ -28,16 +28,16 @@ OUTPUT ----------------- -- SDL -- type Page { - name: String @metadata + name: String } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page } type Query { - createEdge: PageEdge @metadata(exportName: "createEdge", tsModulePath: "grats/src/tests/fixtures/generics/genericTypeUsingClass.ts") + createEdge: PageEdge } -- TypeScript -- import { createEdge as queryCreateEdgeResolver } from "./genericTypeUsingClass"; diff --git a/src/tests/fixtures/generics/genericTypeUsingInterface.ts.expected b/src/tests/fixtures/generics/genericTypeUsingInterface.ts.expected index 0b77684b..b5161608 100644 --- a/src/tests/fixtures/generics/genericTypeUsingInterface.ts.expected +++ b/src/tests/fixtures/generics/genericTypeUsingInterface.ts.expected @@ -28,16 +28,16 @@ OUTPUT ----------------- -- SDL -- type Page { - name: String @metadata + name: String } type PageEdge { - cursor: String @metadata - node: Page @metadata + cursor: String + node: Page } type Query { - createEdge: PageEdge @metadata(exportName: "createEdge", tsModulePath: "grats/src/tests/fixtures/generics/genericTypeUsingInterface.ts") + createEdge: PageEdge } -- TypeScript -- import { createEdge as queryCreateEdgeResolver } from "./genericTypeUsingInterface"; diff --git a/src/tests/fixtures/generics/multiparamGeneric.ts.expected b/src/tests/fixtures/generics/multiparamGeneric.ts.expected index d87cbe55..9759bd72 100644 --- a/src/tests/fixtures/generics/multiparamGeneric.ts.expected +++ b/src/tests/fixtures/generics/multiparamGeneric.ts.expected @@ -33,15 +33,15 @@ OUTPUT union PageErrResult = Err | Page type Err { - error: String @metadata + error: String } type Page { - title: String @metadata + title: String } type Query { - pageResult: PageErrResult @metadata(exportName: "pageResult", tsModulePath: "grats/src/tests/fixtures/generics/multiparamGeneric.ts") + pageResult: PageErrResult } -- TypeScript -- import { pageResult as queryPageResultResolver } from "./multiparamGeneric"; diff --git a/src/tests/fixtures/generics/result.ts.expected b/src/tests/fixtures/generics/result.ts.expected index fcb44a5a..7b89fdfa 100644 --- a/src/tests/fixtures/generics/result.ts.expected +++ b/src/tests/fixtures/generics/result.ts.expected @@ -31,15 +31,15 @@ OUTPUT union PageResult = Err | Page type Err { - message: String @metadata + message: String } type Page { - name: String @metadata + name: String } type SomeType { - pageResult: PageResult @metadata + pageResult: PageResult } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/generics/scalarPassedAsNonGqlGenericArg.ts.expected b/src/tests/fixtures/generics/scalarPassedAsNonGqlGenericArg.ts.expected index 9580564d..21f922d3 100644 --- a/src/tests/fixtures/generics/scalarPassedAsNonGqlGenericArg.ts.expected +++ b/src/tests/fixtures/generics/scalarPassedAsNonGqlGenericArg.ts.expected @@ -18,11 +18,11 @@ OUTPUT ----------------- -- SDL -- type OtherType { - wrapper: Wrapper @metadata + wrapper: Wrapper } type Wrapper { - value: String @metadata + value: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/generics/todo/genericTypeMemberOfUnion.ts.expected b/src/tests/fixtures/generics/todo/genericTypeMemberOfUnion.ts.expected index 8edad882..17a0a3a2 100644 --- a/src/tests/fixtures/generics/todo/genericTypeMemberOfUnion.ts.expected +++ b/src/tests/fixtures/generics/todo/genericTypeMemberOfUnion.ts.expected @@ -25,7 +25,7 @@ OUTPUT ----------------- -- SDL -- type Dog { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/headers/customHeaders.ts.expected b/src/tests/fixtures/headers/customHeaders.ts.expected index b3573050..138834af 100644 --- a/src/tests/fixtures/headers/customHeaders.ts.expected +++ b/src/tests/fixtures/headers/customHeaders.ts.expected @@ -14,7 +14,7 @@ OUTPUT -- SDL -- # Generated SDL type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- // Generated TS diff --git a/src/tests/fixtures/headers/multilineHeader.ts.expected b/src/tests/fixtures/headers/multilineHeader.ts.expected index f7dcaa30..7b5521fd 100644 --- a/src/tests/fixtures/headers/multilineHeader.ts.expected +++ b/src/tests/fixtures/headers/multilineHeader.ts.expected @@ -15,7 +15,7 @@ OUTPUT # Generated SDL # multi-line type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- // Generated TS diff --git a/src/tests/fixtures/input_types/InputType.ts.expected b/src/tests/fixtures/input_types/InputType.ts.expected index 435dc048..01932792 100644 --- a/src/tests/fixtures/input_types/InputType.ts.expected +++ b/src/tests/fixtures/input_types/InputType.ts.expected @@ -21,7 +21,7 @@ input MyInputType { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/InputTypeInterface.ts.expected b/src/tests/fixtures/input_types/InputTypeInterface.ts.expected index d4ee0c14..eaa2b462 100644 --- a/src/tests/fixtures/input_types/InputTypeInterface.ts.expected +++ b/src/tests/fixtures/input_types/InputTypeInterface.ts.expected @@ -23,7 +23,7 @@ input MyInputType { } type User { - myField(input: MyInputType!): String @metadata + myField(input: MyInputType!): String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/InputTypeOptionalField.ts.expected b/src/tests/fixtures/input_types/InputTypeOptionalField.ts.expected index c159edfe..9e6f4488 100644 --- a/src/tests/fixtures/input_types/InputTypeOptionalField.ts.expected +++ b/src/tests/fixtures/input_types/InputTypeOptionalField.ts.expected @@ -21,7 +21,7 @@ input MyInputType { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/InputTypeWithDeprecatedField.ts.expected b/src/tests/fixtures/input_types/InputTypeWithDeprecatedField.ts.expected index 54518a71..fe6a8533 100644 --- a/src/tests/fixtures/input_types/InputTypeWithDeprecatedField.ts.expected +++ b/src/tests/fixtures/input_types/InputTypeWithDeprecatedField.ts.expected @@ -25,7 +25,7 @@ input MyInputType { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/InputTypeWithDescription.ts.expected b/src/tests/fixtures/input_types/InputTypeWithDescription.ts.expected index 99558aee..89644248 100644 --- a/src/tests/fixtures/input_types/InputTypeWithDescription.ts.expected +++ b/src/tests/fixtures/input_types/InputTypeWithDescription.ts.expected @@ -25,7 +25,7 @@ input MyInputType { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/InputTypeWithFieldDescription.ts.expected b/src/tests/fixtures/input_types/InputTypeWithFieldDescription.ts.expected index 90ca9215..92bd92cd 100644 --- a/src/tests/fixtures/input_types/InputTypeWithFieldDescription.ts.expected +++ b/src/tests/fixtures/input_types/InputTypeWithFieldDescription.ts.expected @@ -23,7 +23,7 @@ input MyInputType { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/input_types/RenamedInputType.ts.expected b/src/tests/fixtures/input_types/RenamedInputType.ts.expected index f088e278..9ab5c957 100644 --- a/src/tests/fixtures/input_types/RenamedInputType.ts.expected +++ b/src/tests/fixtures/input_types/RenamedInputType.ts.expected @@ -21,7 +21,7 @@ input OtherName { } type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/FieldReturnsInterface.ts.expected b/src/tests/fixtures/interfaces/FieldReturnsInterface.ts.expected index ad5ac84a..c4a86aa5 100644 --- a/src/tests/fixtures/interfaces/FieldReturnsInterface.ts.expected +++ b/src/tests/fixtures/interfaces/FieldReturnsInterface.ts.expected @@ -27,15 +27,15 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type SomeType { - me: Person @metadata + me: Person } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/IgnoresExtendsClause.ts.expected b/src/tests/fixtures/interfaces/IgnoresExtendsClause.ts.expected index 6ea023a0..97e93528 100644 --- a/src/tests/fixtures/interfaces/IgnoresExtendsClause.ts.expected +++ b/src/tests/fixtures/interfaces/IgnoresExtendsClause.ts.expected @@ -31,15 +31,15 @@ OUTPUT ----------------- -- SDL -- interface Actor { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements Actor { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/ImplementsInterface.ts.expected b/src/tests/fixtures/interfaces/ImplementsInterface.ts.expected index 5bfdfa6b..ac482c20 100644 --- a/src/tests/fixtures/interfaces/ImplementsInterface.ts.expected +++ b/src/tests/fixtures/interfaces/ImplementsInterface.ts.expected @@ -27,15 +27,15 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/ImplementsInterfaceWithTypeParam.ts.expected b/src/tests/fixtures/interfaces/ImplementsInterfaceWithTypeParam.ts.expected index c49a4fe0..0c0fbc18 100644 --- a/src/tests/fixtures/interfaces/ImplementsInterfaceWithTypeParam.ts.expected +++ b/src/tests/fixtures/interfaces/ImplementsInterfaceWithTypeParam.ts.expected @@ -31,15 +31,15 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/ImplementsMultipleInterfaces.ts.expected b/src/tests/fixtures/interfaces/ImplementsMultipleInterfaces.ts.expected index 7212b01f..272807f7 100644 --- a/src/tests/fixtures/interfaces/ImplementsMultipleInterfaces.ts.expected +++ b/src/tests/fixtures/interfaces/ImplementsMultipleInterfaces.ts.expected @@ -33,19 +33,19 @@ OUTPUT ----------------- -- SDL -- interface Actor { - name: String @metadata + name: String } interface Person { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements Actor & Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/ImplementsRenamedInterface.ts.expected b/src/tests/fixtures/interfaces/ImplementsRenamedInterface.ts.expected index ff9c061a..16852311 100644 --- a/src/tests/fixtures/interfaces/ImplementsRenamedInterface.ts.expected +++ b/src/tests/fixtures/interfaces/ImplementsRenamedInterface.ts.expected @@ -19,11 +19,11 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceDefinitionExtendsGqlInterface.ts.expected b/src/tests/fixtures/interfaces/InterfaceDefinitionExtendsGqlInterface.ts.expected index 4754c8df..e77999fa 100644 --- a/src/tests/fixtures/interfaces/InterfaceDefinitionExtendsGqlInterface.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceDefinitionExtendsGqlInterface.ts.expected @@ -34,17 +34,17 @@ OUTPUT ----------------- -- SDL -- interface Mammal { - legs: Int @metadata + legs: Int } interface Person implements Mammal { - legs: Int @metadata - name: String @metadata + legs: Int + name: String } interface User implements Mammal & Person { - legs: Int @metadata - name: String @metadata + legs: Int + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLInt, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceExtendsInterface.ts.expected b/src/tests/fixtures/interfaces/InterfaceExtendsInterface.ts.expected index a4fbdc75..f722ceb9 100644 --- a/src/tests/fixtures/interfaces/InterfaceExtendsInterface.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceExtendsInterface.ts.expected @@ -26,16 +26,16 @@ OUTPUT ----------------- -- SDL -- interface Actor implements Node & Person { - id: String @metadata - name: String @metadata + id: String + name: String } interface Node { - id: String @metadata + id: String } interface Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceFieldWithDescription.ts.expected b/src/tests/fixtures/interfaces/InterfaceFieldWithDescription.ts.expected index 8a479d4f..483159d3 100644 --- a/src/tests/fixtures/interfaces/InterfaceFieldWithDescription.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceFieldWithDescription.ts.expected @@ -31,15 +31,15 @@ OUTPUT -- SDL -- interface IPerson { """The person's name""" - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements IPerson { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceFieldWithDescriptionThatDiffersFromType.ts.expected b/src/tests/fixtures/interfaces/InterfaceFieldWithDescriptionThatDiffersFromType.ts.expected index f8fd01dc..69795873 100644 --- a/src/tests/fixtures/interfaces/InterfaceFieldWithDescriptionThatDiffersFromType.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceFieldWithDescriptionThatDiffersFromType.ts.expected @@ -34,16 +34,16 @@ OUTPUT -- SDL -- interface IPerson { """The person's name""" - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements IPerson { """The user's name""" - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceImplementsInterface.ts.expected b/src/tests/fixtures/interfaces/InterfaceImplementsInterface.ts.expected index a4fbdc75..f722ceb9 100644 --- a/src/tests/fixtures/interfaces/InterfaceImplementsInterface.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceImplementsInterface.ts.expected @@ -26,16 +26,16 @@ OUTPUT ----------------- -- SDL -- interface Actor implements Node & Person { - id: String @metadata - name: String @metadata + id: String + name: String } interface Node { - id: String @metadata + id: String } interface Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceMergedIntoObject.invalid.ts.expected b/src/tests/fixtures/interfaces/InterfaceMergedIntoObject.invalid.ts.expected index 86de6340..cfc984d8 100644 --- a/src/tests/fixtures/interfaces/InterfaceMergedIntoObject.invalid.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceMergedIntoObject.invalid.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- interface Foo { - id: String @metadata + id: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceWithCustomName.ts.expected b/src/tests/fixtures/interfaces/InterfaceWithCustomName.ts.expected index fe5ebb61..8ad54388 100644 --- a/src/tests/fixtures/interfaces/InterfaceWithCustomName.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceWithCustomName.ts.expected @@ -27,15 +27,15 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceWithDeprecatedField.ts.expected b/src/tests/fixtures/interfaces/InterfaceWithDeprecatedField.ts.expected index b5990ff8..57112ef7 100644 --- a/src/tests/fixtures/interfaces/InterfaceWithDeprecatedField.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceWithDeprecatedField.ts.expected @@ -29,15 +29,15 @@ OUTPUT ----------------- -- SDL -- interface IPerson { - name: String @deprecated(reason: "Not used anymore") @metadata + name: String @deprecated(reason: "Not used anymore") } type SomeType { - me: User @metadata + me: User } type User implements IPerson { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/InterfaceWithDescription.ts.expected b/src/tests/fixtures/interfaces/InterfaceWithDescription.ts.expected index e438362c..722d6717 100644 --- a/src/tests/fixtures/interfaces/InterfaceWithDescription.ts.expected +++ b/src/tests/fixtures/interfaces/InterfaceWithDescription.ts.expected @@ -32,15 +32,15 @@ OUTPUT -- SDL -- """An interface describing the common elements of all people types.""" interface IPerson { - name: String @metadata + name: String } type SomeType { - me: User @metadata + me: User } type User implements IPerson { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/interfaces/extendInterfaceWithNonGqlType.ts.expected b/src/tests/fixtures/interfaces/extendInterfaceWithNonGqlType.ts.expected index 4d87cf8f..60420596 100644 --- a/src/tests/fixtures/interfaces/extendInterfaceWithNonGqlType.ts.expected +++ b/src/tests/fixtures/interfaces/extendInterfaceWithNonGqlType.ts.expected @@ -18,7 +18,7 @@ OUTPUT ----------------- -- SDL -- interface IPerson { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/regression/fieldFollowedByDeprecated.ts.expected b/src/tests/fixtures/regression/fieldFollowedByDeprecated.ts.expected index e6eb4a8f..2140189e 100644 --- a/src/tests/fixtures/regression/fieldFollowedByDeprecated.ts.expected +++ b/src/tests/fixtures/regression/fieldFollowedByDeprecated.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- type User { - name: String @deprecated @metadata(name: "graphQLName") + name: String @deprecated } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/ClassMethodWithContextValue.ts.expected b/src/tests/fixtures/resolver_context/ClassMethodWithContextValue.ts.expected index c9c6df9d..4ead0dc6 100644 --- a/src/tests/fixtures/resolver_context/ClassMethodWithContextValue.ts.expected +++ b/src/tests/fixtures/resolver_context/ClassMethodWithContextValue.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/ClassMethodWithContextValueExported.ts.expected b/src/tests/fixtures/resolver_context/ClassMethodWithContextValueExported.ts.expected index 0b809f39..985135e1 100644 --- a/src/tests/fixtures/resolver_context/ClassMethodWithContextValueExported.ts.expected +++ b/src/tests/fixtures/resolver_context/ClassMethodWithContextValueExported.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/ContextValueBeforeArgs.ts.expected b/src/tests/fixtures/resolver_context/ContextValueBeforeArgs.ts.expected index 2d7a422a..1f403c94 100644 --- a/src/tests/fixtures/resolver_context/ContextValueBeforeArgs.ts.expected +++ b/src/tests/fixtures/resolver_context/ContextValueBeforeArgs.ts.expected @@ -17,7 +17,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting(fallbackGreeting: String!): String @metadata + greeting(fallbackGreeting: String!): String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/ContextValueOptional.ts.expected b/src/tests/fixtures/resolver_context/ContextValueOptional.ts.expected index aa89e735..c3e90748 100644 --- a/src/tests/fixtures/resolver_context/ContextValueOptional.ts.expected +++ b/src/tests/fixtures/resolver_context/ContextValueOptional.ts.expected @@ -20,7 +20,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/ContextValueReadTwice.ts.expected b/src/tests/fixtures/resolver_context/ContextValueReadTwice.ts.expected index 591af0d1..45b9d893 100644 --- a/src/tests/fixtures/resolver_context/ContextValueReadTwice.ts.expected +++ b/src/tests/fixtures/resolver_context/ContextValueReadTwice.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_context/FunctionWithContextValue.ts.expected b/src/tests/fixtures/resolver_context/FunctionWithContextValue.ts.expected index 4cb44f48..e37280dc 100644 --- a/src/tests/fixtures/resolver_context/FunctionWithContextValue.ts.expected +++ b/src/tests/fixtures/resolver_context/FunctionWithContextValue.ts.expected @@ -19,7 +19,7 @@ OUTPUT ----------------- -- SDL -- type User { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/resolver_context/FunctionWithContextValue.ts") + greeting: String } -- TypeScript -- import { greeting as userGreetingResolver } from "./FunctionWithContextValue"; diff --git a/src/tests/fixtures/resolver_context/MultipleClassMethodsReferencingContextValue.ts.expected b/src/tests/fixtures/resolver_context/MultipleClassMethodsReferencingContextValue.ts.expected index 642d5696..7feaa158 100644 --- a/src/tests/fixtures/resolver_context/MultipleClassMethodsReferencingContextValue.ts.expected +++ b/src/tests/fixtures/resolver_context/MultipleClassMethodsReferencingContextValue.ts.expected @@ -24,8 +24,8 @@ OUTPUT ----------------- -- SDL -- type SomeType { - alsoGreeting: String @metadata - greeting: String @metadata + alsoGreeting: String + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_info/ClassMethodWithInfoValue.ts.expected b/src/tests/fixtures/resolver_info/ClassMethodWithInfoValue.ts.expected index 0bdecbf1..985c2d9f 100644 --- a/src/tests/fixtures/resolver_info/ClassMethodWithInfoValue.ts.expected +++ b/src/tests/fixtures/resolver_info/ClassMethodWithInfoValue.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(name: "greetz") + greeting: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/resolver_info/FunctionWithInfoValue.ts.expected b/src/tests/fixtures/resolver_info/FunctionWithInfoValue.ts.expected index 1427893a..ad7ba50d 100644 --- a/src/tests/fixtures/resolver_info/FunctionWithInfoValue.ts.expected +++ b/src/tests/fixtures/resolver_info/FunctionWithInfoValue.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type Query { - greetz: String @metadata(exportName: "greetz", tsModulePath: "grats/src/tests/fixtures/resolver_info/FunctionWithInfoValue.ts") + greetz: String } -- TypeScript -- import { greetz as queryGreetzResolver } from "./FunctionWithInfoValue"; diff --git a/src/tests/fixtures/resolver_info/StaticMethodWithInfoValue.ts.expected b/src/tests/fixtures/resolver_info/StaticMethodWithInfoValue.ts.expected index 4be7c32d..fa6be127 100644 --- a/src/tests/fixtures/resolver_info/StaticMethodWithInfoValue.ts.expected +++ b/src/tests/fixtures/resolver_info/StaticMethodWithInfoValue.ts.expected @@ -22,11 +22,11 @@ OUTPUT ----------------- -- SDL -- type Query { - greeting: String @metadata(exportName: "SomeType", name: "greetz", tsModulePath: "grats/src/tests/fixtures/resolver_info/StaticMethodWithInfoValue.ts") + greeting: String } type SomeType { - someField: String @metadata + someField: String } -- TypeScript -- import { SomeType as queryGreetingResolver } from "./StaticMethodWithInfoValue"; diff --git a/src/tests/fixtures/semantic_nullability/semanticNonNull.ts.expected b/src/tests/fixtures/semantic_nullability/semanticNonNull.ts.expected index ad5c5698..92ffb25f 100644 --- a/src/tests/fixtures/semantic_nullability/semanticNonNull.ts.expected +++ b/src/tests/fixtures/semantic_nullability/semanticNonNull.ts.expected @@ -52,10 +52,10 @@ Passing a negative level or a level greater than the list dimension is an error. directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION type User { - name: String @metadata @semanticNonNull + name: String @semanticNonNull } -- TypeScript -- -import { GraphQLSchema, GraphQLObjectType, GraphQLString, defaultFieldResolver } from "graphql"; +import { defaultFieldResolver, GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; async function assertNonNull(value: T | Promise): Promise { const awaited = await value; if (awaited == null) diff --git a/src/tests/fixtures/semantic_nullability/semanticNonNullMatchesInterface.ts.expected b/src/tests/fixtures/semantic_nullability/semanticNonNullMatchesInterface.ts.expected index d5213cff..7a408e5c 100644 --- a/src/tests/fixtures/semantic_nullability/semanticNonNullMatchesInterface.ts.expected +++ b/src/tests/fixtures/semantic_nullability/semanticNonNullMatchesInterface.ts.expected @@ -59,14 +59,14 @@ Passing a negative level or a level greater than the list dimension is an error. directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION interface IPerson { - name: String @metadata @semanticNonNull + name: String @semanticNonNull } type User implements IPerson { - name: String @metadata @semanticNonNull + name: String @semanticNonNull } -- TypeScript -- -import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType, defaultFieldResolver } from "graphql"; +import { defaultFieldResolver, GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; async function assertNonNull(value: T | Promise): Promise { const awaited = await value; if (awaited == null) diff --git a/src/tests/fixtures/semantic_nullability/semanticNull.ts.expected b/src/tests/fixtures/semantic_nullability/semanticNull.ts.expected index e24a5a2c..8f0604f6 100644 --- a/src/tests/fixtures/semantic_nullability/semanticNull.ts.expected +++ b/src/tests/fixtures/semantic_nullability/semanticNull.ts.expected @@ -55,7 +55,7 @@ Passing a negative level or a level greater than the list dimension is an error. directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/subscriptions/InterfaceWithAsyncIterable.ts.expected b/src/tests/fixtures/subscriptions/InterfaceWithAsyncIterable.ts.expected index d87e3eed..ce1827bb 100644 --- a/src/tests/fixtures/subscriptions/InterfaceWithAsyncIterable.ts.expected +++ b/src/tests/fixtures/subscriptions/InterfaceWithAsyncIterable.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- interface NotSubscription { - greetings: [String!] @metadata + greetings: [String!] } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/subscriptions/NonSubscriptionClassWithAsyncIterable.ts.expected b/src/tests/fixtures/subscriptions/NonSubscriptionClassWithAsyncIterable.ts.expected index a89dd791..01b29426 100644 --- a/src/tests/fixtures/subscriptions/NonSubscriptionClassWithAsyncIterable.ts.expected +++ b/src/tests/fixtures/subscriptions/NonSubscriptionClassWithAsyncIterable.ts.expected @@ -30,10 +30,10 @@ OUTPUT ----------------- -- SDL -- type User { - greetings: [String!]! @metadata - greetingsMaybe: [String]! @metadata - maybeGreetings: [String!] @metadata - maybeGreetingsMaybe: [String] @metadata + greetings: [String!]! + greetingsMaybe: [String]! + maybeGreetings: [String!] + maybeGreetingsMaybe: [String] } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts.expected b/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts.expected index 5a74beda..38b3616e 100644 --- a/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts.expected +++ b/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts.expected @@ -40,16 +40,13 @@ OUTPUT ----------------- -- SDL -- type Subscription { - greetings: String! @metadata(exportName: "greetings", tsModulePath: "grats/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts") - greetingsMaybe: String @metadata(exportName: "greetingsMaybe", tsModulePath: "grats/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts") - maybeGreetings: String @metadata(exportName: "maybeGreetings", tsModulePath: "grats/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts") - maybeGreetingsMaybe: String @metadata(exportName: "maybeGreetingsMaybe", tsModulePath: "grats/src/tests/fixtures/subscriptions/SubscriptionFunctionFieldWithAsyncIterable.ts") + greetings: String! + greetingsMaybe: String + maybeGreetings: String + maybeGreetingsMaybe: String } -- TypeScript -- -import { greetings as subscriptionGreetingsResolver } from "./SubscriptionFunctionFieldWithAsyncIterable"; -import { greetingsMaybe as subscriptionGreetingsMaybeResolver } from "./SubscriptionFunctionFieldWithAsyncIterable"; -import { maybeGreetings as subscriptionMaybeGreetingsResolver } from "./SubscriptionFunctionFieldWithAsyncIterable"; -import { maybeGreetingsMaybe as subscriptionMaybeGreetingsMaybeResolver } from "./SubscriptionFunctionFieldWithAsyncIterable"; +import { greetings as subscriptionGreetingsResolver, greetingsMaybe as subscriptionGreetingsMaybeResolver, maybeGreetings as subscriptionMaybeGreetingsResolver, maybeGreetingsMaybe as subscriptionMaybeGreetingsMaybeResolver } from "./SubscriptionFunctionFieldWithAsyncIterable"; import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const SubscriptionType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/fixtures/todo/EnumFromUnionTypeWithVariantWithDescription.ts.expected b/src/tests/fixtures/todo/EnumFromUnionTypeWithVariantWithDescription.ts.expected index 583f511d..b6dd02ea 100644 --- a/src/tests/fixtures/todo/EnumFromUnionTypeWithVariantWithDescription.ts.expected +++ b/src/tests/fixtures/todo/EnumFromUnionTypeWithVariantWithDescription.ts.expected @@ -24,7 +24,7 @@ enum MyEnum { } type SomeType { - hello: MyEnum @metadata + hello: MyEnum } -- TypeScript -- import { GraphQLSchema, GraphQLEnumType, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/todo/userExample.ts.expected b/src/tests/fixtures/todo/userExample.ts.expected index 0c971b20..10b51c2b 100644 --- a/src/tests/fixtures/todo/userExample.ts.expected +++ b/src/tests/fixtures/todo/userExample.ts.expected @@ -27,17 +27,16 @@ OUTPUT ----------------- -- SDL -- type SomeType { - me: User @metadata(exportName: "me", tsModulePath: "grats/src/tests/fixtures/todo/userExample.ts") + me: User } type User { - firstName: String @metadata - fullName: String @metadata(exportName: "fullName", tsModulePath: "grats/src/tests/fixtures/todo/userExample.ts") - lastName: String @metadata + firstName: String + fullName: String + lastName: String } -- TypeScript -- -import { fullName as userFullNameResolver } from "./userExample"; -import { me as someTypeMeResolver } from "./userExample"; +import { fullName as userFullNameResolver, me as someTypeMeResolver } from "./userExample"; import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const UserType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/fixtures/type_definitions/ClassImplementsNonGqlInterface.ts.expected b/src/tests/fixtures/type_definitions/ClassImplementsNonGqlInterface.ts.expected index 8934abd1..d1726422 100644 --- a/src/tests/fixtures/type_definitions/ClassImplementsNonGqlInterface.ts.expected +++ b/src/tests/fixtures/type_definitions/ClassImplementsNonGqlInterface.ts.expected @@ -20,7 +20,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type User { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions/ClassWithDescription.ts.expected b/src/tests/fixtures/type_definitions/ClassWithDescription.ts.expected index 0fb62abb..16b161f1 100644 --- a/src/tests/fixtures/type_definitions/ClassWithDescription.ts.expected +++ b/src/tests/fixtures/type_definitions/ClassWithDescription.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions/ClassWithDescriptionAndCustomName.ts.expected b/src/tests/fixtures/type_definitions/ClassWithDescriptionAndCustomName.ts.expected index 29444824..ee9aed25 100644 --- a/src/tests/fixtures/type_definitions/ClassWithDescriptionAndCustomName.ts.expected +++ b/src/tests/fixtures/type_definitions/ClassWithDescriptionAndCustomName.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions/RenamedType.ts.expected b/src/tests/fixtures/type_definitions/RenamedType.ts.expected index e2c8fa57..9f191fe9 100644 --- a/src/tests/fixtures/type_definitions/RenamedType.ts.expected +++ b/src/tests/fixtures/type_definitions/RenamedType.ts.expected @@ -16,7 +16,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsInterface.ts.expected b/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsInterface.ts.expected index 4b197768..70e7c4ae 100644 --- a/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsInterface.ts.expected +++ b/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsInterface.ts.expected @@ -19,11 +19,11 @@ OUTPUT ----------------- -- SDL -- interface Person { - hello: String @metadata + hello: String } type User implements Person { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsMultipleInterfaces.ts.expected b/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsMultipleInterfaces.ts.expected index e00711d9..ea4a8914 100644 --- a/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsMultipleInterfaces.ts.expected +++ b/src/tests/fixtures/type_definitions/TypeFromClassDefinitionImplementsMultipleInterfaces.ts.expected @@ -28,16 +28,16 @@ OUTPUT ----------------- -- SDL -- interface Node { - id: String @metadata + id: String } interface Person { - hello: String @metadata + hello: String } type User implements Node & Person { - hello: String @metadata - id: String @metadata + hello: String + id: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_alias/AliasOfUnknownDefinesType.ts.expected b/src/tests/fixtures/type_definitions_from_alias/AliasOfUnknownDefinesType.ts.expected index dcd41f15..273cf2d4 100644 --- a/src/tests/fixtures/type_definitions_from_alias/AliasOfUnknownDefinesType.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/AliasOfUnknownDefinesType.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - greeting: String @metadata(exportName: "greeting", tsModulePath: "grats/src/tests/fixtures/type_definitions_from_alias/AliasOfUnknownDefinesType.ts") + greeting: String } -- TypeScript -- import { greeting as someTypeGreetingResolver } from "./AliasOfUnknownDefinesType"; diff --git a/src/tests/fixtures/type_definitions_from_alias/AliasType.ts.expected b/src/tests/fixtures/type_definitions_from_alias/AliasType.ts.expected index 6e9588dc..3c847830 100644 --- a/src/tests/fixtures/type_definitions_from_alias/AliasType.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/AliasType.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_alias/AliasWithDescription.ts.expected b/src/tests/fixtures/type_definitions_from_alias/AliasWithDescription.ts.expected index 1dc06ba2..404a1a30 100644 --- a/src/tests/fixtures/type_definitions_from_alias/AliasWithDescription.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/AliasWithDescription.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_alias/AliasWithDescriptionAndCustomName.ts.expected b/src/tests/fixtures/type_definitions_from_alias/AliasWithDescriptionAndCustomName.ts.expected index 26f8f577..d70cec8b 100644 --- a/src/tests/fixtures/type_definitions_from_alias/AliasWithDescriptionAndCustomName.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/AliasWithDescriptionAndCustomName.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_alias/QueryAsAliasOfUnknown.ts.expected b/src/tests/fixtures/type_definitions_from_alias/QueryAsAliasOfUnknown.ts.expected index 3b2b2742..0601c6b2 100644 --- a/src/tests/fixtures/type_definitions_from_alias/QueryAsAliasOfUnknown.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/QueryAsAliasOfUnknown.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type Query { - foo: String @metadata(exportName: "foo", tsModulePath: "grats/src/tests/fixtures/type_definitions_from_alias/QueryAsAliasOfUnknown.ts") + foo: String } -- TypeScript -- import { foo as queryFooResolver } from "./QueryAsAliasOfUnknown"; diff --git a/src/tests/fixtures/type_definitions_from_alias/RenamedType.ts.expected b/src/tests/fixtures/type_definitions_from_alias/RenamedType.ts.expected index 3a9d8297..882bb72f 100644 --- a/src/tests/fixtures/type_definitions_from_alias/RenamedType.ts.expected +++ b/src/tests/fixtures/type_definitions_from_alias/RenamedType.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/InterfaceType.ts.expected b/src/tests/fixtures/type_definitions_from_interface/InterfaceType.ts.expected index 6e5cc9f6..2124343f 100644 --- a/src/tests/fixtures/type_definitions_from_interface/InterfaceType.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/InterfaceType.ts.expected @@ -12,7 +12,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeExtendsGqlInterface.ts.expected b/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeExtendsGqlInterface.ts.expected index 7d34ed06..553173e5 100644 --- a/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeExtendsGqlInterface.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeExtendsGqlInterface.ts.expected @@ -20,11 +20,11 @@ OUTPUT ----------------- -- SDL -- interface Person { - name: String @metadata + name: String } type User implements Person { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeImplementsInterface.ts.expected b/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeImplementsInterface.ts.expected index b87edd9d..14eb4cb8 100644 --- a/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeImplementsInterface.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/InterfaceTypeImplementsInterface.ts.expected @@ -22,12 +22,12 @@ OUTPUT ----------------- -- SDL -- interface HasName { - name: String @metadata + name: String } type User implements HasName { - hello: String @metadata - name: String @metadata + hello: String + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescription.ts.expected b/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescription.ts.expected index b2efcabd..44fe757b 100644 --- a/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescription.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescription.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescriptionAndCustomName.ts.expected b/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescriptionAndCustomName.ts.expected index d937860d..f139fc43 100644 --- a/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescriptionAndCustomName.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/InterfaceWithDescriptionAndCustomName.ts.expected @@ -17,7 +17,7 @@ OUTPUT -- SDL -- """The root of all evil.""" type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/type_definitions_from_interface/RenamedType.ts.expected b/src/tests/fixtures/type_definitions_from_interface/RenamedType.ts.expected index 1f971a73..a2aae648 100644 --- a/src/tests/fixtures/type_definitions_from_interface/RenamedType.ts.expected +++ b/src/tests/fixtures/type_definitions_from_interface/RenamedType.ts.expected @@ -14,7 +14,7 @@ OUTPUT ----------------- -- SDL -- type SomeType { - hello: String @metadata + hello: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/typename/PropertySignatureTypename.ts.expected b/src/tests/fixtures/typename/PropertySignatureTypename.ts.expected index 60f86532..09932219 100644 --- a/src/tests/fixtures/typename/PropertySignatureTypename.ts.expected +++ b/src/tests/fixtures/typename/PropertySignatureTypename.ts.expected @@ -19,11 +19,11 @@ OUTPUT ----------------- -- SDL -- interface IPerson { - name: String @metadata + name: String } type User implements IPerson { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; diff --git a/src/tests/fixtures/typename/PropertyTypename.ts.expected b/src/tests/fixtures/typename/PropertyTypename.ts.expected index a608c5ed..3ebd0ce8 100644 --- a/src/tests/fixtures/typename/PropertyTypename.ts.expected +++ b/src/tests/fixtures/typename/PropertyTypename.ts.expected @@ -13,7 +13,7 @@ OUTPUT ----------------- -- SDL -- type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/unions/DefineUnionType.ts.expected b/src/tests/fixtures/unions/DefineUnionType.ts.expected index 395d509f..47b5ed1f 100644 --- a/src/tests/fixtures/unions/DefineUnionType.ts.expected +++ b/src/tests/fixtures/unions/DefineUnionType.ts.expected @@ -31,15 +31,15 @@ OUTPUT union Actor = Entity | User type Entity { - description: String @metadata + description: String } type SomeType { - me: Actor @metadata + me: Actor } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/unions/DefineUnionTypeWithInterfaces.ts.expected b/src/tests/fixtures/unions/DefineUnionTypeWithInterfaces.ts.expected index 37cdff56..7fecbbb1 100644 --- a/src/tests/fixtures/unions/DefineUnionTypeWithInterfaces.ts.expected +++ b/src/tests/fixtures/unions/DefineUnionTypeWithInterfaces.ts.expected @@ -31,15 +31,15 @@ OUTPUT union Actor = Entity | User type Entity { - description: String @metadata + description: String } type SomeType { - me: Actor @metadata + me: Actor } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/unions/DefineUnionTypeWithTypeLiterals.ts.expected b/src/tests/fixtures/unions/DefineUnionTypeWithTypeLiterals.ts.expected index 42fd4ece..4c1f4dfb 100644 --- a/src/tests/fixtures/unions/DefineUnionTypeWithTypeLiterals.ts.expected +++ b/src/tests/fixtures/unions/DefineUnionTypeWithTypeLiterals.ts.expected @@ -31,15 +31,15 @@ OUTPUT union Actor = Entity | User type Entity { - description: String @metadata + description: String } type SomeType { - me: Actor @metadata + me: Actor } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/fixtures/unions/UnionWithDescription.ts.expected b/src/tests/fixtures/unions/UnionWithDescription.ts.expected index 9134ed6a..863344b7 100644 --- a/src/tests/fixtures/unions/UnionWithDescription.ts.expected +++ b/src/tests/fixtures/unions/UnionWithDescription.ts.expected @@ -35,15 +35,15 @@ OUTPUT union Actor = Entity | User type Entity { - description: String @metadata + description: String } type SomeType { - me: Actor @metadata + me: Actor } type User { - name: String @metadata + name: String } -- TypeScript -- import { GraphQLSchema, GraphQLUnionType, GraphQLObjectType, GraphQLString } from "graphql"; diff --git a/src/tests/integrationFixtures/complexPlurals/schema.ts b/src/tests/integrationFixtures/complexPlurals/schema.ts index d0b659e4..20ed285f 100644 --- a/src/tests/integrationFixtures/complexPlurals/schema.ts +++ b/src/tests/integrationFixtures/complexPlurals/schema.ts @@ -1,6 +1,4 @@ -import { arrayOfArrayOfPromises as queryArrayOfArrayOfPromisesResolver } from "./index"; -import { arrayOfPromises as queryArrayOfPromisesResolver } from "./index"; -import { asyncIterableOfArrayOfPromises as queryAsyncIterableOfArrayOfPromisesResolver } from "./index"; +import { arrayOfArrayOfPromises as queryArrayOfArrayOfPromisesResolver, arrayOfPromises as queryArrayOfPromisesResolver, asyncIterableOfArrayOfPromises as queryAsyncIterableOfArrayOfPromisesResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const QueryType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/integrationFixtures/deprecated/schema.ts b/src/tests/integrationFixtures/deprecated/schema.ts index 81beda53..05839c96 100644 --- a/src/tests/integrationFixtures/deprecated/schema.ts +++ b/src/tests/integrationFixtures/deprecated/schema.ts @@ -1,5 +1,4 @@ -import { goodBye as queryGoodByeResolver } from "./index"; -import { hello as queryHelloResolver } from "./index"; +import { goodBye as queryGoodByeResolver, hello as queryHelloResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const QueryType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/integrationFixtures/killsParentOnExceptionOnQuery/schema.ts b/src/tests/integrationFixtures/killsParentOnExceptionOnQuery/schema.ts index 25b4b5a8..2f46859e 100644 --- a/src/tests/integrationFixtures/killsParentOnExceptionOnQuery/schema.ts +++ b/src/tests/integrationFixtures/killsParentOnExceptionOnQuery/schema.ts @@ -1,5 +1,4 @@ -import { alwaysThrowsKillsParentOnException as queryAlwaysThrowsKillsParentOnExceptionResolver } from "./index"; -import { hello as queryHelloResolver } from "./index"; +import { alwaysThrowsKillsParentOnException as queryAlwaysThrowsKillsParentOnExceptionResolver, hello as queryHelloResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const QueryType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/integrationFixtures/nonNullableListIncludesNull/schema.ts b/src/tests/integrationFixtures/nonNullableListIncludesNull/schema.ts index aa3b064d..27c86650 100644 --- a/src/tests/integrationFixtures/nonNullableListIncludesNull/schema.ts +++ b/src/tests/integrationFixtures/nonNullableListIncludesNull/schema.ts @@ -1,5 +1,4 @@ -import { someList as querySomeListResolver } from "./index"; -import { someListOfLists as querySomeListOfListsResolver } from "./index"; +import { someList as querySomeListResolver, someListOfLists as querySomeListOfListsResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const QueryType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/integrationFixtures/promiseOfPromise/schema.ts b/src/tests/integrationFixtures/promiseOfPromise/schema.ts index 0789f9cf..257566f1 100644 --- a/src/tests/integrationFixtures/promiseOfPromise/schema.ts +++ b/src/tests/integrationFixtures/promiseOfPromise/schema.ts @@ -1,5 +1,4 @@ -import { promiseOfPromise as queryPromiseOfPromiseResolver } from "./index"; -import { promiseOfPromisePromise as queryPromiseOfPromisePromiseResolver } from "./index"; +import { promiseOfPromise as queryPromiseOfPromiseResolver, promiseOfPromisePromise as queryPromiseOfPromisePromiseResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const QueryType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/src/tests/integrationFixtures/resolveTypeViaClass/schema.ts b/src/tests/integrationFixtures/resolveTypeViaClass/schema.ts index e59de4f1..b3f0eb0b 100644 --- a/src/tests/integrationFixtures/resolveTypeViaClass/schema.ts +++ b/src/tests/integrationFixtures/resolveTypeViaClass/schema.ts @@ -1,8 +1,5 @@ import DefaultNodeClass from "./index"; -import { Guest as GuestClass } from "./index"; -import { ThisNameGetsIgnored as RenamedNodeClass } from "./index"; -import { User as UserClass } from "./index"; -import { node as queryNodeResolver } from "./index"; +import { Guest as GuestClass, ThisNameGetsIgnored as RenamedNodeClass, User as UserClass, node as queryNodeResolver } from "./index"; import { GraphQLSchema, GraphQLObjectType, GraphQLInterfaceType, GraphQLID, GraphQLNonNull } from "graphql"; export function getSchema(): GraphQLSchema { const GqlNodeType: GraphQLInterfaceType = new GraphQLInterfaceType({ diff --git a/src/tests/integrationFixtures/strictSemanticNullability/schema.ts b/src/tests/integrationFixtures/strictSemanticNullability/schema.ts index d9b62ea4..fda93a90 100644 --- a/src/tests/integrationFixtures/strictSemanticNullability/schema.ts +++ b/src/tests/integrationFixtures/strictSemanticNullability/schema.ts @@ -1,8 +1,5 @@ -import { actuallyReturnsAsyncNull as queryActuallyReturnsAsyncNullResolver } from "./index"; -import { actuallyReturnsNull as queryActuallyReturnsNullResolver } from "./index"; -import { me as queryMeResolver } from "./index"; -import { names as subscriptionNamesResolver } from "./index"; -import { GraphQLSchema, GraphQLObjectType, GraphQLString, defaultFieldResolver, GraphQLInterfaceType } from "graphql"; +import { actuallyReturnsAsyncNull as queryActuallyReturnsAsyncNullResolver, actuallyReturnsNull as queryActuallyReturnsNullResolver, me as queryMeResolver, names as subscriptionNamesResolver } from "./index"; +import { defaultFieldResolver, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInterfaceType } from "graphql"; async function assertNonNull(value: T | Promise): Promise { const awaited = await value; if (awaited == null) diff --git a/src/tests/test.ts b/src/tests/test.ts index 16119fdc..cb762eed 100644 --- a/src/tests/test.ts +++ b/src/tests/test.ts @@ -17,9 +17,8 @@ import { Command } from "commander"; import { locate } from "../Locate"; import { gqlErr, ReportableDiagnostics } from "../utils/DiagnosticError"; import { writeFileSync } from "fs"; -import { codegen } from "../codegen"; +import { codegen } from "../codegen/schemaCodegen"; import { diff } from "jest-diff"; -import { METADATA_DIRECTIVE_NAMES } from "../metadataDirectives"; import * as semver from "semver"; import { GratsConfig, @@ -134,12 +133,17 @@ const testDirs = [ return formatDiagnosticsWithContext(code, schemaResult.err); } - const { schema, doc } = schemaResult.value; + const { schema, doc, resolvers } = schemaResult.value; // We run codegen here just ensure that it doesn't throw. const executableSchema = applyTypeScriptHeader( parsedOptions.raw.grats, - codegen(schema, parsedOptions.raw.grats, `${fixturesDir}/${fileName}`), + codegen( + schema, + resolvers, + parsedOptions.raw.grats, + `${fixturesDir}/${fileName}`, + ), ); const LOCATION_REGEX = /^\/\/ Locate: (.*)/; @@ -157,9 +161,6 @@ const testDirs = [ const docSansDirectives = { ...doc, definitions: doc.definitions.filter((def) => { - if (def.kind === "DirectiveDefinition") { - return !METADATA_DIRECTIVE_NAMES.has(def.name.value); - } if (def.kind === "ScalarTypeDefinition") { return !specifiedScalarTypes.some( (scalar) => scalar.name === def.name.value, @@ -216,9 +217,14 @@ const testDirs = [ throw new Error(schemaResult.err.formatDiagnosticsWithContext()); } - const { schema, doc } = schemaResult.value; + const { schema, doc, resolvers } = schemaResult.value; - const tsSchema = codegen(schema, parsedOptions.raw.grats, schemaPath); + const tsSchema = codegen( + schema, + resolvers, + parsedOptions.raw.grats, + schemaPath, + ); writeFileSync(schemaPath, tsSchema); @@ -273,10 +279,7 @@ function printSDLFromSchemaWithoutDirectives(schema: GraphQLSchema): string { new GraphQLSchema({ ...schema.toConfig(), directives: schema.getDirectives().filter((directive) => { - return ( - !METADATA_DIRECTIVE_NAMES.has(directive.name) && - directive.name !== SEMANTIC_NON_NULL_DIRECTIVE - ); + return directive.name !== SEMANTIC_NON_NULL_DIRECTIVE; }), }), ); diff --git a/src/transforms/addInterfaceFields.ts b/src/transforms/addInterfaceFields.ts index 4e3eceb4..b2ecd37c 100644 --- a/src/transforms/addInterfaceFields.ts +++ b/src/transforms/addInterfaceFields.ts @@ -12,7 +12,6 @@ import { err, ok } from "../utils/Result"; import { InterfaceMap, computeInterfaceMap } from "../InterfaceGraph"; import { extend, nullThrows, uniqueId } from "../utils/helpers"; import { FIELD_TAG } from "../Extractor"; -import { FIELD_METADATA_DIRECTIVE } from "../metadataDirectives"; /** * Grats allows you to define GraphQL fields on TypeScript interfaces using @@ -84,15 +83,10 @@ function addAbstractFieldDefinition( // Extending an interface is a bit more complicated. We need to add the field // to the interface, and to each type that implements the interface. - // The interface field definition is not executable, so we don't - // need to annotate it with the details of the implementation. - const directives = field.directives?.filter((directive) => { - return directive.name.value !== FIELD_METADATA_DIRECTIVE; - }); newDocs.push({ kind: Kind.INTERFACE_TYPE_EXTENSION, name: doc.name, - fields: [{ ...field, directives }], + fields: [field], }); for (const implementor of interfaceGraph.get(nameDefinition.name.value)) { @@ -115,7 +109,7 @@ function addAbstractFieldDefinition( newDocs.push({ kind: Kind.INTERFACE_TYPE_EXTENSION, name, - fields: [{ ...field, directives }], + fields: [field], loc: doc.loc, }); break; diff --git a/src/transforms/applyDefaultNullability.ts b/src/transforms/applyDefaultNullability.ts index 8b1ef263..a313537c 100644 --- a/src/transforms/applyDefaultNullability.ts +++ b/src/transforms/applyDefaultNullability.ts @@ -3,7 +3,6 @@ import { DiagnosticsResult, gqlErr } from "../utils/DiagnosticError"; import { err, ok } from "../utils/Result"; import * as ts from "typescript"; import * as E from "../Errors"; -import { KILLS_PARENT_ON_EXCEPTION_DIRECTIVE } from "../metadataDirectives"; import { addSemanticNonNullDirective, makeSemanticNonNullDirective, @@ -24,9 +23,7 @@ export function applyDefaultNullability( const errors: ts.DiagnosticWithLocation[] = []; const newDoc = visit(doc, { [Kind.FIELD_DEFINITION]: (t) => { - const killsParent = t.directives?.find( - (d) => d.name.value === KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, - ); + const killsParent = t.killsParentOnException; if (killsParent) { // You can only use @killsParentOnException if nullableByDefault is on. diff --git a/src/transforms/makeResolverSignature.ts b/src/transforms/makeResolverSignature.ts new file mode 100644 index 00000000..a97e7507 --- /dev/null +++ b/src/transforms/makeResolverSignature.ts @@ -0,0 +1,100 @@ +import { DocumentNode, Kind } from "graphql"; +import { + ResolverArgument, + ResolverDefinition, + Metadata, + FieldDefinition, +} from "../metadata"; +import { nullThrows } from "../utils/helpers"; +import { ResolverArgument as DirectiveResolverArgument } from "../resolverSignature"; + +export function makeResolverSignature(documentAst: DocumentNode): Metadata { + const resolvers: Metadata = { + types: {}, + }; + + for (const declaration of documentAst.definitions) { + if (declaration.kind !== Kind.OBJECT_TYPE_DEFINITION) { + continue; + } + if (declaration.fields == null) { + continue; + } + + const fieldResolvers: Record = {}; + + for (const fieldAst of declaration.fields) { + const fieldResolver = nullThrows(fieldAst?.resolver); + const fieldName = fieldAst.name.value; + let resolver: ResolverDefinition; + switch (fieldResolver.kind) { + case "property": + resolver = { + kind: "property", + name: fieldResolver.name, + }; + break; + case "function": + resolver = { + kind: "function", + path: fieldResolver.path, + exportName: fieldResolver.exportName, + arguments: transformArgs(fieldResolver.arguments), + }; + break; + case "method": + resolver = { + kind: "method", + name: fieldResolver.name, + arguments: transformArgs(fieldResolver.arguments), + }; + break; + case "staticMethod": + resolver = { + kind: "staticMethod", + path: fieldResolver.path, + exportName: fieldResolver.exportName, + name: fieldResolver.name, + arguments: transformArgs(fieldResolver.arguments), + }; + break; + default: + // @ts-expect-error + throw new Error(`Unknown resolver kind: ${fieldResolver.kind}`); + } + + fieldResolvers[fieldName] = { resolver }; + } + + resolvers.types[declaration.name.value] = fieldResolvers; + } + + return resolvers; +} + +function transformArgs( + args: DirectiveResolverArgument[] | null, +): ResolverArgument[] | null { + if (args == null) { + return null; + } + return args.map((arg): ResolverArgument => { + switch (arg.kind) { + case "argumentsObject": + return { kind: "argumentsObject" }; + case "named": + return { kind: "named", name: arg.name }; + case "source": + return { kind: "source" }; + case "information": + return { kind: "information" }; + case "context": + return { kind: "context" }; + case "unresolved": + throw new Error("Unresolved argument in resolver"); + default: + // @ts-expect-error + throw new Error(`Unknown argument kind: ${arg.kind}`); + } + }); +} diff --git a/src/transforms/resolveResolverParams.ts b/src/transforms/resolveResolverParams.ts index 3670256d..525522f9 100644 --- a/src/transforms/resolveResolverParams.ts +++ b/src/transforms/resolveResolverParams.ts @@ -10,16 +10,15 @@ import { err, ok } from "../utils/Result"; import { DiagnosticsResult, FixableDiagnosticWithLocation, - gqlRelated, tsErr, + tsRelated, } from "../utils/DiagnosticError"; import { nullThrows } from "../utils/helpers"; import { - NamedFieldParam, - PositionalFieldParam, - Unresolved, - UnresolvedResolverParam, -} from "../metadataDirectives.js"; + NamedResolverArgument, + ResolverArgument, + UnresolvedResolverArgument, +} from "../resolverSignature"; import * as E from "../Errors"; import { GraphQLConstructor } from "../GraphQLConstructor"; @@ -56,28 +55,30 @@ class ResolverParamsResolver { } private transformField(field: FieldDefinitionNode): FieldDefinitionNode { - if (field.resolverParams == null) { + const resolver = nullThrows(field.resolver); + + if (resolver.kind === "property" || resolver.arguments == null) { return field; } + // Resolve all the params individually - const resolverParams: UnresolvedResolverParam[] = field.resolverParams.map( - (param) => this.transformParam(param), + const resolverParams: ResolverArgument[] = resolver.arguments.map((param) => + this.transformParam(param), ); // Now we check to see if the params are a valid combination... const args = resolverParams.find( - (param): param is NamedFieldParam => - param.kind === "named" && param.name === "args", + (param) => param.kind === "argumentsObject", ); - const positionalArgs: PositionalFieldParam[] = resolverParams.filter( - (param) => param.kind === "positionalArg", + const positionalArgs: NamedResolverArgument[] = resolverParams.filter( + (param) => param.kind === "named", ); if (args != null && positionalArgs.length > 0) { this.errors.push( - tsErr(nullThrows(args.sourceNode), E.positionalArgAndArgsObject(), [ - gqlRelated( - positionalArgs[0].inputDefinition, + tsErr(nullThrows(args.node), E.positionalArgAndArgsObject(), [ + tsRelated( + positionalArgs[0].node, "Positional GraphQL argument defined here", ), ]), @@ -93,13 +94,21 @@ class ResolverParamsResolver { fieldArgs.push(positionalArg.inputDefinition); } - return { ...field, arguments: fieldArgs, resolverParams }; + const newResolver = { + ...resolver, + arguments: resolverParams, + }; + + return { ...field, arguments: fieldArgs, resolver: newResolver }; } - transformParam(param: UnresolvedResolverParam): UnresolvedResolverParam { + transformParam(param: ResolverArgument): ResolverArgument { switch (param.kind) { case "named": - case "positionalArg": + case "argumentsObject": + case "information": + case "context": + case "source": return param; case "unresolved": { const unwrappedType = this.gql.nullableType(param.inputDefinition.type); @@ -116,9 +125,9 @@ class ResolverParamsResolver { } switch (resolved.value.kind) { case "CONTEXT": - return { kind: "named", name: "context" }; + return { kind: "context", node: param.node }; case "INFO": - return { kind: "named", name: "info" }; + return { kind: "information", node: param.node }; default: { // We'll assume it's supposed to be a positional arg. return this.resolveToPositionalArg(param) ?? param; @@ -130,22 +139,26 @@ class ResolverParamsResolver { return this.resolveToPositionalArg(param) ?? param; } default: { - const _exhaustive: never = param; - throw new Error("Unexpected param kind"); + // @ts-expect-error + throw new Error(`Unexpected param kind ${param.kind}`); } } } - resolveToPositionalArg(unresolved: Unresolved): PositionalFieldParam | null { + resolveToPositionalArg( + unresolved: UnresolvedResolverArgument, + ): ResolverArgument | null { if (unresolved.inputDefinition.name.kind === "ERROR") { this.errors.push(unresolved.inputDefinition.name.err); return null; } return { - kind: "positionalArg", + kind: "named", + name: unresolved.inputDefinition.name.value.value, inputDefinition: { ...unresolved.inputDefinition, name: unresolved.inputDefinition.name.value, }, + node: unresolved.node, }; } } diff --git a/src/transforms/resolveTypes.ts b/src/transforms/resolveTypes.ts index 2ae226df..3b9a4942 100644 --- a/src/transforms/resolveTypes.ts +++ b/src/transforms/resolveTypes.ts @@ -342,8 +342,8 @@ function mayReferenceGenerics( return ( definition.kind === Kind.OBJECT_TYPE_DEFINITION || definition.kind === Kind.UNION_TYPE_DEFINITION || - definition.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION || - definition.kind === Kind.INTERFACE_TYPE_DEFINITION + definition.kind === Kind.INTERFACE_TYPE_DEFINITION || + definition.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ); } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 541da2c4..e7721601 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -49,3 +49,9 @@ export function nullThrows(value: T | null | undefined): T { } return value; } + +// Predicate function for filtering out null values +// Includes TypeScript refinement for narrowing the type +export function isNonNull(value: T | null | undefined): value is T { + return value != null; +} diff --git a/website/docs/03-resolvers/06-nullability.mdx b/website/docs/03-resolvers/06-nullability.mdx index f85bc300..d4c303e0 100644 --- a/website/docs/03-resolvers/06-nullability.mdx +++ b/website/docs/03-resolvers/06-nullability.mdx @@ -7,7 +7,11 @@ By default, Grats makes all fields nullable in keeping with **[GraphQL best prac This approach allows for maximally resilient network requests, since a single error will not take down the entire query, generally it will only affect the field that threw the error. -However, there are some fields where it is necessary to have a non-nullable field. In this case, you may add the docblock tag `@killsParentOnException` to the field. This will cause the field to be typed as non-nullable, but _it comes at a price_. Should the resolver throw, the error will bubble up to the first nullable parent. +If you find this pervasive nullability makes client application development frustrating, you may wish to enable [Semantic Nullability](../05-guides/04-strict-semantic-nullability.mdx). + +## @killsParentOnException + +However, there are some fields where it is necessary to have a non-nullable field, for example for `id`. In this case, you may add the docblock tag `@killsParentOnException` to the field. This will cause the field to be typed as non-nullable, but _it comes at a price_. Should the resolver throw, the error will bubble up to the first nullable parent. :::caution If `@killsParentOnException` is used too liberally, small errors can take down huge portions of your query. diff --git a/website/docs/04-docblock-tags/snippets/04-interface-field-common-impl.out b/website/docs/04-docblock-tags/snippets/04-interface-field-common-impl.out index 744d3bf5..e2536288 100644 --- a/website/docs/04-docblock-tags/snippets/04-interface-field-common-impl.out +++ b/website/docs/04-docblock-tags/snippets/04-interface-field-common-impl.out @@ -43,8 +43,7 @@ type User implements Greetable { name: String } === SNIP === -import { greeting as petGreetingResolver } from "./04-interface-field-common-impl.grats"; -import { greeting as userGreetingResolver } from "./04-interface-field-common-impl.grats"; +import { greeting as petGreetingResolver, greeting as userGreetingResolver } from "./04-interface-field-common-impl.grats"; import { GraphQLSchema, GraphQLInterfaceType, GraphQLString, GraphQLObjectType } from "graphql"; export function getSchema(): GraphQLSchema { const GreetableType: GraphQLInterfaceType = new GraphQLInterfaceType({ diff --git a/website/docs/05-guides/snippets/result-after.out b/website/docs/05-guides/snippets/result-after.out index dded420a..e5454f87 100644 --- a/website/docs/05-guides/snippets/result-after.out +++ b/website/docs/05-guides/snippets/result-after.out @@ -81,8 +81,7 @@ type Query { myValue: MyValueResult } === SNIP === -import { myOtherValue as queryMyOtherValueResolver } from "./result-after.grats"; -import { myValue as queryMyValueResolver } from "./result-after.grats"; +import { myOtherValue as queryMyOtherValueResolver, myValue as queryMyValueResolver } from "./result-after.grats"; import { GraphQLSchema, GraphQLObjectType, GraphQLUnionType, GraphQLString } from "graphql"; export function getSchema(): GraphQLSchema { const MyErrorType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/website/docs/06-faq/02-why-use-comments.md b/website/docs/06-faq/02-why-use-comments.md index f0366370..acce64a8 100644 --- a/website/docs/06-faq/02-why-use-comments.md +++ b/website/docs/06-faq/02-why-use-comments.md @@ -2,9 +2,13 @@ Depending upon the capabilities of the target language, there are a number of ways to implement an [implementation-first](https://jordaneldredge.com/blog/implementation-first/) GraphQL server like Grats. Some libraries use [introspection](https://strawberry.rocks) while others use [macros](https://github.com/graphql-rust/juniper). _So why does Grats choose to use comments instead?_ Let's explore why these other approaches are not a good fit for TypeScript and Grats. -## Why not use introspection? +## Why not use decorators? -Because TypeScript types are stripped at runtime, it's fundamentally impossible to use introspection to "see" the types of fields/arguments etc. at runtime. This means that introspection cannot be used to derive a GraphQL schema from TypeScript types at runtime. +Other tools, like [Strawberry](https://strawberry.rocks) for Python, use decorators to annotate types and fields. I think this is probably the ideal API for an implementation-first GraphQL server. It uses existing language syntax and, via introspection, can derive the GraphQL schema at runtime without requiring an additional build step. + +However, there ara a few reasons why it's not feasible in TypeScript. Firstly, because decorators are runtime constructs, they cannot be attached to types, which would prevent things like defining GraphQL input/output types with TypeScript `type`s. + +Secondly, TypeScript types are stripped at runtime so it is fundamentally impossible to use introspection to "see" the types of fields/arguments etc. This means that introspection cannot be used to derive a GraphQL schema from TypeScript types at runtime. If we want to use type annotations to derive GraphQL types, we must use do so statically/at build time. That said, introspection can be used to derived _pieces_ of a GraphQL schema. For example, names of type/class decorations and field names. [TypeGraphQL](https://typegraphql.com/) is a library that is similar to Grats in many ways, but uses decorators and introspection instead of comments. However, since types are not visible at runtime, it ends up needing to fall back to a builder-like API to express things like field return types, and argument types. This tradeoff has the advantage of avoiding a build step, but at the cost of a more complex API. diff --git a/website/docs/06-faq/03-how-grats-works.md b/website/docs/06-faq/03-how-grats-works.md index 95faba03..fce64b4a 100644 --- a/website/docs/06-faq/03-how-grats-works.md +++ b/website/docs/06-faq/03-how-grats-works.md @@ -4,7 +4,7 @@ _This is a technical deep dive for those who are curious about how Grats works u --- -For users who want to have a better mental model of how Grats works, or just for the curious, here's a high level overview of how Grats is implemented. For a description of Grats' values and aspirations, see [Design Principles](./04-design-principles.md). +For users who want to have a better mental model of how Grats works, or just for the curious, here's a high level overview of how Grats is implemented. For a description of Grats' values and aspirations, see [Design Principles](./04-design-principles.md) for thoughts on its API design see [API Design](02-why-use-comments.md). ## At build time @@ -14,19 +14,21 @@ When statically analyzing your code to infer GraphQL schema, grats first looks f Grats then iterates over these files and checks via Regex to see if they contain any `@gql*` tags. If they do, Grats will parse the file and iterate over every `@gql*` tag. For each tag it finds that maps to a top-level GraphQL construct (type, interface, etc.), it asks the TypeScript compiler for the AST node to which that comment is attached. Now that Grats has an AST node and an expectation of what GraphQL construct it's trying to infer, it tries all the different inference strategies it has available to it. If it can't infer a GraphQL construct, it will report a diagnostic error to the user. That sounds a lot fancier than what the code looks like: a series of switch and if statements. -If it's able to infer a GraphQL construct, it will build up a GraphQL AST node representing the schema definition. In the case of `@gqlType` or similar, this may mean inspecting child elements of the AST node for child constructs like `@gqlField` and recursively inspecting those nodes. These GraphQL AST nodes are the exact shape the [`graphql-js`](https://graphql.org/graphql-js/) builds when it parses a GraphQL SDL file, and thus are API-compatible with `graphql-js` utilities. However, we play one clever trick. When we construct the location information for each GraphQL AST node, which would usually contain the line and column number of the Schema Definition Language (SDL) text from which it was parsed, we instead use the location information from the TypeScript AST node, including its file path, line number, and column number. +If it's able to infer a GraphQL construct, it will build up a GraphQL AST node representing the schema definition. In the case of `@gqlType` or similar, this may mean inspecting child elements of the AST node for child constructs like `@gqlField` and recursively inspecting those nodes. These GraphQL AST nodes are the shape the [`graphql-js`](https://graphql.org/graphql-js/) builds when it parses a GraphQL SDL file, and thus are API-compatible with `graphql-js` utilities. However, we play a few clever tricks: + +When we construct the location information for each GraphQL AST node, which would usually contain the line and column number of the Schema Definition Language (SDL) text from which it was parsed, we instead use the location information from the TypeScript AST node, including its file path, line number, and column number. By building up these AST nodes, Grats is able to use the same code that `graphql-js` uses to validate GraphQL schema to validate Grats' inferred schema. And because we have populated the location information with the TypeScript AST node, the diagnostics we get from `graphql-js` will actually "point" the the TypeScript source code that Grats uses as the source of truth for that AST node. You can read more about this technique in [this note](https://jordaneldredge.com/notes/compile-to-ast/). One final trick we employ is using TypeScript's representation of a diagnostic. This allows us to use TypeScript's error printer for free. -In a few cases, like when a field name does not match its property/method name, Grats tracks this fact by annotating some constructs with custom server directives. These allow the extraction phase of Grats to communicate additional information to the codegen phase of Grats. Since these directives are an implementation detail of Grats, they are not included in the generated SDL. You can think of the annotated SDL AST as an internal [intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation) (IR) used by Grats. +In a few cases, like when a field name does not match its property/method name, Grats tracks this fact by annotating some AST objects with additional properties. These allow other phases of Grats to see additional information. When all transformations are complete, the remaining metadata is collected into a single `Metadata` object that is passed to the codegen phase. We are currently experimenting with allowing Grats to optionally output this metadata as a JSON file for use by other tools. ### TypeScript code generation With the GraphQL AST in hand, Grats must now generate TypeScript code that will construct your `GraphQLSchema` at runtime. To do this, Grats uses the AST to constructs a `GraphQLSchema` object in memory. This normalized representation of the schema, with all extensions merged and all types in a flat list, is then passed to a codegen function that recursively walks the schema and generates TypeScript code for each type. -The implementation of each field's `resolve` function is synthesized based on which IR directives are encountered on that field. In some cases that means importing user-defined resolver functions. +The implementation of each field's `resolve` function is synthesized based on information about that field in the `Metadata` object. In some cases that means importing user-defined resolver functions. To implement our code generation, we again lean into our [design principle](./04-design-principles.md#a-few-dependencies-well-leveraged) of "a few dependencies well leveraged" by using TypeScript's AST construction utilities. We then use TypeScript's code printer to emit a formatted TypeScript file. By constructing a TypeScript AST rather than simply concatenating strings, we get a few benefits: @@ -35,7 +37,7 @@ To implement our code generation, we again lean into our [design principle](./04 ### GraphQL SDL code generation -Using the same build-time `GraphQLSchema` object from the previous step, we use `printSchema` from `graphql-js` to generate a GraphQL SDL file. `graphql-js` automatically strips server directives, but we also manually strip the definitions of our IR directives. +Using the same build-time `GraphQLSchema` object from the previous step, we use `printSchema` from `graphql-js` to generate a GraphQL SDL file. ## At runtime diff --git a/website/docs/gratsSample.out b/website/docs/gratsSample.out index 15624d3e..f1a5d519 100644 --- a/website/docs/gratsSample.out +++ b/website/docs/gratsSample.out @@ -38,8 +38,7 @@ type User { name: String } === SNIP === -import { me as queryMeResolver } from "./gratsSample.grats"; -import { viewer as queryViewerResolver } from "./gratsSample.grats"; +import { me as queryMeResolver, viewer as queryViewerResolver } from "./gratsSample.grats"; import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"; export function getSchema(): GraphQLSchema { const UserType: GraphQLObjectType = new GraphQLObjectType({ diff --git a/website/scripts/gratsCode.ts b/website/scripts/gratsCode.ts index 9b861e8b..409a412e 100644 --- a/website/scripts/gratsCode.ts +++ b/website/scripts/gratsCode.ts @@ -26,6 +26,8 @@ function processFile(file: string) { strictSemanticNullability: false, schemaHeader: null, tsSchemaHeader: null, + EXPERIMENTAL__emitMetadata: false, + EXPERIMENTAL__emitResolverMap: false, }; const parsedOptions = { options: {}, @@ -42,8 +44,8 @@ function processFile(file: string) { throw new Error("Invalid grats code"); } - const { doc, schema } = schemaAndDocResult.value; - const typeScript = codegen(schema, config, file); + const { doc, schema, resolvers } = schemaAndDocResult.value; + const typeScript = codegen(schema, resolvers, config, file); const graphql = printSDLWithoutMetadata(doc); const fileContent = fs.readFileSync(file, "utf8"); diff --git a/website/src/components/PlaygroundFeatures/ConfigBar.tsx b/website/src/components/PlaygroundFeatures/ConfigBar.tsx index 3400be10..94712b0c 100644 --- a/website/src/components/PlaygroundFeatures/ConfigBar.tsx +++ b/website/src/components/PlaygroundFeatures/ConfigBar.tsx @@ -68,6 +68,7 @@ export default function ConfigBar(): JSX.Element { > + {outputOption === "sdl" && ( diff --git a/website/src/components/PlaygroundFeatures/editors/OutputView.tsx b/website/src/components/PlaygroundFeatures/editors/OutputView.tsx index 1d7bf693..07df10ad 100644 --- a/website/src/components/PlaygroundFeatures/editors/OutputView.tsx +++ b/website/src/components/PlaygroundFeatures/editors/OutputView.tsx @@ -2,6 +2,7 @@ import React from "react"; import GraphQLOutputView from "./GraphQLOutputView"; import CodegenOutputView from "./CodegenOutputView"; import { getOutputOption, useAppSelector } from "../store"; +import ResolverSignatureOutput from "./ResolverSignatureOutput"; export default function OutputView() { const outputOption = useAppSelector(getOutputOption); @@ -10,5 +11,7 @@ export default function OutputView() { return ; case "typescript": return ; + case "resolverSignatures": + return ; } } diff --git a/website/src/components/PlaygroundFeatures/editors/ResolverSignatureOutput.tsx b/website/src/components/PlaygroundFeatures/editors/ResolverSignatureOutput.tsx new file mode 100644 index 00000000..f3e7f914 --- /dev/null +++ b/website/src/components/PlaygroundFeatures/editors/ResolverSignatureOutput.tsx @@ -0,0 +1,64 @@ +import React, { useState, useEffect } from "react"; +import { EditorState } from "@codemirror/state"; +import { EditorView, keymap, lineNumbers } from "@codemirror/view"; +import { defaultKeymap } from "@codemirror/commands"; +// import { jsonLanguage } from "@codemirror/lang-json"; +import { + defaultHighlightStyle, + syntaxHighlighting, +} from "@codemirror/language"; +import { getResolverSignatureOutputString, onSelectorChange } from "../store"; +import { Theme } from "./theme"; +import store from "../store"; + +export default function OutputView() { + const [ref, setRef] = useState(null); + useEffect(() => { + if (ref != null) { + createOutputView(store, ref); + } + }, [ref]); + return ( +
+ ); +} + +export function createOutputView(store, right: HTMLDivElement) { + console.log(getResolverSignatureOutputString(store.getState())); + const outputState = EditorState.create({ + doc: getResolverSignatureOutputString(store.getState()), + extensions: [ + Theme, + keymap.of(defaultKeymap), + lineNumbers(), + // jsonLanguage, + EditorState.readOnly.of(true), + EditorView.lineWrapping, + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + ], + }); + + const rightView = new EditorView({ + state: outputState, + parent: right, + }); + + // When the output changes, update the output view + onSelectorChange(store, getResolverSignatureOutputString, (output) => { + console.log("output", output); + rightView.dispatch({ + changes: { + from: 0, + to: rightView.state.doc.length, + insert: output, + }, + }); + }); +} diff --git a/website/src/components/PlaygroundFeatures/linter.ts b/website/src/components/PlaygroundFeatures/linter.ts index 11f53b0b..e0b2014c 100644 --- a/website/src/components/PlaygroundFeatures/linter.ts +++ b/website/src/components/PlaygroundFeatures/linter.ts @@ -1,7 +1,8 @@ import { createSystem, createVirtualCompilerHost } from "@typescript/vfs"; import * as ts from "typescript"; import { buildSchemaAndDocResultWithHost, GratsConfig } from "grats/src/lib"; -import { codegen } from "grats/src/codegen"; +import { codegen } from "grats/src/codegen/schemaCodegen"; +// import codegen from "grats/src/codegen/resolverCodegen"; import { ReportableDiagnostics } from "grats/src/utils/DiagnosticError"; import { printSDLWithoutMetadata } from "grats/src/printSchema"; import { linter } from "@codemirror/lint"; @@ -87,6 +88,8 @@ export function createLinter( store.dispatch({ type: "NEW_DOCUMENT_TEXT", value: text }); + const destination = "index.ts"; + if (result.kind === "ERROR") { const errorText = result.err.formatDiagnosticsWithContext(); const output = `# ERROR MESSAGE\n# =============\n\n${commentLines( @@ -96,6 +99,7 @@ export function createLinter( type: "GRATS_EMITTED_NEW_RESULT", graphql: output, typescript: output, + resolveSignatures: output, }); return result.err._diagnostics @@ -103,7 +107,7 @@ export function createLinter( if (diagnostic.file == null) { return false; } - return diagnostic.file.fileName === "index.ts"; + return diagnostic.file.fileName === destination; }) .map((diagnostic) => { const actions = []; @@ -121,13 +125,18 @@ export function createLinter( }); } - const codegenOutput = computeCodegenOutput(result.value.schema, config); + const codegenOutput = computeCodegenOutput( + result.value.schema, + config, + result.value.resolvers, + ); const output = computeOutput(result.value.doc, view); store.dispatch({ type: "GRATS_EMITTED_NEW_RESULT", graphql: output, typescript: codegenOutput, + resolverSignatures: JSON.stringify(result.value.resolvers, null, 2), }); return []; @@ -147,8 +156,9 @@ function computeOutput( function computeCodegenOutput( schema: GraphQLSchema, config: GratsConfig, + resolverSignature: string, ): string { - return codegen(schema, config, "./schema.ts"); + return codegen(schema, resolverSignature, config, "./schema.ts"); } function commentLines(text: string): string { diff --git a/website/src/components/PlaygroundFeatures/store.ts b/website/src/components/PlaygroundFeatures/store.ts index bebf36bf..1b6b7e4f 100644 --- a/website/src/components/PlaygroundFeatures/store.ts +++ b/website/src/components/PlaygroundFeatures/store.ts @@ -12,12 +12,14 @@ export type State = { reportTypeScriptTypeErrors: boolean; }; view: { + /** @deprecated */ showGratsDirectives: boolean; - outputOption: "sdl" | "typescript"; + outputOption: "sdl" | "typescript" | "resolverSignatures"; }; gratsResult: null | { graphql: string; typescript: string; + resolverSignatures: string; }; VERSION: number; }; @@ -30,10 +32,6 @@ export type Action = type: "SET_WHOLE_STATE"; state: State; } - | { - type: "SHOW_GRATS_DIRECTIVE_INPUT_CHANGED"; - value: boolean; - } | { type: "DEFAULT_NULLABLE_INPUT_CHANGED"; value: boolean; @@ -42,6 +40,7 @@ export type Action = type: "GRATS_EMITTED_NEW_RESULT"; graphql: string; typescript: string; + resolverSignatures: string; } | { type: "NEW_DOCUMENT_TEXT"; @@ -49,7 +48,7 @@ export type Action = } | { type: "OUTPUT_VIEW_SELECTION_CHANGED"; - value: "sdl" | "typescript"; + value: "sdl" | "typescript" | "resolverSignatures"; }; function reducer(state: State = stateFromUrl(), action: Action) { @@ -58,15 +57,6 @@ function reducer(state: State = stateFromUrl(), action: Action) { return stateFromUrl(); case "SET_WHOLE_STATE": return action.state; - case "SHOW_GRATS_DIRECTIVE_INPUT_CHANGED": - return { - ...state, - view: { - ...state.view, - showGratsDirectives: action.value, - }, - }; - case "OUTPUT_VIEW_SELECTION_CHANGED": return { ...state, @@ -89,6 +79,7 @@ function reducer(state: State = stateFromUrl(), action: Action) { gratsResult: { graphql: action.graphql, typescript: action.typescript, + resolverSignatures: action.resolverSignatures, }, }; case "NEW_DOCUMENT_TEXT": { @@ -142,15 +133,25 @@ export function getDoc(state: State) { return state.doc; } -export function getOutputOption(state: State): "sdl" | "typescript" { +export function getOutputOption( + state: State, +): "sdl" | "typescript" | "resolverSignatures" { return state.view.outputOption; } -export function getGratsGraphqlResult(state: State): string | null { +export function getGratsGraphqlResult(state: State): string | null | undefined { return state.gratsResult?.graphql; } -export function getGratsTypeScriptResult(state: State): string | null { +export function getGratsResolverSignatureResult( + state: State, +): string | null | undefined { + return state.gratsResult?.resolverSignatures; +} + +export function getGratsTypeScriptResult( + state: State, +): string | null | undefined { return state.gratsResult?.typescript; } @@ -158,10 +159,6 @@ export function getNullableByDefault(state): boolean { return state.config.nullableByDefault; } -export function getShowGratsDirectives(state): boolean { - return state.view.showGratsDirectives; -} - export const getGraphQLOutputString = createSelector( getGratsGraphqlResult, (gratsResult) => { @@ -169,6 +166,16 @@ export const getGraphQLOutputString = createSelector( }, ); +export const getResolverSignatureOutputString = createSelector( + getGratsResolverSignatureResult, + (resolverSignatureResult) => { + if (resolverSignatureResult == null) { + return "Loading..."; + } + return resolverSignatureResult; + }, +); + export const getTypeScriptOutputString = createSelector( getGratsTypeScriptResult, (gratsResult) => { @@ -183,7 +190,7 @@ export type SerializableState = { reportTypeScriptTypeErrors: boolean; }; view: { - showGratsDirectives: boolean; + outputOption: "sdl" | "typescript" | "resolverSignatures"; }; VERSION: number; }; @@ -203,9 +210,9 @@ export const getUrlHash = createSelector( export function useUrlState(store) { useEffect(() => { const hash = getUrlHash(store.getState()); - window.history.replaceState(null, null, hash); + window.history.replaceState(null, "", hash); return onSelectorChange(store, getUrlHash, (urlHash) => { - window.history.replaceState(null, null, urlHash); + window.history.replaceState(null, "", urlHash); }); }, [store]); }