eleventy-plugin-vite
eleventy-plugin-vite copied to clipboard
Unable to generate images with eleventy-img at build time
The current website I have uses Nunchucks as templating language, md for posts, and that's mostly it. The config is quite straightforward for a blog. I've taken most of the configuration for it from the project Eleventy Plus Vite.
// .eleventy.js
const markdownIt = require('markdown-it')
const markdownItAnchor = require('markdown-it-anchor')
const EleventyPluginNavigation = require('@11ty/eleventy-navigation');
const EleventyPluginRss = require('@11ty/eleventy-plugin-rss');
const EleventyPluginSyntaxhighlight = require('@11ty/eleventy-plugin-syntaxhighlight');
const EleventyPluginTimeToRead = require('eleventy-plugin-time-to-read');
const EleventyVitePlugin = require('@11ty/eleventy-plugin-vite');
const rollupPluginCritical = require('rollup-plugin-critical').default;
const { eleventyImagePlugin } = require("@11ty/eleventy-img");
const filters = require('./utils/filters.js');
const transforms = require('./utils/transforms.js');
const shortcodes = require('./utils/shortcodes.js');
const { resolve } = require('path')
module.exports = function (eleventyConfig) {
// Plugins
eleventyConfig.addPlugin(EleventyPluginNavigation);
eleventyConfig.addPlugin(EleventyPluginRss);
eleventyConfig.addPlugin(EleventyPluginSyntaxhighlight);
eleventyConfig.addPlugin(EleventyPluginTimeToRead);
eleventyConfig.addPlugin(eleventyImagePlugin, {
// NOTE: this config options don't actually do anything. See shortcodes.
// Set global default options
formats: ["webp", "jpeg"],
urlPath: "/images/",
outputDir: './_site/images/',
// Notably `outputDir` is resolved automatically
// to the project output directory
defaultAttributes: {
loading: "lazy",
decoding: "async"
}
});
eleventyConfig.addPlugin(EleventyVitePlugin, {
tempFolderName: '.11ty-vite', // Default name of the temp folder
// Vite options (equal to vite.config.js inside project root)
viteOptions: {
publicDir: 'public',
clearScreen: false,
server: {
mode: 'development',
middlewareMode: true,
},
appType: 'custom',
assetsInclude: ['**/*.xml', '**/*.txt'],
build: {
mode: 'production',
sourcemap: 'true',
manifest: true,
// This puts CSS and JS in subfolders – remove if you want all of it to be in /assets instead
rollupOptions: {
output: {
assetFileNames: 'assets/styles/main.[hash].css',
chunkFileNames: 'assets/scripts/[name].[hash].js',
entryFileNames: 'assets/scripts/[name].[hash].js'
},
plugins: [rollupPluginCritical({
criticalUrl: './_site/',
criticalBase: './_site/',
criticalPages: [
{ uri: 'index.html', template: 'index' },
{ uri: 'archive/index.html', template: 'archive' },
{ uri: '404.html', template: '404' },
],
criticalConfig: {
inline: true,
dimensions: [
{
height: 900,
width: 375,
},
{
height: 720,
width: 1280,
},
{
height: 1080,
width: 1920,
}
],
penthouse: {
forceInclude: ['.fonts-loaded-1 body', '.fonts-loaded-2 body'],
}
}
})
]
}
}
}
});
// Filters
Object.keys(filters).forEach((filterName) => {
eleventyConfig.addFilter(filterName, filters[filterName])
});
// Transforms
Object.keys(transforms).forEach((transformName) => {
eleventyConfig.addTransform(transformName, transforms[transformName])
});
// Shortcodes
Object.keys(shortcodes).forEach((shortcodeName) => {
eleventyConfig.addShortcode(shortcodeName, shortcodes[shortcodeName])
});
// front matter options (More separator)
eleventyConfig.setFrontMatterParsingOptions({
excerpt: true,
excerpt_separator: "<!-- more -->"
});
// Customize Markdown library and settings:
let markdownLibrary = markdownIt({
html: true,
breaks: true,
linkify: true
}).use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: 'after',
class: 'direct-link',
symbol: '#',
level: [1, 2, 3, 4]
}),
slugify: eleventyConfig.getFilter('slug')
});
eleventyConfig.setLibrary('md', markdownLibrary);
// re-enabling indendted code in MD parsing
eleventyConfig.amendLibrary("md", mdLib => mdLib.enable("code"));
// Layouts
eleventyConfig.addLayoutAlias('base', 'base.njk');
eleventyConfig.addLayoutAlias('post', 'post.njk');
eleventyConfig.addLayoutAlias('page', 'page.njk');
// Copy/pass-through files
eleventyConfig.addPassthroughCopy("public");
eleventyConfig.addPassthroughCopy('src/scripts');
eleventyConfig.addPassthroughCopy('src/styles');
eleventyConfig.addCollection("posts", require("./_11ty/getPosts"));
return {
templateFormats: ['md', 'njk', 'liquid'],
htmlTemplateEngine: 'njk',
passthroughFileCopy: true,
dir: {
input: 'src',
// better not use "public" as it's used by vite for static assets
output: '_site',
includes: '_includes',
layouts: 'layouts',
data: '_data'
}
};
}
I am using njk shortcodes to generate the images.
// utils/shortcodes.js
const Image = require("@11ty/eleventy-img");
const urlPath = '/images/';
const baseImagePath = "./src/images/";
const outputDir = "./_site/images/";
module.exports = {
year: function() {
return `${new Date().getFullYear()}`
},
image: function(src, alt, cls, sizes, widths) {
let options = {
widths: [300, 600, 1200],
formats: ['jpeg', 'webp'],
urlPath,
outputDir
};
// generate images, while this is async we don’t wait
Image(`${baseImagePath}${src}`, options);
let imageAttributes = {
class: cls,
alt,
sizes,
loading: "lazy",
decoding: "async",
};
// get metadata even if the images are not fully generated yet
let metadata = Image.statsSync(`${baseImagePath}${src}`, options);
return Image.generateHTML(metadata, imageAttributes);
},
metaImageURL: function(src) {
const options = {
widths: [1200],
formats: ["jpeg"],
urlPath,
outputDir
};
Image(`${baseImagePath}${src}`, options);
let metadata = Image.statsSync(`${baseImagePath}${src}`, options)
let data = metadata.jpeg[metadata.jpeg.length - 1];
return data.url;
}
};
Now when run in dev mode, the images are generated and copied without problems in _site/images/
, but when I run the build process, the _site/images/
folder does not contain any of the images that should have been generated.
Any help would be greaty appreciated, unfortunately I don't know Vite in depth enough to pester around with the config without making a mess 🙂
By default (and judging from your settings this shouldn't have changed) Vite should put your images in the _site/assets/
subfolder along with your scripts and stylesheets. They're only present at _site/images/
since the dev server uses Vite as a middleware, meaning it works in place and doesn't discard what eleventy generates, but the images should be present in both folders.
Vite should recognise the reference to your images in html and change it to link to assets
automatically in the final output. Is that not the case for you?
Well that was interesting, and silly me for not checking earlier... I checked the generated HTML and now I'm more confused than before, what would normally be:
<picture><source type="image/jpeg" srcset="/images/rpql6wqYYO-300.jpeg 300w, /images/rpql6wqYYO-600.jpeg 600w, /images/rpql6wqYYO-1200.jpeg 1200w" sizes="(min-width: 600px) 600px, `1200px"><source type="image/webp" srcset="/images/rpql6wqYYO-300.webp 300w, /images/rpql6wqYYO-600.webp 600w, /images/rpql6wqYYO-1200.webp 1200w" sizes="(min-width: 600px) 600px, `1200px"><img class="" alt="An image of thick ropes laying on the ground next to each other - Photo Credit: Matteo Pescarin" loading="lazy" decoding="async" src="/images/rpql6wqYYO-300.jpeg" width="1200" height="520"></picture>
when building I get instead:
<picture><source type="image/jpeg" srcset="/assets/styles/main.62fd7692.css 300w, /assets/styles/main.6878b93c.css 600w, /assets/styles/main.1825ebd2.css 1200w" sizes="(min-width: 600px) 600px, `1200px"><source type="image/webp" srcset="/assets/styles/main.9312b2c5.css 300w, /assets/styles/main.f99bff27.css 600w, /assets/styles/main.60027b5b.css 1200w" sizes="(min-width: 600px) 600px, `1200px"><img class="" alt="An image of thick ropes laying on the ground next to each other - Photo Credit: Matteo Pescarin" loading="lazy" decoding="async" src="/assets/styles/main.62fd7692.css" width="1200" height="520"></picture>
if I was unsure before, now I'm completely at loss. :disappointed:
I relation to your point: no, when the images are created, are only present in _site/images/
.
Can only guess but I think what causes this is your rollupOptions
. Images are also assets, so assetFileNames: 'assets/styles/main.[hash].css'
would cause all of them to be renamed to css files, which they're not. I don't know how to output stylesheets and images in different folders using Rollup, but all of them should be output and linked correctly into the assets folder if you remove that part from your configuration.
Alternatively, you could try and have Vite copy the images without any renaming by outputting them in the public
subfolder, though you would probably need a custom function in place of Image.generateHTML
, as images would need to be linked to the root folder instead of the subfolder for static assets.
As @ngdio pointed out, the problem here is the misconfiguration of assetFileNames
. A proper configuration for your use case might look like this:
assetFileNames: (assetInfo) => {
const extension = path.extname(assetInfo.name);
if (['.jpeg', '.png', '.avif', '.webp'].includes(extension)) {
return 'assets/images/[name].[hash].[ext]';
}
if ('.css' === extension) {
return 'assets/styles/[name].[hash].[ext]';
}
return 'assets/[name].[hash].[ext]';
},
For best performance @11ty/eleventy-img
should output the images to the same path. So the caching of the plugin can properly work. For that usecase one would probably also omit the [hash]
token for the image assets.
If the problem still persists for you, feel free to comment. Closing for now as it's not a bug of the plugin.