Unsolving the mysteries of yarn/npm link for libraries development
Introduction
Imagine you are developing a web app, that web app belongs to a family of web apps that shares the same style in their visual components, which means that you are probably going to build a UI library to centralize all your components. That will give you the possibility of changing some behaviour, even the look and feel of specific components across all your applications by just modifying that library.
Now we have an issue: How do we test our library integration with the rest of the ecosystem locally without releasing a version every time we modify something? That’s a common bad practice that you’ll see at some companies, just releasing beta/minor versions to test stuff as they don’t have a local way to test the integration of the library with their apps.
Testing the individual components from your library can easily be done by using for example Storybook, but the integration with the rest of your applications is the tricky part, here is where you should use yarn link, the idea behind this is pretty simple: It just creates a symlink to whatever you point to.
Time to add some real work
Let's assume we have the app myApp which uses myLibrary-UI, being myLibrary-UI the UI library that provides all the base components to myApp.
You are gonna have something like this in your package.json :
1
2
3
4
5
6
7
8
{
"name": "myApp",
//engines and scripts
"dependencies": {
"@myCompany/myLibrary-ui": "^0.0.1",
//all your dependencies
},
}
Right now when we run yarn we can say that it generates the following structure: myApp is going to contain our library in its package, but if we want to use the local version instead of the published version, how do we override the version it uses?
It’s pretty simple, you should just build your library locally, then you run yarn link in the directory you build it, and by doing it you’ll register your package locally. After this, you should just go to the root directory of your application and run yarn link "@yourCompany/myLibrary-ui" , this will create a symlink to your local copy that will be resolved before the copy is installed by yarn.
By now we already have our app running using our local version of the library, this way we can easily test the integration of the new version with our app, and we can also use it to prepare our app to adopt any breaking changes from our library in case we need a simultaneous release. This might seems to be pretty straight forward but now is when the real issues begin.
Multiple definitions
I’ll assume we are using React in our library, but we are also using React in our application, this should be easily solved by our package manager while installing them in a regular way.
But as we are linking it locally we have the problem of multiple React definitions in our project, this gonna generate multiple errors that are not that easy to debug and are really not very descriptive.
The easy way to fix it is by using the link command, we just go to our library and we create a link from there to the React definition in our application, something like this: npm link "../myApp/node_modules/React” . This will create a symlink in our library to the React definition in our app. The result is our project will only use the React definition in myApp but it will run our local version of myLibrary-UI .
Now you are able to develop your library and test its integration with your apps locally just by using link commands. There are other ways to avoid multiple definitions, for example, let’s say we are using styled-components in both packages, but our app is also using Next.js, we can solve this issue by adding the resolver for this specific package in Webpack configuration:
1
2
3
4
5
6
7
8
9
module.exports = {
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
"styled-components": path.resolve('./node_modules/styled-components'),
};
return config;
},
};
Conclusion
Sometimes it’s really important to test your library locally so you can test its integration with other apps that use it. That’s really tricky sometimes but it helps you a lot to develop a better library.
By using link command you can easily achieve a configuration that allows you to test your libraries in a local environment, saving you from making for example test releases to check the integration.
Hope you like this article and most important I wish you find it helpful, I’ve been fighting with issues to test integrations between multiple packages a couple of times, some of this came out after several fails by myself.