Renaming some SPA Middleware variables to be package manager agnostic

This commit is contained in:
Justin Robb 2019-07-31 13:15:07 -07:00
parent 751882cf3c
commit 7c7dd6569f
5 changed files with 43 additions and 44 deletions

View File

@ -21,20 +21,20 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
{ {
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
private readonly string _npmScriptName; private readonly string _scriptName;
/// <summary> /// <summary>
/// Constructs an instance of <see cref="AngularCliBuilder"/>. /// Constructs an instance of <see cref="AngularCliBuilder"/>.
/// </summary> /// </summary>
/// <param name="npmScript">The name of the script in your package.json file that builds the server-side bundle for your Angular application.</param> /// <param name="scriptName">The name of the script in your package.json file that builds the server-side bundle for your Angular application.</param>
public AngularCliBuilder(string npmScript) public AngularCliBuilder(string scriptName)
{ {
if (string.IsNullOrEmpty(npmScript)) if (string.IsNullOrEmpty(scriptName))
{ {
throw new ArgumentException("Cannot be null or empty.", nameof(npmScript)); throw new ArgumentException("Cannot be null or empty.", nameof(scriptName));
} }
_npmScriptName = npmScript; _scriptName = scriptName;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -47,37 +47,36 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
} }
var logger = LoggerFinder.GetOrCreateLogger( var logger = LoggerFinder.GetOrCreateLogger(
spaBuilder.ApplicationBuilder, spaBuilder.ApplicationBuilder,
nameof(AngularCliBuilder)); nameof(AngularCliBuilder));
var npmScriptRunner = new NodeScriptRunner( var scriptRunner = new NodeScriptRunner(
sourcePath, sourcePath,
_npmScriptName, _scriptName,
"--watch", "--watch",
null, null,
pkgManagerName); pkgManagerName);
npmScriptRunner.AttachToLogger(logger); scriptRunner.AttachToLogger(logger);
using (var stdOutReader = new EventedStreamStringReader(npmScriptRunner.StdOut)) using (var stdOutReader = new EventedStreamStringReader(scriptRunner.StdOut))
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{ {
try try
{ {
await npmScriptRunner.StdOut.WaitForMatch( await scriptRunner.StdOut.WaitForMatch(
new Regex("Date", RegexOptions.None, RegexMatchTimeout)); new Regex("Date", RegexOptions.None, RegexMatchTimeout));
} }
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"The NPM script '{_npmScriptName}' exited without indicating success.\n" + $"The {pkgManagerName} script '{_scriptName}' exited without indicating success.\n" +
$"Output was: {stdOutReader.ReadAsString()}\n" + $"Output was: {stdOutReader.ReadAsString()}\n" +
$"Error output was: {stdErrReader.ReadAsString()}", ex); $"Error output was: {stdErrReader.ReadAsString()}", ex);
} }
catch (OperationCanceledException ex) catch (OperationCanceledException ex)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"The NPM script '{_npmScriptName}' timed out without indicating success. " + $"The {pkgManagerName} script '{_scriptName}' timed out without indicating success. " +
$"Output was: {stdOutReader.ReadAsString()}\n" + $"Output was: {stdOutReader.ReadAsString()}\n" +
$"Error output was: {stdErrReader.ReadAsString()}", ex); $"Error output was: {stdErrReader.ReadAsString()}", ex);
} }

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
public static void Attach( public static void Attach(
ISpaBuilder spaBuilder, ISpaBuilder spaBuilder,
string npmScriptName) string scriptName)
{ {
var pkgManagerName = spaBuilder.Options.PackageManagerName; var pkgManagerName = spaBuilder.Options.PackageManagerName;
var sourcePath = spaBuilder.Options.SourcePath; var sourcePath = spaBuilder.Options.SourcePath;
@ -32,15 +32,15 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath)); throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
} }
if (string.IsNullOrEmpty(npmScriptName)) if (string.IsNullOrEmpty(scriptName))
{ {
throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName)); throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
} }
// Start Angular CLI and attach to middleware pipeline // Start Angular CLI and attach to middleware pipeline
var appBuilder = spaBuilder.ApplicationBuilder; var appBuilder = spaBuilder.ApplicationBuilder;
var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName); var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, pkgManagerName, logger); var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, scriptName, pkgManagerName, logger);
// Everything we proxy is hardcoded to target http://localhost because: // Everything we proxy is hardcoded to target http://localhost because:
// - the requests are always from the local machine (we're not accepting remote // - the requests are always from the local machine (we're not accepting remote
@ -63,27 +63,27 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
} }
private static async Task<AngularCliServerInfo> StartAngularCliServerAsync( private static async Task<AngularCliServerInfo> StartAngularCliServerAsync(
string sourcePath, string npmScriptName, string pkgManagerName, ILogger logger) string sourcePath, string scriptName, string pkgManagerName, ILogger logger)
{ {
var portNumber = TcpPortFinder.FindAvailablePort(); var portNumber = TcpPortFinder.FindAvailablePort();
logger.LogInformation($"Starting @angular/cli on port {portNumber}..."); logger.LogInformation($"Starting @angular/cli on port {portNumber}...");
var npmScriptRunner = new NodeScriptRunner( var scriptRunner = new NodeScriptRunner(
sourcePath, npmScriptName, $"--port {portNumber}", null, pkgManagerName); sourcePath, scriptName, $"--port {portNumber}", null, pkgManagerName);
npmScriptRunner.AttachToLogger(logger); scriptRunner.AttachToLogger(logger);
Match openBrowserLine; Match openBrowserLine;
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{ {
try try
{ {
openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch( openBrowserLine = await scriptRunner.StdOut.WaitForMatch(
new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout)); new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout));
} }
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"The NPM script '{npmScriptName}' exited without indicating that the " + $"The {pkgManagerName} script '{scriptName}' exited without indicating that the " +
$"Angular CLI was listening for requests. The error output was: " + $"Angular CLI was listening for requests. The error output was: " +
$"{stdErrReader.ReadAsString()}", ex); $"{stdErrReader.ReadAsString()}", ex);
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -20,10 +20,10 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
/// sure not to enable the Angular CLI server. /// sure not to enable the Angular CLI server.
/// </summary> /// </summary>
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param> /// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
/// <param name="npmScript">The name of the script in your package.json file that launches the Angular CLI process.</param> /// <param name="scriptName">The name of the script in your package.json file that launches the Angular CLI process.</param>
public static void UseAngularCliServer( public static void UseAngularCliServer(
this ISpaBuilder spaBuilder, this ISpaBuilder spaBuilder,
string npmScript) string scriptName)
{ {
if (spaBuilder == null) if (spaBuilder == null)
{ {
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
throw new InvalidOperationException($"To use {nameof(UseAngularCliServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); throw new InvalidOperationException($"To use {nameof(UseAngularCliServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
} }
AngularCliMiddleware.Attach(spaBuilder, npmScript); AngularCliMiddleware.Attach(spaBuilder, scriptName);
} }
} }
} }

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
public static void Attach( public static void Attach(
ISpaBuilder spaBuilder, ISpaBuilder spaBuilder,
string npmScriptName) string scriptName)
{ {
var pkgManagerName = spaBuilder.Options.PackageManagerName; var pkgManagerName = spaBuilder.Options.PackageManagerName;
var sourcePath = spaBuilder.Options.SourcePath; var sourcePath = spaBuilder.Options.SourcePath;
@ -31,15 +31,15 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath)); throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
} }
if (string.IsNullOrEmpty(npmScriptName)) if (string.IsNullOrEmpty(scriptName))
{ {
throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName)); throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
} }
// Start create-react-app and attach to middleware pipeline // Start create-react-app and attach to middleware pipeline
var appBuilder = spaBuilder.ApplicationBuilder; var appBuilder = spaBuilder.ApplicationBuilder;
var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName); var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
var portTask = StartCreateReactAppServerAsync(sourcePath, npmScriptName, pkgManagerName, logger); var portTask = StartCreateReactAppServerAsync(sourcePath, scriptName, pkgManagerName, logger);
// Everything we proxy is hardcoded to target http://localhost because: // Everything we proxy is hardcoded to target http://localhost because:
// - the requests are always from the local machine (we're not accepting remote // - the requests are always from the local machine (we're not accepting remote
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
} }
private static async Task<int> StartCreateReactAppServerAsync( private static async Task<int> StartCreateReactAppServerAsync(
string sourcePath, string npmScriptName, string pkgManagerName, ILogger logger) string sourcePath, string scriptName, string pkgManagerName, ILogger logger)
{ {
var portNumber = TcpPortFinder.FindAvailablePort(); var portNumber = TcpPortFinder.FindAvailablePort();
logger.LogInformation($"Starting create-react-app server on port {portNumber}..."); logger.LogInformation($"Starting create-react-app server on port {portNumber}...");
@ -72,11 +72,11 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
{ "PORT", portNumber.ToString() }, { "PORT", portNumber.ToString() },
{ "BROWSER", "none" }, // We don't want create-react-app to open its own extra browser window pointing to the internal dev server port { "BROWSER", "none" }, // We don't want create-react-app to open its own extra browser window pointing to the internal dev server port
}; };
var npmScriptRunner = new NodeScriptRunner( var scriptRunner = new NodeScriptRunner(
sourcePath, npmScriptName, null, envVars, pkgManagerName); sourcePath, scriptName, null, envVars, pkgManagerName);
npmScriptRunner.AttachToLogger(logger); scriptRunner.AttachToLogger(logger);
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{ {
try try
{ {
@ -84,13 +84,13 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
// it doesn't do so until it's finished compiling, and even then only if there were // it doesn't do so until it's finished compiling, and even then only if there were
// no compiler warnings. So instead of waiting for that, consider it ready as soon // no compiler warnings. So instead of waiting for that, consider it ready as soon
// as it starts listening for requests. // as it starts listening for requests.
await npmScriptRunner.StdOut.WaitForMatch( await scriptRunner.StdOut.WaitForMatch(
new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout)); new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout));
} }
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"The NPM script '{npmScriptName}' exited without indicating that the " + $"The {pkgManagerName} script '{scriptName}' exited without indicating that the " +
$"create-react-app server was listening for requests. The error output was: " + $"create-react-app server was listening for requests. The error output was: " +
$"{stdErrReader.ReadAsString()}", ex); $"{stdErrReader.ReadAsString()}", ex);
} }

View File

@ -20,10 +20,10 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
/// sure not to enable the create-react-app server. /// sure not to enable the create-react-app server.
/// </summary> /// </summary>
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param> /// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
/// <param name="npmScript">The name of the script in your package.json file that launches the create-react-app server.</param> /// <param name="scriptName">The name of the script in your package.json file that launches the create-react-app server.</param>
public static void UseReactDevelopmentServer( public static void UseReactDevelopmentServer(
this ISpaBuilder spaBuilder, this ISpaBuilder spaBuilder,
string npmScript) string scriptName)
{ {
if (spaBuilder == null) if (spaBuilder == null)
{ {
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
throw new InvalidOperationException($"To use {nameof(UseReactDevelopmentServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); throw new InvalidOperationException($"To use {nameof(UseReactDevelopmentServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
} }
ReactDevelopmentServerMiddleware.Attach(spaBuilder, npmScript); ReactDevelopmentServerMiddleware.Attach(spaBuilder, scriptName);
} }
} }
} }