【Deno】esm.shによるnpmパッケージ管理に便利なCLIスクリプトについて
はじめに
新たなJavaScript/TypeScriptランタイムであるDenoでは、Node.js向けであるnpmのパッケージが使えないというデメリットがあります。が、様々な人による様々な努力によって、それは改善されてきています。この記事では、Denoからnpmのパッケージを使うときによく用いられるCDNであるesm.shによる「努力」について簡単にまとめます。
Denoとesm.sh
Denoでは以下のように、URLによってモジュールを指定します。
import * as flags from "https://deno.land/std/flags/mod.ts";
このコードは、Denoの標準モジュールにあるflags
という、コマンドライン引数を処理するモジュールを読み込んでいます。Denoにおいてnpmのパッケージを読み込んで使いたいときには、まずこの例のようにURLで指定しなければなりません。
しかしnpmのパッケージは、URLで指定したとしてもそのままでは使えません。なぜなら、fs
やchild_process
などのNode.jsにしかない内部モジュールを使っているからです。そこで用いられるのが、Denoの標準モジュールのnode
と、それをうまく適用してくれるCDN「esm.sh」です。
esm.shはesbuildを使ってnpmパッケージをES Module形式にして配信しているCDNです。esm.shはUser-Agentの値を元に配信するモジュールを自動で選定しますが、Denoからのアクセスには、node
標準モジュールを使用してNode.js内部モジュールの解決を行ったモジュールを配信します。それにより、多くのnpmのパッケージをDenoから使うことができます。
import * as msgpack from "https://esm.sh/@msgpack/msgpack@2.8.0";
またesm.shは、X-TypeScript-Types
というHTTP Headerを返してくれます。これはDenoのためにそのモジュールの型定義ファイルの場所を示すもので、Denoはこの情報を元に型定義ファイルを読み込みに行きます。これにより、エディターの補完が効くなどの利便性を受けることができます。
最近のバージョンのDenoには試験的に、npmのパッケージを読み込む機能が搭載されていますが、それを使うには--unstable
オプションが必須になってしまうため、ここでは紹介しません。
esm.shによるCLIスクリプト
そんなesm.shですが、(具体的な日時は不明ですがいつからか)Deno向けに便利なスクリプトを公開し始めました。
The CLI script is using to manage the imports with import maps, it will resolve the dependencies automatically and always pin the build version. To use the CLI mode, you need to run the init command in your project root directory:
deno run -A -r https://esm.sh init
上記のコマンドを実行すると、次のようなdeno.json
(Denoの設定ファイル)が作られます。
{
"importMap": "import_map.json",
"tasks": {
"npm:add": "deno run -A https://esm.sh/v95 add",
"npm:update": "deno run -A https://esm.sh/v95 update",
"npm:remove": "deno run -A https://esm.sh/v95 remove"
}
}
見ての通りこれは、npm add
などのコマンドの代替を追加するものです。deno task npm:add
のようにして使います。
deno task npm:add @msgpack/msgpack
仕組み
https://esm.sh
もまたesm.shが提供するモジュールのように、User-Agentを見て返す内容を選定しています。Denoからアクセスされたときには次のファイルの内容を返します。
これは、コマンドライン引数に応じてimport_map.json
というファイルを書き換えるスクリプトです。
Import mapとは、JavaScript/TypeScriptのimport
の挙動を変えるために使われるもので、Webブラウザでも使えるほか、Denoでも使うことができます。Denoではdeno.json
にImport mapを記述したファイルへのパスを書いて使います。
実際のimport_map.json
の内容は次のようになります。
{
"imports": {
"@msgpack/msgpack": "https://esm.sh/v95/@msgpack/msgpack@2.8.0"
},
"scopes": {}
}
このImport mapによりスクリプト内では次のように書けるようになります。
- import * as msgpack from "https://esm.sh/@msgpack/msgpack@2.8.0";
+ import * as msgpack from "@msgpack/msgpack";
おわりに
今回紹介したesm.shによる「努力」とは、import_map.json
をうまく書き換えるスクリプトをtask機能で呼び出せるようにすることで、Node.jsでの開発のときのようにnpmのパッケージを管理できるようにする、というものでした。
なかなか開発体験が高くなる、よいものだと感じました。(これまではdeps.ts
を使って頑張っていましたから……。)Deno本体のnpmパッケージ読み込み機能が今後どうなるかがわかりませんが、少なくとも記事執筆時点では、この方法はとても有用なものであると感じます。
Discussion