今の依存関係管理方法は?import map?import maps?って何?
これはDeno Advent Calendar 2021の20日目の記事です
前提
最近すっかりおざなりになっていたdenoで、また何か新しく作ろうかと思い、着手しようと思ったものの「そういえば依存関係の管理は今の流行りは何?」という雑な思いから始まりました
(ちなみにこういうものを作ってました)
https://github.com/lion-man44/cret | https://deno.land/x/pre_commit@0.1.4 | https://nest.land/package/simple-dotenv
そしてあれこれ読んでみたりしたものの非常に公式や他の方のまとめたドキュメントが読みづらいと感じてしまい、メモがてら自分でつけることにしました
なので既にご存知の方や詳細をもっと詳しく知りたいという方は、
https://github.com/WICG/import-maps
こちらなどを読むと詳しく知れるかと思います
結論
deps.json
を使って --importmap
or --import-map
のオプションを使い、 deno run
する
(e.g. deno run --importmap=deps.json mod.ts
)
容易に追加したり削除したりする、要はnpm likeな何かが欲しいということであれば
をオススメします
import-maps
とは何なのか
Deno自体はパッケージ管理を行う何かを持っていません( npm
のようなものを)
そのため起動時にinstallするような挙動になっています(zero installと呼ばれています)
import { emptyDir } from "https://deno.land/std@0.117.0/fs/mod.ts";
console.log(emptyDir);
// => [AsyncFunction: emptyDir]
こういった感じで今まで指定していました
しかしこの指定方法は a.ts
b.ts
と数が増えていくごとに当然他のファイルでも使う場合に「まとまりがないため都度import文が必要になったり」「version指定をしていた場合は他のupdateする時も一々grepして、必要なファイル全てが変更になったり(まぁこれはある意味メリットでは?という見方もあるかもしれません)」、非常に面倒であると言わざるを得ません
ハードコーディングのつらみと言える部分です
その中でdenoには deps.ts
の方にまとめておく、といった文化がありました
https://deno.land/x/oak@v10.0.0/deps.ts
いつ頃の仕様になるのかEcmaScriptの仕様として2018年ごろにimport mapsの仕様が完成されたみたいです
その実装方式自体は下記のような形になりました
<script type="importmap">
{
"imports": {
"vue": "node_modules/vue/dist/vue.common.prod.js"
}
}
</script>
少し疑問が湧きましたが私の中ではまだ未解決です
- HTML上でnode_modulesを指定する上でフォルダ構成を想像させてしまうので、攻撃者の理解の助けになってしまう可能性がある
- importmapがHTMLに書かれている、かつlocal fileを指定している場合に、このリクエスト自体は一度local networkの外に出てから検索しに行くのか?
さて、denoではこれをjsonファイルとして読み込むことが可能になりました
// import-map.json
{
"imports": {
"fs/": "https://deno.land/std@0.117.0/fs/"
}
}
// mod.ts
import { EOL } from "fs/eol.ts";
console.log(EOL);
// => { "LF": "\n", "CRLF": "\r\n" }
以前までは —-unstable
な機能だったのですが、今ではstableになったので、 deno run
のオプションとして —-importmap
というオプションを持っています
できること
- promise化されているので
import(module).then
ができる(dynamic import
)- 上記のような形でできることの実際のメリットはまだ分からない(そのmoduleが読み込まれた時点でloading iconとかを動かしたりとか?)
import('https://deno.land/std@0.117.0/fs/mod.ts')
.then((mod) => {
console.log(mod);
});
/* =>
Module {
EOL: { LF: "\n", CRLF: "\r\n" },
_createWalkEntry: [AsyncFunction: _createWalkEntry],
_createWalkEntrySync: [Function: _createWalkEntrySync],
detect: [Function: detect],
emptyDir: [AsyncFunction: emptyDir],
emptyDirSync: [Function: emptyDirSync],
ensureDir: [AsyncFunction: ensureDir],
ensureDirSync: [Function: ensureDirSync],
ensureFile: [AsyncFunction: ensureFile],
ensureFileSync: [Function: ensureFileSync],
ensureLink: [AsyncFunction: ensureLink],
ensureLinkSync: [Function: ensureLinkSync],
ensureSymlink: [AsyncFunction: ensureSymlink],
ensureSymlinkSync: [Function: ensureSymlinkSync],
exists: [AsyncFunction: exists],
existsSync: [Function: existsSync],
expandGlob: [AsyncGeneratorFunction: expandGlob],
expandGlobSync: [GeneratorFunction: expandGlobSync],
format: [Function: format],
move: [AsyncFunction: move],
moveSync: [Function: moveSync],
walk: [AsyncGeneratorFunction: walk],
walkSync: [GeneratorFunction: walkSync]
}
*/
-
--importmap
に与えられるファイル名は自由 - 直接のfile自体を読み込むことも可能だが、最後を
/
にすることでその配下を全て事前importが可能 (EOL
の例を見てください)-
"fs/": "https://deno.land/std@0.117.0/fs/*"
といったアスタリスクを使用することはできなかった
-
- local fileを指定する場合は下記のような形でファイルを取得することができる
-
const B
とconsole.log(B)
をcomment outしてもerrorが出ることはないです
-
// deps.json
{
"imports": {
"test": "./b.ts"
}
}
// b.ts
export const B = 123
// a.ts
import { B } from 'test';
console.log(B);
// => 123
- override optionとして
scopes
propertyが使える(上の例を再利用します)- 用途としてはimportしたはいいけど特定部分でerrorを出してしまう場合にmonkey patchを当てる用途です
// deps.json
{
"imports": {
"test": "./b.ts"
},
"scopes": {
"./": {
"test": "./c.ts"
}
}
}
// c.ts
export const B = "updated"
// a.ts
import { B } from "test";
console.log(B);
// => "updated"
まとめ
決定的に素晴らしいもの、というのはまだ掴めてないですし、これの背景はよく分からなかったですが、少なからず本体の方ではまぁ導入するだけあって温度感は高いものでした
しかし実際にはまだ dep
みたいな外側からそれを扱うツールが必要最低限の機能だけになっているので npm
や yarn
のような感じで使うと物足りない感じがあるかもしれません
それにsemver rangeが導入されていないため、自動アップデートによる追従などができずにちょっと面倒という感じもあります(ただその issue についても既に立っており、大方好意的な捉え方がされているみたいですがその後の返答がないので何かあったのでしょうか、といったところです)
Denoはまだまだ成長途中かついろいろなものが不安定の中動いている成長途中のプログラミング言語です
ほぼほぼTypeScriptを知っている人であれば、使うことができます。その不安定さも含めて今後が楽しみなプロダクトです
Discussion