loading...
Cover image for uwc part 6: Compiling unbundles w/ Polymer CLI

uwc part 6: Compiling unbundles w/ Polymer CLI

btopro profile image Bryan Ollendyke ・4 min read

I know, I know. You read the title and came here to say "YOU MONSTER, POLYMER!? HOW DARE YOU". But hear me out. I'm not talking about Polymer() or PolymerElement but their CLI build tool.

Yes, this is deprecated, but here's why I still use it and will continue to until I can find a replacement for this:

  • It has a simple, documented configuration that lives in 1 file (polymer.json)
  • It can be configured to unbundle so that it compiles the files in place and places them in a similar final location

GitHub logo elmsln / unbundled-webcomponents

"The magic script" - Unbundled Web components for lazy-hydration routines hitting maximal browsers

polymer.json

Here's the polymer.json configuration we use

{
  "entrypoint": "src/index.html",
  "extraDependencies": [
    "node_modules/fetch-ie8/fetch.js",
    "node_modules/promise-polyfill/dist/polyfill.min.js",
    "node_modules/web-animations-js/*.map",
    "node_modules/web-animations-js/*.js",
    "node_modules/@lrnwebcomponents/wc-autoload/wc-autoload.js",
    "node_modules/@lrnwebcomponents/dynamic-import-registry/dynamic-import-registry.js",
    "node_modules/@webcomponents/webcomponentsjs/*.js",
    "node_modules/@webcomponents/webcomponentsjs/bundles/*.js",
    "!node_modules/@webcomponents/webcomponentsjs/gulpfile.js"
  ],
  "sources": [],
  "builds": [
    {
      "name": "es6",
      "browserCapabilities": ["es2017", "modules"],
      "js": {
        "minify": false,
        "transformImportMeta": true
      },
      "css": {
        "minify": false
      },
      "html": {
        "minify": false
      },
      "bundle": false
    },
    {
      "name": "es6-amd",
      "browserCapabilities": [
        "es2015"
      ],
      "js": {
        "minify": false,
        "transformModulesToAmd": true,
        "transformImportMeta": true
      },
      "css": {
        "minify": false
      },
      "html": {
        "minify": false
      },
      "bundle": false
    },
    {
      "name": "es5-amd",
      "js": {
        "compile": true,
        "minify": true,
        "transformModulesToAmd": true,
        "transformImportMeta": true
      },
      "css": {
        "minify": true
      },
      "html": {
        "minify": true
      },
      "bundle": false
    }
  ],
  "moduleResolution": "node",
  "npm": true,
  "lint": { 
    "rules": ["polymer-3"]
  }
}

This is highly readable but the key concepts to note is that the "name" property is what the folder is in the end. The "js" settings allow for single babel translations like converting import statements to define / AMD format as well as "transformImportMeta" which allows you to safely use import.meta.url in your JS code (a common convention in web components to discover relative paths to things on the front end).

Polymer build hits an entrypoint (defined here as src/index.html). In this file you'll see <script type="module" src="./app.js"></script> so then anything referenced in this entry point file will get compiled https://github.com/elmsln/unbundled-webcomponents/blob/master/app/src/app.js

Let's take an example of how this works with a real example if we implemented this (you can find a production example here from HAX11ty https://github.com/elmsln/hax11ty/blob/master/unbundled-webcomponents/app/src/app.js)

import "@lrnwebcomponents/h-a-x/h-a-x.js"; will be resolved to something like app/dist/build/{BUILDNAME}/node_modules/@lrnwebcomponents/h-a-x/h-a-x.js which you can see a copy of here - https://github.com/elmsln/unbundled-webcomponents/blob/master/app/dist/build/es6/node_modules/%40lrnwebcomponents/h-a-x/h-a-x.js

Because these files are compiled in place, the previous article about client side build detection will resolve that {BUILDNAME} at run time to either serve the user /es6/ (we label es8 this cause really the folder name is es6+ and I don't want a plus sign), /es6-amd/, or /es5-amd/.

Because they are compiled in place, we're able to leverage the Dynamic Element Hydration concept.

The "Magic Script" ensures all of this is orchestrated into a simple copy and paste methodology and whalla! We have an unbundling routine that compiles to 3 targets, lazy loads any tag it notices in the DOM, and requires minimal technical ability to integrate into new applications / CMSs via copy and paste of two-lines.

This has created a sustainable pipeline for development for our team in that we follow this workflow:

  • All elements worked on in our mono-repo / PRs accepted there - https://github.com/elmsln/lrnwebcomponents
  • After local testing, the configuration for our production version of unbundled-webcomponents (seen in this polymer.json configuration for HAXcms - https://github.com/elmsln/HAXcms/blob/master/polymer.json#L13-L17 ) ensures that any new elements are automatically discovered
  • I run a build script locally, it compiles everything, pushes it out to our CDNs and project repos (hax11ty, haxcms, elmsln, hax-all-the-things, cdn, and a mirror)
  • All of our properties benefit from the pipeline improvements because when video-player.js gets updated on our CDN (as example), then every website at penn state leveraging that CDN / copy of the file will benefit.
  • Because we always use "the magic script" and because the source for that also lives on the CDN, we can enhance the performance, timing, polyfill support and anything else we need without ever touching the end point integrations.

Bonus

Now I'll get into how HAX / HAXcms directly leverage this capability in order to dynamically import the pieces they need, allowing for faster paints than would otherwise be possible with an unknown amount of functionality.

It gets a bit into the weeds of my brain for our flag ship products so I'll say it's Bonus beyond the main arc of this article :)

Posted on by:

btopro profile

Bryan Ollendyke

@btopro

@elmsln @haxcamp @btopro #HAXTheWeb #drupal #webcomponents #edtech ✻ Full stack unicorn Adjunct professor teaching about webdev, ethics, and everything in between

Discussion

markdown guide