Skip to content

一个简单的包

本教程将创建一个 napi-rs v3 插件,从 Node.js 调用它,并为项目配置 CI。请先完成前置条件

创建项目

请选择一个你有权使用的包名。强烈建议使用 scope,因为发布工作流会为每个目标创建一个 npm 包。

以下命令使用 Yarn 模板,并为其他选项提供非交互式默认值:

sh
npx @napi-rs/cli new cool \
  --name @your-scope/cool \
  --no-interactive

改用 pnpm 模板:

sh
pnpm dlx @napi-rs/cli new cool \
  --name @your-scope/cool \
  --package-manager pnpm \
  --no-interactive

运行命令前,请替换 @your-scope。如果希望交互式选择 Node-API 级别、目标、许可证和 CI 工作流,请省略 --no-interactive

INFO

napi new 支持持续维护的 Yarn 和 pnpm 模板。它不会生成一个之后可随意切换包管理器的通用模板。

了解生成的项目

进入项目并安装依赖:

sh
cd cool
yarn install

pnpm 模板请使用 pnpm install。最先会用到的文件有:

text
.
├── .github/workflows/CI.yml
├── Cargo.toml
├── build.rs
├── package.json
├── src/lib.rs
└── __test__/index.spec.ts
  • src/lib.rs 是 Rust 插件源代码。
  • Cargo.toml 声明 cdylib 和 napi-rs v3 crate。
  • build.rs 调用 napi-rs 构建设置,必须保留在 crate 根目录。
  • package.json 包含 CLI 脚本与 napi 打包配置。
  • .github/workflows/CI.yml 构建并测试从模板保留的目标任务。

此时还没有 npm/。各平台构建完成后,发布任务会通过 napi create-npm-dirs 创建各平台包目录。

生成的 Rust 源代码导出了一个简单函数:

src/lib.rs
rust
#![deny(clippy::all)]

use napi_derive::napi;

#[napi]
pub fn plus_100(input: u32) -> u32 {
  input + 100
}

该宏会把 Rust 函数公开为 JavaScript 函数 plus100,并在构建时写入相应的 TypeScript 声明。

构建插件

为当前平台运行模板的 release 构建:

sh
yarn build

pnpm 请运行 pnpm build。该脚本调用 napi build --platform --release,生成类似以下文件:

text
cool.darwin-arm64.node
index.js
index.d.ts

.node 的确切后缀取决于当前操作系统、架构和 ABI。例如 Linux glibc 构建使用 linux-x64-gnu。需要调试构建时使用 yarn build:debug

生成的声明包含:

index.d.ts
ts
export declare function plus100(input: number): number

从 Node.js 调用原生函数:

sh
node -e "const { plus100 } = require('./index.js'); console.log(plus100(42))"

输出为:

text
142

修改并测试 Rust API

src/lib.rs 中添加另一个导出函数:

src/lib.rs
rust
#[napi]
pub fn multiply(left: i32, right: i32) -> i32 {
  left * right
}

重新构建,然后验证运行时导出和生成的类型:

sh
yarn build
node -e "const { multiply } = require('./index.js'); console.log(multiply(6, 7))"

__test__/index.spec.ts 中添加对应的 AVA 断言:

**test/index.spec.ts**

ts
import test from 'ava'

import { multiply } from '../index'

test('multiply in native code', (t) => {
  t.is(multiply(6, 7), 42)
})

运行测试:

sh
yarn test

准备仓库

推送生成的工作流之前,请更新 package.json

  • name 设为你有权发布的包名和 scope。
  • repository 设为最终 GitHub 仓库。npm provenance 会检查该元数据,因此不要保留模板仓库 URL。
  • 检查 licensedescriptionkeywordshomepagebugs
  • 检查 napi.targets。它控制包创建与发布,但每个目标仍需实际的 CI 构建任务。

如果之后更改包名或二进制名称,请使用 CLI,使 Cargo、包配置、CI 和生成的绑定名称保持一致:

sh
yarn napi rename \
  --name @your-scope/cool \
  --binary-name cool \
  --repository https://github.com/your-name/cool.git

然后创建并推送仓库:

sh
git init
git add .
git commit -m "Create napi-rs package"
git branch -M main
git remote add origin git@github.com:your-name/cool.git
git push -u origin main

准备 npm 和 GitHub Actions

生成的工作流通过 npm 发布,并创建 GitHub release。对于其基于 token 的设置:

  1. 创建要使用的 npm scope 和包访问权限。
  2. 创建一个能发布根包和所有平台包的 npm automation token。
  3. 将其添加为 NPM_TOKEN Actions secret。
  4. 为 GitHub release 保留工作流的 contents: write 权限,为 npm provenance 保留 id-token: write 权限。
  5. 尝试发布前,先确保普通 CI 路径成功运行。

WARNING

发布不是原子操作。napi pre-publish 会更新包元数据、发布平台包,并且可能在 npm 发布根包之前创建或更新 GitHub release。不要把真实凭据用于试运行。

请阅读发布原生包,了解完整的发布前检查、发布与恢复流程。确切的副作用和标志见 napi pre-publish

后续阅读