VueプロジェクトのデバッグでVSCode上のブレークポイントがきれいに止まらない件に対応する

## 概要
VueのHelloWorldなプロジェクトをVSCode上でデバッグしたかったのですが、なんだかうまく止まらない。これの解決を目指したお話です。

## HelloWorld
Vue関連のインストール等は省略します。
```shell
> vue create hello-world-test
```
と打ってHelloWorldなプロジェクトを作成します。
プロジェクト名に大文字は使えないんですね。

こんなApp.vueができました。CSS部は省略。
```Vue:App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>
```
## ちょっといじってデバッグ
Options APIの書き方でそのままdate()とcreated()を追加して、表示内容を変えました。これをデバッグ実行して処理をdebuggerで止めたいと思います。
```Vue:App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld :msg="currentTime"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data() {
    return {
      currentTime: null,
    };
  },
  created() {
    // eslint-disable-next-line no-debugger
    debugger;
    this.currentTime = new Date();
  },
}
```
VSCodeのlaunch.jsonでもってデバッグ実行の構成を設定します。
構成の追加で「Chrome起動」を選択したまんま。
```JSON:launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Chrome",
            "request": "launch",
            "type": "chrome",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}"
        }
    ]
}
```
下記のコマンドで立ち上げて、VSCodeの「実行とデバッグ」からLaunch Chromeを実行。
```shell
> npm run serve
```
すると・・・止まりましたが、App.vue?****（数字）というファイルが開いた形でブレークしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3736537/a2184adc-4a76-39e6-b8ee-8b0413a3159b.png)
左のソースで開いてほしい。

これは多分ハイドレーションがどうとかこうとかいうやつで、詳しくは触れません。
launch.jsonにsourceMapsを追記という記事がよく出てくるのですが、自分の環境ではlaunch.jsonをいじっても状況が変わりませんでした。
それで結論としては、**vue.config.js**に下記の通り追記するとうまく行きました。
```diff_JavaScript:vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
-  transpileDependencies: true
+  transpileDependencies: true,
+  configureWebpack: {
+    devtool: "source-map",
+  },
})
```
設定を変えたら`npm run serve`し直して、Launch Chrome。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3736537/e6710962-5c20-a001-7384-3a2ea30bcacf.png)
ハテナが付かなくなった！・・・けど、まだ左のソースと違うものが開いている。

App.vueのタブにマウスオーバーしてみると、"\\${workspaceFolder}"直下のsrc/App.vueというパスになっています。これは正しくは"\\${workspaceFolder}/hello-world-test"以下でなくてはなりません。
こちらについては**launch.json**を直す必要があります。下記の通りです。
```diff_JSON:launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Chrome",
            "request": "launch",
            "type": "chrome",
            "url": "http://localhost:8080",
-            "webRoot": "${workspaceFolder}"
+            "webRoot": "${workspaceFolder}/hello-world-test"
        }
    ]
}
```
これで改めてLaunch Chromeしてみると、期待通り元のソースに対してdebuggerが機能しました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3736537/9da2ecfe-ed45-5134-44aa-b658e8088b85.png)
この状態になれば、ブレークポイントを貼る方法についてもきれいに止まるようになります。めでたしめでたし。
※環境によってはこれだけではダメかもしれません。launch.jsonの"sourceMapPathOverrides"という設定が必要かもしれませんが、よく分かっていないこともあるため、ここでは触れません。

## サーバー起動＆Chrome起動のハッピーセット
動かすとき、いちいち`npm run serve`してVSCodeのデバッグ実行をクリックして～、という手順は繰り返していると煩わしいものです。これをまとめてやってくれる設定の書き方があります。launch.jsonを以下のようにします。
```JSON:launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "compounds": [
        {
            "name": "Debug on Chrome",
            "configurations": [
                "Run Serve",
                "Launch Chrome"
            ]
        }
    ],
    "configurations": [
        {
            "name": "Launch Chrome",
            "request": "launch",
            "type": "chrome",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}/hello-world-test",
        },
        {
            "name": "Run Serve",
            "request": "launch",
            "type": "node",
            "runtimeExecutable": "npm",
            "runtimeArgs": [
                "run",
                "serve"
            ],
            "cwd": "${workspaceFolder}/hello-world-test",
            "skipFiles": [
                "<node_internals>/**"
            ]
        }
```
"compounds"のところがポイント。2つの処理を両方やってくれるようになります。

しかし、この状態で前章のようにデバッグしようとすると、初回起動時はなんだか止まってくれません。またdebuggerを置いていると、何やら止まっているものの (index)（数字）とかVM（数字）といった謎のファイルが開き、「ソース ～～ を読み込めませんでした: ソースが見つかりません。」というエラーっぽいものが出たりします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3736537/fa7d4245-1433-2ec8-d33c-c5bda4445ea0.png)
謎のファイル。

ただ、Chromeの画面をリロードすると、以降はブレークポイントで止まるようになります。
この「初回実行時だけなんか止まらない」問題を解決します。

詳しいことは自分がよく分かっていませんが、要するに「まだ準備できてないけどChromeがページを開いちゃって止まっちゃった」という状況なのだろうと思います。それで対策として行うのは、「開発用サーバーのポートが準備出来たことを確認してからChromeを立ち上げる」という制御をするということです。

これを達成するため、設定ファイルと実行ファイルを追加します。いずれも.vscodeフォルダに置いておけばいいと思います。
```JSON:tasks.json
{
    "version": "2.0.0",
    "options": {
        "shell": {
            "executable": "powershell.exe"
        }
    },
    "tasks": [
        {
            "label": "WaitForPortOpen",
            "type": "shell",
            "windows": {
                "command": [
                    "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process;",
                    ". .vscode/wait.ps1;"
                ]
            },
            "group": "none",
            "presentation": {
                "panel": "shared",
                "reveal": "never",
                "showReuseMessage": false,
                "close": true
            }
        }
    ]
}
```
```PowerShell:wait.ps1
$ErrorActionPreference = "Stop"

while (!(Test-NetConnection -ComputerName 127.0.0.1 -Port 8080).TcpTestSucceeded) {
    Start-Sleep -Seconds 5
}
```
さらに、launch.jsonに下記の通り記述を追加します。
```diff_JSON:launch.json
        {
            "name": "Launch Chrome",
            "request": "launch",
            "type": "chrome",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}/hello-world-test",
+            "preLaunchTask": "WaitForPortOpen"
        },
```
前述の通り、Launch Chromeする前のタスクとして、"WaitForPortOpen"というものをtasks.jsonに用意しました。内容は"wait.ps1"をPowerShellで実行するという感じです。そのwait.ps1は、ポートが通るまで待ちますよ、という命令をしています。
`Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process`
は、PowerShellで実行を許可するためのおまじないです。
"presentation"の中の設定は実行してからVSCodeにPowerShellのターミナルを残さないとか色々の設定です。お好みに合わせて変えてもらえればと思います。ここでは詳しくは解説しません。

以上の設定を踏まえてDebug on Chromeを実行すれば、初回起動時でもきれいにブレークポイントで止まるようになります！

## 参考
https://v2.ja.vuejs.org/v2/cookbook/debugging-in-vscode#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B

https://yokoken.hatenablog.com/entry/2019/08/13/024344

https://stackoverflow.com/questions/71648436/powershell-looping-test-netconnection