Aller au contenu principal

Comment patcher un plugin Capacitor (Bun, npm, pnpm)

Comment patcher un plugin Capacitor avec bun patch, patch-package ou pnpm patch, appliquer des correctifs natifs iOS et Android, et savoir quand forker ou remonter en amont.

Logo des plugins Capgo sur un fond bleu foncé pour le guide sur la correction des plugins Capacitor

Pour corriger un plugin Capacitor, modifiez les fichiers du plugin dans node_modules, enregistrez la modification sous forme de diff avec bun patch --commit (ou patch-package sur npm et Yarn, pnpm patch-commit sur pnpm), committez le fichier généré dans patches/, puis exécuter bunx cap sync pour que les projets natifs l’appliquent. Le gestionnaire de paquets réapplique le patch à chaque installation, pour votre équipe et en CI.

Le patching est la bonne approche lorsqu’un plugin présente un petit bug, une contrainte de version qui bloque votre build, ou un correctif en amont fusionné mais non publié. Ce guide détaille le workflow pour chaque gestionnaire de paquets, la manière dont les patches natifs atteignent Xcode et Gradle, et quand un fork ou une PR en amont est plus judicieux.

Pourquoi le patching fonctionne pour le plugin natif code

Un plugin Capacitor est un package npm composé de trois parties : du JavaScript dans dist/, du code Android dans android/, et du code iOS dans ios/ ainsi qu'un podspec et/ou Package.swift. Capacitor compile les parties natives directement depuis node_modules:

  • Android: android/capacitor.settings.gradle inclut pour chaque plugin le android dossier à partir de node_modules.
  • iOS avec CocoaPods: le Podfile fait référence à chaque plugin avec :path => '../../node_modules/...'.
  • iOS avec SPM: CapApp-SPM/Package.swift fait référence à chaque plugin par un chemin local dans node_modules.

Ainsi, un changement dans node_modules/@scope/plugin/ios/Sources/... se retrouve dans le binaire de votre application après cap sync et un rebuild. Rien n’a besoin d’être publié.

Avant de patcher

  1. Vérifier la présence d’une release. Consultez le changelog et les PR ouvertes du plugin. Mieux vaut mettre à jour que patcher.
  2. Lisez la licence. Les licences MIT et Apache-2.0 autorisent les modifications privées. Les licences copyleft comme GPL ou MPL-2.0 peuvent vous obliger à publier le code source modifié quand vous distribuez l'app. Vérifiez avant de déployer.
  3. Notez la raison. Indiquez l'URL du ticket ou une raison concise dans votre message de commit. Six mois plus tard, plus personne ne s'en souvient. patches/ contient un fichier.

Patcher avec Bun

Bun intègre le patching. Pas de package supplémentaire, pas de postinstall script.

# 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

L'étape 1 permet à Bun de vous fournir une copie privée du package dans node_modules, afin que les modifications n'impactent pas le cache global de Bun. L'étape 3 génère un diff et l'enregistre dans package.json:

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

Commitez les deux package.json et le patches/ dossier. Chaque bun install, y compris bun install --frozen-lockfile en CI, applique le patch.

La clé contient la version exacte. Si vous incrémentez la version du plugin, Bun ne correspondra plus au patch ; recréez-la pour la nouvelle version ou supprimez-la si le correctif a été publié.

Patch avec npm ou Yarn : patch-package

npm install --save-dev patch-package

Ajoutez une postinstall script so patches apply after every install:

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

Ensuite, modifiez les fichiers dans node_modules et générez le patch :

npx patch-package @capacitor/haptics

Cela écrit patches/@capacitor+haptics+8.0.2.patch. Commitez-le. Si le patch ne s'applique plus après une mise à jour, patch-package échoue l'installation avec un message clair.

Yarn Berry a son propre yarn patch <pkg> / yarn patch-commit -s <path> flux qui stocke le patch dans le resolutions champ.

Patcher avec pnpm

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

pnpm enregistre le patch sous patchedDependencies et l'applique lors de l'installation.

Exemple : corriger une erreur de build natif dans un plugin

Cas fréquent : votre app utilise AGP 9, et un plugin plus ancien android/build.gradle référence encore proguard-android.txt, ce que AGP 9 rejette. Vous ne pouvez pas attendre une release.

bun patch some-capacitor-plugin

Modifier 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'
     }
 }

Enregistrez et synchronisez :

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

Le contexte complet de cette erreur se trouve dans corriger les erreurs de build du plugin Capacitor avec AGP 9.

Exemple : assouplir une contrainte de dépendance iOS

Deux plugins épinglent des versions différentes du même SDK et CocoaPods ne parvient pas à les résoudre. Si vous avez vérifié que le plugin fonctionne avec la version plus récente de SDK, assouplissez son podspec :

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

Pour les apps SPM, la même contrainte se trouve dans le 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")

Ensuite :

bunx cap sync ios

Avec CocoaPods, vous devrez peut-être cd ios/App && pod update FirebaseMessaging une fois, car Podfile.lock conserve toujours l’ancienne version. Avec SPM, utilisez Fichier > Paquets > Résoudre les versions des paquets dans Xcode.

Patcher le core de Capacitor lui-même

Parfois, le bug se trouve dans @capacitor/android, @capacitor/ios ou dans le CLI, et la correction se trouve dans une pull request en amont. Vous pouvez patcher ces paquets de la même manière, mais chaque équipe finit par maintenir les mêmes diffs.

Capgo publie @capgo/capacitor-patch pour cela. C’est un package uniquement basé sur des hooks, avec un catalogue de petits patches versionnés qui renvoient vers leurs PR en amont. Il ne fait rien tant que vous n’optez pas pour son utilisation :

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;

Les patches de package s'exécutent avant cap sync et cap update, et les patches du projet natif s'appliquent ensuite. bunx capgo-capacitor-patch doctor effectue un dry run. Avec strict: true, la synchro échoue si un patch sélectionné ne s'applique plus, ce qui vous indique qu'une mise à niveau l'a rendu obsolète. Consultez le doc du plugin pour la liste complète des options.

Patcher, fork ou upstream ?

Situation Meilleure option
Quelques lignes, correctif déjà fusionné en amont Patch, à supprimer après la prochaine release
Contrainte de version trop stricte Patch, ouvrir une issue en amont
Bug sans correctif en amont Patch et envoi d'une PR avec le même diff
Nouvelle fonctionnalité ou refactorisation importante Fork, publication sous votre scope
Plugin abandonné Forkez ou passez à un plugin maintenu

Bien gérer ses forks

Si vous forkez, publiez-le pour que les installations restent reproductibles :

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

L'installation depuis une URL git fonctionne aussi, mais le dossier du dist/ plugin doit exister dans cette branche ou être généré par un prepare script, et tous les gestionnaires de paquets n'exécutent pas les scripts de cycle de vie des dépendances par défaut. Un paquet publié permet d'éviter cela.

N'oubliez pas que le nom npm modifie le nom du module natif que CLI génère pour SPM (@acme/capacitor-foo devient AcmeCapacitorFoo), alors mettez à jour le nom Package.swift du fork pour correspondre. Les détails sont dans migrer un plugin Capacitor vers SPM.

Avant de forker un plugin pour une fonctionnalité manquante, vérifiez le répertoire de plugins Capgo. Il existe peut-être déjà un plugin maintenu qui couvre ce besoin.

Faire de l'upstream

L’objectif est de supprimer votre patch. Ouvrez une issue avec un repro minimal, vos versions de Capacitor et du plugin, ainsi que le diff du patch. Puis ouvrez une PR. Les mainteneurs fusionnent les petites corrections testées bien plus vite que de simples rapports d’issue.

Patchs et live updates

Votre bundle web inclut le JavaScript du plugin depuis dist/, donc un patch côté JS est livré avec le prochain build web. Avec les live updates Capgo qui atteint les utilisateurs sans publication sur les stores.

Les patches natifs sont différents. Les modifications de Swift, Kotlin, Java, Gradle, podspec ou Package.swift ne prennent effet que dans un nouveau build natif soumis aux stores. N’envoyez pas un bundle web qui dépend d’un patch natif aux utilisateurs qui exécutent encore l’ancien binaire. Capgo contrôle de compatibilité compare les versions des plugins natifs entre un bundle et un canal pour détecter ce problème.

Troubleshooting

Le patch n’est pas appliqué en CI. Assurez-vous que patches/ est commit et non ignoré. Pour patch-package, vérifiez que la CI n’utilise pas --ignore-scripts, qui saute postinstall. Pour Bun, vérifiez patchedDependencies est dans le commit package.json.

Ma modification native n'a aucun effet. Vous avez modifié le fichier sans recompiler. Exécutez bunx cap sync, puis nettoyez : dans Xcode Produit > Nettoyer le dossier de build, sur Android : ./gradlew clean. Avec SPM, réinitialisez aussi les caches de paquets.

bun patch --commit indique qu'il n'y a aucune modification. Vous avez modifié un fichier qui ne fait pas partie du package (par exemple une copie hoistée dans un autre dossier). Modifiez le chemin Bun affiché à l'étape 1.

Le patch échoue après la mise à niveau du plugin. Vérifiez si le correctif est inclus dans la nouvelle release. Sinon, refaites le patch sur la nouvelle version.

Le plugin compile, mais l'application plante sur cette fonctionnalité. Les patches contournent la suite de tests du plugin. Testez le chemin patché sur un appareil réel sur les deux plateformes. Le guide de dépannage iOS de Capacitor et guide de dépannage Android explique comment lire les logs natifs.

Live updates pour les apps Capacitor

Lorsqu'un bug de la couche web est en ligne, déployez le correctif via Capgo au lieu d'attendre des jours l'approbation des stores. Les utilisateurs reçoivent la mise à jour en arrière-plan, tandis que les changements natifs suivent le circuit de validation habituel.

Les derniers articles du blog

Capgo vous offre les meilleures informations pour créer une application mobile vraiment professionnelle.