diff --git a/CHANGELOG.md b/CHANGELOG.md index ccb7b90b0..36a71ff89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Features +- **Breaking Change**: make `graphql-js` packages a peer dependencies, bump `graphql` to `^14.1.1` and `@types/graphql` to `^14.0.5` (#239) - **Breaking Change**: change the default `PrintSchemaOptions` option `commentDescriptions` to false (no more `#` comments in SDL) - add support for passing `PrintSchemaOptions` in `buildSchema.emitSchemaFile` (e.g. `commentDescriptions: true` to restore previous behavior) - add `buildTypeDefsAndResolvers` utils function for generating apollo-like `typeDefs` and `resolvers` pair (#233) diff --git a/README.md b/README.md index 168c1bd14..5039a5dfb 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,9 @@ Below you can find installation instructions that are also important. ### Installation -1. Install module: +1. Install the package and the `graphql` (peer dependency): ``` -npm i type-graphql +npm i graphql @types/graphql type-graphql ``` 2. `reflect-metadata` shim is required: diff --git a/docs/installation.md b/docs/installation.md index 2ee9322bc..01bc2a0c5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,23 +4,22 @@ title: Installation Before getting started with TypeGraphQL we need to install some additional dependencies and properly configure TypeScript configuration for our project. -## Prerequisites - -Before you begin, make sure your development environment includes Node.js and npm. +> #### Prerequisites +> Before you begin, make sure your development environment includes Node.js and npm. ## Packages installation -Install module: +First, you have to install the main package, as well as the [`graphql-js`](https://github.com/graphql/graphql-js) (and it's typings) which is a peer dependency of TypeGraphQL: ```sh -npm i type-graphql +npm i graphql @types/graphql type-graphql ``` -`reflect-metadata` shim is required: +Also, the `reflect-metadata` shim is required to make the type reflection works: ```sh npm i reflect-metadata ``` -and make sure to import it on top of your entry file (before you use/import `type-graphql` or your resolvers): +Please make sure to import it on top of your entry file (before you use/import `type-graphql` or your resolvers): ```typescript import "reflect-metadata"; ``` diff --git a/package-lock.json b/package-lock.json index dab562a62..9dcdc7fa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -303,9 +303,10 @@ } }, "@types/graphql": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.0.3.tgz", - "integrity": "sha512-TcFkpEjcQK7w8OcrQcd7iIBPjU0rdyi3ldj6d0iJ4PPSzbWqPBvXj9KSwO14hTOX2dm9RoiH7VuxksJLNYdXUQ==" + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.0.5.tgz", + "integrity": "sha512-bwGYLE0SRy5ZraC91dqI2bxbspfm10kyJ2Yjuvk4OjdGznh7fkoWW+xXZHfFydJaqu9syZi099cpiZw3GlPDiA==", + "dev": true }, "@types/gulp": { "version": "4.0.5", @@ -450,6 +451,11 @@ "@types/spdy": "*" } }, + "@types/semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" + }, "@types/serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", @@ -1013,7 +1019,50 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "argparse": { @@ -3678,8 +3727,33 @@ "has-unicode": "^2.0.0", "object-assign": "^4.1.0", "signal-exit": "^3.0.0", + "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } } }, "get-caller-file": { @@ -3922,9 +3996,10 @@ "dev": true }, "graphql": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", - "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.1.1.tgz", + "integrity": "sha512-C5zDzLqvfPAgTtP8AUPIt9keDabrdRAqSWjj2OPRKrKxI9Fb65I36s1uCs1UUBFnSWTdO7hyHi7z1ZbwKMKF6Q==", + "dev": true, "requires": { "iterall": "^1.2.2" }, @@ -3932,7 +4007,8 @@ "iterall": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", - "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==", + "dev": true } } }, @@ -7162,7 +7238,16 @@ "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { - "safe-buffer": "^5.1.1" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } } }, "minizlib": { @@ -8973,10 +9058,9 @@ "dev": true }, "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "semver-compare": { "version": "1.0.0", @@ -9700,7 +9784,17 @@ "minipass": "^2.2.4", "minizlib": "^1.1.0", "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true, + "optional": true + } } }, "test-exclude": { @@ -10846,7 +10940,34 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, - "optional": true + "optional": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } }, "wordwrap": { "version": "0.0.3", diff --git a/package.json b/package.json index c59419123..49f2cc2f8 100644 --- a/package.json +++ b/package.json @@ -39,21 +39,26 @@ "check": "tsc --noEmit", "lint": "tslint --project tsconfig.json" }, + "peerDependencies": { + "@types/graphql": "^14.0.5", + "graphql": "^14.1.1" + }, "dependencies": { "@types/glob": "^7.1.1", - "@types/graphql": "^14.0.3", "@types/node": "*", + "@types/semver": "^5.5.0", "class-validator": ">=0.9.1", "glob": "^7.1.3", - "graphql": "^14.0.2", "graphql-query-complexity": "^0.2.2", "graphql-subscriptions": "^1.0.0", + "semver": "^5.6.0", "tslib": "^1.9.3" }, "devDependencies": { "@types/del": "^3.0.1", "@types/express": "^4.16.0", "@types/express-graphql": "^0.6.2", + "@types/graphql": "^14.0.5", "@types/gulp": "^4.0.5", "@types/gulp-replace": "0.0.31", "@types/gulp-shell": "0.0.31", @@ -73,6 +78,7 @@ "del": "^3.0.0", "express": "^4.16.4", "express-graphql": "^0.7.1", + "graphql": "^14.1.1", "graphql-playground-middleware-express": "^1.7.8", "graphql-redis-subscriptions": "^2.0.0", "graphql-tag": "^2.10.0", diff --git a/src/errors/UnmetGraphQLPeerDependencyError.ts b/src/errors/UnmetGraphQLPeerDependencyError.ts new file mode 100644 index 000000000..53f140786 --- /dev/null +++ b/src/errors/UnmetGraphQLPeerDependencyError.ts @@ -0,0 +1,16 @@ +import { + getPeerDependencyGraphQLRequirement, + getInstalledGraphQLVersion, +} from "../utils/graphql-version"; + +export class UnmetGraphQLPeerDependencyError extends Error { + constructor() { + super( + `Looks like you use an incorrect version of the 'graphql' package: "${getInstalledGraphQLVersion()}". ` + + `Please ensure that you have installed a version ` + + `that meets TypeGraphQL's requirement: "${getPeerDependencyGraphQLRequirement()}".`, + ); + + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/src/errors/index.ts b/src/errors/index.ts index 11da7e92a..edc47bbde 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -12,4 +12,5 @@ export * from "./ReflectMetadataMissingError"; export * from "./SymbolKeysNotSupportedError"; export * from "./UnauthorizedError"; export * from "./UnionResolveTypeError"; +export * from "./UnmetGraphQLPeerDependencyError"; export * from "./WrongNullableListOptionError"; diff --git a/src/schema/schema-generator.ts b/src/schema/schema-generator.ts index a6ef11df7..079de0ed3 100644 --- a/src/schema/schema-generator.ts +++ b/src/schema/schema-generator.ts @@ -40,6 +40,7 @@ import { } from "../errors"; import { ResolverFilterData, ResolverTopicData } from "../interfaces"; import { getFieldMetadataFromInputType, getFieldMetadataFromObjectType } from "./utils"; +import { ensureInstalledCorrectGraphQLPackage } from "../utils/graphql-version"; interface ObjectTypeInfo { target: Function; @@ -98,6 +99,7 @@ export abstract class SchemaGenerator { } private static checkForErrors(options: SchemaGeneratorOptions) { + ensureInstalledCorrectGraphQLPackage(); if (getMetadataStorage().authorizedFields.length !== 0 && options.authChecker === undefined) { throw new Error( "You need to provide `authChecker` function for `@Authorized` decorator usage!", diff --git a/src/utils/graphql-version.ts b/src/utils/graphql-version.ts new file mode 100644 index 000000000..2f2c507cd --- /dev/null +++ b/src/utils/graphql-version.ts @@ -0,0 +1,22 @@ +import * as semVer from "semver"; + +import { UnmetGraphQLPeerDependencyError } from "../errors"; + +export function getInstalledGraphQLVersion(): string { + const graphqlPackageJson = require("graphql/package.json"); + return graphqlPackageJson.version; +} + +export function getPeerDependencyGraphQLRequirement(): string { + const ownPackageJson = require("../../package.json"); + return ownPackageJson.peerDependencies.graphql; +} + +export function ensureInstalledCorrectGraphQLPackage() { + const installedVersion = getInstalledGraphQLVersion(); + const versionRequirement = getPeerDependencyGraphQLRequirement(); + + if (!semVer.satisfies(installedVersion, versionRequirement)) { + throw new UnmetGraphQLPeerDependencyError(); + } +} diff --git a/tests/functional/peer-dependency.ts b/tests/functional/peer-dependency.ts new file mode 100644 index 000000000..28cb71f5d --- /dev/null +++ b/tests/functional/peer-dependency.ts @@ -0,0 +1,28 @@ +import { ensureInstalledCorrectGraphQLPackage } from "../../src/utils/graphql-version"; +import { UnmetGraphQLPeerDependencyError } from "../../src/errors"; + +describe("`graphql` package peer dependency", () => { + it("should have installed correct version", async () => { + ensureInstalledCorrectGraphQLPackage(); + expect(true).toBe(true); + }); + + it("should throw error when the installed version doesn't fulfill requirement", async () => { + expect.assertions(5); + jest.mock("graphql/package.json", () => ({ + version: "14.0.2", + })); + const graphqlVersion = require("../../src/utils/graphql-version"); + + try { + graphqlVersion.ensureInstalledCorrectGraphQLPackage(); + } catch (err) { + expect(err).toBeInstanceOf(Error); + expect(err).toBeInstanceOf(UnmetGraphQLPeerDependencyError); + const error = err as UnmetGraphQLPeerDependencyError; + expect(error.message).toContain("incorrect version"); + expect(error.message).toContain("graphql"); + expect(error.message).toContain("requirement"); + } + }); +});