Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ssr with react and express and requiring stats.json explanation

I am looking at the code example at the bottom which is a react ssr example:

in the configureProduction function, it has this line:

const clientStats = require('./assets/stats.json');

What is this stats.json file that is being required?

import express from 'express';
import { join } from 'path';
import { log } from 'winston';

/**
 * Configures hot reloading and assets paths for local development environment.
 * Use the `npm start` command to start the local development server.
 *
 * @param app Express app
 */
const configureDevelopment = app => {
    const clientConfig = require('../webpack/client');
    const serverConfig = require('../webpack/server');
    const publicPath = clientConfig.output.publicPath;
    const outputPath = clientConfig.output.path;

    const multiCompiler = require('webpack')([clientConfig, serverConfig]);
    const clientCompiler = multiCompiler.compilers[0];

    app.use(require('webpack-dev-middleware')(multiCompiler, {publicPath}));
    app.use(require('webpack-hot-middleware')(clientCompiler));

    app.use(publicPath, express.static(outputPath));

    app.use(require('webpack-hot-server-middleware')(multiCompiler, {
        serverRendererOptions: { outputPath }
    }));

    app.set('views', join(__dirname, '../public/views'));
};

/**
 * Configures assets paths for production environment.
 * This environment is used in deployment and inside the docker container.
 * Use the `npm run build` command to create a production build.
 *
 * @param app Express app
 */
const configureProduction = app => {
    const clientStats = require('./assets/stats.json');
    const serverRender = require('./assets/app.server.js').default;
    const publicPath = '/';
    const outputPath = join(__dirname, 'assets');

    app.use(publicPath, express.static(outputPath));
    app.use(serverRender({
        clientStats,
        outputPath
    }));

    app.set('views', join(__dirname, 'views'));
};

const app = express();

log('info', `Configuring server for environment: ${process.env.NODE_ENV}...`);
if (process.env.NODE_ENV === 'development') {
    configureDevelopment(app);
} else {
    configureProduction(app);
}

log('info', 'Configuring server engine...');
app.set('view engine', 'ejs');
app.set('port', process.env.PORT || 3000);

app.listen(app.get('port'), () => log('info', `Server listening on port ${app.get('port')}...`));
like image 516
dagda1 Avatar asked Feb 18 '18 08:02

dagda1


3 Answers

This is likely to be a file generated by a webpack plugin (https://github.com/danethurber/webpack-manifest-plugin) after building the client-side bundle, that file name is hashed and necessary to the server so it knows how to render the base template which will then bootstrap the client.

Of course that's a guess since we don't have access to your json file, webpack configuration or package.json..

This repository uses a similar approach: https://github.com/CheesecakeLabs/react-redux-boilerplate/ It builds the client, generates the same kind of file and then builds the server bundle using that JSON file as information point to understand how the client bundle is named.

The JSON file should be similar to this:

{
  "apple-touch-icon.png": "114dec1694406188ff0cb2698607cbca.png",
  "production.css": "production.fbee6dc76218b122f7ff.css",
  "production.css.map": "production.fbee6dc76218b122f7ff.css.map",
  "production.js": "production.fbee6dc76218b122f7ff.js",
  "production.js.map": "production.fbee6dc76218b122f7ff.js.map",
  "safari-pinned-tab.svg": "f157afc1cf258044878dab6647d2800b.svg"
}
like image 137
enapupe Avatar answered Nov 15 '22 21:11

enapupe


The stats.json file is generated by the webpack-stats-plugin and can be use by the Node process to "identify the correct bundle path in your server": https://github.com/FormidableLabs/webpack-stats-plugin

like image 45
Miguel Calderón Avatar answered Nov 15 '22 20:11

Miguel Calderón


The project you are looking at is below

https://github.com/rherwig/template-react-16-ssr/blob/master/src/index.js

If you look at the client.production.js file on below

https://github.com/rherwig/template-react-16-ssr/blob/4402eb87fb2e45c16b0b6bd7d093d68ed529077b/webpack/client.production.js#L36

The code uses

plugins: [
    new ExtractCssChunks(),
    new webpack.optimize.CommonsChunkPlugin({
        names: ['bootstrap'],
        filename: '[name].js',
        minChunks: Infinity
    }),
    new StatsWebpackPlugin('stats.json'),
    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: JSON.stringify('production')
        }
    })
]

As you can see it uses StatsWebpackPlugin to save the stats in stats.json. Now let's look at the usage

const serverRender = require('./assets/app.server.js').default;

app.use(serverRender({
        clientStats,
        outputPath
    }));

So it is passing the clientStats and outputPath to the serverRender, which the default export of the assets/app.server.js. Now if you look at file

https://github.com/rherwig/template-react-16-ssr/blob/master/src/server/index.js

export default ({ clientStats }) => async (req, res) => {
    const app = (
        <App/>
    );

    const appString = ReactDOM.renderToString(app);
    const chunkNames = flushChunkNames();
    const { js, styles, cssHash } = flushChunks(clientStats, { chunkNames });
    ....    
};

It passes the clientStats to flushChunks which is from webpack-flush-chunks. Which is to get the css, js include scripts for the generated files. This is then used to render the template

    res.render('index', {
        appString,
        js,
        styles,
        cssHash
    });

If you look at the index.ejs template

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <%- styles %>

    <title>React 16 | Sample</title>
</head>
<body>

<div id="react-root"><%- appString %></div>

<%- cssHash %>
<%- js %>

</body>
</html>

It uses the CSS, JS links on the rendered page. All this was needed because we need information of chunks which generated because of the plugins ExtractCssChunks and webpack.optimize.CommonsChunkPlugin

like image 26
Tarun Lalwani Avatar answered Nov 15 '22 20:11

Tarun Lalwani