diff --git a/integrationTests/webpack/package.json b/integrationTests/webpack/package.json index 66dd836f1e..de78f7a8d5 100644 --- a/integrationTests/webpack/package.json +++ b/integrationTests/webpack/package.json @@ -3,7 +3,7 @@ "description": "graphql-js should be compatible with Webpack", "type": "module", "scripts": { - "test": "webpack && node test.js" + "test": "webpack && node test.js && node test-esm.js" }, "dependencies": { "graphql": "file:../graphql.tgz", diff --git a/integrationTests/webpack/test-esm.js b/integrationTests/webpack/test-esm.js new file mode 100644 index 0000000000..ddb562b859 --- /dev/null +++ b/integrationTests/webpack/test-esm.js @@ -0,0 +1,14 @@ +import assert from 'assert'; + +/* eslint-disable n/no-missing-import */ +import mjs from './dist/main-mjs.cjs'; +/* eslint-enable n/no-missing-import */ + +assert.deepStrictEqual(mjs.result, { + data: { + __proto__: null, + hello: 'world', + }, +}); + +console.log('Test script: Got correct result from Webpack esm bundle!'); diff --git a/integrationTests/webpack/test.js b/integrationTests/webpack/test.js index 7abe49a679..8888695fa3 100644 --- a/integrationTests/webpack/test.js +++ b/integrationTests/webpack/test.js @@ -2,7 +2,6 @@ import assert from 'assert'; /* eslint-disable n/no-missing-import */ import cjs from './dist/main-cjs.cjs'; -import mjs from './dist/main-mjs.cjs'; /* eslint-enable n/no-missing-import */ assert.deepStrictEqual(cjs.result, { @@ -12,11 +11,4 @@ assert.deepStrictEqual(cjs.result, { }, }); -assert.deepStrictEqual(mjs.result, { - data: { - __proto__: null, - hello: 'world', - }, -}); - -console.log('Test script: Got correct result from Webpack bundle!'); +console.log('Test script: Got correct result from Webpack cjs bundle!'); diff --git a/src/jsutils/__tests__/checkForMultiplePackageInstances-test.ts b/src/jsutils/__tests__/checkForMultiplePackageInstances-test.ts new file mode 100644 index 0000000000..454e7df857 --- /dev/null +++ b/src/jsutils/__tests__/checkForMultiplePackageInstances-test.ts @@ -0,0 +1,49 @@ +/* eslint-disable no-console */ +import { expect } from 'chai'; +import { afterEach, beforeEach, describe, it } from 'mocha'; + +import { checkForMultiplePackageInstances } from '../checkForMultiplePackageInstances.js'; + +describe('check for different library versions', () => { + class OtherPackageClass {} + const graphqlPackageInstanceCheckSymbol = Symbol.for( + 'graphql-js:check-multiple-package-instances', + ); + const globalObject = globalThis as { + [graphqlPackageInstanceCheckSymbol]?: unknown; + }; + const orig = globalObject[graphqlPackageInstanceCheckSymbol]; + const origError = console.error; + let errors: Array<unknown> = []; + beforeEach(() => { + errors = []; + console.error = (...args) => { + errors = args; + }; + }); + + afterEach(() => { + globalObject[graphqlPackageInstanceCheckSymbol] = orig; + console.error = origError; + }); + + it('does not log an error under normal circumstances', () => { + checkForMultiplePackageInstances(); + expect(errors).to.deep.equal([]); + checkForMultiplePackageInstances(); + expect(errors).to.deep.equal([]); + checkForMultiplePackageInstances(); + expect(errors).to.deep.equal([]); + }); + + it('logs an error if another package has been loaded first', () => { + // simulate other version of this lib to have been loaded before this version + globalObject[graphqlPackageInstanceCheckSymbol] = new OtherPackageClass(); + + checkForMultiplePackageInstances(); + + expect(errors[0]).to.match( + /Multiple colliding versions of the `graphql` package detected\./m, + ); + }); +}); diff --git a/src/jsutils/checkForMultiplePackageInstances.ts b/src/jsutils/checkForMultiplePackageInstances.ts new file mode 100644 index 0000000000..edd8dcd7ad --- /dev/null +++ b/src/jsutils/checkForMultiplePackageInstances.ts @@ -0,0 +1,37 @@ +const graphqlPackageInstanceCheckSymbol = Symbol.for( + 'graphql-js:check-multiple-package-instances', +); + +class Check {} + +/** + * A check which throws an error warning when multi-realm constructors are detected. + */ +export function checkForMultiplePackageInstances() { + const globalObject = globalThis as { + [graphqlPackageInstanceCheckSymbol]?: Check; + }; + if (!globalObject[graphqlPackageInstanceCheckSymbol]) { + globalObject[graphqlPackageInstanceCheckSymbol] = new Check(); + return; + } + if (!(globalObject[graphqlPackageInstanceCheckSymbol] instanceof Check)) { + // eslint-disable-next-line no-console + console.error( + new Error( + `Multiple colliding versions of the \`graphql\` package detected. + +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.`, + ), + ); + } +} diff --git a/src/jsutils/instanceOf.ts b/src/jsutils/instanceOf.ts index 489d63f42d..92b4672048 100644 --- a/src/jsutils/instanceOf.ts +++ b/src/jsutils/instanceOf.ts @@ -1,5 +1,8 @@ +import { checkForMultiplePackageInstances } from './checkForMultiplePackageInstances.js'; import { inspect } from './inspect.js'; +checkForMultiplePackageInstances(); + /** * A replacement for instanceof which includes an error warning when multi-realm * constructors are detected.