Skip to content

Feature request: a sane strategy/documentation for advancedChunks #7473

@sapphi-red

Description

@sapphi-red

**_This issue was moved from **https://github.com/vitejs/vite/issues/21271_


Description

We are using rolldown with vite 8.

The main problem right now is that the default strategy is product thousands of tiny chunks.

Example: If you open https://contra.com/gajus, currently, its loading 575 chunks. That's a lot.

And most of them are tiny

Image

What we need is some sane strategy for chunk merging.

Suggested solution

I've spent a considerable amount of time trying to figure out a chunking strategy that would just work, but to no avail, but I am running into several issues:

  1. Using advancedChunks produces broken bundles due to side-effects.
    Example of an error:

     ReferenceError: Cannot access 'create' before initialization
    at computeParams (/Users/gajus/Developer/contra/gaia/node_modules/.pnpm/lib0@0.2.108/node_modules/lib0/environment.js:34:7)
    

    My understanding is that these happen because we end up including chunks with side effects.

  2. Using advancedChunks pulls unexpected dependencies into the bundle
    Let's say I have a large dependency that I know is used only in some rare parts of the app (e.g. @popperjs/core). My intuition tells me "Put that in its own separate chunk so that it is not accidentally loaded with the rest of the app". However, what actually happens is that if I set @popperjs/core as a test, then that detects that it (as an example) depends on zod (a popular dependency), and now the bundle becomes @popperjs/core + zod, and since most of the app depends on zod, it means that now we are loading this bundle everywhere, i.e., the opposite of what I wanted. I think the solution is prioritize bundling shared packages first, to avoid this, but documentation is scarce.

This is the latest attempt with logic behind every decision explained:

{
  advancedChunks: {
    // Groups with higher priority are chosen first.
    groups: [
      {
        maxSize: 500_000,
        minShareCount: 5,
        minSize: 50_000,
        name: 'shared',
        priority: 180,
        test: (id) => {
          // We want to try to bundle as much as possible application code,
          // without including any node_modules. Think: utilities, components, hooks, etc.
          if (id.includes('node_modules')) {
            return false;
          }

          // We don't want to bundle any modals because they are designed to be loaded
          // as needed.
          if (id.includes('/Modals/')) {
            return false;
          }

          return true;
        },
      },
      {
        name: 'core',
        priority: 200,
        test: (id) => {
          if (
            id.includes('/node_modules/.pnpm/vike@') ||
            id.includes('/node_modules/.pnpm/react@') ||
            id.includes('/node_modules/.pnpm/react-dom@') ||
            id.includes('/node_modules/.pnpm/relay-runtime@') ||
            // This check will cover `@sentry-internal+browser-utils@` and other packages like that
            id.includes('/node_modules/.pnpm/@sentry') ||
            id.includes('/node_modules/.pnpm/framer-motion@') ||
            id.includes('/node_modules/.pnpm/zod@') ||
            id.includes('/node_modules/.pnpm/date-fns@') ||
            id.includes('/node_modules/.pnpm/@babel+runtime') ||
            id.includes('/node_modules/.pnpm/@radix-ui') ||
            id.includes(
              '/node_modules/.pnpm/@sanity+styled-components@',
            )
          ) {
            return true;
          }

          // If you are debating of what should be included here,
          // just add `console.log('>>> module', id);` and look for
          // what you know is going to be needed for every part of the app.

          return false;
        },
      },
      {
        maxSize: 500_000,
        minShareCount: 5,
        minSize: 50_000,
        name: 'vendor',
        priority: 190,
        test: (id) => {
          // This is the inverse of 'shared' group.
          // The idea is that we want to bundle node modules into
          // chunks that are no smaller than 50KB to avoid small chunks.

          // Exclude all application code.
          if (id.includes('/contra-web-app/src/')) {
            return false;
          }

          return id.includes('/node_modules/.pnpm/');
        },
      },
    ],
  },
}

As mentioned earlier though, this produces broken bundles and it is not clear what's the way around it.

Part of this is a bug/feature request (do not group scripts with side effects) and part of this is a request to provide better documentation.

Alternative

No response

Additional context

Also, I would expect test to allow me to return group name, not just boolean, e.g. if we have modals in a directory structure /modals/A, /modals/B, etc. I would want to produce separate groups modal-a, modal-b. It is not clear how to do with the current advancedChunks feature.

Metadata

Metadata

Assignees

No one assigned

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions