Saltar al contenido principal

Cómo parchear un plugin de Capacitor (Bun, npm, pnpm)

Cómo parchear un plugin de Capacitor con bun patch, patch-package o pnpm patch, aplicar correcciones nativas de iOS y Android, y decidir cuándo hacer fork o enviar los cambios upstream.

Logotipos de plugins de Capgo sobre un fondo azul oscuro para la guía sobre cómo parchear plugins de Capacitor

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.gradle incluye la carpeta android carpeta de node_modules.
  • iOS con CocoaPods: el Podfile referencia cada plugin con :path => '../../node_modules/...'.
  • iOS con SPM: CapApp-SPM/Package.swift referencia cada plugin por ruta local en node_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

  1. Revisa si hay un release. Mira el changelog del plugin y los PR abiertos. Actualizar es mejor que parchear.
  2. 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.
  3. 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 &gt; Paquetes &gt; 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 &gt; 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.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.