Support new config options to launch the Node process with a debug listener. This is compatible with node-inspector.

This commit is contained in:
SteveSandersonMS
2016-07-26 18:33:27 +01:00
parent 79872c1bde
commit f2f67fe880
5 changed files with 60 additions and 13 deletions

View File

@@ -68,10 +68,12 @@ namespace Microsoft.AspNetCore.NodeServices
switch (options.HostingModel) switch (options.HostingModel)
{ {
case NodeHostingModel.Http: case NodeHostingModel.Http:
return new HttpNodeInstance(options.ProjectPath, options.WatchFileExtensions, logger, /* port */ 0); return new HttpNodeInstance(options.ProjectPath, options.WatchFileExtensions, logger,
options.LaunchWithDebugging, options.DebuggingPort, /* port */ 0);
case NodeHostingModel.Socket: case NodeHostingModel.Socket:
var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string
return new SocketNodeInstance(options.ProjectPath, options.WatchFileExtensions, pipeName, logger); return new SocketNodeInstance(options.ProjectPath, options.WatchFileExtensions, pipeName, logger,
options.LaunchWithDebugging, options.DebuggingPort);
default: default:
throw new ArgumentException("Unknown hosting model: " + options.HostingModel); throw new ArgumentException("Unknown hosting model: " + options.HostingModel);
} }

View File

@@ -21,5 +21,7 @@ namespace Microsoft.AspNetCore.NodeServices
public string ProjectPath { get; set; } public string ProjectPath { get; set; }
public string[] WatchFileExtensions { get; set; } public string[] WatchFileExtensions { get; set; }
public ILogger NodeInstanceOutputLogger { get; set; } public ILogger NodeInstanceOutputLogger { get; set; }
public bool LaunchWithDebugging { get; set; }
public int? DebuggingPort { get; set; }
} }
} }

View File

@@ -33,15 +33,18 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private bool _disposed; private bool _disposed;
private int _portNumber; private int _portNumber;
public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger, int port = 0) public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger,
: base( bool launchWithDebugging, int? debuggingPort, int port = 0)
: base(
EmbeddedResourceReader.Read( EmbeddedResourceReader.Read(
typeof(HttpNodeInstance), typeof(HttpNodeInstance),
"/Content/Node/entrypoint-http.js"), "/Content/Node/entrypoint-http.js"),
projectPath, projectPath,
watchFileExtensions, watchFileExtensions,
MakeCommandLineOptions(port), MakeCommandLineOptions(port),
nodeInstanceOutputLogger) nodeInstanceOutputLogger,
launchWithDebugging,
debuggingPort)
{ {
_client = new HttpClient(); _client = new HttpClient();
} }

View File

@@ -21,11 +21,22 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
{ {
protected readonly ILogger OutputLogger; protected readonly ILogger OutputLogger;
private const string ConnectionEstablishedMessage = "[Microsoft.AspNetCore.NodeServices:Listening]"; private const string ConnectionEstablishedMessage = "[Microsoft.AspNetCore.NodeServices:Listening]";
private const string DebuggingStartedMessageFormat = @"-----
*** Node.js debugging is enabled ***
{0}
To debug, run:
node-inspector{1}
If you haven't yet installed node-inspector, you can do so as follows:
npm install -g node-inspector
-----";
private readonly TaskCompletionSource<object> _connectionIsReadySource = new TaskCompletionSource<object>(); private readonly TaskCompletionSource<object> _connectionIsReadySource = new TaskCompletionSource<object>();
private bool _disposed; private bool _disposed;
private readonly StringAsTempFile _entryPointScript; private readonly StringAsTempFile _entryPointScript;
private FileSystemWatcher _fileSystemWatcher; private FileSystemWatcher _fileSystemWatcher;
private readonly Process _nodeProcess; private readonly Process _nodeProcess;
private int? _nodeDebuggingPort;
private bool _nodeProcessNeedsRestart; private bool _nodeProcessNeedsRestart;
private readonly string[] _watchFileExtensions; private readonly string[] _watchFileExtensions;
@@ -34,7 +45,9 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
string projectPath, string projectPath,
string[] watchFileExtensions, string[] watchFileExtensions,
string commandLineArguments, string commandLineArguments,
ILogger nodeOutputLogger) ILogger nodeOutputLogger,
bool launchWithDebugging,
int? debuggingPort)
{ {
if (nodeOutputLogger == null) if (nodeOutputLogger == null)
{ {
@@ -44,7 +57,8 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
OutputLogger = nodeOutputLogger; OutputLogger = nodeOutputLogger;
_entryPointScript = new StringAsTempFile(entryPointScript); _entryPointScript = new StringAsTempFile(entryPointScript);
var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments); var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments,
launchWithDebugging, debuggingPort);
_nodeProcess = LaunchNodeProcess(startInfo); _nodeProcess = LaunchNodeProcess(startInfo);
_watchFileExtensions = watchFileExtensions; _watchFileExtensions = watchFileExtensions;
_fileSystemWatcher = BeginFileWatcher(projectPath); _fileSystemWatcher = BeginFileWatcher(projectPath);
@@ -84,11 +98,23 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
// This method is virtual, as it provides a way to override the NODE_PATH or the path to node.exe // This method is virtual, as it provides a way to override the NODE_PATH or the path to node.exe
protected virtual ProcessStartInfo PrepareNodeProcessStartInfo( protected virtual ProcessStartInfo PrepareNodeProcessStartInfo(
string entryPointFilename, string projectPath, string commandLineArguments) string entryPointFilename, string projectPath, string commandLineArguments,
bool launchWithDebugging, int? debuggingPort)
{ {
string debuggingArgs;
if (launchWithDebugging)
{
debuggingArgs = debuggingPort.HasValue ? $"--debug={debuggingPort.Value} " : "--debug ";
_nodeDebuggingPort = debuggingPort;
}
else
{
debuggingArgs = string.Empty;
}
var startInfo = new ProcessStartInfo("node") var startInfo = new ProcessStartInfo("node")
{ {
Arguments = "\"" + entryPointFilename + "\" " + (commandLineArguments ?? string.Empty), Arguments = debuggingArgs + "\"" + entryPointFilename + "\" " + (commandLineArguments ?? string.Empty),
UseShellExecute = false, UseShellExecute = false,
RedirectStandardInput = true, RedirectStandardInput = true,
RedirectStandardOutput = true, RedirectStandardOutput = true,
@@ -201,7 +227,12 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
{ {
if (evt.Data != null) if (evt.Data != null)
{ {
if (!initializationIsCompleted) if (IsDebuggerListeningMessage(evt.Data))
{
var debugPortArg = _nodeDebuggingPort.HasValue ? $" --debug-port={_nodeDebuggingPort.Value}" : string.Empty;
OutputLogger.LogWarning(string.Format(DebuggingStartedMessageFormat, evt.Data, debugPortArg));
}
else if (!initializationIsCompleted)
{ {
_connectionIsReadySource.SetException( _connectionIsReadySource.SetException(
new InvalidOperationException("The Node.js process failed to initialize: " + evt.Data)); new InvalidOperationException("The Node.js process failed to initialize: " + evt.Data));
@@ -218,6 +249,11 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
_nodeProcess.BeginErrorReadLine(); _nodeProcess.BeginErrorReadLine();
} }
private static bool IsDebuggerListeningMessage(string message)
{
return message.StartsWith("Debugger listening on port ", StringComparison.OrdinalIgnoreCase);
}
private FileSystemWatcher BeginFileWatcher(string rootDir) private FileSystemWatcher BeginFileWatcher(string rootDir)
{ {
if (_watchFileExtensions == null || _watchFileExtensions.Length == 0) if (_watchFileExtensions == null || _watchFileExtensions.Length == 0)

View File

@@ -37,14 +37,18 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private string _socketAddress; private string _socketAddress;
private VirtualConnectionClient _virtualConnectionClient; private VirtualConnectionClient _virtualConnectionClient;
public SocketNodeInstance(string projectPath, string[] watchFileExtensions, string socketAddress, ILogger nodeInstanceOutputLogger) : base( public SocketNodeInstance(string projectPath, string[] watchFileExtensions, string socketAddress,
ILogger nodeInstanceOutputLogger, bool launchWithDebugging, int? debuggingPort)
: base(
EmbeddedResourceReader.Read( EmbeddedResourceReader.Read(
typeof(SocketNodeInstance), typeof(SocketNodeInstance),
"/Content/Node/entrypoint-socket.js"), "/Content/Node/entrypoint-socket.js"),
projectPath, projectPath,
watchFileExtensions, watchFileExtensions,
MakeNewCommandLineOptions(socketAddress), MakeNewCommandLineOptions(socketAddress),
nodeInstanceOutputLogger) nodeInstanceOutputLogger,
launchWithDebugging,
debuggingPort)
{ {
_socketAddress = socketAddress; _socketAddress = socketAddress;
} }