Skip to content

antfu/vite-plugin-optimize-exclude

Repository files navigation

vite-plugin-optimize-exclude

npm version npm downloads bundle JSDocs License

Exclude ESM dependencies from Vite optimization. To reduce the chance of "New dependencies detected" page reloads.

Install

Vite

npm i vite-plugin-optimize-exclude -D
// vite.config.ts
import { defineConfig } from 'vite'
import OptimizeExclude from 'vite-plugin-optimize-exclude'

export default defineConfig({
  plugins: [
    OptimizeExclude()
  ]
})

Nuxt

npm i vite-plugin-optimize-exclude -D
// nuxt.config.ts
import OptimizeExclude from 'vite-plugin-optimize-exclude'

export default defineNuxtConfig({
  vite: {
    plugins: [
      OptimizeExclude()
    ]
  }
})

How it works?

Vite's deps optimize feature use esbuild to bundle all discovered dependencies into multiple chunks, where common dependencies will be bundled into a shared chunk. For example, the dependency tree of vue-router and pinia would look like this:

stateDiagram-v2
  A: vue-router
  B: pinia
  C: vue
  D: @vue/runtime-dom
  E: @vue/runtime-core
  F: @vue/reactivity
  G: @vue/shared
  A --> C
  B --> C
  C --> D
  D --> E
  E --> F
  F --> G
Loading

After deps optimize, esbuild will detect that vue-related packages are common dependencies of vue-router and pinia, and bundle them into a shared chunk:

stateDiagram-v2
  CC: Common Chunk 1ab42e
  A: vue-router
  B: pinia
  C: vue
  D: @vue/runtime-dom
  E: @vue/runtime-core
  F: @vue/reactivity
  G: @vue/shared
  A --> CC
  B --> CC
  state CC {
    [*] --> C
    C --> D
    D --> E
    E --> F
    F --> G
  }
Loading

This usually works well. But when a new dependency is discovered and it uses things from the shared chunk that was not exposed in the chunk entry, for example, @vueuse/core deps in @vue/runtime-core, the story becomes:

stateDiagram-v2
  CC: Common Chunk 1ab42e
  A: vue-router
  B: pinia
  C: vue
  D: @vue/runtime-dom
  E: @vue/runtime-core
  F: @vue/reactivity
  G: @vue/shared
  H: @vueuse/core
  I: @vueuse/shared
  A --> CC
  B --> CC
  H --> I
  I --> E
  state CC {
    [*] --> C
    C --> D
    D --> E
    E --> F
    F --> G
  }
Loading

Which won't work, so the chunks need to be regenerated:

stateDiagram-v2
  CC: Common Chunk a91c24
  A: vue-router
  B: pinia
  C: vue
  D: @vue/runtime-dom
  E: @vue/runtime-core
  F: @vue/reactivity
  G: @vue/shared
  H: @vueuse/core
  I: @vueuse/shared
  A --> CC
  B --> CC
  H --> CC

  state CC {
    [*] --> C
    [*] --> I
    I --> E
    C --> D
    D --> E
    E --> F
    F --> G
  }
Loading

In that case, because the old chunk is already executed in the browser, we have to do a page refresh to get the latest chunk. Theoretically, this happens every time when you introduce a new dependency that imports vue, or any other packages that are already in common chunks. This is one of the reasons you might encounter the annoying page reloads consistently.

While Vite's deps optimization is mostly used for converting CJS dependencies to ESM (and might also reduce the file count), as more and more libraries are published in ESM, for many dependencies we don't actually need to optimize them anymore.

This plugin scans your node_modules and excludes all ESM dependencies from the optimization process. Fewer packages to be preoptimized, less work to do, and less chance of encountering the shared chunk issue.

Note

The more dependents a package has, the more important for it to be ESM-ready so all the dependents can benefit from bypassing the optimization. For example, the popular react package is still in CJS, meaning that we can't bail out of the optimization process for react and all its dependents. The community should help to push the ecosystem towards ESM.

Sponsors

License

MIT License © 2024-PRESENT Anthony Fu

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published