要修补 Capacitor 插件,请在 node_modules中编辑插件文件,并使用 bun patch --commit (或 patch-package (在 npm 和 Yarn 上, pnpm patch-commit 在 pnpm 上),将生成的文件提交到 patches/,然后运行 bunx cap sync ,这样原生项目就能读取到它。包管理器会在每次安装时重新应用补丁,你的团队和 CI 中都是如此。
当插件有小 bug、有阻碍构建的版本约束,或上游修复已合并但尚未发布时,打补丁就是正确做法。本指南涵盖每种包管理器的工作流程、原生补丁如何进入 Xcode 和 Gradle,以及何时 fork 或向上游提 PR 更合适。
为什么打补丁对原生插件 code 有效
Capacitor 插件是一个 npm 包,包含三个部分:JavaScript 位于 dist/,Android code 位于 android/,以及 iOS code 位于 ios/ ,外加 podspec 和/或 Package.swift。Capacitor 直接从 node_modules:
- Android:
android/capacitor.settings.gradle包含每个插件的android文件夹,来自node_modules. - 使用 CocoaPods 的 iOS:Podfile 通过以下方式引用每个插件:
:path => '../../node_modules/...'. - 使用 SPM 的 iOS:
CapApp-SPM/Package.swift在以下位置通过本地路径引用每个插件:node_modules.
因此,对 node_modules/@scope/plugin/ios/Sources/... 的修改最终会进入你的应用二进制文件,在 cap sync 以及重新构建。无需发布任何内容。
打补丁前
- 检查是否有新版本发布。 查看插件的更新日志和开放 PR。升级优于打补丁。
- 阅读许可证。 MIT 和 Apache-2.0 允许私有修改。GPL 或 MPL-2.0 等 Copyleft 许可证可能要求你在分发应用时公开修改后的源代码。发布前请确认。
- 记录原因。 在 commit message 中放入 issue URL 或一行原因。六个月后没人记得为什么
patches/里会有这个文件。
使用 Bun 打补丁
Bun 内置了补丁功能。无需额外包,也无需 postinstall 脚本。
# 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
步骤 1 让 Bun 在 node_modules中生成包的私有副本,因此编辑不会影响 Bun 的全局缓存。步骤 3 会写入 diff 并记录在 package.json:
{
"patchedDependencies": {
"@capacitor/haptics@8.0.2": "patches/@capacitor%2Fhaptics@8.0.2.patch"
}
}
提交两者 package.json 以及 patches/ 文件夹。每次 bun install,包括 bun install --frozen-lockfile 在 CI 中,应用该补丁。
键名包含确切版本。如果你升级了插件,Bun 将不再匹配该补丁,因此需要为新版本重新创建它,或者如果修复已发布则删除它。
使用 npm 或 Yarn 打补丁:patch-package
npm install --save-dev patch-package
添加一个 postinstall 脚本,让补丁在每次安装后都能应用:
{
"scripts": {
"postinstall": "patch-package"
}
}
然后编辑 node_modules 中的文件并生成补丁:
npx patch-package @capacitor/haptics
这会写入 patches/@capacitor+haptics+8.0.2.patch。提交它。如果升级后补丁无法应用, patch-package 会让安装失败并给出明确提示。
Yarn Berry 有自己的 yarn patch <pkg> / yarn patch-commit -s <path> 流程,会把 patch 存到 resolutions 该字段中。
使用 pnpm 打 patch
pnpm patch @capacitor/haptics
# edit the temporary folder pnpm prints
pnpm patch-commit <path-printed-above>
pnpm 会把 patch 记录在 patchedDependencies 并在安装时应用。
示例:修复插件中的原生构建错误
常见情况:你的应用用了 AGP 9,而某个旧插件的 android/build.gradle 仍然引用了 proguard-android.txt,而 AGP 9 不接受它。你没法等新版本发布。
bun patch some-capacitor-plugin
编辑 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'
}
}
保存并同步:
bun patch --commit node_modules/some-capacitor-plugin
bunx cap sync android
cd android && ./gradlew assembleDebug
有关此错误的完整背景信息,请参阅 修复 Capacitor 插件在 AGP 9 下的构建错误.
示例:放宽 iOS 依赖约束
两个插件固定了同一 SDK 的不同版本,导致 CocoaPods 无法解析。如果您已验证该插件与较新的 SDK 兼容,请放宽其 podspec:
- s.dependency 'FirebaseMessaging', '11.15.0'
+ s.dependency 'FirebaseMessaging', '>= 11.15.0', '< 13.0'
对于 SPM 应用,相同的约束位于插件的 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")
然后:
bunx cap sync ios
使用 CocoaPods 时,你可能需要 cd ios/App && pod update FirebaseMessaging 一次,因为 Podfile.lock 仍然保留着旧版本。如果使用 SPM,请使用 File > Packages > Resolve Package Versions 在 Xcode 中。
修补 Capacitor 核心本身
有时 bug 位于 @capacitor/android, @capacitor/ios 或 CLI 中,修复方案位于上游 pull request 里。你可以用同样的方式修补这些包,但每个团队最终都在维护相同的 diff。
Capgo 发布了 @capgo/capacitor-patch 用于此目的。这是一个仅包含 hook 的包,内含一系列小型、版本受限的补丁,并链接回其上游 PR。除非你选择加入,否则它不会执行任何操作:
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;
包补丁在...之前运行 cap sync 以及 cap update,原生项目补丁随后执行。 bunx capgo-capacitor-patch doctor 执行 dry run。使用 strict: true,当所选补丁不再适用时同步会失败,这能提示你升级已使其过时。参见 插件文档 以查看完整选项列表。
打补丁、fork 还是上游?
| 场景 | 最佳选项 |
|---|---|
| 几行代码,修复已合并到上游 | 打补丁,下个版本后移除 |
| 版本约束过于严格 | 打补丁,向上游提 issue |
| 存在 Bug 且上游尚未修复 | 打补丁并提交包含相同 diff 的 PR |
| 新功能或大规模重构 | Fork 并在你的 scope 下发布 |
| 插件已弃用 | Fork,或切换到受维护的插件 |
Fork 指南
如果你 Fork,请发布它以确保安装可复现:
# in the fork, rename to your scope in package.json: "@acme/capacitor-foo"
bun run build
bun publish --access restricted
从 git URL 安装也可以,但插件的 dist/ 文件夹必须存在于该分支中,或由 prepare 脚本构建,且并非所有包管理器默认都会运行依赖生命周期脚本。发布包可以避免此问题。
请记住,npm 名称会改变 CLI 为 SPM 生成的原生模块名称(@acme/capacitor-foo 会变为 AcmeCapacitorFoo),因此请更新 fork 的 Package.swift 以保持一致。详情见 将 Capacitor 插件迁移到 SPM.
在 fork 插件以添加缺失功能之前,请先查看 Capgo 插件目录。可能已有仍在维护的插件覆盖了该功能。
向上游提交
目标是删掉你的补丁。开一个 issue,附上最小复现、你的 Capacitor 和插件版本,以及补丁 diff。然后提交 PR。维护者合并小型且经过测试的修复,比仅凭 issue 报告快得多。
补丁与 Live Updates
你的 web bundle 包含来自 dist/的插件 JavaScript,因此 JS 端的补丁会随下一次 web 构建发布。使用 Capgo 实时更新 无需通过应用商店发布即可触达用户。
原生补丁则不同。对 Swift、Kotlin、Java、Gradle、podspec 等 Package.swift 的修改,只有在提交到商店的新原生构建中才会生效。不要向仍运行旧二进制的用户推送依赖原生补丁的 Web bundle。Capgo 的 兼容性检查 会对比 bundle 和 channel 之间的原生插件版本,以捕获此类问题。
故障排除
补丁未在 CI 中应用。 请确保 patches/ 已提交且未被忽略。对于 patch-package,请确认 CI 未使用 --ignore-scripts,该选项会跳过 postinstall。对于 Bun,请检查 patchedDependencies 位于已提交的 package.json.
我的原生修改没有生效。 你编辑了文件但没有重新构建。请运行 bunx cap sync,然后清理:在 Xcode 中 产品 > 清理构建文件夹,在 Android 上 ./gradlew clean。如果使用 SPM,还需重置包缓存。
bun patch --commit 显示没有更改。 你编辑的文件不属于该包(例如位于其他文件夹中的提升副本)。请编辑第 1 步中 Bun 打印的路径。
升级插件后补丁失败。 检查新发布版本中是否已包含该修复。如果没有,请针对新版本重新应用补丁。
插件构建成功,但应用在该功能上崩溃。 补丁会跳过插件自身的测试套件。请在两个平台的真机上测试打过补丁的路径。下文 Capacitor iOS 故障排除指南 以及 Android 故障排除指南 介绍如何读取原生日志。