Debugging Webpack-bundled Node.js Applications in VSCode
Tags: node, webdev, webpack, vscode
Published: 2023-10-18 14:55:10

Debugging a Node.js application using Visual Studio Code (VSCode) is a smooth experience, as VSCode comes with built-in support for Node.js debugging. Here's how you can set it up:

1. **Open Your Node.js Project in VSCode**:
   Launch Visual Studio Code, and then open your Node.js project or folder.

2. **Create a Launch Configuration**:
   Before you can debug, you'll need a launch configuration:
   - Click on the Debug icon in the sidebar (or press `Ctrl + Shift + D`).
   - In the Debug sidebar, click on the gear ⚙️ icon, and then select "Node.js" from the dropdown. This will create a `.vscode/launch.json` file with default debug configurations.
   - Modify the configurations if needed. The default configuration should be fine for most simple applications.

When you start debugging in VSCode using a configuration from `launch.json`, the debugger executes commands and settings defined in the selected configuration. The specific actions depend on the content of the `launch.json` and the type of debugger.

Here's a breakdown of common fields in `launch.json` for a Node.js application:

1. **`type`**: Specifies the type of debugger to use. For Node.js applications, it's typically set to `"node"`.

2. **`request`**: Indicates the type of debugging session. Common values are:
   - `"launch"`: Starts the program in debugging mode.
   - `"attach"`: Attaches the debugger to an already running program.

3. **`name`**: A descriptive name for the debugging configuration.

4. **`program`**: Specifies the entry file of your application. It tells the debugger which file to start executing. E.g., `"${workspaceFolder}/app.js"`.

5. **`args`**: An array of command-line arguments that will be passed to the Node.js application.

6. **`runtimeExecutable`**: If you want to use a different runtime than the default `node`, you can specify its path here.

7. **`env`**: Specifies environment variables that will be available to the application.

8. **`cwd`**: Sets the current working directory for the debugging session.

9. **`console`**: Determines where the console output is displayed. Typical values are `internalConsole`, `integratedTerminal`, or `externalTerminal`.

10. **`sourceMaps`**: Indicates whether the debugger should use JavaScript source maps to map the generated bundled code back to its original source.

11. **`sourceMapPathOverrides`**: Provides a mapping between the source path in the source map (often bundled paths) and the actual file locations on disk.

12. **`skipFiles`**: An array of file or folder names, or glob patterns, to skip when debugging.

13. **`preLaunchTask`** and **`postDebugTask`**: Reference tasks defined in `.vscode/tasks.json` to run before launching the debugger or after the debugging session ends, respectively.

When you click the green "play" button or press `F5` in VSCode to start debugging using a particular configuration, VSCode initializes the debugger specified in the `type` field and executes the program with the given settings. The debugger will then either start or attach to the specified program, and the debugging session will begin.

Remember, the specific fields in `launch.json` might vary depending on the debugger type and the nature of the application being debugged. Always refer to the documentation or official guides for the specific debugger you're using for more details.

3. **Set Breakpoints**:
   Navigate to the JavaScript or TypeScript file where you want to set a breakpoint. Click on the left margin next to the line numbers where you want to stop execution. A red dot will appear, indicating a breakpoint.

4. **Start Debugging**:
   - Click on the green play ▶️ button in the Debug sidebar or press `F5`. This will start your Node.js application in debug mode.
   - Execution will pause at your breakpoints, allowing you to inspect variables, view the call stack, step through the code, and more.

5. **Debugging Features**:
   - **Variables Panel**: View the values of variables in the current scope.
   - **Watch Panel**: Watch the values of specific variables. They update in real-time as you step through your code.
   - **Call Stack**: View the call stack to see the sequence of function calls leading to the current breakpoint.
   - **Stepping Controls**: Use the controls in the Debug toolbar to continue execution (`F5`), step over a function (`F10`), step into a function (`F11`), or step out of a function (`Shift + F11`).

6. **Inspecting Variables and Using Debug Console**:
   - Hover over variables in your code to see their current values.
   - Use the Debug Console to execute arbitrary JavaScript in the context of the paused execution. You can evaluate variables, call functions, etc.

7. **Additional Configurations**:
   If you're working with specific setups like Nodemon, TypeScript, or other tools, you might need additional configurations in your `launch.json`. There are many recipes available in the [VSCode documentation](https://code.visualstudio.com/docs/nodejs/nodejs-debugging) and online communities to help with those scenarios.

8. **Terminate/Restart**:
   While debugging, you can terminate or restart the debugging session using the red square 🔲 or the restart 🔄 button in the Debug toolbar, respectively.

With these steps, you should be able to effectively debug your Node.js applications right inside VSCode, taking advantage of the powerful debugging features it offers.

**Webpack-bundled Node.js Applications**

If your Node.js application is bundled using Webpack, you can still debug it in Visual Studio Code, but you'll need to ensure source maps are generated by Webpack and configure VSCode to use them. Here's how to set that up:

1. **Webpack Configuration**:
   
   Make sure your Webpack configuration is set up to produce source maps. You can do this by setting the `devtool` property in your `webpack.config.js`:

   ```javascript
   module. Exports = {
      output: {
            //...other output configuration properties
            devtoolModuleFilenameTemplate: '[absolute-resource-path]'
      }

       // ... other webpack configuration settings ...
       devtool: 'source-map',
   };
   ```

2. **Update your `launch.json` in VSCode**:

   In your `.vscode/launch.json`, you'll need to point VSCode to the output file produced by Webpack, and you might need to help VSCode map the source file paths in the source map to the file paths in your workspace. Here's an example configuration:

   ```json
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Webpack Bundled App",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\backend\\dist\\bundles\\server\\server.generated.js",
            "sourceMaps": true,
            "outFiles": ["${workspaceFolder}/backend/dist/bundles/server/**/*.js"], // Adjust path to where Webpack outputs your files
            "sourceMapPathOverrides": {
                "webpack://mvc/*": "${workspaceRoot}/*"
            },
            "trace": true,

        },
   ```

   In the above configuration:
   - `"program"` points to the Webpack output file.
   - `"outFiles"` helps VSCode locate the bundled JavaScript files.
   - `"sourceMapPathOverrides"` helps VSCode map the paths from the source map to the actual source files in your workspace.

3. **Set Breakpoints**:
   
   As usual, navigate to your source files (not the bundled ones) in VSCode and set breakpoints by clicking on the left margin next to the line numbers.

4. **Start Debugging**:
   
   Press `F5` or click the green play ▶️ button in the Debug sidebar. This will start your Node.js application in debug mode, and VSCode should pause execution at the breakpoints you've set in your source files, even though the running code is actually the bundled code produced by Webpack.

5. **Inspecting and Stepping Through**:
   
   You can inspect variables, use the debug console, and step through your original source code as if it was running directly, thanks to the source maps.

By ensuring that Webpack generates source maps and correctly configuring VSCode to use them, you can debug your bundled Node.js applications with ease.

Debugging Node.js applications with Webpack can sometimes be tricky due to the source map configuration. 

1. **Inline Source Maps**:
   Ensure that your Webpack configuration uses inline source maps. This can sometimes make it easier for debuggers to locate and match the correct source file:
   ```js
   devtool: 'inline-source-map'
   ```

2. **Webpack Configuration**:
   Ensure that your Webpack configuration has the following:
   ```js
   output: {
       devtoolModuleFilenameTemplate: '[absolute-resource-path]'
   }
   ```

3. **VSCode Configuration**:
   Modify your `.vscode/launch.json` to include:
   ```json
   "sourceMapPathOverrides": {
       "webpack:///./~/*": "${workspaceRoot}/node_modules/*",
       "webpack:///*": "${workspaceRoot}/*",
       "webpack:///src/*": "${workspaceRoot}/src/*"
   }
   ```

   Note: Adjust the paths accordingly based on your project's structure and the generated source maps.

4. **Verify Webpack Source Maps**:
   When you compile your application with Webpack, ensure that the source maps are generated correctly. They should be available as separate `.map` files or inline in the transpiled JavaScript files.

5. **Clean Cache**:
   VSCode maintains an internal cache for debugging information. Close VSCode, delete the `.vscode/.js-debug` folder from your user directory, and then reopen VSCode.

6. **Node Version**:
   Ensure you're using a version of Node.js that's compatible with the debugger in VSCode. Some very old or very new versions of Node.js might have issues with certain debugger features.

7. **Check Breakpoint Location**:
   Ensure that the code where you're placing a breakpoint is actually being executed. Sometimes, if the code path isn't reached, the breakpoint won't be hit.

8. **Use Debugger Statement**:
   As a temporary measure, you can insert a `debugger;` statement directly into your source code where you want to break. This is a programmatic way to tell any debugger to pause execution, and it can sometimes help verify that the debugging environment is functioning correctly.

7. **Restart VS Code**: Sometimes, the IDE might need a restart for new configurations to take effect.

8. **Clean and Rebuild**: Delete the `dist` or output directory and run Webpack again to ensure that you have the latest transpiled files and source maps.

9. **Start Debugging**:
    - Set a breakpoint in your source file.
    - Use the debugging pane in VS Code to launch your program using the configuration you just set up (e.g., "Launch Program").

10. **Check Webpack's Pathing**: If it's still not working, it's possible that Webpack's mapping doesn't match our expectations. Look into the generated source maps and see how paths are being represented. Adjust `sourceMapPathOverrides` based on that.

11. **VS Code Extensions**: Some Webpack configurations or setups might be more complex and require specific extensions for debugging. For instance, if you're using Webpack with TypeScript, the setup can be more intricate.

12. **Manual Verification**: Open the transpiled file in the `dist` (or equivalent) directory in VS Code. If you can set a breakpoint there, it means the source map isn't being correctly mapped to the original source file.

If after trying these steps you're still facing issues, it might be worth creating a minimal reproduction of your setup and seeking assistance on platforms like Stack Overflow or the GitHub repositories for Webpack or the VSCode debugger. They might be able to offer more specific guidance or identify potential bugs.

**EXTRA : Using `nodemon` with Visual Studio Code's debugger **

Using `nodemon` with Visual Studio Code's debugger can be quite beneficial, as it will automatically restart your Node.js application when file changes in the directory are detected. Here's how you can set it up:

1. **Ensure `nodemon` is Installed**:
   If you haven't already installed `nodemon`, you can do so by running:
   ```bash
   npm install -g nodemon
   ```

2. **Open Your Project in VSCode**.

3. **Open the Debugging Panel**:
   - Click on the debug icon in the left sidebar or press `Ctrl + Shift + D`.

4. **Configure Debug Settings**:
   - Click on the gear (⚙️) icon at the top of the debug panel. This will either open an existing `launch.json` or prompt you to create one if it doesn't exist.

5. **Add a `nodemon` Configuration**:
   - In the `launch.json`, add a new configuration object to use `nodemon`:

   ```json
   {
       "type": "node",
       "request": "launch",
       "name": "Nodemon Debug",
       "runtimeExecutable": "nodemon",
       "program": "${workspaceFolder}/path/to/yourScript.js",
       "restart": true,
       "console": "integratedTerminal",
       "internalConsoleOptions": "neverOpen",
       "runtimeArgs": [
           "--inspect"
       ]
   }
   ```
The `runtimeArgs` property in the Visual Studio Code's `launch.json` configuration provides a way to specify arguments that will be passed to the Node.js runtime (or any other runtime you might be debugging with) when the debugger is launched.

For Node.js, common runtime arguments include:

- `--inspect`: This activates the V8 inspector and allows debugging and profiling of Node.js applications. 
- `--inspect-brk`: This is similar to `--inspect` but also breaks (pauses execution) before user code starts, allowing you to debug code in the very beginning of your program.
- `--require some-module`: This preloads a module before the application starts.

In the context of the previous nodemon example:

```json
"runtimeArgs": [
    "--inspect"
]
```

The `--inspect` argument tells Node.js to start the application in debugging mode with the V8 inspector activated. This makes it possible for VSCode to attach its debugger to the Node.js process and interact with the V8 inspector.

In general, any option that you might pass to the Node.js executable on the command line can be specified in the `runtimeArgs` array when configuring the debugger in VSCode.

In the context of Visual Studio Code's `launch.json` configuration for debugging, the `console` property specifies where the output of the debugged program should be displayed.

The `console` property can have one of these values:

1. `internalConsole`: This directs output to the debugging console inside Visual Studio Code.
2. `integratedTerminal`: This directs output to VS Code's integrated terminal.
3. `externalTerminal`: This directs output to an external terminal or command prompt.

Here's an example of how it can be used in a debug configuration:

```json
{
    "type": "node",
    "request": "launch",
    "name": "Launch Program",
    "program": "${workspaceFolder}/app.js",
    "console": "integratedTerminal"
}
```

In this configuration, when you debug the program, its output will be shown in VS Code's integrated terminal rather than the debug console.

Choosing between these options often depends on the nature of your application and your debugging needs. For instance, if your application expects terminal input or behaves differently in a terminal environment, you might choose `integratedTerminal` or `externalTerminal`. On the other hand, if you're only interested in viewing debug output, `internalConsole` might be sufficient.

The `internalConsoleOptions` setting in Visual Studio Code's `launch.json` configuration specifies the behavior of the internal debug console when a debugging session is started.

It can have one of the following values:

1. `neverOpen`: The internal debug console will not automatically open when a debug session starts.
2. `openOnSessionStart`: The internal debug console will automatically open every time a new debug session starts.
3. `openOnFirstSessionStart`: The internal debug console will automatically open when the first debug session starts, but not for subsequent restarts or new sessions.

Here's an example of how you can use this setting in a debug configuration:

```json
{
    "type": "node",
    "request": "launch",
    "name": "Launch Program",
    "program": "${workspaceFolder}/app.js",
    "internalConsoleOptions": "openOnSessionStart"
}
```

In this configuration, every time you start a debug session, the internal debug console in VS Code will automatically open. If you were to set it to `neverOpen`, you'd need to manually open the debug console if you wanted to view its content during a debugging session.

   Make sure to replace `path/to/yourScript.js` with the relative path to the script you want to run from the root of your workspace.

6. **Save the Configuration**.

7. **Start Debugging with `nodemon`**:
   - Go back to the debug panel, select "Nodemon Debug" from the dropdown at the top, and press the green play (▶️) button or hit `F5`. This will start your application with `nodemon` in debug mode.

With this setup, every time you make changes to your code and save, `nodemon` will automatically restart your app, and VSCode will reattach the debugger, allowing you to seamlessly debug your constantly evolving codebase.