I really like that the days of AMD and CommonJS are over. Native modules can be used in the browser.

Have a class and export it:

class Library{
  static sayHello(){
    alert('Hello');
  }
}

export default Library;

Import it elsewhere:

import Library from './lib.js'

Library.sayHello();

Reference only the entry point file:

  <script
    type="module"
    src="./js/script.js"
    async></script>

That’s it. Nice.

With HTTP2 loading more than a combined and minified script is not such an issue any more when used with compression.

ECMAScript import maps

This relatively new feature allows to specify a list of modules and control their names. These are the names used in import statements. It is a JSON object and must conform to the Import map JSON representation format. Here is an example:

{
  "imports": {
    "./lib.js": "./lib.js"
  }
}

At the moment of writing it is not possible to use external import maps, meaning you must provide the map in a script element and cannot reference a JSON file using the src attribute.

The result looks like this:

  <script
    type="importmap"
    nonce="NqIr5ziC">{"imports":{"./lib.js":"./lib.js"}}</script>

Content Security Policy

But what about security? Every javascript application written should have a CSP to prevent XSS. And how do they work with import maps?

Discussed here, and here, we can read that the usual suspect of CSP nonce and hash should be usable.

Sample

I created a sample in a GitHub repository with four different variants. Two each for nonce and hash, one with meta tag and the other with HTTP Header.

I just applied nonce and hash to the script tags and the meta tag based versions worked right away.

Fun fact: The behavior is different when using a hash.

In that case we additionally need to publish the integrity hash of each loaded module. This is done using a link tag with the rel="modulepreload" attribute.

  <link
    rel="modulepreload"
    href="./js/lib.js"
    integrity="sha512-c...">
  <script
    type="importmap"
    integrity="sha512-a...">{"imports":{"./lib.js":"./lib.js"}}</script>
  <script
    type="module"
    integrity="sha512-b..."
    src="./js/script.js"
    async></script>

It of course means that modules are not loaded on demand any more.

Preloading allows modules and their dependencies to be downloaded early, and can also significantly reduce the overall download and processing time. This is because it allows pages to fetch modules in parallel, instead of sequentially as each module is processed and its dependencies are discovered. Note however that you can’t just preload everything! What you choose to preload must be balanced against other operations that might then be starved, adversely affecting user experience. MDN

Beside that, mind your HTTP header size in your web server and Proxies.