【Deno】Deno1.28「npm完全に理解した」

Denoは先日v1.25で2022/08/25に[npm対応したぞー](https://qiita.com/rana_kualu/items/7eb1acff8f66948b04eb)と発表したのですが、[そのバージョン](https://deno.com/blog/v1.25)では`Experimental npm support`、あくまで実験的サポートであり、まだ実戦投入できる段階ではありませんでした。
そんなわけで実際に使えるのはまだまだ先だねえと思っていたのですが、2022/11/14に公開されたv1.28で早くも`Experimental`が取れました。
はえーよ。

ということで以下は[該当のリリースノート](https://deno.com/blog/v1.28)の紹介です。


# [Deno 1.28: Featuring 1.3 Million New Modules](https://deno.com/blog/v1.28)

[Deno 1.28](https://github.com/denoland/deno/releases/tag/v1.28.0)を公開できたことをうれしく思います。
本リリースでは、npmの互換性を安定させました。
すなわち、130万以上のnpmモジュールをDenoがインポートできるようになったことを意味します。
これによってPrisma・Mongoose・MySQLなどお気に入りの永続化モジュール、React・Vueなどのフロントエンドフレームワークを使用できるようになり、アプリの構築がこれまで以上に簡単安全に行えるようになります。

Denoを使えば、より簡単に、より安全にnpmを使用できます。
Denoのビルトインツールチェーン、ファーストクラスライブラリ、IDEサポートが、あなたの生産活動をより高速にします。
またDenoの安全側に倒したパーミッションモデルは、安全性の怪しいパッケージについて心配する必要を減らし、開発だけに集中できるよう支援します。

最新のJavaScriptはES ModulesとWeb標準APIで構築されるべきだとの持論をDenoは持っており、このリリースにおいてもその姿勢を変えることはありません。
npmモジュールのインポートは、標準に即した方法でURLを経由して行われます。
我々は、fetch・Request・ResponseといったWebAPIを用いて、何のブラウザを使っているかに影響されない未来へと向かっています。
Denoは、モダンで安全な環境であなたのお気に入りのnpmモジュールを使えるようにすることで、npmを一段上のステージへと引き上げました。
npmを使いこなすため、今週は毎日ハウツーガイドを公開する予定です。
[Twitter](https://twitter.com/deno_land)や[Youtube](https://www.youtube.com/c/Deno_land)をぜひフォローしてください。

## Installing or Upgrading Deno

既にDenoがインストールされている場合は、1コマンドでアップグレードできます。

```js
deno upgrade
```

初めてインストールする場合は、以下のコマンドを実行しましょう。

```js
# MacOS and Linux
curl -fsSL https://deno.land/install.sh | sh

# Windows
irm https://deno.land/install.ps1 | iex
```

その他のインストール方法については[こちら](https://github.com/denoland/deno_install)をご覧ください。

## Using npm

`npm:`という指定で、npmパッケージを使用することができます。

```js
import { chalk } from "npm:chalk@5";
```

また[Import Maps](https://deno.land/manual@v1.28.0/basics/modules/import_maps)では、以下のように指定します。

```js
{
  "imports": {
    "chalk": "npm:chalk@5"
  }
}
```

npm scriptsやCLIもDenoから実行可能です。

```js
deno run --allow-env --allow-read npm:create-vite-extra
```

サブコマンドが必要であれば、以下の形式で指定します。

```js
deno run npm:<package_name> <subcommand>
```

例として、Denoで`npx prisma init`を実行するには以下のようにします。

```js
deno run npm:prisma init
```

### Security

npmはソフトウェアを簡単に共有・インストールできるようにしましたが、依存関係をインストール・実行する際に何の確認も行っていないため、[サプライチェーン攻撃](https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/)に非常に脆弱となっています。

[typosquatting](https://jfrog.com/blog/large-scale-npm-attack-targets-azure-developers-with-malicious-packages/)や[ソーシャルエンジニアリング](https://github.com/dominictarr/event-stream/issues/116#issuecomment-440927400)、そしてその他の脆弱性を窓口に悪意のあるモジュールがアップロードされ、実運用されているNodeアプリケーションにインストールされ、多くのユーザの機密情報が危険に晒されることがあります。

Denoでは、依存関係のあるサードパーティライブラリがシステムにアクセスしようとした際に通知を行い、そしてアクセスを制限することができます。

```js
deno run npm:install-malware
⚠️  ┌ Deno requests write access to /usr/bin/.
   ├ Requested by `install-malware`
   ├ Run again with --allow-write to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) >
```

より詳しく知りたい場合は[permissions system](https://deno.land/manual/getting_started/permissions)を参照してください。


### No package.json

Denoでのnpmの使用は、よりシンプルです。

`npm install`は必要ありません。
初めて実行した際に自動的にインストールされます。

`package.json`は必要ありません。
コードベースを複雑にするノイズを減らします。

`node_modules`フォルダも必要ありません。
グローバルなディレクトリに一度だけキャッシュされます。
なお後方互換性のために[--node-modules-dir](http://deno.land/manual@v1.28.0/node/npm_specifiers#--node-modules-dir-flag)を使うこともできます。

Denoで今すぐ生産性を上げましょう。
tester・formatter・linterなど多くの[ビルトインツールチェーン](https://deno.land/manual/tools)と、ネイティブのTypeScriptサポートによって、余計なインストールや設定作業に手を取られることなく、今すぐコードを書き始めることができます。

### Ongoing Work

我々の目標は、できるかぎり多くのnpmモジュールをDenoで動かせるようにすることです。
package.jsonが作業ディレクトリに存在する前提で動作しているといった特殊なモジュールは、まだ互換していない可能性があります。

我々は今後数か月で、そのようなパッケージについても対応を行っていく予定です。
我々は現在、幾つかのNodeテストスイートを用いて、Denoでも動作することを確認しており、この範囲をさらに広げていく予定です。
また、コミュニティから報告されたエッジケースについても対応していきます。

`deno compile`をはじめ一部のDenoサブコマンドは、まだnpmに対応していません。
この対応作業は[#15960](https://github.com/denoland/deno/issues/15960)のチケットで進行しています。

より互換性を高めていくため、もしnpmモジュールで問題が発生したら[是非Issueを上げてください](https://github.com/denoland/deno/issues)。
多くの目さえあれば、ほぼ全ての非互換はなくせると我々は確信しています。


## Auto discovery of the lock file

Denoはv1.0からロックファイルをサポートしていますが、これを使うためにはユーザが自己責任で`--lock=<filename>`とフラグを指定する必要があり、不満の温床になっていました。
このような状況から、ロックファイルはほとんど使われないことになっていました。

Deno v1.28からは、設定ファイル`deno.json`が見つかった場合は自動でロックファイルを作成するようになりました。
ロックファイルは、設定ファイルと同じ場所に`deno.lock`という名前で配置されます。
さらに、新しい依存関係が見つかった際にはロックファイルも自動更新されます。

この自動検出シナリオのため、`--lock-write`フラグの動作が拡張されました。
ロックファイルを更新するためにこのフラグを渡す必要はありません。
このフラグを渡すと、ロックファイルが強制的に作成されるようになります。

もしロックファイルの自動検出を無効にしたい場合は、`--no-lock`フラグを使いましょう。

また`--lock`フラグは引数を省略できるようになり、省略した場合のデフォルト値として`./deno.lock`が使用されます。


## Changes to Deno APIs

### Stabilizations

本リリースでは、以下のAPIがstableとなりました。
今後`--unstable`フラグを使う必要はありません。

・Deno.bench()
・Deno.gid()
・Deno.networkInterfaces()
・Deno.systemMemoryInfo()
・Deno.uid()

### New unstable Deno.Command API

実験的実装だった`Deno.spawn`・`Deno.spawnSync`・`Deno.spawnChild`が削除され、統一のAPIとして`Deno.Command`コマンドが追加されました。

基本的な非同期呼び出しはこうです。

```js
let c = new Deno.Command("echo", { args: ["foo"] });
let { stdout, stderr } = await c.output();
```

基本的な同期呼び出しはこうです。

```js
let c = new Deno.Command("echo", { args: ["foo"] });
let { stdout, stderr } = c.outputSync();
```

ストリーミングを含む、少し高度な非同期処理の例です。

```js
const c = new Deno.Command("cat", { stdin: "piped" });
c.spawn();

// catの出力をファイルにパイプする
const file = await Deno.open("output.txt", { write: true });
await c.stdout.pipeTo(file.writable);

const stdin = c.stdin.getWriter();
await stdin.write(new TextEncoder().encode("foobar"));
await stdin.close();

const s = await c.status;
console.log(s);
```

## V8 10.9

V8の最新リリース10.9にアップグレードされました。
これまでは10.8でした。
ほとんどは内部的な改良ですが、ユーザ向けのAPI変更も一部含まれています。

・[Symbols as WeakMap keys](https://github.com/v8/v8/commit/705e374124ae026206e90519356cd980a1a515ba)
・[unship array grouping](https://github.com/v8/v8/commit/f7454be685df8ed70786d1dba522683cd2104e6f) ([proposal](https://github.com/tc39/proposal-array-grouping))

全ての変更点については[こちら](https://gist.github.com/ry/28a28db8a527d905be4533f732bedafa)を参照してください。

## Manual rewrite

Denoの成長に合わせて、[マニュアル](https://deno.land/manual)も成長してきました。
そのため、あちらこちらに継ぎ足しがなされ、徐々に整理がつかなくなってきました。

このリリースでは、目次とテキストをリファクタリングし、シンプルでわかりやすいものにしました。
また[npmの使い方を解説するセクション](https://deno.land/manual/node)も追加しました。

ドキュメントには、常に改善の余地があります。
改善点を見付けた場合は、[ぜひプルリクエストを送ってください](https://github.com/denoland/manual)。


## Launch Week

Denoとnpmで何が作れるようになったかを示すため、今週は毎日チュートリアルとハウツーガイドをYouTubeの動画で、また他の有用なリソースで紹介していきます。

Denoを商用利用していて、技術的質問がある場合は、[Deno Office Hours](https://docs.google.com/forms/d/e/1FAIpQLSewMCz1wB8SMZI8n7xUVeH00tBo5xLBGmj26-QVjjhQ4FfZlQ/viewform)をご利用ください。
エンジニアが30分あなたのお手伝いをします。

[Twitter](https://twitter.com/deno_land)や[Youtube](https://www.youtube.com/c/Deno_land)をフォローして、最新情報を見逃さないようにしましょう。



# 感想

これまでDeno導入を躊躇する一因となっていたnpm非対応問題が、ここにおいてついに解決を見ました。

さらに、単にnpmが使えるようになって便利！ってだけではなく、きちんと安全装置まで組み込んであるのが素晴らしいですね。
この仕組みのおかげで、予め与えておいたパーミッション以上のことをやろうとしても動作しません。
まともだったライブラリに後からマルウェアが混入したなどの事故・事件の被害を軽減できるわけです。

もちろんパーミッションの範囲内での悪さはできるので完全に防げるというわけではないですし、何も考えずに[--allow-all](https://deno.land/manual@v1.28.1/basics/permissions)とか与えたら台無しなので、単にDenoを使っていれば安全というわけではありませんが、現状の何もしないnpmよりは遥かに良いことは間違いありません。
このパーミッションの仕組みは[Bun](https://bun.sh/)にも存在しないため、Bunに対する明白な利点として挙げられるでしょう。

この大きな改善により、これまでDenoを導入することが難しかった環境にも、容易に組み込むことができるようになったことでしょう。
さっそく試してみてはいかがでしょうか。