Skip to content

Support group-local includeDependenciesRecursively for route fanout chunking #9467

@aminpaks

Description

@aminpaks

Summary

Proposal: allow includeDependenciesRecursively on individual output.codeSplitting.groups.

Repro: https://github.com/aminpaks/rolldown-route-fanout-repro

Problem

Large client apps often load routes like this:

entry -> router -> app context -> selected route loader -> Promise.all(route parts)

After the selected route is known, frameworks want to preload only that route's closure:

alpha navigation -> route-alpha + alpha parts + shared

They should not preload sibling route closures:

not route-beta / route-gamma

Rollup can express this with manualChunks: route chunks stay route-owned, shared code stays shared, and the selected-route closure stays clean.

Rolldown codeSplitting.groups captures matched modules' dependencies recursively by default. For route groups, that can make the selected-route boundary too broad unless the whole output opts out of recursive dependency capture.

Current workaround

This works in the repro:

output: {
  preserveEntrySignatures: 'allow-extension',
  strictExecutionOrder: true,
  codeSplitting: {
    includeDependenciesRecursively: false,
    groups: [
      {name: 'route-alpha', test: /routes\/alpha\//},
      {name: 'route-beta', test: /routes\/beta\//},
      {name: 'route-gamma', test: /routes\/gamma\//},
      {name: 'shared', test: /shared\//},
    ],
  },
}

Result:

Rolldown alpha closure:
  - rolldown-runtime
  - route-alpha
  - shared

Rollup alpha closure:
  - route-alpha
  - shared

siblingRouteChunks: 0

The issue is that includeDependenciesRecursively: false is global. In a larger app, route groups may need non-recursive capture, while shared/vendor/framework groups may still rely on recursive capture. Using the global switch can scatter unrelated groups and increase initial script fanout.

Proposed API

Let groups override the global/default behavior:

output: {
  codeSplitting: {
    includeDependenciesRecursively: true,
    groups: [
      {
        name: 'route-alpha',
        test: /routes\/alpha\//,
        includeDependenciesRecursively: false,
      },
      {
        name: 'route-beta',
        test: /routes\/beta\//,
        includeDependenciesRecursively: false,
      },
      {
        name: 'shared',
        test: /shared\//,
        // inherits global/default behavior
      },
    ],
  },
}

Expected behavior:

  • omitted on group: inherit top-level codeSplitting.includeDependenciesRecursively;
  • set on group: only that group's recursive capture changes;
  • route groups can preserve preload boundaries without changing unrelated chunk groups.

Repro steps

git clone https://github.com/aminpaks/rolldown-route-fanout-repro.git
cd rolldown-route-fanout-repro
pnpm install
pnpm run build

The analyzer prints Rollup vs Rolldown output shape and fails if the selected route closure contains sibling route chunks.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions