Add ability to configure environment variables for Node instances, plus auto-populate NODE_ENV based on IHostingEnvironment when possible. Fixes #230

This commit is contained in:
SteveSandersonMS
2016-08-16 16:26:07 -07:00
parent 56cb898bde
commit 098159998d
7 changed files with 62 additions and 24 deletions

View File

@@ -20,12 +20,15 @@ namespace Microsoft.AspNetCore.NodeServices
{ {
// Since this instance is being created through DI, we can access the IHostingEnvironment // Since this instance is being created through DI, we can access the IHostingEnvironment
// to populate options.ProjectPath if it wasn't explicitly specified. // to populate options.ProjectPath if it wasn't explicitly specified.
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
if (string.IsNullOrEmpty(options.ProjectPath)) if (string.IsNullOrEmpty(options.ProjectPath))
{ {
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
options.ProjectPath = hostEnv.ContentRootPath; options.ProjectPath = hostEnv.ContentRootPath;
} }
// Similarly, we can determine the 'is development' value from the hosting environment
options.AddDefaultEnvironmentVariables(hostEnv.IsDevelopment());
// Likewise, if no logger was specified explicitly, we should use the one from DI. // Likewise, if no logger was specified explicitly, we should use the one from DI.
// If it doesn't provide one, CreateNodeInstance will set up a default. // If it doesn't provide one, CreateNodeInstance will set up a default.
if (options.NodeInstanceOutputLogger == null) if (options.NodeInstanceOutputLogger == null)
@@ -69,11 +72,11 @@ namespace Microsoft.AspNetCore.NodeServices
{ {
case NodeHostingModel.Http: case NodeHostingModel.Http:
return new HttpNodeInstance(options.ProjectPath, options.WatchFileExtensions, logger, return new HttpNodeInstance(options.ProjectPath, options.WatchFileExtensions, logger,
options.LaunchWithDebugging, options.DebuggingPort, /* port */ 0); options.EnvironmentVariables, 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); options.EnvironmentVariables, options.LaunchWithDebugging, options.DebuggingPort);
default: default:
throw new ArgumentException("Unknown hosting model: " + options.HostingModel); throw new ArgumentException("Unknown hosting model: " + options.HostingModel);
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.NodeServices.HostingModels; using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -22,6 +23,23 @@ namespace Microsoft.AspNetCore.NodeServices
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 bool LaunchWithDebugging { get; set; }
public IDictionary<string, string> EnvironmentVariables { get; set; }
public int? DebuggingPort { get; set; } public int? DebuggingPort { get; set; }
public NodeServicesOptions AddDefaultEnvironmentVariables(bool isDevelopmentMode)
{
if (EnvironmentVariables == null)
{
EnvironmentVariables = new Dictionary<string, string>();
}
if (!EnvironmentVariables.ContainsKey("NODE_ENV"))
{
// These strings are a de-facto standard in Node
EnvironmentVariables["NODE_ENV"] = isDevelopmentMode ? "development" : "production";
}
return this;
}
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
@@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private int _portNumber; private int _portNumber;
public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger, public HttpNodeInstance(string projectPath, string[] watchFileExtensions, ILogger nodeInstanceOutputLogger,
bool launchWithDebugging, int? debuggingPort, int port = 0) IDictionary<string, string> environmentVars, bool launchWithDebugging, int? debuggingPort, int port = 0)
: base( : base(
EmbeddedResourceReader.Read( EmbeddedResourceReader.Read(
typeof(HttpNodeInstance), typeof(HttpNodeInstance),
@@ -43,6 +44,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
watchFileExtensions, watchFileExtensions,
MakeCommandLineOptions(port), MakeCommandLineOptions(port),
nodeInstanceOutputLogger, nodeInstanceOutputLogger,
environmentVars,
launchWithDebugging, launchWithDebugging,
debuggingPort) debuggingPort)
{ {

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -46,6 +47,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
string[] watchFileExtensions, string[] watchFileExtensions,
string commandLineArguments, string commandLineArguments,
ILogger nodeOutputLogger, ILogger nodeOutputLogger,
IDictionary<string, string> environmentVars,
bool launchWithDebugging, bool launchWithDebugging,
int? debuggingPort) int? debuggingPort)
{ {
@@ -58,7 +60,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
_entryPointScript = new StringAsTempFile(entryPointScript); _entryPointScript = new StringAsTempFile(entryPointScript);
var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments, var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments,
launchWithDebugging, debuggingPort); environmentVars, launchWithDebugging, debuggingPort);
_nodeProcess = LaunchNodeProcess(startInfo); _nodeProcess = LaunchNodeProcess(startInfo);
_watchFileExtensions = watchFileExtensions; _watchFileExtensions = watchFileExtensions;
_fileSystemWatcher = BeginFileWatcher(projectPath); _fileSystemWatcher = BeginFileWatcher(projectPath);
@@ -99,7 +101,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
// 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) IDictionary<string, string> environmentVars, bool launchWithDebugging, int? debuggingPort)
{ {
string debuggingArgs; string debuggingArgs;
if (launchWithDebugging) if (launchWithDebugging)
@@ -122,6 +124,19 @@ If you haven't yet installed node-inspector, you can do so as follows:
WorkingDirectory = projectPath WorkingDirectory = projectPath
}; };
// Append environment vars
if (environmentVars != null)
{
foreach (var envVarKey in environmentVars.Keys)
{
var envVarValue = environmentVars[envVarKey];
if (envVarValue != null)
{
SetEnvironmentVariable(startInfo, envVarKey, envVarValue);
}
}
}
// Append projectPath to NODE_PATH so it can locate node_modules // Append projectPath to NODE_PATH so it can locate node_modules
var existingNodePath = Environment.GetEnvironmentVariable("NODE_PATH") ?? string.Empty; var existingNodePath = Environment.GetEnvironmentVariable("NODE_PATH") ?? string.Empty;
if (existingNodePath != string.Empty) if (existingNodePath != string.Empty)
@@ -130,11 +145,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
} }
var nodePathValue = existingNodePath + Path.Combine(projectPath, "node_modules"); var nodePathValue = existingNodePath + Path.Combine(projectPath, "node_modules");
#if NET451 SetEnvironmentVariable(startInfo, "NODE_PATH", nodePathValue);
startInfo.EnvironmentVariables["NODE_PATH"] = nodePathValue;
#else
startInfo.Environment["NODE_PATH"] = nodePathValue;
#endif
return startInfo; return startInfo;
} }
@@ -179,6 +190,15 @@ If you haven't yet installed node-inspector, you can do so as follows:
} }
} }
private static void SetEnvironmentVariable(ProcessStartInfo startInfo, string name, string value)
{
#if NET451
startInfo.EnvironmentVariables[name] = value;
#else
startInfo.Environment[name] = value;
#endif
}
private static Process LaunchNodeProcess(ProcessStartInfo startInfo) private static Process LaunchNodeProcess(ProcessStartInfo startInfo)
{ {
var process = Process.Start(startInfo); var process = Process.Start(startInfo);

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -38,7 +39,8 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private VirtualConnectionClient _virtualConnectionClient; private VirtualConnectionClient _virtualConnectionClient;
public SocketNodeInstance(string projectPath, string[] watchFileExtensions, string socketAddress, public SocketNodeInstance(string projectPath, string[] watchFileExtensions, string socketAddress,
ILogger nodeInstanceOutputLogger, bool launchWithDebugging, int? debuggingPort) ILogger nodeInstanceOutputLogger, IDictionary<string, string> environmentVars,
bool launchWithDebugging, int? debuggingPort)
: base( : base(
EmbeddedResourceReader.Read( EmbeddedResourceReader.Read(
typeof(SocketNodeInstance), typeof(SocketNodeInstance),
@@ -47,6 +49,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
watchFileExtensions, watchFileExtensions,
MakeNewCommandLineOptions(socketAddress), MakeNewCommandLineOptions(socketAddress),
nodeInstanceOutputLogger, nodeInstanceOutputLogger,
environmentVars,
launchWithDebugging, launchWithDebugging,
debuggingPort) debuggingPort)
{ {

View File

@@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
_nodeServices = _fallbackNodeServices = Configuration.CreateNodeServices(new NodeServicesOptions _nodeServices = _fallbackNodeServices = Configuration.CreateNodeServices(new NodeServicesOptions
{ {
ProjectPath = _applicationBasePath ProjectPath = _applicationBasePath
}); }.AddDefaultEnvironmentVariables(hostEnv.IsDevelopment()));
} }
} }

View File

@@ -35,16 +35,8 @@ namespace Microsoft.AspNetCore.Builder
"To enable ReactHotModuleReplacement, you must also enable HotModuleReplacement."); "To enable ReactHotModuleReplacement, you must also enable HotModuleReplacement.");
} }
string projectPath; var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
if (options.ProjectPath == null) var projectPath = options.ProjectPath ?? hostEnv.ContentRootPath;
{
var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
projectPath = hostEnv.ContentRootPath;
}
else
{
projectPath = options.ProjectPath;
}
// Unlike other consumers of NodeServices, WebpackDevMiddleware dosen't share Node instances, nor does it // Unlike other consumers of NodeServices, WebpackDevMiddleware dosen't share Node instances, nor does it
// use your DI configuration. It's important for WebpackDevMiddleware to have its own private Node instance // use your DI configuration. It's important for WebpackDevMiddleware to have its own private Node instance
@@ -55,7 +47,7 @@ namespace Microsoft.AspNetCore.Builder
{ {
ProjectPath = projectPath, ProjectPath = projectPath,
WatchFileExtensions = new string[] { } // Don't watch anything WatchFileExtensions = new string[] { } // Don't watch anything
}); }.AddDefaultEnvironmentVariables(hostEnv.IsDevelopment()));
// Get a filename matching the middleware Node script // Get a filename matching the middleware Node script
var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware), var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware),