Apology for the winding question title which might not make sense at first sight. Let me explain.
I have a library package named foo-bar-ui for some shared UI components. All the components are exported from a single index file.
// index.js
export * from './lib/ComponentA';
export * from './lib/ComponentB';
export * from './lib/ComponentC';
And in the application package, I can happily import things from this library in below format.
import { ComponentA, ComponentB } from 'foo-bar-ui';
And one day I added a new third party dependency react-xyz for ComponentC. It's only used by ComponentC, hence I declared it as a peer dependency in the library package. Any client application consuming this library can install react-xyz on its own if it needs to use ComponentC.
However, my current application only consumes ComponentA and ComponentB. Webpack bundling has no complaint at all as it does tree-shaking, which discards ComponentC completely hence there is no warning about missing react-xyz. But Jest complains on that. Below execution failure is observed in client applications consuming the library package, who did not install react-xyz as they do not use ComponentC.
Test suite failed to run
Cannot find module 'react-xyz' from 'node_modules/foo-bar-ui/lib/ComponentC.js'
Require stack:
node_modules/foo-bar-ui/lib/ComponentC.js
node_modules/foo-bar-ui/index.js'
The immediate solution seems to be exporting each individual components separately, like what lodash-es does, e.g. import isNil from 'lodash-es/isNil';. And Node.js subpath exports seems to be the tool doing the job. But I found it problematic with TypeScript, ESLint and Jest all together with module resolution issues.
What's the ultimate recommended approach to solve this problem? It seems straightforward but oddly I could not land on any easy and succinct solution right away.
It's best if you cover both of the scenarios, when the library is installed and when it's not. And jest.mock supports "virtual" modules, allowing you to mock non-existent modules, so your test can look like this: