Christian Heilmann

Automatically starting a server when starting a debug session in VS Code

Thursday, March 17th, 2022 at 12:06 pm

Back in January, I posted about a launch.json file to turn VS code into an end-to-end web debugging environment. One of the features people told me was missing was to start and stop a server with the debugging session. So here is how to do this.

We add two more lines to the existing `launch.json`, defining a task to run before debugging starts and one after it end. Let’s call them `start server` and `stop server` respectively:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "pwa-msedge",
            "request": "launch",
            "name": "webdebug",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}",
            "runtimeExecutable": "stable",
            "runtimeArgs": ["--headless"],
            "preLaunchTask": "start server",
            "postDebugTask": "stop server"
        }
    ]
}

We then need to create a `tasks.json` file in the .`vscode` folder that describes these tasks. Here is the final result:

{
  "version": "2.0.0",
  "tasks": [
    {
        "label": "start server",
        "type": "shell",
        "isBackground": true,
        "command": "http-server",
        "presentation": { "reveal": "silent" },
        "problemMatcher": [{
          "pattern": [{
            "regexp": ".",
            "file": 1,"line": 1,
            "column": 1,"message": 1
          }],
          "background": {
              "activeOnStart": true,
              "beginsPattern": { "regexp": "." },
              "endsPattern": { "regexp": "." }
          },
        }]
    },
    {
        "label": "stop server",
        "command": "echo ${input:terminate}",
        "type": "shell"
    },
  ],
  "inputs": [{
    "id": "terminate",
    "type": "command",
    "command": "workbench.action.tasks.terminate",
    "args": "terminateAll"
  }]
}

Tasks are meant to run, have an end and then tell the debugger that they are ready. Normally you would, for example, use them to do some conversion or pull some information. In this case, it is a bit trickier, as we start a server and that doesn’t give us any feedback. The task never ends as the server starts and keeps running.

The `start server` task is a `shell` task, should run in the background and the command it executes is `http-server`, which is the NPM module of the same name. The presentation property is set to silent, which means that when the server starts, it doesn’t pop up the terminal in Visual Studio Code. When we use background tasks, we need to define a `problemMatcher` that tells the debug process if the task has executed successfully or if there was any issue. This can get rather complex and you need to parse the output on the Console with Regular Expressions. In this case, we keep it very open and allow anything reported on the output Console to be a success (RegEx “.”).

What this task does is open a new Terminal, enter “http-server” and hit enter for us. And once that’s done, we have a local server at our disposal, making the current Workspace folder available as `localhost:8080`, which is also what we defined in our `launch.json` as the address to navigate to.

The `stop server` task is a bit simpler. we just make it send a `terminate` command to the terminal. We then use an `inputs` directive to define the `terminal` CLI command as something that calls `workbench.action.tasks.terminate` with an argument of `terminateAll`. This closes any Terminals opened by tasks earlier.

And that’s all there is to spawn a new local server when you start debugging and close it when the debug session ends.

There is currently a bug in VS Code, that throws an error in your tasks when the `Problems` pane is not empty when the task runs. In the case of using the Edge Developer tools for VS Code extension that means any issue reported there will result in this problem. I reported this to the team and they are working on a solution.

If you want to see this in action, you can fork the simple to-do demo and run it locally. Make sure to have http-server installed via NPM.

Share on Mastodon (needs instance)

Share on Twitter

My other work: