Upgrading Ejected CRA Project
Posted on September 10, 2019
I recently upgraded a react app with significantly large code base and already running in production which was created using CRA and had webpack ejected. The goal was to upgrade React from v15 to v16, webpack from v3 to v4, Babel from v6 to v7 and all the related loader / plugin and dependencies to their latest versions. In addition to that, the project had a significant portion of code written in
cjsx which was to be removed as it's no more a good idea to keep using CoffeeScript.
I'll share some of my learnings and approaches here.
Explore Alternatives To Ejecting CRA
Ejecting webpack does give a lot of customizing power, but do consider other approaches before ejecting. Since upgrading ejected app is quite a task, of manually keeping check of latest version of packages and their compatibility with versions of React and webpack. Consider reading alternatives to ejecting.
Approaches To Upgrading Ejected CRA
Update Packages Manually
I did try updating individual packages and it worked to an extent.
Basic approach should be:
Read the changelog for the particular package.
Follow the migration doc if present.
Identify and upgrade the plugins and loaders or the related config change.
This did work well for upgrading Babel and webpack. For upgrading their plugins and loaders, I checked their respective repos on GitHub for any changelog or deprecation notice. For example, the webpack's
ExtractTextPlugin is deprecated and you should instead use
mode is set to
production you no more need to use the
Copy From The Latest CRA Eject
This approach is more foolproof. Create a new project from CRA and eject it. Copy the webpack config and related files and also
build script files. Basically, everything you see in
scripts folder. Now, compare and update the packages' versions, remove unused packages and add new ones if required.
You should still read and go through the changelog of Babel and especially webpack. It'll help you understand the changes in the config file. This approach also automatically adds new CRA features like support for typescript and other optimizations in the webpack config.
Potential Issues You May Face
After you follow the upgrade procedure and run
npm start or
yarn start, the start script will probably fail the first time. Try to identify the issue, fix it and keep repeating it unless you get the dev server running.
Dev Build Works, Production Does Not
This may happen, since there are different configs for both. As a first hack, pass 'development' to
configFactory in build script. To better identify if the issue is with the production config.
Babel has some significant changes from version 6 to 7. Try changing
BABEL_ENV = 'development' in the build script. It did the trick for me. (Update: No it wasn't actually babel, the issue was with the code. Something to do with propTypes being dropped on prod build but still some code trying to access it, resulting in error.)
Few issues I faced were:
TerserPluginwith options set for best optimizations. I had to modify a few options and add some additional options to override the defaults. If you have ESLint enabled in your webpack config in your project which is suppose to ensure code quality, you may not face this issue.
Styles did not load properly. The development build uses
ExtractTextPlugindid work as expected with the existing code but, with
MiniCssExtractPluginhad to do certain refactoring in the LESS files.
Moving LESS to SASS
The project I was upgrading is using LESS and it's no more a good idea to use LESS. SASS is better in a lot of terms. Refer this article to know more on why you should use SASS over LESS sass-vs-less. I did try to convert my LESS files to SASS and there are npm packages which do that. But it's not 100% compatible conversion. I later decided not to go ahead with it and let LESS remain there for now. Any new CSS should be written in SASS and LESS can be gradually removed over time.
If you get rest of the things working, this is the easiest step. React codemods make it even easier. Just go through the official react migration doc.
In my case running react codemods was not that simple. I had to first convert all CoffeeScript files, both
NPM packages decaffeinate and depercolator are amazing, converting literally all of my
.cjsx files to
.jsx respectively. There were some issues in converting, but these cli commands come with options, like I had to use
--disable-babel-constructor-workaround and then fix the error manually in some of the files.
Hope you found this information helpful. There's much more to this whole upgrading process but I guess I have covered everything in simplest possible way. Drop in a comment if you'd like to know more.