关于 build-nitro-modules
用于构建 React Native Nitro Modules 的端到端 Skill,覆盖仓库脚手架、TypeScript HybridObject 规格、原生实现和发布准备。重点在 Nitro 专属 API 设计、代码生成、平台绑定与验证。
主要功能
- 01使用 Nitrogen 搭建模块仓库
- 02编写 TypeScript HybridObject 规格
- 03生成 C++、Swift 和 Kotlin 原生代码
- 04指导原生实现与示例应用接线
- 05涵盖代码生成、验证和发布准备
适用场景
- 01创建 React Native Nitro Module
- 02定义类型化的 HybridObject API
- 03实现跨平台原生模块绑定
- 04准备模块仓库以便发布
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ margelo/ react-native-skills --skill build-nitro-modules安装后在 Claude Code 中输入/build-nitro-modules确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/example-android-config.mdexample-app-setup.mdexample-metro-install.mdnative-implement-cpp.mdnative-implement-kotlin.mdnative-implement-swift.mdnative-nitrogen-codegen.mdrelease-it-publishing.mdrepo-structure-and-workflow.mdsetup-monorepo-init.mdspec-hybrid-object.mdspec-hybrid-view.mdspec-nitro-json.mdspec-package-publish.mdvision-camera-golden-standard.md详细介绍
构建 Nitro 模块
概述
端到端技能,用于构建 React Native Nitro 模块:通过 Nitrogen 搭建 monorepo、编写 TypeScript HybridObject 规范、生成原生代码、实现各平台代码(C++/Swift/Kotlin)、接入示例应用,并为发布做准备。
Nitro 模块使用代码生成流程(nitrogen),读取 .nitro.ts 规范文件并生成原生 C++/Swift/Kotlin 样板代码。随后,你需要补全实现。这与旧式 Turbo Modules 有本质区别。
nitrogen/generated/ 下的生成文件是输出产物。应修改 .nitro.ts 规范或原生实现源文件,然后重新运行 nitrogen,而不是手动编辑生成文件。这些文件可以提交到 git,许多 Nitro 库也会提交,但仓库策略可以另行决定。必须将它们包含在 npm 包中,以便使用者构建原生库。
配合 API 设计使用
设计公开的 TypeScript、JavaScript、React 或 React Native API 时,先使用 api-design。本技能补充 Nitro 特有的约束:HybridObject 状态、生成的规范、原生资源所有权、零拷贝数据、线程处理、平台实现、代码生成以及真机验证。
通用公开 API 规则和 API 时效性检查由 api-design 负责。在本技能中,只需针对移动工具链和生成模板决策添加 Nitro 特有的时效性检查:在选择版本、配置字段或原生实现细节之前,核实当前 Nitro、React Native、Gradle、Xcode、Swift、Kotlin、NDK 和包管理工具的文档或源码。
如果用户正在构建仅含 JS 的 React 或 React Native 库,除非任务涉及 Nitro、HybridObjects、原生模块、代码生成、C++/Swift/Kotlin 绑定或 react-native-nitro-modules,否则不要应用本技能。
实现或审查由 Swift 支持的 HybridObject、AVFoundation/会话代码、DispatchQueue 用法、Swift 并发或线程绑定的 Swift 状态时,配合使用 swift。实现或审查由 Kotlin 支持的 HybridObject、Android 线程处理、协程、Kotlin 可空性、密封结果模型或 Android 服务访问时,配合使用 kotlin。实现或审查由 C++ 支持的 HybridObject、共享原生引擎、CMake、RAII 所有权或生成的 C++ 规范绑定时,配合使用 cpp。
仓库与发布参考
仅在创建仓库、重组布局、添加示例/文档/CI 或更改工作流策略时,加载 repo-structure-and-workflow.md。
仅在设置或审查 bun release / release-it 时,加载 release-it-publishing.md。
Nitro API 设计规则
- 进行原生模块开发时,优先使用 Nitro Modules,而不是 TurboModules 或手写 JSI。Nitro 通常更快、更安全,因为它能避免许多原始 JSI 的生命周期、线程处理和运行时销毁隐患。只有在需要 Nitro 的 Raw JSI Methods 时才使用原始 JSI。
- 为便于自动链接,根 HybridObject 应保持可默认构造。通过工厂方法创建依赖参数的对象。
- 使用 HybridObject 管理原生状态:原生资源、预热引擎、文件、图像、数据库、传感器会话、流及其他有状态对象。
- 如果需要原生初始化,应将工厂方法设为异步,并返回一个已就绪的 HybridObject,例如
createCameraSession(...): Promise<CameraSession>。 - 一个面向 JS 的 HybridObject 规范可以由多个实现该生成规范的原生具体类实现。可利用这一点在一个 TypeScript 类型背后隐藏后端策略,例如,让
CameraVideoOutput由电影文件输出或视频数据输出加资源写入器实现。工厂负责选择原生实现,并返回共享规范类型。 - 导出的 JS 工厂对象应使用产品或领域名词,而不是生成的规范类型名称。例如,导出
VisionCamera = createHybridObject<CameraFactory>('CameraFactory')或Images = createHybridObject<ImageFactory>('ImageFactory')。这样可以避免与CameraFactory/ImageFactory类型冲突,无须机械地将名称改为小写或添加Hybrid前缀。 - 每个 HybridObject 都应只负责一个用途或生命周期。
- 不要只按领域名词划分 HybridObject 边界。即使属于同一功能领域,一次性命令、由应用持有的实时会话、原生视图和长生命周期引擎也属于不同契约。
- 当返回的 HybridObject 在选项、结果、生命周期或未来的平台支持方面存在差异时,应按稳定的语义能力拆分。例如,根
DataScannerFactory可以提供createBarcodeScanner()和createTextScanner(),而不是提供一个宽泛的扫描器,其文本字段因 Android 目前只支持条码而必须设为可空。 - 工厂可以在当前不支持相应能力的平台上报告
isTextScannerAvailable: false,或拒绝createTextScanner()。未来平台支持该能力时,应补全现有能力并将可用性设为 true,而不是重新设计一个臃肿的 HybridObject。 - 如果平台专属能力所代表的概念是稳定的,也可以将其作为一等 HybridObject。例如,仅限 iOS 的
CameraObjectOutput可以扩展共享的CameraOutput,标记为@platform iOS,并在 Android 上拒绝createObjectOutput(...),而不是给每种输出都添加可空的对象扫描字段。 - 使用工厂方法区分工作流,并让返回的 HybridObject 更具约束力。如果
createLiveScanner()返回一个实时扫描器会话,那么该类型应保证实现所能控制的基本会话操作。无法提供实时工作流的后端应创建失败或返回范围更窄的对象,而不是迫使实时对象暴露无效方法、可空的基本属性或反复执行can*检查。 - 返回已配置的句柄,避免状态过时。如果
configure(...)绑定了设备、输出、流或原生图结构,应返回一个新的 HybridObject 句柄,用于执行仅对该已配置资源有意义的命令。例如,CameraSession.configure(...)应返回供setZoom(...)和focusTo(...)使用的CameraController句柄,而不是把这些方法放在CameraSession上并隐式依赖“当前设备”状态。 - 重新配置应替换或使原生目标已更改的句柄失效。若命令的目标取决于最近一次成功配置,不要将命令放在宽泛的父 HybridObject 上。
- 对于原生协商,应将请求意图与解析后的状态分别建模。输入使用带优先级的约束或偏好,然后返回或发出描述会话实际选择结果的解析后配置 HybridObject/struct。当调用方需要在创建或启动原生会话前预览结果时,应提供显式的解析方法。
- 使用能力字段来发现工作流、表示可选偏好和真正可变的支持情况。不要用能力字段掩盖过于庞大的 HybridObject,尤其是当其方法在受支持的后端上正常使用时仍不受支持。
- 将 HybridObject 视为主要 API 对象。每个主要 HybridObject 都应有自己的
.nitro.ts文件。 - 仅当文件以基类 HybridObject 命名且子 HybridObject 几乎不添加成员时,才将同一继承家族放在一个
.nitro.ts文件中,例如ScannedCode.nitro.ts中的ScannedCode、ScannedBarcode和ScannedQRCode。 - 将命名的代码生成类型放在各自的
.ts文件中:字符串字面量联合类型/枚举、struct/interface、选项对象、事件对象、回调选项 struct 和辅助类型。Nitro 需要为生成的原生 struct 和类似枚举的值命名。将这些类型导入.nitro.ts规范,并在src/index.ts中重新导出公共类型。 - 在方法签名中直接内联简单函数回调,例如
addErrorListener(listener: (error: Error) => void): ListenerSubscription。除非某个函数类型作为公共概念被多个 API 复用,否则不要创建ScannerErrorListener之类的一次性别名。 - 只有当多个辅助类型构成一个紧密耦合的逻辑结构时,才将它们放在同一个文件中,例如
DynamicRange及其定义所需的确切字面量联合类型。 - 对于共享原生状态和专用结果形状,使用 HybridObject 继承。将 ID、边界框、原始值、格式和值类型等共享属性放在基对象上,而不是在每个子类型中重复。
- 对于异构原生结果家族,使用 HybridObject 继承。例如,
ScannedItem持有共用状态和方法,而ScannedBarcode、ScannedQRCode和ScannedFace扩展它并添加专用属性。API 可以返回ScannedItem[];JS 可根据判别属性缩小类型,而只需要共用行为的原生代码可以接受生成的基规范。 - 不要将状态类型家族建模为一个 Nitro struct 或 HybridObject,并将所有子类型字段设为可空。应使用 HybridObject 继承、可辨识联合类型或平台协议/接口一致性,确保
barcode和barcodeType之类的关系在编译时安全。 - 将公共 Nitro HybridObject 视为命令式 API。若生成的 Nitro API 面向用户,应按 1:1 方式导出;不要添加会预解析值、转换字符串/枚举、重塑选项、注入隐藏默认值,或调用与
.nitro.ts规范公开形状不同的内部方法的 JS 封装。 - JS/TS 层适用于有意构建的更高层 API,例如 React hooks、React 组件、UI 组合辅助工具,或 HybridObject 仅作为内部实现细节且不符合用户对 API 的心智模型的情况。在这些情形下,应明确划分边界:封装层是公共 API,Nitro 对象则是内部对象。
- 只自动链接 JS 必须直接构造的公共根对象、工厂、视图或全局工具。其他 HybridObject 可以由工厂方法返回,无须在
nitro.json中单独添加自动链接条目。不要为同一面向 JS 的规范的每个原生具体实现都添加自动链接。 - 对于原生扩展点,将面向 JS 的基 HybridObject 规范与公共原生协议/接口配对。基规范让 JS 能通过类型化 API 传递对象;原生协议/接口则向第一方和第三方原生代码公开平台专属句柄和行为。
- 从 JS 接受可扩展 HybridObject 时,应接受生成的基规范类型,然后在原生端转换为原生协议/接口,并在不符合时抛出明确错误。这样既能保持 JS 的可移植性,也能让原生集成具备强类型。
- 使用 Nitro struct 表示领域形状、选项组和同类型参数集群。不要仅为减少参数数量,就把热路径中无关的值封装进 struct;Nitro 会急切转换 struct,因此不必要的封装可能比显式参数更慢。
- 请记住,TypeScript 可选字段在生成的 Swift、Kotlin 和 C++ 中会成为原生可选类型(
T?/std::optional<T>)。只有当缺失值属于预期的公共/原生契约时,才使用可选 Nitro 字段;不要因为 JS 封装层会将其转换掉就使用可选字段。 - 优先让 Nitro 方法生成的 struct 成为真正的公共输入形状。如果需要默认值或解析后的选项,应在规范/原生实现中明确建模,或在内部 HybridObject 之上提供有意设计的更高层 API;不要在公共 HybridObject 上随意添加 JS 规范化层。
- 不要将高频原生结果、解析后的载荷、图像、缓冲区或带有许多昂贵可选字段的对象建模为扁平 struct。Nitro struct 会被急切转换,因此对于调用方可能永远不会读取的数据,优先使用带惰性属性或方法的有状态 HybridObject。
- 明确决定每个结果应采用 Nitro struct 还是 HybridObject。字段少且不可变、标量字段转换成本低的结果可以使用 struct。如果结果持有原生状态、可能暴露惰性昂贵数据、需要零拷贝二进制访问,或很可能增加行为/方法,则使用 HybridObject。
- 对于少量或真正零拷贝的原生数据访问,使用
ArrayBuffer。对于大型媒体、照片、扫描结果、模型输出或字节载荷,应返回 HybridObject,并提供toArrayBuffer()、toBase64()或saveToTemporaryFile()等惰性方法,而不是将字节急切转换为 JS。 - 不要仅仅因为
ArrayBuffer支持零拷贝就选择它。已解码的文本载荷应使用string;原始字节、二进制载荷、媒体或 API 契约要求按字节访问的不透明数据应使用ArrayBuffer。 - 当转换成本很高时,应通过 HybridObject 隐藏原始原生状态。例如,原生条码/照片/结果对象可以惰性地暴露
rawValue、bounds、format或字节数据,而不必为每次检测都转换所有字段。 - 对于重复事件,优先使用
addOn...Listener(callback): ListenerSubscription,并让每个订阅负责自己的清理。这样可避免一个调用方通过共享对象状态替换另一个调用方的回调。 - 监听器订阅应为包含
remove: () => void函数字段的扁平 struct,而不是 HybridObject,除非它们还公开清理操作以外的原生状态。调用处仍使用subscription.remove()。 - 清理监听器应停止后续事件的发出。它无需取消已被正在执行的原生事件快照选中的回调,因此不要仅为满足这一保证而添加锁或复杂的订阅状态。
- 不要默认采用锁定监听器添加、移除和回调调用的实现模式。优先使用单一所有者线程/队列或不可变快照;仅当确有共享可变竞态且无法通过所有权消除时,才添加锁。
- 仅对由对象拥有的单个热路径回调使用
setOn...(callback | undefined)风格的 API,且替换或移除回调应是自然操作。动词set和文档必须明确说明替换语义。 - 如果原生 API 仅公开一个 delegate/回调,但 JS API 表示重复事件,则优先在内部进行多路复用并公开可追加的监听器,除非这样做对热路径不安全或开销过高。
- 仅对必须在特定 JS runtime 或 worklet 线程上同步执行的少见线程绑定热路径,使用
Sync<(...) => ...>回调。 - 对于 Nitro Views,应公开原始的
getHostComponent封装。只有在 React 组件或 hooks 能减少重复的设置代码,同时仍基于同一组原生对象和 refs 时,才添加它们。创建或审查 Nitro View 组件时,请加载 spec-hybrid-view.md。 - 命名、平台抽象、同步/异步边界、监听器清理、错误、变体、TypeScript 门面和 JSDoc 契约均应遵循
api-design。
Nitro 原生实现规则
- 调试奇怪的构建失败之前,先检查 Nitro 当前的最低要求。当前 Nitro 文档列出的要求为:React Native 0.75+、Xcode 16.4+、Swift 5.9+、Android
compileSdkVersion34+ 和 NDK 27+;Nitro Views 当前要求 React Native 0.78+ 和新架构。 - 每个原生 HybridObject 实现都必须实现或继承该平台生成的
Hybrid*Spec。切勿只实现一个独立的原生类,并指望 Nitro 自动发现它。 - 默认将原生实现类声明为
final,除非确实需要继承。Swift 和 Kotlin HybridObject 尤其应遵守这一点。 - 每个文件中恰好保留一个顶层原生实现类型。
Hybrid*Factory.swift/.kt文件只包含Hybrid*Factory类型,不得包含其他 struct/class/enum/interface/protocol。辅助 session、协调器、delegate、选项适配器和平台封装应放在各自的文件中。 - 不要为了避免拆分文件而将辅助类型嵌套在主类型中。应将辅助类型放在各自有名称的文件中。
- 将原生文件名视为范围契约。以 HybridObject 命名的文件应专注于该 HybridObject 的实现,不要在其中堆积扩展方法/属性、平台辅助代码、转换器、delegate、protocol 或可复用工具。
- 将行数作为审查原生文件的参考:只有在满足每文件一个类型且工厂中无辅助代码这两项规则后,约 300 行以下通常才算可接受。如果文件过长是因为包含了可独立命名的辅助代码,就应拆分文件。
- 将原生转换辅助代码放在最小且有意义的类型上。如果原生转换只读取一个生成的 struct 或 enum,即使它返回多个原生值,也应放在该类型上。除非集合转换带来了去重、校验、非空检查、批处理或错误聚合等实际行为,否则应在调用处组合集合。
- 切勿将领域转换放在宽泛的原生/通用接收类型上,例如 Kotlin
Int或 SwiftInt。应通过领域类型的 companion、静态工厂或初始化器方向实现,例如BarcodeFormat.Companion.fromFormat(format: Int)或BarcodeFormat.from(format:)。 - 将每个 Kotlin 扩展
fun/val/var和 Swift 扩展方法/属性放在单独且有名称的扩展/转换器文件中,并尽可能使用 internal/package 可见性。Kotlin 转换器使用Type+operation.kt这样的名称,例如BarcodeFormat+fromMLKitBarcodeFormat.kt。Hybrid*文件或其他实现文件没有私有/微小/同文件例外;这样可以确保代码拆分、可维护性以及未来审查差异的整洁。 Hybrid*Factory.kt或Hybrid*.swift文件不得包含条码格式映射、companion 扩展、平台转换工具或实现之后的其他扩展。Hybrid*Factory文件仅负责协调:解析生成的选项、调用校验/预检辅助代码、创建/启动原生 session 或平台 API,并返回/拒绝 Promise。不要在工厂文件中定义 session/协调器/delegate 类、原生选项适配器 struct/class、presenter 查找辅助代码、builder/config 适配器、条码映射、权限分支、Info.plist/manifest 检查或能力检查。- 将原生转换拆分到每个源类型/目标类型各自专注的扩展文件中,例如
Barcode+toScannedCode.kt、TargetBarcodeFormat+toMLKitFormat.kt和BarcodeFormat+fromMLKitBarcodeFormat.kt。不要用具体集合扩展(例如Array<TargetBarcodeFormat>.toMLKitFormats())封装简单的map { it.toX() }。 - 将平台预检移出
Hybrid*工厂和实现方法。授权状态分支、Info.plist/manifest 键校验、权限检查、硬件能力检查和服务可用性检查应放在专注的辅助/扩展文件中;HybridObject 调用处应简短明了,采用守卫条件或单行try/check调用。 - 不要将内联预检代码替换为宽泛的
Utils文件。应使用小型且有名称的文件,例如Bundle+CameraUsageDescription.swift、AVCaptureDevice+CameraAuthorization.swift,或专注的 Android 权限/能力辅助文件。 - 在 Kotlin 中,如果 Android 平台提供了匹配的注解或
@IntDef,就应为平台Int常量添加注解,例如 CameraX 闪光灯/拍摄/镜像模式注解。根据目标将注解放在函数、返回值或参数上;选择语法前应确认该注解属于 Java 还是 Kotlin。 - 从带 Android 注解的
Int转换时,如果受支持,应在领域工厂/companion 函数的format: Int参数上添加注解;与裸Int相比,这能提供更好的 IDE 和 lint 反馈。 - 尽早校验无效输入和必须支持的行为。不要仅仅因为当前原生后端忽略某项可选偏好,就拒绝跨平台配置。如果操作仍能完成核心任务,就忽略该偏好或降低其优先级,并通过能力或解析后的状态报告支持情况。
- 不要悄悄吞掉真实失败。如果无法交付请求的结果,应抛出异常、拒绝 Promise,或通过明确的错误回调/监听器发出错误。
- 优先使用能清晰传递给 JS 的 Nitro/运行时错误或语言原生异常。除非生成的 API 明确要求,否则避免使用 Objective-C 风格的
NSError公共路径。 - 不要在 Nitro Modules 中添加空的 iOS 桥接头文件,例如
Bridge.h。Nitro 会生成所需的 Swift/C++ 桥接代码。只有在确实需要手写 Objective-C 代码时才添加 Objective-C/Objective-C++ 头文件,并且只在 podspec 中包含必需文件。 - 在 Android 中访问 context 时,应惰性使用
NitroModules.applicationContext;如果该 context 不可用,应抛出清晰的错误。 - Nitro 方法仅在执行快速、确定的本地操作时保持同步,例如低成本对象创建、缓存元数据或纯转换。如果实现复杂、开销大、随时间可能失败、本身已异步,或跨越线程/进程边界,就应让 Nitro API 返回
Promise<T>。 - 使用符合平台线程模型的原生 Promise 辅助方法。在 Swift 中,对于由 DispatchQueue 管理的工作(例如 AVFoundation/session 队列),优先使用
Promise.parallel(queue);只有在端到端封装 Swiftasync/await或基于 Task 的 API 时才使用Promise.async。对于 UIKit/VisionKit 主线程回调 API,优先使用手动 Promise,并在 Nitro 入口/回调边界直接调用DispatchQueue.main.async;不要将Task { @MainActor in ... }用作通用的主线程切换方式。 - 除非原生 API 有要求,否则避免使用主线程/UI 线程。将主线程代码限制在 UI/呈现/视图变更上,并在专属队列/dispatcher/executor 或原生异步 API 上执行解析、转换、I/O、session 协商和 CPU 工作。
- 避免手动创建或传递
Promise<T>实例。优先使用Promise.async、Promise.parallel、Promise.resolved和Promise.rejected,因为它们通过结构化控制流保证只完成一次。只有在桥接无法使用这些辅助方法的原生 completion/delegate/callback API 时,才有理由手动创建 Promise;应将其限制在最小作用域,不要通过任意辅助代码传递,并确保每条路径都恰好 resolve 或 reject 一次。 - 为 callback/listener API 编写手动 Promise 之前,先查找或添加通用的 suspend/async 适配器,并将其放在专注的扩展文件中。在 Android 上,应使用
suspendCancellableCoroutine将 GoogleTask<T>一次性封装为Task+await.kt,然后在 HybridObject 方法中使用return Promise.async(scope) { task.await() }。 - 如果可复用的 callback-to-suspend 适配器可以一次性表达 API,就不要在
Hybrid*方法中手动接线addOnSuccessListener/addOnFailureListener/addOnCanceledListener。 - 切勿将线程切换隐藏在生成的属性 getter 或 setter 后面。如果只能在特定队列/线程上读取或修改原生状态,应改为公开异步方法、监听器/事件或明确的生命周期操作。
- 避免在单次操作中串联 Task、DispatchQueue、协程 dispatcher、executor 和 JS/Nitro 运行时切换。为每个 HybridObject 或 session 选择一个原生所有者队列/线程/dispatcher,并在 Promise、生命周期或回调边界处切换一次。反复切换通常说明 HybridObject 边界或生命周期句柄设计有误。
- 切勿通过
setTimeout、休眠、人为延迟、额外线程切换或重复调用原生方法来修复 Nitro 生命周期、就绪状态或竞态问题。应通过 Promise、监听器/事件、返回已配置的 HybridObject、显式状态转换或原生 completion 回调来建模就绪状态。仅在外部硬件、OS 服务、远程服务或网络存在不确定性时使用重试,并确保重试有界、可取消且具备幂等性。 - 对拥有原生资源或大量分配内存的 HybridObject 实现
memorySize,以便 JS VM 能在内存压力下回收它们。 - 对 Nitro Views,如果视图拥有需要在复用前重置的状态,请实现
prepareForRecycle。完整的视图规范、实现、注册和回收工作流请参阅 spec-hybrid-view.md。 - 在同一库中混合使用 C++ HybridObject 和 Swift/Kotlin HybridObject。对于共享代码或热点代码(例如 OpenCV/帧处理/存储引擎)使用 C++;对于平台服务、权限、文件路径、相机/session API 和 OS 集成使用 Swift/Kotlin。
- C++ HybridObject 可以接收由 Swift/Kotlin 实现的 HybridObject,并调用其生成的 C++ spec API。例如,C++
StorageFactory可以接收 Swift/KotlinPlatformContext,并通过生成的 C++ 接口调用getTemporaryDirectory()或writeFile(...)。C++ 只能访问公共 spec API,不能访问 Swift/Kotlin 的私有字段。 - 除非已确认当前 Nitrogen 支持这一方向,否则不要依赖 Swift/Kotlin 调用由 C++ 实现的 HybridObject。
Nitro 测试规则
- 优先编写行为测试,而不是类型结构测试。Nitrogen 已在编译时强制执行规范,因此测试应覆盖实际功能行为、输入、设置、失败路径和执行顺序场景。
- 条件允许时,使用
react-native-harness在真实 React Native 环境中进行端到端测试。对于依赖原生功能较多的库,优先使用真实设备或 CI 设备云,覆盖依赖硬件或 OS 行为的 API 表面。 - 在 GitHub Actions 中,对于仅通过 React Native 公开的 API,优先使用 harness 端到端测试任务,而不是独立的原生单元测试。仅当包具有可在 React Native 之外使用的原生目标,或 RN harness 无法测试相关行为时,才添加 Kotlin JUnit、XCTest 或其他仅限原生的测试。
先询问——开始任何操作之前
首先确定用户想做什么:
“你是要从头创建一个新的 Nitro Module 库,还是要向现有库添加一个新的 HybridObject?”
如果要创建新库——执行任何命令之前,先询问以下所有问题:
- 库名称——这个库应该叫什么?(例如:
react-native-math) - 是否使用带有
packages/文件夹的 monorepo——这个库是否应该放在 monorepo 中的packages/<name>目录下?(强烈推荐——默认:是) - 示例应用——是否应创建示例应用来测试模块?应放在哪里?(推荐——默认:是;需要或可能需要多个示例时使用
apps/example;对于应靠近生成的 RN 配置的小型单示例仓库,仅使用example) - 原生语言——使用哪些平台和语言?
- iOS:
swift(默认)或cpp - Android:
kotlin(默认)或cpp - 仅跨平台 C++:两者均为
cpp
- iOS:
- 模块用途——简要描述模块的功能,以便设计合适的规范方法
在这五个问题都得到回答之前,不要继续执行构建流程的第 1 步之后的步骤。
如果要向现有库添加 HybridObject——只询问:
- HybridObject 名称——这个新的 HybridObject 应该叫什么?(例如:
Camera、Crypto) - 原生语言——iOS:
swift还是cpp?Android:kotlin还是cpp? - 用途——这个 HybridObject 有什么作用?
然后直接跳转到 spec-hybrid-object.md(编写规范)、spec-nitro-json.md(添加自动链接条目)、native-nitrogen-codegen.md(重新运行 nitrogen)以及相关的原生实现文件。跳过所有设置、monorepo 和示例应用步骤。
如果新的 HybridObject 是可渲染的视图组件,请使用 spec-hybrid-view.md,而不是普通的 HybridObject 规范参考;它涵盖 HybridView 规范、原生 view 实现、Android 视图管理器注册,以及 getHostComponent JS 接线。
典型构建流程
# 0. Work on a separate branch; open a draft PR after the first useful commit
# 1. Scaffold
bunx nitrogen@latest init react-native-math
# 2. Run codegen (from package folder after writing spec + nitro.json)
cd packages/react-native-math && bunx nitrogen
# 3. Create example app
bunx @react-native-community/cli@latest init --skip-install MathExample
mkdir -p apps && mv MathExample apps/example
# Alternative: mv MathExample example
# 4. Install and test
cd apps/example && bun add ../../packages/react-native-math
bun add react-native-nitro-modules@<same-version-as-package>
bun example android
bun example ios
下方提供完整的逐步参考。
适用时机
在以下情况下参考这些指南:
- 使用 Nitro 框架创建任何新的 React Native 原生模块
- 检查 Nitro 的最低平台要求
- 在制定实现决策之前,确认当前 Nitro、React Native 和原生工具链的要求
- 设计或审查基于 Nitro 的库的公共 API 形态
- 决定 API 应采用静态、实例、同步、异步、基于回调还是基于资源的形式
- 编写 HybridObject TypeScript 规范(
*.nitro.ts文件) - 运行 Nitrogen 代码生成并实现生成的接口
- 为 Nitro 库设置 monorepo 示例应用
- 选择仓库布局、根目录整洁性、共享配置位置和 CI 结构
- 为 Nitro 库建立分支、草稿 PR 和 squash 合并工作流
- 配置适用于 monorepo 结构的 Android Gradle 路径
- 调试自动链接故障或生成文件缺失问题
- 准备 Nitro 模块包以发布到 npm
- 使用
release-it和bun release设置一键发布
按优先级排序的指南
| 优先级 | 类别 | 影响 | 参考 |
|---|---|---|---|
| 0 | 通用公共 API 形态 | 严重 | api-design |
| 0 | Nitro API 约束 | 严重 | 本 SKILL.md |
| 1 | 仓库结构与工作流 | 高 | repo-structure-and-workflow.md |
| 2 | Nitrogen 脚手架 | 严重 | setup-monorepo-init.md |
| 3 | HybridObject 规范 | 严重 | spec-hybrid-object.md |
| 3 | Hybrid View 组件 (如果要构建视图) | 高 | spec-hybrid-view.md |
| 4 | nitro.json 自动链接 | 严重 | spec-nitro-json.md |
| 5 | Nitrogen 代码生成 | 高 | native-nitrogen-codegen.md |
| 6 | C++ 实现 | 高 | native-implement-cpp.md |
| 7 | Kotlin 实现 | 高 | native-implement-kotlin.md |
| 8 | Swift 实现 | 高 | native-implement-swift.md |
| 9 | 示例应用设置 (按需) | 高 | example-app-setup.md |
| 10 | Android Gradle 路径 (用于示例应用时) | 高 | example-android-config.md |
| 11 | Metro + 安装 + 测试 (用于示例应用时) | 高 | example-metro-install.md |
| 12 | npm 发布准备 | 中 | spec-package-publish.md |
| 13 | release-it 发布 | 中 | release-it-publishing.md |
| 14 | VisionCamera 风格的完整库模式 | 中 | vision-camera-golden-standard.md |
快速参考
最精简的 HybridObject 规范(src/specs/Math.nitro.ts)
import type { HybridObject } from 'react-native-nitro-modules'
export interface Math extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
add(a: number, b: number): number
}
最精简的运行时与类型导出(src/index.ts)
import { NitroModules } from 'react-native-nitro-modules'
import type { Math } from './specs/Math.nitro'
export const math = NitroModules.createHybridObject<Math>('Math')
export type { Math } from './specs/Math.nitro'
src/index.ts、index.ts、index.js 和 index.tsx 等包入口必须保持为聚合导出文件。它们可以包含直接重新导出,以及一行 Nitro 根导出,例如 export const camera = NitroModules.createHybridObject<Camera>('Camera'),但不能包含实际实现逻辑、函数、类、钩子、组件、分支、副作用或辅助定义。请将实际定义移至职责明确的文件中,再重新导出。
对于 Nitro 包中的 JS/TS 源代码,必须明确处理缺省值和变更:绝不能用 void 0 代替 undefined,也绝不能使用 ??=、||= 或 &&= 等逻辑赋值运算符。优先使用显式的 if 代码块或直接赋值,让回退行为清晰可见。
最精简的 nitro.json
{
"$schema": "https://nitro.margelo.com/nitro.schema.json",
"cxxNamespace": ["math"],
"ios": { "iosModuleName": "NitroMath" },
"android": {
"androidNamespace": ["math"],
"androidCxxLibName": "NitroMath"
},
"autolinking": {
"Math": {
"ios": {
"language": "swift",
"implementationClassName": "HybridMath"
},
"android": {
"language": "kotlin",
"implementationClassName": "HybridMath"
}
}
}
}
根目录 package.json 脚本
{
"scripts": {
"specs": "bun --cwd packages/react-native-math run specs",
"example": "bun --cwd apps/example"
}
}
运行:bun example android、bun example ios、bun specs
参考资料
| 文件 | 描述 |
|---|---|
| repo-structure-and-workflow.md | 根目录布局、README/文档、packages/apps/config/scripts、CI、分支、草稿 PR 和 squash 合并工作流 |
| setup-monorepo-init.md | 收集脚手架输入并运行 nitrogen init |
| spec-hybrid-object.md | 编写 *.nitro.ts 规格并导出 HybridObjects |
| spec-hybrid-view.md | 构建 Nitro View 组件:HybridView 规格、原生视图、getHostComponent、hybridRef、回调、回收 |
| spec-nitro-json.md | nitro.json 的所有字段、自动链接、命名空间配置 |
| native-nitrogen-codegen.md | 运行 Nitrogen 并验证生成的文件 |
| native-implement-cpp.md | 使用 C++ 实现 HybridObjects |
| native-implement-kotlin.md | 使用 Kotlin 实现 HybridObjects(Android) |
| native-implement-swift.md | 使用 Swift 实现 HybridObjects(iOS) |
| example-app-setup.md | 初始化 RN CLI 示例应用、配置工作区、对齐版本 |
| example-android-config.md | 修正 monorepo 路径的 settings.gradle 和 build.gradle |
| example-metro-install.md | Metro watchFolders、安装库、App.tsx 用法、测试运行 |
| spec-package-publish.md | package.json 作者、files 字段和 npm 发布准备 |
| release-it-publishing.md | 使用 release-it 和 bun release 一键发布 |
| vision-camera-golden-standard.md | 借鉴 VisionCamera 的软件包布局、API 分层、Nitro 对象建模和发布模式 |
问题 → 技能映射
| 问题 | 参考资料 | 操作 |
|---|---|---|
| 需要先设计公共 API | api-design + 此 SKILL.md | 设计 TS/React API,然后应用 Nitro 约束 |
| 需要最新的通用 API | api-design | 决策前查看官方文档、发行说明、源代码仓库、软件包元数据或 llms-full.txt |
| 需要推荐的仓库结构 | 仓库结构与工作流 | 使用 main、完善的 README、packages/、apps/ 或 example/、可选的 Fumadocs、scripts/、config/ 和 .github/workflows/ |
| 不确定应使用静态模块还是实例 API | 此 SKILL.md | 对于原生状态、资源、预热和零拷贝数据,优先使用 HybridObjects |
| 不知道从何开始 | 初始化单仓库 | 使用 nitrogen init 搭建脚手架 |
| 规范文件语法错误 | HybridObject 规范 | 修复 *.nitro.ts 接口 |
| 需要原生视图组件 | HybridView 规范 | 编写 HybridView 规范,实现 view,注册 Android 管理器,并接入 getHostComponent |
视图回调属性收到的是 true/始终不触发 | HybridView 规范 | 使用 callback(...) 包装每个函数属性(包括 hybridRef) |
| Nitro View 在 Android 上提示“找不到视图管理器” | HybridView 规范 | 在 createViewManagers 中添加生成的 Hybrid*ViewManager |
| 自动链接不起作用 | nitro.json | 检查 nitro.json 中的自动链接配置块 |
| Nitrogen 未生成任何文件 | Nitrogen 原生代码生成 | 确认规范文件扩展名,并从正确的目录运行命令 |
| 不清楚 C++ 类型 | 原生 C++ 实现 | 按照类型参考链接查看权威示例 |
| Kotlin 编译错误 | 原生 Kotlin 实现 | 检查注解和 override 修饰符 |
| Swift 编译错误 | 原生 Swift 实现 | 检查类继承和属性签名 |
| 示例应用无法构建(Android) | Android 示例配置 | 修复 Gradle 单仓库路径配置 |
| Metro 无法解析库 | Metro 示例安装 | 在 metro.config.js 中添加 watchFolders |
| 示例与软件包版本不一致 | 示例应用设置 | 在各工作区统一 react-native 版本 |
| npm 上的软件包缺少文件 | 软件包发布规范 | 修复 package.json 中的 files 字段 |
| 需要一键发布 | 使用 release-it 发布 | 配置通过 bun release 调用的 release-it |
| 需要功能完备的库结构 | VisionCamera 黄金标准 | 使用受 VisionCamera 启发的软件包、API、钩子、视图和 Nitro 对象模型 |