question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[build] importing from hashed chunks makes caching terribly ineffective

See original GitHub issue

Update

I’ve published a plugin that solves the issue for me: https://github.com/jacekkarczmarczyk/importmap-plugin See https://github.com/vitejs/vite/issues/6773#issuecomment-1308048405 for example usage in the issue reproduction repository

Describe the bug

Built files import other files which names contain content hash. So if the chunk A changes its contents then the output file changes its hash (A.123.js becomes A.234.js). So if there’s other file that imports from A chunk then it also changes its contents and hash because import {...} from 'A.123.js' becomes import {...} from 'A.234.js'.

Imagine now that I’m defining an env variable with build time. Main chunk imports this file to show the build time in App.vue. However main chunk exports the following vue related function:

function lc (e, t, r, n, i, a, o, s) {
  var f = typeof e == 'function' ? e.options : e;
  t && (f.render = t, f.staticRenderFns = r, f._compiled = !0), n && (f.functional = !0), a && (f._scopeId = 'data-v-' + a);
  var u;
  if (o ? (u = function (d) {
    d = d || this.$vnode && this.$vnode.ssrContext || this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext, !d && typeof __VUE_SSR_CONTEXT__ != 'undefined' && (d = __VUE_SSR_CONTEXT__), i && i.call(this, d), d && d._registeredComponents && d._registeredComponents.add(o);
...

that is being imported by all component chunks (note that this is just example, depending on the project there might be other user defined functions that are exported from main chunk, also in my actual project I’ve extracted vendors to separate chunks but still vue related functions were exported from main chunk).

So now when I build again env chunk will change its contents and hash/name, therefore main chunk will change it’s contents and hash/name, therefor ALL other chunks will change their names. That makes caching very innefective. Also worth to mention that that technique worked fine in vue-cli - when build time changed only single small env.hash.js chunk was changed, all others remained unchanged (EDIT: there’s also relatively small runtime chunk that also was changed)

Reproduction

https://github.com/jacekkarczmarczyk/vite-chunks-very-very-bad

Run yarn && yarn build && git add . && yarn build && git status

It behaves the same when displaying build time is moved to an async Home.vue route component, in this case hash change of main chunk is definitely unjustified

System Info

System:
    OS: Windows 10 10.0.19043
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 3.34 GB / 15.87 GB
  Binaries:
    Node: 16.11.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 8.0.0 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    @vitejs/plugin-legacy: ^1.6.4 => 1.6.4
    vite: ^2.7.2 => 2.7.13

Used Package Manager

yarn

Logs

No response

Validations

Additional notes

I believe that this more like rollup issue (or whatever is used for generating chunks) but if it’s possible to fix it by using some rollup’s settings then vite should use these settings by default. And if it’s not possible to fix it by using different settings then I think that chosing rollup was not the best choice. This page https://bundlers.tooling.report/hashing/js-import-cascade/ and this https://bundlers.tooling.report/hashing/avoid-cascade/ however suggest that this should not be an issue for rollup

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:9
  • Comments:9

github_iconTop GitHub Comments

7reactions
anatolsommercommented, Mar 8, 2022

I had a very similar problem and came up with this hacky solution:

import { defineConfig } from 'vite'
import { createHash } from 'crypto'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].js',
        chunkFileNames: chunkInfo => {
          if (chunkInfo.isDynamicEntry) {
            const hash = createHash('md5')
              .update(Object.values(chunkInfo.modules).map(m => m.code).join())
              .digest('hex')
              .substr(0, 6)
            return 'assets/[name].' + hash + '.js'
          } else {
            return 'assets/[name].[hash].js'
          }
        }
      }
    }
  }
})

When adding Cache-Control: must-revalidate, max-age=0 for /assets/index.js (my entry file) and Cache-Control: public, max-age=31557600 to all other assets it seems to work fine but I had no chance to really test it yet and I’m quite new to Vite/Rollup and ESM in general so this might be a terrible idea for some reason I don’t know…

Every kind of feedback is very appreciated! 😃

0reactions
jacekkarczmarczykcommented, Nov 25, 2022

For anyone interested - here’s a proof of concept based on @lukastaegert’s comment in rollup repo:

https://github.com/jacekkarczmarczyk/importmap-plugin (UPDATE: now supports SystemJS)

Feel free to steal the code and create a proper vite/rollup plugin. Any comments welcome

Plugin applied to the original reproduction (https://github.com/jacekkarczmarczyk/vite-chunks-very-very-bad/tree/main-stable-hash-plugin - yarn && yarn build && git add . && yarn build && git status) - working as expected (only modified chunk changed the hash):

image

Read more comments on GitHub >

github_iconTop Results From Across the Web

Caching
Running our build script, npm run build , with this configuration should produce the following output: ... Asset Size Chunks Chunk Names main....
Read more >
Caching a table in Memory - Ask TOM
Caching a table in Memory Hi, Is it enough if we use ALTER TABLE CACHE; to push the table to cache ?. Do...
Read more >
Changelog — Python 3.11.1 documentation
gh-91577: Move imports in SharedMemory methods to module level so that they can be executed late in python finalization.
Read more >
Webpack from Nothing - Going to Production
minify/gzip/de-biggen the JavaScript we make the user download. create a ... Creating a hashed filename is called caching, since the filename allows us...
Read more >
PHP: The Right Way
An easy-to-read, quick reference for PHP best practices, accepted coding standards, and links to authoritative PHP tutorials around the Web.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found