Web制作向けwebpack


## Web制作向けwebpack
JavaScriptのBundle、TypeScriptのBundleとTranspile、CSSやSCSSのBundleとベンダープレフィックスの追加、画像の圧縮、Pugのコンパイル、JavaScriptとCSSのLint、ソースマップの出力などを行います。


## ファイル構成

```
 website
 ├ src
 │　├ pug
 │　│ ├ index.pug
 │　│ ├ sub.pug
 │　│ ├ _include
 │　│   └ _included.pug
 │　│
 │　├ img
 │　│ ├ img.jpg
 │　│ ├ img.png
 │　│ ├ img.svg
 │　│ └ img.gif
 │　│ 
 │　├ css
 │　│ ├ style.css
 │　│ └ sub.css
 │　│ 
 │　├ scss
 │　│ ├ style.scss
 │　│ └ sub.scss
 │　│
 │　├ ts
 │　│ └ sub.ts
 │　│
 │　├ js
 │　│ └ sub.js
 │　│
 │　├ index.js
 │　└ index.ts
 │
 ├ package.json
 ├ package-lock.json
 │
 ├.gitignore
 │
 ├ tsconfig.json
 │
 ├ .babelrc
 │
 ├ .browserslistrc
 │
 ├ postcss.config.js
 │
 ├ .eslintrc.json 
 ├ .eslintignore
 │
 ├ .prettierrc.json
 ├ .prettierignore
 │
 ├ .stylelintrc.json
 └ .stylelintignore
```





## webpack使用の準備

### package.jsonの作成

```:terminal
npm init -y
```




### webpackのインストール

```:terminal
npm i -D webpack webpack-cli
```


|パッケージ|内容|
|---|---|
|webpack|webpack本体|
|webpack-cli|webpackのコマンドラインツール|


### package.jsonの編集
package.jsonにビルドコマンドを追記

```:packege.json
"scripts": {
  "dev": "webpack --mode development",
  "build": "webpack --mode production",
  "watch": "webpack  --mode development --watch"
}
```




## JavaScriptファイルのバンドル


### webpack.config.jsの作成

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

```:webpack.config.js
const path = require("path");

module.exports = {
  mode: "production",

  entry: "./src/index.js",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  }
};
``` 




### バンドルするJavaScriptファイルの準備

#### index.jsの作成
webpackを使うにはエントリーポイントになるjsファイルが必要なので最初にindex.jsを作成します。

```:src/index.js
import { hello } from "./js/sub";

console.log("JavaScript 変換成功");

hello();
``` 

#### sub.jsの作成
バンドルできているか確認用に、importするjsファイルも作成します。

```:src/js/sub.js 
export function hello() {
   console.log("JavaScript import成功");
}
```

#### JavaScrptを読み込むHTML(動作確認用)の準備
バンドルして生成したmain.jsを読み込む動作確認用のHTMLを作成します。

```:src/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>トップページ</title>
 
     <!-- defer対応ブラウザの時はこちらを使う-->
     <!-- <script src="../dist/js/main.js" defer></script> -->
 
  </head>
  <body>
 
    <!-- defer非対応ブラウザの時はこちらを使う-->
    <script src="../dist/js/main.js"></script>
  </body>
</html>
``` 
 


### ビルド

```:terminal
npm run build
```











## JavaScriptのTranspile
旧ブラウザに対応する必要がある場合、Babelを使ってJavaScriptのバージョンを下げます。
[IE11対応](https://qiita.com/takeshisakuma/items/9d9e921059b76676d2bc)

### Babelのインストール

```:terminal
npm i -D babel-loader @babel/core @babel/preset-env
```

|パッケージ|内容|
|---|---|
|babel-loader|webpackでBabelを使えるようにする|
|@babel/core|Babel本体|
|@babel/preset-env|指定したブラウザ環境で動作するように変換するプラグイン|

### Babelの設定ファイルを作成

```:.babelrc
{
  "presets": ["@babel/preset-env"]
}
``` 



### .browserslistrcの準備
Transpileターゲットのブラウザを指定します。

```:.browserslistrc
defaults	
```

[.browserslistrcで対象ブラウザを指定する](https://qiita.com/takeshisakuma/items/0bc1c39ee976bd1f52d7)

### webpack.config.jsの変更
jsファイルをbabelに渡します。

```:webpack.config.js
const path = require("path");

module.exports = {
  mode: "production",

  entry: "./src/index.js"
    
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },
  
  module: {
    rules: [
    
      //追加
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      }
      
    ]
  }
};
``` 

### Transpileの動作確認
今回はPolyfillの追加は扱いませんので、[with IE(JavaScriptのIE11対応)](https://qiita.com/takeshisakuma/items/9d9e921059b76676d2bc)を参照してください。

### index.jsの変更
アロー関数でをTranspileして、IE11で動作確認してみます。

```:index.js
import { hello } from "./js/sub";

console.log("JavaScript 変換成功");

hello();

//追加
window.addEventListener("load", () => {
  console.log("IE11で表示されたらJavaScript Transpile成功");
});
```

### ビルド

```:terminal
npm run build
```

### IE11で動作確認
IE11でsrc/index.htmlを開き、「Transpile成功」が表示されればTranspileが出来ていることが確認できました。















## TypeScriptファイルのコンパイル、バンドル
今回はPolyfillの追加は扱いませんので、[with IE(JavaScriptのIE11対応)](https://qiita.com/takeshisakuma/items/9d9e921059b76676d2bc)を参照してください。


### TypeScriptのインストール

```:terminal
npm i -D typescript ts-loader
```


|パッケージ|内容|
|---|---|
|typescript|TypeScript本体|
|ts-loader|BabelでTypeScriptを読み込めるようにする|


### webpack.config.jsの変更
tsファイルをts-loaderで読み込む設定を追加します。

```:webpack.config.js
const path = require("path");

module.exports = {

  mode: "production",

  //エントリーポイントをsrc/index.tsに変更
  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },

  module: {
    rules: [
    
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
      
      //追加
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },
      
    ],
  },
  
  //追加　import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"],
  },
  
};
``` 

### tsconfig.jsonの作成
プロジェクトルートにTyppeScriptの設定ファイルのtsconfig.jsonを作成します。

```:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",//生成するJSモジュールの形式
    "target": "es5",//生成するJSのバージョン
    "noImplicitAny": false,//trueの場合、暗黙的なany型を許容しない
  },
  "exclude": ["node_modules"] //node_modulesフォルダは除外
}
```

設定はこちらを参照してください。
https://www.typescriptlang.org/ja/tsconfig

### TypeScriptファイルの作成
#### index.tsの作成

```:src/index.ts
import { hello } from "./ts/sub";

console.log("TypeScript 変換成功");

hello();

window.addEventListener("load", () => {
  console.log("IE11で表示されたらTypeScript Transpile成功");
});
```

#### sub.tsの作成

```:src/ts/sub.ts
export function hello() {
  console.log("TypeScript import成功");
}
``` 


### ビルド

```:terminal
npm run build
```















## CSSファイルのバンドル

```:terminal
npm i -D css-loader style-loader mini-css-extract-plugin
```

|パッケージ|内容|
|---|---|
|css-loader|cssを読み込む|
|style-loader|スタイルを```<style>```要素としてページに反映させる|
|mini-css-extract-plugin|CSSをJSにバンドルせず、別ファイルにわける|

### 変換するCSSファイルの準備
#### index.tsの変更
index.tsからcssを読み込みます。

```:src/index.ts
import { hello } from "./ts/sub";
import "./css/style.css";

console.log("TypeScript 変換成功");

hello();

window.addEventListener("load", () => {
  console.log("IE11で表示されたらTypeScript Transpile成功");
});
``` 

#### style.cssの作成

```:/src/css/style.css
@import url("sub.css");

.example {
  display: flex;
  color: black;
  width: 100%;
  background-color: red;
}
``` 

#### sub.cssの作成
importしたcssがバンドルされるか確認用のcssを作ります。

```:/src/css/sub.css
body {
  background-color: green;
}
``` 

### webpack.config.jsの変更
背景画像などをurlで指定する場合に、エラーにならないように url: falseを指定します。

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },

      //追加
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },
        ],
      },
    ],
  },
　//追加
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"],
  },
};
``` 


#### src/index.htmlの変更
バンドルしたdist/style.cssを読み込みます。

```:src/index.html
<!DOCTYPE html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>トップページ</title>

      <!-- 追加 CSSファイルの読み込み -->
      <link rel="stylesheet" href="../dist/css/style.css">


      <!-- defer対応ブラウザの時はこちらを使う-->
      <!-- <script src="../dist/js/main.js" defer></script> -->

    </head>
    <body>

    <!-- defer非対応ブラウザの時はこちらを使う-->
    <script src="../dist/js/main.js"></script>
  </body>
</html>
``` 


### ビルド

```:terminal
npm run build
```






## SASSファイルのコンパイル、バンドル

### パッケージのインストール

```:terminal
npm i -D sass-loader node-sass
```


|パッケージ|内容|
|---|---|
|sass-loader|webpackでSASSを読み込めるようにする|
|node-sass|SASSを使えるようにする|

### index.tsの変更
index.jsからscssを読み込むように変更

```:src/js/index.js
import { hello } from "./ts/sub";
 
//CSSからSCSSに変更
//import "./css/style.css";
import "./scss/style.scss";
 
console.log("TypeScript 変換成功");
 
hello();
 
window.addEventListener("load", () => {
  console.log("IE11で表示されたらTypeScript Transpile成功");
});
```

### 変換するscssの準備

```:/src/scss/style.scss
@import "./sub.scss";

.example {
  display: flex;
  color: black;
  width: 100%;
  background-color: red;
}
``` 

importしたSCSSがバンドルできるか確認用のSCSS

```:/src/scss/style.scss
body {
  background-color: green;
}
``` 





### webpack.config.jsの変更

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
module.exports = {
  mode: "production",
 
  //entry: "./src/index.js",
  entry: "./src/index.ts",
 
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },
 
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
 
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },

          "postcss-loader",
        ],
      },
 
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },
 
          "postcss-loader",
 
          "sass-loader",
        ],
      },
    ],
  },
 
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"],
  },
};
```

### ビルド

```:terminal
npm run build
```















## PostCSSの使用

### パッケージのインストール

```:terminal
npm i -D postcss postcss-loader
```


|パッケージ|内容|
|---|---|
|postcss|PostCSS本体|
|postcss-loader|webpackでPostCSSを使えるようにする|

### webpack.config.jsの変更

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },
          //追加
          "postcss-loader",
        ],
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },
          //追加
          "postcss-loader",

          "sass-loader",
        ],
      },
    ],
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"],
  },
};
``` 






### CSSのプロパティにベンダープレフィックスを付ける

####  パッケージのインストール

```:terminal
npm i -D autoprefixer
```

#### postcss.config.jsファイルの作成
post-cssの設定ファイル内でautoprefixerを指定します。
AutoprefixerでIE 11対応を行うために、grid: trueを指定してみます。

```:postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer")({ grid: true })
  ]
};
```

#### 対象ブラウザの指定
作成済みであれば不要です。
Babelの項目で.browswerslistrcを作成していないときは、新規に作成します。

```:.browswerslistrc
ie 11
```


### CSSのminify
devでビルドしたときもminifyします。
不要無い場合は不要です。

#### パッケージのインストール

```:terminal
npm i -D cssnano 
```

#### postcss.config.jsファイルの変更
post-cssの設定ファイル内でcssnanoを追加します。

```:postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer")({ grid: true }), 
    require("cssnano")]
};
``` 




### プロパティのソート
Stylelintでソートする場合は不要です。

#### パッケージのインストール

```:terminal
npm i -D postcss-sorting
```

#### postcss.config.jsファイルの変更
post-cssの設定ファイル内でpostcss-sortingを追加

```:postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer")({ grid: true }),
    require("cssnano"),
    require("postcss-sorting")({
      "properties-order": ["margin", "padding", "border", "background"]
    })
  ]
};
```









## 画像を圧縮する

### パッケージのインストール

```:terminal
npm i -D copy-webpack-plugin imagemin-webpack-plugin imagemin-pngquant imagemin-mozjpeg imagemin-gifsicle imagemin-svgo
```


|パッケージ|内容|
|---|---|
|copy-webpack-plugin|ファイルのコピー|
|imagemin-webpack-plugin|imageminプラグイン本体|
|imagemin-pngquant|pngを圧縮|
|imagemin-mozjpeg|jpgを圧縮|
|imagemin-gifsicle|gifを圧縮|
|imagemin-svgo|svgを圧縮|

### webpack.config.jsの変更

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },

          "postcss-loader",
        ],
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },

          "postcss-loader",

          "sass-loader",
        ],
      },
    ],
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" },
      ],
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80",
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256,
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true,
        }),
      ],
    }),
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
     extensions: [".ts", ".js", ".json"],
  },
};
``` 




### ビルド

```:terminal
npm run build
```

src/img内の画像ファイルと、src/favicon.pngとsrc/favicon.svgがdist配下にコピー、圧縮されます。















## Pugのコンパイル

### パッケージのインストール

```:terminal
npm i -D html-webpack-plugin pug-html-loader pug-loader
```



|パッケージ|内容|
|---|---|
|html-webpack-plugin|HTMLファイルを出力|
|pug-html-loader|PugをHTMLに変換|
|pug-loader|webpackでPugを使えるようにする|

### webpack.config.jsの変更
HTMLは1ファイルにバンドルしないので、個別に指定します。

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader",
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },

          "postcss-loader",
        ],
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false,
            },
          },

          "postcss-loader",

          "sass-loader",
        ],
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true,
            },
          },
        ],
      },
    ],
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" },
      ],
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80",
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256,
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true,
        }),
      ],
    }),

   //追加
   new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false, //minifyしない
    }),
    
    //追加
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false,
    }),
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"],
  },
};
``` 
 
 



### pugファイルを作成

```:src/pug/index.pug
html(lang="ja")
  head
    meta(charset="UTF-8")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    title トップページ
    //script(src="./js/main.js" defer)
  body
    p トップページ
    a(href="sub.html") リンク
    img(src="./img/img.jpg")
    script(src="./js/main.js")
    include ./_include/_included.pug
``` 
 

```:src/pug/test.pug
html(lang="ja")
  head
    meta(charset="UTF-8")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    title サブページ
    //script(src="./js/main.js" defer)
  body
    p サブページ
    script(src="./js/main.js")
``` 


```:src\pug\_include\_included.pug
p Include 成功
```

### ビルド

```:terminal
npm run build
```











## コードの整形、Lint


### prettier

#### インストール

```:terminal
npm i -D prettier
```

#### prettierの設定ファイルの作成


```:.prettierrc
 {
   "printWidth": 120,
   "tabWidth": 2,
   "singleQuote": false,
   "trailingComma": "none",
   "semi": true,
   "parser": "typescript",
   "endOfLine": "auto"
 }
```

[.prettierrc](https://qiita.com/takeshisakuma/items/bbb2cd2f1c65de70e363)









### eslint

#### インストール

```:terminal
npm i -D eslint eslint-loader 
```

#### .eslintrc.jsonの作成

```:.eslintrc.json
{
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module"
  },
  
   "env": {
       "browser": true
     },
  
  "root": true,
  
  "rules": {
       "quotes": ["error", "single"] //クォーテーションががシングルのときエラー
     }
}
```
 
設定はこちらを参照してください。
https://eslint.org/docs/rules/

#### webpack.config.jsの変更

```:webpack.config.json
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },

  module: {
    rules: [
    
      //追加
      {
        enforce: "pre",
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, //autofixモードの有効化
          failOnError: true //エラー検出時にビルド中断
        }
      },

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader"
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader"
        ]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader",

          "sass-loader"
        ]
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css"
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" }
      ]
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80"
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
           progressive: true
        })
      ]
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false //minifyしない
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false
    })
  ],
 
  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"]
  }
}
``` 

#### Lint

```:terminal
//lint
npx eslint src/index.ts

//lint+自動修正
npx eslint src/index.ts --fix
 
//複数ファイルをlint+自動修正
npx eslint src/**/*.ts --fix
```




### PrettierとEslintの連携
#### インストール

```:terminal
npm i -D eslint-plugin-prettier eslint-config-prettier
```


|パッケージ|内容|
|---|---|
|eslint-plugin-prettier|ESLint上でPrettierを動かす|
|eslint-config-prettier|ESLintとPrettierで競合するルールを無効にする|


#### .eslintrc.jsonの変更

```:.eslintrc.json
{
  //追加
  "extends": ["plugin:prettier/recommended"],
  
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module"
  },
  
  "env": {
    "browser": true
  },
  
  "root": true,
  
  "rules": {
    "quotes": ["error", "single"]//クォーテーションががシングルのときエラー
  }
  
}
``` 

















### stylelint

```:terminal
npm i -D stylelint stylelint-webpack-plugin stylelint-scss
```


|パッケージ|内容|
|---|---|
|stylelint|Stylelint本体|
|stylelint-webpack-plugin|ビルド時にstylelintを実行|
|stylelint-scss|SCSS用のルールセット|

#### webpack.config.jsの変更
webpack.config.jsにローダーを登録します。

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

const StyleLintPlugin = require("stylelint-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },

  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, //autofixモードの有効化
          failOnError: true //エラー検出時にビルド中断
        }
      },

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader"
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader"
        ]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader",

          "sass-loader"
        ]
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css"
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" }
      ]
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80"
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true
        })
      ]
    }),

    new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false //minifyしない
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false
    }),

    new StyleLintPlugin({
      configFile: ".stylelintrc",
      fix: true //自動修正可能なものは修正
    })
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"]
  }
}
```
 


#### .stylelintrcの作成

```:.stylelintrc
{
  "plugins": ["stylelint-scss"], 
  "rules": {
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true,
    "color-no-invalid-hex": true
  }
}
```
 
[.stylelintrc](https://qiita.com/takeshisakuma/items/a7a3b8cc0ce05422f686)



#### Lint

```:terminal
//lint
npx stylelint src/scss/style.scss

//lint+自動修正
npx stylelint src/scss/style.scss --fix

//複数ファイルをlint+自動修正
npx stylelint src/scss/**.scss --fix
```




### PrettierとStylelintの連携

####  インストール

```:termina:
npm i -D stylelint-prettier stylelint-config-prettier
```



|パッケージ|内容|
|---|---|
|stylelint-prettier|Stylelint上でPrettierを動かす|
|stylelint-config-prettier|StylelintとPrettierで競合するルールを無効にする|

#### .stylelintrcの変更

```:.stylelintrc
{
  "plugins": ["stylelint-scss"], 
 
  "extends": [
    "stylelint-prettier/recommended",
    "stylelint-config-prettier"
  ],
    
  "rules": {
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true,
    "color-no-invalid-hex": true
  }
}
``` 



### コード整形の除外
.stylelintignore、.prettierignore、.eslintignoreで、除外したいファイルやフォルダを指定できます。
書き方は.gitignoreと同じです。



### git commit前にLint

#### インストール

```:terminal
npm i -D lint-staged husky
```



|パッケージ|内容|
|---|---|
|lint-staged|ステージングされたファイルにLint |
|husky|gitコマンドをきっかけにして指定したコマンドを実行|


#### package.jsonの変更

```:package.json
"husky": {
  "hooks": {
    "pre-commit": "lint-staged" // コミット前に以下のコマンドが実行される
  }
}, 
"lint-staged": {
  "*.{ts,tsx}": [
    "tslint --fix",
    "git add"
  ],
  "*.{css,scss}": [
    "stylelint --fix",
    "git add"
  ]
}
```




## distフォルダ内のファイルの削除
srcフォルダ内から削除したファイルから生成したファイルがdistフォルダに残ったままになっていると事故がおこりやすいので、ビルドする度に自動で削除するようにします。

###  インストール

```:terminal
npm i -D clean-webpack-plugin
```


### webpack.config.jsの変更

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

const StyleLintPlugin = require("stylelint-webpack-plugin");

//追加
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },

  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, //autofixモードの有効化
          failOnError: true //エラー検出時にビルド中断
        }
      },

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader"
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader"
        ]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader",

          "sass-loader"
        ]
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },

  plugins: [
  
    //追加
    new CleanWebpackPlugin({ verbose: true }),

    new MiniCssExtractPlugin({
      filename: "css/style.css"
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" }
      ]
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80"
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true
        })
      ]
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false //minifyしない
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false
    }),
 
    new StyleLintPlugin({
      configFile: ".stylelintrc",
      fix: true //自動修正可能なものは修正
    })
  ],
 
  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"]
  }
};
``` 










## ソースマップの生成

### webpack.config.jsの変更
devtool: "source-map"を追加すると、ビルド時にJSとCSSにソースマップが生成されます。

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

const StyleLintPlugin = require("stylelint-webpack-plugin");

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  devtool: "source-map",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },

  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, //autofixモードの有効化
          failOnError: true //エラー検出時にビルド中断
        }
      },

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader"
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader"
        ]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader",

          "sass-loader"
        ]
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },

  plugins: [
    new CleanWebpackPlugin({ verbose: true }),

    new MiniCssExtractPlugin({
      filename: "css/style.css"
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" }
      ]
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80"
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true
        })
      ]
    }),

    new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false //minifyしない
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false
    }),

    new StyleLintPlugin({
      configFile: ".stylelintrc",
      fix: true //自動修正可能なものは修正
    })
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"]
  }
};

```


### ビルド

```:terminal
npm run build
```

dist/css/style.css.mapとdist/js/main.js.mapが生成されます。








## webpack-dev-server


### インストール

```:terminal
npm i -D webpack-dev-server
```

### webpack.config.jsの変更
devServerの設定を追加します。

```:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const ImageminMozjpeg = require("imagemin-mozjpeg");

const HtmlWebpackPlugin = require("html-webpack-plugin");

const StyleLintPlugin = require("stylelint-webpack-plugin");

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production",

  //entry: "./src/index.js",
  entry: "./src/index.ts",

  devtool: "source-map",

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js"
  },

  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, //autofixモードの有効化
          failOnError: true //エラー検出時にビルド中断
        }
      },

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },

      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "ts-loader"
      },

      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,

          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader"
        ]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              url: false
            }
          },

          "postcss-loader",

          "sass-loader"
        ]
      },

      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },

  //追加
  devServer: {
    contentBase: path.resolve(__dirname, "dist"),
    open: true, //起動時にブラウザを開く
    overlay: true //エラーをオーバーレイ表示
  },

  plugins: [
    new CleanWebpackPlugin({ verbose: true }),

    new MiniCssExtractPlugin({
      filename: "css/style.css"
    }),

    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" },
        { from: "./src/favicon.png", to: "favicon.png" },
        { from: "./src/favicon.svg", to: "favicon.svg" }
      ]
    }),

    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: "70-80"
      },
      gifsicle: {
        interlaced: false,
        optimizationLevel: 10,
        colors: 256
      },
      svgo: {},
      plugins: [
        ImageminMozjpeg({
          quality: 85,
          progressive: true
        })
      ]
    }),

    new HtmlWebpackPlugin({
      template: "./src/pug/index.pug", //変換元のPugファイルの指定
      filename: "index.html", //出力するHTMLのファイル名
      inject: false, //バンドルしたjsファイルを読み込むscriptタグを自動出力しない
      minify: false //minifyしない
    }),
    new HtmlWebpackPlugin({
      template: "./src/pug/sub.pug",
      filename: "sub.html",
      inject: false,
      minify: false
    }),

    new StyleLintPlugin({
      configFile: ".stylelintrc",
      fix: true //自動修正可能なものは修正
    })
  ],

  //import文で拡張子が.tsのファイルを解決する
  resolve: {
    extensions: [".ts", ".js", ".json"]
  }
};
``` 
 

### dev-serverの起動

```:terminal
//webpack4までの場合
npx webpack-dev-server

//webpack5以降の場合
npx webpack serve
```


## webpackの設定ファイルのジェネレータ

[Create App](https://createapp.dev/)


## リポジトリ
https://github.com/takeshisakuma/webpack_for_webdesign