mcpField
B
build-nitro-modules

build-nitro-modules

github.com/margelo开发178941 次安装10 周前更新
代码仓库

关于 build-nitro-modules

用于构建 React Native Nitro Modules 的端到端 Skill,覆盖仓库脚手架、TypeScript HybridObject 规格、原生实现和发布准备。重点在 Nitro 专属 API 设计、代码生成、平台绑定与验证。

主要功能

  1. 01使用 Nitrogen 搭建模块仓库
  2. 02编写 TypeScript HybridObject 规格
  3. 03生成 C++、Swift 和 Kotlin 原生代码
  4. 04指导原生实现与示例应用接线
  5. 05涵盖代码生成、验证和发布准备

适用场景

  1. 01创建 React Native Nitro Module
  2. 02定义类型化的 HybridObject API
  3. 03实现跨平台原生模块绑定
  4. 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.md
example-app-setup.md
example-metro-install.md
native-implement-cpp.md
native-implement-kotlin.md
native-implement-swift.md
native-nitrogen-codegen.md
release-it-publishing.md
repo-structure-and-workflow.md
setup-monorepo-init.md
spec-hybrid-object.md
spec-hybrid-view.md
spec-nitro-json.md
spec-package-publish.md
vision-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 compileSdkVersion 34+ 和 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 或 Swift Int。应通过领域类型的 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);只有在端到端封装 Swift async/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 将 Google Task<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/Kotlin PlatformContext,并通过生成的 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?”


如果要创建新库——执行任何命令之前,先询问以下所有问题:
  1. 库名称——这个库应该叫什么?(例如:react-native-math)
  2. 是否使用带有 packages/ 文件夹的 monorepo——这个库是否应该放在 monorepo 中的 packages/<name> 目录下?(强烈推荐——默认:是)
  3. 示例应用——是否应创建示例应用来测试模块?应放在哪里?(推荐——默认:是;需要或可能需要多个示例时使用 apps/example;对于应靠近生成的 RN 配置的小型单示例仓库,仅使用 example)
  4. 原生语言——使用哪些平台和语言?
    • iOS:swift(默认)或 cpp
    • Android:kotlin(默认)或 cpp
    • 仅跨平台 C++:两者均为 cpp
  5. 模块用途——简要描述模块的功能,以便设计合适的规范方法

在这五个问题都得到回答之前,不要继续执行构建流程的第 1 步之后的步骤。

如果要向现有库添加 HybridObject——只询问:
  1. HybridObject 名称——这个新的 HybridObject 应该叫什么?(例如:Camera、Crypto)
  2. 原生语言——iOS:swift 还是 cpp?Android:kotlin 还是 cpp?
  3. 用途——这个 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
0Nitro API 约束严重本 SKILL.md
1仓库结构与工作流高repo-structure-and-workflow.md
2Nitrogen 脚手架严重setup-monorepo-init.md
3HybridObject 规范严重spec-hybrid-object.md
3Hybrid View 组件 (如果要构建视图)高spec-hybrid-view.md
4nitro.json 自动链接严重spec-nitro-json.md
5Nitrogen 代码生成高native-nitrogen-codegen.md
6C++ 实现高native-implement-cpp.md
7Kotlin 实现高native-implement-kotlin.md
8Swift 实现高native-implement-swift.md
9示例应用设置 (按需)高example-app-setup.md
10Android Gradle 路径 (用于示例应用时)高example-android-config.md
11Metro + 安装 + 测试 (用于示例应用时)高example-metro-install.md
12npm 发布准备中spec-package-publish.md
13release-it 发布中release-it-publishing.md
14VisionCamera 风格的完整库模式中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.mdnitro.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.mdMetro watchFolders、安装库、App.tsx 用法、测试运行
spec-package-publish.mdpackage.json 作者、files 字段和 npm 发布准备
release-it-publishing.md使用 release-it 和 bun release 一键发布
vision-camera-golden-standard.md借鉴 VisionCamera 的软件包布局、API 分层、Nitro 对象建模和发布模式

问题 → 技能映射

问题参考资料操作
需要先设计公共 APIapi-design + 此 SKILL.md设计 TS/React API,然后应用 Nitro 约束
需要最新的通用 APIapi-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 对象模型

评论

支持 Markdown