グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



“開発者の”npmパッケージが原因で問題を解決できない! node_modulesのライブラリにパッチを当てる方法【最終手段】

今年も残すところあとひと月。気がつけば師も走る12月になりました。歳を重ねるごとに1年が早く感じられるのは不思議なものです。今年の汚れは今年のうちに、辛かった思い出も2024年に吐き出しきって気持ちよく2025年を迎えたいものです。

今回はnpmパッケージにパッチを当てる方法についてご紹介します。

経緯(概略)

とある機能をnpmパッケージを利用して開発中していた際、IMEに起因するnpmパッケージの問題で機能的なトラブルが発生しました。

OSSのnpmパッケージでは珍しいことではありませんが、IMEを使用しない言語話者がパッケージ開発者の多くを占めている印象もあり、IME関連で問題が起こるケースは少なくないようです。

アプリケーション側で対応できればよかったのですが、今回の問題はパッケージのコアの部分で発生しており、ソースコードを直接修正する必要がありました。

当社ではパッケージマネージャにpnpmを採用しており、調べたところpnpmが公式にパッチを当てる方法をサポートされていることがわかりました。

pnpm patch <pkg> | pnpm

パッチの作成には以下のコマンドを使用します。

pnpm patch <pkg name>@<version>

pnpmでパッチを当てる

今回はパッチを当てる例として牛が喋ってくれるパッケージ piuccio/cowsay: cowsay is a configurable talking cowを例にしてパッチを当てていきます。

プロジェクトの作成

プロジェクト用のディレクトリを作成しpackage.jsonを以下のように記入します。

{
  "name": "playground-pnpm-patch",
  "type": "module",
  "private": true,
  "dependencies": {
    "cowsay": "1.6.0"
  }
}

エントリーファイルを/src/index.jsとして作成します。

import cowsay from "cowsay";

console.log(
  cowsay.say({
    text: "I'm a moooodule",
    e: "oO",
    T: "U ",
  })
);

実行結果

node src/index.js
 _________________
< I'm a moooodule >
 -----------------
        \   ^__^
         \  (oO)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||

牛が喋っています。

patch コマンドでパッチを作成する

以下のコマンドを実行します。

pnpm patch cowsay@1.6.0

すると、以下のように案内が表示されます。

Patch: You can now edit the package at:

  /path/to/playground-pnpm-patch/node_modules/.pnpm_patches/cowsay@1.6.0 (​file:///path/to/playground-pnpm-patch/node_modules/.pnpm_patches/cowsay@1.6.0​)

To commit your changes, run:

  pnpm patch-commit '/path/to/playground-pnpm-patch/node_modules/.pnpm_patches/cowsay@1.6.0'

作成されたディレクトリ/path/to/playground-pnpm-patch/node_modules/.pnpm_patches/cowsay@1.6.0内にあるソースコードを修正していきます。

cowsay では色々なアスキーアートが用意されているようですが、今回はデフォルトのアスキーアートを使用しているので、対象ファイルは ./node_modules/.pnpm_patches/cowsay@1.6.0/cows/default.cowになります。

cat ./node_modules/.pnpm_patches/cowsay@1.6.0/cows/default.cow
$the_cow = <<"EOC";
        $thoughts   ^__^
         $thoughts  ($eyes)\\_______
            (__)\\       )\\/\\
             $tongue ||----w |
                ||     ||
EOC

ここで牛の耳を^から@に変えてみましょう

  $the_cow = <<"EOC";
-         $thoughts   ^__^
+         $thoughts   @__@
           $thoughts  ($eyes)\\_______
              (__)\\       )\\/\\
               $tongue ||----w |
                  ||     ||
  EOC

保存して終了したら patch コマンドで表示されたコマンドを使用してパッチを適用します。

pnpm patch-commit '/path/to/playground-pnpm-patch/node_modules/.pnpm_patches/cowsay@1.6.0'

上記コマンドを実行すると以下のファイルの変更とパッチファイルが作成されます。

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git restore ..." to discard changes in working directory)
        modified:   package.json
        modified:   pnpm-lock.yaml

Untracked files:
  (use "git add ..." to include in what will be committed)
        patches/

patches ディレクトリには修正したファイルのgitパッチファイルが作成されています。

cat ./patches/cowsay@1.6.0.patch
diff --git a/cows/default.cow b/cows/default.cow
index 761eafdb47334326c6cd6bc5621a77e278d81eca..e1f0e1240b4372054bd63ccbe2652bf8a5a52909 100644
--- a/cows/default.cow
+++ b/cows/default.cow
@@ -1,5 +1,5 @@
 $the_cow = <<"EOC";
-        $thoughts   ^__^
+        $thoughts   @__@
          $thoughts  ($eyes)\\_______
             (__)\\       )\\/\\
              $tongue ||----w |

動作検証

最初に作成したエントリーファイルの/src/index.jsを再実行して牛のアスキーアートの耳が変更されたか確認します。

 node src/index.js
 _________________
< I'm a moooodule >
 -----------------
        \   @__@
         \  (oO)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||

かわいいですね。

結果として、牛の耳が変更されていることを確認できます。

pnpm 以外の場合

当社ではパッケージマネージャが pnpm でしたので使用しませんでしたが、pnpm 以外では ds300/patch-package: Fix broken node modules instantly といったnpmパッケージがあるようです。

元々はこちらのパッケージを最初に見つけた後に、このパッケージのREADMEの案内でpnpmのpatchコマンドを知ったという経緯です。

pnpm以外のパッケージマネージャをご使用の方は上記のパッケージ経由でnpmパッケージにパッチを簡便に当てることができると思います。

npm パッケージにパッチを当てることへの問題

ここまでパッチを当てる方法についてご紹介してきましたが、パッチを当てなくて済むのが一番よいことです。

npm パッケージにパッチを当てるのは最終手段であり、以下のリスクを伴います

  • 該当のソースコードが更新された時にパッチが壊れてしまう
  • 該当のソースコード以外のファイルでnpmパッケージが修正対応した場合に挙動が不安定になる
  • パッチの存在をチーム内で共有していない場合、トラブルの原因となる

したがって、パッケージ作者に問題を報告し、公式対応を待つのが最善の方法です。しかし、緊急時にはパッチの適用が役立つ場面もあります。

まとめ

pnpmのpatchコマンドを使用してnpmパッケージにパッチを当てる方法についてご紹介しました。

前にも述べたようにパッチを当てるのには相応のリスクが付きものです。さしずめ開発者の最終手段、秘奥義といったところでしょうか。出来ることなら秘奥義は最後まで隠しておきたいものです。

本題とは話が逸れますがnpmパッケージで問題がある際のデバッグがつらすぎました。そしてIMEもよく問題を引き起こしてくれました。

今年の汚れは今年のうちに、つらい思い出も 2024年に置いていって、2025年は明るい未来になって欲しいものです。

この記事のカテゴリ

FOLLOW US

最新の情報をお届けします