From edbed587defb8254098fd3e61869097e53880a67 Mon Sep 17 00:00:00 2001 From: James K Nelson Date: Wed, 3 Apr 2019 23:18:53 +0900 Subject: [PATCH 01/12] SSR support --- README.md | 8 +- packages/create-react-app/createReactApp.js | 11 +- .../react-dev-utils/WebpackDevServerUtils.js | 10 +- .../react-dev-utils/devRendererMiddleware.js | 175 ++++++++++++++++ packages/react-dev-utils/package.json | 4 +- .../printHostingInstructions.js | 12 +- packages/react-scripts/bin/react-scripts.js | 1 + packages/react-scripts/config/env.js | 16 +- packages/react-scripts/config/paths.js | 103 +++++++--- .../react-scripts/config/webpack.config.js | 186 +++++++++++------- .../config/webpackDevServer.config.js | 78 +++++++- packages/react-scripts/package.json | 6 + packages/react-scripts/scripts/build.js | 28 +-- packages/react-scripts/scripts/init.js | 10 +- packages/react-scripts/scripts/serve.js | 110 +++++++++++ packages/react-scripts/scripts/start.js | 26 ++- .../scripts/utils/createJestConfig.js | 2 +- .../react-scripts/template-ssr/src/index.js | 12 ++ .../template-ssr/src/index.node.js | 24 +++ .../src/index.node.tsx | 24 +++ .../template-typescript-ssr/src/index.tsx | 12 ++ .../template-typescript/public/index.html | 2 +- .../react-scripts/template/public/index.html | 2 +- 23 files changed, 712 insertions(+), 150 deletions(-) create mode 100644 packages/react-dev-utils/devRendererMiddleware.js create mode 100644 packages/react-scripts/scripts/serve.js create mode 100644 packages/react-scripts/template-ssr/src/index.js create mode 100644 packages/react-scripts/template-ssr/src/index.node.js create mode 100644 packages/react-scripts/template-typescript-ssr/src/index.node.tsx create mode 100644 packages/react-scripts/template-typescript-ssr/src/index.tsx diff --git a/README.md b/README.md index 347d4359760..9fc198cf9d1 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,11 @@ The build is minified and the filenames include the hashes.
Your app is ready to be deployed. +### `npm run serve` or `yarn serve` + +Launches a server for the content in the `build` folder.
+When an `index.node.js` file exists, it loads the Node bundle and server the app with server rendering. + ## User Guide You can find detailed instructions on using Create React App and many tips in [its documentation](https://facebook.github.io/create-react-app/). @@ -149,6 +154,7 @@ Your environment will have everything you need to build a modern single-page Rea - A live development server that warns about common mistakes. - A build script to bundle JS, CSS, and images for production, with hashes and sourcemaps. - An offline-first [service worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) and a [web app manifest](https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/), meeting all the [Progressive Web App](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) criteria. (_Note: Using the service worker is opt-in as of `react-scripts@2.0.0` and higher_) +- Ability to build a Node.js version of the app, which can be used for implementind Server Rendering. - Hassle-free updates for the above tools with a single dependency. Check out [this guide](https://github.com/nitishdayal/cra_closer_look) for an overview of how these tools fit together. @@ -171,8 +177,6 @@ Here are a few common cases where you might want to try something else: - If you need to **publish a React component**, [nwb](https://github.com/insin/nwb) can [also do this](https://github.com/insin/nwb#react-components-and-libraries), as well as [Neutrino's react-components preset](https://neutrino.js.org/packages/react-components/). -- If you want to do **server rendering** with React and Node.js, check out [Next.js](https://github.com/zeit/next.js/) or [Razzle](https://github.com/jaredpalmer/razzle). Create React App is agnostic of the backend, and just produces static HTML/JS/CSS bundles. - - If your website is **mostly static** (for example, a portfolio or a blog), consider using [Gatsby](https://www.gatsbyjs.org/) instead. Unlike Create React App, it pre-renders the website into HTML at the build time. - Finally, if you need **more customization**, check out [Neutrino](https://neutrino.js.org/) and its [React preset](https://neutrino.js.org/packages/react/). diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 2e69901b455..67a8c612c6f 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -79,6 +79,7 @@ const program = new commander.Command(packageJson.name) .option('--use-npm') .option('--use-pnp') .option('--typescript') + .option('--ssr') .allowUnknownOption() .on('--help', () => { console.log(` Only ${chalk.green('')} is required.`); @@ -181,6 +182,7 @@ createApp( program.useNpm, program.usePnp, program.typescript, + program.ssr, hiddenProgram.internalTestingTemplate ); @@ -191,6 +193,7 @@ function createApp( useNpm, usePnp, useTypescript, + useSsr, template ) { const root = path.resolve(name); @@ -296,7 +299,8 @@ function createApp( template, useYarn, usePnp, - useTypescript + useTypescript, + useSsr ); } @@ -380,7 +384,8 @@ function run( template, useYarn, usePnp, - useTypescript + useTypescript, + useSsr ) { getInstallPackage(version, originalDirectory).then(packageToInstall => { const allDependencies = ['react', 'react-dom', packageToInstall]; @@ -436,7 +441,7 @@ function run( cwd: process.cwd(), args: nodeArgs, }, - [root, appName, verbose, originalDirectory, template], + [root, appName, verbose, originalDirectory, template, useSsr], ` var init = require('${packageName}/scripts/init.js'); init.apply(null, JSON.parse(process.argv[1])); diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js index 1d7021e037c..4c654a67646 100644 --- a/packages/react-dev-utils/WebpackDevServerUtils.js +++ b/packages/react-dev-utils/WebpackDevServerUtils.js @@ -139,14 +139,14 @@ function createCompiler({ let tsMessagesResolver; if (useTypeScript) { - compiler.hooks.beforeCompile.tap('beforeCompile', () => { + compiler.compilers[0].hooks.beforeCompile.tap('beforeCompile', () => { tsMessagesPromise = new Promise(resolve => { tsMessagesResolver = msgs => resolve(msgs); }); }); forkTsCheckerWebpackPlugin - .getCompilerHooks(compiler) + .getCompilerHooks(compiler.compilers[0]) .receive.tap('afterTypeScriptCheck', (diagnostics, lints) => { const allMsgs = [...diagnostics, ...lints]; const format = message => @@ -173,7 +173,7 @@ function createCompiler({ // them in a readable focused way. // We only construct the warnings and errors for speed: // https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548 - const statsData = stats.toJson({ + const statsData = stats.stats[0].toJson({ all: false, warnings: true, errors: true, @@ -195,8 +195,8 @@ function createCompiler({ // Push errors and warnings into compilation result // to show them after page refresh triggered by user. - stats.compilation.errors.push(...messages.errors); - stats.compilation.warnings.push(...messages.warnings); + stats.stats[0].compilation.errors.push(...messages.errors); + stats.stats[0].compilation.warnings.push(...messages.warnings); if (messages.errors.length > 0) { devSocket.errors(messages.errors); diff --git a/packages/react-dev-utils/devRendererMiddleware.js b/packages/react-dev-utils/devRendererMiddleware.js new file mode 100644 index 00000000000..b3ff9b59267 --- /dev/null +++ b/packages/react-dev-utils/devRendererMiddleware.js @@ -0,0 +1,175 @@ +'use strict'; + +const chalk = require('chalk'); +const Module = require('module'); +const path = require('path'); +const tmp = require('tmp'); +const fs = require('fs'); +const clearConsole = require('./clearConsole'); + +module.exports = function devRendererMiddleware( + nodeBuildPath, + registerSourceMap +) { + return (req, res, next) => { + let cache = {}; + let { webpackStats, fs: memoryFs } = res.locals; + + // The index.html file contains `