npm / cli

the package manager for JavaScript
https://docs.npmjs.com/cli/
Other
8.54k stars 3.2k forks source link

[BUG] Workspaces hoisted package is not resolving to dependent's peer dependencies #2989

Open ruluva opened 3 years ago

ruluva commented 3 years ago

Current Behavior:

I have a project using multiple workspaces. For simplicity I created a smaller project that reproduces the issue. I am using @testing-library/react to help with some unit tests, and I have pulled in the required peer dependencies of react and react-dom. Each package is using its own version of React, one is using 17.0.1 and the other 17.0.2. Both are using the same @testing-library/react package version. The hoisted packages are react (17.0.2), react-dom (17.0.2) and @testing-library/react (11.2.5). Package b has its own React versions for react and react-dom in its local node_modules. I have both projects using the @testing-library/react render function in a set of test files. When I run jest on project a, it passes without issue. When I run jest on project b (with its local versions), it fails with the following error:

package-b@1.0.0 test jest

FAIL tests/components/App.test.tsx ● Console

console.error
  Error: Uncaught [Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app
  See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.]
      at reportException (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
      at innerInvokeEventListeners (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:341:9)
      at invokeEventListeners (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
      at HTMLUnknownElementImpl._dispatch (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
      at HTMLUnknownElementImpl.dispatchEvent (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
      at HTMLUnknownElement.dispatchEvent (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
      at Object.invokeGuardedCallbackDev (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:3994:16)
      at invokeGuardedCallback (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
      at beginWork$1 (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:23964:7)
      at performUnitOfWork (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22779:12) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app
  See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
      at resolveDispatcher (/Users/USER_DIRECTORY/experimental/npm-workspaces/packages/package-b/node_modules/react/cjs/react.development.js:1476:13)
      at Object.useState (/Users/USER_DIRECTORY/experimental/npm-workspaces/packages/package-b/node_modules/react/cjs/react.development.js:1507:20)
      at App (/Users/USER_DIRECTORY/experimental/npm-workspaces/packages/package-b/src/components/App.tsx:10:29)
      at renderWithHooks (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:14985:18)
      at mountIndeterminateComponent (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:17811:13)
      at beginWork (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:19049:16)
      at HTMLUnknownElement.callCallback (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:3945:14)
      at HTMLUnknownElement.callTheUserObjectsOperation (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
      at innerInvokeEventListeners (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:338:25)
      at invokeEventListeners (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
      at HTMLUnknownElementImpl._dispatch (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
      at HTMLUnknownElementImpl.dispatchEvent (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
      at HTMLUnknownElement.dispatchEvent (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
      at Object.invokeGuardedCallbackDev (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:3994:16)
      at invokeGuardedCallback (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
      at beginWork$1 (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:23964:7)
      at performUnitOfWork (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22779:12)
      at workLoopSync (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22707:5)
      at renderRootSync (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22670:7)
      at performSyncWorkOnRoot (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22293:18)
      at scheduleUpdateOnFiber (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:21881:7)
      at updateContainer (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:25482:3)
      at /Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:26021:7
      at unbatchedUpdates (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22431:12)
      at legacyRenderSubtreeIntoContainer (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:26020:5)
      at Object.render (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:26103:10)
      at /Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/@testing-library/react/dist/pure.js:99:25
      at batchedUpdates$1 (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom.development.js:22380:12)
      at act (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/react-dom/cjs/react-dom-test-utils.development.js:1042:14)
      at Object.render (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/@testing-library/react/dist/pure.js:95:26)
      at Object.<anonymous> (/Users/USER_DIRECTORY/experimental/npm-workspaces/packages/package-b/tests/components/App.test.tsx:5:29)
      at Object.asyncJestTest (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
      at /Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jest-jasmine2/build/queueRunner.js:45:12
      at new Promise (<anonymous>)
      at mapper (/Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
      at /Users/USER_DIRECTORY/experimental/npm-workspaces/node_modules/jest-jasmine2/build/queueRunner.js:75:41
      at processTicksAndRejections (internal/process/task_queues.js:93:5)

  at VirtualConsole.<anonymous> (../../node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
  at reportException (../../node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:28)
  at innerInvokeEventListeners (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:341:9)
  at invokeEventListeners (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
  at HTMLUnknownElementImpl._dispatch (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
  at HTMLUnknownElementImpl.dispatchEvent (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
  at HTMLUnknownElement.dispatchEvent (../../node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)

console.error
  The above error occurred in the <App> component:

      at App (/Users/USER_DIRECTORY/experimental/npm-workspaces/packages/package-b/src/components/App.tsx:10:29)

  Consider adding an error boundary to your tree to customize error handling behavior.
  Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

  at logCapturedError (../../node_modules/react-dom/cjs/react-dom.development.js:20085:23)
  at update.callback (../../node_modules/react-dom/cjs/react-dom.development.js:20118:5)
  at callCallback (../../node_modules/react-dom/cjs/react-dom.development.js:12318:12)
  at commitUpdateQueue (../../node_modules/react-dom/cjs/react-dom.development.js:12339:9)
  at commitLifeCycles (../../node_modules/react-dom/cjs/react-dom.development.js:20736:11)
  at commitLayoutEffects (../../node_modules/react-dom/cjs/react-dom.development.js:23426:7)
  at HTMLUnknownElement.callCallback (../../node_modules/react-dom/cjs/react-dom.development.js:3945:14)
  at HTMLUnknownElement.callTheUserObjectsOperation (../../node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)

● Testing render

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

   8 | export const App = (props: AppProps): JSX.Element => {
   9 |   // Create the count state.
> 10 |   const [count, setCount] = useState(0);
     |                             ^
  11 |   const [user, setUser] = useState<string>();
  12 |
  13 |   // Create the counter (+1 every second).

  at resolveDispatcher (node_modules/react/cjs/react.development.js:1476:13)
  at Object.useState (node_modules/react/cjs/react.development.js:1507:20)
  at App (src/components/App.tsx:10:29)
  at renderWithHooks (../../node_modules/react-dom/cjs/react-dom.development.js:14985:18)
  at mountIndeterminateComponent (../../node_modules/react-dom/cjs/react-dom.development.js:17811:13)
  at beginWork (../../node_modules/react-dom/cjs/react-dom.development.js:19049:16)
  at HTMLUnknownElement.callCallback (../../node_modules/react-dom/cjs/react-dom.development.js:3945:14)
  at HTMLUnknownElement.callTheUserObjectsOperation (../../node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
  at innerInvokeEventListeners (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:338:25)
  at invokeEventListeners (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
  at HTMLUnknownElementImpl._dispatch (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
  at HTMLUnknownElementImpl.dispatchEvent (../../node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
  at HTMLUnknownElement.dispatchEvent (../../node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
  at Object.invokeGuardedCallbackDev (../../node_modules/react-dom/cjs/react-dom.development.js:3994:16)
  at invokeGuardedCallback (../../node_modules/react-dom/cjs/react-dom.development.js:4056:31)
  at beginWork$1 (../../node_modules/react-dom/cjs/react-dom.development.js:23964:7)
  at performUnitOfWork (../../node_modules/react-dom/cjs/react-dom.development.js:22779:12)
  at workLoopSync (../../node_modules/react-dom/cjs/react-dom.development.js:22707:5)
  at renderRootSync (../../node_modules/react-dom/cjs/react-dom.development.js:22670:7)
  at performSyncWorkOnRoot (../../node_modules/react-dom/cjs/react-dom.development.js:22293:18)
  at scheduleUpdateOnFiber (../../node_modules/react-dom/cjs/react-dom.development.js:21881:7)
  at updateContainer (../../node_modules/react-dom/cjs/react-dom.development.js:25482:3)
  at ../../node_modules/react-dom/cjs/react-dom.development.js:26021:7
  at unbatchedUpdates (../../node_modules/react-dom/cjs/react-dom.development.js:22431:12)
  at legacyRenderSubtreeIntoContainer (../../node_modules/react-dom/cjs/react-dom.development.js:26020:5)
  at Object.render (../../node_modules/react-dom/cjs/react-dom.development.js:26103:10)
  at ../../node_modules/@testing-library/react/dist/pure.js:99:25
  at batchedUpdates$1 (../../node_modules/react-dom/cjs/react-dom.development.js:22380:12)
  at act (../../node_modules/react-dom/cjs/react-dom-test-utils.development.js:1042:14)
  at Object.render (../../node_modules/@testing-library/react/dist/pure.js:95:26)
  at Object.<anonymous> (tests/components/App.test.tsx:5:29)

So the @testing-library/react packages seems to be referencing the root level React versions, which cause an issue with the local versions.

Expected Behavior:

I expected the @testing-library/react to reference the workspace specific React versions.

Steps To Reproduce:

Package A package.json: "dependencies": { "react": "17.0.2", "react-dom": "17.0.2", }, "devDependencies": { "@testing-library/react": "11.2.5", "react-test-renderer": "17.0.2", }

Package B package.json "dependencies": { "react": "17.0.1", "react-dom": "17.0.1", }, "devDependencies": { "@testing-library/react": "11.2.5", "react-test-renderer": "17.0.1", }

And I created a very simple test for each package:

import { render } from '@testing-library/react'; import { App } from '../../src/components/App';

test('Testing render', () => { const renderedElement = render();

expect(renderedElement).toBeDefined();

});

Environment:

OS: MacOS 11.2.3 Node: 14.15.4 npm: 7.7.5

darcyclarke commented 3 years ago

@ruluva appreciate you filing this; We've had some other feedback around this kind of scenario & have started to consider enhancements/options to resolve this in a nice way (ref: see this drafted PR in our npm/rfcs repo). In the meantime, you can work around this problem by using either --legacy-bundling or the --global-style flags to help nest your deps & ensure they don't conflict (this, unfortunately, means many deps won't be deduped across your workspaces - again, we're working on a solution for that)