
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@crustacean/rust
Advanced tools
Nx plugin for integrating Rust/Cargo into Nx workspaces.
Cargo.toml files and infers build, test, lint, fmt, artifacts, and prepublish targetscargo build, cargo test, cargo clippy, cargo fmt, NAPI-RS artifact collection, and prepublishCargo.toml (and package.json for NAPI-RS) using Nx Releasepnpm nx g @crustacean/rust:init
This initializes the workspace with:
Cargo.toml workspace (resolver 2)rust-toolchain.toml (stable channel, rustfmt + clippy).rustfmt.toml with opinionated defaultstarget/ added to .gitignorenx.json for automatic target inferencelibraryCreate a Rust library crate.
pnpm nx g @crustacean/rust:library packages/my-lib
| Option | Type | Default | Description |
|---|---|---|---|
directory | string | — | Project path relative to workspace root |
name | string | — | Crate name (inferred from directory) |
edition | string | 2024 | Rust edition |
tags | string | — | Nx tags (comma-separated) |
publishable | boolean | false | Configure for publishing to crates.io |
description | string | — | Crate description (required for publishing) |
license | string | MIT | SPDX license identifier |
applicationCreate a Rust binary application.
pnpm nx g @crustacean/rust:application packages/my-app
| Option | Type | Default | Description |
|---|---|---|---|
directory | string | — | Project path relative to workspace root |
name | string | — | Crate name (inferred from directory) |
edition | string | 2024 | Rust edition |
tags | string | — | Nx tags (comma-separated) |
napiCreate a NAPI-RS library (Rust-to-Node.js binding).
pnpm nx g @crustacean/rust:napi packages/my-napi
| Option | Type | Default | Description |
|---|---|---|---|
directory | string | — | Project path relative to workspace root |
name | string | — | Crate name (inferred from directory) |
edition | string | 2024 | Rust edition |
tags | string | — | Nx tags (comma-separated) |
publishable | boolean | false | Configure for publishing to npm/crates.io |
description | string | — | Package description |
license | string | MIT | SPDX license identifier |
targets | string[] | [x86_64-unknown-linux-gnu, ...] | NAPI-RS build targets (Rust target triples) |
The generator creates:
Cargo.toml with crate-type = ["cdylib"] and napi dependenciessrc/lib.rs with NAPI boilerplatebuild.rs for napi-build.cargo/config.toml with linker flags for macOSpackage.json with napi config, @napi-rs/cli devDependency, and build script.npmignore excluding Rust source files| Executor | Description |
|---|---|
build | Build a Rust crate with cargo build |
test | Run tests with cargo test |
lint | Lint with cargo clippy |
fmt | Format with cargo fmt |
artifacts | Collect NAPI-RS .node binaries from CI artifacts |
prepublish | Generate platform-specific npm packages for NAPI-RS |
The plugin automatically detects Cargo.toml files and registers targets. Configure target names in nx.json:
{
"plugins": [
{
"plugin": "@crustacean/rust/plugin",
"options": {
"buildTargetName": "build",
"testTargetName": "test",
"lintTargetName": "lint",
"fmtTargetName": "fmt",
"artifactsTargetName": "artifacts",
"prepublishTargetName": "prepublish"
}
}
]
}
For NAPI-RS projects (crates with napi dependency), the plugin also infers artifacts and prepublish targets. The prepublish target automatically depends on artifacts.
Configure Nx Release to version Rust crates by pointing versionActions to this plugin:
{
"release": {
"projects": ["packages/*"],
"version": {
"conventionalCommits": true,
"versionActions": "@crustacean/rust/version-actions"
}
}
}
The version actions handle:
Cargo.tomlversion in Cargo.toml (preserves TOML formatting)version in package.json if present (NAPI-RS projects)[dependencies], [dev-dependencies], and [build-dependencies]workspace = true dependencies (managed by Cargo workspace inheritance)For mixed workspaces (Rust + JS), use release groups:
{
"release": {
"groups": {
"rust": {
"projects": ["tag:type:rust-lib"],
"version": {
"versionActions": "@crustacean/rust/version-actions"
}
},
"js": {
"projects": ["tag:type:js-lib"]
}
},
"version": {
"conventionalCommits": true
}
}
}
Publishing a NAPI-RS package to npm is a multi-step process because the native .node binary must be compiled for each target platform. The final npm distribution consists of:
@scope/my-napi) — JS loader + type declarations@scope/my-napi-linux-x64-gnu, @scope/my-napi-darwin-arm64, etc.) — each contains a single .node binaryThe main package lists platform packages as optionalDependencies. When users install the main package, npm automatically downloads only the binary matching their OS/arch.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ ┌─────────┐
│ CI Build │──►│ Collect │──►│ Prepublish │──►│ Version │──►│ Publish │
│ Matrix │ │ Artifacts │ │ │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ └─────────┘
napi build napi artifacts napi prepublish nx release npm publish
(per platform) (all .node) (npm packages) version (all pkgs)
Each platform is built in a separate CI job. The build executor compiles the crate with napi build --platform --release:
# .github/workflows/release.yml
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-13
- target: aarch64-apple-darwin
os: macos-14
- target: x86_64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@v4
- uses: napi-rs/setup-napi-action@v1
- run: pnpm nx build my-napi --release --target=${{ matrix.target }}
- uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: packages/my-napi/*.node
Each job produces a .node file named <binary>.<platform>.node (e.g., my_napi.linux-x64-gnu.node).
After all build jobs complete, a single job downloads all artifacts and runs napi artifacts to organize them into the npm package structure:
collect:
needs: [build]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: packages/my-napi/artifacts
merge-multiple: true
- run: pnpm nx artifacts my-napi
The artifacts executor runs napi artifacts --output-dir ./artifacts --npm-dir ./npm which copies each .node file into the corresponding platform directory under npm/.
Executor options:
| Option | Type | Default | Description |
|---|---|---|---|
distPath | string | ./dist | Directory containing built .node files |
jsBinding | boolean | true | Generate JS binding files |
dts | boolean | true | Generate TypeScript declarations |
args | array | — | Additional arguments passed to napi |
napi prepublish generates the platform-specific npm packages. Each gets its own package.json with os and cpu fields. The main package.json is updated with optionalDependencies pointing to each platform package.
- run: pnpm nx prepublish my-napi
The prepublish executor runs napi prepublish -p npm which creates:
packages/my-napi/
├── npm/
│ ├── linux-x64-gnu/
│ │ ├── package.json ← { "os": ["linux"], "cpu": ["x64"], ... }
│ │ └── my_napi.linux-x64-gnu.node
│ ├── darwin-arm64/
│ │ ├── package.json
│ │ └── my_napi.darwin-arm64.node
│ └── ...
├── package.json ← updated with optionalDependencies
├── index.js ← JS loader (auto-detects platform)
└── index.d.ts ← TypeScript declarations
Executor options:
| Option | Type | Default | Description |
|---|---|---|---|
npmDir | string | npm | Directory for platform-specific packages |
tagStyle | string | lerna | Git tag style (npm or lerna) |
ghRelease | boolean | false | Create a GitHub release |
ghReleaseName | string | — | Custom GitHub release name |
ghReleaseId | string | — | Existing GitHub release ID |
dryRun | boolean | false | Dry run without file changes |
args | array | — | Additional arguments passed to napi |
Use Nx Release to bump versions. The RustVersionActions automatically updates both Cargo.toml and package.json:
- run: pnpm nx release version --specifier=patch
This bumps:
Cargo.toml → version = "1.0.1"package.json → "version": "1.0.1"Platform-specific package versions are derived from the main package.json by napi prepublish, so they don't need separate versioning.
Publish all packages to npm. The main package and each platform package are published separately:
- run: |
cd packages/my-napi
npm publish --access public
for dir in npm/*/; do
cd "$dir"
npm publish --access public
cd ../..
done
Alternatively, use napi prepublish with --skip-optional-publish=false (default) which handles publishing platform packages automatically when run as a prepublishOnly script.
name: Release
on:
push:
tags:
- 'my-napi@*'
jobs:
build:
strategy:
matrix:
include:
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-latest }
- { target: x86_64-apple-darwin, os: macos-13 }
- { target: aarch64-apple-darwin, os: macos-14 }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: napi-rs/setup-napi-action@v1
- run: pnpm nx build my-napi --release --target=${{ matrix.target }}
- uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: packages/my-napi/*.node
publish:
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@v4
with:
path: packages/my-napi/artifacts
merge-multiple: true
- run: pnpm nx artifacts my-napi
- run: pnpm nx prepublish my-napi --no-gh-release
- run: |
cd packages/my-napi
npm publish --access public
for dir in npm/*/; do
(cd "$dir" && npm publish --access public)
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Are platform-specific packages Nx projects?
No. They are generated artifacts created by napi prepublish under the npm/ directory. They don't have Cargo.toml and are not registered in the Nx workspace. Their version is derived from the main package.json — no separate versioning needed.
Does VersionActions bump platform package versions?
No, and it doesn't need to. Platform packages are regenerated by napi prepublish each time, inheriting the version from the main package.json. The VersionActions bumps Cargo.toml + main package.json, which is sufficient.
Can I use nx release publish for NAPI-RS packages?
nx release publish works for the main package but won't automatically publish platform packages (since they aren't Nx projects). Use the shell loop shown above, or configure prepublishOnly in package.json to handle it via npm lifecycle scripts.
What about napi prepublish --gh-release?
If you use GitHub Releases to distribute binaries (alternative to npm), pass --ghRelease to the prepublish executor. This uploads .node files as GitHub Release assets. Users then download the binary directly instead of via npm optionalDependencies.
FAQs
@crustacean/rust
We found that @crustacean/rust demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.