Learning by example
I could start this article by paraphrasing npm link definition from npm docs, but I think that “learning by example” is the best way to understand how something works.
Let’s start by creating new directory called linked-dir somewhere in your workspace, and inside of it let’s create simple package.json file:
Now, let’s add a file to our newly created linked-dir directory. It may be something like image, icon or just text file – the idea here is to populate our directory with some random content (in my case it’s rys1.png):
Now, using console change your working directory to – guess what – linked-dir, and execute npm link command:
By checking the last line of output we can see that some kind of “pointer” has been created. Source of it is set to /Users/[user]/.nvm/versions/node/v8.9.3/lib/node_modules/linked-dir which is our global registry of packages (if you are not using tools like nvm, your source may be set to $HOME/lib/node_modules/linked-dir), and destination is set to the directory you’re currently in.
Now, in the same workspace as previously, create new directory called my-local-project, create package.json for your new project, and after that execute npm link linked-dir command. Now it’s time to see some magic:
By checking console output, we can see that another pointer has been created. Everything starts in node_modules directory of newly created project, then it points to global registry of npm packages, and finally it points to the place we started from – our linked-dir.
Does it mean that we created a connection between linked-dir and my-local-project? Let’s check it out by putting another file to linked-dir, and in the next step let’s check content of my-local-project/node_modules/linked-dir:
Both directories contain the same files – what just happened? Using npm, we linked a package.
As documentation states, package linking is a two-step process. First, using npm link command we register our package in the global registry of all packages on our local machine, and later we can “put” the same package to some other packages or projects by executing npm link <package_name>. Final results is the same as after npm install, but instead of downloading given package from remote repository, we used our own machine to include it inside project we’re currently working on. Under the hood all of this is achieved using symlinks (Linux, macOS), or techniques similar to it (junction points on Windows). If you are interested in all the details of npm link, you should visit npm docs of this command.
Knowing the basics of npm link, now I’d like to show you two useful use cases for this command. Are you ready?
Understanding another perspective
npm link can help you understand a perspective other than one’s own. What I mean by saying that? I mean that developers who are staying in the state of “deep work” for many hours can sometimes lost their abilities to craft user-friendly APIs, because they are focused only on the internals of given solution, package or utility (by the way, this is the reason why you should try to follow API-first approach).
Imagine that you’ve been developing “the next [name-of-a-famous-library-here]” for many hours, and suddenly you noticed that some of your unit tests started failing because of recent changes. You were in the peak of your flow, so you expected to deliver even more features in the next few hours, instead of fixing tests. But okay – you might say – developer’s life. It’s time to fix some tests and right after that I can go back to “real work”. Unfortunately, process of fixing tests took longer than expected, so your mood got worse, and the same with quality with your code. Some of us already know that coding in a bad mood can result in something which would not meet your quality standards in regular conditions, and it’s especially risky when you work on some publicly exposed solutions (packages, tools, utilities for other developers).
How does the npm link help in this kind of situations? By using this command while working on – let’s say – npm package, you can always create another project just to test your library as a regular developer, any by using npm link <package name> you can now try to create something based on it.
Whole process is the same as before – first you have to execute npm link inside the directory where you work on something:
So now, having this package linked, you are able to create project next to the directory you’re currently in, and just import this tool and see how does it feel to work with it (VS Code workspaces make all of this super easy):
As you see on the image above, right after importing this library few questions appear:
- Is the exposed interface of my library sufficient for the goal I’d like to achieve? Maybe something feels odd?
- Are the docs good enough? Can I learn how to use this tool just by looking at description of parameters?
- Is the configuration good enough? Are users able to inject custom behavior to my tool?
Answering to this kind of questions can definitely raise the quality of the project you’re working on.
Organize your local workspace
To see this in practice, let’s take a look at one of these projects you may have somewhere on your hard drive. It all starts in the same way:
As your project grows, you may finally want to bundle everything into one package. So you know the rules – it’s time to open the console, run npm install …, install webpack, few loaders, configure it, check everything, fix some issues… Or maybe not? With npm link we can simplify this process!
Please look at the console output on the image below. First, I executed npm link blog-common-utils to link one of my magic packages into my project, and later it was time for npm run bundle.
There are no errors, no issues, everything has been bundled properly. What just happened?
Please take a look at my package.json once again:
At the end of this file I’ve added new section – scripts – with just one command (bundle). As a result of executing npm run bundle, I delegate process of bundling my project into another package – package with common utils for my blog.
Let’s see how it works now. As you see on the image above, it all starts in file bundle.js:
Here, using Node.js library execa I’m executing webpack bundling process, using config file from the same package:
Here I’ve included some bits of code you may be not familiar with.
The most important part of this file is property context set to process.cwd() which returns current working directory (directory with our tutorial project). We want to bundle files outside of this package with utilities, and that’s why it looks in this way.
Then, by convention, our entry file will be always called app.js – if we want to use this common bundle config, there has to be something called app.js in the project root directory. Output is straightforward – everything will be created inside dist directory. Let’s omit resolveLoader for a while and let’s focus on module property. We can use it to define the rules of bundling various types of modules and files by Webpack. In our example, all .js files will be passed through babel with preset env applied.
What’s the role of resolveLoader property? If we would not include it inside the config, Webpack would take a look at the context property and try to import loader from process.cwd() – current working directory. As a result, we would see something like this:
Module build failed: Error: Couldn’t find preset “env” relative to directory “/Users/przemyslawsmyrdek/Dev/new-blog-tutorial”
Because of the fact that our goal was to delegate as much of the bundling config as possible to separate package, we need to provide this additional property and now our bundling process works flawlessly.
It’s also worth to see package.json of the package with common utils:
As you see, there are few dependencies required to bundle our projects and to do some additional stuff. In the future we could add more loaders, more modules with configuration or maybe more helper functions, and as a result we would gain such a great library for enhancing our day-to-day development.
All of this thanks to simple npm link command.
Know your tools – use the full power of npm
npm link has of course some limitations – the biggest ‘issue’ I noticed is the fact that linking a package doesn’t mean installing it. Installation of a package in npm gives you yet another set of great features like executable scripts, and with link I was not able to replicate this. However, even with this limited set of use cases for npm link, this command can really enhance your workflow and I hope that examples provided by me motived you to spend some time on playing with it.
I also want to add something for people who use npm as just another package manager, or even package installer. There’s nothing wrong with limiting usage of it to just installing packages, but it’s also worth to check what it means to manage packages. And managing means much more than just installing them. Recently I had a chance to spend more time playing with pack, version, link and even publish, and I have to say I was really surprised about its capabilities. For the full list of npm commands and tools please visit https://docs.npmjs.com/cli/npm.
What about your feelings about npm link – have you had a chance to use this feature yet? Please feel free to share all the useful use cases of it in the comments!