Public Theta Blog

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: Enabledeno.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.tsdependencies
  • dev_deps.tsdevDependencies

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.modgo.sumを導入したように、ファイルでまとめて管理するように戻してしまったほうがいいのではという感がいなめなず、結果として、外部ライブラリを使うときにこそ良さを発揮するであろうサンドックス機能の恩恵にも与りにくくなっている。

とはいえ、TypeScriptとawaitがそのまま使えるのと、テストランナーやフォーマッター、リンターがデフォルトでついてくるのは、プロジェクトをミニマルに保ちたい場合にはうれしい利点となっている。

当面はTypeScriptのみで外部ライブラリさえ使わない小さな個人プロジェクトで使いつつ、今後の行く末に期待したい。