11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
16.86k stars 487 forks source link

Minify HTML Output failing on <iframe> srcdoc #2834

Closed rocc-o closed 1 year ago

rocc-o commented 1 year ago

I'm minifying HTML output with Transform "html-minifier": "^4.0.0" plugin and "@11ty/eleventy": "^2.0.0"

//.eleventy.js

const htmlmin = require("html-minifier");

module.exports = function (eleventyConfig) {

eleventyConfig.addTransform("htmlmin", function(content, outputPath) {
if( outputPath && outputPath.endsWith(".html") ) {
  let minified = htmlmin.minify(content, {
    useShortDoctype: true,
    removeComments: true,
    collapseWhitespace: true
  });
  return minified;
}

et cetera...

Never had a problem. Now, when I'm trying to include an iframe with srcdoc attribute via components, like this:

{# includes/partials/components/articles-video-iframe.njk #}

    <iframe 
    title="{{ title_alt_text }}" 
    allowfullscreen 
    loading="lazy" 
          srcdoc="<style> body, .full { width: 100%; height: 100%; margin: 0; position: absolute; display: flex; justify-content: center; object-fit: cover } </style>
          <a href='{{ video_url }}' class='full'>

    <picture class='full'>

    <source
    media='(max-width: 360px)'
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w504.webp 504w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w572.webp 572w'
    type='image/webp'>

    <source
    media='(min-width: 361px) and (max-width: 428px)'
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w688.webp 688w'
    type='image/webp'>

    <source
    media='(min-width: 429px) and (max-width: 2560px)'
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w688.webp 688w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w1266.webp 1266w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w1360.webp 1360w'
    type='image/webp'>

    <source
    media='(max-width: 360px)'
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w504.jpg 504w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w572.jpg 572w'
    >

    <source
    media='(min-width: 361px) and (max-width: 428px)'
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w688.jpg 688w'
    >

    <img
    sizes='(min-width: 880px) 680px, 79.46vw'
    srcset='
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w688.jpg 688w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w1266.jpg 1266w,
    /assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w1360.jpg 1360w'
    src='/assets/images/{{ folder_articles_images }}/large-{{ image_number }}-w688.jpg'
    loading='lazy' class='full' width='688' height='344' alt='{{ alt_text }}' crossorigin='anonymous'>

    </picture>

          <svg role='img' version='1.1' viewBox='0 0 68 48' width='68px' style='position: relative;'>
              <title>Click to load video</title>
              <path d='M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z' fill='#f00'></path>
              <path d='M 45,24 27,14 27,34' fill='#fff'></path>
          </svg>
          </a>"
    ></iframe>

everything is minified but not what is inside the srcdoc attribute. I strongly suspect the single quotes which I am required to use inside the srcdoc attribute.

Is there any way to fix this?

AleksandrHovhannisyan commented 1 year ago

@rocc-o I believe you'll want to pull out your srcdoc and store it in a variable, use double quotes (not single) for HTML attributes, and escape the resulting HTML (e.g., with a custom filter or a built-in one) before rendering it. For example, my eleventy-plugin-code-demo plugin uses these lines of code to generate an iframe whose srcdoc contains HTML:

https://github.com/AleksandrHovhannisyan/eleventy-plugin-code-demo/blob/bf2139cd19211f29239ee88cfd9abc0cea9438b7/src/utils.js#L64-L71

j-f1 commented 1 year ago

I believe this is an issue with html-minifier (specifically https://github.com/kangax/html-minifier/issues/762) and not Eleventy itself.

rocc-o commented 1 year ago

@AleksandrHovhannisyan What a very nice plugin! I'll definitely try it tomorrow. Thanks so much! @j-f1 I see. But never implemented since 2016!

rocc-o commented 1 year ago

@AleksandrHovhannisyan I'm installing your plugin but I get:

[11ty] Eleventy CLI Fatal Error: (more in DEBUG output)
[11ty] 1. Error in your Eleventy config file '.eleventy.js'. You may need to run `npm install`. (via EleventyConfigError)   
[11ty] 2. Cannot find module 'lodash/escape'

So I tried npm i --save lodash.escape and even npm install but still the same error. Node v16.13.0; NPM 8.1.0

AleksandrHovhannisyan commented 1 year ago

@rocc-o Oh, that's strange! Let's move that particular discussion over to my repo: https://github.com/AleksandrHovhannisyan/eleventy-plugin-code-demo/issues/new?assignees=AleksandrHovhannisyan&labels=&template=bug_report.md&title=

rocc-o commented 1 year ago

Yes, thanks!