Denoをはじめる
目次
煩雑な設定なしにTypeScriptを実行/テスト/リント/フォーマットできる環境を試してみたかったので、Denoを改めて使ってみる。
準備
ここではmacOS/VS Codeで進めていく。
Denoのインストール
参考リンク: https://github.com/denoland/deno_install
macOSでHomebrewを使う場合は:
brew install deno
公式のインストールスクリプトも用意されている。
Shell(macOS/Linux)の場合:
curl -fsSL https://deno.land/x/install/install.sh | sh
PowerShell(Windows)の場合:
iwr https://deno.land/x/install/install.ps1 -useb | iex
DenoのCLI
参考リンク: https://deno.land/manual/getting_started/command_line_interface
バージョンの確認は次のコマンドで行う:
deno --version
更新は次のコマンドで行う:
deno upgrade
ヘルプは次のコマンドで表示される:
deno help
deno -h
deno --help
Denoの設定ファイル
Denoではすべてデフォルトのままで使う場合、設定ファイルは必要ない。ただし、TypeScriptやリンター、フォーマッターの設定を記述したい場合、--config
オプションで渡すことのできる.json
ファイルまたは.jsonc
ファイルを用意しておくことができる。
デフォルトの名前は決められていないが、将来的なサポートのためにdeno.json
またはdeno.jsonc
という名前が推奨されている。
IDEの設定
参考リンク: https://deno.land/manual/getting_started/setup_your_environment
VS Code
参考リンク: https://deno.land/manual/vscode_deno
Deno公式拡張機能のhttps://marketplace.visualstudio.com/items?itemName=denoland.vscode-denoをインストールし、設定からDeno: Enable
(deno.enable
)を有効にする。Deno: Initialize Workspace Configuration
コマンドで.vscode/settings.json
を作成してもよい。
.vscode/settings.json
の例:
{
"deno.config": "./deno.json",
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"[typescript][typescriptreact][javascript][javascriptreact][json][jsonc][markdown]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}
}
なお、設定ファイルの名前にdeno.jsonc
またはdeno.json
を使っている場合は自動で認識されるので、deno.config
で設定する必要はない。
tasks.json
で使えるタスク定義やプロブレムマッチャーも提供されている。
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "deno",
"command": "run",
"args": ["src/main.ts"],
"problemMatcher": ["$deno"]
},
{
"type": "deno",
"command": "test",
"problemMatcher": ["$deno-test"]
},
{
"type": "deno",
"command": "lint",
"problemMatcher": ["$deno-lint"]
}
]
}
JetBrainsのIDE
JetBrains公式プラグインのhttps://plugins.jetbrains.com/plugin/14382-denoをインストールする。
ファイルの実行
参考リンク: https://deno.land/manual/getting_started/command_line_interface
Denoでファイルを実行するにはdeno run
コマンドを用いる。
hello.ts
:
console.log("Hello, World!");
コマンド:
deno run hello.ts
出力:
Hello, World!
リモートのファイルも実行できる。
コマンド:
# 最新版
deno run https://deno.land/std/examples/welcome.ts
# 0.133.0
deno run https://deno.land/[email protected]/examples/welcome.ts
出力:
Welcome to Deno!
ファイル名の後の引数はすべてスクリプトに対する引数となる。
deno run main.ts --help
パーミッション
参考リンク: https://deno.land/manual/getting_started/permissions
Denoではデフォルトでネットワークやファイルシステムへのアクセスがすべてオフになっている。プログラムからのアクセスを許可するには、実行時にオプションやプロンプトから許可を与える必要がある。
以下、オプションを列挙するが、実際にdeno run
を実行するには実行するファイル名が引数として必要となるとなることには注意。
# ネットワークアクセス
deno run --allow-net
deno run --allow-net=github.com,localhost:8080
# ファイルシステムアクセス(読み取り)
deno run --allow-read
deno run --allow-read=
# ファイルシステムアクセス(書き込み)
deno run --allow-write
deno run --allow-write=
# 環境変数
deno run --allow-env
deno run --allow-env=HOME,PATH
# 高分解能時間測定(high-resolution time measurement)
deno run --allow-hrtime
DenoはV8エンジンのサンドボックス機能を使うことでセキュアな実行をサポートしているが、コマンドやFFIはサンドボックス外での実行を許可することになるので、注意が必要となる。
# コマンド(サブプロセス)の実行
deno run --allow-run
deno run --allow-run=imagemagick,ffmpeg
# FFI(外部関数インターフェース)によるダイナミックライブラリの読込・実行
deno run --allow-ffi
上記のすべてを許可するには--allow-all
または-A
が使えるが、当然注意が必要となる。
# すべて許可
deno run --allow-all
deno run -A
テスト
参考リンク: https://deno.land/manual/testing
テストの記述
ファイル名が以下の形式のファイルが、テストファイルとして認識される。
test.ts
*.test.ts
*_test.ts
hello.test.ts
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts"
Deno.test("Hello, Test!", () => {
assert("Hello, Test!".includes("Test"))
})
Deno.test
のAPIについては:
std/testing/asserts.ts
モジュールについては:
をそれぞれ参照。
テストの実行
deno test
でカレントディレクトリ以下の{*_,*.,}test.{ts, tsx, mts, js, mjs, jsx, cjs, cts}
、つまり、
hello/test.ts
hello.test.ts
hello_test.ts
のようなファイルに記述されているテストがすべて実行される。
# ディレクトリの指定
deno test hello/
# ファイルの指定
deno test hello.test.ts
も可能。
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts"
Deno.test("Hello, Test!", { permissions: { read: true } }, () => {
assert(true)
})
のようにパーミッションが必要な場合は、deno run
の場合と同様にオプションにより実行に許可を与える。
deno test --allow-read
リント
参考リンク: https://deno.land/manual/tools/linter
コードのリントはdeno lint
コマンドで行う。
# すべてのファイル(カレントディレクトリ以下)
deno lint
# 指定したディレクトリ
deno lint src/
# 指定したファイル
deno lint src/main.ts src/lib.ts
ルールの一覧は:
を参照。
設定ファイルで、含めるファイルやリントのルールを指定できることもできる。
{
"lint": {
"files": {
"include": ["src/"],
"exclude": ["src/testdata/"]
},
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"],
"exclude": ["no-unused-vars"]
}
}
}
フォーマット
参考リンク: https://deno.land/manual/tools/formatter
コードのフォーマットはdeno fmt
コマンドで行う。
# すべてのファイル(カレントディレクトリ以下)
deno fmt
# 指定したディレクトリ
deno fmt src/
# 指定したファイル
deno fmt src/main.ts src/lib.ts
設定ファイルで、含めるファイルやフォーマットのオプションを指定することもできる。
{
"fmt": {
"files": {
"include": ["src/"],
"exclude": ["src/testdata/"]
},
"options": {
"useTabs": true,
"lineWidth": 80,
"indentWidth": 4,
"singleQuote": true,
"proseWrap": "preserve"
}
}
}
依存パッケージの管理
deps.ts
参考リンク: https://deno.land/manual/examples/manage_dependencies
依存パッケージを何らかのファイルにまとめて管理したい場合は、一つの慣習として次のような名前のファイルにリモートの依存先を記述し、そこからimport
して使う。
deps.ts
(dependencies
)dev_deps.ts
(devDependencies
)
src/deps.ts
:
export * as yaml from "https://deno.land/[email protected]/encoding/yaml.ts"
src/main.ts
:
import { yaml } from "./deps.ts"
const article = yaml.parse(`
title: Hello, Deno!
description: An introduction to Deno.
`)
console.log(article)
lock.json
参考リンク: https://deno.land/manual/linking_to_external_code/integrity_checking.md
複数の環境で本当に同一のパッケージが使われているか整合性を確かめるには、ロックファイルを使う。
ファイル名は公式マニュアルの例ではlock.json
となっているが、個人的にはdeno.lock.json
を使いたい。
ロックファイルを生成するには:
deno cache --lock-write --lock=deno.lock.json src/deps.ts
ロックファイルを使ってキャッシュを再読み込みするには:
deno cache --reload --lock=deno.lock.json src/deps.ts
感想
Node.jsへの反省として発表されてからしばらく経っているが、当初思ったよりは普及しておらず、開発中の機能も多い。
特に、外部ライブラリを使う起点となるパッケージ管理の仕組みについては、Goが結局go.mod
やgo.sum
を導入したように、ファイルでまとめて管理するように戻してしまったほうがいいのではという感がいなめなず、結果として、外部ライブラリを使うときにこそ良さを発揮するであろうサンドックス機能の恩恵にも与りにくくなっている。
とはいえ、TypeScriptとawait
がそのまま使えるのと、テストランナーやフォーマッター、リンターがデフォルトでついてくるのは、プロジェクトをミニマルに保ちたい場合にはうれしい利点となっている。
当面はTypeScriptのみで外部ライブラリさえ使わない小さな個人プロジェクトで使いつつ、今後の行く末に期待したい。