deno.json（Deno設定ファイル）の書き方【随時更新】

2021.09.14リリースのDeno v1.14で、deno.json（Denoの設定ファイル）が導入されした。

元々、DenoにはCargo.tomlやpackage.jsonのような設定ファイルが存在しませんでしたが、import-mapやtsconfig.jsonの一括管理や、lintやfmtの設定を行う目的で設定ファイルが導入されました。

この記事では、設定ファイルの書き方について解説します。

:::note warn
2023.04.27のv1.33以降、設定ファイルの書き方が変更されています (詳しくは後述)。
:::

# deno.jsonの特徴

Node.jsにおけるpackage.jsonやtsconfig.jsonの反省を踏まえた設計になっています。

- ブラウザと同様、deno.jsonが無くても動作する（あくまでオプションという扱い）
- ファイル中にコメントを書く場合は、拡張子をjsoncにする必要がある
- 未知のプロパティを許可しない（他のプログラムの設定ファイルとして使うことはできない）
- 外部モジュールの読み込みには関与しない
  - ブラウザと挙動を合わせる
  - package.jsonの「パスのルートまで遡って設定ファイルを発見する」という動作は、URLからのimport時にサーバーに無駄な負荷をかける
- 実行時の**エントリポイントでのみ使用される**
  - 指定できるのは1ファイルのみ

外部モジュールのトップにdeno.jsonが置いてあったとしても、そのモジュールをimportするプログラムからは単に無視されます。ここがpackage.jsonとの大きな違いです。

# deno.jsonの構文

deno.jsonには主に以下の内容を指定できます。

- tsconfig.jsonの設定
- import-mapの設定
- Denoサブコマンドに関する設定(`deno lint` / `deno fmt` / `deno test` / `deno bench`)
- ロックファイルの設定

通常はプロジェクトフォルダの1番上にdeno.jsonを設置します。
ファイル中にコメントを書く場合は拡張子を`jsonc`にします。

また、Deno v1.34以降、ファイル名はglob展開されるようになりました。(例: `**/*.ts`)

なお基本的にデフォルト値のままでも動作するため、`task`とimport-mapの設定以外は記述しないことが多いです。

```jsonc:deno.jsonc (例)
// 注：全てoptionalなプロパティ（指定してもしなくてもよい）
// 基本的にはデフォルト設定で動作するので記述不要。
{
  // tsconfig.jsonのcompilerOptionsに相当
  "compilerOptions": {
    "allowJs": true,
    "lib": ["deno.window"],
    "strict": true
    // ...etc
  },

  // import-mapのimportsプロパティに相当
  "imports": {
    // (例)
    "lodash": "https://esm.sh/lodash@4.17.21"
  },

  // import-mapのscopesプロパティに相当
  "scopes": {
    "https://deno.land/x/example/": {
      "https://deno.land/std@0.177.0/": "./patched/"
    }
  },

  // 外部ファイルのimport-mapを使用したい場合に指定する
  // コマンドラインフラグに`--import-map=<FILE>`が渡された場合は、その値で上書きされる
  "importMap": "./import-map.json",

  // deno taskコマンド設定
  "tasks": {
    // (例) `deno task task_name`を実行すると`echo 1`が走る
    "task_name": "echo 1"
  },

  // deno lintコマンド設定
  "lint": {
    // lint対象に含めるファイル
    "include": ["./include/"],

    // lint対象から除外するファイル
    "exclude": ["./exclude/"],

    // lint対象のルールを指定する
    "rules": {
      "tags": ["recommended"], // ベースのルールセット (デフォルトは"recommended")
      "include": ["ban-untagged-todo"], // tagsで指定したルールに対して追加したいルール
      "exclude": ["no-unused-vars"] // tagsで指定したルールに対して除外したいルール
      // ルール名は https://lint.deno.land/ を参照
    },

    // 結果の表示形式
    "report": "pretty" // デフォルト
    // "report": "compact" // 省略形
    // "report": "json" // JSON形式
  },

  // deno fmtコマンド設定
  "fmt": {
    // フォーマット対象に含めるファイル
    "include": ["./include/"],

    // フォーマット対象から除外するファイル
    "exclude": ["./exclude/"],

    // インデント幅
    "indentWidth": 2,

    // 1行当たりの文字数
    "lineWidth": 80,

    // 行末にセミコロンを付加するかどうか
    "semiColons": true,

    // 引用符にシングルクォーテーションとダブルクォーテーションのどちらを使用するか
    "singleQuote": false,

    // インデントにスペースとタブのどちらを使用するか
    "useTabs": false,

    // Markdownファイル内での文章の改行方法
    "proseWrap": "always" // 文章をlineWidth幅で改行する (デフォルト)
    // "proseWrap": "preserve" // 改行位置を変更しない
    // "proseWrap": "never" // 全ての改行を削除する
  },

  // deno benchコマンド設定
  "bench": {
    // ベンチマーク対象に含めるファイル
    "include": ["./include/"],
    // ベンチマーク対象から除外するファイル
    "exclude": ["./exclude/"]
  },

  // deno testコマンド設定
  "test": {
    // テスト対象に含めるファイル
    "include": ["./include/"],
    // テスト対象から除外するファイル
    "exclude": ["./exclude/"]
  },

  // フォーマッター、リンター、テスト、ベンチマーク等全てにおいて無視されるファイル
  "exclude": ["./node_modules/"],

  // npmライブラリの保存場所をnode_modulesディレクトリにするかどうか
  // trueにすると、実行時に--node-modules-dirフラグを渡したときと同様の挙動になる
  "nodeModulesDir": false,

  // ロックファイル(deno.lock)を生成するかどうか
  "lock": true
}
```

上記以外のプロパティがあった場合、エラーが発生します。

### glob展開

Deno v1.34以降、ファイル名はglob展開されます。
この機能はrustの[globライブラリ](https://crates.io/crates/glob)を使用して実装されているようです。そのドキュメントによると、使用できる構文は以下のものです。

|文法|意味|
|---|---|
|`?`|任意の1文字に一致|
|`*`|任意の文字列に一致 (空文字列にも一致)
|`**`|カレントディレクトリと任意のサブディレクトリに一致<br>ただし、`**a`や`b**`, `***`のような構文はエラー
|`[...]`|括弧内の任意の文字と一致<br>(例：`[0-9]`/`[a-b]`/`[abc]`)<br>Unicode順に従って文字の範囲を指定することができる
|`[!...]`|括弧内にない任意の文字と一致<br>(例：`[!0-9]`/`[!a-b]`/`[!abc]`)

メタ文字(`?`, `*`, `[`, `]`)自体にマッチさせたい時は、各括弧`[]`構文を使用してマッチできます。(例: `[?]`)

- `[[]`と`[]]`はそれぞれ`[`と`]`という文字に対してマッチする。
- `[![]`と`[!]]`も同様に、それぞれ`[`と`]`という文字以外に対してマッチする。
- `[...]`内で`-`という文字にマッチさせたい時は、先頭か末尾に配置する<br>(例：❌`[a-b]`、⭕`[-ab]`)

これらのglob展開は、`include`/`exclude`のファイル名指定のほか、`deno task`コマンドでも使用することができます。

:::note warn
**構文の変更について**

2023.04.27のv1.33以降、設定ファイルの書き方が変更されています (flat config)。

```json:v1.33以前のdeno.json (例)
    "files": {
      "include": ["src/"],
      "exclude": ["src/testdata/"]
    },
    "options": {
      "useTabs": true,
      "lineWidth": 80,
      "indentWidth": 4,
      "singleQuote": true,
      "proseWrap": "preserve"
    }
```
が
```json:v1.33以降のdeno.json (例)
    "include": ["src/"],
    "exclude": ["src/testdata/"],
    "useTabs": true,
    "lineWidth": 80,
    "indentWidth": 4,
    "singleQuote": true,
    "proseWrap": "preserve"
```

に変更されています。

従来形式の設定ファイルを使用すると、以下のようなwarningが表示されます。
**従来形式は今夏リリースのDeno v2で使用できなくなる予定**ですので、早めに更新しましょう。

```
Warning: "options" configuration is deprecated. Please use "flat" options instead.
Warning: "files" configuration is deprecated. Please use "include" and "exclude" instead.
```
:::

# deno.jsonの使い方

Deno1.18以降、deno.jsonは自動で読み込みされます。

#### コマンドライン実行時の設定

コマンドラインから実行する際は、ルートに向かって自動で`deno.json(c)`を探します。最初に見つかった`deno.json(c)`が使用されます。

- ファイル名を指定して実行した場合（例：`deno fmt ./src/serve.ts`）
    →ファイルが存在するディレクトリからルートに向かって`deno.json(c)`を探索
- ファイル名を指定せずに実行した場合
    →カレントディレクトリからルートに向かって`deno.json(c)`を探索

```shell
> deno run ./src/serve.ts # ./srcからルートに向かってdeno.json(c)を探索
> deno fmt ./src/serve.ts # ./srcからルートに向かってdeno.json(c)を探索

> deno fmt        # カレントディレクトリからルートに向かってdeno.json(c)を探索
> deno lint       # カレントディレクトリからルートに向かってdeno.json(c)を探索
> deno task serve # カレントディレクトリからルートに向かってdeno.json(c)を探索
```

設定ファイルを強制的に指定して実行したい場合は、コマンドラインフラグの`--config`で指定します。

```shell
> deno run --config ./deno.json ./mod.ts
```

設定ファイルを無視したい場合は、コマンドラインフラグで`--no-config`を指定します。

```shell
> deno run --no-config ./mod.ts
```

#### エディタの設定

vscode等の[拡張機能](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)からも、自動読み込みが行われます。
強制的に指定する場合は`./vscode/settings.json`等のエディタ設定ファイルを開き、`deno.config`プロパティにdeno.jsonへのパスを指定します。

```json:.vscode/settings.json
{
  "deno.enable": true,
  "deno.lint": true,
  "deno.config": "./deno.jsonc"
}
```

# まとめ

- Deno設定ファイル（deno.json）を使うと、`deno fmt`や`deno lint`の設定やimport-mapの指定を行うことができる
- コマンド実行の際には自動で読み込まれる
- 2023年4月のv1.33から書き方が変わったので注意

フォーマッターやリンターの設定については、デフォルト値がスタイルガイドに沿うような形で設定されているため、通常は設定を上書きする必要はありません。

deno.jsonの機能は順次追加されています。
それぞれの機能が導入されたバージョンは、

- v1.14：リンター・フォーマッター・tsconfigの設定をサポート
- v1.18：設定ファイルの自動検出をサポート
- v1.20：import-mapとタスクランナーをサポート
- v1.24：`deno test`サブコマンドの設定をサポート
- v1.27：リンターの`report`設定を追加
- v1.29：ロックファイルと`deno bench`コマンドの設定をサポート
- v1.30：ファイル中に直接import-mapを記述可能になる / フォーマッタのセミコロン有無設定を追加
- v1.33：flat configの導入で階層構造が変更([#17799](https://github.com/denoland/deno/pull/17799))
- v1.34：全コマンドまとめて`exclude`するための設定と、npmライブラリの保存に`node_modules`ディレクトリを使用するかどうかの設定、ファイル名のglob展開に対応

という時系列でした。