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?
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]'
},
},
},
},
})