---
title: '一个简单的包'
description: 创建、构建、测试并准备一个 napi-rs v3 包。
---

# 一个简单的包

本教程将创建一个 napi-rs v3 插件，从 Node.js 调用它，并为项目配置 CI。请先完成[前置条件](./getting-started#前置条件)。

## 创建项目

请选择一个你有权使用的包名。强烈建议使用 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。
- 检查 `license`、`description`、`keywords`、`homepage` 和 `bugs`。
- 检查 `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。不要把真实凭据用于试运行。

:::

请阅读[发布原生包](/cn/docs/deep-dive/release)，了解完整的发布前检查、发布与恢复流程。确切的副作用和标志见 [`napi pre-publish`](/cn/docs/cli/pre-publish)。

## 后续阅读

- [值](/cn/docs/concepts/values)：Rust 到 JavaScript 的转换。
- [异步函数](/cn/docs/concepts/async-fn)：异步导出。
- [支持与兼容性](/cn/docs/more/support-compatibility)：扩展目标矩阵前阅读。
- [交叉编译](/cn/docs/cross-build)：非宿主目标构建。
