Skip to content

VS Code Debugging Improvements #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 6, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 49 additions & 37 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -5,26 +5,65 @@
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"name": ".NET Core & Vue Server",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"logging": {
"moduleLoad": false
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"VUE_DEV_SERVER_PROGRESS": "true"
},
"cwd": "${workspaceFolder}",
"preLaunchTask": "build-vue-cli-serve",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll",
"args": [],
"cwd": "${workspaceFolder}",
"args": [
"/mode:attach"
],
"stopAtEntry": false,
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
},
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
"action": "startDebugging",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"name": "Chrome Browser"
},
// this will kill any stray vue-cli processes, as the .NET app can't shut them down when it is killed by the debugger
"postDebugTask": "kill"
},
{
"name": ".NET Core Web",
"type": "coreclr",
"request": "launch",
"logging": {
"moduleLoad": false
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"cwd": "${workspaceFolder}",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll",
"args": [
"/mode:start"
],
"stopAtEntry": false,
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "startDebugging",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"name": "Chrome Browser"
},
// this will kill any stray vue-cli processes, as the .NET app can't shut them down when it is killed by the debugger
"postDebugTask": "kill"
},
{
"name": ".NET Core Attach",
@@ -33,47 +72,20 @@
"processId": "${command:pickProcess}"
},
{
"name": "Launch Chrome",
"name": "Chrome Browser",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5000",
"webRoot": "${workspaceFolder}/ClientApp",
"breakOnLoad": true,
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///*": "${webRoot}/*",
"webpack:///./*": "${webRoot}/*",
"webpack:///src/*": "${webRoot}/*"
}
},
{
"name": "Launch Firefox",
"name": "Firefox Browser",
"type": "firefox",
"request": "launch",
"url": "http://localhost:5000",
"webRoot": "${workspaceFolder}/ClientApp",
"pathMappings": [
{
"url": "webpack:///src/",
"path": "${webRoot}/"
}
]
}
],
"compounds": [
{
"name": "Debug SPA and API (Chrome)",
"configurations": [
".NET Core Launch (web)",
"Launch Chrome"
]
},
{
"name": "Debug SPA and API (Firefox)",
"configurations": [
".NET Core Launch (web)",
"Launch Firefox"
]
"webRoot": "${workspaceFolder}/ClientApp"
}
]
}
66 changes: 66 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -37,6 +37,72 @@
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "kill",
"command": "dotnet",
"type": "process",
"args": [
"run",
"${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll",
"/mode:kill"
],
"presentation": {
"echo": false,
"reveal": "never",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"problemMatcher": []
},
{
"label": "vue-cli-serve",
"command": "npm",
"type": "shell",
"isBackground": true,
"options": {
"cwd": "${workspaceFolder}/ClientApp/",
"env": {
"VUE_DEV_SERVER_PROGRESS": "true"
}
},
"args": [
"run",
"serve"
],
"presentation": {
"echo": false,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": false,
"clear": true
},
"problemMatcher": [
{
"pattern": [
{
"regexp": ".",
"file": 1,
"location": 2,
"message": 3
}
],
"background": {
"activeOnStart": true,
"beginsPattern": ".",
"endsPattern": "App running at",
}
}
]
},
{
"label": "build-vue-cli-serve",
"dependsOn": ["build", "vue-cli-serve"],
"dependsOrder": "sequence",
}

]
}
2 changes: 1 addition & 1 deletion ClientApp/vue.config.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@ module.exports = {
},
transpileDependencies: ['vuetify'],
devServer: {
progress: false
progress: !!process.env.VUE_DEV_SERVER_PROGRESS
}
}
145 changes: 145 additions & 0 deletions CommandLine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// original source: https://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser

using System;
using System.Collections.Specialized;
using System.Text.RegularExpressions;

namespace CommandLine
{
public static class Arguments
{

public static bool TryGetOptions(string[] args, bool inConsole, out string mode, out ushort port, out bool https)
{
var arguments = Parse(args);
var validArgs = true;

mode = arguments["m"] ?? arguments["mode"] ?? "start";
https = arguments["http"] == null && arguments["https"] != null;
var portString = arguments["p"] ?? arguments["port"] ?? "8080";

if (mode != "start" && mode != "attach" && mode != "kill")
{
if (inConsole)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid mode; Allowed values are start | attach | kill");
Console.ResetColor();
}
validArgs = false;
}

if (!ushort.TryParse(portString, out port) || port < 80)
{
if (inConsole)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid port number specified.");
Console.ResetColor();
}
validArgs = false;
}

if (arguments["h"] != null || arguments["help"] != null) validArgs = false;

if (inConsole && !validArgs)
{
Console.WriteLine();
Console.WriteLine(" Mode Argument Options (Defaults to start)");
Console.WriteLine(" -m | --mode start -> Start the SPA server and proxy to that.");
Console.WriteLine(" -m | --mode attach -> Attach to existing SPA server");
Console.WriteLine(" -m | --mode kill -> Shutdown any existing SPA server on the specified port (used after debugging in VS Code)");
Console.WriteLine();
Console.WriteLine(" Port Argument (Defaults to 8080)");
Console.WriteLine(" -p | --port 8080 -> Specify what port to start or attach to, minimum of 80");
Console.WriteLine();
Console.WriteLine(" HTTPS (Defaults to false)");
Console.WriteLine(" -https -> Uses HTTPS");
Console.WriteLine();

}

return validArgs;

}

public static StringDictionary Parse(string[] args)
{
var parameters = new StringDictionary();
Regex splitter = new Regex(@"^-{1,2}|^/|=|:",
RegexOptions.IgnoreCase | RegexOptions.Compiled);

Regex remover = new Regex(@"^['""]?(.*?)['""]?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);

string parameter = null;
string[] parts;

// Valid parameters forms:
// {-,/,--}param{ ,=,:}((",')value(",'))
// Examples:
// -param1 value1 --param2 /param3:"Test-:-work"
// /param4=happy -param5 '--=nice=--'
foreach (string txt in args)
{
// Look for new parameters (-,/ or --) and a
// possible enclosed value (=,:)
parts = splitter.Split(txt, 3);

switch (parts.Length)
{
// Found a value (for the last parameter
// found (space separator))
case 1:
if (parameter != null)
{
if (!parameters.ContainsKey(parameter))
{
parts[0] =
remover.Replace(parts[0], "$1");

parameters.Add(parameter, parts[0]);
}
parameter = null;
}
// else Error: no parameter waiting for a value (skipped)
break;

// Found just a parameter
case 2:
// The last parameter is still waiting.
// With no value, set it to true.
if (parameter != null && !parameters.ContainsKey(parameter))
parameters.Add(parameter, "true");

parameter = parts[1];
break;

// Parameter with enclosed value
case 3:
// The last parameter is still waiting.
// With no value, set it to true.
if (parameter != null && !parameters.ContainsKey(parameter))
parameters.Add(parameter, "true");

parameter = parts[1];

// Remove possible enclosing characters (",')
if (!parameters.ContainsKey(parameter))
{
parts[2] = remover.Replace(parts[2], "$1");
parameters.Add(parameter, parts[2]);
}

parameter = null;
break;
}
}
// In case a parameter is still waiting
if (parameter != null && !parameters.ContainsKey(parameter))
parameters.Add(parameter, "true");

return parameters;
}
}
}
14 changes: 9 additions & 5 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VueCliMiddleware;

namespace AspNetCoreVueStarter
{
public class Program
{
public static void Main(string[] args)
{
if (!CommandLine.Arguments.TryGetOptions(args, true, out string mode, out ushort port, out bool https)) return;

if (mode == "kill") {
Console.WriteLine($"Killing process serving port {port}...");
PidUtils.KillPort(port, true, true);
return;
}

CreateHostBuilder(args).Build().Run();
}

17 changes: 14 additions & 3 deletions Startup.cs
Original file line number Diff line number Diff line change
@@ -34,6 +34,9 @@ public void ConfigureServices(IServiceCollection services)
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

_ = CommandLine.Arguments.TryGetOptions(System.Environment.GetCommandLineArgs(), false, out string mode, out ushort port, out bool https);

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
@@ -45,7 +48,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseHsts();
}

// app.UseHttpsRedirection();
if (https) app.UseHttpsRedirection();

app.UseStaticFiles();
if (!env.IsDevelopment())
{
@@ -73,11 +77,18 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

if (env.IsDevelopment())
{

// run npm process with client app
spa.UseVueCli(npmScript: "serve", port: 8080);
if (mode == "start") {
spa.UseVueCli(npmScript: "serve", port: port, forceKill: true, https: https);
}

// if you just prefer to proxy requests from client app, use proxy to SPA dev server instead,
// app should be already running before starting a .NET client:
// spa.UseProxyToSpaDevelopmentServer("http://localhost:8080"); // your Vue app port
// run npm process with client app
if (mode == "attach") {
spa.UseProxyToSpaDevelopmentServer($"{(https ? "https" : "http")}://localhost:{port}"); // your Vue app port
}
}
});
}