WebPack dev Serverを使用したTypeScript+jQueryの開発環境構築

今回はタイトル通りWebpack-dev-serverを使用したTypeScript+jQueryを実行可能にする開発環境構築についての記事を書いていきたいと思います。

##使用するライブラリ、モジュール
・yarn 1.16.0
・typeScript 3.6.4
・ts-loader 6.2.1
・jquery 3.4.1
・WebPack 4.41.2
・WebPack-cli 3.3.9
・WebPack-dev-server 3.9.0
・@types/jquery 3.3.31

##各種モジュールのインストール
開発環境でのみの使用となるのでdevdependenciesとしてインストールしていきます。

```
 yarn add --dev webpack webpack-cli webpack-dev-server typeScript ts-loader jquery @types/jquery
```

##ファイル構成
```
typescript-webpack/ //root directory
  ├ dist/  //Webpackの成果物がdistディレクトリ以下に出力されるように今回は設定しています。
    └ html
      └ index.html
    └ bundle.js
  ├ node_modules/
  ├ src/
    └ main.ts
    └ title.ts
  ├ package.json
  ├ tsconfig.json
  ├ webpack.config.json
  ├ yarn.lock
```

##webpack.config.jsでWebpackの設定
webpack.config.jsというのは、モジュール管理を行っているjavascriptのソースコードを解析して1つのファイルにバンドルしてくれるバンドラーモジュールであるWebpackの設定ファイルです。
最近のjavascriptでは、各種クラスコンポーネントや、関数をモジュール単位（ファイル単位）で管理することで、保守性や可読性を維持しています。モジュール単位で管理することで、あるファイルとあるファイルとの間で依存関係が生じます。
こういった依存関係を明示的に示すimport文やexport文は、javascriptが実行されるブラウザでは用意されていないので、import文やexport文を解析して1つのファイルにバンドルしてくれるwebpackが必要というわけです。

各種モジュールをインストール後、設定ファイルを作成します。

```
  touch webpack.config.js
```

下記がwebpackの設定ファイルである、webpack.config.jsになります。

```js
const path = require('path');
const outputPath = path.resolve(__dirname, './dist');
const htmlContentPath = path.resolve(__dirname, './dist/html');

module.exports = {
    mode: 'development',
    entry: './src/main.ts',
    output: {
        filename: 'bundle.js',
        path:  outputPath
    },
    module: {
        rules:[
            {
                use: 'ts-loader',
                exclude: /node_modules/,
            }
        ]
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    devServer: {
        contentBase: htmlContentPath
    }
};
```
##mode
modeオプションは、development環境やproduction環境の選択ができるオプションになります。

<img width="830" alt="Screen Shot 2019-10-28 at 9.54.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308967/3554c035-fed6-5a37-d448-4e92a14a38b8.png">

開発環境と本番環境の違いはアセットファイルの圧縮が行われるかどうかやビルド時間、パフォーマンスに違いがあります。
modeオプションをproductionに変更して行われるファイル圧縮で出力されるファイルは可読性が皆無ですが、その分ブラウザのみが読み取れるように記述がソースコードよりもより短く出力されているのでパフォーマンスも良いです。ですがその分ビルド時間がdev環境より長くなります。

##entry
entryオプションでは、webpack.config.jsファイルの位置から、どのディレクトリ以下のファイルをビルドするのかを相対パスとして明示的に示してあげるオプションです。

##output
outputオプションでは、webpackでモジュールバンドルしたファイルのファイル名を設定するfilenameオプションや、そのファイルをどこに出力するのかを絶対パスで明示するpathオプションを設定することができます。pathオプションや絶対パスでの指定になるのでNode.js APIであるpathを使用して、絶対パスを取得する必要があります。今回その絶対パスとその絶対パスからどのディレクトリに出力するのかを指定するため、outputpath変数に格納してそのままpathオプションで明示しています。

```js
const path = require('path');
outputPath = path.resolve(__dirname, './dist')
console.log(outputpath); // /Users/~/typescript-webpack/dist
```

##module
moduleオプションでは、どういったモジュールを使用するのかを明示するrulesオプションを指定することができます。今回使用するのはts-loaderというwebpackがTypeScriptを識別して読み取ることができるようにするモジュールを使用するため、ts-loaderをuseオプションに指定します。この他にcss-loaderやstyle-loaderなどのcssファイルで記述されたスタイルをjavascriptのオブジェクトとして扱うことで、javascriptでcssの値を操作を可能にするモジュール等を指定して使用することもできます。今回は、typescript+jQueryを実行することができる開発環境の構築をメインとした記事となりますので、割愛します。またexcludeオプションでは、node_modules以下にインストールされているts-loaderを使用しているためexcludeオプションでnode_modulesを指定しています。

##resolve
resolveオプションでは、webpackがどのファイルを読み取れるようにするのかをファイルの拡張子として指定することができるextensionオプションを使うことができます。他にもオプションがあるとは思うのですが、こちらも割愛します。

##devserver
devserverオプションは、先程インストールしたwebpack-dev-serverの設定オプションです。
今回は、contentBaseオプションのみを指定しています。contentBaseオプションでは、webpack-dev-serverのコンテントディレクトリを絶対パスで指定することができます。こちらも絶対パスでの指定になるので、outputオプションと同じくNode.js APIのpathを使用しています。

```js
const path = require('path');
const htmlContentPath = path.resolve(__dirname, './dist/html');
console.log(htmlContentPath); ///Users/~/typescript-webpack/dist/html
```

以上がwebpack.config.jsの説明となりますが、webpackは非常に細かく設定することでバンドルする際のチューニングを行うことができるところがwebpackの良さだとよく耳にしますのでこれからもwebpackについて学んでいきたいと考えています。下記が公式サイトのwebpack.config.jsの説明が行われているURLになってますので気になる方はぜひ見てみてください。
https://webpack.js.org/configuration/

## package.json
```json
{
  "name": "typescript-webpack",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "webpack-dev-server --open",
    "build": "webpack"
  },
  "dependencies": {
    "@types/jquery": "^3.3.31",
    "jquery": "^3.4.1",
    "lodash": "^4.17.15",
    "ts-loader": "^6.2.1",
    "typescript": "^3.6.4"
  },
  "devDependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9",
    "webpack-dev-server": "^3.9.0"
  }
}
```
今回scriptsオプションでは、yarn devコマンドでwebpack-dev-server --open、yarn buildコマンドでwebpackが実行されるようにしています。
webpack-dev-serverコマンドでwebpack-dev-serverが起動し、webpack.config.jsのcontentBaseオプションに基づいて開発サーバーが起動されるようになっています。
openオプションを使用することでデフォルトブラウザが起動するようになっています。

##tsconfig.json
次にtypescriptのコンパイラの設定ファイルを生成します。

```
touch tsconfig.json
```

```json
{
    "compilerOptions": {
      "target": "ES5",
      "module": "commonjs",
      "strict": true,
      "noImplicitAny": true,
      "strictNullChecks": true,
      "noUnusedLocals": false, 
      "noUnusedParameters": true,
      "noImplicitReturns": true,
      "moduleResolution": "node",
      "esModuleInterop": true
    }
  }

```
今回はトランスパイルモジュールのBabelを使用せず、TypeScriptのコンパイルにより生成されるjavascriptファイルをバンドルするようにしているためtargetはES5です。また生成されるjavascriptのモジュール管理はcommonjsで行っています。requirejs,umd,amdだったり様々なオプションが使用できるようになっていますがこちらの違いを知りたい方は下記のURLが参考になったので読んでみると良いかと思われます。

tsconfigのmoduleの設定値の違い: commonjs, amd, system, umd, es6, es2015, esnext, none
https://qiita.com/suin/items/3a73eb73eea51cfca9e1

tsconfig.jsonの日本語訳
https://qiita.com/alfas/items/539ade65926deb530e0e

typescript公式
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html


##jQuery
TypeScriptで外部ライブラリを使用する場合必ず型定義ファイルが必要となります。型定義ファイルは、自作することもできるのですが、多くの外部ライブラリの型定義ファイルが下記のリポジトリで公開されています。
https://github.com/DefinitelyTyped/DefinitelyTyped

すでに一番最初にyarnを使用してインストールした@types/jqueryも上記のリポジトリで公開されている型定義ファイルの1つです。
つまりこの型定義ファイルをインストールした時点ですでにTypeScirptでjQueryを記述できるようになっています。

##実際に動かしてみる
上記のファイル構成を参照してルートディレクトリで下記のコマンドを実行してください。

```
touch ./dist/html/index.html
```

```
touch ./src/main.ts ./src/title.ts
```

dist/index.html

```html
<!DOCTYPE html>
  <html lang="ja">
      <head>
          <meta charset="utf-8">
          <title>webpack-typescript practice</title>
      </head>
      <body>
          <h2 id="title">Hello Webpack</h2>
          <script src="bundle.js"></script>
      </body>
  </html>
```

src/main.ts

```ts
export class Title {
    constructor(public el: JQuery<HTMLElement>){
        this.el = el;
    }
    public fadeOut(){
        this.el.fadeOut();
    }
}
```

src/title.ts

```ts
import $ from 'jquery';
import { Title } from './title';
const el = new Title($('#title'));
el.fadeOut();
```

今回は、コンストラクタの引数にjQueryオブジェクトを定義して、インスタンス生成後にそのインスタンスメソッドを実行した簡単なものです。
最後に下記のコマンドを実行すると動きます。

```
yarn dev
```

お疲れさまでした。久しぶりのQiitaでの投稿で緊張していますが誰かの役に立っていれば幸いです。
また間違った記述等ありましたら、指摘をよろしくおねがいします。