Skip to content

Bundling a Vite app is creating unexpected circular dependencies #20202

Open
@kevinfarrugia

Description

@kevinfarrugia

Describe the bug

When using manualChunks to define a custom chunking strategy it is resulting in unexpected circular dependencies. In the repro provided, I am creating a separate chunk for each component and a framework chunk. This is only for demonstration purposes, but the issue is present even with a less aggressive chunking strategy.

When building, the framework chunk is dependent on a component chunk even if there are no implicit dependencies. However, the chunking moves some common code (originating from lodash/isString into the component.Foo chunk and the framework chunk then imports it.

Ideally I would like to avoid these circular dependencies from occurring without having to be able to identify each one manually. For example, in this case I would not expect the following code to be added to the component:

function getDefaultExportFromCjs(x) {
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}

I am unsure if this is a Vite or Rollup issue, so please excuse me if I opened the issue in the wrong repo.

Thank you

Reproduction

https://github.com/kevinfarrugia/vite-chunk-dependencies

Steps to reproduce

The scope of this project is to demonstrate the dependencies of a Vite project.

Installation

npm install

Usage

The dependencies of the project can be visualized using Madge. To generate a dependency graph, run:

npm run build && npx madge ./dist --json

The output will be:

{
  "assets/app-D6GzwUaY.js": [
    "assets/component.TodoForm-CZovwjkK.js",
    "assets/component.TodoList-44ZiI1-M.js",
    "assets/framework-CtvSml8s.js"
  ],
  "assets/component.Foo-D0Zme1m7.js": [
    "assets/component.Label-BySnBOvf.js",
    "assets/framework-CtvSml8s.js"
  ],
  "assets/component.Label-BySnBOvf.js": [
    "assets/framework-CtvSml8s.js"
  ],
  "assets/component.TodoForm-CZovwjkK.js": [
    "assets/component.Foo-D0Zme1m7.js",
    "assets/framework-CtvSml8s.js"
  ],
  "assets/component.TodoItem-CzBrJbZI.js": [
    "assets/component.Label-BySnBOvf.js",
    "assets/framework-CtvSml8s.js"
  ],
  "assets/component.TodoList-44ZiI1-M.js": [
    "assets/component.Label-BySnBOvf.js",
    "assets/component.TodoItem-CzBrJbZI.js",
    "assets/framework-CtvSml8s.js"
  ],
  "assets/framework-CtvSml8s.js": [
    "assets/component.Foo-D0Zme1m7.js"
  ],
  "assets/index-CWx-HeYK.js": [
    "assets/app-D6GzwUaY.js",
    "assets/component.Foo-D0Zme1m7.js",
    "assets/component.Label-BySnBOvf.js",
    "assets/component.TodoForm-CZovwjkK.js",
    "assets/component.TodoItem-CzBrJbZI.js",
    "assets/component.TodoList-44ZiI1-M.js",
    "assets/framework-CtvSml8s.js"
  ]
}

As can be seen from the preceding output, the assets/framework-CtvSml8s.js file is dependent on assets/component.Foo-D0Zme1m7.js, which is a component that is used in the assets/app-D5DJEYJu.js file. This is not desired because it creates a circular dependency. The assets/framework-CtvSml8s.js file should not depend on any components, as it is meant to be a framework file that provides utility functions and does not need to know about the components.

Looking at the contents of the framework-CtvSml8s.js file, we can see that it imports the getDefaultExportFromCjs from the component.Foo chunk:

import { g as getDefaultExportFromCjs } from "./component.Foo-D0Zme1m7.js";

This is defined as:

function getDefaultExportFromCjs(x) {
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}

This function is used within the component.Foo chunk in the following line:

const isString = /* @__PURE__ */ getDefaultExportFromCjs(isStringExports);

where isString is imported from lodash/isString. Removing the lodash/isString dependency from src/components/Foo/index.jsx will remove the circular dependency.

System Info

System:
    OS: Linux 6.11 Ubuntu 24.04.2 LTS 24.04.2 LTS (Noble Numbat)
    CPU: (24) x64 AMD Ryzen AI 9 HX 370 w/ Radeon 890M
    Memory: 43.97 GB / 62.09 GB
    Container: Yes
    Shell: 5.2.21 - /bin/bash
  Binaries:
    Node: 22.15.0 - ~/.nvm/versions/node/v22.15.0/bin/node
    npm: 10.9.2 - ~/.nvm/versions/node/v22.15.0/bin/npm
  Browsers:
    Chrome: 137.0.7151.55

Used Package Manager

npm

Logs

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions