◆ importmap を使うと静的 import も使えていい感じにできた

前記事を書いた直後に importmap を使えばもっといい感じにできそうと気づいたので importmap を使う版も作ってみました

作るものは HTML ファイル中の script タグで相互に import できるというものです
標準機能ではないので 通常の script タグとして実行はせず text/module という type にしてそれを使う側で処理します

前回のものとの区別のため名前は data-module で指定することにしました
最初に全部の data-module 属性付きの script 要素から URL を作成します
あとは名前と URL のマッピングを importmap に指定してエントリポイントのモジュールを import するだけです

<!DOCTYPE html>

<script type="text/module" data-module="add">
export const add = (a, b) => a + b
</script>

<script type="text/module" data-module="up">
import { add } from "add"
const up = x => add(x, 1)

export default up
</script>

<script type="text/module" data-module="c1">
import * as c2 from "c2"

export const name = "C1"
export default () => `${name} - ${c2.name}`
</script>

<script type="text/module" data-module="c2">
import * as c1 from "c1"

export const name = "C2"
export default () => `${name} - ${c1.name}`
</script>

<script type="text/module" data-module="main">
import up from "up"
import c1 from "c1"
import c2 from "c2"

console.log(up(1))
// 2

console.log(c1())
// C1 - C2

console.log(c2())
// C2 - C1
</script>

<script>
const name_to_url = {}
for (const script of document.querySelectorAll("script[data-module]")) {
name_to_url[script.dataset.module] = URL.createObjectURL(new Blob([script.innerHTML], { type: "text/javascript" }))
}

const importmap = document.createElement("script")
importmap.type = "importmap"
importmap.innerHTML = `{"imports": ${JSON.stringify(name_to_url)}}`
document.head.append(importmap)

import("main")
</script>

静的な import が使えるので循環参照の問題もなくなりました

注意点は最後の importmap を作る script タグは type="module" にしてはいけないです
module のロード後に importmap の更新が起きるのでエラーになります