Para parchear un plugin de Capacitor, edita los archivos del plugin en node_modules, guarda el cambio como un diff con bun patch --commit (o patch-package en npm y Yarn, pnpm patch-commit en pnpm), haz commit del archivo generado en patches/, luego ejecuta bunx cap sync para que los proyectos nativos lo incluyan. El gestor de paquetes vuelve a aplicar el parche en cada instalación, para tu equipo y en CI.
Parchear es la opción correcta cuando un plugin tiene un bug pequeño, una restricción de versión que bloquea tu build, o un fix upstream ya mergeado pero sin publicar. Esta guía cubre el flujo de trabajo para cada gestor de paquetes, cómo llegan los parches nativos a Xcode y Gradle, y cuándo conviene más hacer un fork o enviar un PR upstream.
Por qué parchear funciona con los plugins nativos de code
Un plugin de Capacitor es un paquete de npm con tres partes: JavaScript en dist/, code de Android en android/, y code de iOS en ios/ más un podspec y/o Package.swift. Capacitor compila las partes nativas directamente desde node_modules:
- Android:
android/capacitor.settings.gradleincluye la carpetaandroidcarpeta denode_modules. - iOS con CocoaPods: el Podfile referencia cada plugin con
:path => '../../node_modules/...'. - iOS con SPM:
CapApp-SPM/Package.swiftreferencia cada plugin por ruta local ennode_modules.
Así que un cambio en node_modules/@scope/plugin/ios/Sources/... termina en el binario de tu app después de cap sync y un rebuild. No hay que publicar nada.
Antes de aplicar el parche
- Revisa si hay un release. Mira el changelog del plugin y los PR abiertos. Actualizar es mejor que parchear.
- Lee la licencia. MIT y Apache-2.0 permiten modificaciones privadas. Licencias copyleft como GPL o MPL-2.0 pueden obligarte a publicar el código modificado si distribuyes la app. Verifica antes de lanzar.
- Anota el porqué. Pon la URL del issue o una razón de una línea en tu commit message. En seis meses nadie recordará por qué
patches/tiene un archivo dentro.
Aplicar parches con Bun
Bun tiene parcheo integrado. Sin paquetes extra, sin 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
El paso 1 hace que Bun te dé una copia privada del paquete en node_modules, para que las ediciones no toquen la caché global de Bun. El paso 3 escribe un diff y lo registra en package.json:
{
"patchedDependencies": {
"@capacitor/haptics@8.0.2": "patches/@capacitor%2Fhaptics@8.0.2.patch"
}
}
Haz commit de ambos package.json y la patches/ carpeta. Cada bun install, incluyendo bun install --frozen-lockfile en CI aplica el parche.
La clave contiene la versión exacta. Si actualizas el plugin, Bun ya no coincide con el parche, así que créalo de nuevo para la nueva versión o elimínalo si la corrección ya se lanzó.
Aplicar parches con npm o Yarn: patch-package
npm install --save-dev patch-package
Añade una postinstall script para que los parches se apliquen tras cada instalación:
{
"scripts": {
"postinstall": "patch-package"
}
}
Luego edita los archivos en node_modules y genera el parche:
npx patch-package @capacitor/haptics
Esto escribe patches/@capacitor+haptics+8.0.2.patch. Haz commit. Si el parche deja de aplicarse tras una actualización, patch-package hace fallar la instalación con un mensaje claro.
Yarn Berry tiene su propio yarn patch <pkg> / yarn patch-commit -s <path> flujo que guarda el parche en el resolutions campo.
Aplicar parches con pnpm
pnpm patch @capacitor/haptics
# edit the temporary folder pnpm prints
pnpm patch-commit <path-printed-above>
pnpm registra el parche bajo patchedDependencies y lo aplica durante la instalación.
Ejemplo: corregir un error de build nativo en un plugin
Un caso común: tu app usa AGP 9, y un plugin más antiguo android/build.gradle todavía hace referencia a proguard-android.txt, que AGP 9 rechaza. No puedes esperar a un release.
bun patch some-capacitor-plugin
Editar 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'
}
}
Guarda y sincroniza:
bun patch --commit node_modules/some-capacitor-plugin
bunx cap sync android
cd android && ./gradlew assembleDebug
El contexto completo de este error está en arreglar errores de build del plugin Capacitor con AGP 9.
Ejemplo: relajar una restricción de dependencia en iOS
Dos plugins fijan versiones diferentes del mismo SDK y CocoaPods no puede resolverlas. Si has verificado que el plugin funciona con el SDK más nuevo, relaja su podspec:
- s.dependency 'FirebaseMessaging', '11.15.0'
+ s.dependency 'FirebaseMessaging', '>= 11.15.0', '< 13.0'
Para apps SPM, la misma restricción está en el 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")
Luego:
bunx cap sync ios
Con CocoaPods es posible que necesites cd ios/App && pod update FirebaseMessaging una vez, porque Podfile.lock sigue conteniendo la versión anterior. Con SPM, usa Archivo > Paquetes > Resolver versiones de paquetes en Xcode.
Parchear el core de Capacitor
A veces el bug está en @capacitor/android, @capacitor/ios o en CLI, y la solución está en un pull request upstream. Puedes parchear esos paquetes igual, pero cada equipo termina manteniendo los mismos diffs.
Capgo publica @capgo/capacitor-patch para esto. Es un paquete solo de hooks con un catálogo de parches pequeños y con control de versión que enlazan a sus PRs upstream. No hace nada hasta que lo activas:
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;
Los parches de paquetes se ejecutan antes de cap sync y cap update, y los parches del proyecto nativo se ejecutan después. bunx capgo-capacitor-patch doctor hace un dry run. Con strict: true, sync falla cuando un parche seleccionado ya no aplica, lo que te indica cuándo una actualización lo dejó obsoleto. Consulta la docs del plugin para ver la lista completa de opciones.
¿Parchear, hacer fork o enviar upstream?
| Situación | Mejor opción |
|---|---|
| Unas pocas líneas, fix ya fusionado upstream | Patch, eliminar tras la próxima release |
| Restricción de versión demasiado estricta | Patch, abrir un issue upstream |
| Bug sin corrección upstream aún | Parchear y enviar un PR con el mismo diff |
| Nueva funcionalidad o refactor grande | Fork, publicar bajo tu scope |
| Plugin abandonado | Fork, o cambiar a un plugin mantenido |
Hacer fork correctamente
Si haces fork, publícalo para que las instalaciones sean reproducibles:
# in the fork, rename to your scope in package.json: "@acme/capacitor-foo"
bun run build
bun publish --access restricted
Instalar desde una URL de git también funciona, pero la carpeta dist/ debe existir en esa rama o ser generada por un prepare script, y no todos los gestores de paquetes ejecutan scripts de ciclo de vida de dependencias por defecto. Un paquete publicado evita eso.
Recuerda que el nombre npm cambia el nombre del módulo nativo que CLI genera para SPM (@acme/capacitor-foo pasa a ser AcmeCapacitorFoo), así que actualiza el del fork Package.swift para que coincida. Los detalles están en migrar un plugin de Capacitor a SPM.
Antes de hacer fork de un plugin por una función que falta, revisa el directorio del plugin Capgo. Es posible que ya exista un plugin mantenido que lo cubra.
Hacer upstream
El objetivo es eliminar tu patch. Abre un issue con un repro mínimo, las versiones de tu Capacitor y del plugin, y el diff del patch. Luego abre un PR. Los maintainers hacen merge de los fixes pequeños y testeados mucho más rápido que con solo reportes de issues.
Parches y Live Updates
Tu web bundle incluye el JavaScript del plugin de dist/, por lo que un patch en el lado JS se envía con el siguiente web build. Con actualizaciones Live Update de Capgo eso llega a los usuarios sin un release en la store.
Los patches nativos son distintos. Los cambios en Swift, Kotlin, Java, Gradle, podspec o Package.swift solo tienen efecto en una nueva compilación nativa enviada a las tiendas. No envíes un bundle web que dependa de un parche nativo a usuarios que aún ejecutan el binario antiguo. El de Capgo comprobación de compatibilidad compara las versiones de los plugins nativos entre un bundle y un channel para detectarlo.
Solución de problemas
El patch no se aplica en CI. Asegúrate de que patches/ está en el commit y no se ignora. Para patch-package, comprueba que la CI no use --ignore-scripts, lo que omite postinstall. Para Bun, verifica que patchedDependencies esté en el archivo commiteado package.json.
Mi cambio nativo no tiene efecto. Editaste el archivo pero no recompilaste. Ejecuta bunx cap sync, luego haz clean en Xcode Producto > Limpiar carpeta de build, en Android: ./gradlew clean. Con SPM, también restablece las cachés de paquetes.
bun patch --commit indica que no hay cambios. Editaste un archivo que no forma parte del paquete (por ejemplo, una copia hoisted en otra carpeta). Edita la ruta Bun impresa en el paso 1.
El patch falla después de actualizar el plugin. Comprueba si la corrección está en la nueva versión. Si no, vuelve a aplicar el patch contra la nueva versión.
El plugin se compila, pero la app se cierra al usar esa función. Los parches omiten la suite de pruebas del propio plugin. Prueba la ruta parcheada en un dispositivo real en ambas plataformas. El Guía de troubleshooting de iOS de Capacitor y Guía de troubleshooting de Android cubre cómo leer los logs nativos.