Flask + Webpack with VSCodeの開発環境

# 概要

個人開発中のFlaskアプリでjavascriptが大きくなりすぎたため、jsファイルを分割して管理することにしました。

しかし分割しすぎるとパフォーマンスに問題がでるらしく、回避のためwebpackで数個のjsファイルにbundle(build)する形をとることにしました。

ここで困ってしまったのが、「**FlaskとWebpackを併用する場合のフォルダ構造やデバッグ/ビルドの設定のあるべき姿**」とは?という点です。

結局正解は分からないままなのですが、備忘録がてら自分がどうしたかを記載しておきます。

# 前提知識

### Flaskとは

Webアプリのバックエンドをpythonで書くことができるフレームワークの１つです。他にはdjangoなどがあります。

<iframe width="560" height="315" src="https://www.youtube.com/embed/EQIAzH0HvzQ?si=78848ms2Q5CTw2mv" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### Webpackとは

> 複数の JavaScript モジュールを一つ（またはいくつか）のファイルへバンドル（=bundle: 束にする、包む）してくれます。

> モジュールを 1 つ（もしくは少数）にまとめることでブラウザからのリクエスト数を減らし、ファイル転送の効率が向上します。

https://zenn.dev/sprout2000/articles/9d026d3d9e0e8f

# 採用した構造・設定

### フォルダ構造

まずフォルダ構造については次のようにしました。
もともとFlaskアプリとして作っていたため、ちょっと歪な気がします。詳しい説明・今後修正したい点についてはコメントに記載の通りです。

```python
root
├─.vscode
│  ├─launch.json # webpackでbundle→front/distからstaticにコピー→run.pyを実行する設定を記載
│  └─tasks.json # launch.jsonを構成する各タスクを定義
├─backend
│  ├─routes #flaskのpyファイルを配置
│  └─templates # front/back共用のため、rootに配置すべきだったかも。ここのhtmlファイルはstaticのjsをロードする。
├─env # venvの仮想環境 pythonに関わるもののためbackend下の方が適切かも
├─frontend
│  ├─dist # ビルド(webpackのbundle)結果は一旦ここに配置
│  ├─node_modules # node.jsのパッケージの実物
│  ├─src # module毎にjsをbundleする(複数のbundleファイルを出力する)
│  │   ├─moduleA
│  │   └─moduleB
│  ├─package.json # node.jsのパッケージのリストを記載
│  └─webpack.config.js # webpackのbundle設定を記載
├─static # distからbundleしたjsをここにコピー。cssもここ
├─ run.py # app.run() flaskの実行処理を記載
├─ requests.txt # backend下の方が適切かも
├─ LICENSE.txt
└─ .gitignore
```

### 設定内容

launch.json, tasks.json, webpack.config.jsの設定内容は次の通り。
F5キーでwebpackのバンドルからFlaskの実行、ブラウザの起動を行います。

※jsonにコメントは本来書けないので、コピペの時はコメントを消してください。

```launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Flask with npm build",
            "type": "python",
            "request": "launch",
            "program": "./run.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "preLaunchTask": "build-and-copy-and-open-browser" //webpackのビルド,ビルド成果物のコピー,ブラウザ起動
        },
        {
            "name": "Flask Only",
            "type": "python",
            "request": "launch",
            "program": "./run.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "preLaunchTask": "open-browser", //ブラウザ起動
        }
    ]
}
```

```tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      // ビルド
      "label": "build",
      "type": "shell",
      "command": "npm --prefix ./frontend run build", 
      "problemMatcher": []
    },
    {
      // ビルド成果物のコピー
      "label": "copy-js-files-from-dist-to-static",
      "type": "shell",
      "command": "cp ./frontend/dist/*.js ./static/", 
      "problemMatcher": []
    },
    {
      // ビルド&ビルド成果物のコピー
      "label": "build-and-copy",
      "dependsOn": ["build", "copy-js-files-from-dist-to-static"],
      "dependsOrder": "sequence",
      "problemMatcher": []
    },
    {
    　// ブラウザ起動
      "label": "open-browser",
      "type": "shell",
      "command": "start http://localhost:5000",
      "problemMatcher": []
    },
    {
      // ビルド,ビルド成果物のコピー,ブラウザの起動
      "label": "build-and-copy-and-open-browser",
      "dependsOn": ["build-and-copy", "open-browser"],
      "dependsOrder": "sequence",
      "problemMatcher": []
    }    
  ]
}
```

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

module.exports = {

  devtool: 'source-map',
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),  // 静的ファイルのディレクトリ
        },
        hot: true  // ホットモジュールリプレースメントを有効にする
    },
  mode: 'development',

  // エントリーポイント（主なJavaScriptファイル）
  entry: {
    'moduleA': './src/moduleA/main.js',
    'moduleB': './src/moduleB/main.js',
  },

  // 出力先は一旦dist
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname,'dist')
  },

};
```