Skip to content

Commit

Permalink
Backport instanceOf Error Check Improvements
Browse files Browse the repository at this point in the history
This backports graphql#3172 to the `15.x.x` branch so that users of GraphQL
v15.x can take advantage of it. GraphQL v16.x has some breaking changes
and is not possible to run when you have other libraries depending on
the behavior of v15, such as GraphQL Code Generator.
  • Loading branch information
tubbo committed Jun 17, 2021
1 parent 1611bbb commit 4fdaade
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 8 deletions.
32 changes: 29 additions & 3 deletions src/jsutils/__tests__/instanceOf-test.js
Expand Up @@ -4,19 +4,45 @@ import { describe, it } from 'mocha';
import instanceOf from '../instanceOf';

describe('instanceOf', () => {
it('allows instances to have share the same constructor name', () => {
function getMinifiedClass(tag: string) {
class SomeNameAfterMinification {
// $FlowFixMe[unsupported-syntax]
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 {}
class Foo {
// $FlowFixMe[unsupported-syntax]
get [Symbol.toStringTag]() {
return 'Foo';
}
}
return Foo;
}
const Foo1 = getFoo();
const Foo2 = getFoo();

expect(() => instanceOf(new Foo1(), Foo2)).to.throw(
/^Cannot use Foo "\[object Object\]" from another module or realm./m,
/^Cannot use Foo "{}" from another module or realm./m,
);
expect(() => instanceOf(new Foo2(), Foo1)).to.throw(
/^Cannot use Foo "\[object Object\]" from another module or realm./m,
/^Cannot use Foo "{}" from another module or realm./m,
);
});
});
18 changes: 13 additions & 5 deletions src/jsutils/instanceOf.js
@@ -1,3 +1,5 @@
import inspect from './inspect';

/**
* A replacement for instanceof which includes an error warning when multi-realm
* constructors are detected.
Expand All @@ -20,12 +22,18 @@ export default process.env.NODE_ENV === 'production'
if (value instanceof constructor) {
return true;
}
if (value) {
const valueClass = value.constructor;
const className = constructor.name;
if (className && valueClass && valueClass.name === className) {
if (typeof value === 'object' && value !== null) {
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} "${value}" from another module or realm.
`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
Expand Down

0 comments on commit 4fdaade

Please sign in to comment.