const fs = require("node:fs"); const { TemplatePath } = require("@11ty/eleventy-utils"); const eleventyImage = require("../img.js"); const setupLogger = eleventyImage.setupLogger; const Util = require("./util.js"); const debug = require("debug")("Eleventy:Image"); function eleventyImageOnRequestDuringServePlugin(eleventyConfig, options = {}) { try { // Throw an error if the application is not using Eleventy 3.0.0-alpha.7 or newer (including prereleases). eleventyConfig.versionCheck(">=3.0.0-alpha.7"); } catch(e) { console.log( `[11ty/eleventy-img] Warning: your version of Eleventy is incompatible with the dynamic image rendering plugin (see \`eleventyImageOnRequestDuringServePlugin\`). Any dynamically rendered images will 404 (be missing) during --serve mode but will not affect the standard build output: ${e.message}` ); } setupLogger(eleventyConfig, {}); // Eleventy 3.0 or newer only. eleventyConfig.setServerOptions({ onRequest: { // TODO work with dev-server’s option for `injectedScriptsFolder` "/.11ty/image/": async function({ url }) { // src could be file path or full url let src = url.searchParams.get("src"); let imageFormat = url.searchParams.get("format"); let width = parseInt(url.searchParams.get("width"), 10); let via = url.searchParams.get("via"); let defaultOptions; if(via === "webc") { defaultOptions = eleventyConfig.getFilter("__private_eleventyImageConfigurationOptions")(); } else if(via === "transform") { defaultOptions = eleventyConfig.getFilter("__private_eleventyImageTransformConfigurationOptions")(); } // if using this plugin directly (not via webc or transform), global default options will need to be passed in to the `addPlugin` call directly // Prefer options passed to this plugin, fallback to Transform plugin or WebC options if the image source was generated via those options. let opts = Object.assign({}, defaultOptions, options, { widths: [width || "auto"], formats: [imageFormat || "auto"], dryRun: true, cacheOptions: { // We *do* want to write files to .cache for re-use here. dryRun: false }, transformOnRequest: false, // use the built images so we don’t go in a loop generatedVia: Util.KEYS.requested, }); Util.addConfig(eleventyConfig, opts); debug( `%o transformed on request to %o at %o width.`, src, imageFormat, width ); try { if(!Util.isFullUrl(src)) { // Image path on file system must be in working directory src = TemplatePath.absolutePath(".", src); if(!fs.existsSync(src) || !src.startsWith(TemplatePath.absolutePath("."))) { throw new Error(`Invalid path: ${src}`); } } let stats = await eleventyImage(src, opts); let format = Object.keys(stats).pop(); let stat = stats[format][0]; if(!stat) { throw new Error("Invalid image format."); } if(!stat.buffer) { throw new Error("Could not find `buffer` property for image."); } return { headers: { // TODO Set cache headers to match eleventy-fetch cache options (though remote fetchs are still written to .cache) "Content-Type": stat.sourceType, }, body: stat.buffer, }; } catch (error) { debug("Error attempting to transform %o: %O", src, error); return { status: 500, headers: { "Content-Type": "image/svg+xml", "x-error-message": error.message }, body: ``, }; } } } }); } module.exports = { eleventyImageOnRequestDuringServePlugin, };