private static async Task <int> StartCreateVueCliAppServerAsync( string sourcePath, string nodeScriptName, ILogger logger, string packageManager) { var portNumber = TcpPortFinder.FindAvailablePort(); logger.LogInformation($"Starting vue-cli-service server on port {portNumber}..."); string arguments = $"--port {portNumber}"; var envVars = new Dictionary <string, string> { }; var nodeScriptRunner = new NodeScriptRunner(sourcePath, nodeScriptName, arguments, envVars, packageManager); using (var stdErrReader = new EventedStreamStringReader(nodeScriptRunner.StdErr)) { try { // Although the dev server may eventually tell us the URL it's listening on, // 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 // as it starts listening for requests. await nodeScriptRunner.StdOut.WaitForMatch(new Regex("App running at:", RegexOptions.None, RegexMatchTimeout)); } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The node script '{nodeScriptName}' exited without indicating that the " + $"node server was listening for requests. The error output was: " + $"{stdErrReader.ReadAsString()}", ex); } } return(portNumber); }
private static async Task <(string scheme, int port)> StartServerAsync( IApplicationBuilder appBuilder, string sourcePath, string npmScriptName, string packageManager, ILogger logger) { var scheme = "http"; var portNumber = TcpPortFinder.FindAvailablePort(); logger.LogInformation($"Starting vue-cli-service server on port {portNumber}..."); //var envVars = new Dictionary<string, string> //{ // { "PORT", portNumber.ToString() }, // { "BROWSER", "none" }, // We don't want vue-cli-service to open its own extra browser window pointing to the internal dev server port //}; var diagnosticSource = appBuilder.ApplicationServices.GetRequiredService <DiagnosticSource>(); #if NETCOREAPP2_1 || NETCOREAPP2_2 var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IApplicationLifetime>().ApplicationStopping; #else var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IHostApplicationLifetime>().ApplicationStopping; #endif var npmScriptRunner = new NodeScriptRunner( sourcePath, npmScriptName, $"--port {portNumber}", null, packageManager, diagnosticSource, applicationStoppingToken); npmScriptRunner.AttachToLogger(logger); using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) { try { // Although the dev server may eventually tell us the URL it's listening on, // 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 // as it starts listening for requests. await npmScriptRunner.StdOut.WaitForMatch( new Regex("App running at", RegexOptions.None, RegexMatchTimeout)); string nextLine = await npmScriptRunner.StdOut.ReadLine(); if (nextLine.Contains("https://")) { scheme = "https"; } } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The {packageManager} script '{npmScriptName}' exited without indicating that the " + $"vue-cli-service server was listening for requests. The error output was: " + $"{stdErrReader.ReadAsString()}", ex); } } return(scheme, portNumber); }
/// <inheritdoc /> public async Task Build(ISpaBuilder spaBuilder) { var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand; var sourcePath = spaBuilder.Options.SourcePath; if (string.IsNullOrEmpty(sourcePath)) { 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 appBuilder = spaBuilder.ApplicationBuilder; var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IHostApplicationLifetime>().ApplicationStopping; var logger = LoggerFinder.GetOrCreateLogger( appBuilder, nameof(AngularCliBuilder)); var diagnosticSource = appBuilder.ApplicationServices.GetRequiredService <DiagnosticSource>(); var scriptRunner = new NodeScriptRunner( sourcePath, _scriptName, "--watch", null, pkgManagerCommand, diagnosticSource, applicationStoppingToken); scriptRunner.AttachToLogger(logger); using (var stdOutReader = new EventedStreamStringReader(scriptRunner.StdOut)) using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr)) { try { await scriptRunner.StdOut.WaitForMatch( new Regex("Date", RegexOptions.None, RegexMatchTimeout)); } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{_scriptName}' exited without indicating success.\n" + $"Output was: {stdOutReader.ReadAsString()}\n" + $"Error output was: {stdErrReader.ReadAsString()}", ex); } catch (OperationCanceledException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{_scriptName}' timed out without indicating success. " + $"Output was: {stdOutReader.ReadAsString()}\n" + $"Error output was: {stdErrReader.ReadAsString()}", ex); } } }
/// <inheritdoc /> public async Task Build(ISpaBuilder spaBuilder) { //var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand; // todo: use this for .netcore >3.x var pkgManagerCommand = "npm"; var sourcePath = spaBuilder.Options.SourcePath; if (string.IsNullOrEmpty(sourcePath)) { throw new InvalidOperationException($"To use {nameof(ChikoAngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); } var logger = spaBuilder.ApplicationBuilder.ApplicationServices.GetService <ILoggerFactory>() ?.CreateLogger <ChikoAngularCliBuilder>(); var scriptRunner = new NodeScriptRunner( sourcePath, _scriptName, "", // "--watch", // todo: make it configurable null, pkgManagerCommand); scriptRunner.AttachToLogger(logger); using (var stdOutReader = new EventedStreamStringReader(scriptRunner.StdOut)) using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr)) { try { await scriptRunner.StdOut.WaitForMatch( new Regex("chunk", RegexOptions.None, RegexMatchTimeout)); } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{_scriptName}' exited without indicating success.\n" + $"Output was: {stdOutReader.ReadAsString()}\n" + $"Error output was: {stdErrReader.ReadAsString()}", ex); } catch (OperationCanceledException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{_scriptName}' timed out without indicating success. " + $"Output was: {stdOutReader.ReadAsString()}\n" + $"Error output was: {stdErrReader.ReadAsString()}", ex); } } }
private static async Task <AngularCliServerInfo> StartAngularCliServerAsync( string sourcePath, string scriptName, string pkgManagerCommand, int portNumber, ILogger logger) { if (portNumber == default(int)) { portNumber = TcpPortFinder.FindAvailablePort(); } logger.LogInformation($"Starting @angular/cli on port {portNumber}..."); var scriptRunner = new NodeScriptRunner( sourcePath, scriptName, $"--port {portNumber}", null, pkgManagerCommand); scriptRunner.AttachToLogger(logger); Match openBrowserLine; using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr)) { try { openBrowserLine = await scriptRunner.StdOut.WaitForMatch( new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout)); } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{scriptName}' exited without indicating that the " + $"Angular CLI was listening for requests. The error output was: " + $"{stdErrReader.ReadAsString()}", ex); } } var uri = new Uri(openBrowserLine.Groups[1].Value); var serverInfo = new AngularCliServerInfo { Port = uri.Port }; // Even after the Angular CLI claims to be listening for requests, there's a short // period where it will give an error if you make a request too quickly await WaitForAngularCliServerToAcceptRequests(uri); return(serverInfo); }
private static async Task <int> StartCreateVueAppServerAsync( string sourcePath, string scriptName, string pkgManagerCommand, int portNumber, ILogger logger, DiagnosticSource diagnosticSource, CancellationToken applicationStoppingToken) { if (portNumber == default) { portNumber = TcpPortFinder.FindAvailablePort(); } logger.LogInformation($"Starting vue-cli server on port {portNumber}..."); var envVars = new Dictionary <string, string> { { "PORT", portNumber.ToString() }, { "BROWSER", "none" }, // We don't want vue-cli to open its own extra browser window pointing to the internal dev server port }; var scriptRunner = new NodeScriptRunner( sourcePath, scriptName, null, envVars, pkgManagerCommand, diagnosticSource, applicationStoppingToken); scriptRunner.AttachToLogger(logger); using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr)) { try { // Although the Vue dev server may eventually tell us the URL it's listening on, // 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 // as it starts listening for requests. await scriptRunner.StdOut.WaitForMatch( new Regex(CLI_SERVE_COMPLETION_REGEX, RegexOptions.None, RegexMatchTimeout)); } catch (EndOfStreamException ex) { throw new InvalidOperationException( $"The {pkgManagerCommand} script '{scriptName}' exited without indicating that the " + $"vue-cli server was listening for requests. The error output was: " + $"{stdErrReader.ReadAsString()}", ex); } } return(portNumber); }