如何“优雅”地修改 node_modules 下的代码?

直接修改 node_modules 下的代码不是被推荐的做法,应该仅在应急情况下考虑

前言

在实际开发过程中当我们遇到 node_modules 中的 A 包有 bug 时候,通常开发者有几个选择:

方法一:给 A 包提 issue 等待他人修复并发布:做好石沉大海或修复周期很长的准备。

方法二:给 A 包提 mr 自行修复并等待发布:很棒,不过你最好祈祷作者发版积极,并且新版本向下兼容。

方法三:把 A 包的源码拖出来自己维护:有点暴力且事后维护成本较高,不过应急时也能勉强接受。

等等,可如果出问题的包是“幽灵依赖”呢,比如项目的依赖链是: A -> B -> C,此时 C 包有 bug。那么上面三个方法的改动需要同时影响到 A、B、C 三个包,修复周期可能就更长了,可是你今晚就要上线啊,这可怎么办?

1

上线要紧,直接手动修改 node_modules 下的代码给缺陷包打个临时补丁吧,可问题又来了,改动只能在本地生效,构建却在云端, 积极的同学开始写起了脚本,然后陷入一个个坑里...

上述场景下即可考虑使用 patch-package 这个包,假设我们现在的源码结构如下所示:

├── node_modules  
│    └── lodash         
│        └── toString.js 
├── src                     
│    └── app.js // 依赖 lodash 的 toString 方法
└── package.json

node_modules/lodash/toString.js

var baseToString = require('./_baseToString')

function toString(value) {
  return value == null ? '' : baseToString(value);
}

module.exports = toString;

src/app.js

const toString = require('lodash/toString')
console.log(toString(123)); 

假设现在需要修改 node_modules/lodash/toString.js 文件,只需要遵循以下几步即可“优雅”完成修改:

实操

安装依赖

yarn add patch-package postinstall-postinstall -D

修改 toString.js 文件

function toString(value) {
	console.log('it works!!!'); // 这里插入一行代码
  return value == null ? '' : baseToString(value);
}

module.exports = toString;

生成修改文件

npx patch-package lodash

这一步运行后会生成 patches/lodash+4.17.21.patch,目录结构变成下面这样:

├── node_modules  
│    └── lodash         
│        └── toString.js 
├── patches                     
│    └── lodash+4.17.21.patch
├── src                     
│    └── app.js
└── package.json

其中 .patch 文件内容如下:

diff --git a/node_modules/lodash/toString.js b/node_modules/lodash/toString.js
index daaf681..8308e76 100644
--- a/node_modules/lodash/toString.js
+++ b/node_modules/lodash/toString.js
@@ -22,6 +22,7 @@ var baseToString = require('./_baseToString');
  * // => '1,2,3'
  */
 function toString(value) {
+  console.log('it works!!!');
   return value == null ? '' : baseToString(value);
 }

第四步:修改 package.json 文件

"scripts": {
+  "postinstall": "patch-package"
}

最后重装一下依赖,测试最终效果:

rm -rf node_modules
yarn
node ./src/app.js

// it works!!!
// 123

可以看到,即便重装依赖,我们对 node_modules 下代码的修改还是被 patch-package 还原并最终生效。

至此我们便完成一次临时打补丁的操作,不过这并非真正优雅的长久之计,长期看还是需要彻底修复第三方包缺陷并逐步移除项目中的 .patch 文件。