next.jsneovimjavascript-debuggerneovim-plugin

Correct setup for debugging NextJs app inside Neovim with dap


I've been trying to set up neovim and dap for Nexjs app for more then 5 month. So I'm here.

This is the .vscode/launch.json in project directory.

  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch file",
      "type": "go",
      "request": "launch",
      "env": {
        "USER_NAME": "REPLACE_FROM_SECRETS_MANAGER",
        "USER_PASS": "REPLACE_FROM_SECRETS_MANAGER",
        "BASE_URL": "https://dev.liminal.security"
      },
      "mode": "debug",
      "program": "${file}"
    },
    {
      "name": "Next.js: debug server-side",
      "type": "node-terminal",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "command": "rm -rf .next/ && pnpm next dev -p 3000"
    },
    {
      "name": "Next.js: debug client-side",
      "type": "pwa-chrome",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "url": "http://localhost:3000"
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node-terminal",
      "request": "launch",
      "command": "pnpm dev",
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "serverReadyAction": {
        "pattern": "started server on .+, url: (https?://.+)",
        "uriFormat": "%s",
        "action": "debugWithChrome"
      }
    }
  ]
}

I'm using Neovim 9+ version, this is my neovim config

I succeed in debugging a single js file. So the vscode-js-debug is installed properly.

I would really appreciate any help...

I tried all adapters, another debugger, looking through logs, launch an debugger from terminal, but didn't succeed. I use neovim as an idea and want to have the same functionality for debugging as VSCode


Solution

  • Probably won't be able to share an experience fulfilling "want to have the same functionality for debugging as VSCode" request but can give you some ideas/understandings that worked in my case.

    Serverside part

    In order to debug serverside (basically everything which doesn't have "use client" line on top of the source file would be server-rendered, right?) part of Next.js application we'll:

    1. Add mfussenegger/nvim-dap to your configuration along with some dependencies and code to configure it with microsoft/vscode-js-debug debug adapter (for js/ts code):
    {
        'mfussenegger/nvim-dap',
        dependencies = {
          { 'theHamsta/nvim-dap-virtual-text', config = true },
          { 'rcarriga/nvim-dap-ui', dependencies = { 'nvim-neotest/nvim-nio' }, config = true },
        },
        config = function()
          require('dap').adapters['pwa-node'] = {
            type = 'server',
            host = 'localhost',
            port = '${port}',
            executable = {
              command = 'node',
              -- TODO update with your correct path (when you install it in the next step)
              args = { '/YOUR_PATH/js-debug/src/dapDebugServer.js', '${port}' },
            },
          }
    
          -- ...
    
          local dap = require 'dap'
          local js_based_languages = { 'typescript', 'javascript', 'typescriptreact', 'javascriptreact' }
          for _, language in ipairs(js_based_languages) do
            dap.configurations[language] = {
              -- ...
              {
                name = 'Next.js: debug server-side',
                type = 'pwa-node',
                request = 'attach',
                port = 9231,
                skipFiles = { '<node_internals>/**', 'node_modules/**' },
                cwd = '${workspaceFolder}',
              },
              -- ...
          end
    

    Note: I had a bad experience with nvim-dap-vscode-js for remote (dockerized) application debug (breakpoints in TypeScript were not hit), but dapDebugServer.js configured directly did a better job.

    1. Install vscode-js-debug adapter as described in here, Install vscode-js-debug: section (basically, download release archive, extract it and update the path to dapDebugServer.js in the example config above).

    2. Launch your application with debug enabled, like this:

      "scripts": {
        "dev-debug": "NODE_OPTIONS='--inspect=9230' next",
    }
    
    npm run dev-debug
    

    Peculiarity here is that despite mentioning This would try to start multiple debuggers on the same port: one for the npm/yarn process and one for Next.js. (here) and specifically saying that NODE_OPTIONS should go inside scripts section of package.json like this NODE_OPTIONS='--inspect' next dev, next still launches 2 debug processes in 2 consecutive ports when done like mentioned in the docs. The second port (note: we specify 9230 debug port in scripts launch, so attaching to 9231 as specified in configuration) is usable for serverside next.js debugging though (maybe it was like that just on the version, which was in use, that is, 14.0.0), or maybe a bug.

    1. At this point you should be able to set a breakpoint in some server-rendered page, attach to the running app and get it hit.

    Clientside part

    1. For this case to work we'll need to setup one more debug adapter, namely, Microsoft/vscode-chrome-debug. Despite the fact that This repository has been archived it works with current Chrome (125.0.6422.60) at the time of writing. Maybe there's a corresponding binary at the new repository "microsoft/vscode-js-debug" or some special usage of dapDebugServer.js, this question needs further investigation.
          require('dap').adapters['chrome'] = {
            type = 'executable',
            command = 'node',
            -- TODO change to your correct path
            args = { '/YOUR_PATH/vscode-chrome-debug/out/src/chromeDebug.js' },
          }
    
    1. And add another run debug configuration (project that I tested wasn't using Turbopack, if you use it, then check the link near sourceMapPathOverrides to see corresponding path mappings):
          for _, language in ipairs(js_based_languages) do
            dap.configurations[language] = {
              -- ...
              {
                name = 'Next.js: debug client-side',
                type = 'chrome',
                request = 'launch',
                url = 'http://localhost:3000',
                webRoot = '${workspaceFolder}',
                sourceMaps = true, -- https://github.com/vercel/next.js/issues/56702#issuecomment-1913443304
                sourceMapPathOverrides = {
                  ['webpack://_N_E/*'] = '${webRoot}/*',
                },
              },
              -- ...
          end
    
    1. Set a breakpoint (in some client-side code) and launch this configuration, it would start up the browser with remote debugging enabled and attach the debugger to it.

    Note: you'd need to have Google Chrome available on your system (I tested on WSL2 and browser was launching from inside of it). Note2: sometimes the breakpoints don't work the first time the page loads, try refreshing to see if that makes it work.

    So if you'd like to debug the whole full-stack, you'd need to run 2 debug sessions in parallel, 1 for server side and 1 for client side which could involve a bit more complex manipulations with dap-ui so that it is shown at a proper time.

    As for

    serverReadyAction

    "The discussion of microsoft/debug-adapter-protocol#23 came to the conclusion not to extend the Debug Adapter Protocol, but to implement a new VS Code feature instead." phrase from this issue, means, as far as I understand, that its support should be implemented in, for example, nvim-dap module or somewhere else on Neovim side. And the feature itself assumes that VS Code when there's console output that matches specified regexp, extracts a value from it (http link) and passes it as a parameter to the second debug configuration (referenced by name in the block) which is launched.

    Hope this helps!