Debugging Typescript with Visual Studio Code and module aliasing

Note: from VS Code versions after 1.46.0, the internal javascript debugger has an auto-attach mode which seems sweet, and maybe make all the text below deprecated. Turn option “Debug: Toggle Auto Attach” on, and run a npm/yarn command: your breakpoints should be hit. More details here:
https://github.com/microsoft/vscode-js-debug#debug-nodejs-processes-in-the-terminal.

Our policy is to go more and more towards Javascript, for client-side applications as well as backoffice jobs, and with that, to embrace technologies such as NodeJS and language add-ons such as TypeScript. Frameworks like Angular (web apps) and NestJS (server-side apps) provide a full range of ready-to-use schematics and release-builds optimizations. Now we want to use an IDE which leverages those tools with powerful IntelliSense and debugging possibilities. IntelliJ IDEA comes as a choice of reference but is not for every budget, so we’ll give a go for Visual Code Studio. This post is about my findings on how to debug a NestJS app (NodeJS app following Angular policies) with VSCode.

Debugging NestJS apps

To avoid ridiculous import paths ‘../../../../adir/amodule’, module aliasing is used in the form of ‘@myalias/amodule’, improving readability and ease in moving code files. The aliases are set in the tsconfig.json file at the root of the project, needing a ‘baseDir’ attribute and a set of ‘paths’ (both to set under the ‘compilerOptions’). VSCode IntelliSense knows this trick perfectly and reacts accordingly, but when it comes to debugging, the default launch.json created by VSCode struggles at matching the paths, and quickly results in :

Debugger listening on ws://127.0.0.1:31189/821980d6-4e30-4360-8ac5-6b47af4faced
For help, see: https://nodejs.org/en/docs/inspectorDebugger attached.
Error: Cannot find module ‘@myalias/app.service’
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)
at Function.Module._load (internal/modules/cjs/loader.js:508:25)
at Module.require (internal/modules/cjs/loader.js:637:17)

The trick here is to indicate explicitly that the tsconfig-paths module must be used, just like it’s indicated in the default package.json ‘start’ script :
“start”: “ts-node -r tsconfig-paths/register src/main.ts”
In VSCode launch.json file, it’s done by :
“runtimeArgs”: [“–nolazy”, “-r”, “ts-node/register”, “-r”, “tsconfig-paths/register”]

To wrap it up, VSCode launch.json file should look like :

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
“version”: “0.2.0”,
“configurations”: [
{
“type”: “node”,
“request”: “launch”,
“name”: “Debug Nest Framework”,
“args”: [“${workspaceFolder}/src/main.ts”],
“runtimeArgs”: [“–nolazy”, “-r”, “ts-node/register”, “-r”, “tsconfig-paths/register”],
“sourceMaps”: true,
“cwd”: “${workspaceRoot}”,
“protocol”: “inspector”
}
]
}

Debugging NestJS tests

It’s very very useful to be able to debug the tests written for the application – the same tests which will be automatically run within Continuous Integration. NestJS uses the Jest framework to write tests (close to the Jasmine framework), and here are the launch.json configuration to add to debug all tests or just the test currently shown in the editor :

{
    ...
    "configurations": [
        ...,
        {      
          "type": "node",
          "request": "launch",
          "name": "Debug test - all",
          "program": "${workspaceFolder}/node_modules/.bin/jest",
          "args": ["--runInBand"],
          "console": "integratedTerminal",
          "internalConsoleOptions": "neverOpen",
          "disableOptimisticBPs": true,
          "windows": {
            "program": "${workspaceFolder}/node_modules/jest/bin/jest",
          }
        },
        {
          "type": "node",
          "request": "launch",
          "name": "Debug test - current file",
          "program": "${workspaceFolder}/node_modules/.bin/jest",
          "args": [
            "${fileBasenameNoExtension}",
            "--config",
            "jest.config.js"
          ],
          "console": "integratedTerminal",
          "internalConsoleOptions": "neverOpen",
          "disableOptimisticBPs": true,
          "windows": {
            "program": "${workspaceFolder}/node_modules/jest/bin/jest",
          }
        }

    }
}