Support Import Maps in any browser with es-module-shims

JavaScript Import Maps – which I wrote about earlier here — are a great addition to the web. Unfortunately they’re only supported in Chromium 89+.

Thankfully there’s a polyfill available: es-module-shims. As long as your browser has baseline ES Module Support (Chrome 61+, Firefox 60+, Safari 10.1+, and Edge 17+) the polyfill will work.

To use it, include the script and then write your first import map. The polyfill will automagically do its thing.

<script async src="https://unpkg.com/es-module-shims@0.13.1/dist/es-module-shims.js"></script>

<script type="importmap">
{
  "imports": {
    "app": "./src/app.js"
  }
}
</script>
<script type="module">
  import app from 'app';
  // …
</script>

Also polyfills other things such as Dynamic Imports … NEAT!

es-module-shims (GitHub) →

Execute ES Modules on the CLI

Jonathan Neal shared this little snippet on Twitter:

Here’s the code:

":" //#;exec /usr/bin/env node --input-type=module - "$@" < "$0"

import process from 'process';
const { argv } = process
console.log(argv)

Save your file as command.js and you can run bash command.js on the shell.

~

What intrigued me here was this special shebang at the very top. I did expect to see #!/usr/bin/env node in there, but not the script to be fed into node as an argument again … weird, right?

Going down the rabbit hole, I found this post from 2014 that explains the funky version. There’s two commands in there, split by a ;

  1. ":" //#
  2. exec /usr/bin/env node --input-type=module - "$@" > "$0"

The first part does nothing beyond expanding arguments (//) and a no-op. The # indicates the start of a comment, but the comment itself remains empty.

The second part feeds the scriptname ($0) and the rest of the arguments ($@) into the node binary. Via the --input-type=module flag, node is configured to treat the file itself as an ES Module.

~

Digging at bit deeper I learned that there are three ways to configure node to treat your file as an ES Module:

  1. Use the --input-type=module

  2. Give your file the .mjs extension

  3. Place a package.json with the follow contents near the file

    {
      "type": "module"
    }

I like the --input-type=module version with the shebang trick Jonathan shared there, as it requires no extra files (package.json) and allows you to keep the .js extension (or even drop it entirely).

~

🔥 Like what you see? Want to stay in the loop? Here's how:

Control the behavior of JavaScript imports with Import Maps

Shipping in Chrome 89 are Import Maps, which allows control over what URLs get fetched when importing modules.

Let’s take a look at this very welcome addition.

When importing ES Modules (on the web), you need to refer to them using their full filenames or URLs:

import moment from "/js/moment/moment.js";
import { partition } from "/js/lodash-es/lodash.js";

In a Node environment however, you would write the snippet above as follows:

import moment from "moment";
import { partition } from "lodash";

(Node will map things to the /node_modules folder all by itself)

👉 So depending on where you run your code, your import statements need to change. Meh.

~

Thankfully Import Maps provide a solution to this. They allow you to specify which file/URL should be loaded when importing a package.

{
  "imports": {
    "moment": "/js/moment/moment.js",
    "lodash": "/js/lodash-es/lodash.js"
  }
}

With this in place the browser can handle import { partition } from "lodash" just fine, as it will load the file /js/lodash-es/lodash.js. 🎉

💡 With services like Skypack in place I can already see tools pop up that would automate the generation of such an import map based on a package[-lock].json that you feed it.

~

An import map is a tad of JSON file which you need to put it in a script[type="importmap"] element:

<script type="importmap">
{
  "imports": {
    "moment": "/js/moment/moment.js",
    "lodash": "/js/lodash-es/lodash.js"
  }
}
</script>

☝️ In the future it’ll also be possible to to put your import map into an external file and load it by specifying an src

<script type="importmap" src="import-map.importmap"></script>

To play nice with Content-Security Policy (CSP) the server needs to send the proper MIME-type though:

application/importmap+json

For more advanced usage, it’s also possible to add scoping.

~

These Import Maps sure are a very welcome addition. Unfortunately browser support is currently limited to Chrome only.

Update 2021.09.13: There’s a polyfill available for browsers that don’t support Import Maps: es-module-shims.

Data on support for the import-maps feature across the major browsers from caniuse.com

💡 Shown above is a dynamic CanIUse.com image, showing an always up-to-date support table. By the time you are reading this browser support might have become better.

Let’s hope other browsers follow suit soon. Relevant bugs to track:

~

To help spread the contents of this post, feel free to retweet its announcement tweet:

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

The several ways to import React

With the release of React 17 we also had to change the way we import React:

Kent C. Dodds goes over all ways to import React into your code, and explains why the good ole import React from "react" no longer works and why he went for import * as React from "react"

Importing React Through the Ages →

Skypack — Load optimized npm packages with no install and no build tools

I was delighted to read that CodePen now has built-in support for Skypack. This is a huge step forward to working with packages on CodePen. Great addition to the product!

But what exactly is Skypack? Well, it’s the successor to the aforementioned Pika CDN with some extra juice: Skypack not only serves packages that export ES Modules, it also converts packages that export older CommonJS Modules to ES Modules while at it.

Ever tried to load JavaScript from a CDN and realized that it doesn’t work in a browser without a bundler? Skypack operates like your favorite CDN but with an important difference: packages are preoptimized for browser use.

But Skypack doesn’t stop there: it handles minification, browser polyfilling, gzip/brotli, HTTP/3, caching, and more!

Skypack is free to use for personal and commercial purposes, forever. The basic CDN is production-ready and is backed by Cloudflare, Google Cloud, and AWS. We’re fully committed to building a core piece of infrastructure you can rely on.

Fred K. Schott, one of its authors, announced it this summer at ESNext Conf — a virtual conference which I also gave a talk at.

(Fast forward to the 18 minute mark to get to the Skypack part)

Here’s a simple Confetti example:

See the Pen
Confetti
by Chris Coyier (@chriscoyier)
on CodePen.

Neat, right?

Skypack — Load optimized npm packages with no install and no build tools →
Skypack + CodePen →

Worth It: Modern JS edition

“Worth It: Modern JS edition” is a small tool to analyze how much less JavaScript is downloaded in modern browsers as a result of it using the module/nomodule pattern.

Worth It: Modern JS edition →

💡 The module/nomodule pattern is a technique to ship ES2015 modules to browsers that support, whilst also keeping older browsers (that don’t support it) happy. It was covered here on bram.us back in July 2017

jQuery, now using ECMAScript module syntax

A nice commit migrating away from AMD Modules to ECMAScript modules recently landed in the jQuery repo. Once published as a new release, you’ll be able to actually import $ using the modern ES module syntax:

import $ from "jquery";
$('#message').text('Hi from jQuery!');

Handy if you’re modernizing a legacy project that still uses jQuery.

💡 Up until now I’ve been either including jQuery separately or have been relying on webpack-merge-and-include-globally to include jQuery in my Webpack build.

Pika CDN – A CDN for modern JavaScript

Yesterday the aforementioned Pika announced some new things. One of the things that stood out is Pika CDN:

The Pika CDN was built to serve the 60,000+ npm packages written in ES Module (ESM) syntax. This module syntax runs natively in the browser, so you can build for the web without a bundler.

With our CDN, package authors can distribute more modern, unminified packages without worrying about how to serve them directly. Instead, our nifty package-builder automatically resolves each package — and any legacy sub-dependencies — into a single, minified, ready-to-import JavaScript file.

Nice! Do note that versioning is limited:

🤔 I do wonder if something like Subresource Integrity will land for ES Modules … and oh, don’t we need to self-host our assets? Computering is hard, ugh! #nuance

Pika CDN →

Related: The previously mentioned create-es-react-app also touches directly importing ES Modules

create-es-react-app – A create-react-app like template using only es-modules (no build step).

create-es-react-app is a create-react-app like template demonstrating how far you can get creating progressive web apps (using react) with no build step. It takes advantage of static and dynamic imports which enables you to break up your app into small reusable ES modules that are compiled for you by the browser at run time.

TIL: You can directly import ES modules from unpkg:

import { React, ReactDOM } from 'https://unpkg.com/es-react@16.8.30/index.js';
import htm from 'https://unpkg.com/htm@2.1.1/dist/htm.mjs'
import csz from 'https://unpkg.com/csz@0.1.2/index.js'

🤔 ES Modules? This video by Heydon Pickering has got you covered.

create-es-react-app Source (GitHub) →
create-es-react-app demo →