2018年12月4日

プログラミング

かゆいところに手が届く!patch-packageでnpmパッケージを乗りこなそう

かゆいところに手が届く!patch-packageでnpmパッケージを乗りこなそうかゆいところに手が届く!patch-packageでnpmパッケージを乗りこなそう

目次

  1. はじめに
  2. npmパッケージを修正するには
  3. patch-packageの導入
  4. パッチの作り方
  5. まとめ

はじめに

npmパッケージを利用する際、バージョンの違いなどで動かなかったり、少しだけカスタマイズしたいといったような場面に遭遇することはないでしょうか?そんなときに使える patch-package というツールをご紹介します。

npmパッケージを修正するには

npm install によって追加されたパッケージはプロジェクトのnode_modulesというディレクトリ内に保存されます。
このnode_modules内のファイルを変更してしまえばnpmパッケージを修正できるのですが、いくつか方法があります。

node_modules内を直接修正する方法

node_modules内のファイルは一般的にはJavaScriptですので、そのファイルを直接変更してしまうことが可能です。
しかし、node_modules内のファイルは npm install 時などに上書きされる可能性がありますし、.gitignoreとしてGitの管理から除外することが一般的ですので、その都度修正する必要があり実用的ではありません。

パッケージをForkする方法

npm install で依存関係を追加する際、npmのパッケージ名の他にGitリポジトリのURLやGitHubのプロジェクト名を指定することが可能です。
これを利用して、ライブラリのGitプロジェクトをForkして修正し、依存関係にはそちらのForkしたプロジェクトを追加するという方法があります。
こちらの方法は広く使われているのですが、ちょっとした変更であってもForkしたGitプロジェクトを作成する必要があったり、元となるライブラリの更新に追従していくのが大変だったりと、実際やってみると大変なことが多いです。

postinstallで修正する方法

npmには、package.jsonのscriptsに postinstall という名前のスクリプトを追加すると、 npm install 後に任意のコマンドを実行できるという機能があります。(npmの公式ドキュメント
これを利用して、 npm install 後にnode_modules内のファイルが自動的に修正されるような仕組みになれば良さそうなのですが、ファイル内の特定の部分を修正するスクリプトを自分で書く必要が出てきます。

patch-packageは、複雑なシェルスクリプトを書くことなくpostinstallでパッチを当てることができるツールです。

patch-packageの導入

patch-packageは開発時に使われるツールですので、devDependenciesとして依存関係に追加します。

npmパッケージの使い方については以前の記事も参考にしてみてください。

npm install patch-package --save-dev

その後、package.jsonの scriptsに "postinstall": "patch-package" を追加します。

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

これで npm install 後にpatch-packageが実行されます。

npmコマンドではなくyarnを使う場合、postinstall-postinstallというパッケージも追加することが推奨されています。詳しくはpatch-packageのREADMEを確認してください。

パッチの作り方

例として react-native-parallax-view をみてみましょう。このパッケージはReact NativeのScrollViewにパララックス(視差効果)を追加するものですが、React Nativeのバージョン0.8.0がのころにメンテナンスされていたライブラリで、最新のReact Nativeで動かすにはいくつかの問題点があります。

  • 問題点1: React.PropTypesという廃止された機能を使っている
  • 問題点2: コンポーネントの定義にReact.createClassという廃止された方法を使っている

実際に npm install react-native-parallax-view で依存関係に追加し、 import ParallaxView from 'react-native-parallax-view' という記述をコードに追加すると以下のようなエラーが発生します。

undefined is not an object (evaluating 'React.PropTypes.number')

問題点1のPropTypesというのはFlowやTypeScriptを使わないときにコンポーネントのpropsの型チェックを動的に行うための機能ですが、動作には関係のないところですのでPropTypesを使っているところを全て削除してしまいましょう。

問題点2については、現在の書き方に従うならReact.Componentを継承したクラスに書き換えたいところですが、書き換え箇所が多くなってしまいます。今回は、 create-react-class という互換用のパッケージを使いましょう。

// create-react-classを読み込むために追加
var createReactClass = require('create-react-class'); 

// React.createClassから書き換え
var ParallaxView = createReactClass({

これらの修正を node_modules/react-native-parallax-view/lib/ParallaxView.js に対して直接加えると、エラーが発生しなくなるはずです。(今回は例ですので、実際にParallaxViewを使うのは割愛します)
修正が終わったら、 patch-package <パッチを作りたいパッケージ名> という形式でpatch-packageを実行します。

npx patch-package react-native-parallax-view

正しく実行されていれば、プロジェクト内に以下のような内容の patches/react-native-parallax-view+2.0.6.patch というパッチファイルが作成されていると思います。

patch-package
--- a/node_modules/react-native-parallax-view/lib/ParallaxView.js
+++ b/node_modules/react-native-parallax-view/lib/ParallaxView.js
@@ -9,32 +9,17 @@ var {
     ScrollView,
     Animated,
     } = ReactNative;
+var createReactClass = require('create-react-class');
 /**
  * BlurView temporarily removed until semver stuff is set up properly
  */
 //var BlurView /* = require('react-native-blur').BlurView */;
 var ScrollableMixin = require('react-native-scrollable-mixin');
 var screen = Dimensions.get('window');
-var ScrollViewPropTypes = ScrollView.propTypes;

-var ParallaxView = React.createClass({
+var ParallaxView = createReactClass({
     mixins: [ScrollableMixin],

-    propTypes: {
-        ...ScrollViewPropTypes,
-        windowHeight: React.PropTypes.number,
-        backgroundSource: React.PropTypes.oneOfType([
-          React.PropTypes.shape({
-            uri: React.PropTypes.string,
-          }),
-          // Opaque type returned by require('./image.jpg')
-          React.PropTypes.number,
-        ]),
-        header: React.PropTypes.node,
-        blur: React.PropTypes.string,
-        contentInset: React.PropTypes.object,
-    },
-
     getDefaultProps: function () {
         return {
             windowHeight: 300,

patch-packageを導入していると、このpatchesというディレクトリに作成されたパッチがpostinstallで適用されます。そのためpatchesディレクトリをGitで管理することで、他の開発メンバーが npm install したときにもnode_modules内が修正されます。
パッチファイルのファイル名に注目すると、パッケージ名の後にバージョンが入っており、node_modules内のパッケージが該当のバージョンのときのみ適用されます。パッケージのバージョンアップをするごとにパッチの見直しをして、不要になったらパッチを使わないようにしましょう。

まとめ

patch-packageを使うことによって簡単にnpmパッケージを修正することができました。
特にReact Native向けのライブラリではnode_modules内で管理されているネイティブモジュールのコードに対してもパッチを当てることが可能ですので、使いたくなる場面が多くなります。
npmライブラリをそのまま使えないという状況自体が好ましくはないため、あくまで一時的な手段としてとらえるとしてとらえるのがいいでしょう。ですが、どんな状況でも逃げ道があるということを知っておくと安心です。

2

0

AUTHOR

しんのき エンジニア

新しい技術が好きなWebエンジニアです。 元々インフラやPHPをやっていたのですが、最近はReact NativeとFirebaseを使って頑張ってます。

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にeishis, Inc.を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

しまだ

しまだ デザイナー

WebAR/VRのデザインと3DCG制作がメインです。 肩書きは「アニメ案件に関わりたいデザイナー」。

Miu マーケター

ドイツでWEBマーケティングしています。

しんのき エンジニア

新しい技術が好きなWebエンジニアです。 元々インフラやPHPをやっていたのですが、最近はReact NativeとFirebaseを使って頑張ってます。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザイン・HTML/CSSマークアップ・JSアニメーション実装を担当しています。

furuya エンジニア

サーバーサイド、フロントエンド、Unityと色々手を出してる雑食系エンジニア。ReactNativeが最近のマイブーム。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

うえまつゆい エンジニア

サーバーサイドエンジニアからフロントエンドエンジニアになりました。主にReact Nativeでのアプリ開発をしています。

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

CONTACT PAGE TOP