🦕

2021年のDenoの変更点やできごとのまとめ

2021/12/18に公開

Deno Advent Calendar 18日目の記事です。

https://qiita.com/advent-calendar/2021/deno

この記事では2021年にDenoに関して起きた変更やできごとなどについてまとめます。

Denoランタイムに関する変更点

--no-check=remoteのサポート

--no-checkオプションでremoteを指定すると、リモートモジュール(http:またはhttps:で始まるモジュール)の型チェックを無効化できるようになりました。

$ deno run --no-check=remote mod.ts

Import assertionsのサポート

Deno v1.17でImport assertionsが実装されました。

現在はjsonファイルのみがサポートされています。

import modules from "./modules.json" assert { type: "json" };

Object.keys(modules);

Import mapsの安定化

Deno v1.8にてImport mapsが安定化されました。

またDeno v1.10にてリモートのImport mapsファイルもサポートされました。

$ deno run --import-map=https://deno.land/x/aleph@v0.3.0-beta.19/import_map.json main.ts

ネイティブHTTPサーバ

Deno本体にHTTPサーバが組み込まれました。

RequestResponseなどのオブジェクトが利用されており、極力Web APIに沿うようにデザインされています。

const listener = Deno.listen({ port: 3000 });
(async () => {
  for await (const conn of listener) {
    (async () => {
      const httpConn = Deno.serveHttp(conn);
      for await (const { request, respondWith } of httpConn) {
        const response = new Response("Hello world!");
        respondWith(response);
      }
    })();
  }
})();

このHTTPサーバはRustで実装されており、元々標準ライブラリに存在していたHTTPサーバ(std/http)と比較して大幅なパフォーマンス向上が見込まれます。

また、Deno.upgradeWebSocketというAPIも提供されており、Deno.serveHttpと併用することでWebSocketサーバを立てることもできます。

Node.js互換モード

v1.15でNode.js互換モードが実装されました

https://zenn.dev/uki00a/articles/node-compat-mode-introduced-in-deno-v1-15

Denoの実行時に--compatを指定することで、Node.jsの組み込みモジュール(events, fsなど)の読み込みやCommonJS形式のモジュールの実行などが有効化されます。

$ deno run --compat --unstable main.mjs

Node.jsとの互換性についてはまだ100%ではないものの、現時点でESLintやExpressなどのパッケージがある程度動くようです。

React 17のJSXトランスフォームのサポート

React 17の新しいJSXトランスフォームがDenoでサポートされました。

.jsxまたは.tsxファイル中で@jsxImportSourceを使うことで有効化されます。

/** @jsxImportSource https://esm.sh/preact@10.5.15 */

export function Hello(props) {
  return (
    <div>Hello, {props.name}</div>
  )
}

また、--configオプションで指定する設定ファイルで有効化することも可能です。

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "https://esm.sh/react@17.0.2"
  }
}

--allow-env--allow-runでリスト指定がサポート

--allow-write--allow-readなどのオプションでは、以下のように権限をコンマ区切りでリスト指定することができます。

$ deno run --allow-write=tmp,dist main.ts

しかし、--allow-run--allow-envではこのリスト指定はサポートされていませんでした。

Deno v1.9で--allow-env--allow-runのリスト指定がサポートされ、より厳しく権限を設定できるようになりました。

$ deno test --allow-run=redis-server,redis-cli --allow-net ./tests

Deno.permissionsの安定化

DenoのパーミッションAPI(Deno.permissions)を使用すると、プログラムから動的にパーミッションの問い合わせや付与などを行うことができます。

このパーミッションAPIも安定化されたため、今後は--unstableを指定せずに利用できます。

// --allow-runが指定されているか問い合わせる
await permissions.query({ name: "run" });

// --allow-readの付与を要求する
await Deno.permissions.request({ name: "read", path: "." })

シグナルAPI

v1.16で新しいシグナルAPIが実装されました

const signalListener = () => {
  // ...
};
Deno.addSignalListener("SIGTERM", signalListener);
Deno.removeSignalListener("SIGTERM", signalListener);

元々、Deno.signal()という異なるAPIが存在していたのですが、誤った使い方をされてしまいがちであることなどから、新しいAPIが導入されました。

この変更に合わせて、既存のDeno.signal()は削除されています。

--promptオプション

--promptオプションはDeno v1.9で実装された機能です。

デフォルトでは許可されていない処理を実行しようとすると、Denoはプロセスを停止させます。

例えば、--allow-readが与えられていない状況で下記コードを実行すると、エラーが発生します。

console.log(Deno.cwd());

--promptオプションを与えてDenoを実行すると、権限が与えられていない処理に遭遇した際に、Denoはプロンプトを表示して必要な権限の許可をユーザに対して求めます。

もしユーザが権限の付与を許可した場合、Denoは自身に対象の権限を付与した上で処理を続行します。

これにより、例えばスクリプトなどの実行時に段階的に権限を与えることができるようになりました。

プライベートモジュールのサポート

プライベートなモジュールのダウンロードなどをサポートするために、DENO_AUTH_TOKENSという環境変数が導入されました。

この環境変数に{token}@{hostname[:port]}形式でトークンを指定することで、該当のホストからモジュールをダウンロードする際に、Authorizationヘッダに指定されたBearerトークンが自動で設定されます。

トークンはセミコロン区切りで複数指定することができます。

また、Basic認証もサポートされています。

$ DENO_AUTH_TOKENS=user:password@127.0.0.1:3000 deno run mod.ts

プラグインシステムの廃止

元々、Denoにはプラグインシステムが存在しました。

これはユーザがRustを使用してDenoを拡張する仕組みで、Deno.openPluginというAPIを使うことで、任意のプラグインを読み込むことができました。

Deno v1.13でDeno.openPluginが廃止され、代わりにFFIが導入されました。

const dylib = Deno.dlopen("./add.so", {
  add: {
    parameters: ["i32", "i32"],
    result: "i32",
  },
});
console.assert(dylib.symbols.add(1, 2) === 3);

Deno.dlopenはまだ安定化されておらず、利用には--unstableが必要です。

パフォーマンス向上

Deno v1.9でserde_v8というRustクレートが導入されました。

これにより、Deno内部でのop呼びだしのオーバーヘッドが大幅に削減されました。

その他にも、様々なAPIでパフォーマンス向上が図られています(大きなものとしては、URLのパフォーマンスが3倍にまで向上しています)

Web標準との互換性について

DenoにはWPTという、各種ブラウザなどでWeb標準への準拠度を確認するためのテストスイートが導入されています。

これにより全体的にWeb標準との互換性が向上しています。

また、MDN Web Docsの「ブラウザーの互換性」欄にDenoの情報が掲載されるようになりました。

https://deno.com/blog/deno-on-mdn.html

また、下記のように様々なAPIが実装されています。

globalThis.location

globalThis.locationが実装されました。

Denoを起動する際に--locationオプションで指定されたオリジンが設定されます。

また、これの実装合わせて、fetch()で相対URLの指定がサポートされました。

Worker

大きな変更点として、Workerを生成する際にパーミッションを指定できるようになりました。

const worker = new Worker(new URL("./worker.ts", import.meta.url).href, {
  type: "module",
  deno: {
    namespace: true,
    permissions: {
      read: ["."],
      write: false,
    },
  },
});

また、Workerに関連して下記のAPIも実装されました。

fetch()

URIスキームとして、下記がサポートされました。

このうち、file:はDeno独自の拡張で、利用する際は--allow-readが必要です。

const res = await fetch("file:///home/uki00a/.vimrc");
console.log(await res.text());

また、ReadableStreamを利用したリクエストボディのストリーミングや、AbortControllerによるリクエストのキャンセルなどもサポートされています。

Web Storage API

DenoでlocalStoragesessionStorageなどが実装されました。

https://zenn.dev/uki00a/articles/web-storage-api-in-deno

WebSocketStream

まだ利用には--unstableが必要ですが、WebSocketStreamが実装されました。

const stream = new WebSocketStream("ws://localhost:3000");
const { readable, writable } = await stream.connection;

const writer = writable.getWriter();
await writer.write("Hello world!");

setTimeout(() => stream.close(), 5000);

for await (const message of readable) {
  console.log(message);
}

URLPattern

URLPatternが実装されました。

簡易的なAPIサーバなどを実装する際に便利かもしれません。

const pattern = new URLPattern({ pathname: "/users/:id" });
pattern.exec("https://example.com/users/1").pathname.groups; // {id: '1'}

WebGPU API

まだunstableですがWebGPU APIが実装されました。

下記リポジトリに実際の利用例などについて掲載されています。

https://github.com/crowlKats/webgpu-examples

コンパイラAPI

元々、DenoからTypeScriptコンパイラを利用するために、Deno.transpileOnlyDeno.bundle, 及びDeno.compileの3種類のAPIが提供されていました。

これらのAPIがDeno.emitという単一のAPIに統合されました。

const result = await Deno.emit("/mod.ts", {
  bundle: "module",
  sources: {
    "/mod.ts": `import { add } from "./add.ts";
console.log(add(1, 2));`,
    "/add.ts": `export const add = (a: number, b: number) => a + b;`,
  },
});

非推奨化されたAPI

Deno名前空間に実装されていた以下のAPIが非推奨化されています。

  • Deno.Buffer
  • Deno.readAll
  • Deno.readAllSync
  • Deno.writeAll
  • Deno.writeAllSync
  • Deno.iter
  • Deno.iterSync
  • Deno.copy

これらのAPIはdeno_std/io/utilなどに移動しているため、今後はそちらに移行するとよいでしょう。

deno testの変更点

deno testはDenoに搭載されたテストランナです。

--jobによるテストケースの並行実行

Deno v1.10で--jobオプションがサポートされ、テストケースの並行実行がサポートされました。

$ deno test --jobs 2

--docによるJSDocコメントやMarkdownファイル内のコードブロックの型チェックがサポート

--docオプションによるJSDocコメント(v1.10)やMarkdownファイル(v1.13)内のコードブロックの型チェックがサポートされました。

$ deno test --doc --no-run --import-map=import_map.test.json

簡単な例として、以下のようなファイルがあったとします。

add.ts
/**
 * ```ts
 * import { add } from "./add.ts";
 * add(1, "2"); // NG
 * ```
 */
export function add(a: number, b: number): number {
  return a + b;
}

このファイルに対してdeno test --docを実行すると、JSDocコメント内のコードで関数に与える引数の型が間違っているため、型エラーが報告されます。

$ deno test --doc add.ts

--shuffleによるテストケースの実行順序のランダム化

Deno v1.12でサポートされました。

これによりテストケースの実行順序をランダムにできるため、不安定なテストケースが生まれることの防止につながるかもしれません。

$ deno test --shuffle -A

テストケースごとのパーミッションの制御

Deno.testでテストケースごとに細かくパーミッションを制御できるようになりました。

Deno v1.10で追加され、Deno v1.16で安定化されました。

Deno.test({
  name: "This should fail",
  permissions: {
    net: false, // --allow-netを無効化
  },
  fn: async () => {
    const _ = await fetch("https://example.com"); // --allow-netが無効化されているため、失敗します
  },
});

サブテストAPI

Deno v1.15で追加された機能で、テストケースのグループ化ができます。

まだ--unstableが必要ですが、おそらく来年にリリースされるであろうv1.18にて安定化される予定です。

Deno.test("nested test case", async (t) => {
  const success = await t.step("step 1", async (t) => {
    const success = await t.step("step 1-1", () => {
      throw new Error("Failed!");
    });
    if (!success) throw new Error("Failed!");

    await t.step("step 1-2", () => {});
  });

  if (success) throw new Error("Failed!");
});

--watchオプションのサポート

Deno v1.10でdeno test--watchオプションがサポートされました。

これにより、ファイルを変更するたびにテストを自動で再実行することができます。

$ deno test --watch -A

テストカバレッジ機能の強化

元々、Denoでのカバレッジの収集とレポートの生成はdeno test --coverageという単一のコマンドで実現されていました。

Deno v1.8で使い方が以下のように変更されました。

  • カバレッジを収集する際はdeno test --coverageを使う
  • レポートを生成する際は、deno coverageを使う

さらに、deno coverage--lcovオプションを指定することで、lcov形式のレポートが生成できるようになりました。

deno fmtの変更点

deno fmtはDenoに組み込まれたコードフォーマッタです。

Markdownサポート

Deno v1.7でMarkdownファイルのフォーマットがサポートされました。

$ deno fmt README.md

--extのサポート

Deno v1.7のMarkdownサポートに合わせて追加されました。

deno fmt -で標準入力経由でフォーマット対象のコードを渡す際に指定することが想定されています。

$ cat mod.js | deno fmt --ext=js -
$ cat README.md | deno fmt --ext=md -

JSONサポート

Deno v1.8でJSONファイルのフォーマットがサポートされました。

$ deno fmt modules-lock.json

設定ファイルによる挙動のカスタマイズ (v1.14)

Deno v1.14で設定ファイル(--config)によってdeno fmtの挙動をカスタマイズできるようになりました。

インデント幅やタブの使用有無などを設定できます。

$ deno fmt --config deno.jsonc

詳しい設定内容については、下記の記事で詳しくまとまっているためそちらを参照ください。

deno lintの変更点

deno lintはDeno本体に組み込まれたリンタです。

まず大きな変更点として--unstableオプションが不要になりました。 (v1.10)

また、deno fmt同様に設定ファイルによる挙動のカスタマイズがサポートされています。 (v1.14)

$ deno fmt --config deno.jsonc

詳しい設定内容については下記の記事を参照ください。

また、--watchオプションのサポートも追加されており、ファイルの変更時にリンタを自動で再実行することができます。(v1.15)

deno compileの変更点

deno compileはJavaScriptやTypeScriptコードをバンドルし、スタンドアロンな実行可能ファイルを生成するコマンドです。

安定化

Deno v1.10でdeno compileコマンドが安定化されました。

そのため、今後はdeno compile--unstableオプションを指定せずに利用できます。

--targetオプションによるクロスコンパイル

Deno v1.7で--targetオプションによるクロスコンパイルがサポートされました。

例えば、LinuxやMacなどのOS上でWindows向けのバイナリを生成できます。

$ deno compile --target x86_64-pc-windows-msvc main.ts

パーミッションなどのサポート

Deno v1.7でパーミッション(--allow-read--allow-netなど)や証明書(--cert)などをバイナリに組み込めるようになりました。

$ deno compile --allow-read main.ts

deno lspの変更点

deno lspコマンドはDenoの本体に組み込まれたLanguage Serverです。

大きな変更点としてDeno v1.7.5で安定化が行われました。
そのため、今後は--unstableオプションの指定は不要です。

その他にも下記の機能などが追加されています。

  • import節の入力補完がサポート
  • Test code lens (エディタやIDEから直接特定のテストケースを実行できる)
  • Refactoring code actions (コードの関数抽出などができる)

deno replの変更点

deno replコマンドを実行すると、REPLを起動できます。

REPLに対する大きな変更点として、TypeScriptコードの実行やNode.js互換モード(--compat)がサポートされています。(ただし、入力されたTypeScriptコードの型チェックはサポートされていません)

その他にもimportexportの評価などもサポートされ、コードを貼り付けて利用する際の利便性が向上しています。

deno uninstallコマンド

Deno v1.15で実装された新しいコマンドです。

deno installによってインストールされたコマンドをアンインストールできます

$ deno uninstall udd

deno_stdの変更点

deno_std/http

Deno v1.9でネイティブHTTPサーバ(Deno.serveHttp)が実装されたのに合わせて、http/server.tsもそれをベースに再実装されました。

import { serve } from "https://deno.land/std@0.118.0/http/server.ts";

await serve((request) => {
  return new Response("Hello", { status: 200 });
}, { port: 8000 });

この変更に合わせて、既存のTypeScriptベースのHTTPサーバはhttp/server_legacy.tsに移動していましたが、deno_std v0.118.0で削除されました。

deno_std/ws

deno_std/wsはTypeScriptベースのWebSocketサーバの実装を提供するモジュールです。

Deno本体でDeno.upgradeWebSocketが実装されたため、deno_std v0.118.0で削除されました。

deno_std/collections

deno_std v0.103.0で追加された新しいモジュールです。

コレクション操作に関する様々な関数が提供されています。

Kotlinの標準ライブラリに影響を受けているようです。

import { groupBy } from "https://deno.land/std@0.117.0/collections/group_by.ts";

const users = [{ name: "foo", group: "admin" }, { name: "bar", group: "normal" }];
const usersByGroup = groupBy(users, (it) => it.group);
console.log(usersByGroup["admin"]);

deno_std/crypto

deno_std v0.104.0で追加された新しいモジュールです。

https://github.com/denoland/deno_std/pull/1025

背景

元々、deno_std/hashというモジュールがありました。
このdeno_std/hashのAPIはNode.jsのcryptoモジュールに影響を受けていると思われます。

しかし、Deno本体でWeb Crypto APIの実装が進んできたこともあり、それをベースにしてdeno_std/cryptoが追加されました。

deno_std/cryptoはWeb Crypto APIをベースにしつつ、Web Crypto APIでは提供されていないアルゴリズムなどを提供することを目的としています。

deno_std/encoding/toml

deno_std/encoding/tomlはDenoで実装されたTOMLパーサです。

大きな変更として、内部のパーサが1から書き直され、TOML仕様への準拠度が向上しています。

Deno Deploy

Deno公式によりDeno Deployが公開されました。

Cloduflare Workersなどのように、CDNのエッジ上でTypeScriptやJavaScriptなどを実行することができます。

これに合わせて、FreshSiftなどのDeno Deployで動作するフレームワークが公開されました。

また、OakなどのフレームワークでもDeno Deployのサポートが追加されています。

Denoが法人化

Deno Deployの発表に合わせてDenoの法人化が発表されました。

Deno本体やDeno Deployの開発などを中心に活動しています。

その他にも、TC39へのジョインなど様々な活動が行われています。

https://deno.com/blog/the-deno-company

Deno公式のDockerイメージ

https://hub.docker.com/r/denoland/deno

元々、DenoにはAndy Hayden氏を中心にメンテナンスされていたDockerイメージがありました。

このDockerイメージがDeno公式でメンテナンスされるようになりました。

https://github.com/denoland/deno_docker

Slackの新プラットフォーム

Slackの次世代開発プラットフォームが発表されました。

このプラットフォームでは、CLIやSDKなどでDenoが採用されています。

https://deno.com/blog/slack

dnt

Denoで書いたモジュールをNode.jsから利用できるようにするツールが公開されました

https://github.com/denoland/dnt

12月03日のkt3kさんの記事で解説されているため、詳しくはそちらを参照ください

lint.deno.landがDeno Deployに移行

deno lintの公式サイトであるlint.deno.landがDeno Deployへ移行されました。

それに合わせて、内部実装もdext.ts+Tailwind CSSからFresh+Twindへ移行されています。

doc.deno.landがリニューアル

DenoのAPIドキュメンテーションサイトであるdoc.deno.landがリニューアルされました。

https://twitter.com/deno_land/status/1466810485337120779

合わせて、内部実装もVercel+Next.jsからDeno Deploy+Oak+NanoJSX+Twindに移行されています。

SkypackのDenoサポートが強化

Skypackはnpmパッケージを配信するCDNです。

このSkypackでDenoのサポートの強化が行われています。

https://www.skypack.dev/blog/2021/02/skypack-npm-packages-in-deno/

具体的には、Node.jsの組み込みモジュールの読み込みをdeno_std/nodeへの読み込みに自動で置き換えてくれる機能が追加されています。

これにより、Node.jsの組み込みモジュールに依存したnpmパッケージがDenoから使いやすくなりました

他にも、SkypackはURLにクエリパラメータとしてdtsを指定することでレスポンスにX-TypeScript-Typesヘッダを付与してくれるため、Denoとの相性がよいです。

https://cdn.skypack.dev/react@17.0.1?dts

https://zenn.dev/uki00a/articles/how-to-use-npm-packages-in-deno#x-typescript-typesとは%3F

Flat Data

Flat Dataという、HTTPエンドポイントまたはSQLクエリからデータを取得し、GitHubリポジトリに格納するためのGitHubアクションが公開されました。(GitHub製)

https://github.com/githubocto/flat

Denoを使用したJavaScript/TypeScriptファイルの実行がサポートされており、ダウンロードされたデータを変換することができます。

また、GitHubリポジトリに格納されたデータを可視化するために、Flat Viewerというツールも提供されています。

denodrivers

Denoでは様々なデータベースクライアントライブラリが開発されています。

元々、これらのライブラリは有志によって開発されていました。

これらのライブラリを一箇所で開発・管理することを目的として、denodriversというOrganizationができました。

https://github.com/denodrivers

Discordチャンネルもあるため、開発の動向などについては、こちらを見てみるとよいかもしれません。

https://discord.gg/QXuHBMcgWx

おわりに

今年一年でDenoには様々な機能の追加やできごとなどがありました。

特にDeno Deployの発表やSlackでのDenoの採用などは、今後のDenoの使用率などにも影響してくるのではないかと感じています。

Denoの今後の動きについては、JSConf JPのkt3kさんの発表で解説されているため、興味のある方は見てみてはいかがでしょうか。

https://kt3k.github.io/talk_jsconfjp2021

参考

Discussion