11require ( 'dotenv' ) . config ( ) ;
22const Webmentions = require ( "eleventy-plugin-webmentions" ) ;
33const pluginRss = require ( "@11ty/eleventy-plugin-rss" ) ;
4+ const Image = require ( "@11ty/eleventy-img" ) ;
45const htmlmin = require ( "html-minifier" ) ;
6+ const outdent = require ( "outdent" ) ;
57
6- module . exports = function ( eleventyConfig ) {
8+ /** Maps a config of attribute-value pairs to an HTML string
9+ * representing those same attribute-value pairs.
10+ */
11+ const stringifyAttributes = ( attributeMap ) => {
12+ return Object . entries ( attributeMap )
13+ . map ( ( [ attribute , value ] ) => {
14+ if ( typeof value === 'undefined' ) return '' ;
15+ return `${ attribute } ="${ value } "` ;
16+ } )
17+ . join ( ' ' ) ;
18+ } ;
19+
20+ // shortcode for using 11ty's Image plugin
21+ // from https://www.aleksandrhovhannisyan.com/blog/eleventy-image-plugin/
22+ const imageShortcode = async (
23+ src ,
24+ alt ,
25+ className = undefined ,
26+ widths = [ 400 , 800 , 1280 ] ,
27+ formats = [ 'webp' , 'jpeg' ] ,
28+ sizes = '100vw'
29+ ) => {
30+ const imageMetadata = await Image ( src , {
31+ widths : [ ...widths , null ] ,
32+ formats : [ ...formats , null ] ,
33+ outputDir : '_site/media/images' ,
34+ urlPath : '/media/images' ,
35+ } ) ;
36+ const sourceHtmlString = Object . values ( imageMetadata )
37+ // Map each format to the source HTML markup
38+ . map ( ( images ) => {
39+ // The first entry is representative of all the others
40+ // since they each have the same shape
41+ const { sourceType } = images [ 0 ] ;
42+
43+ // Use our util from earlier to make our lives easier
44+ const sourceAttributes = stringifyAttributes ( {
45+ type : sourceType ,
46+ // srcset needs to be a comma-separated attribute
47+ srcset : images . map ( ( image ) => image . srcset ) . join ( ', ' ) ,
48+ sizes,
49+ } ) ;
50+
51+ // Return one <source> per format
52+ return `<source ${ sourceAttributes } >` ;
53+ } )
54+ . join ( '\n' ) ;
55+
56+ const getLargestImage = ( format ) => {
57+ const images = imageMetadata [ format ] ;
58+ return images [ images . length - 1 ] ;
59+ }
60+
61+ const largestUnoptimizedImg = getLargestImage ( formats [ 0 ] ) ;
62+ const imgAttributes = stringifyAttributes ( {
63+ src : largestUnoptimizedImg . url ,
64+ width : largestUnoptimizedImg . width ,
65+ height : largestUnoptimizedImg . height ,
66+ alt,
67+ loading : 'lazy' ,
68+ decoding : 'async' ,
69+ } ) ;
70+ const imgHtmlString = `<img ${ imgAttributes } >` ;
71+
72+ const pictureAttributes = stringifyAttributes ( {
73+ class : className ,
74+ } ) ;
75+ const picture = `<picture ${ pictureAttributes } >
76+ ${ sourceHtmlString }
77+ ${ imgHtmlString }
78+ </picture>` ;
79+
80+ return outdent `${ picture } ` ;
81+ } ;
82+
83+ module . exports = function ( eleventyConfig ) {
784 // 11ty plugins
885 eleventyConfig . addPlugin ( pluginRss ) ;
986 eleventyConfig . addPlugin ( Webmentions , {
1087 domain : "benkutil.com" ,
1188 token : process . env . WEBMENTIONS_TOKEN
12- } ) ;
13-
89+ } ) ;
90+
91+ // 11ty shortcodes
92+ eleventyConfig . addShortcode ( 'image' , imageShortcode ) ;
93+
1494 // run these configs in production only
15- if ( process . env . ELEVENTY_ENV === 'production' ) {
16- eleventyConfig . addTransform ( "htmlmin" , function ( content , outputPath ) {
95+ if ( process . env . ELEVENTY_ENV === 'production' ) {
96+ eleventyConfig . addTransform ( "htmlmin" , function ( content , outputPath ) {
1797 // find html files
18- if ( outputPath && outputPath . endsWith ( ".html" ) ) {
98+ if ( outputPath && outputPath . endsWith ( ".html" ) ) {
1999 // configure html-minify
20100 let minified = htmlmin . minify ( content , {
21101 useShortDoctype : true ,
@@ -25,10 +105,10 @@ module.exports = function(eleventyConfig) {
25105 } ) ;
26106 return minified ;
27107 }
28-
108+
29109 return content ;
30110 } ) ;
31-
111+
32112 }
33113
34114 // Directory changes
0 commit comments