I'm using the VS Code Debugger for Chrome to debug my web app. In the launch.json where all the configurations are the task is defined like this

{
    "name": "Debug App in Chrome",
    "type": "chrome",
    "request": "launch",
    "url": "http://localhost:8080/",
    "webRoot": "${workspaceRoot}",
    "sourceMaps": true
}

That works fine. Opens the browser on the specific address and the debug tools work fine.

But I want before opening the browser to have a preLaunchTask that would build my project and create a server so when the browser is open there is an actual server on that address/port. So I added this line "preLaunchTask": "server:dev". That task builds the project, starts a watch mode and creates the server. But when I added that task, the prelaunch task is executed successfully but the browser does not open at all.

I'm guessing that is because the --watch flag, actually keeps the task "alive" (and same maybe for the creation of the server) and the VS Code waits for the preLaunchTask to terminate completely before starting the main task?

My question is: Is there a way to tell VS Code that the prelaunch task actually does not terminate, so it should not wait for it to end completely before starting the main task? Or I'll not be able to do all this (building, watching, starting server and opening the browser) with only 1 button?

Yes you can - Just add isBackground:true to your task definition:

{
  // ...
  "isBackground": true
}

Note: If you don't care about parsing terminal output, you can ignore the warning about not having a problemMatcher.

According to this section in the docs:

Can a background task be used as a prelaunchTask in launch.json?

Yes. Since a background task will run until killed, a background task on its own has no signal that it has "completed". To use a background task as a prelaunchTask, you must add an appropriate background problemMatcher to the background task so that there is a way for the task system and debug system to know that the task "finished".

Your task could be:

{
  "type": "npm",
  "script": "watch",
  "problemMatcher": "$tsc-watch",
  "isBackground": true
}

Note: The $tsc-watch is a background problem matcher, as is required for a background task.

You can then use the task as a prelaunchTask in your launch.json file:

{
  "name": "Launch Extension",
  "type": "extensionHost",
  "request": "launch",
  "runtimeExecutable": "${execPath}",
  "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
  "stopOnEntry": false,
  "sourceMaps": true,
  "outFiles": ["${workspaceRoot}/out/src/**/*.js"],
  "preLaunchTask": "npm: watch"
}

For more on background tasks, go to Background / watching tasks.

See Also: How to make vscode not wait for finishing a preLaunchTask?