Webpack bundles not executing when inline

Viewed 57

Short story: when I inline into HTML the output of WebPack, the scripts don't run; but if they're included via <script src= they work fine. How do I make WebPack-ified scripts work inline?

Long Story

I have a little script that previously I had inlined in my static HTML for a landing page:

"use strict";
let floatingCta,
    options = { root: null, rootMargin: "0px", threshold: 1 },
    hasFloatingCtaIntersected = !1;

function createObserverAtfCta() {
    null !== floatingCta
      && new IntersectionObserver(handleAtfCtaIntersect,
        options).observe(floatingCta);
}

function handleAtfCtaIntersect(t, e) {
    t.forEach(function (t) {
        hasFloatingCtaIntersected && (667 < window.innerHeight
          ? t.target.classList.toggle("pin-bottom")
          : t.target.classList.toggle("pin-top"),
          e.unobserve(t.target)),
        (hasFloatingCtaIntersected = !0);
    }); 
}

document.addEventListener("DOMContentLoaded", function () {
    (floatingCta = document.querySelector(".atf.cta")), createObserverAtfCta();
});

When inlined as above, it works as expected. Here's a snippet showing it working:

.button {
  display: inline-block;
  padding: 0.5em;
  background-color: #333;
  color: #fff;
}

.cta.atf {
  z-index: 9999;
}

.pin-top {
  top: 0
}

.pin-bottom,
.pin-top {
  position: fixed;
  width: calc(100vw - 2em)
}

.pin-bottom {
  bottom: 0
}


/* --Setting the scroll root - because the snippet puts the content in an iframe-- */

.scroller {
  height: 100vh;
  width: 100%;
  overflow: auto;
}


/* ----- */
<!doctype html>
<html lang="en-US" class="no-js">

<head>
<script>
"use strict";
var floatingCta,
  options = {
    root: null,
    rootMargin: "0px",
    threshold: 1
  },
  hasFloatingCtaIntersected = !1;

function createObserverAtfCta() {
  options.root = document.querySelector('[data-scroller]')
  null !== floatingCta && new IntersectionObserver(handleAtfCtaIntersect, options).observe(floatingCta);
}

function handleAtfCtaIntersect(t, e) {
  t.forEach(function(t) {
    hasFloatingCtaIntersected && (667 < window.innerHeight ? t.target.classList.toggle("pin-bottom") : t.target.classList.toggle("pin-top"), e.unobserve(t.target)), (hasFloatingCtaIntersected = !0);
  });
}

document.addEventListener("DOMContentLoaded", function() {
  (floatingCta = document.querySelector(".atf.cta")),
  createObserverAtfCta();
});
</script>
</head>

<body>
  <div class="scroller" data-scroller>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus eleifend lectus id elit suscipit auctor. Cras pellentesque lacinia rhoncus. Curabitur in fringilla arcu, vel maximus arcu. In pharetra justo vel massa porta, quis maximus massa luctus.
      Nam consectetur, nisl eu suscipit volutpat, mi ex mattis nunc, id sollicitudin dui urna quis dui. Etiam leo magna, varius quis dolor a, tincidunt ornare dolor. Sed vel pulvinar diam, vel vulputate nibh. Sed et condimentum mauris. Curabitur eu dui
      vitae diam euismod imperdiet. Phasellus id consequat orci. Pellentesque sit amet commodo turpis. Aliquam convallis, mauris in ultrices cursus, risus erat fermentum metus, eu consectetur dolor nisi quis turpis. Maecenas sollicitudin ullamcorper leo
      et posuere. Vivamus aliquam nibh id elementum rhoncus.</p>

    <div class="atf cta" style="display: flex;">
      <div class="button">
        Scroll to watch me Float
      </div>
    </div>

    <p>Morbi scelerisque ex cursus nisl ultricies elementum. Nunc ut sapien sit amet mi feugiat fermentum laoreet at urna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed condimentum risus vehicula purus ornare fringilla. Fusce ac augue lacus.
      Duis sit amet nibh vel eros pretium maximus. Nullam non facilisis lacus. Aliquam finibus euismod fringilla. Nam eget ornare neque. Sed auctor eros ut maximus finibus. Duis sollicitudin sollicitudin urna a semper. Praesent vel sem et ligula imperdiet
      fringilla vel condimentum nibh.</p>

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam in est et lectus lacinia elementum egestas at velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla luctus blandit ex ut consequat.
      Integer eros purus, pharetra a diam in, venenatis rutrum purus. Aenean eleifend commodo malesuada. Suspendisse non gravida ipsum, sit amet pretium ipsum. Aenean feugiat, augue porttitor viverra mollis, nulla urna porta enim, in pellentesque mauris
      nulla a velit. Donec in tempus ante, a finibus justo. Integer blandit convallis arcu at aliquam. In in interdum arcu. Cras vel velit at elit ullamcorper consequat a nec orci. Duis vitae neque dignissim, consequat justo at, posuere ante. Suspendisse
      purus sapien, fermentum interdum tempus ut, sagittis id enim.</p>

    <p>Fusce facilisis, diam eget pellentesque pharetra, augue leo efficitur ligula, non ultrices tortor ante ac purus. Aliquam dictum turpis a nibh venenatis, ut congue sem consectetur. Cras aliquam dui id nisi cursus fringilla. Ut non velit vel arcu venenatis
      efficitur a at nisl. Praesent euismod varius dignissim. Morbi euismod porta quam eu euismod. Donec at gravida neque. Cras pharetra interdum odio egestas mattis. Aliquam ut elit nec libero molestie porttitor sit amet sit amet eros. Etiam condimentum
      ante eu nibh tempor commodo. Sed ornare tellus in est tincidunt maximus. Aliquam facilisis laoreet sapien tristique euismod. Suspendisse rhoncus ex non est convallis eleifend. Donec tincidunt ex quis libero tempor viverra. Fusce imperdiet pellentesque
      feugiat. Nulla tristique, neque efficitur blandit dignissim, nulla lectus luctus lectus, at auctor risus sem non libero.</p>

    <p>Sed id molestie mi. Aliquam ante orci, ornare vitae placerat sit amet, tincidunt viverra nisi. Fusce tempor tellus vitae leo vestibulum, id sagittis mi finibus. Aliquam neque quam, lobortis a nibh nec, porttitor blandit massa. Nulla auctor lacinia
      ornare. Quisque id dolor quis massa ullamcorper molestie ut interdum magna. Fusce ante ligula, tempus in mattis ut, feugiat ac nisi. Etiam eleifend elementum dui vitae viverra. Praesent fringilla pharetra nisi, nec hendrerit mi dictum et. Curabitur
      suscipit purus in orci lobortis dignissim. Nulla rhoncus sodales eros ac placerat. Aenean vel risus augue. Donec iaculis convallis risus, quis condimentum est suscipit eget. Morbi ipsum eros, viverra id dolor vel, euismod mattis libero. Nunc iaculis
      euismod diam, sed condimentum nulla ultricies nec.</p>
  </div>
</body>

</html>

I've pulled it out, to run it through WebPack, primarily so I can both use Babel to transpile it to work in more browsers and to minify it. The ugly, minified, babel-ed output of Webpack is at the end of this question.

I'm putting it back in the static HTML with HtmlWebpackPlugin. If I just let WebPack and HtmlWebpackPlugin do their thing, they inject a script tag at the end of the body:

<script defer src="./public/inlineStickycta.bundle.js></script>

and that also works fine.

However, I want it back inline. So, I've taken a hint from the Writing Your Own Templates section of the HtmlWebpackPlugin docs, where it says,

compilation: the webpack compilation object. This can be used, for example, to get the contents of processed assets and inline them directly in the page, through compilation.assets[...].source() (see the inline template example).

Their example is in pug, but I've converted it to the standard lodash templating syntax, and modified it slightly so it doesn't inline all WebPack bundles:

<!DOCTYPE html>
<html lang="en-US" class="no-js">
  <head>
    <% htmlWebpackPlugin.options.inlineHeadScripts.forEach((script) => { %>
    <script>
      <%
        const re = new RegExp(script);
        const target = htmlWebpackPlugin.files.js.filter((file) =>
           file.match(re))[0];
      %>
        <%= compilation.assets[target
              .substr(htmlWebpackPlugin.files.publicPath.length)].source()
        %>
    </script>
    <% }) %>

This successfully inlines the WebPack-ified script between <script></script> tags in the head of the HTML file; however, the script doesn't execute. I have a half-dozen other scripts with which I want to do this, and the behavior is the same. They work fine if called from src but don't do anything if inlined in <script> tags.

EDIT: I inserted a working snippet above, and tried to insert a failing snippet here ... but I can't actually make it not work in the snippet editor. :( Nevertheless, it does not work in my dev environment, so if someone is trying to help with this, maybe put it on your own web server and see if it fails there.

Does WebPack do something to make it so they won't run inline as part of its bundling process? If so, what incantation do I need to use to make it not do that? If not, can someone take a look at the uglified script at end and see why it might not be executing?

Here's my full webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// Homemade plugin to prevent HtmlWebpackPlugin from both inlining and adding a `src` tag.
const HtmlWebpackFilter = require('./src/htmlWebpackFilter');

module.exports = (env, argv) => {
  console.log('Webpack mode: ', argv.mode);

  const config = {
    entry: {
      'prague-a': [
        path.resolve(__dirname, './src/sendtolog.js'),
        path.resolve(__dirname, './src/webvitals.js')
      ],
      inlineWebvitals:  path.resolve(__dirname,
        './node_modules/web-vitals/dist/polyfill.js'),
      inlineTwitterpixel:  path.resolve(__dirname, './src/twitterpixel.js'),
      inlineStickycta:  path.resolve(__dirname, './src/stickycta.js')
    },
    module: {
      rules: [
        // JavaScript
        {
          test: /\.(js)$/,
          exclude: /node_modules/,
          use: ['babel-loader']
        },
        // Replace placeholders
        {
          test: /\.(js)$/,
          exclude: /node_modules/,
          loader: 'string-replace-loader',
          options: {
            search: '__API_SERVER__',
            replace: ('production' === argv.mode)
              ? '' : 'https://localhost:9293',
            flags: 'g'
          }
        },
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: path.resolve(__dirname, './views/prague-a.ejs'),
        filename: path.resolve(__dirname, 'prague-a.html'),
        // passed as options to the EJS template
        inlineHeadScripts: [ 'inlineWebvitals', 'inlineTwitterpixel',
          'inlineStickycta' ]
      new HtmlWebpackFilter([ 'inlineWebvitals', 'inlineTwitterpixel',
        'inlineStickycta' ])
      // new CleanWebpackPlugin()
    ],
    output: {
      path: path.resolve(__dirname, './public'),
      filename: '[name].bundle.js'
    },
    // temporarily comment out, in case it's not working because of code-splitting
    // optimization: {
    //   splitChunks: {
    //     chunks: 'all'
    //   },
    //   runtimeChunk: true
    // },
    devtool: ('production' === argv.mode) ? false : 'eval',
    mode: argv.mode
  }

  return config;
};

Ugly/minified WebPack output:

!function(){"use strict";var e,t,n,o,r={root:null,rootMargin:"0px",threshold:1},s=!1,l=!1,c=!1,i=0;function u(){null!==e&&new IntersectionObserver(a,r).observe(e)}function a(e,t){e.forEach((function(e){s&&(667<window.innerHeight?e.target.classList.toggle("pin-bottom"):e.target.classList.toggle("pin-top"),t.unobserve(e.target)),s=!0}))}function d(t){t.forEach((function(){l&&("none"===e.style.display?e.style.display="flex":e.style.display="none"),l=!0}))}function f(t){t.forEach((function(){0<i&&0==i%2&&(e.classList.remove("pin-bottom","pin-top"),s=!1,u()),i++}))}function p(t){t.forEach((function(){c&&e&&("none"===e.style.display?e.style.display="flex":e.style.display="none"),c=!0}))}document.addEventListener("DOMContentLoaded",(function(){e=document.querySelector(".atf.cta"),u(),null!==(t=document.querySelector(".bonus-button"))&&new IntersectionObserver(d,r).observe(t),null!==(n=document.querySelector(".check.aida-a.atf"))&&new IntersectionObserver(f,{root:null,rootMargin:"0px",threshold:0}).observe(n),null!==(o=document.querySelector(".purchase-block"))&&new IntersectionObserver(p,{root:null,rootMargin:"0px",threshold:.1}).observe(o)}))}();
0 Answers
Related