vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.4k stars 6.17k forks source link

Using manualChunks breaks code-splitting #12209

Open jaskp opened 1 year ago

jaskp commented 1 year ago

Describe the bug

I'm migrating from webpack (CRA) and have come across an issue where it seems a manual chunk with a react charting library is loaded before a manual vendor chunk with react.

Uncaught TypeError: Cannot read properties of undefined (reading 'Component')
    at charts-f22f3dea.js:682:13176
    at charts-f22f3dea.js:682:14979

Reproduction

https://github.com/jaskp/mre-vite-rollup-chunks

Steps to reproduce

See README.md

System Info

System:
    OS: macOS 13.0.1
    CPU: (8) arm64 Apple M1
    Memory: 53.06 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.11.0 - ~/.nvm/versions/node/v18.11.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.11.0/bin/yarn
    npm: 8.19.2 - ~/.nvm/versions/node/v18.11.0/bin/npm
    Watchman: 2023.01.23.00 - /opt/homebrew/bin/watchman
  Browsers:
    Edge: 110.0.1587.50
    Firefox: 108.0.1
    Safari: 16.1
  npmPackages:
    @vitejs/plugin-react: ^3.1.0 => 3.1.0 
    vite: ^4.1.0 => 4.1.4

Used Package Manager

yarn

Logs

No response

Validations

Tanimodori commented 1 year ago

Your manualChunks configuration is producing circular dependencies.

charts-49173cde.js

import { r as reactExports, p as propTypesExports } from "./vendor-a1c995d1.js";
function getDefaultExportFromCjs(x) {
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
// ...omitted
var _react = reactExports
var Charts = function() {
  // ...omitted
  return _inherits(r, _react.Component) // ...
}
// ...omitted

vendor-a1c995d1.js

import { g as getDefaultExportFromCjs } from "./charts-49173cde.js";
// ...omitted
var reactExports = {};
var react = {
  get exports() {
    return reactExports;
  },
  set exports(v2) {
    reactExports = v2;
  }
};
// ...omitted

Manually splitting commonjsHelpers provide a successful build for me.

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  build: {
    minify: false,
    rollupOptions: {
      output: {
        manualChunks: (id) => {
+         if (id.includes('commonjsHelpers')) return 'commonjsHelpers'
          if (id.includes('apexcharts')) return 'charts'
          if (id.includes('node_modules')) return 'vendor'
        },
      },
    },
  }
})
mkilpatrick commented 1 year ago

I'm not sure if my issue is the same (below), but adding the manualChunks for commonjsHelpers fixed it for me.

Error: Could not import /Users/foo/Desktop/rp-test/dist/assets/server/static.71e779cd.js TypeError [ERR_INVALID_ARG_VALUE]: The argument 'path' must be a string or Uint8Array without null bytes. Received '/Users/foo/Desktop/rp-test/dist/assets/static/\x00commonjsHelpers-88401c09.js'

Notice the \x00 prefix. On Vite 4.4.3.

sdvcrx commented 1 year ago

Rollup related issue: https://github.com/rollup/plugins/issues/591

spaceemotion commented 1 year ago

I am getting similar errors even when I just add the vendor chunk plugin as described by the docs: https://vitejs.dev/guide/build.html#chunking-strategy

Regardless of which strategy I use (just the plugin, custom manualChunks method, or both at the same time), my sites won't load and error out with TypeErrors due to missing dependencies.

ybf970928 commented 1 year ago

exposed to the same problem,I don't know why it automatically introduces unloaded files

image
ranedk commented 8 months ago

Is this closed? Any links to further discussions on this? I am breaking my head for the last 24 hours to figure out this.

I shall try to explain the gigaloop: 1) Nuxt changed hash on every build, irrespective of a change in the code, for all files. Means that caching is bad and almost unusable for PWA. My application is a PWA. 2) Turns out, you have to configure buildIdin nuxt config (not documented anywhere as important). Did it. 3) Running build again and again wasn't changing the hash... but... Changing one small string anywhere would change all file hashes. 4) Turns out, Rollup decided to put all vendor files in entry.js, which is included in all pages. So, all hashes for all pages changed. 5) So I decided to try webpack, turns out there is no equaivalent of vite-pwa in wordpress (obviously!) 6) So started meddling into vite issues and saw an older mention of the issue, still open. Mostly waiting for Rollup to fix this issue. 7) On roll up there is some faded mention of a manualChunks feature. I tried and it works! 8) My new built system has been able to split the vendors to a separate file and the entry.js is small enough. Hashes are not changing after multiple tries... but before I could declare the win. 9) The build BREAKS!

This has been a learning experience for me and I now really appreciate the complexities involved with a frontend bundling ecosystem.

m4bwav commented 6 months ago

Is this closed? Any links to further discussions on this? I am breaking my head for the last 24 hours to figure out this.

I shall try to explain the gigaloop:

1. Nuxt changed hash on every build, irrespective of a change in the code, for all files. Means that caching is bad and almost unusable for PWA. My application is a PWA.

2. Turns out, you have to configure `buildId`in nuxt config (not documented anywhere as important). Did it.

3. Running build again and again wasn't changing the hash... but... Changing one small string anywhere would change all file hashes.

4. Turns out, Rollup decided to put all vendor files in entry.js, which is included in all pages. So, all hashes for all pages changed.

5. So I decided to try webpack, turns out there is no equaivalent of vite-pwa in wordpress (obviously!)

6. So started meddling into vite issues and saw an older mention of the issue, still open. Mostly waiting for Rollup to fix this issue.

7. On roll up there is some faded mention of a manualChunks feature. I tried and it works!

8. My new built system has been able to split the vendors to a separate file and the entry.js is small enough. Hashes are not changing after multiple tries... but before I could declare the win.

9. The build BREAKS!

This has been a learning experience for me and I now really appreciate the complexities involved with a frontend bundling ecosystem.

@ranedk Did you ever figure it out?

hugs7 commented 3 months ago

Also seeing a very similar error with a Lexical import. Works fine if I don't include manual chunks.

image

This leads to this function image (located in /@lexical/devtools-core/LexicalDevtoolsCore.dev.js). Strangely, it's just this function. If I comment this particular component out of the code, everything is fine, even with my manual chunks specified.

Lexical version: 0.16.1


My vite.config.ts

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
    base: "/",
    plugins: [react()],
    optimizeDeps: {
        exclude: ["js-big-decimal"],
    },
    build: {
        emptyOutDir: true,
        chunkSizeWarningLimit: 500,
        rollupOptions: {
            onwarn(warning, warn) {
                if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
                    return;
                }
                warn(warning);
            },
            output: {
                manualChunks(id) {
                    if (id.includes("commonjsHelpers")) {
                        return "commonjsHelpers";
                    } else if (id.includes("node_modules")) {
                        if (id.includes("bootstrap")) {
                            return "bootstrap";
                        } else if (id.includes("react")) {
                            if (id.includes("react-icons")) {
                                return "react-icons";
                            }

                            return "react";
                        } else if (id.includes("axios")) {
                            return "axios";
                        } else if (id.includes("date-fns")) {
                            return "date-fns";
                        } else if (id.includes("lexical")) {
                            return "lexical";
                        } else {
                            return "vendor";
                        }
                    } else if (id.includes("/src/") || id.includes("/constants")) {
                        if (id.includes("themes")) {
                            return;
                        }

                        return "src";
                    } else if (id.includes("/electron")) {
                        return "electron";
                    } else {
                        console.log("id: ", id);
                        return "index";
                    }
                },
            },
        },
    },
});

Update

Managed to fix this by changing my manual chunks to

 manualChunks(id) {
    if (id.includes("node_modules")) return id.toString().split("node_modules/")[1].split("/")[0].toString();
 },

Let me know if this helps you out.