vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
65.92k stars 5.88k forks source link

Feature Request: Exclude some libraries' optimization in node_modules. #4533

Open hyrious opened 2 years ago

hyrious commented 2 years ago

Clear and concise description of the problem

I need ability to exclude the optimization (bundling & caching) of some files in nodemodules. The "exclude"_ means the module resolution should not append ?v=xxx to the resolved file and it should always give me latest contents without restarting the vite server.

One possible solution is creating symlinks. However when I created node_modules/x → node_modules/.skip_optimize/x, nothing has changed. After digging into the source code, I think vite made an assumption that all symlinks are out of node_modules: https://github.com/vitejs/vite/blob/bcb6d753234f1085061e8a6e3623a597bb961ac5/packages/vite/src/node/plugins/resolve.ts#L417

However, keeping files in node_modules has a benefit that it does not hurt node's resolution (see nodejs/node#3402 for more details). Even though vite implements custom resolver, I think it would be good to take this problem into consideration.

Read more context in https://github.com/vitejs/vite/discussions/4429

Suggested solution

In the code above, add a check of optimizeDeps.exclude. If the requested module should be excluded, then don't return something like ?v=xxx.

Validations

hyf0 commented 2 years ago

Maybe we could add this feature by respect import map mentioned in #2483. Using the strategy yyx said, avoid rewriting anything listed in import map.

Weiyi-Feng commented 2 years ago

It's useful when developers use it worked with some 3rd library like pyodide, currently, if developer downloaded the full version of pyodide library and want used it by following code, they will get an error about it:

<script src="/src/lib/pyodide/pyodide.js"></script>
<script type="text/javascript">
    async function main(){
        let pyodide = await loadPyodide({
            indexURL: "/src/lib/pyodide",
        });
        console.log(pyodide.runPython("1 + 2"));
    }
    main();
</script>

error: Uncaught SyntaxError: Unexpected token 'export'

and we can see the /src/lib/pyodide/pyodide.js be converted to the following content:

export default "/src/lib/pyodide/pyodide.js"

but in fact, we only want the original content of it, so adding a exclude parameter is a must.

bluwy commented 2 years ago

I need ability to exclude the optimization (bundling & caching) of some files in node_modules

Currently https://github.com/vitejs/vite/issues/5688 has more discussion about this

I'm not really sure about the feature request to remove ?v=xxx for excluded libs. There are still supposed to be cached strongly to prevent multiple large requests, supporting this would negatively affect many others. I supposed you can have your browser ignore cache when making these kinds of code changes?

hyrious commented 2 years ago

Yes, I can always reload the whole thing in these steps: 1) vite optimize --force 2) restart vite server 3) refresh browser with cache disabled. However that's too much steps and it fallbacks to work like webpack (making changes, bundle the whole thing, refreshing page), in which situation I would prefer to just use esbuild to do this.

antfu's problem is different, he actually needs a strategy to dedup libraries instead of removing the optimization. That means you have to refactor the resolver in vite (or maybe esbuild through plugins) to handle vue-demi correctly.

My problem can be treat in these 2 views:

  1. vite should watch node_modules changes and do re-optimize on affected libraries and send updates to the browser.

  2. or, vite should be able to treat some files in node_modules as the same way as files out of it (as the "exclude" mean), just like user source code. One burden here is vite actually cannot handle commonjs inputs (libraries are transformed to esm through esbuild during prebundling), we must solve this first.

bluwy commented 2 years ago

Sorry for the late reply

  1. vite should watch node_modules changes and do re-optimize on affected libraries and send updates to the browser.

Watching node_modules is really expensive considering the number of files in it. I won't scale quite well and takes a lot of RAM.

  1. or, vite should be able to treat some files in node_modules as the same way as files out of it (as the "exclude" mean), just like user source code. One burden here is vite actually cannot handle commonjs inputs (libraries are transformed to esm through esbuild during prebundling), we must solve this first.

I'm not sure how this can be solved, but if you edit CJS code, they would always have to be prebundled through esbuild.


IMO the vite optimize --force dance isn't that bad considering it's not something you'd do often. I actually have a Vite plugin in the backburner to automate the optimization, and perhaps with Vite 2.9's new optimization flow, the DX won't be impacted as much.

productdevbook commented 2 years ago
  optimizeDeps: {
    exclude: ['@oku/i18n'],
  },

  server: {
    watch: {
      ignored: ['!../../**/node_modules/@oku/**'],
    },
  },

some problem, dont watch :/

jlzm commented 1 year ago

Can provide to ignore the package in node_modules? like sourece code for monorepo .

ryanbliss commented 1 year ago

We're also running into this with our monorepo that uses Lerna, which sets up symlinks through node_modules with the package name as the alias. Our repo has many packages; when we make changes we test locally through our samples. These samples are used by other developers, and it simplifies things if they can copy/paste code from samples without having to grok that they need to replace our symlink imports with "@org/package" or change some config after manually copying a sample to their own directory, etc.

If there is no plan to resolve this issue in the short term, does anyone know any workarounds we can consider?

electrovir commented 11 months ago

I've also arrived here trying to find a way to prevent caching of internal mono-repo packages. My workaround so far is to use nodemon and completely restart the vite dev server every time a change is detected in the other mono-repo packages.

leo-buneev commented 10 months ago

So, there are 4 levels that we need to fix:

  1. Bypass vite's pre-bundling
  2. Bypass browser cache (Cache-Control added by vite)
  3. Bypass vite's file cache (otherwise would return "304 Not Modified")
  4. Trigger page reload / HMR on change in related package

1 is fixed by exclude: ['my-module'] 4 can be fixed by adding watch: ... patterns (or manually refreshing page with f5)

2 and 3 are harder, though. Here's workaround we've used for 2 and 3:

// vite.config.js
plugins: [
  ...,
  {
    name: 'noCache',
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        if (req.url?.includes('/my-module/')) {
          // Bypass vite's file cache - modifies ?v=... parameter
          req.url += Date.now()

          // Bypass browser cache - those headers override "Cache-control" added by Vite
          res.setHeader('Expires', '0')
          res.setHeader('Pragma', 'no-cache')
        }
        next()
      })
    },
  },
]
kasra-r77 commented 8 months ago

We recently encountered a similar situation within our team, and I applied the concepts from here to resolve the issue and crafted a compact plugin for it:

https://github.com/kasra-r77/vite-plugin-dependency-watcher