メインコンテンツへスキップ

Capacitor プラグインにパッチを当てる方法 (Bun, npm, pnpm)

bun パッチ、patch-package、pnpm patch で Capacitor plugin にパッチを当て、iOS/Android のネイティブ修正を適用する方法。フォークするか upstream に還元するかの判断基準も解説。

ダークブルーの背景に配置された Capgo プラグインのロゴ(Capacitor プラグインのパッチ適用ガイド用)

Capacitor プラグインにパッチを適用するには、プラグインのファイルを編集します node_modules、変更を diff として保存します bun patch --commit (または patch-package 、npm と Yarn を使用します pnpm patch-commit pnpm の場合)、生成されたファイルをコミットします patches/、次に実行 bunx cap sync ネイティブプロジェクトがパッチを適用できるようにします。パッケージマネージャーはインストールのたびにパッチを再適用するため、チーム全体や CI でも有効です。

プラグインに小さなバグがある、ビルドをブロックするバージョン制約がある、またはアップストリームの修正がマージされたがリリースされていない場合に、パッチ適用が適切な対応です。このガイドでは、各パッケージマネージャーのワークフロー、ネイティブパッチが Xcode と Gradle にどのように反映されるか、そしてフォークやアップストリームへの PR がより良い選択となるケースを解説します。

ネイティブプラグインのパッチ適用が有効な理由 code

Capacitor プラグインは、3つの部分からなる npm パッケージです: JavaScript は dist/、Android の code は android/、iOS の code は ios/ 、さらに podspec と/または Package.swift。Capacitor はネイティブ部分を直接コンパイルします node_modules:

  • Androidでは: android/capacitor.settings.gradle は各プラグインの android フォルダを…から含みます node_modules.
  • CocoaPods を使う iOS:Podfile が各プラグインを…で参照します :path => '../../node_modules/...'.
  • SPM を使う iOS: CapApp-SPM/Package.swift ではローカルパスで各プラグインを…内で参照します node_modules.

つまり、…への変更は node_modules/@scope/plugin/ios/Sources/... …と再ビルドの後、アプリのバイナリに反映されます cap sync 公開は不要です。

パッチを当てる前に

  1. リリースがないか確認してください。 プラグインのchangelogとオープン中のPRを確認してください。パッチを当てるよりアップグレードする方が望ましいです。
  2. ライセンスを確認してください。 MITやApache-2.0では非公開の改変が認められています。GPLやMPL-2.0などのコピーレフトライセンスでは、アプリを配布する際に改変したソースの公開を求められることがあります。リリース前に確認してください。
  3. 理由を書き留めておいてください。 コミットメッセージにissueのURLや一行の理由を記載してください。半年後には誰も理由を覚えていません。 patches/ ファイルが入っています。

Bunでのパッチ適用

Bun にはパッチ機能が組み込まれています。追加のパッケージも、 postinstall スクリプトも不要です。

# 1. Prepare the package for editing
bun patch @capacitor/haptics

# 2. Edit files under node_modules/@capacitor/haptics

# 3. Save the patch
bun patch --commit node_modules/@capacitor/haptics

ステップ1で、Bun がパッケージのプライベートコピーを node_modulesに作成します。これにより、編集が Bun のグローバルキャッシュに影響しません。ステップ3では差分を書き出し、 package.json:

{
  "patchedDependencies": {
    "@capacitor/haptics@8.0.2": "patches/@capacitor%2Fhaptics@8.0.2.patch"
  }
}

両方をコミットします package.json および patches/ フォルダ。すべての bun install、以下を含む bun install --frozen-lockfile CI でもパッチを適用します。

キーには正確なバージョンが含まれています。プラグインのバージョンを上げた場合、Bun がパッチと一致しなくなるため、新しいバージョン用に再作成するか、修正がリリース済みであれば削除してください。

npm または Yarn でパッチを適用:patch-package

npm install --save-dev patch-package

他の依存関係用の postinstall インストールのたびにパッチが適用されるようにするスクリプト:

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

次に、次のディレクトリ内のファイルを編集し、 node_modules パッチを生成します:

npx patch-package @capacitor/haptics

これにより、次が書き込まれます: patches/@capacitor+haptics+8.0.2.patch。これをコミットしてください。アップグレード後にパッチが適用されなくなった場合は、 patch-package インストールが明確なメッセージとともに失敗します。

Yarn Berry には独自の yarn patch <pkg> / yarn patch-commit -s <path> パッチを保存するフロー resolutions フィールド。

pnpm でのパッチ適用

pnpm patch @capacitor/haptics
# edit the temporary folder pnpm prints
pnpm patch-commit <path-printed-above>

pnpm はパッチを以下に記録します patchedDependencies インストール時に適用されます。

例: プラグインのネイティブビルドエラーを修正する

よくあるケース: アプリが AGP 9 を使用しているが、古いプラグインの android/build.gradle がまだ proguard-android.txtを参照しており、AGP 9 によって拒否されます。リリースを待てません。

bun patch some-capacitor-plugin

編集 node_modules/some-capacitor-plugin/android/build.gradle:

 buildTypes {
     release {
         minifyEnabled false
-        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
     }
 }

保存と同期:

bun patch --commit node_modules/some-capacitor-plugin
bunx cap sync android
cd android && ./gradlew assembleDebug

このエラーの詳細についてはこちら: AGP 9 で Capacitor プラグインのビルドエラーを修正する.

例: iOS の依存関係の制約を緩和する

2つのプラグインが同じ SDK の異なるバージョンを固定していて、CocoaPods がそれらを解決できません。新しい SDK でプラグインが動作することを確認済みなら、その podspec の制約を緩和してください:

-  s.dependency 'FirebaseMessaging', '11.15.0'
+  s.dependency 'FirebaseMessaging', '>= 11.15.0', '< 13.0'

SPM アプリの場合、同じ制約はプラグインの Package.swift:

-  .package(url: "https://github.com/firebase/firebase-ios-sdk.git", exact: "11.15.0")
+  .package(url: "https://github.com/firebase/firebase-ios-sdk.git", "11.15.0"..<"13.0.0")

次に:

bunx cap sync ios

CocoaPods の場合は、以下が必要になることがあります: cd ios/App && pod update FirebaseMessaging 一度だけ実行してください。なぜなら、 Podfile.lock まだ古いバージョンを保持しているからです。SPM の場合は、 File > Packages > Resolve Package Versions を Xcode で選択してください。

Capacitor コア本体のパッチ適用

バグがプラグイン側に存在する場合 @capacitor/android, @capacitor/ios または CLI で、修正が upstream のプルリクエストにある場合です。これらのパッケージにも同じようにパッチを当てられますが、結局どのチームも同じ差分をメンテナンスし続けることになります。

Capgo が公開しています @capgo/capacitor-patch これに対応しています。フック専用のパッケージで、バージョンを限定した小さなパッチのカタログがあり、各パッチは upstream の PR にリンクしています。オプトインするまで何も行いません:

bun add @capgo/capacitor-patch
bunx capgo-capacitor-patch list --all
import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  plugins: {
    CapacitorPatch: {
      patches: ['upstream-pr-8418-android'],
      strict: true,
    },
  },
};

export default config;

パッケージパッチは先に実行されます。 cap sync と cap update、ネイティブプロジェクトのパッチはその後実行されます。 bunx capgo-capacitor-patch doctor でドライランを実行します。 strict: trueでは、選択したパッチが適用できなくなると同期が失敗するので、アップグレードでそのパッチが不要になったことが分かります。詳細は プラグインのドキュメント オプションの全一覧はこちら。

パッチ、フォーク、それともアップストリーム?

状況 最適な選択肢
数行の修正、アップストリームにマージ済み パッチを適用、次のリリース後に削除
バージョン制約が厳しすぎる パッチを適用、アップストリームにIssueを起票
アップストリームで未修正のバグ パッチを適用し、同じ差分でPRを出す
新機能、または大規模なリファクタリング フォークして自分のスコープで公開
プラグインがメンテナンスされていない Forkするか、メンテナンス中のプラグインに切り替える

適切なForkの進め方

Forkする場合は、インストールを再現可能にするため公開しましょう:

# in the fork, rename to your scope in package.json: "@acme/capacitor-foo"
bun run build
bun publish --access restricted

git URL からのインストールも可能ですが、プラグインの dist/ フォルダがそのブランチに存在するか、または prepare スクリプトでビルドされている必要があります。また、すべてのパッケージマネージャーがデフォルトで依存関係のライフサイクルスクリプトを実行するわけではありません。公開済みパッケージならそれを回避できます。

npm の名前を変更すると、CLI が SPM 用に生成するネイティブモジュール名も変わる点に注意してください(@acme/capacitor-foo に変わります AcmeCapacitorFoo)、Fork先の Package.swift を一致させてください。詳細は Capacitor プラグインを SPM に移行する.

機能不足でプラグインをForkする前に、次を確認してください: Capgo プラグインディレクトリ。すでにそれをカバーするメンテナンスされているプラグインが存在する可能性があります。

アップストリームへ反映する

目標はパッチの削除です。最小限の再現手順、Capacitor とプラグインのバージョン、パッチの差分を添えて Issue を作成し、その後 PR を開きます。Issue 報告のみよりも、小さくテスト済みの修正を含む PR の方が、メンテナーがマージする速度ははるかに速くなります。

パッチとライブアップデート

Web バンドルには、 dist/からのプラグインの JavaScript が含まれているため、JS 側へのパッチは次の Web ビルドとともに配信されます。 Capgo Live Updates を使うことで、ストアリリースなしでユーザーに届きます。

ネイティブのパッチは異なります。Swift、Kotlin、Java、Gradle、podspec または Package.swift への変更は、ストアに提出した新しいネイティブビルドでのみ有効になります。まだ古いバイナリを実行しているユーザーに、ネイティブパッチに依存する Web バンドルをプッシュしないでください。Capgo の 互換性チェック bundle と channel 間で native plugin のバージョンを比較し、これを検出します。

トラブルシューティング

CI で patch が適用されていません。 必ず patches/ コミットされ、無視されていないこと。patch-package の場合は、CI が使用していないことを確認してください。 --ignore-scripts、これによりスキップされます postinstallBun の場合は、確認してください。 patchedDependencies がコミットされたファイルに含まれています package.json.

native の変更が反映されません。 ファイルを編集しましたが、再ビルドしていません。実行してください: bunx cap sync、その後 Xcode で clean: Product > Clean Build Folder, Androidの場合 ./gradlew clean. SPMを使用している場合は、パッケージキャッシュもリセットしてください。

bun patch --commit 変更がないと表示されます。 パッケージに含まれていないファイル(別のフォルダにあるhoistされたコピーなど)を編集しています。ステップ1で表示されたパス Bun を編集してください。

プラグインのアップグレード後にパッチが失敗します。 新しいリリースに修正が含まれているか確認してください。含まれていない場合は、新しいバージョンに対して再度パッチを適用してください。

プラグインはビルドできますが、その機能を使用するとアプリがクラッシュします。 パッチを適用するとプラグイン独自のテストスイートはスキップされます。両方のプラットフォームの実機で、パッチを適用したパスをテストしてください。 Capacitor iOSトラブルシューティングガイド と Androidトラブルシューティングガイド では、ネイティブログの読み方について説明しています。

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。