From f2f67fe880dc78e7aa07c89f2dff0c76c7993cb7 Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Tue, 26 Jul 2016 18:33:27 +0100 Subject: [PATCH] Support new config options to launch the Node process with a debug listener. This is compatible with node-inspector. --- .../Configuration/Configuration.cs | 6 ++- .../Configuration/NodeServicesOptions.cs | 2 + .../HostingModels/HttpNodeInstance.cs | 9 ++-- .../HostingModels/OutOfProcessNodeInstance.cs | 48 ++++++++++++++++--- .../HostingModels/SocketNodeInstance.cs | 8 +++- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.AspNetCore.NodeServices/Configuration/Configuration.cs b/src/Microsoft.AspNetCore.NodeServices/Configuration/Configuration.cs index bbae3d2..6ecd43f 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Configuration/Configuration.cs +++ b/src/Microsoft.AspNetCore.NodeServices/Configuration/Configuration.cs @@ -68,10 +68,12 @@ namespace Microsoft.AspNetCore.NodeServices switch (options.HostingModel) { 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: 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: throw new ArgumentException("Unknown hosting model: " + options.HostingModel); } diff --git a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs index 98c50ec..5e6f518 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs +++ b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs @@ -21,5 +21,7 @@ namespace Microsoft.AspNetCore.NodeServices public string ProjectPath { get; set; } public string[] WatchFileExtensions { get; set; } public ILogger NodeInstanceOutputLogger { get; set; } + public bool LaunchWithDebugging { get; set; } + public int? DebuggingPort { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs index 53dabf9..0e828ea 100644 --- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs +++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs @@ -33,15 +33,18 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels private bool _disposed; private int _portNumber; - public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger, int port = 0) - : base( + public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger, + bool launchWithDebugging, int? debuggingPort, int port = 0) + : base( EmbeddedResourceReader.Read( typeof(HttpNodeInstance), "/Content/Node/entrypoint-http.js"), projectPath, watchFileExtensions, MakeCommandLineOptions(port), - nodeInstanceOutputLogger) + nodeInstanceOutputLogger, + launchWithDebugging, + debuggingPort) { _client = new HttpClient(); } diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs index 0664999..01a9055 100644 --- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs +++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs @@ -21,11 +21,22 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels { protected readonly ILogger OutputLogger; 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 _connectionIsReadySource = new TaskCompletionSource(); private bool _disposed; private readonly StringAsTempFile _entryPointScript; private FileSystemWatcher _fileSystemWatcher; private readonly Process _nodeProcess; + private int? _nodeDebuggingPort; private bool _nodeProcessNeedsRestart; private readonly string[] _watchFileExtensions; @@ -34,7 +45,9 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels string projectPath, string[] watchFileExtensions, string commandLineArguments, - ILogger nodeOutputLogger) + ILogger nodeOutputLogger, + bool launchWithDebugging, + int? debuggingPort) { if (nodeOutputLogger == null) { @@ -43,8 +56,9 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels OutputLogger = nodeOutputLogger; _entryPointScript = new StringAsTempFile(entryPointScript); - - var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments); + + var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments, + launchWithDebugging, debuggingPort); _nodeProcess = LaunchNodeProcess(startInfo); _watchFileExtensions = watchFileExtensions; _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 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") { - Arguments = "\"" + entryPointFilename + "\" " + (commandLineArguments ?? string.Empty), + Arguments = debuggingArgs + "\"" + entryPointFilename + "\" " + (commandLineArguments ?? string.Empty), UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, @@ -201,7 +227,12 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels { 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( new InvalidOperationException("The Node.js process failed to initialize: " + evt.Data)); @@ -218,6 +249,11 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels _nodeProcess.BeginErrorReadLine(); } + private static bool IsDebuggerListeningMessage(string message) + { + return message.StartsWith("Debugger listening on port ", StringComparison.OrdinalIgnoreCase); + } + private FileSystemWatcher BeginFileWatcher(string rootDir) { if (_watchFileExtensions == null || _watchFileExtensions.Length == 0) diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/SocketNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/SocketNodeInstance.cs index f5bbce1..5f3a6ec 100644 --- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/SocketNodeInstance.cs +++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/SocketNodeInstance.cs @@ -37,14 +37,18 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels private string _socketAddress; 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( typeof(SocketNodeInstance), "/Content/Node/entrypoint-socket.js"), projectPath, watchFileExtensions, MakeNewCommandLineOptions(socketAddress), - nodeInstanceOutputLogger) + nodeInstanceOutputLogger, + launchWithDebugging, + debuggingPort) { _socketAddress = socketAddress; }