diff --git a/.eslintignore b/.eslintignore
index 27c694cea55..531e70634f8 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -2,5 +2,6 @@ node_modules/
build
my-app*
packages/react-scripts/template
+packages/react-scripts/template-universal
packages/react-scripts/fixtures
fixtures/
diff --git a/README.md b/README.md
index 347d4359760..cfc7443636f 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 implementing 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/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js
index 7b7f1da6ed1..ee4c99917d0 100644
--- a/packages/babel-preset-react-app/create.js
+++ b/packages/babel-preset-react-app/create.js
@@ -29,10 +29,12 @@ module.exports = function(api, opts, env) {
var isEnvProduction = env === 'production';
var isEnvTest = env === 'test';
+ var isTargettingNode = validateBoolOption('node', opts.node, false);
+
var useESModules = validateBoolOption(
'useESModules',
opts.useESModules,
- isEnvDevelopment || isEnvProduction
+ !isTargettingNode && (isEnvDevelopment || isEnvProduction)
);
var isFlowEnabled = validateBoolOption('flow', opts.flow, true);
var isTypeScriptEnabled = validateBoolOption(
@@ -75,21 +77,35 @@ module.exports = function(api, opts, env) {
},
},
],
- (isEnvProduction || isEnvDevelopment) && [
+ isTargettingNode && [
// Latest stable ECMAScript features
require('@babel/preset-env').default,
{
- // Allow importing core-js in entrypoint and use browserlist to select polyfills
- useBuiltIns: 'entry',
- // Set the corejs version we are using to avoid warnings in console
- // This will need to change once we upgrade to corejs@3
- corejs: 3,
+ targets: {
+ node: 6,
+ },
// Do not transform modules to CJS
- modules: false,
+ modules: 'cjs',
// Exclude transforms that make all code slower
exclude: ['transform-typeof-symbol'],
},
],
+ !isTargettingNode &&
+ (isEnvProduction || isEnvDevelopment) && [
+ // Latest stable ECMAScript features
+ require('@babel/preset-env').default,
+ {
+ // Allow importing core-js in entrypoint and use browserlist to select polyfills
+ useBuiltIns: 'entry',
+ // Set the corejs version we are using to avoid warnings in console
+ // This will need to change once we upgrade to corejs@3
+ corejs: 3,
+ // Do not transform modules to CJS
+ modules: false,
+ // Exclude transforms that make all code slower
+ exclude: ['transform-typeof-symbol'],
+ },
+ ],
[
require('@babel/preset-react').default,
{
@@ -170,7 +186,7 @@ module.exports = function(api, opts, env) {
{
corejs: false,
helpers: areHelpersEnabled,
- regenerator: true,
+ regenerator: !isTargettingNode,
// https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
// We should turn this on once the lowest version of Node LTS
// supports ES Modules.
@@ -190,7 +206,7 @@ module.exports = function(api, opts, env) {
],
// Adds syntax support for import()
require('@babel/plugin-syntax-dynamic-import').default,
- isEnvTest &&
+ (isTargettingNode || isEnvTest) &&
// Transform dynamic import to require
require('babel-plugin-dynamic-import-node'),
].filter(Boolean),
diff --git a/packages/babel-preset-react-app/dependencies.js b/packages/babel-preset-react-app/dependencies.js
index 86902d36bee..9273296c671 100644
--- a/packages/babel-preset-react-app/dependencies.js
+++ b/packages/babel-preset-react-app/dependencies.js
@@ -36,6 +36,8 @@ module.exports = function(api, opts) {
var isEnvProduction = env === 'production';
var isEnvTest = env === 'test';
+ var isTargettingNode = validateBoolOption('node', opts.node, false);
+
var areHelpersEnabled = validateBoolOption('helpers', opts.helpers, false);
var useAbsoluteRuntime = validateBoolOption(
'absoluteRuntime',
@@ -80,21 +82,35 @@ module.exports = function(api, opts) {
exclude: ['transform-typeof-symbol'],
},
],
- (isEnvProduction || isEnvDevelopment) && [
+ isTargettingNode && [
// Latest stable ECMAScript features
require('@babel/preset-env').default,
{
- // Allow importing core-js in entrypoint and use browserlist to select polyfills
- useBuiltIns: 'entry',
- // Set the corejs version we are using to avoid warnings in console
- // This will need to change once we upgrade to corejs@3
- corejs: 3,
+ targets: {
+ node: 6,
+ },
// Do not transform modules to CJS
- modules: false,
+ modules: 'cjs',
// Exclude transforms that make all code slower
exclude: ['transform-typeof-symbol'],
},
],
+ !isTargettingNode &&
+ (isEnvProduction || isEnvDevelopment) && [
+ // Latest stable ECMAScript features
+ require('@babel/preset-env').default,
+ {
+ // Allow importing core-js in entrypoint and use browserlist to select polyfills
+ useBuiltIns: 'entry',
+ // Set the corejs version we are using to avoid warnings in console
+ // This will need to change once we upgrade to corejs@3
+ corejs: 3,
+ // Do not transform modules to CJS
+ modules: false,
+ // Exclude transforms that make all code slower
+ exclude: ['transform-typeof-symbol'],
+ },
+ ],
].filter(Boolean),
plugins: [
// Necessary to include regardless of the environment because
@@ -127,11 +143,12 @@ module.exports = function(api, opts) {
{
corejs: false,
helpers: areHelpersEnabled,
- regenerator: true,
+ regenerator: !isTargettingNode,
// https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
// We should turn this on once the lowest version of Node LTS
// supports ES Modules.
- useESModules: isEnvDevelopment || isEnvProduction,
+ useESModules:
+ !isTargettingNode && (isEnvDevelopment || isEnvProduction),
// Undocumented option that lets us encapsulate our runtime, ensuring
// the correct version is used
// https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42
@@ -140,9 +157,9 @@ module.exports = function(api, opts) {
],
// Adds syntax support for import()
require('@babel/plugin-syntax-dynamic-import').default,
- isEnvTest &&
+ (isTargettingNode || isEnvTest) &&
// Transform dynamic import to require
- require('babel-plugin-transform-dynamic-import').default,
+ require('babel-plugin-dynamic-import-node'),
].filter(Boolean),
};
};
diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js
index 2e69901b455..138a4471bd7 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('--universal')
.allowUnknownOption()
.on('--help', () => {
console.log(` Only ${chalk.green('')} is required.`);
@@ -181,6 +182,7 @@ createApp(
program.useNpm,
program.usePnp,
program.typescript,
+ program.universal,
hiddenProgram.internalTestingTemplate
);
@@ -191,6 +193,7 @@ function createApp(
useNpm,
usePnp,
useTypescript,
+ useUniversal,
template
) {
const root = path.resolve(name);
@@ -296,7 +299,8 @@ function createApp(
template,
useYarn,
usePnp,
- useTypescript
+ useTypescript,
+ useUniversal
);
}
@@ -380,7 +384,8 @@ function run(
template,
useYarn,
usePnp,
- useTypescript
+ useTypescript,
+ universal
) {
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, universal],
`
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..ca533e0625c 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);
@@ -256,7 +256,7 @@ function createCompiler({
arg => arg.indexOf('--smoke-test') > -1
);
if (isSmokeTest) {
- compiler.hooks.failed.tap('smokeTest', async () => {
+ compiler.compilers[0].hooks.failed.tap('smokeTest', async () => {
await tsMessagesPromise;
process.exit(1);
});
diff --git a/packages/react-dev-utils/devRendererMiddleware.js b/packages/react-dev-utils/devRendererMiddleware.js
new file mode 100644
index 00000000000..1276eb2ac48
--- /dev/null
+++ b/packages/react-dev-utils/devRendererMiddleware.js
@@ -0,0 +1,173 @@
+'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 `