parcel-bundler / parcel

The zero configuration build tool for the web. πŸ“¦πŸš€
https://parceljs.org
MIT License
43.5k stars 2.27k forks source link

Using iframe `srcdoc` for lazy-loading leads to broken image. #8853

Open NessDan opened 1 year ago

NessDan commented 1 year ago

πŸ› bug report

This is a very popular way to lazy load a YouTube video.

The problem is that after Parcel runs on an HTML page doing this strategy of content-stuffing in the <iframe srcdoc="...">, the image that acts as the poster won't get compiled into the distribution, leading to a broken image.

Before Parcel After Parcel
image image

πŸŽ› Configuration (.babelrc, package.json, cli command)

"scripts": {
    "start": "parcel index.html"
}

πŸ€” Expected Behavior

It should parse over the image in question (/media/images/social-media-share.webp) and should update that reference and copy the file to the dist/ folder.

😯 Current Behavior

The reference to the image stays the same on the iframe and the image is never copied over to the /dist folder.

πŸ’ Possible Solution

Since this is a popular way to lazy load YouTube iframes, maybe parse and support this.

πŸ”¦ Context

Trying to parcel my site (all vanilla HTML / JS) so that I can tree-shake for things like Firebase. Can't because this breaks the homepage iframe.

πŸ’» Code Sample

Make sure to create an image file named /social-media-share.webp as a sibling to the HTML file below:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>
      Example of a YouTube video embedded with a custom preview image
    </title>
  </head>

  <body>
    <!-- https://css-tricks.com/lazy-load-embedded-youtube-videos/ -->
    <iframe
      srcdoc="<style>#tv{background:red;border-radius:50% / 10%;color:#FFF;font-size:2em;height:3em;margin:auto;padding:0;position:absolute;text-align:center;text-indent:.1em;transition:all 150ms ease-out;width:4em;top:0;bottom:0;left:0;right:0;scale:.5}#tv::before{background:inherit;border-radius:5% / 50%;bottom:9%;content:'';left:-5%;position:absolute;right:-5%;top:9%}#tv::after{border-style:solid;border-width:1em 0 1em 1.732em;border-color:transparent transparent transparent white;content:' ';font-size:.75em;height:0;margin:-1em 0 0 -.75em;top:50%;position:absolute;width:0}img{position:absolute;width:100%}#wrap{position:absolute;top:0;left:0;bottom:0;right:0;width:100%;overflow:hidden}</style><div id=wrap><a href=https://www.youtube.com/embed/4rDczKkEeYA?autoplay=1><img src=/social-media-share.webp alt='The Edgeguard Adapter - Play Switch Games with a Keyboard (Kickstarter Video)'><div id='tv'></div></a></div>"
      loading="lazy"
      title="The Edgeguard Adapter - Play Switch Games with a Keyboard (Kickstarter Video)"
      class="kickstarter-video"
      src="https://www.youtube.com/embed/4rDczKkEeYA"
      frameborder="0"
      allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
    ></iframe>
  </body>
</html>

🌍 Your Environment

Software Version(s)
Parcel 2.8.3
Node 18.14.2
npm/Yarn 9.5.0
Operating System Windows 11
mischnic commented 1 year ago

In theory Parcel should process srcdoc with this change:

diff --git packages/transformers/html/src/inline.js packages/transformers/html/src/inline.js
index 9cecc7d2f..b542094ca 100644
--- packages/transformers/html/src/inline.js
+++ packages/transformers/html/src/inline.js
@@ -146,6 +146,40 @@ export default function extractInlineAssets(
       }
     }

+    if (node.tag === 'iframe') {
+      let value = node.attrs?.srcdoc;
+      if (value != null) {
+        if (!node.attrs) {
+          node.attrs = {};
+        }
+        // allow a script/style tag to declare its key
+        if (node.attrs['data-parcel-key']) {
+          parcelKey = node.attrs['data-parcel-key'];
+        }
+        node.attrs['data-parcel-key'] = parcelKey;
+        asset.setAST(ast); // mark dirty
+
+        asset.addDependency({
+          specifier: parcelKey,
+          specifierType: 'esm',
+        });
+
+        parts.push({
+          type: 'html',
+          content: value,
+          uniqueKey: parcelKey,
+          bundleBehavior: 'inline',
+          meta: {
+            type: 'tag',
+            // $FlowFixMe
+            node,
+            startLine: node.location?.start.line,
+          },
+        });
+        asset.setAST(ast);
+      }
+    }
+
     // Process inline style attributes.
     let attrs = node.attrs;
     let style = attrs?.style;

But that leads to an infinite loop currently...