How can I disable the code splitting in CRA 2?
Can you provide more details about what you鈥檙e trying to do and why?
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.
Sorry, it鈥檚 still not quite clear to me what you mean. An example reproducing the problem would be valuable.
I can work on a minimal reproduction case.
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
?
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.
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
.
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.
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).
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.
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?
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.
+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);
}
}
});
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.
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.
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.
+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!
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 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 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/
+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.
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.
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 This workaround is a lifesaver for me. THANK YOU!
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)
@vonkanehoffen THANKS
@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.
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.
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 Try this...
"build": "node scripts/build-non-split.js"
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,
},
you need to add rewire etc. as a dependency @peggers123
For anyone looking to modify their build for both js and css: start with vonkanehoffen's answer, with these small tweaks:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// Consolidate chunk files instead
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
// Move runtime into bundle instead of separate file
config.optimization.runtimeChunk = false;
// JS
config.output.filename = 'static/js/[name].js';
// CSS. "5" is MiniCssPlugin
config.plugins[5].options.filename = 'static/css/[name].css';
This should produce unified, non-hashed filenames for those of us in CMS world (hello Drupal/Wordpress):
+1 to disable code splitting.
We ship a single main.js to third party as well.
@vonkanehoffen workaround is a lifesaver for me too. Thank you!
@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.
Do you have an example of how to do the same for start.js? I can't figure out how to do it. Thank you in advance!
@vonkanehoffen Very nice, but on my side when i'm using the unsplitted file, rxjs in redux-observable isn't working anymore for some reason.
ofType(...).mergeMap is not a function
I imported it like import 'rxjs/add/operator/mergeMap';
@vonkanehoffen Very nice, but on my side when i'm using the unsplitted file, rxjs in redux-observable isn't working anymore for some reason.
ofType(...).mergeMap is not a function
I imported it like import 'rxjs/add/operator/mergeMap';
Alright I just figured it out ...
It is an old project and I wrote my observable methods with the old syntax, I cleaned my code and it works like charm. Thanks a lot !
We took up CRA without realizing that it didn't really fit our use case. ( adding multiple in-page apps/widgets to legacy web-application ) But we loved the ease of CRA development environment and wanted to hold onto it.
Instead of ejecting/changing tools we installed webpack-cli and created our own webpack.config file using CRA's own build config as template. Basically we disabled code splitting and changed output to umd library.
Whether this is acceptable or not we have no idea but it seems to work and saved us from migration.
Would love to see create-react-library some day.
:)
For those who want to muck around with the start.js config like the build one above, this should do the trick:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/start.js');
const configFactory = defaults.__get__('configFactory');
const devConfigFactory = configFactory('development');
// Do your stuff here i.e. devConfigFactory.module.rules[x] = blah
const patchedDevConfigFactory = function() {
return devConfigFactory;
};
defaults.__set__('configFactory', patchedDevConfigFactory);
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?
Semi-related but I feel like the comment by @patope is a good suggestion and should be quicker to implement then what's suggested in this discussion. I know it won't solve the code splitting issue but it could be a workaround in the mean time. Either group the main code and vendor code together in an array as suggested or like so:
{
"main.js": "/static/js/main.2de3a0bd.chunk.js",
"vendor.js": "/static/js/1.d36c981b.chunk.js"
}
This means in whatever custom html file you have written to load the react app, then you can reference the asset-manifest.json. At least, the asset-manifest will be easy to parse for the vendor chunk rather than that vendor chunk name changing on every breaking build and you having to go replace it.
Maybe this can be it's own issue?
I would really like the ability to opt-out off code splitting as well. My use-case is that I am deploying to an IIS Server on Azure and needed to serve a custom index.cshtml
file because I needed to add nonces for CSP. So because of that, I can't easily reuse the properly generated index.html
(with all the correct path references) and had to come up with this approach...
Given an asset-manifest that looks like:
{
"main.css": "/static/css/main.e9fe7d00.chunk.css",
"main.js": "/static/js/main.2de3a0bd.chunk.js",
"main.js.map": "/static/js/main.2de3a0bd.chunk.js.map",
"static/css/1.3d819b91.chunk.css": "/static/css/1.3d819b91.chunk.css",
"static/js/1.d36c981b.chunk.js": "/static/js/1.d36c981b.chunk.js",
"static/js/1.d36c981b.chunk.js.map": "/static/js/1.d36c981b.chunk.js.map",
"runtime~main.js": "/static/js/runtime~main.229c360f.js",
"runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map",
"static/css/1.3d819b91.chunk.css.map": "/static/css/1.3d819b91.chunk.css.map",
"static/css/main.e9fe7d00.chunk.css.map": "/static/css/main.e9fe7d00.chunk.css.map",
"index.html": "/index.html",
"precache-manifest.f113fd604bb1fb9a4df372c00474496f.js": "/precache-manifest.f113fd604bb1fb9a4df372c00474496f.js",
"service-worker.js": "/service-worker.js"
}
My index.cshtml file will look so:
<!-- index.cshtml -->
@using System;
@using System.IO;
@using System.Web;
@using System.Linq;
@using Newtonsoft.Json.Linq;
@{
var seed = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
var nonceToken = Convert.ToBase64String(seed);
}
@{
var path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory.ToString(), "asset-manifest.json");
var assetManifest = JObject.Parse(File.ReadAllText(path));
var assetKeys = assetManifest.Properties().Select(p => p.Name).ToList();
var usableKeys = assetKeys.Where(key => !key.EndsWith(".map"));
var dynamicJSChunkKeys = usableKeys.Where(key => key.StartsWith("static/js") || key.StartsWith("/static/js"));
var dynamicCSSChunkKeys = usableKeys.Where(key => key.StartsWith("static/css") || key.StartsWith("/static/css"));
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="manifest.json" />
<link rel="shortcut icon" href="favicon.ico" />
<title>React App</title>
@* Load main css file *@
<link href='@assetManifest["main.css"]' rel="stylesheet" type="text/css" />
@* Load dynamic css chunk files *@
@foreach (var cssChunkKey in dynamicCSSChunkKeys)
{
<link href='@assetManifest[cssChunkKey]' rel="stylesheet" type="text/css" />
}
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
@* Load webpack runtime js file *@
<script src='@assetManifest["runtime~main.js"]' nonce="@nonceToken"></script>
@* Load main js file *@
<script src='@assetManifest["main.js"]' nonce="@nonceToken"></script>
@* Load dynamic js chunk files *@
@foreach (var jsChunkKey in dynamicJSChunkKeys)
{
<script src='@assetManifest[jsChunkKey]' nonce="@nonceToken"></script>
}
</body>
</html>
If we had the ability to opt-out of code-splitting then my approach would be similar but could be simplified to just referencing the main.js
and main.css
entry in the asset-manifest.json
:
<!-- index.cshtml -->
@using System;
@using System.IO;
@using System.Web;
@using System.Linq;
@using Newtonsoft.Json.Linq;
@{
var seed = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
var nonceToken = Convert.ToBase64String(seed);
}
@{
var path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory.ToString(), "asset-manifest.json");
var assetManifest = JObject.Parse(File.ReadAllText(path));
var assetKeys = assetManifest.Properties().Select(p => p.Name).ToList();
var usableKeys = assetKeys.Where(key => !key.EndsWith(".map"));
var dynamicJSChunkKeys = usableKeys.Where(key => key.StartsWith("static/js") || key.StartsWith("/static/js"));
var dynamicCSSChunkKeys = usableKeys.Where(key => key.StartsWith("static/css") || key.StartsWith("/static/css"));
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="manifest.json" />
<link rel="shortcut icon" href="favicon.ico" />
<title>React App</title>
@* Load main css file that includes vendor css too *@
<link href='@assetManifest["main.css"]' rel="stylesheet" type="text/css" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
@* Load webpack runtime js file *@
<script src='@assetManifest["runtime~main.js"]' nonce="@nonceToken"></script>
@* Load main js file that includes vendor js too *@
<script src='@assetManifest["main.js"]' nonce="@nonceToken"></script>
</body>
</html>
@Weffe Hi, you could try this workaround https://github.com/facebook/create-react-app/issues/5225#issuecomment-442826756
I too have similar requirement. The HTML content is served from CMS, so I need predictable output from the build process. As of now, I've disabled chunking but I'll not be do so when the application scales up.
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 5 days if no further activity occurs.
May we have an official response?
So yeah, here's how I made the filenames predictable
INLINE_RUNTIME_CHUNK=false
PUBLIC_URL=/path/
from where the content will get served in actual environment.in webpack.config.prod.js
splitChunks: {
chunks: 'all',
name: true,
},
in webpack.config.prod.js
Remove [hash:8]
and [contenthash:8]
from everywhere
that way, the build output becomes very predictable
vendors~main.chunk.js
main.chunk.js
runtime~main.js
.
.
.
main.chunk.css
etc.
and in my cms page (or html), I just need to mention
<script src="/path/runtime~main.js"></script>
<script src="/path/vendors~main.chunk.js"></script>
<script src="/path/main.chunk.js"></script>
Note: This /path/
is the same as I mentioned in .env file as PUBLIC_URL
PS:
Forgot to mention, I've changed my BrowserRouter to HashRouter too.
Could disabling the code splitting be an env variable that we could specify. That way when we build, then the build process can see that this special env variable has been set and instead of code-splitting it bundles everything into one file?
Something like:
DISABLE_CODE_SPLITTING=true
I strongly support the ability to disable code splitting for those of us in edge cases where we don't need our built files split across multiple files. Like in the cases of @esturcke, @BrendanBeltzNIH, @tirthaguha, @kylehotchkiss, @BenoKop, and myself for example.
We have been using https://github.com/harrysolovay/rescripts with create-react-app 2.*. Here's the rescripts version of @vonkanehoffen's tip above:
Install @rescripts/cli
as a devDependency.
yarn add -D @rescripts/cli
or
npm install @rescripts/cli --save-dev
start
script in package.json
from "start": "react-scripts start"
to "start": "rescripts start"
.rescriptsrc.js
file at your project root with the following contents:js
module.exports = config => {
config.optimization.runtimeChunk = false;
config.optimization.splitChunks = {
cacheGroups: {
default: false
}
};
return config;
};
Thanks @vonkanehoffen!
I seem to be the only idiot that ran into this problem so I'll put this here. doing what @vonkanehoffen said only worked for me after I solved 2 problems.
First I had a permission problem and doing chmod of 777 for the "build-non-split.js" solved that.
Next I had an error like "const: command not found", and doing:
"build": "node ./scripts/build-non-split.js",
fixed it for me.
I needed to do all this for using create-react-app with Cordova. I kept having CSP problems with all the inline javascript.
I have published a rewiring which makes it very easy to disable chunk splitting without ejecting: https://www.npmjs.com/package/react-app-rewire-disable-chunks
For anyone looking to modify their build for both js and css: start with vonkanehoffen's answer, with these small tweaks:
const rewire = require('rewire'); const defaults = rewire('react-scripts/scripts/build.js'); const config = defaults.__get__('config'); // Consolidate chunk files instead config.optimization.splitChunks = { cacheGroups: { default: false, }, }; // Move runtime into bundle instead of separate file config.optimization.runtimeChunk = false; // JS config.output.filename = 'static/js/[name].js'; // CSS. "5" is MiniCssPlugin config.plugins[5].options.filename = 'static/css/[name].css';
This should produce unified, non-hashed filenames for those of us in CMS world (hello Drupal/Wordpress):
this is exactly what I needed thank you!
For CRA2 can be used next workaround: https://github.com/facebook/create-react-app/issues/5225#issuecomment-442826756
@vonkanehoffen I used build-non-split.js
but still ended up with bunch of split JS files. Something like this:
yarn run v1.9.4
$ node ./script/build-non-split.js
Creating an optimized production build...
Compiled successfully.
File sizes after gzip:
80.3 KB build/static/js/main.713889a9.js
55.96 KB build/static/js/0.0752b6e2.chunk.js
15.82 KB build/static/js/2.85a70eff.chunk.js
14.35 KB build/static/js/4.d2d9d527.chunk.js
14.02 KB build/static/js/3.0488c5af.chunk.js
138 B build/static/css/main.329d99e1.css
The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:
Anything I did wrong?
@ywen I was having the same problem - I followed the workarounds mentioned in this thread and was able to disable chunking in the ways that everyone here has mentioned, but still ended up with multiple chunks. I was able to fix the issue by using the LimitChunkCount plugin:
https://webpack.js.org/plugins/limit-chunk-count-plugin/
I set maxChunks
to 1, and after that I was able to end up with a single main.js
file with no additional JS chunks.
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// Consolidate chunk files instead
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
// Move runtime into bundle instead of separate file
config.optimization.runtimeChunk = false;
// JS
config.output.filename = 'static/js/[name].js';
// CSS. "5" is MiniCssPlugin
config.plugins[5].options.filename = 'static/css/[name].css';
For anyone using this wonderful solution, it seems that the latest release of react-scripts
(3.1.2) broke this for the CSS, at least for me. The css no longer outputs to a consistently named main.css
.
I had to add this at the end of the script to fix it, though I think this might break things for anyone using CSS modules?
config.plugins[5].options.moduleFilename = () => 'static/css/main.css';
I'd just like to say it's a real shame there's not a built-in option to do this, and that we need dirty hacks to accomplish it. In fact, I'm really not sure why it doesn't just output a static name by default. I'm definitely not the most experienced front-end developer, but this whole code-splitting and chunking thing seems like an unnecessary optimization anti pattern that causes more problems (at least for me) than it solves. I can see how it might improve performance a bit for people building large commercial applications. But if that's the case, shouldn't it at least be opt IN, not opt OUT? And at the very least the opt OUT should be clean.
Howdy y'all,
Just wanted to say that I had the same issue as everyone, and this thread was extremely helpful. Thank you all for being clever developers, and publishing your solutions online! I hope react-scripts
includes this functionality at some point in the future.
Hello guys, I'm still searching for a viable solution using CRA/Ionic4... meanwhile I worked around the issue by preloading all the generated chunks. It works as post-build script:
package.json
"scripts": {
"build": "react-scripts build && node ./scripts/post-build.js",
scripts/post-build.js
/**
* CRA Post Build
* Preloads all the chunks found in the "assets-manifest.json"
*/
const fs = require("fs");
const builtHTMLContent = fs.readFileSync("./build/index.html").toString();
const manifest = JSON.parse(fs.readFileSync("./build/asset-manifest.json").toString());
const preload = Object.values(manifest.files)
.filter($ => {
// skip known patterns
if ($.includes('.svg')) return false;
if ($.includes('.chunk.js.map')) return false;
if ($.includes('.chunk.js.LICENSE')) return false;
if ($.includes('.chunk.css.map')) return false;
// skip entry points
const isEntry = manifest.entrypoints.some(_ => $.includes(_));
if (isEntry) return false;
// skip non-chunks
if (!$.includes('.chunk.js') && !$.includes('.chunk.css')) return false;
return true;
})
.map($ => $.includes('.js')
? `<link rel="preload" as="script" href="${$}" />`
: `<link rel="preload" as="stylesheet" href="${$} />`
)
.join('');
const lazyPreload = `
<script>
window.__preloadChunks__ = () => {
const preload = document.createElement('span');
preload.innerHTML = '${preload}';
document.body.appendChild(preload);
};
setTimeout(window.__preloadChunks__, 50);
</script>`;
fs.writeFileSync("./build/index.html", builtHTMLContent.replace('</body>', `${lazyPreload}</body>`));
I found the only way to remove hashes from the CSS (using config-overrides
with react-app-rewired) was to replace the MiniCssExtractPlugin
entirely:
config.plugins = config.plugins.map(plugin => {
return (plugin instanceof MiniCssExtractPlugin) ?
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].css',
chunkFilename: 'static/css/[name].chunk.css'
})
: plugin;
});
(Note: you also need to import the plugin with const MiniCssExtractPlugin = require('mini-css-extract-plugin');
)
My current solution FWIW is to leave the configuration untouched, and then actually run a node script that reads and parses build/index.html
to get all link
/script
/style
in both the <head>
and the <body>
so that I can output them elsewhere.
Building on build-non-split.js
, this removes the MiniCssExtractPlugin so that there's only one output artefact with embedded CSS (main.js
).
// npm install rewire
const rewire = require('rewire');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// Consolidate chunk files instead
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
// Move runtime into bundle instead of separate file
config.optimization.runtimeChunk = false;
// JS
config.output.filename = 'static/js/[name].js';
// CSS remove MiniCssPlugin
config.plugins = config.plugins.filter(plugin =>
!(plugin instanceof MiniCssExtractPlugin));
// CSS replaces all MiniCssExtractPlugin.loader with style-loader
config.module.rules[2].oneOf = config.module.rules[2].oneOf.map(rule => {
if (!rule.hasOwnProperty('use')) return rule;
return Object.assign({}, rule, {
use: rule.use.map(options => /mini-css-extract-plugin/.test(options.loader)
? {loader: require.resolve('style-loader'), options: {}}
: options)
});
});
https://gist.github.com/phdesign/3fd306db2bc53f6368e6f0f73bbeff19
My current solution FWIW is to leave the configuration untouched, and then actually run a node script that reads and parses
build/index.html
to get alllink
/script
/style
in both the<head>
and the<body>
so that I can output them elsewhere.
This is an interesting idea I hadn't thought of. Under normal circumstances I'd consider it hacky of course. But that actually might end up being cleaner and shorter than this build-non-split.js
+ rewire
install that seems to slowly be getting bigger and also requires extra dependencies. Thanks!
https://webpack.js.org/api/module-methods/#magic-comments
This solve my problem without rewire and other solutions
const myComponent = lazy(() => import(
/* webpackMode: "eager" */
`./pages/${_}/index`
))
Using craco I was able to create a single bundle:
// craco.config.js
module.exports = {
webpack: {
configure: {
optimization: {
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false
},
},
},
},
},
}
Improving on @learnitmyway's solution using Craco, this is the config to also avoid using hashnames for both the JS and CSS:
// craco.config.js
module.exports = {
webpack: {
configure: {
output: {
filename: 'static/js/[name].js'
},
optimization: {
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false
},
},
},
},
},
plugins: [
{
plugin: {
overrideWebpackConfig: ({ webpackConfig }) => {
webpackConfig.plugins[5].options.filename = 'static/css/[name].css';
return webpackConfig;
},
},
options: {}
}
],
}
One litte comment to CRACO solution by @leoloso if you want to change MiniCssExtractPlugin settings, or any other webpack plugin inside of craco.config.js
overrideWebpackConfig: ({ webpackConfig }) => {
// find the plugin
let mcep;
webpackConfig.plugins.some(p => {
if (p.constructor.name === 'MiniCssExtractPlugin') {
mcep = p;
return true;
}
});
if (mcep) {
// change settings
mcep.options.filename = 'static/css/[name].css';
}
return webpackConfig;
}
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;
Not working for me!
`
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;
// JS
config.output.filename = 'static/js/[bundle].js';
`
"build": "node ./scripts/build-non-split.js",
Most helpful comment
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:
Save that as build-non-split.js or whatever and amend your package.json like: