From dc18ec7c0c5ba0c89a5e074102b784ba57c1dc53 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Mon, 30 May 2022 00:17:23 +0200 Subject: [PATCH 1/4] feat: replace instanceOf with unique Symbol checks --- src/error/GraphQLError.ts | 9 +++ src/execution/subscribe.ts | 4 +- src/language/source.ts | 8 ++- src/type/definition.ts | 67 ++++++++++++++++--- src/type/directives.ts | 10 ++- src/type/schema.ts | 8 ++- src/utilities/coerceInputValue.ts | 4 +- .../rules/ValuesOfCorrectTypeRule.ts | 4 +- 8 files changed, 93 insertions(+), 21 deletions(-) diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts index 7fec960883..2bb3ccb7cd 100644 --- a/src/error/GraphQLError.ts +++ b/src/error/GraphQLError.ts @@ -29,6 +29,14 @@ export interface GraphQLErrorOptions { extensions?: Maybe; } +const isGraphQLErrorSymbol = Symbol.for('GraphQLError'); + +export function isGraphQLError(error: unknown): error is GraphQLError { + return ( + typeof error === 'object' && error != null && isGraphQLErrorSymbol in error + ); +} + /** * A GraphQLError describes an Error found during the parse, validate, or * execute phases of performing a GraphQL operation. In addition to a message @@ -36,6 +44,7 @@ export interface GraphQLErrorOptions { * GraphQL document and/or execution result that correspond to the Error. */ export class GraphQLError extends Error { + [isGraphQLErrorSymbol]: true = true; /** * An array of `{ line, column }` locations within the source GraphQL document * which correspond to this error. diff --git a/src/execution/subscribe.ts b/src/execution/subscribe.ts index 7a04480bf4..ac601cac45 100644 --- a/src/execution/subscribe.ts +++ b/src/execution/subscribe.ts @@ -3,7 +3,7 @@ import { isAsyncIterable } from '../jsutils/isAsyncIterable'; import type { Maybe } from '../jsutils/Maybe'; import { addPath, pathToArray } from '../jsutils/Path'; -import { GraphQLError } from '../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../error/GraphQLError'; import { locatedError } from '../error/locatedError'; import type { DocumentNode } from '../language/ast'; @@ -169,7 +169,7 @@ export async function createSourceEventStream( } catch (error) { // If it GraphQLError, report it as an ExecutionResult, containing only errors and no data. // Otherwise treat the error as a system-class error and re-throw it. - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { return { errors: [error] }; } throw error; diff --git a/src/language/source.ts b/src/language/source.ts index 15f65fceee..f45b6afc60 100644 --- a/src/language/source.ts +++ b/src/language/source.ts @@ -1,12 +1,13 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; interface Location { line: number; column: number; } +const isSourceSymbol = Symbol.for('Source'); + /** * A representation of source input to GraphQL. The `name` and `locationOffset` parameters are * optional, but they are useful for clients who store GraphQL documents in source files. @@ -15,6 +16,7 @@ interface Location { * The `line` and `column` properties in `locationOffset` are 1-indexed. */ export class Source { + [isSourceSymbol]: true = true; body: string; name: string; locationOffset: Location; @@ -53,5 +55,7 @@ export class Source { * @internal */ export function isSource(source: unknown): source is Source { - return instanceOf(source, Source); + return ( + typeof source === 'object' && source != null && isSourceSymbol in source + ); } diff --git a/src/type/definition.ts b/src/type/definition.ts index 9eea02e8ea..0f1c9befe2 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -2,7 +2,6 @@ import { devAssert } from '../jsutils/devAssert'; import { didYouMean } from '../jsutils/didYouMean'; import { identityFunc } from '../jsutils/identityFunc'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import { keyMap } from '../jsutils/keyMap'; import { keyValMap } from '../jsutils/keyValMap'; @@ -75,11 +74,17 @@ export function assertType(type: unknown): GraphQLType { return type; } +const isGraphQLScalarTypeSymbol = Symbol.for('GraphQLScalarType'); + /** * There are predicates for each kind of GraphQL type. */ export function isScalarType(type: unknown): type is GraphQLScalarType { - return instanceOf(type, GraphQLScalarType); + return ( + typeof type === 'object' && + type != null && + isGraphQLScalarTypeSymbol in type + ); } export function assertScalarType(type: unknown): GraphQLScalarType { @@ -89,8 +94,14 @@ export function assertScalarType(type: unknown): GraphQLScalarType { return type; } +const isGraphQLObjectTypeSymbol = Symbol.for('GraphQLObjectType'); + export function isObjectType(type: unknown): type is GraphQLObjectType { - return instanceOf(type, GraphQLObjectType); + return ( + typeof type === 'object' && + type != null && + isGraphQLObjectTypeSymbol in type + ); } export function assertObjectType(type: unknown): GraphQLObjectType { @@ -100,8 +111,14 @@ export function assertObjectType(type: unknown): GraphQLObjectType { return type; } +const isGraphQLInterfaceTypeSymbol = Symbol.for('GraphQLInterfaceType'); + export function isInterfaceType(type: unknown): type is GraphQLInterfaceType { - return instanceOf(type, GraphQLInterfaceType); + return ( + typeof type === 'object' && + type != null && + isGraphQLInterfaceTypeSymbol in type + ); } export function assertInterfaceType(type: unknown): GraphQLInterfaceType { @@ -113,8 +130,12 @@ export function assertInterfaceType(type: unknown): GraphQLInterfaceType { return type; } +const isGraphQLUnionTypeSymbol = Symbol.for('GraphQLUnionType'); + export function isUnionType(type: unknown): type is GraphQLUnionType { - return instanceOf(type, GraphQLUnionType); + return ( + typeof type === 'object' && type != null && isGraphQLUnionTypeSymbol in type + ); } export function assertUnionType(type: unknown): GraphQLUnionType { @@ -124,8 +145,12 @@ export function assertUnionType(type: unknown): GraphQLUnionType { return type; } +const isGraphQLEnumTypeSymbol = Symbol.for('GraphQLEnumType'); + export function isEnumType(type: unknown): type is GraphQLEnumType { - return instanceOf(type, GraphQLEnumType); + return ( + typeof type === 'object' && type != null && isGraphQLEnumTypeSymbol in type + ); } export function assertEnumType(type: unknown): GraphQLEnumType { @@ -135,10 +160,16 @@ export function assertEnumType(type: unknown): GraphQLEnumType { return type; } +const isGraphQLInputObjectTypeSymbol = Symbol.for('GraphQLInputObjectType'); + export function isInputObjectType( type: unknown, ): type is GraphQLInputObjectType { - return instanceOf(type, GraphQLInputObjectType); + return ( + typeof type === 'object' && + type != null && + isGraphQLInputObjectTypeSymbol in type + ); } export function assertInputObjectType(type: unknown): GraphQLInputObjectType { @@ -150,6 +181,8 @@ export function assertInputObjectType(type: unknown): GraphQLInputObjectType { return type; } +const isGraphQLListTypeSymbol = Symbol.for('GraphQLListType'); + export function isListType( type: GraphQLInputType, ): type is GraphQLList; @@ -158,7 +191,9 @@ export function isListType( ): type is GraphQLList; export function isListType(type: unknown): type is GraphQLList; export function isListType(type: unknown): type is GraphQLList { - return instanceOf(type, GraphQLList); + return ( + typeof type === 'object' && type != null && isGraphQLListTypeSymbol in type + ); } export function assertListType(type: unknown): GraphQLList { @@ -168,6 +203,8 @@ export function assertListType(type: unknown): GraphQLList { return type; } +const isGraphQLNonNullTypeSymbol = Symbol.for('GraphQLNonNullType'); + export function isNonNullType( type: GraphQLInputType, ): type is GraphQLNonNull; @@ -180,7 +217,11 @@ export function isNonNullType( export function isNonNullType( type: unknown, ): type is GraphQLNonNull { - return instanceOf(type, GraphQLNonNull); + return ( + typeof type === 'object' && + type != null && + isGraphQLNonNullTypeSymbol in type + ); } export function assertNonNullType(type: unknown): GraphQLNonNull { @@ -319,6 +360,7 @@ export function assertAbstractType(type: unknown): GraphQLAbstractType { * ``` */ export class GraphQLList { + readonly [isGraphQLListTypeSymbol]?: true = true; readonly ofType: T; constructor(ofType: T) { @@ -365,6 +407,7 @@ export class GraphQLList { * Note: the enforcement of non-nullability occurs within the executor. */ export class GraphQLNonNull { + readonly [isGraphQLNonNullTypeSymbol]: true = true; readonly ofType: T; constructor(ofType: T) { @@ -555,6 +598,7 @@ export interface GraphQLScalarTypeExtensions { * ``` */ export class GraphQLScalarType { + readonly [isGraphQLScalarTypeSymbol]: true = true; name: string; description: Maybe; specifiedByURL: Maybe; @@ -725,6 +769,7 @@ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { * ``` */ export class GraphQLObjectType { + readonly [isGraphQLObjectTypeSymbol]: true = true; name: string; description: Maybe; isTypeOf: Maybe>; @@ -1078,6 +1123,7 @@ export interface GraphQLInterfaceTypeExtensions { * ``` */ export class GraphQLInterfaceType { + readonly [isGraphQLInterfaceTypeSymbol]: true = true; name: string; description: Maybe; resolveType: Maybe>; @@ -1207,6 +1253,7 @@ export interface GraphQLUnionTypeExtensions { * ``` */ export class GraphQLUnionType { + readonly [isGraphQLUnionTypeSymbol]: true = true; name: string; description: Maybe; resolveType: Maybe>; @@ -1334,6 +1381,7 @@ export interface GraphQLEnumTypeExtensions { * will be used as its internal value. */ export class GraphQLEnumType /* */ { + readonly [isGraphQLEnumTypeSymbol]: true = true; name: string; description: Maybe; extensions: Readonly; @@ -1574,6 +1622,7 @@ export interface GraphQLInputObjectTypeExtensions { * ``` */ export class GraphQLInputObjectType { + readonly [isGraphQLInputObjectTypeSymbol]: true = true; name: string; description: Maybe; extensions: Readonly; diff --git a/src/type/directives.ts b/src/type/directives.ts index bb3e441a43..aa28a8f7c9 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -1,6 +1,5 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import type { Maybe } from '../jsutils/Maybe'; import { toObjMap } from '../jsutils/toObjMap'; @@ -24,7 +23,11 @@ import { GraphQLBoolean, GraphQLString } from './scalars'; * Test if the given value is a GraphQL directive. */ export function isDirective(directive: unknown): directive is GraphQLDirective { - return instanceOf(directive, GraphQLDirective); + return ( + typeof directive === 'object' && + directive != null && + isGraphQLDirectiveSymbol in directive + ); } export function assertDirective(directive: unknown): GraphQLDirective { @@ -49,11 +52,14 @@ export interface GraphQLDirectiveExtensions { [attributeName: string]: unknown; } +const isGraphQLDirectiveSymbol = Symbol.for('GraphQLDirective'); + /** * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. */ export class GraphQLDirective { + [isGraphQLDirectiveSymbol]: true = true; name: string; description: Maybe; locations: ReadonlyArray; diff --git a/src/type/schema.ts b/src/type/schema.ts index 7cc576a18f..dde9d44c68 100644 --- a/src/type/schema.ts +++ b/src/type/schema.ts @@ -1,6 +1,5 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -43,7 +42,9 @@ import { * Test if the given value is a GraphQL schema. */ export function isSchema(schema: unknown): schema is GraphQLSchema { - return instanceOf(schema, GraphQLSchema); + return ( + typeof schema === 'object' && schema != null && isSchemaSymbol in schema + ); } export function assertSchema(schema: unknown): GraphQLSchema { @@ -66,6 +67,8 @@ export interface GraphQLSchemaExtensions { [attributeName: string]: unknown; } +const isSchemaSymbol = Symbol.for('GraphQLSchema'); + /** * Schema Definition * @@ -139,6 +142,7 @@ export class GraphQLSchema { extensions: Readonly; astNode: Maybe; extensionASTNodes: ReadonlyArray; + [isSchemaSymbol]: true = true; // Used as a cache for validateSchema(). __validationErrors: Maybe>; diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index 136bee63c9..3c15162308 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -8,7 +8,7 @@ import { addPath, pathToArray } from '../jsutils/Path'; import { printPathArray } from '../jsutils/printPathArray'; import { suggestionList } from '../jsutils/suggestionList'; -import { GraphQLError } from '../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../error/GraphQLError'; import type { GraphQLInputType } from '../type/definition'; import { @@ -154,7 +154,7 @@ function coerceInputValueImpl( try { parseResult = type.parseValue(inputValue); } catch (error) { - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { onError(pathToArray(path), inputValue, error); } else { onError( diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.ts b/src/validation/rules/ValuesOfCorrectTypeRule.ts index 5d81a3833a..cee2148919 100644 --- a/src/validation/rules/ValuesOfCorrectTypeRule.ts +++ b/src/validation/rules/ValuesOfCorrectTypeRule.ts @@ -3,7 +3,7 @@ import { inspect } from '../../jsutils/inspect'; import { keyMap } from '../../jsutils/keyMap'; import { suggestionList } from '../../jsutils/suggestionList'; -import { GraphQLError } from '../../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../../error/GraphQLError'; import type { ValueNode } from '../../language/ast'; import { print } from '../../language/printer'; @@ -138,7 +138,7 @@ function isValidValueNode(context: ValidationContext, node: ValueNode): void { } } catch (error) { const typeStr = inspect(locationType); - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { context.reportError(error); } else { context.reportError( From 60180ee66b0d36c43faad3d2b7a35bc200213ab7 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Tue, 31 May 2022 04:48:38 -0700 Subject: [PATCH 2/4] chore: remove instanceOf check related files --- src/jsutils/__tests__/instanceOf-test.ts | 79 ------------------------ src/jsutils/instanceOf.ts | 54 ---------------- 2 files changed, 133 deletions(-) delete mode 100644 src/jsutils/__tests__/instanceOf-test.ts delete mode 100644 src/jsutils/instanceOf.ts diff --git a/src/jsutils/__tests__/instanceOf-test.ts b/src/jsutils/__tests__/instanceOf-test.ts deleted file mode 100644 index cbd649bfa8..0000000000 --- a/src/jsutils/__tests__/instanceOf-test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { instanceOf } from '../instanceOf'; - -describe('instanceOf', () => { - it('do not throw on values without prototype', () => { - class Foo { - get [Symbol.toStringTag]() { - return 'Foo'; - } - } - - expect(instanceOf(true, Foo)).to.equal(false); - expect(instanceOf(null, Foo)).to.equal(false); - expect(instanceOf(Object.create(null), Foo)).to.equal(false); - }); - - it('detect name clashes with older versions of this lib', () => { - function oldVersion() { - class Foo {} - return Foo; - } - - function newVersion() { - class Foo { - get [Symbol.toStringTag]() { - return 'Foo'; - } - } - return Foo; - } - - const NewClass = newVersion(); - const OldClass = oldVersion(); - expect(instanceOf(new NewClass(), NewClass)).to.equal(true); - expect(() => instanceOf(new OldClass(), NewClass)).to.throw(); - }); - - it('allows instances to have share the same constructor name', () => { - function getMinifiedClass(tag: string) { - class SomeNameAfterMinification { - get [Symbol.toStringTag]() { - return tag; - } - } - return SomeNameAfterMinification; - } - - const Foo = getMinifiedClass('Foo'); - const Bar = getMinifiedClass('Bar'); - expect(instanceOf(new Foo(), Bar)).to.equal(false); - expect(instanceOf(new Bar(), Foo)).to.equal(false); - - const DuplicateOfFoo = getMinifiedClass('Foo'); - expect(() => instanceOf(new DuplicateOfFoo(), Foo)).to.throw(); - expect(() => instanceOf(new Foo(), DuplicateOfFoo)).to.throw(); - }); - - it('fails with descriptive error message', () => { - function getFoo() { - class Foo { - get [Symbol.toStringTag]() { - return 'Foo'; - } - } - return Foo; - } - const Foo1 = getFoo(); - const Foo2 = getFoo(); - - expect(() => instanceOf(new Foo1(), Foo2)).to.throw( - /^Cannot use Foo "{}" from another module or realm./m, - ); - expect(() => instanceOf(new Foo2(), Foo1)).to.throw( - /^Cannot use Foo "{}" from another module or realm./m, - ); - }); -}); diff --git a/src/jsutils/instanceOf.ts b/src/jsutils/instanceOf.ts deleted file mode 100644 index b917cfc3bf..0000000000 --- a/src/jsutils/instanceOf.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { inspect } from './inspect'; - -/** - * A replacement for instanceof which includes an error warning when multi-realm - * constructors are detected. - * See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production - * See: https://webpack.js.org/guides/production/ - */ -export const instanceOf: (value: unknown, constructor: Constructor) => boolean = - /* c8 ignore next 6 */ - // FIXME: https://github.com/graphql/graphql-js/issues/2317 - globalThis.process?.env.NODE_ENV === 'production' - ? function instanceOf(value: unknown, constructor: Constructor): boolean { - return value instanceof constructor; - } - : function instanceOf(value: unknown, constructor: Constructor): boolean { - if (value instanceof constructor) { - return true; - } - if (typeof value === 'object' && value !== null) { - // Prefer Symbol.toStringTag since it is immune to minification. - const className = constructor.prototype[Symbol.toStringTag]; - const valueClassName = - // We still need to support constructor's name to detect conflicts with older versions of this library. - Symbol.toStringTag in value - ? // @ts-expect-error TS bug see, https://github.com/microsoft/TypeScript/issues/38009 - value[Symbol.toStringTag] - : value.constructor?.name; - if (className === valueClassName) { - const stringifiedValue = inspect(value); - throw new Error( - `Cannot use ${className} "${stringifiedValue}" from another module or realm. - -Ensure that there is only one instance of "graphql" in the node_modules -directory. If different versions of "graphql" are the dependencies of other -relied on modules, use "resolutions" to ensure only one version is installed. - -https://yarnpkg.com/en/docs/selective-version-resolutions - -Duplicate "graphql" modules cannot be used at the same time since different -versions may have different capabilities and behavior. The data from one -version used in the function from another could produce confusing and -spurious results.`, - ); - } - } - return false; - }; - -interface Constructor extends Function { - prototype: { - [Symbol.toStringTag]: string; - }; -} From 2ce8509ee635d18ff5b52824647d224acbade0b4 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Tue, 31 May 2022 10:34:07 -0700 Subject: [PATCH 3/4] feat: add version assertion implementation --- src/noMultipleVersions.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/noMultipleVersions.ts diff --git a/src/noMultipleVersions.ts b/src/noMultipleVersions.ts new file mode 100644 index 0000000000..6ab6290cb9 --- /dev/null +++ b/src/noMultipleVersions.ts @@ -0,0 +1,21 @@ +import { version } from './version'; + +const graphqlVersionSymbol = Symbol.for('loadedGraphQLVersion'); + +const currentVersion = ( + globalThis as { [graphqlVersionSymbol]?: undefined | string } +)[graphqlVersionSymbol]; + +if (currentVersion) { + if (currentVersion !== version) { + // eslint-disable-next-line no-console + console.warn( + `Loading GraphQL.js version ${version} while ${currentVersion} has already been loaded.\n` + + 'Please consider configuring your package manager or bundler to only install and resolve a single version of GraphQL.', + ); + } +} else { + (globalThis as { [graphqlVersionSymbol]?: undefined | string })[ + graphqlVersionSymbol + ] = version; +} From d82cf4b03e90794ec2cdc7b05c4b790514dcb4b7 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Tue, 31 May 2022 11:35:07 -0700 Subject: [PATCH 4/4] feat: warning in case multiple graphql-js versions are loaded at the same time --- .c8rc.json | 1 + resources/build-npm.ts | 11 ++++++++++- resources/utils.ts | 2 +- src/index.ts | 3 +++ tsconfig.json | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.c8rc.json b/.c8rc.json index 4781e9f9a0..d68993ecca 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -3,6 +3,7 @@ "include": ["src/"], "exclude": [ "src/**/index.ts", + "src/noMultipleVersions.ts", "src/**/*-fuzz.ts", "src/jsutils/Maybe.ts", "src/jsutils/ObjMap.ts", diff --git a/resources/build-npm.ts b/resources/build-npm.ts index 9836913fad..6f6ec230b2 100644 --- a/resources/build-npm.ts +++ b/resources/build-npm.ts @@ -47,7 +47,15 @@ assert( const tsHost = ts.createCompilerHost(tsOptions); tsHost.writeFile = (filepath, body) => { fs.mkdirSync(path.dirname(filepath), { recursive: true }); - writeGeneratedFile(filepath, body); + let bodyToWrite = body; + const filePath = filepath.split('npmDist')[1]; + if (filePath.endsWith('.js') && !filepath.endsWith('noMultipleVersions.js')) { + bodyToWrite = + `import '${ + new Array(filePath.split('/').length - 2).fill('..').join('/') || '.' + }/noMultipleVersions.js';\n` + bodyToWrite; + } + writeGeneratedFile(filepath, bodyToWrite); }; const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost); @@ -109,6 +117,7 @@ function buildPackageJSON() { // Temporary workaround to allow "internal" imports, no grantees provided packageJSON.exports['./*.js'] = './*.js'; packageJSON.exports['./*'] = './*.js'; + packageJSON.exports['./noMultipleVersions.js'] = null; // TODO: move to integration tests const publishTag = packageJSON.publishConfig?.tag; diff --git a/resources/utils.ts b/resources/utils.ts index c8d3bb7123..8733f82fb8 100644 --- a/resources/utils.ts +++ b/resources/utils.ts @@ -115,7 +115,7 @@ interface PackageJSON { repository?: { url?: string }; scripts?: { [name: string]: string }; type?: string; - exports: { [path: string]: string }; + exports: { [path: string]: string | null }; types?: string; typesVersions: { [ranges: string]: { [path: string]: Array } }; devDependencies?: { [name: string]: string }; diff --git a/src/index.ts b/src/index.ts index bce254f808..8a13f2544a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,9 @@ * @packageDocumentation */ +// eslint-disable-next-line import/no-unassigned-import +import './noMultipleVersions'; + // The GraphQL.js version info. export { version, versionInfo } from './version'; diff --git a/tsconfig.json b/tsconfig.json index edb522f1d4..5997c42a99 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "include": [ "src/**/*", + "src/noMultipleVersions.ts", "integrationTests/*", "resources/*", "benchmark/benchmark.ts"