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.gradleinclut pour chaque plugin leandroiddossier à partir denode_modules. - iOS avec CocoaPods: le Podfile fait référence à chaque plugin avec
:path => '../../node_modules/...'. - iOS avec SPM:
CapApp-SPM/Package.swiftfait référence à chaque plugin par un chemin local dansnode_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
- Vérifier la présence d’une release. Consultez le changelog et les PR ouvertes du plugin. Mieux vaut mettre à jour que patcher.
- 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.
- 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.