20210911のNode.jsに関する記事は2件です。

「Denoにはnpmがない」の誤解

Node.jsとDenoを比較する際の特徴として、「npmがない」というのがよく言及されています。 これ自体は正しいのですが、まるでパッケージ管理が貧弱かのように聞こえてしまい、誤解を生んでいるので、改めて整理していきたいと思います。 Denoのパッケージ管理についての誤解 誤解①:npmのパッケージが使えない DenoはランタイムとしてNode.jsとの互換性を切ったものの、Node.js向けのコードをブラウザ向けのコードに変換するツールは沢山あります。Denoはブラウザ向けのコードがそのまま動くので、npmパッケージをDenoで利用することが可能です。 現時点では以下のレジストリを経由してDenoでnpmパッケージを使用できることが知られています。 https://esm.sh/ https://unpkg.com/ https://www.skypack.dev/ https://jspm.org/ ただし注意点として、Node.jsの標準ライブラリはDenoの標準ライブラリでポリフィルされているのですが、このポリフィルの実装が途中です。 なので、importしたいnpmパッケージが未実装の標準ライブラリに依存していると、エラーが出る可能性があります。 ちなみに、「ファイル中にrequire()が含まれていると使えない」というのは古い情報です。 誤解②:実行のたびに外部パッケージのURLにアクセスする 外部URLからのimportを用いた場合、初回実行時のみモジュールのURLにアクセスし、2回目以降の実行ではローカルのキャッシュを使用します。 キャッシュに使うディレクトリは環境変数DENO_DIRで制御することが出来ます。 誤解③:パッケージマネージャが無い Node.jsの場合、npmやyarnといったパッケージマネージャが外部コマンドとして存在します。 Denoの場合はどうでしょうか。 @ry: URLインポートの考え方は、Denoが独自のパッケージマネージャーとして機能するというものです。補助的なツールの必要性を明確に避けたいと思います。 https://github.com/denoland/deno/issues/47#issuecomment-395405713 上記コメントにある通り、Deno自体がパッケージマネージャの役割を果たします。 依存関係の解決、依存関係のダウンロード、依存関係の一覧表示など、Node.jsでは外部コマンドとして存在したものがDeno本体に組み込まれています。 パッケージマネージャが無いというよりは、パッケージマネージャの外部コマンドが無いと言ったほうが正しいと思います。 余談ですが、Denoはサードパーティ製ツールの必要性を極力無くすように作られています。フォーマッター、リンター、分散サーバーレスコンピューティング(deno deploy)等がDenoから提供されています。 今まではサードパーティ製ツールの数でエコシステムの成熟度を判断しているような所がありましたが、Denoにおいては公式が提供するものだけで一通り揃うようになっています。 誤解④:import文に必ずURLを使用する必要がある Node.jsとブラウザでは、import文でモジュールを指定する方法が異なります。 ブラウザ // 「モジュールが配信されているURL」を指定する import _ from "https://esm.sh/lodash/"; Node.js // 「モジュール名」を指定する import _ from 'lodash' 本来、Node.jsとブラウザでは(それぞれに固有のAPIを使用しなければ)全く同じコードが利用できるはずです。それなのに、import文の書き方が違うだけで、バンドラ等を利用して実行前に変換作業を行う必要が出てきます。面倒です。 Denoでは、URLを利用したimportが導入されたので、ブラウザと同形のコードが利用できます。バンドラ等の変換プロセスを導入する必要はありません。 ブラウザ&Deno import _ from "https://esm.sh/lodash/"; また、Node.jsとブラウザのimportを近づけるための手段として、import-mapも導入されました。これはブラウザとDenoの両方で利用可能です。 import-map.json { "imports": { "lodash": "https://esm.sh/lodash/" } } import-mapを指定したブラウザ&Deno import _ from 'lodash' import-mapを利用すると、Node.jsのようなスタイルでのimportも可能です。 つまり、 ブラウザ互換のimport文が書ける 必要に応じてNode.jsスタイルのimport文も書ける ということです。 誤解⑤:バージョン管理が面倒 Denoでは、モジュールのバージョン情報もURLで指定するようになっています。 import { assertEquals } from "https://deno.land/std@0.106.0/testing/asserts.ts"; ^^^^^^^^ モジュールのバージョンを上げたい時はどうすればいいでしょうか。ソースコード中のimportをすべて書き換えるのは骨が折れる作業です。 JavaScriptにはexport * from ...という構文が用意されています。これを使って、プロジェクト内のimport文を一箇所にまとめることが出来ます。 deps.ts export * from "https://deno.land/std@0.106.0/testing/asserts.ts"; mod.ts import { assertEquals } from "./deps.ts" export * from ...構文を使って外部モジュールのimportを一箇所に集約することが、公式ドキュメントでも推奨されています。 また、先ほど紹介したimport-mapも、バージョンの一括指定に利用することが出来ます。 誤解⑥:ロックファイルが無い Node.jsにはpackage.jsonのほかにpackage-lock.jsonがありました。Denoにはpackage.jsonが無いのでロックファイルも無いのでは?と思われるかもしれませんが、実行時に--lockフラグを指定してロックファイルを利用できます。 https://deno.land/manual/linking_to_external_code/integrity_checking#caching-and-lock-files npmにあってDenoに無いもの 「Denoにはnpmがない」の言葉通り、npmだけに存在する機能もあります。 package.json Denoにはpackage.jsonがありません。 例えばURLからimportする時に、URLのパスを遡ってpackage.jsonを読みに行くというのはパフォーマンスが悪いですし、webと非互換になってしまいます。 では、バージョン情報などのメタデータはどうやって指定するのかというと、gitのtagが推奨されているようです。 ちなみに、tsconfig.jsonやimport-map.json、Deno.json(設定ファイル)は存在しています。 これらはどれもプログラムのエントリポイントで指定されるもので、パッケージのメタデータを指定するために利用するものではありません。 (Deno.jsonはv1.14以降のみ) node_modules Denoにはnode_modulesがありません。 npmは、「プロジェクト内のnode_modulesフォルダ」にモジュールをダウンロードします。 この挙動により、プロジェクト毎にパッケージがダウンロードされるため、node_modulesフォルダの肥大化が問題になっていました。(インストール先をパッケージ間で共有するように変更したpnpmも存在します。) 一方Denoは、「ローカルのキャッシュフォルダ」にモジュールをダウンロードします。これはpnpmと同じ方式です。ダウンロードされたモジュールは他のプロジェクトと共有されます。 中央集権的なモジュールレジストリ Denoには中央集権的なモジュールレジストリがありません。 Node.jsの場合、npmレジストリが唯一のレジストリです。npmではサーバーが落ちたり、悪意のあるパッケージによってトークンが流出したりするなどの障害が発生していますが、それでもnpmを使い続けなければなりません。また、一度公開されたモジュールが削除されるなどの問題も発生しています。 対してDenoは、任意のレジストリを使用することができます。ユーザーは https://deno.land/x や https://esm.sh や https://www.skypack.dev/ などの中から好きなレジストリを選ぶことが出来ます。レジストリ間で資本主義的な競争原理が働くことで、エコシステム全体の耐障害性や安全性、永続性が向上することが狙いのようです。 まとめ Denoにあるもの パッケージマネージャ 分散型のモジュールレジストリ ブラウザ互換のimport npmから利用可能な豊富なモジュール群 URLの代わりにモジュール名を使用してimportする方法 ロックファイル Denoにないもの 中央集権的なモジュールレジストリ モジュール管理のために必要な外部コマンド package.json node_modules 感想 Denoのモジュール管理方式に対しては、「import文にURLを使うの不便じゃないの?」が一番大きな疑問じゃないかと思います。 結論から言うと、とても便利です。私はローカルで雑用作業に使っている言語(実行環境)をPythonからDenoに切り替えましたが、「ローカルにどのバージョンのパッケージがインストールされているか」を考えなくて済むというのが大きいです。他にも、「URLをctrl-clickすればドキュメントに飛べる」というのも便利です。 最新のエディタ拡張機能を使っていれば未知のURLも自動で補完されるので、入力の面倒さは大分改善されています。 逆に不便に感じた事としては、 「node_modules内のファイルを弄って挙動を確認する」のような事が面倒 npm scriptに相当するものが無い というのがあります。しかし、普通の使い方をする上では問題ないと思っています。 参考 たぶんパッケージ管理ツールが必要です https://github.com/denoland/deno/issues/47 モジュールのバージョン管理、依存関係の管理など https://github.com/denoland/deno/issues/4574 パッケージの提案 https://github.com/denoland/deno/issues/288 URLから直接モジュールをロードするのはとてもかわいいです(※) https://github.com/denoland/deno/issues/195 ※ライアン・ダール(作者)が「Node.jsに関して後悔している10の事」という講演で、index.jsのモジュール解決について index.htmlみたいでかわいいと思った。しかし、モジュール解決が不必要に複雑になってしまった。 と語った事のオマージュ?ということらしい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「Denoにはnpmがない」は部分的に正しい

Node.jsとDenoを比較する際の特徴として、「npmがない」というのがよく言及されています。 これ自体は正しいのですが、まるでパッケージ管理が貧弱かのように聞こえてしまい、誤解を生んでいるので、改めて整理していきたいと思います。 Denoのパッケージ管理についての誤解 誤解①:npmのパッケージが使えない DenoはランタイムとしてNode.jsとの互換性を切ったものの、Node.js向けのコードをブラウザ向けのコードに変換するツールは沢山あります。Denoはブラウザ向けのコードがそのまま動くので、npmパッケージをDenoで利用することが可能です。 現時点では以下のレジストリを経由してDenoでnpmパッケージを使用できることが知られています。 https://esm.sh/ https://unpkg.com/ https://www.skypack.dev/ https://jspm.org/ ただし注意点として、Node.jsの標準ライブラリはDenoの標準ライブラリでポリフィルされているのですが、このポリフィルの実装が途中です。 なので、importしたいnpmパッケージが未実装の標準ライブラリに依存していると、エラーが出る可能性があります。 ちなみに、「ファイル中にrequire()が含まれていると使えない」というのは古い情報です。 誤解②:実行のたびに外部パッケージのURLにアクセスする 外部URLからのimportを用いた場合、初回実行時のみモジュールのURLにアクセスし、2回目以降の実行ではローカルのキャッシュを使用します。 キャッシュに使うディレクトリは環境変数DENO_DIRで制御することが出来ます。 誤解③:パッケージマネージャが無い Node.jsの場合、npmやyarnといったパッケージマネージャが外部コマンドとして存在します。 Denoの場合はどうでしょうか。 @ry: URLインポートの考え方は、Denoが独自のパッケージマネージャーとして機能するというものです。補助的なツールの必要性を明確に避けたいと思います。 https://github.com/denoland/deno/issues/47#issuecomment-395405713 上記コメントにある通り、Deno自体がパッケージマネージャの役割を果たします。 依存関係の解決、依存関係のダウンロード、依存関係の一覧表示など、Node.jsでは外部コマンドとして存在したものがDeno本体に組み込まれています。 パッケージマネージャが無いというよりは、パッケージマネージャの外部コマンドが無いと言ったほうが正しいと思います。 余談ですが、Denoはサードパーティ製ツールの必要性を極力無くすように作られています。フォーマッター、リンター、分散サーバーレスコンピューティング(deno deploy)等がDenoから提供されています。 今まではサードパーティ製ツールの数でエコシステムの成熟度を判断しているような所がありましたが、Denoにおいては公式が提供するものだけで一通り揃うようになっています。 誤解④:import文に必ずURLを使用する必要がある Node.jsとブラウザでは、import文でモジュールを指定する方法が異なります。 ブラウザ // 「モジュールが配信されているURL」を指定する import _ from "https://esm.sh/lodash/"; Node.js // 「モジュール名」を指定する import _ from 'lodash' 本来、Node.jsとブラウザでは(それぞれに固有のAPIを使用しなければ)全く同じコードが利用できるはずです。それなのに、import文の書き方が違うだけで、バンドラ等を利用して実行前に変換作業を行う必要が出てきます。面倒です。 Denoでは、URLを利用したimportが導入されたので、ブラウザと同形のコードが利用できます。バンドラ等の変換プロセスを導入する必要はありません。 ブラウザ&Deno import _ from "https://esm.sh/lodash/"; また、Node.jsとブラウザのimportを近づけるための手段として、import-mapも導入されました。これはブラウザとDenoの両方で利用可能です。 import-map.json { "imports": { "lodash": "https://esm.sh/lodash/" } } import-mapを指定したブラウザ&Deno import _ from 'lodash' import-mapを利用すると、Node.jsのようなスタイルでのimportも可能です。 つまり、 ブラウザ互換のimport文が書ける 必要に応じてNode.jsスタイルのimport文も書ける ということです。 誤解⑤:バージョン管理が面倒 Denoでは、モジュールのバージョン情報もURLで指定するようになっています。 import { assertEquals } from "https://deno.land/std@0.106.0/testing/asserts.ts"; ^^^^^^^^ モジュールのバージョンを上げたい時はどうすればいいでしょうか。ソースコード中のimportをすべて書き換えるのは骨が折れる作業です。 JavaScriptにはexport * from ...という構文が用意されています。これを使って、プロジェクト内のimport文を一箇所にまとめることが出来ます。 deps.ts export * from "https://deno.land/std@0.106.0/testing/asserts.ts"; mod.ts import { assertEquals } from "./deps.ts" export * from ...構文を使って外部モジュールのimportを一箇所に集約することが、公式ドキュメントでも推奨されています。 また、先ほど紹介したimport-mapも、バージョンの一括指定に利用することが出来ます。 誤解⑥:ロックファイルが無い Node.jsにはpackage.jsonのほかにpackage-lock.jsonがありました。Denoにはpackage.jsonが無いのでロックファイルも無いのでは?と思われるかもしれませんが、実行時に--lockフラグを指定してロックファイルを利用できます。 https://deno.land/manual/linking_to_external_code/integrity_checking#caching-and-lock-files npmにあってDenoに無いもの 「Denoにはnpmがない」の言葉通り、npmだけに存在する機能もあります。 package.json Denoにはpackage.jsonがありません。 例えばURLからimportする時に、URLのパスを遡ってpackage.jsonを読みに行くというのはパフォーマンスが悪いですし、webと非互換になってしまいます。 では、バージョン情報などのメタデータはどうやって指定するのかというと、gitのtagが推奨されているようです。 ちなみに、tsconfig.jsonやimport-map.json、Deno.json(設定ファイル)は存在しています。 これらはどれもプログラムのエントリポイントで指定されるもので、パッケージのメタデータを指定するために利用するものではありません。 (Deno.jsonはv1.14以降のみ) node_modules Denoにはnode_modulesがありません。 npmは、「プロジェクト内のnode_modulesフォルダ」にモジュールをダウンロードします。 この挙動により、プロジェクト毎にパッケージがダウンロードされるため、node_modulesフォルダの肥大化が問題になっていました。(インストール先をパッケージ間で共有するように変更したpnpmも存在します。) 一方Denoは、「ローカルのキャッシュフォルダ」にモジュールをダウンロードします。これはpnpmと同じ方式です。ダウンロードされたモジュールは他のプロジェクトと共有されます。 中央集権的なモジュールレジストリ Denoには中央集権的なモジュールレジストリがありません。 Node.jsの場合、npmレジストリが唯一のレジストリです。npmではサーバーが落ちたり、悪意のあるパッケージによってトークンが流出したりするなどの障害が発生していますが、それでもnpmを使い続けなければなりません。また、一度公開されたモジュールが削除されるなどの問題も発生しています。 対してDenoは、任意のレジストリを使用することができます。ユーザーは https://deno.land/x や https://esm.sh や https://www.skypack.dev/ などの中から好きなレジストリを選ぶことが出来ます。レジストリ間で資本主義的な競争原理が働くことで、エコシステム全体の耐障害性や安全性、永続性が向上することが狙いのようです。 まとめ Denoにあるもの パッケージマネージャ 分散型のモジュールレジストリ ブラウザ互換のimport npmから利用可能な豊富なモジュール群 URLの代わりにモジュール名を使用してimportする方法 ロックファイル Denoにないもの 中央集権的なモジュールレジストリ モジュール管理のために必要な外部コマンド package.json node_modules 感想 Denoのモジュール管理方式に対しては、「import文にURLを使うの不便じゃないの?」が一番大きな疑問じゃないかと思います。 結論から言うと、とても便利です。私はローカルで雑用作業に使っている言語(実行環境)をPythonからDenoに切り替えましたが、「ローカルにどのバージョンのパッケージがインストールされているか」を考えなくて済むというのが大きいです。他にも、「URLをctrl-clickすればドキュメントに飛べる」というのも便利です。 最新のエディタ拡張機能を使っていれば未知のURLも自動で補完されるので、入力の面倒さは大分改善されています。 逆に不便に感じた事としては、 「node_modules内のファイルを弄って挙動を確認する」のような事が面倒 npm scriptに相当するものが無い というのがあります。しかし、普通の使い方をする上では問題ないと思っています。 参考 たぶんパッケージ管理ツールが必要です https://github.com/denoland/deno/issues/47 モジュールのバージョン管理、依存関係の管理など https://github.com/denoland/deno/issues/4574 パッケージの提案 https://github.com/denoland/deno/issues/288 URLから直接モジュールをロードするのはとてもかわいいです(※) https://github.com/denoland/deno/issues/195 ※ライアン・ダール(作者)が「Node.jsに関して後悔している10の事」という講演で、index.jsのモジュール解決について index.htmlみたいでかわいいと思った。しかし、モジュール解決が不必要に複雑になってしまった。 と語った事のオマージュ?ということらしい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む