/// <summary> /// Starts the webpack dev server. If the start fails for known reason, modifies the aspnet-webpack module to be compliant with webpack-dev-middleware 5. /// For compatibility purposes as the change is rather samll it's easier to modify the existing module than to create new NPM package and enforce anyone to udpate. /// </summary> private static WebpackDevServerInfo StartWebpackDevServer(IDictionary <string, string> environmentVariables, string projectPath, WebpackDevServerArgs devServerArgs, bool fixAttempted) { // Unlike other consumers of NodeServices, WebpackDevMiddleware dosen't share Node instances, nor does it // use your DI configuration. It's important for WebpackDevMiddleware to have its own private Node instance // because it must *not* restart when files change (if it did, you'd lose all the benefits of Webpack // middleware). And since this is a dev-time-only feature, it doesn't matter if the default transport isn't // as fast as some theoretical future alternative. // This should do it by using Jering.Javascript.NodeJS interop var nodeJSService = NodeInteropFactory.BuildNewInstance(environmentVariables, projectPath); try { return(nodeJSService.InvokeFromStringAsync <WebpackDevServerInfo>( EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware), "/Content/Node/webpack-dev-middleware.js"), //Embedded JS file args: new object[] { JsonSerializer.Serialize(devServerArgs, jsonSerializerOptions) } //Options patched so that they work with aspnet-webpack package ).Result); } catch (Exception ex) { if (fixAttempted) { throw; } if (ex != null && ex.Message.Contains("Dev Middleware has been initialized using an options object that does not match the API schema.")) { //Attempt to modify module file so that it doesn't contain arguments not recognized by the webpack-dev-middleware 5 try { const string SEARCH_PATTERN = "at validate ("; var startIndex = ex.Message.IndexOf(SEARCH_PATTERN); if (startIndex > -1) { startIndex += SEARCH_PATTERN.Length; var endIndex = ex.Message.IndexOf("webpack-dev-middleware", startIndex); var modulesPath = ex.Message.Substring(startIndex, endIndex - startIndex); if (Directory.Exists(modulesPath)) { var modulePath = Path.Combine(modulesPath, @"aspnet-webpack\WebpackDevMiddleware.js"); if (File.Exists(modulePath)) { var fileContent = File.ReadAllText(modulePath); fileContent = fileContent.Replace("noInfo: true,", ""); fileContent = fileContent.Replace("watchOptions: webpackConfig.watchOptions", ""); File.WriteAllText(modulePath, fileContent); nodeJSService.Dispose(); return(StartWebpackDevServer(environmentVariables, projectPath, devServerArgs, true)); } } } } catch (Exception) { } } throw; } }
/// <summary> /// Creates a new instance of <see cref="PrerenderTagHelper"/>. /// </summary> /// <param name="serviceProvider">The <see cref="IServiceProvider"/>.</param> public PrerenderTagHelper(IServiceProvider serviceProvider) { var hostEnv = (IWebHostEnvironment)serviceProvider.GetService(typeof(IWebHostEnvironment)); _nodeServices = NodeInteropFactory.GetInstance(serviceProvider) ?? _fallbackNodeServices; _applicationBasePath = hostEnv.ContentRootPath; var applicationLifetime = (IHostApplicationLifetime)serviceProvider.GetService(typeof(IHostApplicationLifetime)); _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. if (_nodeServices == null) { _nodeServices = _fallbackNodeServices = NodeInteropFactory.BuildNewInstance(serviceProvider); } }