webpack & node-sass & eslintでシンプルなフロント開発環境構築

## 目的
- Web Frameworkで使うことを前提として`ES6`, `Sass`をトランスパイルする。
- フロント開発に必要な人権を満たす開発環境を作る。

## 指針
- フロントエンドを`create-react-app`, `vue-cli`などのツールに頼らずに０ベースで作る。
-> 拡張性の問題(変な依存、余計な設定を盛り込みたくない。cli使っても結局Document読むから面倒臭い)
- 多人数の開発チームを想定して構築する。
- シンプルが正義。

## 経緯
`Spring boot`で個人開発していたが、やっぱりプレーンJSは書きたくなかった。
それと今まで`create-react-app`でごちゃごちゃやっていたので、1からフロント環境を設定したかった。
著者は執筆時点で実務1年ちょいのフロント知識浅めなペーペーなのでマサカリお願いします。

## 要件・設計
- `yarn build`コマンドでjsのバンドルファイルとScssのトランスパイル結果をデプロイ先のフォルダに配置する。
- `yarn dev`コマンドでJS, cssのホットリローディングに対応する。
- メディアクエリーをcssに書きたくないので、SPとPCのcssファイルを分離しておく(そうでないケースもあると思います)。

## 実装
自分は`spring boot`のstaticフォルダを資源の配置先にしているので、そうでない方は読み替えてください。
なお、`node_modules`について開発環境でしか使わないものは`yarn`なら`-D`オプション。`npm`なら`--save-dev`オプションをつけるようにしてください。

###webpack

```bash

yarn add -D webpack webpack-cli
```

```js:webpack.config.js

module.exports = {
  mode: process.env.NODE_ENV || 'development',
  // バンドル対象のファイルのエントリーポイント
  entry: ['./src/index.js'],
  output: {
    // ビルド後のファイル名
    filename: 'bundle.js',
    // ビルド後の配置先ディレクトリ
    // webアプリにビルド結果を配置
    path: __dirname + '../../src/main/resources/static/js'
  },
  module: {
    // どのファイルに対してどんなことをするか
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['@babel/preset-env']
        }
      }
    ]
  }
};
```

次に、設定したようにトランスパイルするため、以下のように`yarn add`、`npm install`してください。

```bash

yarn add -D @babel/core @babel/polyfill @babel/preset-env babel-loader
```

これで`src/index`ファイルが`webpack --config webpack.config.js`を実行すると指定先にコンパイルされたバンドルファイルが生成される。便利！
※`src/index`でimportしているファイルがバンドル対象になるので、他の設定は不要です。

### ESLint
cliツールに頼らないと言いつつもいつも初期化するときはcliでやっていたので、

```bash

yarn add -D eslint
```
して

```bash

npx eslint --init
```
npxコマンドは`npm init`された階層の`node_modules`のcliモジュールをPATHを通さなくても呼び出せる、そんなノリのコマンドっぽい。

で対話形式で設定ファイルを作成する。自分はJSONで作成して以下の通りになった。

```json:.eslintrc.json
{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:vue/essential"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "plugins": ["vue"],
  "rules": {
    "semi": "error",
    "quotes": ["error", "single"]
  }
}
```
ESLintのエディタ設定はVScodeのみ次回更新。

### node-sass
ついでにsassを使いたかったので`node-sass`のcliツールで`.scss`を`.css`にトランスパイルする。

```bash
yarn add -D node-sass
```
`webpack`でもできそうだが、このコマンドで必要なことはできるので採用。

```bash

npx node-sass [トランスパイル対象ファイル] [出力先]
```

とりあえずここまでで必要なことは全て揃っている。
そしたらあとは`npm scripts`の設定をする。
なお、`npm scripts`でスクリプトを書くことになるわけだが、このスクリプトに環境依存を持たせないために、今回追加で`npm-run-all`モジュールを追加する。
> [npm-run-allとは](https://qiita.com/KoheiShingaiHQ/items/fd5a9cf43bbc71aebc8b)

以下がここまで依存関係を追加した結果と、`npm scripts`の設定になる。

```json:package.json
{
  "name": "client",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "run-p run:js run:sass",
    "build": "run-s build:js; yarn build:sass",
    "build:js": "webpack --config webpack.config.js",
    "run:js": "webpack --progress --colors --watch --config webpack.config.js",
    "build:sass": "run-s build:sass:pc build:sass:sp",
    "build:sass:pc":"node-sass src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "build:sass:sp": "node-sass src/style/sp/index.scss ../src/main/resources/static/css/sp.css",
    "run:sass": "run-p run:sass:pc run:sass:sp",
    "run:sass:pc": "node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "run:sass:sp": "node-sass -w src/style/sp/index.scss ../src/main/resources/static/css/sp.css"
  },
  "dependencies": {},
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.6",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.0.6",
    "babel-preset-es2015": "^6.24.1",
    "eslint": "^6.8.0",
    "node-sass": "^4.13.1",
    "npm-run-all": "^4.1.5",
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11"
  }
}

```
重要なのはここ。

```json:package.json
"scripts": {
    "dev": "run-p run:js run:sass",
    "build": "run-s build:js; yarn build:sass",
    "build:js": "webpack --config webpack.config.js",
    "run:js": "webpack --progress --colors --watch --config webpack.config.js",
    "build:sass": "run-s build:sass:pc build:sass:sp",
    "build:sass:pc":"node-sass src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "build:sass:sp": "node-sass src/style/sp/index.scss ../src/main/resources/static/css/sp.css",
    "run:sass": "run-p run:sass:pc run:sass:sp",
    "run:sass:pc": "node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "run:sass:sp": "node-sass -w src/style/sp/index.scss ../src/main/resources/static/css/sp.css"
  }
```

コマンドの内容は雰囲気でわかると思うので、軽く説明。

- JSのホットリローディング

```bash

# ファイル更新のたびにビルドが走り、コンパイルの進捗も表示 + 色付け
webpack --progress --colors --watch
```
- Scssのホットリローディング

```bash

# src/style/pc/index.scssをファイル更新のたびに
# ../src/main/resources/static/css/pc.cssにトランスコンパイル
# 
node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css
```
- npm-run-allのコマンド

```bash
# 1と2を並列で実行
run-p [npm scripts1] [npm scripts2]
```
なお、ビルド時には失敗したならすぐに処理落ちして欲しいのでシーケンシャルに実行する。
ここはケースバイケースでいいと思う。

```bash
# 1と2を直列で実行
run-s [npm scripts1] [npm scripts2]
```

## Example
自分の場合は最終的にこうなった。

```:directory
|--.eslintrc.json
|--package-lock.json
|--package.json
|--src
|  |--index.js
|  |--style
|  |  |--_define.scss
|  |  |--pc
|  |  |  |--index.scss
|  |  |--sp
|  |  |  |--index.scss
|--webpack.config.js
|--yarn.lock
```

```:directory
--resources
|  |  |  |--application.properties
|  |  |  |--static
|  |  |  |  |--css
|  |  |  |  |  |--normalize.css
|  |  |  |  |  |--pc.css
|  |  |  |  |  |--sp.css
|  |  |  |  |--js
|  |  |  |  |  |--bundle.js
|  |  |  |  |--test.html

```
ビルドを実行すると狙った通りの箇所に配置できていることがわかる。

```html:test.html
<!-- for check js and css files without application server -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link href="css/normalize.css" rel="stylesheet"></link>
    <link rel="stylesheet" type="text/css" media="only screen and (min-width: 0px) and (max-width: 480px)" href="css/sp.css">
    <link rel="stylesheet" type="text/css" media="only screen and (min-width: 481px)" href="css/pc.css">
    <title>Document</title>
</head>
<body>
    <h2>title</h2>
    <div id="header">header</div>
    <div class="body">body</div>
    <script src="js/bundle.js"></script>
</body>
</html>
```

## まとめ
やりたいことが他にあったので端折りまくってしまいましたが、所詮備忘録程度に見ていただければ幸いです。