In this post I'm going to cover a few techniques that you can use when debugging library-related issues in your Node.js applications.
I'll be using Vite.js as an example, as it provides an opportunity to cover some specifics related to debugging TypeScript packages and mono repos, but you can apply these techniques to debug any library.
Debug Output 📜
It's common for node.js packages to use the debug
library to log information about their configuration, or operations they perform.
To enable logging for these libraries, set the DEBUG
environment variable:
DEBUG=*
Enable all debug outputDEBUG=vite:*
Enable output for Vite.js core pluginsDEBUG=vite-plugin-ruby:*
Enable output for a specific plugin
Using a Debugger 🎯
Sometimes debug output is not enough to figure out what's the problem. Fortunately, there are many ways to start a debugging session.
Using node --inspect-brk
will break execution until a debugger client is attached. You must provide a JS executable as an argument, for example:
node --inspect-brk "$(npm bin)/vite"
node --inspect-brk $(yarn bin vite)
Add an
inspect
shortcut in yourscripts
if you need to debug often.
I like to use the Node Inspector Manager extension, which will automatically attach the browser's DevTools to the node process.
DevTools provide a great debugging environment, with visual breakpoints, watchers, file navigation, and more. Visual Studio Code is another great option.
It's useful to ignore Node.js internals to avoid stepping into the framework.
Opening Packages 📖
In order to learn more and understand which files you should be debugging, you can run
npm edit
to open a package in your favorite text editor.
The
EDITOR
environment variable will be used to infer the preferred text editor. I like using Sublime Text or Visual Studio Code to navigate the file tree.
For example, running npm edit vite
will open the copy of the vite
package that is installed in the node_modules
for the current directory.
Unlike the previous sections, this is something you can leverage for client-side packages as well.
Editing In-Place ✍️
Because JavaScript is a dynamic language, you can tweak the code in-place, restart the process, and your changes will be picked up.
Disclaimer: This has many caveats: bugs that only happen in your local, or the opposite, code that only seems to work in your local.
Skip to the next section for a better approach.
(I use it all the time though, so convenient 😅)
You can add console.log
as needed, or even modify the behavior of the library, which can be useful to track down bugs.
In TypeScript packages (and some JS packages), make sure to edit the transpiled file instead of the sources. This is often dist/index.js
but that depends on the library and target environment.
For example, in Vite.js the entrypoint is dist/node/index.js
.
Using a Local Library 🔗
When fixing a bug or implementing features, a better flow is to clone the library, and then point to your local copy by using the npm link
or yarn link
commands.
Let's see a step by step example with vite
:
# 1. Clone the repo
git clone git@github.com:vitejs/vite.git
cd vite/packages/vite # Location of package.json
pnpm install
# 2. Make the local package available for linking
npm link # skip if using pnpm
# 3. Start compilation in "watch" mode
pnpm dev
In TypeScript or compiled JS libraries, it's important to build the library in order for changes to the source to be reflected in the linked project.
By convention, libraries define a
build
script to perform compilation, and adev
script that starts compilation inwatch
mode—file changes will start a new build.
Finally, link the library in the project you would like to test the changes:
# 4a. Link the library by name to use the local copy
npm link vite
# 4b. If using pnpm, link it by path instead
pnpm link ../vite/packages/vite
Just as in the previous section, it's important to restart the process you intend to debug after making changes.
Why is it necessary to restart? 🤔
Most processes will load the required libraries into memory, and then use these cached versions during their entire running time.
Restarting the relevant process, such as the Vite.js development server, ensures the updated version of the library is loaded.
Additional Resources 📖
I hope this summary has been useful! A similar guide for Ruby is available 😃
Please refer to the following documentation for more information:
debug
: Conventions and available filters- Debugging Guide for node.js: CLI flags and inspector clients