diff --git a/build/common.props b/build/common.props
index 89ade5d..72ab892 100644
--- a/build/common.props
+++ b/build/common.props
@@ -16,7 +16,8 @@
-
+
+
diff --git a/build/dependencies.props b/build/dependencies.props
index 4370015..639387b 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -2,10 +2,10 @@
2.0.0-*
5.0.2
- 4.3.0
2.1.0-*
10.0.1
$(BundledNETStandardPackageVersion)
+ 2.0.0-*
2.0.0-*
4.7.0
diff --git a/build/repo.targets b/build/repo.targets
deleted file mode 100644
index 537aa36..0000000
--- a/build/repo.targets
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/misc/LatencyTest/LatencyTest.csproj b/samples/misc/LatencyTest/LatencyTest.csproj
index 8c17cdb..43d5f17 100644
--- a/samples/misc/LatencyTest/LatencyTest.csproj
+++ b/samples/misc/LatencyTest/LatencyTest.csproj
@@ -1,9 +1,9 @@
-
+
- netcoreapp2.0
+ netcoreapp2.0;net461
false
exe
diff --git a/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj b/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj
index 0604193..4bbb9ec 100644
--- a/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj
+++ b/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj
@@ -1,9 +1,9 @@
-
+
- netcoreapp2.0
+ netcoreapp2.0;net461
true
false
diff --git a/samples/misc/Webpack/ActionResults/PrerenderResult.cs b/samples/misc/Webpack/ActionResults/PrerenderResult.cs
index e80b5b1..4f2c8f5 100644
--- a/samples/misc/Webpack/ActionResults/PrerenderResult.cs
+++ b/samples/misc/Webpack/ActionResults/PrerenderResult.cs
@@ -27,6 +27,7 @@ namespace Webpack.ActionResults
{
var nodeServices = context.HttpContext.RequestServices.GetRequiredService();
var hostEnv = context.HttpContext.RequestServices.GetRequiredService();
+ var applicationLifetime = context.HttpContext.RequestServices.GetRequiredService();
var applicationBasePath = hostEnv.ContentRootPath;
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;
@@ -34,6 +35,7 @@ namespace Webpack.ActionResults
var prerenderedHtml = await Prerenderer.RenderToString(
applicationBasePath,
nodeServices,
+ applicationLifetime.ApplicationStopping,
_moduleExport,
request.GetEncodedUrl(),
request.Path + request.QueryString.Value,
diff --git a/samples/misc/Webpack/Webpack.csproj b/samples/misc/Webpack/Webpack.csproj
index 9cabae2..eedacab 100644
--- a/samples/misc/Webpack/Webpack.csproj
+++ b/samples/misc/Webpack/Webpack.csproj
@@ -1,9 +1,9 @@
-
+
- netcoreapp2.0
+ netcoreapp2.0;net461
true
false
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj b/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj
index 8ebd6a6..91b1a4b 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj
@@ -4,7 +4,7 @@
Socket-based RPC for Microsoft.AspNetCore.NodeServices.
- netcoreapp2.0
+ netstandard2.0
aspnetcore;aspnetcoremvc;nodeservices
true
@@ -15,14 +15,6 @@
-
-
-
-
-
-
-
-
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs
index d50d146..5fcd667 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs
@@ -18,11 +18,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
PipeDirection.InOut,
PipeOptions.Asynchronous);
-#if NET451
- _namedPipeClientStream.Connect();
-#else
await _namedPipeClientStream.ConnectAsync().ConfigureAwait(false);
-#endif
return _namedPipeClientStream;
}
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs
index 535a762..cbd1f99 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs
@@ -11,9 +11,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
public static StreamConnection Create()
{
-#if NET451
- return new NamedPipeConnection();
-#else
var useNamedPipes = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.Windows);
if (useNamedPipes)
@@ -24,7 +21,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{
return new UnixDomainSocketConnection();
}
-#endif
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs
index 1f12a71..6e7ebac 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs
@@ -10,16 +10,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
private NetworkStream _networkStream;
private Socket _socket;
-#if NET451
- public override Task Open(string address)
- {
- // The 'null' assignments avoid the compiler warnings about unassigned fields.
- // Note that this whole class isn't supported on .NET 4.5.1, since that's not cross-platform.
- _networkStream = null;
- _socket = null;
- throw new System.PlatformNotSupportedException();
- }
-#else
public override async Task Open(string address)
{
var endPoint = new UnixDomainSocketEndPoint("/tmp/" + address);
@@ -28,7 +18,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
_networkStream = new NetworkStream(_socket);
return _networkStream;
}
-#endif
public override void Dispose()
{
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs
index ff43b01..b001163 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs
@@ -59,11 +59,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
}
else
{
-#if NET451
- _encodedPath = new byte[0];
-#else
_encodedPath = Array.Empty();
-#endif
_path = string.Empty;
}
}
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs
index b656978..1e6afd1 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs
@@ -49,6 +49,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets
options.ProjectPath,
options.WatchFileExtensions,
MakeNewCommandLineOptions(socketAddress),
+ options.ApplicationStoppingToken,
options.NodeInstanceOutputLogger,
options.EnvironmentVariables,
options.InvocationTimeoutMilliseconds,
diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs b/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs
index 611dff1..391b1f7 100644
--- a/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs
+++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs
@@ -12,11 +12,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections
///
internal class VirtualConnection : Stream
{
-#if NET451
- private readonly static Task CompletedTask = Task.FromResult((object)null);
-#else
private readonly static Task CompletedTask = Task.CompletedTask;
-#endif
private VirtualConnectionClient _host;
private readonly BufferBlock _receivedDataQueue = new BufferBlock();
private ArraySegment _receivedDataNotYetUsed;
diff --git a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs
index 43d703b..73dd228 100644
--- a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs
+++ b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
@@ -42,6 +43,12 @@ namespace Microsoft.AspNetCore.NodeServices
EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node
}
+ var applicationLifetime = serviceProvider.GetService();
+ if (applicationLifetime != null)
+ {
+ ApplicationStoppingToken = applicationLifetime.ApplicationStopping;
+ }
+
// If the DI system gives us a logger, use it. Otherwise, set up a default one.
var loggerFactory = serviceProvider.GetService();
NodeInstanceOutputLogger = loggerFactory != null
@@ -93,5 +100,10 @@ namespace Microsoft.AspNetCore.NodeServices
/// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return.
///
public int InvocationTimeoutMilliseconds { get; set; }
+
+ ///
+ /// A token that indicates when the host application is stopping.
+ ///
+ public CancellationToken ApplicationStoppingToken { 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 8beea51..b1b0fe9 100644
--- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs
+++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs
@@ -42,6 +42,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
options.ProjectPath,
options.WatchFileExtensions,
MakeCommandLineOptions(port),
+ options.ApplicationStoppingToken,
options.NodeInstanceOutputLogger,
options.EnvironmentVariables,
options.InvocationTimeoutMilliseconds,
diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs
index 7f6f172..e353292 100644
--- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs
+++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs
@@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// The root path of the current project. This is used when resolving Node.js module paths relative to the project root.
/// The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes.
/// Additional command-line arguments to be passed to the Node.js instance.
+ /// A token that indicates when the host application is stopping.
/// The to which the Node.js instance's stdout/stderr (and other log information) should be written.
/// Environment variables to be set on the Node.js process.
/// The maximum duration, in milliseconds, to wait for RPC calls to complete.
@@ -55,6 +56,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
string projectPath,
string[] watchFileExtensions,
string commandLineArguments,
+ CancellationToken applicationStoppingToken,
ILogger nodeOutputLogger,
IDictionary environmentVars,
int invocationTimeoutMilliseconds,
@@ -67,7 +69,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
}
OutputLogger = nodeOutputLogger;
- _entryPointScript = new StringAsTempFile(entryPointScript);
+ _entryPointScript = new StringAsTempFile(entryPointScript, applicationStoppingToken);
_invocationTimeoutMilliseconds = invocationTimeoutMilliseconds;
_launchWithDebugging = launchWithDebugging;
@@ -315,11 +317,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
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)
diff --git a/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj b/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj
index f5be3ac..cbf7828 100644
--- a/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj
+++ b/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj
@@ -4,7 +4,7 @@
Invoke Node.js modules at runtime in ASP.NET Core applications.
- netcoreapp2.0
+ netstandard2.0
aspnetcore;aspnetcoremvc;nodeservices
true
true
@@ -20,12 +20,6 @@
-
-
-
-
-
-
diff --git a/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs b/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs
index 22f4b01..c15bca3 100644
--- a/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs
+++ b/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Threading;
namespace Microsoft.AspNetCore.NodeServices
{
@@ -11,27 +12,21 @@ namespace Microsoft.AspNetCore.NodeServices
private bool _disposedValue;
private bool _hasDeletedTempFile;
private object _fileDeletionLock = new object();
+ private IDisposable _applicationLifetimeRegistration;
///
/// Create a new instance of .
///
/// The contents of the temporary file to be created.
- public StringAsTempFile(string content)
+ /// A token that indicates when the host application is stopping.
+ public StringAsTempFile(string content, CancellationToken applicationStoppingToken)
{
FileName = Path.GetTempFileName();
File.WriteAllText(FileName, content);
// Because .NET finalizers don't reliably run when the process is terminating, also
// add event handlers for other shutdown scenarios.
-#if NET451
- AppDomain.CurrentDomain.ProcessExit += HandleProcessExit;
- AppDomain.CurrentDomain.DomainUnload += HandleProcessExit;
-#else
- // Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't
- // appear to be a way of doing that. So in that case, the temporary file will be
- // left behind.
- System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading;
-#endif
+ _applicationLifetimeRegistration = applicationStoppingToken.Register(EnsureTempFileDeleted);
}
///
@@ -55,12 +50,7 @@ namespace Microsoft.AspNetCore.NodeServices
if (disposing)
{
// Dispose managed state
-#if NET451
- AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit;
- AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit;
-#else
- System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading;
-#endif
+ _applicationLifetimeRegistration.Dispose();
}
EnsureTempFileDeleted();
@@ -81,18 +71,6 @@ namespace Microsoft.AspNetCore.NodeServices
}
}
-#if NET451
- private void HandleProcessExit(object sender, EventArgs args)
- {
- EnsureTempFileDeleted();
- }
-#else
- private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context)
- {
- EnsureTempFileDeleted();
- }
-#endif
-
///
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
///
diff --git a/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj b/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj
index 53d7c7b..5d90573 100644
--- a/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj
+++ b/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj
@@ -4,7 +4,7 @@
Helpers for building single-page applications on ASP.NET MVC Core.
- netcoreapp2.0
+ netstandard2.0
aspnetcore;aspnetcoremvc;nodeservices
true
true
@@ -13,7 +13,8 @@
-
+
+
diff --git a/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs b/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs
index bad274f..c2c5308 100644
--- a/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs
+++ b/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
@@ -24,6 +25,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
private readonly string _applicationBasePath;
+ private readonly CancellationToken _applicationStoppingToken;
private readonly INodeServices _nodeServices;
///
@@ -35,6 +37,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
var hostEnv = (IHostingEnvironment) serviceProvider.GetService(typeof(IHostingEnvironment));
_nodeServices = (INodeServices) serviceProvider.GetService(typeof(INodeServices)) ?? _fallbackNodeServices;
_applicationBasePath = hostEnv.ContentRootPath;
+
+ var applicationLifetime = (IApplicationLifetime) serviceProvider.GetService(typeof(IApplicationLifetime));
+ _applicationStoppingToken = applicationLifetime.ApplicationStopping;
// Consider removing the following. Having it means you can get away with not putting app.AddNodeServices()
// in your startup file, but then again it might be confusing that you don't need to.
@@ -101,6 +106,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
var result = await Prerenderer.RenderToString(
_applicationBasePath,
_nodeServices,
+ _applicationStoppingToken,
new JavaScriptModuleExport(ModuleName)
{
ExportName = ExportName
diff --git a/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs b/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs
index 43c2ed0..e7ce841 100644
--- a/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs
+++ b/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices;
@@ -9,22 +10,16 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
///
public static class Prerenderer
{
- private static readonly Lazy NodeScript;
+ private static readonly object CreateNodeScriptLock = new object();
- static Prerenderer()
- {
- NodeScript = new Lazy(() =>
- {
- var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
- return new StringAsTempFile(script); // Will be cleaned up on process exit
- });
- }
+ private static StringAsTempFile NodeScript;
///
/// Performs server-side prerendering by invoking code in Node.js.
///
/// The root path to your application. This is used when resolving project-relative paths.
/// The instance of that will be used to invoke JavaScript code.
+ /// A token that indicates when the host application is stopping.
/// The path to the JavaScript file containing the prerendering logic.
/// The URL of the currently-executing HTTP request. This is supplied to the prerendering code.
/// The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code.
@@ -35,6 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
public static Task RenderToString(
string applicationBasePath,
INodeServices nodeServices,
+ CancellationToken applicationStoppingToken,
JavaScriptModuleExport bootModule,
string requestAbsoluteUrl,
string requestPathAndQuery,
@@ -43,7 +39,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
string requestPathBase)
{
return nodeServices.InvokeExportAsync(
- NodeScript.Value.FileName,
+ GetNodeScriptFilename(applicationStoppingToken),
"renderToString",
applicationBasePath,
bootModule,
@@ -53,5 +49,19 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
timeoutMilliseconds,
requestPathBase);
}
+
+ private static string GetNodeScriptFilename(CancellationToken applicationStoppingToken)
+ {
+ lock(CreateNodeScriptLock)
+ {
+ if (NodeScript == null)
+ {
+ var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
+ NodeScript = new StringAsTempFile(script, applicationStoppingToken); // Will be cleaned up on process exit
+ }
+ }
+
+ return NodeScript.FileName;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs b/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs
index 8afd7b6..9f02223 100644
--- a/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs
+++ b/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs
@@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Builder
// Get a filename matching the middleware Node script
var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware),
"/Content/Node/webpack-dev-middleware.js");
- var nodeScript = new StringAsTempFile(script); // Will be cleaned up on process exit
+ var nodeScript = new StringAsTempFile(script, nodeServicesOptions.ApplicationStoppingToken); // Will be cleaned up on process exit
// Ideally, this would be relative to the application's PathBase (so it could work in virtual directories)
// but it's not clear that such information exists during application startup, as opposed to within the context
diff --git a/templates/AngularSpa/AngularSpa.csproj b/templates/AngularSpa/AngularSpa.csproj
index 96f705d..7ff4100 100644
--- a/templates/AngularSpa/AngularSpa.csproj
+++ b/templates/AngularSpa/AngularSpa.csproj
@@ -9,7 +9,6 @@
-
diff --git a/templates/AureliaSpa/AureliaSpa.csproj b/templates/AureliaSpa/AureliaSpa.csproj
index 4fd7951..ff88f56 100644
--- a/templates/AureliaSpa/AureliaSpa.csproj
+++ b/templates/AureliaSpa/AureliaSpa.csproj
@@ -9,7 +9,6 @@
-
diff --git a/templates/KnockoutSpa/KnockoutSpa.csproj b/templates/KnockoutSpa/KnockoutSpa.csproj
index 4fd7951..ff88f56 100644
--- a/templates/KnockoutSpa/KnockoutSpa.csproj
+++ b/templates/KnockoutSpa/KnockoutSpa.csproj
@@ -9,7 +9,6 @@
-
diff --git a/templates/ReactReduxSpa/ReactReduxSpa.csproj b/templates/ReactReduxSpa/ReactReduxSpa.csproj
index 96f705d..7ff4100 100644
--- a/templates/ReactReduxSpa/ReactReduxSpa.csproj
+++ b/templates/ReactReduxSpa/ReactReduxSpa.csproj
@@ -9,7 +9,6 @@
-
diff --git a/templates/ReactSpa/ReactSpa.csproj b/templates/ReactSpa/ReactSpa.csproj
index 4fd7951..ff88f56 100644
--- a/templates/ReactSpa/ReactSpa.csproj
+++ b/templates/ReactSpa/ReactSpa.csproj
@@ -9,7 +9,6 @@
-
diff --git a/templates/VueSpa/VueSpa.csproj b/templates/VueSpa/VueSpa.csproj
index 4fd7951..ff88f56 100644
--- a/templates/VueSpa/VueSpa.csproj
+++ b/templates/VueSpa/VueSpa.csproj
@@ -9,7 +9,6 @@
-