「株式会社カケハシ x TypeScript」アドベントカレンダーの8日目の記事はライトなTIPSで失礼します。
背景
タイトルが意味不明感ありますが、ライブラリ内で require/import
されているライブラリの差し替え方法を検討します。
私の背景としては Lambda + Node.js
のバックエンドで AWS X-Ray
を設定する時に MySQL
への通信もトレースしたかったのですが、そのためには MySQL
のクライアントをキャプチャする必要がありました。
以下がキャプチャの方法です。
import AWSXRay from 'aws-xray-sdk';
import _mysql from 'mysql';
const mysql = AWSXRay.captureMySQL(_mysql);
const connection = mysql.createConnection(config);
この様に、mysql
モジュールをキャプチャして置き換えたモジュールを利用する必要があります。
ただ、実際に MySQL
へアクセスするする際には Sequelize
などのORMライブラリを利用するのが一般的かと思います。私の場合はTS互換の良さから TypeROM を利用しています。しかし、TypeOrm
は内部で mysql
パッケージを import
しているのですが、そのパッケージを差し替える機能は提供されていないため、どの様に上記のキャプチャー作業をここなったら良いか悩んだのがきっかけです。
検討したこと
最初に検討したのはモジュールのモックです。Jest
などテストフレームワークでモック+スパイ関数などはよく利用するかと思いますので気軽に mysql
パッケージの置き換えができないか?と考えました。
TypeORMのissue に私と同じ課題に当たっている方がおり、主な回答としては module-mock
というライブラリを利用する方法でした。現在は proxyquire
というライブラリになっているのですが、Node.js
の require
をプロキシしてモジュールの置き換えを行うツールの様です。しかしながら、READMEでも言及がある通り、あくまでテストコード開発での利用が想定されております (モックなので当然ですが)。テスト用のツールを本番系のコードの混ぜるのは気が引ける部分があり、モックでの対応は断念しました。
解決案
そこで、tsconfig.json
にある Path Alias
の機能で mysql
パッケージの参照先を自分のコードに置き換えることはできないかと考えました。
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"mysql": [
"./src/mysql.ts"
]
},
// ...
}
}
この様にすることで、import mysql from 'mysql';
で読みこまれるパッケージが node_modules
の中ではなく、自作した src/mysql.ts
に置き換わります。 ただし、エイリアスの解決には, Webpack
やesbuild
でバンドルするか、module-alias などを利用して解決する必要があります。
次に src/mysql.ts
の中では元々の mysql
を読み込んで、キャプチャされた mysql
モジュールをエクスポートする必要があります。これを実現するためには、パッケージを別の名前でインストールできる機能があると便利ですが、npm
の Version6 以降ではインストールするパッケージにエイリアス名を指定することができきます。
今回の例でいくと、
npm install original-mysql@npm:mysql
npm install -D @types/original-mysql@npm:@typed/mysql
を実行するとpackage.jsonにはこの様にインストールされます。
{
"devDependencies": {
"@types/original-mysql": "npm:@types/mysql@2.15.19",
// ...
},
"dependencies": {
"original-mysql": "npm:mysql@2.18.1",
// ...
}
}
最後に src/mysql.ts
の実装ですが、元々のmysqlパッケージを読み込んで、X-Ray
にキャプチャされた mysql
モジュールを提供します。
import { AWSXRay } from '@common/aws/xray';
// X-Rayでラップするためにmysqlモジュールを変更する
import originalMysql from 'originalMysql';
export const capturedMysql = AWSXRay.captureMySQL(originalMysql);
// @ts-ignore
module.exports = capturedMysql as typeof originalMysql;
以上より、TypeORM内で参照される mysql
モジュールが、X-Ray
でキャプチャされた物に差し替えることができました。
まとめ
今回の解決方法は結果的に他のツールに頼らず、TypeScript
とnpm
だけで実現できました。 X-Ray + TypeORM
は一例ですが、さまざまな理由でライブラリ内の参照を置き換えたい場面があると思います。
悩んだ時の一助になれば幸いです。