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
// to populate options.ProjectPath if it wasn't explicitly specified.
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
if (string.IsNullOrEmpty(options.ProjectPath))
{
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
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.
// If it doesn't provide one, CreateNodeInstance will set up a default.
if (options.NodeInstanceOutputLogger == null)
@@ -69,11 +72,11 @@ namespace Microsoft.AspNetCore.NodeServices
{
case NodeHostingModel.Http:
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:
var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string
return new SocketNodeInstance(options.ProjectPath, options.WatchFileExtensions, pipeName, logger,
options.LaunchWithDebugging, options.DebuggingPort);
options.EnvironmentVariables, options.LaunchWithDebugging, options.DebuggingPort);
default:
throw new ArgumentException("Unknown hosting model: " + options.HostingModel);
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.Logging;
@@ -22,6 +23,23 @@ namespace Microsoft.AspNetCore.NodeServices
public string[] WatchFileExtensions { get; set; }
public ILogger NodeInstanceOutputLogger { get; set; }
public bool LaunchWithDebugging { get; set; }
public IDictionary<string, string> EnvironmentVariables { 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.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
@@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private int _portNumber;
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(
EmbeddedResourceReader.Read(
typeof(HttpNodeInstance),
@@ -43,6 +44,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
watchFileExtensions,
MakeCommandLineOptions(port),
nodeInstanceOutputLogger,
environmentVars,
launchWithDebugging,
debuggingPort)
{

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -46,6 +47,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
string[] watchFileExtensions,
string commandLineArguments,
ILogger nodeOutputLogger,
IDictionary<string, string> environmentVars,
bool launchWithDebugging,
int? debuggingPort)
{
@@ -58,7 +60,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
_entryPointScript = new StringAsTempFile(entryPointScript);
var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments,
launchWithDebugging, debuggingPort);
environmentVars, launchWithDebugging, debuggingPort);
_nodeProcess = LaunchNodeProcess(startInfo);
_watchFileExtensions = watchFileExtensions;
_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
protected virtual ProcessStartInfo PrepareNodeProcessStartInfo(
string entryPointFilename, string projectPath, string commandLineArguments,
bool launchWithDebugging, int? debuggingPort)
IDictionary<string, string> environmentVars, bool launchWithDebugging, int? debuggingPort)
{
string debuggingArgs;
if (launchWithDebugging)
@@ -122,6 +124,19 @@ If you haven't yet installed node-inspector, you can do so as follows:
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
var existingNodePath = Environment.GetEnvironmentVariable("NODE_PATH") ?? 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");
#if NET451
startInfo.EnvironmentVariables["NODE_PATH"] = nodePathValue;
#else
startInfo.Environment["NODE_PATH"] = nodePathValue;
#endif
SetEnvironmentVariable(startInfo, "NODE_PATH", nodePathValue);
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)
{
var process = Process.Start(startInfo);

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
@@ -38,7 +39,8 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private VirtualConnectionClient _virtualConnectionClient;
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(
EmbeddedResourceReader.Read(
typeof(SocketNodeInstance),
@@ -47,6 +49,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
watchFileExtensions,
MakeNewCommandLineOptions(socketAddress),
nodeInstanceOutputLogger,
environmentVars,
launchWithDebugging,
debuggingPort)
{

View File

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

View File

@@ -35,16 +35,8 @@ namespace Microsoft.AspNetCore.Builder
"To enable ReactHotModuleReplacement, you must also enable HotModuleReplacement.");
}
string projectPath;
if (options.ProjectPath == null)
{
var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
projectPath = hostEnv.ContentRootPath;
}
else
{
projectPath = options.ProjectPath;
}
var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
var projectPath = options.ProjectPath ?? hostEnv.ContentRootPath;
// 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
@@ -55,7 +47,7 @@ namespace Microsoft.AspNetCore.Builder
{
ProjectPath = projectPath,
WatchFileExtensions = new string[] { } // Don't watch anything
});
}.AddDefaultEnvironmentVariables(hostEnv.IsDevelopment()));
// Get a filename matching the middleware Node script
var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware),