New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can code splitting be done according to chunk size? #4327
Comments
I have tried to accumulate chunk size in manualChunks, and return a new name when the threshold is exceeded. But the final chunk result did not meet expectations, and most of the chunk content was severely reduced because it was processed by tree shaking. |
Similar issue. Described in https://stackoverflow.com/q/73161429/368691
@lukastaegert any advice around how to improve this? Example website https://contra.com/gajus |
CC @brillout |
On that website it's 175 chunks. The one's I inspected are all small. It seems that something is wrong with the code splitting heuristic. |
The problem is not simple. In order to reduce chunk size, Rollup would need to run modules that are not needed yet, so we would need to extend the chunking with a logic to determine when it is "safe" to do that, i.e. running the module has not side effects. Which is entirely possible, of course. Another point is to determine the chunk size contribution of a module. To do that, we need to do something like a "trial rendering" of the module, + some fake minification, which would be costly. Still something we could do, it just amounts to a lot of work which is why I put this lower on my agenda. But I am aware of the problem. For dynamic imports, Rollup already has an optimization for the reduction of the number of chunks, which is to take into account that one of the dynamic importers must already be in memory before the imported module is run. So any code shared between the importer and the imported module is merged into the chunk of the importer (if it is a single one, or into a chunk where the dependencies shared between all importers are placed). If you know a static entry is already in memory when another entry is loaded, you can encode the same relationship via |
Apart from using For instance, I don't understand why Rollup is trying to put each Icon component in a separate chunk (which ends up just a few bytes), when they are imported like regular modules across the application. Here is the full build log: https://gist.github.com/gajus/0149233592085181331dde7076fc50b1 This is example website: https://contra.com/p/gkOQlbLq-validating-postgre-sql-results-and-inferring-query-static-types This is out Vite configuration: import path from 'path';
import { default as react } from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { default as ssr } from 'vite-plugin-ssr/plugin';
const { VITE_BASE_URL } = process.env;
export default defineConfig(({ ssrBuild }) => {
let build;
if (!ssrBuild) {
build = {
emptyOutDir: true,
manifest: true,
minify: true,
polyfillModulePreload: false,
rollupOptions: {
output: {
chunkFileNames: 'chunk-[name].[hash].js',
entryFileNames: 'entry-[name].[hash].js',
inlineDynamicImports: false,
sourcemap: true,
},
},
sourcemap: true,
};
}
return {
base: VITE_BASE_URL ?? '/static/',
build,
plugins: [
react({
babel: {
plugins: [
'babel-plugin-relay',
[
'babel-plugin-styled-components',
{
displayName: true,
fileName: false,
pure: true,
ssr: true,
},
],
],
},
}),
ssr(),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src/'),
},
},
};
}); If you try to inspect this page using Google tools, it doesn't even load – presumably because of 200 JavaScript chunks that that page needs to load. https://search.google.com/test/mobile-friendly/result?id=ULZhtOfQfp1Gxj2wYhyCUw For context, I am familiar with manualChunks: (id: string) => {
// Some IDs are not file paths, e.g.
// 'virtual:vite-plugin-ssr:pageFiles:client:client-routing'
if (!id.includes(__dirname)) {
return undefined;
}
// Rollup ID starts with a weird zero-width character that I was not able
// to remove using trim() or regex.
const pathStartIndex = id.indexOf('/');
// Rollup IDs might include search parameters, e.g.
// 'node_modules/react/cjs/react.production.min.js?commonjs-exports'
const filePath = path.relative(
__dirname,
id.slice(pathStartIndex).split('?')[0]
);
if (filePath.startsWith('src/components/Icons/')) {
return 'icons';
}
if (filePath.startsWith('src/hooks/')) {
return 'hooks';
}
if (filePath.startsWith('node_modules/')) {
return 'vendor';
}
return undefined;
}, However, attempting to modify chunks produces an error runtime:
Even putting everything into a single chunk breaks parts of the website (because of side effects): manualChunks: () => {
return 'everything';
}, Really not sure how to even attempt to tackle this problem. |
Neither have I because this is packed with Vite plugins and no, we are not the maintainers of Vite and I am not familiar with most of this. |
Can you think of a configuration that would improve the odds of rollup working at least with a single chunk? At this point, I would rather just have the entire app in a single chunk than what is happening now. But it looks like having it all in a single chunk causes out of order execution, e.g.
|
Made a little progress. You can make single chunk strategy work if you remove all dynamic imports, i.e. Had to refactor out all instances of: const Inquiry = lazy(async () => {
const module = await import('persona');
return {
default: module.Inquiry,
};
}); At this point, a single chunk is better than 200+ chunks that are loaded on every page. |
Vite claims that they use Rollup's default chunking (vitejs/vite#9565 (comment)). Will try to replicate this issue with a vanilla rollup config early next week. We're also disturbed by this chunking strategy and using Vite. Would really love to group some chunks by defining a minimum chunk size, as atm we're seeing microbial chunks (some even down to ~60 bytes 🙄) Would there be potential to create something similar to Webpack's |
Also probably related: #4547 (in this case there are even a lot of empty chunks). |
Too many small chunks kill website usability. Google literally stopped indexing our website when we had 300 small chunks loading. A few very large chunks kill compile and evaluation time. We made a ton of optimizations to improve page loading time of https://contra.com/gajus. But a single large chunk strategy is severely penalizing our critical rendering path. Will donate USD 1,000 to any individual if they deliver improvements that achieve controlled chunk sizes (offer stands until the end of year). Ideally as contribution to rollup, but could be done as a rollup plugin too. |
So some update from me:
But all of this is doable. However, work on this is currently very much blocked by the 3.0 release that fundamentally changes the larger topology of how rendering is done in Rollup. Once this is released, I can dedicate effort to implement this. This could very much happen this year (I hope to pull of the release in September). (If we pull that off and the result makes you happy, we would also be happy to receive any donations in response...) |
That's great to hear 💚. Thanks for the answer.
Ok that probably explains #4547 then (because the mentioned Vite app uses a Vite transformer that prunes all server-side code). Note that with upcoming techniques like React Server Components, such transformers that heavily prune server-side code will start to become mainstream. So I think running the chunking algorithm at the end (or at least after Vite transformers) will become crucial. If I were to completely disable code splitting (while still enabling bundling), all I would be left with At that point, it's all about 1. determining common modules between bundles, and 2. determining if it's worth it to extract the common parts into chunks. I believe the core of the problem is about 2. that tiny chunks are being considered "worth it". E.g. gajus's app has 200+ chunks but cearly doesn't have 200+ input entries / dynamic imports. But reading your reply, it seems that code splitting works completley differently. |
Ah, actually, I'm just realizing it's not that easy because modules are not allowed to be imported twice. This means chunking is not only an optimization but also a correctness thing. I'm starting to see why it's complex. |
It is still run after all |
Does Rollup v3 in any way help with this issue? |
I have now time to pursue this issue again, as I will. |
@gajus FYI Lukas mentioned "minimal chunk size" in his Vite talk. |
hi @lukastaegert do you have a vision of which API/functionality will be added to address discussed issue? |
Not sure yet. But I will start with simple first, and I am already sure there will be no "optimal" size. |
we would be also interested in the solution, hoping for the best ;) |
Here is finally something to play around with: #4705 |
This issue has been resolved via #4705 as part of rollup@3.3.0. You can test it via |
Will DM Lukas re donation first thing Monday morning. Thank you |
@lukastaegert I realize this is experimental, but it seems to not produce the desired result. I've set
I would not expect any of those under 1KB chunks. The full configuration is:
|
The first question is of course: Are you using the Vite 4 alpha, or a Vite branch that supports Rollup 3, or are otherwise sure you are running Rollup 3? But if you build does not give you a warning "Unknown output options: experimentalMinChunkSize", then you are probably fine. Then the most likely cause is that Rollup is not sure that the small chunks are free of side effects. If a chunk contains a side effect, you cannot safely merge it into another chunk as the side effect may be triggered at the wrong time. It may be interesting to see what is inside those small chunks to figure out if this really is the problem. Maybe there is also some low-hanging fruit regard side effect detection. It would also be interesting to see if the flag has any effect at all:
|
Yes, using Vite 4 alpha and forcing resolution to Rollup version 3.3.0 version. For what it is worth, this option did merge some chunks (total number of chunks reduced from 850+ to ~600). Here is one of the chunks ( import{aM as i,ap as s,aq as c}from"./chunk-NewEditor.bfa24576.js";import{a as r,j as t}from"./chunk-index.fa6d413f.js";import{S as l}from"./chunk-ContraPayments.f68ea1eb.js";const d=e=>r("svg",{width:"1em",height:"1em",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",role:"img",...e,children:[t("path",{d:"m11.095 1.183 8.584 7.821a.975.975 0 0 1 .254 1.08.975.975 0 0 1-.915.626h-1.371v7.836c0 .311-.252.563-.563.563H3.785a.563.563 0 0 1-.562-.563V10.71H1.852a.975.975 0 0 1-.915-.626.975.975 0 0 1 .254-1.08l8.583-7.82a.977.977 0 0 1 1.322 0Z",fill:"url(#green-bank_svg__a)"}),t("path",{d:"M11.11 6.055 9.76 15.967M12.688 7.857h-3.38a1.577 1.577 0 1 0 0 3.154h2.253a1.577 1.577 0 0 1 0 3.154h-3.83",stroke:"#F7F7F7",strokeLinecap:"round",strokeLinejoin:"round"}),t("defs",{children:r("linearGradient",{id:"green-bank_svg__a",x1:10.435,y1:.926,x2:10.435,y2:19.109,gradientUnits:"userSpaceOnUse",children:[t("stop",{stopColor:"#C3DC2B"}),t("stop",{offset:1,stopColor:"#8CC83D"})]})})]}),I=e=>e?i({firstName:e.firstName,lastName:e.lastName},"fullName"):"Deleted user",o=Object.freeze({lg:32,sm:18}),m=Object.freeze({lg:16,sm:8}),g=s.div`
${({theme:e,size:a})=>c`
background: ${e.colors.gray10};
padding: ${m[a]}px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
`}
`,N=({size:e="sm",icon:a})=>t(g,{size:e,children:t(a||d,{height:o[e],width:o[e]})}),n=Object.freeze({lg:32,sm:18}),h=Object.freeze({lg:16,sm:8}),p=s.div`
${({theme:e,size:a})=>c`
background: ${e.colors.gray10};
padding: ${h[a]}px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
`}
`,b=({size:e="sm"})=>t(p,{size:e,children:t(l,{height:n[e],width:n[e]})});export{b as C,d as G,N as P,I as g}; and the actual code: import { SocialUSDCSolidIcon } from '@contra/ui-kit';
import styled, { css } from 'styled-components';
type PayoutAvatarSize = 'lg' | 'sm';
type PayoutAvatarIconProps = {
size?: PayoutAvatarSize;
};
const ICON_SIZE: Record<PayoutAvatarSize, number> = Object.freeze({
lg: 32,
sm: 18,
});
const ICON_PADDING: Record<PayoutAvatarSize, number> = Object.freeze({
lg: 16,
sm: 8,
});
const IconCircleFrame = styled.div<{ size: PayoutAvatarSize }>`
${({ theme, size }) => css`
background: ${theme.colors.gray10};
padding: ${ICON_PADDING[size]}px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
`}
`;
export const CryptoPayoutAvatarIcon = ({
size = 'sm',
}: PayoutAvatarIconProps) => {
return (
<IconCircleFrame size={size}>
<SocialUSDCSolidIcon height={ICON_SIZE[size]} width={ICON_SIZE[size]} />
</IconCircleFrame>
);
}; I assume |
Yes, If const wrappedStyledDiv = (...args) => /*#__PURE__*/styled.div.apply(styled, args) But of course it is not nice, and you still need to "fix" the |
Let me know what are the next steps here – esp. how I can support the effort. |
Yes, I do have something for you to try: #4718 Basically, using that branch/rollup@beta, try adding
and see what the effect is. I also have some idea how to improve the basic algorithm even when not using this, but it will take another couple of days to implement. |
As far as I can tell, the particular chunk that we were looking at ( We are now down to 377 chunks, which is a huge improvement from where we started. This is my Rollup configuration: {
output: {
chunkFileNames: 'chunk-[name].[hash].js',
entryFileNames: 'entry-[name].[hash].js',
experimentalMinChunkSize: 500_000,
inlineDynamicImports: false,
},
treeshake: {
manualPureFunctions: ['styled', 'styled.div', 'Object.freeze'],
},
}, (I assume that However, we still have a couple hundred small chunks:
Looking at the contents of these files:
and output: import { N as styled, aI as Text } from "./chunk-projectCover.b01513ef.js";
import { j as jsx } from "./chunk-index.f1e29f4b.js";
const StyledErrorMessage = styled(Text)`
color: var(--color-uiErrorRegular);
`;
const InputFieldErrorMessage = ({
children,
...props
}) => /* @__PURE__ */ jsx(StyledErrorMessage, {
as: "span",
textStyle: "captionSmall",
...props,
children
});
export {
InputFieldErrorMessage as I
}; My guess is that the outstanding problem is As you can see from my config, I tried adding Is this misconfiguration on my part? |
I see, the difference here is that you are basically calling the return value of
That means you can just write I am still not done with the topic though, as I have another optimization to the minChunkSize algorithm I want to add + improvements for |
Updating to version v3.4.0-1 reduced chunks to 366, down from 377. This is the current configuration: manualPureFunctions: [
'styled',
'Object.freeze',
'forwardRef',
'css',
'createContext',
], Chunks:
Example chunk input (RecentTransactions.tsx): /* eslint-disable relay/unused-fields */
import { useFragment } from 'react-relay';
import { graphql } from 'relay-runtime';
import styled, { css } from 'styled-components';
import { type RecentTransactions_userAccount$key } from '@/__generated__/RecentTransactions_userAccount.graphql';
import { Button } from '@/components/Buttons/index';
import { Link } from '@/components/Link/index';
import { Text } from '@/components/Primitives/index';
import { RecentTransactionSummary } from '@/features/wallet/index';
import { useUserTypeSelector } from '@/hooks/index';
import { type StubRouteHelpers } from '@/utilities/routeHelpers';
const Container = styled.div`
${({ theme }) => css`
background-color: ${theme.colors.white30};
padding: 24px;
border-radius: 20px;
width: 100%;
> * {
width: 100%;
margin: 0;
}
${theme.mediaQueries.lg} {
padding: 32px;
}
`}
`;
type RecentTransactionsProps = {
nodeRef: RecentTransactions_userAccount$key;
};
export const RecentTransactions = ({ nodeRef }: RecentTransactionsProps) => {
const data = useFragment<RecentTransactions_userAccount$key>(
graphql`
fragment RecentTransactions_userAccount on UserAccount {
id
transactionSummary: transactionHistory(first: 3) {
edges {
node {
id
}
}
}
...RecentTransactionSummaryFragment
}
`,
nodeRef
);
const {
routeHelpers: { selectedUserTypeRouteBase },
} = useUserTypeSelector();
// const renderEmptyState = true;
const renderEmptyState =
data.transactionSummary.edges.map((edge) => edge?.node.id).filter(Boolean)
.length === 0;
return (
<Container>
{renderEmptyState ? (
<>
<Text textStyle="subtitleRegular">Recent Transactions</Text>
<Text
color="--color-ui-black--medium-emphasis"
pb={24}
textStyle="captionRegular"
>
You currently don't have any transactions.
</Text>
</>
) : (
<RecentTransactionSummary stripeAccountRef={data} />
)}
<Button
as={Link}
removeMinWidth
to={(routes: StubRouteHelpers) =>
routes.wallet(selectedUserTypeRouteBase)
}
variant="secondary"
>
View Transactions
</Button>
</Container>
);
}; Output: import { b as reactRelay, a as jsxs, F as Fragment, j as jsx } from "./chunk-index.f1e29f4b.js";
import { aT as useUserTypeSelector, aB as Text, aF as Button, aN as Link, N as styled, O as Ce } from "./chunk-constants.edbcb661.js";
import { R as RecentTransactionSummary } from "./chunk-WorkSectionWalletSidebar.640d4e81.js";
import "./chunk-ActiveContraCard.styles.2614e9c7.js";
import "./chunk-prefetch.95008803.js";
const node = function() {
var v0 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "RecentTransactions_userAccount",
"selections": [
v0,
{
"alias": "transactionSummary",
"args": [
{
"kind": "Literal",
"name": "first",
"value": 3
}
],
"concreteType": "MoneyTransactionHistoryConnection",
"kind": "LinkedField",
"name": "transactionHistory",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "MoneyTransactionHistoryEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
v0
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "transactionHistory(first:3)"
},
{
"args": null,
"kind": "FragmentSpread",
"name": "RecentTransactionSummaryFragment"
}
],
"type": "UserAccount",
"abstractKey": null
};
}();
node.hash = "52b49181421748f65a239e1f9c82f260";
const Container = styled.div`
${({
theme
}) => Ce`
background-color: ${theme.colors.white30};
padding: 24px;
border-radius: 20px;
width: 100%;
> * {
width: 100%;
margin: 0;
}
${theme.mediaQueries.lg} {
padding: 32px;
}
`}
`;
const RecentTransactions = ({
nodeRef
}) => {
const data = reactRelay.exports.useFragment(node, nodeRef);
const {
routeHelpers: {
selectedUserTypeRouteBase
}
} = useUserTypeSelector();
const renderEmptyState = data.transactionSummary.edges.map((edge) => edge == null ? void 0 : edge.node.id).filter(Boolean).length === 0;
return /* @__PURE__ */ jsxs(Container, {
children: [renderEmptyState ? /* @__PURE__ */ jsxs(Fragment, {
children: [/* @__PURE__ */ jsx(Text, {
textStyle: "subtitleRegular",
children: "Recent Transactions"
}), /* @__PURE__ */ jsx(Text, {
color: "--color-ui-black--medium-emphasis",
pb: 24,
textStyle: "captionRegular",
children: "You currently don't have any transactions."
})]
}) : /* @__PURE__ */ jsx(RecentTransactionSummary, {
stripeAccountRef: data
}), /* @__PURE__ */ jsx(Button, {
as: Link,
removeMinWidth: true,
to: (routes) => routes.wallet(selectedUserTypeRouteBase),
variant: "secondary",
children: "View Transactions"
})]
});
};
export {
RecentTransactions as R
}; |
No, I checked the file locally and it is working. I think we are hitting another limit: Rollup does not find a reasonable match for that file to merge with. I have some ideas how to improve the base merging algorithm further, I will let you know for the next iteration. Maybe I can also add some logging then to give you more of an idea what is behind this. |
Are these the logs you were looking for? I cannot seem to see other logs. Output of import { r as reactRelay } from "./chunk-index.0055ed09.js";
import { T as Text, B as Button, s as styled, C as Ce } from "./chunk-ErrorBoundary.380bfeab.js";
import { a as jsxs, F as Fragment, j as jsx, L as Link } from "./entry-src/pages/index.page.client.4d2d9d66.js";
import { R as RecentTransactionSummary } from "./chunk-RecentTransactionSummary.37584201.js";
import "./chunk-ContraCard.20e563c2.js";
import "./chunk-Tooltip.c8dd331a.js";
import "./entry-src/pages/discover-talent.page.client.673bb672.js";
import { u as useUserTypeSelector } from "./entry-src/pages/account-deleted.page.client.95433318.js";
const node = function() {
var v0 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "RecentTransactions_userAccount",
"selections": [
v0,
{
"alias": "transactionSummary",
"args": [
{
"kind": "Literal",
"name": "first",
"value": 3
}
],
"concreteType": "MoneyTransactionHistoryConnection",
"kind": "LinkedField",
"name": "transactionHistory",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "MoneyTransactionHistoryEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
v0
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "transactionHistory(first:3)"
},
{
"args": null,
"kind": "FragmentSpread",
"name": "RecentTransactionSummaryFragment"
}
],
"type": "UserAccount",
"abstractKey": null
};
}();
node.hash = "52b49181421748f65a239e1f9c82f260";
const Container = styled.div`
${({
theme
}) => Ce`
background-color: ${theme.colors.white30};
padding: 24px;
border-radius: 20px;
width: 100%;
> * {
width: 100%;
margin: 0;
}
${theme.mediaQueries.lg} {
padding: 32px;
}
`}
`;
const RecentTransactions = ({
nodeRef
}) => {
const data = reactRelay.exports.useFragment(node, nodeRef);
const {
routeHelpers: {
selectedUserTypeRouteBase
}
} = useUserTypeSelector();
const renderEmptyState = data.transactionSummary.edges.map((edge) => edge == null ? void 0 : edge.node.id).filter(Boolean).length === 0;
return /* @__PURE__ */ jsxs(Container, {
children: [renderEmptyState ? /* @__PURE__ */ jsxs(Fragment, {
children: [/* @__PURE__ */ jsx(Text, {
textStyle: "subtitleRegular",
children: "Recent Transactions"
}), /* @__PURE__ */ jsx(Text, {
color: "--color-ui-black--medium-emphasis",
pb: 24,
textStyle: "captionRegular",
children: "You currently don't have any transactions."
})]
}) : /* @__PURE__ */ jsx(RecentTransactionSummary, {
stripeAccountRef: data
}), /* @__PURE__ */ jsx(Button, {
as: Link,
removeMinWidth: true,
to: (routes) => routes.wallet(selectedUserTypeRouteBase),
variant: "secondary",
children: "View Transactions"
})]
});
};
export {
RecentTransactions as R
}; Smallest chunks:
|
Exactly. And these are actually quite interesting. In the first pass, it tried to "grow" the 177 small side effect chunks by merging in suitable chunks from the 262 pure chunks.
Now this looks like nothing was merged, but this is far from true. It just means that after this phase, there were still 177 side effect chunks below the size limit. The next line indeed tells us how much was merged
The 11 is actually what remained from the 262 pure chunks we originally had. So in the first phase, the 177 chunks with side effects were grown by merging in 251 small pure chunks.
That is what I would have expected considering pure chunks can be merged arbitrarily. Basically, all remaining pure chunks were merged into one catch-all chunk. This also tells us what needs to be done to further reduce the number of chunks: Get rid of more side effects. Because in the end, it is the 177 chunks with side effects that make up the majority of the remaining chunks, and those cannot be merged with each other. From the example chunk you are giving me it still looks like it could only be the I think in the end it is actually I would actually add
as the Would be interested to hear how these settings affect your outcome. |
As a quick update, I am debugging issues with our build at the moment. It seems some chunks are being loaded out of order and result in errors like:
If I disable |
Ah I see. It could be we are introducing circular references with some of the merging that were not present before. Will need to think about this. |
Just circling back to confirm that it seems most probable that this is a Rollup issue. I stripped out anything that I thought may have be causing this, but at the end, it all comes down to the presence of |
@lukastaegert Do the latest releases include any related fixes? |
Not yet, I took a slight detour to hopefully improve upon #4732, but I will go back to minChunkSize now. |
Ok, I improved #4723 to prevent merges if the merge would create a cyclic dependency. Would be nice if you could run it again against your code base and report the results (number of chunks, chunk sizes, console output). |
@lukastaegert using rollup via vite 4.0 - can i log the rollout output so i can see what it is building ? vite info flag did not work |
Write a minimal plugin like this: ({
name: 'log-build',
generateBundle(options, bundle) {
console.log(bundle); // or something a little more refined
}
}); |
I guess that the questioner seems to need a max Chunk size, rather than min chunk? Is there a convenient option like 'splitChunks.maxSize' in webpack? |
I have a vue + vite 5 applicaiton (rollup@4.5.0).
So I tried with : rollupOptions: {
output: {
experimentalMinChunkSize: Infinity
}
} And nothing changes, chunks have same size and I get this output :
|
Currently, this algorithm performs very, very poorly if you have multiple dynamic imports.
Additionally, And, as I said, when it works at all. I currently have around 80 chunks under 2kB that will never be combined and only exist as separate files because multiple dynamic imports import them. All of these would be candidates for either inlining or combining into a common chunk. None of them are dynamic, none of them have side effects. Here's an example of one such file (unminified), a pretty generic sort of function that just acts as a getter to determine whether a component is mounted. It's referenced in 88 places in this codebase and could easily be in a common chunk: import { t as reactExports } from "./index-4761524d.js";
const useMounted = () => {
const mountedRef = reactExports.useRef(true);
reactExports.useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
return () => mountedRef.current;
};
export {
useMounted as u
}; This file minified is 0.25 kB. It has no business sitting on its own. Hopefully it's obvious that this doesn't have any side effects. The rest of the files are similar. A few things should to happen for this to be viable for production code:
I've tried everything reasonable to fix this issue, but I think my only option at this point is to use Webpack for production builds. This is obviously very painful, because we use Vite for development, since it's so fast. Having an entirely different build path is sort of crazy, but I don't see any other way. |
The original requirement of this issue is about |
Similar to the maxSize configuration of webpack, can you split different chunks according to the chunk size in manualChunks? For example, the final total size is 500 KB, can it be automatically split into vendor1.js, vendor2.js, vendor3.js, and the size of each vendor does not exceed 200KB?
The text was updated successfully, but these errors were encountered: