◆ import 時に importmap で指定したパスから import できる
◆ 絶対パス・相対パス以外に Node.js で使われるパッケージ名を使うこともできる
◆ node_modules 内ではパッケージ名で import されるので rollup などが必要だったのがいらなくなる

Chrome 89 がリリースされたので気になっていた importmap を使ってみました

importmap

これまで ES Modules の import で指定するモジュールの場所は相対パスか絶対パスを書く必要がありました
Node.js のようにパッケージ名だけ書くのはエラーです

例えば lit-html をロードする場合 こうなります

import { html, render } from "https://unpkg.com/lit-html?module"

HTML と同じフォルダに node_modules を配置していれば相対パスでこうなります

import { html, render } from "./node_modules/lit-html/lit-html.js"

1 ファイルならともかく複数のモジュールに分けてそれぞれが lit-html をロードする場合 絶対パスを全部に書くのは面倒です
相対パスでもフォルダ階層が変わればパスが変わるので不便です

importmap を使えば Node.js と同じようにパッケージ名を書くだけにできます
script タグに type="importmap" を指定して JSON を書きます
中身は imports キーに import のマッピングをオブジェクトで指定します

{
"imports": {
"lit-html": "https://unpkg.com/lit-html?module"
}
}

ドキュメントによると URL は配列にして複数書くこともできるようです
「@std/kv-storage」がなければ polyfill をロードする例のようです
それ以外にも CDN を複数指定しておいて 1 つめの CDN が落ちていたら 2 つめからダウンロードということにも使えそうです

importmap を使って lit-html をロードしてみます

<!doctype html>

<script type="importmap">
{
"imports": {
"lit-html": "https://unpkg.com/lit-html?module"
}
}
</script>
<script type="module">
import { html, render } from "lit-html"

render(html`<h1>a</h1>`, document.getElementById("root"))
</script>

<div id="root"></div>

モジュール内では from に "lit-html" のみの指定で動いています

node_modules

単に絶対パス・相対パスを書くのが楽になるだけでなく node_modules のモジュールをロードする場合にも助かります

これまで lit-element を ES Modules で使おうとすると lit-html のロードで問題がありました
lit-element の内部で lit-html をロードしようとしますが そこではパッケージ名で "lit-html" が指定されています
ここで絶対パス・相対パスが使われていないのでエラーとなります

この問題の対策として全体を webpack したり node_modules 内のパッケージ部分のみを rollup や snowpack したりという対処が必要でした
importmap を使えばこれにも対処できて バンドラーが不要になります

<!doctype html>

<script type="importmap">
{
"imports": {
"lit-html/lit-html.js": "./node_modules/lit-html/lit-html.js",
"lit-html/lib/shady-render.js": "./node_modules/lit-html/lib/shady-render.js",
"lit-element": "./node_modules/lit-element/lit-element.js"
}
}
</script>
<script type="module">
import { html, LitElement } from "lit-element"

customElements.define("elem-1", class extends LitElement {
render() {
return html`<h1>a</h1>`
}
})
</script>

<elem-1></elem-1>

importmap では import 時に指定される名前全体を書く必要があるようで

{
"lit-html": "./node_modules/lit-html"
}

のような設定をしておけば "lit-html/lit-html.js" の import は "./node_modules/lit-html/lit-html.js" を import してくれる ということにはできませんでした
外部からパッケージ名を通して import されるモジュールを全部書かないといけないのは面倒ですが これまでに比べると便利になりました

追記

Github にもっと詳しいドキュメントがありました
https://github.com/WICG/import-maps

マッピングに指定する名前の最後を "/" にすると パッケージ内の全ファイルを importmap に書かなくて良いようです
上の例は ⇩のように書いても動きました

{
"imports": {
"lit-html/": "./node_modules/lit-html/",
"lit-element": "./node_modules/lit-element/lit-element.js"
}
}

また importmap の JSON のトップレベルに imports 以外に scopes も設定できて 特定のスコープ内でのみ有効なマッピングを設定できるようです