Open rightaway opened 1 year ago
Eleventy doesn't even need to output a physical manifest.json file for this. output
key in the function argument isn't needed.
I stumbled upon many conversations and blog posts regarding styles and javascript (i.e. global assets) cache-busting, but not very much is being said about the issue you raise here: content assets, like images in a blog posts or an article.
Cache-busting for content assets is critical for things like documentation websites. I'm surprised nothing close to a best practice exists in that regard. Am I missing something, or is 11ty simply not meant to be used like that? Is there any solid resource I can refer to, in order to setup my configuration to provide cache-busting for content assets?
I was able to do this with built in eleventy events using this.
My contents are in src/site
and my output is to _site
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
module.exports = function(eleventyConfig) {
// A cache to store the hashed file names
const hashCache = {};
// A cache buster if a file changes
const prefixLength ="./src/site".length
eleventyConfig.on('eleventy.beforeWatch', async (changedFiles) => {
for(const file of changedFiles) {
const relativePath = file.slice(prefixLength)
delete hashCache[relativePath]
}
});
// A filter to dynamically hash asset file contents
eleventyConfig.addFilter("digest", async (filePath) => {
// If we've already hashed this file, return the hash
if(hashCache[filePath]) {
return hashCache[filePath];
}
// Get the absolute path to the file inside of src/site
const absolutePath = path.join(__dirname, 'src/site', filePath);
// Digest the file
const fileBuffer = fs.readFileSync(absolutePath);
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
const relativePath = filePath.slice(0, path.basename(filePath).length * -1)
const digestFileName = `${relativePath}${hash}-${path.basename(filePath)}`;
// See if the digest file exists in the output folder _site
const digestFilePath = path.join(__dirname, '_site', digestFileName);
hashCache[filePath] = digestFileName;
if(!fs.existsSync(digestFilePath)) {
if(!fs.existsSync(path.dirname(digestFilePath))) {
fs.mkdirSync(path.dirname(digestFilePath), { recursive: true });
}
fs.copyFileSync(absolutePath, digestFilePath);
}
// Return the digest file name
return digestFileName;
})
// other stuff
return {
dir: {
input: "src/site", // source files
output: "_site", // destination folder
includes: "_includes", // folder for layouts and includes
data: "_data" // folder for data files
}
};
};
Then I digest assets like this:
<link href="{{"/css/style.css" | digest}}" rel="stylesheet">
There's no way to do cache busting based on file content hash without hacks or using a separate process or external dependency. Some of those options are in https://github.com/11ty/eleventy/issues/272.
It's actually quite simple to do like in https://github.com/keithamus/postcss-hash, but it's not possible to hook into eleventy's file outputting. Can we have a function like this?
That would output _data/manifest.json
It would make cache busting very simple to do with an
asset
filter which would get the transformed file name from the file inoutput
(_data/manifest.json). Then<img src={{ "dir1/dir/file.png" | asset }}>
will become<img src=dir1/dir/file-1234567890.png>
.