2022-02-07 03:14:31 +01:00
|
|
|
|
const { EleventyRenderPlugin } = require("@11ty/eleventy");
|
2024-01-29 02:48:13 +01:00
|
|
|
|
const Image = require("@11ty/eleventy-img");
|
|
|
|
|
const CleanCSS = require("clean-css");
|
2022-02-07 03:14:31 +01:00
|
|
|
|
|
2022-02-05 14:48:21 +01:00
|
|
|
|
module.exports = function (eleventyConfig) {
|
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/css");
|
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/fonts");
|
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/images");
|
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/svgs");
|
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/js");
|
2022-02-05 23:41:45 +01:00
|
|
|
|
eleventyConfig.addPassthroughCopy("./src/screenshots");
|
2022-02-06 00:16:53 +01:00
|
|
|
|
eleventyConfig.addPassthroughCopy({
|
|
|
|
|
"./projects/freeCodeCamp": "freeCodeCamp",
|
|
|
|
|
});
|
|
|
|
|
eleventyConfig.addPassthroughCopy({
|
|
|
|
|
"./projects/FrontendMentor": "FrontendMentor",
|
|
|
|
|
});
|
2022-09-04 13:57:08 +02:00
|
|
|
|
eleventyConfig.addPassthroughCopy({
|
|
|
|
|
"./projects/random": "others",
|
|
|
|
|
});
|
|
|
|
|
eleventyConfig.addPassthroughCopy({
|
|
|
|
|
"./projects/devchallenges": "devchallenges",
|
|
|
|
|
});
|
2022-02-06 01:50:47 +01:00
|
|
|
|
eleventyConfig.addPassthroughCopy({
|
|
|
|
|
"./src/assets/faviconstuff": "/",
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-05 14:48:21 +01:00
|
|
|
|
//eleventyConfig.addPassthroughCopy({
|
|
|
|
|
// "./src/assets/images": "img",
|
|
|
|
|
//});
|
|
|
|
|
|
2024-01-29 02:48:13 +01:00
|
|
|
|
// FILTERS
|
|
|
|
|
eleventyConfig.addFilter("cssmin", function (code) {
|
|
|
|
|
return new CleanCSS({}).minify(code).styles;
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-07 03:14:31 +01:00
|
|
|
|
// PLUGINS
|
|
|
|
|
eleventyConfig.addPlugin(EleventyRenderPlugin);
|
|
|
|
|
|
2024-01-29 02:48:13 +01:00
|
|
|
|
// SHORTCODES
|
|
|
|
|
// eleventyConfig.addShortcode("image", imageShortcode);
|
|
|
|
|
eleventyConfig.addAsyncShortcode("imageGen", officialImageShortcode);
|
|
|
|
|
eleventyConfig.addNunjucksShortcode("imageGenSync", imageShortcodeSync);
|
|
|
|
|
// eleventyConfig.addShortcode("imageGenSync", imageShortcodeSync);
|
|
|
|
|
|
2022-02-06 00:16:53 +01:00
|
|
|
|
// WATCH Targets
|
|
|
|
|
eleventyConfig.addWatchTarget("./src/css/");
|
|
|
|
|
eleventyConfig.addWatchTarget("./src/js/");
|
|
|
|
|
|
2022-02-05 14:48:21 +01:00
|
|
|
|
// Return your Object options:
|
|
|
|
|
return {
|
2022-02-07 03:14:31 +01:00
|
|
|
|
// markdownTemplateEngine: "njk",
|
2022-02-05 14:48:21 +01:00
|
|
|
|
dir: {
|
|
|
|
|
input: "src",
|
|
|
|
|
output: "www",
|
2022-02-07 03:14:31 +01:00
|
|
|
|
// ⚠️ These values are relative to the input directory.
|
2022-02-05 14:48:21 +01:00
|
|
|
|
// 📝 _includes is a default value, as is _data but I like them
|
2022-02-07 03:14:31 +01:00
|
|
|
|
// visible here as a reminder 🤷
|
|
|
|
|
includes: "_includes",
|
|
|
|
|
// done to shorten the frontmater so layout: base
|
|
|
|
|
// rather than layout: layouts/base
|
|
|
|
|
layouts: "_includes/layouts",
|
|
|
|
|
data: "_data",
|
2022-02-05 14:48:21 +01:00
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
2024-01-29 02:48:13 +01:00
|
|
|
|
|
|
|
|
|
const imageShortcode = async (
|
|
|
|
|
src,
|
|
|
|
|
alt,
|
|
|
|
|
className = undefined,
|
|
|
|
|
widths = [400, 800, 1280],
|
|
|
|
|
formats = ["webp", "jpeg"],
|
|
|
|
|
sizes = "100vw"
|
|
|
|
|
) => {
|
|
|
|
|
const imageMetadata = await Image(src, {
|
|
|
|
|
widths: [...widths, null],
|
|
|
|
|
formats: [...formats, null],
|
|
|
|
|
outputDir: "_site/assets/images",
|
|
|
|
|
urlPath: "/assets/images",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const sourceHtmlString = Object.values(imageMetadata)
|
|
|
|
|
// Map each format to the source HTML markup
|
|
|
|
|
.map((images) => {
|
|
|
|
|
// The first entry is representative of all the others
|
|
|
|
|
// since they each have the same shape
|
|
|
|
|
const { sourceType } = images[0];
|
|
|
|
|
|
|
|
|
|
// Use our util from earlier to make our lives easier
|
|
|
|
|
const sourceAttributes = stringifyAttributes({
|
|
|
|
|
type: sourceType,
|
|
|
|
|
// srcset needs to be a comma-separated attribute
|
|
|
|
|
srcset: images.map((image) => image.srcset).join(", "),
|
|
|
|
|
sizes,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Return one <source> per format
|
|
|
|
|
return `<source ${sourceAttributes}>`;
|
|
|
|
|
})
|
|
|
|
|
.join("\n");
|
|
|
|
|
|
|
|
|
|
const getLargestImage = (format) => {
|
|
|
|
|
const images = imageMetadata[format];
|
|
|
|
|
return images[images.length - 1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const largestUnoptimizedImg = getLargestImage(formats[0]);
|
|
|
|
|
const imgAttributes = stringifyAttributes({
|
|
|
|
|
src: largestUnoptimizedImg.url,
|
|
|
|
|
width: largestUnoptimizedImg.width,
|
|
|
|
|
height: largestUnoptimizedImg.height,
|
|
|
|
|
alt,
|
|
|
|
|
loading: "lazy",
|
|
|
|
|
decoding: "async",
|
|
|
|
|
});
|
|
|
|
|
const imgHtmlString = `<img ${imgAttributes}>`;
|
|
|
|
|
|
|
|
|
|
const pictureAttributes = stringifyAttributes({
|
|
|
|
|
class: className,
|
|
|
|
|
});
|
|
|
|
|
const picture = `<picture ${pictureAttributes}>
|
|
|
|
|
${sourceHtmlString}
|
|
|
|
|
${imgHtmlString}
|
|
|
|
|
</picture>`;
|
|
|
|
|
|
|
|
|
|
return outdent`${picture}`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function officialImageShortcode(src, alt, sizes = "100vw") {
|
|
|
|
|
if (alt === undefined) {
|
|
|
|
|
// You bet we throw an error on missing alt (alt="" works okay)
|
|
|
|
|
throw new Error(`Missing \`alt\` on responsiveimage from: ${src}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (src.startsWith("/screenshots")) {
|
|
|
|
|
src = "./src/" + src;
|
|
|
|
|
} else {
|
|
|
|
|
src = "./projects/" + src;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log("officalImage: " + src + " " + alt);
|
|
|
|
|
|
|
|
|
|
let metadata = await Image(src, {
|
|
|
|
|
widths: [300, 600],
|
|
|
|
|
formats: ["webp", "jpeg"],
|
|
|
|
|
outputDir: "www/assets/images",
|
|
|
|
|
urlPath: "/assets/images",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(metadata);
|
|
|
|
|
|
|
|
|
|
let lowsrc = metadata.jpeg[0];
|
|
|
|
|
let highsrc = metadata.jpeg[metadata.jpeg.length - 1];
|
|
|
|
|
|
|
|
|
|
return `<picture>
|
|
|
|
|
${Object.values(metadata)
|
|
|
|
|
.map((imageFormat) => {
|
|
|
|
|
return ` <source type="${
|
|
|
|
|
imageFormat[0].sourceType
|
|
|
|
|
}" srcset="${imageFormat
|
|
|
|
|
.map((entry) => entry.srcset)
|
|
|
|
|
.join(", ")}" sizes="${sizes}">`;
|
|
|
|
|
})
|
|
|
|
|
.join("\n")}
|
|
|
|
|
<img
|
|
|
|
|
src="${lowsrc.url}"
|
|
|
|
|
width="${highsrc.width}"
|
|
|
|
|
height="${highsrc.height}"
|
|
|
|
|
alt="${alt}"
|
|
|
|
|
class="card__img"
|
|
|
|
|
loading="lazy"
|
|
|
|
|
decoding="async">
|
|
|
|
|
</picture>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function imageShortcodeSync(
|
|
|
|
|
src,
|
|
|
|
|
alt,
|
|
|
|
|
sizes = "100vw",
|
|
|
|
|
widths = [300, 600],
|
|
|
|
|
cls = "card__img"
|
|
|
|
|
) {
|
|
|
|
|
let options = {
|
|
|
|
|
widths: widths,
|
|
|
|
|
formats: ["webp", "jpeg"],
|
|
|
|
|
outputDir: "www/assets/images",
|
|
|
|
|
urlPath: "/assets/images",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (src.startsWith("/screenshots")) {
|
|
|
|
|
src = "./src/" + src;
|
|
|
|
|
} else {
|
|
|
|
|
src = "./projects/" + src;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// console.log("Sync: " + src + " " + alt);
|
|
|
|
|
// generate images, while this is async we don’t wait
|
|
|
|
|
Image(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(src, options);
|
|
|
|
|
return Image.generateHTML(metadata, imageAttributes);
|
|
|
|
|
}
|