GitHub Pagesで公開するときWebpackのパス関係で少し詰まった話

GitHub Pagesはとても手軽に公開ができ、
自分のようなフロントエンドエンジニアにはとても重宝しています。

ですが、Webpackを用いたページ作成の際、躓き、
これといった解決方法がWeb上に見つからず、自己解決したので、解決方法を残しておきます

## どんな問題か？
GitHub Pagesは通常、  
> https\://userName.github.io/repository/  

というURLがルートとなる構成となっています。（userNameとrepositoryは任意）

その際、ルートディレクトリが1階層ずれてしまい、その影響で

```JavaScript
fetch('../data/hoge.json')
```
といったファイルを読み込もうとしたときパスがずれてしまい404エラーとなってしまいました。

## 問題発生時の環境

WebpackにはJSとSASS(Scss)だけを通して、JSとCSSで出力していました。

長くなってしまったので各詳細はまとめてあります。

<details> 
<summary>ディレクトリ構成</summary>
<div>

```text
├ src/                <- 開発用ディレクトリ
│ ├ script/
│ │ ├ entry.js
│ │ ├ fetchJson.js
│ │ ├ ...
│ │
│ ├ data/
│ │ └ hoge.json
│ │
│ ├ style/
│ │ └ fuga.scss
│ │
│ └ index.html
│
├ dist/               <- コンパイル先ディレクトリ
│ ├ script/
│ │ └ main.js
│ │
│ ├ data/
│ │ └ hoge.json
│ │
│ ├ style/
│ │ └ style.css
│ │
│ └ index.html
│
├ docs/               <- 掲載時ディレクトリ（公開用ディレクトリ）
│ ├ script/
│ │ └ main.js
│ │
│ ├ data/
│ │ └ hoge.json
│ │
│ ├ style/
│ │ └ style.css
│ │
│ └ index.html
│
├ package.json
├ webpack.common.js
├ webpack.dev.js
├ webpack.prod.js
...
```

無駄なファイル、要素などは省いて書いたつもりですが、それでも長くなっちゃいますね。

</div>
</details>

<details> 
<summary>Webpackの設定</summary>
<div>

common, dev, prodで3つに分けています。

```webpack.common.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
var webpack = require('webpack');

module.exports = {
  entry: path.resolve('src', 'script', 'entry.ts'),
  output: {
    filename: 'main.js',
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '../style/style.css',
    }),
  ]
};

```

```webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const sourceMap = true;
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'development',
  watch: true,
  devtool: 'source-map',
  output: {
    path: path.resolve(__dirname, 'dist', 'script'),
  },
  module: {
    rules: [
      {
        test: /\.(sc|c|sa)ss$/,
        use: [
          { loader: MiniCssExtractPlugin.loader, },
          {
            loader: 'css-loader',
            options: { url: false, sourceMap, importLoaders: 2, },
          },
          {
            loader: 'sass-loader',
            options: { sourceMap, },
          },
        ],
      },
    ],
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: 3000,
    watchContentBase: true,
    hot: true,
    open: true,
  },
});
```

```webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'docs', 'script'),
  },
  module: {
    rules: [
      {
        test: /\.(sc|c|sa)ss$/,
        use: [
          { loader: MiniCssExtractPlugin.loader, },
          {
            loader: 'css-loader',
            options: { url: false, importLoaders: 2, },
          },
          {
            loader: 'sass-loader',
          },
        ],
      },
    ],
  },
});
```

</div>
</details>

これで、jsonファイルを読みにいこうすると

```JavaScript
async () => {
  await ('../data/hoge.json')
};
```

開発環境ではOKですが、

GitHub上ではエラーになります

```
GET https://userName.github.io/data/hoge.json 404 (Not Found)
```

読みに行こうとしているパスが１つがずれています。

正しくは

```
https://userName.github.io/repository/data/hoge.json
```

にファイルを取りに行きたいのです。

ですが、webpackの設定をいろいろ調整してみましたが、上手くいきません...。
（1日以上無駄にした）

## 解決策

「webpackの設定が悪いんだ！」と自分に暗示をかけていたのが悪かったです。

1歩後ろに下がって観察してみると簡単に解決策が見つかりました。
（簡単に解決策が見つかりました...。）

**srcディレクトリ構成を変える**  
これが僕の答えです。

srcのすぐ下で開発しないといけないという
謎の固定概念にとらわれていました。

```
├ src
│ └ repository
│   └ index.html
```

という形でsrcと開発ファイルの間に1つディレクトリをはさみ、  
コンパイル時に

```
├ dist
│ └ repository
│   └ index.html
└ docs
  └ index.html
```

というようにディレクトリの階層を変えてあげることで
開発サーバー上でもGitHub Pagesでもエラーが起きず表示することができました！

### 解決時のディレクトリ構成・webpack設定

<details> 
<summary>【解決】ディレクトリ構成</summary>
<div>

```text
├ src/           <- 開発用ディレクトリ
│ └ repository/      <- 1個ディレクトリを追加することで解決！
│   ├ script/
│   │ ├ entry.js
│   │ ├ fetchJson.js
│   │ ├ ...
│   │
│   ├ data/
│   │ └ hoge.json
│   │
│   ├ style/
│   │ └ fuga.scss
│   │
│   └ index.html
│
├ dist/          <- コンパイル先ディレクトリ
│ └ repository/      <- 1個ディレクトリをはさみで解決！
│   ├ script/
│   │ └ main.js
│   │
│   ├ data/
│   │ └ hoge.json
│   │
│   ├ style/
│   │ └ style.css
│   │
│   └ index.html
│
├ docs/           <- 掲載時ディレクトリ（公開用ディレクトリ）
│ │                   <- ここはディレクトリをはさめない！
│ ├ script/
│ │ └ main.js
│ │
│ ├ data/
│ │ └ hoge.json
│ │
│ ├ style/
│ │ └ style.css
│ │
│ └ index.html
│
├ package.json
├ webpack.common.js
├ webpack.dev.js
├ webpack.prod.js
...
```

</div>
</details>

<details> 
<summary>【解決】Webpackの設定</summary>
<div>

common, dev, prodで3つに分けています。

```webpack.common.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
var webpack = require('webpack');

module.exports = {
  // repositoryフォルダを追加
  entry: path.resolve('src', 'repository', 'script', 'entry.ts'),
  output: {
    filename: 'main.js',
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '../style/style.css',
    }),
  ]
};

```

```webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const sourceMap = true;
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'development',
  watch: true,
  devtool: 'source-map',
  output: {
    // repositoryフォルダを追加
    path: path.resolve(__dirname, 'dist', 'repository', 'script'),
  },
  module: {
    rules: [
      {
        test: /\.(sc|c|sa)ss$/,
        use: [
          { loader: MiniCssExtractPlugin.loader, },
          {
            loader: 'css-loader',
            options: { url: false, sourceMap, importLoaders: 2, },
          },
          {
            loader: 'sass-loader',
            options: { sourceMap, },
          },
        ],
      },
    ],
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist', 'repository'),
    port: 3000,
    watchContentBase: true,
    hot: true,
    open: true,
  },
});
```

```webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'production',
  output: {
    // ここはそのまま！
    path: path.resolve(__dirname, 'docs', 'script'),
  },
  module: {
    rules: [
      {
        test: /\.(sc|c|sa)ss$/,
        use: [
          { loader: MiniCssExtractPlugin.loader, },
          {
            loader: 'css-loader',
            options: { url: false, importLoaders: 2, },
          },
          {
            loader: 'sass-loader',
          },
        ],
      },
    ],
  },
});
```

</div>
</details>

## 所感

- Webpackというものはとても便利という反面、  
どうゆう動きをしているのか把握するのは難しい。
- ここが悪いという決めつけが問題解決を遅らせてしまうというのは  
コーディング時によくあるはなし。細かいところを見る視点を持ちながら俯瞰な視点も忘れずに開発したい。

本音はこれがベストプラクティスとは思えないというところです。
Webpackの知見がある方、是非、もっとよい解決策を教えてください！