📑

DockerでNode.js + TypeScript + WebPack環境構築

2022/06/19に公開

はじめに

今回はこちらの本のハンズオンを試すためにDockerの環境を作成しました。

https://www.amazon.co.jp/TypeScriptハンズオン-掌田津耶乃/dp/4798065331/ref=sr_1_1?crid=JMP1K71SELN7&keywords=typescriptハンズオン&qid=1655576915&sprefix=typescript%2Caps%2C188&sr=8-1

作成にあたりトラブルが起きてかなりの時間を費やしたので書籍を今後学習する人のため、webpackでホットリロードを効かせたい人のためにまとめます。

また、環境構築で発生したエラーはこちらに載せました。

https://qiita.com/Sicut_study/items/906a074e294c6b6e3637

開発環境

  • VSCode
  • Ubuntu 20.04 (WSL2)
  • Docker 20.10.12
  • docker-compose version v2.2.3
  • git version 2.25.1

環境構築

作成後は以下のディレクトリになります。

ts_docker_env
┣ docker
  ┣ nginx
    ┣ default.conf
┣ src
  ┣ html
    ┣ js
      ┣ dist
        ┣ main.js
    ┣ ts
      ┣ index.ts
    ┣ index.html
    ┣ node_modules
    ┣ package-lock.json
    ┣ package.json
    ┣ tsconfig.json
    ┣ webpack.config.js
┣ Dockerfile
┣ docker-compose.yml
┣ README.md

ts_docker_envのディレクトリにいるところから始めます。

$ mkdir ts_docker_env
$ cd ts_docker_env

NodeとNginx作成

まずDockerfileとdocker-compose.ymlを作成します。
コンテナはNode.jsのコンテナとNginxのコンテナを利用します。
Node.jsコンテナでコンパイルして、Nginxで表示する構成です。

./Dockerfile
FROM node

Dockerfileは今後パッケージ追加を考えて作成していますが、イメージだけなのでdocker-compose.ymlにimageで指定してもよいです。

./docker-compose.yml
version: '3'
services:
  app:
    image: nginx:latest
    container_name: "app"
    ports:
      - "8080:80"
    volumes:
      - ./src/html:/app
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf

  node:
    build: .
    container_name: node
    tty: true
    user: node
    working_dir: /usr/src/app
    volumes:
      - ./src:/usr/src/app

./docker/nignxのディレクトリを作成します。

$ mkdir -p ./docker/nginx

Nginxの設定ファイルを作成します。

./docker/nginx/default.conf
server {
    listen       80;
    listen       443;
    server_name  localhost;

    location / {
        root   /app;
        index  index.html index.htm;
    }

    error_page  404 /404.html;
    location = /40x.html {
        root   /usr/share/nginx/html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

TypeScriptの追加

./src/html/js/dict./src/html/js/tsのディレクトリを作成します。

$ mkdir -p ./src/html/js/dict
$ mkdir -p ./src/html/js/ts

node.jsの設定ファイルを作成します。

$ docker-compose build
$ docker-compose up

# 別ターミナルを開く
$ docker exec -it node sh
$ npm init -y

./src/package.jsonが作成されたのでscriptsを修正します。

./src/package.json
(省略)
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "clean": "rimraf html/js/dist",
    "tsc": "tsc",
    "build": "npm-run-all clean tsc"
  },
(省略)

必要なライブラリをいれていきます

$ npm install typescript @types/node --save-dev

node_modulesとpackage-lock.jsonが作成されました

次にTypescriptの設定ファイルを作成します

$ npx tsc --init

tsconfig.jsが作成されました

Webpackの追加

Webpackをインストールします

$ npm install webpack ts-loader @webpack-cli/generators

Webpackの設定ファイルを作成します

$ npx webpack-cli init

ターミナルで質問されるので答えていきます。基本的にはエンターですが、一部違うところだけ載せます。

1問目: Typescriptを選択 → Enter
2-4問目: Enter
5問目: noneを選択 → Enter
6問目: Enter
7問目: npmを選択 → Enter
8問目: Enter → y → Enter
9問目: Enter → y → Enter

tsconfig.jsを修正します

./src/tsconfig.js
{
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./html/js/dist",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": [
    "html/js/ts/**/*"
  ]
}

./src/html/js/ts/*のファイルを対象にしました。
またコンテナ上でnpx tsc TSファイル名でトランスコンパルする際に変換したjsのファイルの出力先を./html/js/distに設定しました

./src/html/js/ts.....TSファイルを入れる
./src/html/js/dist.....変換したJSファイルを入れる

という使い分けを行っています。

次にwebpack.config.jsを修正します。

./src/webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");

const isProduction = process.env.NODE_ENV == "production";

const config = {
  entry: "./html/js/ts/index.ts",
  output: {
    path: path.join(__dirname, "/html/js/dist"),
    filename: "main.js"
  },
  devServer: {
    open: true,
    host: "localhost",
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./html/index.html",
    }),

    // Add your plugins here
    // Learn more about plugins from https://webpack.js.org/configuration/plugins/
  ],
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/i,
        loader: "ts-loader",
        exclude: ["/node_modules/"],
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
        type: "asset",
      },

      // Add your rules for custom modules here
      // Learn more about loaders from https://webpack.js.org/loaders/
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".jsx", ".js", "..."],
  },
};

module.exports = () => {
  if (isProduction) {
    config.mode = "production";

    config.plugins.push(new WorkboxWebpackPlugin.GenerateSW());
  } else {
    config.mode = "development";
  }
  return config;
};

サンプルファイルの作成

実際に画面に表示するHTMLを作成します。

./src/html/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>Sample</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
  <script src="./js/dist/main.js"></script>
</head>
<body>
  <h1 class="bg-primary text-white p-2">Sample page</h1>
  <div class="container py-2">
    <p class="h5" id="target">wait...</p>
  </div>
</body>
</html>

tsファイルを作成します

./src/html/js/ts
window.addEventListener('load',(event)=> {
  let p = document.querySelector('#target')
  p!.textContent = "This is message by TypeScript."
})

./src/index.html./src/src/index.tsが不要なファイルなので削除します。webpack-cli initで自動作成してしまったものです

起動確認

$ npm run build
$ npm run serve

起動したらlocalhost:8888にアクセスします。
以下の画面が表示されれば成功です。

ホットリロードを使う場合は以下のコマンドで起動します

$ npm run watch

serveはメモリにコンパイルされるため、開発はwatchを使う必要があるそうです。

Webpack is not compiling

おわりに

たまたま本をやろうと思って購入したらまさかの1章で辛い思いをするとはと思いました。これでやっと肝心の勉強を進められるようになりました。

今回作成した環境は以下のリポジトリにあります

https://github.com/jinwatanabe/ts_docker_env

参考

GitHubで編集を提案

Discussion