Per patchare un plugin Capacitor, modifica i file del plugin in node_modules, salva la modifica come diff con bun patch --commit (o patch-package su npm e Yarn, pnpm patch-commit su pnpm), fai il commit del file generato in patches/, quindi esegui bunx cap sync così i progetti nativi lo riconoscono. Il package manager riapplica la patch a ogni installazione, per il tuo team e in CI.
Il patching è la scelta giusta quando un plugin ha un piccolo bug, un vincolo di versione che blocca la build o una correzione upstream già mergiata ma non ancora rilasciata. Questa guida copre il workflow per ogni package manager, come le patch native arrivano a Xcode e Gradle e quando conviene invece un fork o una PR upstream.
Perché il patching funziona per i plugin nativi code
Un plugin Capacitor è un pacchetto npm composto da tre parti: JavaScript in dist/, Android code in android/, e iOS code in ios/ più un podspec e/o Package.swift. Capacitor compila le parti native direttamente da node_modules:
- Android:
android/capacitor.settings.gradleinclude quello di ogni pluginandroidcartella dinode_modules. - iOS con CocoaPods: il Podfile fa riferimento a ogni plugin con
:path => '../../node_modules/...'. - iOS con SPM:
CapApp-SPM/Package.swiftfa riferimento a ogni plugin tramite percorso locale innode_modules.
Quindi una modifica a node_modules/@scope/plugin/ios/Sources/... finisce nel binario della tua app dopo cap sync e una nuova build. Non serve pubblicare nulla.
Prima di applicare la patch a
- Controlla se c'è una release. Controlla il changelog e le PR aperte del plugin. Aggiornare è meglio che applicare una patch.
- Leggi la licenza. MIT e Apache-2.0 permettono modifiche private. Licenze copyleft come GPL o MPL-2.0 potrebbero obbligarti a pubblicare il codice modificato quando distribuisci l'app. Verifica prima del rilascio.
- Annota il motivo. Inserisci l'URL della issue o una motivazione in una riga nel commit message. Sei mesi dopo nessuno ricorda perché
patches/contiene un file.
Applicare patch con Bun
Bun ha il patching integrato. Nessun pacchetto extra, nessuno 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
Il passaggio 1 permette a Bun di fornirti una copia privata del pacchetto in node_modules, così le modifiche non toccano la cache globale di Bun. Il passaggio 3 scrive un diff e lo registra in package.json:
{
"patchedDependencies": {
"@capacitor/haptics@8.0.2": "patches/@capacitor%2Fhaptics@8.0.2.patch"
}
}
Commit di entrambi package.json e il patches/ cartella. Ogni bun install, tra cui bun install --frozen-lockfile in CI, applica la patch.
La chiave contiene la versione esatta. Se aggiorni il plugin, Bun non corrisponderà più al patch, quindi ricreala per la nuova versione o eliminala se la fix è stata rilasciata.
Patching con npm o Yarn: patch-package
npm install --save-dev patch-package
Aggiungi un postinstall script so patches apply after every install:
{
"scripts": {
"postinstall": "patch-package"
}
}
Poi modifica i file in node_modules e genera la patch:
npx patch-package @capacitor/haptics
Questo scrive patches/@capacitor+haptics+8.0.2.patch. Commit. Se la patch smette di applicarsi dopo un upgrade, patch-package fallisce l'installazione con un messaggio chiaro.
Yarn Berry ha il suo yarn patch <pkg> / yarn patch-commit -s <path> flusso che salva la patch nel resolutions campo.
Applicare patch con pnpm
pnpm patch @capacitor/haptics
# edit the temporary folder pnpm prints
pnpm patch-commit <path-printed-above>
pnpm registra la patch sotto patchedDependencies e la applica all'installazione.
Esempio: correggere un errore di build nativa in un plugin
Un caso comune: la tua app usa AGP 9, e un plugin più vecchio android/build.gradle fa ancora riferimento a proguard-android.txt, che AGP 9 rifiuta. Non puoi aspettare un rilascio.
bun patch some-capacitor-plugin
Modifica 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'
}
}
Salva e sincronizza:
bun patch --commit node_modules/some-capacitor-plugin
bunx cap sync android
cd android && ./gradlew assembleDebug
Il contesto completo di questo errore è in risolvere gli errori di build del plugin Capacitor con AGP 9.
Esempio: allentare un vincolo di dipendenza su iOS
Due plugin fissano versioni diverse dello stesso SDK e CocoaPods non riesce a risolverle. Se hai verificato che il plugin funzioni con la SDK più recente, allenta il suo podspec:
- s.dependency 'FirebaseMessaging', '11.15.0'
+ s.dependency 'FirebaseMessaging', '>= 11.15.0', '< 13.0'
Per le app con SPM, lo stesso vincolo si trova nel 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")
Poi:
bunx cap sync ios
Con CocoaPods potresti dover cd ios/App && pod update FirebaseMessaging una volta, perché Podfile.lock mantiene ancora la vecchia versione. Con SPM, usa File > Pacchetti > Risolvi versioni pacchetti in Xcode.
Patchare il core di Capacitor
A volte il bug è in @capacitor/android, @capacitor/ios o nel CLI, e il fix sta in una pull request upstream. Puoi patchare quei package allo stesso modo, ma ogni team finisce per mantenere gli stessi diff.
Capgo pubblica @capgo/capacitor-patch per questo. È un package di soli hook, con un catalogo di piccole patch vincolate alla versione che rimandano alle rispettive PR upstream. Non fa nulla finché non fai opt-in:
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;
Le patch dei pacchetti vengono eseguite prima cap sync e cap update, e le patch del progetto nativo vengono eseguite dopo. bunx capgo-capacitor-patch doctor fa una dry run. Con strict: true, la sincronizzazione fallisce quando una patch selezionata non è più applicabile, così sai quando un upgrade l'ha resa obsoleta. Vedi la docs del plugin per l'elenco completo delle opzioni.
Patch, fork o upstream?
| Situazione | Opzione migliore |
|---|---|
| Poche righe, fix già mergeato upstream | Patch, da rimuovere dopo la prossima release |
| Vincolo di versione troppo restrittivo | Patch, apri una issue upstream |
| Bug senza fix upstream | Applica la patch e invia una PR con lo stesso diff |
| Nuova feature o refactor importante | Fork e pubblica sotto il tuo scope |
| Plugin abbandonato | Fai un fork o passa a un plugin mantenuto |
Fork fatti bene
Se fai un fork, pubblicalo così le installazioni restano riproducibili:
# in the fork, rename to your scope in package.json: "@acme/capacitor-foo"
bun run build
bun publish --access restricted
Funziona anche l'installazione da un URL git, ma la cartella dist/ del plugin deve esistere in quel branch o essere generata da uno prepare script; non tutti i package manager eseguono di default gli script di lifecycle delle dipendenze. Un pacchetto pubblicato evita tutto questo.
Ricorda che il nome npm cambia il nome del modulo nativo che CLI genera per SPM (@acme/capacitor-foo diventa AcmeCapacitorFoo), quindi aggiorna il Package.swift per allinearlo. Trovi i dettagli in migrare un plugin Capacitor a SPM.
Prima di fare il fork di un plugin per una feature mancante, controlla directory dei plugin di Capgo. Potrebbe già esistere un plugin mantenuto che lo copre.
Upstreaming
L'obiettivo è eliminare il tuo patch. Apri un issue con un minimo repro, le versioni di Capacitor e del plugin, e la diff del patch. Poi apri un PR. I maintainer mergano piccole fix testate molto più velocemente rispetto a semplici issue report.
Patch e live updates
Il tuo web bundle include il JavaScript del plugin da dist/, quindi un patch al lato JS viene rilasciato con la prossima web build. Con Live updates di Capgo che raggiunge gli utenti senza un rilascio sullo store.
I patch nativi sono diversi. Le modifiche a Swift, Kotlin, Java, Gradle, podspec o Package.swift hanno effetto solo in un nuovo native build inviato agli store. Non pubblicare un web bundle che dipende da un patch nativo per gli utenti che eseguono ancora il vecchio binario. Capgo controllo di compatibilità confronta le versioni dei plugin nativi tra un bundle e un channel per intercettare questo problema.
Risoluzione problemi
Il patch non viene applicato in CI. Assicurati che patches/ è committato e non ignorato. Per patch-package, verifica che la CI non usi --ignore-scripts, che salta postinstall. Per Bun, verifica patchedDependencies is in the committed package.json.
La mia modifica nativa non ha effetto. Hai modificato il file ma non hai rifatto il build. Esegui bunx cap sync, poi pulisci: in Xcode Prodotto > Pulisci cartella di build, su Android ./gradlew clean. Con SPM, resetta anche le cache dei pacchetti.
bun patch --commit dice che non ci sono modifiche. Hai modificato un file che non fa parte del pacchetto (ad esempio una copia hoisted in un'altra cartella). Modifica il percorso Bun stampato al passaggio 1.
La patch fallisce dopo l'aggiornamento del plugin. Controlla se il fix è presente nella nuova release. In caso contrario, rifai la patch sulla nuova versione.
Il plugin compila, ma l'app crasha quando usi quella feature. Le patch saltano la suite di test del plugin. Testa il percorso patchato su un dispositivo reale su entrambe le piattaforme. La Guida alla risoluzione problemi iOS di Capacitor e Guida alla risoluzione dei problemi Android spiegano come leggere i log nativi.