From 71cb3abc1327007c917349fd1f1581918496ed09 Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Wed, 10 Feb 2016 12:13:24 -0800 Subject: [PATCH] Update ReactGrid sample to use newer server-side rendering APIs --- samples/react/ReactGrid/.babelrc | 2 +- .../react/ReactGrid/ReactApp/boot-client.jsx | 7 +- .../react/ReactGrid/ReactApp/boot-server.jsx | 25 +++ .../ReactApp/components/ReactApp.jsx | 14 +- samples/react/ReactGrid/Startup.cs | 154 +++++++++--------- .../react/ReactGrid/Views/Home/Index.cshtml | 2 +- .../react/ReactGrid/Views/_ViewImports.cshtml | 2 +- samples/react/ReactGrid/package.json | 9 +- .../Content/Node/prerenderer.js | 26 ++- 9 files changed, 145 insertions(+), 96 deletions(-) create mode 100644 samples/react/ReactGrid/ReactApp/boot-server.jsx diff --git a/samples/react/ReactGrid/.babelrc b/samples/react/ReactGrid/.babelrc index 15f26dd..d4e5f8d 100644 --- a/samples/react/ReactGrid/.babelrc +++ b/samples/react/ReactGrid/.babelrc @@ -1,3 +1,3 @@ { - presets: ["es2015", "react"] + "presets": ["es2015", "react"] } diff --git a/samples/react/ReactGrid/ReactApp/boot-client.jsx b/samples/react/ReactGrid/ReactApp/boot-client.jsx index b50fcf3..d109cdf 100644 --- a/samples/react/ReactGrid/ReactApp/boot-client.jsx +++ b/samples/react/ReactGrid/ReactApp/boot-client.jsx @@ -1,9 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import createBrowserHistory from 'history/lib/createBrowserHistory'; -import ReactApp from './components/ReactApp.jsx'; +import { browserHistory } from 'react-router'; +import { ReactApp } from './components/ReactApp.jsx'; import 'bootstrap/dist/css/bootstrap.css'; // In the browser, we render into a DOM node and hook up to the browser's history APIs -var history = createBrowserHistory(); -ReactDOM.render(, document.getElementById('react-app')); +ReactDOM.render(, document.getElementById('react-app')); diff --git a/samples/react/ReactGrid/ReactApp/boot-server.jsx b/samples/react/ReactGrid/ReactApp/boot-server.jsx new file mode 100644 index 0000000..873802e --- /dev/null +++ b/samples/react/ReactGrid/ReactApp/boot-server.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { renderToString } from 'react-dom/server'; +import { match, RouterContext } from 'react-router'; +import createMemoryHistory from 'history/lib/createMemoryHistory'; +import { routes } from './components/ReactApp'; +React; + +export default function renderApp (params) { + return new Promise((resolve, reject) => { + // Match the incoming request against the list of client-side routes + match({ routes, location: params.location }, (error, redirectLocation, renderProps) => { + if (error) { + throw error; + } + + // Build an instance of the application + const history = createMemoryHistory(params.url); + const app = ; + + // Render it as an HTML string which can be injected into the response + const html = renderToString(app); + resolve({ html }); + }); + }); +} diff --git a/samples/react/ReactGrid/ReactApp/components/ReactApp.jsx b/samples/react/ReactGrid/ReactApp/components/ReactApp.jsx index 1efeef7..6978f1d 100644 --- a/samples/react/ReactGrid/ReactApp/components/ReactApp.jsx +++ b/samples/react/ReactGrid/ReactApp/components/ReactApp.jsx @@ -3,14 +3,16 @@ import { Router, Route } from 'react-router'; import { PeopleGrid } from './PeopleGrid.jsx'; import { PersonEditor } from './PersonEditor.jsx'; -export default class ReactApp extends React.Component { +export const routes = + + + +; + +export class ReactApp extends React.Component { render() { return ( - - - - - + ); } } diff --git a/samples/react/ReactGrid/Startup.cs b/samples/react/ReactGrid/Startup.cs index eb55c5a..6024c42 100755 --- a/samples/react/ReactGrid/Startup.cs +++ b/samples/react/ReactGrid/Startup.cs @@ -1,77 +1,77 @@ -using Microsoft.AspNet.Builder; -using Microsoft.AspNet.Hosting; -using Microsoft.AspNet.SpaServices; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.PlatformAbstractions; - -namespace ReactExample -{ - public class Startup - { - public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) - { - // Setup configuration sources. - var builder = new ConfigurationBuilder() - .SetBasePath(appEnv.ApplicationBasePath) - .AddJsonFile("appsettings.json") - .AddEnvironmentVariables(); - Configuration = builder.Build(); - } - - public IConfigurationRoot Configuration { get; set; } - - // This method gets called by the runtime. - public void ConfigureServices(IServiceCollection services) - { - // Add MVC services to the services container. - services.AddMvc(); - } - - // Configure is called after ConfigureServices is called. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.MinimumLevel = LogLevel.Warning; - loggerFactory.AddConsole(); - loggerFactory.AddDebug(); - - // Configure the HTTP request pipeline. - - // Add the platform handler to the request pipeline. - app.UseIISPlatformHandler(); - - // Add the following to the request pipeline only in development environment. - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - // Add Error handling middleware which catches all application specific errors and - // send the request to the following path or controller action. - app.UseExceptionHandler("/Home/Error"); - } - - // In dev mode, the JS/TS/etc is compiled and served dynamically and supports hot replacement. - // In production, we assume you've used webpack to emit the prebuilt content to disk. - if (env.IsDevelopment()) { - app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { - HotModuleReplacement = true, - ReactHotModuleReplacement = true - }); - } - - // Add static files to the request pipeline. - app.UseStaticFiles(); - - // Add MVC to the request pipeline. - app.UseMvc(routes => - { - routes.MapSpaFallbackRoute( - name: "default", - defaults: new { controller="Home", action = "Index" }); - }); - } - } -} +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.SpaServices.Webpack; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.PlatformAbstractions; + +namespace ReactExample +{ + public class Startup + { + public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) + { + // Setup configuration sources. + var builder = new ConfigurationBuilder() + .SetBasePath(appEnv.ApplicationBasePath) + .AddJsonFile("appsettings.json") + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; set; } + + // This method gets called by the runtime. + public void ConfigureServices(IServiceCollection services) + { + // Add MVC services to the services container. + services.AddMvc(); + } + + // Configure is called after ConfigureServices is called. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.MinimumLevel = LogLevel.Warning; + loggerFactory.AddConsole(); + loggerFactory.AddDebug(); + + // Configure the HTTP request pipeline. + + // Add the platform handler to the request pipeline. + app.UseIISPlatformHandler(); + + // Add the following to the request pipeline only in development environment. + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + // Add Error handling middleware which catches all application specific errors and + // send the request to the following path or controller action. + app.UseExceptionHandler("/Home/Error"); + } + + // In dev mode, the JS/TS/etc is compiled and served dynamically and supports hot replacement. + // In production, we assume you've used webpack to emit the prebuilt content to disk. + if (env.IsDevelopment()) { + app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true, + ReactHotModuleReplacement = true + }); + } + + // Add static files to the request pipeline. + app.UseStaticFiles(); + + // Add MVC to the request pipeline. + app.UseMvc(routes => + { + routes.MapSpaFallbackRoute( + name: "default", + defaults: new { controller="Home", action = "Index" }); + }); + } + } +} diff --git a/samples/react/ReactGrid/Views/Home/Index.cshtml b/samples/react/ReactGrid/Views/Home/Index.cshtml index 40b1291..d16c164 100755 --- a/samples/react/ReactGrid/Views/Home/Index.cshtml +++ b/samples/react/ReactGrid/Views/Home/Index.cshtml @@ -1,4 +1,4 @@ -
+
@section scripts { diff --git a/samples/react/ReactGrid/Views/_ViewImports.cshtml b/samples/react/ReactGrid/Views/_ViewImports.cshtml index 5fb3147..5746f7e 100755 --- a/samples/react/ReactGrid/Views/_ViewImports.cshtml +++ b/samples/react/ReactGrid/Views/_ViewImports.cshtml @@ -1,3 +1,3 @@ @using ReactExample @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" -@addTagHelper "*, Microsoft.AspNet.ReactServices" +@addTagHelper "*, Microsoft.AspNet.SpaServices" diff --git a/samples/react/ReactGrid/package.json b/samples/react/ReactGrid/package.json index 826c5a5..57d8ac4 100644 --- a/samples/react/ReactGrid/package.json +++ b/samples/react/ReactGrid/package.json @@ -4,13 +4,14 @@ "dependencies": { "babel-core": "^6.4.5", "bootstrap": "^3.3.5", + "domain-task": "^1.0.0", "formsy-react": "^0.17.0", "formsy-react-components": "^0.6.3", - "griddle-react": "^0.2.14", + "griddle-react": "^0.3.1", "history": "^1.12.6", - "react": "^0.14.0", - "react-dom": "^0.14.0", - "react-router": "^1.0.0-rc3", + "react": "^0.14.7", + "react-dom": "^0.14.7", + "react-router": "^2.0.0-rc5", "underscore": "^1.8.3" }, "devDependencies": { diff --git a/src/Microsoft.AspNet.SpaServices/Content/Node/prerenderer.js b/src/Microsoft.AspNet.SpaServices/Content/Node/prerenderer.js index 23644a0..e448941 100644 --- a/src/Microsoft.AspNet.SpaServices/Content/Node/prerenderer.js +++ b/src/Microsoft.AspNet.SpaServices/Content/Node/prerenderer.js @@ -8,17 +8,26 @@ // TODO: Consider some general method for checking if you have all the necessary NPM modules installed, // and if not, giving an error that tells you what command to execute to install the missing ones. var fs = require('fs'); -var ts = require('ntypescript'); +var ts = requireIfInstalled('ntypescript'); var babelCore = require('babel-core'); var resolveBabelRc = require('babel-loader/lib/resolve-rc'); // If this ever breaks, we can easily scan up the directory hierarchy ourselves var origJsLoader = require.extensions['.js']; function resolveBabelOptions(relativeToFilename) { var babelRcText = resolveBabelRc(relativeToFilename); - return babelRcText ? JSON.parse(babelRcText) : {}; + try { + return babelRcText ? JSON.parse(babelRcText) : {}; + } catch (ex) { + ex.message = 'Error while parsing babelrc JSON: ' + ex.message; + throw ex; + } } function loadViaTypeScript(module, filename) { + if (!ts) { + throw new Error('Can\'t load .ts/.tsx files because the \'ntypescript\' package isn\'t installed.\nModule requested: ' + module); + } + // First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking) // and is unlikely to need any special compiler options var src = fs.readFileSync(filename, 'utf8'); @@ -45,6 +54,19 @@ function loadViaBabel(module, filename) { } } +function requireIfInstalled(packageName) { + return isPackageInstalled(packageName) ? require(packageName) : null; +} + +function isPackageInstalled(packageName) { + try { + require.resolve(packageName); + return true; + } catch(e) { + return false; + } +} + function register() { require.extensions['.js'] = loadViaBabel; require.extensions['.jsx'] = loadViaBabel;