Deno v1.0.0 がリリースされた ということで、Deno Manual のページを翻訳してみました。
Qiita に翻訳記事を掲載するのは適さないのかもしれませんが、日本では一番エンジニアにリーチしそうに思いましたので Qiita に投稿させて頂きました。
ベースにしたバージョンとコミットは以下のものになります。v1.0.0 のものではないですが、ご了承くださいmm
- version: v1.0.0-rc1
- commit: https://github.com/denoland/deno/blob/6e287d951853ff38fb7002d31b9677c184ae6ffa/std/manual.md
原文では、 Compiler API などの unstable な機能の記述もありましたが、この翻訳では削除しています。
2020-08-18 追記
日本語訳を随時更新していく予定というページがありましたので、そちらのリンクを記載しておきます。
https://yoshixmk.github.io/deno-manual-ja/
はじめに
Deno は、デフォルトでセキュアな JavaScript/TypeScript ランタイムであり、開発者に素晴らしい経験をもたらします。
Deno は、V8, Rust, Tokio によって作られています。
主な特徴について
- デフォルトでセキュアです。明示的に有効にしない限り、ファイル、ネットワーク、環境変数等にアクセスすることができません。
- TypeScript のコードを明示的なコンパイルなしで実行できます。
- 単一の実行可能なファイル(
deno
)のみがリリースされます。 - 依存の検査 (
deno info
) や コードフォーマット (deno fmt
)などを実行する便利なサブコマンドが組み込まれています。 - Deno での動作が保証されている標準モジュールがあります。
- 実行に必要な複数のスクリプトファイルを一つの JavaScript ファイルにバンドルする機能があります。
思想
Deno は、現代的なプログラマのための生産性と安全性が高いスクリプティング環境を目指しています。
Deno は、単一の実行可能ファイルでリリースされます。
Given a URL to a Deno program, it is runnable with nothing more than the ~15 megabyte zipped executable.
Deno は、ランタイムとパッケージマネージャーの両方の役割を持ちます。
モジュールの読み込みには、URLを利用した標準のブラウザ互換のプロトコルが使用されます。
Deno は、bash や Python で書かれたユーティリティースクリプトの優れた代替えになります。
ゴール
- 単一の実行可能ファイル(
deno
)のみがリリースされる。 - 安全なデフォルト値を提供する。
- 特に許可されていない限り、スクリプトはファイル、環境、ネットワークにアクセスできません。
- ブラウザとの互換性があります。JavaScriptのみで書かれており、グローバルな名前空間
Deno
を使用していないDenoプログラムのサブセット(またはそのための機能テスト)は、最新のWebブラウザでも変更なく実行できるようにする必要があります。 - ユニットテスト、コードフォーマッター、リンターなどの組み込みツールを提供し、開発者の体験を向上させます。
- Does not leak V8 concepts into user land.
- 効率的に HTTP を扱える
Node.js との比較
- Deno は npm を使いません
- URL やファイルパスでモジュールを参照します。
- Deno はモジュール解決のアルゴリズムで
package.json
を使用しません。 - Deno のすべての非同期アクションは、Promise を返します。Deno は Node.js とは異なる API を提供します。
- Denoでは、ファイル、ネットワーク、環境変数へのアクセスに明示的な権限が必要です。
- エラーを捕捉しない場合は、Deno は常に終了します。
- "ES Modules"を使います。
require()
はサポートしません。サードパーティモジュールは、URLでインポートできます。
import * as log from "https://deno.land/std/log/mod.ts";
Other key behaviors
- リモートコードは最初の実行時にダウンロード・キャッシュされ、コードが
--reload
フラグで実行されるまで更新されません。 - URL からダウンロードされファイルは、不変でキャッシュ可能である必要があります。
ロゴ
次のロゴは、Deno 本体と同様に、MITライセンスの下で配布されています(パブリックドメインでフリーで使用できます)。
- A hand drawn one by @ry
- An animated one by @hashrock
- A high resolution SVG one by @kevinkassimo
- A pixelated animation one by @tanakaworld
組み込まれているユーティリティーとコマンド
- 依存検査 (
deno info
) - コードフォーマッター (
deno fmt
) - バンドリング (
deno bundle
) - ランタイムの型情報 (
deno types
) - テストランナー (
deno test
) - デバッガ (
--debug
) - リンター (
deno lint
) coming soon
セットアップ
Deno は、macOS, Linux, Windows で動作します。
Deno は、単一の実行可能なバイナリです。
外部の依存ファイルはありません。
ダウンロードとインストール
deno_install は、バイナリをダウンロードし、インストールするための便利なスクリプトを提供します。
シェルでインストール:
curl -fsSL https://deno.land/x/install/install.sh | sh
PowerShell でインストール:
iwr https://deno.land/x/install/install.ps1 -useb | iex
Scoop (Windows)でインストール:
scoop install deno
Chocolatey (Windows)でインストール:
choco install deno
Homebrew (macOS)でインストール:
brew install deno
Cargo でインストール:
cargo install deno
Deno のバイナリは、 github.com/denoland/deno/releasesから tar や zip ファイルをダウンロードし、手動でインストールすることもできます。
これらのパッケージには、実行可能ファイルが1つだけ含まれています。
macOSとLinuxでは、実行可能ビットを設定する必要があります。
インストールし、環境変数 PATH
を設定し、次を実行してみてください。
deno run https://deno.land/std/examples/welcome.ts
インストール後の確認
インストール後に、deno --version
を実行し確認します。
これでDenoバージョンがコンソールに出力されれば、インストールは成功しています。
deno コマンドのオプションと使用方法を確認したい場合は、deno help
を実行してください。
サブコマンドのオプションを確認したい場合は、deno help <subcommand>
を実行してください。
バージョンアップ
Deno をバージョンアップするには、deno upgrade
を実行します。
これは、github.com/denoland/deno/releases から最新のリリースを取得・解凍し、現在の実行ファイルを置き換えます。
ソースからビルド
コントリビュータ向けビルド手順を参照してください。
環境設定
Deno での開発の生産性をあげるために、環境を設定する必要があります。
シェルのオートコンプリート、環境変数、エディタや IDE の設定について説明します。
環境変数
Deno の動作を制御する環境変数があります:
DENO_DIR
のデフォルトは $HOME/.deno
ですが、生成されたソースコードやキャッシュされたソースコードの書き込み先や読み込み先を変更するために、任意のパスを設定することができます。
NO_COLOR
を設定するとカラー出力が無効になります。
https://no-color.org/ を確認してください。
プログラムからは、boolean 定数 Deno.noColor
を用いて --allow-env
を指定せずに NO_COLOR
が設定されているかどうかを確認することができます。
シェル補完
deno completions <shell>
コマンドを使用して、シェルの補完スクリプトを生成できます。
このコマンドは標準出力に出力するため、適切なファイルにリダイレクトする必要があります。
サポートシェル:
- zsh
- bash
- fish
- powershell
- elvish
例:
deno completions bash > /usr/local/etc/bash_completion.d/deno.bash
source /usr/local/etc/bash_completion.d/deno.bash
エディタと IDEs
Deno のモジュールのインポートは、ファイル拡張子を指定することが可能であり、URL 形式でのインポートが可能です。
現時点では、ほとんどのエディターと Language Server ではこれをサポートしていないため、多くのエディターはエラーとして扱います。
コミュニティはこれらの問題を解決するために、いくつかのエディタ用の拡張機能を開発しました。
Support for JetBrains IDEs is not yet available, but you can follow and upvote these issues to stay up to date:
JetBrains IDE のサポートはまだ利用できませんが、これらの issue をフォローし、vote することができます。
- https://youtrack.jetbrains.com/issue/WEB-41607
- https://youtrack.jetbrains.com/issue/WEB-42983
- https://youtrack.jetbrains.com/issue/WEB-31667
API リファレンス
deno types
Deno のランタイムAPIの正確な情報を取得するには、コマンドラインで次のコマンドを実行します。
$ deno types
出力は、Deno に組み込まれている3つのライブラリファイルを連結したものです。
参考サイト
Rust プログラムに Deno を組み込みたい場合は、Rust Deno API を参照してください。
Deno クレートは、crates.io にホストされています。
Examples
Unix の cat コマンドの実装
このプログラムでは、コマンドライン引数をファイル名とみなし、ファイルをオープンして標準出力に出力します。
for (let i = 0; i < Deno.args.length; i++) {
let filename = Deno.args[i];
let file = await Deno.open(filename);
await Deno.copy(file, Deno.stdout);
file.close();
}
The copy()
function here actually makes no more than the necessary kernel -> userspace -> kernel copies.
That is, the same memory from which data is read from the file, is written to stdout.
これは、Deno の I/O ストリームの全般的な設計目標を示しています。
実行:
$ deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd
TCP echo server
これは、ポート8080で接続を受け入れ、送信されたものをクライアントに返す単純なサーバーの例です。
const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
Deno.copy(conn, conn);
}
このプログラムを起動すると、PermissionDeniedエラーが発生します。
$ deno run https://deno.land/std/examples/echo_server.ts
error: Uncaught PermissionDenied: network access to "0.0.0.0:8080", run again with the --allow-net flag
► $deno$/dispatch_json.ts:40:11
at DenoError ($deno$/errors.ts:20:5)
...
セキュリティ上の理由から、Denoは明示的な許可なしにプログラムがネットワークにアクセスすることを許可していません。
ネットワークへのアクセスを許可するには、コマンドオプションを使用します。
$ deno run --allow-net https://deno.land/std/examples/echo_server.ts
テストするには、netcat を使用してデータを送信してみてください:
$ nc localhost 8080
hello world
hello world
cat.ts
の例と同様に、ここでも copy()
関数は不要なメモリのコピーを作成しません。
カーネルからのパケットを受信し、それ以上複雑な処理をせずに送り返します。
権限の確認と取り消し
プログラムによっては、以前に付与された権限を取り消すことが必要になる場合があります。
後の段階でプログラムが権限を必要とする場合、プログラムはエラーになります。
// 権限を確認する
const status = await Deno.permissions.query({ name: "write" });
if (status.state !== "granted") {
throw new Error("need write permission");
}
const log = await Deno.open("request.log", "a+");
// 一部の権限を取り消す
await Deno.permissions.revoke({ name: "read" });
await Deno.permissions.revoke({ name: "write" });
// ログファイルを使う
const encoder = new TextEncoder();
await log.write(encoder.encode("hello\n"));
// これはエラーになります
await Deno.remove("request.log");
File server
これは、HTTPサーバとしてローカルディレクトリを公開します。
deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts
実行:
$ file_server .
Downloading https://deno.land/std/http/file_server.ts...
[...]
HTTP server listening on http://0.0.0.0:4500/
また、最新のバージョンにアップグレードしたい場合は、次のようにします:
$ file_server --reload
指定したモジュールの再読み込み
一部のモジュールのみをアップグレードしたい場合があります。--reload
オプションで対象を指定することにより制御できます。
すべての再読み込み
--reload
すべての標準モジュールの再読み込み
--reload=https://deno.land/std
特定のモジュール(この例では、colors とファイルシステムユーティリティ)を再ロードするには、カンマを使用してURLを区切ります。
--reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts
ホワイトリストによる権限指定
Deno は、ホワイトリストによる権限を指定する方法も提供しています。
これは、ホワイトリストでファイルシステムへのアクセスを制限する例です。
$ deno run --allow-read=/usr https://deno.land/std/examples/cat.ts /etc/passwd
error: Uncaught PermissionDenied: read access to "/etc/passwd", run again with the --allow-read flag
► $deno$/dispatch_json.ts:40:11
at DenoError ($deno$/errors.ts:20:5)
...
/etc
ディレクトリ配下の読み取り権限を付与できます。
$ deno run --allow-read=/etc https://deno.land/std/examples/cat.ts /etc/passwd
--allow-write
は、 --allow-read
と同様に使えます。
次は、ホスト名でネットワークへのアクセスを制限する例です。
const result = await fetch("https://deno.land/");
$ deno run --allow-net=deno.land https://deno.land/std/examples/curl.ts https://deno.land/
子プロセスの実行
例:
// 子プロセスを生成する
const p = Deno.run({
cmd: ["echo", "hello"],
});
// 完了を待機します
await p.status();
実行:
$ deno run --allow-run ./subprocess_simple.ts
hello
ここで関数が 'window.onload' に割り当てられます。
この関数は、メインスクリプトが読み込まれたあとに呼ばれます。
これは、Webブラウザの onload と同じでエントリポイントとして利用できます。
Deno.run()
で子プロセスを生成すると、デフォルトで 親プロセスの stdin
, stdout
, stderr
を引き継ぎます。
子プロセスと通信したい場合は、"piped"
オプションが使用できます。
const fileNames = Deno.args;
const p = Deno.run({
cmd: [
"deno",
"run",
"--allow-read",
"https://deno.land/std/examples/cat.ts",
...fileNames,
],
stdout: "piped",
stderr: "piped",
});
const { code } = await p.status();
if (code === 0) {
const rawOutput = await p.output();
await Deno.stdout.write(rawOutput);
} else {
const rawError = await p.stderrOutput();
const errorString = new TextDecoder().decode(rawError);
console.log(errorString);
}
Deno.exit(code);
実行:
$ deno run --allow-run ./subprocess.ts <somefile>
[file content]
$ deno run --allow-run ./subprocess.ts non_existent_file.md
Uncaught NotFound: No such file or directory (os error 2)
at DenoError (deno/js/errors.ts:22:5)
at maybeError (deno/js/errors.ts:41:12)
at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)
シグナルの捕捉
シグナルの捕捉には Deno.signal()
関数を使います。
for await (const _ of Deno.signal(Deno.Signal.SIGINT)) {
console.log("interrupted!");
}
Deno.signal()
は、プロミスとしても動作します。
await Deno.signal(Deno.Singal.SIGINT);
console.log("interrupted!");
シグナルの捕捉を止めたい場合、signal オブジェクトの dispose()
メソッドを使います。
const sig = Deno.signal(Deno.Signal.SIGINT);
setTimeout(() => { sig.dispose(); }, 5000);
for await (const _ of sig) {
console.log("interrupted");
}
上記の for-await
ループは、5秒後に sig.dispose()が呼ばれ、終了します。
ファイルシステムイベント
ファイルシステムのイベントをポーリングするには:
const watcher = Deno.watchFs("/");
for await (const event of watcher) {
console.log(">>>> event", event);
// { kind: "create", paths: [ "/foo.txt" ] }
}
イベントの正確な順序は、オペレーティングシステムによって異なる場合があることに注意してください。
この機能は、プラットフォームによって異なるシステムコールを使用します。
- Linux: inotify
- macOS: FSEvents
- Windows: ReadDirectoryChangesW
サードパーティコードのインポート
前述の例では、Deno が URL からスクリプトを実行できることがわかりました。
Web ブラウザ上の JavaScript と同様に、Deno は URL から直接ライブラリをインポートできます。
次のコードは、URL を使用してアサーションライブラリをインポートしています。
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
Deno.test("t1", function () {
assertEquals("hello", "hello");
});
Deno.test("t2", function () {
assertEquals("world", "world");
});
実行:
$ deno run test.ts
running 2 tests
test t1 ... ok
test t2 ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
このプログラムの実行に --allow-net
フラグを指定する必要はないですが、ネットワークにアクセスにしています。
ランタイムは、import で指定したファイルをダウンロードしディスクにキャッシュするための特別なアクセス権を持っています。
Denoは、環境変数 $DENO_DIR
で指定されたディレクトリに import で指定したファイルをキャッシュします。
$DENO_DIR
が指定されていない場合、デフォルトでシステムのキャッシュディレクトリになります。
次にプログラムを実行するときには、ダウンロードは行われません。
プログラムが変更されていなければ、再コンパイルもされません。
ディレクトリのデフォルト:
- Linux/Redox:
$XDG_CACHE_HOME/deno
または$HOME/.cache/deno
- Windows:
%LOCALAPPDATA%/deno
(%LOCALAPPDATA%
=FOLDERID_LocalAppData
) - macOS:
$HOME/Library/Caches/deno
- If something fails, it falls back to
$HOME/.deno
しかし、 https://deno.land/
がダウンした場合はどうなりますか?
外部サーバーに依存することは、開発には便利ですが、本番環境では脆弱です。
Production software should always bundle its dependencies.
本番のソフトウェアは常に依存関係をバンドルする必要があります。
Deno では、$DENO_DIR
ディレクトリでダウンロードした依存ファイルが管理されます。$DENO_DIR
は、環境変数として指定できます。
変更される可能性のあるURLを信頼するにはどうすればよいですか。
ロックファイルを使用(--lock
オプションを指定)することで、期待どおりのコードを実行しているか確認できます。
特定のバージョンをどのようにインポートしますか?
URLにバージョンを指定するだけです。
たとえば、次のURLは実行されるコードを完全に指定しています: https://unpkg.com/liltest@0.0.5/dist/liltest.js
本番環境で $DENO_DIR
にコードを保存する前述の手法と組み合わせると、実行される正確なコードを完全に指定し、ネットワークにアクセスせずにコードを実行できます。
同じURLをそれぞれのファイルでインポートするのは扱いにくくありませんか。URLの1つが異なるバージョンのライブラリにリンクしている場合はどうなりますか? 大規模なプロジェクトで、至るところで同じURLを書いてしまうと問題が発生しませんか?
解決策としては、 deps.ts
ファイル(Node.js の package.json
ファイルと同じ目的を果たす)を作成し、外部ライブラリをインポートおよび再エクスポートすることです。
例えば、大規模なプロジェクトで上記のアサーション・ライブラリを使用していたとします。
"https://deno.land/std/testing/asserts.ts"
を各ファイルでインポートするのではなく、サードパーティのコードをエクスポートする deps.ts
ファイルを作成し利用できます。
export {
assert,
assertEquals,
assertStrContains,
} from "https://deno.land/std/testing/asserts.ts";
同じプロジェクト内では、 deps.ts
からインポートして同じURLへの多くの参照を避けることができます:
import { assertEquals, runTests, test } from "./deps.ts";
この設計により、パッケージ管理ソフトウェア、中央集権型コードリポジトリ、余計なファイル形式が生み出す膨大な複雑さを回避できます。
型定義ファイルの利用
Deno のランタイムは、ファーストクラスの言語として JavaScript と TypeScript をサポートしています。
つまり、拡張子(または正しいメディアタイプを提供するサーバー)を含む完全修飾モジュール名が必要です。
Deno には曖昧なモジュール解決はありません。
TypeScript コンパイラは、JavaScript モジュールに型を適用するために、拡張子のないモジュールと Node.js モジュール解決ロジックの両方に依存しています。
このギャップを埋めるために、Deno は、曖昧な解決に頼ることなく型定義ファイルを参照する3つの方法をサポートしています。
コンパイラ ヒント
JavaScript モジュールをインポートしていて、そのモジュールの型定義の場所が分かってる場合は、インポート時に型定義を指定できます。
これはコンパイラのヒントの形式を使います。
コンパイラーのヒントは、.d.ts
ファイルの場所と、関連付けられている JavaScript コードを Deno に伝えます。
The hint is @deno-types
and when specified the value will be used in the compiler instead of the JavaScript module.
このヒントは @deno-types
であり、指定された値はJavaScriptモジュールの代わりにコンパイラで使用されます。
例えば、 foo.js
があり、その型定義ファイル foo.d.ts
である場合、コードは次のようになります:
// @deno-types="./foo.d.ts"
import * as foo from "./foo.js";
型定義ファイルを指定するパスは、モジュールのインポートと同じロジックで解決されます。つまり拡張子が必要で JavaScript フィアルと対応している必要があります。
リモート上のファイルを指定することも可能です。
このヒントは次の行の import
文 (または export ... from
文) に影響し、コンパイル時に指定されたモジュールの代わりに @deno-types
の内容が使われます。
上記の例では、Deno コンパイラは './foo.js' の代わりに './foo.d.ts' を読み込みます。
Deno は、プログラムを実行するときに './foo.js' を読み込みます。
JavaScript ファイルのトリプルスラッシュ の リファレンスディレクティブ
Deno で使用したいモジュールをホスティングしていて型定義ファイルの場所を Deno に認識させたい場合は、コードの中でトリプルスラッシュディレクティブを利用することができます。
例えば、JavaScriptモジュールがあり、そのファイルと一緒にある型定義の場所を Deno に認識させたいとします。
foo.js
は以下のようになるかもしれません。
/// <reference types="./foo.d.ts" />
export const foo = "foo";
Denoはこれを認識し、コンパイラーはファイルの型チェック時に foo.d.ts
を使用しますが、 foo.js
は実行時に読み込まれます。
ディレクティブは、モジュールのインポートと同じロジックで解決されます。つまり拡張子が必要で JavaScript ファイルと対応している必要があります。
リモート上のファイルを指定することも可能です。
カスタムヘッダー X-TypeScript-Types
使用するモジュールをホストしている場合は、カスタム HTTP ヘッダー X-TypeScript-Types
を使用して、型定義ファイルの場所を Deno に認識させることができます。
このヘッダーは、前述のトリプルスラッシュリファレンスと同じように機能しますが、JavaScriptファイル自体のコンテンツを変更する必要がなく、型定義の場所をサーバー自体が決定できることを意味します。
すべての型定義ファイルがサポートされているわけではありません。
Deno はコンパイラヒントを使って指定された .d.ts
ファイルをロードしますが、.d.ts
ファイルの中にはサポートされていない機能が含まれているものもあります。
具体的には、.d.ts
ファイルの中には、モジュールの解決ロジックを使って他のパッケージから型定義をロードしたり参照したりしているものがあります。
例えば、node
を含む型参照ディレクティブは、./node_modules/@types/node/index.d.ts
のようなパスに解決することを期待しています。
Since this depends on non-relative "magical" resolution, Deno cannot resolve this.
TypeScript ファイルでトリプルスラッシュの型参照を使用しないのはなぜですか?
TypeScript コンパイラは、型参照ディレクティブを含むトリプルスラッシュディレクティブをサポートしています。
Deno がこれを使うと TypeScript コンパイラの動作に支障をきたします。
Deno は JavaScript(とJSX)ファイル内のディレクティブのみを検索します。
現在のファイルがメインプログラムであるかどうかの判定
現在のスクリプトがメインプログラムとして実行されたかどうかを判定するには、import.meta.main
をチェックしてください。
if (import.meta.main) {
console.log("main");
}
コマンドラインインターフェース
V8 フラグ
V8 には多くの内部コマンドラインフラグがあります。
# 使用可能な V8 フラグを確認する
$ deno --v8-flags=--help
# 複数のフラグを使用する例
$ deno --v8-flags=--expose-gc,--use-strict
次のフラグは特に便利です。
--async-stack-trace
バンドル
deno bundle <source_file>
は、指定された source_file
のすべての依存関係を含む単一の JavaScript ファイルを出力します。
例:
> deno bundle https://deno.land/std/examples/colors.ts colors.bundle.js
Bundling "colors.bundle.js"
Emitting bundle to "colors.bundle.js"
9.2 kB emitted.
パラメータの出力ファイルを省略すると、stdout
に出力されます。
出力されたバンドルファイルは、他のモジュールと同様に実行できます:
deno run colors.bundle.js
出力されたバンドルファイルは自己完結型 ES モジュールで、コマンドラインで指定されたメインモジュールからのエクスポートが使用可能になります。
export { foo } from "./foo.js";
export const bar = "bar";
次のようにインポートできます:
import { foo, bar } from "./lib.bundle.js";
バンドルされたファイルは、Webブラウザでも読み込むことができます。
自己完結型のESモジュールであるため、 type
の属性は "module"
に設定する必要があります。
例:
<script type="module" src="website.bundle.js"></script>
または、別のESモジュールにインポートして使用することもできます:
<script type="module">
import * as website from "website.bundle.js";
</script>
実行可能なスクリプトのインストール
Denoは、実行可能なファイルを簡単にインストールするための deno install
を提供します。
deno install [OPTIONS ...] [EXE_NAME] [URL] [SCRIPT_ARGS ...]
は、 URL
で利用可能なスクリプトを指定し、 EXE_NAME
という名前でインストールします。
このコマンドは、指定されたCLIオプションとメインモジュールを使用して deno
を呼び出す、小さい実行可能なシェルスクリプトを作成します。
シェルスクリプトは、インストールルートの bin
ディレクトリ配下に配置されます。
例:
$ deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts
[1/1] Compiling https://deno.land/std/http/file_server.ts
✅ Successfully installed file_server.
/Users/deno/.deno/bin/file_server
実行ファイル名を変更するには、-n
/--name
オプションを使います:
deno install --allow-net --allow-read -n serve https://deno.land/std/http/file_server.ts
実行ファイル名が省略される場合、デフォルトで決定します:
- URL パスの最下層の名前を使用します。上記の例は
file_server
になります。 - If the file stem is something generic like 'main', 'mod', 'index' or 'cli', and the path has no parent, take the file name of the parent path. Otherwise settle with the generic name.
インストールルートを変更するには、--root
オプションを使用します。
$ deno install --allow-net --allow-read --root /usr/local https://deno.land/std/http/file_server.ts
インストールルートは、次の優先順位で決定します:
-
--root
オプション - 環境変数
DENO_INSTALL_ROOT
$HOME/.deno
必要に応じて手動でパスに追加する必要があります。
$ echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
インストール時にスクリプト実行に必要な権限を指定する必要があります。
$ deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts 8080
上記のコマンドは、参照権限で実行しポート8080にバインドする file_server
という名前の実行可能ファイルを作成します。
import.meta.main
を使って、実行可能なスクリプトのエントリポイントを指定すると良いでしょう。
例:
// https://example.com/awesome/cli.ts
async function myAwesomeCli(): Promise<void> {
-- snip --
}
if (import.meta.main) {
myAwesomeCli();
}
実行可能なスクリプトに関してユーザに共有する際は、インストールコマンドをドキュメントに記載してください。
# Install using deno install
$ deno install -n awesome_cli https://example.com/awesome/cli.ts
プロキシ
Denoは、モジュールのダウンロードと fetch
API のためのプロキシをサポートしています。
プロキシの設定は環境変数 HTTP_PROXY
と HTTPS_PROXY
から読み込みます。
Windows で環境変数が見つからない場合、レジストリからプロキシ情報を読み出します。
ロックファイル
Denoは、小さなJSONファイルを使用して、モジュールのサブリソースの整合性を保存およびチェックできます。
ロックファイルのチェックを有効し指定するには、--lock=lock.json
オプションを使用します。
ロックファイルを更新または作成するには --lock=lock.json --lock-write
オプションを使用します。
Import maps
Deno は、 import maps をサポートします。
deno コマンドの --importmap=<FILE>
オプションで import map を指定できます。
現在の制限:
- 指定可能な import map は、一つのみです
- フォールバックURLは使えません
- ネームスペース
std:
をサポートしていません - スキームは、
file:
,http:
,https:
のみサポートされます
例:
// import_map.json
{
"imports": {
"http/": "https://deno.land/std/http/"
}
}
// hello_server.ts
import { serve } from "http/server.ts";
const body = new TextEncoder().encode("Hello World\n");
for await (const req of serve(":8000")) {
req.respond({ body });
}
$ deno run --importmap=import_map.json hello_server.ts
WASM サポート
Deno は、 wasm バイナリを実行できます。
const wasmCode = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
65, 42, 11
]);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);
console.log(wasmInstance.exports.main().toString());
WASM ファイルは、import
文でも読み込むことができます:
import { fib } from "./fib.wasm";
console.log(fib(20));
TypeScript のコンパイラオプション
Deno エコシステムを TypeScript の理想的な状態にするために、すべての strict
フラグをデフォルトで有効にしています。
ただし、プログラムの実行時に tsconfig.json
などの設定ファイルをオプションで指定することによりカスタマイズすることが可能です。
アプリケーションを実行するときに -c
オプションを使い、設定ファイルを指定することができます。
deno run -c tsconfig.json mod.ts
設定可能な項目とデフォルトは、次のようになります:
{
"compilerOptions": {
"allowJs": false,
"allowUmdGlobalAccess": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"alwaysStrict": true,
"assumeChangesOnlyAffectDirectDependencies": false,
"checkJs": false,
"disableSizeLimit": false,
"generateCpuProfile": "profile.cpuprofile",
"jsx": "react",
"jsxFactory": "React.createElement",
"lib": [],
"noFallthroughCasesInSwitch": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitUseStrict": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveConstEnums": false,
"removeComments": false,
"resolveJsonModule": true,
"strict": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"suppressExcessPropertyErrors": false,
"suppressImplicitAnyIndexErrors": false,
"useDefineForClassFields": false
}
}
許可される値と使用例に関するドキュメントについては、TypeScript のドキュメントを参照してください。
注:上記にリストされていないオプションは、Deno でサポートされていないか、TypeScriptドキュメントで非推奨/実験的としてリストされています。
プログラムのライフサイクル
Deno は、Web ブラウザの JavaScript と互換のあるライフサイクルイベントをサポートしています: load
と unload
.
これらのイベントを使用し、プログラムのセットアップとクリーンアップのコードを作成することができます。
load
イベントのリスナーは非同期であり、待機します。
unload
イベントのリスナーは同期している必要があります。
どちらのイベントもキャンセルできません。
例:
// main.ts
import "./imported.ts";
const handler = (e: Event): void => {
console.log(`got ${e.type} event in event handler (main)`);
};
window.addEventListener("load", handler);
window.addEventListener("unload", handler);
window.onload = (e: Event): void => {
console.log(`got ${e.type} event in onload function (main)`);
};
window.onunload = (e: Event): void => {
console.log(`got ${e.type} event in onunload function (main)`);
};
// imported.ts
const handler = (e: Event): void => {
console.log(`got ${e.type} event in event handler (imported)`);
};
window.addEventListener("load", handler);
window.addEventListener("unload", handler);
window.onload = (e: Event): void => {
console.log(`got ${e.type} event in onload function (imported)`);
};
window.onunload = (e: Event): void => {
console.log(`got ${e.type} event in onunload function (imported)`);
};
console.log("log from imported script");
window.addEventListener
と window.onload
/window.onunload
の両方を使用してイベントのハンドラーを定義できることに注意してください。
そこには大きな違いがあります。次の例を実行してみましょう:
$ deno run main.ts
log from imported script
log from main script
got load event in onload function (main)
got load event in event handler (imported)
got load event in event handler (main)
got unload event in onunload function (main)
got unload event in event handler (imported)
got unload event in event handler (main)
window.addEventListener
を使用して追加されたすべてのリスナーが実行されましたが、window.onload
および window.onunload
に関しては、imported.ts
で定義されたものを main.ts
の定義がオーバーライドしています。
deno fmt
Deno ships with a built in code formatter that auto-formats TypeScript and JavaScript code.
Denoには、TypeScript や JavaScript のコードを自動でフォーマットするコードフォーマッタが組み込まれています。
# 現在のディレクトリとサブディレクトリのすべての JS/TS ファイルをフォーマットする
deno fmt
# 指定したファイルをフォーマットする
deno fmt myfile1.ts myfile2.ts
# 現在のディレクトリとサブディレクトリのすべての JS/TS ファイルがフォーマットされているかどうかをチェックします
deno fmt --check
# 標準入力をフォーマットし、標準出力に書き込む
cat file.ts | deno fmt -
コードの前に // deno-fmt-ignore
コメントをつけることでフォーマット対象から外します:
// deno-fmt-ignore
export const identity = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
また、ファイルの先頭に // deno-fmt-ignore-file
コメントを追加することで、ファイル全体をフォーマット対象から外すことができます。
内部の詳細
Deno と Linux の類否
Linux | Deno |
---|---|
Processes | Web Workers |
Syscalls | Ops |
File descriptors (fd) | Resource ids (rid) |
Scheduler | Tokio |
Userland: libc++ / glib / boost | https://deno.land/std/ |
/proc/$$/stat | Deno.metrics() |
man pages | deno types |
Resources
Resources(別名 rid
)は、File descriptor の Deno バージョンです。
これは、オープンファイル、ソケット、その他のものを参照するために使用される整数値です。
次のコードでは、オープンしている Resources を確認しています。
const { resources, close } = Deno;
console.log(resources());
// { 0: "stdin", 1: "stdout", 2: "stderr" }
close(0);
console.log(resources());
// { 1: "stdout", 2: "stderr" }
メトリクス
メトリクスは、様々な統計情報のための内部カウンターです。
> console.table(Deno.metrics())
┌──────────────────┬────────┐
│ (index) │ Values │
├──────────────────┼────────┤
│ opsDispatched │ 9 │
│ opsCompleted │ 9 │
│ bytesSentControl │ 504 │
│ bytesSentData │ 0 │
│ bytesReceived │ 856 │
└──────────────────┴────────┘
Schematic diagram
Profiling
To start profiling,
# Make sure we're only building release.
# Build deno and V8's d8.
ninja -C target/release d8
# Start the program we want to benchmark with --prof
./target/release/deno run --allow-net --v8-flags=--prof tests/http_bench.ts &
# Exercise it.
third_party/wrk/linux/wrk http://localhost:4500/
kill `pgrep deno`
V8 will write a file in the current directory that looks like this: isolate-0x7fad98242400-v8.log
. To examine this file:
D8_PATH=target/release/ ./third_party/v8/tools/linux-tick-processor
isolate-0x7fad98242400-v8.log > prof.log
# on macOS, use ./third_party/v8/tools/mac-tick-processor instead
prof.log
will contain information about tick distribution of different calls.
To view the log with Web UI, generate JSON file of the log:
D8_PATH=target/release/ ./third_party/v8/tools/linux-tick-processor
isolate-0x7fad98242400-v8.log --preprocess > prof.json
Open third_party/v8/tools/profview/index.html
in your browser, and select prof.json
to view the distribution graphically.
Useful V8 flags during profiling:
- --prof
- --log-internal-timer-events
- --log-timer-events
- --track-gc
- --log-source-code
- --track-gc-object-stats
To learn more about d8
and profiling, check out the following links:
LLDB を使用したデバッグ
LLDB を使用して Deno をデバッグできます。
$ lldb -- target/debug/deno run tests/worker.js
> run
> bt
> up
> up
> l
Rust コードをデバッグするには、 rust-lldb
が使用できます。
それは、LLDB のラッパーであり、Rust の開発環境に含まれています。
$ rust-lldb -- ./target/debug/deno run --allow-net tests/http_bench.ts
# On macOS, you might get warnings like
# `ImportError: cannot import name _remove_dead_weakref`
# In that case, use system python by setting PATH, e.g.
# PATH=/System/Library/Frameworks/Python.framework/Versions/2.7/bin:$PATH
(lldb) command script import "/Users/kevinqian/.rustup/toolchains/1.36.0-x86_64-apple-darwin/lib/rustlib/etc/lldb_rust_formatters.py"
(lldb) type summary add --no-value --python-function lldb_rust_formatters.print_val -x ".*" --category Rust
(lldb) type category enable Rust
(lldb) target create "../deno/target/debug/deno"
Current executable set to '../deno/target/debug/deno' (x86_64).
(lldb) settings set -- target.run-args "tests/http_bench.ts" "--allow-net"
(lldb) b op_start
(lldb) r
Deno コア
Deno のコアは、スタンドアローン crate としてリリースされます。
V8 自体がコアであり、"libdeno" という バインディング API があります。
詳細については、crate のドキュメントを参照してください。
継続的なベンチマークの計測
こちらからベンチマークを参照してください。
ベンチマークチャートは、//website/data.json
が BenchmarkData[]
型を持つものとします。
BenchmarkData
は次のように定義されています。
interface ExecTimeData {
mean: number;
stddev: number;
user: number;
system: number;
min: number;
max: number;
}
interface BenchmarkData {
created_at: string;
sha1: string;
benchmark: {
[key: string]: ExecTimeData;
};
binarySizeData: {
[key: string]: number;
};
threadCountData: {
[key: string]: number;
};
syscallCountData: {
[key: string]: number;
};
}
コントリビュート
- style guide を参照してください。
- 今後のリリースに向けての進捗状況はこちらで確認できます。
- ベンチマークを悪化させないでください。
- コミュニティチャットルームで助けを求めましょう。
- Issue に取り組み始める前に Issue コメントにその旨を記載してください。
開発
レポジトリの clone
Linux や Mac での clone:
git clone --recurse-submodules https://github.com/denoland/deno.git
Windows のための追加の手順:
- "Developer Mode" を有効にしてください (シンボリックリンクは管理者権限を必要とします).
- git のバージョンが 2.19.2.2.windows.1 以降であることを確認してください。
- checkout の前に、
core.symlinks=true
を設定してください。git config --global core.symlinks true git clone --recurse-submodules https://github.com/denoland/deno.git
前提条件
Denoをビルドする最も簡単な方法は、V8のプリコンパイルバージョンを使用することです:
cargo build -vv
ソースコードから Deno と V8 をビルドしたい場合は、次のようになります:
V8_FROM_SOURCE=1 cargo build -vv
V8 をソースコードからビルドする場合、さらに次の依存があります:
python
または python.exe
が PATH
上に存在し、Python 3 ではなく Python 2 を参照していることを確認してください。
Linux の場合、glib-2.0 の開発ファイルもインストールする必要があります(Ubuntuの場合は、apt install libglib2.0-dev
を実行してください)。
Mac の場合、XCode をインストールする必要があります。
Windows の場合:
-
VS Community 2019 と Desktop development with C++ toolkit を取得し、 必須ツールと以下のすべての C++ ツールを選択してください。
- Visual C++ tools for CMake
- Windows 10 SDK (10.0.17763.0)
- Testing tools core features - Build Tools
- Visual C++ ATL for x86 and x64
- Visual C++ MFC for x86 and x64
- C++/CLI support
- VC++ 2015.3 v14.00 (v140) toolset for desktop
-
"Debugging Tools for Windows" を有効にしてください。 "Control Panel" → "Programs" → "Programs and Features" → Select "Windows Software Development Kit - Windows 10" → "Change" → "Change" → Check "Debugging Tools For Windows" → "Change" -> "Finish" と進みます。
または、 Debugging Tools for Windows を使用します。(注意: ファイルをダウンロードし、手動でX64 Debuggers And Tools-x64_en-us.msi
をインストールする必要があります)
V8 のビルドについての詳細は、rusty_v8's README を参照してください。
ビルド
Cargo でビルドします:
# ビルド
cargo build -vv
# ビルドエラーが発生する場合、最新のマスターを使用していることを確認してからビルドをやり直してみてください
cargo clean && cargo build -vv
# 実行:
./target/debug/deno run cli/tests/002_hello.ts
Testing and Tools
Test deno
:
# Run the whole suite:
cargo test
# Only test cli/js/:
cargo test js_unit_tests
Test std/
:
cargo test std_tests
Lint the code:
./tools/lint.py
Format the code:
./tools/format.py
プルリクエストの作成
プルリクエストを作成する前に、次の事が完了しているか確認してください。
- 関連する Issue があり、それがPR文中で参照されていること
- 変更をカバーするテストがある
-
cargo test
が成功している -
tools/format.py
でコードをフォーマットしている -
./tools/lint.py
が成功している
third_party
の変更
deno_third_party
では、何が利用されているか把握できるように、Deno に依存する外部コードを管理しています。
手作業とプライベートスクリプトにより慎重に管理されます。
変更を加えるためには、 @ry や @piscisaureus の助けが必要になる可能性があります。
Ops の追加(aka bindings)
新しいAPIを追加するときに間違いを犯すことを非常に心配しています。
Deno に Op を追加する際には、他のプラットフォームでの該当インターフェースを調べる必要があります。
この機能が Go、Node、Rust、Python でどのようになっているかを列挙してください。
例として、PR #671で Deno.rename()
がどのように提案され、追加されたかを参照してください。
API のドキュメント作成
公開 API のドキュメントを作成することは重要であり、コードのインラインでそれを行いたいと考えています。
これにより、コードとドキュメントが緊密に結合されていることを確認することができます。
JSDoc の活用
deno
モジュールとグローバル/window
名前空間を介して公開されているすべてのAPIとタイプには、JSDoc ドキュメントが必要です。
このドキュメントは解析され、TypeScript コンパイラで利用できるため、さらに他の場所で利用するのは簡単です。
JSDoc ブロックは、対象のコードの直前にあり、 /**
で開始し */
で終了します。
例:
/** A simple JSDoc comment */
export const FOO = "foo";