😊

TypeScriptのライブラリ内で参照されているライブラリを差し替えるちょっとしたTIPS

2021/12/16に公開

「株式会社カケハシ 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.jsrequire をプロキシしてモジュールの置き換えを行うツールの様です。しかしながら、READMEでも言及がある通り、あくまでテストコード開発での利用が想定されております (モックなので当然ですが)。テスト用のツールを本番系のコードの混ぜるのは気が引ける部分があり、モックでの対応は断念しました。

解決案

そこで、tsconfig.json にある Path Alias の機能で mysql パッケージの参照先を自分のコードに置き換えることはできないかと考えました。

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "mysql": [
        "./src/mysql.ts"
      ]
    },
    // ...
  }
}

この様にすることで、import mysql from 'mysql'; で読みこまれるパッケージが node_modules の中ではなく、自作した src/mysql.ts に置き換わります。 ただし、エイリアスの解決には, Webpackesbuild でバンドルするか、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 でキャプチャされた物に差し替えることができました。

まとめ

今回の解決方法は結果的に他のツールに頼らず、TypeScriptnpmだけで実現できました。 X-Ray + TypeORM は一例ですが、さまざまな理由でライブラリ内の参照を置き換えたい場面があると思います。

悩んだ時の一助になれば幸いです。

Discussion