JavaScript トランスパイルツール構築【Webpack5, ESLint7, Babel7】

[1]:https://nodejs.org/ja/
[2]:https://webpack.js.org/
[3]:https://eslint.org/
[4]:https://ics.media/entry/16028/#webpack-babel-jqueryno
[5]:https://babeljs.io/
[6]:https://github.com/isaacs/node-glob
[7]:https://maku.blog/p/j6iu7it/
[8]:https://qiita.com/ginokinh/items/d4e1a9134c215d0ea701
[9]:https://ics.media/entry/12226/
[10]:https://www.npmjs.com/package/webpack-cli
[11]:https://www.npmjs.com/
[12]:https://kangax.github.io/compat-table/es5/

## 目標

Webpack、ESLint、Babelのトランスパイルツールを作成し、
ES2021で書いたJSをESLintで構文チェックをかける。
構文チェックで問題がなければ、BabelでES5にJSファイルを変換し、１つのJSファイルを出力する。

構文チェックで問題があれば、ファイルを出力せず、エラーを出力する。

ES5に変換する理由
・下記ブラウザ上でJSが動作する様にしたいので。
・対象ブラウザ
　・IE11
　・Chrome 最新版
　・Edge 最新版
　・FireFox 最新版
　・Safari 最新版

✳︎ECMAScriptのブラウザ対応状況については[こちら][12]をどうぞ

## GitHub

設定が面倒っていう人は、GitHubに１つリポジトリ作ったのでこちらからお試しください。
もし、うまく動かないなどありましたら、こちらのコメント欄か、GitHubのIssueにお願いします。

https://github.com/wadakatu/js-transpile-tool

## 動作環境

- MacOS Catalina 
    - version 10.15.7
- [Node.js][1] 
    - version 16.5.0
- npm
    - version 7.19.1
- [Webpack][2]
    - version 5.45.1
- [Webpack-cli][10]
    - version 4.7.2
- [ESLint][3]
    - version 7.31.0  
- [Babel-loader][5]
    - version 8.2.2
- @babel/core
    - version 7.14.8
- @babel/preset-env
    - version 7.14.8
- [glob][6]
    - version 7.1.7  

## 前提条件

- homebrewがインストール済
- 複数のJSファイルを１つにまとめる
- ES2021で書いたJSをES5に変換する

## 初期設定

### nodebrewインストール

```
brew install nodebrew
```

### 環境変数設定

今回は、.zshrcに記載しています。
.bashrcでもそんなに変わらないと思います。

```php
//.zshrcを開く
vim .zshrc

//環境設定を.zshrc内に記載
export PATH=$PATH:<homepath>/.nodebrew/current/bin

//.zshrc再読み込み
source ~/.zshrc
```

### [node][1]インストール

```java
nodebrew install-binary latest
```

上記コマンドでは、最新のnodeをダウンロードします。
特定バージョンのnodeをダウンロードしたい場合は、下記の様にしてください。

```java
//バージョン一覧確認
nodebrew ls-remote

//バージョン指定してダウンロード
nodebrew install-binary <version>
```

### nodeバージョン指定 && node,npmバージョン確認

```java:
//インストールしたバージョン確認
nodebrew ls

//使用したバージョンを指定
nodebrew use <version>

//nodeとnpmのバージョン確認
node -v // v16.5.0 などuseしたバージョンが表示される。
npm -v // 7.19.1など表示される。
```


### メインディレクトリ作成

```
mkdir js-build-tool
```     

### entry、output用ディレクトリ作成

entryはトランスパイル前のjsファイルを格納する場所
outputはトランスパイル後のjsファイルを格納する場所

```java
cd js-build-tool

//src = entry, dist = output
mkdir src dist
```

### npmプロジェクト作成

```
npm init -y
```

上記コマンド入力後、package.jsonが作成されていればOKです。

### [Webpack][2]インストール

```
npm install --save-dev webpack webpack-cli
```

上記コマンド入力後、package.jsonのdevDependenciesに追加されていればOKです。

```
"devDependencies": {
    "webpack": "^5.45.1",
    "webpack-cli": "^4.7.2"
},
```

### [glob][6]インストール

globは指定ディレクトリ直下の全てのJSファイルを変換対象にするために導入します。

```
npm install glob
```
上記コマンド入力後、package.jsonのdependenciesに追加されていればOKです。

```
"dependencies": {
    "glob": "^7.1.7"
}
```

### [ESLint][3]インストール

```
npm install --save-dev eslint
```

上記コマンド入力後、package.jsonのdevDependenciesに追加されていればOKです。

```
"devDependencies": {
    "eslint": "^7.31.0",
    "webpack": "^5.45.1",
    "webpack-cli": "^4.7.2"
},
```

### .eslintrc.json作成

ESLintのルールを設定できるファイルを作成
jsonの他にも、.js, .ymlなどで作成可能

ここでは、設定に対する詳細は省きます。
[ESLintの設定ファイルの各プロパティの意味を理解する][7]

```javascript:.eslintrc.json
{
    "root": true,
    "extends": "eslint:recommended",
    "rules": {
        //セミコロン強制
        "semi": [
            "error",
            "always"
        ],
        //セミコロン２つ続き禁止
        "no-extra-semi": "error",
        //return, throw, continue, break後のコード禁止
        "no-unreachable": "error",
        //宣言されていない変数禁止
        "no-undef": "error",
        //console系禁止
        "no-console": "error"
    },
    "env": {
        "browser": true,
        "es2021": true,
        //jquery使用していなければ、不要
        "jquery": true
    },
    "parserOptions": {
        "sourceType": "module",
        "ecmaVersion": 2021
    }
}
```

### [Babel][5]インストール

```
npm install --save-dev babel-loader @babel/core @babel/preset-env
```

上記コマンド入力後、package.jsonのdevDependenciesに追加されていればOKです。

```
"devDependencies": {
    "@babel/core": "^7.14.8",
    "@babel/preset-env": "^7.14.8",
    "babel-loader": "^8.2.2",
    "eslint": "^7.31.0",
    "webpack": "^5.45.1",
    "webpack-cli": "^4.7.2"
},
```

### webpack.config.js作成

こちらも、.eslintrc.json同様、詳細は省きます。
この[投稿][8]に詳しく書かれています。

```javascript:webpack.config.js
import path from 'path';
import webpack from 'webpack';
import glob from 'glob';

//src直下のjsファイルを全て取得している。
const entries = glob.sync("./src/**/*.js");

export default function(env) {

    //npm run の引数にファイル名を渡す
    const filename = env.filename;
    const __dirname = path.resolve()
 
    return {
        mode: 'production',
        entry: entries,
        output: {
　　　　　　　//先ほど作成したdistフォルダに変換後のJSファイルを作成する様にしている
            path: `${__dirname}/dist`,
            filename: filename + '.js',
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: [
                        {
                            loader: "babel-loader",
                            options: {
                                presets: [
                                    "@babel/preset-env",
                                ]
                            }
                        }
                    ]
                },
            ],
        },
        target: ["web", "es5"],
        plugins: [
            new webpack.ProgressPlugin(),
        ],
    }
}
```

### package.jsonのscripts用意

package.jsonのscriptsは、複数コマンドをまとめることができます。
[こちらのサイト][9]が参考になります。

今回の流れは、ESLintで構文チェックをかけて、エラーがあればそこで処理を止める。
エラーがなければ、BabelでES5にJSファイルを変換し、１つのJSファイルにまとめる。

```php4
//package.jsonを開く
vim package.json

//コマンドを追加する
"scripts": {
    "build": "eslint ./src/**/*.js && webpack",
},
```

上記の様に記載することで、`npm run build -- --env filename=hogehoge`でESLint構文チェックとBabelの変換処理が走ります。

引数の`filename`には、トランスパイル後のJSファイル名を入力してください。

以上で設定は完了です。
ここからは、実際にこの環境を使って、複数JSファイルを１つにまとめてみましょう。

### JSファイル準備

今回は、簡単なJSファイルを２つ用意します。
配列をconsoleに出力するだけです。

- dog.js
- app.js

```javascript:dog.js
export const dogs = ['pochi', 'wan', 'dog']
```

```javascript:app.js
import {dogs} from './dog.js';
console.log(dogs);
```

app.jsを動かすと、下記の様に出力されるはずです。

```php
node app.js

//出力結果
[pachi, wan, dog]
```

この２つのファイルを先ほど作成した、srcフォルダの中に入れましょう。

### npm run buildしてみる(ESLintエラー発生）

```
npm run build -- --env filename=bundle
```

ここで、下記の様にESLintによるエラーが発生します。

```terminal
$ npm run build -- --env filename=bundle

> js-build-tool@1.0.0 build
> eslint ./src/**/*.js && webpack “--env” “filename=bundle”

/Users/hogehoge/Documents/js-build-tool/src/app.js
 2:1 error Unexpected console statement no-console

/Users/hogehoge/Documents/js-build-tool/src/dog.js
 1:43 error Missing semicolon semi

×: 2 problems (2 errors, 0 warnings)
 1 error and 0 warnings potentially fixable with the `--fix` option.
```

まず、app.jsでは、console.logを使用しているのでエラー
そして、dog.jsでは、最後のセミコロンが抜けているのでエラーになっています。

この段階で、distフォルダ内を確認しても、何も出力されていないはずです。

### エラー修正

app.jsに関しては、console.logで出力は見たいので、`.eslintrc.json`を修正しましょう。
`rules`内に記載して、`no-console`をコメントアウトしてみましょう。

```javascript:.eslintrc.json.rules
"rules": {
        //セミコロン強制
        "semi": [
            "error",
            "always"
        ],
        //セミコロン２つ続き禁止
        "no-extra-semi": "error",
        //return, throw, continue, break後のコード禁止
        "no-unreachable": "error",
        //宣言されていない変数禁止
        "no-undef": "error"
        //console系禁止
        //"no-console": "error"
    },
```

dog.jsに関しては、最後のセミコロンをつけましょう。

```javascript:dog.js
export const dogs = ['pochi', 'wan', 'dog'];
```

### 再度npm run buildしてみる

```terminal
$ npm run build -- --env filename=bundle

> js-build-tool@1.0.0 build
> eslint ./src/**/*.js && webpack “--env” “filename=bundle”

asset bundle.js 479 bytes [emitted] [minimized] (name: main)
runtime modules 416 bytes 2 modules
cacheable modules 209 bytes
 ./src/app.js 69 bytes [built] [code generated]
 ./src/dog.js 140 bytes [built] [code generated]
webpack 5.45.1 compiled successfully in 1328 ms
```

特にエラーなく、トランスパイルに成功します。

distフォルダの中を見てみると、`bundle.js`が作成されているはずです。


### 変換後のJSファイルを動かす

```php
node dist/bundle.js

//出力結果を確認する
['pochi', 'wan', 'dog']
```

変換前ファイルと同じ結果が得られればOKです。

## Qiita Engineer Festa 2021

JavaScriptっていろいろな書き方ができるので、
書き方の統一が難しい言語だと思います。
書き方によっては、Chromeでは動作するけど、IEだと動作しなかったなんてことも。。

そういう問題をこの環境で無くして、皆様の開発環境を豊かにできればと思います。

https://qiita.com/official-events/89fd4ad3c24d7882117d

## 参考URL
[Node.js][1]
[Webpack][2]
[ESLint][3]
[最新版で学ぶwebpack 5入門 Babel 7でES2020環境の構築][4]
[glob Github][6]
[ESLintの設定ファイルの各プロパティの意味を理解する][7]
[webpack.config.js の書き方をしっかり理解しよう][8]
[Node.jsユーザーなら押さえておきたい npm-scriptsのタスク実行方法まとめ][9]
[ECMAScript Compatibility Table][12]