Skip to content

Commit

Permalink
improvements & fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kolesnikovav committed Aug 1, 2020
1 parent e5ac250 commit c9dc6fe
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 34 deletions.
56 changes: 48 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# AspSpaService
![Nuget](https://img.shields.io/nuget/v/AspSpaService)
<img alt="Nuget" src="https://img.shields.io/nuget/dt/AspSpaService">

<a href="https://www.nuget.org/packages/AspSpaService">
<img alt="Nuget (with prereleases)" src="https://img.shields.io/nuget/vpre/AspSpaService">
</a>
<a href="https://www.nuget.org/packages/AspSpaService">
<img alt="Nuget" src="https://img.shields.io/nuget/dt/AspSpaService">
</a>

The Asp Net Core plugin for integrating SPA application with Asp Net Core.
This plugin can be used with any web framework in same manner.

# Usage
Install package via nugget
```
dotnet add package AspSpaService
```

Change your Startup.cs configuration file as follows:
```cs
using AspSpaService;
Expand All @@ -25,13 +35,43 @@ using AspSpaService;
var p = Path.Combine(wd,"samples", "hello-vue"); // path to your vuejs project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "serve", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer(
// command for nodejs process
// string
"yarn",
// argument for nodejs process
// string
"serve",
// working directory
// string
p,
// environment variables
new Dictionary<string,string>(),
// timeout for waiting node js process is ready to use
TimeSpan.FromSeconds(15),
// message when timeout has been exceeded
// has defaul value = "Timeout has been exceeded" (can be ommited!)
// string
"Timeout has been exceeded",
//logInformation for node js process
// bool (true by default)
true,
//logError for node js process
// some bundler emits many error strings during compilation
// bool (false by default)
false,
//unsubscribeWhenReady
// stop logging nodejs output when it ready to use
// bool (true by default)
true
);
}
);
}
}

```
This library starts NodeJS process, and waiting for it emits line with served valid Uri

## Sample configuratons
In folder sample/webapi has the web empty project that shows, how to use your favorite web framework with asp.
Expand All @@ -46,7 +86,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-vue"); // path to your vuejs project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "serve", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "serve", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), "Timeout has been exceeded");
}
);
}
Expand All @@ -62,7 +102,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-vite"); // path to your vitejs project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), "Timeout has been exceeded");
}
);
}
Expand All @@ -78,7 +118,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-nuxt"); // path to your nuxt project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), "Timeout has been exceeded");
}
);
}
Expand All @@ -94,7 +134,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-react"); // path to your react project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "start", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "start", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), "Timeout has been exceeded");
}
);
}
Expand All @@ -111,7 +151,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-svelte"); // path to your svelte project
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), "Timeout has been exceeded");
}
);
}
Expand Down
4 changes: 2 additions & 2 deletions samples/webapi/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
// var p = Path.Combine(wd,"samples", "hello-vue");
// app.UseSpa(
// spa => {
// spa.UseAspSpaDevelopmentServer("yarn", "serve", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
// spa.UseAspSpaDevelopmentServer("yarn", "serve", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(10), "Timeout has been exceeded");
// }
// );
// // this block starts vite spa application
Expand All @@ -47,7 +47,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
var p = Path.Combine(wd,"samples", "hello-nuxt");
app.UseSpa(
spa => {
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(15), null);
spa.UseAspSpaDevelopmentServer("yarn", "dev", p, new Dictionary<string,string>(), TimeSpan.FromSeconds(10), null);
}
);
// this block starts react spa application
Expand Down
4 changes: 3 additions & 1 deletion src/AspSpaService/AspSpaService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PackageId>AspSpaService</PackageId>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
<Authors>Aleksandr Kolesnikov</Authors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>AspSpaService</AssemblyName>
Expand All @@ -12,7 +12,9 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/kolesnikovav/AspSpaService.git</RepositoryUrl>
<Title>The Asp Net Core plugin for integrating SPA application with Asp Net Core project</Title>
<Description>The Asp Net Core plugin for integrating SPA application with Asp Net Core. This plugin can be used with any web framework in same manner.</Description>
<Keywords>ASP;Asp Net Core; SPA; vue; vite; nuxt; react; svelte</Keywords>
</PropertyGroup>

<ItemGroup>
Expand Down
17 changes: 16 additions & 1 deletion src/AspSpaService/AspSpaServiceMiddlewareExtentions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,20 @@ private static NodeRunner GetNodeRunner(IApplicationBuilder builder)
/// <param name="envVars">Environment variables for node dev server</param>
/// <param name="timeout">Timeout for node process waiting</param>
/// <param name="timeoutExceedMessage">Message when timeout is exceeded</param>
/// <param name="logInformation">Log node process output</param>
/// <param name="logError">Log node js process error</param>
/// <param name="unsubscribeWhenReady">Stop logging when nodejs process is ready</param>
public static void UseAspSpaDevelopmentServer(
this ISpaBuilder spaBuilder,
string command,
string arguments,
string workingDirectory,
Dictionary<string,string> envVars,
TimeSpan timeout,
string timeoutExceedMessage)
string timeoutExceedMessage = "Timeout has been exceeded ",
bool logInformation = true,
bool logError = false,
bool unsubscribeWhenReady = true)
{
if (spaBuilder == null)
{
Expand All @@ -64,11 +70,20 @@ public static void UseAspSpaDevelopmentServer(
runner.WorkingDirectory = workingDirectory;
runner.EnvVars = envVars;
runner.Timeout = timeout;
runner.TimeoutExceedMessage = timeoutExceedMessage;
runner.Launch(logger);
if (runner.Uri != null)
{
if (unsubscribeWhenReady)
{
runner.UnsubscribeLog(logger);
}
spaBuilder.UseProxyToSpaDevelopmentServer(runner.Uri);
}
else
{

}
}
private static ILogger GetOrCreateLogger(
IApplicationBuilder appBuilder,
Expand Down
82 changes: 60 additions & 22 deletions src/AspSpaService/NodeRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@ public class NodeRunner: IDisposable
/// Environment variables for node process
/// </summary>
public Dictionary<string,string> EnvVars {get;set;} = new Dictionary<string, string>();
/// <summary>
/// Indicates where node process is being served
/// </summary>
/// <summary>
/// Log Node JS Process messages
/// </summary>
public bool LogResult { get; set; } = true;
/// <summary>
/// Log Node JS Process error messages
/// </summary>
public bool LogError { get; set; } = false;
/// <summary>
/// Indicates where node process is being served
/// </summary>
public Uri Uri {
get => this._uri;
}
Expand Down Expand Up @@ -91,43 +99,73 @@ public void Launch(ILogger logger)
this._nodeProcess = Process.Start(p);
this.streamOutputReader = new NodeStreamReader(this._nodeProcess.StandardOutput);
this.streamErrorReader = new NodeStreamReader(this._nodeProcess.StandardError);
if (logger != null)
this.streamOutputReader.OnReceivedLine += this.onResiveLineResult;
if (LogError)
{
this.AttachToLogger(logger);
this.streamErrorReader.OnReceivedLine += this.onResiveLineResult;
}
this.streamOutputReader.OnReceivedLine += this.onResiveLineResult;
this.streamErrorReader.OnReceivedLine += this.onResiveLineResult;
this._nodeProcess.Exited += (a, b) =>
{
if (logger != null)
{
logger.LogError("Node JS Process has been exited with code " + this._nodeProcess.ExitCode.ToString());
}
};
var cStart = DateTime.Now;
this._awaiter.WaitOne(this.Timeout);
var cExit = DateTime.Now;
bool timeoutHasBeenExceeded = (this.Timeout == null) ? false : TimeSpan.Compare(cExit - cStart, this.Timeout) > 0;
if (this.Uri == null)
{
//unsubscribe events
this.streamOutputReader.OnReceivedLine -= this.onResiveLineResult;
if (LogError)
{
this.streamOutputReader.OnReceivedLine -= this.onResiveLineResult;
}
if (this._nodeProcess != null)
{
logger.LogError("Disposing Node JS Process");
if (this._nodeProcess != null && !this._nodeProcess.HasExited)
{
this._nodeProcess.Kill(true);
this._nodeProcess = null;
}
this._uri = null;
logger.LogError("Disposing Node JS Process has been disposed");
}
if (logger != null)
{
if (timeoutHasBeenExceeded)
{
logger.LogError(this.TimeoutExceedMessage + this.Timeout.ToString());
}
}
}
}
catch(Exception ex)
{
var message = $"Failed to start '{Command} {Arguments}'";
throw new InvalidOperationException(message, ex);
}
}
/// <summary>
/// Attach to logger.
/// </summary>
/// <param name="logger">The logger to attach </param>
private void AttachToLogger(ILogger logger)
/// <summary>
/// Stop logging node process output
/// </summary>
public void UnsubscribeLog(ILogger logger)
{
// When the node task emits complete lines, pass them through to the real logger
this.streamOutputReader.OnReceivedLine += line =>
{
logger.LogInformation(line);
};

this.streamErrorReader.OnReceivedLine += line =>
this.streamOutputReader.OnReceivedLine += this.onResiveLineResult;
if (LogError)
{
logger.LogError(line);
};
this.streamErrorReader.OnReceivedLine += this.onResiveLineResult;
}
}
/// <summary>
/// Stop node process and disposes resources
/// </summary>
public void Dispose()
{
if (_nodeProcess != null && !_nodeProcess.HasExited)
if (this._nodeProcess != null && !this._nodeProcess.HasExited)
{
this._nodeProcess.Kill(true);
this._nodeProcess = null;
Expand Down

0 comments on commit c9dc6fe

Please sign in to comment.