create-react-app icon indicating copy to clipboard operation
create-react-app copied to clipboard

Disabling code splitting in CRA2

Open ghost opened this issue 7 years ago • 88 comments
trafficstars

How can I disable the code splitting in CRA 2?

ghost avatar Oct 05 '18 09:10 ghost

Can you provide more details about what you’re trying to do and why?

gaearon avatar Oct 05 '18 11:10 gaearon

Hi @gaearon, I hope I'm not hijacking this thread, but we've run into a similar question. We are mixing CRA with legacy JS. We have certain code that we have to run synchronously before the legacy JS because we have to make it available to our legacy application. In CRA1 would add code like that to src/index.js, but this seems to no longer work. I'm not sure yet if it's because it's being split out or if it's that the main bundle runs async now.

esturcke avatar Oct 08 '18 14:10 esturcke

Sorry, it’s still not quite clear to me what you mean. An example reproducing the problem would be valuable.

gaearon avatar Oct 08 '18 14:10 gaearon

I can work on a minimal reproduction case.

esturcke avatar Oct 08 '18 14:10 esturcke

Actually, I missed the part about the runtime. I'm looking at the generated index.html and I see the inlined runtime followed by

      <script src="/ui/static/js/53.4ef0ec74.chunk.js"></script>
      <script src="/ui/static/js/main.384085f8.chunk.js"></script>

But right now in our product we've only added the main.*.js script. I can work on inlining the runtime, but how would I know to include 53.*.js?

esturcke avatar Oct 08 '18 18:10 esturcke

I don't understand what "added" means. If you have a separate HTML file you create manually (instead of using our HTML output), you can construct it with information in build/asset-manifest.json which gives you a list of all scripts on the page.

gaearon avatar Oct 08 '18 18:10 gaearon

Sorry, yeah, by added I just mean that we have custom HTML with a script tag for main.*.js where we get the full name from build/asset-manifest.json. I'm not sure how to tell that I also need to include 53.*.js.

esturcke avatar Oct 08 '18 18:10 esturcke

It seems like we should put more info in asset-manifest. It should provide enough information for people to build their own HTML regardless of how many chunks there are.

gaearon avatar Oct 08 '18 18:10 gaearon

Is there any way to do this? I work on cancer.gov and we have an embedded widget made in CRA that needs to be a single file for the current build and deployment system to work (I've already been having to overrride the automated hashing with react-app-rewired).

BrendanBeltzNIH avatar Oct 09 '18 18:10 BrendanBeltzNIH

I should add that the issue is caused in part by a peculiarity of our setup. The CRA based app is hosted in a separate repo from cancer.gov as source code (to allow for it to be used on other sites as well) which requires it as a dependency directly from github (so a post-install script has to run to build the CRA app inside of cancer.gov). When we updated webpack to version 4 on cgov, the CRA v1 app stopped working (assumedly because it was being built inside of cgov's node_modules causing some issues with the conflicting webpack versions), so in trying to update to CRA2, I've managed to work around almost every issue except the code splitting. At the end of the day, on cgov, we are requiring the CRA based widget as a library, so I want to be able to output a single file or at least require the package with static filenames.

BrendanBeltzNIH avatar Oct 09 '18 19:10 BrendanBeltzNIH

asset-manifest entries like:"static/js/1.6105a37c.chunk.js": "/static/js/1.6105a37c.chunk.js" are not very useful. Could you generate something like: "main.js": ["/static/js/1.6105a37c.chunk.js","/static/js/main.bd9ad21f.chunk.js"] instead?

patope avatar Oct 10 '18 12:10 patope

Yes, it would be nice if you add an environment variable to opt out of code splitting.

I'm developing an app, which works like an embeddable widget - it doesn't render itself by default, it just adds a global object with some API, which should be used by an eventual user to render the widget to any tag he wishes to embed it. Consequently, I'm not interested in the default html output at all, I need only the final bundles of js and css files.

Formerly, CRA 1.x produced only 1 js and 1 css files and it was convenient. But now, with CRA 2.x, there are 3 js files, and in order to use the app, an eventual user should include 3 js files instead of one. Or I have to copy them all into one file (I haven't tried it yet).

Thus, I would like to have an opportunity to return to the previous behavior, namely, to have only 1 js and 1 css file as the output.

RussCoder avatar Oct 10 '18 15:10 RussCoder

+1 on the environment variable to opt out of splitting. I'm just trying to build a JS bundle for a Wordpress site, while leaving my CRA bundle in a CDN. It's not possible to update wordpress for every CRA JS update, and i ended up having to do this to load my bundle from my wordpress site:

jQuery.getJSON(cdn + '/static/asset-manifest.json', ( data ) => {
    for ( var i in Object.keys( data ) ) {
        const key = Object.keys( data )[i];

        if ( key.indexOf('.js') !== -1 && key.indexOf('.js.map') === -1 ) {
            var path = host + data[key];
            console.log('getting script', path);
            jQuery.getScript(path);
        }
    }
});

kylehotchkiss avatar Oct 10 '18 21:10 kylehotchkiss

Hey @gaearon, we have this issue as well. We need to disable the code splitting because we live inside a Rails app and for now we need to ship a single file.

I read your suggestion about using asset-manifest.json which could be viable and is what we are trying to do right now.

But I have two concerns about it.

  1. When I run the webpack-dev-server all the files are created in memory if I am not mistaking, so I can't find a way to query the assets-manifest.json file in the file system.

  2. How do I know the order in which I could include the CSS and JS. I know some of the chunks have some numbers and maybe I should add the main file at the end but it would be helpful to have documented this strategy so maybe other people could learn how to include those files.

Since this is already done with the Webpack build step when you create the final HTML file, I am assuming that there is some code somewhere in the dependencies that I could give an asnwer my second question, would be helpful if you share a link to it if you know where it lives. I am trying to find it as I am typing this.

yordis avatar Oct 10 '18 22:10 yordis

+1 for me too.

Same issue, currently phasing in React into a legacy app and the multiple JS files mess with the legacy setup. I understand wanting to educate developers as to best practices, but it would be nice if the option to opt out of code splitting was there, perhaps buried deep somewhere but there nevertheless. Thanks for the great work as always!

ghost avatar Oct 19 '18 10:10 ghost

For those of you who want to disable default chunking/code splitting in cra-2 so it bundled and ran from a single file, then you can disable caching within webpack.config by adding the following:

splitChunks: {
   // chunks: 'all',
   // name: false,
   cacheGroups: {
      default: false
   }
}

You can also disable the long term caching just below by commenting out, or setting the runtimeChunk to false.

runtimeChunk: false,

timclifford avatar Oct 19 '18 17:10 timclifford

@timclifford Did I understand correctly that you suggest us to do npm run eject first? Or create a fork of CRA? Since there is no webpack.config by default, or at least I see none.

If you suggest us to eject, it's not an option. Or, speaking differently, it's an option, of course, but not the one we want.

RussCoder avatar Oct 19 '18 17:10 RussCoder

@RussCoder I was yes, providing an option only for those who have ejected the project already. However you can always create your own custom script to hook into react-scripts before it executes to make a few custom tweaks. For example, see this post and example https://daveceddia.com/customize-create-react-app-webpack-without-ejecting/

timclifford avatar Oct 19 '18 18:10 timclifford

+1 This is also a problem for us. We have to embed our output onto a WP site, so having a single output makes a ton of sense. This sounds like a great option to add as a toggle based on an environment variable during build.

rvirani1 avatar Oct 25 '18 18:10 rvirani1

See #4632 for this also. I think the way CRA2 does things out of the box, chunking included, is great. The problem comes when we have to deploy to third party systems we don't have control over. The system I'm deploying to knows how to read the main.js entry from manifest.json and render that filename as a script tag on the bottom of a page it generates. I have no way to change that unfortunately. ...so having some way to turn chunking off (without ejecting) for these unfortunate situations would be great.

vonkanehoffen avatar Oct 26 '18 10:10 vonkanehoffen

Just thought I'd post my temporary solution to this in case it helps anyone. A bit hacky, but at least one doesn't have to eject:

const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
let config = defaults.__get__('config');

config.optimization.splitChunks = {
    cacheGroups: {
        default: false,
    },
};

config.optimization.runtimeChunk = false;

Save that as build-non-split.js or whatever and amend your package.json like:

"build": "./scripts/build-non-split.js",

vonkanehoffen avatar Oct 26 '18 14:10 vonkanehoffen

@vonkanehoffen This workaround is a lifesaver for me. THANK YOU!

jeremyckahn avatar Oct 26 '18 16:10 jeremyckahn

Also, if you're using react-app-rewired, in your config-overrides.js:

module.exports = function override(config) {
  config.optimization.runtimeChunk = false;
  config.optimization.splitChunks = {
    cacheGroups: {
      default: false
    }
  };
  return config;
};

And if you happen to require the bundle js directly for testing (usually from another project), like this:

<script src="http://localhost:3000/static/js/bundle.js"></script>

You have to include a bit more now:

<script src="http://localhost:3000/static/js/bundle.js"></script>
<script src="http://localhost:3000/static/js/0.chunk.js"></script>
<script src="http://localhost:3000/static/js/main.chunk.js"></script>

(edit: also just fyi, as of this writing, react-app-rewired doesn't play nicely with cra 2)

jd327 avatar Oct 30 '18 04:10 jd327

@vonkanehoffen THANKS

sespinosa avatar Nov 04 '18 06:11 sespinosa

@vonkanehoffen TIL you can do that...brilliant. This seems like the right amount of tinkering with the default react-scripts sources. The same can be done for the start.js as well.

joelvoss avatar Nov 06 '18 09:11 joelvoss

We have a system, where we must provide a token for JS files. Having a single main.js works fine since we load it manually. Code splitting does not work for us because we can't neither intercept webpack require calls nor intercept http request using serviceworker due to IE11 support.

With that said, we are forced to use [email protected] for that project.

miraage avatar Nov 08 '18 12:11 miraage

const rewire = require('rewire'); const defaults = rewire('react-scripts/scripts/build.js'); let config = defaults.get('config');

config.optimization.splitChunks = { cacheGroups: { default: false, }, };

config.optimization.runtimeChunk = false;

Sorry to be daft...but I'm having trouble getting this to work. It comes back with:

./scripts/build-non-split.js: line 1: syntax error near unexpected token (' ./scripts/build-non-split.js: line 1: const rewire = require('rewire');'

Any ideas as to what I am doing wrong?

peggers123 avatar Nov 21 '18 19:11 peggers123

@peggers123 Try this...

"build": "node scripts/build-non-split.js"

jw-afc avatar Nov 22 '18 08:11 jw-afc

I am having a similar issue and used @@vonkanehoffen and @jw-afc solution, but when I build the app, it still creates a main file + one chunk file. The only way I've been able to create a single main bundle is by removing the optimization section from the webpack dev config file. Is there a better way to do this without altering the config file? I seems the following options need to be overridden somehow:

splitChunks: {
  chunks: 'all',
  name: false,
},

thehme avatar Nov 28 '18 23:11 thehme

you need to add rewire etc. as a dependency @peggers123

Haroenv avatar Dec 04 '18 14:12 Haroenv