7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptの開発環境をDev Containersで作る手順まとめ

Posted at

はじめに

振り返るとだいぶ手順が多かったのでまとめてみます。

この記事は、書き始めた時期と書き終わった時期に数ヶ月ほどの差があります。
そのため一部チグハグなところがあると思いますが、目を瞑っていただけるとありがたいです。

また、その数ヶ月の間にコンテナの起動方法をDocker ComposeからDev Containersに変えたので、そのあたりが特にチグハグだと思います。

作る環境

  • TypeScript
  • Jest
  • VSCode(ローカルPCにインストール済み)
  • Git

みたいなのを、Dev Containersという拡張機能を使ってDockerで作ります。

ローカルの環境

  • OS:MacOS Sonoma 14.1 (Apple M1)
  • ターミナル:ITerm2をVSCodeから使ってる
  • エディタ:VSCode(拡張機能あり)
    • Git Graph
    • Docker
    • Dev Containers
    • Jest
    • など...
  • Docker Desktopインストール済み

愚痴

私はもともとJavaScriptの学習をしていたのですが、色々やっていくうちにTypeScriptいいなと思い始めました。

ですが、TSはJSと違ってコンパイラ(トランスパイラ)が必要です。
そしてローカルPCにコンパイラを入れるのが嫌だったため、TSをやるときはいつもDockerを立てています。

そして毎回1からDocker立ててTypeScript入れてJest入れて設定ファイル編集して...とかやっているのですが、これはあまりに非効率だと昨日気づきました。
ただ自動化する方法がわかんないので辛いです。

まあそれでも今後少しでも楽になるように、今日のところは上に書いた開発環境を作る手法をメモっておこうと思います。

結論

これは先日怒りの感情に任せて書いたメモです。
これだけだと分かりづらいので、下に詳しい解説を書いていこうと思います。

  1. devcontainer.jsonをコピペで作る
    • TypeScriptとJestを入れる
    • node_modulesをボリュームにする
    • 拡張機能のセッティングする
  2. tsconfig.jsonのセッティング
    • tsc --inittsconfig.jsonを生成する
    • rootdir./srcにする
    • outdir./buildにする
    • targetes2022にする
    • resolveJsonModuletrueにする
    • 一応動くか試してみる
  3. Jestのセッティング(jest.config.js)
    • TypeScriptでテストを書くには追加でパッケージを入れる必要がある
      • @types/jest
      • ts-jest
    • まずはjest --initjest.config.jsを生成する
      • configファイルでTypeScriptを使うか?にyと答えるとやり直し
        (ts-nodeが必要って言われる)
    • デフォルトだとimport使えない問題を解消する
      (ts-jestを入れた上でpresetts-jestを指定する)
    • テストファイル作って一応動かしてみる
      • だいたいここでなんかエラー出るので調べる
  4. .gitignoreを作って以下を書く
    • node_modules インストールしたパッケージがある場所
    • build コンパイルしたjsファイル置き場
    • 時と場合によっては.gitignore
    • やってるとだんだん増えてゆく

1. Dockerコンテナを立てる

まずはDockerコンテナを立てていきます。
忙しい方は下のまとめを読んでみてください。

devcontainer.jsonの作成

昔はdockerfilecompose.yamlを作っていたのですが、最近は.devcontainer/devcontainer.jsonでやるようにしています。

これはVSCodeのDev Containerという拡張機能用のファイルで、Dockerコンテナの構築をやってくれる便利なものです。

以下のファイルを新しく作ります。

.devcontainer/devcontainer.json
{
  "name": "Dev Steps",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
  "workspaceFolder": "/project",
  "workspaceMount": "source=${localWorkspaceFolder},target=/project,type=bind",
  "mounts": [
    "target=${containerWorkspaceFolder}/node_modules,source=dev-steps-volume"
  ],
  "features": {
    "ghcr.io/devcontainers-contrib/features/jest": {
      "version": "latest"
    }
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "Orta.vscode-jest",
        "shd101wyy.markdown-preview-enhanced"
      ]
    }
  },
  "onCreateCommand": "sudo chown node node_modules && yarn install"
}

ここでは、以下のことをやっています。

  • ベースイメージにTypeScript + Node.jsを指定
  • node_modulesをボリュームにする
  • Jestのインストール
    • テストフレームワーク
    • featuresのところに書ける
  • ワークスペースに/projectを指定
  • 使いたい拡張機能のインストール
    • ここではJestとMarkdown Preview Enhanced
  • yarn installを実行
    • node_modulesの権限をnodeに変更

こんな感じです。
あとはここに適宜必要なものを追加していきます。

ちなみに/projectというパスはプロジェクトルートです。
時と場合によって変わったりもします。
また、コンテナの/projectに現在開いているワークスペースのフォルダを紐づけるため、workspaceMountを設定しています。

node_modulesをボリュームにする理由

node_modulesはnpmやyarnでインストールしたパッケージ置き場なので、基本的にファイルの量が膨大になります。
しかも一度package.jsonに追記してしまえば、コマンドひとつ叩くだけでインストールができます。
そんなものをローカルPCに置いておく必要があるのか...?
私が出した答えはNOでした。

/project/node_modulesをボリュームにすると、このディレクトリの中身はコンテナとローカルで共有されなくなります。
なので、コンテナでyarn installすると、コンテナにはパッケージがインストールされますが、ローカルPCにはインストールされません。

コンテナを立てる

必要なファイルができたので、Dockerコンテナを立てていきます。
といってもコマンドを打つわけではなく、VSCode右下に出る通知からやります。

まずはウィンドウをリロードします。
これはコマンドパレットからやるといいと思います。

次に、右下に出てきた通知から「コンテナーで再度開く」をクリックします。

container.png

そしたらしばらく待ちます。
コンテナを起動しているため、結構時間がかかることもあります。

少し待ったら、node_modulesだけがあるVSCodeが立ち上がると思います。
これで準備は完了です。

正常にできているか確認する

さて、ここまでで出来ていて欲しいのは以下です。

  • Node.jsのインストール
  • TypeScriptのインストール
  • Jestのインストール
  • node_modulesのボリューム化
  • ワークスペースが/projectになっている

というわけで、まとめて確認していきます。

まずはVSCodeからターミナルを開きます。
すると、おそらくプロンプトが以下のようになっていると思います。

prompt.png

青い部分が/projectとなっていたら、ワークスペースが/projectに設定されています。

では、ここからコマンドを打って各ツールがあるか確認します。

ターミナル
node --version
v20.11.1

tsc --version
Version 5.3.3

jest --version
29.7.0

バージョンの差異はあれど、だいたいこんな感じに出力と思います。
エラーなく各コマンドが実行できれば成功です。

また、VSCodeのエクスプローラーにnode_modulesがあれば、おそらくnode_modulesのボリューム化は出来ています。
あとは拡張機能が有効されているか確認してみてください。

Gitを設定する

せっかくならGitの設定もここでやってしまいましょう。

Gitを初期化する

まずは初期化です。
律儀にgit initコマンドを打ってもいいですが、せっかくVSCodeを使っているのでVSCodeからやります。

サイドバーのソース管理アイコンを押して、「Initalize Repository」を押します。
(画像は私の前の記事の使い回しですが、実際は英語です)

init.png

ちなみに、VSCodeからGitを操作する方法を記事にしてるので、良かったら見てみてください。

.gitignoreを作る

Gitで管理しないフォルダ/ファイルを設定していきます。

私は基本的に以下を使っています。

.gitignore
.DS_Store
build
node_modules
  • .DS_Store ... Macが内部で使っているファイル、Git管理する必要はない
  • build ... TypeScriptをコンパイルしたJSファイルが入る場所
  • node_modules ... パッケージ置き場

コミットする

ついでにコミットしておいたほうがいいと思います。
設定ミスってぐちゃぐちゃになる前にやっておくと、後戻りできて楽です。

1のまとめ

  1. 拡張機能群を入れる

  2. .devcontainer/devcontainer.jsonを作る

    deccontainer.jsonのテンプレ
    .devcontainer/devcontainer.json
    {
      "name": "Dev Steps",
      "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
      "mounts": [
        {
          "type": "volume",
          "source": "dev-steps-volume",
          "target": "${containerWorkspaceFolder}/node_modules"
        }
      ],
      "features": {
        "ghcr.io/devcontainers-contrib/features/jest": {
          "version": "latest"
        }
      },
      "workspaceFolder": "/project"
    }
    
  3. コンテナを立てる

    1. VSCodeをリロードする
    2. 通知から「コンテナーで再度開く」を選択する
  4. うまく出来ているか確認する

    • ターミナルのプロンプトに/projectがあることを確認する
    • 以下コマンドを打ってみる
      • node --version
      • tsc --version
      • jest --version
    • エクスプローラーにnode_modulesがあることを確認する
    • 拡張機能が有効化されていることを確認する
  5. Gitを設定する

    • VSCodeのサイドバーから初期化する
    • .gitignoreを作る
      • node_modules
      • build
      • .DS_Store
    • コミットする

2. tsconfig.jsonを書き換える

さて、1で作ったdevcontainer.jsonには、typescriptjestをインストールする設定が書いてありました。
そのため、もうすでにこのコンテナにはTypeScript(とJest)が入っています。

ということで、さっそくtsconfig.jsonを書き換えていきます。

ファイルを生成する

まずはターミナルに以下のコマンドを入力して、tsconfig.jsonを生成します。

tsconfig.jsonを生成する
tsc --init

すると何やら長いファイルが生成されます。

tsconfig.json
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    
    // 以下略
}

設定を書き換える

そしたら、ここの設定をお好みで書き換えていきます。
私は以下のように設定することが多いです。

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022", // なんとなく
    "rootDir": "./src",  // srcにtsファイルを入れる
    "outDir": "./build",  // コンパイルしたファイルは./buildに配置
    "resolveJsonModule": true // JSONをimportできるようにする
  },
  "exclude": [
    "**/*.test.ts" // テストファイルはコンパイル対象から除外
  ]
}

ここの設定はお好みでいいと思います。

コンパイルしてみる

この設定でコンパイルできるか試してみます。
ここで動かなかったらtsconfig.jsonの設定を見直します。

まずはsrcディレクトリを作ります。
これはrootDirの設定が./srcなのに合わせています。

そして、srcディレクトリの直下にindex.tsを作ります。

src/index.ts
const text: string = 'Hello World!';
console.log(text);

ではコンパイルします。
以下のコマンドを入力してください。

ターミナル
tsc

すると、新しくbuildディレクトリが作られ、その中にコンパイルしたindex.jsファイルが入ります。
だいたい以下のような中身になるはずです。

build/index.js
"use strict";
const text = 'Hello World!';
console.log(text);

エラーが出ずに上のような結果になれば、ひとまずコンパイルは大丈夫だと思います。
あとはエラーが出た都度tsconfig.jsonを編集する感じです。

2のまとめ

  1. tsc --initでファイルを生成する

  2. 設定を書き換える

    私が使っている設定
    tsconfig.json
    {
      "compilerOptions": {
        "target": "ES2022",
        "rootDir": "./src",
        "outDir": "./build",
        "resolveJsonModule": true
      },
      "exclude": [
        "**/*.test.ts"
      ]
    }
    
  3. tscを実行してコンパイルできることを確認する

3. jest.config.jsを書き換える

次はJestの設定ファイルであるjest.config.jsを書き換えていきます。

ちなみに、この設定ファイルがない状態でjestコマンドを実行すると、以下のように怒られます。

jest
Error: Could not find a config file based on provided values:
略

ファイルを生成する

以下のコマンドを入力します。

ターミナル
yarn init -y
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.
success Saved package.json
Done in 0.04s.

jest --init
? Would you like to use Jest when running "test" script in "package.json"? y
? Would you like to use Typescript for the configuration file? 
? Choose the test environment that will be used for testing
    > node
? Do you want Jest to add coverage reports? n
? Which provider should be used to instrument code for coverage?
    > v8
? Automatically clear mock calls, instances, contexts and results before every test? y

yarn init -yを実行しているのは、package.jsonを生成するためです。
このファイルがないとjest --initを実行した時にエラーが出て失敗してしまいます。
Warningが出ていますが、とりあえず気にしないことにします。

jest --initで設定した内容は以下のとおりです。

  1. testスクリプトをpackage.jsonに追加するか? yes
    • これをやるとyarn testでテストが実行できるようになる
  2. jest.config.jsにTypeScriptを使うか? no
    • これをyにしたらts-nodeが必要って言われた記憶がある
    • JSでもJSDocコメントによって補完は出る
  3. テスト環境はどうするか? node
    • よくわからんけどnodeでいいと思う
  4. カバレッジレポートは必要か? no
    • よくわからんからnoにしてる
  5. v8とbabelのどっちを使うか? v8
    • これもよくわからん
  6. テスト前にデータを消去するか? yes
    • よくわからんが消していいと思う

多分これでも動くとは思いますが、一応自分で調べることを推奨します。

必要なものをインストールする

現状だとテストができないため、必要なパッケージを入れます。

型定義を追加する

現状だと型定義がないため、describeなどの関数を書こうとするとVSCode上でエラーが出てしまいます。
これを解消するため、@types/jestパッケージを入れます。

ターミナル
yarn add --dev @types/jest

これで補完が効いてエラーが出なくなるはずですが、一応試してみます。
というわけで、テストファイルを作ります。

testディレクトリを新しく作り、その中にsum.test.tsを作ってください。

test/sum.test.ts
describe('test', () => {
    test('てすと', () => {
        expect(1 + 1).toBe(2);
    });
});

ここの内容はなんでもいいですが、describeを書いてエラーが出なければ多分大丈夫だと思います。

importを使えるようにする

このJest、実はデフォルトのままだとimport文が使えません。

エラーが発生することを確認する

試しに、src/index.tstest/sum.test.tsを以下のようにします。

src/index.ts
// xとyの合計を返す関数
export function add(x: number, y: number) {
    return x + y;
}
test/sum.test.ts
+ import { add } from "../src/index";

  describe('test', () => {
      test('てすと', () => {
-         expect(1 + 1).toBe(2);
+         expect(add(1, 1)).toBe(2);
      });
  });

そして、以下のコマンドを実行します。

ターミナル
jest
 FAIL  test/sum.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token
    # 略

    SyntaxError: Cannot use import statement outside a module

見ての通り、「SyntaxError: Cannot use import statement outside a module」というエラーが出ました。
これはimportがモジュールではない箇所で使われているというエラーです。

インストールする

このエラーを解決するためには、ts-jestというパッケージが必要です。
これはJestが内部で使う、TypeScriptをNode.jsで実行できるJavaScriptにするためのものです。

というわけで、以下のコマンドを実行してください。

ターミナル
yarn add --dev ts-jest

warning " > ts-jest@29.1.2" has unmet peer dependency "jest@^29.0.0".
warning " > ts-jest@29.1.2" has unmet peer dependency "typescript@>=4.3 <6".

# 略

上のようなWarningが出ますが、ひとまず気にしないことにします。

また、このts-jestを使うためにはtypescriptパッケージが必要です。
これは自動でインストールされないので、手動で入れていきます。

以下のコマンドを実行してください。

ターミナル
yarn add --dev typescript

設定ファイルを書き換える

ということで、jest.config.jsを書き換えていきます。
といっても書き換えるのはpresetのみです。

以下のようにファイルを編集してください。

jest.config.js
/** @type {import('jest').Config} */
const config = {
    // 他の設定はデフォルトのままにしておく
    preset: "ts-jest"
};

これで、先ほどインストールしたts-jestパッケージを、Jestが内部で使うようになるはずです。

テストしてみる

では、いよいよテストを実行してみたいと思います。

まず、src/index.tstest/sum.test.tsが以下のようになっていることを確認してください。
多少違っても、importexpect().toBe()あたりが入っていれば大丈夫です。

src/index.ts
export function add(x: number, y: number) {
    return x + y;
}
test/sum.test.ts
import { add } from "../src/index";

describe('test', () => {
    test('てすと', () => {
        expect(add(1, 1)).toBe(2);
    });
});

確認ができたら、ターミナルに以下のコマンドを入力してください。

ターミナル
jest # yarn test でも可
 PASS  test/hello.test.ts
  test
    ✓ てすと (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.295 s
Ran all test suites.

こんな感じでエラーが出ずに実行できれば成功です。

3のまとめ

  1. yarn init -yを実行してpackage.jsonを生成する
  2. jest --initを実行してjest.config.jsを生成する
    • 「Would you like to use Jest when running "test" script in "package.json"?」はyにしたほうが便利だと思う
    • 「Would you like to use Typescript for the configuration file?」をyにする場合、ts-nodeパッケージが追加で必要だった気がする
  3. 必要なモジュールを入れる
    • @types/jest ... Jestの型定義
    • ts-jest ... TypeScriptで書いたテストを実行するために必要
    • typescript ... ts-jestのために必要
  4. 設定ファイルを書き換える
    • jest.config.jspresetts-jestにする
  5. jestコマンドでテストしてみる

参考

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?