Skip to content
Advertisement

When importing a module from node_modules that uses route based lazy loading in create-react-app, should code splitting work in the same way?

I used create-react-app (react ^17.0.2, react-scripts 4.0.3) to knock up a simple application which uses route based lazy loading to perform code splitting. When I build this application I see separate chunks created per lazy loaded component and this all works fine as all my components are exported using default. My output is:

build/static/js/2.xxxxxxxx.chunk.js
build/static/js/runtime-main.xxxxxxxx.js
build/static/js/main.xxxxxxxx.chunk.js
build/static/js/3.xxxxxxxx.chunk.js
build/static/js/5.xxxxxxxx.chunk.js
build/static/js/4.xxxxxxxx.chunk.js

I then updated my package.json file to include the following entries:

{
  ...
  "main": "dist/App"
  "publishConfig": { "registry": "http://my-private-repo" }
  ...
  "scripts" : {
    ...
    "prepublishOnly": "rm -rf dist && mkdir dist && npx babel src -d dist --copy-files"
  },
  ...
  "devDependencies": {
    "@babel/cli: "^7.16.0",
    "@babel/core: "^7.16.5"
  },
  ...
  "files": [
    "dist/*"
  ]
}

Using this configuration along with a babel.config.js file I then published my application to my private repo, effectively using Babel CLI to transpile everything in the src directory, copying it to the dist folder before publishing the contents of that folder. The babel.config.js simply contains:

module.exports = {
  presets:[
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

Next I created another create-react-app and in it’s root created a .yarnrc file with a registry entry containing my private repo. E.g:

registry "http://my-private-repo"

I then performed a yarn add command:

yarn add 'my-other-application'

Which adds the first application as a dependency to this one. This successfully copied the code into node_modules

Within the index.js of the second application I then import my first application using:

import App from 'my-other-application'

Which I then render using the standard:

<ReactDOM.render(
  <ReactStrictMode>
    <App />
  </ReactStrictMode>
);

Running the second application, everything renders and I can navigate around the routes just fine. However I noticed that the lazy loading behaviour of the first application doesn’t appear to be happening. I confirmed this by building the application and could see that the number of chunks produced was less than my first application:

build/static/js/2.xxxxxxxx.chunk.js
build/static/js/runtime-main.xxxxxxxx.js
build/static/js/main.xxxxxxxx.chunk.js

This to me suggests that code splitting on dynamic imports of my module located in node_modules is not being honoured in the way I was expecting to see. I was expecting to see a similar pattern of chunking to my first application. From reading around on the subject the only conclusion I can draw currently, is that the code from my first application is ending up in a vendor chunk and no further splitting is occurring. Is anyone able to shed some light on this? Is what I was expecting to see achievable with the default webpack configuration provided by create-react-app?

Disclaimer: These technologies are fairly new to me so I apologies if I’ve fundamentally misunderstood something or I’m trying to do something completely unconventional.

Advertisement

Answer

In the classic sense of rubber duck debugging, shortly after posting the above. I realised that I could test my theory about the code splitting not working due to the module being loaded from node_modules. Simply by posting my transpiled code straight into my /src folder and loading the component from there instead. I did this and code splitting still didn’t occur which absolved the create-react-app webpack configuration from being involved.

Instead it suggested an issue during the transpiling phase, and with a more focused google search I came across the answer in this thread: https://github.com/babel/babel/issues/10273#issuecomment-517838332

The problem was in my babel.config.js it needed to be updated to include the modules value of false for preset-env. E.g:

module.exports = {
  presets:[
    ["@babel/present-env, { modules: false }],
    "@babel/preset-react"
  ]
}

After I did this everything started working as I would expect. The key indicator being that the resultant transpiled components retained all of their import statements. Rather than them being converted into promises, which is what was happening prior to the configuration change and what prevented webpack from performing the code splitting.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement