Loadable Components SSR - chunkNames in Server Stats file different from Client stats file

3.7k Views Asked by At

I am trying to set up loadable-components for an SSR un-ejected CRA app. I'm using Craco to override Webpack config and it seems to be working, as the client side loadable-stats.json is being generated. When I run the build command, the chunks are named based on id, as I believe react-scripts is hard-wired to run in production mode. So for my server config, I decided to name the chunks by id too. But unfortunately the chunkNames seem to be the path to the modules in the server stats file, which is generated by the @loadable/webpack-plugin, rather than id. The actual chunks are named by 'id` though, I can see them in the folder.

Here is my code in server.js where I'm trying to read the chunks.

const nodeStatsFile = path.resolve(__dirname, 'node-stats.json');
const nodeExtractor = new ChunkExtractor({ statsFile: nodeStatsFile, entrypoints: ["server"] });
const nodeEntryPointResult = nodeExtractor.requireEntrypoint("server");  // ==> {}
const webStatsFile = path.resolve(__dirname, '../web-stats.json');
const extractor = new ChunkExtractor({ statsFile: webStatsFile });
const client = generateApolloClient();
const app = (<ApolloProvider client={client}>
    <HelmetProvider context={helmetContext}>
      <Router location={req.url} context={staticContext}>
        <CookiesProvider cookies={req.universalCookies}>
            <App />
        </CookiesProvider>
      </Router>
    </HelmetProvider>
  </ApolloProvider>);
getDataFromTree(app).then(() => {
  const jsx = nodeExtractor.collectChunks(app);
  const content = ReactDOMServer.renderToString(jsx);
  const state = client.extract();
  const { helmet } = helmetContext;

  const html = ReactDOMServer.renderToString(
    <Html content={content} helmet={helmet} assets={assets} extractor={extractor} />,
  );
  res.status(staticContext.status || 200);
  res.send(`<!doctype html>${html}`);
  res.end();
})

Due to this, when I run the nodeExtractor (which is reading from the server stats file) to collectChunks and then webExtractor (which is using the client stats file) to getScriptElements I get an empty array of loadable chunks. If I only use the webExtractor it throws an error that chunkNameFromServerStatsFile not found.

My webpack config for the server is using the server.js file, which contains the Express code, as the entry point, whereas the Webpack config for the client has the index file for the app as the entry. I get an empty {} as the server requireEntrypoint result. I'm not really sure what is supposed to happen with that call, the actual documentation is not very clear.

Some people are using the webExtractor only in the server code as well and it seems to be working for them, but I keep getting an error as it's still trying to map the server-side chunks, which it cannot find in the webExtractor.

Any help will be greatly appreciated. Thanks!

1

There are 1 best solutions below

0
On

So this was happening because I forgot to include the @loadable/babel-plugin into my Craco config, for the client-side Webpack build. Works fine after I did that. My final craco.config.js is:

const LoadablePlugin = require('@loadable/webpack-plugin');

module.exports = {
 babel: {
   plugins: ["@loadable/babel-plugin"]  
 },
webpack: {
 mode: "development",
 configure: {
  output: {
    filename: '[name].js',
    chunkFilename: 'static/js/[name].js',
  },
 },
 plugins: [
  new LoadablePlugin(),
 ]
} };

Works perfectly now.