private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(20); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            // Start Vue application and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartVueDevServerAsync(sourcePath, npmScriptName, logger);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the create-react-app server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the create-react-app server has no certificate
            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The vue development server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromMinutes(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string scriptName, int port = 8080, bool https = false, ScriptRunnerType runner = ScriptRunnerType.Npm, string regex = DefaultRegex, bool forceKill = false)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(scriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
            }

            // Start vue-cli and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartVueCliServerAsync(sourcePath, scriptName, logger, port, runner, regex, forceKill);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the vue-cli server)
            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder(https ? "https" : "http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The vue-cli server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
예제 #3
0
    private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

    public static void Attach(
        ISpaBuilder spaBuilder,
        string scriptName)
    {
        var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
        var sourcePath        = spaBuilder.Options.SourcePath;
        var devServerPort     = spaBuilder.Options.DevServerPort;

        if (string.IsNullOrEmpty(sourcePath))
        {
            throw new ArgumentException("Property 'SourcePath' cannot be null or empty", nameof(spaBuilder));
        }

        if (string.IsNullOrEmpty(scriptName))
        {
            throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
        }

        // Start Angular CLI and attach to middleware pipeline
        var appBuilder = spaBuilder.ApplicationBuilder;
        var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IHostApplicationLifetime>().ApplicationStopping;
        var logger                   = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
        var diagnosticSource         = appBuilder.ApplicationServices.GetRequiredService <DiagnosticSource>();
        var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, scriptName, pkgManagerCommand, devServerPort, logger, diagnosticSource, applicationStoppingToken);

        SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
        {
            // On each request, we create a separate startup task with its own timeout. That way, even if
            // the first request times out, subsequent requests could still work.
            var timeout = spaBuilder.Options.StartupTimeout;
            return(angularCliServerInfoTask.WithTimeout(timeout,
                                                        $"The Angular CLI process did not start listening for requests " +
                                                        $"within the timeout period of {timeout.TotalSeconds} seconds. " +
                                                        $"Check the log output for error information."));
        });
    }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new InvalidOperationException("Must set ISpaBuilder.Options.SourcePath before calling this method.");
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            // Start webpack-dev-server and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);

            Task <int> portTask      = null;
            Task <Uri> targetUriTask = null;
            appBuilder.Use(async(context, next) =>
            {
                if (portTask == null)
                {
                    // Get port number of webapp first before we start webpack-dev-server, so that
                    // webpack can use the port number of the webapp for the websocket configuration.
                    var request          = context.Request;
                    int socketPortNumber = request.Host.Port.Value;
                    portTask             = StartWebpackDevServerAsync(sourcePath, npmScriptName, logger, socketPortNumber);
                    // Everything we **proxy** is hardcoded to target http://localhost because:
                    // - the requests are always from the local machine (we're not accepting remote
                    //   requests that go directly to the webpack-dev-server server)
                    // - given that, there's no reason to use https, and we couldn't even if we
                    //   wanted to, because in general the webpack-dev-server server has no certificate
#pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler
                    targetUriTask = portTask.ContinueWith(task =>
                    {
                        // "https" here doesn't work as the webpack-dev-server expects request via "http"
                        Uri uri = new UriBuilder("http", "localhost", task.Result).Uri;
                        return(uri);
                    });
#pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler
                }

#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                await next.Invoke();
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
            });

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The webpack-dev-server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new InvalidOperationException("Must set ISpaBuilder.Options.SourcePath before calling this method.");
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);

            // Clear files in distribution directory aka spaStaticFileOptions.RootPath:
            var spaStaticFileProvider = spaBuilder.ApplicationBuilder.ApplicationServices.GetService(typeof(ISpaStaticFileProvider)) as ISpaStaticFileProvider;
            ClearSpaRootPath(spaStaticFileProvider.FileProvider, logger);

            // Start webpack-dev-server once the application has started
            var        hostApplicationLifetime = spaBuilder.ApplicationBuilder.ApplicationServices.GetService(typeof(IHostApplicationLifetime)) as IHostApplicationLifetime;
            Task <int> portTask         = null;
            Task <Uri> targetUriTask    = null;
            var        socketPortNumber = 0;
            hostApplicationLifetime.ApplicationStarted.Register(() =>
            {
                // When this is called the request pipeline configuration has completed. Only now the addresses
                // at which requests are served are available. We use any address/port combination but use HTTPs
                // if is configured for the project.
                var addressFeature = spaBuilder.ApplicationBuilder.ServerFeatures.Get <IServerAddressesFeature>();
                foreach (var addr in addressFeature.Addresses)
                {
                    var uri          = new Uri(addr);
                    socketPortNumber = uri.Port;
                    if (uri.Scheme == "https")
                    {
                        break;
                    }
                }

                portTask = StartWebpackDevServerAsync(sourcePath, npmScriptName, logger, socketPortNumber);
                // Everything we **proxy** is hardcoded to target http://localhost because:
                // - the requests are always from the local machine (we're not accepting remote
                //   requests that go directly to the webpack-dev-server)
                // - given that, there's no reason to use https when forwarding the request to the webpack
                //   dev server, and we couldn't even if we wanted to, because in general the webpack-dev-server
                //   has no certificate
#pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler
                targetUriTask = portTask.ContinueWith(task =>
                {
                    // "https" here doesn't work as the webpack-dev-server expects request via "http"
                    Uri uri = new UriBuilder("http", "localhost", task.Result).Uri;
                    return(uri);
                });
#pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler
            });

            // Configure proxying. By the time a request comes in, the webpack dev server will be running,
            // so it is fine to configure proxying before the webpack-dev-server has been started.
            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The webpack-dev-server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }