Amplify CLI + Lambda + TypescriptのローカルモックをVisual Studio Codeでデバッグする

Amplify CLIでLambda（Function）をTypescriptで書くときに、ローカルモックでLambdaを実行してVisual Studio Codeでデバッグする方法です。
（**デバッグする** とは、VSCode上でブレークポイントを止めてステップ実行、といったことを指します。）

* [Mocking and testing - Function mocking setup](https://github.com/aws-amplify/amplify-cli/issues/6894)

### 注意点

Amplify CLI（@aws-amplify/cli）のv4.44.0以降では、シンプルなJavascriptの状態でもデバッグできていません。
ブレークポイントをが止まらないという[Issue](https://github.com/aws-amplify/amplify-cli/issues/6894)が上がっていますが、解決策が見つかっていない状態です。
このため、v4.43.0の環境での確認結果になります。

### Lambdaを追加してJavascriptの状態でデバッグする

以下のようにAmplify CLIで**debugfunction**というLambdaを追加したとして進めます。

~~~bash
$ amplify function add debugfunc
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: debugfunc
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World

Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration

? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? No
Successfully added resource debugfunc locally.
~~~

以下のようにローカルモックを実行する構成を追加します。

~~~json:.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "debugfunc",
      "program": "/usr/local/bin/amplify",  /* Amplify CLIの実行パスは環境に応じて変更 */
      "args": [
        "mock",
        "function",
        "debugfunc",
        "--event",
        "src/event.json",
        "--timeout",
        "0"
      ],
      "console": "integratedTerminal"
    },
  ]
}
~~~

**src/index.js** にブレークポイントを設定して実行すると該当箇所で止まることを確認できます。
![capture01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30716/47ea8f53-51b3-a2d2-2cc7-713f40986c63.png)

### LambdaをTypescriptで作成する

Amplify CLIで生成されるLambdaのCloudFormationテンプレート（amplify/backend/function/debugfunc/debugfunc-cloudformation-template.json）の設定に従って、**エントリーとなるjsはsrc/index.js** になるようwebpackを設定していきます。

以下のようにsrcフォルダの内容をts-srcフォルダに動かして、Typescriptのソースを入れるようにします。

~~~bash
$ cd amplify/backend/function/debugfunc/
$ mv src ts-src
$ cd ts-src
$ mv index.js index.ts
$ cd ..
~~~

typescriptやwebpackなど、必要なモジュールをインストールします。

~~~bash
$ npm i -D webpack webpack-cli typescript ts-loader
~~~

以下のように**tsconfig.json**、**webpack.config.js**を作成します。
今回は細かい設定は無視して、デバッグできる最低限を書いています。

~~~json:amplify/backend/function/debugfunc/ts-src/tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "target": "es5",
    "module": "es2015",
  }
}
~~~

~~~javascript:amplify/backend/function/debugfunc/webpack.config.js
module.exports = {
  mode: "development",
  devtool: "source-map",
  entry: "./ts-src/index.ts",
  output: {
    path: `${__dirname}/src`,
    filename: "index.js",
    library: { type: "commonjs" },
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".js"],
  },
};

~~~

ポイントは以下になります。

* ソースマップを出力する。この際、インラインはVSCodeがサポートしていないため、必ず分割で出力します。
* webpackの出力先を **src/index.js** にします。前述のLambdaのハンドラー設定に合わせるためです。

設定したらwebpackを実行します。src/index.jsとマップファイルが生成できることを確認できると思います。

~~~bash
$ pwd
/home/me/dev/amplify/debug-function/amplify/backend/function/debugfunc
$ npx webpack
asset index.js 3.64 KiB [compared for emit] (name: main) 1 related asset
./ts-src/index.ts 2.96 KiB [built] [code generated]
webpack 5.36.2 compiled successfully in 1285 ms
$ ls src
index.js  index.js.map
~~~

### Typescriptの状態でデバッグする

最後はTypescriptのソースにブレークポイントを設定してのデバッグです。
まず、webpackを実行するタスクを追加します。

~~~json:.vscode/tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "webpack",
      "type": "shell",
      "command": "cd amplify/backend/function/debugfunc && npx webpack",
    }
  ]
}
~~~

このタスクをjaunch.jsonの**preLaunchTask**に指定して、デバッグ実行時にビルドされるようにします。
また、フォルダの変更や、ソースマップを参照できるようにも修正を入れます。修正後が以下です。

~~~json:.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "debugfunc",
      "program": "/usr/local/bin/amplify",
      "preLaunchTask":"webpack",
      "args": [
        "mock",
        "function",
        "debugfunc",
        "--event",
        "ts-src/event.json",
        "--timeout",
        "0"
      ],
      "console": "integratedTerminal",
      "sourceMaps": true,
      "smartStep": true,
      "outFiles": ["${workspaceFolder}/amplify/backend/function/debugfunc/src/**/*.js"],
      "sourceMapPathOverrides": {
        "webpack:///./*": "${workspaceFolder}/amplify/backend/function/debugfunc/*"
      }
    },
  ]
}
~~~

**sourceMapPathOverrides** は、出力されているマップファイルの **webpack://** と自分のところのパスを指定します。

これでVSCodeから **ts-src/index.ts** にブレークポイントを設定して実行すると以下のように止まります。
![capture02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30716/bc380159-d37b-d4d6-f2ee-b81497d7ec19.png)




### 参考サイト

* [最新版TypeScript+webpack 5の環境構築まとめ(React, Vue.js, Three.jsのサンプル付き)](https://ics.media/entry/16329/)
* [Amplify Docs - ADVANCED WORKFLOWS - Mocking and testing](https://docs.amplify.aws/cli/usage/mock)
* [VS Code breakpoints ignored during "amplify mock function" #6894](https://github.com/aws-amplify/amplify-cli/issues/6894)
* [Debugging TypeScript](https://code.visualstudio.com/docs/typescript/typescript-debugging)