vitejs / vite

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

Unable to completely disable Rollup's tree shaking #17596

Closed Mooncat25 closed 1 month ago

Mooncat25 commented 3 months ago

Describe the bug

Setting moduleSideEffects to no-treeshake does not completely disable Rollup's tree shaking.

Short Story

I have used the output.manualChunks option to split the code Svelte to a separated chunk, and set moduleSideEffects to no-treeshake in the result of the transform hook to disable tree shake, expecting the code in the Svelte chunk to be consistent. Unfortunately, exports are still being removed.

Long Story

I'm trying to make something like micro-frontends for Vite + Svelte + TS. But module-federation does not support Vite. @originjs/vite-plugin-federation seems to be abandoned. After figuring out the output.manualChunks option in Rollup, I decided to study about it. Basically I want to use it to split the common modules, e.g. Svelte, Axios, common Svelte UI components, etc., into common chunks, so that different apps can share them, i.e. code sharing.

But this issue is blocking the development because the Svelte chunks from different apps are different and thus not interchangeable.

Reproduction

https://github.com/Mooncat25/Rollup-ManualChunks-Problem

Steps to reproduce

  1. Create 2 Vite + Svelte apps, app1 and app2.

  2. Add "lib": ["ESNext"], in the compilerOptions of both tsconfig.node.json.

  3. Add the following plugin in the plugins of both vite.config.ts:

      {
        name: 'vite-plugin-chunk-split',
        apply: 'build',
        enforce: 'post',
    
        config: function (config: UserConfig): Omit<UserConfig, 'plugins'> {
          return {
            ...config,
            build: {
              ...config.build,
              rollupOptions: {
                ...config.build?.rollupOptions,
                output: {
                  manualChunks: function (id: string): string | undefined {
                    if (id.includes('/axios/')) {
                      return 'axios';
                    }
                    if (id.includes('/svelte/src/')) {
                      return 'svelte';
                    }
                  }
                },
              }
            }
          };
        },
    
        transform: function (_: string, id: string): TransformResult {
          if (id.includes('/axios/')) {
            return {
              moduleSideEffects: 'no-treeshake'
            };
          }
          if (id.includes('/svelte/src/')) {
            return {
              moduleSideEffects: 'no-treeshake'
            };
          }
        }
      }
  4. In the App.svelte of either app1 or app2, add the following script in the <script> tag so one of them uses Svelte's onMount:

    import { onMount } from 'svelte';
    
    let randomText = '';
    onMount(() => {
      randomText = (Math.random() * 1000).toString();
    });

    In my reproduction repo, I also add <div>{randomText}</div> to ensure randomText is used.

  5. Build both app1 and app2 with vite build. Observe the svelte chunk in their dist/assets/.

By diffing their contents, you can see that their only difference is the export of onMount.

System Info

System:
    OS: macOS 14.0
    CPU: (8) arm64 Apple M1
    Memory: 75.72 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.11.1 - ~/.nvm/versions/node/v20.11.1/bin/node
    npm: 10.2.4 - ~/.nvm/versions/node/v20.11.1/bin/npm
    pnpm: 8.6.7 - /usr/local/bin/pnpm
  Browsers:
    Chrome: 126.0.6478.127
    Edge: 126.0.2592.81
    Safari: 17.0

Used Package Manager

pnpm

Logs

No response

Validations

Mooncat25 commented 3 months ago

Update: I have tried the solution of Discussion https://github.com/vitejs/vite/discussions/14454, by adding path.join(process.cwd(), 'node_modules/svelte/src/runtime/index.js') into the rollupOptions.input option, setting preserveEntrySignatures: 'allow-extension', and changing the return value of entryFileNames() according to the chunkInfo.facadeModuleId so that the Svelte runtime chunk is named assets/svelte.[hash].js. Now I get consistent output in the Svelte runtime chunk. I have committed the changes on the reproduction repo.

But not really a fan of it. It relies on the internal structure of Svelte runtime. (Maybe I can/should read the main in Svelte's package.json?) And the more I think about it, the more I think it is a bug on Vite. Setting moduleSideEffects: 'no-treeshake' should not remove any export.

Mooncat25 commented 3 months ago

Another update: the solution above does not work in more complicated set up. My actual project still produces Svelte runtime chunks with different exports. One exports text and set_data while the other one doesn't. So the journey of searching for the solution still continues.

bluwy commented 2 months ago

I don't understand how this is a Vite issue? Seems like a Rollup configuration issue. moduleSideEffects: 'no-treeshake' is not related to exports, preserveEntrySignature is which you configured. What moduleSideEffects does is that it tells every module how it should be analyzed. When these modules get chunked, it's a different flow with its own set of treeshaking and bundle analysis.

Anyways, something you may also hit is https://github.com/rollup/rollup/issues/4180, manualChunks may not be the best way to guarantee consistent shared chunk code unless you're ok with potentially additional code added.

Minglu-Huo commented 2 months ago

I encountered a similar issue. Have you found a good solution?