With Safari 16.4 recently adding support for import maps, JavaScript developers can now use import maps in all modern browsers. Older browsers can use a polyfill. Import maps bring better module resolution for JavaScript applications.
An import map allows resolving module specifiers in ad-hoc ways. With import maps, developers can separate the referencing of module dependencies from their location (on disk or a remote server). Developers can use bare module specifiers to import dependencies (e.g., lodash
in import { pluck } from "lodash";
). Dedicated tooling can handle the mapping between dependencies and their location without touching the application code.
As an immediate result of cross-browser support, the JSPM CLI has been relaunched a few days ago as an import map package management tool. The JSPM CLI for instance automatically outputs the following import map for the lit
library:
<script type="importmap">
{
"imports": {
"lit": "https://ga.jspm.io/npm:lit@2.7.0/index.js"
},
"scopes": {
"https://ga.jspm.io/": {
"@lit/reactive-element": "https://ga.jspm.io/npm:@lit/reactive-element@1.6.1/reactive-element.js",
"lit-element/lit-element.js": "https://ga.jspm.io/npm:lit-element@3.3.0/lit-element.js",
"lit-html": "https://ga.jspm.io/npm:lit-html@2.7.0/lit-html.js",
"lit-html/is-server.js": "https://ga.jspm.io/npm:lit-html@2.7.0/is-server.js"
}
}
}
</script>
Guy Bedford, core open source contributor to the JSPM CLI and maintainer of the import maps polyfill, explained how import maps can improve tooling and developer experience when using JavaScript modules:
JSPM respects
package.json
version ranges and supports all the features of Node.js module resolution in a browser-compatible way. It supports arbitrary module URLs and CDN providers e.g. by just adding--provider unpkg
to the install command (or even localnode_modules
mappings via--provider nodemodules
).
Better apps are written when there are fewer steps between the developer and their tools, fewer steps between development and production, and fewer steps between applications and end-users.
Alternatively, developers can provide the import maps manually and remove the need for build tools in some cases. The Lit team provided such a use case on Twitter (see online playground). One Twitter user commented:
Frontend without needing to install npm is something I’ve dreamed of for years.
The import map WhatWG specification details a mechanism to resolve module specifiers based on the content of two fields (imports
and scopes
) passed as a JSON object. The following import map
{
"imports": {
"a": "/a-1.mjs",
"b": "/b-1.mjs",
"c": "/c-1.mjs"
},
"scopes": {
"/scope2/": {
"a": "/a-2.mjs"
},
"/scope2/scope3/": {
"b": "/b-3.mjs"
}
}
}
results in the following module resolutions (using relative URLs for brevity):
The previous table illustrates that when a script (the referrer) located in /scope2/scope3
imports code from module a
(the specifier), that module will be loaded from the /a-2.mjs
location. As there is no map configured under the scope /scope2/scope3
for module a
, the resolution algorithm uses the closest available (i.e., the one for /scope2
).
Import maps are included in HTML files with a <script type="importmap">
tag in the <head>
section of the document:
<!doctype html>
<script type="importmap">
... JSON import map here
</script>
Older browsers will benefit from the es-module-shims
polyfill. Support of import maps in browsers can be detected as follows:
if (HTMLScriptElement.supports('importmap')) { // The importmap feature is supported.}
Developers can refer to the full import maps specification for additional technical details.