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.
2
Answers
For the current being, a temporary solution I opted in is to stop using the single index and directly take the exports from the individual components.
So in a client application, instead of
I would have
This works but it's just that the import statements are not as neat as before.
We could try to eliminate the
/dist/features
portion. Subpath exports could be of help, but I found it to be not working well with TypeScript, ESLint and Jest etc.https://github.com/import-js/eslint-plugin-import/issues/1868
https://github.com/jestjs/jest/issues/12270
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: