Reputation: 15967
I'm building a PWA and part of the startup in the service worker is to cache all the assets. But since vite (by default) inserts the hash of the generated assets into their file names, the exact list of assets to cache varies from run to run, making it impossible to cache the assets.
So how can I access an array of built assets from inside a *.js
file at run-time, so I can cache them with cache.addAll(assets)
?
While that is what I'd really like, I do know there are (at least) two workarounds:
-[hash]
from build.rollupOptions.output.(asset|entry|chunk)Filenames
. That gives a predictable list of assets that can be hard-coded.sw.js
file. But my PWA is already working fine without vite or VitePWA, and I'd like to avoid including this huge dependency. I'd just like to build what I already have with vite (+add Vue3) and be done.The source code for the VitePWA plugin is really an array of 4 plugins that interact in a way I haven't been able to understand.
I'm guessing these are rollup plugins and the path forward is to create a rollup plugin that somehow investigates what assets got created by the other plugins and then in a post step creates a assets.js
or something containing something like:
export const assets = [
"/assets/index-4f46e888.js",
"/assets/index-ef4f98ff.css",
"/assets/link-192-a96fcf4c.png",
"/assets/logo-277e0e97.svg",
"/assets/modulepreload-polyfill-3cfb730f.js",
"/assets/otherPage-892792b9.js",
"/favicon.ico",
"/index.html",
"/otherPage.html",
]
That leaves me with two questions about a "like-VitePWA" approach:
assets.js
above?const assets
from the javascript in index.html
? There seems to be a chicken-and-egg situation. Can I tell rollup that exactly assets.js
needs to be loaded at runtime and not compile time somehow? Or how do I "stuff* const assets
into the bundle in my post step?-[hash]
from build.rollupOptions.output.(asset|entry|chunk)Filenames
This was relatively easy. Put this under build.rollupOptions
in vite.config.js
:
output: {
assetFileNames: "assets/[name][extname]",
entryFileNames: "assets/[name].js",
chunkFileNames: "assets/[name].js",
sourcemap: true,
}
And voila, the output from npm build
in dist/
becomes predictable.
But it is no longer cache-busting because the -[hash]
has been removed. Which is why I'm looking for a way to keep the -[hash]
so it remains cache-busting.
Upvotes: 2
Views: 725
Reputation: 15967
Here is a plugin that prepends a global window.assetList
to the code of the index
chunk when used as:
import assetListPlugin from './assetListPlugin.js'
export default defineConfig({
plugins: [
...,
assetListPlugin('assetList', 'index'),
...,
],
})
And here is ./assetListPlugin.js`:
export default (global, chunkName) => ({
name: 'assetList',
generateBundle(outputOptions, bundle) {
let chunkToModify
const allAssetFileNames = []
Object.values(bundle).forEach((asset) => {
if (asset.name == chunkName && asset.type == 'chunk')
chunkToModify = asset
allAssetFileNames.push(asset.fileName)
})
if (! chunkToModify) {
throw new Error(`No "${chunkName}" chunk found`)
}
chunkToModify.code = `window.${global} = ${JSON.stringify(allAssetFileNames)}; ${chunkToModify.code}`
},
})
Use it in any code in the index
chunk as:
// assetList is now a global variable
console.log(assetList)
What suprised me a little was that it only works for vite build
, not vite
(dev server). But it makes sense. dev only knows about the modules it has served thus far, since it doesn't build everything up-front, so it can't populate the full asset array. This is also documented as:
Output Generation Hooks (except closeBundle) are not called during dev.
(So generateBundle()
is never called during vite
(dev server))
So if I want to do this, run vite build -w
in one terminal and vite preview
in another.
Upvotes: 1