5日目: patch-package を探訪

Yarn v2 or v3 では yarn patch サブコマンドでパッチを当てられるわけだが、Yarn v1 ではどのようにパッチを当てるのがよいかを調べていたところ、以下の記事で ds300/patch-package というパッケージを知ったので探訪してみた。

https://dev.blog.n.inc/2021-06-21-patch-package/

素振りとしては、昨日のスクリプト同様に、is-odd パッケージにパッチを当ててみる。

$ mkdir patch-package-sandbox && cd $_
$ yarn init -y
$ echo node_modules > .gitignore
$ yarn add is-odd patch-package postinstall-postinstall

scripts の設定を追加。

 {
   "name": "patch-package-sandbox",
   "version": "1.0.0",
   "main": "index.js",
   "license": "MIT",
+  "scripts": {
+    "postinstall": "patch-package"
+  },
   "dependencies": {
     "is-odd": "^3.0.1",
     "patch-package": "^8.0.0",
     "postinstall-postinstall": "^2.1.0"
   }
 }

main.mjs を追加。

import isOdd from 'is-odd';

console.log(isOdd('1'));

node_modules にある is-odd のソースコードを修正する。

  module.exports = function isOdd(value) {
+   if (typeof value !== 'number') {
+     throw new TypeError('expected a number');
+   }
    const n = Math.abs(value);
    if (!isNumber(n)) {
      throw new TypeError('expected a number');
    }

その後 yarn patch-package is-odd を実行すると patches/is-odd+3.0.1.patch が作成される。

diff --git a/node_modules/is-odd/index.js b/node_modules/is-odd/index.js
index 79d1f22..af66fef 100644
--- a/node_modules/is-odd/index.js
+++ b/node_modules/is-odd/index.js
@@ -10,6 +10,9 @@
 const isNumber = require('is-number');
 
 module.exports = function isOdd(value) {
+  if (typeof value !== 'number') {
+    throw new TypeError('expected a number');
+  }
   const n = Math.abs(value);
   if (!isNumber(n)) {
     throw new TypeError('expected a number');

これでパッチが適用された。スクリプトを実行してみる。

/path/to/patch-package-sandbox/node_modules/is-odd/index.js:14
    throw new TypeError('expected a number');
          ^

TypeError: expected a number
    at isOdd (/path/to/patch-package-sandbox/node_modules/is-odd/index.js:14:11)
    at file:///path/to/patch-package-sandbox/main.mjs:3:13
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

Yarn v2以降であれば、 yarn patch を使えばいいのだろうが、未だに Yarn v1 を使っているケースは多いと思うので、 その場合のパッチの当て方を試せたのでよかった。

とはいえそこまで使う機会はなさそうではある。ちなみに postinstall-postinstall の存在理由はREADMEによると...

Yarnは、yarn、yarn install、yarn add の後にポストインストールスクリプトを実行しますが、yarn remove の後には実行しません。このパッケージをプロジェクトに追加すると、yarn remove の後でもプロジェクトのpostinstallフックが実行されます。これは、yarn、yarn install、yarn add のために2回実行されるため、ポストインストールスクリプトがidempotentである必要があります。

とのこと。