Dieter Pisarewski
Dieter Pisarewski

Reputation: 879

vite: Adding static assets(with cache busting) to the build

I have large static files which should be busted with a hash for HTTP-caching. If I put them into the public directory, vite only copies them without appending a hash to the filenames. If I put them into the assets directory, vite ignores them, because they are not referenced directly from code, but loaded via XHR requests. The directory structure is pretty standard:

/
├─ src/
│  ├─ assets/
│  │  ├─ locales/
│  │  │  ├─ en.json
│  │  │  ├─ de.json
│  │  │  ├─ ru.json
│  ├─ main.js
├─ public/
├─ dist/
├─ index.html

How do I tell vite to copy those files with hash added to filenames and how do I get the resulting filenames to use them in XHR requests?

Upvotes: 6

Views: 8827

Answers (1)

tony19
tony19

Reputation: 138246

One solution is to use the url suffix to import the .json:

// BEFORE:
// fetch('@/assets/locales/en.json').then(res => res.json()).then(json => /*...*/)

// AFTER:                                   👇
import enUrl from '@/assets/locales/en.json?url'
fetch(enUrl).then(res => res.json()).then(json => /*...*/)

If there are many locale files, you can import them all with import.meta.glob:

async function loadLocales() {
  const localeFiles = await import.meta.glob('@/assets/locales/*.json', { as: 'url', eager: true })

  // get basename of file from path
  const basename = s => s.split('/').at(-1).split('.').at(0)

  // create map of locale name to file -> { en: '/src/assets/locales/en-abc123.json' }
  const resolvedUrls = {}
  for (const [key, filePath] of Object.entries(localeFiles)) {
    resolvedUrls[basename(key)] = filePath
  }
  return resolvedUrls
}

If your files are less than 4KB, they'll be inlined as a data URL, and you won't see the corresponding file in your build output. To disable this inlining, set build.assetsInlineLimit to 0:

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    assetsInlineLimit: 0,
  },
})

Also, to keep the built locale files under dist/locales, you'd have to configure the asset naming:

// vite.config.js
import { normalizePath, defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        assetFileNames(assetInfo) {
         // output src/assets/locales/*.json files to dist/locales
          const pathToFile = normalizePath(assetInfo.name)
          if (/\/src\/assets\/locales\/.*\.json$/.test(pathToFile)) {
            return 'locales/[name]-[hash].json'
          }

          return 'assets/[name]-[hash][extname]'
        },
      },
    },
  },
})

demo

Upvotes: 7

Related Questions