Debug your Typescript module with npm link and Visual Studio Code
Boost Productivity and ditch console.log statements with npm link, source maps, and Visual Studio Code Debugger to troubleshoot your Typescript module.
Tuesday, Aug 18, 2020
Introduction
I've recently encountered the use case where I started to convert an npm package built previously in javascript to typescript.
Why you may ask? "Because all the cool kids are doing it!".
I've yet to set up proper unit testing, so my workflow has been creating a symlink (via npm link) from where my module is located. Making it available to be require('')
or import
into another project on my local machine.
During my initial debugging process I came across an issue where I wanted add breakpoints to the source code .ts
files but could only get VS Code debugger to pick up the breakpoints in the transpiled .js
files. Which is "okay" but I wanted to boost productivity with enabling breakpoints in my actual source files.
Setup
After quite a few frustrating hours I finally got it working on both my Windows and macOS machines with the following steps.
Attention: At the time of writing this, there is an issue with the newer version of VS Code's Js debugger (preview). In your settings, set "debug.javascript.usePreview": false
Update:
If your currently using VS Code's new Javascript Debugger (Preview), depending on when you're reading this, you may need to add a few things to .vscode / launch.json file mentioned below. Shoutout to Connor Peet on the VS Code team.
-
Twitter thread (Follow him on Twitter)
To demonstrate how to set this up we're going to create two completely separate projects:
- Typescript module, moduleA
- Project, projectB
First our Typescript Module that we will be debugging with breakpoints in VS Code.
Change Directory and init package.json file
moduleA/
Install typescript and other dependencies
moduleA/
Create your tsconfig.json file to set options for the typescript transpiler
moduleA / tsconfig.json
Then our typescript module, create file, "index.ts"
moduleA / index.ts
Finally run Typescript's compiler
moduleA /
This should generate the following in your project root
moduleA / index.js
Now you can test in an existing javascript/typescript project or create a new one from scratch.
Create new directory and switch your terminal
projectB/
Create your project application entrypoint, if you havent done so already.
projectB / index.js
Note: This currently won't work and we'll get to that in a second with npm link.
Npm and npm link (package linking)
What is npm link?
npm link is a cli command that will create a symlink in the global node_modules folder on your machine that will then link to where the command was run.
Essentially this allows us to "mock" installing the npm module as a dependency from our global node_modules directory rather from a npm published package.
In the package you wish to test locally, for this example moduleA
, execute the following.
moduleA/
Now this can be installed via npm link {name}
in all of your projects on your machine.
Then in your project you want to test the module in, "Project B", you would execute
projectB/
Not only is this useful for monorepo projects but for testing npm packages before publishing them. Read More
Now if you look into your node_modules directory, you'll see package directory, "moduleA" in the list with a arrow to the far right indicating a symlink was successfully created.
As you inspect the module, you'll see moduleA to its entirety. In addition, newer modifications to your typescript code will be updated to this location as well. So no need to repeat previous steps.
Setting up sourcemaps with tsconfig
Now that we have our module symlinked to our project to start using and testing, we will need to setup sourcemaps to set breakpoints in our typescript source file(s).
What is a source map?
A sourcemap is file that maps the transpiled js files back to its original source, that have either been written in other languages, newer versions of javascript, or typescript. The source map can either be a separate file that has a .map extension or be included inline as a comment in the transpiled code.
The whole purpose of this file is to allow the developer to add breakpoints and debugger statements to their original source code and enabling the browser or other debuggers (VS Code), to reconstruct the source files and present it in the debugger.
To enable this in this example we will need to modify the tsconfig.json file in moduleA.
The typescript compiler provides an option to generate sourcemaps for us, simply add "sourceMap" to true or pass as an flag to the tsc command directly, tsc --sourceMap
moduleA / tsconfig.json
Next time the typescript compiler runs, additional files in the output location should have been created with a .js.map extension.
Now that sourcemaps are setup, the last part is to setup VS Code debugger via the launch.json file.
Setup launch.json for VS Code debugger
What is launch.json?
Json file usually located in the .vscode
directory folder that VS Code keeps for debugging configuration information.
For our purpose we'll setup launch configuration for Node.js debugging.
projectB / .vscode / launch.json
If all of the source files that we wish the VS Code debugger to step through was in our project (projectB) then the following would not be necessary.
Since we want to use and test our moduleA package as a symlink in our projectB project, we need to assist VS Code debugger in setting a few things in our launch.json file.
As a baseline, attempt to run the VS Code debugger.
And.... nada.
Reason it did not work that time, is because we need to add a few configurations to the launch.json file.
Now make the following changes
projectB / .vscode / launch.json
-
runTimeArgs:
Arguments passed to Node.js runtime. The "--preserve-symlinks" tells Node.js to preserve any symlinks paths created via
npm link
. -
sourceMaps:
Enables the VS Code to utilize source maps if its able to find any.
-
outFiles:
Glob patterns for locating compiled/generated javascript files from its original source. The
/**/*.js
is a wildcard search for any js file(s) in the moduleA package within the node_modules directory. Otherwise the debugger would not be able to pause on any breakpoints.
Update: For using VS Code's Javascript Debugger (Preview)
Prior to an upcoming fix, apply the following to your launch.json file
- Change type from
node
topwa-node
- Add resolveSourceMapLocations with an array of [
"${workspaceFolder}/**"
]
Once the fix is live, the resolveSourceMapLocations should default to the glob pattern(s) specified in the outFiles directory.
Now try running the VS Code debugger again ;)
Success!
Conclusion
Hope this helps if you've been tripped up by this as well.
Happy Coding and Cheers!