Configuração manual
Use este guia quando você já tiver um crate Rust ou pacote JavaScript, precisar
de um projeto mínimo, ou quiser colocar os pacotes Rust e JavaScript em partes
diferentes de um monorepo. Se você está começando um pacote independente e quer
o workflow completo de release, napi new geralmente é mais
rápido.
A CLI é uma ferramenta de build e empacotamento. Seu addon continua sendo um crate Cargo comum, então você pode usar o layout de workspace e o gerenciador de pacotes que já possui.
Pré-requisitos
Instale uma toolchain Rust atual, Node.js 22.13+ (ou Node.js 24+) para a CLI atual e a CLI do NAPI-RS no pacote JavaScript que controla o addon:
rustc --version
node --version
npm install --save-dev @napi-rs/cli@^3
Manter a CLI local faz com que as builds locais e a CI usem a versão registrada
pelo projeto. Execute-a por um script de pacote ou por npx napi; uma
instalação global não é necessária.
Projeto mínimo
O menor layout útil é:
my-addon/
├── Cargo.toml
├── build.rs
├── package.json
├── src/
│ └── lib.rs
└── test.cjs
Configure o Cargo
A biblioteca precisa ser um cdylib: o Node carrega a biblioteca compartilhada
resultante em vez de vinculá-la a outro executável Rust. napi-build configura
a saída para a plataforma host, e as features padrão de napi-derive
habilitam validação estrita de macros e geração de definições TypeScript.
[package]
name = "my-addon-native"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = "3"
napi-derive = "3"
[build-dependencies]
napi-build = "2"
Crie o script de build:
fn main() {
napi_build::setup();
}
Exporte uma função Rust
use napi_derive::napi;
#[napi]
pub fn add(left: i32, right: i32) -> i32 {
left + right
}
Configure o pacote JavaScript
binaryName controla o nome do arquivo gerado. --platform adiciona o sufixo
da plataforma atual e gera um loader que seleciona o binário local ou o pacote
opcional de plataforma correspondente.
{
"name": "my-addon",
"version": "0.1.0",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"build": "napi build --platform",
"build:release": "napi build --platform --release",
"test": "node --test test.cjs"
},
"napi": {
"binaryName": "my-addon"
},
"devDependencies": {
"@napi-rs/cli": "^3"
}
}
Compile e chame o addon:
const assert = require('node:assert/strict')
const test = require('node:test')
const { add } = require('./index.js')
test('adds two numbers', () => {
assert.equal(add(2, 3), 5)
})
npm run build
npm test
Uma build de depuração produz estes arquivos no diretório do crate por padrão:
index.d.ts
index.js
my-addon.<platform-arch-abi>.node
O arquivo .node é a biblioteca nativa. Importe index.js, e não um arquivo
de plataforma codificado manualmente: o loader gerado também lida com seleção
de libc, pacotes de plataforma publicados separadamente e um fallback WASI
opcional.
INFO
Sem --platform, a CLI copia um único arquivo my-addon.node, mas não gera
o loader JavaScript. Isso é útil para experimentos de baixo nível; pacotes
publicados normalmente devem usar --platform.
Variações comuns
Funções assíncronas
Habilite a feature async quando uma async fn Rust exportada precisar se
transformar em um Promise JavaScript:
[dependencies]
napi = { version = "3", features = ["async"] }
napi-derive = "3"
tokio = { version = "1", features = ["fs"] }
Veja Assíncrono e concorrência antes de
escolher entre Tokio, AsyncTask, ThreadsafeFunction e streams.
Um diretório de saída personalizado
Todos os caminhos são relativos a --cwd. Coloque os arquivos JavaScript,
TypeScript e nativos gerados em um pacote JavaScript com:
napi build --platform --output-dir ./dist
Mantenha o loader e seu arquivo .node local juntos. Mover apenas index.js
quebra a resolução relativa dele.
Um arquivo de configuração separado
Por padrão, a CLI lê o objeto napi de package.json. Você pode mover esse
objeto para um JSON e passar --config-path; quando ambos existem, o arquivo
separado vence.
{
"binaryName": "my-addon",
"packageName": "@scope/my-addon",
"targets": [
"x86_64-unknown-linux-gnu",
"aarch64-apple-darwin",
"x86_64-pc-windows-msvc"
]
}
napi build --platform --config-path napi.config.json
targets descreve os artefatos que você pretende empacotar. Uma build local
ainda compila um target por vez; passe --target <rust-triple>
explicitamente na CI.
Workspaces Cargo e JavaScript
O crate Rust e o pacote JavaScript não precisam compartilhar um diretório. Por exemplo:
workspace/
├── Cargo.toml # [workspace] members = ["crates/native"]
├── crates/
│ └── native/
│ ├── Cargo.toml # package.name = "my-addon-native"
│ ├── build.rs
│ └── src/lib.rs
└── packages/
└── addon/
└── package.json # controla a configuração napi e a saída gerada
Execute a CLI a partir da raiz do workspace deixando todos os caminhos explícitos:
napi build \
--cwd packages/addon \
--manifest-path ../../Cargo.toml \
--package my-addon-native \
--package-json-path package.json \
--output-dir . \
--platform
A distinção importante é:
| Opção | Seleciona |
|---|---|
--cwd |
Diretório-base para todos os outros caminhos relativos |
--manifest-path |
Cargo.toml do crate ou workspace usado por cargo metadata |
--package |
Nome exato do pacote Cargo a compilar dentro de um workspace |
--package-json-path |
Pacote JavaScript e configuração do NAPI-RS |
--output-dir |
Destino dos arquivos .node, loader e .d.ts |
Se o manifest aponta para um workspace Cargo virtual, --package é
obrigatório. Caso contrário, a CLI não consegue saber qual membro cdylib
controla o addon.
Prepare para distribuir
Para uma única máquina local, o loader gerado e o arquivo .node são
suficientes. Um pacote multiplataforma publicado normalmente usa um pacote npm
opcional separado para cada target:
- Adicione todos os release triples a
napi.targets. - Compile um artefato
--platform --release --target <triple>por job de CI. - Execute
napi create-npm-dirs. - Baixe os artefatos da CI e execute
napi artifacts. - Siga o guia de release e leia todos os efeitos
colaterais de
napi pre-publishantes de publicar.
Não publique um binário compilado na sua máquina de desenvolvimento como se ele desse suporte a outros sistemas operacionais. Use o guia de compilação cruzada e teste o pacote final em cada runtime que você afirma suportar.