モノレポ ( Turborepo ) の Azure Functions（Node Typescript）のデプロイで関数がない状態になることの対応をしたメモ

## 概要
[前回](https://qiita.com/hibohiboo/items/9fa5257ba706e71512a4)はTurborepoに管理されていないフォルダでFunctionsをデプロイしていた。今回、Turborepoの管理にいれたところ、`func start`で動いたAPIがデプロイ時にはなくなってしまう現象が発生した。
結論からいうと、デプロイ時は別フォルダにコピーを作って行った。

[ソースコード](https://github.com/hibohiboo/async-ttrpg/tree/37151abc9ea38d897800c2b18dbf5388b92a4956)

**2024.09.28追記**
別フォルダにコピーせず、[esbuildを使ったバンドルにより解決する方法](https://qiita.com/hibohiboo/items/a840af8ddda07b17c2fc)を別途記事にて記載。[*](https://qiita.com/hibohiboo/items/a840af8ddda07b17c2fc)

## 現象

動作確認では下画像のようにAPIが見えていた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/54e2ad3e-16c4-8a21-2db5-b78b2016b12b.png)

`func azure functionapp publish $APP_NAME`を行ったところ、デプロイは終わっても関数が作成されなかった。
[エラーのトラブルシューティングにおける"Azure Functions ランタイムに到達できない" について](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-recover-storage-account)や[デプロイ後に関数が見つからない](https://learn.microsoft.com/ja-jp/azure/azure-functions/recover-python-functions?tabs=vscode%2Cbash&pivots=python-mode-decorators)をみてもピンとこない。

## 調査
ぐぐったらIssueがいろいろ引っかかった。
[How to support monorepo](https://github.com/Azure/azure-functions-nodejs-library/issues/260)
[True support for multiple function apps in a monorepo ](https://github.com/microsoft/vscode-azurefunctions/issues/2521)

Turborepoで作っているため、node_modulesがルートに作成され、そこに Azure Functionsのライブラリが入っているのが原因のようだ。apiフォルダ内に必要らしい。

[公式でNode.js v4 プログラミング モデルの monorepo ワークスペースの依存関係を管理する](https://learn.microsoft.com/ja-jp/azure/developer/javascript/end-to-end/contoso-real-estate-serverless-api-migration#manage-monorepo-workspace-dependencies-for-nodejs-v4-programming-model)と言ってる割にあまり参考にならなかった。


## 試行錯誤

EsBuildを使ってnode_modulesの階層から解き放とうとしたが、CJSではなくESMとして出力されて頓挫した。

Honoを使ってうまく行ったという人もいたので試したが、ビルドの問題は解決しなかった。
Hono+EsBuildだと1ファイルにまとまるので、CJSにさえ簡単にできればこれでいけそうだったかも。惜しい。


## ソースコード

```json:apps/api/package.json
{
  "name": "api",
  "scripts": {
    "prebuild": "npm run clean",
    "build": "tsc && tsc-alias",
    "watch": "tsc -w",
    "clean": "rimraf dist",
    "predev": "npm run build",
    "dev": "func start --port 7071",
    "lint": "eslint",
    "deploy": "dotenv -e .env -- bash -c 'bash deploy.sh'"
  },
  "dependencies": {
    "@azure/functions": "^4.5.0",
    "date-fns": "^3.6.0",
    "hono": "^4.5.0"
  },
  "devDependencies": {
    "@async-ttrpg/eslint-config-custom": "*",
    "@async-ttrpg/tsconfig": "*",
    "@types/mssql": "^9.1.5",
    "@types/node": "^20.x",
    "dotenv-cli": "^7.4.2",
    "rimraf": "^6.0.1",
    "tsc-alias": "^1.8.10",
    "typescript": "^5.5.3"
  },
  "main": "dist/src/{app.js,functions/*.js}"
}
```

```bash:apps/api/deploy.sh
#!/bin/bash

BUILD_DIR=build
rimraf $BUILD_DIR
mkdir $BUILD_DIR
cp -r dist $BUILD_DIR
cp host.json $BUILD_DIR
cp local.settings.json $BUILD_DIR
# package.json から devDependencies を削除して、本番環境用の node_modules を作成 (@async-ttrpg/typescript-configなどmonorepoの機能で参照しているパッケージがエラーを引き起こすため)
jq 'del(.devDependencies)' package.json > temp.json && mv temp.json $BUILD_DIR/package.json

cd $BUILD_DIR && npm install --omit=dev \
   && func azure functionapp publish $APP_NAME --subscription $AZURE_SUBSCRIPTION_ID
```

```config:apps/api/.env
APP_NAME=HOGEHOGEfunctionsapp
AZURE_SUBSCRIPTION_ID=d2345678-1234-1234-1234-1234567890af
```

```json:.vscode/settings.json
{
  "azureFunctions.deploySubpath": "apps\\api",
  "azureFunctions.projectSubpath": "apps\\api",
  "azureFunctions.postDeployTask": "npm install (functions)",
  "azureFunctions.projectLanguage": "TypeScript",
  "azureFunctions.projectRuntime": "~4",
  "debug.internalConsoleOptions": "neverOpen",
  "azureFunctions.projectLanguageModel": 4,
  "azureFunctions.preDeployTask": "npm prune (functions)",
}
```
   
## サブスクリプションID
関数に表示されているものを使って環境変数`AZURE_SUBSCRIPTION_ID`に記載

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/28a49dca-2213-ae4d-82a1-28bad5341d88.png)

[ソースコード](https://github.com/hibohiboo/async-ttrpg/tree/878143070bcc8068c3eb84967e827760638b4454)

## ビルドパイプラインを作ってみた (2024.08.01 追記)
[HonoでRPCをしたAzureFunctionsをAzureDevOpsのパイプラインからデプロイしたメモ](https://zenn.dev/hibo/articles/d1f7ca9b8f08c4)


## 参考

[esbuild に CJS として import してほしいのに ESM として import されちゃうときのプラグイン](https://zenn.dev/sosukesuzuki/articles/d8220ba2d869f0)