Skip to content

SSR support #6747

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ node_modules/
build
my-app*
packages/react-scripts/template
packages/react-scripts/template-universal
packages/react-scripts/fixtures
fixtures/
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ The build is minified and the filenames include the hashes.<br>

Your app is ready to be deployed.

### `npm run serve` or `yarn serve`

Launches a server for the content in the `build` folder.<br>
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/).
Expand Down Expand Up @@ -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 `[email protected]` 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.
Expand All @@ -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/).
Expand Down
36 changes: 26 additions & 10 deletions packages/babel-preset-react-app/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
{
Expand Down Expand Up @@ -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.
Expand All @@ -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),
Expand Down
39 changes: 28 additions & 11 deletions packages/babel-preset-react-app/dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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),
};
};
11 changes: 8 additions & 3 deletions packages/create-react-app/createReactApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<project-directory>')} is required.`);
Expand Down Expand Up @@ -181,6 +182,7 @@ createApp(
program.useNpm,
program.usePnp,
program.typescript,
program.universal,
hiddenProgram.internalTestingTemplate
);

Expand All @@ -191,6 +193,7 @@ function createApp(
useNpm,
usePnp,
useTypescript,
useUniversal,
template
) {
const root = path.resolve(name);
Expand Down Expand Up @@ -296,7 +299,8 @@ function createApp(
template,
useYarn,
usePnp,
useTypescript
useTypescript,
useUniversal
);
}

Expand Down Expand Up @@ -380,7 +384,8 @@ function run(
template,
useYarn,
usePnp,
useTypescript
useTypescript,
universal
) {
getInstallPackage(version, originalDirectory).then(packageToInstall => {
const allDependencies = ['react', 'react-dom', packageToInstall];
Expand Down Expand Up @@ -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]));
Expand Down
12 changes: 6 additions & 6 deletions packages/react-dev-utils/WebpackDevServerUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand All @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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);
});
Expand Down
Loading