Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Ensure that there is only one instance of "graphql" in the node_modules directory" on staging environment #7446

Closed
TimTim75 opened this issue Dec 10, 2020 · 18 comments
Labels

Comments

@TimTim75
Copy link

I have some legacy code using react-apollo and apollo-client querying and mutating with the apollo react-components.

Trying to updating slowly my code, I integrated hooks, having components still working. To do that, I deleted all apollo related package and replace them by @apollo/client according to Migrating to Apollo Client 3.0.

  "dependencies" : {
    "@apollo/client": "^3.3.4",
    ...
    "graphql": "^14.6.0",
    "graphql-tag": "^2.10.3",
  },

I am using yarn to install my packages and Parcel to bundle my app.

Everything is working like a charm locally. So I have both hooks and components queries cohabiting.

My issue occurs when I run my app on staging environment. Build and Deploy passed, but when I browse to my staging url, I get this error:

Uncaught Error: Cannot use e "__Schema" 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.

Checking my yarn.lock only gives me one reference for graphql.

Looking for a solution, I hit this stackoverflow answer to the exact same issue.

Indeed, if I set NODE_ENV=production instead or staging it works, but my staging environment is not my production environment and the error still occurs with NODE_ENV=development, so that doesn’t answer my issue.

Do you have any key or suggestion ?

@benjamn
Copy link
Member

benjamn commented Dec 10, 2020

@TimTim75 Just to be sure, could you also check for duplicate graphql packages within your node_modules directory?

find node_modules | grep '\bgraphql$'

Note: this command assumes you're using a UNIX-compatible shell with find and grep.

@TimTim75
Copy link
Author

@benjamn thanks for your quick answer

Here is my output of your line

    find node_modules | grep '\bgraphql$'
    > node_modules/@apollo/client/utilities/graphql
    > node_modules/graphql

@benjamn
Copy link
Member

benjamn commented Dec 11, 2020

@TimTim75 Does your Parcel setup do any kind of hot module replacement or fast refresh? Wondering if the graphql package might be getting evaluated more than once, somehow.

@TimTim75
Copy link
Author

@benjamn I don't have any hot module replacement nor fast refresh.

Here is my package.json if it can help in any way.

{
  "name": "my-app",
  "version": "0.0.0",
  "description": "My App.",
  "engines": {
    "node": ">= 12",
    "npm": "6.11",
    "yarn": ">= 1.17"
  },
  "dependencies": {
    "@apollo/client": "^3.3.5",
    "@apollo/react-testing": "^3.0.1",
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.8.4",
    "@babel/node": "^7.8.4",
    "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "@babel/preset-react": "^7.8.3",
    "@formatjs/intl-relativetimeformat": "^4.5.9",
    "@sentry/browser": "^5.12.5",
    "@stripe/react-stripe-js": "^1.0.3",
    "@urbancampus/uc-kit": "^0.3.0",
    "apollo-link": "^1.2.13",
    "apollo-link-context": "^1.0.19",
    "apollo-upload-client": "^12.1.0",
    "babel-core": "^7.0.0-bridge.0",
    "babel-plugin-add-react-displayname": "^0.0.5",
    "babel-plugin-react-intl": "^5.1.18",
    "babel-polyfill": "^6.26.0",
    "date-fns": "^2.10.0",
    "datepicker": "^0.0.0",
    "graphql": "^15.4.0",
    "graphql-tag": "^2.10.3",
    "hellosign-embedded": "^2.7.1",
    "lodash": "^4.17.15",
    "moment": "^2.29.1",
    "parcel-bundler": "^1.12.3",
    "prop-types": "^15.7.1",
    "react": "^16.13.0",
    "react-datepicker": "^2.13.0",
    "react-dom": "^16.13.0",
    "react-ga": "^2.6.0",
    "react-intl": "^3.12.0",
    "react-modal": "^3.11.2",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.1.2",
    "react-test-renderer": "^16.13.0",
    "react-toastify": "^5.5.0",
    "react-tooltip": "^4.0.3",
    "styled-components": "^5.0.1"
  },
  "devDependencies": {
    "babel-jest": "^25.1.0",
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.2",
    "enzyme-to-json": "^3.4.4",
    "eslint": "^7.12.1",
    "eslint-config-airbnb": "^18.2.0",
    "eslint-plugin-import": "^2.20.1",
    "eslint-plugin-jsx-a11y": "^6.2.1",
    "eslint-plugin-react": "^7.18.3",
    "eslint-plugin-react-hooks": "^4.2.0",
    "jest": "^25.1.0",
    "jest-styled-components": "^7.0.0",
    "pug": "^2.0.4"
  },
  "scripts": {
    "build:langs": "NODE_ENV='production' babel-node scripts/mergeMessages.js",
    "build": "npm run build:langs && parcel build src/index.pug --out-dir build --public-url / --no-cache --no-source-maps",
    "start": "parcel src/index.pug --no-cache --no-source-maps",
    "test": "jest -u --coverage",
    "lint": "eslint --ext js ."
  },
  "jest": {
    "setupFilesAfterEnv": [
      "<rootDir>/setupTest.js"
    ],
    "moduleNameMapper": {
      "\\.(css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/scripts/assetsTransformer.js"
    }
  },
  "resolutions": {
    "@babel/preset-env": "7.5.5"
  },
  "alias": {
    "react": "./node_modules/react"
  },
  "eslintConfig": {
    "env": {
      "es6": true,
      "browser": true,
      "node": true
    },
    "parserOptions": {
      "ecmaVersion": 8,
      "ecmaFeatures": {
        "jsx": true
      },
      "sourceType": "module"
    }
  }
}

@TimTim75
Copy link
Author

Hello @benjamn,

Any chances you had a few time to have a look on it ?

Thanks a lot

@cberez
Copy link

cberez commented Jan 12, 2021

Hi @benjamn, any news on this issue? I tried the same setup with version 6.6.3 and we get the same isssue... Thanks

@TimTim75
Copy link
Author

Hello @benjamn,

Do you have any clues on what we can do to avoid this error ?

Thanks

@benjamn
Copy link
Member

benjamn commented Jan 19, 2021

@TimTim75 @cberez I tried using @TimTim75's package.json to create a basic Parcel app, but I wasn't immediately able to reproduce the error. If either of you can reproduce the problem, that would help a lot. I realize that reproduction template is not using Parcel, but I would be happy to add a Parcel example to https://github.com/apollographql/apollo-client/tree/main/examples/bundling if that seems like it would help.

@TimTim75
Copy link
Author

@benjamn Thanks for trying it.

I have tried, on my side, to reproduce the error with the template you gave us. But I can't.

Maybe you would have an idea if I give you more context. What we are trying to do is to have components calls and useQuery hook calls to work together.
But it seems that depending on which I am using, both can't coexist.

I think that if you have a simple way to do that, it will fix our issue.

Thanks

@benjamn
Copy link
Member

benjamn commented Jan 25, 2021

@TimTim75 Ahh, that is helpful!

Where are you importing the Query component from? I believe it should come from @apollo/client/react/components, in case you're still using @apollo/react-components or something else.

The Query component should work together with useQuery (we do that for some of our own legacy apps at Apollo), so if it's not working for you, that's definitely a bug that we will fix (once diagnosed).

@TimTim75
Copy link
Author

Indeed, and it's working like a charm locally, but going on our staging environment gives us the error:

Uncaught Error: Cannot use e "__Schema" 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.

@benjamn
Copy link
Member

benjamn commented Jan 26, 2021

@TimTim75 After some more research, here's my best theory:

In your staging environment, code is presumably minified (to match production), which means class names are shortened, possibly using the same short string for multiple different classes (e in this case).

Your minifier assumes it's safe to use e for multiple different classes because their names don't collide in the same scope, but that optimization is not entirely safe, because graphql package's instanceOf utility examines constructor.name to trigger the Cannot use e "__Schema" from another module or realm error when value instanceof constructor fails. The reasoning here is that instanceof might have failed just because you're using a constructor from a different realm, which is potentially a mistake you'd want to know about. However, I'm confident we've ruled out that possibility in your case, because you do not seem to have multiple copies of the graphql package in your node_modules.

This error is disabled when NODE_ENV is "production", thanks to graphql/graphql-js#1174, but there's still a risk of false positives any time code is minified in a non-production environment, because the minification is what causes the renaming of classes that confuses instanceOf. Since you're running with NODE_ENV="staging" and also minifying the code, this error can happen whenever value instanceof constructor is false, as long as value.constructor.name === constructor.name, which can happen by accident just because multiple different minified classes all have constructor.name === "e". In these cases, instanceOf(value, constructor) really should return false, without an error.

I'm going to attempt a PR to fix this problem upstream in https://github.com/graphql/graphql-js, and I will keep thinking about other ways to work around it, but I wanted to let you know what's going on as soon as I figured it out.

benjamn added a commit to apollographql/graphql-js that referenced this issue Jan 26, 2021
An improvement beyond graphql#1174, for non-production environments that enable
minification, such as NODE_ENV='staging'.

Since graphql-js goes to the trouble of providing a Symbol.toStringTag
property for most of the classes it exports, and that string is immune to
minification (unlike constructor.name), we should use it for error
checking in instanceOf(value, constructor) whenever the constructor
provides a tag, falling back to constructor.name and
value.constructor.name for constructors that do not define
Symbol.toStringTag (as before).

Motivating issue/investigation:
apollographql/apollo-client#7446 (comment)
@benjamn
Copy link
Member

benjamn commented Jan 26, 2021

Here's the PR: graphql/graphql-js#2894. Please have a look!

@benjamn
Copy link
Member

benjamn commented Jan 26, 2021

@TimTim75 In the meantime, if you're using Parcel, you should be able to configure the terser minifier options specifically for the graphql package by writing the following contents into a node_modules/graphql/.terserrc file:

{
  "keep_classnames": true,
  "keep_fnames": true
}

Of course, this file will get thrown away every time you rerun npm install, but you can use a tool called patch-package to make sure it gets added after every npm install.

@cberez
Copy link

cberez commented Jan 28, 2021

@benjamn unfortunately even with the .terserrc fix we still have the issue but disabling minification in parcel (ie parcel build --no-minify) does the work so your analysis seems correct. I'll check if the terser fix needs to be applied to other graphql libs we depend on.

I had a look at the PR on graphql, I hope it won't be stuck until v16 🙏

@cberez
Copy link

cberez commented Jan 28, 2021

Had a look at our Dockerfile and the patches directory wasn't copied before yarn command, fixed it and now the patch works as expected 🎉

We'll be monitoring the graphql PR and keep the patch in the meantime, thx @benjamn

benjamn added a commit to apollographql/graphql-js that referenced this issue Mar 25, 2021
An improvement beyond graphql#1174, for non-production environments that enable
minification, such as NODE_ENV='staging'.

Since graphql-js goes to the trouble of providing a Symbol.toStringTag
property for most of the classes it exports, and that string is immune to
minification (unlike constructor.name), we should use it for error
checking in instanceOf(value, constructor) whenever the constructor
provides a tag, falling back to constructor.name and
value.constructor.name for constructors that do not define
Symbol.toStringTag (as before).

Motivating issue/investigation:
apollographql/apollo-client#7446 (comment)
@phryneas
Copy link
Member

As this has been fixed over in graphql/graphql-js#3172 (shipped in graphql 16), I believe we can close this issue here.

If anything else comes up with a similar warning, please open a new issue over in the graphql repo :)

Copy link
Contributor

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants