概要
複数のServerlessFrameworkプロジェクトをYarn Workspacesを利用して1リポジトリに収めた際の備忘録です。
GitHubにコードサンプルを上げています:https://github.com/shu000/serverless-ts-monorepo-sample
Dependencies
- Node.js v14.15.1
- Yarn v1.22.5
- ServerlessFramework v2.52.1
Step 1. テンプレートのインストール
まずは、ServerlessFrameworkのテンプレートを利用して、hello
パッケージを作成します。
sls create --template aws-nodejs-typescript --path hello
複数のパッケージを用意したいので、同様にbye
パッケージも作成します。
sls create --template aws-nodejs-typescript --path bye
これでディレクトリはこのような状態です(一部ファイル省略)。
bye/
src/
functions/
libs/
package.json
serverless.ts
webpack.config.js
hello/
src/
functions/
libs/
package.json
serverless.ts
webpack.config.js
(以下、helloパッケージのコードサンプルが登場しますが、byeパッケージでも同一の変更が必要です。)
Step 2. モノレポ化
ここまでで1リポジトリ内に2つのnpmパッケージを設置しました。
ここからYarn Workspacesを活用してモノレポと呼べる構成にしていきます。
packages/
bye/
src/
functions/
libs/
package.json
serverless.ts
webpack.config.js
hello/
src/
functions/
libs/
package.json
serverless.ts
webpack.config.js
次にルートディレクトリにpackage.jsonを作成して、Yarn Workspaces設定を記述します。
また、今回は子パッケージのdevDependenciesは共通依存と見做し、ルートのpackage.jsonに書き写してします。
{
"name": "monorepo-sample",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/bye",
"packages/hello"
],
"devDependencies": {
"@serverless/typescript": "^2.23.0",
"@types/aws-lambda": "^8.10.71",
"@types/node": "^14.14.25",
"json-schema-to-ts": "^1.5.0",
"serverless": "^2.23.0",
"serverless-webpack": "^5.3.5",
"ts-loader": "^8.0.15",
"ts-node": "^9.1.1",
"tsconfig-paths": "^3.9.0",
"tsconfig-paths-webpack-plugin": "^3.3.0",
"typescript": "^4.1.3",
"webpack": "^5.20.2",
"webpack-node-externals": "^2.5.2"
}
}
ルートにdevDependenciesを記述したので、bye
およびhello
のpackage.jsonも編集します。
{
"name": "hello",
"version": "1.0.0",
"private": true,
"dependencies": {
"@middy/core": "^1.5.2",
"@middy/http-json-body-parser": "^1.5.2",
"source-map-support": "^0.5.19"
}
}
これで一旦モノレポにはなりました。
ルートディレクトリでyarn
を実行すると、共通するライブラリがルートに巻き取られることが分かります。
Step 3. webpack.config の修正
ここまででは、まだデプロイはうまくいきません。
デフォルトのwebpack.config.jsでは親のnode_modulesを解決できないためです。
従ってwebpack-node-externalsに親のnode_modulesを教えてやります。
externals: [nodeExternals({
modulesDir: path.resolve(__dirname, '../../node_modules')
})],
これで、各パッケージ内でsls deploy
の実行に成功するようになります。
Step 4. 共通パッケージの作成
せっかくモノレポにしたので、重複ソースを共通パッケージとして切り出しましょう。
まずはpackages/my-libs
ディレクトリを作成します。
mkdir packages/my-libs
テンプレートに含まれるsrc/libs
を共通パッケージにしてみます。
mv packages/hello/src/libs packages/my-libs/src
rm -rf packages/bye/src/libs
{
"name": "my-libs",
"version": "1.0.0",
"private": true,
"main": "src/index.ts",
"dependencies": {
"@middy/core": "^1.5.2",
"@middy/http-json-body-parser": "^1.5.2"
}
}
export * from './apiGateway'
export * from './handlerResolver'
export * from './lambda'
上記ではmy-libsのmainをts(src/index.ts)にしています。
今回の構成では、byeやhelloのビルド時に、ts-loaderがmy-libsのtsも併せてビルドしてくれるためです。
通常、他者のパッケージからも呼ばれる場合は、ちゃんとトランスパイルしてからjsをmainに設定しましょう。
これでディレクトリ構成はこんな感じ。
package.json
packages/
my-libs/
src/
index.ts
apiGateway.ts
handlerResolver.ts
lambda.ts
package.json
bye/
src/
functions/
package.json
serverless.ts
webpack.config.js
hello/
src/
functions/
package.json
serverless.ts
webpack.config.js
ではmy-libs
をworkspacesに追加しましょう。
"workspaces": [
"packages/my-libs",
"packages/bye",
"packages/hello"
]
続いてbye
とhello
からmy-libs
を利用してみます。
"dependencies": {
"my-libs": "*",
"source-map-support": "^0.5.19"
}
import type { ValidatedEventAPIGatewayProxyEvent } from 'my-libs';
import { formatJSONResponse } from 'my-libs';
import { middyfy } from 'my-libs';
import { handlerPath } from 'my-libs';
最後に、このままではServerlessFrameworkがmy-libsをバンドルする際にエラーになるため、webpack.configに設定を追加します。
externals: [nodeExternals({
modulesDir: path.resolve(__dirname, '../../node_modules'),
allowlist: ['my-libs']
})],
これで、ルートディレクトリでyarn
を再実行すれば、sls deploy
が成功するようになります!
以上、当方初学者のため、マサカリ募集中です。
参考記事